diff options
author | Wiktor Kwapisiewicz <wiktor@metacode.biz> | 2023-09-05 11:18:37 +0200 |
---|---|---|
committer | Wiktor Kwapisiewicz <wiktor@metacode.biz> | 2023-09-05 11:30:09 +0200 |
commit | a8127384578bb815e07258f6ad1dc65c5e55a187 (patch) | |
tree | 863a14c818b0c10a6d85f237645b4c2fd98fdca8 /openpgp | |
parent | ff8fc9bd682aa6dcaa96a55f9368789807f8900c (diff) |
openpgp: Upgrade ed25519_dalek to version 2.
- This solves the following issue reported to ed25519_dalek:
https://rustsec.org/advisories/RUSTSEC-2022-0093
- Upgrade win_crypto_ng so that it allows usage of rand_core that is
compatible with both win_crypto_ng and ed25519_dalek:
https://github.com/emgre/win-crypto-ng/pull/48
Diffstat (limited to 'openpgp')
-rw-r--r-- | openpgp/Cargo.toml | 14 | ||||
-rw-r--r-- | openpgp/src/crypto/backend/cng/asymmetric.rs | 68 | ||||
-rw-r--r-- | openpgp/src/crypto/backend/rust.rs | 4 | ||||
-rw-r--r-- | openpgp/src/crypto/backend/rust/asymmetric.rs | 77 |
4 files changed, 75 insertions, 88 deletions
diff --git a/openpgp/Cargo.toml b/openpgp/Cargo.toml index 2f48b495..4cadd511 100644 --- a/openpgp/Cargo.toml +++ b/openpgp/Cargo.toml @@ -45,6 +45,8 @@ regex-syntax = "0.6" sha1collisiondetection = { version = "0.2.3", default-features = false, features = ["std"] } thiserror = "1.0.2" xxhash-rust = { version = "0.8", features = ["xxh3"] } +rand = { version = "0.8" } + # At least 0.10.55 is needed due `no-ocb` check: # https://github.com/sfackler/rust-openssl/blob/master/openssl/CHANGELOG.md openssl = { version = "0.10.55", optional = true } @@ -75,16 +77,12 @@ ecdsa = { version = "0.16", optional = true, features = ["hazmat", "arithmetic"] # need the std feature, at least so that ed25519::Error implements # std::error::Error. ed25519 = { version = "1", default-features = false, features = ["std"], optional = true } -ed25519-dalek = { version = "1", default-features = false, features = ["rand", "u64_backend"], optional = true } +ed25519-dalek = { version = "2", features = ["rand_core"], optional = true } generic-array = { version = "0.14.4", optional = true } idea = { version = "0.5", optional = true } md-5 = { version = "0.10", features = ["oid"], optional = true } num-bigint-dig = { version = "0.8", default-features = false, optional = true } p256 = { version = "0.13", optional = true, features = ["ecdh", "ecdsa"] } -# XXX: ed25519-dalek 1.0.1 depends on rand 0.7 and doesn't reexport it. -# https://github.com/dalek-cryptography/ed25519-dalek/blob/1.0.1/Cargo.toml#L28 -rand07 = { package = "rand", version = "0.7.3", optional = true } -rand = { package = "rand", version = "0.8", optional = true } rand_core = { version = "0.6", optional = true } ripemd = { version = "0.1", features = ["oid"], optional = true } rsa = { version = "0.9.0", optional = true } @@ -112,14 +110,12 @@ winapi = { version = "0.3.8", default-features = false, features = ["bcrypt"], o [target.'cfg(all(target_arch = "wasm32", target_os = "unknown"))'.dependencies] chrono = { version = "0.4.10", default-features = false, features = ["std", "wasmbind", "clock"] } getrandom = { version = "0.2", features = ["js"] } -rand07 = { package = "rand", version = "0.7", features = ["wasm-bindgen"] } [build-dependencies] lalrpop = { version = ">=0.17, <0.20", default-features = false } [dev-dependencies] quickcheck = { version = "1", default-features = false } -rand = { version = "0.8" } rpassword = "6.0" criterion = { version = "0.4", features = ["html_reports"] } @@ -130,13 +126,13 @@ crypto-nettle = ["nettle"] crypto-rust = [ "aes", "block-padding", "blowfish", "camellia", "cast5", "cfb-mode", "cipher", "des", "digest", "eax", "ecb", "ed25519", "ed25519-dalek", "generic-array", "idea", - "md-5", "num-bigint-dig", "rand", "rand07", "ripemd", "rsa", "sha-1", "sha2", + "md-5", "num-bigint-dig", "ripemd", "rsa", "sha-1", "sha2", "twofish", "typenum", "x25519-dalek-ng", "p256", "rand_core", "rand_core/getrandom", "ecdsa", "aes-gcm", "dsa" ] crypto-cng = [ "cipher", "eax", "winapi", "win-crypto-ng", "ed25519", "ed25519-dalek", - "num-bigint-dig", "aes-gcm" + "num-bigint-dig", "aes-gcm", "rand_core" ] crypto-openssl = ["openssl", "openssl-sys"] crypto-botan = ["botan/botan3"] diff --git a/openpgp/src/crypto/backend/cng/asymmetric.rs b/openpgp/src/crypto/backend/cng/asymmetric.rs index d75107d7..debddac5 100644 --- a/openpgp/src/crypto/backend/cng/asymmetric.rs +++ b/openpgp/src/crypto/backend/cng/asymmetric.rs @@ -22,6 +22,20 @@ use win_crypto_ng as cng; const CURVE25519_SIZE: usize = 32; +impl TryFrom<&Protected> for ed25519_dalek::SigningKey { + type Error = anyhow::Error; + + fn try_from(value: &Protected) -> Result<Self> { + if value.len() != ed25519_dalek::SECRET_KEY_LENGTH { + return Err(crate::Error::InvalidArgument( + "Bad Ed25519 secret length".into()).into()); + } + Ok(Self::from_bytes(value.as_ref().try_into().map_err(|e: std::array::TryFromSliceError| { + Error::InvalidKey(e.to_string()) + })?)) + } +} + impl Asymmetric for super::Backend { fn supports_algo(algo: PublicKeyAlgorithm) -> bool { use PublicKeyAlgorithm::*; @@ -102,52 +116,36 @@ impl Asymmetric for super::Backend { fn ed25519_generate_key() -> Result<(Protected, [u8; 32])> { let mut rng = cng::random::RandomNumberGenerator::system_preferred(); - let pair = ed25519_dalek::Keypair::generate(&mut rng); - Ok((pair.secret.as_bytes().as_slice().into(), pair.public.to_bytes())) + let pair = ed25519_dalek::SigningKey::generate(&mut rng); + Ok((pair.to_bytes().into(), pair.verifying_key().to_bytes())) } fn ed25519_derive_public(secret: &Protected) -> Result<[u8; 32]> { - use ed25519_dalek::{PublicKey, SecretKey}; - - let secret = SecretKey::from_bytes(secret).map_err(|e| { - Error::InvalidKey(e.to_string()) - })?; - let public = PublicKey::from(&secret); + use ed25519_dalek::SigningKey; + let secret: SigningKey = secret.try_into()?; + let public = secret.verifying_key(); Ok(public.to_bytes()) } - fn ed25519_sign(secret: &Protected, public: &[u8; 32], digest: &[u8]) + fn ed25519_sign(secret: &Protected, _public: &[u8; 32], digest: &[u8]) -> Result<[u8; 64]> { - use ed25519_dalek::{Keypair, Signer}; - use ed25519_dalek::{PUBLIC_KEY_LENGTH, SECRET_KEY_LENGTH}; - - if secret.len() != SECRET_KEY_LENGTH { - return Err(crate::Error::InvalidArgument( - "Bad Ed25519 secret length".into()).into()); - } - - let mut keypair = Protected::from( - vec![0u8; SECRET_KEY_LENGTH + PUBLIC_KEY_LENGTH] - ); - keypair.as_mut()[..SECRET_KEY_LENGTH].copy_from_slice(secret); - keypair.as_mut()[SECRET_KEY_LENGTH..].copy_from_slice(public); - let pair = Keypair::from_bytes(&keypair)?; - unsafe { - memsec::memzero(keypair.as_mut_ptr(), keypair.len()); - } - + use ed25519_dalek::{SigningKey, Signer}; + let pair: SigningKey = secret.try_into()?; Ok(pair.sign(digest).to_bytes().try_into()?) } fn ed25519_verify(public: &[u8; 32], digest: &[u8], signature: &[u8; 64]) -> Result<bool> { - use ed25519_dalek::{PublicKey, Signature}; - use ed25519_dalek::{Verifier}; + use ed25519_dalek::{VerifyingKey, Verifier, Signature}; - let public = PublicKey::from_bytes(public).map_err(|e| { + let public = VerifyingKey::from_bytes(public).map_err(|e| { Error::InvalidKey(e.to_string()) })?; - let signature = Signature::from_bytes(&signature.clone())?; + let signature = signature.as_ref().try_into().map_err(|e: std::array::TryFromSliceError| { + Error::InvalidArgument(e.to_string()) + })?; + + let signature = Signature::from_bytes(signature); Ok(public.verify(digest, &signature).is_ok()) } @@ -921,18 +919,18 @@ where }, (Curve::Ed25519, true) => { // CNG doesn't support EdDSA, use ed25519-dalek instead - use ed25519_dalek::Keypair; + use ed25519_dalek::SigningKey; let mut rng = cng::random::RandomNumberGenerator::system_preferred(); - let Keypair { public, secret } = Keypair::generate(&mut rng); + let key = SigningKey::generate(&mut rng); - let secret: Protected = secret.as_bytes().as_ref().into(); + let secret: Protected = key.to_bytes().as_ref().into(); // Mark MPI as compressed point with 0x40 prefix. See // https://tools.ietf.org/html/draft-ietf-openpgp-rfc4880bis-07#section-13.2. let mut compressed_public = [0u8; 1 + CURVE25519_SIZE]; compressed_public[0] = 0x40; - compressed_public[1..].copy_from_slice(public.as_bytes()); + compressed_public[1..].copy_from_slice(key.verifying_key().as_bytes()); ( EdDSA, diff --git a/openpgp/src/crypto/backend/rust.rs b/openpgp/src/crypto/backend/rust.rs index 41e95492..90e0ed16 100644 --- a/openpgp/src/crypto/backend/rust.rs +++ b/openpgp/src/crypto/backend/rust.rs @@ -21,8 +21,8 @@ impl super::interface::Backend for Backend { } fn random(buf: &mut [u8]) -> Result<()> { - use rand07::rngs::OsRng; - use rand07::RngCore; + use rand::rngs::OsRng; + use rand::RngCore; OsRng.fill_bytes(buf); Ok(()) } diff --git a/openpgp/src/crypto/backend/rust/asymmetric.rs b/openpgp/src/crypto/backend/rust/asymmetric.rs index 7e10d34a..a3031ba8 100644 --- a/openpgp/src/crypto/backend/rust/asymmetric.rs +++ b/openpgp/src/crypto/backend/rust/asymmetric.rs @@ -27,6 +27,20 @@ use super::GenericArrayExt; const CURVE25519_SIZE: usize = 32; +impl TryFrom<&Protected> for ed25519_dalek::SigningKey { + type Error = anyhow::Error; + + fn try_from(value: &Protected) -> Result<Self> { + if value.len() != ed25519_dalek::SECRET_KEY_LENGTH { + return Err(crate::Error::InvalidArgument( + "Bad Ed25519 secret length".into()).into()); + } + Ok(Self::from_bytes(value.as_ref().try_into().map_err(|e: std::array::TryFromSliceError| { + Error::InvalidKey(e.to_string()) + })?)) + } +} + impl Asymmetric for super::Backend { fn supports_algo(algo: PublicKeyAlgorithm) -> bool { use PublicKeyAlgorithm::*; @@ -90,55 +104,37 @@ impl Asymmetric for super::Backend { } fn ed25519_generate_key() -> Result<(Protected, [u8; 32])> { - // ed25519_dalek v1.0.1 doesn't reexport OsRng. It - // depends on 0.7. - use rand07::rngs::OsRng as OsRng; - let pair = ed25519_dalek::Keypair::generate(&mut OsRng); - Ok((pair.secret.as_bytes().as_slice().into(), pair.public.to_bytes())) + use rand::rngs::OsRng as OsRng; + let pair = ed25519_dalek::SigningKey::generate(&mut OsRng); + Ok((pair.to_bytes().into(), pair.verifying_key().to_bytes())) } fn ed25519_derive_public(secret: &Protected) -> Result<[u8; 32]> { - use ed25519_dalek::{PublicKey, SecretKey}; - - let secret = SecretKey::from_bytes(secret).map_err(|e| { - Error::InvalidKey(e.to_string()) - })?; - let public = PublicKey::from(&secret); + use ed25519_dalek::SigningKey; + let secret: SigningKey = secret.try_into()?; + let public = secret.verifying_key(); Ok(public.to_bytes()) } - fn ed25519_sign(secret: &Protected, public: &[u8; 32], digest: &[u8]) + fn ed25519_sign(secret: &Protected, _public: &[u8; 32], digest: &[u8]) -> Result<[u8; 64]> { - use ed25519_dalek::{Keypair, Signer}; - use ed25519_dalek::{PUBLIC_KEY_LENGTH, SECRET_KEY_LENGTH}; - - if secret.len() != SECRET_KEY_LENGTH { - return Err(crate::Error::InvalidArgument( - "Bad Ed25519 secret length".into()).into()); - } - - let mut keypair = Protected::from( - vec![0u8; SECRET_KEY_LENGTH + PUBLIC_KEY_LENGTH] - ); - keypair.as_mut()[..SECRET_KEY_LENGTH].copy_from_slice(secret); - keypair.as_mut()[SECRET_KEY_LENGTH..].copy_from_slice(public); - let pair = Keypair::from_bytes(&keypair)?; - unsafe { - memsec::memzero(keypair.as_mut_ptr(), keypair.len()); - } - + use ed25519_dalek::{SigningKey, Signer}; + let pair: SigningKey = secret.try_into()?; Ok(pair.sign(digest).to_bytes().try_into()?) } fn ed25519_verify(public: &[u8; 32], digest: &[u8], signature: &[u8; 64]) -> Result<bool> { - use ed25519_dalek::{PublicKey, Signature}; - use ed25519_dalek::{Verifier}; + use ed25519_dalek::{VerifyingKey, Verifier, Signature}; - let public = PublicKey::from_bytes(public).map_err(|e| { + let public = VerifyingKey::from_bytes(public).map_err(|e| { Error::InvalidKey(e.to_string()) })?; - let signature = Signature::from_bytes(&signature.clone())?; + let signature = signature.as_ref().try_into().map_err(|e: std::array::TryFromSliceError| { + Error::InvalidArgument(e.to_string()) + })?; + + let signature = Signature::from_bytes(signature); Ok(public.verify(digest, &signature).is_ok()) } @@ -566,22 +562,19 @@ impl<R> Key4<SecretParts, R> let (algo, public, private) = match (&curve, for_signing) { (Curve::Ed25519, true) => { - use ed25519_dalek::Keypair; + use ed25519_dalek::SigningKey; - // ed25519_dalek v1.0.1 doesn't reexport OsRng. It - // depends on 0.7. - use rand07::rngs::OsRng as OsRng; + use rand::rngs::OsRng as OsRng; - let Keypair { public, secret } - = Keypair::generate(&mut OsRng); + let key = SigningKey::generate(&mut OsRng); - let secret: Protected = secret.as_bytes().as_ref().into(); + let secret: Protected = key.to_bytes().as_ref().into(); // Mark MPI as compressed point with 0x40 prefix. See // https://tools.ietf.org/html/draft-ietf-openpgp-rfc4880bis-07#section-13.2. let mut compressed_public = [0u8; 1 + CURVE25519_SIZE]; compressed_public[0] = 0x40; - compressed_public[1..].copy_from_slice(public.as_bytes()); + compressed_public[1..].copy_from_slice(key.verifying_key().as_bytes()); ( PublicKeyAlgorithm::EdDSA, |