diff --git a/CHANGELOG.md b/CHANGELOG.md index c2ea7459..e45575e7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,11 +4,20 @@ 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.13.0] - Unreleased + +### Changed + +- `Conditions` should not be optional as it pertains to `AuthenticatedData` and `AccessControlPolicy` types since conditions-based decryption (CBD) requires conditions. ([#80]) + +[#80]: https://github.com/nucypher/nucypher-core/pull/78 + + ## [0.12.0] - 2023-08-28 ### Changed -- Modified `ThresholdDecryptionResponse` to use `CiphertextHeader` and `AccessControlPolicy` to utilize encapsulation now provided by `ferveo`. ([#74]) +- Modified `ThresholdDecryptionRequest` to use `CiphertextHeader` and `AccessControlPolicy` to utilize encapsulation now provided by `ferveo`. ([#74]) ### Added diff --git a/Cargo.lock b/Cargo.lock index 75f2af8a..1a911a64 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -869,7 +869,7 @@ checksum = "94c7128ba23c81f6471141b90f17654f89ef44a56e14b8a4dd0fddfccd655277" [[package]] name = "nucypher-core" -version = "0.11.0" +version = "0.12.0" dependencies = [ "chacha20poly1305", "ferveo-pre-release", @@ -891,7 +891,7 @@ dependencies = [ [[package]] name = "nucypher-core-python" -version = "0.11.0" +version = "0.12.0" dependencies = [ "derive_more", "ferveo-pre-release", @@ -903,7 +903,7 @@ dependencies = [ [[package]] name = "nucypher-core-wasm" -version = "0.11.0" +version = "0.12.0" dependencies = [ "console_error_panic_hook", "derive_more", diff --git a/nucypher-core-python/nucypher_core/__init__.pyi b/nucypher-core-python/nucypher_core/__init__.pyi index a9053296..c9f6968c 100644 --- a/nucypher-core-python/nucypher_core/__init__.pyi +++ b/nucypher-core-python/nucypher_core/__init__.pyi @@ -434,12 +434,12 @@ class MetadataResponse: @final class AuthenticatedData: - def __init__(self, public_key: DkgPublicKey, conditions: Optional[Conditions]): + def __init__(self, public_key: DkgPublicKey, conditions: Conditions): ... public_key: DkgPublicKey - conditions: Optional[Conditions] + conditions: Conditions def aad(self) -> bytes: ... @@ -452,7 +452,7 @@ class AuthenticatedData: ... -def encrypt_for_dkg(data: bytes, public_key: DkgPublicKey, conditions: Optional[Conditions]) -> Tuple[Ciphertext, AuthenticatedData]: +def encrypt_for_dkg(data: bytes, public_key: DkgPublicKey, conditions: Conditions) -> Tuple[Ciphertext, AuthenticatedData]: ... @@ -464,7 +464,7 @@ class AccessControlPolicy: public_key: DkgPublicKey - conditions: Optional[Conditions] + conditions: Conditions authorization: bytes diff --git a/nucypher-core-python/src/lib.rs b/nucypher-core-python/src/lib.rs index bc14505a..4f545517 100644 --- a/nucypher-core-python/src/lib.rs +++ b/nucypher-core-python/src/lib.rs @@ -100,6 +100,7 @@ impl Address { } #[pyclass(module = "nucypher_core")] +#[derive(derive_more::From, derive_more::AsRef)] pub struct Conditions { backend: nucypher_core::Conditions, } @@ -747,13 +748,11 @@ pub struct AuthenticatedData { #[pymethods] impl AuthenticatedData { #[new] - pub fn new(public_key: &DkgPublicKey, conditions: Option<&Conditions>) -> Self { + pub fn new(public_key: &DkgPublicKey, conditions: &Conditions) -> Self { Self { backend: nucypher_core::AuthenticatedData::new( public_key.as_ref(), - conditions - .map(|conditions| conditions.backend.clone()) - .as_ref(), + conditions.as_ref(), ), } } @@ -772,13 +771,8 @@ impl AuthenticatedData { } #[getter] - pub fn conditions(&self) -> Option { - self.backend - .conditions - .clone() - .map(|conditions| Conditions { - backend: conditions, - }) + pub fn conditions(&self) -> Conditions { + self.backend.conditions.clone().into() } #[staticmethod] @@ -799,16 +793,11 @@ impl AuthenticatedData { pub fn encrypt_for_dkg( data: &[u8], public_key: &DkgPublicKey, - conditions: Option<&Conditions>, + conditions: &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)?; + let (ciphertext, auth_data) = + nucypher_core::encrypt_for_dkg(data, public_key.as_ref(), conditions.as_ref()) + .map_err(FerveoPythonError::FerveoError)?; Ok((ciphertext.into(), auth_data.into())) } @@ -844,14 +833,8 @@ impl AccessControlPolicy { } #[getter] - pub fn conditions(&self) -> Option { - self.backend - .auth_data - .conditions - .clone() - .map(|conditions| Conditions { - backend: conditions, - }) + pub fn conditions(&self) -> Conditions { + self.backend.auth_data.conditions.clone().into() } #[getter] diff --git a/nucypher-core-wasm/src/lib.rs b/nucypher-core-wasm/src/lib.rs index c536356a..64c892c6 100644 --- a/nucypher-core-wasm/src/lib.rs +++ b/nucypher-core-wasm/src/lib.rs @@ -205,7 +205,7 @@ extern "C" { // Conditions // -#[derive(Clone, TryFromJsValue)] +#[derive(Clone, TryFromJsValue, derive_more::From, derive_more::AsRef)] #[wasm_bindgen] pub struct Conditions(nucypher_core::Conditions); @@ -670,13 +670,11 @@ impl AuthenticatedData { #[wasm_bindgen(constructor)] pub fn new( public_key: &DkgPublicKey, - conditions: &OptionConditions, + conditions: &Conditions, ) -> 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), + conditions.as_ref(), ))) } @@ -686,12 +684,12 @@ impl AuthenticatedData { #[wasm_bindgen(getter, js_name = publicKey)] pub fn public_key(&self) -> DkgPublicKey { - DkgPublicKey::from(self.0.public_key) + self.0.public_key.into() } #[wasm_bindgen(getter)] - pub fn conditions(&self) -> Option { - self.0.conditions.clone().map(Conditions) + pub fn conditions(&self) -> Conditions { + self.0.conditions.clone().into() } } @@ -702,15 +700,11 @@ impl AuthenticatedData { pub fn encrypt_for_dkg( data: &[u8], public_key: &DkgPublicKey, - conditions: &OptionConditions, + conditions: &Conditions, ) -> 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)?; + let (ciphertext, auth_data) = + nucypher_core::encrypt_for_dkg(data, public_key.as_ref(), conditions.as_ref()) + .map_err(map_js_err)?; Ok(into_js_array([ JsValue::from(Ciphertext::from(ciphertext)), JsValue::from(AuthenticatedData::from(auth_data)), @@ -748,7 +742,7 @@ impl AccessControlPolicy { #[wasm_bindgen(getter, js_name = publicKey)] pub fn public_key(&self) -> DkgPublicKey { - DkgPublicKey::from(self.0.auth_data.public_key) + self.0.auth_data.public_key.into() } #[wasm_bindgen(getter)] @@ -757,8 +751,8 @@ impl AccessControlPolicy { } #[wasm_bindgen(getter)] - pub fn conditions(&self) -> Option { - self.0.auth_data.conditions.clone().map(Conditions) + pub fn conditions(&self) -> Conditions { + self.0.auth_data.conditions.clone().into() } } diff --git a/nucypher-core-wasm/tests/wasm.rs b/nucypher-core-wasm/tests/wasm.rs index a149b883..3df9227c 100644 --- a/nucypher-core-wasm/tests/wasm.rs +++ b/nucypher-core-wasm/tests/wasm.rs @@ -694,15 +694,12 @@ fn threshold_decryption_request() { let requester_secret = SessionStaticSecret::random(); - let conditions = "{'some': 'condition'}"; - let conditions_js: JsValue = Some(Conditions::new(conditions)).into(); + let conditions = Conditions::new("{'some': 'condition'}"); 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 auth_data = AuthenticatedData::new(&dkg_pk, &conditions).unwrap(); let authorization = b"we_dont_need_no_stinking_badges"; let acp = AccessControlPolicy::new(&auth_data, authorization).unwrap(); @@ -801,21 +798,18 @@ fn threshold_decryption_response() { fn authenticated_data() { let dkg_pk = DkgPublicKey::random(); - let conditions = "{'some': 'condition'}"; - let conditions_js: JsValue = Some(Conditions::new(conditions)).into(); + let conditions = Conditions::new("{'some': 'condition'}"); - let auth_data = - AuthenticatedData::new(&dkg_pk, &conditions_js.unchecked_into::()) - .unwrap(); + let auth_data = AuthenticatedData::new(&dkg_pk, &conditions).unwrap(); assert_eq!( auth_data.public_key().to_bytes().unwrap(), dkg_pk.to_bytes().unwrap() ); - assert_eq!(auth_data.conditions().unwrap().to_string(), conditions); + assert!(auth_data.conditions().equals(&conditions)); let mut expected_aad = dkg_pk.to_bytes().unwrap().to_vec(); - expected_aad.extend(conditions.as_bytes()); + expected_aad.extend(conditions.to_string().as_bytes()); assert_eq!(auth_data.aad().unwrap(), expected_aad.into_boxed_slice()); @@ -826,22 +820,16 @@ fn authenticated_data() { deserialized_auth_data.public_key().to_bytes().unwrap(), dkg_pk.to_bytes().unwrap() ); - assert_eq!( - deserialized_auth_data.conditions().unwrap().to_string(), - conditions, - ); + assert!(deserialized_auth_data.conditions().equals(&conditions)); } #[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 conditions = Conditions::new("{'some': 'condition'}"); - let auth_data = - AuthenticatedData::new(&dkg_pk, &conditions_js.unchecked_into::()) - .unwrap(); + let auth_data = AuthenticatedData::new(&dkg_pk, &conditions).unwrap(); let authorization = b"we_dont_need_no_stinking_badges"; let acp = AccessControlPolicy::new(&auth_data, authorization).unwrap(); @@ -854,7 +842,7 @@ fn access_control_policy() { authorization.to_vec().into_boxed_slice(), acp.authorization() ); - assert_eq!(conditions, acp.conditions().unwrap().to_string()); + assert!(acp.conditions().equals(&conditions)); // mimic serialization/deserialization over the wire let serialized_acp = acp.to_bytes(); @@ -867,10 +855,7 @@ fn access_control_policy() { authorization.to_vec().into_boxed_slice(), deserialized_acp.authorization() ); - assert_eq!( - conditions, - deserialized_acp.conditions().unwrap().to_string() - ); + assert!(deserialized_acp.conditions().equals(&conditions)); // check aad; expected acp and auth_data acps to be the same assert_eq!(deserialized_acp.aad(), auth_data.aad()); @@ -878,14 +863,11 @@ fn access_control_policy() { #[wasm_bindgen_test] fn threshold_message_kit() { - let conditions = "{'some': 'condition'}"; - let conditions_js: JsValue = Some(Conditions::new(conditions)).into(); + let conditions = Conditions::new("{'some': 'condition'}"); let dkg_pk = DkgPublicKey::random(); - let auth_data = - AuthenticatedData::new(&dkg_pk, &conditions_js.unchecked_into::()) - .unwrap(); + let auth_data = AuthenticatedData::new(&dkg_pk, &conditions).unwrap(); let authorization = b"we_dont_need_no_stinking_badges"; let acp = AccessControlPolicy::new(&auth_data, authorization).unwrap(); diff --git a/nucypher-core/src/access_control.rs b/nucypher-core/src/access_control.rs index 28e6af25..90d166d9 100644 --- a/nucypher-core/src/access_control.rs +++ b/nucypher-core/src/access_control.rs @@ -18,17 +18,17 @@ pub struct AuthenticatedData { pub public_key: DkgPublicKey, /// The conditions associated with the encrypted data - pub conditions: Option, + pub conditions: Conditions, } impl Eq for AuthenticatedData {} impl AuthenticatedData { /// Creates a new access control policy. - pub fn new(public_key: &DkgPublicKey, conditions: Option<&Conditions>) -> Self { + pub fn new(public_key: &DkgPublicKey, conditions: &Conditions) -> Self { AuthenticatedData { public_key: *public_key, - conditions: conditions.cloned(), + conditions: conditions.clone(), } } @@ -36,11 +36,7 @@ impl AuthenticatedData { pub fn aad(&self) -> Result, Error> { Ok([ self.public_key.to_bytes()?.to_vec(), - self.conditions - .as_ref() - .map(|c| c.as_ref().as_bytes()) - .unwrap_or_default() - .to_vec(), + self.conditions.as_ref().as_bytes().to_vec(), ] .concat() .into_boxed_slice()) @@ -75,7 +71,7 @@ impl<'a> ProtocolObject<'a> for AuthenticatedData {} pub fn encrypt_for_dkg( data: &[u8], public_key: &DkgPublicKey, - conditions: Option<&Conditions>, + conditions: &Conditions, ) -> Result<(Ciphertext, AuthenticatedData), Error> { let auth_data = AuthenticatedData::new(public_key, conditions); let ciphertext = encrypt( @@ -117,7 +113,7 @@ impl AccessControlPolicy { } /// Return the conditions - pub fn conditions(&self) -> Option { + pub fn conditions(&self) -> Conditions { self.auth_data.conditions.clone() } } @@ -159,7 +155,7 @@ mod tests { let dkg_pk = DkgPublicKey::random(); let conditions = Conditions::new("abcd"); - let auth_data = AuthenticatedData::new(&dkg_pk, Some(&conditions)); + let auth_data = AuthenticatedData::new(&dkg_pk, &conditions); // check aad for auth data; expected to be dkg public key + conditions let mut expected_aad = dkg_pk.to_bytes().unwrap().to_vec(); @@ -168,9 +164,9 @@ mod tests { 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)); + assert_eq!(auth_data.conditions, conditions); - let auth_data_2 = AuthenticatedData::new(&dkg_pk, Some(&Conditions::new("abcd"))); + let auth_data_2 = AuthenticatedData::new(&dkg_pk, &conditions); assert_eq!(auth_data, auth_data_2); // mimic serialization/deserialization over the wire @@ -185,7 +181,7 @@ mod tests { let dkg_pk = DkgPublicKey::random(); let conditions = Conditions::new("abcd"); - let auth_data = AuthenticatedData::new(&dkg_pk, Some(&conditions)); + let auth_data = AuthenticatedData::new(&dkg_pk, &conditions); let authorization = b"we_dont_need_no_stinking_badges"; let acp = AccessControlPolicy::new(&auth_data, authorization); diff --git a/nucypher-core/src/dkg.rs b/nucypher-core/src/dkg.rs index ebc49244..43fd127b 100644 --- a/nucypher-core/src/dkg.rs +++ b/nucypher-core/src/dkg.rs @@ -730,7 +730,7 @@ 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 auth_data = AuthenticatedData::new(&dkg_pk, &Conditions::new("abcd")); let authorization = b"self_authorization"; let acp = AccessControlPolicy::new(&auth_data, authorization); diff --git a/nucypher-core/src/threshold_message_kit.rs b/nucypher-core/src/threshold_message_kit.rs index 3f4adfec..78560167 100644 --- a/nucypher-core/src/threshold_message_kit.rs +++ b/nucypher-core/src/threshold_message_kit.rs @@ -88,7 +88,7 @@ mod tests { let authorization = b"we_dont_need_no_stinking_badges"; let acp = AccessControlPolicy::new( - &AuthenticatedData::new(&dkg_pk, Some(&Conditions::new("abcd"))), + &AuthenticatedData::new(&dkg_pk, &Conditions::new("abcd")), authorization, );