Skip to content

Commit

Permalink
Merge pull request #135 from ekr/minq_draft_21
Browse files Browse the repository at this point in the history
Minq draft 21
  • Loading branch information
bifurcation authored Jul 25, 2017
2 parents 0b79b70 + f4de2a1 commit 4f4f8df
Show file tree
Hide file tree
Showing 14 changed files with 204 additions and 138 deletions.
4 changes: 2 additions & 2 deletions alert.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ const (
AlertBadCertificateHashValue Alert = 114
AlertUnknownPSKIdentity Alert = 115
AlertNoApplicationProtocol Alert = 120
AlertWouldBlock Alert = 254
AlertWouldBlock Alert = 254
AlertNoAlert Alert = 255
)

Expand Down Expand Up @@ -82,7 +82,7 @@ var alertText = map[Alert]string{
AlertUnknownPSKIdentity: "unknown PSK identity",
AlertNoApplicationProtocol: "no application protocol",
AlertNoRenegotiation: "no renegotiation",
AlertWouldBlock: "would have blocked",
AlertWouldBlock: "would have blocked",
AlertNoAlert: "no alert",
}

Expand Down
50 changes: 27 additions & 23 deletions client-state-machine.go
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ func (state ClientStateStart) Next(hm *HandshakeMessage) (HandshakeState, []Hand

compatibleSuites := []CipherSuite{}
for _, suite := range ch.CipherSuites {
if cipherSuiteMap[suite].hash == params.hash {
if cipherSuiteMap[suite].Hash == params.Hash {
compatibleSuites = append(compatibleSuites, suite)
}
}
Expand Down Expand Up @@ -194,17 +194,17 @@ func (state ClientStateStart) Next(hm *HandshakeMessage) (HandshakeState, []Hand
},
Binders: []PSKBinderEntry{
// Note: Stub to get the length fields right
{Binder: bytes.Repeat([]byte{0x00}, params.hash.Size())},
{Binder: bytes.Repeat([]byte{0x00}, params.Hash.Size())},
},
}
ch.Extensions.Add(psk)

// Compute the binder key
h0 := params.hash.New().Sum(nil)
zero := bytes.Repeat([]byte{0}, params.hash.Size())
h0 := params.Hash.New().Sum(nil)
zero := bytes.Repeat([]byte{0}, params.Hash.Size())

earlyHash = params.hash
earlySecret = hkdfExtract(params.hash, zero, key.Key)
earlyHash = params.Hash
earlySecret = HkdfExtract(params.Hash, zero, key.Key)
logf(logTypeCrypto, "early secret: [%d] %x", len(earlySecret), earlySecret)

binderLabel := labelExternalBinder
Expand All @@ -221,7 +221,7 @@ func (state ClientStateStart) Next(hm *HandshakeMessage) (HandshakeState, []Hand
return nil, nil, AlertInternalError
}

truncHash := params.hash.New()
truncHash := params.Hash.New()
truncHash.Write(trunc)

binder := computeFinishedData(params, binderKey, truncHash.Sum(nil))
Expand All @@ -235,7 +235,7 @@ func (state ClientStateStart) Next(hm *HandshakeMessage) (HandshakeState, []Hand
clientHello, _ = HandshakeMessageFromBody(ch)

// Compute early traffic keys
h := params.hash.New()
h := params.Hash.New()
h.Write(clientHello.Marshal())
chHash := h.Sum(nil)

Expand Down Expand Up @@ -351,7 +351,7 @@ func (state ClientStateWaitSH) Next(hm *HandshakeMessage) (HandshakeState, []Han
// Hash the body into a pseudo-message
// XXX: Ignoring some errors here
params := cipherSuiteMap[hrr.CipherSuite]
h := params.hash.New()
h := params.Hash.New()
h.Write(state.clientHello.Marshal())
firstClientHello := &HandshakeMessage{
msgType: HandshakeTypeMessageHash,
Expand Down Expand Up @@ -420,39 +420,39 @@ func (state ClientStateWaitSH) Next(hm *HandshakeMessage) (HandshakeState, []Han
}

// Start up the handshake hash
handshakeHash := params.hash.New()
handshakeHash := params.Hash.New()
handshakeHash.Write(state.firstClientHello.Marshal())
handshakeHash.Write(state.helloRetryRequest.Marshal())
handshakeHash.Write(state.clientHello.Marshal())
handshakeHash.Write(hm.Marshal())

// Compute handshake secrets
zero := bytes.Repeat([]byte{0}, params.hash.Size())
zero := bytes.Repeat([]byte{0}, params.Hash.Size())

var earlySecret []byte
if state.Params.UsingPSK {
if params.hash != state.earlyHash {
if params.Hash != state.earlyHash {
logf(logTypeCrypto, "Change of hash between early and normal init early=[%02x] suite=[%04x] hash=[%02x]",
state.earlyHash, suite, params.hash)
state.earlyHash, suite, params.Hash)
}

earlySecret = state.earlySecret
} else {
earlySecret = hkdfExtract(params.hash, zero, zero)
earlySecret = HkdfExtract(params.Hash, zero, zero)
}

if dhSecret == nil {
dhSecret = zero
}

h0 := params.hash.New().Sum(nil)
h0 := params.Hash.New().Sum(nil)
h2 := handshakeHash.Sum(nil)
preHandshakeSecret := deriveSecret(params, earlySecret, labelDerived, h0)
handshakeSecret := hkdfExtract(params.hash, preHandshakeSecret, dhSecret)
handshakeSecret := HkdfExtract(params.Hash, preHandshakeSecret, dhSecret)
clientHandshakeTrafficSecret := deriveSecret(params, handshakeSecret, labelClientHandshakeTrafficSecret, h2)
serverHandshakeTrafficSecret := deriveSecret(params, handshakeSecret, labelServerHandshakeTrafficSecret, h2)
preMasterSecret := deriveSecret(params, handshakeSecret, labelDerived, h0)
masterSecret := hkdfExtract(params.hash, preMasterSecret, zero)
masterSecret := HkdfExtract(params.Hash, preMasterSecret, zero)

logf(logTypeCrypto, "early secret: [%d] %x", len(earlySecret), earlySecret)
logf(logTypeCrypto, "handshake secret: [%d] %x", len(handshakeSecret), handshakeSecret)
Expand Down Expand Up @@ -485,7 +485,7 @@ func (state ClientStateWaitSH) Next(hm *HandshakeMessage) (HandshakeState, []Han
type ClientStateWaitEE struct {
AuthCertificate func(chain []CertificateEntry) error
Params ConnectionParameters
cryptoParams cipherSuiteParams
cryptoParams CipherSuiteParams
handshakeHash hash.Hash
certificates []*Certificate
masterSecret []byte
Expand Down Expand Up @@ -549,7 +549,7 @@ func (state ClientStateWaitEE) Next(hm *HandshakeMessage) (HandshakeState, []Han
type ClientStateWaitCertCR struct {
AuthCertificate func(chain []CertificateEntry) error
Params ConnectionParameters
cryptoParams cipherSuiteParams
cryptoParams CipherSuiteParams
handshakeHash hash.Hash
certificates []*Certificate
masterSecret []byte
Expand Down Expand Up @@ -617,7 +617,7 @@ func (state ClientStateWaitCertCR) Next(hm *HandshakeMessage) (HandshakeState, [
type ClientStateWaitCert struct {
AuthCertificate func(chain []CertificateEntry) error
Params ConnectionParameters
cryptoParams cipherSuiteParams
cryptoParams CipherSuiteParams
handshakeHash hash.Hash

certificates []*Certificate
Expand Down Expand Up @@ -662,7 +662,7 @@ func (state ClientStateWaitCert) Next(hm *HandshakeMessage) (HandshakeState, []H
type ClientStateWaitCV struct {
AuthCertificate func(chain []CertificateEntry) error
Params ConnectionParameters
cryptoParams cipherSuiteParams
cryptoParams CipherSuiteParams
handshakeHash hash.Hash

certificates []*Certificate
Expand Down Expand Up @@ -724,7 +724,7 @@ func (state ClientStateWaitCV) Next(hm *HandshakeMessage) (HandshakeState, []Han

type ClientStateWaitFinished struct {
Params ConnectionParameters
cryptoParams cipherSuiteParams
cryptoParams CipherSuiteParams
handshakeHash hash.Hash

certificates []*Certificate
Expand Down Expand Up @@ -777,6 +777,9 @@ func (state ClientStateWaitFinished) Next(hm *HandshakeMessage) (HandshakeState,
clientTrafficKeys := makeTrafficKeys(state.cryptoParams, clientTrafficSecret)
serverTrafficKeys := makeTrafficKeys(state.cryptoParams, serverTrafficSecret)

exporterSecret := deriveSecret(state.cryptoParams, state.masterSecret, labelExporterSecret, h4)
logf(logTypeCrypto, "client exporter secret: [%d] %x", len(exporterSecret), exporterSecret)

// Assemble client's second flight
toSend := []HandshakeAction{}

Expand Down Expand Up @@ -839,7 +842,7 @@ func (state ClientStateWaitFinished) Next(hm *HandshakeMessage) (HandshakeState,
logf(logTypeHandshake, "Handshake Hash to be verified: [%d] %x", len(hcv), hcv)

certificateVerify := &CertificateVerifyBody{Algorithm: certScheme}
logf(logTypeHandshake, "Creating CertVerify: %04x %v", certScheme, state.cryptoParams.hash)
logf(logTypeHandshake, "Creating CertVerify: %04x %v", certScheme, state.cryptoParams.Hash)

err = certificateVerify.Sign(cert.PrivateKey, hcv)
if err != nil {
Expand Down Expand Up @@ -895,6 +898,7 @@ func (state ClientStateWaitFinished) Next(hm *HandshakeMessage) (HandshakeState,
resumptionSecret: resumptionSecret,
clientTrafficSecret: clientTrafficSecret,
serverTrafficSecret: serverTrafficSecret,
exporterSecret: exporterSecret,
}
return nextState, toSend, AlertNoAlert
}
10 changes: 5 additions & 5 deletions common_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,12 +53,12 @@ func assertNotByteEquals(t *testing.T, a, b []byte) {
assert(t, !bytes.Equal(a, b), fmt.Sprintf("%+v == %+v", hex.EncodeToString(a), hex.EncodeToString(b)))
}

func assertCipherSuiteParamsEquals(t *testing.T, a, b cipherSuiteParams) {
assertEquals(t, a.suite, b.suite)
func assertCipherSuiteParamsEquals(t *testing.T, a, b CipherSuiteParams) {
assertEquals(t, a.Suite, b.Suite)
// Can't compare aeadFactory values
assertEquals(t, a.hash, b.hash)
assertEquals(t, a.keyLen, b.keyLen)
assertEquals(t, a.ivLen, b.ivLen)
assertEquals(t, a.Hash, b.Hash)
assertEquals(t, a.KeyLen, b.KeyLen)
assertEquals(t, a.IvLen, b.IvLen)
}

func assertDeepEquals(t *testing.T, a, b interface{}) {
Expand Down
37 changes: 34 additions & 3 deletions conn.go
Original file line number Diff line number Diff line change
Expand Up @@ -173,9 +173,10 @@ var (
)

type ConnectionState struct {
HandshakeComplete bool // TLS handshake is complete
CipherSuite CipherSuite // cipher suite in use (TLS_RSA_WITH_RC4_128_SHA, ...)
PeerCertificates []*x509.Certificate // certificate chain presented by remote peer
HandshakeState string // string representation of the handshake state.
CipherSuite CipherSuiteParams // cipher suite in use (TLS_RSA_WITH_RC4_128_SHA, ...)
PeerCertificates []*x509.Certificate // certificate chain presented by remote peer TODO([email protected]): implement
NextProto string // Selected ALPN proto
}

// Conn implements the net.Conn interface, as with "crypto/tls"
Expand Down Expand Up @@ -728,3 +729,33 @@ func (c *Conn) SendKeyUpdate(requestUpdate bool) error {
func (c *Conn) GetHsState() string {
return reflect.TypeOf(c.hState).Name()
}

func (c *Conn) ComputeExporter(label string, context []byte, keyLength int) ([]byte, error) {
_, connected := c.hState.(StateConnected)
if !connected {
return nil, fmt.Errorf("Cannot compute exporter when state is not connected")
}

if c.state.exporterSecret == nil {
return nil, fmt.Errorf("Internal error: no exporter secret")
}

h0 := c.state.cryptoParams.Hash.New().Sum(nil)
tmpSecret := deriveSecret(c.state.cryptoParams, c.state.exporterSecret, label, h0)

hc := c.state.cryptoParams.Hash.New().Sum(context)
return HkdfExpandLabel(c.state.cryptoParams.Hash, tmpSecret, "exporter", hc, keyLength), nil
}

func (c *Conn) State() ConnectionState {
state := ConnectionState{
HandshakeState: c.GetHsState(),
}

if c.handshakeComplete {
state.CipherSuite = cipherSuiteMap[c.state.Params.CipherSuite]
state.NextProto = c.state.Params.NextProto
}

return state
}
34 changes: 25 additions & 9 deletions conn_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ package mint
import (
"bytes"
"crypto/x509"
"io"
"fmt"
"io"
"net"
"sync"
"testing"
Expand Down Expand Up @@ -70,7 +70,7 @@ func (p *pipeConn) SetWriteDeadline(t time.Time) error { return nil }

type bufferedConn struct {
buffer bytes.Buffer
w net.Conn
w net.Conn
}

func (b *bufferedConn) Write(buf []byte) (n int, err error) {
Expand All @@ -92,7 +92,7 @@ func (p *bufferedConn) SetWriteDeadline(t time.Time) error { return nil }

func (b *bufferedConn) Flush() error {
buf := b.buffer.Bytes()

n, err := b.w.Write(buf)
if err != nil {
return err
Expand All @@ -103,11 +103,11 @@ func (b *bufferedConn) Flush() error {
b.buffer.Reset()
return nil
}

func newBufferedConn(p net.Conn) *bufferedConn {
return &bufferedConn{bytes.Buffer{}, p}
}

var (
serverName = "example.com"

Expand Down Expand Up @@ -204,7 +204,7 @@ var (
nbConfig = &Config{
ServerName: serverName,
Certificates: certificates,
NonBlocking : true,
NonBlocking: true,
}

hrrConfig = &Config{
Expand Down Expand Up @@ -274,6 +274,12 @@ func assertKeySetEquals(t *testing.T, k1, k2 keySet) {
assertByteEquals(t, k1.key, k2.key)
}

func computeExporter(t *testing.T, c *Conn, label string, context []byte, length int) []byte {
res, err := c.ComputeExporter(label, context, length)
assertNotError(t, err, "Could not compute exporter")
return res
}

func TestBasicFlows(t *testing.T) {
for _, conf := range []*Config{basicConfig, hrrConfig, alpnConfig, ffdhConfig, x25519Config} {
cConn, sConn := pipe()
Expand All @@ -300,6 +306,16 @@ func TestBasicFlows(t *testing.T) {
assertByteEquals(t, client.state.resumptionSecret, server.state.resumptionSecret)
assertByteEquals(t, client.state.clientTrafficSecret, server.state.clientTrafficSecret)
assertByteEquals(t, client.state.serverTrafficSecret, server.state.serverTrafficSecret)
assertByteEquals(t, client.state.exporterSecret, server.state.exporterSecret)

emptyContext := []byte{}

assertByteEquals(t, computeExporter(t, client, "E", emptyContext, 20), computeExporter(t, server, "E", emptyContext, 20))
assertNotByteEquals(t, computeExporter(t, client, "E", emptyContext, 20), computeExporter(t, server, "E", emptyContext, 21))
assertNotByteEquals(t, computeExporter(t, client, "E", emptyContext, 20), computeExporter(t, server, "F", emptyContext, 20))
assertByteEquals(t, computeExporter(t, client, "E", []byte{'A'}, 20), computeExporter(t, server, "E", []byte{'A'}, 20))
assertNotByteEquals(t, computeExporter(t, client, "E", []byte{'A'}, 20), computeExporter(t, server, "E", []byte{'B'}, 20))

}
}

Expand Down Expand Up @@ -622,14 +638,14 @@ func TestNonblockingHandshakeAndDataFlow(t *testing.T) {
assertEquals(t, clientAlert, AlertWouldBlock)
serverAlert = server.Handshake()
assertEquals(t, serverAlert, AlertWouldBlock)

// Release ClientHello
cbConn.Flush()

// Process ClientHello, send server first flight.
serverAlert = server.Handshake()
assertEquals(t, serverAlert, AlertWouldBlock)

clientAlert = client.Handshake()
assertEquals(t, clientAlert, AlertWouldBlock)

Expand All @@ -645,7 +661,7 @@ func TestNonblockingHandshakeAndDataFlow(t *testing.T) {
cbConn.Flush()
serverAlert = server.Handshake()
assertEquals(t, serverAlert, AlertNoAlert)

assertDeepEquals(t, client.state.Params, server.state.Params)
assertCipherSuiteParamsEquals(t, client.state.cryptoParams, server.state.cryptoParams)
assertByteEquals(t, client.state.resumptionSecret, server.state.resumptionSecret)
Expand Down
Loading

0 comments on commit 4f4f8df

Please sign in to comment.