Test 0-RTT

This has some api tests, but mainly enables bogo tests.

In passing, ensure TLS1.3 NewSessionTicket checks for duplicated
extensions.
This commit is contained in:
Joseph Birr-Pixton 2021-12-29 20:56:29 +00:00 committed by ctz
parent 725870a480
commit a80da9967b
8 changed files with 179 additions and 70 deletions

View File

@ -74,9 +74,6 @@
"KeyUpdate-FromClient": "not implemented (no API yet)",
"KeyUpdate-FromServer": "",
"ExportTrafficSecrets-*": "",
"SkipEarlyData*": "no 0rtt support",
"TLS13-DuplicateTicketEarlyDataInfo": "",
"TLS13-DuplicateTicketEarlyDataSupport": "",
"*-InvalidSignature-*-SHA1-*": "no sha1",
"NoCommonCurves": "nothing to fall back to",
"ClientHelloPadding": "hello padding extension not implemented",
@ -160,6 +157,8 @@
"TrailingMessageData-TLS13-ServerFinished-TLS": ":DECRYPTION_FAILED_OR_BAD_RECORD_MAC:",
"TrailingMessageData-TLS13-ClientCertificate-TLS": ":BAD_HANDSHAKE_MSG:",
"TrailingMessageData-TLS13-ClientCertificateVerify-TLS": ":BAD_HANDSHAKE_MSG:",
"TrailingMessageData-TLS13-EndOfEarlyData-TLS": ":BAD_HANDSHAKE_MSG:",
"Server-NonEmptyEndOfEarlyData-TLS13": ":BAD_HANDSHAKE_MSG:",
"MissingKeyShare-Client-TLS13": ":PEER_MISBEHAVIOUR:",
"MissingKeyShare-Server-TLS13": ":INCOMPATIBLE:",
"EmptyEncryptedExtensions-TLS13": ":BAD_HANDSHAKE_MSG:",
@ -290,6 +289,7 @@
"PartialClientFinishedWithClientHello-TLS12-Resume": ":PEER_MISBEHAVIOUR:",
"PartialServerHelloWithHelloRetryRequest": ":PEER_MISBEHAVIOUR:",
"PartialNewSessionTicketWithServerHelloDone": ":PEER_MISBEHAVIOUR:",
"PartialEndOfEarlyDataWithClientHello": ":PEER_MISBEHAVIOUR:",
"FragmentAcrossChangeCipherSpec-Client": ":PEER_MISBEHAVIOUR:",
"FragmentAcrossChangeCipherSpec-Server-Packed": ":PEER_MISBEHAVIOUR:",
"FragmentAcrossChangeCipherSpec-Client-Resume-Packed": ":PEER_MISBEHAVIOUR:",
@ -340,6 +340,7 @@
"SendServerHelloAsHelloRetryRequest": ":BAD_HANDSHAKE_MSG:",
"TLS13-OnlyPadding": ":PEER_MISBEHAVIOUR:",
"TLS13-EmptyRecords": ":PEER_MISBEHAVIOUR:",
"TLS13-DuplicateTicketEarlyDataSupport": ":PEER_MISBEHAVIOUR:",
"SupportedVersionSelection-TLS12": ":PEER_MISBEHAVIOUR:",
"HelloRetryRequest-CipherChange-TLS13": ":PEER_MISBEHAVIOUR:",
"CurveTest-Client-Compressed-P-256-TLS12": ":PEER_MISBEHAVIOUR:",
@ -364,7 +365,13 @@
"TooManyChangeCipherSpec-Server-TLS13": ":PEER_MISBEHAVIOUR:",
"EarlyData-CipherMismatch-Client-TLS13": ":PEER_MISBEHAVIOUR:",
"EarlyDataWithoutResume-Client-TLS13": ":PEER_MISBEHAVIOUR:",
"EarlyDataVersionDowngrade-Client-TLS13": ":PEER_MISBEHAVIOUR:"
"EarlyDataVersionDowngrade-Client-TLS13": ":PEER_MISBEHAVIOUR:",
"EarlyData-SkipEndOfEarlyData-TLS13": ":DECRYPTION_FAILED_OR_BAD_RECORD_MAC:",
"SkipEarlyData-TooMuchData-TLS13": ":DECRYPTION_FAILED_OR_BAD_RECORD_MAC:",
"SkipEarlyData-HRR-FatalAlert-TLS13": ":HANDSHAKE_FAILURE:",
"SkipEarlyData-HRR-Interleaved-TLS13": ":DECRYPTION_FAILED_OR_BAD_RECORD_MAC:",
"SkipEarlyData-HRR-TooMuchData-TLS13": ":UNEXPECTED_MESSAGE:",
"SkipEarlyData-SecondClientHelloEarlyData-TLS13": ":PEER_MISBEHAVIOUR:"
},
"TestLocalErrorMap": {
"SendServerHelloAsHelloRetryRequest": "remote error: error decoding message",
@ -375,6 +382,8 @@
"Downgrade-TLS10-Client": "tls: no cipher suite supported by both client and server",
"Downgrade-TLS10-Server": "remote error: protocol version not supported",
"TrailingDataWithFinished-Client-TLS13": "local error: bad record MAC",
"TrailingDataWithFinished-Resume-Client-TLS13": "local error: bad record MAC"
}
"TrailingDataWithFinished-Resume-Client-TLS13": "local error: bad record MAC",
"SkipEarlyData-SecondClientHelloEarlyData-TLS13": "remote error: illegal parameter"
},
"HalfRTTTickets": 0
}

