mirror of https://github.com/ctz/rustls
2293 lines
70 KiB
Rust
2293 lines
70 KiB
Rust
use crate::msgs::enums::{ProtocolVersion, HandshakeType};
|
|
use crate::msgs::enums::{CipherSuite, Compression, ExtensionType, ECPointFormat};
|
|
use crate::msgs::enums::{HashAlgorithm, SignatureAlgorithm, ServerNameType};
|
|
use crate::msgs::enums::{SignatureScheme, KeyUpdateRequest, NamedGroup};
|
|
use crate::msgs::enums::{ClientCertificateType, CertificateStatusType};
|
|
use crate::msgs::enums::ECCurveType;
|
|
use crate::msgs::enums::PSKKeyExchangeMode;
|
|
use crate::msgs::base::{Payload, PayloadU8, PayloadU16, PayloadU24};
|
|
use crate::msgs::codec;
|
|
use crate::msgs::codec::{Codec, Reader};
|
|
use crate::key;
|
|
|
|
#[cfg(feature = "logging")]
|
|
use crate::log::warn;
|
|
|
|
use std::fmt;
|
|
use std::io::Write;
|
|
use std::collections;
|
|
use std::mem;
|
|
use webpki;
|
|
|
|
macro_rules! declare_u8_vec(
|
|
($name:ident, $itemtype:ty) => {
|
|
pub type $name = Vec<$itemtype>;
|
|
|
|
impl Codec for $name {
|
|
fn encode(&self, bytes: &mut Vec<u8>) {
|
|
codec::encode_vec_u8(bytes, self);
|
|
}
|
|
|
|
fn read(r: &mut Reader) -> Option<$name> {
|
|
codec::read_vec_u8::<$itemtype>(r)
|
|
}
|
|
}
|
|
}
|
|
);
|
|
|
|
macro_rules! declare_u16_vec(
|
|
($name:ident, $itemtype:ty) => {
|
|
pub type $name = Vec<$itemtype>;
|
|
|
|
impl Codec for $name {
|
|
fn encode(&self, bytes: &mut Vec<u8>) {
|
|
codec::encode_vec_u16(bytes, self);
|
|
}
|
|
|
|
fn read(r: &mut Reader) -> Option<$name> {
|
|
codec::read_vec_u16::<$itemtype>(r)
|
|
}
|
|
}
|
|
}
|
|
);
|
|
|
|
declare_u16_vec!(VecU16OfPayloadU8, PayloadU8);
|
|
declare_u16_vec!(VecU16OfPayloadU16, PayloadU16);
|
|
|
|
#[derive(Debug, PartialEq, Clone)]
|
|
pub struct Random([u8; 32]);
|
|
|
|
static HELLO_RETRY_REQUEST_RANDOM: Random = Random([
|
|
0xcf, 0x21, 0xad, 0x74, 0xe5, 0x9a, 0x61, 0x11,
|
|
0xbe, 0x1d, 0x8c, 0x02, 0x1e, 0x65, 0xb8, 0x91,
|
|
0xc2, 0xa2, 0x11, 0x16, 0x7a, 0xbb, 0x8c, 0x5e,
|
|
0x07, 0x9e, 0x09, 0xe2, 0xc8, 0xa8, 0x33, 0x9c,
|
|
]);
|
|
|
|
static ZERO_RANDOM: Random = Random([0u8; 32]);
|
|
|
|
impl Codec for Random {
|
|
fn encode(&self, bytes: &mut Vec<u8>) {
|
|
bytes.extend_from_slice(&self.0);
|
|
}
|
|
|
|
fn read(r: &mut Reader) -> Option<Random> {
|
|
let bytes = r.take(32)?;
|
|
let mut opaque = [0; 32];
|
|
opaque.clone_from_slice(bytes);
|
|
|
|
Some(Random(opaque))
|
|
}
|
|
}
|
|
|
|
impl Random {
|
|
pub fn from_slice(bytes: &[u8]) -> Random {
|
|
let mut rd = Reader::init(bytes);
|
|
Random::read(&mut rd).unwrap()
|
|
}
|
|
|
|
pub fn write_slice(&self, mut bytes: &mut [u8]) {
|
|
let buf = self.get_encoding();
|
|
bytes.write_all(&buf).unwrap();
|
|
}
|
|
}
|
|
|
|
#[derive(Copy, Clone)]
|
|
pub struct SessionID {
|
|
len: usize,
|
|
data: [u8; 32],
|
|
}
|
|
|
|
impl fmt::Debug for SessionID {
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
let mut t = f.debug_tuple("SessionID");
|
|
for i in 0..self.len() {
|
|
t.field(&self.data[i]);
|
|
}
|
|
t.finish()
|
|
}
|
|
}
|
|
|
|
impl PartialEq for SessionID {
|
|
fn eq(&self, other: &Self) -> bool {
|
|
if self.len != other.len {
|
|
return false;
|
|
}
|
|
|
|
let mut diff = 0u8;
|
|
for i in 0..self.len {
|
|
diff |= self.data[i] ^ other.data[i]
|
|
}
|
|
|
|
diff == 0u8
|
|
}
|
|
}
|
|
|
|
impl Codec for SessionID {
|
|
fn encode(&self, bytes: &mut Vec<u8>) {
|
|
debug_assert!(self.len <= 32);
|
|
bytes.push(self.len as u8);
|
|
bytes.extend_from_slice(&self.data[..self.len]);
|
|
}
|
|
|
|
fn read(r: &mut Reader) -> Option<SessionID> {
|
|
let len = u8::read(r)? as usize;
|
|
if len > 32 {
|
|
return None;
|
|
}
|
|
|
|
let bytes = r.take(len)?;
|
|
let mut out = [0u8; 32];
|
|
out[..len].clone_from_slice(&bytes[..len]);
|
|
|
|
Some(SessionID {
|
|
data: out,
|
|
len,
|
|
})
|
|
}
|
|
}
|
|
|
|
impl SessionID {
|
|
pub fn new(bytes: &[u8]) -> SessionID {
|
|
debug_assert!(bytes.len() <= 32);
|
|
let mut d = [0u8; 32];
|
|
d[..bytes.len()].clone_from_slice(&bytes[..]);
|
|
|
|
SessionID {
|
|
data: d,
|
|
len: bytes.len(),
|
|
}
|
|
}
|
|
|
|
pub fn empty() -> SessionID {
|
|
SessionID {
|
|
data: [0u8; 32],
|
|
len: 0,
|
|
}
|
|
}
|
|
|
|
pub fn len(&self) -> usize {
|
|
self.len
|
|
}
|
|
|
|
pub fn is_empty(&self) -> bool {
|
|
self.len == 0
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Debug)]
|
|
pub struct UnknownExtension {
|
|
pub typ: ExtensionType,
|
|
pub payload: Payload,
|
|
}
|
|
|
|
impl UnknownExtension {
|
|
fn encode(&self, bytes: &mut Vec<u8>) {
|
|
self.payload.encode(bytes);
|
|
}
|
|
|
|
fn read(typ: ExtensionType, r: &mut Reader) -> Option<UnknownExtension> {
|
|
let payload = Payload::read(r)?;
|
|
Some(UnknownExtension {
|
|
typ,
|
|
payload,
|
|
})
|
|
}
|
|
}
|
|
|
|
declare_u8_vec!(ECPointFormatList, ECPointFormat);
|
|
|
|
pub trait SupportedPointFormats {
|
|
fn supported() -> ECPointFormatList;
|
|
}
|
|
|
|
impl SupportedPointFormats for ECPointFormatList {
|
|
fn supported() -> ECPointFormatList {
|
|
vec![ECPointFormat::Uncompressed]
|
|
}
|
|
}
|
|
|
|
declare_u16_vec!(NamedGroups, NamedGroup);
|
|
|
|
declare_u16_vec!(SupportedSignatureSchemes, SignatureScheme);
|
|
|
|
pub trait DecomposedSignatureScheme {
|
|
fn sign(&self) -> SignatureAlgorithm;
|
|
fn make(alg: SignatureAlgorithm, hash: HashAlgorithm) -> SignatureScheme;
|
|
}
|
|
|
|
impl DecomposedSignatureScheme for SignatureScheme {
|
|
fn sign(&self) -> SignatureAlgorithm {
|
|
match *self {
|
|
SignatureScheme::RSA_PKCS1_SHA1 |
|
|
SignatureScheme::RSA_PKCS1_SHA256 |
|
|
SignatureScheme::RSA_PKCS1_SHA384 |
|
|
SignatureScheme::RSA_PKCS1_SHA512 |
|
|
SignatureScheme::RSA_PSS_SHA256 |
|
|
SignatureScheme::RSA_PSS_SHA384 |
|
|
SignatureScheme::RSA_PSS_SHA512 => SignatureAlgorithm::RSA,
|
|
SignatureScheme::ECDSA_NISTP256_SHA256 |
|
|
SignatureScheme::ECDSA_NISTP384_SHA384 |
|
|
SignatureScheme::ECDSA_NISTP521_SHA512 => SignatureAlgorithm::ECDSA,
|
|
_ => SignatureAlgorithm::Unknown(0),
|
|
}
|
|
}
|
|
|
|
fn make(alg: SignatureAlgorithm, hash: HashAlgorithm) -> SignatureScheme {
|
|
use crate::msgs::enums::SignatureAlgorithm::{RSA, ECDSA};
|
|
use crate::msgs::enums::HashAlgorithm::{SHA1, SHA256, SHA384, SHA512};
|
|
|
|
match (alg, hash) {
|
|
(RSA, SHA1) => SignatureScheme::RSA_PKCS1_SHA1,
|
|
(RSA, SHA256) => SignatureScheme::RSA_PKCS1_SHA256,
|
|
(RSA, SHA384) => SignatureScheme::RSA_PKCS1_SHA384,
|
|
(RSA, SHA512) => SignatureScheme::RSA_PKCS1_SHA512,
|
|
(ECDSA, SHA256) => SignatureScheme::ECDSA_NISTP256_SHA256,
|
|
(ECDSA, SHA384) => SignatureScheme::ECDSA_NISTP384_SHA384,
|
|
(ECDSA, SHA512) => SignatureScheme::ECDSA_NISTP521_SHA512,
|
|
(_, _) => unreachable!(),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Debug)]
|
|
pub enum ServerNamePayload {
|
|
HostName(webpki::DNSName),
|
|
Unknown(Payload),
|
|
}
|
|
|
|
impl ServerNamePayload {
|
|
fn read_hostname(r: &mut Reader) -> Option<ServerNamePayload> {
|
|
let len = u16::read(r)? as usize;
|
|
let name = r.take(len)?;
|
|
let dns_name = match webpki::DNSNameRef::try_from_ascii(name) {
|
|
Ok(dns_name) => dns_name,
|
|
Err(_) => {
|
|
warn!("Illegal SNI hostname received {:?}", name);
|
|
return None;
|
|
}
|
|
};
|
|
Some(ServerNamePayload::HostName(dns_name.into()))
|
|
}
|
|
|
|
fn encode_hostname(name: webpki::DNSNameRef, bytes: &mut Vec<u8>) {
|
|
let dns_name_str: &str = name.into();
|
|
(dns_name_str.len() as u16).encode(bytes);
|
|
bytes.extend_from_slice(dns_name_str.as_bytes());
|
|
}
|
|
|
|
fn encode(&self, bytes: &mut Vec<u8>) {
|
|
match *self {
|
|
ServerNamePayload::HostName(ref r) => ServerNamePayload::encode_hostname(r.as_ref(), bytes),
|
|
ServerNamePayload::Unknown(ref r) => r.encode(bytes),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Debug)]
|
|
pub struct ServerName {
|
|
pub typ: ServerNameType,
|
|
pub payload: ServerNamePayload,
|
|
}
|
|
|
|
impl Codec for ServerName {
|
|
fn encode(&self, bytes: &mut Vec<u8>) {
|
|
self.typ.encode(bytes);
|
|
self.payload.encode(bytes);
|
|
}
|
|
|
|
fn read(r: &mut Reader) -> Option<ServerName> {
|
|
let typ = ServerNameType::read(r)?;
|
|
|
|
let payload = match typ {
|
|
ServerNameType::HostName => ServerNamePayload::read_hostname(r)?,
|
|
_ => ServerNamePayload::Unknown(Payload::read(r)?),
|
|
};
|
|
|
|
Some(ServerName {
|
|
typ,
|
|
payload,
|
|
})
|
|
}
|
|
}
|
|
|
|
declare_u16_vec!(ServerNameRequest, ServerName);
|
|
|
|
pub trait ConvertServerNameList {
|
|
fn get_hostname(&self) -> Option<webpki::DNSNameRef>;
|
|
}
|
|
|
|
impl ConvertServerNameList for ServerNameRequest {
|
|
fn get_hostname(&self) -> Option<webpki::DNSNameRef> {
|
|
for name in self {
|
|
if let ServerNamePayload::HostName(ref dns_name) = name.payload {
|
|
return Some(dns_name.as_ref());
|
|
}
|
|
}
|
|
|
|
None
|
|
}
|
|
}
|
|
|
|
pub type ProtocolNameList = VecU16OfPayloadU8;
|
|
|
|
pub trait ConvertProtocolNameList {
|
|
fn from_slices(names: &[&[u8]]) -> Self;
|
|
fn to_vecs(&self) -> Vec<Vec<u8>>;
|
|
fn as_single_slice(&self) -> Option<&[u8]>;
|
|
}
|
|
|
|
impl ConvertProtocolNameList for ProtocolNameList {
|
|
fn from_slices(names: &[&[u8]]) -> ProtocolNameList {
|
|
let mut ret = Vec::new();
|
|
|
|
for name in names {
|
|
ret.push(PayloadU8::new(name.to_vec()));
|
|
}
|
|
|
|
ret
|
|
}
|
|
|
|
fn to_vecs(&self) -> Vec<Vec<u8>> {
|
|
let mut ret = Vec::new();
|
|
for proto in self {
|
|
ret.push(proto.0.clone());
|
|
}
|
|
ret
|
|
}
|
|
|
|
fn as_single_slice(&self) -> Option<&[u8]> {
|
|
if self.len() == 1 {
|
|
Some(&self[0].0)
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
}
|
|
|
|
// --- TLS 1.3 Key shares ---
|
|
#[derive(Clone, Debug)]
|
|
pub struct KeyShareEntry {
|
|
pub group: NamedGroup,
|
|
pub payload: PayloadU16,
|
|
}
|
|
|
|
impl KeyShareEntry {
|
|
pub fn new(group: NamedGroup, payload: &[u8]) -> KeyShareEntry {
|
|
KeyShareEntry {
|
|
group,
|
|
payload: PayloadU16::new(payload.to_vec()),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Codec for KeyShareEntry {
|
|
fn encode(&self, bytes: &mut Vec<u8>) {
|
|
self.group.encode(bytes);
|
|
self.payload.encode(bytes);
|
|
}
|
|
|
|
fn read(r: &mut Reader) -> Option<KeyShareEntry> {
|
|
let group = NamedGroup::read(r)?;
|
|
let payload = PayloadU16::read(r)?;
|
|
|
|
Some(KeyShareEntry {
|
|
group,
|
|
payload,
|
|
})
|
|
}
|
|
}
|
|
|
|
// --- TLS 1.3 PresharedKey offers ---
|
|
#[derive(Clone, Debug)]
|
|
pub struct PresharedKeyIdentity {
|
|
pub identity: PayloadU16,
|
|
pub obfuscated_ticket_age: u32,
|
|
}
|
|
|
|
impl PresharedKeyIdentity {
|
|
pub fn new(id: Vec<u8>, age: u32) -> PresharedKeyIdentity {
|
|
PresharedKeyIdentity {
|
|
identity: PayloadU16::new(id),
|
|
obfuscated_ticket_age: age,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Codec for PresharedKeyIdentity {
|
|
fn encode(&self, bytes: &mut Vec<u8>) {
|
|
self.identity.encode(bytes);
|
|
self.obfuscated_ticket_age.encode(bytes);
|
|
}
|
|
|
|
fn read(r: &mut Reader) -> Option<PresharedKeyIdentity> {
|
|
Some(PresharedKeyIdentity {
|
|
identity: PayloadU16::read(r)?,
|
|
obfuscated_ticket_age: u32::read(r)?,
|
|
})
|
|
}
|
|
}
|
|
|
|
declare_u16_vec!(PresharedKeyIdentities, PresharedKeyIdentity);
|
|
pub type PresharedKeyBinder = PayloadU8;
|
|
pub type PresharedKeyBinders = VecU16OfPayloadU8;
|
|
|
|
#[derive(Clone, Debug)]
|
|
pub struct PresharedKeyOffer {
|
|
pub identities: PresharedKeyIdentities,
|
|
pub binders: PresharedKeyBinders,
|
|
}
|
|
|
|
impl PresharedKeyOffer {
|
|
/// Make a new one with one entry.
|
|
pub fn new(id: PresharedKeyIdentity, binder: Vec<u8>) -> PresharedKeyOffer {
|
|
PresharedKeyOffer {
|
|
identities: vec![ id ],
|
|
binders: vec![ PresharedKeyBinder::new(binder) ],
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Codec for PresharedKeyOffer {
|
|
fn encode(&self, bytes: &mut Vec<u8>) {
|
|
self.identities.encode(bytes);
|
|
self.binders.encode(bytes);
|
|
}
|
|
|
|
fn read(r: &mut Reader) -> Option<PresharedKeyOffer> {
|
|
Some(PresharedKeyOffer {
|
|
identities: PresharedKeyIdentities::read(r)?,
|
|
binders: PresharedKeyBinders::read(r)?,
|
|
})
|
|
}
|
|
}
|
|
|
|
// --- RFC6066 certificate status request ---
|
|
type ResponderIDs = VecU16OfPayloadU16;
|
|
|
|
#[derive(Clone, Debug)]
|
|
pub struct OCSPCertificateStatusRequest {
|
|
pub responder_ids: ResponderIDs,
|
|
pub extensions: PayloadU16,
|
|
}
|
|
|
|
impl Codec for OCSPCertificateStatusRequest {
|
|
fn encode(&self, bytes: &mut Vec<u8>) {
|
|
CertificateStatusType::OCSP.encode(bytes);
|
|
self.responder_ids.encode(bytes);
|
|
self.extensions.encode(bytes);
|
|
}
|
|
|
|
fn read(r: &mut Reader) -> Option<OCSPCertificateStatusRequest> {
|
|
Some(OCSPCertificateStatusRequest {
|
|
responder_ids: ResponderIDs::read(r)?,
|
|
extensions: PayloadU16::read(r)?,
|
|
})
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Debug)]
|
|
pub enum CertificateStatusRequest {
|
|
OCSP(OCSPCertificateStatusRequest),
|
|
Unknown((CertificateStatusType, Payload))
|
|
}
|
|
|
|
impl Codec for CertificateStatusRequest {
|
|
fn encode(&self, bytes: &mut Vec<u8>) {
|
|
match *self {
|
|
CertificateStatusRequest::OCSP(ref r) => r.encode(bytes),
|
|
CertificateStatusRequest::Unknown((typ, ref payload)) => {
|
|
typ.encode(bytes);
|
|
payload.encode(bytes);
|
|
}
|
|
}
|
|
}
|
|
|
|
fn read(r: &mut Reader) -> Option<CertificateStatusRequest> {
|
|
let typ = CertificateStatusType::read(r)?;
|
|
|
|
match typ {
|
|
CertificateStatusType::OCSP => {
|
|
let ocsp_req = OCSPCertificateStatusRequest::read(r)?;
|
|
Some(CertificateStatusRequest::OCSP(ocsp_req))
|
|
}
|
|
_ => {
|
|
let data = Payload::read(r)?;
|
|
Some(CertificateStatusRequest::Unknown((typ, data)))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
impl CertificateStatusRequest {
|
|
pub fn build_ocsp() -> CertificateStatusRequest {
|
|
let ocsp = OCSPCertificateStatusRequest {
|
|
responder_ids: ResponderIDs::new(),
|
|
extensions: PayloadU16::empty(),
|
|
};
|
|
CertificateStatusRequest::OCSP(ocsp)
|
|
}
|
|
}
|
|
|
|
// ---
|
|
// SCTs
|
|
|
|
pub type SCTList = VecU16OfPayloadU16;
|
|
|
|
// ---
|
|
|
|
declare_u8_vec!(PSKKeyExchangeModes, PSKKeyExchangeMode);
|
|
declare_u16_vec!(KeyShareEntries, KeyShareEntry);
|
|
declare_u8_vec!(ProtocolVersions, ProtocolVersion);
|
|
|
|
#[derive(Clone, Debug)]
|
|
pub enum ClientExtension {
|
|
ECPointFormats(ECPointFormatList),
|
|
NamedGroups(NamedGroups),
|
|
SignatureAlgorithms(SupportedSignatureSchemes),
|
|
ServerName(ServerNameRequest),
|
|
SessionTicketRequest,
|
|
SessionTicketOffer(Payload),
|
|
Protocols(ProtocolNameList),
|
|
SupportedVersions(ProtocolVersions),
|
|
KeyShare(KeyShareEntries),
|
|
PresharedKeyModes(PSKKeyExchangeModes),
|
|
PresharedKey(PresharedKeyOffer),
|
|
Cookie(PayloadU16),
|
|
ExtendedMasterSecretRequest,
|
|
CertificateStatusRequest(CertificateStatusRequest),
|
|
SignedCertificateTimestampRequest,
|
|
TransportParameters(Vec<u8>),
|
|
EarlyData,
|
|
Unknown(UnknownExtension),
|
|
}
|
|
|
|
impl ClientExtension {
|
|
pub fn get_type(&self) -> ExtensionType {
|
|
match *self {
|
|
ClientExtension::ECPointFormats(_) => ExtensionType::ECPointFormats,
|
|
ClientExtension::NamedGroups(_) => ExtensionType::EllipticCurves,
|
|
ClientExtension::SignatureAlgorithms(_) => ExtensionType::SignatureAlgorithms,
|
|
ClientExtension::ServerName(_) => ExtensionType::ServerName,
|
|
ClientExtension::SessionTicketRequest |
|
|
ClientExtension::SessionTicketOffer(_) => ExtensionType::SessionTicket,
|
|
ClientExtension::Protocols(_) => ExtensionType::ALProtocolNegotiation,
|
|
ClientExtension::SupportedVersions(_) => ExtensionType::SupportedVersions,
|
|
ClientExtension::KeyShare(_) => ExtensionType::KeyShare,
|
|
ClientExtension::PresharedKeyModes(_) => ExtensionType::PSKKeyExchangeModes,
|
|
ClientExtension::PresharedKey(_) => ExtensionType::PreSharedKey,
|
|
ClientExtension::Cookie(_) => ExtensionType::Cookie,
|
|
ClientExtension::ExtendedMasterSecretRequest => ExtensionType::ExtendedMasterSecret,
|
|
ClientExtension::CertificateStatusRequest(_) => ExtensionType::StatusRequest,
|
|
ClientExtension::SignedCertificateTimestampRequest => ExtensionType::SCT,
|
|
ClientExtension::TransportParameters(_) => ExtensionType::TransportParameters,
|
|
ClientExtension::EarlyData => ExtensionType::EarlyData,
|
|
ClientExtension::Unknown(ref r) => r.typ,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Codec for ClientExtension {
|
|
fn encode(&self, bytes: &mut Vec<u8>) {
|
|
self.get_type().encode(bytes);
|
|
|
|
let mut sub: Vec<u8> = Vec::new();
|
|
match *self {
|
|
ClientExtension::ECPointFormats(ref r) => r.encode(&mut sub),
|
|
ClientExtension::NamedGroups(ref r) => r.encode(&mut sub),
|
|
ClientExtension::SignatureAlgorithms(ref r) => r.encode(&mut sub),
|
|
ClientExtension::ServerName(ref r) => r.encode(&mut sub),
|
|
ClientExtension::SessionTicketRequest |
|
|
ClientExtension::ExtendedMasterSecretRequest |
|
|
ClientExtension::SignedCertificateTimestampRequest |
|
|
ClientExtension::EarlyData => (),
|
|
ClientExtension::SessionTicketOffer(ref r) => r.encode(&mut sub),
|
|
ClientExtension::Protocols(ref r) => r.encode(&mut sub),
|
|
ClientExtension::SupportedVersions(ref r) => r.encode(&mut sub),
|
|
ClientExtension::KeyShare(ref r) => r.encode(&mut sub),
|
|
ClientExtension::PresharedKeyModes(ref r) => r.encode(&mut sub),
|
|
ClientExtension::PresharedKey(ref r) => r.encode(&mut sub),
|
|
ClientExtension::Cookie(ref r) => r.encode(&mut sub),
|
|
ClientExtension::CertificateStatusRequest(ref r) => r.encode(&mut sub),
|
|
ClientExtension::TransportParameters(ref r) => sub.extend_from_slice(r),
|
|
ClientExtension::Unknown(ref r) => r.encode(&mut sub),
|
|
}
|
|
|
|
(sub.len() as u16).encode(bytes);
|
|
bytes.append(&mut sub);
|
|
}
|
|
|
|
fn read(r: &mut Reader) -> Option<ClientExtension> {
|
|
let typ = ExtensionType::read(r)?;
|
|
let len = u16::read(r)? as usize;
|
|
let mut sub = r.sub(len)?;
|
|
|
|
Some(match typ {
|
|
ExtensionType::ECPointFormats => {
|
|
ClientExtension::ECPointFormats(ECPointFormatList::read(&mut sub)?)
|
|
}
|
|
ExtensionType::EllipticCurves => {
|
|
ClientExtension::NamedGroups(NamedGroups::read(&mut sub)?)
|
|
}
|
|
ExtensionType::SignatureAlgorithms => {
|
|
let schemes = SupportedSignatureSchemes::read(&mut sub)?;
|
|
ClientExtension::SignatureAlgorithms(schemes)
|
|
}
|
|
ExtensionType::ServerName => {
|
|
ClientExtension::ServerName(ServerNameRequest::read(&mut sub)?)
|
|
}
|
|
ExtensionType::SessionTicket => {
|
|
if sub.any_left() {
|
|
ClientExtension::SessionTicketOffer(Payload::read(&mut sub)?)
|
|
} else {
|
|
ClientExtension::SessionTicketRequest
|
|
}
|
|
}
|
|
ExtensionType::ALProtocolNegotiation => {
|
|
ClientExtension::Protocols(ProtocolNameList::read(&mut sub)?)
|
|
}
|
|
ExtensionType::SupportedVersions => {
|
|
ClientExtension::SupportedVersions(ProtocolVersions::read(&mut sub)?)
|
|
}
|
|
ExtensionType::KeyShare => {
|
|
ClientExtension::KeyShare(KeyShareEntries::read(&mut sub)?)
|
|
}
|
|
ExtensionType::PSKKeyExchangeModes => {
|
|
ClientExtension::PresharedKeyModes(PSKKeyExchangeModes::read(&mut sub)?)
|
|
}
|
|
ExtensionType::PreSharedKey => {
|
|
ClientExtension::PresharedKey(PresharedKeyOffer::read(&mut sub)?)
|
|
}
|
|
ExtensionType::Cookie => ClientExtension::Cookie(PayloadU16::read(&mut sub)?),
|
|
ExtensionType::ExtendedMasterSecret if !sub.any_left() => {
|
|
ClientExtension::ExtendedMasterSecretRequest
|
|
}
|
|
ExtensionType::StatusRequest => {
|
|
let csr = CertificateStatusRequest::read(&mut sub)?;
|
|
ClientExtension::CertificateStatusRequest(csr)
|
|
}
|
|
ExtensionType::SCT if !sub.any_left() => {
|
|
ClientExtension::SignedCertificateTimestampRequest
|
|
}
|
|
ExtensionType::TransportParameters => {
|
|
ClientExtension::TransportParameters(sub.rest().to_vec())
|
|
}
|
|
ExtensionType::EarlyData if !sub.any_left() => {
|
|
ClientExtension::EarlyData
|
|
}
|
|
_ => ClientExtension::Unknown(UnknownExtension::read(typ, &mut sub)?),
|
|
})
|
|
}
|
|
}
|
|
|
|
impl ClientExtension {
|
|
/// Make a basic SNI ServerNameRequest quoting `hostname`.
|
|
pub fn make_sni(dns_name: webpki::DNSNameRef) -> ClientExtension {
|
|
let name = ServerName {
|
|
typ: ServerNameType::HostName,
|
|
payload: ServerNamePayload::HostName(dns_name.into()),
|
|
};
|
|
|
|
ClientExtension::ServerName(vec![ name ])
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Debug)]
|
|
pub enum ServerExtension {
|
|
ECPointFormats(ECPointFormatList),
|
|
ServerNameAck,
|
|
SessionTicketAck,
|
|
RenegotiationInfo(PayloadU8),
|
|
Protocols(ProtocolNameList),
|
|
KeyShare(KeyShareEntry),
|
|
PresharedKey(u16),
|
|
ExtendedMasterSecretAck,
|
|
CertificateStatusAck,
|
|
SignedCertificateTimestamp(SCTList),
|
|
SupportedVersions(ProtocolVersion),
|
|
TransportParameters(Vec<u8>),
|
|
EarlyData,
|
|
Unknown(UnknownExtension),
|
|
}
|
|
|
|
impl ServerExtension {
|
|
pub fn get_type(&self) -> ExtensionType {
|
|
match *self {
|
|
ServerExtension::ECPointFormats(_) => ExtensionType::ECPointFormats,
|
|
ServerExtension::ServerNameAck => ExtensionType::ServerName,
|
|
ServerExtension::SessionTicketAck => ExtensionType::SessionTicket,
|
|
ServerExtension::RenegotiationInfo(_) => ExtensionType::RenegotiationInfo,
|
|
ServerExtension::Protocols(_) => ExtensionType::ALProtocolNegotiation,
|
|
ServerExtension::KeyShare(_) => ExtensionType::KeyShare,
|
|
ServerExtension::PresharedKey(_) => ExtensionType::PreSharedKey,
|
|
ServerExtension::ExtendedMasterSecretAck => ExtensionType::ExtendedMasterSecret,
|
|
ServerExtension::CertificateStatusAck => ExtensionType::StatusRequest,
|
|
ServerExtension::SignedCertificateTimestamp(_) => ExtensionType::SCT,
|
|
ServerExtension::SupportedVersions(_) => ExtensionType::SupportedVersions,
|
|
ServerExtension::TransportParameters(_) => ExtensionType::TransportParameters,
|
|
ServerExtension::EarlyData => ExtensionType::EarlyData,
|
|
ServerExtension::Unknown(ref r) => r.typ,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Codec for ServerExtension {
|
|
fn encode(&self, bytes: &mut Vec<u8>) {
|
|
self.get_type().encode(bytes);
|
|
|
|
let mut sub: Vec<u8> = Vec::new();
|
|
match *self {
|
|
ServerExtension::ECPointFormats(ref r) => r.encode(&mut sub),
|
|
ServerExtension::ServerNameAck |
|
|
ServerExtension::SessionTicketAck |
|
|
ServerExtension::ExtendedMasterSecretAck |
|
|
ServerExtension::CertificateStatusAck |
|
|
ServerExtension::EarlyData => (),
|
|
ServerExtension::RenegotiationInfo(ref r) => r.encode(&mut sub),
|
|
ServerExtension::Protocols(ref r) => r.encode(&mut sub),
|
|
ServerExtension::KeyShare(ref r) => r.encode(&mut sub),
|
|
ServerExtension::PresharedKey(r) => r.encode(&mut sub),
|
|
ServerExtension::SignedCertificateTimestamp(ref r) => r.encode(&mut sub),
|
|
ServerExtension::SupportedVersions(ref r) => r.encode(&mut sub),
|
|
ServerExtension::TransportParameters(ref r) => sub.extend_from_slice(r),
|
|
ServerExtension::Unknown(ref r) => r.encode(&mut sub),
|
|
}
|
|
|
|
(sub.len() as u16).encode(bytes);
|
|
bytes.append(&mut sub);
|
|
}
|
|
|
|
fn read(r: &mut Reader) -> Option<ServerExtension> {
|
|
let typ = ExtensionType::read(r)?;
|
|
let len = u16::read(r)? as usize;
|
|
let mut sub = r.sub(len)?;
|
|
|
|
Some(match typ {
|
|
ExtensionType::ECPointFormats => {
|
|
ServerExtension::ECPointFormats(ECPointFormatList::read(&mut sub)?)
|
|
}
|
|
ExtensionType::ServerName => ServerExtension::ServerNameAck,
|
|
ExtensionType::SessionTicket => ServerExtension::SessionTicketAck,
|
|
ExtensionType::StatusRequest => ServerExtension::CertificateStatusAck,
|
|
ExtensionType::RenegotiationInfo => {
|
|
ServerExtension::RenegotiationInfo(PayloadU8::read(&mut sub)?)
|
|
}
|
|
ExtensionType::ALProtocolNegotiation => {
|
|
ServerExtension::Protocols(ProtocolNameList::read(&mut sub)?)
|
|
}
|
|
ExtensionType::KeyShare => {
|
|
ServerExtension::KeyShare(KeyShareEntry::read(&mut sub)?)
|
|
}
|
|
ExtensionType::PreSharedKey => {
|
|
ServerExtension::PresharedKey(u16::read(&mut sub)?)
|
|
}
|
|
ExtensionType::ExtendedMasterSecret => ServerExtension::ExtendedMasterSecretAck,
|
|
ExtensionType::SCT => {
|
|
let scts = SCTList::read(&mut sub)?;
|
|
ServerExtension::SignedCertificateTimestamp(scts)
|
|
}
|
|
ExtensionType::SupportedVersions => {
|
|
ServerExtension::SupportedVersions(ProtocolVersion::read(&mut sub)?)
|
|
}
|
|
ExtensionType::TransportParameters => {
|
|
ServerExtension::TransportParameters(sub.rest().to_vec())
|
|
}
|
|
ExtensionType::EarlyData => ServerExtension::EarlyData,
|
|
_ => ServerExtension::Unknown(UnknownExtension::read(typ, &mut sub)?),
|
|
})
|
|
}
|
|
}
|
|
|
|
impl ServerExtension {
|
|
pub fn make_alpn(proto: &[&[u8]]) -> ServerExtension {
|
|
ServerExtension::Protocols(ProtocolNameList::from_slices(proto))
|
|
}
|
|
|
|
pub fn make_empty_renegotiation_info() -> ServerExtension {
|
|
let empty = Vec::new();
|
|
ServerExtension::RenegotiationInfo(PayloadU8::new(empty))
|
|
}
|
|
|
|
pub fn make_sct(sctl: Vec<u8>) -> ServerExtension {
|
|
let scts = SCTList::read_bytes(&sctl)
|
|
.expect("invalid SCT list");
|
|
ServerExtension::SignedCertificateTimestamp(scts)
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct ClientHelloPayload {
|
|
pub client_version: ProtocolVersion,
|
|
pub random: Random,
|
|
pub session_id: SessionID,
|
|
pub cipher_suites: Vec<CipherSuite>,
|
|
pub compression_methods: Vec<Compression>,
|
|
pub extensions: Vec<ClientExtension>,
|
|
}
|
|
|
|
impl Codec for ClientHelloPayload {
|
|
fn encode(&self, bytes: &mut Vec<u8>) {
|
|
self.client_version.encode(bytes);
|
|
self.random.encode(bytes);
|
|
self.session_id.encode(bytes);
|
|
codec::encode_vec_u16(bytes, &self.cipher_suites);
|
|
codec::encode_vec_u8(bytes, &self.compression_methods);
|
|
|
|
if !self.extensions.is_empty() {
|
|
codec::encode_vec_u16(bytes, &self.extensions);
|
|
}
|
|
}
|
|
|
|
fn read(r: &mut Reader) -> Option<ClientHelloPayload> {
|
|
let mut ret = ClientHelloPayload {
|
|
client_version: ProtocolVersion::read(r)?,
|
|
random: Random::read(r)?,
|
|
session_id: SessionID::read(r)?,
|
|
cipher_suites: codec::read_vec_u16::<CipherSuite>(r)?,
|
|
compression_methods: codec::read_vec_u8::<Compression>(r)?,
|
|
extensions: Vec::new(),
|
|
};
|
|
|
|
if r.any_left() {
|
|
ret.extensions = codec::read_vec_u16::<ClientExtension>(r)?;
|
|
}
|
|
|
|
Some(ret)
|
|
}
|
|
}
|
|
|
|
impl ClientHelloPayload {
|
|
/// Returns true if there is more than one extension of a given
|
|
/// type.
|
|
pub fn has_duplicate_extension(&self) -> bool {
|
|
let mut seen = collections::HashSet::new();
|
|
|
|
for ext in &self.extensions {
|
|
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<&ClientExtension> {
|
|
self.extensions.iter().find(|x| x.get_type() == ext)
|
|
}
|
|
|
|
pub fn get_sni_extension(&self) -> Option<&ServerNameRequest> {
|
|
let ext = self.find_extension(ExtensionType::ServerName)?;
|
|
match *ext {
|
|
ClientExtension::ServerName(ref req) => Some(req),
|
|
_ => None,
|
|
}
|
|
}
|
|
|
|
pub fn get_sigalgs_extension(&self) -> Option<&SupportedSignatureSchemes> {
|
|
let ext = self.find_extension(ExtensionType::SignatureAlgorithms)?;
|
|
match *ext {
|
|
ClientExtension::SignatureAlgorithms(ref req) => Some(req),
|
|
_ => None,
|
|
}
|
|
}
|
|
|
|
pub fn get_namedgroups_extension(&self) -> Option<&NamedGroups> {
|
|
let ext = self.find_extension(ExtensionType::EllipticCurves)?;
|
|
match *ext {
|
|
ClientExtension::NamedGroups(ref req) => Some(req),
|
|
_ => None,
|
|
}
|
|
}
|
|
|
|
pub fn get_ecpoints_extension(&self) -> Option<&ECPointFormatList> {
|
|
let ext = self.find_extension(ExtensionType::ECPointFormats)?;
|
|
match *ext {
|
|
ClientExtension::ECPointFormats(ref req) => Some(req),
|
|
_ => None,
|
|
}
|
|
}
|
|
|
|
pub fn get_alpn_extension(&self) -> Option<&ProtocolNameList> {
|
|
let ext = self.find_extension(ExtensionType::ALProtocolNegotiation)?;
|
|
match *ext {
|
|
ClientExtension::Protocols(ref req) => Some(req),
|
|
_ => None,
|
|
}
|
|
}
|
|
|
|
pub fn get_quic_params_extension(&self) -> Option<Vec<u8>> {
|
|
let ext = self.find_extension(ExtensionType::TransportParameters)?;
|
|
match *ext {
|
|
ClientExtension::TransportParameters(ref bytes) => Some(bytes.to_vec()),
|
|
_ => None,
|
|
}
|
|
}
|
|
|
|
pub fn get_ticket_extension(&self) -> Option<&ClientExtension> {
|
|
self.find_extension(ExtensionType::SessionTicket)
|
|
}
|
|
|
|
pub fn get_versions_extension(&self) -> Option<&ProtocolVersions> {
|
|
let ext = self.find_extension(ExtensionType::SupportedVersions)?;
|
|
match *ext {
|
|
ClientExtension::SupportedVersions(ref vers) => Some(vers),
|
|
_ => None,
|
|
}
|
|
}
|
|
|
|
pub fn get_keyshare_extension(&self) -> Option<&KeyShareEntries> {
|
|
let ext = self.find_extension(ExtensionType::KeyShare)?;
|
|
match *ext {
|
|
ClientExtension::KeyShare(ref shares) => Some(shares),
|
|
_ => None,
|
|
}
|
|
}
|
|
|
|
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 = self.find_extension(ExtensionType::PreSharedKey)?;
|
|
match *ext {
|
|
ClientExtension::PresharedKey(ref psk) => Some(psk),
|
|
_ => None,
|
|
}
|
|
}
|
|
|
|
pub fn check_psk_ext_is_last(&self) -> bool {
|
|
self.extensions
|
|
.last()
|
|
.map_or(false, |ext| ext.get_type() == ExtensionType::PreSharedKey)
|
|
}
|
|
|
|
pub fn get_psk_modes(&self) -> Option<&PSKKeyExchangeModes> {
|
|
let ext = self.find_extension(ExtensionType::PSKKeyExchangeModes)?;
|
|
match *ext {
|
|
ClientExtension::PresharedKeyModes(ref psk_modes) => Some(psk_modes),
|
|
_ => None,
|
|
}
|
|
}
|
|
|
|
pub fn psk_mode_offered(&self, mode: PSKKeyExchangeMode) -> bool {
|
|
self.get_psk_modes()
|
|
.and_then(|modes| Some(modes.contains(&mode)))
|
|
.or(Some(false))
|
|
.unwrap()
|
|
}
|
|
|
|
|
|
pub fn set_psk_binder(&mut self, binder: Vec<u8>) {
|
|
let last_extension = self.extensions.last_mut().unwrap();
|
|
if let ClientExtension::PresharedKey(ref mut offer) = *last_extension {
|
|
offer.binders[0] = PresharedKeyBinder::new(binder);
|
|
}
|
|
}
|
|
|
|
pub fn ems_support_offered(&self) -> bool {
|
|
self.find_extension(ExtensionType::ExtendedMasterSecret)
|
|
.is_some()
|
|
}
|
|
|
|
pub fn early_data_extension_offered(&self) -> bool {
|
|
self.find_extension(ExtensionType::EarlyData).is_some()
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub enum HelloRetryExtension {
|
|
KeyShare(NamedGroup),
|
|
Cookie(PayloadU16),
|
|
SupportedVersions(ProtocolVersion),
|
|
Unknown(UnknownExtension),
|
|
}
|
|
|
|
impl HelloRetryExtension {
|
|
pub fn get_type(&self) -> ExtensionType {
|
|
match *self {
|
|
HelloRetryExtension::KeyShare(_) => ExtensionType::KeyShare,
|
|
HelloRetryExtension::Cookie(_) => ExtensionType::Cookie,
|
|
HelloRetryExtension::SupportedVersions(_) => ExtensionType::SupportedVersions,
|
|
HelloRetryExtension::Unknown(ref r) => r.typ,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Codec for HelloRetryExtension {
|
|
fn encode(&self, bytes: &mut Vec<u8>) {
|
|
self.get_type().encode(bytes);
|
|
|
|
let mut sub: Vec<u8> = Vec::new();
|
|
match *self {
|
|
HelloRetryExtension::KeyShare(ref r) => r.encode(&mut sub),
|
|
HelloRetryExtension::Cookie(ref r) => r.encode(&mut sub),
|
|
HelloRetryExtension::SupportedVersions(ref r) => r.encode(&mut sub),
|
|
HelloRetryExtension::Unknown(ref r) => r.encode(&mut sub),
|
|
}
|
|
|
|
(sub.len() as u16).encode(bytes);
|
|
bytes.append(&mut sub);
|
|
}
|
|
|
|
fn read(r: &mut Reader) -> Option<HelloRetryExtension> {
|
|
let typ = ExtensionType::read(r)?;
|
|
let len = u16::read(r)? as usize;
|
|
let mut sub = r.sub(len)?;
|
|
|
|
Some(match typ {
|
|
ExtensionType::KeyShare => {
|
|
HelloRetryExtension::KeyShare(NamedGroup::read(&mut sub)?)
|
|
}
|
|
ExtensionType::Cookie => {
|
|
HelloRetryExtension::Cookie(PayloadU16::read(&mut sub)?)
|
|
}
|
|
ExtensionType::SupportedVersions => {
|
|
HelloRetryExtension::SupportedVersions(ProtocolVersion::read(&mut sub)?)
|
|
}
|
|
_ => HelloRetryExtension::Unknown(UnknownExtension::read(typ, &mut sub)?),
|
|
})
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct HelloRetryRequest {
|
|
pub legacy_version: ProtocolVersion,
|
|
pub session_id: SessionID,
|
|
pub cipher_suite: CipherSuite,
|
|
pub extensions: Vec<HelloRetryExtension>,
|
|
}
|
|
|
|
impl Codec for HelloRetryRequest {
|
|
fn encode(&self, bytes: &mut Vec<u8>) {
|
|
self.legacy_version.encode(bytes);
|
|
HELLO_RETRY_REQUEST_RANDOM.encode(bytes);
|
|
self.session_id.encode(bytes);
|
|
self.cipher_suite.encode(bytes);
|
|
Compression::Null.encode(bytes);
|
|
codec::encode_vec_u16(bytes, &self.extensions);
|
|
}
|
|
|
|
fn read(r: &mut Reader) -> Option<HelloRetryRequest> {
|
|
let session_id = SessionID::read(r)?;
|
|
let cipher_suite = CipherSuite::read(r)?;
|
|
let compression = Compression::read(r)?;
|
|
|
|
if compression != Compression::Null {
|
|
return None;
|
|
}
|
|
|
|
Some(HelloRetryRequest {
|
|
legacy_version: ProtocolVersion::Unknown(0),
|
|
session_id,
|
|
cipher_suite,
|
|
extensions: codec::read_vec_u16::<HelloRetryExtension>(r)?,
|
|
})
|
|
}
|
|
}
|
|
|
|
impl HelloRetryRequest {
|
|
/// Returns true if there is more than one extension of a given
|
|
/// type.
|
|
pub fn has_duplicate_extension(&self) -> bool {
|
|
let mut seen = collections::HashSet::new();
|
|
|
|
for ext in &self.extensions {
|
|
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.extensions
|
|
.iter()
|
|
.any(|ext| {
|
|
ext.get_type() != ExtensionType::KeyShare &&
|
|
ext.get_type() != ExtensionType::SupportedVersions &&
|
|
ext.get_type() != ExtensionType::Cookie
|
|
})
|
|
}
|
|
|
|
fn find_extension(&self, ext: ExtensionType) -> Option<&HelloRetryExtension> {
|
|
self.extensions.iter().find(|x| x.get_type() == ext)
|
|
}
|
|
|
|
pub fn get_requested_key_share_group(&self) -> Option<NamedGroup> {
|
|
let ext = self.find_extension(ExtensionType::KeyShare)?;
|
|
match *ext {
|
|
HelloRetryExtension::KeyShare(grp) => Some(grp),
|
|
_ => None,
|
|
}
|
|
}
|
|
|
|
pub fn get_cookie(&self) -> Option<&PayloadU16> {
|
|
let ext = self.find_extension(ExtensionType::Cookie)?;
|
|
match *ext {
|
|
HelloRetryExtension::Cookie(ref ck) => Some(ck),
|
|
_ => None,
|
|
}
|
|
}
|
|
|
|
pub fn get_supported_versions(&self) -> Option<ProtocolVersion> {
|
|
let ext = self.find_extension(ExtensionType::SupportedVersions)?;
|
|
match *ext {
|
|
HelloRetryExtension::SupportedVersions(ver) => Some(ver),
|
|
_ => None,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct ServerHelloPayload {
|
|
pub legacy_version: ProtocolVersion,
|
|
pub random: Random,
|
|
pub session_id: SessionID,
|
|
pub cipher_suite: CipherSuite,
|
|
pub compression_method: Compression,
|
|
pub extensions: Vec<ServerExtension>,
|
|
}
|
|
|
|
impl Codec for ServerHelloPayload {
|
|
fn encode(&self, bytes: &mut Vec<u8>) {
|
|
self.legacy_version.encode(bytes);
|
|
self.random.encode(bytes);
|
|
|
|
self.session_id.encode(bytes);
|
|
self.cipher_suite.encode(bytes);
|
|
self.compression_method.encode(bytes);
|
|
|
|
if !self.extensions.is_empty() {
|
|
codec::encode_vec_u16(bytes, &self.extensions);
|
|
}
|
|
}
|
|
|
|
// minus version and random, which have already been read.
|
|
fn read(r: &mut Reader) -> Option<ServerHelloPayload> {
|
|
let session_id = SessionID::read(r)?;
|
|
let suite = CipherSuite::read(r)?;
|
|
let compression = Compression::read(r)?;
|
|
|
|
let mut ret = ServerHelloPayload {
|
|
legacy_version: ProtocolVersion::Unknown(0),
|
|
random: ZERO_RANDOM.clone(),
|
|
session_id,
|
|
cipher_suite: suite,
|
|
compression_method: compression,
|
|
extensions: Vec::new(),
|
|
};
|
|
|
|
if r.any_left() {
|
|
ret.extensions = codec::read_vec_u16::<ServerExtension>(r)?;
|
|
}
|
|
|
|
Some(ret)
|
|
}
|
|
}
|
|
|
|
impl HasServerExtensions for ServerHelloPayload {
|
|
fn get_extensions(&self) -> &[ServerExtension] {
|
|
&self.extensions
|
|
}
|
|
}
|
|
|
|
impl ServerHelloPayload {
|
|
pub fn get_key_share(&self) -> Option<&KeyShareEntry> {
|
|
let ext = self.find_extension(ExtensionType::KeyShare)?;
|
|
match *ext {
|
|
ServerExtension::KeyShare(ref share) => Some(share),
|
|
_ => None,
|
|
}
|
|
}
|
|
|
|
pub fn get_psk_index(&self) -> Option<u16> {
|
|
let ext = self.find_extension(ExtensionType::PreSharedKey)?;
|
|
match *ext {
|
|
ServerExtension::PresharedKey(ref index) => Some(*index),
|
|
_ => None,
|
|
}
|
|
}
|
|
|
|
pub fn get_ecpoints_extension(&self) -> Option<&ECPointFormatList> {
|
|
let ext = self.find_extension(ExtensionType::ECPointFormats)?;
|
|
match *ext {
|
|
ServerExtension::ECPointFormats(ref fmts) => Some(fmts),
|
|
_ => None,
|
|
}
|
|
}
|
|
|
|
pub fn ems_support_acked(&self) -> bool {
|
|
self.find_extension(ExtensionType::ExtendedMasterSecret)
|
|
.is_some()
|
|
}
|
|
|
|
pub fn get_sct_list(&self) -> Option<&SCTList> {
|
|
let ext = self.find_extension(ExtensionType::SCT)?;
|
|
match *ext {
|
|
ServerExtension::SignedCertificateTimestamp(ref sctl) => Some(sctl),
|
|
_ => None,
|
|
}
|
|
}
|
|
|
|
pub fn get_supported_versions(&self) -> Option<ProtocolVersion> {
|
|
let ext = self.find_extension(ExtensionType::SupportedVersions)?;
|
|
match *ext {
|
|
ServerExtension::SupportedVersions(vers) => Some(vers),
|
|
_ => None,
|
|
}
|
|
}
|
|
}
|
|
|
|
pub type CertificatePayload = Vec<key::Certificate>;
|
|
|
|
impl Codec for CertificatePayload {
|
|
fn encode(&self, bytes: &mut Vec<u8>) {
|
|
codec::encode_vec_u24(bytes, self);
|
|
}
|
|
|
|
fn read(r: &mut Reader) -> Option<CertificatePayload> {
|
|
// 64KB of certificates is plenty, 16MB is obviously silly
|
|
codec::read_vec_u24_limited(r, 0x10000)
|
|
}
|
|
}
|
|
|
|
// TLS1.3 changes the Certificate payload encoding.
|
|
// That's annoying. It means the parsing is not
|
|
// context-free any more.
|
|
|
|
#[derive(Debug)]
|
|
pub enum CertificateExtension {
|
|
CertificateStatus(CertificateStatus),
|
|
SignedCertificateTimestamp(SCTList),
|
|
Unknown(UnknownExtension),
|
|
}
|
|
|
|
impl CertificateExtension {
|
|
pub fn get_type(&self) -> ExtensionType {
|
|
match *self {
|
|
CertificateExtension::CertificateStatus(_) => ExtensionType::StatusRequest,
|
|
CertificateExtension::SignedCertificateTimestamp(_) => ExtensionType::SCT,
|
|
CertificateExtension::Unknown(ref r) => r.typ,
|
|
}
|
|
}
|
|
|
|
pub fn make_sct(sct_list: Vec<u8>) -> CertificateExtension {
|
|
let sctl = SCTList::read_bytes(&sct_list)
|
|
.expect("invalid SCT list");
|
|
CertificateExtension::SignedCertificateTimestamp(sctl)
|
|
}
|
|
|
|
pub fn get_cert_status(&self) -> Option<&Vec<u8>> {
|
|
match *self {
|
|
CertificateExtension::CertificateStatus(ref cs) => Some(&cs.ocsp_response.0),
|
|
_ => None
|
|
}
|
|
}
|
|
|
|
pub fn get_sct_list(&self) -> Option<&SCTList> {
|
|
match *self {
|
|
CertificateExtension::SignedCertificateTimestamp(ref sctl) => Some(sctl),
|
|
_ => None
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Codec for CertificateExtension {
|
|
fn encode(&self, bytes: &mut Vec<u8>) {
|
|
self.get_type().encode(bytes);
|
|
|
|
let mut sub: Vec<u8> = Vec::new();
|
|
match *self {
|
|
CertificateExtension::CertificateStatus(ref r) => r.encode(&mut sub),
|
|
CertificateExtension::SignedCertificateTimestamp(ref r) => r.encode(&mut sub),
|
|
CertificateExtension::Unknown(ref r) => r.encode(&mut sub),
|
|
}
|
|
|
|
(sub.len() as u16).encode(bytes);
|
|
bytes.append(&mut sub);
|
|
}
|
|
|
|
fn read(r: &mut Reader) -> Option<CertificateExtension> {
|
|
let typ = ExtensionType::read(r)?;
|
|
let len = u16::read(r)? as usize;
|
|
let mut sub = r.sub(len)?;
|
|
|
|
Some(match typ {
|
|
ExtensionType::StatusRequest => {
|
|
let st = CertificateStatus::read(&mut sub)?;
|
|
CertificateExtension::CertificateStatus(st)
|
|
}
|
|
ExtensionType::SCT => {
|
|
let scts = SCTList::read(&mut sub)?;
|
|
CertificateExtension::SignedCertificateTimestamp(scts)
|
|
}
|
|
_ => CertificateExtension::Unknown(UnknownExtension::read(typ, &mut sub)?),
|
|
})
|
|
}
|
|
}
|
|
|
|
declare_u16_vec!(CertificateExtensions, CertificateExtension);
|
|
|
|
#[derive(Debug)]
|
|
pub struct CertificateEntry {
|
|
pub cert: key::Certificate,
|
|
pub exts: CertificateExtensions,
|
|
}
|
|
|
|
impl Codec for CertificateEntry {
|
|
fn encode(&self, bytes: &mut Vec<u8>) {
|
|
self.cert.encode(bytes);
|
|
self.exts.encode(bytes);
|
|
}
|
|
|
|
fn read(r: &mut Reader) -> Option<CertificateEntry> {
|
|
Some(CertificateEntry {
|
|
cert: key::Certificate::read(r)?,
|
|
exts: CertificateExtensions::read(r)?,
|
|
})
|
|
}
|
|
}
|
|
|
|
impl CertificateEntry {
|
|
pub fn new(cert: key::Certificate) -> CertificateEntry {
|
|
CertificateEntry {
|
|
cert,
|
|
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
|
|
.iter()
|
|
.any(|ext| {
|
|
ext.get_type() != ExtensionType::StatusRequest &&
|
|
ext.get_type() != ExtensionType::SCT
|
|
})
|
|
}
|
|
|
|
pub fn get_ocsp_response(&self) -> Option<&Vec<u8>> {
|
|
self.exts
|
|
.iter()
|
|
.find(|ext| ext.get_type() == ExtensionType::StatusRequest)
|
|
.and_then(CertificateExtension::get_cert_status)
|
|
}
|
|
|
|
pub fn get_scts(&self) -> Option<&SCTList> {
|
|
self.exts
|
|
.iter()
|
|
.find(|ext| ext.get_type() == ExtensionType::SCT)
|
|
.and_then(CertificateExtension::get_sct_list)
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct CertificatePayloadTLS13 {
|
|
pub context: PayloadU8,
|
|
pub entries: Vec<CertificateEntry>,
|
|
}
|
|
|
|
impl Codec for CertificatePayloadTLS13 {
|
|
fn encode(&self, bytes: &mut Vec<u8>) {
|
|
self.context.encode(bytes);
|
|
codec::encode_vec_u24(bytes, &self.entries);
|
|
}
|
|
|
|
fn read(r: &mut Reader) -> Option<CertificatePayloadTLS13> {
|
|
Some(CertificatePayloadTLS13 {
|
|
context: PayloadU8::read(r)?,
|
|
entries: codec::read_vec_u24_limited::<CertificateEntry>(r, 0x10000)?,
|
|
})
|
|
}
|
|
}
|
|
|
|
impl CertificatePayloadTLS13 {
|
|
pub fn new(entries: Vec<CertificateEntry>) -> CertificatePayloadTLS13 {
|
|
CertificatePayloadTLS13 {
|
|
context: PayloadU8::empty(),
|
|
entries,
|
|
}
|
|
}
|
|
|
|
pub fn any_entry_has_duplicate_extension(&self) -> bool {
|
|
for entry in &self.entries {
|
|
if entry.has_duplicate_extension() {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
false
|
|
}
|
|
|
|
pub fn any_entry_has_unknown_extension(&self) -> bool {
|
|
for entry in &self.entries {
|
|
if entry.has_unknown_extension() {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
false
|
|
}
|
|
|
|
pub fn any_entry_has_extension(&self) -> bool {
|
|
for entry in &self.entries {
|
|
if !entry.exts.is_empty() {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
false
|
|
}
|
|
|
|
pub fn get_end_entity_ocsp(&self) -> Vec<u8> {
|
|
self.entries.first()
|
|
.and_then(CertificateEntry::get_ocsp_response)
|
|
.cloned()
|
|
.unwrap_or_else( Vec::new)
|
|
}
|
|
|
|
pub fn get_end_entity_scts(&self) -> Option<SCTList> {
|
|
self.entries.first()
|
|
.and_then(CertificateEntry::get_scts)
|
|
.cloned()
|
|
}
|
|
|
|
pub fn convert(&self) -> CertificatePayload {
|
|
let mut ret = Vec::new();
|
|
for entry in &self.entries {
|
|
ret.push(entry.cert.clone());
|
|
}
|
|
ret
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub enum KeyExchangeAlgorithm {
|
|
BulkOnly,
|
|
DH,
|
|
DHE,
|
|
RSA,
|
|
ECDH,
|
|
ECDHE,
|
|
}
|
|
|
|
// We don't support arbitrary curves. It's a terrible
|
|
// idea and unnecessary attack surface. Please,
|
|
// get a grip.
|
|
#[derive(Debug)]
|
|
pub struct ECParameters {
|
|
pub curve_type: ECCurveType,
|
|
pub named_group: NamedGroup,
|
|
}
|
|
|
|
impl Codec for ECParameters {
|
|
fn encode(&self, bytes: &mut Vec<u8>) {
|
|
self.curve_type.encode(bytes);
|
|
self.named_group.encode(bytes);
|
|
}
|
|
|
|
fn read(r: &mut Reader) -> Option<ECParameters> {
|
|
let ct = ECCurveType::read(r)?;
|
|
|
|
if ct != ECCurveType::NamedCurve {
|
|
return None;
|
|
}
|
|
|
|
let grp = NamedGroup::read(r)?;
|
|
|
|
Some(ECParameters {
|
|
curve_type: ct,
|
|
named_group: grp,
|
|
})
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct DigitallySignedStruct {
|
|
pub scheme: SignatureScheme,
|
|
pub sig: PayloadU16,
|
|
}
|
|
|
|
impl DigitallySignedStruct {
|
|
pub fn new(scheme: SignatureScheme, sig: Vec<u8>) -> DigitallySignedStruct {
|
|
DigitallySignedStruct {
|
|
scheme,
|
|
sig: PayloadU16::new(sig),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Codec for DigitallySignedStruct {
|
|
fn encode(&self, bytes: &mut Vec<u8>) {
|
|
self.scheme.encode(bytes);
|
|
self.sig.encode(bytes);
|
|
}
|
|
|
|
fn read(r: &mut Reader) -> Option<DigitallySignedStruct> {
|
|
let scheme = SignatureScheme::read(r)?;
|
|
let sig = PayloadU16::read(r)?;
|
|
|
|
Some(DigitallySignedStruct {
|
|
scheme,
|
|
sig,
|
|
})
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct ClientECDHParams {
|
|
pub public: PayloadU8,
|
|
}
|
|
|
|
impl Codec for ClientECDHParams {
|
|
fn encode(&self, bytes: &mut Vec<u8>) {
|
|
self.public.encode(bytes);
|
|
}
|
|
|
|
fn read(r: &mut Reader) -> Option<ClientECDHParams> {
|
|
let pb = PayloadU8::read(r)?;
|
|
Some(ClientECDHParams { public: pb })
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct ServerECDHParams {
|
|
pub curve_params: ECParameters,
|
|
pub public: PayloadU8,
|
|
}
|
|
|
|
impl ServerECDHParams {
|
|
pub fn new(named_group: NamedGroup, pubkey: &[u8]) -> ServerECDHParams {
|
|
ServerECDHParams {
|
|
curve_params: ECParameters {
|
|
curve_type: ECCurveType::NamedCurve,
|
|
named_group,
|
|
},
|
|
public: PayloadU8::new(pubkey.to_vec()),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Codec for ServerECDHParams {
|
|
fn encode(&self, bytes: &mut Vec<u8>) {
|
|
self.curve_params.encode(bytes);
|
|
self.public.encode(bytes);
|
|
}
|
|
|
|
fn read(r: &mut Reader) -> Option<ServerECDHParams> {
|
|
let cp = ECParameters::read(r)?;
|
|
let pb = PayloadU8::read(r)?;
|
|
|
|
Some(ServerECDHParams {
|
|
curve_params: cp,
|
|
public: pb,
|
|
})
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct ECDHEServerKeyExchange {
|
|
pub params: ServerECDHParams,
|
|
pub dss: DigitallySignedStruct,
|
|
}
|
|
|
|
impl Codec for ECDHEServerKeyExchange {
|
|
fn encode(&self, bytes: &mut Vec<u8>) {
|
|
self.params.encode(bytes);
|
|
self.dss.encode(bytes);
|
|
}
|
|
|
|
fn read(r: &mut Reader) -> Option<ECDHEServerKeyExchange> {
|
|
let params = ServerECDHParams::read(r)?;
|
|
let dss = DigitallySignedStruct::read(r)?;
|
|
|
|
Some(ECDHEServerKeyExchange {
|
|
params,
|
|
dss,
|
|
})
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub enum ServerKeyExchangePayload {
|
|
ECDHE(ECDHEServerKeyExchange),
|
|
Unknown(Payload),
|
|
}
|
|
|
|
impl Codec for ServerKeyExchangePayload {
|
|
fn encode(&self, bytes: &mut Vec<u8>) {
|
|
match *self {
|
|
ServerKeyExchangePayload::ECDHE(ref x) => x.encode(bytes),
|
|
ServerKeyExchangePayload::Unknown(ref x) => x.encode(bytes),
|
|
}
|
|
}
|
|
|
|
fn read(r: &mut Reader) -> Option<ServerKeyExchangePayload> {
|
|
// read as Unknown, fully parse when we know the
|
|
// KeyExchangeAlgorithm
|
|
Payload::read(r).and_then(|x| Some(ServerKeyExchangePayload::Unknown(x)))
|
|
}
|
|
}
|
|
|
|
impl ServerKeyExchangePayload {
|
|
pub fn unwrap_given_kxa(&self, kxa: &KeyExchangeAlgorithm) -> Option<ServerKeyExchangePayload> {
|
|
if let ServerKeyExchangePayload::Unknown(ref unk) = *self {
|
|
let mut rd = Reader::init(&unk.0);
|
|
|
|
let result = match *kxa {
|
|
KeyExchangeAlgorithm::ECDHE => {
|
|
ECDHEServerKeyExchange::read(&mut rd)
|
|
.and_then(|x| Some(ServerKeyExchangePayload::ECDHE(x)))
|
|
}
|
|
_ => None,
|
|
};
|
|
|
|
if !rd.any_left() {
|
|
return result;
|
|
};
|
|
}
|
|
|
|
None
|
|
}
|
|
|
|
pub fn encode_params(&self, bytes: &mut Vec<u8>) {
|
|
bytes.clear();
|
|
|
|
if let ServerKeyExchangePayload::ECDHE(ref x) = *self {
|
|
x.params.encode(bytes);
|
|
}
|
|
}
|
|
|
|
pub fn get_sig(&self) -> Option<DigitallySignedStruct> {
|
|
match *self {
|
|
ServerKeyExchangePayload::ECDHE(ref x) => Some(x.dss.clone()),
|
|
_ => None,
|
|
}
|
|
}
|
|
}
|
|
|
|
// -- EncryptedExtensions (TLS1.3 only) --
|
|
declare_u16_vec!(EncryptedExtensions, ServerExtension);
|
|
|
|
pub trait HasServerExtensions {
|
|
fn get_extensions(&self) -> &[ServerExtension];
|
|
|
|
/// Returns true if there is more than one extension of a given
|
|
/// type.
|
|
fn has_duplicate_extension(&self) -> bool {
|
|
let mut seen = collections::HashSet::new();
|
|
|
|
for ext in self.get_extensions() {
|
|
let typ = ext.get_type().get_u16();
|
|
|
|
if seen.contains(&typ) {
|
|
return true;
|
|
}
|
|
seen.insert(typ);
|
|
}
|
|
|
|
false
|
|
}
|
|
|
|
fn find_extension(&self, ext: ExtensionType) -> Option<&ServerExtension> {
|
|
self.get_extensions().iter().find(|x| x.get_type() == ext)
|
|
}
|
|
|
|
fn get_alpn_protocol(&self) -> Option<&[u8]> {
|
|
let ext = self.find_extension(ExtensionType::ALProtocolNegotiation)?;
|
|
match *ext {
|
|
ServerExtension::Protocols(ref protos) => protos.as_single_slice(),
|
|
_ => None,
|
|
}
|
|
}
|
|
|
|
fn get_quic_params_extension(&self) -> Option<Vec<u8>> {
|
|
let ext = self.find_extension(ExtensionType::TransportParameters)?;
|
|
match *ext {
|
|
ServerExtension::TransportParameters(ref bytes) => Some(bytes.to_vec()),
|
|
_ => None,
|
|
}
|
|
}
|
|
|
|
fn early_data_extension_offered(&self) -> bool {
|
|
self.find_extension(ExtensionType::EarlyData).is_some()
|
|
}
|
|
}
|
|
|
|
impl HasServerExtensions for EncryptedExtensions {
|
|
fn get_extensions(&self) -> &[ServerExtension] {
|
|
self
|
|
}
|
|
}
|
|
|
|
// -- CertificateRequest and sundries --
|
|
declare_u8_vec!(ClientCertificateTypes, ClientCertificateType);
|
|
pub type DistinguishedName = PayloadU16;
|
|
pub type DistinguishedNames = VecU16OfPayloadU16;
|
|
|
|
#[derive(Debug)]
|
|
pub struct CertificateRequestPayload {
|
|
pub certtypes: ClientCertificateTypes,
|
|
pub sigschemes: SupportedSignatureSchemes,
|
|
pub canames: DistinguishedNames,
|
|
}
|
|
|
|
impl Codec for CertificateRequestPayload {
|
|
fn encode(&self, bytes: &mut Vec<u8>) {
|
|
self.certtypes.encode(bytes);
|
|
self.sigschemes.encode(bytes);
|
|
self.canames.encode(bytes);
|
|
}
|
|
|
|
fn read(r: &mut Reader) -> Option<CertificateRequestPayload> {
|
|
let certtypes = ClientCertificateTypes::read(r)?;
|
|
let sigschemes = SupportedSignatureSchemes::read(r)?;
|
|
let canames = DistinguishedNames::read(r)?;
|
|
|
|
Some(CertificateRequestPayload {
|
|
certtypes,
|
|
sigschemes,
|
|
canames,
|
|
})
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub enum CertReqExtension {
|
|
SignatureAlgorithms(SupportedSignatureSchemes),
|
|
AuthorityNames(DistinguishedNames),
|
|
Unknown(UnknownExtension),
|
|
}
|
|
|
|
impl CertReqExtension {
|
|
pub fn get_type(&self) -> ExtensionType {
|
|
match *self {
|
|
CertReqExtension::SignatureAlgorithms(_) => ExtensionType::SignatureAlgorithms,
|
|
CertReqExtension::AuthorityNames(_) => ExtensionType::CertificateAuthorities,
|
|
CertReqExtension::Unknown(ref r) => r.typ,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Codec for CertReqExtension {
|
|
fn encode(&self, bytes: &mut Vec<u8>) {
|
|
self.get_type().encode(bytes);
|
|
|
|
let mut sub: Vec<u8> = Vec::new();
|
|
match *self {
|
|
CertReqExtension::SignatureAlgorithms(ref r) => r.encode(&mut sub),
|
|
CertReqExtension::AuthorityNames(ref r) => r.encode(&mut sub),
|
|
CertReqExtension::Unknown(ref r) => r.encode(&mut sub),
|
|
}
|
|
|
|
(sub.len() as u16).encode(bytes);
|
|
bytes.append(&mut sub);
|
|
}
|
|
|
|
fn read(r: &mut Reader) -> Option<CertReqExtension> {
|
|
let typ = ExtensionType::read(r)?;
|
|
let len = u16::read(r)? as usize;
|
|
let mut sub = r.sub(len)?;
|
|
|
|
Some(match typ {
|
|
ExtensionType::SignatureAlgorithms => {
|
|
let schemes = SupportedSignatureSchemes::read(&mut sub)?;
|
|
if schemes.is_empty() {
|
|
return None;
|
|
}
|
|
CertReqExtension::SignatureAlgorithms(schemes)
|
|
}
|
|
ExtensionType::CertificateAuthorities => {
|
|
let cas = DistinguishedNames::read(&mut sub)?;
|
|
CertReqExtension::AuthorityNames(cas)
|
|
}
|
|
_ => CertReqExtension::Unknown(UnknownExtension::read(typ, &mut sub)?),
|
|
})
|
|
}
|
|
}
|
|
|
|
declare_u16_vec!(CertReqExtensions, CertReqExtension);
|
|
|
|
#[derive(Debug)]
|
|
pub struct CertificateRequestPayloadTLS13 {
|
|
pub context: PayloadU8,
|
|
pub extensions: CertReqExtensions,
|
|
}
|
|
|
|
impl Codec for CertificateRequestPayloadTLS13 {
|
|
fn encode(&self, bytes: &mut Vec<u8>) {
|
|
self.context.encode(bytes);
|
|
self.extensions.encode(bytes);
|
|
}
|
|
|
|
fn read(r: &mut Reader) -> Option<CertificateRequestPayloadTLS13> {
|
|
let context = PayloadU8::read(r)?;
|
|
let extensions = CertReqExtensions::read(r)?;
|
|
|
|
Some(CertificateRequestPayloadTLS13 {
|
|
context,
|
|
extensions,
|
|
})
|
|
}
|
|
}
|
|
|
|
impl CertificateRequestPayloadTLS13 {
|
|
pub fn find_extension(&self, ext: ExtensionType) -> Option<&CertReqExtension> {
|
|
self.extensions.iter().find(|x| x.get_type() == ext)
|
|
}
|
|
|
|
pub fn get_sigalgs_extension(&self) -> Option<&SupportedSignatureSchemes> {
|
|
let ext = self.find_extension(ExtensionType::SignatureAlgorithms)?;
|
|
match *ext {
|
|
CertReqExtension::SignatureAlgorithms(ref sa) => Some(sa),
|
|
_ => None,
|
|
}
|
|
}
|
|
|
|
pub fn get_authorities_extension(&self) -> Option<&DistinguishedNames> {
|
|
let ext = self.find_extension(ExtensionType::CertificateAuthorities)?;
|
|
match *ext {
|
|
CertReqExtension::AuthorityNames(ref an) => Some(an),
|
|
_ => None,
|
|
}
|
|
}
|
|
}
|
|
|
|
// -- NewSessionTicket --
|
|
#[derive(Debug)]
|
|
pub struct NewSessionTicketPayload {
|
|
pub lifetime_hint: u32,
|
|
pub ticket: PayloadU16,
|
|
}
|
|
|
|
impl NewSessionTicketPayload {
|
|
pub fn new(lifetime_hint: u32, ticket: Vec<u8>) -> NewSessionTicketPayload {
|
|
NewSessionTicketPayload {
|
|
lifetime_hint,
|
|
ticket: PayloadU16::new(ticket),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Codec for NewSessionTicketPayload {
|
|
fn encode(&self, bytes: &mut Vec<u8>) {
|
|
self.lifetime_hint.encode(bytes);
|
|
self.ticket.encode(bytes);
|
|
}
|
|
|
|
fn read(r: &mut Reader) -> Option<NewSessionTicketPayload> {
|
|
let lifetime = u32::read(r)?;
|
|
let ticket = PayloadU16::read(r)?;
|
|
|
|
Some(NewSessionTicketPayload {
|
|
lifetime_hint: lifetime,
|
|
ticket,
|
|
})
|
|
}
|
|
}
|
|
|
|
// -- NewSessionTicket electric boogaloo --
|
|
#[derive(Debug)]
|
|
pub enum NewSessionTicketExtension {
|
|
EarlyData(u32),
|
|
Unknown(UnknownExtension),
|
|
}
|
|
|
|
impl NewSessionTicketExtension {
|
|
pub fn get_type(&self) -> ExtensionType {
|
|
match *self {
|
|
NewSessionTicketExtension::EarlyData(_) => ExtensionType::EarlyData,
|
|
NewSessionTicketExtension::Unknown(ref r) => r.typ,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Codec for NewSessionTicketExtension {
|
|
fn encode(&self, bytes: &mut Vec<u8>) {
|
|
self.get_type().encode(bytes);
|
|
|
|
let mut sub: Vec<u8> = Vec::new();
|
|
match *self {
|
|
NewSessionTicketExtension::EarlyData(r) => r.encode(&mut sub),
|
|
NewSessionTicketExtension::Unknown(ref r) => r.encode(&mut sub),
|
|
}
|
|
|
|
(sub.len() as u16).encode(bytes);
|
|
bytes.append(&mut sub);
|
|
}
|
|
|
|
fn read(r: &mut Reader) -> Option<NewSessionTicketExtension> {
|
|
let typ = ExtensionType::read(r)?;
|
|
let len = u16::read(r)? as usize;
|
|
let mut sub = r.sub(len)?;
|
|
|
|
Some(match typ {
|
|
ExtensionType::EarlyData => NewSessionTicketExtension::EarlyData(u32::read(&mut sub)?),
|
|
_ => {
|
|
NewSessionTicketExtension::Unknown(UnknownExtension::read(typ, &mut sub)?)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
declare_u16_vec!(NewSessionTicketExtensions, NewSessionTicketExtension);
|
|
|
|
#[derive(Debug)]
|
|
pub struct NewSessionTicketPayloadTLS13 {
|
|
pub lifetime: u32,
|
|
pub age_add: u32,
|
|
pub nonce: PayloadU8,
|
|
pub ticket: PayloadU16,
|
|
pub exts: NewSessionTicketExtensions,
|
|
}
|
|
|
|
impl NewSessionTicketPayloadTLS13 {
|
|
pub fn new(lifetime: u32,
|
|
age_add: u32,
|
|
nonce: Vec<u8>,
|
|
ticket: Vec<u8>) -> NewSessionTicketPayloadTLS13 {
|
|
NewSessionTicketPayloadTLS13 {
|
|
lifetime,
|
|
age_add,
|
|
nonce: PayloadU8::new(nonce),
|
|
ticket: PayloadU16::new(ticket),
|
|
exts: vec![],
|
|
}
|
|
}
|
|
|
|
pub fn find_extension(&self, ext: ExtensionType) -> Option<&NewSessionTicketExtension> {
|
|
self.exts.iter().find(|x| x.get_type() == ext)
|
|
}
|
|
|
|
pub fn get_max_early_data_size(&self) -> Option<u32> {
|
|
let ext = self.find_extension(ExtensionType::EarlyData)?;
|
|
match *ext {
|
|
NewSessionTicketExtension::EarlyData(ref sz) => Some(*sz),
|
|
_ => None
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Codec for NewSessionTicketPayloadTLS13 {
|
|
fn encode(&self, bytes: &mut Vec<u8>) {
|
|
self.lifetime.encode(bytes);
|
|
self.age_add.encode(bytes);
|
|
self.nonce.encode(bytes);
|
|
self.ticket.encode(bytes);
|
|
self.exts.encode(bytes);
|
|
}
|
|
|
|
fn read(r: &mut Reader) -> Option<NewSessionTicketPayloadTLS13> {
|
|
let lifetime = u32::read(r)?;
|
|
let age_add = u32::read(r)?;
|
|
let nonce = PayloadU8::read(r)?;
|
|
let ticket = PayloadU16::read(r)?;
|
|
let exts = NewSessionTicketExtensions::read(r)?;
|
|
|
|
Some(NewSessionTicketPayloadTLS13 {
|
|
lifetime,
|
|
age_add,
|
|
nonce,
|
|
ticket,
|
|
exts,
|
|
})
|
|
}
|
|
}
|
|
|
|
// -- RFC6066 certificate status types
|
|
|
|
/// Only supports OCSP
|
|
#[derive(Debug)]
|
|
pub struct CertificateStatus {
|
|
pub ocsp_response: PayloadU24
|
|
}
|
|
|
|
impl Codec for CertificateStatus {
|
|
fn encode(&self, bytes: &mut Vec<u8>) {
|
|
CertificateStatusType::OCSP.encode(bytes);
|
|
self.ocsp_response.encode(bytes);
|
|
}
|
|
|
|
fn read(r: &mut Reader) -> Option<CertificateStatus> {
|
|
let typ = CertificateStatusType::read(r)?;
|
|
|
|
match typ {
|
|
CertificateStatusType::OCSP => {
|
|
Some(CertificateStatus {
|
|
ocsp_response: PayloadU24::read(r)?
|
|
})
|
|
}
|
|
_ => None
|
|
}
|
|
}
|
|
}
|
|
|
|
impl CertificateStatus {
|
|
pub fn new(ocsp: Vec<u8>) -> CertificateStatus {
|
|
CertificateStatus { ocsp_response: PayloadU24::new(ocsp) }
|
|
}
|
|
|
|
pub fn take_ocsp_response(&mut self) -> Vec<u8> {
|
|
let new = PayloadU24::new(Vec::new());
|
|
mem::replace(&mut self.ocsp_response, new).0
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub enum HandshakePayload {
|
|
HelloRequest,
|
|
ClientHello(ClientHelloPayload),
|
|
ServerHello(ServerHelloPayload),
|
|
HelloRetryRequest(HelloRetryRequest),
|
|
Certificate(CertificatePayload),
|
|
CertificateTLS13(CertificatePayloadTLS13),
|
|
ServerKeyExchange(ServerKeyExchangePayload),
|
|
CertificateRequest(CertificateRequestPayload),
|
|
CertificateRequestTLS13(CertificateRequestPayloadTLS13),
|
|
CertificateVerify(DigitallySignedStruct),
|
|
ServerHelloDone,
|
|
EarlyData,
|
|
EndOfEarlyData,
|
|
ClientKeyExchange(Payload),
|
|
NewSessionTicket(NewSessionTicketPayload),
|
|
NewSessionTicketTLS13(NewSessionTicketPayloadTLS13),
|
|
EncryptedExtensions(EncryptedExtensions),
|
|
KeyUpdate(KeyUpdateRequest),
|
|
Finished(Payload),
|
|
CertificateStatus(CertificateStatus),
|
|
MessageHash(Payload),
|
|
Unknown(Payload),
|
|
}
|
|
|
|
impl HandshakePayload {
|
|
fn encode(&self, bytes: &mut Vec<u8>) {
|
|
match *self {
|
|
HandshakePayload::HelloRequest |
|
|
HandshakePayload::ServerHelloDone |
|
|
HandshakePayload::EarlyData |
|
|
HandshakePayload::EndOfEarlyData => {}
|
|
HandshakePayload::ClientHello(ref x) => x.encode(bytes),
|
|
HandshakePayload::ServerHello(ref x) => x.encode(bytes),
|
|
HandshakePayload::HelloRetryRequest(ref x) => x.encode(bytes),
|
|
HandshakePayload::Certificate(ref x) => x.encode(bytes),
|
|
HandshakePayload::CertificateTLS13(ref x) => x.encode(bytes),
|
|
HandshakePayload::ServerKeyExchange(ref x) => x.encode(bytes),
|
|
HandshakePayload::ClientKeyExchange(ref x) => x.encode(bytes),
|
|
HandshakePayload::CertificateRequest(ref x) => x.encode(bytes),
|
|
HandshakePayload::CertificateRequestTLS13(ref x) => x.encode(bytes),
|
|
HandshakePayload::CertificateVerify(ref x) => x.encode(bytes),
|
|
HandshakePayload::NewSessionTicket(ref x) => x.encode(bytes),
|
|
HandshakePayload::NewSessionTicketTLS13(ref x) => x.encode(bytes),
|
|
HandshakePayload::EncryptedExtensions(ref x) => x.encode(bytes),
|
|
HandshakePayload::KeyUpdate(ref x) => x.encode(bytes),
|
|
HandshakePayload::Finished(ref x) => x.encode(bytes),
|
|
HandshakePayload::CertificateStatus(ref x) => x.encode(bytes),
|
|
HandshakePayload::MessageHash(ref x) => x.encode(bytes),
|
|
HandshakePayload::Unknown(ref x) => x.encode(bytes),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct HandshakeMessagePayload {
|
|
pub typ: HandshakeType,
|
|
pub payload: HandshakePayload,
|
|
}
|
|
|
|
impl Codec for HandshakeMessagePayload {
|
|
fn encode(&self, bytes: &mut Vec<u8>) {
|
|
// encode payload to learn length
|
|
let mut sub: Vec<u8> = Vec::new();
|
|
self.payload.encode(&mut sub);
|
|
|
|
// output type, length, and encoded payload
|
|
match self.typ {
|
|
HandshakeType::HelloRetryRequest => HandshakeType::ServerHello,
|
|
_ => self.typ,
|
|
}.encode(bytes);
|
|
codec::u24(sub.len() as u32).encode(bytes);
|
|
bytes.append(&mut sub);
|
|
}
|
|
|
|
fn read(r: &mut Reader) -> Option<HandshakeMessagePayload> {
|
|
HandshakeMessagePayload::read_version(r, ProtocolVersion::TLSv1_2)
|
|
}
|
|
}
|
|
|
|
impl HandshakeMessagePayload {
|
|
pub fn length(&self) -> usize {
|
|
let mut buf = Vec::new();
|
|
self.encode(&mut buf);
|
|
buf.len()
|
|
}
|
|
|
|
pub fn read_version(r: &mut Reader, vers: ProtocolVersion) -> Option<HandshakeMessagePayload> {
|
|
let mut typ = HandshakeType::read(r)?;
|
|
let len = codec::u24::read(r)?.0 as usize;
|
|
let mut sub = r.sub(len)?;
|
|
|
|
let payload = match typ {
|
|
HandshakeType::HelloRequest if sub.left() == 0 => HandshakePayload::HelloRequest,
|
|
HandshakeType::ClientHello => {
|
|
HandshakePayload::ClientHello(ClientHelloPayload::read(&mut sub)?)
|
|
}
|
|
HandshakeType::ServerHello => {
|
|
let version = ProtocolVersion::read(&mut sub)?;
|
|
let random = Random::read(&mut sub)?;
|
|
|
|
if random == HELLO_RETRY_REQUEST_RANDOM {
|
|
let mut hrr = HelloRetryRequest::read(&mut sub)?;
|
|
hrr.legacy_version = version;
|
|
typ = HandshakeType::HelloRetryRequest;
|
|
HandshakePayload::HelloRetryRequest(hrr)
|
|
} else {
|
|
let mut shp = ServerHelloPayload::read(&mut sub)?;
|
|
shp.legacy_version = version;
|
|
shp.random = random;
|
|
HandshakePayload::ServerHello(shp)
|
|
}
|
|
}
|
|
HandshakeType::Certificate if vers == ProtocolVersion::TLSv1_3 => {
|
|
let p = CertificatePayloadTLS13::read(&mut sub)?;
|
|
HandshakePayload::CertificateTLS13(p)
|
|
}
|
|
HandshakeType::Certificate => {
|
|
HandshakePayload::Certificate(CertificatePayload::read(&mut sub)?)
|
|
}
|
|
HandshakeType::ServerKeyExchange => {
|
|
let p = ServerKeyExchangePayload::read(&mut sub)?;
|
|
HandshakePayload::ServerKeyExchange(p)
|
|
}
|
|
HandshakeType::ServerHelloDone => {
|
|
if sub.any_left() {
|
|
return None;
|
|
}
|
|
HandshakePayload::ServerHelloDone
|
|
}
|
|
HandshakeType::ClientKeyExchange => {
|
|
HandshakePayload::ClientKeyExchange(Payload::read(&mut sub)?)
|
|
}
|
|
HandshakeType::CertificateRequest if vers == ProtocolVersion::TLSv1_3 => {
|
|
let p = CertificateRequestPayloadTLS13::read(&mut sub)?;
|
|
HandshakePayload::CertificateRequestTLS13(p)
|
|
}
|
|
HandshakeType::CertificateRequest => {
|
|
let p = CertificateRequestPayload::read(&mut sub)?;
|
|
HandshakePayload::CertificateRequest(p)
|
|
}
|
|
HandshakeType::CertificateVerify => {
|
|
HandshakePayload::CertificateVerify(DigitallySignedStruct::read(&mut sub)?)
|
|
}
|
|
HandshakeType::NewSessionTicket if vers == ProtocolVersion::TLSv1_3 => {
|
|
let p = NewSessionTicketPayloadTLS13::read(&mut sub)?;
|
|
HandshakePayload::NewSessionTicketTLS13(p)
|
|
}
|
|
HandshakeType::NewSessionTicket => {
|
|
let p = NewSessionTicketPayload::read(&mut sub)?;
|
|
HandshakePayload::NewSessionTicket(p)
|
|
}
|
|
HandshakeType::EncryptedExtensions => {
|
|
HandshakePayload::EncryptedExtensions(EncryptedExtensions::read(&mut sub)?)
|
|
}
|
|
HandshakeType::KeyUpdate => {
|
|
HandshakePayload::KeyUpdate(KeyUpdateRequest::read(&mut sub)?)
|
|
}
|
|
HandshakeType::Finished => {
|
|
HandshakePayload::Finished(Payload::read(&mut sub)?)
|
|
}
|
|
HandshakeType::CertificateStatus => {
|
|
HandshakePayload::CertificateStatus(CertificateStatus::read(&mut sub)?)
|
|
}
|
|
HandshakeType::MessageHash => {
|
|
// does not appear on the wire
|
|
return None;
|
|
}
|
|
HandshakeType::HelloRetryRequest => {
|
|
// not legal on wire
|
|
return None;
|
|
}
|
|
_ => HandshakePayload::Unknown(Payload::read(&mut sub)?),
|
|
};
|
|
|
|
if sub.any_left() {
|
|
None
|
|
} else {
|
|
Some(HandshakeMessagePayload {
|
|
typ,
|
|
payload,
|
|
})
|
|
}
|
|
}
|
|
|
|
pub fn build_key_update_notify() -> HandshakeMessagePayload {
|
|
HandshakeMessagePayload {
|
|
typ: HandshakeType::KeyUpdate,
|
|
payload: HandshakePayload::KeyUpdate(KeyUpdateRequest::UpdateNotRequested),
|
|
}
|
|
}
|
|
|
|
pub fn get_encoding_for_binder_signing(&self) -> Vec<u8> {
|
|
let mut ret = self.get_encoding();
|
|
|
|
let binder_len = match self.payload {
|
|
HandshakePayload::ClientHello(ref ch) => {
|
|
let offer = ch.get_psk().unwrap();
|
|
|
|
let mut binders_encoding = Vec::new();
|
|
offer.binders.encode(&mut binders_encoding);
|
|
binders_encoding.len()
|
|
}
|
|
_ => 0,
|
|
};
|
|
|
|
let ret_len = ret.len() - binder_len;
|
|
ret.truncate(ret_len);
|
|
ret
|
|
}
|
|
|
|
pub fn build_handshake_hash(hash: &[u8]) -> HandshakeMessagePayload {
|
|
HandshakeMessagePayload {
|
|
typ: HandshakeType::MessageHash,
|
|
payload: HandshakePayload::MessageHash(Payload::new(hash.to_vec()))
|
|
}
|
|
}
|
|
}
|