Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Serialization methods for Eth contracts #115

Merged
merged 7 commits into from
Jan 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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