use crate::noise::errors::WireGuardError;
use crate::noise::session::Session;
use crate::packet::{Packet, WgCookieReply, WgHandshakeBase, WgHandshakeInit, WgHandshakeResp};
#[cfg(not(feature = "mock_instant"))]
use crate::sleepyinstant::Instant;
use crate::x25519;
use aead::{Aead, Payload};
use blake2::digest::{FixedOutput, KeyInit};
use blake2::{Blake2s256, Blake2sMac, Digest};
use chacha20poly1305::XChaCha20Poly1305;
use constant_time_eq::constant_time_eq_n;
use rand_core::OsRng;
use ring::aead::{Aad, CHACHA20_POLY1305, LessSafeKey, Nonce, UnboundKey};
use std::convert::TryInto;
use std::time::{Duration, SystemTime};
#[cfg(feature = "mock_instant")]
use mock_instant::Instant;
pub(crate) const LABEL_MAC1: &[u8; 8] = b"mac1----";
pub(crate) const LABEL_COOKIE: &[u8; 8] = b"cookie--";
const KEY_LEN: usize = 32;
const TIMESTAMP_LEN: usize = 12;
const INITIAL_CHAIN_KEY: [u8; KEY_LEN] = [
96, 226, 109, 174, 243, 39, 239, 192, 46, 195, 53, 226, 160, 37, 210, 208, 22, 235, 66, 6, 248,
114, 119, 245, 45, 56, 209, 152, 139, 120, 205, 54,
];
const INITIAL_CHAIN_HASH: [u8; KEY_LEN] = [
34, 17, 179, 97, 8, 26, 197, 102, 105, 18, 67, 219, 69, 138, 213, 50, 45, 156, 108, 102, 34,
147, 232, 183, 14, 225, 156, 101, 186, 7, 158, 243,
];
#[inline]
pub(crate) fn b2s_hash(data1: &[u8], data2: &[u8]) -> [u8; 32] {
let mut hash = Blake2s256::new();
hash.update(data1);
hash.update(data2);
hash.finalize().into()
}
#[inline]
pub(crate) fn b2s_hmac(key: &[u8], data1: &[u8]) -> [u8; 32] {
use blake2::digest::Update;
type HmacBlake2s = hmac::SimpleHmac<Blake2s256>;
let mut hmac = HmacBlake2s::new_from_slice(key).unwrap();
hmac.update(data1);
hmac.finalize_fixed().into()
}
#[inline]
pub(crate) fn b2s_hmac2(key: &[u8], data1: &[u8], data2: &[u8]) -> [u8; 32] {
use blake2::digest::Update;
type HmacBlake2s = hmac::SimpleHmac<Blake2s256>;
let mut hmac = HmacBlake2s::new_from_slice(key).unwrap();
hmac.update(data1);
hmac.update(data2);
hmac.finalize_fixed().into()
}
#[inline]
pub(crate) fn b2s_keyed_mac_16(key: &[u8], data1: &[u8]) -> [u8; 16] {
let mut hmac = Blake2sMac::new_from_slice(key).unwrap();
blake2::digest::Update::update(&mut hmac, data1);
hmac.finalize_fixed().into()
}
#[inline]
pub(crate) fn b2s_keyed_mac_16_2(key: &[u8], data1: &[u8], data2: &[u8]) -> [u8; 16] {
let mut hmac = Blake2sMac::new_from_slice(key).unwrap();
blake2::digest::Update::update(&mut hmac, data1);
blake2::digest::Update::update(&mut hmac, data2);
hmac.finalize_fixed().into()
}
pub(crate) fn b2s_mac_24(key: &[u8], data1: &[u8]) -> [u8; 24] {
let mut hmac = Blake2sMac::new_from_slice(key).unwrap();
blake2::digest::Update::update(&mut hmac, data1);
hmac.finalize_fixed().into()
}
#[inline]
fn aead_chacha20_seal(ciphertext: &mut [u8], key: &[u8], counter: u64, data: &[u8], aad: &[u8]) {
let mut nonce: [u8; 12] = [0; 12];
nonce[4..12].copy_from_slice(&counter.to_le_bytes());
aead_chacha20_seal_inner(ciphertext, key, nonce, data, aad)
}
#[inline]
fn aead_chacha20_seal_inner(
ciphertext: &mut [u8],
key: &[u8],
nonce: [u8; 12],
data: &[u8],
aad: &[u8],
) {
let key = LessSafeKey::new(UnboundKey::new(&CHACHA20_POLY1305, key).unwrap());
ciphertext[..data.len()].copy_from_slice(data);
let tag = key
.seal_in_place_separate_tag(
Nonce::assume_unique_for_key(nonce),
Aad::from(aad),
&mut ciphertext[..data.len()],
)
.unwrap();
ciphertext[data.len()..].copy_from_slice(tag.as_ref());
}
#[inline]
fn aead_chacha20_open(
buffer: &mut [u8],
key: &[u8],
counter: u64,
data: &[u8],
aad: &[u8],
) -> Result<(), WireGuardError> {
let mut nonce: [u8; 12] = [0; 12];
nonce[4..].copy_from_slice(&counter.to_le_bytes());
aead_chacha20_open_inner(buffer, key, nonce, data, aad)
.map_err(|_| WireGuardError::InvalidAeadTag)?;
Ok(())
}
#[inline]
fn aead_chacha20_open_inner(
buffer: &mut [u8],
key: &[u8],
nonce: [u8; 12],
data: &[u8],
aad: &[u8],
) -> Result<(), ring::error::Unspecified> {
let key = LessSafeKey::new(UnboundKey::new(&CHACHA20_POLY1305, key).unwrap());
let mut inner_buffer = data.to_owned();
let plaintext = key.open_in_place(
Nonce::assume_unique_for_key(nonce),
Aad::from(aad),
&mut inner_buffer,
)?;
buffer.copy_from_slice(plaintext);
Ok(())
}
#[derive(Debug)]
struct Tai64N {
secs: u64,
nano: u32,
}
#[derive(Debug)]
struct TimeStamper {
duration_at_start: Duration,
instant_at_start: Instant,
}
impl TimeStamper {
pub fn new() -> TimeStamper {
TimeStamper {
duration_at_start: SystemTime::now()
.duration_since(SystemTime::UNIX_EPOCH)
.unwrap(),
instant_at_start: Instant::now(),
}
}
pub fn stamp(&self) -> [u8; 12] {
const TAI64_BASE: u64 = (1u64 << 62) + 37;
let mut ext_stamp = [0u8; 12];
let stamp = Instant::now().duration_since(self.instant_at_start) + self.duration_at_start;
ext_stamp[0..8].copy_from_slice(&(stamp.as_secs() + TAI64_BASE).to_be_bytes());
ext_stamp[8..12].copy_from_slice(&stamp.subsec_nanos().to_be_bytes());
ext_stamp
}
}
impl Tai64N {
fn zero() -> Tai64N {
Tai64N { secs: 0, nano: 0 }
}
fn parse(buf: &[u8; 12]) -> Result<Tai64N, WireGuardError> {
if buf.len() < 12 {
return Err(WireGuardError::InvalidTai64nTimestamp);
}
let (sec_bytes, nano_bytes) = buf.split_at(std::mem::size_of::<u64>());
let secs = u64::from_be_bytes(sec_bytes.try_into().unwrap());
let nano = u32::from_be_bytes(nano_bytes.try_into().unwrap());
Ok(Tai64N { secs, nano })
}
pub fn after(&self, other: &Tai64N) -> bool {
(self.secs > other.secs) || ((self.secs == other.secs) && (self.nano > other.nano))
}
}
struct NoiseParams {
static_public: x25519::PublicKey,
static_private: x25519::StaticSecret,
peer_static_public: x25519::PublicKey,
static_shared: x25519::SharedSecret,
sending_mac1_key: [u8; KEY_LEN],
preshared_key: Option<[u8; KEY_LEN]>,
}
impl std::fmt::Debug for NoiseParams {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("NoiseParams")
.field("static_public", &self.static_public)
.field("static_private", &"<redacted>")
.field("peer_static_public", &self.peer_static_public)
.field("static_shared", &"<redacted>")
.field("sending_mac1_key", &self.sending_mac1_key)
.field("preshared_key", &self.preshared_key)
.finish()
}
}
struct HandshakeInitSentState {
local_index: u32,
hash: [u8; KEY_LEN],
chaining_key: [u8; KEY_LEN],
ephemeral_private: x25519::ReusableSecret,
time_sent: Instant,
}
impl std::fmt::Debug for HandshakeInitSentState {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("HandshakeInitSentState")
.field("local_index", &self.local_index)
.field("hash", &self.hash)
.field("chaining_key", &self.chaining_key)
.field("ephemeral_private", &"<redacted>")
.field("time_sent", &self.time_sent)
.finish()
}
}
#[derive(Debug)]
enum HandshakeState {
None,
InitSent(HandshakeInitSentState),
InitReceived {
hash: [u8; KEY_LEN],
chaining_key: [u8; KEY_LEN],
peer_ephemeral_public: x25519::PublicKey,
peer_index: u32,
},
Expired,
}
pub struct Handshake {
params: NoiseParams,
next_index: u32,
previous: HandshakeState,
state: HandshakeState,
cookies: Cookies,
last_handshake_timestamp: Tai64N,
stamper: TimeStamper,
pub(super) last_rtt: Option<u32>,
}
#[derive(Default)]
struct Cookies {
last_mac1: Option<[u8; 16]>,
index: u32,
write_cookie: Option<[u8; 16]>,
}
#[derive(Debug)]
pub struct HalfHandshake {
pub peer_index: u32,
pub peer_static_public: [u8; 32],
}
pub fn parse_handshake_anon(
static_private: &x25519::StaticSecret,
static_public: &x25519::PublicKey,
packet: &WgHandshakeInit,
) -> Result<HalfHandshake, WireGuardError> {
let peer_index = packet.sender_idx.get();
let mut chaining_key = INITIAL_CHAIN_KEY;
let mut hash = INITIAL_CHAIN_HASH;
hash = b2s_hash(&hash, static_public.as_bytes());
let peer_ephemeral_public = x25519::PublicKey::from(packet.unencrypted_ephemeral);
hash = b2s_hash(&hash, peer_ephemeral_public.as_bytes());
chaining_key = b2s_hmac(
&b2s_hmac(&chaining_key, peer_ephemeral_public.as_bytes()),
&[0x01],
);
let ephemeral_shared = static_private.diffie_hellman(&peer_ephemeral_public);
let temp = b2s_hmac(&chaining_key, &ephemeral_shared.to_bytes());
chaining_key = b2s_hmac(&temp, &[0x01]);
let key = b2s_hmac2(&temp, &chaining_key, &[0x02]);
let mut peer_static_public = [0u8; KEY_LEN];
aead_chacha20_open(
&mut peer_static_public,
&key,
0,
&packet.encrypted_static,
&hash,
)?;
Ok(HalfHandshake {
peer_index,
peer_static_public,
})
}
impl NoiseParams {
fn new(
static_private: x25519::StaticSecret,
static_public: x25519::PublicKey,
peer_static_public: x25519::PublicKey,
preshared_key: Option<[u8; 32]>,
) -> NoiseParams {
let static_shared = static_private.diffie_hellman(&peer_static_public);
let initial_sending_mac_key = b2s_hash(LABEL_MAC1, peer_static_public.as_bytes());
NoiseParams {
static_public,
static_private,
peer_static_public,
static_shared,
sending_mac1_key: initial_sending_mac_key,
preshared_key,
}
}
fn set_static_private(
&mut self,
static_private: x25519::StaticSecret,
static_public: x25519::PublicKey,
) {
let check_key = x25519::PublicKey::from(&static_private);
assert_eq!(check_key.as_bytes(), static_public.as_bytes());
self.static_private = static_private;
self.static_public = static_public;
self.static_shared = self.static_private.diffie_hellman(&self.peer_static_public);
}
}
impl Handshake {
pub(crate) fn new(
static_private: x25519::StaticSecret,
static_public: x25519::PublicKey,
peer_static_public: x25519::PublicKey,
global_idx: u32,
preshared_key: Option<[u8; 32]>,
) -> Handshake {
let params = NoiseParams::new(
static_private,
static_public,
peer_static_public,
preshared_key,
);
Handshake {
params,
next_index: global_idx,
previous: HandshakeState::None,
state: HandshakeState::None,
last_handshake_timestamp: Tai64N::zero(),
stamper: TimeStamper::new(),
cookies: Default::default(),
last_rtt: None,
}
}
pub(crate) fn is_in_progress(&self) -> bool {
!matches!(self.state, HandshakeState::None | HandshakeState::Expired)
}
pub(crate) fn timer(&self) -> Option<Instant> {
match self.state {
HandshakeState::InitSent(HandshakeInitSentState { time_sent, .. }) => Some(time_sent),
_ => None,
}
}
pub(crate) fn set_expired(&mut self) {
self.previous = HandshakeState::Expired;
self.state = HandshakeState::Expired;
}
pub(crate) fn is_expired(&self) -> bool {
matches!(self.state, HandshakeState::Expired)
}
pub(crate) fn has_cookie(&self) -> bool {
self.cookies.write_cookie.is_some()
}
pub(crate) fn clear_cookie(&mut self) {
self.cookies.write_cookie = None;
}
fn inc_index(&mut self) -> u32 {
let index = self.next_index;
let idx8 = index as u8;
self.next_index = (index & !0xff) | u32::from(idx8.wrapping_add(1));
self.next_index
}
pub(crate) fn set_static_private(
&mut self,
private_key: x25519::StaticSecret,
public_key: x25519::PublicKey,
) {
self.params.set_static_private(private_key, public_key)
}
pub(super) fn receive_handshake_initialization(
&mut self,
packet: crate::packet::Packet<WgHandshakeInit>,
) -> Result<(crate::packet::Packet<WgHandshakeResp>, Session), WireGuardError> {
let mut chaining_key = INITIAL_CHAIN_KEY;
let mut hash = INITIAL_CHAIN_HASH;
hash = b2s_hash(&hash, self.params.static_public.as_bytes());
let peer_index = packet.sender_idx;
let peer_ephemeral_public = x25519::PublicKey::from(packet.unencrypted_ephemeral);
hash = b2s_hash(&hash, peer_ephemeral_public.as_bytes());
chaining_key = b2s_hmac(
&b2s_hmac(&chaining_key, peer_ephemeral_public.as_bytes()),
&[0x01],
);
let ephemeral_shared = self
.params
.static_private
.diffie_hellman(&peer_ephemeral_public);
let temp = b2s_hmac(&chaining_key, &ephemeral_shared.to_bytes());
chaining_key = b2s_hmac(&temp, &[0x01]);
let key = b2s_hmac2(&temp, &chaining_key, &[0x02]);
let mut peer_static_public_decrypted = [0u8; KEY_LEN];
aead_chacha20_open(
&mut peer_static_public_decrypted,
&key,
0,
&packet.encrypted_static,
&hash,
)?;
if !constant_time_eq_n(
self.params.peer_static_public.as_bytes(),
&peer_static_public_decrypted,
) {
return Err(WireGuardError::WrongKey);
}
hash = b2s_hash(&hash, &packet.encrypted_static);
let temp = b2s_hmac(&chaining_key, self.params.static_shared.as_bytes());
chaining_key = b2s_hmac(&temp, &[0x01]);
let key = b2s_hmac2(&temp, &chaining_key, &[0x02]);
let mut timestamp = [0u8; TIMESTAMP_LEN];
aead_chacha20_open(&mut timestamp, &key, 0, &packet.encrypted_timestamp, &hash)?;
let timestamp = Tai64N::parse(×tamp)?;
if !timestamp.after(&self.last_handshake_timestamp) {
return Err(WireGuardError::WrongTai64nTimestamp);
}
self.last_handshake_timestamp = timestamp;
hash = b2s_hash(&hash, &packet.encrypted_timestamp);
self.previous = std::mem::replace(
&mut self.state,
HandshakeState::InitReceived {
chaining_key,
hash,
peer_ephemeral_public,
peer_index: peer_index.get(),
},
);
Ok(self.format_handshake_response(packet.into_bytes()))
}
pub(super) fn receive_handshake_response(
&mut self,
packet: &WgHandshakeResp,
) -> Result<Session, WireGuardError> {
let (state, is_previous) = match (&self.state, &self.previous) {
(HandshakeState::InitSent(s), _) if s.local_index == packet.receiver_idx.get() => {
(s, false)
}
(_, HandshakeState::InitSent(s)) if s.local_index == packet.receiver_idx.get() => {
(s, true)
}
_ => return Err(WireGuardError::UnexpectedPacket),
};
let peer_index = packet.sender_idx;
let local_index = state.local_index;
let unencrypted_ephemeral = x25519::PublicKey::from(packet.unencrypted_ephemeral);
let mut hash = b2s_hash(&state.hash, unencrypted_ephemeral.as_bytes());
let temp = b2s_hmac(&state.chaining_key, unencrypted_ephemeral.as_bytes());
let mut chaining_key = b2s_hmac(&temp, &[0x01]);
let ephemeral_shared = state
.ephemeral_private
.diffie_hellman(&unencrypted_ephemeral);
let temp = b2s_hmac(&chaining_key, &ephemeral_shared.to_bytes());
chaining_key = b2s_hmac(&temp, &[0x01]);
let temp = b2s_hmac(
&chaining_key,
&self
.params
.static_private
.diffie_hellman(&unencrypted_ephemeral)
.to_bytes(),
);
chaining_key = b2s_hmac(&temp, &[0x01]);
let temp = b2s_hmac(
&chaining_key,
&self.params.preshared_key.unwrap_or([0u8; 32])[..],
);
chaining_key = b2s_hmac(&temp, &[0x01]);
let temp2 = b2s_hmac2(&temp, &chaining_key, &[0x02]);
let key = b2s_hmac2(&temp, &temp2, &[0x03]);
hash = b2s_hash(&hash, &temp2);
aead_chacha20_open(&mut [], &key, 0, &packet.encrypted_nothing, &hash)?;
let temp1 = b2s_hmac(&chaining_key, &[]);
let temp2 = b2s_hmac(&temp1, &[0x01]);
let temp3 = b2s_hmac2(&temp1, &temp2, &[0x02]);
let rtt_time = Instant::now().duration_since(state.time_sent);
self.last_rtt = Some(rtt_time.as_millis() as u32);
if is_previous {
self.previous = HandshakeState::None;
} else {
self.state = HandshakeState::None;
}
Ok(Session::new(local_index, peer_index.get(), temp3, temp2))
}
pub(super) fn receive_cookie_reply(
&mut self,
packet: &WgCookieReply,
) -> Result<(), WireGuardError> {
let mac1 = match self.cookies.last_mac1 {
Some(mac) => mac,
None => {
return Err(WireGuardError::UnexpectedPacket);
}
};
let local_index = self.cookies.index;
if packet.receiver_idx != local_index {
return Err(WireGuardError::WrongIndex);
}
let key = b2s_hash(LABEL_COOKIE, self.params.peer_static_public.as_bytes());
let payload = Payload {
aad: &mac1[0..16],
msg: &packet.encrypted_cookie,
};
let plaintext = XChaCha20Poly1305::new_from_slice(&key)
.unwrap()
.decrypt(&packet.nonce.into(), payload)
.map_err(|_| WireGuardError::InvalidAeadTag)?;
let cookie = plaintext
.try_into()
.map_err(|_| WireGuardError::InvalidPacket)?;
self.cookies.write_cookie = Some(cookie);
Ok(())
}
fn init_mac1_and_mac2<T: WgHandshakeBase>(&mut self, packet: &mut T, local_index: u32) {
*packet.mac1_mut() = b2s_keyed_mac_16(
&self.params.sending_mac1_key,
&packet.as_bytes()[..T::MAC1_OFF],
);
*packet.mac2_mut() = if let Some(cookie) = self.cookies.write_cookie {
b2s_keyed_mac_16(&cookie, &packet.as_bytes()[..T::MAC2_OFF])
} else {
[0u8; 16]
};
self.cookies.index = local_index;
self.cookies.last_mac1 = Some(*packet.mac1());
}
pub(super) fn format_handshake_initiation(&mut self) -> crate::packet::Packet<WgHandshakeInit> {
let mut handshake = WgHandshakeInit::new();
let local_index = self.inc_index();
let mut chaining_key = INITIAL_CHAIN_KEY;
let mut hash = INITIAL_CHAIN_HASH;
hash = b2s_hash(&hash, self.params.peer_static_public.as_bytes());
let ephemeral_private = x25519::ReusableSecret::random_from_rng(OsRng);
handshake.sender_idx.set(local_index);
handshake
.unencrypted_ephemeral
.copy_from_slice(x25519::PublicKey::from(&ephemeral_private).as_bytes());
hash = b2s_hash(&hash, &handshake.unencrypted_ephemeral);
chaining_key = b2s_hmac(
&b2s_hmac(&chaining_key, &handshake.unencrypted_ephemeral),
&[0x01],
);
let ephemeral_shared = ephemeral_private.diffie_hellman(&self.params.peer_static_public);
let temp = b2s_hmac(&chaining_key, &ephemeral_shared.to_bytes());
chaining_key = b2s_hmac(&temp, &[0x01]);
let key = b2s_hmac2(&temp, &chaining_key, &[0x02]);
aead_chacha20_seal(
&mut handshake.encrypted_static,
&key,
0,
self.params.static_public.as_bytes(),
&hash,
);
hash = b2s_hash(&hash, &handshake.encrypted_static);
let temp = b2s_hmac(&chaining_key, self.params.static_shared.as_bytes());
chaining_key = b2s_hmac(&temp, &[0x01]);
let key = b2s_hmac2(&temp, &chaining_key, &[0x02]);
let timestamp = self.stamper.stamp();
aead_chacha20_seal(
&mut handshake.encrypted_timestamp,
&key,
0,
×tamp,
&hash,
);
hash = b2s_hash(&hash, &handshake.encrypted_timestamp);
let time_now = Instant::now();
self.previous = std::mem::replace(
&mut self.state,
HandshakeState::InitSent(HandshakeInitSentState {
local_index,
chaining_key,
hash,
ephemeral_private,
time_sent: time_now,
}),
);
self.init_mac1_and_mac2(&mut handshake, local_index);
Packet::copy_from(&handshake)
}
fn format_handshake_response(
&mut self,
buf: crate::packet::Packet,
) -> (crate::packet::Packet<WgHandshakeResp>, Session) {
let state = std::mem::replace(&mut self.state, HandshakeState::None);
let (mut chaining_key, mut hash, peer_ephemeral_public, peer_index) = match state {
HandshakeState::InitReceived {
chaining_key,
hash,
peer_ephemeral_public,
peer_index,
} => (chaining_key, hash, peer_ephemeral_public, peer_index),
_ => {
panic!("Unexpected attempt to call send_handshake_response");
}
};
let ephemeral_private = x25519::ReusableSecret::random_from_rng(OsRng);
let local_index = self.inc_index();
let mut resp = WgHandshakeResp::new(
local_index,
peer_index,
*x25519::PublicKey::from(&ephemeral_private).as_bytes(),
);
hash = b2s_hash(&hash, &resp.unencrypted_ephemeral);
let temp = b2s_hmac(&chaining_key, &resp.unencrypted_ephemeral);
chaining_key = b2s_hmac(&temp, &[0x01]);
let ephemeral_shared = ephemeral_private.diffie_hellman(&peer_ephemeral_public);
let temp = b2s_hmac(&chaining_key, &ephemeral_shared.to_bytes());
chaining_key = b2s_hmac(&temp, &[0x01]);
let temp = b2s_hmac(
&chaining_key,
&ephemeral_private
.diffie_hellman(&self.params.peer_static_public)
.to_bytes(),
);
chaining_key = b2s_hmac(&temp, &[0x01]);
let temp = b2s_hmac(
&chaining_key,
&self.params.preshared_key.unwrap_or([0u8; 32])[..],
);
chaining_key = b2s_hmac(&temp, &[0x01]);
let temp2 = b2s_hmac2(&temp, &chaining_key, &[0x02]);
let key = b2s_hmac2(&temp, &temp2, &[0x03]);
hash = b2s_hash(&hash, &temp2);
aead_chacha20_seal(&mut resp.encrypted_nothing, &key, 0, &[], &hash);
let temp1 = b2s_hmac(&chaining_key, &[]);
let temp2 = b2s_hmac(&temp1, &[0x01]);
let temp3 = b2s_hmac2(&temp1, &temp2, &[0x02]);
self.init_mac1_and_mac2(&mut resp, local_index);
let packet = buf.overwrite_with(&resp);
(packet, Session::new(local_index, peer_index, temp2, temp3))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn chacha20_seal_rfc7530_test_vector() {
let plaintext = b"Ladies and Gentlemen of the class of '99: If I could offer you only one tip for the future, sunscreen would be it.";
let aad: [u8; 12] = [
0x50, 0x51, 0x52, 0x53, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
];
let key: [u8; 32] = [
0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d,
0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b,
0x9c, 0x9d, 0x9e, 0x9f,
];
let nonce: [u8; 12] = [
0x07, 0x00, 0x00, 0x00, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
];
let mut buffer = vec![0; plaintext.len() + 16];
aead_chacha20_seal_inner(&mut buffer, &key, nonce, plaintext, &aad);
const EXPECTED_CIPHERTEXT: [u8; 114] = [
0xd3, 0x1a, 0x8d, 0x34, 0x64, 0x8e, 0x60, 0xdb, 0x7b, 0x86, 0xaf, 0xbc, 0x53, 0xef,
0x7e, 0xc2, 0xa4, 0xad, 0xed, 0x51, 0x29, 0x6e, 0x08, 0xfe, 0xa9, 0xe2, 0xb5, 0xa7,
0x36, 0xee, 0x62, 0xd6, 0x3d, 0xbe, 0xa4, 0x5e, 0x8c, 0xa9, 0x67, 0x12, 0x82, 0xfa,
0xfb, 0x69, 0xda, 0x92, 0x72, 0x8b, 0x1a, 0x71, 0xde, 0x0a, 0x9e, 0x06, 0x0b, 0x29,
0x05, 0xd6, 0xa5, 0xb6, 0x7e, 0xcd, 0x3b, 0x36, 0x92, 0xdd, 0xbd, 0x7f, 0x2d, 0x77,
0x8b, 0x8c, 0x98, 0x03, 0xae, 0xe3, 0x28, 0x09, 0x1b, 0x58, 0xfa, 0xb3, 0x24, 0xe4,
0xfa, 0xd6, 0x75, 0x94, 0x55, 0x85, 0x80, 0x8b, 0x48, 0x31, 0xd7, 0xbc, 0x3f, 0xf4,
0xde, 0xf0, 0x8e, 0x4b, 0x7a, 0x9d, 0xe5, 0x76, 0xd2, 0x65, 0x86, 0xce, 0xc6, 0x4b,
0x61, 0x16,
];
const EXPECTED_TAG: [u8; 16] = [
0x1a, 0xe1, 0x0b, 0x59, 0x4f, 0x09, 0xe2, 0x6a, 0x7e, 0x90, 0x2e, 0xcb, 0xd0, 0x60,
0x06, 0x91,
];
assert_eq!(buffer[..plaintext.len()], EXPECTED_CIPHERTEXT);
assert_eq!(buffer[plaintext.len()..], EXPECTED_TAG);
}
#[test]
fn symmetric_chacha20_seal_open() {
let aad: [u8; 32] = Default::default();
let key: [u8; 32] = Default::default();
let counter = 0;
let mut encrypted_nothing: [u8; 16] = Default::default();
aead_chacha20_seal(&mut encrypted_nothing, &key, counter, &[], &aad);
eprintln!("encrypted_nothing: {encrypted_nothing:?}");
aead_chacha20_open(&mut [], &key, counter, &encrypted_nothing, &aad)
.expect("Should open what we just sealed");
}
}