mirror of https://github.com/ctz/rustls
Fix more bogo-found issues
- in shim, support versions for server tests. - check handshake defragmenter is aligned on key changes (like ccs) - don't include SupportedVersions ext if it would be empty - don't offer or support PSK_KE in clients (no pfs) - tighten validation of hrr extensions - tighten validation of encrypted extensions - tighten validation of certificate extensions - alter assorted alert descriptions - if a server sends an ECPointFormats extension (they typically don't) check it contains Uncompressed. - tighten validation of certificate messages/extensions - tighten validation of client certreq message - tighten validation of keyshares extensions received by server - loosen suite compatiblity check on resumption by client
This commit is contained in:
parent
a82414ed67
commit
b7f0a7d9e3
|
@ -11,7 +11,11 @@
|
|||
"MinorVersionTolerance": "",
|
||||
"MajorVersionTolerance": "",
|
||||
"FragmentedClientVersion": "",
|
||||
"ConflictingVersionNegotiation": "",
|
||||
"ConflictingVersionNegotiation-2": "",
|
||||
"PointFormat-Server-Missing": "we require ecc",
|
||||
"EMS-Forbidden-TLS13": "TODO: unsupported but should be",
|
||||
"ExtendedMasterSecret-*": "",
|
||||
"ECDSAKeyUsage-*": "TODO: we don't do anything with key usages",
|
||||
"CheckRecordVersion-*": "we don't look at record version",
|
||||
"TLS13-WrongOuterRecord": "we're lax on this",
|
||||
|
@ -49,10 +53,12 @@
|
|||
"*-ShortHeader": "",
|
||||
"ShortHeader-*": "",
|
||||
"SkipEarlyData*": "no 0rtt support",
|
||||
"TLS13-DuplicateTicketEarlyDataInfo": "",
|
||||
"NoCommonCurves": "nothing to fall back to",
|
||||
"ClientHelloPadding": "hello padding extension not implemented",
|
||||
"TLS13-HelloRetryRequest-Client-Sync*": "we remember the server's preference and don't need a second HRR",
|
||||
"TLS13-HelloRetryRequest-Client-Async*": "",
|
||||
"SendHelloRetryRequest-2": "",
|
||||
"Resume-Client-CipherMismatch": "tries to vary to unimplemented CBC-mode cs",
|
||||
"*Auth-SHA1-Fallback*": "",
|
||||
"RSA-PSS-Large": "",
|
||||
|
@ -65,7 +71,6 @@
|
|||
"*-Client-ClientAuth-ECDSA": "",
|
||||
"Basic-Server-*-ECDSA-*": "",
|
||||
"FallbackSCSV*": "fallback countermeasure not yet implemented",
|
||||
"ExtendedMasterSecret-*": "",
|
||||
"RequireAnyClientCertificate-TLS12": "we don't send an alert in this case",
|
||||
"TooManyKeyUpdates": "no limit implemented",
|
||||
"Renegotiate-Client-*": "no reneg",
|
||||
|
@ -206,6 +211,29 @@
|
|||
"UnofferedExtension-Client-TLS13": ":PEER_MISBEHAVIOUR:",
|
||||
"RenegotiationInfo-Forbidden-TLS13": ":PEER_MISBEHAVIOUR:",
|
||||
"UnknownExtension-Client-TLS13": ":PEER_MISBEHAVIOUR:",
|
||||
"TLS13-RequestContextInHandshake": ":BAD_HANDSHAKE_MSG:",
|
||||
"UnnecessaryHelloRetryRequest": ":PEER_MISBEHAVIOUR:",
|
||||
"UnknownCurve-HelloRetryRequest": ":PEER_MISBEHAVIOUR:",
|
||||
"DisabledCurve-HelloRetryRequest": ":PEER_MISBEHAVIOUR:",
|
||||
"HelloRetryRequest-Empty": ":PEER_MISBEHAVIOUR:",
|
||||
"HelloRetryRequest-EmptyCookie": ":PEER_MISBEHAVIOUR:",
|
||||
"HelloRetryRequest-Unknown": ":INCOMPATIBLE:",
|
||||
"ServerBogusVersion": ":BAD_HANDSHAKE_MSG:",
|
||||
"MinimumVersion-Client-TLS13-TLS12": ":INCOMPATIBLE:",
|
||||
"MinimumVersion-Client2-TLS13-TLS12": ":INCOMPATIBLE:",
|
||||
"MinimumVersion-Server-TLS13-TLS12": ":INCOMPATIBLE:",
|
||||
"MinimumVersion-Server2-TLS13-TLS12": ":INCOMPATIBLE:",
|
||||
"DuplicateKeyShares": ":PEER_MISBEHAVIOUR:",
|
||||
"PartialEncryptedExtensionsWithServerHello": ":PEER_MISBEHAVIOUR:",
|
||||
"PartialClientFinishedWithClientHello": ":PEER_MISBEHAVIOUR:",
|
||||
"PointFormat-EncryptedExtensions-TLS13": ":PEER_MISBEHAVIOUR:",
|
||||
"Ticket-Forbidden-TLS13": ":PEER_MISBEHAVIOUR:",
|
||||
"PointFormat-Server-MissingUncompressed": ":INCOMPATIBLE:",
|
||||
"NegotiatePSKResumption-TLS13": ":PEER_MISBEHAVIOUR:",
|
||||
"PointFormat-Client-MissingUncompressed": ":PEER_MISBEHAVIOUR:",
|
||||
"SendUnsolicitedOCSPOnCertificate-TLS13": ":PEER_MISBEHAVIOUR:",
|
||||
"SendUnsolicitedSCTOnCertificate-TLS13": ":PEER_MISBEHAVIOUR:",
|
||||
"SendUnknownExtensionOnCertificate-TLS13": ":PEER_MISBEHAVIOUR:",
|
||||
"LargePlaintext": ":PEER_MISBEHAVIOUR:"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -74,7 +74,8 @@ impl Options {
|
|||
}
|
||||
|
||||
fn tls13_supported(&self) -> bool {
|
||||
self.support_tls13 && self.version_allowed(ProtocolVersion::TLSv1_3)
|
||||
self.support_tls13 && (self.version_allowed(ProtocolVersion::TLSv1_3) ||
|
||||
self.version_allowed(ProtocolVersion::Unknown(0x7f12)))
|
||||
}
|
||||
|
||||
fn tls12_supported(&self) -> bool {
|
||||
|
@ -139,6 +140,16 @@ fn make_server_cfg(opts: &Options) -> Arc<rustls::ServerConfig> {
|
|||
cfg.set_protocols(&opts.protocols);
|
||||
}
|
||||
|
||||
cfg.versions.clear();
|
||||
|
||||
if opts.tls12_supported() {
|
||||
cfg.versions.push(ProtocolVersion::TLSv1_2);
|
||||
}
|
||||
|
||||
if opts.tls13_supported() {
|
||||
cfg.versions.push(ProtocolVersion::TLSv1_3);
|
||||
}
|
||||
|
||||
Arc::new(cfg)
|
||||
}
|
||||
|
||||
|
|
|
@ -365,9 +365,9 @@ impl ClientSessionImpl {
|
|||
self.common.start_encryption_tls12(self.secrets.as_ref().unwrap());
|
||||
}
|
||||
|
||||
pub fn find_cipher_suite(&self, suite: &CipherSuite) -> Option<&'static SupportedCipherSuite> {
|
||||
pub fn find_cipher_suite(&self, suite: CipherSuite) -> Option<&'static SupportedCipherSuite> {
|
||||
for scs in &self.config.ciphersuites {
|
||||
if &scs.suite == suite {
|
||||
if scs.suite == suite {
|
||||
return Some(scs);
|
||||
}
|
||||
}
|
||||
|
|
216
src/client_hs.rs
216
src/client_hs.rs
|
@ -7,14 +7,14 @@ use msgs::handshake::{SessionID, Random, ServerHelloPayload};
|
|||
use msgs::handshake::{ClientExtension, ServerExtension, HasServerExtensions};
|
||||
use msgs::handshake::{SupportedSignatureSchemes, SupportedMandatedSignatureSchemes};
|
||||
use msgs::handshake::DecomposedSignatureScheme;
|
||||
use msgs::handshake::{NamedGroups, SupportedGroups, KeyShareEntry};
|
||||
use msgs::handshake::{NamedGroups, SupportedGroups, KeyShareEntry, EncryptedExtensions};
|
||||
use msgs::handshake::{ECPointFormatList, SupportedPointFormats};
|
||||
use msgs::handshake::{ProtocolNameList, ConvertProtocolNameList};
|
||||
use msgs::handshake::{CertificatePayloadTLS13, CertificateEntry};
|
||||
use msgs::handshake::ServerKeyExchangePayload;
|
||||
use msgs::handshake::DigitallySignedStruct;
|
||||
use msgs::handshake::{PresharedKeyIdentity, PresharedKeyOffer, HelloRetryRequest};
|
||||
use msgs::enums::{ClientCertificateType, PSKKeyExchangeMode};
|
||||
use msgs::enums::{ClientCertificateType, PSKKeyExchangeMode, ECPointFormat};
|
||||
use msgs::codec::Codec;
|
||||
use msgs::persist;
|
||||
use msgs::ccs::ChangeCipherSpecPayload;
|
||||
|
@ -66,6 +66,14 @@ fn ticket_timebase() -> u64 {
|
|||
time::get_time().sec as u64
|
||||
}
|
||||
|
||||
fn check_aligned_handshake(sess: &mut ClientSessionImpl) -> Result<(), TLSError> {
|
||||
if !sess.common.handshake_joiner.is_empty() {
|
||||
Err(illegal_param(sess, "keys changed with pending hs fragment"))
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn find_session(sess: &mut ClientSessionImpl) -> Option<persist::ClientSessionValue> {
|
||||
let key = persist::ClientSessionKey::session_for_dns_name(&sess.handshake_data.dns_name);
|
||||
let key_buf = key.get_encoding();
|
||||
|
@ -121,7 +129,7 @@ fn randomise_sessionid_for_ticket(csv: &mut persist::ClientSessionValue) {
|
|||
pub fn fill_in_psk_binder(sess: &mut ClientSessionImpl, hmp: &mut HandshakeMessagePayload) {
|
||||
// We need to know the hash function of the suite we're trying to resume into.
|
||||
let resuming = sess.handshake_data.resuming_session.as_ref().unwrap();
|
||||
let suite_hash = sess.find_cipher_suite(&resuming.cipher_suite).unwrap().get_hash();
|
||||
let suite_hash = sess.find_cipher_suite(resuming.cipher_suite).unwrap().get_hash();
|
||||
|
||||
// The binder is calculated over the clienthello, but doesn't include itself or its
|
||||
// length, or the length of its container.
|
||||
|
@ -213,7 +221,9 @@ fn emit_client_hello_for_retry(sess: &mut ClientSessionImpl,
|
|||
}
|
||||
|
||||
let mut exts = Vec::new();
|
||||
exts.push(ClientExtension::SupportedVersions(supported_versions));
|
||||
if !supported_versions.is_empty() {
|
||||
exts.push(ClientExtension::SupportedVersions(supported_versions));
|
||||
}
|
||||
exts.push(ClientExtension::make_sni(&sess.handshake_data.dns_name));
|
||||
exts.push(ClientExtension::ECPointFormats(ECPointFormatList::supported()));
|
||||
exts.push(ClientExtension::NamedGroups(NamedGroups::supported()));
|
||||
|
@ -228,7 +238,9 @@ fn emit_client_hello_for_retry(sess: &mut ClientSessionImpl,
|
|||
}
|
||||
|
||||
if support_tls13 && sess.config.enable_tickets {
|
||||
let psk_modes = vec![ PSKKeyExchangeMode::PSK_DHE_KE, PSKKeyExchangeMode::PSK_KE ];
|
||||
// We could support PSK_KE here too. Such connections don't
|
||||
// have forward secrecy, and are similar to TLS1.2 resumption.
|
||||
let psk_modes = vec![ PSKKeyExchangeMode::PSK_DHE_KE ];
|
||||
exts.push(ClientExtension::PresharedKeyModes(psk_modes));
|
||||
}
|
||||
|
||||
|
@ -253,7 +265,7 @@ fn emit_client_hello_for_retry(sess: &mut ClientSessionImpl,
|
|||
(resuming.get_obfuscated_ticket_age(ticket_timebase()), resuming.cipher_suite)
|
||||
};
|
||||
|
||||
let binder_len = sess.find_cipher_suite(&suite).unwrap().get_hash().output_len;
|
||||
let binder_len = sess.find_cipher_suite(suite).unwrap().get_hash().output_len;
|
||||
let binder = vec![0u8; binder_len];
|
||||
|
||||
let psk_identity = PresharedKeyIdentity::new(ticket, obfuscated_ticket_age);
|
||||
|
@ -327,6 +339,13 @@ fn sent_unsolicited_extensions(sess: &ClientSessionImpl,
|
|||
false
|
||||
}
|
||||
|
||||
fn has_key_share(sess: &mut ClientSessionImpl,
|
||||
group: NamedGroup) -> bool {
|
||||
sess.handshake_data.offered_key_shares
|
||||
.iter()
|
||||
.any(|share| share.group == group)
|
||||
}
|
||||
|
||||
fn find_key_share(sess: &mut ClientSessionImpl,
|
||||
group: NamedGroup)
|
||||
-> Option<suites::KeyExchange> {
|
||||
|
@ -349,21 +368,25 @@ fn find_key_share_and_discard_others(sess: &mut ClientSessionImpl,
|
|||
ret
|
||||
}
|
||||
|
||||
// Extensions we expect in plaintext in the ServerHello.
|
||||
static ALLOWED_PLAINTEXT_EXTS: &'static [ExtensionType] = &[
|
||||
ExtensionType::KeyShare,
|
||||
ExtensionType::PreSharedKey
|
||||
];
|
||||
|
||||
// Only the intersection of things we offer, and those disallowed
|
||||
// in TLS1.3
|
||||
static DISALLOWED_TLS13_EXTS: &'static [ExtensionType] = &[
|
||||
ExtensionType::ECPointFormats,
|
||||
ExtensionType::SessionTicket,
|
||||
ExtensionType::RenegotiationInfo
|
||||
];
|
||||
|
||||
fn validate_server_hello_tls13(sess: &mut ClientSessionImpl,
|
||||
server_hello: &ServerHelloPayload)
|
||||
-> Result<(), TLSError> {
|
||||
// This function applies TLS1.3-specific constraints to the
|
||||
// ServerHello:
|
||||
// - That it only contains ServerExtensions that can appear
|
||||
// in plaintext.
|
||||
|
||||
let allowed_plaintext_exts = &[
|
||||
ExtensionType::KeyShare,
|
||||
ExtensionType::PreSharedKey
|
||||
];
|
||||
|
||||
for ext in &server_hello.extensions {
|
||||
if !allowed_plaintext_exts.contains(&ext.get_type()) {
|
||||
if !ALLOWED_PLAINTEXT_EXTS.contains(&ext.get_type()) {
|
||||
sess.common.send_fatal_alert(AlertDescription::UnsupportedExtension);
|
||||
return Err(TLSError::PeerMisbehavedError("server sent unexpected cleartext ext"
|
||||
.to_string()));
|
||||
|
@ -380,14 +403,11 @@ fn start_handshake_traffic(sess: &mut ClientSessionImpl,
|
|||
let hash = suite.get_hash();
|
||||
let mut key_schedule = KeySchedule::new(hash);
|
||||
|
||||
// PSK_KE means allowing a missing server key_share
|
||||
// here, but critically only if resuming from something.
|
||||
let mut skip_key_share = false;
|
||||
|
||||
if let Some(selected_psk) = server_hello.get_psk_index() {
|
||||
if let Some(ref resuming) = sess.handshake_data.resuming_session {
|
||||
if suite.suite != resuming.cipher_suite {
|
||||
return Err(TLSError::PeerMisbehavedError("server resuming wrong suite"
|
||||
let resume_from_suite = sess.find_cipher_suite(resuming.cipher_suite).unwrap();
|
||||
if !resume_from_suite.can_resume_to(suite) {
|
||||
return Err(TLSError::PeerMisbehavedError("server resuming incompatible suite"
|
||||
.to_string()));
|
||||
}
|
||||
|
||||
|
@ -398,7 +418,6 @@ fn start_handshake_traffic(sess: &mut ClientSessionImpl,
|
|||
|
||||
info!("Resuming using PSK");
|
||||
key_schedule.input_secret(&resuming.master_secret.0);
|
||||
skip_key_share = server_hello.get_key_share().is_none();
|
||||
} else {
|
||||
return Err(TLSError::PeerMisbehavedError("server selected unoffered psk".to_string()));
|
||||
}
|
||||
|
@ -408,29 +427,26 @@ fn start_handshake_traffic(sess: &mut ClientSessionImpl,
|
|||
sess.handshake_data.resuming_session.take();
|
||||
}
|
||||
|
||||
if skip_key_share {
|
||||
info!("Server didn't contribute DH share");
|
||||
key_schedule.input_empty();
|
||||
} else {
|
||||
let their_key_share = try! {
|
||||
server_hello.get_key_share()
|
||||
.ok_or_else(|| {
|
||||
sess.common.send_fatal_alert(AlertDescription::MissingExtension);
|
||||
TLSError::PeerMisbehavedError("missing key share".to_string())
|
||||
})
|
||||
};
|
||||
let their_key_share = try! {
|
||||
server_hello.get_key_share()
|
||||
.ok_or_else(|| {
|
||||
sess.common.send_fatal_alert(AlertDescription::MissingExtension);
|
||||
TLSError::PeerMisbehavedError("missing key share".to_string())
|
||||
})
|
||||
};
|
||||
|
||||
let our_key_share = try!(find_key_share_and_discard_others(sess,
|
||||
their_key_share.group));
|
||||
let shared = try! {
|
||||
our_key_share.complete(&their_key_share.payload.0)
|
||||
.ok_or_else(|| TLSError::PeerMisbehavedError("key exchange failed"
|
||||
.to_string()))
|
||||
};
|
||||
let our_key_share = try!(find_key_share_and_discard_others(sess,
|
||||
their_key_share.group));
|
||||
let shared = try! {
|
||||
our_key_share.complete(&their_key_share.payload.0)
|
||||
.ok_or_else(|| TLSError::PeerMisbehavedError("key exchange failed"
|
||||
.to_string()))
|
||||
};
|
||||
|
||||
save_kx_hint(sess, their_key_share.group);
|
||||
key_schedule.input_secret(&shared.premaster_secret);
|
||||
}
|
||||
save_kx_hint(sess, their_key_share.group);
|
||||
key_schedule.input_secret(&shared.premaster_secret);
|
||||
|
||||
try!(check_aligned_handshake(sess));
|
||||
|
||||
let handshake_hash = sess.handshake_data.transcript.get_current_hash();
|
||||
let write_key = key_schedule.derive(SecretKind::ClientHandshakeTrafficSecret, &handshake_hash);
|
||||
|
@ -472,7 +488,7 @@ fn handle_server_hello(sess: &mut ClientSessionImpl, m: Message) -> Result<ConnS
|
|||
sess.common.is_tls13 = true;
|
||||
}
|
||||
_ => {
|
||||
sess.common.send_fatal_alert(AlertDescription::HandshakeFailure);
|
||||
sess.common.send_fatal_alert(AlertDescription::ProtocolVersion);
|
||||
return Err(TLSError::PeerIncompatibleError("server does not support TLS v1.2/v1.3"
|
||||
.to_string()));
|
||||
}
|
||||
|
@ -499,7 +515,17 @@ fn handle_server_hello(sess: &mut ClientSessionImpl, m: Message) -> Result<ConnS
|
|||
try!(process_alpn_protocol(sess, server_hello.get_alpn_protocol()));
|
||||
}
|
||||
|
||||
let scs = sess.find_cipher_suite(&server_hello.cipher_suite);
|
||||
// If ECPointFormats extension is supplied by the server, it must contain
|
||||
// Uncompressed. But it's allowed to be omitted.
|
||||
if let Some(point_fmts) = server_hello.get_ecpoints_extension() {
|
||||
if !point_fmts.contains(&ECPointFormat::Uncompressed) {
|
||||
sess.common.send_fatal_alert(AlertDescription::HandshakeFailure);
|
||||
return Err(TLSError::PeerMisbehavedError("server does not support uncompressed points"
|
||||
.to_string()));
|
||||
}
|
||||
}
|
||||
|
||||
let scs = sess.find_cipher_suite(server_hello.cipher_suite);
|
||||
|
||||
if scs.is_none() {
|
||||
sess.common.send_fatal_alert(AlertDescription::HandshakeFailure);
|
||||
|
@ -590,10 +616,53 @@ fn handle_hello_retry_request(sess: &mut ClientSessionImpl,
|
|||
sess.handshake_data.transcript.add_message(&m);
|
||||
debug!("Got HRR {:?}", hrr);
|
||||
|
||||
let has_cookie = hrr.get_cookie().is_some();
|
||||
let req_group = hrr.get_requested_key_share_group();
|
||||
|
||||
// A retry request is illegal if it contains no cookie and asks for
|
||||
// retry of a group we already sent.
|
||||
if !has_cookie && req_group.map(|g| has_key_share(sess, g)).unwrap_or(false) {
|
||||
return Err(illegal_param(sess, "server requested hrr with our group"));
|
||||
}
|
||||
|
||||
// Or asks for us to retry on an unsupported group.
|
||||
if let Some(group) = req_group {
|
||||
if !NamedGroups::supported().contains(&group) {
|
||||
return Err(illegal_param(sess, "server requested hrr with bad group"));
|
||||
}
|
||||
}
|
||||
|
||||
// Or has an empty cookie.
|
||||
if has_cookie && hrr.get_cookie().unwrap().len() == 0 {
|
||||
return Err(illegal_param(sess, "server requested hrr with empty cookie"));
|
||||
}
|
||||
|
||||
// Or has something unrecognised
|
||||
if hrr.has_unknown_extension() {
|
||||
sess.common.send_fatal_alert(AlertDescription::UnsupportedExtension);
|
||||
return Err(TLSError::PeerIncompatibleError("server sent hrr with unhandled extension"
|
||||
.to_string()));
|
||||
}
|
||||
|
||||
// Or has the same extensions more than once
|
||||
if hrr.has_duplicate_extension() {
|
||||
return Err(illegal_param(sess, "server send duplicate hrr extensions"));
|
||||
}
|
||||
|
||||
// Or asks us to change nothing.
|
||||
if !has_cookie && req_group.is_none() {
|
||||
return Err(illegal_param(sess, "server requested hrr with no changes"));
|
||||
}
|
||||
|
||||
// Or asks us to talk a protocol we didn't offer, or doesn't support HRR at all.
|
||||
match hrr.server_version {
|
||||
ProtocolVersion::TLSv1_3 | ProtocolVersion::Unknown(TLS13_DRAFT) => {
|
||||
}
|
||||
_ => {
|
||||
return Err(illegal_param(sess, "server requested unsupported version in hrr"));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(emit_client_hello_for_retry(sess, Some(hrr)))
|
||||
}
|
||||
|
||||
|
@ -615,13 +684,8 @@ pub static EXPECT_SERVER_HELLO_OR_RETRY: Handler = Handler {
|
|||
handle: handle_server_hello_or_retry,
|
||||
};
|
||||
|
||||
fn handle_encrypted_extensions(sess: &mut ClientSessionImpl,
|
||||
m: Message)
|
||||
-> Result<ConnState, TLSError> {
|
||||
let exts = extract_handshake!(m, HandshakePayload::EncryptedExtensions).unwrap();
|
||||
info!("TLS1.3 encrypted extensions: {:?}", exts);
|
||||
sess.handshake_data.transcript.add_message(&m);
|
||||
|
||||
fn validate_encrypted_extensions(sess: &mut ClientSessionImpl,
|
||||
exts: &EncryptedExtensions) -> Result<(), TLSError> {
|
||||
if exts.has_duplicate_extension() {
|
||||
sess.common.send_fatal_alert(AlertDescription::DecodeError);
|
||||
return Err(TLSError::PeerMisbehavedError("server sent duplicate encrypted extensions"
|
||||
|
@ -634,6 +698,26 @@ fn handle_encrypted_extensions(sess: &mut ClientSessionImpl,
|
|||
return Err(TLSError::PeerMisbehavedError(msg));
|
||||
}
|
||||
|
||||
for ext in exts {
|
||||
if ALLOWED_PLAINTEXT_EXTS.contains(&ext.get_type()) ||
|
||||
DISALLOWED_TLS13_EXTS.contains(&ext.get_type()) {
|
||||
sess.common.send_fatal_alert(AlertDescription::UnsupportedExtension);
|
||||
let msg = "server sent inappropriate encrypted extension".to_string();
|
||||
return Err(TLSError::PeerMisbehavedError(msg));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_encrypted_extensions(sess: &mut ClientSessionImpl,
|
||||
m: Message)
|
||||
-> Result<ConnState, TLSError> {
|
||||
let exts = extract_handshake!(m, HandshakePayload::EncryptedExtensions).unwrap();
|
||||
info!("TLS1.3 encrypted extensions: {:?}", exts);
|
||||
sess.handshake_data.transcript.add_message(&m);
|
||||
|
||||
try!(validate_encrypted_extensions(sess, exts));
|
||||
try!(process_alpn_protocol(sess, exts.get_alpn_protocol()));
|
||||
|
||||
if sess.handshake_data.resuming_session.is_some() {
|
||||
|
@ -656,6 +740,21 @@ fn handle_certificate(sess: &mut ClientSessionImpl, m: Message) -> Result<ConnSt
|
|||
|
||||
if sess.common.is_tls13 {
|
||||
let cert_chain = extract_handshake!(m, HandshakePayload::CertificateTLS13).unwrap();
|
||||
|
||||
// This is only non-empty for client auth.
|
||||
if cert_chain.context.len() > 0 {
|
||||
warn!("certificate with non-empty context during handshake");
|
||||
sess.common.send_fatal_alert(AlertDescription::DecodeError);
|
||||
return Err(TLSError::CorruptMessagePayload(ContentType::Handshake));
|
||||
}
|
||||
|
||||
if cert_chain.any_entry_has_duplicate_extension() ||
|
||||
cert_chain.any_entry_has_unknown_extension() {
|
||||
warn!("certificate chain contains unsolicited/unknown extension");
|
||||
sess.common.send_fatal_alert(AlertDescription::UnsupportedExtension);
|
||||
return Err(TLSError::PeerMisbehavedError("bad cert chain extensions".to_string()));
|
||||
}
|
||||
|
||||
sess.handshake_data.server_cert_chain = cert_chain.convert();
|
||||
Ok(ConnState::ExpectCertificateVerify)
|
||||
} else {
|
||||
|
@ -903,6 +1002,13 @@ fn handle_certificate_req_tls13(sess: &mut ClientSessionImpl,
|
|||
// Fortunately the problems here in TLS1.2 and prior are corrected in
|
||||
// TLS1.3.
|
||||
|
||||
// Must be empty during handshake.
|
||||
if certreq.context.len() > 0 {
|
||||
warn!("Server sent non-empty certreq context");
|
||||
sess.common.send_fatal_alert(AlertDescription::DecodeError);
|
||||
return Err(TLSError::CorruptMessagePayload(ContentType::Handshake));
|
||||
}
|
||||
|
||||
let tls13_sign_schemes = SupportedSignatureSchemes::supported_sign_tls13();
|
||||
let compat_sigschemes = certreq.sigschemes
|
||||
.iter()
|
||||
|
@ -911,6 +1017,7 @@ fn handle_certificate_req_tls13(sess: &mut ClientSessionImpl,
|
|||
.collect::<Vec<SignatureScheme>>();
|
||||
|
||||
if compat_sigschemes.is_empty() {
|
||||
sess.common.send_fatal_alert(AlertDescription::DecodeError);
|
||||
return Err(TLSError::PeerIncompatibleError("server sent bad certreq schemes".to_string()));
|
||||
}
|
||||
|
||||
|
@ -1275,6 +1382,7 @@ fn handle_finished_tls13(sess: &mut ClientSessionImpl, m: Message) -> Result<Con
|
|||
emit_finished_tls13(sess);
|
||||
|
||||
/* Now move to our application traffic keys. */
|
||||
try!(check_aligned_handshake(sess));
|
||||
let write_key = sess.common
|
||||
.get_key_schedule()
|
||||
.derive(SecretKind::ClientApplicationTrafficSecret, &handshake_hash);
|
||||
|
|
|
@ -862,6 +862,27 @@ impl ClientHelloPayload {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn has_keyshare_extension_with_duplicates(&self) -> bool {
|
||||
let entries = self.get_keyshare_extension();
|
||||
if entries.is_none() {
|
||||
return false;
|
||||
}
|
||||
|
||||
let mut seen = collections::HashSet::new();
|
||||
|
||||
for kse in entries.unwrap() {
|
||||
let grp = kse.group.get_u16();
|
||||
|
||||
if seen.contains(&grp) {
|
||||
return true;
|
||||
}
|
||||
|
||||
seen.insert(grp);
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
pub fn get_psk(&self) -> Option<&PresharedKeyOffer> {
|
||||
let ext = try_ret!(self.find_extension(ExtensionType::PreSharedKey));
|
||||
match *ext {
|
||||
|
@ -990,6 +1011,15 @@ impl HelloRetryRequest {
|
|||
false
|
||||
}
|
||||
|
||||
pub fn has_unknown_extension(&self) -> bool {
|
||||
self.extensions
|
||||
.iter()
|
||||
.any(|ext| {
|
||||
ext.get_type() != ExtensionType::KeyShare &&
|
||||
ext.get_type() != ExtensionType::Cookie
|
||||
})
|
||||
}
|
||||
|
||||
fn find_extension(&self, ext: ExtensionType) -> Option<&HelloRetryExtension> {
|
||||
self.extensions.iter().find(|x| x.get_type() == ext)
|
||||
}
|
||||
|
@ -1094,6 +1124,14 @@ impl ServerHelloPayload {
|
|||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_ecpoints_extension(&self) -> Option<&ECPointFormatList> {
|
||||
let ext = try_ret!(self.find_extension(ExtensionType::ECPointFormats));
|
||||
match *ext {
|
||||
ServerExtension::ECPointFormats(ref fmts) => Some(fmts),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub type ASN1Cert = key::Certificate; // PayloadU24;
|
||||
|
@ -1180,6 +1218,25 @@ impl CertificateEntry {
|
|||
exts: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
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 has_unknown_extension(&self) -> bool {
|
||||
!self.exts.is_empty()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -1210,6 +1267,26 @@ impl CertificatePayloadTLS13 {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn any_entry_has_duplicate_extension(&self) -> bool {
|
||||
for ent in &self.list {
|
||||
if ent.has_duplicate_extension() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
pub fn any_entry_has_unknown_extension(&self) -> bool {
|
||||
for ent in &self.list {
|
||||
if ent.has_unknown_extension() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
pub fn convert(&self) -> CertificatePayload {
|
||||
let mut ret = Vec::new();
|
||||
for entry in &self.list {
|
||||
|
|
|
@ -260,10 +260,15 @@ fn illegal_param(sess: &mut ServerSessionImpl, why: &str) -> TLSError {
|
|||
TLSError::PeerMisbehavedError(why.to_string())
|
||||
}
|
||||
|
||||
fn decode_error(sess: &mut ServerSessionImpl, why: &str) -> TLSError {
|
||||
sess.common.send_fatal_alert(AlertDescription::DecodeError);
|
||||
TLSError::PeerMisbehavedError(why.to_string())
|
||||
}
|
||||
|
||||
fn can_resume(sess: &ServerSessionImpl,
|
||||
resumedata: &Option<persist::ServerSessionValue>) -> bool {
|
||||
// The RFCs underspecify what happens if we try to resume to
|
||||
// an unoffered/varying suite. We just don't resume if offered this.
|
||||
// an unoffered/varying suite. We merely don't resume in weird cases.
|
||||
resumedata.is_some() &&
|
||||
resumedata.as_ref().unwrap().cipher_suite == sess.common.get_suite().suite
|
||||
}
|
||||
|
@ -292,6 +297,18 @@ fn start_resumption(sess: &mut ServerSessionImpl,
|
|||
return Ok(ConnState::ExpectCCS);
|
||||
}
|
||||
|
||||
// Changing the keys must not span any fragmented handshake
|
||||
// messages. Otherwise the defragmented messages will have
|
||||
// been protected with two different record layer protections,
|
||||
// which is illegal. Not mentioned in RFC.
|
||||
fn check_aligned_handshake(sess: &mut ServerSessionImpl) -> Result<(), TLSError> {
|
||||
if !sess.common.handshake_joiner.is_empty() {
|
||||
Err(illegal_param(sess, "keys changed with pending hs fragment"))
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn emit_server_hello_tls13(sess: &mut ServerSessionImpl,
|
||||
share: &KeyShareEntry,
|
||||
chosen_psk_idx: Option<usize>,
|
||||
|
@ -329,6 +346,8 @@ fn emit_server_hello_tls13(sess: &mut ServerSessionImpl,
|
|||
}),
|
||||
};
|
||||
|
||||
try!(check_aligned_handshake(sess));
|
||||
|
||||
debug!("sending server hello {:?}", sh);
|
||||
sess.handshake_data.transcript.add_message(&sh);
|
||||
sess.common.send_msg(sh, false);
|
||||
|
@ -504,7 +523,7 @@ fn emit_finished_tls13(sess: &mut ServerSessionImpl) {
|
|||
sess.handshake_data.hash_at_server_fin = sess.handshake_data.transcript.get_current_hash();
|
||||
sess.common.send_msg(m, true);
|
||||
|
||||
/* Now move to application data keys. */
|
||||
// Now move to application data keys.
|
||||
sess.common.get_mut_key_schedule().input_empty();
|
||||
let write_key = sess.common
|
||||
.get_key_schedule()
|
||||
|
@ -566,6 +585,10 @@ fn handle_client_hello_tls13(sess: &mut ServerSessionImpl,
|
|||
let shares_ext = try!(client_hello.get_keyshare_extension()
|
||||
.ok_or_else(|| incompatible(sess, "client didn't send keyshares")));
|
||||
|
||||
if client_hello.has_keyshare_extension_with_duplicates() {
|
||||
return Err(illegal_param(sess, "client sent duplicate keyshares"));
|
||||
}
|
||||
|
||||
let share_groups: Vec<NamedGroup> = shares_ext.iter()
|
||||
.map(|share| share.group)
|
||||
.collect();
|
||||
|
@ -602,6 +625,10 @@ fn handle_client_hello_tls13(sess: &mut ServerSessionImpl,
|
|||
return Err(illegal_param(sess, "psk extension in wrong position"));
|
||||
}
|
||||
|
||||
if psk_offer.binders.len() == 0 {
|
||||
return Err(decode_error(sess, "psk extension missing binder"));
|
||||
}
|
||||
|
||||
if psk_offer.binders.len() != psk_offer.identities.len() {
|
||||
return Err(illegal_param(sess, "psk extension mismatched ids/binders"));
|
||||
}
|
||||
|
@ -648,6 +675,7 @@ fn handle_client_hello_tls13(sess: &mut ServerSessionImpl,
|
|||
emit_certificate_tls13(sess);
|
||||
try!(emit_certificate_verify_tls13(sess, &sigschemes_ext, signer));
|
||||
}
|
||||
try!(check_aligned_handshake(sess));
|
||||
emit_finished_tls13(sess);
|
||||
|
||||
if sess.handshake_data.doing_client_auth && full_handshake {
|
||||
|
@ -659,6 +687,9 @@ fn handle_client_hello_tls13(sess: &mut ServerSessionImpl,
|
|||
|
||||
fn handle_client_hello(sess: &mut ServerSessionImpl, m: Message) -> Result<ConnState, TLSError> {
|
||||
let client_hello = extract_handshake!(m, HandshakePayload::ClientHello).unwrap();
|
||||
let tls13_enabled = sess.config.versions.contains(&ProtocolVersion::TLSv1_3);
|
||||
let tls12_enabled = sess.config.versions.contains(&ProtocolVersion::TLSv1_2);
|
||||
debug!("we got a clienthello {:?}", client_hello);
|
||||
|
||||
if client_hello.client_version.get_u16() < ProtocolVersion::TLSv1_2.get_u16() {
|
||||
sess.common.send_fatal_alert(AlertDescription::ProtocolVersion);
|
||||
|
@ -672,26 +703,26 @@ fn handle_client_hello(sess: &mut ServerSessionImpl, m: Message) -> Result<ConnS
|
|||
}
|
||||
|
||||
if client_hello.has_duplicate_extension() {
|
||||
sess.common.send_fatal_alert(AlertDescription::DecodeError);
|
||||
return Err(TLSError::PeerMisbehavedError("client sent duplicate extensions".to_string()));
|
||||
return Err(decode_error(sess, "client sent duplicate extensions"));
|
||||
}
|
||||
|
||||
// Are we doing TLS1.3?
|
||||
let maybe_versions_ext = client_hello.get_versions_extension();
|
||||
if let Some(versions) = maybe_versions_ext {
|
||||
let tls13_enabled = sess.config.versions.contains(&ProtocolVersion::TLSv1_3);
|
||||
let tls12_enabled = sess.config.versions.contains(&ProtocolVersion::TLSv1_2);
|
||||
|
||||
if versions.contains(&ProtocolVersion::Unknown(0x7f12)) && tls13_enabled {
|
||||
sess.common.is_tls13 = true;
|
||||
} else if !versions.contains(&ProtocolVersion::TLSv1_2) || !tls12_enabled {
|
||||
sess.common.send_fatal_alert(AlertDescription::ProtocolVersion);
|
||||
return Err(incompatible(sess, "TLS1.2 not offered/enabled"));
|
||||
}
|
||||
} else {
|
||||
if !tls12_enabled && tls13_enabled {
|
||||
sess.common.send_fatal_alert(AlertDescription::ProtocolVersion);
|
||||
return Err(incompatible(sess, "Server requires TLS1.3, but client omitted versions ext"));
|
||||
}
|
||||
}
|
||||
|
||||
// Common to TLS1.2 and TLS1.3: ciphersuite and certificate selection.
|
||||
debug!("we got a clienthello {:?}", client_hello);
|
||||
|
||||
let default_sigschemes_ext = SupportedSignatureSchemes::default();
|
||||
|
||||
let sni_ext = client_hello.get_sni_extension();
|
||||
|
@ -1244,14 +1275,15 @@ fn handle_finished_tls13(sess: &mut ServerSessionImpl, m: Message) -> Result<Con
|
|||
// main application data keying.
|
||||
sess.handshake_data.transcript.add_message(&m);
|
||||
|
||||
/* Now move to using application data keys for client traffic.
|
||||
* Server traffic is already done. */
|
||||
// Now move to using application data keys for client traffic.
|
||||
// Server traffic is already done.
|
||||
let read_key = sess.common
|
||||
.get_key_schedule()
|
||||
.derive(SecretKind::ClientApplicationTrafficSecret,
|
||||
&sess.handshake_data.hash_at_server_fin);
|
||||
|
||||
let suite = sess.common.get_suite();
|
||||
try!(check_aligned_handshake(sess));
|
||||
sess.common.set_message_decrypter(cipher::new_tls13_read(suite, &read_key));
|
||||
sess.common
|
||||
.get_mut_key_schedule()
|
||||
|
|
|
@ -214,6 +214,24 @@ impl SupportedCipherSuite {
|
|||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Can a session using suite self resume using suite new_suite?
|
||||
pub fn can_resume_to(&self, new_suite: &SupportedCipherSuite) -> bool {
|
||||
if self.usable_for_version(ProtocolVersion::TLSv1_3) &&
|
||||
new_suite.usable_for_version(ProtocolVersion::TLSv1_3) {
|
||||
// TLS1.3 actually specifies requirements here: suites are compatible
|
||||
// for resumption if they have the same KDF hash
|
||||
self.hash == new_suite.hash
|
||||
} else if self.usable_for_version(ProtocolVersion::TLSv1_2) &&
|
||||
new_suite.usable_for_version(ProtocolVersion::TLSv1_2) {
|
||||
// Previous versions don't specify any constraint, so we don't
|
||||
// resume between suites to avoid bad interactions.
|
||||
self.suite == new_suite.suite
|
||||
} else {
|
||||
// Suites for different versions definitely can't resume!
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub static TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256: SupportedCipherSuite =
|
||||
|
|
Loading…
Reference in New Issue