use {agreement, ec, error, rand};
use super::ops::*;
use super::private_key::*;
use super::public_key::*;
use untrusted;
macro_rules! ecdh {
( $NAME:ident, $bits:expr, $name_str:expr, $private_key_ops:expr,
$public_key_ops:expr, $nid:expr, $ecdh:ident,
$generate_private_key:ident, $public_from_private:ident) =>
{
#[doc="ECDH using the NSA Suite B"]
#[doc=$name_str]
#[doc="curve."]
pub static $NAME: agreement::Algorithm = agreement::Algorithm {
i: ec::AgreementAlgorithmImpl {
public_key_len: 1 + (2 * (($bits + 7) / 8)),
elem_and_scalar_len: ($bits + 7) / 8,
nid: $nid,
generate_private_key: $generate_private_key,
public_from_private: $public_from_private,
ecdh: $ecdh,
},
};
fn $ecdh(out: &mut [u8], my_private_key: &ec::PrivateKey,
peer_public_key: untrusted::Input)
-> Result<(), error::Unspecified> {
ecdh($private_key_ops, $public_key_ops, out, my_private_key,
peer_public_key)
}
fn $generate_private_key(rng: &rand::SecureRandom)
-> Result<ec::PrivateKey, error::Unspecified> {
generate_private_key($private_key_ops, rng)
}
fn $public_from_private(public_out: &mut [u8],
private_key: &ec::PrivateKey)
-> Result<(), error::Unspecified> {
public_from_private($private_key_ops, public_out, private_key)
}
}
}
ecdh!(ECDH_P256, 256, "P-256 (secp256r1)", &p256::PRIVATE_KEY_OPS,
&p256::PUBLIC_KEY_OPS, 415 , p256_ecdh,
p256_generate_private_key, p256_public_from_private);
ecdh!(ECDH_P384, 384, "P-384 (secp384r1)", &p384::PRIVATE_KEY_OPS,
&p384::PUBLIC_KEY_OPS, 715 , p384_ecdh,
p384_generate_private_key, p384_public_from_private);
fn ecdh(private_key_ops: &PrivateKeyOps, public_key_ops: &PublicKeyOps,
out: &mut [u8], my_private_key: &ec::PrivateKey,
peer_public_key: untrusted::Input) -> Result<(), error::Unspecified> {
let peer_public_key = try!(parse_uncompressed_point(public_key_ops,
peer_public_key));
let my_private_key = private_key_as_scalar(private_key_ops, my_private_key);
let product = private_key_ops.point_mul(&my_private_key, &peer_public_key);
big_endian_affine_from_jacobian(private_key_ops, Some(out), None, &product)
}
#[cfg(test)]
mod tests {
use core;
use {agreement, ec, test};
use super::super::{ops, private_key};
static SUPPORTED_SUITE_B_ALGS:
[(&'static str, &'static agreement::Algorithm,
&'static ops::CommonOps); 2] = [
("P-256", &agreement::ECDH_P256, &ops::p256::COMMON_OPS),
("P-384", &agreement::ECDH_P384, &ops::p384::COMMON_OPS),
];
#[test]
fn test_agreement_suite_b_ecdh_generate() {
let random_00 = test::rand::FixedByteRandom { byte: 0x00 };
let random_ff = test::rand::FixedByteRandom { byte: 0xff };
for &(_, alg, ops) in SUPPORTED_SUITE_B_ALGS.iter() {
assert!(agreement::EphemeralPrivateKey::generate(alg, &random_00)
.is_err());
assert!(agreement::EphemeralPrivateKey::generate(alg, &random_ff)
.is_err());
let mut n_bytes = [0u8; ec::SCALAR_MAX_BYTES];
let num_bytes = ops.num_limbs * ops::LIMB_BYTES;
private_key::test_util::big_endian_from_limbs(
&mut n_bytes[..num_bytes], &ops.n.limbs[..ops.num_limbs]);
{
let n_bytes = &mut n_bytes[..num_bytes];
let rng = test::rand::FixedSliceRandom { bytes: n_bytes };
assert!(agreement::EphemeralPrivateKey::generate(alg, &rng)
.is_err());
}
let mut n_minus_1_bytes = n_bytes;
{
let n_minus_1_bytes = &mut n_minus_1_bytes[..num_bytes];
n_minus_1_bytes[num_bytes - 1] -= 1;
let rng = test::rand::FixedSliceRandom {
bytes: n_minus_1_bytes
};
let key = agreement::EphemeralPrivateKey::generate(alg, &rng)
.unwrap();
assert_eq!(&n_minus_1_bytes[..], &key.bytes()[..num_bytes]);
}
let mut n_plus_1_bytes = n_bytes;
{
let n_plus_1_bytes = &mut n_plus_1_bytes[..num_bytes];
n_plus_1_bytes[num_bytes - 1] += 1;
let rng = test::rand::FixedSliceRandom {
bytes: n_plus_1_bytes
};
assert!(agreement::EphemeralPrivateKey::generate(alg, &rng)
.is_err());
}
{
let bytes = [
&n_bytes[..num_bytes],
&n_plus_1_bytes[..num_bytes],
&[0u8; ec::SCALAR_MAX_BYTES][..num_bytes],
&n_minus_1_bytes[..num_bytes],
];
let rng = test::rand::FixedSliceSequenceRandom {
bytes: &bytes,
current: core::cell::UnsafeCell::new(0),
};
let key = agreement::EphemeralPrivateKey::generate(alg, &rng)
.unwrap();
assert_eq!(&n_minus_1_bytes[..num_bytes],
&key.bytes()[..num_bytes]);
}
}
}
}