mirror of
https://github.com/refraction-networking/utls.git
synced 2025-04-01 19:17:36 +03:00
* crypto/tls: Add hybrid post-quantum key agreement (#13) * import: client-side KEM from cloudflare/go * import: server-side KEM from cloudflare/go * fix: modify test to get rid of CFEvents. Note: uTLS does not promise any server-side functionality, and this change is made to be able to conduct unit tests which requires both side to be able to handle KEM Curves. Co-authored-by: Christopher Wood <caw@heapingbits.net> Co-Authored-By: Bas Westerbaan <bas@westerbaan.name> ---- Based on: * crypto/tls: Add hybrid post-quantum key agreement Adds X25519Kyber512Draft00, X25519Kyber768Draft00, and P256Kyber768Draft00 hybrid post-quantum key agreements with temporary group identifiers. The hybrid post-quantum key exchanges uses plain X{25519,448} instead of HPKE, which we assume will be more likely to be adopted. The order is chosen to match CECPQ2. Not enabled by default. Adds CFEvents to detect `HelloRetryRequest`s and to signal which key agreement was used. Co-authored-by: Christopher Wood <caw@heapingbits.net> [bas, 1.20.1: also adds P256Kyber768Draft00] [pwu, 1.20.4: updated circl to v1.3.3, moved code to cfevent.go] * crypto: add support for CIRCL signature schemes * only partially port the commit from cloudflare/go. We would stick to the official x509 at the cost of incompatibility. Co-Authored-By: Bas Westerbaan <bas@westerbaan.name> Co-Authored-By: Christopher Patton <3453007+cjpatton@users.noreply.github.com> Co-Authored-By: Peter Wu <peter@lekensteyn.nl> * crypto/tls: add new X25519Kyber768Draft00 code point Ported from cloudflare/go to support the upcoming new post-quantum keyshare. ---- * Point tls.X25519Kyber768Draft00 to the new 0x6399 identifier while the old 0xfe31 identifier is available as tls.X25519Kyber768Draft00Old. * Make sure that the kem.PrivateKey can always be mapped to the CurveID that was linked to it. This is needed since we now have two ID aliasing to the same scheme, and clients need to be able to detect whether the key share presented by the server actually matches the key share that the client originally sent. * Update tests, add the new identifier and remove unnecessary code. Link: https://mailarchive.ietf.org/arch/msg/tls/HAWpNpgptl--UZNSYuvsjB-Pc2k/ Link: https://datatracker.ietf.org/doc/draft-tls-westerbaan-xyber768d00/02/ Co-Authored-By: Peter Wu <peter@lekensteyn.nl> Co-Authored-By: Bas Westerbaan <bas@westerbaan.name> --------- Co-authored-by: Bas Westerbaan <bas@westerbaan.name> Co-authored-by: Christopher Patton <3453007+cjpatton@users.noreply.github.com> Co-authored-by: Peter Wu <peter@lekensteyn.nl>
768 lines
22 KiB
Go
768 lines
22 KiB
Go
// Copyright 2017 Google Inc. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package tls
|
|
|
|
import (
|
|
"crypto"
|
|
"crypto/ecdh"
|
|
"crypto/x509"
|
|
"hash"
|
|
"time"
|
|
|
|
"github.com/cloudflare/circl/kem"
|
|
)
|
|
|
|
// ClientHandshakeState includes both TLS 1.3-only and TLS 1.2-only states,
|
|
// only one of them will be used, depending on negotiated version.
|
|
//
|
|
// ClientHandshakeState will be converted into and from either
|
|
// - clientHandshakeState (TLS 1.2)
|
|
// - clientHandshakeStateTLS13 (TLS 1.3)
|
|
//
|
|
// uTLS will call .handshake() on one of these private internal states,
|
|
// to perform TLS handshake using standard crypto/tls implementation.
|
|
type PubClientHandshakeState struct {
|
|
C *Conn
|
|
ServerHello *PubServerHelloMsg
|
|
Hello *PubClientHelloMsg
|
|
MasterSecret []byte
|
|
Session *SessionState
|
|
|
|
State12 TLS12OnlyState
|
|
State13 TLS13OnlyState
|
|
|
|
uconn *UConn
|
|
}
|
|
|
|
// TLS 1.3 only
|
|
type TLS13OnlyState struct {
|
|
Suite *PubCipherSuiteTLS13
|
|
EcdheKey *ecdh.PrivateKey
|
|
KeySharesEcdheParams KeySharesEcdheParameters
|
|
KEMKey *KemPrivateKey
|
|
EarlySecret []byte
|
|
BinderKey []byte
|
|
CertReq *CertificateRequestMsgTLS13
|
|
UsingPSK bool
|
|
SentDummyCCS bool
|
|
Transcript hash.Hash
|
|
TrafficSecret []byte // client_application_traffic_secret_0
|
|
}
|
|
|
|
// TLS 1.2 and before only
|
|
type TLS12OnlyState struct {
|
|
FinishedHash FinishedHash
|
|
Suite PubCipherSuite
|
|
}
|
|
|
|
func (chs *PubClientHandshakeState) toPrivate13() *clientHandshakeStateTLS13 {
|
|
if chs == nil {
|
|
return nil
|
|
} else {
|
|
return &clientHandshakeStateTLS13{
|
|
c: chs.C,
|
|
serverHello: chs.ServerHello.getPrivatePtr(),
|
|
hello: chs.Hello.getPrivatePtr(),
|
|
ecdheKey: chs.State13.EcdheKey,
|
|
keySharesEcdheParams: chs.State13.KeySharesEcdheParams,
|
|
kemKey: chs.State13.KEMKey.ToPrivate(),
|
|
|
|
session: chs.Session,
|
|
earlySecret: chs.State13.EarlySecret,
|
|
binderKey: chs.State13.BinderKey,
|
|
|
|
certReq: chs.State13.CertReq.toPrivate(),
|
|
usingPSK: chs.State13.UsingPSK,
|
|
sentDummyCCS: chs.State13.SentDummyCCS,
|
|
suite: chs.State13.Suite.toPrivate(),
|
|
transcript: chs.State13.Transcript,
|
|
masterSecret: chs.MasterSecret,
|
|
trafficSecret: chs.State13.TrafficSecret,
|
|
|
|
uconn: chs.uconn,
|
|
}
|
|
}
|
|
}
|
|
|
|
func (chs13 *clientHandshakeStateTLS13) toPublic13() *PubClientHandshakeState {
|
|
if chs13 == nil {
|
|
return nil
|
|
} else {
|
|
tls13State := TLS13OnlyState{
|
|
KeySharesEcdheParams: chs13.keySharesEcdheParams,
|
|
EcdheKey: chs13.ecdheKey,
|
|
KEMKey: chs13.kemKey.ToPublic(),
|
|
EarlySecret: chs13.earlySecret,
|
|
BinderKey: chs13.binderKey,
|
|
CertReq: chs13.certReq.toPublic(),
|
|
UsingPSK: chs13.usingPSK,
|
|
SentDummyCCS: chs13.sentDummyCCS,
|
|
Suite: chs13.suite.toPublic(),
|
|
TrafficSecret: chs13.trafficSecret,
|
|
Transcript: chs13.transcript,
|
|
}
|
|
return &PubClientHandshakeState{
|
|
C: chs13.c,
|
|
ServerHello: chs13.serverHello.getPublicPtr(),
|
|
Hello: chs13.hello.getPublicPtr(),
|
|
|
|
Session: chs13.session,
|
|
|
|
MasterSecret: chs13.masterSecret,
|
|
|
|
State13: tls13State,
|
|
|
|
uconn: chs13.uconn,
|
|
}
|
|
}
|
|
}
|
|
|
|
func (chs *PubClientHandshakeState) toPrivate12() *clientHandshakeState {
|
|
if chs == nil {
|
|
return nil
|
|
} else {
|
|
return &clientHandshakeState{
|
|
c: chs.C,
|
|
serverHello: chs.ServerHello.getPrivatePtr(),
|
|
hello: chs.Hello.getPrivatePtr(),
|
|
suite: chs.State12.Suite.getPrivatePtr(),
|
|
session: chs.Session,
|
|
|
|
masterSecret: chs.MasterSecret,
|
|
|
|
finishedHash: chs.State12.FinishedHash.getPrivateObj(),
|
|
|
|
uconn: chs.uconn,
|
|
}
|
|
}
|
|
}
|
|
|
|
func (chs12 *clientHandshakeState) toPublic12() *PubClientHandshakeState {
|
|
if chs12 == nil {
|
|
return nil
|
|
} else {
|
|
tls12State := TLS12OnlyState{
|
|
Suite: chs12.suite.getPublicObj(),
|
|
FinishedHash: chs12.finishedHash.getPublicObj(),
|
|
}
|
|
return &PubClientHandshakeState{
|
|
C: chs12.c,
|
|
ServerHello: chs12.serverHello.getPublicPtr(),
|
|
Hello: chs12.hello.getPublicPtr(),
|
|
|
|
Session: chs12.session,
|
|
|
|
MasterSecret: chs12.masterSecret,
|
|
|
|
State12: tls12State,
|
|
|
|
uconn: chs12.uconn,
|
|
}
|
|
}
|
|
}
|
|
|
|
// type EcdheParameters interface {
|
|
// ecdheParameters
|
|
// }
|
|
|
|
type CertificateRequestMsgTLS13 struct {
|
|
Raw []byte
|
|
OcspStapling bool
|
|
Scts bool
|
|
SupportedSignatureAlgorithms []SignatureScheme
|
|
SupportedSignatureAlgorithmsCert []SignatureScheme
|
|
CertificateAuthorities [][]byte
|
|
}
|
|
|
|
func (crm *certificateRequestMsgTLS13) toPublic() *CertificateRequestMsgTLS13 {
|
|
if crm == nil {
|
|
return nil
|
|
} else {
|
|
return &CertificateRequestMsgTLS13{
|
|
Raw: crm.raw,
|
|
OcspStapling: crm.ocspStapling,
|
|
Scts: crm.scts,
|
|
SupportedSignatureAlgorithms: crm.supportedSignatureAlgorithms,
|
|
SupportedSignatureAlgorithmsCert: crm.supportedSignatureAlgorithmsCert,
|
|
CertificateAuthorities: crm.certificateAuthorities,
|
|
}
|
|
}
|
|
}
|
|
|
|
func (crm *CertificateRequestMsgTLS13) toPrivate() *certificateRequestMsgTLS13 {
|
|
if crm == nil {
|
|
return nil
|
|
} else {
|
|
return &certificateRequestMsgTLS13{
|
|
raw: crm.Raw,
|
|
ocspStapling: crm.OcspStapling,
|
|
scts: crm.Scts,
|
|
supportedSignatureAlgorithms: crm.SupportedSignatureAlgorithms,
|
|
supportedSignatureAlgorithmsCert: crm.SupportedSignatureAlgorithmsCert,
|
|
certificateAuthorities: crm.CertificateAuthorities,
|
|
}
|
|
}
|
|
}
|
|
|
|
type PubCipherSuiteTLS13 struct {
|
|
Id uint16
|
|
KeyLen int
|
|
Aead func(key, fixedNonce []byte) aead
|
|
Hash crypto.Hash
|
|
}
|
|
|
|
func (c *cipherSuiteTLS13) toPublic() *PubCipherSuiteTLS13 {
|
|
if c == nil {
|
|
return nil
|
|
} else {
|
|
return &PubCipherSuiteTLS13{
|
|
Id: c.id,
|
|
KeyLen: c.keyLen,
|
|
Aead: c.aead,
|
|
Hash: c.hash,
|
|
}
|
|
}
|
|
}
|
|
|
|
func (c *PubCipherSuiteTLS13) toPrivate() *cipherSuiteTLS13 {
|
|
if c == nil {
|
|
return nil
|
|
} else {
|
|
return &cipherSuiteTLS13{
|
|
id: c.Id,
|
|
keyLen: c.KeyLen,
|
|
aead: c.Aead,
|
|
hash: c.Hash,
|
|
}
|
|
}
|
|
}
|
|
|
|
type PubServerHelloMsg struct {
|
|
Raw []byte
|
|
Vers uint16
|
|
Random []byte
|
|
SessionId []byte
|
|
CipherSuite uint16
|
|
CompressionMethod uint8
|
|
NextProtoNeg bool
|
|
NextProtos []string
|
|
OcspStapling bool
|
|
Scts [][]byte
|
|
ExtendedMasterSecret bool
|
|
TicketSupported bool
|
|
SecureRenegotiation []byte
|
|
SecureRenegotiationSupported bool
|
|
AlpnProtocol string
|
|
|
|
// 1.3
|
|
SupportedVersion uint16
|
|
ServerShare keyShare
|
|
SelectedIdentityPresent bool
|
|
SelectedIdentity uint16
|
|
Cookie []byte // HelloRetryRequest extension
|
|
SelectedGroup CurveID // HelloRetryRequest extension
|
|
|
|
}
|
|
|
|
func (shm *PubServerHelloMsg) getPrivatePtr() *serverHelloMsg {
|
|
if shm == nil {
|
|
return nil
|
|
} else {
|
|
return &serverHelloMsg{
|
|
raw: shm.Raw,
|
|
vers: shm.Vers,
|
|
random: shm.Random,
|
|
sessionId: shm.SessionId,
|
|
cipherSuite: shm.CipherSuite,
|
|
compressionMethod: shm.CompressionMethod,
|
|
nextProtoNeg: shm.NextProtoNeg,
|
|
nextProtos: shm.NextProtos,
|
|
ocspStapling: shm.OcspStapling,
|
|
scts: shm.Scts,
|
|
extendedMasterSecret: shm.ExtendedMasterSecret,
|
|
ticketSupported: shm.TicketSupported,
|
|
secureRenegotiation: shm.SecureRenegotiation,
|
|
secureRenegotiationSupported: shm.SecureRenegotiationSupported,
|
|
alpnProtocol: shm.AlpnProtocol,
|
|
supportedVersion: shm.SupportedVersion,
|
|
serverShare: shm.ServerShare,
|
|
selectedIdentityPresent: shm.SelectedIdentityPresent,
|
|
selectedIdentity: shm.SelectedIdentity,
|
|
cookie: shm.Cookie,
|
|
selectedGroup: shm.SelectedGroup,
|
|
}
|
|
}
|
|
}
|
|
|
|
func (shm *serverHelloMsg) getPublicPtr() *PubServerHelloMsg {
|
|
if shm == nil {
|
|
return nil
|
|
} else {
|
|
return &PubServerHelloMsg{
|
|
Raw: shm.raw,
|
|
Vers: shm.vers,
|
|
Random: shm.random,
|
|
SessionId: shm.sessionId,
|
|
CipherSuite: shm.cipherSuite,
|
|
CompressionMethod: shm.compressionMethod,
|
|
NextProtoNeg: shm.nextProtoNeg,
|
|
NextProtos: shm.nextProtos,
|
|
OcspStapling: shm.ocspStapling,
|
|
Scts: shm.scts,
|
|
ExtendedMasterSecret: shm.extendedMasterSecret,
|
|
TicketSupported: shm.ticketSupported,
|
|
SecureRenegotiation: shm.secureRenegotiation,
|
|
SecureRenegotiationSupported: shm.secureRenegotiationSupported,
|
|
AlpnProtocol: shm.alpnProtocol,
|
|
SupportedVersion: shm.supportedVersion,
|
|
ServerShare: shm.serverShare,
|
|
SelectedIdentityPresent: shm.selectedIdentityPresent,
|
|
SelectedIdentity: shm.selectedIdentity,
|
|
Cookie: shm.cookie,
|
|
SelectedGroup: shm.selectedGroup,
|
|
}
|
|
}
|
|
}
|
|
|
|
type PubClientHelloMsg struct {
|
|
Raw []byte
|
|
Vers uint16
|
|
Random []byte
|
|
SessionId []byte
|
|
CipherSuites []uint16
|
|
CompressionMethods []uint8
|
|
NextProtoNeg bool
|
|
ServerName string
|
|
OcspStapling bool
|
|
Scts bool
|
|
Ems bool // [uTLS] actually implemented due to its prevalence
|
|
SupportedCurves []CurveID
|
|
SupportedPoints []uint8
|
|
TicketSupported bool
|
|
SessionTicket []uint8
|
|
SupportedSignatureAlgorithms []SignatureScheme
|
|
SecureRenegotiation []byte
|
|
SecureRenegotiationSupported bool
|
|
AlpnProtocols []string
|
|
|
|
// 1.3
|
|
SupportedSignatureAlgorithmsCert []SignatureScheme
|
|
SupportedVersions []uint16
|
|
Cookie []byte
|
|
KeyShares []KeyShare
|
|
EarlyData bool
|
|
PskModes []uint8
|
|
PskIdentities []PskIdentity
|
|
PskBinders [][]byte
|
|
QuicTransportParameters []byte
|
|
}
|
|
|
|
func (chm *PubClientHelloMsg) getPrivatePtr() *clientHelloMsg {
|
|
if chm == nil {
|
|
return nil
|
|
} else {
|
|
return &clientHelloMsg{
|
|
raw: chm.Raw,
|
|
vers: chm.Vers,
|
|
random: chm.Random,
|
|
sessionId: chm.SessionId,
|
|
cipherSuites: chm.CipherSuites,
|
|
compressionMethods: chm.CompressionMethods,
|
|
serverName: chm.ServerName,
|
|
ocspStapling: chm.OcspStapling,
|
|
supportedCurves: chm.SupportedCurves,
|
|
supportedPoints: chm.SupportedPoints,
|
|
ticketSupported: chm.TicketSupported,
|
|
sessionTicket: chm.SessionTicket,
|
|
supportedSignatureAlgorithms: chm.SupportedSignatureAlgorithms,
|
|
supportedSignatureAlgorithmsCert: chm.SupportedSignatureAlgorithmsCert,
|
|
secureRenegotiationSupported: chm.SecureRenegotiationSupported,
|
|
secureRenegotiation: chm.SecureRenegotiation,
|
|
extendedMasterSecret: chm.Ems,
|
|
alpnProtocols: chm.AlpnProtocols,
|
|
scts: chm.Scts,
|
|
|
|
supportedVersions: chm.SupportedVersions,
|
|
cookie: chm.Cookie,
|
|
keyShares: KeyShares(chm.KeyShares).ToPrivate(),
|
|
earlyData: chm.EarlyData,
|
|
pskModes: chm.PskModes,
|
|
pskIdentities: PskIdentities(chm.PskIdentities).ToPrivate(),
|
|
pskBinders: chm.PskBinders,
|
|
quicTransportParameters: chm.QuicTransportParameters,
|
|
|
|
nextProtoNeg: chm.NextProtoNeg,
|
|
}
|
|
}
|
|
}
|
|
|
|
func (chm *clientHelloMsg) getPublicPtr() *PubClientHelloMsg {
|
|
if chm == nil {
|
|
return nil
|
|
} else {
|
|
return &PubClientHelloMsg{
|
|
Raw: chm.raw,
|
|
Vers: chm.vers,
|
|
Random: chm.random,
|
|
SessionId: chm.sessionId,
|
|
CipherSuites: chm.cipherSuites,
|
|
CompressionMethods: chm.compressionMethods,
|
|
NextProtoNeg: chm.nextProtoNeg,
|
|
ServerName: chm.serverName,
|
|
OcspStapling: chm.ocspStapling,
|
|
Scts: chm.scts,
|
|
Ems: chm.extendedMasterSecret,
|
|
SupportedCurves: chm.supportedCurves,
|
|
SupportedPoints: chm.supportedPoints,
|
|
TicketSupported: chm.ticketSupported,
|
|
SessionTicket: chm.sessionTicket,
|
|
SupportedSignatureAlgorithms: chm.supportedSignatureAlgorithms,
|
|
SecureRenegotiation: chm.secureRenegotiation,
|
|
SecureRenegotiationSupported: chm.secureRenegotiationSupported,
|
|
AlpnProtocols: chm.alpnProtocols,
|
|
|
|
SupportedSignatureAlgorithmsCert: chm.supportedSignatureAlgorithmsCert,
|
|
SupportedVersions: chm.supportedVersions,
|
|
Cookie: chm.cookie,
|
|
KeyShares: keyShares(chm.keyShares).ToPublic(),
|
|
EarlyData: chm.earlyData,
|
|
PskModes: chm.pskModes,
|
|
PskIdentities: pskIdentities(chm.pskIdentities).ToPublic(),
|
|
PskBinders: chm.pskBinders,
|
|
QuicTransportParameters: chm.quicTransportParameters,
|
|
}
|
|
}
|
|
}
|
|
|
|
// UnmarshalClientHello allows external code to parse raw client hellos.
|
|
// It returns nil on failure.
|
|
func UnmarshalClientHello(data []byte) *PubClientHelloMsg {
|
|
m := &clientHelloMsg{}
|
|
if m.unmarshal(data) {
|
|
return m.getPublicPtr()
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Marshal allows external code to convert a ClientHello object back into
|
|
// raw bytes.
|
|
func (chm *PubClientHelloMsg) Marshal() ([]byte, error) {
|
|
return chm.getPrivatePtr().marshal()
|
|
}
|
|
|
|
// A CipherSuite is a specific combination of key agreement, cipher and MAC
|
|
// function. All cipher suites currently assume RSA key agreement.
|
|
type PubCipherSuite struct {
|
|
Id uint16
|
|
// the lengths, in bytes, of the key material needed for each component.
|
|
KeyLen int
|
|
MacLen int
|
|
IvLen int
|
|
Ka func(version uint16) keyAgreement
|
|
// flags is a bitmask of the suite* values, above.
|
|
Flags int
|
|
Cipher func(key, iv []byte, isRead bool) interface{}
|
|
Mac func(macKey []byte) hash.Hash
|
|
Aead func(key, fixedNonce []byte) aead
|
|
}
|
|
|
|
func (cs *PubCipherSuite) getPrivatePtr() *cipherSuite {
|
|
if cs == nil {
|
|
return nil
|
|
} else {
|
|
return &cipherSuite{
|
|
id: cs.Id,
|
|
keyLen: cs.KeyLen,
|
|
macLen: cs.MacLen,
|
|
ivLen: cs.IvLen,
|
|
ka: cs.Ka,
|
|
flags: cs.Flags,
|
|
cipher: cs.Cipher,
|
|
mac: cs.Mac,
|
|
aead: cs.Aead,
|
|
}
|
|
}
|
|
}
|
|
|
|
func (cs *cipherSuite) getPublicObj() PubCipherSuite {
|
|
if cs == nil {
|
|
return PubCipherSuite{}
|
|
} else {
|
|
return PubCipherSuite{
|
|
Id: cs.id,
|
|
KeyLen: cs.keyLen,
|
|
MacLen: cs.macLen,
|
|
IvLen: cs.ivLen,
|
|
Ka: cs.ka,
|
|
Flags: cs.flags,
|
|
Cipher: cs.cipher,
|
|
Mac: cs.mac,
|
|
Aead: cs.aead,
|
|
}
|
|
}
|
|
}
|
|
|
|
// A FinishedHash calculates the hash of a set of handshake messages suitable
|
|
// for including in a Finished message.
|
|
type FinishedHash struct {
|
|
Client hash.Hash
|
|
Server hash.Hash
|
|
|
|
// Prior to TLS 1.2, an additional MD5 hash is required.
|
|
ClientMD5 hash.Hash
|
|
ServerMD5 hash.Hash
|
|
|
|
// In TLS 1.2, a full buffer is sadly required.
|
|
Buffer []byte
|
|
|
|
Version uint16
|
|
Prf func(result, secret, label, seed []byte)
|
|
}
|
|
|
|
func (fh *FinishedHash) getPrivateObj() finishedHash {
|
|
if fh == nil {
|
|
return finishedHash{}
|
|
} else {
|
|
return finishedHash{
|
|
client: fh.Client,
|
|
server: fh.Server,
|
|
clientMD5: fh.ClientMD5,
|
|
serverMD5: fh.ServerMD5,
|
|
buffer: fh.Buffer,
|
|
version: fh.Version,
|
|
prf: fh.Prf,
|
|
}
|
|
}
|
|
}
|
|
|
|
func (fh *finishedHash) getPublicObj() FinishedHash {
|
|
if fh == nil {
|
|
return FinishedHash{}
|
|
} else {
|
|
return FinishedHash{
|
|
Client: fh.client,
|
|
Server: fh.server,
|
|
ClientMD5: fh.clientMD5,
|
|
ServerMD5: fh.serverMD5,
|
|
Buffer: fh.buffer,
|
|
Version: fh.version,
|
|
Prf: fh.prf}
|
|
}
|
|
}
|
|
|
|
// TLS 1.3 Key Share. See RFC 8446, Section 4.2.8.
|
|
type KeyShare struct {
|
|
Group CurveID `json:"group"`
|
|
Data []byte `json:"key_exchange,omitempty"` // optional
|
|
}
|
|
|
|
type KeyShares []KeyShare
|
|
type keyShares []keyShare
|
|
|
|
func (kss keyShares) ToPublic() []KeyShare {
|
|
var KSS []KeyShare
|
|
for _, ks := range kss {
|
|
KSS = append(KSS, KeyShare{Data: ks.data, Group: ks.group})
|
|
}
|
|
return KSS
|
|
}
|
|
func (KSS KeyShares) ToPrivate() []keyShare {
|
|
var kss []keyShare
|
|
for _, KS := range KSS {
|
|
kss = append(kss, keyShare{data: KS.Data, group: KS.Group})
|
|
}
|
|
return kss
|
|
}
|
|
|
|
// TLS 1.3 PSK Identity. Can be a Session Ticket, or a reference to a saved
|
|
// session. See RFC 8446, Section 4.2.11.
|
|
type PskIdentity struct {
|
|
Label []byte `json:"identity"`
|
|
ObfuscatedTicketAge uint32 `json:"obfuscated_ticket_age"`
|
|
}
|
|
|
|
type PskIdentities []PskIdentity
|
|
type pskIdentities []pskIdentity
|
|
|
|
func (pss pskIdentities) ToPublic() []PskIdentity {
|
|
var PSS []PskIdentity
|
|
for _, ps := range pss {
|
|
PSS = append(PSS, PskIdentity{Label: ps.label, ObfuscatedTicketAge: ps.obfuscatedTicketAge})
|
|
}
|
|
return PSS
|
|
}
|
|
|
|
func (PSS PskIdentities) ToPrivate() []pskIdentity {
|
|
var pss []pskIdentity
|
|
for _, PS := range PSS {
|
|
pss = append(pss, pskIdentity{label: PS.Label, obfuscatedTicketAge: PS.ObfuscatedTicketAge})
|
|
}
|
|
return pss
|
|
}
|
|
|
|
// ClientSessionState is public, but all its fields are private. Let's add setters, getters and constructor
|
|
|
|
// ClientSessionState contains the state needed by clients to resume TLS sessions.
|
|
func MakeClientSessionState(
|
|
SessionTicket []uint8,
|
|
Vers uint16,
|
|
CipherSuite uint16,
|
|
MasterSecret []byte,
|
|
ServerCertificates []*x509.Certificate,
|
|
VerifiedChains [][]*x509.Certificate) *ClientSessionState {
|
|
css := &ClientSessionState{
|
|
ticket: SessionTicket,
|
|
session: &SessionState{
|
|
version: Vers,
|
|
cipherSuite: CipherSuite,
|
|
secret: MasterSecret,
|
|
peerCertificates: ServerCertificates,
|
|
verifiedChains: VerifiedChains,
|
|
},
|
|
}
|
|
return css
|
|
}
|
|
|
|
// Encrypted ticket used for session resumption with server
|
|
func (css *ClientSessionState) SessionTicket() []uint8 {
|
|
return css.ticket
|
|
}
|
|
|
|
// SSL/TLS version negotiated for the session
|
|
func (css *ClientSessionState) Vers() uint16 {
|
|
return css.session.version
|
|
}
|
|
|
|
// Ciphersuite negotiated for the session
|
|
func (css *ClientSessionState) CipherSuite() uint16 {
|
|
return css.session.cipherSuite
|
|
}
|
|
|
|
// MasterSecret generated by client on a full handshake
|
|
func (css *ClientSessionState) MasterSecret() []byte {
|
|
return css.session.secret
|
|
}
|
|
|
|
// Certificate chain presented by the server
|
|
func (css *ClientSessionState) ServerCertificates() []*x509.Certificate {
|
|
return css.session.peerCertificates
|
|
}
|
|
|
|
// Certificate chains we built for verification
|
|
func (css *ClientSessionState) VerifiedChains() [][]*x509.Certificate {
|
|
return css.session.verifiedChains
|
|
}
|
|
|
|
func (css *ClientSessionState) SetSessionTicket(SessionTicket []uint8) {
|
|
css.ticket = SessionTicket
|
|
}
|
|
func (css *ClientSessionState) SetVers(Vers uint16) {
|
|
if css.session == nil {
|
|
css.session = &SessionState{}
|
|
}
|
|
css.session.version = Vers
|
|
}
|
|
func (css *ClientSessionState) SetCipherSuite(CipherSuite uint16) {
|
|
if css.session == nil {
|
|
css.session = &SessionState{}
|
|
}
|
|
css.session.cipherSuite = CipherSuite
|
|
}
|
|
func (css *ClientSessionState) SetMasterSecret(MasterSecret []byte) {
|
|
if css.session == nil {
|
|
css.session = &SessionState{}
|
|
}
|
|
css.session.secret = MasterSecret
|
|
}
|
|
func (css *ClientSessionState) SetServerCertificates(ServerCertificates []*x509.Certificate) {
|
|
if css.session == nil {
|
|
css.session = &SessionState{}
|
|
}
|
|
css.session.peerCertificates = ServerCertificates
|
|
}
|
|
func (css *ClientSessionState) SetVerifiedChains(VerifiedChains [][]*x509.Certificate) {
|
|
if css.session == nil {
|
|
css.session = &SessionState{}
|
|
}
|
|
css.session.verifiedChains = VerifiedChains
|
|
}
|
|
|
|
// TicketKey is the internal representation of a session ticket key.
|
|
type TicketKey struct {
|
|
AesKey [16]byte
|
|
HmacKey [16]byte
|
|
// created is the time at which this ticket key was created. See Config.ticketKeys.
|
|
Created time.Time
|
|
}
|
|
|
|
type TicketKeys []TicketKey
|
|
type ticketKeys []ticketKey
|
|
|
|
func TicketKeyFromBytes(b [32]byte) TicketKey {
|
|
// [uTLS]
|
|
// empty config is required
|
|
config := &Config{}
|
|
tk := config.ticketKeyFromBytes(b)
|
|
return tk.ToPublic()
|
|
}
|
|
|
|
func (tk ticketKey) ToPublic() TicketKey {
|
|
return TicketKey{
|
|
AesKey: tk.aesKey,
|
|
HmacKey: tk.hmacKey,
|
|
Created: tk.created,
|
|
}
|
|
}
|
|
|
|
func (TK TicketKey) ToPrivate() ticketKey {
|
|
return ticketKey{
|
|
aesKey: TK.AesKey,
|
|
hmacKey: TK.HmacKey,
|
|
created: TK.Created,
|
|
}
|
|
}
|
|
|
|
func (tks ticketKeys) ToPublic() []TicketKey {
|
|
var TKS []TicketKey
|
|
for _, ks := range tks {
|
|
TKS = append(TKS, ks.ToPublic())
|
|
}
|
|
return TKS
|
|
}
|
|
|
|
func (TKS TicketKeys) ToPrivate() []ticketKey {
|
|
var tks []ticketKey
|
|
for _, TK := range TKS {
|
|
tks = append(tks, TK.ToPrivate())
|
|
}
|
|
return tks
|
|
}
|
|
|
|
type KemPrivateKey struct {
|
|
SecretKey kem.PrivateKey
|
|
CurveID CurveID
|
|
}
|
|
|
|
func (kpk *KemPrivateKey) ToPrivate() *kemPrivateKey {
|
|
if kpk == nil {
|
|
return nil
|
|
} else {
|
|
return &kemPrivateKey{
|
|
secretKey: kpk.SecretKey,
|
|
curveID: kpk.CurveID,
|
|
}
|
|
}
|
|
}
|
|
|
|
func (kpk *kemPrivateKey) ToPublic() *KemPrivateKey {
|
|
if kpk == nil {
|
|
return nil
|
|
} else {
|
|
return &KemPrivateKey{
|
|
SecretKey: kpk.secretKey,
|
|
CurveID: kpk.curveID,
|
|
}
|
|
}
|
|
}
|