From 32d6b94ed31c3bda1b5bec776a0a1e3ac4423ed4 Mon Sep 17 00:00:00 2001 From: Bob Wall Date: Tue, 28 Jan 2020 12:13:39 -0700 Subject: [PATCH 1/2] Add constant time EQ for structs For data types that contain private info (PrivateKey, Plaintext, DerivedSymmetricKey, and SigningKeypair), implement a constant time PartialEq, and remove the Revealed wrapper we were using around these types when comparison was needed. --- src/api.rs | 97 +++++++++++++++++++++++++++++------------ src/api_480.rs | 82 ++++++++++++++++++++++++++-------- src/internal/ed25519.rs | 27 ++++++++++-- src/lib.rs | 4 +- 4 files changed, 158 insertions(+), 52 deletions(-) diff --git a/src/api.rs b/src/api.rs index 1bf6ec7..fd2946b 100644 --- a/src/api.rs +++ b/src/api.rs @@ -18,7 +18,6 @@ use crate::internal::schnorr::{SchnorrSign, SchnorrSigning}; pub use crate::internal::sha256::{Sha256, Sha256Hashing}; pub use crate::internal::ByteVector; use crate::nonemptyvec::NonEmptyVec; -use crate::Revealed; use clear_on_drop::clear::Clear; use gridiron::fp_256::Fp256; use gridiron::fp_256::Monty as Monty256; @@ -80,12 +79,13 @@ impl Recrypt new_bytes_type_no_eq!(DerivedSymmetricKey, 32); -impl PartialEq for Revealed { - fn eq(&self, other: &Revealed) -> bool { - self.0.bytes == other.0.bytes +// Constant-time data-invariant implementation of eq for DerivedSymmetricKey. +impl PartialEq for DerivedSymmetricKey { + fn eq(&self, other: &DerivedSymmetricKey) -> bool { + let byte_pairs = self.bytes.iter().zip(other.bytes.iter()); + return byte_pairs.fold(0, |acc, (next_a, next_b)| acc | (next_a ^ next_b)) == 0; } } @@ -130,17 +130,11 @@ impl Plaintext { bytes_only_debug!(Plaintext); -impl PartialEq for Revealed { - fn eq(&self, other: &Revealed<Plaintext>) -> bool { - self.0.bytes[..] == other.0.bytes[..] - } -} - -/// If you are looking for PartialEq for Plaintext, see PartialEq for Revealed<Plaintext> -#[cfg(test)] +// Constant-time data-invariant implementation of eq for Plaintext. impl PartialEq for Plaintext { fn eq(&self, other: &Plaintext) -> bool { - self.bytes[..] == other.bytes[..] && self._internal_fp12 == other._internal_fp12 + let byte_pairs = self.bytes.iter().zip(other.bytes.iter()); + return byte_pairs.fold(0, |acc, (next_a, next_b)| acc | (next_a ^ next_b)) == 0; } } @@ -1036,8 +1030,6 @@ impl PartialEq for PublicKey { } #[derive(Default, Debug, Clone)] -#[cfg_attr(test, derive(PartialEq))] -// If you are looking for PartialEq for PrivateKey, see PartialEq for Revealed<PrivateKey> pub struct PrivateKey { bytes: [u8; PrivateKey::ENCODED_SIZE_BYTES], _internal_key: internal::PrivateKey<Monty256>, @@ -1085,9 +1077,11 @@ impl PrivateKey { } } -impl PartialEq for Revealed<PrivateKey> { - fn eq(&self, other: &Revealed<PrivateKey>) -> bool { - self.0.bytes[..] == other.0.bytes +// Constant-time data-invariant implementation of eq for PrivateKey. +impl PartialEq for PrivateKey { + fn eq(&self, other: &PrivateKey) -> bool { + let byte_pairs = self.bytes.iter().zip(other.bytes.iter()); + return byte_pairs.fold(0, |acc, (next_a, next_b)| acc | (next_a ^ next_b)) == 0; } } @@ -1405,7 +1399,7 @@ pub(crate) mod test { dest.copy_from_slice(src); let expected_result = DerivedSymmetricKey::new(dest); let result = Recrypt::new().derive_symmetric_key(&pt); - assert_eq!(Revealed(expected_result), Revealed(result)) + assert_eq!(expected_result, result) } #[test] @@ -1421,7 +1415,7 @@ pub(crate) mod test { dest.copy_from_slice(src); let expected_result = DerivedSymmetricKey::new(dest); let result = api.derive_symmetric_key(&pt); - assert_eq!(Revealed(expected_result), Revealed(result)); + assert_eq!(expected_result, result); //This hashes, but also mods the value so it's not the same. let private_key_result = api.derive_private_key(&pt); assert_ne!(private_key_result.bytes(), result.bytes()); @@ -1538,24 +1532,24 @@ pub(crate) mod test { fn generate_ed25519_key_pair() { use rand::SeedableRng; let api = Recrypt::new_with_rand(rand_chacha::ChaChaRng::from_seed([0u8; 32])); - let signing_keypair = Revealed(api.generate_ed25519_key_pair()); - let expected_signing_keypair = Revealed(SigningKeypair::new_unchecked([ + let signing_keypair = api.generate_ed25519_key_pair(); + let expected_signing_keypair = SigningKeypair::new_unchecked([ 118, 184, 224, 173, 160, 241, 61, 144, 64, 93, 106, 229, 83, 134, 189, 40, 189, 210, 25, 184, 160, 141, 237, 26, 168, 54, 239, 204, 139, 119, 13, 199, 32, 253, 186, 201, 177, 11, 117, 135, 187, 167, 181, 188, 22, 59, 206, 105, 231, 150, 215, 30, 78, 212, 76, 16, 252, 180, 72, 134, 137, 247, 161, 68, - ])); + ]); let expected_pub = PublicSigningKey::new([ 32, 253, 186, 201, 177, 11, 117, 135, 187, 167, 181, 188, 22, 59, 206, 105, 231, 150, 215, 30, 78, 212, 76, 16, 252, 180, 72, 134, 137, 247, 161, 68, ]); assert_eq!(signing_keypair, expected_signing_keypair); - assert_eq!(signing_keypair.0.public_key(), expected_pub); + assert_eq!(signing_keypair.public_key(), expected_pub); //Assert that the generation doesn't just return the same value. - let keypair_two = Revealed(api.generate_ed25519_key_pair()); + let keypair_two = api.generate_ed25519_key_pair(); assert_ne!(keypair_two, expected_signing_keypair); - assert_ne!(keypair_two.0.public_key(), expected_pub); + assert_ne!(keypair_two.public_key(), expected_pub); } #[test] //written against AuthHash, but valid for all types generated from that macro @@ -1629,6 +1623,55 @@ pub(crate) mod test { assert_eq!(priv_key._internal_key, Default::default()) } + // check the equality functions for private keys, plaintexts, and derived symmetric keys + #[test] + fn private_keys_equal() -> Result<()> { + let priv_key1 = PrivateKey::new_from_slice(&hex::decode( + "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef", + )?)?; + let priv_key2 = PrivateKey::new_from_slice(&hex::decode( + "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef", + )?)?; + assert_eq!(priv_key1, priv_key2); + Ok(()) + } + + #[test] + fn private_keys_not_equal() -> Result<()> { + let priv_key1 = PrivateKey::new_from_slice(&hex::decode( + "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef", + )?)?; + let priv_key2 = PrivateKey::new_from_slice(&hex::decode( + "ef0123456789abcd0123456789abcdef0123456789abcdef0123456789abcdef", + )?)?; + assert_ne!(priv_key1, priv_key2); + Ok(()) + } + + // Also derives symmetric keys from the plaintexts and compares those. + #[test] + fn plaintexts_equal() -> Result<()> { + let pt1 = Plaintext::new_from_slice(&hex::decode("3e0348980131e4db298445c3ef424ad60ebfa816069689be559f5ffeecf5e635201172f1bc931833b431a8d7a118e90d516de84e6e4de2f3105695b7699104ee18dd4598f93417ed736b40515a4817499a748be1bf126c132a8a4e8da83780a9054d6e1de22e21e446dbaa3a121d103fdf813a31afac09881beb0a3ae974ffdd537049eea02dade975525c720d152c87b4f0e76645c4cf46ee0e731378ad5c5d12630a32d0610c52c3c56fc0d7666ad6464adeca698a2ee4c44666c05d2e58154b961a595a445b156ce0bdd3e13ffa5b296e8c364aecec6208a0aa54cdea40455032a11458b08d143a51013dcdb8febd01bd93966bff2fc8bbd121efc19fedcb576d82e70838f8f987c5cb887a857d4a6d68c8bbf9196d72b98bea0a62d3fda109a46c28c6d87851223f38712226ba8a5c36197ee016baa27051c398a95c184820e6493c972f7e53936a2abd9c22483d3595fee87ad2a2771af0cc847548bc233f258d4bf77df8265b566ef54c288ad3a8034d18b3af4cb1d71b2da649200fa1")?)?; + let pt2 = Plaintext::new_from_slice(&hex::decode("3e0348980131e4db298445c3ef424ad60ebfa816069689be559f5ffeecf5e635201172f1bc931833b431a8d7a118e90d516de84e6e4de2f3105695b7699104ee18dd4598f93417ed736b40515a4817499a748be1bf126c132a8a4e8da83780a9054d6e1de22e21e446dbaa3a121d103fdf813a31afac09881beb0a3ae974ffdd537049eea02dade975525c720d152c87b4f0e76645c4cf46ee0e731378ad5c5d12630a32d0610c52c3c56fc0d7666ad6464adeca698a2ee4c44666c05d2e58154b961a595a445b156ce0bdd3e13ffa5b296e8c364aecec6208a0aa54cdea40455032a11458b08d143a51013dcdb8febd01bd93966bff2fc8bbd121efc19fedcb576d82e70838f8f987c5cb887a857d4a6d68c8bbf9196d72b98bea0a62d3fda109a46c28c6d87851223f38712226ba8a5c36197ee016baa27051c398a95c184820e6493c972f7e53936a2abd9c22483d3595fee87ad2a2771af0cc847548bc233f258d4bf77df8265b566ef54c288ad3a8034d18b3af4cb1d71b2da649200fa1")?)?; + assert_eq!(pt1, pt2); + let dk1 = Recrypt::new().derive_symmetric_key(&pt1); + let dk2 = Recrypt::new().derive_symmetric_key(&pt2); + assert_eq!(dk1, dk2); + Ok(()) + } + + // Also derives symmetric keys from the plaintexts and compares those. + #[test] + fn plaintexts_not_equal() -> Result<()> { + let pt1 = Plaintext::new_from_slice(&hex::decode("3e0348980131e4db298445c3ef424ad60ebfa816069689be559f5ffeecf5e635201172f1bc931833b431a8d7a118e90d516de84e6e4de2f3105695b7699104ee18dd4598f93417ed736b40515a4817499a748be1bf126c132a8a4e8da83780a9054d6e1de22e21e446dbaa3a121d103fdf813a31afac09881beb0a3ae974ffdd537049eea02dade975525c720d152c87b4f0e76645c4cf46ee0e731378ad5c5d12630a32d0610c52c3c56fc0d7666ad6464adeca698a2ee4c44666c05d2e58154b961a595a445b156ce0bdd3e13ffa5b296e8c364aecec6208a0aa54cdea40455032a11458b08d143a51013dcdb8febd01bd93966bff2fc8bbd121efc19fedcb576d82e70838f8f987c5cb887a857d4a6d68c8bbf9196d72b98bea0a62d3fda109a46c28c6d87851223f38712226ba8a5c36197ee016baa27051c398a95c184820e6493c972f7e53936a2abd9c22483d3595fee87ad2a2771af0cc847548bc233f258d4bf77df8265b566ef54c288ad3a8034d18b3af4cb1d71b2da649200fa1")?)?; + let pt2 = Plaintext::new_from_slice(&hex::decode("3e0348980131e4db298445c3ef424ad60ebfa816069689be559f5ffeecf5e635201172f1bc931833b431a8d7a118e90d516de84e6e4de2f3105695b7699104ee18dd4598f93417ed736b40515a4817499a748be1bf126c132a8a4e8da83780a9054d6e1de22e21e446dbaa3a121d103fdf813a31afac09881beb0a3ae974ffdd537049efa02dade975525c720d152c87b4f0e76645c4cf46ee0e731378ad5c5d12630a32d0610c52c3c56fc0d7666ad6464adeca698a2ee4c44666c05d2e58154b961a595a445b156ce0bdd3e13ffa5b296e8c364aecec6208a0aa54cdea40455032a11458b08d143a51013dcdb8febd01bd93966bff2fc8bbd121efc19fedcb576d82e70838f8f987c5cb887a857d4a6d68c8bbf9196d72b98bea0a62d3fda109a46c28c6d87851223f38712226ba8a5c36197ee016baa27051c398a95c184820e6493c972f7e53936a2abd9c22483d3595fee87ad2a2771af0cc847548bc233f258d4bf77df8265b566ef54c288ad3a8034d18b3af4cb1d71b2da649200fa1")?)?; + assert_ne!(pt1, pt2); + let dk1 = Recrypt::new().derive_symmetric_key(&pt1); + let dk2 = Recrypt::new().derive_symmetric_key(&pt2); + assert_ne!(dk1, dk2); + Ok(()) + } + #[test] fn gen_random_fp12_not_same() { let recrypt = Recrypt::new(); diff --git a/src/api_480.rs b/src/api_480.rs index 2b71ee5..fc4e876 100644 --- a/src/api_480.rs +++ b/src/api_480.rs @@ -18,7 +18,6 @@ use crate::internal::schnorr::{SchnorrSign, SchnorrSigning}; pub use crate::internal::sha256::{Sha256, Sha256Hashing}; pub use crate::internal::ByteVector; use crate::nonemptyvec::NonEmptyVec; -use crate::Revealed; use clear_on_drop::clear::Clear; use gridiron::fp_480::Fp480; use gridiron::fp_480::Monty as Monty480; @@ -80,12 +79,13 @@ impl<CR: rand::CryptoRng + rand::RngCore> Recrypt480<Sha256, Ed25519, RandomByte } // Hashed but not encrypted Plaintext used for envelope encryption -// If you are looking for PartialEq for DerivedSymmetricKey, see PartialEq for Revealed<DerivedSymmetricKey> new_bytes_type_no_eq!(DerivedSymmetricKey, 32); -impl PartialEq for Revealed<DerivedSymmetricKey> { - fn eq(&self, other: &Revealed<DerivedSymmetricKey>) -> bool { - self.0.bytes == other.0.bytes +// Constant-time data-invariant implementation of eq for DerivedSymmetricKey. +impl PartialEq for DerivedSymmetricKey { + fn eq(&self, other: &DerivedSymmetricKey) -> bool { + let byte_pairs = self.bytes.iter().zip(other.bytes.iter()); + return byte_pairs.fold(0, |acc, (next_a, next_b)| acc | (next_a ^ next_b)) == 0; } } @@ -131,17 +131,11 @@ impl Plaintext { bytes_only_debug!(Plaintext); -impl PartialEq for Revealed<Plaintext> { - fn eq(&self, other: &Revealed<Plaintext>) -> bool { - self.0.bytes[..] == other.0.bytes[..] - } -} - -/// If you are looking for PartialEq for Plaintext, see PartialEq for Revealed<Plaintext> -#[cfg(test)] +// Constant-time data-invariant implementation of eq for Plaintext. impl PartialEq for Plaintext { fn eq(&self, other: &Plaintext) -> bool { - self.bytes[..] == other.bytes[..] && self._internal_fp12 == other._internal_fp12 + let byte_pairs = self.bytes.iter().zip(other.bytes.iter()); + return byte_pairs.fold(0, |acc, (next_a, next_b)| acc | (next_a ^ next_b)) == 0; } } @@ -1071,8 +1065,6 @@ impl PartialEq for PublicKey { } #[derive(Default, Debug, Clone)] -#[cfg_attr(test, derive(PartialEq))] -// If you are looking for PartialEq for PrivateKey, see PartialEq for Revealed<PrivateKey> pub struct PrivateKey { bytes: SixtyBytes, _internal_key: internal::PrivateKey<Monty480>, @@ -1120,9 +1112,11 @@ impl PrivateKey { } } -impl PartialEq for Revealed<PrivateKey> { - fn eq(&self, other: &Revealed<PrivateKey>) -> bool { - self.0.bytes == other.0.bytes +// Constant-time data-invariant implementation of eq for PrivateKey. +impl PartialEq for PrivateKey { + fn eq(&self, other: &PrivateKey) -> bool { + let byte_pairs = self.bytes.0.iter().zip(other.bytes.0.iter()); + return byte_pairs.fold(0, |acc, (next_a, next_b)| acc | (next_a ^ next_b)) == 0; } } @@ -1575,4 +1569,54 @@ pub(crate) mod test { PrivateKey::new((Fr480::from(1u8) - Fr480::from(2u8)).to_bytes_60()) ); } + + // check the equality functions for private keys, plaintexts, and derived symmetric keys + #[test] + fn private_keys_equal() -> Result<()> { + let priv_key1 = PrivateKey::new_from_slice(&hex::decode( + "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef01234567", + )?)?; + let priv_key2 = PrivateKey::new_from_slice(&hex::decode( + "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef01234567", + )?)?; + assert_eq!(priv_key1, priv_key2); + Ok(()) + } + + #[test] + fn private_keys_not_equal() -> Result<()> { + let priv_key1 = PrivateKey::new_from_slice(&hex::decode( + "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef01234567", + )?)?; + let priv_key2 = PrivateKey::new_from_slice(&hex::decode( + "ef0123456789abcd0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef01234567", + )?)?; + assert_ne!(priv_key1, priv_key2); + Ok(()) + } + + // Also derives symmetric keys from the plaintexts and compares those. + #[test] + fn plaintexts_equal() -> Result<()> { + let pt1 = Plaintext::new_from_slice(&hex::decode("a5e480feba36757014d53c9efd823cd69ae0f8a175c1dd8740fa402faaebe6a114fabec5aa4815d20b41ec651df6cb5ab880849b50509234cbae117a6e59eb77959515d01f9f943c70450183c2d72d3b9ec31e65db41fc90978b31f80537f8b77a5a119b83e8f5420837541402ba85d824325b3641f6d3de35c40ea246804db5a68b0d4897b5169488d76d0a32d2006cdf754e15bbfd4813501a2e88fa3beb1a4dcebf4ccf88d8ba9ea0a9f53355fdc8d44b77caca7c73ad939f9e57d3edabb3c3f60328fc2c01b41bcb4cb533a95477107f447e36a12456abf8c550d1ae359d0f7087a16d4528832a3120f28793d2383f2a2574853f19210895d39bf3840ef106729b7fbdff3ba208666c21b657c2fcdae9e9d801dc205922ee25edbdc6b789907b7fb49d41460184e9b570d3ae6c25ee801c2178ed6874c67901d7fbb73838d7433fa9ae7638ce007ed83fb517e5b0bb8b939b22097ff3db80dd647dbfbda0126e1a35270606e1b28936dfcc8decf4992448d567977bda52fbb4d4c147bb73c6aab1195d8f79e4e458141869569e9cee435140cff55224a94b204a4a103ddad3a8082a1665ad7c52cef851a9376efbf07f6310481a04ea49d0a58072f5089db9fdc54022c52b9321b2b10bd94ad1a739ba8fa4f54d3dade5a62c611bcb4362d7dfb8b0913df8d66021a5f2870aa75e334a72a8c77f8dfb0d5e88a5828017289ba62636dbaa95eebda33b79714e594f19dff834510366a89cab27f7d5af4cc496ae109d5eb907cb9386bd126706d963ed9e6d56439e8bbd735f506243aa2e38474cc53c153b37392e5e56b4c271d7182dd4d31395b3c76f0e6ec6fb79cf02498742c265daa98cccab211d0d1eef2e9e7009defd5c5b0fc2e9d6f3c1518fc964cdd98aa019b4b2a1db22f734788f6dcbe6e8b3fdeb9dfea69410c14c039ee6ec4489171f744e2af555773da6886a2a7a3ad5b032b85e91d56d2baf4176004a37a60e6a00b8dc08b99031d316766af541")?)?; + let pt2 = Plaintext::new_from_slice(&hex::decode("a5e480feba36757014d53c9efd823cd69ae0f8a175c1dd8740fa402faaebe6a114fabec5aa4815d20b41ec651df6cb5ab880849b50509234cbae117a6e59eb77959515d01f9f943c70450183c2d72d3b9ec31e65db41fc90978b31f80537f8b77a5a119b83e8f5420837541402ba85d824325b3641f6d3de35c40ea246804db5a68b0d4897b5169488d76d0a32d2006cdf754e15bbfd4813501a2e88fa3beb1a4dcebf4ccf88d8ba9ea0a9f53355fdc8d44b77caca7c73ad939f9e57d3edabb3c3f60328fc2c01b41bcb4cb533a95477107f447e36a12456abf8c550d1ae359d0f7087a16d4528832a3120f28793d2383f2a2574853f19210895d39bf3840ef106729b7fbdff3ba208666c21b657c2fcdae9e9d801dc205922ee25edbdc6b789907b7fb49d41460184e9b570d3ae6c25ee801c2178ed6874c67901d7fbb73838d7433fa9ae7638ce007ed83fb517e5b0bb8b939b22097ff3db80dd647dbfbda0126e1a35270606e1b28936dfcc8decf4992448d567977bda52fbb4d4c147bb73c6aab1195d8f79e4e458141869569e9cee435140cff55224a94b204a4a103ddad3a8082a1665ad7c52cef851a9376efbf07f6310481a04ea49d0a58072f5089db9fdc54022c52b9321b2b10bd94ad1a739ba8fa4f54d3dade5a62c611bcb4362d7dfb8b0913df8d66021a5f2870aa75e334a72a8c77f8dfb0d5e88a5828017289ba62636dbaa95eebda33b79714e594f19dff834510366a89cab27f7d5af4cc496ae109d5eb907cb9386bd126706d963ed9e6d56439e8bbd735f506243aa2e38474cc53c153b37392e5e56b4c271d7182dd4d31395b3c76f0e6ec6fb79cf02498742c265daa98cccab211d0d1eef2e9e7009defd5c5b0fc2e9d6f3c1518fc964cdd98aa019b4b2a1db22f734788f6dcbe6e8b3fdeb9dfea69410c14c039ee6ec4489171f744e2af555773da6886a2a7a3ad5b032b85e91d56d2baf4176004a37a60e6a00b8dc08b99031d316766af541")?)?; + assert_eq!(pt1, pt2); + let dk1 = Recrypt480::new().derive_symmetric_key(&pt1); + let dk2 = Recrypt480::new().derive_symmetric_key(&pt2); + assert_eq!(dk1, dk2); + Ok(()) + } + + // Also derives symmetric keys from the plaintexts and compares those. + #[test] + fn plaintexts_not_equal() -> Result<()> { + let pt1 = Plaintext::new_from_slice(&hex::decode("a5e480feba36757014d53c9efd823cd69ae0f8a175c1dd8740fa402faaebe6a114fabec5aa4815d20b41ec651df6cb5ab880849b50509234cbae117a6e59eb77959515d01f9f943c70450183c2d72d3b9ec31e65db41fc90978b31f80537f8b77a5a119b83e8f5420837541402ba85d824325b3641f6d3de35c40ea246804db5a68b0d4897b5169488d76d0a32d2006cdf754e15bbfd4813501a2e88fa3beb1a4dcebf4ccf88d8ba9ea0a9f53355fdc8d44b77caca7c73ad939f9e57d3edabb3c3f60328fc2c01b41bcb4cb533a95477107f447e36a12456abf8c550d1ae359d0f7087a16d4528832a3120f28793d2383f2a2574853f19210895d39bf3840ef106729b7fbdff3ba208666c21b657c2fcdae9e9d801dc205922ee25edbdc6b789907b7fb49d41460184e9b570d3ae6c25ee801c2178ed6874c67901d7fbb73838d7433fa9ae7638ce007ed83fb517e5b0bb8b939b22097ff3db80dd647dbfbda0126e1a35270606e1b28936dfcc8decf4992448d567977bda52fbb4d4c147bb73c6aab1195d8f79e4e458141869569e9cee435140cff55224a94b204a4a103ddad3a8082a1665ad7c52cef851a9376efbf07f6310481a04ea49d0a58072f5089db9fdc54022c52b9321b2b10bd94ad1a739ba8fa4f54d3dade5a62c611bcb4362d7dfb8b0913df8d66021a5f2870aa75e334a72a8c77f8dfb0d5e88a5828017289ba62636dbaa95eebda33b79714e594f19dff834510366a89cab27f7d5af4cc496ae109d5eb907cb9386bd126706d963ed9e6d56439e8bbd735f506243aa2e38474cc53c153b37392e5e56b4c271d7182dd4d31395b3c76f0e6ec6fb79cf02498742c265daa98cccab211d0d1eef2e9e7009defd5c5b0fc2e9d6f3c1518fc964cdd98aa019b4b2a1db22f734788f6dcbe6e8b3fdeb9dfea69410c14c039ee6ec4489171f744e2af555773da6886a2a7a3ad5b032b85e91d56d2baf4176004a37a60e6a00b8dc08b99031d316766af541")?)?; + let pt2 = Plaintext::new_from_slice(&hex::decode("a5e480feba36757014d53c9efd823cd69ae0f8a175c1dd8740fa402faaebe6a114fabec5aa4815d20b41ec651df6cb5ab880849b50509234cbae117a6e59eb77959515d01f9f943c70450183c2d72d3b9ec31e65db41fc90978b31f80537f8b77a5a119b83e8f5420837541402ba85d824325b3641f6d3de35c40ea246804db5a68b0d4897b5169488d76d0a32d2006cdf754e15bbfd4813501a2e88fa3beb1a4dcebf4ccf88d8ba9ea0a9f53355fdc8d44b77caca7c73ad939f9e57d3edabb3c3f60328fc2c01b41bcb4cb533a95477107f447e36a12456abf8c550d1ae359d0f7087a16d4528832a3120f28793d2383f2a2574853f19210895d39bf3840ef106729b7fbdff3ba208666c21b657c2fcdae9e9d801dc205922ee25edbdc6b789907b7fb49d41460184e9b570d3ae6c25ee801c2178ed6874c67901d7fbb73838d7433fa9ae7638ce007ed83fb517e5b0bb8b939b22097ff3db80dd647dbfbda0126e1a35270606e1b28936dfcc8decf4992448d567977bda52fbb4d4c147bb73c6aab1195d8f79e4e458141869569e9cee435140cff55224a94b204a4a103ddad3a8082a1665ad7c52cef851a9376efbf07f6310481a04ea49d0a58072f5089db9fdc54022c52b9321b2b10bd94ad1a739ba8fa4f54d3dade5a62c611bcb4362d7dfb8b0913df8d66021a5f2870aa75e334a72a8c77f8dfb0d5e88a5828017289ba62636dbaa95eebda33b79714e594f19dff834510366a89cab27f7d5af4cc496ae109d5eb907cb9386bd126706d963ed9e6d56439e8bbd735f506243aa2e38474cc53c153b37392e5e56b4c271d7182dd4d31395b3c76f0e6ec6fb79cf02498742c265daa98cccab211d0d1eef2e9e7009defd5c5b0fc2e9d6f3c1518fc964cdd98aa019b4b2a1db22f734788f6dcbe6e8b3fdeb9dfea69410c14c039ee6ec4489171f744e2af555773da6886a2a7a3ad5b032b85e91d56d2baf4176004a37a60e6a00b8dc08b99031d316766af641")?)?; + assert_ne!(pt1, pt2); + let dk1 = Recrypt480::new().derive_symmetric_key(&pt1); + let dk2 = Recrypt480::new().derive_symmetric_key(&pt2); + assert_ne!(dk1, dk2); + Ok(()) + } + } diff --git a/src/internal/ed25519.rs b/src/internal/ed25519.rs index 1d81924..7172774 100644 --- a/src/internal/ed25519.rs +++ b/src/internal/ed25519.rs @@ -2,7 +2,6 @@ use crate::api_common::RecryptErr; use crate::internal::hashable::Hashable; use crate::internal::ByteVector; use crate::internal::{array_split_64, take_lock}; -use crate::Revealed; use clear_on_drop::clear::Clear; use ed25519_dalek; use ed25519_dalek::PublicKey; @@ -127,9 +126,11 @@ impl SigningKeypair { } } -impl PartialEq for Revealed<SigningKeypair> { - fn eq(&self, other: &Revealed<SigningKeypair>) -> bool { - self.0.bytes[..] == other.0.bytes[..] +// Constant-time data-invariant implementation of eq for SigningKeyPair, which includes a private key. +impl PartialEq for SigningKeypair { + fn eq(&self, other: &SigningKeypair) -> bool { + let byte_pairs = self.bytes.iter().zip(other.bytes.iter()); + return byte_pairs.fold(0, |acc, (next_a, next_b)| acc | (next_a ^ next_b)) == 0; } } @@ -247,4 +248,22 @@ pub(crate) mod test { let bytes: [u8; 64] = key_pair.into(); assert_eq!(key_pair_bytes[..], bytes[..]) } + + #[test] + fn signing_keypairs_equal() { + let sk1 = good_signing_keypair(); + let sk2 = good_signing_keypair(); + assert_eq!(sk1, sk2) + } + + #[test] + fn signing_keypairs_not_equal() { + let sk1 = good_signing_keypair(); + let sk2 = SigningKeypair::new_unchecked([ + 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 138, 136, 227, 221, 116, 9, 241, 149, 253, 82, 219, 45, 60, 186, 93, 114, 202, + 103, 9, 191, 29, 148, 18, 27, 243, 116, 136, 1, 180, 15, 111, 92, + ]); + assert_ne!(sk1, sk2) + } } diff --git a/src/lib.rs b/src/lib.rs index 9b21938..c8220f5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -28,7 +28,7 @@ //! let decrypted_val = recrypt.decrypt(encrypted_val, &priv_key).unwrap(); //! //! // plaintext recovered. -//! assert_eq!(Revealed(pt), Revealed(decrypted_val)) +//! assert_eq!(pt, decrypted_val) //! ``` //! ## Single-hop Transform Encryption Example @@ -74,7 +74,7 @@ //! let decrypted_val = recrypt.decrypt(transformed_val, &target_priv_key).unwrap(); //! //! // plaintext recovered. -//! assert_eq!(Revealed(pt), Revealed(decrypted_val)); +//! assert_eq!(pt, decrypted_val); //! ``` //! //! ## Constant Time and Equality From 792039480a5983809aed8dd8867c8005298bb5d2 Mon Sep 17 00:00:00 2001 From: Bob Wall <bob.wall@ironcorelabs.com> Date: Tue, 28 Jan 2020 16:17:44 -0700 Subject: [PATCH 2/2] Address code review. --- src/api.rs | 6 +++--- src/api_480.rs | 6 +++--- src/internal/ed25519.rs | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/api.rs b/src/api.rs index fd2946b..15f224e 100644 --- a/src/api.rs +++ b/src/api.rs @@ -85,7 +85,7 @@ new_bytes_type_no_eq!(DerivedSymmetricKey, 32); impl PartialEq for DerivedSymmetricKey { fn eq(&self, other: &DerivedSymmetricKey) -> bool { let byte_pairs = self.bytes.iter().zip(other.bytes.iter()); - return byte_pairs.fold(0, |acc, (next_a, next_b)| acc | (next_a ^ next_b)) == 0; + byte_pairs.fold(0, |acc, (next_a, next_b)| acc | (next_a ^ next_b)) == 0 } } @@ -134,7 +134,7 @@ bytes_only_debug!(Plaintext); impl PartialEq for Plaintext { fn eq(&self, other: &Plaintext) -> bool { let byte_pairs = self.bytes.iter().zip(other.bytes.iter()); - return byte_pairs.fold(0, |acc, (next_a, next_b)| acc | (next_a ^ next_b)) == 0; + byte_pairs.fold(0, |acc, (next_a, next_b)| acc | (next_a ^ next_b)) == 0 } } @@ -1081,7 +1081,7 @@ impl PrivateKey { impl PartialEq for PrivateKey { fn eq(&self, other: &PrivateKey) -> bool { let byte_pairs = self.bytes.iter().zip(other.bytes.iter()); - return byte_pairs.fold(0, |acc, (next_a, next_b)| acc | (next_a ^ next_b)) == 0; + byte_pairs.fold(0, |acc, (next_a, next_b)| acc | (next_a ^ next_b)) == 0 } } diff --git a/src/api_480.rs b/src/api_480.rs index fc4e876..60c2ab8 100644 --- a/src/api_480.rs +++ b/src/api_480.rs @@ -85,7 +85,7 @@ new_bytes_type_no_eq!(DerivedSymmetricKey, 32); impl PartialEq for DerivedSymmetricKey { fn eq(&self, other: &DerivedSymmetricKey) -> bool { let byte_pairs = self.bytes.iter().zip(other.bytes.iter()); - return byte_pairs.fold(0, |acc, (next_a, next_b)| acc | (next_a ^ next_b)) == 0; + byte_pairs.fold(0, |acc, (next_a, next_b)| acc | (next_a ^ next_b)) == 0 } } @@ -135,7 +135,7 @@ bytes_only_debug!(Plaintext); impl PartialEq for Plaintext { fn eq(&self, other: &Plaintext) -> bool { let byte_pairs = self.bytes.iter().zip(other.bytes.iter()); - return byte_pairs.fold(0, |acc, (next_a, next_b)| acc | (next_a ^ next_b)) == 0; + byte_pairs.fold(0, |acc, (next_a, next_b)| acc | (next_a ^ next_b)) == 0 } } @@ -1116,7 +1116,7 @@ impl PrivateKey { impl PartialEq for PrivateKey { fn eq(&self, other: &PrivateKey) -> bool { let byte_pairs = self.bytes.0.iter().zip(other.bytes.0.iter()); - return byte_pairs.fold(0, |acc, (next_a, next_b)| acc | (next_a ^ next_b)) == 0; + byte_pairs.fold(0, |acc, (next_a, next_b)| acc | (next_a ^ next_b)) == 0 } } diff --git a/src/internal/ed25519.rs b/src/internal/ed25519.rs index 7172774..6711b6b 100644 --- a/src/internal/ed25519.rs +++ b/src/internal/ed25519.rs @@ -130,7 +130,7 @@ impl SigningKeypair { impl PartialEq for SigningKeypair { fn eq(&self, other: &SigningKeypair) -> bool { let byte_pairs = self.bytes.iter().zip(other.bytes.iter()); - return byte_pairs.fold(0, |acc, (next_a, next_b)| acc | (next_a ^ next_b)) == 0; + byte_pairs.fold(0, |acc, (next_a, next_b)| acc | (next_a ^ next_b)) == 0 } }