diff options
author | Igor Matuszewski <igor@sequoia-pgp.org> | 2020-06-17 00:57:53 +0200 |
---|---|---|
committer | Igor Matuszewski <xanewok@gmail.com> | 2020-07-29 15:24:51 +0000 |
commit | 5d7b4f63acb7c21e5837f22b926fda48d2e02796 (patch) | |
tree | 197315182d130aca239993e6a5a1ec3dcd128426 | |
parent | 8a44967d2d10ad293f7c0743e6eb46619be494fd (diff) |
openpgp: Move some ECDH helper functions to backend-agnostic module
-rw-r--r-- | openpgp/src/crypto/backend/nettle/ecdh.rs | 130 | ||||
-rw-r--r-- | openpgp/src/crypto/ecdh.rs | 133 |
2 files changed, 133 insertions, 130 deletions
diff --git a/openpgp/src/crypto/backend/nettle/ecdh.rs b/openpgp/src/crypto/backend/nettle/ecdh.rs index f3f820b5..d6062789 100644 --- a/openpgp/src/crypto/backend/nettle/ecdh.rs +++ b/openpgp/src/crypto/backend/nettle/ecdh.rs @@ -4,11 +4,11 @@ use nettle::{curve25519, ecc, ecdh, random::Yarrow}; use crate::{Error, Result}; use crate::crypto::SessionKey; -use crate::crypto::ecdh::{aes_key_wrap, aes_key_unwrap, kdf, pkcs5_pad, pkcs5_unpad}; +use crate::crypto::ecdh::{encrypt_shared, decrypt_shared}; use crate::crypto::mem::Protected; use crate::crypto::mpi::{MPI, PublicKey, SecretKeyMaterial, Ciphertext}; use crate::packet::{key, Key}; -use crate::types::{Curve, HashAlgorithm, SymmetricAlgorithm, PublicKeyAlgorithm}; +use crate::types::Curve; /// Wraps a session key using Elliptic Curve Diffie-Hellman. #[allow(non_snake_case)] @@ -118,54 +118,6 @@ pub fn encrypt<R>(recipient: &Key<key::PublicParts, R>, } } -/// Wraps a session key. -/// -/// After using Elliptic Curve Diffie-Hellman to compute a shared -/// secret, this function deterministically encrypts the given session -/// key. -/// -/// `VB` is the ephemeral public key (with 0x40 prefix), `S` is the -/// shared Diffie-Hellman secret. -#[allow(non_snake_case)] -pub fn encrypt_shared<R>(recipient: &Key<key::PublicParts, R>, - session_key: &SessionKey, VB: MPI, - S: &Protected) - -> Result<Ciphertext> - where R: key::KeyRole -{ - match recipient.mpis() { - &PublicKey::ECDH{ ref curve, ref hash, ref sym,.. } => { - // m = sym_alg_ID || session key || checksum || pkcs5_padding; - let mut m = Vec::with_capacity(40); - m.extend_from_slice(session_key); - let m = pkcs5_pad(m.into(), 40)?; - // Note: We always pad up to 40 bytes to obfuscate the - // length of the symmetric key. - - // Compute KDF input. - let param = make_param(recipient, curve, hash, sym); - - // Z_len = the key size for the KEK_alg_ID used with AESKeyWrap - // Compute Z = KDF( S, Z_len, Param ); - #[allow(non_snake_case)] - let Z = kdf(S, sym.key_size()?, *hash, ¶m)?; - - // Compute C = AESKeyWrap( Z, m ) as per [RFC3394] - #[allow(non_snake_case)] - let C = aes_key_wrap(*sym, &Z, &m)?; - - // Output (MPI(VB) || len(C) || C). - Ok(Ciphertext::ECDH { - e: VB, - key: C.into_boxed_slice(), - }) - } - - _ => - Err(Error::InvalidArgument("Expected an ECDHPublicKey".into()).into()), - } -} - /// Unwraps a session key using Elliptic Curve Diffie-Hellman. #[allow(non_snake_case)] pub fn decrypt<R>(recipient: &Key<key::PublicParts, R>, @@ -273,81 +225,3 @@ pub fn decrypt<R>(recipient: &Key<key::PublicParts, R>, Err(Error::InvalidArgument("Expected an ECDHPublicKey".into()).into()), } } - -/// Unwraps a session key. -/// -/// After using Elliptic Curve Diffie-Hellman to compute the shared -/// secret, this function decrypts the given encrypted session key. -/// -/// `recipient` is the message receiver's public key, `S` is the -/// shared Diffie-Hellman secret used to encrypt `ciphertext`. -#[allow(non_snake_case)] -pub fn decrypt_shared<R>(recipient: &Key<key::PublicParts, R>, - S: &Protected, - ciphertext: &Ciphertext) - -> Result<SessionKey> - where R: key::KeyRole -{ - match (recipient.mpis(), ciphertext) { - (PublicKey::ECDH { ref curve, ref hash, ref sym, ..}, - Ciphertext::ECDH { ref key, .. }) => { - // Compute KDF input. - let param = make_param(recipient, curve, hash, sym); - - // Z_len = the key size for the KEK_alg_ID used with AESKeyWrap - // Compute Z = KDF( S, Z_len, Param ); - #[allow(non_snake_case)] - let Z = kdf(&S, sym.key_size()?, *hash, ¶m)?; - - // Compute m = AESKeyUnwrap( Z, C ) as per [RFC3394] - let m = aes_key_unwrap(*sym, &Z, key)?; - let cipher = SymmetricAlgorithm::from(m[0]); - let m = pkcs5_unpad(m, 1 + cipher.key_size()? + 2)?; - - Ok(m.into()) - }, - - _ => - Err(Error::InvalidArgument( - "Expected an ECDH key and ciphertext".into()).into()), - } -} - -fn make_param<P, R>(recipient: &Key<P, R>, - curve: &Curve, hash: &HashAlgorithm, - sym: &SymmetricAlgorithm) - -> Vec<u8> - where P: key::KeyParts, - R: key::KeyRole -{ - // Param = curve_OID_len || curve_OID || - // public_key_alg_ID || 03 || 01 || KDF_hash_ID || - // KEK_alg_ID for AESKeyWrap || "Anonymous Sender " || - // recipient_fingerprint; - let fp = recipient.fingerprint(); - - let mut param = Vec::with_capacity( - 1 + curve.oid().len() // Length and Curve OID, - + 1 // Public key algorithm ID, - + 4 // KDF parameters, - + 20 // "Anonymous Sender ", - + fp.as_bytes().len()); // Recipients key fingerprint. - - param.push(curve.oid().len() as u8); - param.extend_from_slice(curve.oid()); - param.push(PublicKeyAlgorithm::ECDH.into()); - param.push(3); - param.push(1); - param.push((*hash).into()); - param.push((*sym).into()); - param.extend_from_slice(b"Anonymous Sender "); - param.extend_from_slice(fp.as_bytes()); - assert_eq!(param.len(), - 1 + curve.oid().len() // Length and Curve OID, - + 1 // Public key algorithm ID, - + 4 // KDF parameters, - + 20 // "Anonymous Sender ", - + fp.as_bytes().len()); // Recipients key fingerprint. - - param -} diff --git a/openpgp/src/crypto/ecdh.rs b/openpgp/src/crypto/ecdh.rs index 5f815afd..e4ae971e 100644 --- a/openpgp/src/crypto/ecdh.rs +++ b/openpgp/src/crypto/ecdh.rs @@ -3,12 +3,102 @@ use crate::vec_truncate; use crate::{Error, Result}; +use crate::crypto::SessionKey; use crate::crypto::mem::Protected; -use crate::types::{SymmetricAlgorithm, HashAlgorithm}; +use crate::crypto::mpi::{self, MPI}; +use crate::key; +use crate::packet::Key; +use crate::types::{Curve, HashAlgorithm, PublicKeyAlgorithm, SymmetricAlgorithm}; use crate::utils::{read_be_u64, write_be_u64}; pub use crate::crypto::backend::ecdh::{encrypt, decrypt}; -pub use crate::crypto::backend::ecdh::{encrypt_shared, decrypt_shared}; + +/// Wraps a session key. +/// +/// After using Elliptic Curve Diffie-Hellman to compute a shared +/// secret, this function deterministically encrypts the given session +/// key. +/// +/// `VB` is the ephemeral public key (with 0x40 prefix), `S` is the +/// shared Diffie-Hellman secret. +#[allow(non_snake_case)] +pub fn encrypt_shared<R>(recipient: &Key<key::PublicParts, R>, + session_key: &SessionKey, VB: MPI, + S: &Protected) + -> Result<mpi::Ciphertext> + where R: key::KeyRole +{ + match recipient.mpis() { + &mpi::PublicKey::ECDH { ref curve, ref hash, ref sym,.. } => { + // m = sym_alg_ID || session key || checksum || pkcs5_padding; + let mut m = Vec::with_capacity(40); + m.extend_from_slice(session_key); + let m = pkcs5_pad(m.into(), 40)?; + // Note: We always pad up to 40 bytes to obfuscate the + // length of the symmetric key. + + // Compute KDF input. + let param = make_param(recipient, curve, hash, sym); + + // Z_len = the key size for the KEK_alg_ID used with AESKeyWrap + // Compute Z = KDF( S, Z_len, Param ); + #[allow(non_snake_case)] + let Z = kdf(S, sym.key_size()?, *hash, ¶m)?; + + // Compute C = AESKeyWrap( Z, m ) as per [RFC3394] + #[allow(non_snake_case)] + let C = aes_key_wrap(*sym, &Z, &m)?; + + // Output (MPI(VB) || len(C) || C). + Ok(mpi::Ciphertext::ECDH { + e: VB, + key: C.into_boxed_slice(), + }) + } + + _ => + Err(Error::InvalidArgument("Expected an ECDHPublicKey".into()).into()), + } +} + +/// Unwraps a session key. +/// +/// After using Elliptic Curve Diffie-Hellman to compute the shared +/// secret, this function decrypts the given encrypted session key. +/// +/// `recipient` is the message receiver's public key, `S` is the +/// shared Diffie-Hellman secret used to encrypt `ciphertext`. +#[allow(non_snake_case)] +pub fn decrypt_shared<R>(recipient: &Key<key::PublicParts, R>, + S: &Protected, + ciphertext: &mpi::Ciphertext) + -> Result<SessionKey> + where R: key::KeyRole +{ + match (recipient.mpis(), ciphertext) { + (mpi::PublicKey::ECDH { ref curve, ref hash, ref sym, ..}, + mpi::Ciphertext::ECDH { ref key, .. }) => { + // Compute KDF input. + let param = make_param(recipient, curve, hash, sym); + + // Z_len = the key size for the KEK_alg_ID used with AESKeyWrap + // Compute Z = KDF( S, Z_len, Param ); + #[allow(non_snake_case)] + let Z = kdf(&S, sym.key_size()?, *hash, ¶m)?; + + // Compute m = AESKeyUnwrap( Z, C ) as per [RFC3394] + let m = aes_key_unwrap(*sym, &Z, key)?; + let cipher = SymmetricAlgorithm::from(m[0]); + let m = pkcs5_unpad(m, 1 + cipher.key_size()? + 2)?; + + Ok(m.into()) + }, + + _ => + Err(Error::InvalidArgument( + "Expected an ECDH key and ciphertext".into()).into()), + } +} /// Derives a secret key for session key wrapping. /// @@ -255,6 +345,45 @@ pub fn aes_key_unwrap(algo: SymmetricAlgorithm, key: &Protected, } } +fn make_param<P, R>(recipient: &Key<P, R>, + curve: &Curve, hash: &HashAlgorithm, + sym: &SymmetricAlgorithm) + -> Vec<u8> + where P: key::KeyParts, + R: key::KeyRole +{ + // Param = curve_OID_len || curve_OID || + // public_key_alg_ID || 03 || 01 || KDF_hash_ID || + // KEK_alg_ID for AESKeyWrap || "Anonymous Sender " || + // recipient_fingerprint; + let fp = recipient.fingerprint(); + + let mut param = Vec::with_capacity( + 1 + curve.oid().len() // Length and Curve OID, + + 1 // Public key algorithm ID, + + 4 // KDF parameters, + + 20 // "Anonymous Sender ", + + fp.as_bytes().len()); // Recipients key fingerprint. + + param.push(curve.oid().len() as u8); + param.extend_from_slice(curve.oid()); + param.push(PublicKeyAlgorithm::ECDH.into()); + param.push(3); + param.push(1); + param.push((*hash).into()); + param.push((*sym).into()); + param.extend_from_slice(b"Anonymous Sender "); + param.extend_from_slice(fp.as_bytes()); + assert_eq!(param.len(), + 1 + curve.oid().len() // Length and Curve OID, + + 1 // Public key algorithm ID, + + 4 // KDF parameters, + + 20 // "Anonymous Sender ", + + fp.as_bytes().len()); // Recipients key fingerprint. + + param +} + const AES_KEY_WRAP_IV: u64 = 0xa6a6a6a6a6a6a6a6; #[cfg(test)] |