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:
Joseph Birr-Pixton 2017-01-22 17:10:53 +00:00
parent a82414ed67
commit b7f0a7d9e3
7 changed files with 343 additions and 69 deletions

View File

@ -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:"
}
}

View File

@ -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)
}

View File

@ -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);
}
}

View File

@ -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);

View File

@ -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 {

View File

@ -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()

View File

@ -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 =