use core::convert::TryInto;
use ring::aead;
use s2n_quic_core::crypto::{
retry::{IntegrityTag, NONCE_BYTES, SECRET_KEY_BYTES},
CryptoError, RetryKey,
};
lazy_static::lazy_static! {
static ref SECRET_KEY: aead::LessSafeKey = aead::LessSafeKey::new(
aead::UnboundKey::new(&aead::AES_128_GCM, &SECRET_KEY_BYTES).unwrap(),
);
}
#[derive(Debug)]
pub struct RingRetryKey;
impl RetryKey for RingRetryKey {
fn generate_tag(pseudo_packet: &[u8]) -> IntegrityTag {
let nonce = aead::Nonce::assume_unique_for_key(NONCE_BYTES);
let tag = SECRET_KEY
.seal_in_place_separate_tag(nonce, aead::Aad::from(pseudo_packet), &mut [])
.expect("in_out len is 0 and should always be less than the nonce max bytes");
tag.as_ref()
.try_into()
.expect("AES_128_GCM tag len should always be 128 bits")
}
fn validate(pseudo_packet: &[u8], tag: IntegrityTag) -> Result<(), CryptoError> {
let expected = RingRetryKey::generate_tag(pseudo_packet);
ring::constant_time::verify_slices_are_equal(&expected, &tag)
.map_err(|_| CryptoError::DECRYPT_ERROR)
}
}
#[cfg(test)]
mod tests {
use super::*;
use hex_literal::hex;
use s2n_codec::{DecoderBufferMut, Encoder, EncoderBuffer};
use s2n_quic_core::{
connection,
connection::id::ConnectionInfo,
crypto::retry,
inet, packet,
packet::number::{PacketNumberSpace, TruncatedPacketNumber},
random, token,
varint::VarInt,
};
#[test]
fn test_tag_validation() {
let invalid_tag: [u8; 16] = hex!("00112233445566778899aabbccddeeff");
assert!(RingRetryKey::validate(
&retry::example::PSEUDO_PACKET,
retry::example::EXPECTED_TAG
)
.is_ok());
assert!(RingRetryKey::validate(&retry::example::PSEUDO_PACKET, invalid_tag).is_err());
}
fn pn(space: PacketNumberSpace) -> TruncatedPacketNumber {
let pn = space.new_packet_number(VarInt::new(0x1).unwrap());
pn.truncate(pn).unwrap()
}
#[test]
fn test_packet_encode() {
let remote_address = inet::ip::SocketAddress::default();
let mut token_format = token::testing::Format::new();
let packet = packet::initial::Initial {
version: 0x01,
destination_connection_id: &retry::example::ODCID[..],
source_connection_id: &retry::example::DCID[..],
token: &retry::example::TOKEN[..],
packet_number: pn(PacketNumberSpace::Initial),
payload: &[1u8, 2, 3, 4, 5][..],
};
let mut buf = vec![0u8; 1200];
let mut encoder = EncoderBuffer::new(&mut buf);
encoder.encode(&packet);
let len = encoder.len();
let decoder = DecoderBufferMut::new(&mut buf[..len]);
let connection_info = ConnectionInfo::new(&remote_address);
let mut output_buf = vec![0u8; 1200];
if let Some(packet) =
match packet::ProtectedPacket::decode(decoder, &connection_info, &3).unwrap() {
(packet::ProtectedPacket::Initial(packet), _) => Some(packet),
_ => None,
}
{
let local_conn_id = connection::LocalId::try_from_bytes(&retry::example::SCID).unwrap();
if let Some(range) = packet::retry::Retry::encode_packet::<_, RingRetryKey, _>(
&remote_address,
&packet,
&local_conn_id,
&mut random::testing::Generator(5),
&mut token_format,
&mut output_buf,
) {
assert_eq!(&output_buf[range], &retry::example::PACKET[..]);
}
}
}
#[test]
#[should_panic]
fn test_odcid_different_from_local_cid() {
let remote_address = inet::ip::SocketAddress::default();
let mut token_format = token::testing::Format::new();
let packet = packet::initial::Initial {
version: 0xff00_0020,
destination_connection_id: &retry::example::ODCID[..],
source_connection_id: &retry::example::DCID[..],
token: &retry::example::TOKEN[..],
packet_number: pn(PacketNumberSpace::Initial),
payload: &[1u8, 2, 3, 4, 5][..],
};
let mut buf = vec![0u8; 1200];
let mut encoder = EncoderBuffer::new(&mut buf);
encoder.encode(&packet);
let len = encoder.len();
let decoder = DecoderBufferMut::new(&mut buf[..len]);
let connection_info = ConnectionInfo::new(&remote_address);
let mut output_buf = vec![0u8; 1200];
if let Some(packet) =
match packet::ProtectedPacket::decode(decoder, &connection_info, &3).unwrap() {
(packet::ProtectedPacket::Initial(packet), _) => Some(packet),
_ => None,
}
{
let local_conn_id =
connection::LocalId::try_from_bytes(&retry::example::ODCID).unwrap();
assert!(packet::retry::Retry::encode_packet::<_, RingRetryKey, _>(
&remote_address,
&packet,
&local_conn_id,
&mut random::testing::Generator(5),
&mut token_format,
&mut output_buf,
)
.is_none());
}
}
}