View File

@ -11,7 +11,7 @@ use rustls;
use rustls::internal::msgs::enums::ProtocolVersion;
use rustls::quic::{self, ClientQuicExt, QuicExt, ServerQuicExt};
use rustls::server::ClientHello;
use rustls::{ClientConnection, Connection};
use rustls::{ClientConnection, Connection, ServerConnection};
use std::convert::TryInto;
use std::env;
@ -391,6 +391,11 @@ fn make_server_cfg(opts: &Options) -> Arc<rustls::ServerConfig> {
.collect::<Vec<_>>();
}
if opts.enable_early_data {
// see kMaxEarlyDataAccepted in boringssl, which bogo validates
cfg.max_early_data_size = 14336;
}
Arc::new(cfg)
}
@ -512,6 +517,9 @@ fn handle_err(err: rustls::Error) -> ! {
Error::CorruptMessage => quit(":GARBAGE:"),
Error::DecryptError => quit(":DECRYPTION_FAILED_OR_BAD_RECORD_MAC:"),
Error::PeerIncompatibleError(_) => quit(":INCOMPATIBLE:"),
Error::PeerMisbehavedError(s) if s == "too much early_data received" => {
quit(":TOO_MUCH_READ_EARLY_DATA:")
}
Error::PeerMisbehavedError(_) => quit(":PEER_MISBEHAVIOUR:"),
Error::NoCertificatesPresented => quit(":NO_CERTS:"),
Error::AlertReceived(AlertDescription::UnexpectedMessage) => quit(":BAD_ALERT:"),
@ -546,6 +554,13 @@ fn client(conn: &mut Connection) -> &mut ClientConnection {
conn.try_into().unwrap()
}
fn server(conn: &mut Connection) -> &mut ServerConnection {
match conn {
Connection::Server(s) => s,
_ => panic!("Connection is not a ServerConnection"),
}
}
fn exec(opts: &Options, mut sess: Connection, count: usize) {
let mut sent_message = false;
@ -593,6 +608,23 @@ fn exec(opts: &Options, mut sess: Connection, count: usize) {
}
}
if opts.server && opts.enable_early_data {
if let Some(ref mut ed) = server(&mut sess).early_data() {
let mut data = Vec::new();
let data_len = ed
.read_to_end(&mut data)
.expect("cannot read early_data");
for b in data.iter_mut() {
*b ^= 0xff;
}
sess.writer()
.write_all(&data[..data_len])
.expect("cannot echo early_data in 1rtt data");
}
}
if !sess.is_handshaking() && opts.export_keying_material > 0 && !sent_exporter {
let mut export = Vec::new();
export.resize(opts.export_keying_material, 0u8);
@ -633,7 +665,7 @@ fn exec(opts: &Options, mut sess: Connection, count: usize) {
quench_writes = true;
}
if opts.enable_early_data && !sess.is_handshaking() && count > 0 {
if opts.enable_early_data && !opts.server && !sess.is_handshaking() && count > 0 {
if opts.expect_accept_early_data && !client(&mut sess).is_early_data_accepted() {
quit_err("Early data was not accepted, but we expect the opposite");
} else if opts.expect_reject_early_data && client(&mut sess).is_early_data_accepted() {
@ -882,8 +914,8 @@ fn main() {
"-enable-signed-cert-timestamps" => {
opts.send_sct = true;
}
"-enable-early-data" |
"-on-resume-enable-early-data" => {
"-enable-early-data" => {
opts.tickets = false;
opts.enable_early_data = true;
}
"-on-resume-shim-writes-first" => {
@ -985,6 +1017,7 @@ fn main() {
"-on-initial-tls13-variant" |
"-on-initial-expect-curve-id" |
"-on-resume-export-early-keying-material" |
"-on-resume-enable-early-data" |
"-export-early-keying-material" |
"-handshake-twice" |
"-on-resume-verify-fail" |
@ -1006,11 +1039,6 @@ fn main() {
}
}
if opts.enable_early_data && opts.server {
println!("For now we only test client-side early data");
process::exit(BOGO_NACK);
}
println!("opts {:?}", opts);
let mut server_cfg = if opts.server {

View File

@ -976,6 +976,14 @@ impl ExpectTraffic {
cx: &mut ClientContext<'_>,
nst: &NewSessionTicketPayloadTLS13,
) -> Result<(), Error> {
if nst.has_duplicate_extension() {
cx.common
.send_fatal_alert(AlertDescription::IllegalParameter);
return Err(Error::PeerMisbehavedError(
"peer sent duplicate NewSessionTicket extensions".into(),
));
}
let handshake_hash = self.transcript.get_current_hash();
let secret = self
.key_schedule

View File

@ -2025,6 +2025,21 @@ impl NewSessionTicketPayloadTLS13 {
}
}
pub fn has_duplicate_extension(&self) -> bool {
let mut seen = collections::HashSet::new();
for ext in &self.exts {
let typ = ext.get_type().get_u16();
if seen.contains(&typ) {
return true;
}
seen.insert(typ);
}
false
}
pub fn find_extension(&self, ext: ExtensionType) -> Option<&NewSessionTicketExtension> {
self.exts
.iter()

View File

@ -214,11 +214,6 @@ impl Tls13ClientSessionValue {
bytes
}
// Test-only
pub fn set_max_early_data_size(&mut self, new: u32) {
self.max_early_data_size = new;
}
pub fn max_early_data_size(&self) -> u32 {
self.max_early_data_size
}

View File

@ -639,6 +639,27 @@ impl EarlyDataState {
}
}
// these branches not reachable externally, unless something else goes wrong.
#[test]
fn test_read_in_new_state() {
assert_eq!(
format!("{:?}", EarlyDataState::default().read(&mut [0u8; 5])),
"Err(Kind(BrokenPipe))"
);
}
#[cfg(feature = "read_buf")]
#[test]
fn test_read_buf_in_new_state() {
assert_eq!(
format!(
"{:?}",
EarlyDataState::default().read_buf(&mut io::ReadBuf::new(&mut [0u8; 5]))
),
"Err(Kind(BrokenPipe))"
);
}
/// State associated with a server connection.
#[derive(Default)]
pub struct ServerConnectionData {

View File

@ -1126,8 +1126,7 @@ impl ExpectFinished {
(id, stateful_lifetime)
};
let age_add = rand::random_u32()?; // nb, we don't do 0-RTT data, so whatever
#[allow(unused_mut)]
let age_add = rand::random_u32()?;
let mut payload = NewSessionTicketPayloadTLS13::new(lifetime, age_add, nonce, ticket);
if config.max_early_data_size > 0 {

View File

@ -12,8 +12,6 @@ use std::sync::Arc;
use std::sync::Mutex;
use rustls::client::ResolvesClientCert;
use rustls::internal::msgs::codec::{Codec, Reader};
use rustls::internal::msgs::persist::Tls13ClientSessionValue;
#[cfg(feature = "quic")]
use rustls::quic::{self, ClientQuicExt, QuicExt, ServerQuicExt};
use rustls::server::{ClientHello, ResolvesServerCert};
@ -2628,11 +2626,25 @@ fn key_log_for_tls13() {
assert_eq!("SERVER_TRAFFIC_SECRET_0", client_resume_log[3].label);
assert_eq!("EXPORTER_SECRET", client_resume_log[4].label);
assert_eq!(client_resume_log[0], server_resume_log[0]);
assert_eq!(client_resume_log[1], server_resume_log[1]);
assert_eq!(client_resume_log[2], server_resume_log[2]);
assert_eq!(client_resume_log[3], server_resume_log[3]);
assert_eq!(client_resume_log[4], server_resume_log[4]);
assert_eq!(6, server_resume_log.len());
assert_eq!("CLIENT_EARLY_TRAFFIC_SECRET", server_resume_log[0].label);
assert_eq!(
"CLIENT_HANDSHAKE_TRAFFIC_SECRET",
server_resume_log[1].label
);
assert_eq!(
"SERVER_HANDSHAKE_TRAFFIC_SECRET",
server_resume_log[2].label
);
assert_eq!("CLIENT_TRAFFIC_SECRET_0", server_resume_log[3].label);
assert_eq!("SERVER_TRAFFIC_SECRET_0", server_resume_log[4].label);
assert_eq!("EXPORTER_SECRET", server_resume_log[5].label);
assert_eq!(client_resume_log[0], server_resume_log[1]);
assert_eq!(client_resume_log[1], server_resume_log[2]);
assert_eq!(client_resume_log[2], server_resume_log[3]);
assert_eq!(client_resume_log[3], server_resume_log[4]);
assert_eq!(client_resume_log[4], server_resume_log[5]);
}
#[test]
@ -3066,53 +3078,24 @@ fn early_data_not_available() {
assert!(client.early_data().is_none());
}
#[test]
fn early_data_is_available_on_resumption() {
fn early_data_configs() -> (Arc<ClientConfig>, Arc<ServerConfig>) {
let kt = KeyType::Rsa;
let mut client_config = make_client_config(kt);
client_config.enable_early_data = true;
client_config.session_storage = Arc::new(ClientStorage::new());
let storage = Arc::new(ClientStorage::new());
client_config.session_storage = storage.clone();
let mut server_config = make_server_config(kt);
server_config.max_early_data_size = 1234;
(Arc::new(client_config), Arc::new(server_config))
}
let client_config = Arc::new(client_config);
#[test]
fn early_data_is_available_on_resumption() {
let (client_config, server_config) = early_data_configs();
let server_config = Arc::new(make_server_config(kt));
let (mut client, mut server) = make_pair_for_arc_configs(&client_config, &server_config);
do_handshake(&mut client, &mut server);
/* discover the session data in the storage, and edit it to fool the
* client on resumption that the server supports 0rtt. */
let session_key = storage
.last_put_key
.lock()
.unwrap()
.clone()
.unwrap();
let session_value_bytes = storage
.storage
.get(&session_key)
.unwrap();
let cs = CipherSuite::read_bytes(&session_value_bytes[..2]).unwrap();
let suite = match ALL_CIPHER_SUITES
.iter()
.find(|supported| supported.suite() == cs)
.unwrap()
{
#[cfg(feature = "tls12")]
SupportedCipherSuite::Tls12(_) => unreachable!(),
SupportedCipherSuite::Tls13(inner) => inner,
};
let mut reader = Reader::init(&session_value_bytes[2..]);
let mut session_value = Tls13ClientSessionValue::read(suite, &mut reader).unwrap();
session_value.set_max_early_data_size(128);
storage
.storage
.put(session_key, session_value.get_encoding());
let (mut client, mut server) = make_pair_for_arc_configs(&client_config, &server_config);
assert!(client.early_data().is_some());
assert_eq!(
@ -3120,7 +3103,7 @@ fn early_data_is_available_on_resumption() {
.early_data()
.unwrap()
.bytes_left(),
128
1234
);
client
.early_data()
@ -3135,8 +3118,59 @@ fn early_data_is_available_on_resumption() {
.unwrap(),
5
);
let err = do_handshake_until_error(&mut client, &mut server);
assert_eq!(err, Err(ErrorFromPeer::Server(Error::DecryptError)));
do_handshake(&mut client, &mut server);
let mut received_early_data = [0u8; 5];
assert_eq!(
server
.early_data()
.expect("early_data didn't happen")
.read(&mut received_early_data)
.expect("early_data failed unexpectedly"),
5
);
assert_eq!(&received_early_data[..], b"hello");
}
#[test]
fn early_data_not_available_on_server_before_client_hello() {
let mut server = ServerConnection::new(Arc::new(make_server_config(KeyType::Rsa))).unwrap();
assert!(server.early_data().is_none());
}
#[test]
fn early_data_can_be_rejected_by_server() {
let (client_config, server_config) = early_data_configs();
let (mut client, mut server) = make_pair_for_arc_configs(&client_config, &server_config);
do_handshake(&mut client, &mut server);
let (mut client, mut server) = make_pair_for_arc_configs(&client_config, &server_config);
assert!(client.early_data().is_some());
assert_eq!(
client
.early_data()
.unwrap()
.bytes_left(),
1234
);
client
.early_data()
.unwrap()
.flush()
.unwrap();
assert_eq!(
client
.early_data()
.unwrap()
.write(b"hello")
.unwrap(),
5
);
server.reject_early_data();
do_handshake(&mut client, &mut server);
assert_eq!(client.is_early_data_accepted(), false);
}
#[cfg(feature = "quic")]