//! Elliptic Curve Diffie-Hellman.
use nettle::{cipher, curve25519, mode, mode::Mode, ecc, ecdh, random::Yarrow};
use crate::{Error, Result};
use crate::crypto::SessionKey;
use crate::crypto::ecdh::{kdf, pkcs5_pad, pkcs5_unpad};
use crate::crypto::mem::Protected;
use crate::crypto::mpi::{MPI, PublicKey, SecretKeyMaterial, Ciphertext};
use crate::packet::{key, Key};
use crate::utils::{write_be_u64, read_be_u64};
use crate::types::{Curve, HashAlgorithm, SymmetricAlgorithm, PublicKeyAlgorithm};
/// Wraps a session key using Elliptic Curve Diffie-Hellman.
#[allow(non_snake_case)]
pub fn encrypt<R>(recipient: &Key<key::PublicParts, R>,
session_key: &SessionKey)
-> Result<Ciphertext>
where R: key::KeyRole
{
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 v: Protected =
curve25519::private_key(&mut rng).into();
// 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: Protected =
vec![0; curve25519::CURVE25519_SIZE].into();
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.
// Note: ecc::Point and ecc::Scalar are cleaned up by
// nettle.
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)?;
// Get the X coordinate, safely dispose of Y.
let (Sx, Sy) = S.as_bytes();
Protected::from(Sy); // Just a precaution.
// Zero-pad to the size of the underlying field,
// rounded to the next byte.
let mut Sx