#[cfg(test)]
mod tests {
use super::super::curves::{
BrainpoolP256r1, BrainpoolP384r1, BrainpoolP512r1, CryptoRng, Curve, P256, P384, PublicKey, Secp256k1,
SecretKey,
};
struct TestRng {
state: u64,
}
impl TestRng {
fn new(seed: u64) -> Self {
Self {
state: if seed == 0 { 0xdeadbeefcafef00d } else { seed },
}
}
}
impl CryptoRng for TestRng {
fn fill_bytes(&mut self, dest: &mut [u8]) {
for chunk in dest.chunks_mut(8) {
let mut x = self.state;
x ^= x << 13;
x ^= x >> 7;
x ^= x << 17;
self.state = x;
for (i, b) in chunk.iter_mut().enumerate() {
*b = (x >> (8 * i)) as u8;
}
}
}
}
fn roundtrip<C: Curve>() {
let mut rng_a = TestRng::new(0xA11CE);
let mut rng_b = TestRng::new(0xB0B);
let (pk_a, sk_a) = C::keygen(&mut rng_a);
let (pk_b, sk_b) = C::keygen(&mut rng_b);
let s_ab = C::ecdh(&sk_a, &pk_b).expect("alice ecdh ok");
let s_ba = C::ecdh(&sk_b, &pk_a).expect("bob ecdh ok");
assert_eq!(s_ab, s_ba, "ECDH round-trip mismatch");
assert!(s_ab.iter().any(|&b| b != 0), "shared secret is all-zero");
}
#[test]
fn ecdh_p256_roundtrip() {
roundtrip::<P256>();
}
#[test]
fn ecdh_p384_roundtrip() {
roundtrip::<P384>();
}
#[test]
fn ecdh_secp256k1_roundtrip() {
roundtrip::<Secp256k1>();
}
#[test]
fn ecdh_brainpoolp256r1_roundtrip() {
roundtrip::<BrainpoolP256r1>();
}
#[test]
fn ecdh_brainpoolp384r1_roundtrip() {
roundtrip::<BrainpoolP384r1>();
}
#[test]
fn ecdh_brainpoolp512r1_roundtrip() {
roundtrip::<BrainpoolP512r1>();
}
#[test]
fn ecdh_shared_secret_widths() {
let mut rng = TestRng::new(1);
let (pk_a, _sk_a) = P256::keygen(&mut rng);
let (_pk_b, sk_b) = P256::keygen(&mut rng);
let s = P256::ecdh(&sk_b, &pk_a).unwrap();
assert_eq!(s.len(), 32);
let (pk_a, _sk_a) = P384::keygen(&mut rng);
let (_pk_b, sk_b) = P384::keygen(&mut rng);
let s = P384::ecdh(&sk_b, &pk_a).unwrap();
assert_eq!(s.len(), 48);
let (pk_a, _sk_a) = BrainpoolP512r1::keygen(&mut rng);
let (_pk_b, sk_b) = BrainpoolP512r1::keygen(&mut rng);
let s = BrainpoolP512r1::ecdh(&sk_b, &pk_a).unwrap();
assert_eq!(s.len(), 64);
}
#[test]
fn ecdh_and_ecdsa_share_keys() {
use crate::hash::sha256::Sha256;
let mut rng = TestRng::new(2);
let (alice_pk, alice_sk) = P256::keygen(&mut rng);
let (bob_pk, bob_sk) = P256::keygen(&mut rng);
let shared_a = P256::ecdh(&alice_sk, &bob_pk).expect("alice ecdh");
let shared_b = P256::ecdh(&bob_sk, &alice_pk).expect("bob ecdh");
assert_eq!(shared_a, shared_b);
let msg = b"keys are the same";
let sig = P256::sign_rfc6979_msg::<Sha256>(&alice_sk, msg);
assert!(P256::verify_msg::<Sha256>(&alice_pk, msg, &sig));
}
#[test]
fn ecdh_rejects_wrong_length_pubkey() {
let mut rng = TestRng::new(7);
let (_pk, sk) = P256::keygen(&mut rng);
let bad = PublicKey { bytes: vec![0x04; 64] }; assert!(P256::ecdh(&sk, &bad).is_none());
}
#[test]
fn ecdh_rejects_wrong_tag_pubkey() {
let mut rng = TestRng::new(8);
let (mut pk, sk) = P256::keygen(&mut rng);
pk.bytes[0] = 0x02; assert!(P256::ecdh(&sk, &pk).is_none());
}
#[test]
fn ecdh_rejects_off_curve_pubkey() {
let mut rng = TestRng::new(9);
let (mut pk, sk) = P256::keygen(&mut rng);
let len = pk.bytes.len();
pk.bytes[len - 1] ^= 0x01;
assert!(
P256::ecdh(&sk, &pk).is_none(),
"off-curve point must be rejected by ECDH"
);
}
#[test]
fn ecdh_rejects_infinity_pubkey() {
let mut rng = TestRng::new(10);
let (_pk, sk) = P256::keygen(&mut rng);
let mut bytes = vec![0u8; 65];
bytes[0] = 0x04;
let pk = PublicKey { bytes };
assert!(P256::ecdh(&sk, &pk).is_none());
}
#[test]
fn ecdh_rejects_zero_secret() {
let mut rng = TestRng::new(11);
let (pk, _sk_real) = P256::keygen(&mut rng);
let sk_zero = SecretKey { bytes: vec![0u8; 32] };
assert!(P256::ecdh(&sk_zero, &pk).is_none());
}
}