diff options
author | Justus Winter <justus@sequoia-pgp.org> | 2024-04-16 15:29:43 +0200 |
---|---|---|
committer | Justus Winter <justus@sequoia-pgp.org> | 2024-04-16 15:29:43 +0200 |
commit | 83860faa021ad1bdc3ebb1a8b0deec651c0b5e46 (patch) | |
tree | 51e00803a2ade8de9a524695261d843f60f2e999 /openpgp/src | |
parent | f650e183e645d22873247939fedf522736863f16 (diff) |
openpgp: Support NistP521 using the RustCrypto backend.
Diffstat (limited to 'openpgp/src')
-rw-r--r-- | openpgp/src/crypto/backend/rust/asymmetric.rs | 93 | ||||
-rw-r--r-- | openpgp/src/crypto/backend/rust/ecdh.rs | 38 |
2 files changed, 128 insertions, 3 deletions
diff --git a/openpgp/src/crypto/backend/rust/asymmetric.rs b/openpgp/src/crypto/backend/rust/asymmetric.rs index d8adf44f..a5a434f8 100644 --- a/openpgp/src/crypto/backend/rust/asymmetric.rs +++ b/openpgp/src/crypto/backend/rust/asymmetric.rs @@ -67,10 +67,8 @@ impl Asymmetric for super::Backend { fn supports_curve(curve: &Curve) -> bool { use self::Curve::*; match curve { - NistP256 | NistP384 + NistP256 | NistP384 | NistP521 => true, - NistP521 - => false, Ed25519 | Cv25519 => true, BrainpoolP256 | BrainpoolP512 | Unknown(_) @@ -323,6 +321,31 @@ impl KeyPair { }) }, + Curve::NistP521 => { + use p521::Scalar; + const LEN: usize = 66; + + let key = scalar.value_padded(LEN); + let key = Scalar::reduce_bytes(GA::try_from_slice(&key)?); + let dig = pad_truncating(digest, LEN); + let dig = GA::try_from_slice(&dig)?; + + let sig = loop { + let mut k: Protected = vec![0; LEN].into(); + crate::crypto::random(&mut k); + let k = Scalar::reduce_bytes( + GA::try_from_slice(&k)?); + if let Ok(s) = key.try_sign_prehashed(k, &dig) { + break s.0; + } + }; + + Ok(mpi::Signature::ECDSA { + r: MPI::new(&sig.r().to_bytes()), + s: MPI::new(&sig.s().to_bytes()), + }) + }, + _ => Err(Error::UnsupportedEllipticCurve(curve.clone()).into()), }, @@ -504,6 +527,30 @@ impl<P: key::KeyParts, R: key::KeyRole> Key<P, R> { key.verify_prehashed(&dig, &sig).map_err(bad) }, + Curve::NistP521 => { + use p521::{AffinePoint, ecdsa::Signature}; + const LEN: usize = 66; + + let key = AffinePoint::from_encoded_point( + &EncodedPoint::<p521::NistP521>::from_bytes(q.value())?); + let key = if key.is_some().into() { + key.unwrap() + } else { + return Err(Error::InvalidKey( + "Point is not on the curve".into()).into()); + }; + + let sig = Signature::from_scalars( + GA::try_clone_from_slice( + &r.value_padded(LEN).map_err(bad)?)?, + GA::try_clone_from_slice( + &s.value_padded(LEN).map_err(bad)?)?) + .map_err(bad)?; + let dig = pad_truncating(digest, LEN); + let dig = GA::try_from_slice(&dig)?; + key.verify_prehashed(&dig, &sig).map_err(bad) + }, + _ => Err(Error::UnsupportedEllipticCurve(curve.clone()).into()), }, _ => Err(Error::MalformedPacket(format!( @@ -688,6 +735,46 @@ impl<R> Key4<SecretParts, R> Ok((PublicKeyAlgorithm::ECDH, public_mpis, private_mpis)) }, + (Curve::NistP521, true) => { + use p521::{EncodedPoint, SecretKey}; + + let secret = SecretKey::random( + &mut p521::elliptic_curve::rand_core::OsRng); + let public = EncodedPoint::from(secret.public_key()); + + let public_mpis = mpi::PublicKey::ECDSA { + curve, + q: MPI::new(public.as_bytes()), + }; + let private_mpis = mpi::SecretKeyMaterial::ECDSA { + scalar: Vec::from(secret.to_bytes().as_slice()).into(), + }; + + Ok((PublicKeyAlgorithm::ECDSA, public_mpis, private_mpis)) + }, + + (Curve::NistP521, false) => { + use p521::{EncodedPoint, SecretKey}; + + let secret = SecretKey::random( + &mut p521::elliptic_curve::rand_core::OsRng); + let public = EncodedPoint::from(secret.public_key()); + + let public_mpis = mpi::PublicKey::ECDH { + q: MPI::new(public.as_bytes()), + hash: + crate::crypto::ecdh::default_ecdh_kdf_hash(&curve), + sym: + crate::crypto::ecdh::default_ecdh_kek_cipher(&curve), + curve, + }; + let private_mpis = mpi::SecretKeyMaterial::ECDH { + scalar: Vec::from(secret.to_bytes().as_slice()).into(), + }; + + Ok((PublicKeyAlgorithm::ECDH, public_mpis, private_mpis)) + }, + _ => Err(Error::UnsupportedEllipticCurve(curve).into()), } } diff --git a/openpgp/src/crypto/backend/rust/ecdh.rs b/openpgp/src/crypto/backend/rust/ecdh.rs index eed2f827..77fd7d23 100644 --- a/openpgp/src/crypto/backend/rust/ecdh.rs +++ b/openpgp/src/crypto/backend/rust/ecdh.rs @@ -104,6 +104,28 @@ pub fn encrypt<R>(recipient: &Key<key::PublicParts, R>, (VB, shared) }, + Curve::NistP521 => { + use p521::{EncodedPoint, PublicKey, ecdh::EphemeralSecret}; + + // Decode the recipient's public key. + let recipient_key = PublicKey::from_sec1_bytes(q.value())?; + + // Generate a keypair and perform Diffie-Hellman. + let secret = EphemeralSecret::random( + &mut p521::elliptic_curve::rand_core::OsRng); + let public = EncodedPoint::from(PublicKey::from(&secret)); + let shared = secret.diffie_hellman(&recipient_key); + + // Encode our public key. + let VB = MPI::new(public.as_bytes()); + + // Encode the shared secret. + let shared: &[u8] = shared.raw_secret_bytes(); + let shared = Protected::from(shared); + + (VB, shared) + }, + _ => return Err(Error::UnsupportedEllipticCurve(curve.clone()).into()), }; @@ -175,6 +197,22 @@ pub fn decrypt<R>(recipient: &Key<key::PublicParts, R>, Vec::from(secret.raw_secret_bytes().as_slice()).into() }, + Curve::NistP521 => { + use p521::{SecretKey, PublicKey}; + const NISTP521_SIZE: usize = 66; + + // Get the public part V of the ephemeral key. + let V = PublicKey::from_sec1_bytes(e.value())?; + + let scalar: [u8; NISTP521_SIZE] = + scalar.value_padded(NISTP521_SIZE).as_ref().try_into()?; + let scalar = GA::try_from_slice(&scalar)?; + let r = SecretKey::from_bytes(&scalar)?; + + let secret = diffie_hellman(r.to_nonzero_scalar(), V.as_affine()); + Vec::from(secret.raw_secret_bytes().as_slice()).into() + }, + _ => { return Err(Error::UnsupportedEllipticCurve(curve.clone()).into()); }, |