use aws_lc_rs::agreement::EphemeralPrivateKey;
use aws_lc_rs::agreement::PublicKey;
use aws_lc_rs::agreement::UnparsedPublicKey;
use aws_lc_rs::agreement::X25519;
use aws_lc_rs::agreement::agree_ephemeral;
use aws_lc_rs::rand::SystemRandom;
use crate::crypto::kex::Initiate;
use crate::crypto::kex::Reply;
use crate::types::Error;
use crate::types::Result;
use crate::types::Vec;
pub struct Initiator {
private_key: EphemeralPrivateKey,
public_key: PublicKey,
}
pub struct Replier {}
impl Initiate for Initiator {
fn new() -> Result<Self> {
let rng = SystemRandom::new();
let private_key = EphemeralPrivateKey::generate(&X25519, &rng)?;
let public_key = private_key.compute_public_key()?;
Ok(Self {
private_key,
public_key,
})
}
fn public_key(&self) -> &[u8] {
self.public_key.as_ref()
}
fn agree(self, peer_public_key: &[u8]) -> Result<Vec<u8, 128>> {
let peer_public_key = UnparsedPublicKey::new(&X25519, peer_public_key);
let shared_secret = agree_ephemeral(
self.private_key,
peer_public_key,
Error::Crypto,
|key_material| Ok(make_shared_secret(key_material)),
)?;
Ok(shared_secret)
}
}
impl Reply for Replier {
fn agree(peer_public_key: &[u8]) -> Result<(Vec<u8, 128>, Vec<u8, 2048>)> {
let rng = SystemRandom::new();
let private_key = EphemeralPrivateKey::generate(&X25519, &rng)?;
let public_key = private_key.compute_public_key()?;
let mut public_key_bytes = Vec::new();
public_key_bytes.extend_from_slice(public_key.as_ref());
let peer_public_key = UnparsedPublicKey::new(&X25519, peer_public_key);
let shared_secret = agree_ephemeral(
private_key,
peer_public_key,
Error::Crypto,
|key_material| Ok(make_shared_secret(key_material)),
)?;
Ok((shared_secret, public_key_bytes))
}
}
fn make_shared_secret(key_material: &[u8]) -> Vec<u8, 128> {
let needs_padding = key_material[0] & 0x80 != 0;
let mut shared_secret = Vec::new();
if needs_padding {
let len = (key_material.len() + 1) as u32;
shared_secret.extend_from_slice(&len.to_be_bytes());
shared_secret.push(0x00);
shared_secret.extend_from_slice(key_material);
} else {
let len = key_material.len() as u32;
shared_secret.extend_from_slice(&len.to_be_bytes());
shared_secret.extend_from_slice(key_material);
}
shared_secret
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn kex_works() {
let client = Initiator::new().unwrap();
let client_public_key = client.public_key().to_owned();
let (server_shared_secret, server_public_key) = Replier::agree(&client_public_key).unwrap();
let client_shared_secret = client.agree(&server_public_key).unwrap();
assert_eq!(client_shared_secret.len(), server_shared_secret.len());
assert_eq!(client_shared_secret.as_ref(), server_shared_secret.as_ref());
}
}