From 30aab6c9f8e815af6b8af53aacf54f1bc31d0fff Mon Sep 17 00:00:00 2001 From: derekpierre Date: Wed, 16 Aug 2023 18:38:22 -0400 Subject: [PATCH 01/14] Initial code and tests for AccessControlPolicy and its use by ThresholdDecryptionRequest. Requires changes to DkgPublicKey in ferveo. --- Cargo.lock | 34 ++---- nucypher-core-python/Cargo.toml | 2 +- .../nucypher_core/__init__.py | 1 + .../nucypher_core/__init__.pyi | 27 ++++- nucypher-core-python/src/lib.rs | 76 ++++++++++--- nucypher-core-wasm/Cargo.toml | 2 +- nucypher-core-wasm/src/lib.rs | 61 +++++++++- nucypher-core-wasm/tests/wasm.rs | 38 ++++++- nucypher-core/Cargo.toml | 2 +- nucypher-core/src/access_control.rs | 104 ++++++++++++++++++ nucypher-core/src/dkg.rs | 43 ++++---- nucypher-core/src/lib.rs | 3 + 12 files changed, 325 insertions(+), 68 deletions(-) create mode 100644 nucypher-core/src/access_control.rs diff --git a/Cargo.lock b/Cargo.lock index f2654251..45c29449 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -518,8 +518,7 @@ dependencies = [ [[package]] name = "ferveo-common-pre-release" version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81b88f694e924a2878d4abf89b79df44cdee2e0670875aba885bb769ccd71be3" +source = "git+https://github.com/derekpierre/nucypher-ferveo.git?branch=acp#59875467165f9fff22c1decaad75ecf46006a5f2" dependencies = [ "ark-ec", "ark-serialize", @@ -534,8 +533,7 @@ dependencies = [ [[package]] name = "ferveo-pre-release" version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a34e97a82e328847d4abf9d5a4dfec06c693210fcb3e6e9feb78f225b8126464" +source = "git+https://github.com/derekpierre/nucypher-ferveo.git?branch=acp#59875467165f9fff22c1decaad75ecf46006a5f2" dependencies = [ "ark-bls12-381", "ark-ec", @@ -638,8 +636,7 @@ dependencies = [ [[package]] name = "group-threshold-cryptography-pre-release" version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ade4859b58171e6815b0641e23f615f5dab030ecf2376b069d652f323fa760b" +source = "git+https://github.com/derekpierre/nucypher-ferveo.git?branch=acp#59875467165f9fff22c1decaad75ecf46006a5f2" dependencies = [ "ark-bls12-381", "ark-ec", @@ -869,7 +866,7 @@ checksum = "94c7128ba23c81f6471141b90f17654f89ef44a56e14b8a4dd0fddfccd655277" [[package]] name = "nucypher-core" -version = "0.10.0" +version = "0.11.0" dependencies = [ "chacha20poly1305", "ferveo-pre-release", @@ -891,19 +888,19 @@ dependencies = [ [[package]] name = "nucypher-core-python" -version = "0.10.0" +version = "0.11.0" dependencies = [ "derive_more", "ferveo-pre-release", "nucypher-core", "pyo3", - "pyo3-build-config 0.19.1", + "pyo3-build-config", "umbral-pre", ] [[package]] name = "nucypher-core-wasm" -version = "0.10.0" +version = "0.11.0" dependencies = [ "console_error_panic_hook", "derive_more", @@ -1033,7 +1030,7 @@ dependencies = [ "libc", "memoffset", "parking_lot", - "pyo3-build-config 0.18.3", + "pyo3-build-config", "pyo3-ffi", "pyo3-macros", "unindent", @@ -1049,16 +1046,6 @@ dependencies = [ "target-lexicon", ] -[[package]] -name = "pyo3-build-config" -version = "0.19.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "554db24f0b3c180a9c0b1268f91287ab3f17c162e15b54caaae5a6b3773396b0" -dependencies = [ - "once_cell", - "target-lexicon", -] - [[package]] name = "pyo3-ffi" version = "0.18.3" @@ -1066,7 +1053,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd4d7c5337821916ea2a1d21d1092e8443cf34879e53a0ac653fbb98f44ff65c" dependencies = [ "libc", - "pyo3-build-config 0.18.3", + "pyo3-build-config", ] [[package]] @@ -1395,8 +1382,7 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] name = "subproductdomain-pre-release" version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7331c4e4ab9b7c9ed4ad2e950e8760826622c7ab110d9f1b3f350e2005017eab" +source = "git+https://github.com/derekpierre/nucypher-ferveo.git?branch=acp#59875467165f9fff22c1decaad75ecf46006a5f2" dependencies = [ "anyhow", "ark-ec", diff --git a/nucypher-core-python/Cargo.toml b/nucypher-core-python/Cargo.toml index 75f9794e..c930cec9 100644 --- a/nucypher-core-python/Cargo.toml +++ b/nucypher-core-python/Cargo.toml @@ -11,7 +11,7 @@ crate-type = ["cdylib"] pyo3 = "0.18" nucypher-core = { path = "../nucypher-core" } umbral-pre = { version = "0.11.0", features = ["bindings-python"] } -ferveo = { version = "0.2.1", package = "ferveo-pre-release", features = ["bindings-python"] } +ferveo = { package = "ferveo-pre-release", git = "https://github.com/derekpierre/nucypher-ferveo.git", branch = "acp", features = ["bindings-python"] } derive_more = { version = "0.99", default-features = false, features = ["from", "as_ref"] } [build-dependencies] diff --git a/nucypher-core-python/nucypher_core/__init__.py b/nucypher-core-python/nucypher_core/__init__.py index cb500c48..a1a7eb8a 100644 --- a/nucypher-core-python/nucypher_core/__init__.py +++ b/nucypher-core-python/nucypher_core/__init__.py @@ -17,6 +17,7 @@ MetadataRequest, MetadataResponse, MetadataResponsePayload, + AccessControlPolicy, ThresholdDecryptionRequest, ThresholdDecryptionResponse, EncryptedThresholdDecryptionRequest, diff --git a/nucypher-core-python/nucypher_core/__init__.pyi b/nucypher-core-python/nucypher_core/__init__.pyi index 96093475..90153e4d 100644 --- a/nucypher-core-python/nucypher_core/__init__.pyi +++ b/nucypher-core-python/nucypher_core/__init__.pyi @@ -11,8 +11,9 @@ from .umbral import ( ) from .ferveo import ( - FerveoPublicKey, Ciphertext, + DkgPublicKey, + FerveoPublicKey, FerveoVariant ) @@ -429,16 +430,34 @@ class MetadataResponse: ... +@final +class AccessControlPolicy: + + def __init__(self, public_key: DkgPublicKey, conditions: Optional[Conditions], authorization: bytes): + ... + + public_key: DkgPublicKey + + conditions: Optional[Conditions] + + authorization: bytes + + @staticmethod + def from_bytes(data: bytes) -> AccessControlPolicy: + ... + + def __bytes__(self) -> bytes: + ... + @final class ThresholdDecryptionRequest: - def __init__(self, ritual_id: int, variant: FerveoVariant, ciphertext: Ciphertext, conditions: Optional[Conditions], - context: Optional[Context]): + def __init__(self, ritual_id: int, variant: FerveoVariant, ciphertext: Ciphertext, acp: AccessControlPolicy, context: Optional[Context]): ... ritual_id: int - conditions: Optional[Conditions] + acp: AccessControlPolicy context: Optional[Context] diff --git a/nucypher-core-python/src/lib.rs b/nucypher-core-python/src/lib.rs index b33e1ccf..fbd410d1 100644 --- a/nucypher-core-python/src/lib.rs +++ b/nucypher-core-python/src/lib.rs @@ -6,8 +6,7 @@ extern crate alloc; use alloc::collections::{BTreeMap, BTreeSet}; - -use ferveo::bindings_python::{Ciphertext, FerveoPublicKey, FerveoVariant}; +use ferveo::bindings_python::{Ciphertext, DkgPublicKey, FerveoPublicKey, FerveoVariant}; use pyo3::class::basic::CompareOp; use pyo3::exceptions::{PyTypeError, PyValueError}; use pyo3::prelude::*; @@ -734,6 +733,64 @@ impl SessionSecretFactory { } } +// +// Access control metadata for encrypted data. +// +#[pyclass(module = "nucypher_core")] +#[derive(derive_more::From, derive_more::AsRef)] +pub struct AccessControlPolicy { + backend: nucypher_core::AccessControlPolicy, +} + +#[pymethods] +impl AccessControlPolicy { + #[new] + pub fn new( + public_key: &DkgPublicKey, + authorization: &[u8], + conditions: Option<&Conditions>, + ) -> Self { + Self { + backend: nucypher_core::AccessControlPolicy::new( + public_key.as_ref(), + authorization, + conditions + .map(|conditions| conditions.backend.clone()) + .as_ref(), + ), + } + } + + #[getter] + pub fn public_key(&self) -> DkgPublicKey { + self.backend.public_key.into() + } + + #[getter] + pub fn conditions(&self) -> Option { + self.backend + .conditions + .clone() + .map(|conditions| Conditions { + backend: conditions, + }) + } + + #[getter] + pub fn authorization(&self) -> &[u8] { + self.backend.authorization.as_ref() + } + + #[staticmethod] + pub fn from_bytes(data: &[u8]) -> PyResult { + from_bytes::<_, nucypher_core::AccessControlPolicy>(data) + } + + fn __bytes__(&self) -> PyObject { + to_bytes(self) + } +} + // // Threshold Decryption Request // @@ -751,16 +808,14 @@ impl ThresholdDecryptionRequest { ritual_id: u32, variant: FerveoVariant, ciphertext: &Ciphertext, - conditions: Option<&Conditions>, + acp: &AccessControlPolicy, context: Option<&Context>, ) -> PyResult { Ok(Self { backend: nucypher_core::ThresholdDecryptionRequest::new( ritual_id, ciphertext.as_ref(), - conditions - .map(|conditions| conditions.backend.clone()) - .as_ref(), + acp.as_ref(), context.map(|context| context.backend.clone()).as_ref(), variant.into(), ), @@ -773,13 +828,8 @@ impl ThresholdDecryptionRequest { } #[getter] - pub fn conditions(&self) -> Option { - self.backend - .conditions - .clone() - .map(|conditions| Conditions { - backend: conditions, - }) + pub fn acp(&self) -> AccessControlPolicy { + self.backend.acp.clone().into() } #[getter] diff --git a/nucypher-core-wasm/Cargo.toml b/nucypher-core-wasm/Cargo.toml index a4aed797..536bbc1b 100644 --- a/nucypher-core-wasm/Cargo.toml +++ b/nucypher-core-wasm/Cargo.toml @@ -20,7 +20,7 @@ default = ["console_error_panic_hook"] [dependencies] umbral-pre = { version = "0.11.0", features = ["bindings-wasm"] } -ferveo = { version = "0.2.1", package = "ferveo-pre-release", features = ["bindings-wasm"] } +ferveo = { package = "ferveo-pre-release", git = "https://github.com/derekpierre/nucypher-ferveo.git", branch = "acp", features = ["bindings-wasm"] } nucypher-core = { path = "../nucypher-core" } wasm-bindgen = "0.2.86" js-sys = "0.3.63" diff --git a/nucypher-core-wasm/src/lib.rs b/nucypher-core-wasm/src/lib.rs index 623a024c..872b820a 100644 --- a/nucypher-core-wasm/src/lib.rs +++ b/nucypher-core-wasm/src/lib.rs @@ -12,7 +12,7 @@ use alloc::{ }; use core::fmt; -use ferveo::bindings_wasm::{Ciphertext, FerveoVariant}; +use ferveo::bindings_wasm::{Ciphertext, DkgPublicKey, FerveoVariant}; use js_sys::Error; use umbral_pre::bindings_wasm::{ Capsule, PublicKey, RecoverableSignature, SecretKey, Signer, VerifiedCapsuleFrag, @@ -648,6 +648,55 @@ impl SessionSecretFactory { } } +// +// AccessControlPolicy +// + +#[wasm_bindgen] +#[derive(PartialEq, Eq, Debug, derive_more::From, derive_more::AsRef)] +pub struct AccessControlPolicy(nucypher_core::AccessControlPolicy); + +generate_from_bytes!(AccessControlPolicy); +generate_equals!(AccessControlPolicy); + +#[wasm_bindgen] +impl AccessControlPolicy { + #[wasm_bindgen(constructor)] + pub fn new( + public_key: &DkgPublicKey, + authorization: &[u8], + conditions: &OptionConditions, + ) -> Result { + let typed_conditions = try_from_js_option::(conditions)?; + + Ok(Self(nucypher_core::AccessControlPolicy::new( + public_key.as_ref(), + authorization, + typed_conditions.as_ref().map(|conditions| &conditions.0), + ))) + } + + #[wasm_bindgen(getter, js_name = publicKey)] + pub fn public_key(&self) -> DkgPublicKey { + DkgPublicKey::from(self.0.public_key) + } + + #[wasm_bindgen(getter)] + pub fn authorization(&self) -> Box<[u8]> { + self.0.authorization.clone() + } + + #[wasm_bindgen(getter)] + pub fn conditions(&self) -> Option { + self.0.conditions.clone().map(Conditions) + } + + #[wasm_bindgen(js_name = toBytes)] + pub fn to_bytes(&self) -> Box<[u8]> { + to_bytes(self) + } +} + // // Threshold Decryption Request // @@ -666,16 +715,15 @@ impl ThresholdDecryptionRequest { ritual_id: u32, variant: &FerveoVariant, ciphertext: &Ciphertext, - conditions: &OptionConditions, + access_control_policy: &AccessControlPolicy, context: &OptionContext, ) -> Result { - let typed_conditions = try_from_js_option::(conditions)?; let typed_context = try_from_js_option::(context)?; Ok(Self(nucypher_core::ThresholdDecryptionRequest::new( ritual_id, ciphertext.as_ref(), - typed_conditions.as_ref().map(|conditions| &conditions.0), + access_control_policy.as_ref(), typed_context.as_ref().map(|context| &context.0), variant.clone().into(), ))) @@ -696,6 +744,11 @@ impl ThresholdDecryptionRequest { self.0.ciphertext.clone().into() } + #[wasm_bindgen(getter, js_name = accessControlPolicy)] + pub fn access_control_policy(&self) -> AccessControlPolicy { + self.0.acp.clone().into() + } + pub fn encrypt( &self, shared_secret: &SessionSharedSecret, diff --git a/nucypher-core-wasm/tests/wasm.rs b/nucypher-core-wasm/tests/wasm.rs index b44e967e..88335148 100644 --- a/nucypher-core-wasm/tests/wasm.rs +++ b/nucypher-core-wasm/tests/wasm.rs @@ -702,11 +702,19 @@ fn threshold_decryption_request() { let message = "my-message".as_bytes(); let ciphertext = ferveo_encrypt(message, conditions.as_bytes(), &dkg_pk).unwrap(); + let authorization = b"we_dont_need_no_stinking_badges"; + let acp = AccessControlPolicy::new( + &dkg_pk, + authorization, + &conditions_js.unchecked_into::(), + ) + .unwrap(); + let request = ThresholdDecryptionRequest::new( ritual_id, &FerveoVariant::simple(), &ciphertext, - &conditions_js.unchecked_into::(), + &acp, &context.unchecked_into::(), ) .unwrap(); @@ -787,3 +795,31 @@ fn threshold_decryption_response() { .decrypt(&random_shared_secret) .is_err()); } + +#[wasm_bindgen_test] +fn access_control_policy() { + let dkg_pk = DkgPublicKey::random(); + + let conditions = "{'some': 'condition'}"; + let conditions_js: JsValue = Some(Conditions::new(conditions)).into(); + + let authorization = b"we_dont_need_no_stinking_badges"; + let acp = AccessControlPolicy::new( + &dkg_pk, + authorization, + &conditions_js.unchecked_into::(), + ) + .unwrap(); + + // mimic serialization/deserialization over the wire + let serialized_acp = acp.to_bytes(); + let deserialized_acp = AccessControlPolicy::from_bytes(&serialized_acp).unwrap(); + assert_eq!( + dkg_pk.to_bytes().unwrap(), + deserialized_acp.public_key().to_bytes().unwrap() + ); + assert_eq!( + authorization.to_vec().into_boxed_slice(), + deserialized_acp.authorization() + ); +} diff --git a/nucypher-core/Cargo.toml b/nucypher-core/Cargo.toml index 63abfba8..9d92fb37 100644 --- a/nucypher-core/Cargo.toml +++ b/nucypher-core/Cargo.toml @@ -11,7 +11,7 @@ categories = ["cryptography", "no-std"] [dependencies] umbral-pre = { version = "0.11.0", features = ["serde"] } -ferveo = { version = "0.2.1", package = "ferveo-pre-release" } +ferveo = { package = "ferveo-pre-release", git = "https://github.com/derekpierre/nucypher-ferveo.git", branch = "acp" } serde = { version = "1", default-features = false, features = ["derive"] } generic-array = { version = "0.14", features = ["zeroize"] } sha3 = "0.10" diff --git a/nucypher-core/src/access_control.rs b/nucypher-core/src/access_control.rs new file mode 100644 index 00000000..c1b8bb91 --- /dev/null +++ b/nucypher-core/src/access_control.rs @@ -0,0 +1,104 @@ +use alloc::boxed::Box; +use alloc::string::String; + +use ferveo::api::DkgPublicKey; +use serde::{Deserialize, Serialize}; +use umbral_pre::serde_bytes; + +use crate::conditions::Conditions; +use crate::versioning::{ + messagepack_deserialize, messagepack_serialize, ProtocolObject, ProtocolObjectInner, +}; + +// TODO should this be in umbral? + +/// Access control metadata for encrypted data. +#[derive(Debug, Serialize, Deserialize, Clone)] +pub struct AccessControlPolicy { + /// The public key for the encrypted data + pub public_key: DkgPublicKey, + + /// The authorization data for the encrypter of the data + #[serde(with = "serde_bytes::as_base64")] + pub authorization: Box<[u8]>, + + /// The conditions associated with the encrypted data + pub conditions: Option, +} + +impl AccessControlPolicy { + /// Creates a new access control policy. + pub fn new( + public_key: &DkgPublicKey, + authorization: &[u8], + conditions: Option<&Conditions>, + ) -> Self { + AccessControlPolicy { + public_key: *public_key, + authorization: authorization.to_vec().into(), + conditions: conditions.cloned(), + } + } +} + +impl PartialEq for AccessControlPolicy { + fn eq(&self, other: &Self) -> bool { + self.public_key.to_bytes().unwrap() == other.public_key.to_bytes().unwrap() + && self.authorization == other.authorization + && self.conditions == other.conditions + } +} + +impl Eq for AccessControlPolicy {} + +impl<'a> ProtocolObjectInner<'a> for AccessControlPolicy { + fn version() -> (u16, u16) { + (1, 0) + } + + fn brand() -> [u8; 4] { + *b"ACPo" + } + + fn unversioned_to_bytes(&self) -> Box<[u8]> { + messagepack_serialize(&self) + } + + fn unversioned_from_bytes(minor_version: u16, bytes: &[u8]) -> Option> { + if minor_version == 0 { + Some(messagepack_deserialize(bytes)) + } else { + None + } + } +} + +impl<'a> ProtocolObject<'a> for AccessControlPolicy {} + +#[cfg(test)] +mod tests { + use ferveo::api::DkgPublicKey; + + use crate::access_control::AccessControlPolicy; + use crate::conditions::Conditions; + use crate::versioning::ProtocolObject; + + #[test] + fn access_control_policy() { + let dkg_pk = DkgPublicKey::random(); + + let conditions = Conditions::new("abcd"); + let authorization = b"we_dont_need_no_stinking_badges"; + let acp = AccessControlPolicy::new(&dkg_pk, authorization, Some(&conditions)); + + // mimic serialization/deserialization over the wire + let serialized_acp = acp.to_bytes(); + let deserialized_acp = AccessControlPolicy::from_bytes(&serialized_acp).unwrap(); + assert_eq!(dkg_pk, deserialized_acp.public_key); + // assert_eq!(conditions, deserialized_acp.conditions); + assert_eq!( + authorization.to_vec().into_boxed_slice(), + deserialized_acp.authorization + ); + } +} diff --git a/nucypher-core/src/dkg.rs b/nucypher-core/src/dkg.rs index cd4f86df..8c4066dc 100644 --- a/nucypher-core/src/dkg.rs +++ b/nucypher-core/src/dkg.rs @@ -1,23 +1,22 @@ use alloc::boxed::Box; use alloc::string::String; use core::fmt; + +use chacha20poly1305::aead::{Aead, AeadCore, KeyInit, OsRng}; +use chacha20poly1305::{ChaCha20Poly1305, Key, Nonce}; use ferveo::api::{Ciphertext, FerveoVariant}; use generic_array::typenum::Unsigned; - use serde::{Deserialize, Serialize}; use umbral_pre::serde_bytes; // TODO should this be in umbral? +use crate::access_control::AccessControlPolicy; +use crate::conditions::Context; +use crate::dkg::session::{SessionSharedSecret, SessionStaticKey}; use crate::versioning::{ messagepack_deserialize, messagepack_serialize, DeserializationError, ProtocolObject, ProtocolObjectInner, }; -use chacha20poly1305::aead::{Aead, AeadCore, KeyInit, OsRng}; -use chacha20poly1305::{ChaCha20Poly1305, Key, Nonce}; - -use crate::conditions::{Conditions, Context}; -use crate::dkg::session::{SessionSharedSecret, SessionStaticKey}; - /// Errors during encryption. #[derive(Debug)] pub enum EncryptionError { @@ -105,6 +104,7 @@ pub mod session { use alloc::boxed::Box; use alloc::string::String; use core::fmt; + use generic_array::{ typenum::{Unsigned, U32}, GenericArray, @@ -115,10 +115,9 @@ pub mod session { use serde::{Deserialize, Deserializer, Serialize, Serializer}; use umbral_pre::serde_bytes; use x25519_dalek::{PublicKey, SharedSecret, StaticSecret}; - - use crate::secret_box::{kdf, SecretBox}; use zeroize::ZeroizeOnDrop; + use crate::secret_box::{kdf, SecretBox}; use crate::versioning::{ messagepack_deserialize, messagepack_serialize, ProtocolObject, ProtocolObjectInner, }; @@ -356,8 +355,8 @@ pub struct ThresholdDecryptionRequest { pub ritual_id: u32, /// The ciphertext to generate a decryption share for. pub ciphertext: Ciphertext, - /// A blob of bytes containing decryption conditions for this message. - pub conditions: Option, + /// The associated access control metadata. + pub acp: AccessControlPolicy, /// A blob of bytes containing context required to evaluate conditions. pub context: Option, /// The ferveo variant to use for the decryption share derivation. @@ -369,14 +368,14 @@ impl ThresholdDecryptionRequest { pub fn new( ritual_id: u32, ciphertext: &Ciphertext, - conditions: Option<&Conditions>, + acp: &AccessControlPolicy, context: Option<&Context>, variant: FerveoVariant, ) -> Self { Self { ritual_id, ciphertext: ciphertext.clone(), - conditions: conditions.cloned(), + acp: acp.clone(), context: context.cloned(), variant, } @@ -394,7 +393,7 @@ impl ThresholdDecryptionRequest { impl<'a> ProtocolObjectInner<'a> for ThresholdDecryptionRequest { fn version() -> (u16, u16) { - (2, 0) + (3, 0) } fn brand() -> [u8; 4] { @@ -597,19 +596,21 @@ mod tests { use ferveo::api::{encrypt as ferveo_encrypt, DkgPublicKey, FerveoVariant, SecretBox}; use crate::{ - Conditions, Context, EncryptedThresholdDecryptionRequest, - EncryptedThresholdDecryptionResponse, ProtocolObject, SessionSecretFactory, - SessionStaticKey, ThresholdDecryptionRequest, ThresholdDecryptionResponse, + EncryptedThresholdDecryptionRequest, EncryptedThresholdDecryptionResponse, + SessionSecretFactory, SessionStaticKey, ThresholdDecryptionRequest, + ThresholdDecryptionResponse, }; use generic_array::typenum::Unsigned; use rand_core::RngCore; + use crate::access_control::AccessControlPolicy; + use crate::conditions::{Conditions, Context}; use crate::dkg::session::SessionStaticSecret; use crate::dkg::{ decrypt_with_shared_secret, encrypt_with_shared_secret, DecryptionError, NonceSize, }; - use crate::versioning::ProtocolObjectInner; + use crate::versioning::{ProtocolObject, ProtocolObjectInner}; #[test] fn decryption_with_shared_secret() { @@ -731,10 +732,14 @@ mod tests { let aad = "my-add".as_bytes(); let ciphertext = ferveo_encrypt(SecretBox::new(message), aad, &dkg_pk).unwrap(); + let authorization = b"self_authorization"; + let acp = + AccessControlPolicy::new(&dkg_pk, authorization, Some(&Conditions::new("abcd"))); + let request = ThresholdDecryptionRequest::new( ritual_id, &ciphertext, - Some(&Conditions::new("abcd")), + &acp, Some(&Context::new("efgh")), variant, ); diff --git a/nucypher-core/src/lib.rs b/nucypher-core/src/lib.rs index 19255742..6f48964e 100644 --- a/nucypher-core/src/lib.rs +++ b/nucypher-core/src/lib.rs @@ -7,6 +7,7 @@ extern crate alloc; +mod access_control; mod address; mod conditions; mod dkg; @@ -25,6 +26,8 @@ mod versioning; /// Error returned by various `verify()` methods in the crate. pub struct VerificationError; +pub use access_control::AccessControlPolicy; + pub use address::Address; pub use conditions::{Conditions, Context}; pub use dkg::{ From 2af52423c06ffa60bd3241ad1e3ca48b62268003 Mon Sep 17 00:00:00 2001 From: derekpierre Date: Wed, 16 Aug 2023 19:30:31 -0400 Subject: [PATCH 02/14] Initial code for ThresholdMessageKit and tests. --- .../nucypher_core/__init__.py | 1 + .../nucypher_core/__init__.pyi | 20 ++++ nucypher-core-python/src/lib.rs | 51 +++++++++ nucypher-core-wasm/src/lib.rs | 46 ++++++++ nucypher-core-wasm/tests/wasm.rs | 33 ++++++ nucypher-core/src/lib.rs | 2 + nucypher-core/src/threshold_message_kit.rs | 100 ++++++++++++++++++ 7 files changed, 253 insertions(+) create mode 100644 nucypher-core/src/threshold_message_kit.rs diff --git a/nucypher-core-python/nucypher_core/__init__.py b/nucypher-core-python/nucypher_core/__init__.py index a1a7eb8a..b13cd175 100644 --- a/nucypher-core-python/nucypher_core/__init__.py +++ b/nucypher-core-python/nucypher_core/__init__.py @@ -18,6 +18,7 @@ MetadataResponse, MetadataResponsePayload, AccessControlPolicy, + ThresholdMessageKit, ThresholdDecryptionRequest, ThresholdDecryptionResponse, EncryptedThresholdDecryptionRequest, diff --git a/nucypher-core-python/nucypher_core/__init__.pyi b/nucypher-core-python/nucypher_core/__init__.pyi index 90153e4d..59980f7b 100644 --- a/nucypher-core-python/nucypher_core/__init__.pyi +++ b/nucypher-core-python/nucypher_core/__init__.pyi @@ -449,6 +449,26 @@ class AccessControlPolicy: def __bytes__(self) -> bytes: ... +@final +class ThresholdMessageKit: + + def __init__(self, kem_ciphertext: Ciphertext, dem_ciphertext: bytes, acp: AccessControlPolicy): + ... + + kem_ciphertext: Ciphertext + + dem_ciphertext: bytes + + acp: AccessControlPolicy + + @staticmethod + def from_bytes(data: bytes) -> ThresholdMessageKit: + ... + + def __bytes__(self) -> bytes: + ... + + @final class ThresholdDecryptionRequest: diff --git a/nucypher-core-python/src/lib.rs b/nucypher-core-python/src/lib.rs index fbd410d1..bb570761 100644 --- a/nucypher-core-python/src/lib.rs +++ b/nucypher-core-python/src/lib.rs @@ -791,6 +791,57 @@ impl AccessControlPolicy { } } +// +// ThresholdMessageKit +// +#[pyclass(module = "nucypher_core")] +#[derive(derive_more::From, derive_more::AsRef)] +pub struct ThresholdMessageKit { + backend: nucypher_core::ThresholdMessageKit, +} + +#[pymethods] +impl ThresholdMessageKit { + #[new] + pub fn new( + kem_ciphertext: &Ciphertext, + dem_ciphertext: &[u8], + acp: &AccessControlPolicy, + ) -> Self { + Self { + backend: nucypher_core::ThresholdMessageKit::new( + kem_ciphertext.as_ref(), + dem_ciphertext, + acp.as_ref(), + ), + } + } + + #[getter] + pub fn kem_ciphertext(&self) -> Ciphertext { + self.backend.kem_ciphertext.clone().into() + } + + #[getter] + pub fn dem_ciphertext(&self) -> &[u8] { + self.backend.dem_ciphertext.as_ref() + } + + #[getter] + pub fn acp(&self) -> AccessControlPolicy { + self.backend.acp.clone().into() + } + + #[staticmethod] + pub fn from_bytes(data: &[u8]) -> PyResult { + from_bytes::<_, nucypher_core::ThresholdMessageKit>(data) + } + + fn __bytes__(&self) -> PyObject { + to_bytes(self) + } +} + // // Threshold Decryption Request // diff --git a/nucypher-core-wasm/src/lib.rs b/nucypher-core-wasm/src/lib.rs index 872b820a..b97b272b 100644 --- a/nucypher-core-wasm/src/lib.rs +++ b/nucypher-core-wasm/src/lib.rs @@ -697,6 +697,52 @@ impl AccessControlPolicy { } } +// +// ThresholdMessageKit +// +#[wasm_bindgen] +#[derive(PartialEq, Eq, Debug, derive_more::From, derive_more::AsRef)] +pub struct ThresholdMessageKit(nucypher_core::ThresholdMessageKit); + +generate_from_bytes!(ThresholdMessageKit); +generate_equals!(ThresholdMessageKit); + +#[wasm_bindgen] +impl ThresholdMessageKit { + #[wasm_bindgen(constructor)] + pub fn new( + kem_ciphertext: &Ciphertext, + dem_ciphertext: &[u8], + acp: &AccessControlPolicy, + ) -> Self { + Self(nucypher_core::ThresholdMessageKit::new( + kem_ciphertext.as_ref(), + dem_ciphertext, + acp.as_ref(), + )) + } + + #[wasm_bindgen(getter, js_name = kemCiphertext)] + pub fn kem_ciphertext(&self) -> Ciphertext { + self.0.kem_ciphertext.clone().into() + } + + #[wasm_bindgen(getter, js_name = demCiphertext)] + pub fn dem_ciphertext(&self) -> Box<[u8]> { + self.0.dem_ciphertext.clone() + } + + #[wasm_bindgen(getter, js_name = accessControlPolicy)] + pub fn access_control_policy(&self) -> AccessControlPolicy { + self.0.acp.clone().into() + } + + #[wasm_bindgen(js_name = toBytes)] + pub fn to_bytes(&self) -> Box<[u8]> { + to_bytes(self) + } +} + // // Threshold Decryption Request // diff --git a/nucypher-core-wasm/tests/wasm.rs b/nucypher-core-wasm/tests/wasm.rs index 88335148..6b37eaa9 100644 --- a/nucypher-core-wasm/tests/wasm.rs +++ b/nucypher-core-wasm/tests/wasm.rs @@ -823,3 +823,36 @@ fn access_control_policy() { deserialized_acp.authorization() ); } + +#[wasm_bindgen_test] +fn threshold_message_kit() { + let conditions = "{'some': 'condition'}"; + let conditions_js: JsValue = Some(Conditions::new(conditions)).into(); + + let dkg_pk = DkgPublicKey::random(); + let symmetric_key = "The Tyranny of Merit".as_bytes(); + let kem_ciphertext = ferveo_encrypt(symmetric_key, conditions.as_bytes(), &dkg_pk).unwrap(); + + let authorization = b"we_dont_need_no_stinking_badges"; + + let acp = AccessControlPolicy::new( + &dkg_pk, + authorization, + &conditions_js.unchecked_into::(), + ) + .unwrap(); + + let dem_ciphertext = b"data_encapsulation"; + + let tmk = ThresholdMessageKit::new(&kem_ciphertext, dem_ciphertext, &acp); + + // mimic serialization/deserialization over the wire + let serialized_tmk = tmk.to_bytes(); + let deserialized_tmk = ThresholdMessageKit::from_bytes(&serialized_tmk).unwrap(); + assert_eq!( + dem_ciphertext.to_vec().into_boxed_slice(), + deserialized_tmk.dem_ciphertext() + ); + assert_eq!(kem_ciphertext, deserialized_tmk.kem_ciphertext()); + assert_eq!(acp, deserialized_tmk.access_control_policy()); +} diff --git a/nucypher-core/src/lib.rs b/nucypher-core/src/lib.rs index 6f48964e..5486ef7e 100644 --- a/nucypher-core/src/lib.rs +++ b/nucypher-core/src/lib.rs @@ -20,6 +20,7 @@ mod reencryption; mod retrieval_kit; mod revocation_order; mod secret_box; +mod threshold_message_kit; mod treasure_map; mod versioning; @@ -45,6 +46,7 @@ pub use node_metadata::{ pub use reencryption::{ReencryptionRequest, ReencryptionResponse}; pub use retrieval_kit::RetrievalKit; pub use revocation_order::RevocationOrder; +pub use threshold_message_kit::ThresholdMessageKit; pub use treasure_map::{EncryptedTreasureMap, TreasureMap}; pub use versioning::ProtocolObject; diff --git a/nucypher-core/src/threshold_message_kit.rs b/nucypher-core/src/threshold_message_kit.rs new file mode 100644 index 00000000..520ec421 --- /dev/null +++ b/nucypher-core/src/threshold_message_kit.rs @@ -0,0 +1,100 @@ +use alloc::boxed::Box; +use alloc::string::String; + +use ferveo::api::Ciphertext; +use serde::{Deserialize, Serialize}; +use umbral_pre::serde_bytes; + +use crate::access_control::AccessControlPolicy; +use crate::versioning::{ + messagepack_deserialize, messagepack_serialize, ProtocolObject, ProtocolObjectInner, +}; + +// TODO should this be in umbral? + +/// Access control metadata for encrypted data. +#[derive(PartialEq, Eq, Debug, Serialize, Deserialize, Clone)] +pub struct ThresholdMessageKit { + /// The data encapsulation ciphertext (DEM). + pub kem_ciphertext: Ciphertext, + + /// The key encapsulation ciphertext (KEM). + #[serde(with = "serde_bytes::as_base64")] + pub dem_ciphertext: Box<[u8]>, + + /// The associated access control metadata. + pub acp: AccessControlPolicy, +} + +impl ThresholdMessageKit { + /// Creates a new threshold message kit. + pub fn new( + kem_ciphertext: &Ciphertext, + dem_ciphertext: &[u8], + acp: &AccessControlPolicy, + ) -> Self { + ThresholdMessageKit { + kem_ciphertext: kem_ciphertext.clone(), + dem_ciphertext: dem_ciphertext.to_vec().into(), + acp: acp.clone(), + } + } +} + +impl<'a> ProtocolObjectInner<'a> for ThresholdMessageKit { + fn version() -> (u16, u16) { + (1, 0) + } + + fn brand() -> [u8; 4] { + *b"TMKi" + } + + fn unversioned_to_bytes(&self) -> Box<[u8]> { + messagepack_serialize(&self) + } + + fn unversioned_from_bytes(minor_version: u16, bytes: &[u8]) -> Option> { + if minor_version == 0 { + Some(messagepack_deserialize(bytes)) + } else { + None + } + } +} + +impl<'a> ProtocolObject<'a> for ThresholdMessageKit {} + +#[cfg(test)] +mod tests { + use crate::access_control::AccessControlPolicy; + use crate::conditions::Conditions; + use crate::threshold_message_kit::ThresholdMessageKit; + use crate::versioning::ProtocolObject; + use ferveo::api::{encrypt as ferveo_encrypt, DkgPublicKey, SecretBox}; + + #[test] + fn threshold_message_kit() { + let dkg_pk = DkgPublicKey::random(); + let symmetric_key = "The Tyranny of Merit".as_bytes().to_vec(); + let aad = "my-add".as_bytes(); + let kem_ciphertext = ferveo_encrypt(SecretBox::new(symmetric_key), aad, &dkg_pk).unwrap(); + + let authorization = b"we_dont_need_no_stinking_badges"; + let acp = AccessControlPolicy::new(&dkg_pk, authorization, Some(&Conditions::new("abcd"))); + + let dem_ciphertext = b"data_encapsulation"; + + let tmk = ThresholdMessageKit::new(&kem_ciphertext, dem_ciphertext, &acp); + + // mimic serialization/deserialization over the wire + let serialized_tmk = tmk.to_bytes(); + let deserialized_tmk = ThresholdMessageKit::from_bytes(&serialized_tmk).unwrap(); + assert_eq!( + dem_ciphertext.to_vec().into_boxed_slice(), + deserialized_tmk.dem_ciphertext + ); + assert_eq!(kem_ciphertext, deserialized_tmk.kem_ciphertext); + assert_eq!(acp, deserialized_tmk.acp); + } +} From 3bab4c30c88bc1763841666a49cc4337b661661c Mon Sep 17 00:00:00 2001 From: derekpierre Date: Thu, 17 Aug 2023 13:48:23 -0400 Subject: [PATCH 03/14] Add aad() function to AccessControlPolicy. --- .../nucypher_core/__init__.pyi | 3 +++ nucypher-core-python/src/lib.rs | 5 +++++ nucypher-core-wasm/src/lib.rs | 4 ++++ nucypher-core-wasm/tests/wasm.rs | 12 +++++++++++ nucypher-core/src/access_control.rs | 21 ++++++++++++++++++- nucypher-core/src/conditions.rs | 2 +- 6 files changed, 45 insertions(+), 2 deletions(-) diff --git a/nucypher-core-python/nucypher_core/__init__.pyi b/nucypher-core-python/nucypher_core/__init__.pyi index 59980f7b..05e30f1a 100644 --- a/nucypher-core-python/nucypher_core/__init__.pyi +++ b/nucypher-core-python/nucypher_core/__init__.pyi @@ -442,6 +442,9 @@ class AccessControlPolicy: authorization: bytes + def aad(self) -> bytes: + ... + @staticmethod def from_bytes(data: bytes) -> AccessControlPolicy: ... diff --git a/nucypher-core-python/src/lib.rs b/nucypher-core-python/src/lib.rs index bb570761..07c92ddc 100644 --- a/nucypher-core-python/src/lib.rs +++ b/nucypher-core-python/src/lib.rs @@ -761,6 +761,11 @@ impl AccessControlPolicy { } } + pub fn aad(&self, py: Python) -> PyObject { + let result = self.backend.aad(); + PyBytes::new(py, result.as_ref()).into() + } + #[getter] pub fn public_key(&self) -> DkgPublicKey { self.backend.public_key.into() diff --git a/nucypher-core-wasm/src/lib.rs b/nucypher-core-wasm/src/lib.rs index b97b272b..4f426a7f 100644 --- a/nucypher-core-wasm/src/lib.rs +++ b/nucypher-core-wasm/src/lib.rs @@ -676,6 +676,10 @@ impl AccessControlPolicy { ))) } + pub fn aad(&self) -> Box<[u8]> { + self.0.aad() + } + #[wasm_bindgen(getter, js_name = publicKey)] pub fn public_key(&self) -> DkgPublicKey { DkgPublicKey::from(self.0.public_key) diff --git a/nucypher-core-wasm/tests/wasm.rs b/nucypher-core-wasm/tests/wasm.rs index 6b37eaa9..6f39230e 100644 --- a/nucypher-core-wasm/tests/wasm.rs +++ b/nucypher-core-wasm/tests/wasm.rs @@ -822,6 +822,18 @@ fn access_control_policy() { authorization.to_vec().into_boxed_slice(), deserialized_acp.authorization() ); + assert_eq!( + conditions, + deserialized_acp.conditions().unwrap().to_string() + ); + + // check aad; expected to be dkg public key + conditions + let aad = acp.aad(); + + let mut expected_aad = dkg_pk.to_bytes().unwrap().to_vec(); + expected_aad.extend(conditions.as_bytes()); + + assert_eq!(expected_aad.into_boxed_slice(), aad); } #[wasm_bindgen_test] diff --git a/nucypher-core/src/access_control.rs b/nucypher-core/src/access_control.rs index c1b8bb91..d0977532 100644 --- a/nucypher-core/src/access_control.rs +++ b/nucypher-core/src/access_control.rs @@ -1,5 +1,6 @@ use alloc::boxed::Box; use alloc::string::String; +use alloc::vec::Vec; use ferveo::api::DkgPublicKey; use serde::{Deserialize, Serialize}; @@ -39,6 +40,16 @@ impl AccessControlPolicy { conditions: conditions.cloned(), } } + + /// Return the aad. + pub fn aad(&self) -> Box<[u8]> { + let public_key_bytes = self.public_key.to_bytes().unwrap(); + let condition_bytes = self.conditions.as_ref().unwrap().as_ref().as_bytes(); + let mut result = Vec::with_capacity(public_key_bytes.len() + condition_bytes.len()); + result.extend(public_key_bytes); + result.extend(condition_bytes); + result.into_boxed_slice() + } } impl PartialEq for AccessControlPolicy { @@ -95,10 +106,18 @@ mod tests { let serialized_acp = acp.to_bytes(); let deserialized_acp = AccessControlPolicy::from_bytes(&serialized_acp).unwrap(); assert_eq!(dkg_pk, deserialized_acp.public_key); - // assert_eq!(conditions, deserialized_acp.conditions); + assert_eq!(conditions, deserialized_acp.conditions.unwrap()); assert_eq!( authorization.to_vec().into_boxed_slice(), deserialized_acp.authorization ); + + // check aad; expected to be dkg public key + conditions + let aad = acp.aad(); + + let mut expected_aad = dkg_pk.to_bytes().unwrap().to_vec(); + expected_aad.extend(conditions.as_ref().as_bytes()); + + assert_eq!(expected_aad.into_boxed_slice(), aad); } } diff --git a/nucypher-core/src/conditions.rs b/nucypher-core/src/conditions.rs index f41c6ee0..9191a23d 100644 --- a/nucypher-core/src/conditions.rs +++ b/nucypher-core/src/conditions.rs @@ -3,7 +3,7 @@ use core::fmt; use serde::{Deserialize, Serialize}; -/// Reencryption conditions. +/// Access control conditions. #[derive(PartialEq, Eq, Debug, Clone, Serialize, Deserialize)] pub struct Conditions(String); From 07b43399293915652b200ff672abb86da1ad49dd Mon Sep 17 00:00:00 2001 From: derekpierre Date: Thu, 17 Aug 2023 14:19:36 -0400 Subject: [PATCH 04/14] Rename ThresholdMessageKit kem/dem ciphertext to header and payload respectively. --- .../nucypher_core/__init__.pyi | 6 ++-- nucypher-core-python/src/lib.rs | 18 +++++------ nucypher-core-wasm/src/lib.rs | 22 ++++++-------- nucypher-core-wasm/tests/wasm.rs | 12 ++++---- nucypher-core/src/threshold_message_kit.rs | 30 ++++++++----------- 5 files changed, 38 insertions(+), 50 deletions(-) diff --git a/nucypher-core-python/nucypher_core/__init__.pyi b/nucypher-core-python/nucypher_core/__init__.pyi index 05e30f1a..88902625 100644 --- a/nucypher-core-python/nucypher_core/__init__.pyi +++ b/nucypher-core-python/nucypher_core/__init__.pyi @@ -455,12 +455,12 @@ class AccessControlPolicy: @final class ThresholdMessageKit: - def __init__(self, kem_ciphertext: Ciphertext, dem_ciphertext: bytes, acp: AccessControlPolicy): + def __init__(self, header: Ciphertext, payload: bytes, acp: AccessControlPolicy): ... - kem_ciphertext: Ciphertext + header: Ciphertext - dem_ciphertext: bytes + payload: bytes acp: AccessControlPolicy diff --git a/nucypher-core-python/src/lib.rs b/nucypher-core-python/src/lib.rs index 07c92ddc..1ace71fd 100644 --- a/nucypher-core-python/src/lib.rs +++ b/nucypher-core-python/src/lib.rs @@ -808,28 +808,24 @@ pub struct ThresholdMessageKit { #[pymethods] impl ThresholdMessageKit { #[new] - pub fn new( - kem_ciphertext: &Ciphertext, - dem_ciphertext: &[u8], - acp: &AccessControlPolicy, - ) -> Self { + pub fn new(header: &Ciphertext, payload: &[u8], acp: &AccessControlPolicy) -> Self { Self { backend: nucypher_core::ThresholdMessageKit::new( - kem_ciphertext.as_ref(), - dem_ciphertext, + header.as_ref(), + payload, acp.as_ref(), ), } } #[getter] - pub fn kem_ciphertext(&self) -> Ciphertext { - self.backend.kem_ciphertext.clone().into() + pub fn header(&self) -> Ciphertext { + self.backend.header.clone().into() } #[getter] - pub fn dem_ciphertext(&self) -> &[u8] { - self.backend.dem_ciphertext.as_ref() + pub fn payload(&self) -> &[u8] { + self.backend.payload.as_ref() } #[getter] diff --git a/nucypher-core-wasm/src/lib.rs b/nucypher-core-wasm/src/lib.rs index 4f426a7f..b34f4b40 100644 --- a/nucypher-core-wasm/src/lib.rs +++ b/nucypher-core-wasm/src/lib.rs @@ -714,26 +714,22 @@ generate_equals!(ThresholdMessageKit); #[wasm_bindgen] impl ThresholdMessageKit { #[wasm_bindgen(constructor)] - pub fn new( - kem_ciphertext: &Ciphertext, - dem_ciphertext: &[u8], - acp: &AccessControlPolicy, - ) -> Self { + pub fn new(header: &Ciphertext, payload: &[u8], acp: &AccessControlPolicy) -> Self { Self(nucypher_core::ThresholdMessageKit::new( - kem_ciphertext.as_ref(), - dem_ciphertext, + header.as_ref(), + payload, acp.as_ref(), )) } - #[wasm_bindgen(getter, js_name = kemCiphertext)] - pub fn kem_ciphertext(&self) -> Ciphertext { - self.0.kem_ciphertext.clone().into() + #[wasm_bindgen(getter)] + pub fn header(&self) -> Ciphertext { + self.0.header.clone().into() } - #[wasm_bindgen(getter, js_name = demCiphertext)] - pub fn dem_ciphertext(&self) -> Box<[u8]> { - self.0.dem_ciphertext.clone() + #[wasm_bindgen(getter)] + pub fn payload(&self) -> Box<[u8]> { + self.0.payload.clone() } #[wasm_bindgen(getter, js_name = accessControlPolicy)] diff --git a/nucypher-core-wasm/tests/wasm.rs b/nucypher-core-wasm/tests/wasm.rs index 6f39230e..7bb302ff 100644 --- a/nucypher-core-wasm/tests/wasm.rs +++ b/nucypher-core-wasm/tests/wasm.rs @@ -843,7 +843,7 @@ fn threshold_message_kit() { let dkg_pk = DkgPublicKey::random(); let symmetric_key = "The Tyranny of Merit".as_bytes(); - let kem_ciphertext = ferveo_encrypt(symmetric_key, conditions.as_bytes(), &dkg_pk).unwrap(); + let header = ferveo_encrypt(symmetric_key, conditions.as_bytes(), &dkg_pk).unwrap(); let authorization = b"we_dont_need_no_stinking_badges"; @@ -854,17 +854,17 @@ fn threshold_message_kit() { ) .unwrap(); - let dem_ciphertext = b"data_encapsulation"; + let payload = b"data_encapsulation"; - let tmk = ThresholdMessageKit::new(&kem_ciphertext, dem_ciphertext, &acp); + let tmk = ThresholdMessageKit::new(&header, payload, &acp); // mimic serialization/deserialization over the wire let serialized_tmk = tmk.to_bytes(); let deserialized_tmk = ThresholdMessageKit::from_bytes(&serialized_tmk).unwrap(); assert_eq!( - dem_ciphertext.to_vec().into_boxed_slice(), - deserialized_tmk.dem_ciphertext() + payload.to_vec().into_boxed_slice(), + deserialized_tmk.payload() ); - assert_eq!(kem_ciphertext, deserialized_tmk.kem_ciphertext()); + assert_eq!(header, deserialized_tmk.header()); assert_eq!(acp, deserialized_tmk.access_control_policy()); } diff --git a/nucypher-core/src/threshold_message_kit.rs b/nucypher-core/src/threshold_message_kit.rs index 520ec421..2d12982d 100644 --- a/nucypher-core/src/threshold_message_kit.rs +++ b/nucypher-core/src/threshold_message_kit.rs @@ -15,12 +15,12 @@ use crate::versioning::{ /// Access control metadata for encrypted data. #[derive(PartialEq, Eq, Debug, Serialize, Deserialize, Clone)] pub struct ThresholdMessageKit { - /// The data encapsulation ciphertext (DEM). - pub kem_ciphertext: Ciphertext, + /// The key encapsulation ciphertext + pub header: Ciphertext, - /// The key encapsulation ciphertext (KEM). + /// The bulk data encapsulation ciphertext #[serde(with = "serde_bytes::as_base64")] - pub dem_ciphertext: Box<[u8]>, + pub payload: Box<[u8]>, /// The associated access control metadata. pub acp: AccessControlPolicy, @@ -28,14 +28,10 @@ pub struct ThresholdMessageKit { impl ThresholdMessageKit { /// Creates a new threshold message kit. - pub fn new( - kem_ciphertext: &Ciphertext, - dem_ciphertext: &[u8], - acp: &AccessControlPolicy, - ) -> Self { + pub fn new(header: &Ciphertext, payload: &[u8], acp: &AccessControlPolicy) -> Self { ThresholdMessageKit { - kem_ciphertext: kem_ciphertext.clone(), - dem_ciphertext: dem_ciphertext.to_vec().into(), + header: header.clone(), + payload: payload.to_vec().into(), acp: acp.clone(), } } @@ -78,23 +74,23 @@ mod tests { let dkg_pk = DkgPublicKey::random(); let symmetric_key = "The Tyranny of Merit".as_bytes().to_vec(); let aad = "my-add".as_bytes(); - let kem_ciphertext = ferveo_encrypt(SecretBox::new(symmetric_key), aad, &dkg_pk).unwrap(); + let header = ferveo_encrypt(SecretBox::new(symmetric_key), aad, &dkg_pk).unwrap(); let authorization = b"we_dont_need_no_stinking_badges"; let acp = AccessControlPolicy::new(&dkg_pk, authorization, Some(&Conditions::new("abcd"))); - let dem_ciphertext = b"data_encapsulation"; + let payload = b"data_encapsulation"; - let tmk = ThresholdMessageKit::new(&kem_ciphertext, dem_ciphertext, &acp); + let tmk = ThresholdMessageKit::new(&header, payload, &acp); // mimic serialization/deserialization over the wire let serialized_tmk = tmk.to_bytes(); let deserialized_tmk = ThresholdMessageKit::from_bytes(&serialized_tmk).unwrap(); assert_eq!( - dem_ciphertext.to_vec().into_boxed_slice(), - deserialized_tmk.dem_ciphertext + payload.to_vec().into_boxed_slice(), + deserialized_tmk.payload ); - assert_eq!(kem_ciphertext, deserialized_tmk.kem_ciphertext); + assert_eq!(header, deserialized_tmk.header); assert_eq!(acp, deserialized_tmk.acp); } } From 32cc6c84037a32f94d1839c7bbe495aca2b3f196 Mon Sep 17 00:00:00 2001 From: derekpierre Date: Thu, 17 Aug 2023 14:27:44 -0400 Subject: [PATCH 05/14] Rename ThresholdMessageKit underlying member for the access control policy to "acp"; also rename accessor method. --- nucypher-core-wasm/src/lib.rs | 12 ++++++------ nucypher-core-wasm/tests/wasm.rs | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/nucypher-core-wasm/src/lib.rs b/nucypher-core-wasm/src/lib.rs index b34f4b40..d0f14987 100644 --- a/nucypher-core-wasm/src/lib.rs +++ b/nucypher-core-wasm/src/lib.rs @@ -732,8 +732,8 @@ impl ThresholdMessageKit { self.0.payload.clone() } - #[wasm_bindgen(getter, js_name = accessControlPolicy)] - pub fn access_control_policy(&self) -> AccessControlPolicy { + #[wasm_bindgen(getter)] + pub fn acp(&self) -> AccessControlPolicy { self.0.acp.clone().into() } @@ -761,7 +761,7 @@ impl ThresholdDecryptionRequest { ritual_id: u32, variant: &FerveoVariant, ciphertext: &Ciphertext, - access_control_policy: &AccessControlPolicy, + acp: &AccessControlPolicy, context: &OptionContext, ) -> Result { let typed_context = try_from_js_option::(context)?; @@ -769,7 +769,7 @@ impl ThresholdDecryptionRequest { Ok(Self(nucypher_core::ThresholdDecryptionRequest::new( ritual_id, ciphertext.as_ref(), - access_control_policy.as_ref(), + acp.as_ref(), typed_context.as_ref().map(|context| &context.0), variant.clone().into(), ))) @@ -790,8 +790,8 @@ impl ThresholdDecryptionRequest { self.0.ciphertext.clone().into() } - #[wasm_bindgen(getter, js_name = accessControlPolicy)] - pub fn access_control_policy(&self) -> AccessControlPolicy { + #[wasm_bindgen(getter)] + pub fn acp(&self) -> AccessControlPolicy { self.0.acp.clone().into() } diff --git a/nucypher-core-wasm/tests/wasm.rs b/nucypher-core-wasm/tests/wasm.rs index 7bb302ff..ca701f16 100644 --- a/nucypher-core-wasm/tests/wasm.rs +++ b/nucypher-core-wasm/tests/wasm.rs @@ -866,5 +866,5 @@ fn threshold_message_kit() { deserialized_tmk.payload() ); assert_eq!(header, deserialized_tmk.header()); - assert_eq!(acp, deserialized_tmk.access_control_policy()); + assert_eq!(acp, deserialized_tmk.acp()); } From da87ad0e5b477d33ffa14269f293e7c3baab4d4b Mon Sep 17 00:00:00 2001 From: derekpierre Date: Thu, 17 Aug 2023 15:24:43 -0400 Subject: [PATCH 06/14] Add AccessControlPolicy and ThresholdMessageKit to `nucypher_core` python module. --- nucypher-core-python/src/lib.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/nucypher-core-python/src/lib.rs b/nucypher-core-python/src/lib.rs index 1ace71fd..2ef4ceec 100644 --- a/nucypher-core-python/src/lib.rs +++ b/nucypher-core-python/src/lib.rs @@ -1525,6 +1525,8 @@ fn _nucypher_core(py: Python, core_module: &PyModule) -> PyResult<()> { core_module.add_class::()?; core_module.add_class::()?; core_module.add_class::()?; + core_module.add_class::()?; + core_module.add_class::()?; // Build the umbral module let umbral_module = PyModule::new(py, "umbral")?; From a5cfd82094560b975a70e896c52dcca7debb1ef7 Mon Sep 17 00:00:00 2001 From: derekpierre Date: Fri, 18 Aug 2023 15:09:21 -0400 Subject: [PATCH 07/14] Update nucypher-core to incorporate latest changes from ferveo regarding use/exposure of key encapsulation i.e. use of CiphertextHader, and rework of Ciphertext. --- Cargo.lock | 8 ++--- .../nucypher_core/__init__.pyi | 13 ++++---- nucypher-core-python/nucypher_core/ferveo.py | 1 + nucypher-core-python/nucypher_core/ferveo.pyi | 17 +++++++++-- nucypher-core-python/src/lib.rs | 29 +++++++----------- nucypher-core-wasm/src/lib.rs | 26 +++++++--------- nucypher-core-wasm/tests/wasm.rs | 27 +++++++---------- nucypher-core/src/dkg.rs | 26 ++++++++-------- nucypher-core/src/threshold_message_kit.rs | 30 ++++++------------- 9 files changed, 80 insertions(+), 97 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 45c29449..33c80b0f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -518,7 +518,7 @@ dependencies = [ [[package]] name = "ferveo-common-pre-release" version = "0.1.0" -source = "git+https://github.com/derekpierre/nucypher-ferveo.git?branch=acp#59875467165f9fff22c1decaad75ecf46006a5f2" +source = "git+https://github.com/derekpierre/nucypher-ferveo.git?branch=acp#ce7d280c46173297b0d123b54bac6e57e9f9cc36" dependencies = [ "ark-ec", "ark-serialize", @@ -533,7 +533,7 @@ dependencies = [ [[package]] name = "ferveo-pre-release" version = "0.2.1" -source = "git+https://github.com/derekpierre/nucypher-ferveo.git?branch=acp#59875467165f9fff22c1decaad75ecf46006a5f2" +source = "git+https://github.com/derekpierre/nucypher-ferveo.git?branch=acp#ce7d280c46173297b0d123b54bac6e57e9f9cc36" dependencies = [ "ark-bls12-381", "ark-ec", @@ -636,7 +636,7 @@ dependencies = [ [[package]] name = "group-threshold-cryptography-pre-release" version = "0.1.0" -source = "git+https://github.com/derekpierre/nucypher-ferveo.git?branch=acp#59875467165f9fff22c1decaad75ecf46006a5f2" +source = "git+https://github.com/derekpierre/nucypher-ferveo.git?branch=acp#ce7d280c46173297b0d123b54bac6e57e9f9cc36" dependencies = [ "ark-bls12-381", "ark-ec", @@ -1382,7 +1382,7 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] name = "subproductdomain-pre-release" version = "0.1.0" -source = "git+https://github.com/derekpierre/nucypher-ferveo.git?branch=acp#59875467165f9fff22c1decaad75ecf46006a5f2" +source = "git+https://github.com/derekpierre/nucypher-ferveo.git?branch=acp#ce7d280c46173297b0d123b54bac6e57e9f9cc36" dependencies = [ "anyhow", "ark-ec", diff --git a/nucypher-core-python/nucypher_core/__init__.pyi b/nucypher-core-python/nucypher_core/__init__.pyi index 88902625..350ae688 100644 --- a/nucypher-core-python/nucypher_core/__init__.pyi +++ b/nucypher-core-python/nucypher_core/__init__.pyi @@ -12,9 +12,10 @@ from .umbral import ( from .ferveo import ( Ciphertext, + CiphertextHeader, DkgPublicKey, FerveoPublicKey, - FerveoVariant + FerveoVariant, ) @@ -455,12 +456,10 @@ class AccessControlPolicy: @final class ThresholdMessageKit: - def __init__(self, header: Ciphertext, payload: bytes, acp: AccessControlPolicy): + def __init__(self, ciphertext: Ciphertext, acp: AccessControlPolicy): ... - header: Ciphertext - - payload: bytes + ciphertext: Ciphertext acp: AccessControlPolicy @@ -475,7 +474,7 @@ class ThresholdMessageKit: @final class ThresholdDecryptionRequest: - def __init__(self, ritual_id: int, variant: FerveoVariant, ciphertext: Ciphertext, acp: AccessControlPolicy, context: Optional[Context]): + def __init__(self, ritual_id: int, variant: FerveoVariant, ciphertext_header: CiphertextHeader, acp: AccessControlPolicy, context: Optional[Context]): ... ritual_id: int @@ -486,7 +485,7 @@ class ThresholdDecryptionRequest: variant: FerveoVariant - ciphertext: Ciphertext + ciphertext_header: CiphertextHeader def encrypt(self, shared_secret: SessionSharedSecret, requester_public_key: SessionStaticKey) -> EncryptedThresholdDecryptionRequest: diff --git a/nucypher-core-python/nucypher_core/ferveo.py b/nucypher-core-python/nucypher_core/ferveo.py index 3593b4b2..c8fa3fe4 100644 --- a/nucypher-core-python/nucypher_core/ferveo.py +++ b/nucypher-core-python/nucypher_core/ferveo.py @@ -35,3 +35,4 @@ ValidatorsNotSorted = _ferveo.ValidatorsNotSorted ValidatorPublicKeyMismatch = _ferveo.ValidatorPublicKeyMismatch SerializationError = _ferveo.SerializationError +CiphertextHeader = _ferveo.CiphertextHeader diff --git a/nucypher-core-python/nucypher_core/ferveo.pyi b/nucypher-core-python/nucypher_core/ferveo.pyi index 1dfab2f0..ff17fd19 100644 --- a/nucypher-core-python/nucypher_core/ferveo.pyi +++ b/nucypher-core-python/nucypher_core/ferveo.pyi @@ -119,6 +119,9 @@ class Dkg: @final class Ciphertext: + header: CiphertextHeader + payload: bytes + @staticmethod def from_bytes(data: bytes) -> Ciphertext: ... @@ -127,6 +130,16 @@ class Ciphertext: ... +@final +class CiphertextHeader: + @staticmethod + def from_bytes(data: bytes) -> CiphertextHeader: + ... + + def __bytes__(self) -> bytes: + ... + + @final class DecryptionShareSimple: @staticmethod @@ -159,7 +172,7 @@ class AggregatedTranscript: def create_decryption_share_simple( self, dkg: Dkg, - ciphertext: Ciphertext, + ciphertext_header: CiphertextHeader, aad: bytes, validator_keypair: Keypair ) -> DecryptionShareSimple: @@ -168,7 +181,7 @@ class AggregatedTranscript: def create_decryption_share_precomputed( self, dkg: Dkg, - ciphertext: Ciphertext, + ciphertext_header: CiphertextHeader, aad: bytes, validator_keypair: Keypair ) -> DecryptionSharePrecomputed: diff --git a/nucypher-core-python/src/lib.rs b/nucypher-core-python/src/lib.rs index 2ef4ceec..01ec5a2d 100644 --- a/nucypher-core-python/src/lib.rs +++ b/nucypher-core-python/src/lib.rs @@ -6,7 +6,9 @@ extern crate alloc; use alloc::collections::{BTreeMap, BTreeSet}; -use ferveo::bindings_python::{Ciphertext, DkgPublicKey, FerveoPublicKey, FerveoVariant}; +use ferveo::bindings_python::{ + Ciphertext, CiphertextHeader, DkgPublicKey, FerveoPublicKey, FerveoVariant, +}; use pyo3::class::basic::CompareOp; use pyo3::exceptions::{PyTypeError, PyValueError}; use pyo3::prelude::*; @@ -808,24 +810,15 @@ pub struct ThresholdMessageKit { #[pymethods] impl ThresholdMessageKit { #[new] - pub fn new(header: &Ciphertext, payload: &[u8], acp: &AccessControlPolicy) -> Self { + pub fn new(ciphertext: &Ciphertext, acp: &AccessControlPolicy) -> Self { Self { - backend: nucypher_core::ThresholdMessageKit::new( - header.as_ref(), - payload, - acp.as_ref(), - ), + backend: nucypher_core::ThresholdMessageKit::new(ciphertext.as_ref(), acp.as_ref()), } } #[getter] - pub fn header(&self) -> Ciphertext { - self.backend.header.clone().into() - } - - #[getter] - pub fn payload(&self) -> &[u8] { - self.backend.payload.as_ref() + pub fn ciphertext(&self) -> Ciphertext { + self.backend.ciphertext.clone().into() } #[getter] @@ -859,14 +852,14 @@ impl ThresholdDecryptionRequest { pub fn new( ritual_id: u32, variant: FerveoVariant, - ciphertext: &Ciphertext, + ciphertext_header: &CiphertextHeader, acp: &AccessControlPolicy, context: Option<&Context>, ) -> PyResult { Ok(Self { backend: nucypher_core::ThresholdDecryptionRequest::new( ritual_id, - ciphertext.as_ref(), + ciphertext_header.as_ref(), acp.as_ref(), context.map(|context| context.backend.clone()).as_ref(), variant.into(), @@ -893,8 +886,8 @@ impl ThresholdDecryptionRequest { } #[getter] - pub fn ciphertext(&self) -> Ciphertext { - self.backend.ciphertext.clone().into() + pub fn ciphertext_header(&self) -> CiphertextHeader { + self.backend.ciphertext_header.clone().into() } #[getter] diff --git a/nucypher-core-wasm/src/lib.rs b/nucypher-core-wasm/src/lib.rs index d0f14987..7e977314 100644 --- a/nucypher-core-wasm/src/lib.rs +++ b/nucypher-core-wasm/src/lib.rs @@ -12,7 +12,7 @@ use alloc::{ }; use core::fmt; -use ferveo::bindings_wasm::{Ciphertext, DkgPublicKey, FerveoVariant}; +use ferveo::bindings_wasm::{Ciphertext, CiphertextHeader, DkgPublicKey, FerveoVariant}; use js_sys::Error; use umbral_pre::bindings_wasm::{ Capsule, PublicKey, RecoverableSignature, SecretKey, Signer, VerifiedCapsuleFrag, @@ -714,22 +714,16 @@ generate_equals!(ThresholdMessageKit); #[wasm_bindgen] impl ThresholdMessageKit { #[wasm_bindgen(constructor)] - pub fn new(header: &Ciphertext, payload: &[u8], acp: &AccessControlPolicy) -> Self { + pub fn new(ciphertext: &Ciphertext, acp: &AccessControlPolicy) -> Self { Self(nucypher_core::ThresholdMessageKit::new( - header.as_ref(), - payload, + ciphertext.as_ref(), acp.as_ref(), )) } #[wasm_bindgen(getter)] - pub fn header(&self) -> Ciphertext { - self.0.header.clone().into() - } - - #[wasm_bindgen(getter)] - pub fn payload(&self) -> Box<[u8]> { - self.0.payload.clone() + pub fn ciphertext(&self) -> Ciphertext { + self.0.ciphertext.clone().into() } #[wasm_bindgen(getter)] @@ -760,7 +754,7 @@ impl ThresholdDecryptionRequest { pub fn new( ritual_id: u32, variant: &FerveoVariant, - ciphertext: &Ciphertext, + ciphertext_header: &CiphertextHeader, acp: &AccessControlPolicy, context: &OptionContext, ) -> Result { @@ -768,7 +762,7 @@ impl ThresholdDecryptionRequest { Ok(Self(nucypher_core::ThresholdDecryptionRequest::new( ritual_id, - ciphertext.as_ref(), + ciphertext_header.as_ref(), acp.as_ref(), typed_context.as_ref().map(|context| &context.0), variant.clone().into(), @@ -785,9 +779,9 @@ impl ThresholdDecryptionRequest { self.0.variant.into() } - #[wasm_bindgen(getter)] - pub fn ciphertext(&self) -> Ciphertext { - self.0.ciphertext.clone().into() + #[wasm_bindgen(getter, js_name = ciphertextHeader)] + pub fn ciphertext_header(&self) -> CiphertextHeader { + self.0.ciphertext_header.clone().into() } #[wasm_bindgen(getter)] diff --git a/nucypher-core-wasm/tests/wasm.rs b/nucypher-core-wasm/tests/wasm.rs index ca701f16..084835f3 100644 --- a/nucypher-core-wasm/tests/wasm.rs +++ b/nucypher-core-wasm/tests/wasm.rs @@ -1,5 +1,3 @@ -use nucypher_core_wasm::*; - use ferveo::bindings_wasm::{ferveo_encrypt, DkgPublicKey, FerveoVariant, Keypair}; use umbral_pre::bindings_wasm::{ generate_kfrags, reencrypt, Capsule, RecoverableSignature, SecretKey, Signer, @@ -9,6 +7,8 @@ use wasm_bindgen::prelude::*; use wasm_bindgen::JsCast; use wasm_bindgen_test::*; +use nucypher_core_wasm::*; + // // Test utilities // @@ -699,9 +699,6 @@ fn threshold_decryption_request() { let context: JsValue = Some(Context::new("{'user': 'context'}")).into(); let dkg_pk = DkgPublicKey::random(); - let message = "my-message".as_bytes(); - let ciphertext = ferveo_encrypt(message, conditions.as_bytes(), &dkg_pk).unwrap(); - let authorization = b"we_dont_need_no_stinking_badges"; let acp = AccessControlPolicy::new( &dkg_pk, @@ -710,10 +707,14 @@ fn threshold_decryption_request() { ) .unwrap(); + let message = "my-message".as_bytes(); + let ciphertext = ferveo_encrypt(message, &acp.aad(), &dkg_pk).unwrap(); + let ciphertext_header = ciphertext.header().unwrap(); + let request = ThresholdDecryptionRequest::new( ritual_id, &FerveoVariant::simple(), - &ciphertext, + &ciphertext_header, &acp, &context.unchecked_into::(), ) @@ -842,9 +843,6 @@ fn threshold_message_kit() { let conditions_js: JsValue = Some(Conditions::new(conditions)).into(); let dkg_pk = DkgPublicKey::random(); - let symmetric_key = "The Tyranny of Merit".as_bytes(); - let header = ferveo_encrypt(symmetric_key, conditions.as_bytes(), &dkg_pk).unwrap(); - let authorization = b"we_dont_need_no_stinking_badges"; let acp = AccessControlPolicy::new( @@ -854,17 +852,14 @@ fn threshold_message_kit() { ) .unwrap(); - let payload = b"data_encapsulation"; + let data = "The Tyranny of Merit".as_bytes(); + let ciphertext = ferveo_encrypt(data, &acp.aad(), &dkg_pk).unwrap(); - let tmk = ThresholdMessageKit::new(&header, payload, &acp); + let tmk = ThresholdMessageKit::new(&ciphertext, &acp); // mimic serialization/deserialization over the wire let serialized_tmk = tmk.to_bytes(); let deserialized_tmk = ThresholdMessageKit::from_bytes(&serialized_tmk).unwrap(); - assert_eq!( - payload.to_vec().into_boxed_slice(), - deserialized_tmk.payload() - ); - assert_eq!(header, deserialized_tmk.header()); + assert_eq!(ciphertext, deserialized_tmk.ciphertext()); assert_eq!(acp, deserialized_tmk.acp()); } diff --git a/nucypher-core/src/dkg.rs b/nucypher-core/src/dkg.rs index 8c4066dc..ec048af9 100644 --- a/nucypher-core/src/dkg.rs +++ b/nucypher-core/src/dkg.rs @@ -4,7 +4,7 @@ use core::fmt; use chacha20poly1305::aead::{Aead, AeadCore, KeyInit, OsRng}; use chacha20poly1305::{ChaCha20Poly1305, Key, Nonce}; -use ferveo::api::{Ciphertext, FerveoVariant}; +use ferveo::api::{CiphertextHeader, FerveoVariant}; use generic_array::typenum::Unsigned; use serde::{Deserialize, Serialize}; use umbral_pre::serde_bytes; // TODO should this be in umbral? @@ -354,7 +354,7 @@ pub struct ThresholdDecryptionRequest { /// The ID of the ritual. pub ritual_id: u32, /// The ciphertext to generate a decryption share for. - pub ciphertext: Ciphertext, + pub ciphertext_header: CiphertextHeader, /// The associated access control metadata. pub acp: AccessControlPolicy, /// A blob of bytes containing context required to evaluate conditions. @@ -367,14 +367,14 @@ impl ThresholdDecryptionRequest { /// Creates a new decryption request. pub fn new( ritual_id: u32, - ciphertext: &Ciphertext, + ciphertext_header: &CiphertextHeader, acp: &AccessControlPolicy, context: Option<&Context>, variant: FerveoVariant, ) -> Self { Self { ritual_id, - ciphertext: ciphertext.clone(), + ciphertext_header: ciphertext_header.clone(), acp: acp.clone(), context: context.cloned(), variant, @@ -393,7 +393,7 @@ impl ThresholdDecryptionRequest { impl<'a> ProtocolObjectInner<'a> for ThresholdDecryptionRequest { fn version() -> (u16, u16) { - (3, 0) + (4, 0) } fn brand() -> [u8; 4] { @@ -594,13 +594,6 @@ impl<'a> ProtocolObject<'a> for EncryptedThresholdDecryptionResponse {} #[cfg(test)] mod tests { use ferveo::api::{encrypt as ferveo_encrypt, DkgPublicKey, FerveoVariant, SecretBox}; - - use crate::{ - EncryptedThresholdDecryptionRequest, EncryptedThresholdDecryptionResponse, - SessionSecretFactory, SessionStaticKey, ThresholdDecryptionRequest, - ThresholdDecryptionResponse, - }; - use generic_array::typenum::Unsigned; use rand_core::RngCore; @@ -611,6 +604,11 @@ mod tests { decrypt_with_shared_secret, encrypt_with_shared_secret, DecryptionError, NonceSize, }; use crate::versioning::{ProtocolObject, ProtocolObjectInner}; + use crate::{ + EncryptedThresholdDecryptionRequest, EncryptedThresholdDecryptionResponse, + SessionSecretFactory, SessionStaticKey, ThresholdDecryptionRequest, + ThresholdDecryptionResponse, + }; #[test] fn decryption_with_shared_secret() { @@ -736,9 +734,11 @@ mod tests { let acp = AccessControlPolicy::new(&dkg_pk, authorization, Some(&Conditions::new("abcd"))); + let ciphertext_header = ciphertext.header().unwrap(); + let request = ThresholdDecryptionRequest::new( ritual_id, - &ciphertext, + &ciphertext_header, &acp, Some(&Context::new("efgh")), variant, diff --git a/nucypher-core/src/threshold_message_kit.rs b/nucypher-core/src/threshold_message_kit.rs index 2d12982d..f2c90eef 100644 --- a/nucypher-core/src/threshold_message_kit.rs +++ b/nucypher-core/src/threshold_message_kit.rs @@ -3,7 +3,6 @@ use alloc::string::String; use ferveo::api::Ciphertext; use serde::{Deserialize, Serialize}; -use umbral_pre::serde_bytes; use crate::access_control::AccessControlPolicy; use crate::versioning::{ @@ -16,11 +15,7 @@ use crate::versioning::{ #[derive(PartialEq, Eq, Debug, Serialize, Deserialize, Clone)] pub struct ThresholdMessageKit { /// The key encapsulation ciphertext - pub header: Ciphertext, - - /// The bulk data encapsulation ciphertext - #[serde(with = "serde_bytes::as_base64")] - pub payload: Box<[u8]>, + pub ciphertext: Ciphertext, /// The associated access control metadata. pub acp: AccessControlPolicy, @@ -28,10 +23,9 @@ pub struct ThresholdMessageKit { impl ThresholdMessageKit { /// Creates a new threshold message kit. - pub fn new(header: &Ciphertext, payload: &[u8], acp: &AccessControlPolicy) -> Self { + pub fn new(ciphertext: &Ciphertext, acp: &AccessControlPolicy) -> Self { ThresholdMessageKit { - header: header.clone(), - payload: payload.to_vec().into(), + ciphertext: ciphertext.clone(), acp: acp.clone(), } } @@ -63,34 +57,28 @@ impl<'a> ProtocolObject<'a> for ThresholdMessageKit {} #[cfg(test)] mod tests { + use ferveo::api::{encrypt as ferveo_encrypt, DkgPublicKey, SecretBox}; + use crate::access_control::AccessControlPolicy; use crate::conditions::Conditions; use crate::threshold_message_kit::ThresholdMessageKit; use crate::versioning::ProtocolObject; - use ferveo::api::{encrypt as ferveo_encrypt, DkgPublicKey, SecretBox}; #[test] fn threshold_message_kit() { let dkg_pk = DkgPublicKey::random(); - let symmetric_key = "The Tyranny of Merit".as_bytes().to_vec(); - let aad = "my-add".as_bytes(); - let header = ferveo_encrypt(SecretBox::new(symmetric_key), aad, &dkg_pk).unwrap(); + let data = "The Tyranny of Merit".as_bytes().to_vec(); let authorization = b"we_dont_need_no_stinking_badges"; let acp = AccessControlPolicy::new(&dkg_pk, authorization, Some(&Conditions::new("abcd"))); - let payload = b"data_encapsulation"; - - let tmk = ThresholdMessageKit::new(&header, payload, &acp); + let ciphertext = ferveo_encrypt(SecretBox::new(data), &acp.aad(), &dkg_pk).unwrap(); + let tmk = ThresholdMessageKit::new(&ciphertext, &acp); // mimic serialization/deserialization over the wire let serialized_tmk = tmk.to_bytes(); let deserialized_tmk = ThresholdMessageKit::from_bytes(&serialized_tmk).unwrap(); - assert_eq!( - payload.to_vec().into_boxed_slice(), - deserialized_tmk.payload - ); - assert_eq!(header, deserialized_tmk.header); + assert_eq!(ciphertext, deserialized_tmk.ciphertext); assert_eq!(acp, deserialized_tmk.acp); } } From 71312cf78a4940974dc9c903cb723090983520ef Mon Sep 17 00:00:00 2001 From: derekpierre Date: Mon, 21 Aug 2023 21:48:22 -0400 Subject: [PATCH 08/14] Modify AccessControlPolicy to be instantiated with an AuthenticatedData object which works around to chicken and egg issue for obtaining the aad to use when encrypting data, but which is needed for the AccessControlPolicy after the ciphertext is signed. Update bindings/tests. --- .../nucypher_core/__init__.py | 1 + .../nucypher_core/__init__.pyi | 23 ++- nucypher-core-python/src/lib.rs | 67 +++++++-- nucypher-core-wasm/src/lib.rs | 58 ++++++-- nucypher-core-wasm/tests/wasm.rs | 90 ++++++++---- nucypher-core/src/access_control.rs | 134 +++++++++++++----- nucypher-core/src/dkg.rs | 11 +- nucypher-core/src/lib.rs | 2 +- nucypher-core/src/threshold_message_kit.rs | 7 +- 9 files changed, 307 insertions(+), 86 deletions(-) diff --git a/nucypher-core-python/nucypher_core/__init__.py b/nucypher-core-python/nucypher_core/__init__.py index b13cd175..468be9bd 100644 --- a/nucypher-core-python/nucypher_core/__init__.py +++ b/nucypher-core-python/nucypher_core/__init__.py @@ -18,6 +18,7 @@ MetadataResponse, MetadataResponsePayload, AccessControlPolicy, + AuthenticatedData, ThresholdMessageKit, ThresholdDecryptionRequest, ThresholdDecryptionResponse, diff --git a/nucypher-core-python/nucypher_core/__init__.pyi b/nucypher-core-python/nucypher_core/__init__.pyi index 350ae688..06c2721e 100644 --- a/nucypher-core-python/nucypher_core/__init__.pyi +++ b/nucypher-core-python/nucypher_core/__init__.pyi @@ -431,10 +431,31 @@ class MetadataResponse: ... +@final +class AuthenticatedData: + + def __init__(self, public_key: DkgPublicKey, conditions: Optional[Conditions]): + ... + + public_key: DkgPublicKey + + conditions: Optional[Conditions] + + def aad(self) -> bytes: + ... + + @staticmethod + def from_bytes(data: bytes) -> AuthenticatedData: + ... + + def __bytes__(self) -> bytes: + ... + + @final class AccessControlPolicy: - def __init__(self, public_key: DkgPublicKey, conditions: Optional[Conditions], authorization: bytes): + def __init__(self, auth_data: AuthenticatedData, authorization: bytes): ... public_key: DkgPublicKey diff --git a/nucypher-core-python/src/lib.rs b/nucypher-core-python/src/lib.rs index 01ec5a2d..06f91f84 100644 --- a/nucypher-core-python/src/lib.rs +++ b/nucypher-core-python/src/lib.rs @@ -736,26 +736,21 @@ impl SessionSecretFactory { } // -// Access control metadata for encrypted data. +// Authenticated data. // #[pyclass(module = "nucypher_core")] #[derive(derive_more::From, derive_more::AsRef)] -pub struct AccessControlPolicy { - backend: nucypher_core::AccessControlPolicy, +pub struct AuthenticatedData { + backend: nucypher_core::AuthenticatedData, } #[pymethods] -impl AccessControlPolicy { +impl AuthenticatedData { #[new] - pub fn new( - public_key: &DkgPublicKey, - authorization: &[u8], - conditions: Option<&Conditions>, - ) -> Self { + pub fn new(public_key: &DkgPublicKey, conditions: Option<&Conditions>) -> Self { Self { - backend: nucypher_core::AccessControlPolicy::new( + backend: nucypher_core::AuthenticatedData::new( public_key.as_ref(), - authorization, conditions .map(|conditions| conditions.backend.clone()) .as_ref(), @@ -783,6 +778,55 @@ impl AccessControlPolicy { }) } + #[staticmethod] + pub fn from_bytes(data: &[u8]) -> PyResult { + from_bytes::<_, nucypher_core::AuthenticatedData>(data) + } + + fn __bytes__(&self) -> PyObject { + to_bytes(self) + } +} + +// +// Access control metadata for encrypted data. +// +#[pyclass(module = "nucypher_core")] +#[derive(derive_more::From, derive_more::AsRef)] +pub struct AccessControlPolicy { + backend: nucypher_core::AccessControlPolicy, +} + +#[pymethods] +impl AccessControlPolicy { + #[new] + pub fn new(auth_data: &AuthenticatedData, authorization: &[u8]) -> Self { + Self { + backend: nucypher_core::AccessControlPolicy::new(auth_data.as_ref(), authorization), + } + } + + pub fn aad(&self, py: Python) -> PyObject { + let result = self.backend.auth_data.aad(); + PyBytes::new(py, result.as_ref()).into() + } + + #[getter] + pub fn public_key(&self) -> DkgPublicKey { + self.backend.auth_data.public_key.into() + } + + #[getter] + pub fn conditions(&self) -> Option { + self.backend + .auth_data + .conditions + .clone() + .map(|conditions| Conditions { + backend: conditions, + }) + } + #[getter] pub fn authorization(&self) -> &[u8] { self.backend.authorization.as_ref() @@ -1518,6 +1562,7 @@ fn _nucypher_core(py: Python, core_module: &PyModule) -> PyResult<()> { core_module.add_class::()?; core_module.add_class::()?; core_module.add_class::()?; + core_module.add_class::()?; core_module.add_class::()?; core_module.add_class::()?; diff --git a/nucypher-core-wasm/src/lib.rs b/nucypher-core-wasm/src/lib.rs index 7e977314..7a26ba60 100644 --- a/nucypher-core-wasm/src/lib.rs +++ b/nucypher-core-wasm/src/lib.rs @@ -648,6 +648,52 @@ impl SessionSecretFactory { } } +// +// AuthenticatedData +// + +#[wasm_bindgen] +#[derive(PartialEq, Eq, Debug, derive_more::From, derive_more::AsRef)] +pub struct AuthenticatedData(nucypher_core::AuthenticatedData); + +generate_from_bytes!(AuthenticatedData); +generate_equals!(AuthenticatedData); + +#[wasm_bindgen] +impl AuthenticatedData { + #[wasm_bindgen(constructor)] + pub fn new( + public_key: &DkgPublicKey, + conditions: &OptionConditions, + ) -> Result { + let typed_conditions = try_from_js_option::(conditions)?; + + Ok(Self(nucypher_core::AuthenticatedData::new( + public_key.as_ref(), + typed_conditions.as_ref().map(|conditions| &conditions.0), + ))) + } + + pub fn aad(&self) -> Box<[u8]> { + self.0.aad() + } + + #[wasm_bindgen(getter, js_name = publicKey)] + pub fn public_key(&self) -> DkgPublicKey { + DkgPublicKey::from(self.0.public_key) + } + + #[wasm_bindgen(getter)] + pub fn conditions(&self) -> Option { + self.0.conditions.clone().map(Conditions) + } + + #[wasm_bindgen(js_name = toBytes)] + pub fn to_bytes(&self) -> Box<[u8]> { + to_bytes(self) + } +} + // // AccessControlPolicy // @@ -663,16 +709,12 @@ generate_equals!(AccessControlPolicy); impl AccessControlPolicy { #[wasm_bindgen(constructor)] pub fn new( - public_key: &DkgPublicKey, + auth_data: &AuthenticatedData, authorization: &[u8], - conditions: &OptionConditions, ) -> Result { - let typed_conditions = try_from_js_option::(conditions)?; - Ok(Self(nucypher_core::AccessControlPolicy::new( - public_key.as_ref(), + auth_data.as_ref(), authorization, - typed_conditions.as_ref().map(|conditions| &conditions.0), ))) } @@ -682,7 +724,7 @@ impl AccessControlPolicy { #[wasm_bindgen(getter, js_name = publicKey)] pub fn public_key(&self) -> DkgPublicKey { - DkgPublicKey::from(self.0.public_key) + DkgPublicKey::from(self.0.auth_data.public_key) } #[wasm_bindgen(getter)] @@ -692,7 +734,7 @@ impl AccessControlPolicy { #[wasm_bindgen(getter)] pub fn conditions(&self) -> Option { - self.0.conditions.clone().map(Conditions) + self.0.auth_data.conditions.clone().map(Conditions) } #[wasm_bindgen(js_name = toBytes)] diff --git a/nucypher-core-wasm/tests/wasm.rs b/nucypher-core-wasm/tests/wasm.rs index 084835f3..c20b9369 100644 --- a/nucypher-core-wasm/tests/wasm.rs +++ b/nucypher-core-wasm/tests/wasm.rs @@ -699,13 +699,13 @@ fn threshold_decryption_request() { let context: JsValue = Some(Context::new("{'user': 'context'}")).into(); let dkg_pk = DkgPublicKey::random(); + + let auth_data = + AuthenticatedData::new(&dkg_pk, &conditions_js.unchecked_into::()) + .unwrap(); + let authorization = b"we_dont_need_no_stinking_badges"; - let acp = AccessControlPolicy::new( - &dkg_pk, - authorization, - &conditions_js.unchecked_into::(), - ) - .unwrap(); + let acp = AccessControlPolicy::new(&auth_data, authorization).unwrap(); let message = "my-message".as_bytes(); let ciphertext = ferveo_encrypt(message, &acp.aad(), &dkg_pk).unwrap(); @@ -797,6 +797,41 @@ fn threshold_decryption_response() { .is_err()); } +#[wasm_bindgen_test] +fn authenticated_data() { + let dkg_pk = DkgPublicKey::random(); + + let conditions = "{'some': 'condition'}"; + let conditions_js: JsValue = Some(Conditions::new(conditions)).into(); + + let auth_data = + AuthenticatedData::new(&dkg_pk, &conditions_js.unchecked_into::()) + .unwrap(); + + assert_eq!( + auth_data.public_key().to_bytes().unwrap(), + dkg_pk.to_bytes().unwrap() + ); + assert_eq!(auth_data.conditions().unwrap().to_string(), conditions); + + let mut expected_aad = dkg_pk.to_bytes().unwrap().to_vec(); + expected_aad.extend(conditions.as_bytes()); + + assert_eq!(auth_data.aad(), expected_aad.into_boxed_slice()); + + // mimic serialization/deserialization over the wire + let serialized_auth_data = auth_data.to_bytes(); + let deserialized_auth_data = AuthenticatedData::from_bytes(&serialized_auth_data).unwrap(); + assert_eq!( + deserialized_auth_data.public_key().to_bytes().unwrap(), + dkg_pk.to_bytes().unwrap() + ); + assert_eq!( + deserialized_auth_data.conditions().unwrap().to_string(), + conditions, + ); +} + #[wasm_bindgen_test] fn access_control_policy() { let dkg_pk = DkgPublicKey::random(); @@ -804,13 +839,22 @@ fn access_control_policy() { let conditions = "{'some': 'condition'}"; let conditions_js: JsValue = Some(Conditions::new(conditions)).into(); + let auth_data = + AuthenticatedData::new(&dkg_pk, &conditions_js.unchecked_into::()) + .unwrap(); + let authorization = b"we_dont_need_no_stinking_badges"; - let acp = AccessControlPolicy::new( - &dkg_pk, - authorization, - &conditions_js.unchecked_into::(), - ) - .unwrap(); + let acp = AccessControlPolicy::new(&auth_data, authorization).unwrap(); + + assert_eq!( + dkg_pk.to_bytes().unwrap(), + acp.public_key().to_bytes().unwrap() + ); + assert_eq!( + authorization.to_vec().into_boxed_slice(), + acp.authorization() + ); + assert_eq!(conditions, acp.conditions().unwrap().to_string()); // mimic serialization/deserialization over the wire let serialized_acp = acp.to_bytes(); @@ -828,13 +872,8 @@ fn access_control_policy() { deserialized_acp.conditions().unwrap().to_string() ); - // check aad; expected to be dkg public key + conditions - let aad = acp.aad(); - - let mut expected_aad = dkg_pk.to_bytes().unwrap().to_vec(); - expected_aad.extend(conditions.as_bytes()); - - assert_eq!(expected_aad.into_boxed_slice(), aad); + // check aad; expected acp and auth_data acps to be the same + assert_eq!(deserialized_acp.aad(), auth_data.aad()); } #[wasm_bindgen_test] @@ -843,14 +882,13 @@ fn threshold_message_kit() { let conditions_js: JsValue = Some(Conditions::new(conditions)).into(); let dkg_pk = DkgPublicKey::random(); - let authorization = b"we_dont_need_no_stinking_badges"; - let acp = AccessControlPolicy::new( - &dkg_pk, - authorization, - &conditions_js.unchecked_into::(), - ) - .unwrap(); + let auth_data = + AuthenticatedData::new(&dkg_pk, &conditions_js.unchecked_into::()) + .unwrap(); + + let authorization = b"we_dont_need_no_stinking_badges"; + let acp = AccessControlPolicy::new(&auth_data, authorization).unwrap(); let data = "The Tyranny of Merit".as_bytes(); let ciphertext = ferveo_encrypt(data, &acp.aad(), &dkg_pk).unwrap(); diff --git a/nucypher-core/src/access_control.rs b/nucypher-core/src/access_control.rs index d0977532..6da6253e 100644 --- a/nucypher-core/src/access_control.rs +++ b/nucypher-core/src/access_control.rs @@ -11,32 +11,21 @@ use crate::versioning::{ messagepack_deserialize, messagepack_serialize, ProtocolObject, ProtocolObjectInner, }; -// TODO should this be in umbral? - -/// Access control metadata for encrypted data. +/// Authenticated data for encrypted data. #[derive(Debug, Serialize, Deserialize, Clone)] -pub struct AccessControlPolicy { +pub struct AuthenticatedData { /// The public key for the encrypted data pub public_key: DkgPublicKey, - /// The authorization data for the encrypter of the data - #[serde(with = "serde_bytes::as_base64")] - pub authorization: Box<[u8]>, - /// The conditions associated with the encrypted data pub conditions: Option, } -impl AccessControlPolicy { +impl AuthenticatedData { /// Creates a new access control policy. - pub fn new( - public_key: &DkgPublicKey, - authorization: &[u8], - conditions: Option<&Conditions>, - ) -> Self { - AccessControlPolicy { + pub fn new(public_key: &DkgPublicKey, conditions: Option<&Conditions>) -> Self { + AuthenticatedData { public_key: *public_key, - authorization: authorization.to_vec().into(), conditions: conditions.cloned(), } } @@ -52,15 +41,74 @@ impl AccessControlPolicy { } } -impl PartialEq for AccessControlPolicy { +impl PartialEq for AuthenticatedData { fn eq(&self, other: &Self) -> bool { self.public_key.to_bytes().unwrap() == other.public_key.to_bytes().unwrap() - && self.authorization == other.authorization && self.conditions == other.conditions } } -impl Eq for AccessControlPolicy {} +impl Eq for AuthenticatedData {} + +impl<'a> ProtocolObjectInner<'a> for AuthenticatedData { + fn version() -> (u16, u16) { + (1, 0) + } + + fn brand() -> [u8; 4] { + *b"AuDa" + } + + fn unversioned_to_bytes(&self) -> Box<[u8]> { + messagepack_serialize(&self) + } + + fn unversioned_from_bytes(minor_version: u16, bytes: &[u8]) -> Option> { + if minor_version == 0 { + Some(messagepack_deserialize(bytes)) + } else { + None + } + } +} + +impl<'a> ProtocolObject<'a> for AuthenticatedData {} + +/// Access control policy data for encrypted data. +#[derive(Eq, PartialEq, Debug, Serialize, Deserialize, Clone)] +pub struct AccessControlPolicy { + /// The authenticated data for the access control policy + pub auth_data: AuthenticatedData, + + /// The authorization data for the authenticated data + #[serde(with = "serde_bytes::as_base64")] + pub authorization: Box<[u8]>, +} + +impl AccessControlPolicy { + /// Creates a new access control policy. + pub fn new(auth_data: &AuthenticatedData, authorization: &[u8]) -> Self { + AccessControlPolicy { + auth_data: auth_data.clone(), + authorization: authorization.to_vec().into(), + } + } + + /// Return the aad. + pub fn aad(&self) -> Box<[u8]> { + self.auth_data.aad() + } + + /// Return the DKG public key + pub fn public_key(&self) -> DkgPublicKey { + self.auth_data.public_key + } + + /// Return the conditions + pub fn conditions(&self) -> Option { + self.auth_data.conditions.clone() + } +} impl<'a> ProtocolObjectInner<'a> for AccessControlPolicy { fn version() -> (u16, u16) { @@ -90,34 +138,56 @@ impl<'a> ProtocolObject<'a> for AccessControlPolicy {} mod tests { use ferveo::api::DkgPublicKey; - use crate::access_control::AccessControlPolicy; + use crate::access_control::{AccessControlPolicy, AuthenticatedData}; use crate::conditions::Conditions; use crate::versioning::ProtocolObject; #[test] - fn access_control_policy() { + fn authenticated_data() { let dkg_pk = DkgPublicKey::random(); + let conditions = Conditions::new("abcd"); + + let auth_data = AuthenticatedData::new(&dkg_pk, Some(&conditions)); + // check aad for auth data; expected to be dkg public key + conditions + let mut expected_aad = dkg_pk.to_bytes().unwrap().to_vec(); + expected_aad.extend(conditions.as_ref().as_bytes()); + let auth_data_aad = auth_data.aad(); + assert_eq!(expected_aad.into_boxed_slice(), auth_data_aad); + + assert_eq!(auth_data.public_key, dkg_pk); + assert_eq!(auth_data.conditions, Some(conditions)); + + let auth_data_2 = AuthenticatedData::new(&dkg_pk, Some(&Conditions::new("abcd"))); + assert_eq!(auth_data, auth_data_2); + + // mimic serialization/deserialization over the wire + let serialized_auth_data = auth_data.to_bytes(); + let deserialized_auth_data = AuthenticatedData::from_bytes(&serialized_auth_data).unwrap(); + assert_eq!(auth_data.public_key, deserialized_auth_data.public_key); + assert_eq!(auth_data.conditions, deserialized_auth_data.conditions); + } + + #[test] + fn access_control_policy() { + let dkg_pk = DkgPublicKey::random(); let conditions = Conditions::new("abcd"); + + let auth_data = AuthenticatedData::new(&dkg_pk, Some(&conditions)); let authorization = b"we_dont_need_no_stinking_badges"; - let acp = AccessControlPolicy::new(&dkg_pk, authorization, Some(&conditions)); + let acp = AccessControlPolicy::new(&auth_data, authorization); + + // check that aad for auth_data and acp are the same + assert_eq!(auth_data.aad(), acp.aad()); // mimic serialization/deserialization over the wire let serialized_acp = acp.to_bytes(); let deserialized_acp = AccessControlPolicy::from_bytes(&serialized_acp).unwrap(); - assert_eq!(dkg_pk, deserialized_acp.public_key); - assert_eq!(conditions, deserialized_acp.conditions.unwrap()); + assert_eq!(auth_data.public_key, deserialized_acp.public_key()); + assert_eq!(auth_data.conditions, deserialized_acp.conditions()); assert_eq!( authorization.to_vec().into_boxed_slice(), deserialized_acp.authorization ); - - // check aad; expected to be dkg public key + conditions - let aad = acp.aad(); - - let mut expected_aad = dkg_pk.to_bytes().unwrap().to_vec(); - expected_aad.extend(conditions.as_ref().as_bytes()); - - assert_eq!(expected_aad.into_boxed_slice(), aad); } } diff --git a/nucypher-core/src/dkg.rs b/nucypher-core/src/dkg.rs index ec048af9..ebc49244 100644 --- a/nucypher-core/src/dkg.rs +++ b/nucypher-core/src/dkg.rs @@ -605,9 +605,9 @@ mod tests { }; use crate::versioning::{ProtocolObject, ProtocolObjectInner}; use crate::{ - EncryptedThresholdDecryptionRequest, EncryptedThresholdDecryptionResponse, - SessionSecretFactory, SessionStaticKey, ThresholdDecryptionRequest, - ThresholdDecryptionResponse, + AuthenticatedData, EncryptedThresholdDecryptionRequest, + EncryptedThresholdDecryptionResponse, SessionSecretFactory, SessionStaticKey, + ThresholdDecryptionRequest, ThresholdDecryptionResponse, }; #[test] @@ -730,9 +730,10 @@ mod tests { let aad = "my-add".as_bytes(); let ciphertext = ferveo_encrypt(SecretBox::new(message), aad, &dkg_pk).unwrap(); + let auth_data = AuthenticatedData::new(&dkg_pk, Some(&Conditions::new("abcd"))); + let authorization = b"self_authorization"; - let acp = - AccessControlPolicy::new(&dkg_pk, authorization, Some(&Conditions::new("abcd"))); + let acp = AccessControlPolicy::new(&auth_data, authorization); let ciphertext_header = ciphertext.header().unwrap(); diff --git a/nucypher-core/src/lib.rs b/nucypher-core/src/lib.rs index 5486ef7e..8d406ec6 100644 --- a/nucypher-core/src/lib.rs +++ b/nucypher-core/src/lib.rs @@ -27,7 +27,7 @@ mod versioning; /// Error returned by various `verify()` methods in the crate. pub struct VerificationError; -pub use access_control::AccessControlPolicy; +pub use access_control::{AccessControlPolicy, AuthenticatedData}; pub use address::Address; pub use conditions::{Conditions, Context}; diff --git a/nucypher-core/src/threshold_message_kit.rs b/nucypher-core/src/threshold_message_kit.rs index f2c90eef..f5439832 100644 --- a/nucypher-core/src/threshold_message_kit.rs +++ b/nucypher-core/src/threshold_message_kit.rs @@ -59,7 +59,7 @@ impl<'a> ProtocolObject<'a> for ThresholdMessageKit {} mod tests { use ferveo::api::{encrypt as ferveo_encrypt, DkgPublicKey, SecretBox}; - use crate::access_control::AccessControlPolicy; + use crate::access_control::{AccessControlPolicy, AuthenticatedData}; use crate::conditions::Conditions; use crate::threshold_message_kit::ThresholdMessageKit; use crate::versioning::ProtocolObject; @@ -70,7 +70,10 @@ mod tests { let data = "The Tyranny of Merit".as_bytes().to_vec(); let authorization = b"we_dont_need_no_stinking_badges"; - let acp = AccessControlPolicy::new(&dkg_pk, authorization, Some(&Conditions::new("abcd"))); + let acp = AccessControlPolicy::new( + &AuthenticatedData::new(&dkg_pk, Some(&Conditions::new("abcd"))), + authorization, + ); let ciphertext = ferveo_encrypt(SecretBox::new(data), &acp.aad(), &dkg_pk).unwrap(); let tmk = ThresholdMessageKit::new(&ciphertext, &acp); From 1136efe24d0d73f1812944bac3b868f960861564 Mon Sep 17 00:00:00 2001 From: derekpierre Date: Tue, 22 Aug 2023 13:08:31 -0400 Subject: [PATCH 09/14] Add encrypt_for_dkg function so that Ciphertext and AuthenticatedData can be returned from the same call. Added python/wasm bindings. --- .../nucypher_core/__init__.py | 1 + .../nucypher_core/__init__.pyi | 4 +++ nucypher-core-python/src/lib.rs | 23 ++++++++++++++++- nucypher-core-wasm/src/lib.rs | 25 +++++++++++++++++++ nucypher-core/src/access_control.rs | 18 ++++++++++++- nucypher-core/src/lib.rs | 2 +- 6 files changed, 70 insertions(+), 3 deletions(-) diff --git a/nucypher-core-python/nucypher_core/__init__.py b/nucypher-core-python/nucypher_core/__init__.py index 468be9bd..fe122c44 100644 --- a/nucypher-core-python/nucypher_core/__init__.py +++ b/nucypher-core-python/nucypher_core/__init__.py @@ -28,4 +28,5 @@ SessionStaticKey, SessionStaticSecret, SessionSecretFactory, + encrypt_for_dkg, ) diff --git a/nucypher-core-python/nucypher_core/__init__.pyi b/nucypher-core-python/nucypher_core/__init__.pyi index 06c2721e..e803ea62 100644 --- a/nucypher-core-python/nucypher_core/__init__.pyi +++ b/nucypher-core-python/nucypher_core/__init__.pyi @@ -452,6 +452,10 @@ class AuthenticatedData: ... +def encrypt_for_dkg(data: bytes, public_key: DkgPublicKey, conditions: Optional[Conditions]) -> Tuple[Ciphertext, AuthenticatedData]: + ... + + @final class AccessControlPolicy: diff --git a/nucypher-core-python/src/lib.rs b/nucypher-core-python/src/lib.rs index 06f91f84..b03032ba 100644 --- a/nucypher-core-python/src/lib.rs +++ b/nucypher-core-python/src/lib.rs @@ -7,7 +7,7 @@ extern crate alloc; use alloc::collections::{BTreeMap, BTreeSet}; use ferveo::bindings_python::{ - Ciphertext, CiphertextHeader, DkgPublicKey, FerveoPublicKey, FerveoVariant, + Ciphertext, CiphertextHeader, DkgPublicKey, FerveoPublicKey, FerveoPythonError, FerveoVariant, }; use pyo3::class::basic::CompareOp; use pyo3::exceptions::{PyTypeError, PyValueError}; @@ -788,6 +788,26 @@ impl AuthenticatedData { } } +// +// Encrypt for DKG. +// +#[pyfunction] +pub fn encrypt_for_dkg( + data: &[u8], + public_key: &DkgPublicKey, + conditions: Option<&Conditions>, +) -> PyResult<(Ciphertext, AuthenticatedData)> { + let (ciphertext, auth_data) = nucypher_core::encrypt_for_dkg( + data, + public_key.as_ref(), + conditions + .map(|conditions| conditions.backend.clone()) + .as_ref(), + ) + .map_err(FerveoPythonError::FerveoError)?; + Ok((ciphertext.into(), auth_data.into())) +} + // // Access control metadata for encrypted data. // @@ -1565,6 +1585,7 @@ fn _nucypher_core(py: Python, core_module: &PyModule) -> PyResult<()> { core_module.add_class::()?; core_module.add_class::()?; core_module.add_class::()?; + core_module.add_function(wrap_pyfunction!(encrypt_for_dkg, core_module)?)?; // Build the umbral module let umbral_module = PyModule::new(py, "umbral")?; diff --git a/nucypher-core-wasm/src/lib.rs b/nucypher-core-wasm/src/lib.rs index 7a26ba60..9e632d77 100644 --- a/nucypher-core-wasm/src/lib.rs +++ b/nucypher-core-wasm/src/lib.rs @@ -194,6 +194,9 @@ extern "C" { #[wasm_bindgen(typescript_type = "[Address, EncryptedKeyFrag]")] pub type VerifiedRevocationOrder; + + #[wasm_bindgen(typescript_type = "[Ciphertext, AuthenticatedData]")] + pub type DkgEncryptionResult; } // @@ -694,6 +697,28 @@ impl AuthenticatedData { } } +// +// Encrypt for dkg +// +#[wasm_bindgen(js_name = "encryptForDkg")] +pub fn encrypt_for_dkg( + data: &[u8], + public_key: &DkgPublicKey, + conditions: &OptionConditions, +) -> Result { + let typed_conditions = try_from_js_option::(conditions)?; + let (ciphertext, auth_data) = nucypher_core::encrypt_for_dkg( + data, + public_key.as_ref(), + typed_conditions.as_ref().map(|conditions| &conditions.0), + ) + .map_err(map_js_err)?; + Ok(into_js_array([ + JsValue::from(Ciphertext::from(ciphertext)), + JsValue::from(AuthenticatedData::from(auth_data)), + ])) +} + // // AccessControlPolicy // diff --git a/nucypher-core/src/access_control.rs b/nucypher-core/src/access_control.rs index 6da6253e..a6a14248 100644 --- a/nucypher-core/src/access_control.rs +++ b/nucypher-core/src/access_control.rs @@ -2,7 +2,8 @@ use alloc::boxed::Box; use alloc::string::String; use alloc::vec::Vec; -use ferveo::api::DkgPublicKey; +use ferveo::api::{encrypt, Ciphertext, DkgPublicKey, SecretBox}; +use ferveo::Error; use serde::{Deserialize, Serialize}; use umbral_pre::serde_bytes; @@ -74,6 +75,21 @@ impl<'a> ProtocolObjectInner<'a> for AuthenticatedData { impl<'a> ProtocolObject<'a> for AuthenticatedData {} +/// Encrypt data based on conditions and dkg public key. +pub fn encrypt_for_dkg( + data: &[u8], + public_key: &DkgPublicKey, + conditions: Option<&Conditions>, +) -> Result<(Ciphertext, AuthenticatedData), Error> { + let auth_data = AuthenticatedData::new(public_key, conditions); + let ciphertext = encrypt( + SecretBox::new(data.to_vec()), + auth_data.aad().as_ref(), + public_key, + )?; + Ok((ciphertext, auth_data)) +} + /// Access control policy data for encrypted data. #[derive(Eq, PartialEq, Debug, Serialize, Deserialize, Clone)] pub struct AccessControlPolicy { diff --git a/nucypher-core/src/lib.rs b/nucypher-core/src/lib.rs index 8d406ec6..da0fb1a6 100644 --- a/nucypher-core/src/lib.rs +++ b/nucypher-core/src/lib.rs @@ -27,7 +27,7 @@ mod versioning; /// Error returned by various `verify()` methods in the crate. pub struct VerificationError; -pub use access_control::{AccessControlPolicy, AuthenticatedData}; +pub use access_control::{encrypt_for_dkg, AccessControlPolicy, AuthenticatedData}; pub use address::Address; pub use conditions::{Conditions, Context}; From 5ed307f57ad6e0c02ae065baac494a4028f8f729 Mon Sep 17 00:00:00 2001 From: derekpierre Date: Tue, 22 Aug 2023 16:05:27 -0400 Subject: [PATCH 10/14] Make ThresholdMessageKit more efficient by not returning the Ciphertext object purely to get the CiphertextHeader. Add decrypt_with_shared_secret to ThresholdMessageKit so that to decrypt data, the ciphertext does not need to be returned to python/js from Rust and then passed back in to Rust to decrypt. Keep it in Rust and just do the decryption internally. --- Cargo.lock | 8 +++--- .../nucypher_core/__init__.pyi | 9 ++++--- nucypher-core-python/src/lib.rs | 15 +++++++++-- nucypher-core-wasm/src/lib.rs | 18 ++++++++++--- nucypher-core-wasm/tests/wasm.rs | 5 +++- nucypher-core/src/threshold_message_kit.rs | 26 +++++++++++++++++-- 6 files changed, 65 insertions(+), 16 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 33c80b0f..b85818c9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -518,7 +518,7 @@ dependencies = [ [[package]] name = "ferveo-common-pre-release" version = "0.1.0" -source = "git+https://github.com/derekpierre/nucypher-ferveo.git?branch=acp#ce7d280c46173297b0d123b54bac6e57e9f9cc36" +source = "git+https://github.com/derekpierre/nucypher-ferveo.git?branch=acp#c3fe68a3214b398db617e687e5244371661a77f7" dependencies = [ "ark-ec", "ark-serialize", @@ -533,7 +533,7 @@ dependencies = [ [[package]] name = "ferveo-pre-release" version = "0.2.1" -source = "git+https://github.com/derekpierre/nucypher-ferveo.git?branch=acp#ce7d280c46173297b0d123b54bac6e57e9f9cc36" +source = "git+https://github.com/derekpierre/nucypher-ferveo.git?branch=acp#c3fe68a3214b398db617e687e5244371661a77f7" dependencies = [ "ark-bls12-381", "ark-ec", @@ -636,7 +636,7 @@ dependencies = [ [[package]] name = "group-threshold-cryptography-pre-release" version = "0.1.0" -source = "git+https://github.com/derekpierre/nucypher-ferveo.git?branch=acp#ce7d280c46173297b0d123b54bac6e57e9f9cc36" +source = "git+https://github.com/derekpierre/nucypher-ferveo.git?branch=acp#c3fe68a3214b398db617e687e5244371661a77f7" dependencies = [ "ark-bls12-381", "ark-ec", @@ -1382,7 +1382,7 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] name = "subproductdomain-pre-release" version = "0.1.0" -source = "git+https://github.com/derekpierre/nucypher-ferveo.git?branch=acp#ce7d280c46173297b0d123b54bac6e57e9f9cc36" +source = "git+https://github.com/derekpierre/nucypher-ferveo.git?branch=acp#c3fe68a3214b398db617e687e5244371661a77f7" dependencies = [ "anyhow", "ark-ec", diff --git a/nucypher-core-python/nucypher_core/__init__.pyi b/nucypher-core-python/nucypher_core/__init__.pyi index e803ea62..a9053296 100644 --- a/nucypher-core-python/nucypher_core/__init__.pyi +++ b/nucypher-core-python/nucypher_core/__init__.pyi @@ -15,7 +15,7 @@ from .ferveo import ( CiphertextHeader, DkgPublicKey, FerveoPublicKey, - FerveoVariant, + FerveoVariant, SharedSecret, ) @@ -484,10 +484,13 @@ class ThresholdMessageKit: def __init__(self, ciphertext: Ciphertext, acp: AccessControlPolicy): ... - ciphertext: Ciphertext - acp: AccessControlPolicy + ciphertext_header: CiphertextHeader + + def decrypt_with_shared_secret(self, shared_secret: SharedSecret): + ... + @staticmethod def from_bytes(data: bytes) -> ThresholdMessageKit: ... diff --git a/nucypher-core-python/src/lib.rs b/nucypher-core-python/src/lib.rs index b03032ba..3790a583 100644 --- a/nucypher-core-python/src/lib.rs +++ b/nucypher-core-python/src/lib.rs @@ -8,6 +8,7 @@ extern crate alloc; use alloc::collections::{BTreeMap, BTreeSet}; use ferveo::bindings_python::{ Ciphertext, CiphertextHeader, DkgPublicKey, FerveoPublicKey, FerveoPythonError, FerveoVariant, + SharedSecret, }; use pyo3::class::basic::CompareOp; use pyo3::exceptions::{PyTypeError, PyValueError}; @@ -881,8 +882,12 @@ impl ThresholdMessageKit { } #[getter] - pub fn ciphertext(&self) -> Ciphertext { - self.backend.ciphertext.clone().into() + pub fn ciphertext_header(&self) -> PyResult { + let header = self + .backend + .ciphertext_header() + .map_err(FerveoPythonError::from)?; + Ok(CiphertextHeader::from(header)) } #[getter] @@ -890,6 +895,12 @@ impl ThresholdMessageKit { self.backend.acp.clone().into() } + pub fn decrypt_with_shared_secret(&self, shared_secret: &SharedSecret) -> PyResult> { + self.backend + .decrypt_with_shared_secret(shared_secret.as_ref()) + .map_err(|err| FerveoPythonError::FerveoError(err).into()) + } + #[staticmethod] pub fn from_bytes(data: &[u8]) -> PyResult { from_bytes::<_, nucypher_core::ThresholdMessageKit>(data) diff --git a/nucypher-core-wasm/src/lib.rs b/nucypher-core-wasm/src/lib.rs index 9e632d77..3b2f840c 100644 --- a/nucypher-core-wasm/src/lib.rs +++ b/nucypher-core-wasm/src/lib.rs @@ -12,7 +12,9 @@ use alloc::{ }; use core::fmt; -use ferveo::bindings_wasm::{Ciphertext, CiphertextHeader, DkgPublicKey, FerveoVariant}; +use ferveo::bindings_wasm::{ + Ciphertext, CiphertextHeader, DkgPublicKey, FerveoVariant, JsResult, SharedSecret, +}; use js_sys::Error; use umbral_pre::bindings_wasm::{ Capsule, PublicKey, RecoverableSignature, SecretKey, Signer, VerifiedCapsuleFrag, @@ -788,9 +790,10 @@ impl ThresholdMessageKit { )) } - #[wasm_bindgen(getter)] - pub fn ciphertext(&self) -> Ciphertext { - self.0.ciphertext.clone().into() + #[wasm_bindgen(getter, js_name=ciphertextHeader)] + pub fn ciphertext_header(&self) -> JsResult { + let header = self.0.ciphertext_header().map_err(map_js_err)?; + Ok(CiphertextHeader::from(header)) } #[wasm_bindgen(getter)] @@ -798,6 +801,13 @@ impl ThresholdMessageKit { self.0.acp.clone().into() } + #[wasm_bindgen(js_name = decryptWithSharedSecret)] + pub fn decrypt_with_shared_secret(&self, shared_secret: &SharedSecret) -> JsResult> { + self.0 + .decrypt_with_shared_secret(shared_secret.as_ref()) + .map_err(map_js_err) + } + #[wasm_bindgen(js_name = toBytes)] pub fn to_bytes(&self) -> Box<[u8]> { to_bytes(self) diff --git a/nucypher-core-wasm/tests/wasm.rs b/nucypher-core-wasm/tests/wasm.rs index c20b9369..459acbfb 100644 --- a/nucypher-core-wasm/tests/wasm.rs +++ b/nucypher-core-wasm/tests/wasm.rs @@ -898,6 +898,9 @@ fn threshold_message_kit() { // mimic serialization/deserialization over the wire let serialized_tmk = tmk.to_bytes(); let deserialized_tmk = ThresholdMessageKit::from_bytes(&serialized_tmk).unwrap(); - assert_eq!(ciphertext, deserialized_tmk.ciphertext()); + assert_eq!( + ciphertext.header().unwrap(), + deserialized_tmk.ciphertext_header().unwrap() + ); assert_eq!(acp, deserialized_tmk.acp()); } diff --git a/nucypher-core/src/threshold_message_kit.rs b/nucypher-core/src/threshold_message_kit.rs index f5439832..d7352336 100644 --- a/nucypher-core/src/threshold_message_kit.rs +++ b/nucypher-core/src/threshold_message_kit.rs @@ -1,7 +1,9 @@ use alloc::boxed::Box; use alloc::string::String; +use alloc::vec::Vec; -use ferveo::api::Ciphertext; +use ferveo::api::{Ciphertext, CiphertextHeader, SharedSecret}; +use ferveo::Error; use serde::{Deserialize, Serialize}; use crate::access_control::AccessControlPolicy; @@ -29,6 +31,23 @@ impl ThresholdMessageKit { acp: acp.clone(), } } + + /// Returns ciphertext header. + pub fn ciphertext_header(&self) -> Result { + self.ciphertext.header() + } + + /// Decrypts encrypted data. + pub fn decrypt_with_shared_secret( + &self, + shared_secret: &SharedSecret, + ) -> Result, Error> { + ferveo::api::decrypt_with_shared_secret( + &self.ciphertext, + self.acp.aad().as_ref(), + shared_secret, + ) + } } impl<'a> ProtocolObjectInner<'a> for ThresholdMessageKit { @@ -81,7 +100,10 @@ mod tests { // mimic serialization/deserialization over the wire let serialized_tmk = tmk.to_bytes(); let deserialized_tmk = ThresholdMessageKit::from_bytes(&serialized_tmk).unwrap(); - assert_eq!(ciphertext, deserialized_tmk.ciphertext); + assert_eq!( + ciphertext.header().unwrap(), + deserialized_tmk.ciphertext_header().unwrap() + ); assert_eq!(acp, deserialized_tmk.acp); } } From 2ba7f5d2e122b3f37d251dd58812fbb2df04b27f Mon Sep 17 00:00:00 2001 From: derekpierre Date: Tue, 22 Aug 2023 16:21:18 -0400 Subject: [PATCH 11/14] Update `nucypher-core` changelog with latest changes. --- CHANGELOG.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 005fcdb4..e83aac8b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,24 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [0.12.0] - Unreleased + +### Changed + +- Modified `ThresholdDecryptionResponse` to use `CiphertextHeader` and `AccessControlPolicy` to utilize encapsulation now provided by `ferveo`. ([#74]) + +### Added + +- Added `ThresholdMessageKit` which is the representation of data encrypted via `ferveo` that utilizes data encapsulation and ephemeral symmetric key. ([#74]) +- Added `AccessControlPolicy` which contains access metadata (conditions, public key, authorization etc.) which forms part of the `ThresholdMessageKit`. ([#74]) +- Added `AuthenticatedData` which forms part of the `AccessControlPolicy` and is needed to ensure that the aad is consistent during encryption process and during decryption process. ([#74]) +- Added `encrypt_for_dkg` method for generation of `ferveo` `Ciphertext` and `AuthenticatedData`. ([#74]) + + +[#74]: https://github.com/nucypher/nucypher-core/pull/74 + + ## [0.11.0] - 2023-08-01 ### Changed From 89031a0f1dd3f4e9649ea54c22d55f4010386069 Mon Sep 17 00:00:00 2001 From: derekpierre Date: Tue, 22 Aug 2023 20:04:07 -0400 Subject: [PATCH 12/14] Fix mypy build error. Related to this https://pyo3.rs/main/function/signature#trailing-optional-arguments. --- nucypher-core-python/src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/nucypher-core-python/src/lib.rs b/nucypher-core-python/src/lib.rs index 3790a583..dda04728 100644 --- a/nucypher-core-python/src/lib.rs +++ b/nucypher-core-python/src/lib.rs @@ -793,6 +793,7 @@ impl AuthenticatedData { // Encrypt for DKG. // #[pyfunction] +#[pyo3(signature = (data, public_key, conditions))] pub fn encrypt_for_dkg( data: &[u8], public_key: &DkgPublicKey, From 54d155a9987bede354762a7349a6c664fc231c5f Mon Sep 17 00:00:00 2001 From: derekpierre Date: Wed, 23 Aug 2023 07:31:15 -0400 Subject: [PATCH 13/14] Use to_bytes macro for dkg wasm bindings. --- nucypher-core-wasm/src/lib.rs | 42 ++++++----------------------------- 1 file changed, 7 insertions(+), 35 deletions(-) diff --git a/nucypher-core-wasm/src/lib.rs b/nucypher-core-wasm/src/lib.rs index 3b2f840c..09c8d8d8 100644 --- a/nucypher-core-wasm/src/lib.rs +++ b/nucypher-core-wasm/src/lib.rs @@ -662,6 +662,7 @@ impl SessionSecretFactory { pub struct AuthenticatedData(nucypher_core::AuthenticatedData); generate_from_bytes!(AuthenticatedData); +generate_to_bytes!(AuthenticatedData); generate_equals!(AuthenticatedData); #[wasm_bindgen] @@ -692,11 +693,6 @@ impl AuthenticatedData { pub fn conditions(&self) -> Option { self.0.conditions.clone().map(Conditions) } - - #[wasm_bindgen(js_name = toBytes)] - pub fn to_bytes(&self) -> Box<[u8]> { - to_bytes(self) - } } // @@ -730,6 +726,7 @@ pub fn encrypt_for_dkg( pub struct AccessControlPolicy(nucypher_core::AccessControlPolicy); generate_from_bytes!(AccessControlPolicy); +generate_to_bytes!(AccessControlPolicy); generate_equals!(AccessControlPolicy); #[wasm_bindgen] @@ -763,11 +760,6 @@ impl AccessControlPolicy { pub fn conditions(&self) -> Option { self.0.auth_data.conditions.clone().map(Conditions) } - - #[wasm_bindgen(js_name = toBytes)] - pub fn to_bytes(&self) -> Box<[u8]> { - to_bytes(self) - } } // @@ -778,6 +770,7 @@ impl AccessControlPolicy { pub struct ThresholdMessageKit(nucypher_core::ThresholdMessageKit); generate_from_bytes!(ThresholdMessageKit); +generate_to_bytes!(ThresholdMessageKit); generate_equals!(ThresholdMessageKit); #[wasm_bindgen] @@ -807,11 +800,6 @@ impl ThresholdMessageKit { .decrypt_with_shared_secret(shared_secret.as_ref()) .map_err(map_js_err) } - - #[wasm_bindgen(js_name = toBytes)] - pub fn to_bytes(&self) -> Box<[u8]> { - to_bytes(self) - } } // @@ -823,6 +811,7 @@ impl ThresholdMessageKit { pub struct ThresholdDecryptionRequest(nucypher_core::ThresholdDecryptionRequest); generate_from_bytes!(ThresholdDecryptionRequest); +generate_to_bytes!(ThresholdDecryptionRequest); generate_equals!(ThresholdDecryptionRequest); #[wasm_bindgen] @@ -876,11 +865,6 @@ impl ThresholdDecryptionRequest { .encrypt(shared_secret.as_ref(), requester_public_key.as_ref()), ) } - - #[wasm_bindgen(js_name = toBytes)] - pub fn to_bytes(&self) -> Box<[u8]> { - to_bytes(self) - } } // @@ -892,6 +876,7 @@ impl ThresholdDecryptionRequest { pub struct EncryptedThresholdDecryptionRequest(nucypher_core::EncryptedThresholdDecryptionRequest); generate_from_bytes!(EncryptedThresholdDecryptionRequest); +generate_to_bytes!(EncryptedThresholdDecryptionRequest); #[wasm_bindgen] impl EncryptedThresholdDecryptionRequest { @@ -914,11 +899,6 @@ impl EncryptedThresholdDecryptionRequest { .map_err(map_js_err) .map(ThresholdDecryptionRequest) } - - #[wasm_bindgen(js_name = toBytes)] - pub fn to_bytes(&self) -> Box<[u8]> { - to_bytes(self) - } } // @@ -930,6 +910,7 @@ impl EncryptedThresholdDecryptionRequest { pub struct ThresholdDecryptionResponse(nucypher_core::ThresholdDecryptionResponse); generate_from_bytes!(ThresholdDecryptionResponse); +generate_to_bytes!(ThresholdDecryptionResponse); #[wasm_bindgen] impl ThresholdDecryptionResponse { @@ -960,11 +941,6 @@ impl ThresholdDecryptionResponse { ) -> EncryptedThresholdDecryptionResponse { EncryptedThresholdDecryptionResponse(self.0.encrypt(shared_secret.as_ref())) } - - #[wasm_bindgen(js_name = toBytes)] - pub fn to_bytes(&self) -> Box<[u8]> { - to_bytes(self) - } } // @@ -978,6 +954,7 @@ pub struct EncryptedThresholdDecryptionResponse( ); generate_from_bytes!(EncryptedThresholdDecryptionResponse); +generate_to_bytes!(EncryptedThresholdDecryptionResponse); #[wasm_bindgen] impl EncryptedThresholdDecryptionResponse { @@ -995,11 +972,6 @@ impl EncryptedThresholdDecryptionResponse { .map_err(map_js_err) .map(ThresholdDecryptionResponse) } - - #[wasm_bindgen(js_name = toBytes)] - pub fn to_bytes(&self) -> Box<[u8]> { - to_bytes(self) - } } // From ef2568daedda906d13c917c7d0a9fb4353a0feea Mon Sep 17 00:00:00 2001 From: derekpierre Date: Wed, 23 Aug 2023 07:32:02 -0400 Subject: [PATCH 14/14] Use equals from DkgPublicKey directly for AuthenticatedData equals. --- nucypher-core/src/access_control.rs | 3 +-- nucypher-core/src/threshold_message_kit.rs | 2 -- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/nucypher-core/src/access_control.rs b/nucypher-core/src/access_control.rs index a6a14248..989ac521 100644 --- a/nucypher-core/src/access_control.rs +++ b/nucypher-core/src/access_control.rs @@ -44,8 +44,7 @@ impl AuthenticatedData { impl PartialEq for AuthenticatedData { fn eq(&self, other: &Self) -> bool { - self.public_key.to_bytes().unwrap() == other.public_key.to_bytes().unwrap() - && self.conditions == other.conditions + self.public_key == other.public_key && self.conditions == other.conditions } } diff --git a/nucypher-core/src/threshold_message_kit.rs b/nucypher-core/src/threshold_message_kit.rs index d7352336..3ed6b53b 100644 --- a/nucypher-core/src/threshold_message_kit.rs +++ b/nucypher-core/src/threshold_message_kit.rs @@ -11,8 +11,6 @@ use crate::versioning::{ messagepack_deserialize, messagepack_serialize, ProtocolObject, ProtocolObjectInner, }; -// TODO should this be in umbral? - /// Access control metadata for encrypted data. #[derive(PartialEq, Eq, Debug, Serialize, Deserialize, Clone)] pub struct ThresholdMessageKit {