//! Elliptic Curve Diffie-Hellman.
use Error;
use packet::Key;
use Result;
use constants::{
Curve,
HashAlgorithm,
SymmetricAlgorithm,
PublicKeyAlgorithm,
};
use conversions::{
write_be_u64,
read_be_u64,
};
use crypto::mpis::{MPI, PublicKey, SecretKey, Ciphertext};
use nettle::{cipher, curve25519, mode, Mode, ecc, ecdh, Yarrow};
/// Wraps a session key using Elliptic Curve Diffie-Hellman.
#[allow(non_snake_case)]
pub fn encrypt(recipient: &Key, session_key: &[u8]) -> Result<Ciphertext>
{
let mut rng = Yarrow::default();
if let &PublicKey::ECDH {
ref curve, ref q,..
} = recipient.mpis() {
match curve {
Curve::Cv25519 => {
// Obtain the authenticated recipient public key R
let R = q.decode_point(curve)?.0;
// Generate an ephemeral key pair {v, V=vG}
let mut v =
::crypto::SessionKey::from(curve25519::private_key(&mut rng));
// Compute the public key. We need to add an encoding
// octet in front of the key.
let mut VB = [0x40; 1 + curve25519::CURVE25519_SIZE];
curve25519::mul_g(&mut VB[1..], &v)
.expect("buffers are of the wrong size");
let VB = MPI::new(&VB);
// Compute the shared point S = vR;
let mut S = [0; curve25519::CURVE25519_SIZE];
curve25519::mul(&mut S, &v, R)
.expect("buffers are of the wrong size");
encrypt_shared(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.
let (Rx, Ry) = q.decode_point(curve)?;
let (R, v, field_sz) = match curve {
Curve::NistP256 => {
let R = ecc::Point::new::<ecc::Secp256r1>(Rx, Ry)?;
let v =
ecc::Scalar::new_random::<ecc::Secp256r1, _>(&mut rng);
let field_sz = 256;
(R, v, field_sz)
}
Curve::NistP384 => {
let R = ecc::Point::new::<ecc::Secp384r1>(Rx, Ry)?;
let v =
ecc::Scalar::new_random::<ecc::Secp384r1, _>(&mut rng);
let field_sz = 384;
(R, v, field_sz)
}
Curve::NistP521 => {
let R = ecc::Point::new::<ecc::Secp521r1>(Rx, Ry)?;
let v =
ecc::Scalar::new_random::<ecc::Secp521r1, _>(&mut rng);
let field_sz = 521;
(R, v, field_sz)
}
_ => unreachable!(),
};
// Compute the public key.
let VB = ecdh::point_mul_g(&v);
let (VBx, VBy) = VB.as_bytes();
let VB = MPI::new_weierstrass(&VBx, &VBy, field_sz);
// Compute the shared point S = vR;
let S = ecdh::point_mul(&v, &R)?;
let (Sx,_) = S.as_bytes();
encrypt_shared(recipient, session_key, VB, &Sx)
}
// Not implemented in Nettle
Curve::BrainpoolP256 | Curve::BrainpoolP512 =>
Err(Error::UnsupportedEllipticCurve(curve.clone()).into()),
// N/A
Curve::Unknown(_) | Curve::Ed25519 =>
Err(Error::UnsupportedEllipticCurve(curve.clone()).into()),
}
} else {
Err(Error::InvalidArgument("Expected an ECDHPublicKey".into()).into())
}
}
/// Wraps a session key.
///
/// After using Elliptic Curve Diffie-Hellman to compute a shared
/// secret, this function deterministically encrypts the given session