summaryrefslogtreecommitdiffstats
path: root/openpgp/src/crypto/ecdh.rs
diff options
context:
space:
mode:
Diffstat (limited to 'openpgp/src/crypto/ecdh.rs')
-rw-r--r--openpgp/src/crypto/ecdh.rs133
1 files changed, 131 insertions, 2 deletions
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)]