From a5cfd82094560b975a70e896c52dcca7debb1ef7 Mon Sep 17 00:00:00 2001 From: derekpierre Date: Fri, 18 Aug 2023 15:09:21 -0400 Subject: [PATCH] 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); } }