use {ec, error, rand};
use untrusted;
pub use ec::PUBLIC_KEY_MAX_LEN;
pub use ec::suite_b::ecdh::{ECDH_P256, ECDH_P384};
pub use ec::curve25519::x25519::X25519;
pub struct Algorithm {
#[doc(hidden)]
pub i: ec::AgreementAlgorithmImpl,
}
pub struct EphemeralPrivateKey {
private_key: ec::PrivateKey,
alg: &'static Algorithm,
}
impl<'a> EphemeralPrivateKey {
pub fn generate(alg: &'static Algorithm, rng: &rand::SecureRandom)
-> Result<EphemeralPrivateKey, error::Unspecified> {
Ok(EphemeralPrivateKey {
private_key: try!(ec::PrivateKey::generate(&alg.i.curve, rng)),
alg: alg,
})
}
#[cfg(test)]
pub fn from_test_vector(alg: &'static Algorithm, test_vector: &[u8])
-> EphemeralPrivateKey {
EphemeralPrivateKey {
private_key:
ec::PrivateKey::from_test_vector(&alg.i.curve, test_vector),
alg: alg,
}
}
#[inline]
pub fn algorithm(&self) -> &'static Algorithm { self.alg }
#[inline(always)]
pub fn public_key_len(&self) -> usize { self.alg.i.curve.public_key_len }
#[inline(always)]
pub fn compute_public_key(&self, out: &mut [u8])
-> Result<(), error::Unspecified> {
self.private_key.compute_public_key(&self.alg.i.curve, out)
}
#[cfg(test)]
pub fn bytes(&'a self) -> &'a [u8] { self.private_key.bytes() }
}
pub fn agree_ephemeral<F, R, E>(my_private_key: EphemeralPrivateKey,
peer_public_key_alg: &Algorithm,
peer_public_key: untrusted::Input,
error_value: E, kdf: F) -> Result<R, E>
where F: FnOnce(&[u8]) -> Result<R, E> {
if peer_public_key_alg.i.curve.id != my_private_key.alg.i.curve.id {
return Err(error_value);
}
let alg = &my_private_key.alg.i;
let mut shared_key = [0u8; ec::ELEM_MAX_BYTES];
let shared_key =
&mut shared_key[..alg.curve.elem_and_scalar_len];
try!((alg.ecdh)(shared_key, &my_private_key.private_key, peer_public_key)
.map_err(|_| error_value));
kdf(shared_key)
}
#[cfg(test)]
mod tests {
use {test, rand};
use untrusted;
use super::*;
#[test]
fn test_agreement_agree_ephemeral() {
let rng = rand::SystemRandom::new();
test::from_file("src/ec/ecdh_tests.txt", |section, test_case| {
assert_eq!(section, "");
let curve_name = test_case.consume_string("Curve");
let alg = alg_from_curve_name(&curve_name);
let peer_public = test_case.consume_bytes("PeerQ");
let peer_public = untrusted::Input::from(&peer_public);
match test_case.consume_optional_string("Error") {
None => {
let my_private = test_case.consume_bytes("D");
let my_public = test_case.consume_bytes("MyQ");
let output = test_case.consume_bytes("Output");
let private_key =
EphemeralPrivateKey::from_test_vector(alg, &my_private);
let mut computed_public = [0u8; PUBLIC_KEY_MAX_LEN];
let computed_public =
&mut computed_public[..private_key.public_key_len()];
assert!(
private_key.compute_public_key(computed_public).is_ok());
assert_eq!(computed_public, &my_public[..]);
assert!(agree_ephemeral(private_key, alg, peer_public, (),
|key_material| {
assert_eq!(key_material, &output[..]);
Ok(())
}).is_ok());
},
Some(_) => {
let dummy_private_key =
try!(EphemeralPrivateKey::generate(alg, &rng));
fn kdf_not_called(_: &[u8]) -> Result<(), ()> {
panic!("The KDF was called during ECDH when the peer's \
public key is invalid.");
}
assert!(
agree_ephemeral(dummy_private_key, alg, peer_public,
(), kdf_not_called).is_err());
}
}
return Ok(());
});
}
fn alg_from_curve_name(curve_name: &str) -> &'static Algorithm {
if curve_name == "P-256" {
&ECDH_P256
} else if curve_name == "P-384" {
&ECDH_P384
} else if curve_name == "X25519" {
&X25519
} else {
panic!("Unsupported curve: {}", curve_name);
}
}
}