diff options
Diffstat (limited to 'openpgp/src/crypto')
-rw-r--r-- | openpgp/src/crypto/backend/botan/asymmetric.rs | 27 | ||||
-rw-r--r-- | openpgp/src/crypto/backend/cng/asymmetric.rs | 51 | ||||
-rw-r--r-- | openpgp/src/crypto/backend/interface.rs | 16 | ||||
-rw-r--r-- | openpgp/src/crypto/backend/nettle/asymmetric.rs | 33 | ||||
-rw-r--r-- | openpgp/src/crypto/backend/openssl/asymmetric.rs | 29 | ||||
-rw-r--r-- | openpgp/src/crypto/backend/rust/asymmetric.rs | 53 |
6 files changed, 209 insertions, 0 deletions
diff --git a/openpgp/src/crypto/backend/botan/asymmetric.rs b/openpgp/src/crypto/backend/botan/asymmetric.rs index 0ff8b51b..77943875 100644 --- a/openpgp/src/crypto/backend/botan/asymmetric.rs +++ b/openpgp/src/crypto/backend/botan/asymmetric.rs @@ -61,6 +61,33 @@ impl Asymmetric for super::Backend { let secret = Privkey::load_x25519(&secret)?; Ok(secret.agree(public, 32, b"", "Raw")?.into()) } + + fn ed25519_generate_key() -> Result<(Protected, [u8; 32])> { + let mut rng = RandomNumberGenerator::new_userspace()?; + let secret = Privkey::create("Ed25519", "", &mut rng)?; + let (public, secret) = secret.get_ed25519_key()?; + Ok((secret.into(), public.as_slice().try_into()?)) + } + + fn ed25519_derive_public(secret: &Protected) -> Result<[u8; 32]> { + let secret = Privkey::load_ed25519(secret)?; + let (public, secret) = secret.get_ed25519_key()?; + let _ = Protected::from(secret); // Securely dispose. + Ok(public.as_slice().try_into()?) + } + + fn ed25519_sign(secret: &Protected, _public: &[u8; 32], digest: &[u8]) + -> Result<[u8; 64]> { + let mut rng = RandomNumberGenerator::new_userspace()?; + let secret = Privkey::load_ed25519(&secret)?; + Ok(secret.sign(digest, "", &mut rng)?.as_slice().try_into()?) + } + + fn ed25519_verify(public: &[u8; 32], digest: &[u8], signature: &[u8; 64]) + -> Result<bool> { + let pk = Pubkey::load_ed25519(public)?; + Ok(pk.verify(digest, signature, "")?) + } } // CONFIDENTIALITY: Botan clears the MPIs after use. diff --git a/openpgp/src/crypto/backend/cng/asymmetric.rs b/openpgp/src/crypto/backend/cng/asymmetric.rs index f62f062f..b2299726 100644 --- a/openpgp/src/crypto/backend/cng/asymmetric.rs +++ b/openpgp/src/crypto/backend/cng/asymmetric.rs @@ -78,6 +78,57 @@ impl Asymmetric for super::Backend { shared.reverse(); Ok(shared) } + + 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.secret.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); + Ok(public.to_bytes()) + } + + 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()); + } + + 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}; + + let public = PublicKey::from_bytes(public).map_err(|e| { + Error::InvalidKey(e.to_string()) + })?; + let signature = Signature::from_bytes(&signature.clone())?; + Ok(public.verify(digest, &signature).is_ok()) + } } impl Signer for KeyPair { diff --git a/openpgp/src/crypto/backend/interface.rs b/openpgp/src/crypto/backend/interface.rs index 81343c6c..21034862 100644 --- a/openpgp/src/crypto/backend/interface.rs +++ b/openpgp/src/crypto/backend/interface.rs @@ -36,4 +36,20 @@ pub trait Asymmetric { /// Computes the shared point. fn x25519_shared_point(secret: &Protected, public: &[u8; 32]) -> Result<Protected>; + + /// Generates an Ed25519 key pair. + /// + /// Returns a tuple containing the secret and public key. + fn ed25519_generate_key() -> Result<(Protected, [u8; 32])>; + + /// Computes the public key for a given secret key. + fn ed25519_derive_public(secret: &Protected) -> Result<[u8; 32]>; + + /// Creates an Ed25519 signature. + fn ed25519_sign(secret: &Protected, public: &[u8; 32], digest: &[u8]) + -> Result<[u8; 64]>; + + /// Verifies an Ed25519 signature. + fn ed25519_verify(public: &[u8; 32], digest: &[u8], signature: &[u8; 64]) + -> Result<bool>; } diff --git a/openpgp/src/crypto/backend/nettle/asymmetric.rs b/openpgp/src/crypto/backend/nettle/asymmetric.rs index 52d93388..a83c5c1f 100644 --- a/openpgp/src/crypto/backend/nettle/asymmetric.rs +++ b/openpgp/src/crypto/backend/nettle/asymmetric.rs @@ -39,6 +39,39 @@ impl Asymmetric for super::Backend { curve25519::mul(&mut s, secret, public)?; Ok(s) } + + fn ed25519_generate_key() -> Result<(Protected, [u8; 32])> { + debug_assert_eq!(ed25519::ED25519_KEY_SIZE, 32); + let mut rng = Yarrow::default(); + let mut public = [0; 32]; + let secret: Protected = + ed25519::private_key(&mut rng).into(); + ed25519::public_key(&mut public, &secret)?; + Ok((secret, public)) + } + + fn ed25519_derive_public(secret: &Protected) -> Result<[u8; 32]> { + debug_assert_eq!(ed25519::ED25519_KEY_SIZE, 32); + let mut public = [0; 32]; + ed25519::public_key(&mut public, secret)?; + Ok(public) + } + + fn ed25519_sign(secret: &Protected, public: &[u8; 32], digest: &[u8]) + -> Result<[u8; 64]> { + debug_assert_eq!(ed25519::ED25519_KEY_SIZE, 32); + debug_assert_eq!(ed25519::ED25519_SIGNATURE_SIZE, 64); + let mut sig = [0u8; 64]; + ed25519::sign(public, secret, digest, &mut sig)?; + Ok(sig) + } + + fn ed25519_verify(public: &[u8; 32], digest: &[u8], signature: &[u8; 64]) + -> Result<bool> { + debug_assert_eq!(ed25519::ED25519_KEY_SIZE, 32); + debug_assert_eq!(ed25519::ED25519_SIGNATURE_SIZE, 64); + Ok(ed25519::verify(public, digest, signature)?) + } } impl Signer for KeyPair { diff --git a/openpgp/src/crypto/backend/openssl/asymmetric.rs b/openpgp/src/crypto/backend/openssl/asymmetric.rs index baffde97..cee5ebba 100644 --- a/openpgp/src/crypto/backend/openssl/asymmetric.rs +++ b/openpgp/src/crypto/backend/openssl/asymmetric.rs @@ -47,6 +47,35 @@ impl Asymmetric for super::Backend { deriver.set_peer(&public)?; Ok(deriver.derive_to_vec()?.into()) } + + fn ed25519_generate_key() -> Result<(Protected, [u8; 32])> { + let pair = openssl::pkey::PKey::generate_ed25519()?; + Ok((pair.raw_private_key()?.into(), + pair.raw_public_key()?.as_slice().try_into()?)) + } + + fn ed25519_derive_public(secret: &Protected) -> Result<[u8; 32]> { + let key = PKey::private_key_from_raw_bytes( + secret, openssl::pkey::Id::ED25519)?; + Ok(key.raw_public_key()?.as_slice().try_into()?) + } + + fn ed25519_sign(secret: &Protected, _public: &[u8; 32], digest: &[u8]) + -> Result<[u8; 64]> { + let key = PKey::private_key_from_raw_bytes( + secret, openssl::pkey::Id::ED25519)?; + + let mut signer = OpenSslSigner::new_without_digest(&key)?; + Ok(signer.sign_oneshot_to_vec(digest)?.as_slice().try_into()?) + } + + fn ed25519_verify(public: &[u8; 32], digest: &[u8], signature: &[u8; 64]) + -> Result<bool> { + let key = PKey::public_key_from_raw_bytes( + public, openssl::pkey::Id::ED25519)?; + let mut verifier = Verifier::new_without_digest(&key)?; + Ok(verifier.verify_oneshot(signature, digest)?) + } } impl TryFrom<&ProtectedMPI> for BigNum { diff --git a/openpgp/src/crypto/backend/rust/asymmetric.rs b/openpgp/src/crypto/backend/rust/asymmetric.rs index 1a558bfb..13049578 100644 --- a/openpgp/src/crypto/backend/rust/asymmetric.rs +++ b/openpgp/src/crypto/backend/rust/asymmetric.rs @@ -61,6 +61,59 @@ impl Asymmetric for super::Backend { let public = PublicKey::from(public.clone()); Ok((&secret.diffie_hellman(&public).as_bytes()[..]).into()) } + + 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.secret.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); + Ok(public.to_bytes()) + } + + 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()); + } + + 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}; + + let public = PublicKey::from_bytes(public).map_err(|e| { + Error::InvalidKey(e.to_string()) + })?; + let signature = Signature::from_bytes(&signature.clone())?; + Ok(public.verify(digest, &signature).is_ok()) + } } fn pkcs1_padding(hash_algo: HashAlgorithm) -> Result<Pkcs1v15Sign> { |