-
Notifications
You must be signed in to change notification settings - Fork 114
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add integrity check for Retry packets. (#223)
* Add integrity check for Retry packets.
- Loading branch information
Showing
5 changed files
with
90 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
use crate::crypto::CryptoError; | ||
use hex_literal::hex; | ||
|
||
pub type IntegrityTag = [u8; 16]; | ||
|
||
pub trait RetryCrypto { | ||
fn generate_tag(payload: &[u8]) -> IntegrityTag; | ||
fn validate(payload: &[u8], tag: IntegrityTag) -> Result<(), CryptoError>; | ||
} | ||
|
||
//= https://tools.ietf.org/id/draft-ietf-quic-tls-32.txt#5.8 | ||
//# The Retry Integrity Tag is a 128-bit field that is computed as the | ||
//# output of AEAD_AES_128_GCM ([AEAD]) used with the following inputs: | ||
//# | ||
//# * The secret key, K, is 128 bits equal to | ||
//# 0xccce187ed09a09d05728155a6cb96be1. | ||
//# | ||
pub const SECRET_KEY_BYTES: [u8; 16] = hex!("ccce187ed09a09d05728155a6cb96be1"); | ||
|
||
//= https://tools.ietf.org/id/draft-ietf-quic-tls-32.txt#5.8 | ||
//# * The nonce, N, is 96 bits equal to 0xe54930f97f2136f0530a8c1c. | ||
|
||
pub const NONCE_BYTES: [u8; 12] = hex!("e54930f97f2136f0530a8c1c"); | ||
|
||
//= https://tools.ietf.org/id/draft-ietf-quic-tls-32.txt#A.4 | ||
//# This shows a Retry packet that might be sent in response to the | ||
//# Initial packet in Appendix A.2. The integrity check includes the | ||
//# client-chosen connection ID value of 0x8394c8f03e515708, but that | ||
//# value is not included in the final Retry packet: | ||
pub const EXAMPLE_PSEUDO_RETRY_PACKET: [u8; 29] = | ||
hex!("088394c8f03e515708 ffff000020 00 08f067a5502a4262b5 746f6b656e"); | ||
|
||
pub const EXAMPLE_EXPECTED_TAG: [u8; 16] = hex!("59756519dd6cc85bd90e33a934d2ff85"); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
use core::convert::TryInto; | ||
use ring::aead; | ||
use s2n_quic_core::crypto::{ | ||
retry::{IntegrityTag, NONCE_BYTES, SECRET_KEY_BYTES}, | ||
CryptoError, RetryCrypto, | ||
}; | ||
|
||
lazy_static::lazy_static! { | ||
/// Compute the Initial salt once, as the seed is constant | ||
static ref SECRET_KEY: aead::LessSafeKey = aead::LessSafeKey::new( | ||
aead::UnboundKey::new(&aead::AES_128_GCM, &SECRET_KEY_BYTES).unwrap(), | ||
); | ||
} | ||
|
||
#[derive(Debug)] | ||
pub struct RingRetryCrypto; | ||
|
||
impl RetryCrypto for RingRetryCrypto { | ||
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 = RingRetryCrypto::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_quic_core::crypto::retry::{EXAMPLE_EXPECTED_TAG, EXAMPLE_PSEUDO_RETRY_PACKET}; | ||
|
||
#[test] | ||
fn test_valid_tag() { | ||
let invalid_tag: [u8; 16] = hex!("00112233445566778899aabbccddeeff"); | ||
|
||
assert!( | ||
RingRetryCrypto::validate(&EXAMPLE_PSEUDO_RETRY_PACKET, EXAMPLE_EXPECTED_TAG).is_ok() | ||
); | ||
assert!(RingRetryCrypto::validate(&EXAMPLE_PSEUDO_RETRY_PACKET, invalid_tag).is_err()); | ||
} | ||
} |