From 97882c0e92409f91988972ecdc07d3e527798039 Mon Sep 17 00:00:00 2001 From: derekpierre Date: Fri, 26 May 2023 10:27:41 -0400 Subject: [PATCH 01/15] Initial work to use Curve 25519 instead of Umbral for decryption request/response encryption. E2EThresholdDecryptionRequest object is no longer needed as an intermediary since a shared secret key is created/used. --- Cargo.lock | 60 ++++ nucypher-core-python/Cargo.toml | 1 + .../nucypher_core/__init__.py | 1 - .../nucypher_core/__init__.pyi | 16 +- nucypher-core-python/src/lib.rs | 65 ++-- nucypher-core-wasm/Cargo.toml | 2 + nucypher-core-wasm/src/lib.rs | 76 ++--- nucypher-core-wasm/tests/wasm.rs | 73 +++-- nucypher-core/Cargo.toml | 5 + nucypher-core/src/dkg.rs | 310 +++++++++++------- nucypher-core/src/lib.rs | 5 +- 11 files changed, 360 insertions(+), 254 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 231d33d5..2510af9b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -336,6 +336,20 @@ dependencies = [ "typenum", ] +[[package]] +name = "curve25519-dalek" +version = "4.0.0-rc.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03d928d978dbec61a1167414f5ec534f24bea0d7a0d24dd9b6233d3d8223e585" +dependencies = [ + "cfg-if", + "fiat-crypto", + "packed_simd_2", + "platforms", + "subtle", + "zeroize", +] + [[package]] name = "darling" version = "0.13.4" @@ -549,6 +563,12 @@ dependencies = [ "subtle", ] +[[package]] +name = "fiat-crypto" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e825f6987101665dea6ec934c09ec6d721de7bc1bf92248e1d5810c8cd636b77" + [[package]] name = "fnv" version = "1.0.7" @@ -786,6 +806,12 @@ version = "0.2.144" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b00cc1c228a6782d0f076e7b232802e0c5689d41bb5df366f2a6b6621cfdfe1" +[[package]] +name = "libm" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fc7aa29613bd6a620df431842069224d8bc9011086b1db4c0e0cd47fa03ec9a" + [[package]] name = "lock_api" version = "0.4.9" @@ -831,14 +857,17 @@ checksum = "94c7128ba23c81f6471141b90f17654f89ef44a56e14b8a4dd0fddfccd655277" name = "nucypher-core" version = "0.8.0" dependencies = [ + "chacha20poly1305", "ferveo-pre-release", "generic-array", "hex", + "rand_core", "rmp-serde", "serde", "serde_with 1.14.0", "sha3", "umbral-pre", + "x25519-dalek", ] [[package]] @@ -851,6 +880,7 @@ dependencies = [ "pyo3", "pyo3-build-config 0.19.0", "umbral-pre", + "x25519-dalek", ] [[package]] @@ -862,10 +892,12 @@ dependencies = [ "ferveo-pre-release", "js-sys", "nucypher-core", + "rand_core", "umbral-pre", "wasm-bindgen", "wasm-bindgen-derive", "wasm-bindgen-test", + "x25519-dalek", ] [[package]] @@ -910,6 +942,16 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" +[[package]] +name = "packed_simd_2" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1914cd452d8fccd6f9db48147b29fd4ae05bea9dc5d9ad578509f72415de282" +dependencies = [ + "cfg-if", + "libm", +] + [[package]] name = "parking_lot" version = "0.12.1" @@ -939,6 +981,12 @@ version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f746c4065a8fa3fe23974dd82f15431cc8d40779821001404d10d2e79ca7d79" +[[package]] +name = "platforms" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3d7ddaed09e0eb771a79ab0fd64609ba0afb0a8366421957936ad14cbd13630" + [[package]] name = "poly1305" version = "0.8.0" @@ -1777,6 +1825,18 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" +[[package]] +name = "x25519-dalek" +version = "2.0.0-rc.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fabd6e16dd08033932fc3265ad4510cc2eab24656058a6dcb107ffe274abcc95" +dependencies = [ + "curve25519-dalek", + "rand_core", + "serde", + "zeroize", +] + [[package]] name = "zeroize" version = "1.6.0" diff --git a/nucypher-core-python/Cargo.toml b/nucypher-core-python/Cargo.toml index 7372d359..99a6f0df 100644 --- a/nucypher-core-python/Cargo.toml +++ b/nucypher-core-python/Cargo.toml @@ -13,6 +13,7 @@ nucypher-core = { path = "../nucypher-core" } umbral-pre = { version = "0.10.0", features = ["bindings-python"] } ferveo = { package = "ferveo-pre-release", version = "0.1.0-alpha.8", features = ["bindings-python"] } derive_more = { version = "0.99", default-features = false, features = ["from", "as_ref"] } +x25519-dalek = "2.0.0-rc.2" [build-dependencies] pyo3-build-config = "*" diff --git a/nucypher-core-python/nucypher_core/__init__.py b/nucypher-core-python/nucypher_core/__init__.py index 501c856f..4f37ec29 100644 --- a/nucypher-core-python/nucypher_core/__init__.py +++ b/nucypher-core-python/nucypher_core/__init__.py @@ -18,7 +18,6 @@ MetadataResponse, MetadataResponsePayload, ThresholdDecryptionRequest, - E2EThresholdDecryptionRequest, ThresholdDecryptionResponse, EncryptedThresholdDecryptionRequest, EncryptedThresholdDecryptionResponse, diff --git a/nucypher-core-python/nucypher_core/__init__.pyi b/nucypher-core-python/nucypher_core/__init__.pyi index fb2d3332..88c9443f 100644 --- a/nucypher-core-python/nucypher_core/__init__.pyi +++ b/nucypher-core-python/nucypher_core/__init__.pyi @@ -434,27 +434,13 @@ class ThresholdDecryptionRequest: ... -class E2EThresholdDecryptionRequest: - - decryption_request: ThresholdDecryptionRequest - - response_encrypting_key: PublicKey - - @staticmethod - def from_bytes(data: bytes) -> E2EThresholdDecryptionRequest: - ... - - def __bytes__(self) -> bytes: - ... - - class EncryptedThresholdDecryptionRequest: ritual_id: int def decrypt( self, sk: SecretKey - ) -> E2EThresholdDecryptionRequest: + ) -> ThresholdDecryptionRequest: ... @staticmethod diff --git a/nucypher-core-python/src/lib.rs b/nucypher-core-python/src/lib.rs index 7aab285d..604742b6 100644 --- a/nucypher-core-python/src/lib.rs +++ b/nucypher-core-python/src/lib.rs @@ -636,6 +636,14 @@ impl ReencryptionResponse { // Threshold Decryption Request // +#[pyclass(module = "nucypher_core")] +#[derive(derive_more::From, derive_more::AsRef)] +pub struct SharedSecret(x25519_dalek::SharedSecret); + +#[pyclass(module = "nucypher_core")] +#[derive(derive_more::From, derive_more::AsRef)] +pub struct RequesterPublicKey(x25519_dalek::PublicKey); + #[pyclass(module = "nucypher_core")] #[derive(derive_more::From, derive_more::AsRef)] pub struct ThresholdDecryptionRequest { @@ -713,14 +721,13 @@ impl ThresholdDecryptionRequest { pub fn encrypt( &self, - request_encrypting_key: &PublicKey, - response_encrypting_key: &PublicKey, + shared_secret: &SharedSecret, + requester_public_key: &RequesterPublicKey, ) -> EncryptedThresholdDecryptionRequest { EncryptedThresholdDecryptionRequest { - backend: self.backend.encrypt( - request_encrypting_key.as_ref(), - response_encrypting_key.as_ref(), - ), + backend: self + .backend + .encrypt(shared_secret.as_ref(), requester_public_key.as_ref()), } } @@ -734,37 +741,6 @@ impl ThresholdDecryptionRequest { } } -// -// E2EThresholdDecryptionRequest -// -#[pyclass(module = "nucypher_core")] -#[derive(derive_more::From, derive_more::AsRef)] -pub struct E2EThresholdDecryptionRequest { - backend: nucypher_core::E2EThresholdDecryptionRequest, -} - -#[pymethods] -impl E2EThresholdDecryptionRequest { - #[getter] - pub fn decryption_request(&self) -> ThresholdDecryptionRequest { - self.backend.decryption_request.clone().into() - } - - #[getter] - pub fn response_encrypting_key(&self) -> PublicKey { - self.backend.response_encrypting_key.into() - } - - #[staticmethod] - pub fn from_bytes(data: &[u8]) -> PyResult { - from_bytes::<_, nucypher_core::E2EThresholdDecryptionRequest>(data) - } - - fn __bytes__(&self) -> PyObject { - to_bytes(self) - } -} - // // EncryptedThresholdDecryptionRequest // @@ -782,10 +758,10 @@ impl EncryptedThresholdDecryptionRequest { self.backend.ritual_id } - pub fn decrypt(&self, sk: &SecretKey) -> PyResult { + pub fn decrypt(&self, shared_secret: &SharedSecret) -> PyResult { self.backend - .decrypt(sk.as_ref()) - .map(E2EThresholdDecryptionRequest::from) + .decrypt(shared_secret.as_ref()) + .map(ThresholdDecryptionRequest::from) .map_err(|err| PyValueError::new_err(format!("{}", err))) } @@ -823,9 +799,9 @@ impl ThresholdDecryptionResponse { self.backend.decryption_share.as_ref() } - pub fn encrypt(&self, encrypting_key: &PublicKey) -> EncryptedThresholdDecryptionResponse { + pub fn encrypt(&self, shared_secret: &SharedSecret) -> EncryptedThresholdDecryptionResponse { EncryptedThresholdDecryptionResponse { - backend: self.backend.encrypt(encrypting_key.as_ref()), + backend: self.backend.encrypt(shared_secret.as_ref()), } } @@ -851,9 +827,9 @@ pub struct EncryptedThresholdDecryptionResponse { #[pymethods] impl EncryptedThresholdDecryptionResponse { - pub fn decrypt(&self, sk: &SecretKey) -> PyResult { + pub fn decrypt(&self, shared_secret: &SharedSecret) -> PyResult { self.backend - .decrypt(sk.as_ref()) + .decrypt(shared_secret.as_ref()) .map(ThresholdDecryptionResponse::from) .map_err(|err| PyValueError::new_err(format!("{}", err))) } @@ -1334,7 +1310,6 @@ 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::()?; core_module.add_class::()?; diff --git a/nucypher-core-wasm/Cargo.toml b/nucypher-core-wasm/Cargo.toml index 1ebb0814..cfbbb477 100644 --- a/nucypher-core-wasm/Cargo.toml +++ b/nucypher-core-wasm/Cargo.toml @@ -27,7 +27,9 @@ js-sys = "0.3.63" console_error_panic_hook = { version = "0.1", optional = true } derive_more = { version = "0.99", default-features = false, features = ["from", "as_ref"] } wasm-bindgen-derive = "0.2.1" +x25519-dalek = "2.0.0-rc.2" [dev-dependencies] console_error_panic_hook = "0.1" wasm-bindgen-test = "0.3.36" +rand_core = "0.6.4" diff --git a/nucypher-core-wasm/src/lib.rs b/nucypher-core-wasm/src/lib.rs index fe6cb1a4..c0e16b3f 100644 --- a/nucypher-core-wasm/src/lib.rs +++ b/nucypher-core-wasm/src/lib.rs @@ -542,6 +542,14 @@ impl EncryptedTreasureMap { } } +#[wasm_bindgen] +#[derive(derive_more::From, derive_more::AsRef)] +pub struct SharedSecret(x25519_dalek::SharedSecret); + +#[wasm_bindgen] +#[derive(derive_more::From, derive_more::AsRef)] +pub struct RequesterPublicKey(x25519_dalek::PublicKey); + // // Threshold Decryption Request // @@ -598,13 +606,13 @@ impl ThresholdDecryptionRequest { pub fn encrypt( &self, - request_encrypting_key: &PublicKey, - response_encrypting_key: &PublicKey, + shared_secret: &SharedSecret, + requester_public_key: &RequesterPublicKey, ) -> EncryptedThresholdDecryptionRequest { - EncryptedThresholdDecryptionRequest(self.0.encrypt( - request_encrypting_key.as_ref(), - response_encrypting_key.as_ref(), - )) + EncryptedThresholdDecryptionRequest( + self.0 + .encrypt(shared_secret.as_ref(), requester_public_key.as_ref()), + ) } #[wasm_bindgen(js_name = fromBytes)] @@ -618,37 +626,6 @@ impl ThresholdDecryptionRequest { } } -// -// E2EThresholdDecryptionRequest -// - -#[wasm_bindgen] -#[derive(PartialEq, Debug, derive_more::From, derive_more::AsRef)] -pub struct E2EThresholdDecryptionRequest(nucypher_core::E2EThresholdDecryptionRequest); - -#[wasm_bindgen] -impl E2EThresholdDecryptionRequest { - #[wasm_bindgen(js_name = fromBytes)] - pub fn from_bytes(data: &[u8]) -> Result { - from_bytes::<_, nucypher_core::E2EThresholdDecryptionRequest>(data) - } - - #[wasm_bindgen(js_name = toBytes)] - pub fn to_bytes(&self) -> Box<[u8]> { - to_bytes(self) - } - - #[wasm_bindgen(getter, js_name = decryptionRequest)] - pub fn decryption_request(&self) -> ThresholdDecryptionRequest { - ThresholdDecryptionRequest::from(self.0.decryption_request.clone()) - } - - #[wasm_bindgen(getter, js_name = responseEncryptingKey)] - pub fn response_encrypting_key(&self) -> PublicKey { - PublicKey::from(self.0.response_encrypting_key) - } -} - // // EncryptedThresholdDecryptionRequest // @@ -664,11 +641,19 @@ impl EncryptedThresholdDecryptionRequest { self.0.ritual_id } - pub fn decrypt(&self, sk: &SecretKey) -> Result { + #[wasm_bindgen(getter, js_name = requesterPublicKey)] + pub fn requester_public_key(&self) -> Box<[u8]> { + self.0.requester_public_key.clone() + } + + pub fn decrypt( + &self, + shared_secret: &SharedSecret, + ) -> Result { self.0 - .decrypt(sk.as_ref()) + .decrypt(shared_secret.as_ref()) .map_err(map_js_err) - .map(E2EThresholdDecryptionRequest) + .map(ThresholdDecryptionRequest) } #[wasm_bindgen(js_name = fromBytes)] @@ -704,8 +689,8 @@ impl ThresholdDecryptionResponse { self.0.decryption_share.clone() } - pub fn encrypt(&self, encrypting_key: &PublicKey) -> EncryptedThresholdDecryptionResponse { - EncryptedThresholdDecryptionResponse(self.0.encrypt(encrypting_key.as_ref())) + pub fn encrypt(&self, shared_secret: &SharedSecret) -> EncryptedThresholdDecryptionResponse { + EncryptedThresholdDecryptionResponse(self.0.encrypt(shared_secret.as_ref())) } #[wasm_bindgen(js_name = fromBytes)] @@ -731,9 +716,12 @@ pub struct EncryptedThresholdDecryptionResponse( #[wasm_bindgen] impl EncryptedThresholdDecryptionResponse { - pub fn decrypt(&self, sk: &SecretKey) -> Result { + pub fn decrypt( + &self, + shared_secret: &SharedSecret, + ) -> Result { self.0 - .decrypt(sk.as_ref()) + .decrypt(shared_secret.as_ref()) .map_err(map_js_err) .map(ThresholdDecryptionResponse) } diff --git a/nucypher-core-wasm/tests/wasm.rs b/nucypher-core-wasm/tests/wasm.rs index db1ee189..aefa2968 100644 --- a/nucypher-core-wasm/tests/wasm.rs +++ b/nucypher-core-wasm/tests/wasm.rs @@ -1,4 +1,5 @@ use nucypher_core_wasm::*; +use rand_core::OsRng; use ferveo::bindings_wasm::{ferveo_encrypt, DkgPublicKey, Keypair}; use umbral_pre::bindings_wasm::{ @@ -9,6 +10,8 @@ use wasm_bindgen::prelude::*; use wasm_bindgen::JsCast; use wasm_bindgen_test::*; +use x25519_dalek::{EphemeralSecret, PublicKey as DalekPublicKey}; + // // Test utilities // @@ -673,11 +676,16 @@ fn metadata_response() { #[wasm_bindgen_test] fn threshold_decryption_request() { let ritual_id: u16 = 5; - let request_secret = SecretKey::random(); - let request_encrypting_key = request_secret.public_key(); + let service_secret = EphemeralSecret::random_from_rng(OsRng); + let service_public_key = DalekPublicKey::from(&service_secret); + + let requester_secret = EphemeralSecret::random_from_rng(OsRng); + let requester_public_key = DalekPublicKey::from(&requester_secret); - let response_secret = SecretKey::random(); - let response_encrypting_key = response_secret.public_key(); + let service_shared_secret = + SharedSecret::from(service_secret.diffie_hellman(&requester_public_key)); + let requester_shared_secret = + SharedSecret::from(requester_secret.diffie_hellman(&service_public_key)); let conditions = "{'some': 'condition'}"; let conditions_js: JsValue = Some(Conditions::new(conditions)).into(); @@ -696,7 +704,9 @@ fn threshold_decryption_request() { ) .unwrap(); - let encrypted_request = request.encrypt(&request_encrypting_key, &response_encrypting_key); + // requester encrypts request to send to service + let requester_key = RequesterPublicKey::from(requester_public_key); + let encrypted_request = request.encrypt(&requester_shared_secret, &requester_key); // mimic encrypted request going over the wire let encrypted_request_bytes = encrypted_request.to_bytes(); @@ -705,44 +715,55 @@ fn threshold_decryption_request() { assert_eq!(encrypted_request_from_bytes, encrypted_request); assert_eq!(encrypted_request_from_bytes.ritual_id(), ritual_id); - - let e2e_request = encrypted_request_from_bytes - .decrypt(&request_secret) - .unwrap(); + // TODO clean up storage/use of requester public key assert_eq!( - response_encrypting_key.to_compressed_bytes(), - e2e_request.response_encrypting_key().to_compressed_bytes() + encrypted_request_from_bytes.requester_public_key(), + requester_public_key.to_bytes().to_vec().into_boxed_slice() ); - assert_eq!(request, e2e_request.decryption_request()); - // wrong secret key used - assert!(encrypted_request_from_bytes - .decrypt(&response_secret) - .is_err()); + // service decrypts request + let decrypted_request = encrypted_request_from_bytes + .decrypt(&service_shared_secret) + .unwrap(); + assert_eq!(request, decrypted_request); - let random_secret_key = SecretKey::random(); + // wrong key used + let random_secret_key = EphemeralSecret::random_from_rng(OsRng); + let random_shared_secret = + SharedSecret::from(random_secret_key.diffie_hellman(&service_public_key)); assert!(encrypted_request_from_bytes - .decrypt(&random_secret_key) + .decrypt(&random_shared_secret) .is_err()); } #[wasm_bindgen_test] fn threshold_decryption_response() { - let response_secret = SecretKey::random(); - let response_encrypting_key = response_secret.public_key(); + let service_secret = EphemeralSecret::random_from_rng(OsRng); + let service_public_key = DalekPublicKey::from(&service_secret); + + let requester_secret = EphemeralSecret::random_from_rng(OsRng); + let requester_public_key = DalekPublicKey::from(&requester_secret); + + let service_shared_secret = + SharedSecret::from(service_secret.diffie_hellman(&requester_public_key)); + let requester_shared_secret = + SharedSecret::from(requester_secret.diffie_hellman(&service_public_key)); let decryption_share = b"The Tyranny of Merit"; let response = ThresholdDecryptionResponse::new(decryption_share).unwrap(); - let encrypted_response = response.encrypt(&response_encrypting_key); - let encrypted_response_bytes = encrypted_response.to_bytes(); + // service encrypts response to send back + let encrypted_response = response.encrypt(&service_shared_secret); + // mimic serialization/deserialization over the wire + let encrypted_response_bytes = encrypted_response.to_bytes(); let encrypted_response_from_bytes = EncryptedThresholdDecryptionResponse::from_bytes(&encrypted_response_bytes).unwrap(); + // requester decrypts response let decrypted_response = encrypted_response_from_bytes - .decrypt(&response_secret) + .decrypt(&requester_shared_secret) .unwrap(); assert_eq!(response, decrypted_response); assert_eq!( @@ -751,8 +772,10 @@ fn threshold_decryption_response() { ); // wrong secret key used - let random_secret_key = SecretKey::random(); + let random_secret_key = EphemeralSecret::random_from_rng(OsRng); + let random_shared_secret = + SharedSecret::from(random_secret_key.diffie_hellman(&service_public_key)); assert!(encrypted_response_from_bytes - .decrypt(&random_secret_key) + .decrypt(&random_shared_secret) .is_err()); } diff --git a/nucypher-core/Cargo.toml b/nucypher-core/Cargo.toml index d8298b86..9cf1384d 100644 --- a/nucypher-core/Cargo.toml +++ b/nucypher-core/Cargo.toml @@ -18,3 +18,8 @@ sha3 = "0.10" rmp-serde = "1" serde_with = "1.14" hex = "0.4" +x25519-dalek = "2.0.0-rc.2" +chacha20poly1305 = "0.10.1" + +[dev-dependencies] +rand_core = "0.6.4" \ No newline at end of file diff --git a/nucypher-core/src/dkg.rs b/nucypher-core/src/dkg.rs index 8d411eb3..29069c5b 100644 --- a/nucypher-core/src/dkg.rs +++ b/nucypher-core/src/dkg.rs @@ -1,17 +1,104 @@ use alloc::boxed::Box; use alloc::string::String; +use core::fmt; +use generic_array::typenum::Unsigned; use ferveo::api::Ciphertext; -use serde::{Deserialize, Serialize}; -use umbral_pre::{decrypt_original, encrypt, serde_bytes, Capsule, PublicKey, SecretKey}; - use crate::conditions::{Conditions, Context}; -use crate::key_frag::DecryptionError; +use serde::{Deserialize, Serialize}; +use umbral_pre::serde_bytes; // TODO should this be in umbral? +use x25519_dalek::{PublicKey, SharedSecret}; use crate::versioning::{ - messagepack_deserialize, messagepack_serialize, ProtocolObject, ProtocolObjectInner, + messagepack_deserialize, messagepack_serialize, DeserializationError, ProtocolObject, + ProtocolObjectInner, }; +use chacha20poly1305::aead::{Aead, AeadCore, KeyInit, OsRng}; +use chacha20poly1305::{ChaCha20Poly1305, Key, Nonce}; + +/// Errors during encryption. +#[derive(Debug)] +pub enum EncryptionError { + /// Given plaintext is too large for the backend to handle. + PlaintextTooLarge, +} + +impl fmt::Display for EncryptionError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::PlaintextTooLarge => write!(f, "Plaintext is too large to encrypt"), + } + } +} + +/// Errors during decryption. +#[derive(Debug)] +pub enum DecryptionError { + /// Ciphertext (which should be prepended by the nonce) is shorter than the nonce length. + CiphertextTooShort, + /// The ciphertext and the attached authentication data are inconsistent. + /// This can happen if: + /// - an incorrect key is used, + /// - the ciphertext is modified or cut short, + /// - an incorrect authentication data is provided on decryption. + AuthenticationFailed, + /// Unable to create object from decrypted ciphertext + DeserializationFailed(DeserializationError), +} + +impl fmt::Display for DecryptionError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::CiphertextTooShort => write!(f, "The ciphertext must include the nonce"), + Self::AuthenticationFailed => write!( + f, + "Decryption of ciphertext failed: \ + either someone tampered with the ciphertext or \ + you are using an incorrect decryption key." + ), + Self::DeserializationFailed(err) => write!(f, "deserialization failed: {}", err), + } + } +} + +type NonceSize = ::NonceSize; + +fn encrypt_with_shared_secret( + shared_secret: &SharedSecret, + plaintext: &[u8], +) -> Result, EncryptionError> { + let key = Key::from_slice(shared_secret.as_ref()); + let cipher = ChaCha20Poly1305::new(&key); + let nonce = ChaCha20Poly1305::generate_nonce(&mut OsRng); + let mut result = nonce.to_vec(); + let ciphertext = cipher + .encrypt(&nonce, plaintext.as_ref()) + .map_err(|_err| EncryptionError::PlaintextTooLarge)?; + result.extend(ciphertext); + Ok(result.into_boxed_slice()) +} + +fn decrypt_with_shared_secret( + shared_secret: &SharedSecret, + ciphertext: &Box<[u8]>, +) -> Result, DecryptionError> { + let nonce_size = ::to_usize(); + let buf_size = ciphertext.as_ref().len(); + if buf_size < nonce_size { + return Err(DecryptionError::CiphertextTooShort); + } + let nonce = Nonce::from_slice(&ciphertext.as_ref()[..nonce_size]); + let encrypted_data = &ciphertext.as_ref()[nonce_size..]; + + let key = Key::from_slice(shared_secret.as_ref()); + let cipher = ChaCha20Poly1305::new(&key); + let plaintext = cipher + .decrypt(&nonce, encrypted_data.as_ref()) + .map_err(|_err| DecryptionError::AuthenticationFailed)?; + Ok(plaintext.into_boxed_slice()) +} + /// The ferveo variant to use for the decryption share derivation. #[derive(PartialEq, Eq, Debug, Serialize, Deserialize, Copy, Clone)] pub enum FerveoVariant { @@ -57,14 +144,10 @@ impl ThresholdDecryptionRequest { /// Encrypts the decryption request. pub fn encrypt( &self, - request_encrypting_key: &PublicKey, - response_encrypting_key: &PublicKey, + shared_secret: &SharedSecret, + requester_public_key: &PublicKey, ) -> EncryptedThresholdDecryptionRequest { - EncryptedThresholdDecryptionRequest::new( - self, - request_encrypting_key, - response_encrypting_key, - ) + EncryptedThresholdDecryptionRequest::new(self, shared_secret, requester_public_key) } } @@ -92,79 +175,33 @@ impl<'a> ProtocolObjectInner<'a> for ThresholdDecryptionRequest { impl<'a> ProtocolObject<'a> for ThresholdDecryptionRequest {} -/// A request for an Ursula to derive a decryption share that specifies the key to encrypt Ursula's response. -#[derive(PartialEq, Debug, Clone, Serialize, Deserialize)] -pub struct E2EThresholdDecryptionRequest { - /// The decryption request. - pub decryption_request: ThresholdDecryptionRequest, - /// The key to encrypt the corresponding decryption response. - pub response_encrypting_key: PublicKey, -} - -impl E2EThresholdDecryptionRequest { - /// Create E2E decryption request. - pub fn new( - decryption_request: &ThresholdDecryptionRequest, - response_encrypting_key: &PublicKey, - ) -> Self { - Self { - decryption_request: decryption_request.clone(), - response_encrypting_key: *response_encrypting_key, - } - } -} - -impl<'a> ProtocolObjectInner<'a> for E2EThresholdDecryptionRequest { - fn version() -> (u16, u16) { - (1, 0) - } - - fn brand() -> [u8; 4] { - *b"E2eR" - } - - 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 E2EThresholdDecryptionRequest {} - /// An encrypted request for an Ursula to derive a decryption share. #[derive(PartialEq, Debug, Clone, Serialize, Deserialize)] pub struct EncryptedThresholdDecryptionRequest { /// ID of the ritual pub ritual_id: u16, - /// TODO Umbral for now - but change - capsule: Capsule, + #[serde(with = "serde_bytes::as_base64")] + /// Public key of requester + /// TODO this should not be Box + pub requester_public_key: Box<[u8]>, + + #[serde(with = "serde_bytes::as_base64")] + /// Encrypted request ciphertext: Box<[u8]>, } impl EncryptedThresholdDecryptionRequest { fn new( request: &ThresholdDecryptionRequest, - request_encrypting_key: &PublicKey, - response_encrypting_key: &PublicKey, + shared_secret: &SharedSecret, + requester_public_key: &PublicKey, ) -> Self { - let e2e_decryption_request = - E2EThresholdDecryptionRequest::new(request, response_encrypting_key); - // TODO: using Umbral for encryption to avoid introducing more crypto primitives. - let (capsule, ciphertext) = - encrypt(request_encrypting_key, &e2e_decryption_request.to_bytes()) - .expect("encryption failed - out of memory?"); - let ritual_id = request.ritual_id; + let ciphertext = encrypt_with_shared_secret(shared_secret, &request.to_bytes()) + .expect("encryption failed - out of memory?"); Self { - ritual_id, - capsule, + ritual_id: request.ritual_id, + requester_public_key: requester_public_key.to_bytes().to_vec().into_boxed_slice(), ciphertext, } } @@ -172,13 +209,11 @@ impl EncryptedThresholdDecryptionRequest { /// Decrypts the decryption request pub fn decrypt( &self, - sk: &SecretKey, - ) -> Result { - let decryption_request_bytes = decrypt_original(sk, &self.capsule, &self.ciphertext) - .map_err(DecryptionError::DecryptionFailed)?; - let decryption_request = - E2EThresholdDecryptionRequest::from_bytes(&decryption_request_bytes) - .map_err(DecryptionError::DeserializationFailed)?; + shared_secret: &SharedSecret, + ) -> Result { + let decryption_request_bytes = decrypt_with_shared_secret(shared_secret, &self.ciphertext)?; + let decryption_request = ThresholdDecryptionRequest::from_bytes(&decryption_request_bytes) + .map_err(DecryptionError::DeserializationFailed)?; Ok(decryption_request) } } @@ -224,8 +259,8 @@ impl ThresholdDecryptionResponse { } /// Encrypts the decryption response. - pub fn encrypt(&self, encrypting_key: &PublicKey) -> EncryptedThresholdDecryptionResponse { - EncryptedThresholdDecryptionResponse::new(encrypting_key, self) + pub fn encrypt(&self, shared_secret: &SharedSecret) -> EncryptedThresholdDecryptionResponse { + EncryptedThresholdDecryptionResponse::new(self, shared_secret) } } @@ -256,31 +291,25 @@ impl<'a> ProtocolObject<'a> for ThresholdDecryptionResponse {} /// An encrypted response from Ursula with a derived decryption share. #[derive(PartialEq, Debug, Clone, Serialize, Deserialize)] pub struct EncryptedThresholdDecryptionResponse { - /// TODO Umbral for now - but change - capsule: Capsule, #[serde(with = "serde_bytes::as_base64")] ciphertext: Box<[u8]>, } impl EncryptedThresholdDecryptionResponse { - fn new( - encrypting_key: &PublicKey, - threshold_decryption_response: &ThresholdDecryptionResponse, - ) -> Self { + fn new(response: &ThresholdDecryptionResponse, shared_secret: &SharedSecret) -> Self { // TODO: using Umbral for encryption to avoid introducing more crypto primitives. - let (capsule, ciphertext) = - encrypt(encrypting_key, &threshold_decryption_response.to_bytes()) - .expect("encryption failed - out of memory?"); - Self { - capsule, - ciphertext, - } + let ciphertext = encrypt_with_shared_secret(shared_secret, &response.to_bytes()) + .expect("encryption failed - out of memory?"); + Self { ciphertext } } /// Decrypts the decryption request - pub fn decrypt(&self, sk: &SecretKey) -> Result { - let decryption_response_bytes = decrypt_original(sk, &self.capsule, &self.ciphertext) - .map_err(DecryptionError::DecryptionFailed)?; + pub fn decrypt( + &self, + shared_secret: &SharedSecret, + ) -> Result { + let decryption_response_bytes = + decrypt_with_shared_secret(shared_secret, &self.ciphertext)?; let decryption_response = ThresholdDecryptionResponse::from_bytes(&decryption_response_bytes) .map_err(DecryptionError::DeserializationFailed)?; @@ -314,8 +343,8 @@ impl<'a> ProtocolObject<'a> for EncryptedThresholdDecryptionResponse {} #[cfg(test)] mod tests { + use x25519_dalek::{EphemeralSecret, PublicKey}; use ferveo::api::{encrypt as ferveo_encrypt, DkgPublicKey, SecretBox}; - use umbral_pre::SecretKey; use crate::{ Conditions, Context, EncryptedThresholdDecryptionRequest, @@ -323,20 +352,45 @@ mod tests { ThresholdDecryptionRequest, ThresholdDecryptionResponse, }; + use generic_array::typenum::Unsigned; + + use crate::dkg::{decrypt_with_shared_secret, DecryptionError, NonceSize}; + use rand_core::OsRng; + + #[test] + fn decryption_with_shared_secret() { + let service_secret = EphemeralSecret::random_from_rng(OsRng); + + let requester_secret = EphemeralSecret::random_from_rng(OsRng); + let requester_public_key = PublicKey::from(&requester_secret); + + let service_shared_secret = service_secret.diffie_hellman(&requester_public_key); + + let ciphertext = b"1".to_vec().into_boxed_slice(); // length less than nonce size + let nonce_size = ::to_usize(); + assert!(ciphertext.len() < nonce_size); + + assert!(matches!( + decrypt_with_shared_secret(&service_shared_secret, &ciphertext).unwrap_err(), + DecryptionError::CiphertextTooShort + )); + } + #[test] fn threshold_decryption_request() { let ritual_id = 0; - let request_secret = SecretKey::random(); - let request_encrypting_key = request_secret.public_key(); + let service_secret = EphemeralSecret::random_from_rng(OsRng); + let service_public_key = PublicKey::from(&service_secret); - let response_secret = SecretKey::random(); - let response_encrypting_key = response_secret.public_key(); + let requester_secret = EphemeralSecret::random_from_rng(OsRng); + let requester_public_key = PublicKey::from(&requester_secret); - let random_secret_key = SecretKey::random(); + let service_shared_secret = service_secret.diffie_hellman(&requester_public_key); + let requester_shared_secret = requester_secret.diffie_hellman(&service_public_key); let dkg_pk = DkgPublicKey::random(); - let message = "my_message".as_bytes().to_vec(); + let message = "The Tyranny of Merit".as_bytes().to_vec(); let aad = "my-add".as_bytes(); let ciphertext = ferveo_encrypt(SecretBox::new(message), aad, &dkg_pk).unwrap(); @@ -348,47 +402,60 @@ mod tests { FerveoVariant::SIMPLE, ); - let encrypted_request = request.encrypt(&request_encrypting_key, &response_encrypting_key); + // requester encrypts request to send to service + let encrypted_request = request.encrypt(&requester_shared_secret, &requester_public_key); + // mimic serialization/deserialization over the wire let encrypted_request_bytes = encrypted_request.to_bytes(); let encrypted_request_from_bytes = EncryptedThresholdDecryptionRequest::from_bytes(&encrypted_request_bytes).unwrap(); assert_eq!(encrypted_request_from_bytes.ritual_id, ritual_id); + assert_eq!( + encrypted_request_from_bytes.requester_public_key, + requester_public_key.as_bytes().to_vec().into_boxed_slice() + ); - let e2e_request = encrypted_request_from_bytes - .decrypt(&request_secret) + // service decrypts request + let decrypted_request = encrypted_request_from_bytes + .decrypt(&service_shared_secret) .unwrap(); - assert_eq!(response_encrypting_key, e2e_request.response_encrypting_key); - assert_eq!(request, e2e_request.decryption_request); + assert_eq!(decrypted_request, request); - // wrong secret key used + // wrong shared key used + let random_secret_key = EphemeralSecret::random_from_rng(OsRng); + let random_shared_secret = random_secret_key.diffie_hellman(&requester_public_key); assert!(encrypted_request_from_bytes - .decrypt(&response_secret) - .is_err()); - - assert!(encrypted_request_from_bytes - .decrypt(&random_secret_key) + .decrypt(&random_shared_secret) .is_err()); } #[test] fn threshold_decryption_response() { - let response_secret = SecretKey::random(); - let response_encrypting_key = response_secret.public_key(); + let service_secret = EphemeralSecret::random_from_rng(OsRng); + let service_public_key = PublicKey::from(&service_secret); + + let requester_secret = EphemeralSecret::random_from_rng(OsRng); + let requester_public_key = PublicKey::from(&requester_secret); + + let service_shared_secret = service_secret.diffie_hellman(&requester_public_key); + let requester_shared_secret = requester_secret.diffie_hellman(&service_public_key); let decryption_share = b"The Tyranny of Merit"; let response = ThresholdDecryptionResponse::new(decryption_share); - let encrypted_response = response.encrypt(&response_encrypting_key); + // service encrypts response to send back + let encrypted_response = response.encrypt(&service_shared_secret); + // mimic serialization/deserialization over the wire let encrypted_response_bytes = encrypted_response.to_bytes(); let encrypted_response_from_bytes = EncryptedThresholdDecryptionResponse::from_bytes(&encrypted_response_bytes).unwrap(); + // requester decrypts response let decrypted_response = encrypted_response_from_bytes - .decrypt(&response_secret) + .decrypt(&requester_shared_secret) .unwrap(); assert_eq!(response, decrypted_response); assert_eq!( @@ -396,10 +463,11 @@ mod tests { decrypted_response.decryption_share ); - // wrong secret key used - let random_secret_key = SecretKey::random(); + // wrong shared key used + let random_secret_key = EphemeralSecret::random_from_rng(OsRng); + let random_shared_secret = random_secret_key.diffie_hellman(&requester_public_key); assert!(encrypted_response_from_bytes - .decrypt(&random_secret_key) + .decrypt(&random_shared_secret) .is_err()); } } diff --git a/nucypher-core/src/lib.rs b/nucypher-core/src/lib.rs index 147fb698..94739e54 100644 --- a/nucypher-core/src/lib.rs +++ b/nucypher-core/src/lib.rs @@ -27,9 +27,8 @@ pub struct VerificationError; pub use address::Address; pub use conditions::{Conditions, Context}; pub use dkg::{ - E2EThresholdDecryptionRequest, EncryptedThresholdDecryptionRequest, - EncryptedThresholdDecryptionResponse, FerveoVariant, ThresholdDecryptionRequest, - ThresholdDecryptionResponse, + EncryptedThresholdDecryptionRequest, EncryptedThresholdDecryptionResponse, FerveoVariant, + ThresholdDecryptionRequest, ThresholdDecryptionResponse, }; pub use fleet_state::FleetStateChecksum; pub use hrac::HRAC; From 080ece2f087ff8e26c4526bb7bb597bb83646e85 Mon Sep 17 00:00:00 2001 From: derekpierre Date: Fri, 26 May 2023 12:40:04 -0400 Subject: [PATCH 02/15] Fix clippy warnings. --- nucypher-core/src/dkg.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/nucypher-core/src/dkg.rs b/nucypher-core/src/dkg.rs index 29069c5b..dccaaec6 100644 --- a/nucypher-core/src/dkg.rs +++ b/nucypher-core/src/dkg.rs @@ -69,7 +69,7 @@ fn encrypt_with_shared_secret( plaintext: &[u8], ) -> Result, EncryptionError> { let key = Key::from_slice(shared_secret.as_ref()); - let cipher = ChaCha20Poly1305::new(&key); + let cipher = ChaCha20Poly1305::new(key); let nonce = ChaCha20Poly1305::generate_nonce(&mut OsRng); let mut result = nonce.to_vec(); let ciphertext = cipher @@ -81,20 +81,20 @@ fn encrypt_with_shared_secret( fn decrypt_with_shared_secret( shared_secret: &SharedSecret, - ciphertext: &Box<[u8]>, + ciphertext: &[u8], ) -> Result, DecryptionError> { let nonce_size = ::to_usize(); - let buf_size = ciphertext.as_ref().len(); + let buf_size = ciphertext.len(); if buf_size < nonce_size { return Err(DecryptionError::CiphertextTooShort); } - let nonce = Nonce::from_slice(&ciphertext.as_ref()[..nonce_size]); - let encrypted_data = &ciphertext.as_ref()[nonce_size..]; + let nonce = Nonce::from_slice(&ciphertext[..nonce_size]); + let encrypted_data = &ciphertext[nonce_size..]; let key = Key::from_slice(shared_secret.as_ref()); - let cipher = ChaCha20Poly1305::new(&key); + let cipher = ChaCha20Poly1305::new(key); let plaintext = cipher - .decrypt(&nonce, encrypted_data.as_ref()) + .decrypt(nonce, encrypted_data) .map_err(|_err| DecryptionError::AuthenticationFailed)?; Ok(plaintext.into_boxed_slice()) } From 37234eae90aa849afd3095af736ee36ecc816030 Mon Sep 17 00:00:00 2001 From: derekpierre Date: Fri, 26 May 2023 19:58:12 -0400 Subject: [PATCH 03/15] Fix serialization of requester public key to use actual object instead of Box. --- Cargo.lock | 1 + nucypher-core-wasm/src/lib.rs | 6 +++--- nucypher-core-wasm/tests/wasm.rs | 3 +-- nucypher-core/Cargo.toml | 2 +- nucypher-core/src/dkg.rs | 9 +++------ 5 files changed, 9 insertions(+), 12 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2510af9b..005ec5f3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -346,6 +346,7 @@ dependencies = [ "fiat-crypto", "packed_simd_2", "platforms", + "serde", "subtle", "zeroize", ] diff --git a/nucypher-core-wasm/src/lib.rs b/nucypher-core-wasm/src/lib.rs index c0e16b3f..67d155b0 100644 --- a/nucypher-core-wasm/src/lib.rs +++ b/nucypher-core-wasm/src/lib.rs @@ -547,7 +547,7 @@ impl EncryptedTreasureMap { pub struct SharedSecret(x25519_dalek::SharedSecret); #[wasm_bindgen] -#[derive(derive_more::From, derive_more::AsRef)] +#[derive(PartialEq, Eq, Debug, derive_more::From, derive_more::AsRef)] pub struct RequesterPublicKey(x25519_dalek::PublicKey); // @@ -642,8 +642,8 @@ impl EncryptedThresholdDecryptionRequest { } #[wasm_bindgen(getter, js_name = requesterPublicKey)] - pub fn requester_public_key(&self) -> Box<[u8]> { - self.0.requester_public_key.clone() + pub fn requester_public_key(&self) -> RequesterPublicKey { + RequesterPublicKey::from(self.0.requester_public_key) } pub fn decrypt( diff --git a/nucypher-core-wasm/tests/wasm.rs b/nucypher-core-wasm/tests/wasm.rs index aefa2968..2070642d 100644 --- a/nucypher-core-wasm/tests/wasm.rs +++ b/nucypher-core-wasm/tests/wasm.rs @@ -715,10 +715,9 @@ fn threshold_decryption_request() { assert_eq!(encrypted_request_from_bytes, encrypted_request); assert_eq!(encrypted_request_from_bytes.ritual_id(), ritual_id); - // TODO clean up storage/use of requester public key assert_eq!( encrypted_request_from_bytes.requester_public_key(), - requester_public_key.to_bytes().to_vec().into_boxed_slice() + requester_key ); // service decrypts request diff --git a/nucypher-core/Cargo.toml b/nucypher-core/Cargo.toml index 9cf1384d..48bc2a34 100644 --- a/nucypher-core/Cargo.toml +++ b/nucypher-core/Cargo.toml @@ -18,7 +18,7 @@ sha3 = "0.10" rmp-serde = "1" serde_with = "1.14" hex = "0.4" -x25519-dalek = "2.0.0-rc.2" +x25519-dalek = { version="2.0.0-rc.2", features = ["serde"] } chacha20poly1305 = "0.10.1" [dev-dependencies] diff --git a/nucypher-core/src/dkg.rs b/nucypher-core/src/dkg.rs index dccaaec6..3e935b83 100644 --- a/nucypher-core/src/dkg.rs +++ b/nucypher-core/src/dkg.rs @@ -181,10 +181,8 @@ pub struct EncryptedThresholdDecryptionRequest { /// ID of the ritual pub ritual_id: u16, - #[serde(with = "serde_bytes::as_base64")] /// Public key of requester - /// TODO this should not be Box - pub requester_public_key: Box<[u8]>, + pub requester_public_key: PublicKey, #[serde(with = "serde_bytes::as_base64")] /// Encrypted request @@ -201,7 +199,7 @@ impl EncryptedThresholdDecryptionRequest { .expect("encryption failed - out of memory?"); Self { ritual_id: request.ritual_id, - requester_public_key: requester_public_key.to_bytes().to_vec().into_boxed_slice(), + requester_public_key: *requester_public_key, ciphertext, } } @@ -297,7 +295,6 @@ pub struct EncryptedThresholdDecryptionResponse { impl EncryptedThresholdDecryptionResponse { fn new(response: &ThresholdDecryptionResponse, shared_secret: &SharedSecret) -> Self { - // TODO: using Umbral for encryption to avoid introducing more crypto primitives. let ciphertext = encrypt_with_shared_secret(shared_secret, &response.to_bytes()) .expect("encryption failed - out of memory?"); Self { ciphertext } @@ -413,7 +410,7 @@ mod tests { assert_eq!(encrypted_request_from_bytes.ritual_id, ritual_id); assert_eq!( encrypted_request_from_bytes.requester_public_key, - requester_public_key.as_bytes().to_vec().into_boxed_slice() + requester_public_key ); // service decrypts request From 49d206a0c8411c31832de7cb0ccff6e57efd21b0 Mon Sep 17 00:00:00 2001 From: derekpierre Date: Mon, 29 May 2023 20:55:52 -0400 Subject: [PATCH 04/15] Wrap x25519-dalek objects with our own - RequestSecretFactory, RequestSecretKey, RequestPublicKey, RequestSharedSecret. Updated/fixed python and wasm bindings accordingly. Added tests. --- Cargo.lock | 12 +- nucypher-core-python/Cargo.toml | 1 - .../nucypher_core/__init__.py | 4 + .../nucypher_core/__init__.pyi | 54 +++ nucypher-core-python/src/lib.rs | 140 +++++- nucypher-core-wasm/Cargo.toml | 1 - nucypher-core-wasm/src/lib.rs | 119 +++++- nucypher-core-wasm/tests/wasm.rs | 56 +-- nucypher-core/Cargo.toml | 13 +- nucypher-core/src/dkg.rs | 399 ++++++++++++++++-- nucypher-core/src/lib.rs | 6 +- nucypher-core/src/secret_box.rs | 76 ++++ 12 files changed, 794 insertions(+), 87 deletions(-) create mode 100644 nucypher-core/src/secret_box.rs diff --git a/Cargo.lock b/Cargo.lock index 005ec5f3..db368206 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -862,13 +862,18 @@ dependencies = [ "ferveo-pre-release", "generic-array", "hex", - "rand_core", + "hkdf", + "rand 0.8.5", + "rand_chacha 0.3.1", + "rand_core 0.6.4", "rmp-serde", "serde", "serde_with 1.14.0", + "sha2", "sha3", "umbral-pre", "x25519-dalek", + "zeroize", ] [[package]] @@ -881,7 +886,6 @@ dependencies = [ "pyo3", "pyo3-build-config 0.19.0", "umbral-pre", - "x25519-dalek", ] [[package]] @@ -893,12 +897,10 @@ dependencies = [ "ferveo-pre-release", "js-sys", "nucypher-core", - "rand_core", "umbral-pre", "wasm-bindgen", "wasm-bindgen-derive", "wasm-bindgen-test", - "x25519-dalek", ] [[package]] @@ -1833,7 +1835,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fabd6e16dd08033932fc3265ad4510cc2eab24656058a6dcb107ffe274abcc95" dependencies = [ "curve25519-dalek", - "rand_core", + "rand_core 0.6.4", "serde", "zeroize", ] diff --git a/nucypher-core-python/Cargo.toml b/nucypher-core-python/Cargo.toml index 99a6f0df..7372d359 100644 --- a/nucypher-core-python/Cargo.toml +++ b/nucypher-core-python/Cargo.toml @@ -13,7 +13,6 @@ nucypher-core = { path = "../nucypher-core" } umbral-pre = { version = "0.10.0", features = ["bindings-python"] } ferveo = { package = "ferveo-pre-release", version = "0.1.0-alpha.8", features = ["bindings-python"] } derive_more = { version = "0.99", default-features = false, features = ["from", "as_ref"] } -x25519-dalek = "2.0.0-rc.2" [build-dependencies] pyo3-build-config = "*" diff --git a/nucypher-core-python/nucypher_core/__init__.py b/nucypher-core-python/nucypher_core/__init__.py index 4f37ec29..947d769c 100644 --- a/nucypher-core-python/nucypher_core/__init__.py +++ b/nucypher-core-python/nucypher_core/__init__.py @@ -21,4 +21,8 @@ ThresholdDecryptionResponse, EncryptedThresholdDecryptionRequest, EncryptedThresholdDecryptionResponse, + RequestSharedSecret, + RequestPublicKey, + RequestSecretKey, + RequestKeyFactory, ) diff --git a/nucypher-core-python/nucypher_core/__init__.pyi b/nucypher-core-python/nucypher_core/__init__.pyi index 88c9443f..d9eb9e8e 100644 --- a/nucypher-core-python/nucypher_core/__init__.pyi +++ b/nucypher-core-python/nucypher_core/__init__.pyi @@ -437,6 +437,8 @@ class ThresholdDecryptionRequest: class EncryptedThresholdDecryptionRequest: ritual_id: int + requester_public_key: RequestPublicKey + def decrypt( self, sk: SecretKey @@ -483,3 +485,55 @@ class EncryptedThresholdDecryptionResponse: def __bytes__(self) -> bytes: ... + + +class RequestSharedSecret: + ... + + +class RequestPublicKey: + + @staticmethod + def from_bytes(data: bytes) -> RequestPublicKey: + ... + + def __bytes__(self) -> bytes: + ... + + +class RequestSecretKey: + + @staticmethod + def random() -> RequestSecretKey: + ... + + def public_key(self) -> RequestPublicKey: + ... + + def diffie_hellman(self, their_public_key: RequestPublicKey) -> RequestSharedSecret: + ... + + +class RequestKeyFactory: + + @staticmethod + def random() -> RequestKeyFactory: + ... + + @staticmethod + def seed_size() -> int: + ... + + @staticmethod + def from_secure_randomness(seed: bytes) -> RequestKeyFactory: + ... + + def make_secret(self, label: bytes) -> bytes: + ... + + def make_key(self, label: bytes) -> RequestSecretKey: + ... + + def make_factory(self, label: bytes) -> RequestKeyFactory: + ... + diff --git a/nucypher-core-python/src/lib.rs b/nucypher-core-python/src/lib.rs index 604742b6..32906b3c 100644 --- a/nucypher-core-python/src/lib.rs +++ b/nucypher-core-python/src/lib.rs @@ -633,16 +633,123 @@ impl ReencryptionResponse { } // -// Threshold Decryption Request +// Request Keys // #[pyclass(module = "nucypher_core")] #[derive(derive_more::From, derive_more::AsRef)] -pub struct SharedSecret(x25519_dalek::SharedSecret); +pub struct RequestSharedSecret { + backend: nucypher_core::RequestSharedSecret, +} #[pyclass(module = "nucypher_core")] #[derive(derive_more::From, derive_more::AsRef)] -pub struct RequesterPublicKey(x25519_dalek::PublicKey); +pub struct RequestPublicKey { + backend: nucypher_core::RequestPublicKey, +} + +#[pymethods] +impl RequestPublicKey { + #[staticmethod] + pub fn from_bytes(data: &[u8]) -> PyResult { + from_bytes::<_, nucypher_core::RequestPublicKey>(data) + } + + fn __bytes__(&self) -> PyObject { + to_bytes(self) + } + + fn __str__(&self) -> PyResult { + Ok(format!("{}", self.backend)) + } +} + +#[pyclass(module = "nucypher_core")] +#[derive(derive_more::From, derive_more::AsRef)] +pub struct RequestSecretKey { + backend: nucypher_core::RequestSecretKey, +} + +#[pymethods] +impl RequestSecretKey { + #[staticmethod] + pub fn random() -> PyResult { + Ok(Self { + backend: nucypher_core::RequestSecretKey::random(), + }) + } + + #[getter] + pub fn public_key(&self) -> RequestPublicKey { + RequestPublicKey { + backend: self.backend.public_key(), + } + } + + pub fn diffie_hellman(&self, their_public_key: &RequestPublicKey) -> RequestSharedSecret { + RequestSharedSecret { + backend: self.backend.diffie_hellman(their_public_key.as_ref()), + } + } + + fn __str__(&self) -> PyResult { + Ok(format!("{}", self.backend)) + } +} + +#[pyclass(module = "nucypher_core")] +#[derive(derive_more::From, derive_more::AsRef)] +pub struct RequestKeyFactory { + backend: nucypher_core::RequestKeyFactory, +} + +#[pymethods] +impl RequestKeyFactory { + #[staticmethod] + pub fn random() -> PyResult { + Ok(Self { + backend: nucypher_core::RequestKeyFactory::random(), + }) + } + + #[staticmethod] + pub fn seed_size() -> usize { + nucypher_core::RequestKeyFactory::seed_size() + } + + #[staticmethod] + pub fn from_secure_randomness(seed: &[u8]) -> PyResult { + let factory = nucypher_core::RequestKeyFactory::from_secure_randomness(seed) + .map_err(|err| PyValueError::new_err(format!("{}", err)))?; + Ok(Self { backend: factory }) + } + + pub fn make_secret(&self, label: &[u8]) -> PyObject { + let secret = self.backend.make_secret(label); + let bytes: &[u8] = secret.as_secret().as_ref(); + Python::with_gil(|py| PyBytes::new(py, bytes).into()) + } + + pub fn make_key(&self, label: &[u8]) -> RequestSecretKey { + RequestSecretKey { + backend: self.backend.make_key(label), + } + } + + pub fn make_factory(&self, label: &[u8]) -> RequestKeyFactory { + RequestKeyFactory { + backend: self.backend.make_factory(label), + } + } + + fn __str__(&self) -> PyResult { + Ok(format!("{}", self.backend)) + } +} + +// +// Threshold Decryption Request +// #[pyclass(module = "nucypher_core")] #[derive(derive_more::From, derive_more::AsRef)] @@ -721,8 +828,8 @@ impl ThresholdDecryptionRequest { pub fn encrypt( &self, - shared_secret: &SharedSecret, - requester_public_key: &RequesterPublicKey, + shared_secret: &RequestSharedSecret, + requester_public_key: &RequestPublicKey, ) -> EncryptedThresholdDecryptionRequest { EncryptedThresholdDecryptionRequest { backend: self @@ -758,7 +865,14 @@ impl EncryptedThresholdDecryptionRequest { self.backend.ritual_id } - pub fn decrypt(&self, shared_secret: &SharedSecret) -> PyResult { + pub fn requester_public_key(&self) -> RequestPublicKey { + RequestPublicKey::from(self.backend.requester_public_key) + } + + pub fn decrypt( + &self, + shared_secret: &RequestSharedSecret, + ) -> PyResult { self.backend .decrypt(shared_secret.as_ref()) .map(ThresholdDecryptionRequest::from) @@ -799,7 +913,10 @@ impl ThresholdDecryptionResponse { self.backend.decryption_share.as_ref() } - pub fn encrypt(&self, shared_secret: &SharedSecret) -> EncryptedThresholdDecryptionResponse { + pub fn encrypt( + &self, + shared_secret: &RequestSharedSecret, + ) -> EncryptedThresholdDecryptionResponse { EncryptedThresholdDecryptionResponse { backend: self.backend.encrypt(shared_secret.as_ref()), } @@ -827,7 +944,10 @@ pub struct EncryptedThresholdDecryptionResponse { #[pymethods] impl EncryptedThresholdDecryptionResponse { - pub fn decrypt(&self, shared_secret: &SharedSecret) -> PyResult { + pub fn decrypt( + &self, + shared_secret: &RequestSharedSecret, + ) -> PyResult { self.backend .decrypt(shared_secret.as_ref()) .map(ThresholdDecryptionResponse::from) @@ -1313,6 +1433,10 @@ 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::()?; + core_module.add_class::()?; // Build the umbral module let umbral_module = PyModule::new(py, "umbral")?; diff --git a/nucypher-core-wasm/Cargo.toml b/nucypher-core-wasm/Cargo.toml index cfbbb477..43716137 100644 --- a/nucypher-core-wasm/Cargo.toml +++ b/nucypher-core-wasm/Cargo.toml @@ -32,4 +32,3 @@ x25519-dalek = "2.0.0-rc.2" [dev-dependencies] console_error_panic_hook = "0.1" wasm-bindgen-test = "0.3.36" -rand_core = "0.6.4" diff --git a/nucypher-core-wasm/src/lib.rs b/nucypher-core-wasm/src/lib.rs index 67d155b0..5956ab88 100644 --- a/nucypher-core-wasm/src/lib.rs +++ b/nucypher-core-wasm/src/lib.rs @@ -542,13 +542,111 @@ impl EncryptedTreasureMap { } } +// +// Request Keys +// + #[wasm_bindgen] #[derive(derive_more::From, derive_more::AsRef)] -pub struct SharedSecret(x25519_dalek::SharedSecret); +pub struct RequestSharedSecret(nucypher_core::RequestSharedSecret); #[wasm_bindgen] #[derive(PartialEq, Eq, Debug, derive_more::From, derive_more::AsRef)] -pub struct RequesterPublicKey(x25519_dalek::PublicKey); +pub struct RequestPublicKey(nucypher_core::RequestPublicKey); + +#[wasm_bindgen] +impl RequestPublicKey { + #[wasm_bindgen(js_name = fromBytes)] + pub fn from_bytes(data: &[u8]) -> Result { + from_bytes::<_, nucypher_core::RequestPublicKey>(data) + } + + #[wasm_bindgen(js_name = toBytes)] + pub fn to_bytes(&self) -> Box<[u8]> { + to_bytes(self) + } + + #[allow(clippy::inherent_to_string)] + #[wasm_bindgen(js_name = toString)] + pub fn to_string(&self) -> String { + format!("{}", self.0) + } +} + +#[wasm_bindgen] +#[derive(derive_more::From, derive_more::AsRef)] +pub struct RequestSecretKey(nucypher_core::RequestSecretKey); + +#[wasm_bindgen] +impl RequestSecretKey { + /// Generates a secret key using the default RNG and returns it. + pub fn random() -> Self { + Self(nucypher_core::RequestSecretKey::random()) + } + + /// Generates a secret key using the default RNG and returns it. + #[wasm_bindgen(js_name = publicKey)] + pub fn public_key(&self) -> RequestPublicKey { + RequestPublicKey(self.0.public_key()) + } + + #[wasm_bindgen(js_name = diffieHellman)] + pub fn diffie_hellman(&self, their_public_key: &RequestPublicKey) -> RequestSharedSecret { + RequestSharedSecret(self.0.diffie_hellman(their_public_key.as_ref())) + } + + #[allow(clippy::inherent_to_string)] + #[wasm_bindgen(js_name = toString)] + pub fn to_string(&self) -> String { + format!("{}", self.0) + } +} + +#[wasm_bindgen] +pub struct RequestKeyFactory(nucypher_core::RequestKeyFactory); + +#[wasm_bindgen] +impl RequestKeyFactory { + /// Generates a secret key factory using the default RNG and returns it. + pub fn random() -> Self { + Self(nucypher_core::RequestKeyFactory::random()) + } + + #[wasm_bindgen(js_name = seedSize)] + pub fn seed_size() -> usize { + nucypher_core::RequestKeyFactory::seed_size() + } + + #[wasm_bindgen(js_name = fromSecureRandomness)] + pub fn from_secure_randomness(seed: &[u8]) -> Result { + nucypher_core::RequestKeyFactory::from_secure_randomness(seed) + .map(Self) + .map_err(map_js_err) + } + + #[wasm_bindgen(js_name = makeSecret)] + pub fn make_secret(&self, label: &[u8]) -> Vec { + let secret = self.0.make_secret(label); + let bytes: &[u8] = secret.as_secret().as_ref(); + bytes.into() + } + + #[wasm_bindgen(js_name = makeKey)] + pub fn make_key(&self, label: &[u8]) -> RequestSecretKey { + RequestSecretKey(self.0.make_key(label)) + } + + #[wasm_bindgen(js_name = makeFactory)] + pub fn make_factory(&self, label: &[u8]) -> Self { + Self(self.0.make_factory(label)) + } + + #[allow(clippy::inherent_to_string)] + #[wasm_bindgen(js_name = toString)] + pub fn to_string(&self) -> String { + format!("{}", self.0) + } +} // // Threshold Decryption Request @@ -606,8 +704,8 @@ impl ThresholdDecryptionRequest { pub fn encrypt( &self, - shared_secret: &SharedSecret, - requester_public_key: &RequesterPublicKey, + shared_secret: &RequestSharedSecret, + requester_public_key: &RequestPublicKey, ) -> EncryptedThresholdDecryptionRequest { EncryptedThresholdDecryptionRequest( self.0 @@ -642,13 +740,13 @@ impl EncryptedThresholdDecryptionRequest { } #[wasm_bindgen(getter, js_name = requesterPublicKey)] - pub fn requester_public_key(&self) -> RequesterPublicKey { - RequesterPublicKey::from(self.0.requester_public_key) + pub fn requester_public_key(&self) -> RequestPublicKey { + RequestPublicKey::from(self.0.requester_public_key) } pub fn decrypt( &self, - shared_secret: &SharedSecret, + shared_secret: &RequestSharedSecret, ) -> Result { self.0 .decrypt(shared_secret.as_ref()) @@ -689,7 +787,10 @@ impl ThresholdDecryptionResponse { self.0.decryption_share.clone() } - pub fn encrypt(&self, shared_secret: &SharedSecret) -> EncryptedThresholdDecryptionResponse { + pub fn encrypt( + &self, + shared_secret: &RequestSharedSecret, + ) -> EncryptedThresholdDecryptionResponse { EncryptedThresholdDecryptionResponse(self.0.encrypt(shared_secret.as_ref())) } @@ -718,7 +819,7 @@ pub struct EncryptedThresholdDecryptionResponse( impl EncryptedThresholdDecryptionResponse { pub fn decrypt( &self, - shared_secret: &SharedSecret, + shared_secret: &RequestSharedSecret, ) -> Result { self.0 .decrypt(shared_secret.as_ref()) diff --git a/nucypher-core-wasm/tests/wasm.rs b/nucypher-core-wasm/tests/wasm.rs index 2070642d..36b18e9d 100644 --- a/nucypher-core-wasm/tests/wasm.rs +++ b/nucypher-core-wasm/tests/wasm.rs @@ -1,5 +1,4 @@ use nucypher_core_wasm::*; -use rand_core::OsRng; use ferveo::bindings_wasm::{ferveo_encrypt, DkgPublicKey, Keypair}; use umbral_pre::bindings_wasm::{ @@ -10,8 +9,6 @@ use wasm_bindgen::prelude::*; use wasm_bindgen::JsCast; use wasm_bindgen_test::*; -use x25519_dalek::{EphemeralSecret, PublicKey as DalekPublicKey}; - // // Test utilities // @@ -673,19 +670,31 @@ fn metadata_response() { // ThresholdDecryptionRequestResponse // +#[wasm_bindgen_test] +fn request_public_key() { + let secret = RequestSecretKey::random(); + let public_key = secret.public_key(); + + // mimic transmission public key over the wire + let serialized_public_key = public_key.to_bytes(); + let deserialized_public_key = + RequestPublicKey::from_bytes(serialized_public_key.as_ref()).unwrap(); + + assert_eq!(public_key, deserialized_public_key); + assert_eq!(serialized_public_key, deserialized_public_key.to_bytes()); +} + #[wasm_bindgen_test] fn threshold_decryption_request() { let ritual_id: u16 = 5; - let service_secret = EphemeralSecret::random_from_rng(OsRng); - let service_public_key = DalekPublicKey::from(&service_secret); + let service_secret = RequestSecretKey::random(); + let service_public_key = service_secret.public_key(); - let requester_secret = EphemeralSecret::random_from_rng(OsRng); - let requester_public_key = DalekPublicKey::from(&requester_secret); + let requester_secret = RequestSecretKey::random(); + let requester_public_key = requester_secret.public_key(); - let service_shared_secret = - SharedSecret::from(service_secret.diffie_hellman(&requester_public_key)); - let requester_shared_secret = - SharedSecret::from(requester_secret.diffie_hellman(&service_public_key)); + let service_shared_secret = service_secret.diffie_hellman(&requester_public_key); + let requester_shared_secret = requester_secret.diffie_hellman(&service_public_key); let conditions = "{'some': 'condition'}"; let conditions_js: JsValue = Some(Conditions::new(conditions)).into(); @@ -705,7 +714,7 @@ fn threshold_decryption_request() { .unwrap(); // requester encrypts request to send to service - let requester_key = RequesterPublicKey::from(requester_public_key); + let requester_key = requester_secret.public_key(); let encrypted_request = request.encrypt(&requester_shared_secret, &requester_key); // mimic encrypted request going over the wire @@ -727,9 +736,9 @@ fn threshold_decryption_request() { assert_eq!(request, decrypted_request); // wrong key used - let random_secret_key = EphemeralSecret::random_from_rng(OsRng); + let random_secret_key = RequestSecretKey::random(); let random_shared_secret = - SharedSecret::from(random_secret_key.diffie_hellman(&service_public_key)); + RequestSharedSecret::from(random_secret_key.diffie_hellman(&service_public_key)); assert!(encrypted_request_from_bytes .decrypt(&random_shared_secret) .is_err()); @@ -737,16 +746,14 @@ fn threshold_decryption_request() { #[wasm_bindgen_test] fn threshold_decryption_response() { - let service_secret = EphemeralSecret::random_from_rng(OsRng); - let service_public_key = DalekPublicKey::from(&service_secret); + let service_secret = RequestSecretKey::random(); + let service_public_key = service_secret.public_key(); - let requester_secret = EphemeralSecret::random_from_rng(OsRng); - let requester_public_key = DalekPublicKey::from(&requester_secret); + let requester_secret = RequestSecretKey::random(); + let requester_public_key = requester_secret.public_key(); - let service_shared_secret = - SharedSecret::from(service_secret.diffie_hellman(&requester_public_key)); - let requester_shared_secret = - SharedSecret::from(requester_secret.diffie_hellman(&service_public_key)); + let service_shared_secret = service_secret.diffie_hellman(&requester_public_key); + let requester_shared_secret = requester_secret.diffie_hellman(&service_public_key); let decryption_share = b"The Tyranny of Merit"; @@ -771,9 +778,8 @@ fn threshold_decryption_response() { ); // wrong secret key used - let random_secret_key = EphemeralSecret::random_from_rng(OsRng); - let random_shared_secret = - SharedSecret::from(random_secret_key.diffie_hellman(&service_public_key)); + let random_secret_key = RequestSecretKey::random(); + let random_shared_secret = random_secret_key.diffie_hellman(&service_public_key); assert!(encrypted_response_from_bytes .decrypt(&random_shared_secret) .is_err()); diff --git a/nucypher-core/Cargo.toml b/nucypher-core/Cargo.toml index 48bc2a34..7cacefef 100644 --- a/nucypher-core/Cargo.toml +++ b/nucypher-core/Cargo.toml @@ -13,13 +13,16 @@ categories = ["cryptography", "no-std"] umbral-pre = { version = "0.10.0", features = ["serde"] } ferveo = { package = "ferveo-pre-release", version = "0.1.0-alpha.8" } serde = { version = "1", default-features = false, features = ["derive"] } -generic-array = "0.14" +generic-array = { version="0.14", features = ["zeroize"] } sha3 = "0.10" rmp-serde = "1" serde_with = "1.14" hex = "0.4" -x25519-dalek = { version="2.0.0-rc.2", features = ["serde"] } +hkdf = "0.12.3" +sha2 = "0.10.6" +x25519-dalek = { version="2.0.0-rc.2", features = ["serde", "static_secrets"] } chacha20poly1305 = "0.10.1" - -[dev-dependencies] -rand_core = "0.6.4" \ No newline at end of file +zeroize = { version="1.6.0", features = ["derive"] } +rand_core = "0.6.4" +rand_chacha = "0.3.1" +rand = "0.8.5" \ No newline at end of file diff --git a/nucypher-core/src/dkg.rs b/nucypher-core/src/dkg.rs index 3e935b83..72e6fe89 100644 --- a/nucypher-core/src/dkg.rs +++ b/nucypher-core/src/dkg.rs @@ -1,13 +1,11 @@ use alloc::boxed::Box; use alloc::string::String; use core::fmt; -use generic_array::typenum::Unsigned; use ferveo::api::Ciphertext; +use generic_array::typenum::Unsigned; -use crate::conditions::{Conditions, Context}; use serde::{Deserialize, Serialize}; use umbral_pre::serde_bytes; // TODO should this be in umbral? -use x25519_dalek::{PublicKey, SharedSecret}; use crate::versioning::{ messagepack_deserialize, messagepack_serialize, DeserializationError, ProtocolObject, @@ -17,6 +15,9 @@ use crate::versioning::{ use chacha20poly1305::aead::{Aead, AeadCore, KeyInit, OsRng}; use chacha20poly1305::{ChaCha20Poly1305, Key, Nonce}; +use crate::conditions::{Conditions, Context}; +use crate::dkg::request_keys::{RequestPublicKey, RequestSharedSecret}; + /// Errors during encryption. #[derive(Debug)] pub enum EncryptionError { @@ -65,7 +66,7 @@ impl fmt::Display for DecryptionError { type NonceSize = ::NonceSize; fn encrypt_with_shared_secret( - shared_secret: &SharedSecret, + shared_secret: &RequestSharedSecret, plaintext: &[u8], ) -> Result, EncryptionError> { let key = Key::from_slice(shared_secret.as_ref()); @@ -80,7 +81,7 @@ fn encrypt_with_shared_secret( } fn decrypt_with_shared_secret( - shared_secret: &SharedSecret, + shared_secret: &RequestSharedSecret, ciphertext: &[u8], ) -> Result, DecryptionError> { let nonce_size = ::to_usize(); @@ -108,6 +109,279 @@ pub enum FerveoVariant { PRECOMPUTED, } +pub mod request_keys { + use alloc::boxed::Box; + use alloc::string::String; + use core::fmt; + use generic_array::typenum::Unsigned; + use generic_array::{typenum::U32, ArrayLength, GenericArray}; + use hkdf::Hkdf; + use rand::SeedableRng; + use rand_chacha::ChaCha20Rng; + use rand_core::{CryptoRng, OsRng, RngCore}; + use serde::{Deserialize, Serialize}; + use sha2::Sha256; + use x25519_dalek::{PublicKey, SharedSecret, StaticSecret}; + + use crate::secret_box::SecretBox; + use zeroize::ZeroizeOnDrop; + + use crate::versioning::{ + messagepack_deserialize, messagepack_serialize, ProtocolObject, ProtocolObjectInner, + }; + + /// A Diffie-Hellman shared secret + #[derive(ZeroizeOnDrop)] + pub struct RequestSharedSecret(pub(crate) SharedSecret); + + /// Implementation of Diffie-Hellman shared secret + impl RequestSharedSecret { + /// Create new shared secret from underlying library. + pub fn new(shared_secret: SharedSecret) -> Self { + Self(shared_secret) + } + + /// Convert this shared secret to a byte array. + #[inline] + pub fn to_bytes(&self) -> [u8; 32] { + self.0.to_bytes() + } + + /// View this shared secret key as a byte array. + #[inline] + pub fn as_bytes(&self) -> &[u8; 32] { + self.0.as_bytes() + } + } + + impl AsRef<[u8]> for RequestSharedSecret { + /// View this shared secret key as a byte array. + #[inline] + fn as_ref(&self) -> &[u8] { + self.0.as_bytes() + } + } + + impl fmt::Display for RequestSharedSecret { + /// Format shared secret information. + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "RequestSharedSecret...") + } + } + + /// A request public key. + #[derive(PartialEq, Eq, Hash, Copy, Clone, Debug, Serialize, Deserialize)] + pub struct RequestPublicKey(pub(crate) PublicKey); + + /// Implementation of request public key + impl RequestPublicKey { + /// Convert this public key to a byte array. + #[inline] + pub fn to_bytes(&self) -> [u8; 32] { + self.0.to_bytes() + } + + /// View this public key as a byte array. + #[inline] + pub fn as_bytes(&self) -> &[u8; 32] { + self.0.as_bytes() + } + } + + impl AsRef<[u8]> for RequestPublicKey { + /// View this public key as a byte array. + #[inline] + fn as_ref(&self) -> &[u8] { + self.0.as_bytes() + } + } + + impl fmt::Display for RequestPublicKey { + /// Format public key information. + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let bytes = self.as_ref(); + let bytes = if bytes.len() > 8 { &bytes[..8] } else { bytes }; + write!(f, "RequestPublicKey: {}", hex::encode(bytes),) + } + } + + impl<'a> From<&'a RequestSecretKey> for RequestPublicKey { + /// Compute corresponding [`RequestPublicKey`]. + fn from(secret: &'a RequestSecretKey) -> RequestPublicKey { + let public_key = PublicKey::from(&secret.0); + RequestPublicKey(public_key) + } + } + + impl<'a> ProtocolObjectInner<'a> for RequestPublicKey { + fn version() -> (u16, u16) { + (1, 0) + } + + fn brand() -> [u8; 4] { + *b"TRPk" + } + + 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 RequestPublicKey {} + + /// A request secret key. + #[derive(ZeroizeOnDrop)] + pub struct RequestSecretKey(pub(crate) StaticSecret); + + impl RequestSecretKey { + /// Perform diffie-hellman + pub fn diffie_hellman(&self, their_public_key: &RequestPublicKey) -> RequestSharedSecret { + let shared_secret = self.0.diffie_hellman(&their_public_key.0); + RequestSharedSecret(shared_secret) + } + + /// Create secret key from rng. + pub fn random_from_rng(csprng: T) -> Self { + let secret_key = StaticSecret::random_from_rng(csprng); + Self(secret_key) + } + + /// Create random secret key. + pub fn random() -> Self { + Self::random_from_rng(OsRng) + } + + /// Returns a public key corresponding to this secret key. + pub fn public_key(&self) -> RequestPublicKey { + RequestPublicKey::from(self) + } + } + + impl fmt::Display for RequestSecretKey { + /// Format information above secret key. + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "RequestSecretKey:...") + } + } + + pub(crate) fn kdf>( + seed: &[u8], + salt: Option<&[u8]>, + info: Option<&[u8]>, + ) -> SecretBox> { + let hk = Hkdf::::new(salt, seed); + + let mut okm = SecretBox::new(GenericArray::::default()); + + let def_info = info.unwrap_or(&[]); + + // We can only get an error here if `S` is too large, and it's known at compile-time. + hk.expand(def_info, okm.as_mut_secret()).unwrap(); + + okm + } + + type SecretKeyFactorySeedSize = U32; // the size of the seed material for key derivation + type RequestKeyFactoryDerivedSize = U32; // the size of the derived key (before hashing to scalar) + type RequestKeyFactorySeed = GenericArray; + + /// Error thrown when invalid random seed provided for creating key factory. + pub struct InvalidRequestFactorySeedLengthError; + + impl fmt::Display for InvalidRequestFactorySeedLengthError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "Invalid seed length") + } + } + + /// This class handles keyring material for request keys, by allowing deterministic + /// derivation of `RequestSecretKey` objects based on labels. + #[derive(Clone, ZeroizeOnDrop, PartialEq)] + pub struct RequestKeyFactory(SecretBox); + + impl RequestKeyFactory { + /// Creates a secret key factory using the given RNG. + pub fn random_with_rng(rng: &mut (impl CryptoRng + RngCore)) -> Self { + let mut bytes = SecretBox::new(RequestKeyFactorySeed::default()); + rng.fill_bytes(bytes.as_mut_secret()); + Self(bytes) + } + + /// Creates a secret key factory using the default RNG. + pub fn random() -> Self { + Self::random_with_rng(&mut OsRng) + } + + /// Returns the seed size required by + pub fn seed_size() -> usize { + SecretKeyFactorySeedSize::to_usize() + } + + /// Creates a RequestKeyFactory factory using the given random bytes. + /// + /// **Warning:** make sure the given seed has been obtained + /// from a cryptographically secure source of randomness! + pub fn from_secure_randomness( + seed: &[u8], + ) -> Result { + if seed.len() != Self::seed_size() { + // TODO: should I just panic! here instead? + return Err(InvalidRequestFactorySeedLengthError); + } + Ok(Self(SecretBox::new(*RequestKeyFactorySeed::from_slice( + seed, + )))) + } + + /// Creates an untyped bytestring deterministically from the given label. + /// This can be used externally to seed some kind of a secret key. + pub fn make_secret( + &self, + label: &[u8], + ) -> SecretBox> { + let prefix = b"REQUEST_SECRET_DERIVATION/"; + let info = [prefix, label].concat(); + kdf::(self.0.as_secret(), None, Some(&info)) + } + + /// Creates a `RequestSecretKey` deterministically from the given label. + pub fn make_key(&self, label: &[u8]) -> RequestSecretKey { + let prefix = b"REQUEST_KEY_DERIVATION/"; + let info = [prefix, label].concat(); + let seed = kdf::(self.0.as_secret(), None, Some(&info)); + let rng = + ChaCha20Rng::from_seed(<[u8; 32]>::try_from(seed.as_secret().as_slice()).unwrap()); + RequestSecretKey::random_from_rng(rng) + } + + /// Creates a `RequestKeyFactory` deterministically from the given label. + pub fn make_factory(&self, label: &[u8]) -> Self { + let prefix = b"REQUEST_KEY_FACTORY_DERIVATION/"; + let info = [prefix, label].concat(); + let derived_seed = + kdf::(self.0.as_secret(), None, Some(&info)); + Self(derived_seed) + } + } + + impl fmt::Display for RequestKeyFactory { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "SecretKeyFactory:...") + } + } +} + /// A request for an Ursula to derive a decryption share. #[derive(PartialEq, Eq, Debug, Clone, Serialize, Deserialize)] pub struct ThresholdDecryptionRequest { @@ -144,8 +418,8 @@ impl ThresholdDecryptionRequest { /// Encrypts the decryption request. pub fn encrypt( &self, - shared_secret: &SharedSecret, - requester_public_key: &PublicKey, + shared_secret: &RequestSharedSecret, + requester_public_key: &RequestPublicKey, ) -> EncryptedThresholdDecryptionRequest { EncryptedThresholdDecryptionRequest::new(self, shared_secret, requester_public_key) } @@ -182,7 +456,7 @@ pub struct EncryptedThresholdDecryptionRequest { pub ritual_id: u16, /// Public key of requester - pub requester_public_key: PublicKey, + pub requester_public_key: RequestPublicKey, #[serde(with = "serde_bytes::as_base64")] /// Encrypted request @@ -192,8 +466,8 @@ pub struct EncryptedThresholdDecryptionRequest { impl EncryptedThresholdDecryptionRequest { fn new( request: &ThresholdDecryptionRequest, - shared_secret: &SharedSecret, - requester_public_key: &PublicKey, + shared_secret: &RequestSharedSecret, + requester_public_key: &RequestPublicKey, ) -> Self { let ciphertext = encrypt_with_shared_secret(shared_secret, &request.to_bytes()) .expect("encryption failed - out of memory?"); @@ -207,7 +481,7 @@ impl EncryptedThresholdDecryptionRequest { /// Decrypts the decryption request pub fn decrypt( &self, - shared_secret: &SharedSecret, + shared_secret: &RequestSharedSecret, ) -> Result { let decryption_request_bytes = decrypt_with_shared_secret(shared_secret, &self.ciphertext)?; let decryption_request = ThresholdDecryptionRequest::from_bytes(&decryption_request_bytes) @@ -257,7 +531,10 @@ impl ThresholdDecryptionResponse { } /// Encrypts the decryption response. - pub fn encrypt(&self, shared_secret: &SharedSecret) -> EncryptedThresholdDecryptionResponse { + pub fn encrypt( + &self, + shared_secret: &RequestSharedSecret, + ) -> EncryptedThresholdDecryptionResponse { EncryptedThresholdDecryptionResponse::new(self, shared_secret) } } @@ -294,7 +571,7 @@ pub struct EncryptedThresholdDecryptionResponse { } impl EncryptedThresholdDecryptionResponse { - fn new(response: &ThresholdDecryptionResponse, shared_secret: &SharedSecret) -> Self { + fn new(response: &ThresholdDecryptionResponse, shared_secret: &RequestSharedSecret) -> Self { let ciphertext = encrypt_with_shared_secret(shared_secret, &response.to_bytes()) .expect("encryption failed - out of memory?"); Self { ciphertext } @@ -303,7 +580,7 @@ impl EncryptedThresholdDecryptionResponse { /// Decrypts the decryption request pub fn decrypt( &self, - shared_secret: &SharedSecret, + shared_secret: &RequestSharedSecret, ) -> Result { let decryption_response_bytes = decrypt_with_shared_secret(shared_secret, &self.ciphertext)?; @@ -340,26 +617,27 @@ impl<'a> ProtocolObject<'a> for EncryptedThresholdDecryptionResponse {} #[cfg(test)] mod tests { - use x25519_dalek::{EphemeralSecret, PublicKey}; use ferveo::api::{encrypt as ferveo_encrypt, DkgPublicKey, SecretBox}; use crate::{ Conditions, Context, EncryptedThresholdDecryptionRequest, - EncryptedThresholdDecryptionResponse, FerveoVariant, ProtocolObject, + EncryptedThresholdDecryptionResponse, FerveoVariant, ProtocolObject, RequestKeyFactory, ThresholdDecryptionRequest, ThresholdDecryptionResponse, }; use generic_array::typenum::Unsigned; - use crate::dkg::{decrypt_with_shared_secret, DecryptionError, NonceSize}; - use rand_core::OsRng; + use crate::dkg::request_keys::RequestSecretKey; + use crate::dkg::{ + decrypt_with_shared_secret, encrypt_with_shared_secret, DecryptionError, NonceSize, + }; #[test] fn decryption_with_shared_secret() { - let service_secret = EphemeralSecret::random_from_rng(OsRng); + let service_secret = RequestSecretKey::random(); - let requester_secret = EphemeralSecret::random_from_rng(OsRng); - let requester_public_key = PublicKey::from(&requester_secret); + let requester_secret = RequestSecretKey::random(); + let requester_public_key = requester_secret.public_key(); let service_shared_secret = service_secret.diffie_hellman(&requester_public_key); @@ -373,15 +651,74 @@ mod tests { )); } + #[test] + fn request_key_factory() { + let secret_factory = RequestKeyFactory::random(); + + // ensure that shared secret derived from factory can be used correctly + let label_1 = b"label_1".to_vec().into_boxed_slice(); + let service_secret_key = secret_factory.make_key(&label_1.as_ref()); + let service_public_key = service_secret_key.public_key(); + + let label_2 = b"label_2".to_vec().into_boxed_slice(); + let requester_secret_key = secret_factory.make_key(&label_2.as_ref()); + let requester_public_key = requester_secret_key.public_key(); + + let service_shared_secret = service_secret_key.diffie_hellman(&requester_public_key); + let requester_shared_secret = requester_secret_key.diffie_hellman(&service_public_key); + + let data_to_encrypt = b"The Tyranny of Merit".to_vec().into_boxed_slice(); + let ciphertext = + encrypt_with_shared_secret(&requester_shared_secret, &data_to_encrypt.as_ref()) + .unwrap(); + let decrypted_data = + decrypt_with_shared_secret(&service_shared_secret, &ciphertext).unwrap(); + assert_eq!(decrypted_data, data_to_encrypt); + + // ensure same key can be generated by the same factory using the same seed + let same_requester_secret_key = secret_factory.make_key(&label_2.as_ref()); + let same_requester_public_key = same_requester_secret_key.public_key(); + assert_eq!(requester_public_key, same_requester_public_key); + + // ensure different key generated using same seed but using different factory + let other_secret_factory = RequestKeyFactory::random(); + let not_same_requester_secret_key = other_secret_factory.make_key(&label_2.as_ref()); + let not_same_requester_public_key = not_same_requester_secret_key.public_key(); + assert_ne!(requester_public_key, not_same_requester_public_key); + + // ensure that two secret factories with the same seed generate the same keys + let secret_factory_label = b"seeded_secret_factory_label".to_vec().into_boxed_slice(); + let seeded_secret_factory_1 = secret_factory.make_factory(&secret_factory_label.as_ref()); + let seeded_secret_factory_2 = secret_factory.make_factory(&secret_factory_label.as_ref()); + + let key_label = b"seeded_factory_key_label".to_vec().into_boxed_slice(); + let sk_1 = seeded_secret_factory_1.make_key(&key_label); + let pk_1 = sk_1.public_key(); + + let sk_2 = seeded_secret_factory_2.make_key(&key_label); + let pk_2 = sk_2.public_key(); + + assert_eq!(pk_1, pk_2); + + // test secure randomness + let bytes = [0u8; 32]; + let factory = RequestKeyFactory::from_secure_randomness(&bytes); + assert!(factory.is_ok()); + + let bytes = [0u8; 31]; + let factory = RequestKeyFactory::from_secure_randomness(&bytes); + assert!(factory.is_err()); + } + #[test] fn threshold_decryption_request() { let ritual_id = 0; - let service_secret = EphemeralSecret::random_from_rng(OsRng); - let service_public_key = PublicKey::from(&service_secret); + let service_secret = RequestSecretKey::random(); + let service_public_key = service_secret.public_key(); - let requester_secret = EphemeralSecret::random_from_rng(OsRng); - let requester_public_key = PublicKey::from(&requester_secret); + let requester_secret = RequestSecretKey::random(); + let requester_public_key = requester_secret.public_key(); let service_shared_secret = service_secret.diffie_hellman(&requester_public_key); let requester_shared_secret = requester_secret.diffie_hellman(&service_public_key); @@ -420,7 +757,7 @@ mod tests { assert_eq!(decrypted_request, request); // wrong shared key used - let random_secret_key = EphemeralSecret::random_from_rng(OsRng); + let random_secret_key = RequestSecretKey::random(); let random_shared_secret = random_secret_key.diffie_hellman(&requester_public_key); assert!(encrypted_request_from_bytes .decrypt(&random_shared_secret) @@ -429,11 +766,11 @@ mod tests { #[test] fn threshold_decryption_response() { - let service_secret = EphemeralSecret::random_from_rng(OsRng); - let service_public_key = PublicKey::from(&service_secret); + let service_secret = RequestSecretKey::random(); + let service_public_key = service_secret.public_key(); - let requester_secret = EphemeralSecret::random_from_rng(OsRng); - let requester_public_key = PublicKey::from(&requester_secret); + let requester_secret = RequestSecretKey::random(); + let requester_public_key = requester_secret.public_key(); let service_shared_secret = service_secret.diffie_hellman(&requester_public_key); let requester_shared_secret = requester_secret.diffie_hellman(&service_public_key); @@ -461,7 +798,7 @@ mod tests { ); // wrong shared key used - let random_secret_key = EphemeralSecret::random_from_rng(OsRng); + let random_secret_key = RequestSecretKey::random(); let random_shared_secret = random_secret_key.diffie_hellman(&requester_public_key); assert!(encrypted_response_from_bytes .decrypt(&random_shared_secret) diff --git a/nucypher-core/src/lib.rs b/nucypher-core/src/lib.rs index 94739e54..0b7554e9 100644 --- a/nucypher-core/src/lib.rs +++ b/nucypher-core/src/lib.rs @@ -18,6 +18,7 @@ mod node_metadata; mod reencryption; mod retrieval_kit; mod revocation_order; +mod secret_box; mod treasure_map; mod versioning; @@ -27,8 +28,9 @@ pub struct VerificationError; pub use address::Address; pub use conditions::{Conditions, Context}; pub use dkg::{ - EncryptedThresholdDecryptionRequest, EncryptedThresholdDecryptionResponse, FerveoVariant, - ThresholdDecryptionRequest, ThresholdDecryptionResponse, + request_keys::{RequestKeyFactory, RequestPublicKey, RequestSecretKey, RequestSharedSecret}, + DecryptionError, EncryptedThresholdDecryptionRequest, EncryptedThresholdDecryptionResponse, + EncryptionError, FerveoVariant, ThresholdDecryptionRequest, ThresholdDecryptionResponse, }; pub use fleet_state::FleetStateChecksum; pub use hrac::HRAC; diff --git a/nucypher-core/src/secret_box.rs b/nucypher-core/src/secret_box.rs new file mode 100644 index 00000000..bc9fe583 --- /dev/null +++ b/nucypher-core/src/secret_box.rs @@ -0,0 +1,76 @@ +/* +This module implements a similar API to what the crate `secrecy` provides. +So, why our own implementation? + +First `secrecy::Secret` does not put its contents in a `Box`. +Using `Box` is a general recommendation of working with secret data, +because it prevents the compiler from putting it on stack, thus avoiding possible copies on borrow. + +Now, one could use `secrecy::Secret>`. +The problem here is that `secrecy::Secret` requires its type parameter to implement `Zeroize`. +This means that for a foreign type `F` (even if it does implement `Zeroize`) +we need to define `impl Zeroize for Box`. +But the compiler does not allow impls of foreign traits on foreign types. +This means that we also need to wrap `F` in a local type, impl `Zeroize` for the wrapper, +and then for the box of the wrapper. +This is too much boilerplate. + +Additionally, `secrecy::Secret>` means that after each `expose_secret()` +we will need to deal with opening the `Box` as well. +It's an inconvenience, albeit a minor one. + +The situation may improve in the future, and `secrecy` will actually become usable. +*/ + +use alloc::boxed::Box; + +use zeroize::{Zeroize, ZeroizeOnDrop}; + +/// A container for secret data. +/// Makes the usage of secret data explicit and easy to track, +/// prevents the secret data from being put on stack, +/// and zeroizes the contents on drop. +#[derive(Clone)] // No Debug derivation, to avoid exposing the secret data accidentally. +pub struct SecretBox(Box) +where + T: Zeroize + Clone; + +impl PartialEq> for SecretBox { + fn eq(&self, other: &Self) -> bool { + self.0 == other.0 + } +} + +impl SecretBox +where + T: Zeroize + Clone, +{ + pub(crate) fn new(val: T) -> Self { + Self(Box::new(val)) + } + + /// Returns an immutable reference to the secret data. + pub fn as_secret(&self) -> &T { + self.0.as_ref() + } + + /// Returns a mutable reference to the secret data. + pub fn as_mut_secret(&mut self) -> &mut T { + self.0.as_mut() + } +} + +impl Drop for SecretBox +where + T: Zeroize + Clone, +{ + fn drop(&mut self) { + self.0.zeroize() + } +} + +// Alternatively, it could be derived automatically, +// but there's some compilation problem in the macro. +// See https://github.com/RustCrypto/utils/issues/786 +// So we're asserting that this object is zeroized on drop, since there is a Drop impl just above. +impl ZeroizeOnDrop for SecretBox where T: Zeroize + Clone {} From e45e56634b15f22d4e953fcbc17892e4189ca41e Mon Sep 17 00:00:00 2001 From: derekpierre Date: Mon, 29 May 2023 21:10:23 -0400 Subject: [PATCH 05/15] Update changelog for 0.9.0 (Unreleased). --- CHANGELOG.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b99ca19c..14facd02 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,20 +3,23 @@ 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.9.0] - Unreleased ### Added - Re-exported `ferveo` Python and WASM bindings. ([#58]) +- - Added `RequestSharedSecret`, `RequestPublicKey`, `RequestSecretKey`, `RequestKeyFactory` as wrappers for underlying Curve 25519 key functionality. ([#54]) ### Changed - Replaced opaque types with native `ferveo` types. ([#53]) +- Removed `E2EThresholdDecryptionRequest` type and bindings. ([54]) +- Modified `EncryptedThresholdDecryptionRequest`/`EncryptedThresholdDecryptionResponse` to use Curve 25519 keys instead of Umbral keys for encryption/decryption. ([#54]) [#53]: https://github.com/nucypher/nucypher-core/pull/53 [#58]: https://github.com/nucypher/nucypher-core/pull/58 +[#54]: https://github.com/nucypher/nucypher-core/pull/54 ## [0.8.0] - 2023-05-23 @@ -38,7 +41,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added -- Add `ThresholdDecryptionRequest`/`ThresholdDecryptionResponse` types and bindings. ([#48])` +- Add `ThresholdDecryptionRequest`/`ThresholdDecryptionResponse` types and bindings. ([#48]) - Add `ferveo_public_key` field to `NodeMetadataPayload`. ([#48]) From 74a8a45a7ec8689b892b47d6889e6f432ce70bf5 Mon Sep 17 00:00:00 2001 From: derekpierre Date: Mon, 29 May 2023 21:32:15 -0400 Subject: [PATCH 06/15] Add Rust pre-commit hooks for `nucypher-core` repos. --- .pre-commit-config.yaml | 8 ++++++++ CHANGELOG.md | 6 ++++-- 2 files changed, 12 insertions(+), 2 deletions(-) create mode 100644 .pre-commit-config.yaml diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 00000000..ba2ac980 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,8 @@ +repos: + - repo: https://github.com/doublify/pre-commit-rust + rev: v1.0 + hooks: + - id: fmt + - id: cargo-check + - id: clippy + args: ["--all", "--all-features", "--", "-D", "warnings"] diff --git a/CHANGELOG.md b/CHANGELOG.md index 14facd02..36f80c51 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,9 +8,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Re-exported `ferveo` Python and WASM bindings. ([#58]) -- - Added `RequestSharedSecret`, `RequestPublicKey`, `RequestSecretKey`, `RequestKeyFactory` as wrappers for underlying Curve 25519 key functionality. ([#54]) +- Added `RequestSharedSecret`, `RequestPublicKey`, `RequestSecretKey`, `RequestKeyFactory` as wrappers for underlying Curve 25519 key functionality. ([#54]) +- Added Rust `pre-commit` hooks for repos. -### Changed + +### Changed - Replaced opaque types with native `ferveo` types. ([#53]) - Removed `E2EThresholdDecryptionRequest` type and bindings. ([54]) From 0a861aa76d0790348bfd83fff178742306e6728d Mon Sep 17 00:00:00 2001 From: derekpierre Date: Tue, 30 May 2023 08:33:58 -0400 Subject: [PATCH 07/15] Add changelog entry about adding secret box functionality. --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 36f80c51..f8535dd6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Re-exported `ferveo` Python and WASM bindings. ([#58]) - Added `RequestSharedSecret`, `RequestPublicKey`, `RequestSecretKey`, `RequestKeyFactory` as wrappers for underlying Curve 25519 key functionality. ([#54]) - Added Rust `pre-commit` hooks for repos. +- Added `secret_box` functionality. ### Changed From 2108f813aa4eece9a6532ebd84c896be41a99795 Mon Sep 17 00:00:00 2001 From: derekpierre Date: Tue, 30 May 2023 10:04:59 -0400 Subject: [PATCH 08/15] Fix incorrect python bindings. --- nucypher-core-python/nucypher_core/__init__.pyi | 8 ++++---- nucypher-core/src/dkg.rs | 9 +++++---- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/nucypher-core-python/nucypher_core/__init__.pyi b/nucypher-core-python/nucypher_core/__init__.pyi index d9eb9e8e..110db05f 100644 --- a/nucypher-core-python/nucypher_core/__init__.pyi +++ b/nucypher-core-python/nucypher_core/__init__.pyi @@ -423,7 +423,7 @@ class ThresholdDecryptionRequest: ciphertext: Ciphertext - def encrypt(self, request_encrypting_key: PublicKey, response_encrypting_key: PublicKey) -> EncryptedThresholdDecryptionRequest: + def encrypt(self, shared_secret: RequestSharedSecret, requester_public_key: RequestPublicKey) -> EncryptedThresholdDecryptionRequest: ... @staticmethod @@ -441,7 +441,7 @@ class EncryptedThresholdDecryptionRequest: def decrypt( self, - sk: SecretKey + shared_secret: RequestSharedSecret ) -> ThresholdDecryptionRequest: ... @@ -460,7 +460,7 @@ class ThresholdDecryptionResponse: decryption_share: bytes - def encrypt(self, encrypting_key: PublicKey) -> EncryptedThresholdDecryptionResponse: + def encrypt(self, shared_secret: RequestSharedSecret) -> EncryptedThresholdDecryptionResponse: ... @staticmethod @@ -475,7 +475,7 @@ class EncryptedThresholdDecryptionResponse: def decrypt( self, - sk: SecretKey + shared_secret: RequestSharedSecret ) -> ThresholdDecryptionResponse: ... diff --git a/nucypher-core/src/dkg.rs b/nucypher-core/src/dkg.rs index 72e6fe89..37d253a4 100644 --- a/nucypher-core/src/dkg.rs +++ b/nucypher-core/src/dkg.rs @@ -293,7 +293,7 @@ pub mod request_keys { } type SecretKeyFactorySeedSize = U32; // the size of the seed material for key derivation - type RequestKeyFactoryDerivedSize = U32; // the size of the derived key (before hashing to scalar) + type RequestKeyFactoryDerivedKeySize = U32; // the size of the derived key type RequestKeyFactorySeed = GenericArray; /// Error thrown when invalid random seed provided for creating key factory. @@ -349,17 +349,18 @@ pub mod request_keys { pub fn make_secret( &self, label: &[u8], - ) -> SecretBox> { + ) -> SecretBox> { let prefix = b"REQUEST_SECRET_DERIVATION/"; let info = [prefix, label].concat(); - kdf::(self.0.as_secret(), None, Some(&info)) + kdf::(self.0.as_secret(), None, Some(&info)) } /// Creates a `RequestSecretKey` deterministically from the given label. pub fn make_key(&self, label: &[u8]) -> RequestSecretKey { let prefix = b"REQUEST_KEY_DERIVATION/"; let info = [prefix, label].concat(); - let seed = kdf::(self.0.as_secret(), None, Some(&info)); + let seed = + kdf::(self.0.as_secret(), None, Some(&info)); let rng = ChaCha20Rng::from_seed(<[u8; 32]>::try_from(seed.as_secret().as_slice()).unwrap()); RequestSecretKey::random_from_rng(rng) From 56c1c6ea57d8b448ce0d8294ab902f18d3c360c1 Mon Sep 17 00:00:00 2001 From: derekpierre Date: Tue, 30 May 2023 15:45:19 -0400 Subject: [PATCH 09/15] Fix request key python bindings and added ritual id member to ThresholdDecryptionResponse and EncryptedThresholdDecryptionResponse. Code cleanup. --- CHANGELOG.md | 2 +- Cargo.lock | 1 + .../nucypher_core/__init__.pyi | 6 ++- nucypher-core-python/src/lib.rs | 35 +++++++++++++---- nucypher-core-wasm/src/lib.rs | 24 ++++++++++-- nucypher-core-wasm/tests/wasm.rs | 26 +++++++------ nucypher-core/src/dkg.rs | 38 ++++++++++++------- 7 files changed, 94 insertions(+), 38 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f8535dd6..53dbfe3e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,7 +18,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Replaced opaque types with native `ferveo` types. ([#53]) - Removed `E2EThresholdDecryptionRequest` type and bindings. ([54]) - Modified `EncryptedThresholdDecryptionRequest`/`EncryptedThresholdDecryptionResponse` to use Curve 25519 keys instead of Umbral keys for encryption/decryption. ([#54]) - +- Modified `ThresholdDecryptionResponse`/`EncryptedThresholdDecryptionResponse` to include `ritual_id` member in struct. [#53]: https://github.com/nucypher/nucypher-core/pull/53 [#58]: https://github.com/nucypher/nucypher-core/pull/58 diff --git a/Cargo.lock b/Cargo.lock index db368206..0b9ccec4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1597,6 +1597,7 @@ dependencies = [ "proc-macro2", "quote", "syn 1.0.109", + "wasm-bindgen", ] [[package]] diff --git a/nucypher-core-python/nucypher_core/__init__.pyi b/nucypher-core-python/nucypher_core/__init__.pyi index 110db05f..adb0d604 100644 --- a/nucypher-core-python/nucypher_core/__init__.pyi +++ b/nucypher-core-python/nucypher_core/__init__.pyi @@ -455,11 +455,13 @@ class EncryptedThresholdDecryptionRequest: class ThresholdDecryptionResponse: - def __init__(self, decryption_share: bytes): + def __init__(self, ritual_id: int, decryption_share: bytes): ... decryption_share: bytes + ritual_id: int + def encrypt(self, shared_secret: RequestSharedSecret) -> EncryptedThresholdDecryptionResponse: ... @@ -473,6 +475,8 @@ class ThresholdDecryptionResponse: class EncryptedThresholdDecryptionResponse: + ritual_id: int + def decrypt( self, shared_secret: RequestSharedSecret diff --git a/nucypher-core-python/src/lib.rs b/nucypher-core-python/src/lib.rs index 32906b3c..b05dc3d3 100644 --- a/nucypher-core-python/src/lib.rs +++ b/nucypher-core-python/src/lib.rs @@ -643,7 +643,7 @@ pub struct RequestSharedSecret { } #[pyclass(module = "nucypher_core")] -#[derive(derive_more::From, derive_more::AsRef)] +#[derive(Clone, PartialEq, Eq, derive_more::From, derive_more::AsRef)] pub struct RequestPublicKey { backend: nucypher_core::RequestPublicKey, } @@ -659,6 +659,14 @@ impl RequestPublicKey { to_bytes(self) } + fn __richcmp__(&self, other: &Self, op: CompareOp) -> PyResult { + richcmp(self, other, op) + } + + fn __hash__(&self) -> PyResult { + hash("RequestPublicKey", self) + } + fn __str__(&self) -> PyResult { Ok(format!("{}", self.backend)) } @@ -679,7 +687,6 @@ impl RequestSecretKey { }) } - #[getter] pub fn public_key(&self) -> RequestPublicKey { RequestPublicKey { backend: self.backend.public_key(), @@ -831,10 +838,11 @@ impl ThresholdDecryptionRequest { shared_secret: &RequestSharedSecret, requester_public_key: &RequestPublicKey, ) -> EncryptedThresholdDecryptionRequest { + let encrypted_request = self + .backend + .encrypt(shared_secret.as_ref(), requester_public_key.as_ref()); EncryptedThresholdDecryptionRequest { - backend: self - .backend - .encrypt(shared_secret.as_ref(), requester_public_key.as_ref()), + backend: encrypted_request, } } @@ -865,8 +873,9 @@ impl EncryptedThresholdDecryptionRequest { self.backend.ritual_id } + #[getter] pub fn requester_public_key(&self) -> RequestPublicKey { - RequestPublicKey::from(self.backend.requester_public_key) + self.backend.requester_public_key.into() } pub fn decrypt( @@ -902,12 +911,17 @@ pub struct ThresholdDecryptionResponse { #[pymethods] impl ThresholdDecryptionResponse { #[new] - pub fn new(decryption_share: &[u8]) -> Self { + pub fn new(ritual_id: u16, decryption_share: &[u8]) -> Self { ThresholdDecryptionResponse { - backend: nucypher_core::ThresholdDecryptionResponse::new(decryption_share), + backend: nucypher_core::ThresholdDecryptionResponse::new(ritual_id, decryption_share), } } + #[getter] + pub fn ritual_id(&self) -> u16 { + self.backend.ritual_id + } + #[getter] pub fn decryption_share(&self) -> &[u8] { self.backend.decryption_share.as_ref() @@ -944,6 +958,11 @@ pub struct EncryptedThresholdDecryptionResponse { #[pymethods] impl EncryptedThresholdDecryptionResponse { + #[getter] + pub fn ritual_id(&self) -> u16 { + self.backend.ritual_id + } + pub fn decrypt( &self, shared_secret: &RequestSharedSecret, diff --git a/nucypher-core-wasm/src/lib.rs b/nucypher-core-wasm/src/lib.rs index 5956ab88..a896f27b 100644 --- a/nucypher-core-wasm/src/lib.rs +++ b/nucypher-core-wasm/src/lib.rs @@ -571,6 +571,10 @@ impl RequestPublicKey { pub fn to_string(&self) -> String { format!("{}", self.0) } + + pub fn equals(&self, other: &RequestPublicKey) -> bool { + self.0 == other.0 + } } #[wasm_bindgen] @@ -660,7 +664,7 @@ pub struct ThresholdDecryptionRequest(nucypher_core::ThresholdDecryptionRequest) impl ThresholdDecryptionRequest { #[wasm_bindgen(constructor)] pub fn new( - id: u16, + ritual_id: u16, variant: u8, ciphertext: &Ciphertext, conditions: &OptionConditions, @@ -676,7 +680,7 @@ impl ThresholdDecryptionRequest { }; Ok(Self(nucypher_core::ThresholdDecryptionRequest::new( - id, + ritual_id, ciphertext.as_ref(), typed_conditions.as_ref().map(|conditions| &conditions.0), typed_context.as_ref().map(|context| &context.0), @@ -776,12 +780,21 @@ pub struct ThresholdDecryptionResponse(nucypher_core::ThresholdDecryptionRespons #[wasm_bindgen] impl ThresholdDecryptionResponse { #[wasm_bindgen(constructor)] - pub fn new(decryption_share: &[u8]) -> Result { + pub fn new( + ritual_id: u16, + decryption_share: &[u8], + ) -> Result { Ok(Self(nucypher_core::ThresholdDecryptionResponse::new( + ritual_id, decryption_share, ))) } + #[wasm_bindgen(getter, js_name = ritualId)] + pub fn ritual_id(&self) -> u16 { + self.0.ritual_id + } + #[wasm_bindgen(getter, js_name = decryptionShare)] pub fn decryption_share(&self) -> Box<[u8]> { self.0.decryption_share.clone() @@ -817,6 +830,11 @@ pub struct EncryptedThresholdDecryptionResponse( #[wasm_bindgen] impl EncryptedThresholdDecryptionResponse { + #[wasm_bindgen(getter, js_name = ritualId)] + pub fn ritual_id(&self) -> u16 { + self.0.ritual_id + } + pub fn decrypt( &self, shared_secret: &RequestSharedSecret, diff --git a/nucypher-core-wasm/tests/wasm.rs b/nucypher-core-wasm/tests/wasm.rs index 36b18e9d..5d407bd9 100644 --- a/nucypher-core-wasm/tests/wasm.rs +++ b/nucypher-core-wasm/tests/wasm.rs @@ -691,10 +691,6 @@ fn threshold_decryption_request() { let service_public_key = service_secret.public_key(); let requester_secret = RequestSecretKey::random(); - let requester_public_key = requester_secret.public_key(); - - let service_shared_secret = service_secret.diffie_hellman(&requester_public_key); - let requester_shared_secret = requester_secret.diffie_hellman(&service_public_key); let conditions = "{'some': 'condition'}"; let conditions_js: JsValue = Some(Conditions::new(conditions)).into(); @@ -714,8 +710,9 @@ fn threshold_decryption_request() { .unwrap(); // requester encrypts request to send to service - let requester_key = requester_secret.public_key(); - let encrypted_request = request.encrypt(&requester_shared_secret, &requester_key); + let requester_shared_secret = requester_secret.diffie_hellman(&service_public_key); + let requester_public_key = requester_secret.public_key(); + let encrypted_request = request.encrypt(&requester_shared_secret, &requester_public_key); // mimic encrypted request going over the wire let encrypted_request_bytes = encrypted_request.to_bytes(); @@ -726,10 +723,12 @@ fn threshold_decryption_request() { assert_eq!(encrypted_request_from_bytes.ritual_id(), ritual_id); assert_eq!( encrypted_request_from_bytes.requester_public_key(), - requester_key + requester_public_key ); // service decrypts request + let service_shared_secret = + service_secret.diffie_hellman(&encrypted_request_from_bytes.requester_public_key()); let decrypted_request = encrypted_request_from_bytes .decrypt(&service_shared_secret) .unwrap(); @@ -746,21 +745,21 @@ fn threshold_decryption_request() { #[wasm_bindgen_test] fn threshold_decryption_response() { + let ritual_id = 10; + let service_secret = RequestSecretKey::random(); - let service_public_key = service_secret.public_key(); let requester_secret = RequestSecretKey::random(); let requester_public_key = requester_secret.public_key(); - let service_shared_secret = service_secret.diffie_hellman(&requester_public_key); - let requester_shared_secret = requester_secret.diffie_hellman(&service_public_key); - let decryption_share = b"The Tyranny of Merit"; - let response = ThresholdDecryptionResponse::new(decryption_share).unwrap(); + let response = ThresholdDecryptionResponse::new(ritual_id, decryption_share).unwrap(); // service encrypts response to send back + let service_shared_secret = service_secret.diffie_hellman(&requester_public_key); let encrypted_response = response.encrypt(&service_shared_secret); + assert_eq!(encrypted_response.ritual_id(), ritual_id); // mimic serialization/deserialization over the wire let encrypted_response_bytes = encrypted_response.to_bytes(); @@ -768,10 +767,13 @@ fn threshold_decryption_response() { EncryptedThresholdDecryptionResponse::from_bytes(&encrypted_response_bytes).unwrap(); // requester decrypts response + let service_public_key = service_secret.public_key(); + let requester_shared_secret = requester_secret.diffie_hellman(&service_public_key); let decrypted_response = encrypted_response_from_bytes .decrypt(&requester_shared_secret) .unwrap(); assert_eq!(response, decrypted_response); + assert_eq!(response.ritual_id(), ritual_id); assert_eq!( response.decryption_share(), decrypted_response.decryption_share() diff --git a/nucypher-core/src/dkg.rs b/nucypher-core/src/dkg.rs index 37d253a4..d3f9fb98 100644 --- a/nucypher-core/src/dkg.rs +++ b/nucypher-core/src/dkg.rs @@ -518,6 +518,9 @@ impl<'a> ProtocolObject<'a> for EncryptedThresholdDecryptionRequest {} /// A response from Ursula with a derived decryption share. #[derive(PartialEq, Eq, Debug, Serialize, Deserialize, Clone)] pub struct ThresholdDecryptionResponse { + /// The ID of the ritual. + pub ritual_id: u16, + /// The decryption share to include in the response. #[serde(with = "serde_bytes::as_base64")] pub decryption_share: Box<[u8]>, @@ -525,8 +528,9 @@ pub struct ThresholdDecryptionResponse { impl ThresholdDecryptionResponse { /// Creates and a new decryption response. - pub fn new(decryption_share: &[u8]) -> Self { + pub fn new(ritual_id: u16, decryption_share: &[u8]) -> Self { ThresholdDecryptionResponse { + ritual_id, decryption_share: decryption_share.to_vec().into(), } } @@ -567,6 +571,9 @@ impl<'a> ProtocolObject<'a> for ThresholdDecryptionResponse {} /// An encrypted response from Ursula with a derived decryption share. #[derive(PartialEq, Debug, Clone, Serialize, Deserialize)] pub struct EncryptedThresholdDecryptionResponse { + /// The ID of the ritual. + pub ritual_id: u16, + #[serde(with = "serde_bytes::as_base64")] ciphertext: Box<[u8]>, } @@ -575,7 +582,10 @@ impl EncryptedThresholdDecryptionResponse { fn new(response: &ThresholdDecryptionResponse, shared_secret: &RequestSharedSecret) -> Self { let ciphertext = encrypt_with_shared_secret(shared_secret, &response.to_bytes()) .expect("encryption failed - out of memory?"); - Self { ciphertext } + Self { + ritual_id: response.ritual_id, + ciphertext, + } } /// Decrypts the decryption request @@ -716,14 +726,10 @@ mod tests { let ritual_id = 0; let service_secret = RequestSecretKey::random(); - let service_public_key = service_secret.public_key(); let requester_secret = RequestSecretKey::random(); let requester_public_key = requester_secret.public_key(); - let service_shared_secret = service_secret.diffie_hellman(&requester_public_key); - let requester_shared_secret = requester_secret.diffie_hellman(&service_public_key); - let dkg_pk = DkgPublicKey::random(); let message = "The Tyranny of Merit".as_bytes().to_vec(); let aad = "my-add".as_bytes(); @@ -738,6 +744,8 @@ mod tests { ); // requester encrypts request to send to service + let service_public_key = service_secret.public_key(); + let requester_shared_secret = requester_secret.diffie_hellman(&service_public_key); let encrypted_request = request.encrypt(&requester_shared_secret, &requester_public_key); // mimic serialization/deserialization over the wire @@ -752,6 +760,8 @@ mod tests { ); // service decrypts request + let service_shared_secret = + service_secret.diffie_hellman(&encrypted_request_from_bytes.requester_public_key); let decrypted_request = encrypted_request_from_bytes .decrypt(&service_shared_secret) .unwrap(); @@ -767,21 +777,20 @@ mod tests { #[test] fn threshold_decryption_response() { - let service_secret = RequestSecretKey::random(); - let service_public_key = service_secret.public_key(); + let ritual_id = 5; + let service_secret = RequestSecretKey::random(); let requester_secret = RequestSecretKey::random(); - let requester_public_key = requester_secret.public_key(); - - let service_shared_secret = service_secret.diffie_hellman(&requester_public_key); - let requester_shared_secret = requester_secret.diffie_hellman(&service_public_key); let decryption_share = b"The Tyranny of Merit"; - let response = ThresholdDecryptionResponse::new(decryption_share); + let response = ThresholdDecryptionResponse::new(ritual_id, decryption_share); // service encrypts response to send back + let requester_public_key = requester_secret.public_key(); + let service_shared_secret = service_secret.diffie_hellman(&requester_public_key); let encrypted_response = response.encrypt(&service_shared_secret); + assert_eq!(encrypted_response.ritual_id, ritual_id); // mimic serialization/deserialization over the wire let encrypted_response_bytes = encrypted_response.to_bytes(); @@ -789,10 +798,13 @@ mod tests { EncryptedThresholdDecryptionResponse::from_bytes(&encrypted_response_bytes).unwrap(); // requester decrypts response + let service_public_key = service_secret.public_key(); + let requester_shared_secret = requester_secret.diffie_hellman(&service_public_key); let decrypted_response = encrypted_response_from_bytes .decrypt(&requester_shared_secret) .unwrap(); assert_eq!(response, decrypted_response); + assert_eq!(response.ritual_id, ritual_id); assert_eq!( response.decryption_share, decrypted_response.decryption_share From 2b3e15b695fe43764484752833f265845d5b9a27 Mon Sep 17 00:00:00 2001 From: derekpierre Date: Thu, 1 Jun 2023 11:05:35 -0400 Subject: [PATCH 10/15] Respond to RFCs for #54. Change `diffie_hellman()` to `derive_shared_secret()`. Remove unnecessary `make_secret()` from RequestSecretFactory. General code cleanup. --- .../nucypher_core/__init__.pyi | 5 +- nucypher-core-python/src/lib.rs | 18 ++--- nucypher-core-wasm/src/lib.rs | 11 +-- nucypher-core-wasm/tests/wasm.rs | 14 ++-- nucypher-core/src/dkg.rs | 74 ++++++------------- nucypher-core/src/secret_box.rs | 16 ++++ 6 files changed, 56 insertions(+), 82 deletions(-) diff --git a/nucypher-core-python/nucypher_core/__init__.pyi b/nucypher-core-python/nucypher_core/__init__.pyi index adb0d604..d519a259 100644 --- a/nucypher-core-python/nucypher_core/__init__.pyi +++ b/nucypher-core-python/nucypher_core/__init__.pyi @@ -514,7 +514,7 @@ class RequestSecretKey: def public_key(self) -> RequestPublicKey: ... - def diffie_hellman(self, their_public_key: RequestPublicKey) -> RequestSharedSecret: + def derive_shared_secret(self, their_public_key: RequestPublicKey) -> RequestSharedSecret: ... @@ -532,9 +532,6 @@ class RequestKeyFactory: def from_secure_randomness(seed: bytes) -> RequestKeyFactory: ... - def make_secret(self, label: bytes) -> bytes: - ... - def make_key(self, label: bytes) -> RequestSecretKey: ... diff --git a/nucypher-core-python/src/lib.rs b/nucypher-core-python/src/lib.rs index b05dc3d3..94fe6d25 100644 --- a/nucypher-core-python/src/lib.rs +++ b/nucypher-core-python/src/lib.rs @@ -693,14 +693,14 @@ impl RequestSecretKey { } } - pub fn diffie_hellman(&self, their_public_key: &RequestPublicKey) -> RequestSharedSecret { + pub fn derive_shared_secret(&self, their_public_key: &RequestPublicKey) -> RequestSharedSecret { RequestSharedSecret { - backend: self.backend.diffie_hellman(their_public_key.as_ref()), + backend: self.backend.derive_shared_secret(their_public_key.as_ref()), } } - fn __str__(&self) -> PyResult { - Ok(format!("{}", self.backend)) + fn __str__(&self) -> String { + self.backend.to_string() } } @@ -731,12 +731,6 @@ impl RequestKeyFactory { Ok(Self { backend: factory }) } - pub fn make_secret(&self, label: &[u8]) -> PyObject { - let secret = self.backend.make_secret(label); - let bytes: &[u8] = secret.as_secret().as_ref(); - Python::with_gil(|py| PyBytes::new(py, bytes).into()) - } - pub fn make_key(&self, label: &[u8]) -> RequestSecretKey { RequestSecretKey { backend: self.backend.make_key(label), @@ -749,8 +743,8 @@ impl RequestKeyFactory { } } - fn __str__(&self) -> PyResult { - Ok(format!("{}", self.backend)) + fn __str__(&self) -> String { + self.backend.to_string() } } diff --git a/nucypher-core-wasm/src/lib.rs b/nucypher-core-wasm/src/lib.rs index a896f27b..5dd08e4e 100644 --- a/nucypher-core-wasm/src/lib.rs +++ b/nucypher-core-wasm/src/lib.rs @@ -595,8 +595,8 @@ impl RequestSecretKey { } #[wasm_bindgen(js_name = diffieHellman)] - pub fn diffie_hellman(&self, their_public_key: &RequestPublicKey) -> RequestSharedSecret { - RequestSharedSecret(self.0.diffie_hellman(their_public_key.as_ref())) + pub fn derive_shared_secret(&self, their_public_key: &RequestPublicKey) -> RequestSharedSecret { + RequestSharedSecret(self.0.derive_shared_secret(their_public_key.as_ref())) } #[allow(clippy::inherent_to_string)] @@ -628,13 +628,6 @@ impl RequestKeyFactory { .map_err(map_js_err) } - #[wasm_bindgen(js_name = makeSecret)] - pub fn make_secret(&self, label: &[u8]) -> Vec { - let secret = self.0.make_secret(label); - let bytes: &[u8] = secret.as_secret().as_ref(); - bytes.into() - } - #[wasm_bindgen(js_name = makeKey)] pub fn make_key(&self, label: &[u8]) -> RequestSecretKey { RequestSecretKey(self.0.make_key(label)) diff --git a/nucypher-core-wasm/tests/wasm.rs b/nucypher-core-wasm/tests/wasm.rs index 5d407bd9..615943ca 100644 --- a/nucypher-core-wasm/tests/wasm.rs +++ b/nucypher-core-wasm/tests/wasm.rs @@ -675,6 +675,8 @@ fn request_public_key() { let secret = RequestSecretKey::random(); let public_key = secret.public_key(); + assert_eq!(secret.public_key(), secret.public_key()); + // mimic transmission public key over the wire let serialized_public_key = public_key.to_bytes(); let deserialized_public_key = @@ -710,7 +712,7 @@ fn threshold_decryption_request() { .unwrap(); // requester encrypts request to send to service - let requester_shared_secret = requester_secret.diffie_hellman(&service_public_key); + let requester_shared_secret = requester_secret.derive_shared_secret(&service_public_key); let requester_public_key = requester_secret.public_key(); let encrypted_request = request.encrypt(&requester_shared_secret, &requester_public_key); @@ -728,7 +730,7 @@ fn threshold_decryption_request() { // service decrypts request let service_shared_secret = - service_secret.diffie_hellman(&encrypted_request_from_bytes.requester_public_key()); + service_secret.derive_shared_secret(&encrypted_request_from_bytes.requester_public_key()); let decrypted_request = encrypted_request_from_bytes .decrypt(&service_shared_secret) .unwrap(); @@ -737,7 +739,7 @@ fn threshold_decryption_request() { // wrong key used let random_secret_key = RequestSecretKey::random(); let random_shared_secret = - RequestSharedSecret::from(random_secret_key.diffie_hellman(&service_public_key)); + RequestSharedSecret::from(random_secret_key.derive_shared_secret(&service_public_key)); assert!(encrypted_request_from_bytes .decrypt(&random_shared_secret) .is_err()); @@ -757,7 +759,7 @@ fn threshold_decryption_response() { let response = ThresholdDecryptionResponse::new(ritual_id, decryption_share).unwrap(); // service encrypts response to send back - let service_shared_secret = service_secret.diffie_hellman(&requester_public_key); + let service_shared_secret = service_secret.derive_shared_secret(&requester_public_key); let encrypted_response = response.encrypt(&service_shared_secret); assert_eq!(encrypted_response.ritual_id(), ritual_id); @@ -768,7 +770,7 @@ fn threshold_decryption_response() { // requester decrypts response let service_public_key = service_secret.public_key(); - let requester_shared_secret = requester_secret.diffie_hellman(&service_public_key); + let requester_shared_secret = requester_secret.derive_shared_secret(&service_public_key); let decrypted_response = encrypted_response_from_bytes .decrypt(&requester_shared_secret) .unwrap(); @@ -781,7 +783,7 @@ fn threshold_decryption_response() { // wrong secret key used let random_secret_key = RequestSecretKey::random(); - let random_shared_secret = random_secret_key.diffie_hellman(&service_public_key); + let random_shared_secret = random_secret_key.derive_shared_secret(&service_public_key); assert!(encrypted_response_from_bytes .decrypt(&random_shared_secret) .is_err()); diff --git a/nucypher-core/src/dkg.rs b/nucypher-core/src/dkg.rs index d3f9fb98..ec335357 100644 --- a/nucypher-core/src/dkg.rs +++ b/nucypher-core/src/dkg.rs @@ -113,17 +113,17 @@ pub mod request_keys { use alloc::boxed::Box; use alloc::string::String; use core::fmt; - use generic_array::typenum::Unsigned; - use generic_array::{typenum::U32, ArrayLength, GenericArray}; - use hkdf::Hkdf; + use generic_array::{ + typenum::{Unsigned, U32}, + GenericArray, + }; use rand::SeedableRng; use rand_chacha::ChaCha20Rng; use rand_core::{CryptoRng, OsRng, RngCore}; use serde::{Deserialize, Serialize}; - use sha2::Sha256; use x25519_dalek::{PublicKey, SharedSecret, StaticSecret}; - use crate::secret_box::SecretBox; + use crate::secret_box::{kdf, SecretBox}; use zeroize::ZeroizeOnDrop; use crate::versioning::{ @@ -199,9 +199,7 @@ pub mod request_keys { impl fmt::Display for RequestPublicKey { /// Format public key information. fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let bytes = self.as_ref(); - let bytes = if bytes.len() > 8 { &bytes[..8] } else { bytes }; - write!(f, "RequestPublicKey: {}", hex::encode(bytes),) + write!(f, "RequestPublicKey: {}", hex::encode(&self.as_ref()[..8])) } } @@ -246,7 +244,10 @@ pub mod request_keys { impl RequestSecretKey { /// Perform diffie-hellman - pub fn diffie_hellman(&self, their_public_key: &RequestPublicKey) -> RequestSharedSecret { + pub fn derive_shared_secret( + &self, + their_public_key: &RequestPublicKey, + ) -> RequestSharedSecret { let shared_secret = self.0.diffie_hellman(&their_public_key.0); RequestSharedSecret(shared_secret) } @@ -275,23 +276,6 @@ pub mod request_keys { } } - pub(crate) fn kdf>( - seed: &[u8], - salt: Option<&[u8]>, - info: Option<&[u8]>, - ) -> SecretBox> { - let hk = Hkdf::::new(salt, seed); - - let mut okm = SecretBox::new(GenericArray::::default()); - - let def_info = info.unwrap_or(&[]); - - // We can only get an error here if `S` is too large, and it's known at compile-time. - hk.expand(def_info, okm.as_mut_secret()).unwrap(); - - okm - } - type SecretKeyFactorySeedSize = U32; // the size of the seed material for key derivation type RequestKeyFactoryDerivedKeySize = U32; // the size of the derived key type RequestKeyFactorySeed = GenericArray; @@ -336,7 +320,6 @@ pub mod request_keys { seed: &[u8], ) -> Result { if seed.len() != Self::seed_size() { - // TODO: should I just panic! here instead? return Err(InvalidRequestFactorySeedLengthError); } Ok(Self(SecretBox::new(*RequestKeyFactorySeed::from_slice( @@ -344,23 +327,11 @@ pub mod request_keys { )))) } - /// Creates an untyped bytestring deterministically from the given label. - /// This can be used externally to seed some kind of a secret key. - pub fn make_secret( - &self, - label: &[u8], - ) -> SecretBox> { - let prefix = b"REQUEST_SECRET_DERIVATION/"; - let info = [prefix, label].concat(); - kdf::(self.0.as_secret(), None, Some(&info)) - } - /// Creates a `RequestSecretKey` deterministically from the given label. pub fn make_key(&self, label: &[u8]) -> RequestSecretKey { let prefix = b"REQUEST_KEY_DERIVATION/"; let info = [prefix, label].concat(); - let seed = - kdf::(self.0.as_secret(), None, Some(&info)); + let seed = kdf::(self.0.as_secret(), Some(&info)); let rng = ChaCha20Rng::from_seed(<[u8; 32]>::try_from(seed.as_secret().as_slice()).unwrap()); RequestSecretKey::random_from_rng(rng) @@ -370,8 +341,7 @@ pub mod request_keys { pub fn make_factory(&self, label: &[u8]) -> Self { let prefix = b"REQUEST_KEY_FACTORY_DERIVATION/"; let info = [prefix, label].concat(); - let derived_seed = - kdf::(self.0.as_secret(), None, Some(&info)); + let derived_seed = kdf::(self.0.as_secret(), Some(&info)); Self(derived_seed) } } @@ -650,7 +620,7 @@ mod tests { let requester_secret = RequestSecretKey::random(); let requester_public_key = requester_secret.public_key(); - let service_shared_secret = service_secret.diffie_hellman(&requester_public_key); + let service_shared_secret = service_secret.derive_shared_secret(&requester_public_key); let ciphertext = b"1".to_vec().into_boxed_slice(); // length less than nonce size let nonce_size = ::to_usize(); @@ -675,8 +645,9 @@ mod tests { let requester_secret_key = secret_factory.make_key(&label_2.as_ref()); let requester_public_key = requester_secret_key.public_key(); - let service_shared_secret = service_secret_key.diffie_hellman(&requester_public_key); - let requester_shared_secret = requester_secret_key.diffie_hellman(&service_public_key); + let service_shared_secret = service_secret_key.derive_shared_secret(&requester_public_key); + let requester_shared_secret = + requester_secret_key.derive_shared_secret(&service_public_key); let data_to_encrypt = b"The Tyranny of Merit".to_vec().into_boxed_slice(); let ciphertext = @@ -745,7 +716,7 @@ mod tests { // requester encrypts request to send to service let service_public_key = service_secret.public_key(); - let requester_shared_secret = requester_secret.diffie_hellman(&service_public_key); + let requester_shared_secret = requester_secret.derive_shared_secret(&service_public_key); let encrypted_request = request.encrypt(&requester_shared_secret, &requester_public_key); // mimic serialization/deserialization over the wire @@ -761,7 +732,7 @@ mod tests { // service decrypts request let service_shared_secret = - service_secret.diffie_hellman(&encrypted_request_from_bytes.requester_public_key); + service_secret.derive_shared_secret(&encrypted_request_from_bytes.requester_public_key); let decrypted_request = encrypted_request_from_bytes .decrypt(&service_shared_secret) .unwrap(); @@ -769,7 +740,7 @@ mod tests { // wrong shared key used let random_secret_key = RequestSecretKey::random(); - let random_shared_secret = random_secret_key.diffie_hellman(&requester_public_key); + let random_shared_secret = random_secret_key.derive_shared_secret(&requester_public_key); assert!(encrypted_request_from_bytes .decrypt(&random_shared_secret) .is_err()); @@ -788,7 +759,8 @@ mod tests { // service encrypts response to send back let requester_public_key = requester_secret.public_key(); - let service_shared_secret = service_secret.diffie_hellman(&requester_public_key); + + let service_shared_secret = service_secret.derive_shared_secret(&requester_public_key); let encrypted_response = response.encrypt(&service_shared_secret); assert_eq!(encrypted_response.ritual_id, ritual_id); @@ -799,7 +771,7 @@ mod tests { // requester decrypts response let service_public_key = service_secret.public_key(); - let requester_shared_secret = requester_secret.diffie_hellman(&service_public_key); + let requester_shared_secret = requester_secret.derive_shared_secret(&service_public_key); let decrypted_response = encrypted_response_from_bytes .decrypt(&requester_shared_secret) .unwrap(); @@ -812,7 +784,7 @@ mod tests { // wrong shared key used let random_secret_key = RequestSecretKey::random(); - let random_shared_secret = random_secret_key.diffie_hellman(&requester_public_key); + let random_shared_secret = random_secret_key.derive_shared_secret(&requester_public_key); assert!(encrypted_response_from_bytes .decrypt(&random_shared_secret) .is_err()); diff --git a/nucypher-core/src/secret_box.rs b/nucypher-core/src/secret_box.rs index bc9fe583..64135c14 100644 --- a/nucypher-core/src/secret_box.rs +++ b/nucypher-core/src/secret_box.rs @@ -24,6 +24,9 @@ The situation may improve in the future, and `secrecy` will actually become usab use alloc::boxed::Box; +use generic_array::{ArrayLength, GenericArray}; +use hkdf::Hkdf; +use sha2::Sha256; use zeroize::{Zeroize, ZeroizeOnDrop}; /// A container for secret data. @@ -74,3 +77,16 @@ where // See https://github.com/RustCrypto/utils/issues/786 // So we're asserting that this object is zeroized on drop, since there is a Drop impl just above. impl ZeroizeOnDrop for SecretBox where T: Zeroize + Clone {} + +pub fn kdf>(seed: &[u8], info: Option<&[u8]>) -> SecretBox> { + let hk = Hkdf::::new(None, seed); + + let mut okm = SecretBox::new(GenericArray::::default()); + + let def_info = info.unwrap_or_default(); + + // We can only get an error here if `S` is too large, and it's known at compile-time. + hk.expand(def_info, okm.as_mut_secret()).unwrap(); + + okm +} From 8b1b90c510ca0ce4a0e7b2d55bee2a1904493fd4 Mon Sep 17 00:00:00 2001 From: derekpierre Date: Sat, 3 Jun 2023 13:20:56 -0400 Subject: [PATCH 11/15] More RFCs from #54. --- CHANGELOG.md | 9 ++++--- nucypher-core/Cargo.toml | 2 +- nucypher-core/src/dkg.rs | 56 +++++++++++++++------------------------- 3 files changed, 27 insertions(+), 40 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 53dbfe3e..388cc6d0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,16 +9,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Re-exported `ferveo` Python and WASM bindings. ([#58]) - Added `RequestSharedSecret`, `RequestPublicKey`, `RequestSecretKey`, `RequestKeyFactory` as wrappers for underlying Curve 25519 key functionality. ([#54]) -- Added Rust `pre-commit` hooks for repos. -- Added `secret_box` functionality. +- Added Rust `pre-commit` hooks for repos. ([#54]) +- Added `secret_box` functionality. ([#54]) ### Changed - Replaced opaque types with native `ferveo` types. ([#53]) -- Removed `E2EThresholdDecryptionRequest` type and bindings. ([54]) +- Removed `E2EThresholdDecryptionRequest` type and bindings. ([#54]) - Modified `EncryptedThresholdDecryptionRequest`/`EncryptedThresholdDecryptionResponse` to use Curve 25519 keys instead of Umbral keys for encryption/decryption. ([#54]) -- Modified `ThresholdDecryptionResponse`/`EncryptedThresholdDecryptionResponse` to include `ritual_id` member in struct. +- Modified `ThresholdDecryptionResponse`/`EncryptedThresholdDecryptionResponse` to include `ritual_id` member in struct. ([#54]) + [#53]: https://github.com/nucypher/nucypher-core/pull/53 [#58]: https://github.com/nucypher/nucypher-core/pull/58 diff --git a/nucypher-core/Cargo.toml b/nucypher-core/Cargo.toml index 7cacefef..42ac394b 100644 --- a/nucypher-core/Cargo.toml +++ b/nucypher-core/Cargo.toml @@ -25,4 +25,4 @@ chacha20poly1305 = "0.10.1" zeroize = { version="1.6.0", features = ["derive"] } rand_core = "0.6.4" rand_chacha = "0.3.1" -rand = "0.8.5" \ No newline at end of file +rand = "0.8.5" diff --git a/nucypher-core/src/dkg.rs b/nucypher-core/src/dkg.rs index ec335357..5dd1c193 100644 --- a/nucypher-core/src/dkg.rs +++ b/nucypher-core/src/dkg.rs @@ -121,6 +121,7 @@ pub mod request_keys { use rand_chacha::ChaCha20Rng; use rand_core::{CryptoRng, OsRng, RngCore}; use serde::{Deserialize, Serialize}; + use sha2::{Digest, Sha256}; use x25519_dalek::{PublicKey, SharedSecret, StaticSecret}; use crate::secret_box::{kdf, SecretBox}; @@ -132,33 +133,33 @@ pub mod request_keys { /// A Diffie-Hellman shared secret #[derive(ZeroizeOnDrop)] - pub struct RequestSharedSecret(pub(crate) SharedSecret); + pub struct RequestSharedSecret { + shared_secret: SharedSecret, + hashed_bytes: [u8; 32], + } /// Implementation of Diffie-Hellman shared secret impl RequestSharedSecret { /// Create new shared secret from underlying library. pub fn new(shared_secret: SharedSecret) -> Self { - Self(shared_secret) - } - - /// Convert this shared secret to a byte array. - #[inline] - pub fn to_bytes(&self) -> [u8; 32] { - self.0.to_bytes() + let hash = Sha256::digest(shared_secret.as_bytes()); + let hashed_bytes = hash.as_slice().try_into().expect("invalid length"); + Self { + shared_secret, + hashed_bytes, + } } /// View this shared secret key as a byte array. - #[inline] pub fn as_bytes(&self) -> &[u8; 32] { - self.0.as_bytes() + &self.hashed_bytes } } impl AsRef<[u8]> for RequestSharedSecret { /// View this shared secret key as a byte array. - #[inline] fn as_ref(&self) -> &[u8] { - self.0.as_bytes() + self.as_bytes() } } @@ -171,26 +172,18 @@ pub mod request_keys { /// A request public key. #[derive(PartialEq, Eq, Hash, Copy, Clone, Debug, Serialize, Deserialize)] - pub struct RequestPublicKey(pub(crate) PublicKey); + pub struct RequestPublicKey(PublicKey); /// Implementation of request public key impl RequestPublicKey { /// Convert this public key to a byte array. - #[inline] pub fn to_bytes(&self) -> [u8; 32] { self.0.to_bytes() } - - /// View this public key as a byte array. - #[inline] - pub fn as_bytes(&self) -> &[u8; 32] { - self.0.as_bytes() - } } impl AsRef<[u8]> for RequestPublicKey { /// View this public key as a byte array. - #[inline] fn as_ref(&self) -> &[u8] { self.0.as_bytes() } @@ -203,14 +196,6 @@ pub mod request_keys { } } - impl<'a> From<&'a RequestSecretKey> for RequestPublicKey { - /// Compute corresponding [`RequestPublicKey`]. - fn from(secret: &'a RequestSecretKey) -> RequestPublicKey { - let public_key = PublicKey::from(&secret.0); - RequestPublicKey(public_key) - } - } - impl<'a> ProtocolObjectInner<'a> for RequestPublicKey { fn version() -> (u16, u16) { (1, 0) @@ -249,23 +234,24 @@ pub mod request_keys { their_public_key: &RequestPublicKey, ) -> RequestSharedSecret { let shared_secret = self.0.diffie_hellman(&their_public_key.0); - RequestSharedSecret(shared_secret) + RequestSharedSecret::new(shared_secret) } /// Create secret key from rng. - pub fn random_from_rng(csprng: T) -> Self { + pub fn random_from_rng(csprng: &mut (impl RngCore + CryptoRng)) -> Self { let secret_key = StaticSecret::random_from_rng(csprng); Self(secret_key) } /// Create random secret key. pub fn random() -> Self { - Self::random_from_rng(OsRng) + Self::random_from_rng(&mut OsRng) } /// Returns a public key corresponding to this secret key. pub fn public_key(&self) -> RequestPublicKey { - RequestPublicKey::from(self) + let public_key = PublicKey::from(&self.0); + RequestPublicKey(public_key) } } @@ -332,9 +318,9 @@ pub mod request_keys { let prefix = b"REQUEST_KEY_DERIVATION/"; let info = [prefix, label].concat(); let seed = kdf::(self.0.as_secret(), Some(&info)); - let rng = + let mut rng = ChaCha20Rng::from_seed(<[u8; 32]>::try_from(seed.as_secret().as_slice()).unwrap()); - RequestSecretKey::random_from_rng(rng) + RequestSecretKey::random_from_rng(&mut rng) } /// Creates a `RequestKeyFactory` deterministically from the given label. From 7735aba48bcc323fde37898e00fbee9c80e1e1a1 Mon Sep 17 00:00:00 2001 From: derekpierre Date: Tue, 6 Jun 2023 08:57:42 -0400 Subject: [PATCH 12/15] Ritual id is now a u32 - modify object protocol version for incompatible change. Improve error name for invalid seed length. --- CHANGELOG.md | 1 + Cargo.lock | 2 +- nucypher-core-python/src/lib.rs | 12 +++++------ nucypher-core-wasm/src/lib.rs | 12 +++++------ nucypher-core-wasm/tests/wasm.rs | 2 +- nucypher-core/src/dkg.rs | 36 +++++++++++++++++++------------- 6 files changed, 37 insertions(+), 28 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 388cc6d0..5163b806 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Removed `E2EThresholdDecryptionRequest` type and bindings. ([#54]) - Modified `EncryptedThresholdDecryptionRequest`/`EncryptedThresholdDecryptionResponse` to use Curve 25519 keys instead of Umbral keys for encryption/decryption. ([#54]) - Modified `ThresholdDecryptionResponse`/`EncryptedThresholdDecryptionResponse` to include `ritual_id` member in struct. ([#54]) +- Ritual ID for `ThresholdDecryption[Request/Response]` / `EncryptedThresholdDecryption[Request/Response]` is now u32 instead of u16. ([#54]) [#53]: https://github.com/nucypher/nucypher-core/pull/53 diff --git a/Cargo.lock b/Cargo.lock index 0b9ccec4..02abd3da 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -901,6 +901,7 @@ dependencies = [ "wasm-bindgen", "wasm-bindgen-derive", "wasm-bindgen-test", + "x25519-dalek", ] [[package]] @@ -1597,7 +1598,6 @@ dependencies = [ "proc-macro2", "quote", "syn 1.0.109", - "wasm-bindgen", ] [[package]] diff --git a/nucypher-core-python/src/lib.rs b/nucypher-core-python/src/lib.rs index 94fe6d25..04a6b96d 100644 --- a/nucypher-core-python/src/lib.rs +++ b/nucypher-core-python/src/lib.rs @@ -762,7 +762,7 @@ pub struct ThresholdDecryptionRequest { impl ThresholdDecryptionRequest { #[new] pub fn new( - ritual_id: u16, + ritual_id: u32, variant: u8, ciphertext: &Ciphertext, conditions: Option<&Conditions>, @@ -792,7 +792,7 @@ impl ThresholdDecryptionRequest { } #[getter] - pub fn ritual_id(&self) -> u16 { + pub fn ritual_id(&self) -> u32 { self.backend.ritual_id } @@ -863,7 +863,7 @@ pub struct EncryptedThresholdDecryptionRequest { #[pymethods] impl EncryptedThresholdDecryptionRequest { #[getter] - pub fn ritual_id(&self) -> u16 { + pub fn ritual_id(&self) -> u32 { self.backend.ritual_id } @@ -905,14 +905,14 @@ pub struct ThresholdDecryptionResponse { #[pymethods] impl ThresholdDecryptionResponse { #[new] - pub fn new(ritual_id: u16, decryption_share: &[u8]) -> Self { + pub fn new(ritual_id: u32, decryption_share: &[u8]) -> Self { ThresholdDecryptionResponse { backend: nucypher_core::ThresholdDecryptionResponse::new(ritual_id, decryption_share), } } #[getter] - pub fn ritual_id(&self) -> u16 { + pub fn ritual_id(&self) -> u32 { self.backend.ritual_id } @@ -953,7 +953,7 @@ pub struct EncryptedThresholdDecryptionResponse { #[pymethods] impl EncryptedThresholdDecryptionResponse { #[getter] - pub fn ritual_id(&self) -> u16 { + pub fn ritual_id(&self) -> u32 { self.backend.ritual_id } diff --git a/nucypher-core-wasm/src/lib.rs b/nucypher-core-wasm/src/lib.rs index 5dd08e4e..9392a1d0 100644 --- a/nucypher-core-wasm/src/lib.rs +++ b/nucypher-core-wasm/src/lib.rs @@ -657,7 +657,7 @@ pub struct ThresholdDecryptionRequest(nucypher_core::ThresholdDecryptionRequest) impl ThresholdDecryptionRequest { #[wasm_bindgen(constructor)] pub fn new( - ritual_id: u16, + ritual_id: u32, variant: u8, ciphertext: &Ciphertext, conditions: &OptionConditions, @@ -682,7 +682,7 @@ impl ThresholdDecryptionRequest { } #[wasm_bindgen(getter, js_name = ritualId)] - pub fn ritual_id(&self) -> u16 { + pub fn ritual_id(&self) -> u32 { self.0.ritual_id } @@ -732,7 +732,7 @@ pub struct EncryptedThresholdDecryptionRequest(nucypher_core::EncryptedThreshold #[wasm_bindgen] impl EncryptedThresholdDecryptionRequest { #[wasm_bindgen(getter, js_name = ritualId)] - pub fn ritual_id(&self) -> u16 { + pub fn ritual_id(&self) -> u32 { self.0.ritual_id } @@ -774,7 +774,7 @@ pub struct ThresholdDecryptionResponse(nucypher_core::ThresholdDecryptionRespons impl ThresholdDecryptionResponse { #[wasm_bindgen(constructor)] pub fn new( - ritual_id: u16, + ritual_id: u32, decryption_share: &[u8], ) -> Result { Ok(Self(nucypher_core::ThresholdDecryptionResponse::new( @@ -784,7 +784,7 @@ impl ThresholdDecryptionResponse { } #[wasm_bindgen(getter, js_name = ritualId)] - pub fn ritual_id(&self) -> u16 { + pub fn ritual_id(&self) -> u32 { self.0.ritual_id } @@ -824,7 +824,7 @@ pub struct EncryptedThresholdDecryptionResponse( #[wasm_bindgen] impl EncryptedThresholdDecryptionResponse { #[wasm_bindgen(getter, js_name = ritualId)] - pub fn ritual_id(&self) -> u16 { + pub fn ritual_id(&self) -> u32 { self.0.ritual_id } diff --git a/nucypher-core-wasm/tests/wasm.rs b/nucypher-core-wasm/tests/wasm.rs index 615943ca..077d5449 100644 --- a/nucypher-core-wasm/tests/wasm.rs +++ b/nucypher-core-wasm/tests/wasm.rs @@ -688,7 +688,7 @@ fn request_public_key() { #[wasm_bindgen_test] fn threshold_decryption_request() { - let ritual_id: u16 = 5; + let ritual_id: u32 = 5; let service_secret = RequestSecretKey::random(); let service_public_key = service_secret.public_key(); diff --git a/nucypher-core/src/dkg.rs b/nucypher-core/src/dkg.rs index 5dd1c193..013c8c95 100644 --- a/nucypher-core/src/dkg.rs +++ b/nucypher-core/src/dkg.rs @@ -267,9 +267,9 @@ pub mod request_keys { type RequestKeyFactorySeed = GenericArray; /// Error thrown when invalid random seed provided for creating key factory. - pub struct InvalidRequestFactorySeedLengthError; + pub struct InvalidRequestFactorySeedLength; - impl fmt::Display for InvalidRequestFactorySeedLengthError { + impl fmt::Display for InvalidRequestFactorySeedLength { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "Invalid seed length") } @@ -304,9 +304,9 @@ pub mod request_keys { /// from a cryptographically secure source of randomness! pub fn from_secure_randomness( seed: &[u8], - ) -> Result { + ) -> Result { if seed.len() != Self::seed_size() { - return Err(InvalidRequestFactorySeedLengthError); + return Err(InvalidRequestFactorySeedLength); } Ok(Self(SecretBox::new(*RequestKeyFactorySeed::from_slice( seed, @@ -343,7 +343,7 @@ pub mod request_keys { #[derive(PartialEq, Eq, Debug, Clone, Serialize, Deserialize)] pub struct ThresholdDecryptionRequest { /// The ID of the ritual. - pub ritual_id: u16, + 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. @@ -357,7 +357,7 @@ pub struct ThresholdDecryptionRequest { impl ThresholdDecryptionRequest { /// Creates a new decryption request. pub fn new( - ritual_id: u16, + ritual_id: u32, ciphertext: &Ciphertext, conditions: Option<&Conditions>, context: Option<&Context>, @@ -384,7 +384,7 @@ impl ThresholdDecryptionRequest { impl<'a> ProtocolObjectInner<'a> for ThresholdDecryptionRequest { fn version() -> (u16, u16) { - (1, 0) + (2, 0) } fn brand() -> [u8; 4] { @@ -410,7 +410,7 @@ impl<'a> ProtocolObject<'a> for ThresholdDecryptionRequest {} #[derive(PartialEq, Debug, Clone, Serialize, Deserialize)] pub struct EncryptedThresholdDecryptionRequest { /// ID of the ritual - pub ritual_id: u16, + pub ritual_id: u32, /// Public key of requester pub requester_public_key: RequestPublicKey, @@ -449,7 +449,7 @@ impl EncryptedThresholdDecryptionRequest { impl<'a> ProtocolObjectInner<'a> for EncryptedThresholdDecryptionRequest { fn version() -> (u16, u16) { - (1, 0) + (2, 0) } fn brand() -> [u8; 4] { @@ -475,7 +475,7 @@ impl<'a> ProtocolObject<'a> for EncryptedThresholdDecryptionRequest {} #[derive(PartialEq, Eq, Debug, Serialize, Deserialize, Clone)] pub struct ThresholdDecryptionResponse { /// The ID of the ritual. - pub ritual_id: u16, + pub ritual_id: u32, /// The decryption share to include in the response. #[serde(with = "serde_bytes::as_base64")] @@ -484,7 +484,7 @@ pub struct ThresholdDecryptionResponse { impl ThresholdDecryptionResponse { /// Creates and a new decryption response. - pub fn new(ritual_id: u16, decryption_share: &[u8]) -> Self { + pub fn new(ritual_id: u32, decryption_share: &[u8]) -> Self { ThresholdDecryptionResponse { ritual_id, decryption_share: decryption_share.to_vec().into(), @@ -502,7 +502,7 @@ impl ThresholdDecryptionResponse { impl<'a> ProtocolObjectInner<'a> for ThresholdDecryptionResponse { fn version() -> (u16, u16) { - (1, 0) + (2, 0) } fn brand() -> [u8; 4] { @@ -528,7 +528,7 @@ impl<'a> ProtocolObject<'a> for ThresholdDecryptionResponse {} #[derive(PartialEq, Debug, Clone, Serialize, Deserialize)] pub struct EncryptedThresholdDecryptionResponse { /// The ID of the ritual. - pub ritual_id: u16, + pub ritual_id: u32, #[serde(with = "serde_bytes::as_base64")] ciphertext: Box<[u8]>, @@ -560,7 +560,7 @@ impl EncryptedThresholdDecryptionResponse { impl<'a> ProtocolObjectInner<'a> for EncryptedThresholdDecryptionResponse { fn version() -> (u16, u16) { - (1, 0) + (2, 0) } fn brand() -> [u8; 4] { @@ -719,6 +719,10 @@ mod tests { // service decrypts request let service_shared_secret = service_secret.derive_shared_secret(&encrypted_request_from_bytes.requester_public_key); + assert_eq!( + service_shared_secret.as_bytes(), + requester_shared_secret.as_bytes() + ); let decrypted_request = encrypted_request_from_bytes .decrypt(&service_shared_secret) .unwrap(); @@ -758,6 +762,10 @@ mod tests { // requester decrypts response let service_public_key = service_secret.public_key(); let requester_shared_secret = requester_secret.derive_shared_secret(&service_public_key); + assert_eq!( + requester_shared_secret.as_bytes(), + service_shared_secret.as_bytes() + ); let decrypted_response = encrypted_response_from_bytes .decrypt(&requester_shared_secret) .unwrap(); From b80326885bbb8ed69debd3db970d6db28a93744b Mon Sep 17 00:00:00 2001 From: derekpierre Date: Tue, 6 Jun 2023 10:41:03 -0400 Subject: [PATCH 13/15] Rename various dkg Request key objects to use a Session prefixed name instead. --- CHANGELOG.md | 2 +- .../nucypher_core/__init__.py | 8 +- .../nucypher_core/__init__.pyi | 32 ++-- nucypher-core-python/src/lib.rs | 72 ++++---- nucypher-core-wasm/src/lib.rs | 60 +++---- nucypher-core-wasm/tests/wasm.rs | 19 +- nucypher-core/src/dkg.rs | 165 +++++++++--------- nucypher-core/src/lib.rs | 2 +- 8 files changed, 180 insertions(+), 180 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5163b806..ab5987bd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Re-exported `ferveo` Python and WASM bindings. ([#58]) -- Added `RequestSharedSecret`, `RequestPublicKey`, `RequestSecretKey`, `RequestKeyFactory` as wrappers for underlying Curve 25519 key functionality. ([#54]) +- Added `SessionSharedSecret`, `SessionStaticKey`, `SessionStaticSecret`, `SessionSecretFactory` as wrappers for underlying Curve 25519 key functionality. ([#54]) - Added Rust `pre-commit` hooks for repos. ([#54]) - Added `secret_box` functionality. ([#54]) diff --git a/nucypher-core-python/nucypher_core/__init__.py b/nucypher-core-python/nucypher_core/__init__.py index 947d769c..cb500c48 100644 --- a/nucypher-core-python/nucypher_core/__init__.py +++ b/nucypher-core-python/nucypher_core/__init__.py @@ -21,8 +21,8 @@ ThresholdDecryptionResponse, EncryptedThresholdDecryptionRequest, EncryptedThresholdDecryptionResponse, - RequestSharedSecret, - RequestPublicKey, - RequestSecretKey, - RequestKeyFactory, + SessionSharedSecret, + SessionStaticKey, + SessionStaticSecret, + SessionSecretFactory, ) diff --git a/nucypher-core-python/nucypher_core/__init__.pyi b/nucypher-core-python/nucypher_core/__init__.pyi index d519a259..e96c3263 100644 --- a/nucypher-core-python/nucypher_core/__init__.pyi +++ b/nucypher-core-python/nucypher_core/__init__.pyi @@ -423,7 +423,7 @@ class ThresholdDecryptionRequest: ciphertext: Ciphertext - def encrypt(self, shared_secret: RequestSharedSecret, requester_public_key: RequestPublicKey) -> EncryptedThresholdDecryptionRequest: + def encrypt(self, shared_secret: SessionSharedSecret, requester_public_key: SessionStaticKey) -> EncryptedThresholdDecryptionRequest: ... @staticmethod @@ -437,11 +437,11 @@ class ThresholdDecryptionRequest: class EncryptedThresholdDecryptionRequest: ritual_id: int - requester_public_key: RequestPublicKey + requester_public_key: SessionStaticKey def decrypt( self, - shared_secret: RequestSharedSecret + shared_secret: SessionSharedSecret ) -> ThresholdDecryptionRequest: ... @@ -462,7 +462,7 @@ class ThresholdDecryptionResponse: ritual_id: int - def encrypt(self, shared_secret: RequestSharedSecret) -> EncryptedThresholdDecryptionResponse: + def encrypt(self, shared_secret: SessionSharedSecret) -> EncryptedThresholdDecryptionResponse: ... @staticmethod @@ -479,7 +479,7 @@ class EncryptedThresholdDecryptionResponse: def decrypt( self, - shared_secret: RequestSharedSecret + shared_secret: SessionSharedSecret ) -> ThresholdDecryptionResponse: ... @@ -491,37 +491,37 @@ class EncryptedThresholdDecryptionResponse: ... -class RequestSharedSecret: +class SessionSharedSecret: ... -class RequestPublicKey: +class SessionStaticKey: @staticmethod - def from_bytes(data: bytes) -> RequestPublicKey: + def from_bytes(data: bytes) -> SessionStaticKey: ... def __bytes__(self) -> bytes: ... -class RequestSecretKey: +class SessionStaticSecret: @staticmethod - def random() -> RequestSecretKey: + def random() -> SessionStaticSecret: ... - def public_key(self) -> RequestPublicKey: + def public_key(self) -> SessionStaticKey: ... - def derive_shared_secret(self, their_public_key: RequestPublicKey) -> RequestSharedSecret: + def derive_shared_secret(self, their_public_key: SessionStaticKey) -> SessionSharedSecret: ... -class RequestKeyFactory: +class SessionSecretFactory: @staticmethod - def random() -> RequestKeyFactory: + def random() -> SessionSecretFactory: ... @staticmethod @@ -529,10 +529,10 @@ class RequestKeyFactory: ... @staticmethod - def from_secure_randomness(seed: bytes) -> RequestKeyFactory: + def from_secure_randomness(seed: bytes) -> SessionSecretFactory: ... - def make_key(self, label: bytes) -> RequestSecretKey: + def make_key(self, label: bytes) -> SessionStaticSecret: ... def make_factory(self, label: bytes) -> RequestKeyFactory: diff --git a/nucypher-core-python/src/lib.rs b/nucypher-core-python/src/lib.rs index 04a6b96d..63b84d8e 100644 --- a/nucypher-core-python/src/lib.rs +++ b/nucypher-core-python/src/lib.rs @@ -633,26 +633,26 @@ impl ReencryptionResponse { } // -// Request Keys +// Session Keys // #[pyclass(module = "nucypher_core")] #[derive(derive_more::From, derive_more::AsRef)] -pub struct RequestSharedSecret { - backend: nucypher_core::RequestSharedSecret, +pub struct SessionSharedSecret { + backend: nucypher_core::SessionSharedSecret, } #[pyclass(module = "nucypher_core")] #[derive(Clone, PartialEq, Eq, derive_more::From, derive_more::AsRef)] -pub struct RequestPublicKey { - backend: nucypher_core::RequestPublicKey, +pub struct SessionStaticKey { + backend: nucypher_core::SessionStaticKey, } #[pymethods] -impl RequestPublicKey { +impl SessionStaticKey { #[staticmethod] pub fn from_bytes(data: &[u8]) -> PyResult { - from_bytes::<_, nucypher_core::RequestPublicKey>(data) + from_bytes::<_, nucypher_core::SessionStaticKey>(data) } fn __bytes__(&self) -> PyObject { @@ -664,7 +664,7 @@ impl RequestPublicKey { } fn __hash__(&self) -> PyResult { - hash("RequestPublicKey", self) + hash("SessionStaticKey", self) } fn __str__(&self) -> PyResult { @@ -674,27 +674,27 @@ impl RequestPublicKey { #[pyclass(module = "nucypher_core")] #[derive(derive_more::From, derive_more::AsRef)] -pub struct RequestSecretKey { - backend: nucypher_core::RequestSecretKey, +pub struct SessionStaticSecret { + backend: nucypher_core::SessionStaticSecret, } #[pymethods] -impl RequestSecretKey { +impl SessionStaticSecret { #[staticmethod] pub fn random() -> PyResult { Ok(Self { - backend: nucypher_core::RequestSecretKey::random(), + backend: nucypher_core::SessionStaticSecret::random(), }) } - pub fn public_key(&self) -> RequestPublicKey { - RequestPublicKey { + pub fn public_key(&self) -> SessionStaticKey { + SessionStaticKey { backend: self.backend.public_key(), } } - pub fn derive_shared_secret(&self, their_public_key: &RequestPublicKey) -> RequestSharedSecret { - RequestSharedSecret { + pub fn derive_shared_secret(&self, their_public_key: &SessionStaticKey) -> SessionSharedSecret { + SessionSharedSecret { backend: self.backend.derive_shared_secret(their_public_key.as_ref()), } } @@ -706,39 +706,39 @@ impl RequestSecretKey { #[pyclass(module = "nucypher_core")] #[derive(derive_more::From, derive_more::AsRef)] -pub struct RequestKeyFactory { - backend: nucypher_core::RequestKeyFactory, +pub struct SessionSecretFactory { + backend: nucypher_core::SessionSecretFactory, } #[pymethods] -impl RequestKeyFactory { +impl SessionSecretFactory { #[staticmethod] pub fn random() -> PyResult { Ok(Self { - backend: nucypher_core::RequestKeyFactory::random(), + backend: nucypher_core::SessionSecretFactory::random(), }) } #[staticmethod] pub fn seed_size() -> usize { - nucypher_core::RequestKeyFactory::seed_size() + nucypher_core::SessionSecretFactory::seed_size() } #[staticmethod] pub fn from_secure_randomness(seed: &[u8]) -> PyResult { - let factory = nucypher_core::RequestKeyFactory::from_secure_randomness(seed) + let factory = nucypher_core::SessionSecretFactory::from_secure_randomness(seed) .map_err(|err| PyValueError::new_err(format!("{}", err)))?; Ok(Self { backend: factory }) } - pub fn make_key(&self, label: &[u8]) -> RequestSecretKey { - RequestSecretKey { + pub fn make_key(&self, label: &[u8]) -> SessionStaticSecret { + SessionStaticSecret { backend: self.backend.make_key(label), } } - pub fn make_factory(&self, label: &[u8]) -> RequestKeyFactory { - RequestKeyFactory { + pub fn make_factory(&self, label: &[u8]) -> SessionSecretFactory { + SessionSecretFactory { backend: self.backend.make_factory(label), } } @@ -829,8 +829,8 @@ impl ThresholdDecryptionRequest { pub fn encrypt( &self, - shared_secret: &RequestSharedSecret, - requester_public_key: &RequestPublicKey, + shared_secret: &SessionSharedSecret, + requester_public_key: &SessionStaticKey, ) -> EncryptedThresholdDecryptionRequest { let encrypted_request = self .backend @@ -868,13 +868,13 @@ impl EncryptedThresholdDecryptionRequest { } #[getter] - pub fn requester_public_key(&self) -> RequestPublicKey { + pub fn requester_public_key(&self) -> SessionStaticKey { self.backend.requester_public_key.into() } pub fn decrypt( &self, - shared_secret: &RequestSharedSecret, + shared_secret: &SessionSharedSecret, ) -> PyResult { self.backend .decrypt(shared_secret.as_ref()) @@ -923,7 +923,7 @@ impl ThresholdDecryptionResponse { pub fn encrypt( &self, - shared_secret: &RequestSharedSecret, + shared_secret: &SessionSharedSecret, ) -> EncryptedThresholdDecryptionResponse { EncryptedThresholdDecryptionResponse { backend: self.backend.encrypt(shared_secret.as_ref()), @@ -959,7 +959,7 @@ impl EncryptedThresholdDecryptionResponse { pub fn decrypt( &self, - shared_secret: &RequestSharedSecret, + shared_secret: &SessionSharedSecret, ) -> PyResult { self.backend .decrypt(shared_secret.as_ref()) @@ -1446,10 +1446,10 @@ 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::()?; - 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")?; diff --git a/nucypher-core-wasm/src/lib.rs b/nucypher-core-wasm/src/lib.rs index 9392a1d0..a4ca38ec 100644 --- a/nucypher-core-wasm/src/lib.rs +++ b/nucypher-core-wasm/src/lib.rs @@ -543,22 +543,22 @@ impl EncryptedTreasureMap { } // -// Request Keys +// Session Keys // #[wasm_bindgen] #[derive(derive_more::From, derive_more::AsRef)] -pub struct RequestSharedSecret(nucypher_core::RequestSharedSecret); +pub struct SessionSharedSecret(nucypher_core::SessionSharedSecret); #[wasm_bindgen] #[derive(PartialEq, Eq, Debug, derive_more::From, derive_more::AsRef)] -pub struct RequestPublicKey(nucypher_core::RequestPublicKey); +pub struct SessionStaticKey(nucypher_core::SessionStaticKey); #[wasm_bindgen] -impl RequestPublicKey { +impl SessionStaticKey { #[wasm_bindgen(js_name = fromBytes)] - pub fn from_bytes(data: &[u8]) -> Result { - from_bytes::<_, nucypher_core::RequestPublicKey>(data) + pub fn from_bytes(data: &[u8]) -> Result { + from_bytes::<_, nucypher_core::SessionStaticKey>(data) } #[wasm_bindgen(js_name = toBytes)] @@ -572,31 +572,31 @@ impl RequestPublicKey { format!("{}", self.0) } - pub fn equals(&self, other: &RequestPublicKey) -> bool { + pub fn equals(&self, other: &SessionStaticKey) -> bool { self.0 == other.0 } } #[wasm_bindgen] #[derive(derive_more::From, derive_more::AsRef)] -pub struct RequestSecretKey(nucypher_core::RequestSecretKey); +pub struct SessionStaticSecret(nucypher_core::SessionStaticSecret); #[wasm_bindgen] -impl RequestSecretKey { +impl SessionStaticSecret { /// Generates a secret key using the default RNG and returns it. pub fn random() -> Self { - Self(nucypher_core::RequestSecretKey::random()) + Self(nucypher_core::SessionStaticSecret::random()) } /// Generates a secret key using the default RNG and returns it. #[wasm_bindgen(js_name = publicKey)] - pub fn public_key(&self) -> RequestPublicKey { - RequestPublicKey(self.0.public_key()) + pub fn public_key(&self) -> SessionStaticKey { + SessionStaticKey(self.0.public_key()) } - #[wasm_bindgen(js_name = diffieHellman)] - pub fn derive_shared_secret(&self, their_public_key: &RequestPublicKey) -> RequestSharedSecret { - RequestSharedSecret(self.0.derive_shared_secret(their_public_key.as_ref())) + #[wasm_bindgen(js_name = deriveSharedSecret)] + pub fn derive_shared_secret(&self, their_public_key: &SessionStaticKey) -> SessionSharedSecret { + SessionSharedSecret(self.0.derive_shared_secret(their_public_key.as_ref())) } #[allow(clippy::inherent_to_string)] @@ -607,30 +607,30 @@ impl RequestSecretKey { } #[wasm_bindgen] -pub struct RequestKeyFactory(nucypher_core::RequestKeyFactory); +pub struct SessionSecretFactory(nucypher_core::SessionSecretFactory); #[wasm_bindgen] -impl RequestKeyFactory { +impl SessionSecretFactory { /// Generates a secret key factory using the default RNG and returns it. pub fn random() -> Self { - Self(nucypher_core::RequestKeyFactory::random()) + Self(nucypher_core::SessionSecretFactory::random()) } #[wasm_bindgen(js_name = seedSize)] pub fn seed_size() -> usize { - nucypher_core::RequestKeyFactory::seed_size() + nucypher_core::SessionSecretFactory::seed_size() } #[wasm_bindgen(js_name = fromSecureRandomness)] - pub fn from_secure_randomness(seed: &[u8]) -> Result { - nucypher_core::RequestKeyFactory::from_secure_randomness(seed) + pub fn from_secure_randomness(seed: &[u8]) -> Result { + nucypher_core::SessionSecretFactory::from_secure_randomness(seed) .map(Self) .map_err(map_js_err) } #[wasm_bindgen(js_name = makeKey)] - pub fn make_key(&self, label: &[u8]) -> RequestSecretKey { - RequestSecretKey(self.0.make_key(label)) + pub fn make_key(&self, label: &[u8]) -> SessionStaticSecret { + SessionStaticSecret(self.0.make_key(label)) } #[wasm_bindgen(js_name = makeFactory)] @@ -701,8 +701,8 @@ impl ThresholdDecryptionRequest { pub fn encrypt( &self, - shared_secret: &RequestSharedSecret, - requester_public_key: &RequestPublicKey, + shared_secret: &SessionSharedSecret, + requester_public_key: &SessionStaticKey, ) -> EncryptedThresholdDecryptionRequest { EncryptedThresholdDecryptionRequest( self.0 @@ -737,13 +737,13 @@ impl EncryptedThresholdDecryptionRequest { } #[wasm_bindgen(getter, js_name = requesterPublicKey)] - pub fn requester_public_key(&self) -> RequestPublicKey { - RequestPublicKey::from(self.0.requester_public_key) + pub fn requester_public_key(&self) -> SessionStaticKey { + SessionStaticKey::from(self.0.requester_public_key) } pub fn decrypt( &self, - shared_secret: &RequestSharedSecret, + shared_secret: &SessionSharedSecret, ) -> Result { self.0 .decrypt(shared_secret.as_ref()) @@ -795,7 +795,7 @@ impl ThresholdDecryptionResponse { pub fn encrypt( &self, - shared_secret: &RequestSharedSecret, + shared_secret: &SessionSharedSecret, ) -> EncryptedThresholdDecryptionResponse { EncryptedThresholdDecryptionResponse(self.0.encrypt(shared_secret.as_ref())) } @@ -830,7 +830,7 @@ impl EncryptedThresholdDecryptionResponse { pub fn decrypt( &self, - shared_secret: &RequestSharedSecret, + shared_secret: &SessionSharedSecret, ) -> Result { self.0 .decrypt(shared_secret.as_ref()) diff --git a/nucypher-core-wasm/tests/wasm.rs b/nucypher-core-wasm/tests/wasm.rs index 077d5449..732319ee 100644 --- a/nucypher-core-wasm/tests/wasm.rs +++ b/nucypher-core-wasm/tests/wasm.rs @@ -672,7 +672,7 @@ fn metadata_response() { #[wasm_bindgen_test] fn request_public_key() { - let secret = RequestSecretKey::random(); + let secret = SessionStaticSecret::random(); let public_key = secret.public_key(); assert_eq!(secret.public_key(), secret.public_key()); @@ -680,7 +680,7 @@ fn request_public_key() { // mimic transmission public key over the wire let serialized_public_key = public_key.to_bytes(); let deserialized_public_key = - RequestPublicKey::from_bytes(serialized_public_key.as_ref()).unwrap(); + SessionStaticKey::from_bytes(serialized_public_key.as_ref()).unwrap(); assert_eq!(public_key, deserialized_public_key); assert_eq!(serialized_public_key, deserialized_public_key.to_bytes()); @@ -689,10 +689,10 @@ fn request_public_key() { #[wasm_bindgen_test] fn threshold_decryption_request() { let ritual_id: u32 = 5; - let service_secret = RequestSecretKey::random(); + let service_secret = SessionStaticSecret::random(); let service_public_key = service_secret.public_key(); - let requester_secret = RequestSecretKey::random(); + let requester_secret = SessionStaticSecret::random(); let conditions = "{'some': 'condition'}"; let conditions_js: JsValue = Some(Conditions::new(conditions)).into(); @@ -737,9 +737,8 @@ fn threshold_decryption_request() { assert_eq!(request, decrypted_request); // wrong key used - let random_secret_key = RequestSecretKey::random(); - let random_shared_secret = - RequestSharedSecret::from(random_secret_key.derive_shared_secret(&service_public_key)); + let random_secret_key = SessionStaticSecret::random(); + let random_shared_secret = random_secret_key.derive_shared_secret(&service_public_key); assert!(encrypted_request_from_bytes .decrypt(&random_shared_secret) .is_err()); @@ -749,9 +748,9 @@ fn threshold_decryption_request() { fn threshold_decryption_response() { let ritual_id = 10; - let service_secret = RequestSecretKey::random(); + let service_secret = SessionStaticSecret::random(); - let requester_secret = RequestSecretKey::random(); + let requester_secret = SessionStaticSecret::random(); let requester_public_key = requester_secret.public_key(); let decryption_share = b"The Tyranny of Merit"; @@ -782,7 +781,7 @@ fn threshold_decryption_response() { ); // wrong secret key used - let random_secret_key = RequestSecretKey::random(); + let random_secret_key = SessionStaticSecret::random(); let random_shared_secret = random_secret_key.derive_shared_secret(&service_public_key); assert!(encrypted_response_from_bytes .decrypt(&random_shared_secret) diff --git a/nucypher-core/src/dkg.rs b/nucypher-core/src/dkg.rs index 013c8c95..ce8dcea3 100644 --- a/nucypher-core/src/dkg.rs +++ b/nucypher-core/src/dkg.rs @@ -16,7 +16,7 @@ use chacha20poly1305::aead::{Aead, AeadCore, KeyInit, OsRng}; use chacha20poly1305::{ChaCha20Poly1305, Key, Nonce}; use crate::conditions::{Conditions, Context}; -use crate::dkg::request_keys::{RequestPublicKey, RequestSharedSecret}; +use crate::dkg::session::{SessionSharedSecret, SessionStaticKey}; /// Errors during encryption. #[derive(Debug)] @@ -66,7 +66,7 @@ impl fmt::Display for DecryptionError { type NonceSize = ::NonceSize; fn encrypt_with_shared_secret( - shared_secret: &RequestSharedSecret, + shared_secret: &SessionSharedSecret, plaintext: &[u8], ) -> Result, EncryptionError> { let key = Key::from_slice(shared_secret.as_ref()); @@ -81,7 +81,7 @@ fn encrypt_with_shared_secret( } fn decrypt_with_shared_secret( - shared_secret: &RequestSharedSecret, + shared_secret: &SessionSharedSecret, ciphertext: &[u8], ) -> Result, DecryptionError> { let nonce_size = ::to_usize(); @@ -109,7 +109,8 @@ pub enum FerveoVariant { PRECOMPUTED, } -pub mod request_keys { +/// Module for session key objects. +pub mod session { use alloc::boxed::Box; use alloc::string::String; use core::fmt; @@ -133,13 +134,13 @@ pub mod request_keys { /// A Diffie-Hellman shared secret #[derive(ZeroizeOnDrop)] - pub struct RequestSharedSecret { + pub struct SessionSharedSecret { shared_secret: SharedSecret, hashed_bytes: [u8; 32], } /// Implementation of Diffie-Hellman shared secret - impl RequestSharedSecret { + impl SessionSharedSecret { /// Create new shared secret from underlying library. pub fn new(shared_secret: SharedSecret) -> Self { let hash = Sha256::digest(shared_secret.as_bytes()); @@ -150,59 +151,59 @@ pub mod request_keys { } } - /// View this shared secret key as a byte array. + /// View this shared secret as a byte array. pub fn as_bytes(&self) -> &[u8; 32] { &self.hashed_bytes } } - impl AsRef<[u8]> for RequestSharedSecret { - /// View this shared secret key as a byte array. + impl AsRef<[u8]> for SessionSharedSecret { + /// View this shared secret as a byte array. fn as_ref(&self) -> &[u8] { self.as_bytes() } } - impl fmt::Display for RequestSharedSecret { + impl fmt::Display for SessionSharedSecret { /// Format shared secret information. fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "RequestSharedSecret...") + write!(f, "SessionSharedSecret...") } } - /// A request public key. + /// A session public key. #[derive(PartialEq, Eq, Hash, Copy, Clone, Debug, Serialize, Deserialize)] - pub struct RequestPublicKey(PublicKey); + pub struct SessionStaticKey(PublicKey); - /// Implementation of request public key - impl RequestPublicKey { + /// Implementation of session static key + impl SessionStaticKey { /// Convert this public key to a byte array. pub fn to_bytes(&self) -> [u8; 32] { self.0.to_bytes() } } - impl AsRef<[u8]> for RequestPublicKey { + impl AsRef<[u8]> for SessionStaticKey { /// View this public key as a byte array. fn as_ref(&self) -> &[u8] { self.0.as_bytes() } } - impl fmt::Display for RequestPublicKey { + impl fmt::Display for SessionStaticKey { /// Format public key information. fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "RequestPublicKey: {}", hex::encode(&self.as_ref()[..8])) + write!(f, "SessionStaticKey: {}", hex::encode(&self.as_ref()[..8])) } } - impl<'a> ProtocolObjectInner<'a> for RequestPublicKey { + impl<'a> ProtocolObjectInner<'a> for SessionStaticKey { fn version() -> (u16, u16) { (1, 0) } fn brand() -> [u8; 4] { - *b"TRPk" + *b"TSSk" } fn unversioned_to_bytes(&self) -> Box<[u8]> { @@ -221,23 +222,23 @@ pub mod request_keys { } } - impl<'a> ProtocolObject<'a> for RequestPublicKey {} + impl<'a> ProtocolObject<'a> for SessionStaticKey {} - /// A request secret key. + /// A session secret key. #[derive(ZeroizeOnDrop)] - pub struct RequestSecretKey(pub(crate) StaticSecret); + pub struct SessionStaticSecret(pub(crate) StaticSecret); - impl RequestSecretKey { + impl SessionStaticSecret { /// Perform diffie-hellman pub fn derive_shared_secret( &self, - their_public_key: &RequestPublicKey, - ) -> RequestSharedSecret { + their_public_key: &SessionStaticKey, + ) -> SessionSharedSecret { let shared_secret = self.0.diffie_hellman(&their_public_key.0); - RequestSharedSecret::new(shared_secret) + SessionSharedSecret::new(shared_secret) } - /// Create secret key from rng. + /// Create secret key from RNG. pub fn random_from_rng(csprng: &mut (impl RngCore + CryptoRng)) -> Self { let secret_key = StaticSecret::random_from_rng(csprng); Self(secret_key) @@ -249,92 +250,92 @@ pub mod request_keys { } /// Returns a public key corresponding to this secret key. - pub fn public_key(&self) -> RequestPublicKey { + pub fn public_key(&self) -> SessionStaticKey { let public_key = PublicKey::from(&self.0); - RequestPublicKey(public_key) + SessionStaticKey(public_key) } } - impl fmt::Display for RequestSecretKey { + impl fmt::Display for SessionStaticSecret { /// Format information above secret key. fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "RequestSecretKey:...") + write!(f, "SessionStaticSecret:...") } } - type SecretKeyFactorySeedSize = U32; // the size of the seed material for key derivation - type RequestKeyFactoryDerivedKeySize = U32; // the size of the derived key - type RequestKeyFactorySeed = GenericArray; + type SessionSecretFactorySeedSize = U32; // the size of the seed material for key derivation + type SessionSecretFactoryDerivedKeySize = U32; // the size of the derived key + type SessionSecretFactorySeed = GenericArray; /// Error thrown when invalid random seed provided for creating key factory. - pub struct InvalidRequestFactorySeedLength; + pub struct InvalidSessionSecretFactorySeedLength; - impl fmt::Display for InvalidRequestFactorySeedLength { + impl fmt::Display for InvalidSessionSecretFactorySeedLength { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "Invalid seed length") } } - /// This class handles keyring material for request keys, by allowing deterministic - /// derivation of `RequestSecretKey` objects based on labels. + /// This class handles keyring material for session keys, by allowing deterministic + /// derivation of `SessionStaticSecret` objects based on labels. #[derive(Clone, ZeroizeOnDrop, PartialEq)] - pub struct RequestKeyFactory(SecretBox); + pub struct SessionSecretFactory(SecretBox); - impl RequestKeyFactory { - /// Creates a secret key factory using the given RNG. + impl SessionSecretFactory { + /// Creates a session secret factory using the given RNG. pub fn random_with_rng(rng: &mut (impl CryptoRng + RngCore)) -> Self { - let mut bytes = SecretBox::new(RequestKeyFactorySeed::default()); + let mut bytes = SecretBox::new(SessionSecretFactorySeed::default()); rng.fill_bytes(bytes.as_mut_secret()); Self(bytes) } - /// Creates a secret key factory using the default RNG. + /// Creates a session secret factory using the default RNG. pub fn random() -> Self { Self::random_with_rng(&mut OsRng) } /// Returns the seed size required by pub fn seed_size() -> usize { - SecretKeyFactorySeedSize::to_usize() + SessionSecretFactorySeedSize::to_usize() } - /// Creates a RequestKeyFactory factory using the given random bytes. + /// Creates a `SessionSecretFactory` using the given random bytes. /// /// **Warning:** make sure the given seed has been obtained /// from a cryptographically secure source of randomness! pub fn from_secure_randomness( seed: &[u8], - ) -> Result { + ) -> Result { if seed.len() != Self::seed_size() { - return Err(InvalidRequestFactorySeedLength); + return Err(InvalidSessionSecretFactorySeedLength); } - Ok(Self(SecretBox::new(*RequestKeyFactorySeed::from_slice( + Ok(Self(SecretBox::new(*SessionSecretFactorySeed::from_slice( seed, )))) } - /// Creates a `RequestSecretKey` deterministically from the given label. - pub fn make_key(&self, label: &[u8]) -> RequestSecretKey { - let prefix = b"REQUEST_KEY_DERIVATION/"; + /// Creates a `SessionStaticSecret` deterministically from the given label. + pub fn make_key(&self, label: &[u8]) -> SessionStaticSecret { + let prefix = b"SESSION_KEY_DERIVATION/"; let info = [prefix, label].concat(); - let seed = kdf::(self.0.as_secret(), Some(&info)); + let seed = kdf::(self.0.as_secret(), Some(&info)); let mut rng = ChaCha20Rng::from_seed(<[u8; 32]>::try_from(seed.as_secret().as_slice()).unwrap()); - RequestSecretKey::random_from_rng(&mut rng) + SessionStaticSecret::random_from_rng(&mut rng) } - /// Creates a `RequestKeyFactory` deterministically from the given label. + /// Creates a `SessionSecretFactory` deterministically from the given label. pub fn make_factory(&self, label: &[u8]) -> Self { - let prefix = b"REQUEST_KEY_FACTORY_DERIVATION/"; + let prefix = b"SESSION_SECRET_FACTORY_DERIVATION/"; let info = [prefix, label].concat(); - let derived_seed = kdf::(self.0.as_secret(), Some(&info)); + let derived_seed = kdf::(self.0.as_secret(), Some(&info)); Self(derived_seed) } } - impl fmt::Display for RequestKeyFactory { + impl fmt::Display for SessionSecretFactory { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "SecretKeyFactory:...") + write!(f, "SessionSecretFactory:...") } } } @@ -375,8 +376,8 @@ impl ThresholdDecryptionRequest { /// Encrypts the decryption request. pub fn encrypt( &self, - shared_secret: &RequestSharedSecret, - requester_public_key: &RequestPublicKey, + shared_secret: &SessionSharedSecret, + requester_public_key: &SessionStaticKey, ) -> EncryptedThresholdDecryptionRequest { EncryptedThresholdDecryptionRequest::new(self, shared_secret, requester_public_key) } @@ -413,7 +414,7 @@ pub struct EncryptedThresholdDecryptionRequest { pub ritual_id: u32, /// Public key of requester - pub requester_public_key: RequestPublicKey, + pub requester_public_key: SessionStaticKey, #[serde(with = "serde_bytes::as_base64")] /// Encrypted request @@ -423,8 +424,8 @@ pub struct EncryptedThresholdDecryptionRequest { impl EncryptedThresholdDecryptionRequest { fn new( request: &ThresholdDecryptionRequest, - shared_secret: &RequestSharedSecret, - requester_public_key: &RequestPublicKey, + shared_secret: &SessionSharedSecret, + requester_public_key: &SessionStaticKey, ) -> Self { let ciphertext = encrypt_with_shared_secret(shared_secret, &request.to_bytes()) .expect("encryption failed - out of memory?"); @@ -438,7 +439,7 @@ impl EncryptedThresholdDecryptionRequest { /// Decrypts the decryption request pub fn decrypt( &self, - shared_secret: &RequestSharedSecret, + shared_secret: &SessionSharedSecret, ) -> Result { let decryption_request_bytes = decrypt_with_shared_secret(shared_secret, &self.ciphertext)?; let decryption_request = ThresholdDecryptionRequest::from_bytes(&decryption_request_bytes) @@ -494,7 +495,7 @@ impl ThresholdDecryptionResponse { /// Encrypts the decryption response. pub fn encrypt( &self, - shared_secret: &RequestSharedSecret, + shared_secret: &SessionSharedSecret, ) -> EncryptedThresholdDecryptionResponse { EncryptedThresholdDecryptionResponse::new(self, shared_secret) } @@ -535,7 +536,7 @@ pub struct EncryptedThresholdDecryptionResponse { } impl EncryptedThresholdDecryptionResponse { - fn new(response: &ThresholdDecryptionResponse, shared_secret: &RequestSharedSecret) -> Self { + fn new(response: &ThresholdDecryptionResponse, shared_secret: &SessionSharedSecret) -> Self { let ciphertext = encrypt_with_shared_secret(shared_secret, &response.to_bytes()) .expect("encryption failed - out of memory?"); Self { @@ -547,7 +548,7 @@ impl EncryptedThresholdDecryptionResponse { /// Decrypts the decryption request pub fn decrypt( &self, - shared_secret: &RequestSharedSecret, + shared_secret: &SessionSharedSecret, ) -> Result { let decryption_response_bytes = decrypt_with_shared_secret(shared_secret, &self.ciphertext)?; @@ -588,22 +589,22 @@ mod tests { use crate::{ Conditions, Context, EncryptedThresholdDecryptionRequest, - EncryptedThresholdDecryptionResponse, FerveoVariant, ProtocolObject, RequestKeyFactory, + EncryptedThresholdDecryptionResponse, FerveoVariant, ProtocolObject, SessionSecretFactory, ThresholdDecryptionRequest, ThresholdDecryptionResponse, }; use generic_array::typenum::Unsigned; - use crate::dkg::request_keys::RequestSecretKey; + use crate::dkg::session::SessionStaticSecret; use crate::dkg::{ decrypt_with_shared_secret, encrypt_with_shared_secret, DecryptionError, NonceSize, }; #[test] fn decryption_with_shared_secret() { - let service_secret = RequestSecretKey::random(); + let service_secret = SessionStaticSecret::random(); - let requester_secret = RequestSecretKey::random(); + let requester_secret = SessionStaticSecret::random(); let requester_public_key = requester_secret.public_key(); let service_shared_secret = service_secret.derive_shared_secret(&requester_public_key); @@ -620,7 +621,7 @@ mod tests { #[test] fn request_key_factory() { - let secret_factory = RequestKeyFactory::random(); + let secret_factory = SessionSecretFactory::random(); // ensure that shared secret derived from factory can be used correctly let label_1 = b"label_1".to_vec().into_boxed_slice(); @@ -649,7 +650,7 @@ mod tests { assert_eq!(requester_public_key, same_requester_public_key); // ensure different key generated using same seed but using different factory - let other_secret_factory = RequestKeyFactory::random(); + let other_secret_factory = SessionSecretFactory::random(); let not_same_requester_secret_key = other_secret_factory.make_key(&label_2.as_ref()); let not_same_requester_public_key = not_same_requester_secret_key.public_key(); assert_ne!(requester_public_key, not_same_requester_public_key); @@ -670,11 +671,11 @@ mod tests { // test secure randomness let bytes = [0u8; 32]; - let factory = RequestKeyFactory::from_secure_randomness(&bytes); + let factory = SessionSecretFactory::from_secure_randomness(&bytes); assert!(factory.is_ok()); let bytes = [0u8; 31]; - let factory = RequestKeyFactory::from_secure_randomness(&bytes); + let factory = SessionSecretFactory::from_secure_randomness(&bytes); assert!(factory.is_err()); } @@ -682,9 +683,9 @@ mod tests { fn threshold_decryption_request() { let ritual_id = 0; - let service_secret = RequestSecretKey::random(); + let service_secret = SessionStaticSecret::random(); - let requester_secret = RequestSecretKey::random(); + let requester_secret = SessionStaticSecret::random(); let requester_public_key = requester_secret.public_key(); let dkg_pk = DkgPublicKey::random(); @@ -729,7 +730,7 @@ mod tests { assert_eq!(decrypted_request, request); // wrong shared key used - let random_secret_key = RequestSecretKey::random(); + let random_secret_key = SessionStaticSecret::random(); let random_shared_secret = random_secret_key.derive_shared_secret(&requester_public_key); assert!(encrypted_request_from_bytes .decrypt(&random_shared_secret) @@ -740,8 +741,8 @@ mod tests { fn threshold_decryption_response() { let ritual_id = 5; - let service_secret = RequestSecretKey::random(); - let requester_secret = RequestSecretKey::random(); + let service_secret = SessionStaticSecret::random(); + let requester_secret = SessionStaticSecret::random(); let decryption_share = b"The Tyranny of Merit"; @@ -777,7 +778,7 @@ mod tests { ); // wrong shared key used - let random_secret_key = RequestSecretKey::random(); + let random_secret_key = SessionStaticSecret::random(); let random_shared_secret = random_secret_key.derive_shared_secret(&requester_public_key); assert!(encrypted_response_from_bytes .decrypt(&random_shared_secret) diff --git a/nucypher-core/src/lib.rs b/nucypher-core/src/lib.rs index 0b7554e9..3ed94ba7 100644 --- a/nucypher-core/src/lib.rs +++ b/nucypher-core/src/lib.rs @@ -28,7 +28,7 @@ pub struct VerificationError; pub use address::Address; pub use conditions::{Conditions, Context}; pub use dkg::{ - request_keys::{RequestKeyFactory, RequestPublicKey, RequestSecretKey, RequestSharedSecret}, + session::{SessionSecretFactory, SessionSharedSecret, SessionStaticKey, SessionStaticSecret}, DecryptionError, EncryptedThresholdDecryptionRequest, EncryptedThresholdDecryptionResponse, EncryptionError, FerveoVariant, ThresholdDecryptionRequest, ThresholdDecryptionResponse, }; From 8e7a4fa84a5b2939e0530d89b0ee7c38012d72c4 Mon Sep 17 00:00:00 2001 From: derekpierre Date: Tue, 6 Jun 2023 10:48:35 -0400 Subject: [PATCH 14/15] Use KDF instead of hash function for additional entrope for SessionSharedSecret. --- nucypher-core/src/dkg.rs | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/nucypher-core/src/dkg.rs b/nucypher-core/src/dkg.rs index ce8dcea3..564eed1e 100644 --- a/nucypher-core/src/dkg.rs +++ b/nucypher-core/src/dkg.rs @@ -122,7 +122,6 @@ pub mod session { use rand_chacha::ChaCha20Rng; use rand_core::{CryptoRng, OsRng, RngCore}; use serde::{Deserialize, Serialize}; - use sha2::{Digest, Sha256}; use x25519_dalek::{PublicKey, SharedSecret, StaticSecret}; use crate::secret_box::{kdf, SecretBox}; @@ -135,25 +134,22 @@ pub mod session { /// A Diffie-Hellman shared secret #[derive(ZeroizeOnDrop)] pub struct SessionSharedSecret { - shared_secret: SharedSecret, - hashed_bytes: [u8; 32], + derived_bytes: [u8; 32], } /// Implementation of Diffie-Hellman shared secret impl SessionSharedSecret { /// Create new shared secret from underlying library. pub fn new(shared_secret: SharedSecret) -> Self { - let hash = Sha256::digest(shared_secret.as_bytes()); - let hashed_bytes = hash.as_slice().try_into().expect("invalid length"); - Self { - shared_secret, - hashed_bytes, - } + let info = b"SESSION_SHARED_SECRET_DERIVATION/"; + let derived_key = kdf::(shared_secret.as_bytes(), Some(info)); + let derived_bytes = <[u8; 32]>::try_from(derived_key.as_secret().as_slice()).unwrap(); + Self { derived_bytes } } /// View this shared secret as a byte array. pub fn as_bytes(&self) -> &[u8; 32] { - &self.hashed_bytes + &self.derived_bytes } } From 253dfde60e6106ceeda696b86cb2f605ce3cd557 Mon Sep 17 00:00:00 2001 From: derekpierre Date: Tue, 6 Jun 2023 11:11:31 -0400 Subject: [PATCH 15/15] Remove unnecessary/excession make_factory method from SessionSecretFactory. --- .../nucypher_core/__init__.pyi | 4 ---- nucypher-core-python/src/lib.rs | 6 ------ nucypher-core-wasm/src/lib.rs | 5 ----- nucypher-core/src/dkg.rs | 19 ++++++++----------- 4 files changed, 8 insertions(+), 26 deletions(-) diff --git a/nucypher-core-python/nucypher_core/__init__.pyi b/nucypher-core-python/nucypher_core/__init__.pyi index e96c3263..fce1d08b 100644 --- a/nucypher-core-python/nucypher_core/__init__.pyi +++ b/nucypher-core-python/nucypher_core/__init__.pyi @@ -534,7 +534,3 @@ class SessionSecretFactory: def make_key(self, label: bytes) -> SessionStaticSecret: ... - - def make_factory(self, label: bytes) -> RequestKeyFactory: - ... - diff --git a/nucypher-core-python/src/lib.rs b/nucypher-core-python/src/lib.rs index 63b84d8e..5aa09c55 100644 --- a/nucypher-core-python/src/lib.rs +++ b/nucypher-core-python/src/lib.rs @@ -737,12 +737,6 @@ impl SessionSecretFactory { } } - pub fn make_factory(&self, label: &[u8]) -> SessionSecretFactory { - SessionSecretFactory { - backend: self.backend.make_factory(label), - } - } - fn __str__(&self) -> String { self.backend.to_string() } diff --git a/nucypher-core-wasm/src/lib.rs b/nucypher-core-wasm/src/lib.rs index a4ca38ec..a84f4fc3 100644 --- a/nucypher-core-wasm/src/lib.rs +++ b/nucypher-core-wasm/src/lib.rs @@ -633,11 +633,6 @@ impl SessionSecretFactory { SessionStaticSecret(self.0.make_key(label)) } - #[wasm_bindgen(js_name = makeFactory)] - pub fn make_factory(&self, label: &[u8]) -> Self { - Self(self.0.make_factory(label)) - } - #[allow(clippy::inherent_to_string)] #[wasm_bindgen(js_name = toString)] pub fn to_string(&self) -> String { diff --git a/nucypher-core/src/dkg.rs b/nucypher-core/src/dkg.rs index 564eed1e..3ca231cf 100644 --- a/nucypher-core/src/dkg.rs +++ b/nucypher-core/src/dkg.rs @@ -264,6 +264,7 @@ pub mod session { type SessionSecretFactorySeed = GenericArray; /// Error thrown when invalid random seed provided for creating key factory. + #[derive(Debug)] pub struct InvalidSessionSecretFactorySeedLength; impl fmt::Display for InvalidSessionSecretFactorySeedLength { @@ -319,14 +320,6 @@ pub mod session { ChaCha20Rng::from_seed(<[u8; 32]>::try_from(seed.as_secret().as_slice()).unwrap()); SessionStaticSecret::random_from_rng(&mut rng) } - - /// Creates a `SessionSecretFactory` deterministically from the given label. - pub fn make_factory(&self, label: &[u8]) -> Self { - let prefix = b"SESSION_SECRET_FACTORY_DERIVATION/"; - let info = [prefix, label].concat(); - let derived_seed = kdf::(self.0.as_secret(), Some(&info)); - Self(derived_seed) - } } impl fmt::Display for SessionSecretFactory { @@ -590,6 +583,7 @@ mod tests { }; use generic_array::typenum::Unsigned; + use rand_core::RngCore; use crate::dkg::session::SessionStaticSecret; use crate::dkg::{ @@ -652,9 +646,12 @@ mod tests { assert_ne!(requester_public_key, not_same_requester_public_key); // ensure that two secret factories with the same seed generate the same keys - let secret_factory_label = b"seeded_secret_factory_label".to_vec().into_boxed_slice(); - let seeded_secret_factory_1 = secret_factory.make_factory(&secret_factory_label.as_ref()); - let seeded_secret_factory_2 = secret_factory.make_factory(&secret_factory_label.as_ref()); + let mut secret_factory_seed = [0u8; 32]; + rand::thread_rng().fill_bytes(&mut secret_factory_seed); + let seeded_secret_factory_1 = + SessionSecretFactory::from_secure_randomness(&secret_factory_seed).unwrap(); + let seeded_secret_factory_2 = + SessionSecretFactory::from_secure_randomness(&secret_factory_seed).unwrap(); let key_label = b"seeded_factory_key_label".to_vec().into_boxed_slice(); let sk_1 = seeded_secret_factory_1.make_key(&key_label);