Skip to content

RFC8773 and RFC7520 #226

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
170 changes: 136 additions & 34 deletions client-state-machine.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,27 @@ func (state clientStateStart) Next(hr handshakeMessageReader) (HandshakeState, [
alpn = &ALPNExtension{Protocols: state.Opts.NextProtos}
}

// Raw public keys
var cct *ClientCertTypeExtension
var sct *ServerCertTypeExtension
if state.Config.AllowRawPublicKeys || !state.Config.ForbidCertificates {
cct = &ClientCertTypeExtension{HandshakeType: HandshakeTypeClientHello}
sct = &ServerCertTypeExtension{HandshakeType: HandshakeTypeClientHello}

if state.Config.AllowRawPublicKeys {
cct.CertificateTypes = append(cct.CertificateTypes, CertificateTypeRawPublicKey)
sct.CertificateTypes = append(sct.CertificateTypes, CertificateTypeRawPublicKey)
}

if !state.Config.ForbidCertificates {
cct.CertificateTypes = append(cct.CertificateTypes, CertificateTypeX509)
sct.CertificateTypes = append(sct.CertificateTypes, CertificateTypeX509)
}
}
if state.Config.AllowRawPublicKeys || state.Config.ForbidCertificates {
logf(logTypeHandshake, "[ClientStateStart] AllowRawPublicKeys only")
}

// Construct base ClientHello
ch := &ClientHelloBody{
LegacyVersion: wireVersion(state.hsCtx.hIn),
Expand All @@ -113,7 +134,7 @@ func (state clientStateStart) Next(hr handshakeMessageReader) (HandshakeState, [
return nil, nil, AlertInternalError
}
for _, ext := range []ExtensionBody{&sv, &sni, &ks, &sg, &sa} {
err := ch.Extensions.Add(ext)
err = ch.Extensions.Add(ext)
if err != nil {
logf(logTypeHandshake, "[ClientStateStart] Error adding extension type=[%v] [%v]", ext.Type(), err)
return nil, nil, AlertInternalError
Expand All @@ -128,6 +149,18 @@ func (state clientStateStart) Next(hr handshakeMessageReader) (HandshakeState, [
return nil, nil, AlertInternalError
}
}
if cct != nil {
err = ch.Extensions.Add(cct)
if err != nil {
logf(logTypeHandshake, "[ClientStateStart] Error adding ClientCertType extension [%v]", err)
return nil, nil, AlertInternalError
}
err = ch.Extensions.Add(sct)
if err != nil {
logf(logTypeHandshake, "[ClientStateStart] Error adding ServerCertType extension [%v]", err)
return nil, nil, AlertInternalError
}
}
if state.cookie != nil {
err := ch.Extensions.Add(&CookieExtension{Cookie: state.cookie})
if err != nil {
Expand Down Expand Up @@ -157,6 +190,7 @@ func (state clientStateStart) Next(hr handshakeMessageReader) (HandshakeState, [
// Handle PSK and EarlyData just before transmitting, so that we can
// calculate the PSK binder value
var psk *PreSharedKeyExtension
var certWithExternPSK *CertWithExternPSKExtension
var ed *EarlyDataExtension
var offeredPSK PreSharedKey
var earlyHash crypto.Hash
Expand Down Expand Up @@ -215,6 +249,12 @@ func (state clientStateStart) Next(hr handshakeMessageReader) (HandshakeState, [
{Binder: bytes.Repeat([]byte{0x00}, params.Hash.Size())},
},
}

if state.Config.CertWithExternPSK == true {
logf(logTypeHandshake, "Adding CertWithExternPSKExtension")
certWithExternPSK = &CertWithExternPSKExtension{}
ch.Extensions.Add(certWithExternPSK)
}
ch.Extensions.Add(psk)

// Compute the binder key
Expand Down Expand Up @@ -443,11 +483,13 @@ func (state clientStateWaitSH) Next(hr handshakeMessageReader) (HandshakeState,
// Do PSK or key agreement depending on extensions
serverPSK := PreSharedKeyExtension{HandshakeType: HandshakeTypeServerHello}
serverKeyShare := KeyShareExtension{HandshakeType: HandshakeTypeServerHello}
certWithExternPSK := CertWithExternPSKExtension{}

foundExts, err := sh.Extensions.Parse(
[]ExtensionBody{
&serverPSK,
&serverKeyShare,
&certWithExternPSK,
})
if err != nil {
logf(logTypeHandshake, "[ClientWaitSH] Error processing extensions [%v]", err)
Expand All @@ -456,6 +498,12 @@ func (state clientStateWaitSH) Next(hr handshakeMessageReader) (HandshakeState,

if foundExts[ExtensionTypePreSharedKey] && (serverPSK.SelectedIdentity == 0) {
state.Params.UsingPSK = true
logf(logTypeHandshake, "[ClientStateWaitSH] UsingPSK")
}

if foundExts[ExtensionTypeCertWithExternPSK] {
state.Params.UsingCertWithExternPSK = true
logf(logTypeHandshake, "[ClientStateWaitSH] Found ExtensionTypeCertWithExternPSK")
}

var dhSecret []byte
Expand Down Expand Up @@ -590,11 +638,15 @@ func (state clientStateWaitEE) Next(hr handshakeMessageReader) (HandshakeState,

serverALPN := &ALPNExtension{}
serverEarlyData := &EarlyDataExtension{}
serverClientCertType := &ClientCertTypeExtension{HandshakeType: HandshakeTypeEncryptedExtensions}
serverServerCertType := &ServerCertTypeExtension{HandshakeType: HandshakeTypeEncryptedExtensions}

foundExts, err := ee.Extensions.Parse(
[]ExtensionBody{
serverALPN,
serverEarlyData,
serverClientCertType,
serverServerCertType,
})
if err != nil {
logf(logTypeHandshake, "[ClientStateWaitEE] Error decoding extensions: %v", err)
Expand All @@ -607,6 +659,15 @@ func (state clientStateWaitEE) Next(hr handshakeMessageReader) (HandshakeState,
state.Params.NextProto = serverALPN.Protocols[0]
}

if foundExts[ExtensionTypeClientCertType] &&
foundExts[ExtensionTypeServerCertType] &&
state.Config.AllowRawPublicKeys {
foundClient := serverClientCertType.CertificateTypes[0] == CertificateTypeRawPublicKey
foundServer := serverServerCertType.CertificateTypes[0] == CertificateTypeRawPublicKey
state.Params.UsingRawPublicKeys = foundClient && foundServer
logf(logTypeHandshake, "[ClientStateWaitEE] UsingRawPublicKeys")
}

state.handshakeHash.Write(hm.Marshal())

toSend := []HandshakeAction{}
Expand All @@ -617,7 +678,8 @@ func (state clientStateWaitEE) Next(hr handshakeMessageReader) (HandshakeState,
KeySet: makeTrafficKeys(state.cryptoParams, state.clientHandshakeTrafficSecret)})
}

if state.Params.UsingPSK {
if state.Params.UsingPSK && !state.Params.UsingCertWithExternPSK {
logf(logTypeHandshake, "[ClientStateWaitEE] UsingPSK, not UsingCertWithExternPSK")
logf(logTypeHandshake, "[ClientStateWaitEE] -> [ClientStateWaitFinished]")
nextState := clientStateWaitFinished{
Params: state.Params,
Expand Down Expand Up @@ -818,44 +880,72 @@ func (state clientStateWaitCV) Next(hr handshakeMessageReader) (HandshakeState,
hcv := state.handshakeHash.Sum(nil)
logf(logTypeHandshake, "Handshake Hash to be verified: [%d] %x", len(hcv), hcv)

serverPublicKey := state.serverCertificate.CertificateList[0].CertData.PublicKey
if err := certVerify.Verify(serverPublicKey, hcv); err != nil {
logf(logTypeHandshake, "[ClientStateWaitCV] Server signature failed to verify")
return nil, nil, AlertHandshakeFailure
}

certs := make([]*x509.Certificate, len(state.serverCertificate.CertificateList))
rawCerts := make([][]byte, len(state.serverCertificate.CertificateList))
certs := make([][]byte, len(state.serverCertificate.CertificateList))
for i, certEntry := range state.serverCertificate.CertificateList {
certs[i] = certEntry.CertData
rawCerts[i] = certEntry.CertData.Raw
}

var verifiedChains [][]*x509.Certificate
if !state.Config.InsecureSkipVerify {
opts := x509.VerifyOptions{
Roots: state.Config.RootCAs,
CurrentTime: state.Config.time(),
DNSName: state.Config.ServerName,
Intermediates: x509.NewCertPool(),
var err error
var serverPublicKey crypto.PublicKey
var eeCert *x509.Certificate
if !state.Params.UsingRawPublicKeys {
eeCert, err = x509.ParseCertificate(certs[0])
if err != nil {
logf(logTypeHandshake, "[ClientStateWaitCV] Unable to parse client cert: %v", err)
}

for i, cert := range certs {
if i == 0 {
continue
}
opts.Intermediates.AddCert(cert)
serverPublicKey = eeCert.PublicKey
} else {
serverPublicKey, err = unmarshalSigningKey(certs[0])
if state.Params.UsingPSK {
logf(logTypeHandshake, "[ClientStateWaitCV] UsingRawPublicKeys and PSK")
} else {
logf(logTypeHandshake, "[ClientStateWaitCV] UsingRawPublicKeys")
}
var err error
verifiedChains, err = certs[0].Verify(opts)
if err != nil {
logf(logTypeHandshake, "[ClientStateWaitCV] Certificate verification failed: %s", err)
return nil, nil, AlertBadCertificate
logf(logTypeHandshake, "[ClientStateWaitCV] Unable to parse raw public key: %v", err)
}
}

if err := certVerify.Verify(serverPublicKey, hcv); err != nil {
logf(logTypeHandshake, "[ClientStateWaitCV] Server signature failed to verify")
return nil, nil, AlertHandshakeFailure
}

var verifiedChains [][]*x509.Certificate
if !state.Params.UsingRawPublicKeys {
if !state.Config.InsecureSkipVerify {
opts := x509.VerifyOptions{
Roots: state.Config.RootCAs,
CurrentTime: state.Config.time(),
DNSName: state.Config.ServerName,
Intermediates: x509.NewCertPool(),
}

for i, cert := range certs {
if i == 0 {
continue
}

caCert, err := x509.ParseCertificate(cert)
if err != nil {
logf(logTypeHandshake, "[ClientStateWaitCV] Error parsing server chain: %v", err)
return nil, nil, AlertDecodeError
}

opts.Intermediates.AddCert(caCert)
}
var err error
verifiedChains, err = eeCert.Verify(opts)
if err != nil {
logf(logTypeHandshake, "[ClientStateWaitCV] Certificate verification failed: %s", err)
return nil, nil, AlertBadCertificate
}
}
}

if state.Config.VerifyPeerCertificate != nil {
if err := state.Config.VerifyPeerCertificate(rawCerts, verifiedChains); err != nil {
if err := state.Config.VerifyPeerCertificate(certs, verifiedChains); err != nil {
logf(logTypeHandshake, "[ClientStateWaitCV] Application rejected server certificate: %s", err)
return nil, nil, AlertBadCertificate
}
Expand Down Expand Up @@ -888,7 +978,7 @@ type clientStateWaitFinished struct {

certificates []*Certificate
serverCertificateRequest *CertificateRequestBody
peerCertificates []*x509.Certificate
peerCertificates [][]byte
verifiedChains [][]*x509.Certificate

masterSecret []byte
Expand Down Expand Up @@ -1000,12 +1090,23 @@ func (state clientStateWaitFinished) Next(hr handshakeMessageReader) (HandshakeS
state.handshakeHash.Write(certm.Marshal())
} else {
// Create and send Certificate, CertificateVerify
certificate := &CertificateBody{
CertificateList: make([]CertificateEntry, len(cert.Chain)),
}
for i, entry := range cert.Chain {
certificate.CertificateList[i] = CertificateEntry{CertData: entry}
var certList []CertificateEntry
if !state.Params.UsingRawPublicKeys {
certList = make([]CertificateEntry, len(cert.Chain))
for i, entry := range cert.Chain {
certList[i] = CertificateEntry{CertData: entry.Raw}
}
} else {
certData, err := marshalSigningKey(cert.PrivateKey.Public())
if err != nil {
logf(logTypeHandshake, "[ClientStateWaitFinished] Unable to marshal raw public key [%v]", err)
return nil, nil, AlertInternalError
}

certList = []CertificateEntry{{CertData: certData}}
}

certificate := &CertificateBody{CertificateList: certList}
certm, err := state.hsCtx.hOut.HandshakeMessageFromBody(certificate)
if err != nil {
logf(logTypeHandshake, "[ClientStateWaitFinished] Error marshaling Certificate [%v]", err)
Expand All @@ -1015,6 +1116,7 @@ func (state clientStateWaitFinished) Next(hr handshakeMessageReader) (HandshakeS
toSend = append(toSend, QueueHandshakeMessage{certm})
state.handshakeHash.Write(certm.Marshal())

// Create and send CertificateVerify
hcv := state.handshakeHash.Sum(nil)
logf(logTypeHandshake, "Handshake Hash to be verified: [%d] %x", len(hcv), hcv)

Expand Down
16 changes: 16 additions & 0 deletions common.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,9 @@ const (
ExtensionTypeCookie ExtensionType = 44
ExtensionTypePSKKeyExchangeModes ExtensionType = 45
ExtensionTypeTicketEarlyDataInfo ExtensionType = 46
ExtensionTypeClientCertType ExtensionType = 19
ExtensionTypeServerCertType ExtensionType = 20
ExtensionTypeCertWithExternPSK ExtensionType = 33
)

// enum {...} NamedGroup
Expand Down Expand Up @@ -164,6 +167,19 @@ const (
KeyUpdateRequested KeyUpdateRequest = 1
)

/*
0 X.509 Y [RFC6091]
1 OpenPGP_RESERVED N [RFC6091][RFC8446] Used in TLS versions prior to 1.3.
2 Raw Public Key Y [RFC7250]
3 1609Dot2 N
*/
type CertificateType uint8

const (
CertificateTypeX509 CertificateType = 0
CertificateTypeRawPublicKey CertificateType = 2
)

type State uint8

const (
Expand Down
18 changes: 18 additions & 0 deletions common_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package mint

import (
"bytes"
"crypto"
"encoding/base64"
"encoding/hex"
"fmt"
"reflect"
Expand All @@ -18,6 +20,22 @@ func unhex(h string) []byte {
return b
}

func computeEpskid(der []byte) []byte {
b64 := base64.StdEncoding.EncodeToString(der)
logf(logTypeVerbose, "Base64 DER of SubjectPublicKeyInfo %s", b64)

extract := HkdfExtract(crypto.SHA256, nil, der)
b64 = base64.StdEncoding.EncodeToString(extract)
logf(logTypeVerbose, "Base64 of HKDF-Extract %s", b64)

epskid := HkdfExpand(crypto.SHA256, extract, []byte("tls13-bspsk-identity"), 32)
b64 = base64.StdEncoding.EncodeToString(epskid)
logf(logTypeVerbose, "Base64 of HKDF-Expand (epskid) %s", b64)

return epskid

}

func assertTrue(t *testing.T, test bool, msg string) {
t.Helper()
prefix := string("")
Expand Down
25 changes: 16 additions & 9 deletions conn.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
type Certificate struct {
Chain []*x509.Certificate
PrivateKey crypto.Signer
PublicKey crypto.PublicKey
}

type PreSharedKey struct {
Expand Down Expand Up @@ -120,14 +121,20 @@ type Config struct {
// the verifiedChains argument will always be nil.
VerifyPeerCertificate func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error

CipherSuites []CipherSuite
Groups []NamedGroup
SignatureSchemes []SignatureScheme
NextProtos []string
PSKs PreSharedKeyCache
PSKModes []PSKKeyExchangeMode
NonBlocking bool
UseDTLS bool
CipherSuites []CipherSuite
Groups []NamedGroup
SignatureSchemes []SignatureScheme
NextProtos []string
PSKs PreSharedKeyCache
PSKModes []PSKKeyExchangeMode
NonBlocking bool
UseDTLS bool
CertWithExternPSK bool

// These bools are arranged in opposite directions so that their default
// values reflect the correct default semantics (certs yes, raw keys no)
AllowRawPublicKeys bool
ForbidCertificates bool

RecordLayer RecordLayerFactory

Expand Down Expand Up @@ -250,7 +257,7 @@ var (
type ConnectionState struct {
HandshakeState State
CipherSuite CipherSuiteParams // cipher suite in use (TLS_RSA_WITH_RC4_128_SHA, ...)
PeerCertificates []*x509.Certificate // certificate chain presented by remote peer
PeerCertificates [][]byte // certificate chain presented by remote peer
VerifiedChains [][]*x509.Certificate // verified chains built from PeerCertificates
NextProto string // Selected ALPN proto
UsingPSK bool // Are we using PSK.
Expand Down
Loading