utls/u_public.go
Gaukas Wang 8094658e76
new: Support TLS-PSK (TLS 1.3) (#231)
* uTLS: X25519Kyber768Draft00 hybrid post-quantum key agreement by cloudflare/go (#222)

* 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>

* new: enable PQ parrots (#225)

* Redesign KeySharesEcdheParameters into KeySharesParameters which supports multiple types of keys.

* Optimize program logic to prevent using unwanted keys

* new: more parrots and safety update (#227)

* new: PQ and other parrots

Add new preset parrots:
- HelloChrome_114_Padding_PSK_Shuf
- HelloChrome_115_PQ
- HelloChrome_115_PQ_PSK

* new: ShuffleChromeTLSExtensions

Implement a new function `ShuffleChromeTLSExtensions(exts []TLSExtension) []TLSExtension`.

* update: include psk parameter for parrot-related functions

Update following functions' prototype to accept an optional pskExtension (of type *FakePreSharedKeyExtension):
- `UClient(conn net.Conn, config *Config, clientHelloID ClientHelloID)` => `UClient(conn net.Conn, config *Config, clientHelloID ClientHelloID, pskExtension ...*FakePreSharedKeyExtension)`
- `UTLSIdToSpec(id ClientHelloID)` => `UTLSIdToSpec(id ClientHelloID, pskExtension ...*FakePreSharedKeyExtension)`

* new: pre-defined error from UTLSIdToSpec

Update UTLSIdToSpec to return more comprehensive errors by pre-defining them, allowing easier error comparing/unwrapping.

* new: UtlsPreSharedKeyExtension

In `u_pre_shared_key.go`, create `PreSharedKeyExtension` as an interface, with 3 implementations:
- `UtlsPreSharedKeyExtension` implements full support for `pre_shared_key` less resuming after seeing HRR.
- `FakePreSharedKeyExtension` uses CipherSuiteID, SessionSecret and Identities to calculate the corresponding binders and send them, without setting the internal states. Therefore if the server accepts the PSK and tries to resume, the connection fails.
- `HardcodedPreSharedKeyExtension` allows user to hardcode Identities and Binders to be sent in the extension without setting the internal states. Therefore if the server accepts the PSK and tries to resume, the connection fails.

TODO: Only one of FakePreSharedKeyExtension and HardcodedPreSharedKeyExtension should be kept, the other one should be just removed. We still need to learn more of the safety of hardcoding both Identities and Binders without recalculating the latter.

* update: PSK minor changes and example

* Updates PSK implementations for more comprehensible interfaces when applying preset/json/raw fingerprints.
* Revert FakePreSharedKeyExtension to the old implementation. Add binder size checking.
* Implement TLS-PSK example

New bug: setting `tls.Config.ClientSessionCache` will cause PSK to fail. Currently users must set only `tls.UtlsPreSharedKeyExtension.ClientSessionCacheOverride`.

* fix: PSK failing if config session cache set

* Fix a bug causing PSK to fail if Config.ClientSessionCache is set.
* Removed `ClientSessionCacheOverride` from `UtlsPreSharedKeyExtension`. Set the `ClientSessionCache` in `Config`!

Co-Authored-By: zeeker999 <13848632+zeeker999@users.noreply.github.com>

* Optimize tls resumption (#235)

* feat: bug fix and refactor

* feat: improve example docs: add detailed explanation about the design feat: add assertion on uApplyPatch

* fix: address comments
feat: add option `OmitEmptyPsk` and throw error on empty psk by default
feat: revert changes to public interfaces

* fix: weird residue caused by merging conflict

* fix: remove merge conflict residue code

---------

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>
Co-authored-by: zeeker999 <13848632+zeeker999@users.noreply.github.com>
Co-authored-by: 3andne <52860475+3andne@users.noreply.github.com>
2023-08-27 12:48:31 -06:00

781 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
KeySharesParams *KeySharesParameters
KEMKey *KemPrivateKey
EarlySecret []byte
BinderKey []byte
CertReq *CertificateRequestMsgTLS13
UsingPSK bool // don't set this field when building client hello
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,
keySharesParams: chs.State13.KeySharesParams,
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{
KeySharesParams: chs13.keySharesParams,
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 // used by go tls to determine whether to add the session ticket ext
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
cachedPrivateHello *clientHelloMsg // todo: further optimize to reduce clientHelloMsg construction
}
func (chm *PubClientHelloMsg) getPrivatePtr() *clientHelloMsg {
if chm == nil {
return nil
} else {
private := &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,
}
chm.cachedPrivateHello = private
return private
}
}
func (chm *PubClientHelloMsg) getCachedPrivatePtr() *clientHelloMsg {
if chm == nil {
return nil
} else {
return chm.cachedPrivateHello
}
}
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,
cachedPrivateHello: chm,
}
}
}
// 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,
}
}
}