Skip to content

Commit

Permalink
Merge pull request #115 from fjarri/contract-serialization
Browse files Browse the repository at this point in the history
Serialization methods for Eth contracts
  • Loading branch information
fjarri authored Jan 17, 2023
2 parents 1b24c75 + 9569a34 commit be5fd92
Show file tree
Hide file tree
Showing 9 changed files with 126 additions and 25 deletions.
15 changes: 13 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

Under construction.
### Added

- Added `Signature::to_be_bytes()`, `Capsule::to_bytes_simple()`, and `CapsuleFrag::to_bytes_simple()` to use in Ethereum contracts. ([#115])


### Fixed

- Added the missing `VerifiedCapsuleFrag::unverify()` method in WASM bindings. ([#115])
- Added the missing `Signature::to_der_bytes()` and `from_der_bytes()` that were missing in the Rust library but present in the bindings. ([#115])


[#115]: https://github.com/nucypher/rust-umbral/pull/115


## [0.8.0] - 2023-01-15
Expand Down Expand Up @@ -220,7 +231,7 @@ the corresponding methods in Python and WASM bindings. ([#84])

- Initial release.

[Unreleased]: https://github.com/nucypher/rust-umbral/compare/v0.7.0...HEAD
[Unreleased]: https://github.com/nucypher/rust-umbral/compare/v0.8.0...HEAD
[0.2.0]: https://github.com/nucypher/rust-umbral/releases/tag/v0.2.0
[0.3.0]: https://github.com/nucypher/rust-umbral/releases/tag/v0.3.0
[0.4.0]: https://github.com/nucypher/rust-umbral/releases/tag/v0.4.0
Expand Down
9 changes: 9 additions & 0 deletions umbral-pre-python/umbral_pre/__init__.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,9 @@ class Signature:
def to_der_bytes(self) -> bytes:
...

def to_be_bytes(self) -> bytes:
...


class Capsule:

Expand All @@ -90,6 +93,9 @@ class Capsule:
def __bytes__(self) -> bytes:
...

def to_bytes_simple(self) -> bytes:
...


def encrypt(delegating_pk: PublicKey, plaintext: bytes) -> Tuple[Capsule, bytes]:
...
Expand Down Expand Up @@ -162,6 +168,9 @@ class CapsuleFrag:
def __bytes__(self) -> bytes:
...

def to_bytes_simple(self) -> bytes:
...


class VerifiedCapsuleFrag:

Expand Down
15 changes: 15 additions & 0 deletions umbral-pre/src/bindings_python.rs
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,11 @@ impl Signature {
Python::with_gil(|py| PyBytes::new(py, &serialized).into())
}

fn to_be_bytes(&self) -> PyObject {
let serialized = self.backend.to_be_bytes();
Python::with_gil(|py| PyBytes::new(py, &serialized).into())
}

fn verify(&self, verifying_pk: &PublicKey, message: &[u8]) -> bool {
self.backend.verify(&verifying_pk.backend, message)
}
Expand Down Expand Up @@ -278,6 +283,11 @@ impl Capsule {
to_bytes(self)
}

fn to_bytes_simple(&self) -> PyObject {
let serialized = self.backend.to_bytes_simple();
Python::with_gil(|py| PyBytes::new(py, &serialized).into())
}

fn __richcmp__(&self, other: &Self, op: CompareOp) -> PyResult<bool> {
richcmp(self, other, op)
}
Expand Down Expand Up @@ -470,6 +480,11 @@ impl CapsuleFrag {
to_bytes(self)
}

fn to_bytes_simple(&self) -> PyObject {
let serialized = self.backend.to_bytes_simple();
Python::with_gil(|py| PyBytes::new(py, &serialized).into())
}

fn __richcmp__(&self, other: &Self, op: CompareOp) -> PyResult<bool> {
richcmp(self, other, op)
}
Expand Down
19 changes: 19 additions & 0 deletions umbral-pre/src/bindings_wasm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,11 @@ impl Signature {
self.0.to_der_bytes()
}

#[wasm_bindgen(js_name = toBEBytes)]
pub fn to_be_bytes(&self) -> Box<[u8]> {
self.0.to_be_bytes()
}

#[wasm_bindgen(js_name = fromDerBytes)]
pub fn from_der_bytes(data: &[u8]) -> Result<Signature, Error> {
umbral_pre::Signature::try_from_der_bytes(data)
Expand Down Expand Up @@ -270,6 +275,11 @@ impl Capsule {
self.0.to_bytes().map_err(map_js_err)
}

#[wasm_bindgen(js_name = toBytesSimple)]
pub fn to_bytes_simple(&self) -> Box<[u8]> {
self.0.to_bytes_simple()
}

#[wasm_bindgen(js_name = fromBytes)]
pub fn from_bytes(data: &[u8]) -> Result<Capsule, Error> {
umbral_pre::Capsule::from_bytes(data)
Expand Down Expand Up @@ -319,6 +329,11 @@ impl CapsuleFrag {
self.0.to_bytes().map_err(map_js_err)
}

#[wasm_bindgen(js_name = toBytesSimple)]
pub fn to_bytes_simple(&self) -> Box<[u8]> {
self.0.to_bytes_simple()
}

#[wasm_bindgen(js_name = fromBytes)]
pub fn from_bytes(data: &[u8]) -> Result<CapsuleFrag, Error> {
umbral_pre::CapsuleFrag::from_bytes(data)
Expand Down Expand Up @@ -349,6 +364,10 @@ pub struct VerifiedCapsuleFrag(umbral_pre::VerifiedCapsuleFrag);

#[wasm_bindgen]
impl VerifiedCapsuleFrag {
pub fn unverify(&self) -> CapsuleFrag {
CapsuleFrag(self.0.clone().unverify())
}

#[wasm_bindgen(js_name = toBytes)]
pub fn to_bytes(&self) -> Result<Box<[u8]>, Error> {
self.0.to_bytes().map_err(map_js_err)
Expand Down
15 changes: 7 additions & 8 deletions umbral-pre/src/capsule.rs
Original file line number Diff line number Diff line change
Expand Up @@ -131,17 +131,16 @@ impl Capsule {
}
}

pub(crate) fn to_associated_data_bytes(&self) -> Box<[u8]> {
/// Serializes the capsule by concatenating the byte represenation of its constituents:
/// - `e` (compressed curve point, 33 bytes),
/// - `v` (compressed curve point, 33 bytes),
/// - `signature` (big-endian scalar, 32 bytes).
pub fn to_bytes_simple(&self) -> Box<[u8]> {
let e = self.point_e.to_compressed_array();
let v = self.point_v.to_compressed_array();
let s = self.signature.to_array();

let e_ref: &[u8] = e.as_ref();
let v_ref: &[u8] = v.as_ref();
let s_ref: &[u8] = s.as_ref();

let v: Vec<u8> = [e_ref, v_ref, s_ref].concat();
v.into()
let v: &[&[u8]] = &[&e, &v, &s];
v.concat().into()
}

/// Verifies the integrity of the capsule.
Expand Down
40 changes: 40 additions & 0 deletions umbral-pre/src/capsule_frag.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use alloc::boxed::Box;
use core::fmt;

use rand_core::{CryptoRng, RngCore};
Expand Down Expand Up @@ -130,6 +131,45 @@ impl CapsuleFrag {
}
}

/// Serializes the capsule frag by concatenating the byte represenation of its constituents:
/// - `e1` (compressed curve point, 33 bytes),
/// - `v1` (compressed curve point, 33 bytes),
/// - `kfrag_id` (32 bytes),
/// - `precursor` (compressed curve point, 33 bytes),
/// - `e2` (compressed curve point, 33 bytes),
/// - `v2` (compressed curve point, 33 bytes),
/// - `commitment` (compressed curve point, 33 bytes),
/// - `kfrag_pok` (compressed curve point, 33 bytes),
/// - `signature` (big-endian scalar, 32 bytes),
/// - `kfrag_signature` (ECDSA signature serialized as `r` and `s`,
/// each a 32 byte big-endian scalar).
pub fn to_bytes_simple(&self) -> Box<[u8]> {
let e1 = self.point_e1.to_compressed_array();
let v1 = self.point_v1.to_compressed_array();
let precursor = self.precursor.to_compressed_array();

let e2 = self.proof.point_e2.to_compressed_array();
let v2 = self.proof.point_v2.to_compressed_array();
let commitment = self.proof.kfrag_commitment.to_compressed_array();
let pok = self.proof.kfrag_pok.to_compressed_array();
let sig = self.proof.signature.to_array();
let kfrag_sig = self.proof.kfrag_signature.to_be_bytes();

let v: &[&[u8]] = &[
&e1,
&v1,
self.kfrag_id.as_ref(),
&precursor,
&e2,
&v2,
&commitment,
&pok,
&sig,
&kfrag_sig,
];
v.concat().into()
}

/// Verifies the integrity of the capsule fragment, given the original capsule,
/// the encrypting party's key, the decrypting party's key, and the signing key.
#[allow(clippy::many_single_char_names)]
Expand Down
29 changes: 17 additions & 12 deletions umbral-pre/src/keys.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use core::cmp::Ordering;
use core::fmt;

use ecdsa::{
signature::{DigestVerifier, RandomizedDigestSigner},
signature::{DigestVerifier, RandomizedDigestSigner, Signature as SignatureTrait},
Signature as BackendSignature, SigningKey, VerifyingKey,
};
use generic_array::{
Expand Down Expand Up @@ -39,18 +39,31 @@ use crate::serde_bytes::{
pub struct Signature(BackendSignature<CurveType>);

impl Signature {
pub(crate) fn to_der_bytes(&self) -> Box<[u8]> {
/// Returns the signature serialized as concatenated `r` and `s`
/// in big endian order (32+32 bytes).
pub fn to_be_bytes(&self) -> Box<[u8]> {
self.0.as_bytes().into()
}

/// Returns the signature serialized in ASN.1 DER format.
pub fn to_der_bytes(&self) -> Box<[u8]> {
self.0.to_der().as_bytes().into()
}

#[cfg(feature = "serde-support")]
pub(crate) fn try_from_der_bytes(bytes: &[u8]) -> Result<Self, String> {
/// Restores the signature from a bytestring in ASN.1 DER format.
pub fn try_from_der_bytes(bytes: &[u8]) -> Result<Self, String> {
// Note that it will not normalize `s` automatically,
// and if it is not normalized, verification will fail.
BackendSignature::<CurveType>::from_der(bytes)
.map(Self)
.map_err(|err| format!("Internal backend error: {}", err))
}

/// Verifies that the given message was signed with the secret counterpart of the given key.
/// The message is hashed internally.
pub fn verify(&self, verifying_pk: &PublicKey, message: &[u8]) -> bool {
verifying_pk.verify_digest(digest_for_signing(message), self)
}
}

#[cfg(feature = "serde-support")]
Expand Down Expand Up @@ -84,14 +97,6 @@ impl TryFromBytes for Signature {
}
}

impl Signature {
/// Verifies that the given message was signed with the secret counterpart of the given key.
/// The message is hashed internally.
pub fn verify(&self, verifying_pk: &PublicKey, message: &[u8]) -> bool {
verifying_pk.verify_digest(digest_for_signing(message), self)
}
}

impl fmt::Display for Signature {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt_public("Signature", &self.to_der_bytes(), f)
Expand Down
3 changes: 3 additions & 0 deletions umbral-pre/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@
//! ## Available feature flags
//!
//! * `default-rng` - adds methods that use the system RNG (default).
//! * `default-serialization` - adds methods for default binary serialization
//! that matches the serialization in the bindings.
//! MessagePack, `serde`-based.
//! * `serde-support` - implements `serde`-based serialization and deserialization.
//! * `bindings-python` - adds a `bindings_python` submodule allowing dependent crates
//! to use and re-export some of the Python-wrapped Umbral types.
Expand Down
6 changes: 3 additions & 3 deletions umbral-pre/src/pre.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ pub fn encrypt_with_rng(
) -> Result<(Capsule, Box<[u8]>), EncryptionError> {
let (capsule, key_seed) = Capsule::from_public_key(rng, delegating_pk);
let dem = DEM::new(key_seed.as_secret());
dem.encrypt(rng, plaintext, &capsule.to_associated_data_bytes())
dem.encrypt(rng, plaintext, &capsule.to_bytes_simple())
.map(|ciphertext| (capsule, ciphertext))
}

Expand All @@ -66,7 +66,7 @@ pub fn decrypt_original(
) -> Result<Box<[u8]>, DecryptionError> {
let key_seed = capsule.open_original(delegating_sk);
let dem = DEM::new(key_seed.as_secret());
dem.decrypt(ciphertext, &capsule.to_associated_data_bytes())
dem.decrypt(ciphertext, &capsule.to_bytes_simple())
}

/// Creates `shares` fragments of `delegating_sk`,
Expand Down Expand Up @@ -184,7 +184,7 @@ pub fn decrypt_reencrypted(
.open_reencrypted(receiving_sk, delegating_pk, &cfrags)
.map_err(ReencryptionError::OnOpen)?;
let dem = DEM::new(key_seed.as_secret());
dem.decrypt(&ciphertext, &capsule.to_associated_data_bytes())
dem.decrypt(&ciphertext, &capsule.to_bytes_simple())
.map_err(ReencryptionError::OnDecryption)
}

Expand Down

0 comments on commit be5fd92

Please sign in to comment.