diff options
Diffstat (limited to 'openpgp/src/crypto')
-rw-r--r-- | openpgp/src/crypto/backend/cng.rs | 2 | ||||
-rw-r--r-- | openpgp/src/crypto/backend/cng/ecdh.rs | 7 | ||||
-rw-r--r-- | openpgp/src/crypto/backend/nettle.rs | 4 | ||||
-rw-r--r-- | openpgp/src/crypto/backend/nettle/asymmetric.rs | 75 | ||||
-rw-r--r-- | openpgp/src/crypto/backend/nettle/ecdh.rs | 48 | ||||
-rw-r--r-- | openpgp/src/crypto/backend/rust.rs | 2 | ||||
-rw-r--r-- | openpgp/src/crypto/backend/rust/ecdh.rs | 4 | ||||
-rw-r--r-- | openpgp/src/crypto/ecdh.rs | 4 | ||||
-rw-r--r-- | openpgp/src/crypto/mpi.rs | 63 |
9 files changed, 200 insertions, 9 deletions
diff --git a/openpgp/src/crypto/backend/cng.rs b/openpgp/src/crypto/backend/cng.rs index b2e830d8..767259e5 100644 --- a/openpgp/src/crypto/backend/cng.rs +++ b/openpgp/src/crypto/backend/cng.rs @@ -42,6 +42,8 @@ impl Curve { match &self { NistP256 | NistP384 | NistP521 | Ed25519 | Cv25519 => true, + Ed448 | Cv448 + => false, BrainpoolP256 | BrainpoolP512 | Unknown(_) => false, } diff --git a/openpgp/src/crypto/backend/cng/ecdh.rs b/openpgp/src/crypto/backend/cng/ecdh.rs index ab687e24..95bbcb44 100644 --- a/openpgp/src/crypto/backend/cng/ecdh.rs +++ b/openpgp/src/crypto/backend/cng/ecdh.rs @@ -60,6 +60,8 @@ where encrypt_wrap(recipient, session_key, VB, &S) } + Curve::Cv448 => + return Err(Error::UnsupportedEllipticCurve(curve.clone()).into()), Curve::NistP256 | Curve::NistP384 | Curve::NistP521 => { let (Rx, Ry) = q.decode_point(curve)?; @@ -142,7 +144,7 @@ where Err(Error::UnsupportedEllipticCurve(curve.clone()).into()), // N/A - Curve::Unknown(_) | Curve::Ed25519 => + Curve::Unknown(_) | Curve::Ed25519 | Curve::Ed448 => Err(Error::UnsupportedEllipticCurve(curve.clone()).into()), } } @@ -204,6 +206,9 @@ where secret.into() } + Curve::Cv448 => + return Err(Error::UnsupportedEllipticCurve(curve.clone()).into()), + Curve::NistP256 | Curve::NistP384 | Curve::NistP521 => { // Get the public part V of the ephemeral key and // compute the shared point S = rV = rvG, where (r, R) diff --git a/openpgp/src/crypto/backend/nettle.rs b/openpgp/src/crypto/backend/nettle.rs index ef68cc35..c64bb41a 100644 --- a/openpgp/src/crypto/backend/nettle.rs +++ b/openpgp/src/crypto/backend/nettle.rs @@ -70,6 +70,10 @@ impl Curve { match &self { NistP256 | NistP384 | NistP521 | Ed25519 | Cv25519 => true, + Ed448 + => nettle::ed448::IS_SUPPORTED, + Cv448 + => nettle::curve448::IS_SUPPORTED, BrainpoolP256 | BrainpoolP512 | Unknown(_) => false, } diff --git a/openpgp/src/crypto/backend/nettle/asymmetric.rs b/openpgp/src/crypto/backend/nettle/asymmetric.rs index 9efad861..fc362401 100644 --- a/openpgp/src/crypto/backend/nettle/asymmetric.rs +++ b/openpgp/src/crypto/backend/nettle/asymmetric.rs @@ -4,13 +4,24 @@ //! [`Decryptor`]: super::super::asymmetric::Decryptor //! [`KeyPair`]: super::super::asymmetric::KeyPair -use nettle::{curve25519, ecc, ecdh, ecdsa, ed25519, dsa, rsa, random::Yarrow}; +use nettle::{ + curve25519, + curve448, + dsa, + ecc, + ecdh, + ecdsa, + ed25519, + ed448, + random::Yarrow, + rsa, +}; use crate::{Error, Result}; use crate::packet::{key, Key}; use crate::crypto::asymmetric::{KeyPair, Decryptor, Signer}; -use crate::crypto::mpi::{self, MPI, PublicKey}; +use crate::crypto::mpi::{self, MPI, ProtectedMPI, PublicKey}; use crate::crypto::SessionKey; use crate::types::{Curve, HashAlgorithm}; @@ -94,6 +105,17 @@ impl Signer for KeyPair { s: MPI::new(&sig[ed25519::ED25519_KEY_SIZE..]), }) }, + + Curve::Ed448 => { + let public = q.decode_octet_string(curve.field_size()?)?; + let secret = scalar.decode_octet_string(curve.field_size()?)?; + let mut sig = vec![0; ed448::ED448_SIGNATURE_SIZE]; + ed448::sign(public, secret, digest, &mut sig)?; + Ok(mpi::Signature::EdDSA { + r: MPI::new_octet_string(sig), + s: MPI::zero(), + }) + }, _ => Err( Error::UnsupportedEllipticCurve(curve.clone()).into()), }, @@ -286,6 +308,19 @@ impl<P: key::KeyParts, R: key::KeyRole> Key<P, R> { ed25519::verify(&q.value()[1..], digest, &signature)? }, + Curve::Ed448 => { + let public = q.decode_octet_string(curve.field_size()?)?; + let signature = + r.decode_octet_string(curve.field_size()? * 2)?; + assert_eq!(signature.len(), ed448::ED448_SIGNATURE_SIZE); + if ! s.is_zero() { + return Err(Error::BadSignature( + "Ed448 signature's S parameter is not zero".into()) + .into()); + } + + ed448::verify(public, digest, signature)? + }, _ => return Err(Error::UnsupportedEllipticCurve(curve.clone()).into()), }, @@ -492,6 +527,42 @@ impl<R> Key4<SecretParts, R> (public_mpis, sec, ECDH) } + (Curve::Ed448, true) => { + let private: Protected = + ed448::private_key(&mut rng).into(); + let mut public = [0; ed448::ED448_KEY_SIZE]; + ed448::public_key(&mut public, &private)?; + + let public = PublicKey::EdDSA { + curve: Curve::Ed448, + q: MPI::new_octet_string(public), + }; + let private = mpi::SecretKeyMaterial::EdDSA { + scalar: ProtectedMPI::new_octet_string(private), + }; + + (public, private.into(), EdDSA) + }, + + (Curve::Cv448, false) => { + let private: Protected = + curve448::private_key(&mut rng).into(); + let mut public = [0; curve448::CURVE448_SIZE]; + curve448::mul_g(&mut public, &private)?; + + let public = PublicKey::ECDH { + curve: Curve::Cv448, + q: MPI::new_octet_string(public), + hash: HashAlgorithm::SHA512, + sym: SymmetricAlgorithm::AES256, + }; + let private = mpi::SecretKeyMaterial::ECDH { + scalar: ProtectedMPI::new_octet_string(private), + }; + + (public, private.into(), ECDH) + }, + (Curve::NistP256, true) | (Curve::NistP384, true) | (Curve::NistP521, true) => { let (public, private, field_sz) = match curve { diff --git a/openpgp/src/crypto/backend/nettle/ecdh.rs b/openpgp/src/crypto/backend/nettle/ecdh.rs index eea5661d..7aec6d73 100644 --- a/openpgp/src/crypto/backend/nettle/ecdh.rs +++ b/openpgp/src/crypto/backend/nettle/ecdh.rs @@ -1,6 +1,12 @@ //! Elliptic Curve Diffie-Hellman. -use nettle::{curve25519, ecc, ecdh, random::Yarrow}; +use nettle::{ + curve25519, + curve448, + ecc, + ecdh, + random::Yarrow, +}; use crate::{Error, Result}; use crate::crypto::SessionKey; @@ -45,6 +51,28 @@ pub fn encrypt<R>(recipient: &Key<key::PublicParts, R>, encrypt_wrap(recipient, session_key, VB, &S) } + Curve::Cv448 => { + // Obtain the authenticated recipient public key R + let R = q.decode_octet_string(curve.field_size()?)?; + + // Generate an ephemeral key pair {v, V=vG} + let v: Protected = + curve448::private_key(&mut rng).into(); + + // Compute the public key. + let mut VB = [0; curve448::CURVE448_SIZE]; + curve448::mul_g(&mut VB, &v) + .expect("buffers are of the wrong size"); + let VB = MPI::new_octet_string(&VB); + + // Compute the shared point S = vR; + let mut S: Protected = + vec![0; curve448::CURVE448_SIZE].into(); + curve448::mul(&mut S, &v, R) + .expect("buffers are of the wrong size"); + + encrypt_wrap(recipient, session_key, VB, &S) + }, Curve::NistP256 | Curve::NistP384 | Curve::NistP521 => { // Obtain the authenticated recipient public key R and // generate an ephemeral private key v. @@ -107,7 +135,7 @@ pub fn encrypt<R>(recipient: &Key<key::PublicParts, R>, Err(Error::UnsupportedEllipticCurve(curve.clone()).into()), // N/A - Curve::Unknown(_) | Curve::Ed25519 => + Curve::Unknown(_) | Curve::Ed25519 | Curve::Ed448 => Err(Error::UnsupportedEllipticCurve(curve.clone()).into()), } } else { @@ -154,6 +182,20 @@ pub fn decrypt<R>(recipient: &Key<key::PublicParts, R>, S } + Curve::Cv448 => { + // Get the public part V of the ephemeral key. + let V = e.decode_octet_string(curve.field_size()?)?; + let r = scalar.decode_octet_string(curve.field_size()?)?; + + // Compute the shared point S = rV = rvG, where (r, R) + // is the recipient's key pair. + let mut S: Protected = + vec![0; curve448::CURVE448_SIZE].into(); + curve448::mul(&mut S, r, V) + .expect("buffers are of the wrong size"); + S + }, + Curve::NistP256 | Curve::NistP384 | Curve::NistP521 => { // Get the public part V of the ephemeral key and // compute the shared point S = rV = rvG, where (r, R) @@ -208,7 +250,7 @@ pub fn decrypt<R>(recipient: &Key<key::PublicParts, R>, Err(Error::UnsupportedEllipticCurve(curve.clone()).into()), // N/A - Curve::Unknown(_) | Curve::Ed25519 => + Curve::Unknown(_) | Curve::Ed25519 | Curve::Ed448 => return Err(Error::UnsupportedEllipticCurve(curve.clone()).into()), }; diff --git a/openpgp/src/crypto/backend/rust.rs b/openpgp/src/crypto/backend/rust.rs index 26284d33..75274934 100644 --- a/openpgp/src/crypto/backend/rust.rs +++ b/openpgp/src/crypto/backend/rust.rs @@ -48,6 +48,8 @@ impl Curve { => false, Ed25519 | Cv25519 => true, + Ed448 | Cv448 + => false, BrainpoolP256 | BrainpoolP512 | Unknown(_) => false, } diff --git a/openpgp/src/crypto/backend/rust/ecdh.rs b/openpgp/src/crypto/backend/rust/ecdh.rs index 8531013d..b05ee7ba 100644 --- a/openpgp/src/crypto/backend/rust/ecdh.rs +++ b/openpgp/src/crypto/backend/rust/ecdh.rs @@ -52,6 +52,8 @@ pub fn encrypt<R>(recipient: &Key<key::PublicParts, R>, (VB, shared) }, + Curve::Cv448 => + return Err(Error::UnsupportedEllipticCurve(curve.clone()).into()), Curve::NistP256 => { use p256::{EncodedPoint, PublicKey, ecdh::EphemeralSecret}; @@ -111,6 +113,8 @@ pub fn decrypt<R>(recipient: &Key<key::PublicParts, R>, let secret = r.diffie_hellman(&V); Vec::from(secret.to_bytes()).into() }, + Curve::Cv448 => + return Err(Error::UnsupportedEllipticCurve(curve.clone()).into()), Curve::NistP256 => { use p256::{ SecretKey, diff --git a/openpgp/src/crypto/ecdh.rs b/openpgp/src/crypto/ecdh.rs index 2092ad07..33016dec 100644 --- a/openpgp/src/crypto/ecdh.rs +++ b/openpgp/src/crypto/ecdh.rs @@ -26,6 +26,7 @@ pub(crate) use crate::crypto::backend::ecdh::{encrypt, decrypt}; pub(crate) fn default_ecdh_kdf_hash(curve: &Curve) -> HashAlgorithm { match curve { Curve::Cv25519 => HashAlgorithm::SHA256, + Curve::Cv448 => HashAlgorithm::SHA512, // From RFC6637: Curve::NistP256 => HashAlgorithm::SHA256, Curve::NistP384 => HashAlgorithm::SHA384, @@ -35,6 +36,7 @@ pub(crate) fn default_ecdh_kdf_hash(curve: &Curve) -> HashAlgorithm { Curve::BrainpoolP512 => HashAlgorithm::SHA512, // Conservative default. Curve::Ed25519 // Odd: Not an encryption algo. + | Curve::Ed448 // Odd: Not an encryption algo. | Curve::Unknown(_) => HashAlgorithm::SHA512, } } @@ -43,6 +45,7 @@ pub(crate) fn default_ecdh_kdf_hash(curve: &Curve) -> HashAlgorithm { pub(crate) fn default_ecdh_kek_cipher(curve: &Curve) -> SymmetricAlgorithm { match curve { Curve::Cv25519 => SymmetricAlgorithm::AES128, + Curve::Cv448 => SymmetricAlgorithm::AES256, // From RFC6637: Curve::NistP256 => SymmetricAlgorithm::AES128, Curve::NistP384 => SymmetricAlgorithm::AES192, @@ -52,6 +55,7 @@ pub(crate) fn default_ecdh_kek_cipher(curve: &Curve) -> SymmetricAlgorithm { Curve::BrainpoolP512 => SymmetricAlgorithm::AES256, // Conservative default. Curve::Ed25519 // Odd: Not an encryption algo. + | Curve::Ed448 // Odd: Not an encryption algo. | Curve::Unknown(_) => SymmetricAlgorithm::AES256, } } diff --git a/openpgp/src/crypto/mpi.rs b/openpgp/src/crypto/mpi.rs index e21ba98a..26625474 100644 --- a/openpgp/src/crypto/mpi.rs +++ b/openpgp/src/crypto/mpi.rs @@ -71,6 +71,11 @@ impl MPI { } } + /// Creates new MPI encoding a native octet string. + pub fn new_octet_string<B: AsRef<[u8]>>(bytes: B) -> Self { + Self::new_octet_string_common(bytes.as_ref()).into() + } + /// Creates new MPI encoding an uncompressed EC point. /// /// Encodes the given point on a elliptic curve (see [Section 6 of @@ -106,11 +111,11 @@ impl MPI { /// /// [Section 13.2 of RFC4880bis]: https://tools.ietf.org/html/draft-ietf-openpgp-rfc4880bis-09#section-13.2 pub fn new_compressed_point(x: &[u8]) -> Self { - Self::new_compressed_point_common(x).into() + Self::new_octet_string_common(x).into() } /// Common implementation shared between MPI and ProtectedMPI. - fn new_compressed_point_common(x: &[u8]) -> Vec<u8> { + fn new_octet_string_common(x: &[u8]) -> Vec<u8> { let mut val = vec![0; 1 + x.len()]; val[0] = 0x40; val[1..].copy_from_slice(x); @@ -153,6 +158,36 @@ impl MPI { crate::crypto::pad(self.value(), to) } + /// Decodes a native octet string encoded as MPI. + /// + /// Decodes the MPI into a native octet string for use with ECC + /// algorithms. + /// + /// # Errors + /// + /// `Error::MalformedMPI` if the point is formatted incorrectly or + /// the length doesn't match the expected length. + pub fn decode_octet_string(&self, expected_size: usize) -> Result<&[u8]> { + Self::decode_octet_string_common(self.value(), expected_size) + } + + /// Common implementation shared between MPI and ProtectedMPI. + fn decode_octet_string_common(value: &[u8], expected_size: usize) + -> Result<&[u8]> { + if value.len() != 1 + expected_size { + return Err(Error::MalformedMPI( + format!("Bad size of octet string: {} expected: {}", + value.len(), 1 + expected_size)).into()); + } + + if value.get(0).map(|&b| b != 0x40).unwrap_or(true) { + return Err(Error::MalformedMPI( + "Bad encoding of octet string".into()).into()); + } + + Ok(&value[1..]) + } + /// Decodes an EC point encoded as MPI. /// /// Decodes the MPI into a point on an elliptic curve (see @@ -201,6 +236,10 @@ impl MPI { Ok((&value[1..], &[])) }, + Ed448 | Cv448 => + Err(Error::InvalidOperation( + "Native octet string is not a point".into()).into()), + NistP256 | NistP384 | NistP521 @@ -372,6 +411,11 @@ impl std::hash::Hash for ProtectedMPI { } impl ProtectedMPI { + /// Creates new MPI encoding a native octet string. + pub fn new_octet_string<B: AsRef<[u8]>>(bytes: B) -> Self { + MPI::new_octet_string_common(bytes.as_ref()).into() + } + /// Creates new MPI encoding an uncompressed EC point. /// /// Encodes the given point on a elliptic curve (see [Section 6 of @@ -394,7 +438,7 @@ impl ProtectedMPI { /// /// [Section 13.2 of RFC4880bis]: https://tools.ietf.org/html/draft-ietf-openpgp-rfc4880bis-09#section-13.2 pub fn new_compressed_point(x: &[u8]) -> Self { - MPI::new_compressed_point_common(x).into() + MPI::new_octet_string_common(x).into() } /// Returns the length of the MPI in bits. @@ -428,6 +472,19 @@ impl ProtectedMPI { v } + /// Decodes a native octet string encoded as MPI. + /// + /// Decodes the MPI into a native octet string for use with ECC + /// algorithms. + /// + /// # Errors + /// + /// `Error::MalformedMPI` if the point is formatted incorrectly or + /// the length doesn't match the expected length. + pub fn decode_octet_string(&self, expected_size: usize) -> Result<&[u8]> { + MPI::decode_octet_string_common(self.value(), expected_size) + } + /// Decodes an EC point encoded as MPI. /// /// Decodes the MPI into a point on an elliptic curve (see |