From 83b751c71fee1de2f9d84d4a8981420a215bbfb1 Mon Sep 17 00:00:00 2001 From: Robin Salen Date: Tue, 17 Oct 2023 15:50:52 -0400 Subject: [PATCH 01/10] Move to AES-GCM --- Cargo.toml | 2 +- README.md | 2 +- src/dkg/key_generation.rs | 4 +-- src/dkg/secret_share.rs | 55 +++++++++++++++++++++------------------ src/error.rs | 5 ++++ 5 files changed, 39 insertions(+), 29 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 51ea152..1712885 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,7 +26,7 @@ digest = { version = "0.10", default-features = false, features = ["alloc"] } getrandom = { version = "0.2", default-features = false, features = ["js"] } rand = { version = "0.8", default-features = false, features = ["alloc", "getrandom", "libc"] } sha2 = { version = "0.10", default-features = false } -aes = { version = "0.7", default-features = false, features = ["ctr"] } +aes-gcm = { version = "0.10", default-features = false, features = ["aes", "alloc", "getrandom", "zeroize"] } hkdf = { version = "0.12", default-features = false } zeroize = { version = "1", default-features = false, features = ["alloc", "zeroize_derive"] } diff --git a/README.md b/README.md index 9e9b087..49d05af 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ Note however that two parameters are not modular, at least in the current versio - the hash function targeted security parameter: this crate assumes 128 bits of collision security for the ciphersuite's internal hashers. One **MUST** provide a hasher with _at least_ 128 bits of collision security when instantiating an ICE-FROST ciphersuite. -- the secret share encryption mechanism: this part of the distributed key generation currently relies on AES128-CTR with HKDF instantiated from SHA-256. +- the secret share encryption mechanism: this part of the distributed key generation currently relies on AES128-GCM with HKDF instantiated from SHA-256. This library also provides by default an example instantiation over the Secp256k1 curve with SHA-256, to be used in tests and benchmarks. diff --git a/src/dkg/key_generation.rs b/src/dkg/key_generation.rs index 8ce4eaf..353bf28 100644 --- a/src/dkg/key_generation.rs +++ b/src/dkg/key_generation.rs @@ -2075,7 +2075,7 @@ mod test { // Wrong decryption from nonce { let mut wrong_encrypted_secret_share = p1_their_encrypted_secret_shares[1].clone(); - wrong_encrypted_secret_share.nonce = [42; 16]; + wrong_encrypted_secret_share.nonce = [42; 12]; let p1_my_encrypted_secret_shares = vec![ p1_their_encrypted_secret_shares[0].clone(), p2_their_encrypted_secret_shares[0].clone(), @@ -2404,7 +2404,7 @@ mod test { { let wrong_encrypted_secret_share = - EncryptedSecretShare::new(1, 2, [0; 16], vec![0]); + EncryptedSecretShare::new(1, 2, [0; 12], vec![0]); let p1_my_encrypted_secret_shares = vec![ p1_their_encrypted_secret_shares[0].clone(), diff --git a/src/dkg/secret_share.rs b/src/dkg/secret_share.rs index 92aab90..9673842 100644 --- a/src/dkg/secret_share.rs +++ b/src/dkg/secret_share.rs @@ -2,7 +2,7 @@ //! and their public commitments, along with their encrypted versions //! post Diffie-Hellman key exchange. //! -//! This module currently only supports AES128-CTR with HKDF instantiated +//! This module currently only supports AES128-GCM with HKDF instantiated //! from SHA-256. use core::marker::PhantomData; @@ -17,10 +17,13 @@ use ark_ec::{CurveGroup, Group}; use ark_ff::{Field, Zero}; use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; +use digest::generic_array::GenericArray; use rand::{CryptoRng, RngCore}; -use aes::cipher::{generic_array::GenericArray, FromBlockCipher, NewBlockCipher, StreamCipher}; -use aes::{Aes128, Aes128Ctr}; +use aes_gcm::{ + aead::{Aead, AeadCore, KeyInit}, + Aes128Gcm, Key, +}; use hkdf::Hkdf; use sha2::Sha256; @@ -118,8 +121,8 @@ pub struct EncryptedSecretShare { pub sender_index: u32, /// The participant index that this secret share was calculated for. pub receiver_index: u32, - /// The nonce to be used for decryption with AES-CTR mode. - pub nonce: [u8; 16], + /// The nonce to be used for decryption with AES-GCM mode. It is 96-bit long. + pub nonce: [u8; 12], /// The encrypted polynomial evaluation. pub(crate) encrypted_polynomial_evaluation: Vec, #[zeroize(skip)] @@ -139,7 +142,7 @@ impl EncryptedSecretShare { pub fn new( sender_index: u32, receiver_index: u32, - nonce: [u8; 16], + nonce: [u8; 12], encrypted_polynomial_evaluation: Vec, ) -> Self { Self { @@ -195,33 +198,32 @@ impl VerifiableSecretSharingCommitment { pub(crate) fn encrypt_share( share: &SecretShare, aes_key: &[u8], - mut rng: impl RngCore + CryptoRng, + rng: impl RngCore + CryptoRng, ) -> FrostResult> { let hkdf = Hkdf::::new(None, aes_key); let mut final_aes_key = [0u8; 16]; hkdf.expand(&[], &mut final_aes_key) .map_err(|_| Error::Custom("KDF expansion failed unexpectedly".to_string()))?; - let mut nonce_array = [0u8; 16]; - rng.fill_bytes(&mut nonce_array); - - let final_aes_key = GenericArray::from_slice(&final_aes_key); - let nonce = GenericArray::from_slice(&nonce_array); - let cipher = Aes128::new(final_aes_key); - let mut cipher = Aes128Ctr::from_block_cipher(cipher, nonce); + let key = Key::::from_slice(&final_aes_key); + let nonce = Aes128Gcm::generate_nonce(rng); + let cipher = Aes128Gcm::new(&key); let mut share_bytes = Vec::with_capacity(share.polynomial_evaluation.compressed_size()); share .polynomial_evaluation .serialize_compressed(&mut share_bytes) .map_err(|_| Error::CompressionError)?; - cipher.apply_keystream(&mut share_bytes); + + let encrypted_polynomial_evaluation = cipher + .encrypt(&nonce, share_bytes.as_ref()) + .map_err(|_| Error::EncryptionError)?; Ok(EncryptedSecretShare:: { sender_index: share.sender_index, receiver_index: share.receiver_index, - nonce: nonce_array, - encrypted_polynomial_evaluation: share_bytes, + nonce: (*nonce).try_into().unwrap(), // This unwrap cannot fail. + encrypted_polynomial_evaluation, _phantom: PhantomData, }) } @@ -235,17 +237,20 @@ pub(crate) fn decrypt_share( hkdf.expand(&[], &mut final_aes_key) .expect("KDF expansion failed unexpectedly"); - let final_aes_key = GenericArray::from_slice(&final_aes_key); + let key = Key::::from_slice(&final_aes_key); let nonce = GenericArray::from_slice(&encrypted_share.nonce); - let cipher = Aes128::new(final_aes_key); - let mut cipher = Aes128Ctr::from_block_cipher(cipher, nonce); + let cipher = Aes128Gcm::new(&key); - let mut bytes = encrypted_share.encrypted_polynomial_evaluation.clone(); - cipher.apply_keystream(&mut bytes); + let bytes = cipher + .decrypt( + nonce, + encrypted_share.encrypted_polynomial_evaluation.as_ref(), + ) + .map_err(|_| Error::DecryptionError)?; let evaluation = - Scalar::::deserialize_compressed(&bytes[..]).map_err(|_| Error::DecryptionError)?; + Scalar::::deserialize_compressed(&bytes[..]).map_err(|_| Error::DecompressionError)?; Ok(SecretShare { sender_index: encrypted_share.sender_index, @@ -282,8 +287,8 @@ mod test { } for _ in 0..100 { - let mut nonce = [0u8; 16]; - let mut encrypted_polynomial_evaluation = vec![0u8; 16]; + let mut nonce = [0u8; 12]; + let mut encrypted_polynomial_evaluation = vec![0u8; 12]; rng.fill_bytes(&mut nonce); rng.fill_bytes(&mut encrypted_polynomial_evaluation); let encrypted_secret_share = EncryptedSecretShare::::new( diff --git a/src/error.rs b/src/error.rs index bb20169..c4b1004 100644 --- a/src/error.rs +++ b/src/error.rs @@ -19,6 +19,8 @@ pub enum Error { DecompressionError, /// Encrypted secret share decryption failure DecryptionError, + /// Secret share encryption failure + EncryptionError, /// Secret share verification failure ShareVerificationError, /// Complaint verification failure @@ -73,6 +75,9 @@ impl core::fmt::Display for Error { Error::DecryptionError => { write!(f, "Could not decrypt encrypted share.") } + Error::EncryptionError => { + write!(f, "Could not encrypt secret share.") + } Error::ShareVerificationError => { write!(f, "The secret share is not correct.") } From 36cb6e20e997905860068453067e70eaa06665fc Mon Sep 17 00:00:00 2001 From: Robin Salen Date: Tue, 17 Oct 2023 15:52:42 -0400 Subject: [PATCH 02/10] Reuse HASH_SEC_PARAM constant --- src/dkg/complaint.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/dkg/complaint.rs b/src/dkg/complaint.rs index e0bb4b8..09feed9 100644 --- a/src/dkg/complaint.rs +++ b/src/dkg/complaint.rs @@ -11,6 +11,7 @@ use rand::{CryptoRng, RngCore}; use sha2::Sha256; use crate::ciphersuite::CipherSuite; +use crate::HASH_SEC_PARAM; use ark_ec::Group; use ark_ff::field_hashers::{DefaultFieldHasher, HashToField}; @@ -47,7 +48,7 @@ impl Complaint { let a1 = C::G::generator().mul(r); let a2 = accused_pk.mul(r); - let hasher = as HashToField>>::new( + let hasher = as HashToField>>::new( "Complaint Context".as_bytes(), ); @@ -84,7 +85,7 @@ impl Complaint { /// -- a1 + h.pk_i = z.g /// -- a2 + h.k_il = z.pk_l pub fn verify(&self, pk_i: &C::G, pk_l: &C::G) -> FrostResult { - let hasher = as HashToField>>::new( + let hasher = as HashToField>>::new( "Complaint Context".as_bytes(), ); From 4a67cdf6dbc5d71b06c8b14360de4c2ecda267ae Mon Sep 17 00:00:00 2001 From: Robin Salen Date: Tue, 17 Oct 2023 17:53:14 -0400 Subject: [PATCH 03/10] Make the internal hasher for share encryption generic --- Cargo.toml | 1 + src/ciphersuite.rs | 4 ++++ src/dkg/secret_share.rs | 15 ++++++--------- src/lib.rs | 9 ++++++++- 4 files changed, 19 insertions(+), 10 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 1712885..ec8cbc1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,6 +18,7 @@ exclude = [ ".gitignore" ] rustdoc-args = ["--html-in-header", "/opt/rustwide/workdir/docs/assets/rustdoc-include-katex-header.html"] [dependencies] +aead = { version = "0.5", default-features = false, features = ["alloc", "getrandom"]} ark-secp256k1 = { version = "0.4", default-features = false } ark-ff = { version = "0.4", default-features = false } ark-ec = { version = "0.4", default-features = false } diff --git a/src/ciphersuite.rs b/src/ciphersuite.rs index 7efd839..91060a5 100644 --- a/src/ciphersuite.rs +++ b/src/ciphersuite.rs @@ -3,6 +3,7 @@ use core::fmt::Debug; use core::marker::{Send, Sync}; +use aead::{Aead, KeyInit}; use zeroize::Zeroize; use ark_ec::CurveGroup; @@ -27,6 +28,9 @@ pub trait CipherSuite: Copy + Clone + PartialEq + Eq + Debug + Send + Sync + Zer /// The underlying hasher used to construct all random oracles of this [`CipherSuite`] . type InnerHasher: Default + Clone + Digest + DynDigest; + /// The underlying hasher used to construct all random oracles of this [`CipherSuite`] . + type Cipher: Aead + KeyInit; + ////////////////////////////////////////////////////////////////////////////////////////////// // Required methods diff --git a/src/dkg/secret_share.rs b/src/dkg/secret_share.rs index 9673842..81d8aa2 100644 --- a/src/dkg/secret_share.rs +++ b/src/dkg/secret_share.rs @@ -20,10 +20,7 @@ use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; use digest::generic_array::GenericArray; use rand::{CryptoRng, RngCore}; -use aes_gcm::{ - aead::{Aead, AeadCore, KeyInit}, - Aes128Gcm, Key, -}; +use aead::{Aead, AeadCore, Key, KeyInit}; use hkdf::Hkdf; use sha2::Sha256; @@ -205,9 +202,9 @@ pub(crate) fn encrypt_share( hkdf.expand(&[], &mut final_aes_key) .map_err(|_| Error::Custom("KDF expansion failed unexpectedly".to_string()))?; - let key = Key::::from_slice(&final_aes_key); - let nonce = Aes128Gcm::generate_nonce(rng); - let cipher = Aes128Gcm::new(&key); + let key = Key::::from_slice(&final_aes_key); + let nonce = C::Cipher::generate_nonce(rng); + let cipher = C::Cipher::new(key); let mut share_bytes = Vec::with_capacity(share.polynomial_evaluation.compressed_size()); share @@ -237,10 +234,10 @@ pub(crate) fn decrypt_share( hkdf.expand(&[], &mut final_aes_key) .expect("KDF expansion failed unexpectedly"); - let key = Key::::from_slice(&final_aes_key); + let key = Key::::from_slice(&final_aes_key); let nonce = GenericArray::from_slice(&encrypted_share.nonce); - let cipher = Aes128Gcm::new(&key); + let cipher = C::Cipher::new(&key); let bytes = cipher .decrypt( diff --git a/src/lib.rs b/src/lib.rs index 593751a..b1b6426 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -18,12 +18,14 @@ //! This CipherSuite is used to parameterize ICE-FROST over an arbitrary curve backend, with //! an arbitrary underlying hasher instantiating all random oracles. //! The following example creates an ICE-FROST CipherSuite over the Secp256k1 curve, -//! with SHA-256 as internal hash function. +//! with SHA-256 as internal hash function, and AES-GCM with a 128-bit key and 96-bit nonce +//! as internal block cipher. //! //! ```rust //! use ice_frost::CipherSuite; //! use sha2::Sha256; //! use zeroize::Zeroize; +//! use aes_gcm::Aes128Gcm; //! use ark_secp256k1::Projective as G; //! //! #[derive(Debug, Copy, Clone, PartialEq, Eq, Default, Zeroize)] @@ -36,6 +38,8 @@ //! //! type InnerHasher = Sha256; //! +//! type Cipher = Aes128Gcm; +//! //! fn context_string() -> String { //! "ICE-FROST_SECP256K1_SHA256".to_owned() //! } @@ -1255,6 +1259,7 @@ pub mod sign; pub mod testing { use super::*; + use aes_gcm::Aes128Gcm; use ark_secp256k1::Projective as G; use sha2::Sha256; @@ -1273,6 +1278,8 @@ pub mod testing { type InnerHasher = Sha256; + type Cipher = Aes128Gcm; + fn context_string() -> String { "ICE-FROST_SECP256K1_SHA256".to_owned() } From d8bd772d740398c9149d61bf36eea58f37df4fe9 Mon Sep 17 00:00:00 2001 From: Robin Salen Date: Tue, 17 Oct 2023 18:36:19 -0400 Subject: [PATCH 04/10] Change nonce type --- src/ciphersuite.rs | 2 +- src/dkg/key_generation.rs | 4 +- src/dkg/secret_share.rs | 107 +++++++++++++++++++++++++++++++++----- 3 files changed, 98 insertions(+), 15 deletions(-) diff --git a/src/ciphersuite.rs b/src/ciphersuite.rs index 91060a5..0a918f1 100644 --- a/src/ciphersuite.rs +++ b/src/ciphersuite.rs @@ -29,7 +29,7 @@ pub trait CipherSuite: Copy + Clone + PartialEq + Eq + Debug + Send + Sync + Zer type InnerHasher: Default + Clone + Digest + DynDigest; /// The underlying hasher used to construct all random oracles of this [`CipherSuite`] . - type Cipher: Aead + KeyInit; + type Cipher: Aead + KeyInit + Clone; ////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/dkg/key_generation.rs b/src/dkg/key_generation.rs index 353bf28..9418507 100644 --- a/src/dkg/key_generation.rs +++ b/src/dkg/key_generation.rs @@ -2075,7 +2075,7 @@ mod test { // Wrong decryption from nonce { let mut wrong_encrypted_secret_share = p1_their_encrypted_secret_shares[1].clone(); - wrong_encrypted_secret_share.nonce = [42; 12]; + wrong_encrypted_secret_share.nonce = [42; 12].into(); let p1_my_encrypted_secret_shares = vec![ p1_their_encrypted_secret_shares[0].clone(), p2_their_encrypted_secret_shares[0].clone(), @@ -2404,7 +2404,7 @@ mod test { { let wrong_encrypted_secret_share = - EncryptedSecretShare::new(1, 2, [0; 12], vec![0]); + EncryptedSecretShare::new(1, 2, [0; 12].into(), vec![0]); let p1_my_encrypted_secret_shares = vec![ p1_their_encrypted_secret_shares[0].clone(), diff --git a/src/dkg/secret_share.rs b/src/dkg/secret_share.rs index 81d8aa2..8774d99 100644 --- a/src/dkg/secret_share.rs +++ b/src/dkg/secret_share.rs @@ -5,6 +5,7 @@ //! This module currently only supports AES128-GCM with HKDF instantiated //! from SHA-256. +use core::fmt::Debug; use core::marker::PhantomData; use crate::serialization::impl_serialization_traits; @@ -17,10 +18,9 @@ use ark_ec::{CurveGroup, Group}; use ark_ff::{Field, Zero}; use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; -use digest::generic_array::GenericArray; use rand::{CryptoRng, RngCore}; -use aead::{Aead, AeadCore, Key, KeyInit}; +use aead::{Aead, AeadCore, Key, KeyInit, Nonce}; use hkdf::Hkdf; use sha2::Sha256; @@ -112,20 +112,104 @@ impl SecretShare { } /// A secret share encrypted with a participant's public key -#[derive(Clone, Debug, Eq, PartialEq, CanonicalSerialize, CanonicalDeserialize, Zeroize)] +#[derive(Clone, Zeroize)] pub struct EncryptedSecretShare { /// The index of the share maker. pub sender_index: u32, /// The participant index that this secret share was calculated for. pub receiver_index: u32, - /// The nonce to be used for decryption with AES-GCM mode. It is 96-bit long. - pub nonce: [u8; 12], + /// The nonce to be used for decryption of this encrypted share. + pub nonce: Nonce, /// The encrypted polynomial evaluation. pub(crate) encrypted_polynomial_evaluation: Vec, #[zeroize(skip)] _phantom: PhantomData, } +impl PartialEq for EncryptedSecretShare { + fn eq(&self, other: &Self) -> bool { + self.sender_index == other.sender_index + && self.receiver_index == other.receiver_index + && self.nonce.as_slice() == other.nonce.as_slice() + && self.encrypted_polynomial_evaluation == other.encrypted_polynomial_evaluation + } +} + +impl Eq for EncryptedSecretShare {} + +impl Debug for EncryptedSecretShare { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!( + f, + "EncryptedSecretShare {{ sender_index: {}, receiver_index: {}, nonce: {:?}, encrypted_polynomial_evaluation: {:?} }}", + self.sender_index, + self.receiver_index, + self.nonce, + self.encrypted_polynomial_evaluation + ) + } +} + +// Required trait to implement `CanonicalDeserialize` below. +impl ark_serialize::Valid for EncryptedSecretShare { + fn check(&self) -> Result<(), ark_serialize::SerializationError> { + self.sender_index.check()?; + self.receiver_index.check()?; + self.nonce.to_vec().check()?; + self.encrypted_polynomial_evaluation.check() + } +} + +impl CanonicalSerialize for EncryptedSecretShare { + fn serialize_with_mode( + &self, + mut writer: W, + compress: ark_serialize::Compress, + ) -> Result<(), ark_serialize::SerializationError> { + self.sender_index + .serialize_with_mode(&mut writer, compress)?; + self.receiver_index + .serialize_with_mode(&mut writer, compress)?; + self.nonce + .to_vec() + .serialize_with_mode(&mut writer, compress)?; + self.encrypted_polynomial_evaluation + .serialize_with_mode(&mut writer, compress) + } + + fn serialized_size(&self, compress: ark_serialize::Compress) -> usize { + self.sender_index.serialized_size(compress) + + self.receiver_index.serialized_size(compress) + + self.nonce.serialized_size(compress) + + self + .encrypted_polynomial_evaluation + .serialized_size(compress) + } +} + +impl CanonicalDeserialize for EncryptedSecretShare { + fn deserialize_with_mode( + mut reader: R, + compress: ark_serialize::Compress, + validate: ark_serialize::Validate, + ) -> Result { + let sender_index = u32::deserialize_with_mode(&mut reader, compress, validate)?; + let receiver_index = u32::deserialize_with_mode(&mut reader, compress, validate)?; + let nonce_vec = Vec::::deserialize_with_mode(&mut reader, compress, validate)?; + let nonce = Nonce::::from_exact_iter(nonce_vec) + .ok_or(ark_serialize::SerializationError::InvalidData)?; + let encrypted_polynomial_evaluation = + Vec::::deserialize_with_mode(&mut reader, compress, validate)?; + + Ok(EncryptedSecretShare::::new( + sender_index, + receiver_index, + nonce, + encrypted_polynomial_evaluation, + )) + } +} + impl_serialization_traits!(EncryptedSecretShare); impl Drop for EncryptedSecretShare { @@ -139,7 +223,7 @@ impl EncryptedSecretShare { pub fn new( sender_index: u32, receiver_index: u32, - nonce: [u8; 12], + nonce: Nonce, encrypted_polynomial_evaluation: Vec, ) -> Self { Self { @@ -219,7 +303,7 @@ pub(crate) fn encrypt_share( Ok(EncryptedSecretShare:: { sender_index: share.sender_index, receiver_index: share.receiver_index, - nonce: (*nonce).try_into().unwrap(), // This unwrap cannot fail. + nonce, encrypted_polynomial_evaluation, _phantom: PhantomData, }) @@ -235,9 +319,8 @@ pub(crate) fn decrypt_share( .expect("KDF expansion failed unexpectedly"); let key = Key::::from_slice(&final_aes_key); - - let nonce = GenericArray::from_slice(&encrypted_share.nonce); - let cipher = C::Cipher::new(&key); + let nonce = Nonce::::from_slice(&encrypted_share.nonce); + let cipher = C::Cipher::new(key); let bytes = cipher .decrypt( @@ -291,7 +374,7 @@ mod test { let encrypted_secret_share = EncryptedSecretShare::::new( rng.next_u32(), rng.next_u32(), - nonce, + nonce.into(), encrypted_polynomial_evaluation, ); let mut bytes = Vec::with_capacity(encrypted_secret_share.compressed_size()); @@ -300,7 +383,7 @@ mod test { .unwrap(); assert_eq!( encrypted_secret_share, - EncryptedSecretShare::deserialize_compressed(&bytes[..]).unwrap() + EncryptedSecretShare::deserialize_compressed(&bytes[..]).unwrap(), ); } } From b81601fa44781030ab97f27761953f2cb46812b0 Mon Sep 17 00:00:00 2001 From: Robin Salen Date: Tue, 17 Oct 2023 18:54:08 -0400 Subject: [PATCH 05/10] Fix hardcoded length --- src/dkg/secret_share.rs | 42 ++++++++++++++++++++++++++--------------- 1 file changed, 27 insertions(+), 15 deletions(-) diff --git a/src/dkg/secret_share.rs b/src/dkg/secret_share.rs index 8774d99..ee029d7 100644 --- a/src/dkg/secret_share.rs +++ b/src/dkg/secret_share.rs @@ -20,7 +20,7 @@ use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; use rand::{CryptoRng, RngCore}; -use aead::{Aead, AeadCore, Key, KeyInit, Nonce}; +use aead::{Aead, AeadCore, Key, KeyInit, KeySizeUser, Nonce}; use hkdf::Hkdf; use sha2::Sha256; @@ -276,20 +276,27 @@ impl VerifiableSecretSharingCommitment { } } +/// Encrypt a `SecretShare` given an initial slice of bytes. +/// +/// This will perform an HMAC-based Extract-and-Expand Key Derivation Function (HKDF) +/// expansion over the Initial Key Material passed as input. The obtained Output Key +/// Material will then be passed to this `Ciphersuite`'s cipher along with a random +/// nonce to encrypt the private polynomial evaluation corresponding to this secret share. pub(crate) fn encrypt_share( share: &SecretShare, - aes_key: &[u8], + initial_key_bytes: &[u8], rng: impl RngCore + CryptoRng, ) -> FrostResult> { - let hkdf = Hkdf::::new(None, aes_key); - let mut final_aes_key = [0u8; 16]; - hkdf.expand(&[], &mut final_aes_key) + let hkdf = Hkdf::::new(None, initial_key_bytes); + let mut final_aead_key = vec![0u8; ::key_size()]; + hkdf.expand(&[], &mut final_aead_key) .map_err(|_| Error::Custom("KDF expansion failed unexpectedly".to_string()))?; - let key = Key::::from_slice(&final_aes_key); - let nonce = C::Cipher::generate_nonce(rng); + let key = Key::::from_slice(&final_aead_key); // This cannot panic. let cipher = C::Cipher::new(key); + let nonce = C::Cipher::generate_nonce(rng); + let mut share_bytes = Vec::with_capacity(share.polynomial_evaluation.compressed_size()); share .polynomial_evaluation @@ -309,22 +316,27 @@ pub(crate) fn encrypt_share( }) } +/// Decrypt an `EncryptedSecretShare` given an initial slice of bytes. +/// +/// This will perform an HMAC-based Extract-and-Expand Key Derivation Function (HKDF) +/// expansion over the Initial Key Material passed as input. The obtained Output Key +/// Material will then be passed to this `Ciphersuite`'s cipher along with the associated +/// nonce used to obtain this `EncryptedSecretShare` to decrypt it. pub(crate) fn decrypt_share( encrypted_share: &EncryptedSecretShare, - aes_key: &[u8], + initial_key_bytes: &[u8], ) -> FrostResult> { - let hkdf = Hkdf::::new(None, aes_key); - let mut final_aes_key = [0u8; 16]; - hkdf.expand(&[], &mut final_aes_key) - .expect("KDF expansion failed unexpectedly"); + let hkdf = Hkdf::::new(None, initial_key_bytes); + let mut final_aead_key = vec![0u8; ::key_size()]; + hkdf.expand(&[], &mut final_aead_key) + .map_err(|_| Error::Custom("KDF expansion failed unexpectedly".to_string()))?; - let key = Key::::from_slice(&final_aes_key); - let nonce = Nonce::::from_slice(&encrypted_share.nonce); + let key = Key::::from_slice(&final_aead_key); // This cannot panic. let cipher = C::Cipher::new(key); let bytes = cipher .decrypt( - nonce, + &encrypted_share.nonce, encrypted_share.encrypted_polynomial_evaluation.as_ref(), ) .map_err(|_| Error::DecryptionError)?; From 1c7685c776c87903a4bb6af5ebcdb0679d3e1d04 Mon Sep 17 00:00:00 2001 From: Robin Salen Date: Tue, 17 Oct 2023 19:01:05 -0400 Subject: [PATCH 06/10] Pacify clippy --- src/dkg/secret_share.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/dkg/secret_share.rs b/src/dkg/secret_share.rs index ee029d7..74aa3d3 100644 --- a/src/dkg/secret_share.rs +++ b/src/dkg/secret_share.rs @@ -12,6 +12,9 @@ use crate::serialization::impl_serialization_traits; use crate::utils::{Scalar, ToString, Vec}; use crate::{Error, FrostResult}; +#[cfg(not(feature = "std"))] +use alloc::vec; + use crate::ciphersuite::CipherSuite; use ark_ec::{CurveGroup, Group}; From 12a929f015eeda814aae5334de2a43a549eb16f0 Mon Sep 17 00:00:00 2001 From: Robin Salen Date: Tue, 17 Oct 2023 19:08:37 -0400 Subject: [PATCH 07/10] Add comment --- src/dkg/secret_share.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/dkg/secret_share.rs b/src/dkg/secret_share.rs index 74aa3d3..8ea42a5 100644 --- a/src/dkg/secret_share.rs +++ b/src/dkg/secret_share.rs @@ -158,6 +158,9 @@ impl ark_serialize::Valid for EncryptedSecretShare { fn check(&self) -> Result<(), ark_serialize::SerializationError> { self.sender_index.check()?; self.receiver_index.check()?; + // Collecting is not ideal for this and the `serialize_with_mode` / `deserialize_with_mode` + // implementation below, but is necessary, at least until the `GenericArray` dependency of + // the aead trait gets bumped to 1.0. self.nonce.to_vec().check()?; self.encrypted_polynomial_evaluation.check() } From 0500da17b7339992272eb0b1378d48e94245ff3c Mon Sep 17 00:00:00 2001 From: Robin Salen Date: Tue, 17 Oct 2023 19:19:01 -0400 Subject: [PATCH 08/10] Update doc --- src/ciphersuite.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/ciphersuite.rs b/src/ciphersuite.rs index 0a918f1..2cdc067 100644 --- a/src/ciphersuite.rs +++ b/src/ciphersuite.rs @@ -25,10 +25,11 @@ pub trait CipherSuite: Copy + Clone + PartialEq + Eq + Debug + Send + Sync + Zer /// A byte array of a given length for this [`CipherSuite`]'s binary hashers. type HashOutput: AsRef<[u8]> + AsMut<[u8]> + Default; - /// The underlying hasher used to construct all random oracles of this [`CipherSuite`] . + /// The underlying hasher used to construct all random oracles of this [`CipherSuite`]. type InnerHasher: Default + Clone + Digest + DynDigest; - /// The underlying hasher used to construct all random oracles of this [`CipherSuite`] . + /// The underlying cipher used to encrypt and decrypt all `SecretShare` + /// generated during a DKG phase of this [`CipherSuite`]. type Cipher: Aead + KeyInit + Clone; ////////////////////////////////////////////////////////////////////////////////////////////// From 1f836b0d892db0ae241e2679b763895a71cc9062 Mon Sep 17 00:00:00 2001 From: Robin Salen Date: Wed, 18 Oct 2023 19:11:34 -0400 Subject: [PATCH 09/10] Fix README description --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 49d05af..a7caf5c 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ Note however that two parameters are not modular, at least in the current versio - the hash function targeted security parameter: this crate assumes 128 bits of collision security for the ciphersuite's internal hashers. One **MUST** provide a hasher with _at least_ 128 bits of collision security when instantiating an ICE-FROST ciphersuite. -- the secret share encryption mechanism: this part of the distributed key generation currently relies on AES128-GCM with HKDF instantiated from SHA-256. +- the secret share encryption mechanism: this part of the distributed key generation currently relies on the ciphersuite's AEAD but with a fixed HKDF instantiated from SHA-256. This library also provides by default an example instantiation over the Secp256k1 curve with SHA-256, to be used in tests and benchmarks. From e9b3dd293689952a4acf3f1ba03ac54afbbbbc9d Mon Sep 17 00:00:00 2001 From: Robin Salen Date: Fri, 20 Oct 2023 17:59:06 -0400 Subject: [PATCH 10/10] Clarify README --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index a7caf5c..eccf365 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,8 @@ Please see the documentation for usage examples. This library has a modular backend supporting - arbitrary curves defined with the arkworks library suite; -- arbitrary hash functions for the internal random oracles of the ICE-FROST ciphersuite. +- arbitrary hash functions for the internal random oracles of the ICE-FROST ciphersuite; +- an arbitrary AEAD for the secret shares encryption part of the DKG / Key resharing phase. Note however that two parameters are not modular, at least in the current version: