summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorIgor Matuszewski <igor@sequoia-pgp.org>2020-06-17 00:57:53 +0200
committerIgor Matuszewski <xanewok@gmail.com>2020-07-29 15:24:51 +0000
commit5d7b4f63acb7c21e5837f22b926fda48d2e02796 (patch)
tree197315182d130aca239993e6a5a1ec3dcd128426
parent8a44967d2d10ad293f7c0743e6eb46619be494fd (diff)
openpgp: Move some ECDH helper functions to backend-agnostic module
-rw-r--r--openpgp/src/crypto/backend/nettle/ecdh.rs130
-rw-r--r--openpgp/src/crypto/ecdh.rs133
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, &param)?;
-
- // 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, &param)?;
-
- // 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, &param)?;
+
+ // 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, &param)?;
+
+ // 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)]