summaryrefslogtreecommitdiffstats
path: root/openpgp/src/crypto
diff options
context:
space:
mode:
Diffstat (limited to 'openpgp/src/crypto')
-rw-r--r--openpgp/src/crypto/backend/cng.rs2
-rw-r--r--openpgp/src/crypto/backend/cng/ecdh.rs7
-rw-r--r--openpgp/src/crypto/backend/nettle.rs4
-rw-r--r--openpgp/src/crypto/backend/nettle/asymmetric.rs75
-rw-r--r--openpgp/src/crypto/backend/nettle/ecdh.rs48
-rw-r--r--openpgp/src/crypto/backend/rust.rs2
-rw-r--r--openpgp/src/crypto/backend/rust/ecdh.rs4
-rw-r--r--openpgp/src/crypto/ecdh.rs4
-rw-r--r--openpgp/src/crypto/mpi.rs63
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