diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a9595a5a..128fc813 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -51,8 +51,8 @@ jobs: with: toolchain: ${{ matrix.rust }} - uses: RustCrypto/actions/cargo-hack-install@master - - run: cargo hack test --release --feature-powerset --exclude-features os_rng,serde - - run: cargo test --release --features os_rng + - run: cargo hack test --release --feature-powerset --exclude-features getrandom,serde + - run: cargo test --release --features getrandom - run: cargo test --release --features serde minimal-versions: @@ -66,7 +66,7 @@ jobs: - uses: dtolnay/rust-toolchain@nightly - run: cargo update -Z minimal-versions - uses: dtolnay/rust-toolchain@stable - - run: cargo test --release --features os_rng,serde,pkcs5 + - run: cargo test --release --features getrandom,serde,pkcs5 nightly: runs-on: ubuntu-latest diff --git a/Cargo.lock b/Cargo.lock index 3968744f..fb8381a1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -120,13 +120,13 @@ checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9" [[package]] name = "chacha20" -version = "0.10.0-rc.3" +version = "0.10.0-rc.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3585020fc6766ef7ff5c58d69819dbca16a19008ae347bb5d3e4e145c495eb38" +checksum = "f895fb33c1ad22da4bc79d37c0bddff8aee2ba4575705345eb73b8ffbc386074" dependencies = [ "cfg-if", "cpufeatures", - "rand_core 0.10.0-rc-2", + "rand_core 0.10.0-rc-3", ] [[package]] @@ -140,6 +140,12 @@ dependencies = [ "inout", ] +[[package]] +name = "cmov" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c11ed919bd3bae4af5ab56372b627dfc32622aba6cec36906e8ab46746037c9d" + [[package]] name = "const-oid" version = "0.10.1" @@ -157,35 +163,37 @@ dependencies = [ [[package]] name = "crypto-bigint" -version = "0.7.0-rc.10" +version = "0.7.0-rc.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6715836b4946e8585016e80b79c7561476aff3b22f7b756778e7b109d86086c6" +checksum = "a7ddf7876857d903765f30de7c789044ea2e49a4f611fe96416827175cef6b34" dependencies = [ + "ctutils", + "getrandom 0.4.0-rc.0", "num-traits", - "rand_core 0.10.0-rc-2", + "rand_core 0.10.0-rc-3", "serdect", - "subtle", "zeroize", ] [[package]] name = "crypto-common" -version = "0.2.0-rc.5" +version = "0.2.0-rc.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "919bd05924682a5480aec713596b9e2aabed3a0a6022fab6847f85a99e5f190a" +checksum = "e6165b8029cdc3e765b74d3548f85999ee799d5124877ce45c2c85ca78e4d4aa" dependencies = [ + "getrandom 0.4.0-rc.0", "hybrid-array", + "rand_core 0.10.0-rc-3", ] [[package]] name = "crypto-primes" -version = "0.7.0-pre.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdd9b2855017318a49714c07ee8895b89d3510d54fa6d86be5835de74c389609" +version = "0.7.0-dev" +source = "git+https://github.com/tarcieri/crypto-primes?branch=crypto-bigint%2Fv0.7.0-rc.12#a96b3c9e40db97dfd3df73d9368ff096292fcb16" dependencies = [ "crypto-bigint", "libm", - "rand_core 0.10.0-rc-2", + "rand_core 0.10.0-rc-3", ] [[package]] @@ -197,6 +205,15 @@ dependencies = [ "cipher", ] +[[package]] +name = "ctutils" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "925ebe186f09a7894817213f89eae07c39374f5c934613605af7accc8aea6414" +dependencies = [ + "cmov", +] + [[package]] name = "der" version = "0.8.0-rc.9" @@ -303,6 +320,19 @@ dependencies = [ "wasi", ] +[[package]] +name = "getrandom" +version = "0.4.0-rc.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b99f0d993a2b9b97b9a201193aa8ad21305cde06a3be9a7e1f8f4201e5cc27e" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "rand_core 0.10.0-rc-3", + "wasip2", +] + [[package]] name = "ghash" version = "0.6.0-rc.3" @@ -483,7 +513,7 @@ dependencies = [ "cbc", "der", "pbkdf2", - "rand_core 0.10.0-rc-2", + "rand_core 0.10.0-rc-3", "scrypt", "sha2", "spki", @@ -497,7 +527,7 @@ checksum = "77089aec8290d0b7bb01b671b091095cf1937670725af4fd73d47249f03b12c0" dependencies = [ "der", "pkcs5", - "rand_core 0.10.0-rc-2", + "rand_core 0.10.0-rc-3", "spki", ] @@ -591,13 +621,12 @@ dependencies = [ [[package]] name = "rand" -version = "0.10.0-rc.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e7d245ced4538f0406b1579d3d4a6515a2ff1bdf20733492e2e4fc90a648769" +version = "0.10.0-rc.5" +source = "git+https://github.com/rust-random/rand#ff07ec205347dd749a586aaee7218cb21590ea4c" dependencies = [ "chacha20", - "getrandom", - "rand_core 0.10.0-rc-2", + "getrandom 0.4.0-rc.0", + "rand_core 0.10.0-rc-3", ] [[package]] @@ -616,14 +645,14 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" dependencies = [ - "getrandom", + "getrandom 0.3.3", ] [[package]] name = "rand_core" -version = "0.10.0-rc-2" +version = "0.10.0-rc-3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "104a23e4e8b77312a823b6b5613edbac78397e2f34320bc7ac4277013ec4478e" +checksum = "f66ee92bc15280519ef199a274fe0cafff4245d31bc39aaa31c011ad56cb1f05" [[package]] name = "rand_xorshift" @@ -676,6 +705,7 @@ dependencies = [ "base64ct", "const-oid", "crypto-bigint", + "crypto-common", "crypto-primes", "digest", "hex", @@ -683,8 +713,8 @@ dependencies = [ "pkcs1", "pkcs8", "proptest", - "rand 0.10.0-rc.1", - "rand_core 0.10.0-rc-2", + "rand 0.10.0-rc.5", + "rand_core 0.10.0-rc-3", "rstest", "serde", "serde_json", @@ -695,7 +725,6 @@ dependencies = [ "sha3", "signature", "spki", - "subtle", "zeroize", ] @@ -896,7 +925,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a0251c9d6468f4ba853b6352b190fb7c1e405087779917c238445eb03993826" dependencies = [ "digest", - "rand_core 0.10.0-rc-2", + "rand_core 0.10.0-rc-3", ] [[package]] @@ -939,7 +968,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2d31c77bdf42a745371d260a26ca7163f1e0924b64afa0b688e61b5a9fa02f16" dependencies = [ "fastrand", - "getrandom", + "getrandom 0.3.3", "once_cell", "rustix", "windows-sys", diff --git a/Cargo.toml b/Cargo.toml index 0a6e77da..e000210d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,15 +15,15 @@ exclude = ["marvin_toolkit/", "thirdparty/"] [dependencies] const-oid = { version = "0.10", default-features = false } -crypto-bigint = { version = "0.7.0-rc.10", default-features = false, features = ["zeroize", "alloc"] } -crypto-primes = { version = "0.7.0-pre.4", default-features = false } +crypto-bigint = { version = "0.7.0-rc.12", default-features = false, features = ["zeroize", "alloc"] } +crypto-primes = { version = "0.7.0-dev", default-features = false } digest = { version = "0.11.0-rc.4", default-features = false, features = ["alloc", "oid"] } rand_core = { version = "0.10.0-rc-2", default-features = false } signature = { version = "3.0.0-rc.5", default-features = false, features = ["alloc", "digest", "rand_core"] } -subtle = { version = "2.6.1", default-features = false } zeroize = { version = "1.8", features = ["alloc"] } # optional dependencies +crypto-common = { version = "0.2.0-rc.8", optional = true, features = ["getrandom"] } pkcs1 = { version = "0.8.0-rc.3", optional = true, default-features = false, features = ["alloc", "pem"] } pkcs8 = { version = "0.11.0-rc.8", optional = true, default-features = false, features = ["alloc", "pem"] } serdect = { version = "0.4", optional = true } @@ -31,15 +31,14 @@ sha1 = { version = "0.11.0-rc.3", optional = true, default-features = false, fea sha2 = { version = "0.11.0-rc.3", optional = true, default-features = false, features = ["oid"] } spki = { version = "0.8.0-rc.4", optional = true, default-features = false, features = ["alloc"] } serde = { version = "1.0.184", optional = true, default-features = false, features = ["derive"] } -rand = { version = "0.10.0-rc.1", optional = true, default-features = false } [dev-dependencies] base64ct = { version = "1", features = ["alloc"] } hex-literal = "1" proptest = "1" serde_test = "1.0.89" -rand = { version = "0.10.0-rc.1", features = ["chacha"] } -rand_core = { version = "0.10.0-rc-2", default-features = false } +rand = { version = "0.10.0-rc.5", features = ["chacha"] } +rand_core = { version = "0.10.0-rc-3", default-features = false } sha1 = { version = "0.11.0-rc.3", default-features = false, features = ["oid"] } sha2 = { version = "0.11.0-rc.3", default-features = false, features = ["oid"] } sha3 = { version = "0.11.0-rc.3", default-features = false, features = ["oid"] } @@ -55,10 +54,10 @@ name = "key" default = ["std", "encoding"] encoding = ["dep:pkcs1", "dep:pkcs8", "dep:spki"] hazmat = [] -os_rng = ["crypto-bigint/rand_core", "rand/os_rng"] +getrandom = ["crypto-bigint/getrandom", "crypto-common"] serde = ["encoding", "dep:serde", "dep:serdect", "crypto-bigint/serde"] pkcs5 = ["pkcs8/encryption"] -std = ["pkcs1?/std", "pkcs8?/std", "crypto-bigint/rand"] +std = ["pkcs1?/std", "pkcs8?/std"] [package.metadata.docs.rs] features = ["std", "serde", "hazmat", "sha2"] @@ -69,3 +68,7 @@ opt-level = 2 [profile.bench] debug = true + +[patch.crates-io] +crypto-primes = { git = "https://github.com/tarcieri/crypto-primes", branch = "crypto-bigint/v0.7.0-rc.12" } +rand = { git = "https://github.com/rust-random/rand" } diff --git a/src/algorithms/oaep.rs b/src/algorithms/oaep.rs index 178dde80..9f8edcde 100644 --- a/src/algorithms/oaep.rs +++ b/src/algorithms/oaep.rs @@ -3,9 +3,9 @@ use alloc::boxed::Box; use alloc::vec::Vec; +use crypto_bigint::{Choice, CtEq, CtOption, CtSelect}; use digest::{Digest, FixedOutputReset}; use rand_core::TryCryptoRng; -use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; use zeroize::Zeroizing; use super::mgf::{mgf1_xor, mgf1_xor_digest}; @@ -242,14 +242,14 @@ fn decrypt_inner( // looking_for_index: 1 if we are still looking for the 0x01 // index: the offset of the first 0x01 byte // zero_before_one: 1 if we saw a non-zero byte before the 1 - let mut looking_for_index = Choice::from(1u8); + let mut looking_for_index = Choice::TRUE; let mut index = 0u32; - let mut nonzero_before_one = Choice::from(0u8); + let mut nonzero_before_one = Choice::FALSE; for (i, el) in db.iter().skip(h_size).enumerate() { let equals0 = el.ct_eq(&0u8); let equals1 = el.ct_eq(&1u8); - index.conditional_assign(&(i as u32), looking_for_index & equals1); + index.ct_assign(&(i as u32), looking_for_index & equals1); looking_for_index &= !equals1; nonzero_before_one |= looking_for_index & !equals0; } diff --git a/src/algorithms/pkcs1v15.rs b/src/algorithms/pkcs1v15.rs index 73ac02d3..0260b6d1 100644 --- a/src/algorithms/pkcs1v15.rs +++ b/src/algorithms/pkcs1v15.rs @@ -8,9 +8,9 @@ use alloc::vec::Vec; use const_oid::AssociatedOid; +use crypto_bigint::{Choice, CtEq, CtSelect}; use digest::Digest; use rand_core::TryCryptoRng; -use subtle::{Choice, ConditionallySelectable, ConstantTimeEq}; use zeroize::Zeroizing; use crate::errors::{Error, Result}; @@ -99,8 +99,8 @@ fn decrypt_inner(em: Vec, k: usize) -> Result<(u8, Vec, u32)> { for (i, el) in em.iter().enumerate().skip(2) { let equals0 = el.ct_eq(&0u8); - index.conditional_assign(&(i as u32), Choice::from(looking_for_index) & equals0); - looking_for_index.conditional_assign(&0u8, equals0); + index.ct_assign(&(i as u32), Choice::new(looking_for_index) & equals0); + looking_for_index.ct_assign(&0u8, equals0); } // The PS padding must be at least 8 bytes long, and it starts two @@ -109,12 +109,12 @@ fn decrypt_inner(em: Vec, k: usize) -> Result<(u8, Vec, u32)> { // Ref: https://github.com/dalek-cryptography/subtle/issues/20 // This is currently copy & paste from the constant time impl in // go, but very likely not sufficient. - let valid_ps = Choice::from((((2i32 + 8i32 - index as i32 - 1i32) >> 31) & 1) as u8); + let valid_ps = Choice::new((((2i32 + 8i32 - index as i32 - 1i32) >> 31) & 1) as u8); let valid = - first_byte_is_zero & second_byte_is_two & Choice::from(!looking_for_index & 1) & valid_ps; - index = u32::conditional_select(&0, &(index + 1), valid); + first_byte_is_zero & second_byte_is_two & Choice::new(!looking_for_index & 1) & valid_ps; + index = u32::ct_select(&0, &(index + 1), valid); - Ok((valid.unwrap_u8(), em, index)) + Ok((valid.to_u8(), em, index)) } #[inline] @@ -155,7 +155,8 @@ pub(crate) fn pkcs1v15_sign_unpad(prefix: &[u8], hashed: &[u8], em: &[u8], k: us ok &= el.ct_eq(&0xff) } - if ok.unwrap_u8() != 1 { + // TODO(tarcieri): avoid branching here by e.g. using a pseudorandom rejection symbol + if !ok.to_bool() { return Err(Error::Verification); } diff --git a/src/algorithms/pss.rs b/src/algorithms/pss.rs index 1ef482f8..42dbf33d 100644 --- a/src/algorithms/pss.rs +++ b/src/algorithms/pss.rs @@ -10,8 +10,8 @@ //! [RFC8017 ยง 8.1]: https://datatracker.ietf.org/doc/html/rfc8017#section-8.1 use alloc::vec::Vec; +use crypto_bigint::{Choice, CtEq, CtSelect}; use digest::{Digest, FixedOutputReset}; -use subtle::{Choice, ConditionallySelectable, ConstantTimeEq}; use super::mgf::{mgf1_xor, mgf1_xor_digest}; use crate::errors::{Error, Result}; @@ -225,9 +225,7 @@ fn emsa_pss_verify_salt(db: &[u8], em_len: usize, s_len: usize, h_len: usize) -> // position is "position 1") does not have hexadecimal value 0x01, // output "inconsistent" and stop. let (zeroes, rest) = db.split_at(em_len - h_len - s_len - 2); - let valid: Choice = zeroes - .iter() - .fold(Choice::from(1u8), |a, e| a & e.ct_eq(&0x00)); + let valid: Choice = zeroes.iter().fold(Choice::TRUE, |a, e| a & e.ct_eq(&0x00)); valid & rest[0].ct_eq(&0x01) } @@ -240,8 +238,8 @@ fn emsa_pss_get_salt_len(db: &[u8], em_len: usize, h_len: usize) -> (usize, Choi let max_scan_len = em_len - h_len - 2; let mut separator_pos = 0u32; - let mut found_separator = Choice::from(0u8); - let mut padding_valid = Choice::from(1u8); + let mut found_separator = Choice::FALSE; + let mut padding_valid = Choice::TRUE; // Single forward scan to find separator and validate padding for i in 0..=max_scan_len { @@ -252,9 +250,8 @@ fn emsa_pss_get_salt_len(db: &[u8], em_len: usize, h_len: usize) -> (usize, Choi // Update separator position if we found one and haven't found one before let should_update_pos = is_separator & !found_separator; - separator_pos = u32::conditional_select(&separator_pos, &i, should_update_pos); - found_separator = - Choice::conditional_select(&found_separator, &Choice::from(1u8), should_update_pos); + separator_pos = u32::ct_select(&separator_pos, &i, should_update_pos); + found_separator = Choice::ct_select(&found_separator, &Choice::TRUE, should_update_pos); // Padding is invalid if we see a non-zero, non-separator byte before finding separator let corrupts_padding = is_invalid & !found_separator; @@ -265,7 +262,7 @@ fn emsa_pss_get_salt_len(db: &[u8], em_len: usize, h_len: usize) -> (usize, Choi let final_valid = found_separator & padding_valid; // Return 0 length on failure - let result_len = u32::conditional_select(&0u32, &salt_len, final_valid); + let result_len = u32::ct_select(&0u32, &salt_len, final_valid); (result_len as usize, final_valid) } diff --git a/src/algorithms/rsa.rs b/src/algorithms/rsa.rs index 5a32b0a0..326cd445 100644 --- a/src/algorithms/rsa.rs +++ b/src/algorithms/rsa.rs @@ -184,7 +184,7 @@ fn blind( let mut r: BoxedUint = BoxedUint::one_with_precision(bits); let mut ir: Option = None; while ir.is_none() { - r = BoxedUint::try_random_mod(rng, key.n()).map_err(|_| Error::Rng)?; + r = BoxedUint::try_random_mod_vartime(rng, key.n()).map_err(|_| Error::Rng)?; if r.is_zero().into() { r = BoxedUint::one_with_precision(bits); } diff --git a/src/key.rs b/src/key.rs index beec7404..3b1f74f9 100644 --- a/src/key.rs +++ b/src/key.rs @@ -52,8 +52,9 @@ impl Hash for RsaPublicKey { fn hash(&self, state: &mut H) { // Domain separator for RSA private keys state.write(b"RsaPublicKey"); - Hash::hash(&self.n, state); - Hash::hash(&self.e, state); + // TODO(tarcieri): to match the `PartialEq` impl we should strip leading zeros + Hash::hash(&self.n.as_limbs(), state); + Hash::hash(&self.e.as_limbs(), state); } } diff --git a/src/pss/signing_key.rs b/src/pss/signing_key.rs index f2eb2a15..51f0b6e9 100644 --- a/src/pss/signing_key.rs +++ b/src/pss/signing_key.rs @@ -27,9 +27,9 @@ use { }, }; -#[cfg(feature = "os_rng")] +#[cfg(feature = "getrandom")] use { - rand::rngs::OsRng, + crypto_common::SysRng, signature::{hazmat::PrehashSigner, MultipartSigner, Signer}, }; @@ -162,33 +162,33 @@ where } } -#[cfg(feature = "os_rng")] +#[cfg(feature = "getrandom")] impl PrehashSigner for SigningKey where D: Digest + FixedOutputReset, { fn sign_prehash(&self, prehash: &[u8]) -> signature::Result { - self.sign_prehash_with_rng(&mut OsRng, prehash) + self.sign_prehash_with_rng(&mut SysRng, prehash) } } -#[cfg(feature = "os_rng")] +#[cfg(feature = "getrandom")] impl Signer for SigningKey where D: Digest + FixedOutputReset, { fn try_sign(&self, msg: &[u8]) -> signature::Result { - self.try_sign_with_rng(&mut OsRng, msg) + self.try_sign_with_rng(&mut SysRng, msg) } } -#[cfg(feature = "os_rng")] +#[cfg(feature = "getrandom")] impl MultipartSigner for SigningKey where D: Digest + FixedOutputReset, { fn try_multipart_sign(&self, msg: &[&[u8]]) -> signature::Result { - self.try_multipart_sign_with_rng(&mut OsRng, msg) + self.try_multipart_sign_with_rng(&mut SysRng, msg) } } diff --git a/tests/pkcs1.rs b/tests/pkcs1.rs index 3a11ad30..b6384720 100644 --- a/tests/pkcs1.rs +++ b/tests/pkcs1.rs @@ -2,14 +2,13 @@ #![cfg(feature = "encoding")] -use crypto_bigint::BoxedUint; +use crypto_bigint::{BoxedUint, CtEq}; use hex_literal::hex; use rsa::{ pkcs1::{DecodeRsaPrivateKey, DecodeRsaPublicKey, EncodeRsaPrivateKey, EncodeRsaPublicKey}, traits::{PrivateKeyParts, PublicKeyParts}, RsaPrivateKey, RsaPublicKey, }; -use subtle::ConstantTimeEq; #[cfg(feature = "encoding")] use rsa::pkcs1::LineEnding; diff --git a/tests/pkcs8.rs b/tests/pkcs8.rs index 16a5c313..e63f0b0e 100644 --- a/tests/pkcs8.rs +++ b/tests/pkcs8.rs @@ -2,7 +2,7 @@ #![cfg(feature = "encoding")] -use crypto_bigint::BoxedUint; +use crypto_bigint::{BoxedUint, CtEq}; use hex_literal::hex; use rsa::{ pkcs1v15, @@ -12,7 +12,6 @@ use rsa::{ RsaPrivateKey, RsaPublicKey, }; use sha2::Sha256; -use subtle::ConstantTimeEq; #[cfg(feature = "encoding")] use rsa::pkcs8::LineEnding;