From e6e379674bdf46d95ca0adb844bd5673797409f0 Mon Sep 17 00:00:00 2001 From: EKR Date: Sun, 18 Jun 2017 06:27:40 -0700 Subject: [PATCH 1/8] Implement 1-RTT Exporter. --- alert.go | 4 ++-- client-state-machine.go | 4 ++++ conn.go | 17 +++++++++++++++++ conn_test.go | 28 +++++++++++++++++++--------- frame-reader.go | 3 +-- record-layer_test.go | 4 +--- server-state-machine.go | 17 +++++++++++++++++ state-machine.go | 1 + 8 files changed, 62 insertions(+), 16 deletions(-) diff --git a/alert.go b/alert.go index 639e323..5e31035 100644 --- a/alert.go +++ b/alert.go @@ -46,7 +46,7 @@ const ( AlertBadCertificateHashValue Alert = 114 AlertUnknownPSKIdentity Alert = 115 AlertNoApplicationProtocol Alert = 120 - AlertWouldBlock Alert = 254 + AlertWouldBlock Alert = 254 AlertNoAlert Alert = 255 ) @@ -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", } diff --git a/client-state-machine.go b/client-state-machine.go index 63a8d52..274186e 100644 --- a/client-state-machine.go +++ b/client-state-machine.go @@ -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{} @@ -895,6 +898,7 @@ func (state ClientStateWaitFinished) Next(hm *HandshakeMessage) (HandshakeState, resumptionSecret: resumptionSecret, clientTrafficSecret: clientTrafficSecret, serverTrafficSecret: serverTrafficSecret, + exporterSecret: exporterSecret, } return nextState, toSend, AlertNoAlert } diff --git a/conn.go b/conn.go index 6d1b06e..5d6b53f 100644 --- a/conn.go +++ b/conn.go @@ -728,3 +728,20 @@ 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) { + _, ok := c.hState.(StateConnected) + if !ok { + 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 +} diff --git a/conn_test.go b/conn_test.go index 6cf8b4d..0b7ce86 100644 --- a/conn_test.go +++ b/conn_test.go @@ -3,8 +3,8 @@ package mint import ( "bytes" "crypto/x509" - "io" "fmt" + "io" "net" "sync" "testing" @@ -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) { @@ -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 @@ -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" @@ -204,7 +204,7 @@ var ( nbConfig = &Config{ ServerName: serverName, Certificates: certificates, - NonBlocking : true, + NonBlocking: true, } hrrConfig = &Config{ @@ -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, length int) []byte { + res, err := c.ComputeExporter(label, []byte{}, 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() @@ -300,6 +306,10 @@ 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) + assertByteEquals(t, computeExporter(t, client, "E", 20), computeExporter(t, server, "E", 20)) + assertNotByteEquals(t, computeExporter(t, client, "E", 20), computeExporter(t, server, "E", 21)) + assertNotByteEquals(t, computeExporter(t, client, "E", 20), computeExporter(t, server, "F", 20)) } } @@ -622,14 +632,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) @@ -645,7 +655,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) diff --git a/frame-reader.go b/frame-reader.go index 35075bd..7889f29 100644 --- a/frame-reader.go +++ b/frame-reader.go @@ -2,8 +2,7 @@ // This is used for both TLS Records and TLS Handshake Messages package mint -import ( -) +import () type framing interface { headerLen() int diff --git a/record-layer_test.go b/record-layer_test.go index 998cb48..86243a0 100644 --- a/record-layer_test.go +++ b/record-layer_test.go @@ -3,8 +3,8 @@ package mint import ( "bytes" "fmt" - "net" "io" + "net" "testing" ) @@ -333,5 +333,3 @@ func TestNonblockingRecord(t *testing.T) { assertEquals(t, pt.contentType, RecordTypeAlert) assertByteEquals(t, pt.fragment, plaintext[5:]) } - - diff --git a/server-state-machine.go b/server-state-machine.go index 2721bd2..a9edf77 100644 --- a/server-state-machine.go +++ b/server-state-machine.go @@ -523,6 +523,9 @@ func (state ServerStateNegotiated) Next(hm *HandshakeMessage) (HandshakeState, [ serverTrafficKeys := makeTrafficKeys(params, serverTrafficSecret) toSend = append(toSend, RekeyOut{Label: "application", KeySet: serverTrafficKeys}) + exporterSecret := deriveSecret(params, masterSecret, labelExporterSecret, h4) + logf(logTypeCrypto, "server exporter secret: [%d] %x", len(exporterSecret), exporterSecret) + if state.Params.UsingEarlyData { clientEarlyTrafficKeys := makeTrafficKeys(params, state.clientEarlyTrafficSecret) @@ -536,6 +539,7 @@ func (state ServerStateNegotiated) Next(hm *HandshakeMessage) (HandshakeState, [ clientHandshakeTrafficSecret: clientHandshakeTrafficSecret, clientTrafficSecret: clientTrafficSecret, serverTrafficSecret: serverTrafficSecret, + exporterSecret: exporterSecret, } toSend = append(toSend, []HandshakeAction{ RekeyIn{Label: "early", KeySet: clientEarlyTrafficKeys}, @@ -558,6 +562,7 @@ func (state ServerStateNegotiated) Next(hm *HandshakeMessage) (HandshakeState, [ clientHandshakeTrafficSecret: clientHandshakeTrafficSecret, clientTrafficSecret: clientTrafficSecret, serverTrafficSecret: serverTrafficSecret, + exporterSecret: exporterSecret, } nextState, moreToSend, alert := waitFlight2.Next(nil) toSend = append(toSend, moreToSend...) @@ -573,6 +578,7 @@ type ServerStateWaitEOED struct { handshakeHash hash.Hash clientTrafficSecret []byte serverTrafficSecret []byte + exporterSecret []byte } func (state ServerStateWaitEOED) Next(hm *HandshakeMessage) (HandshakeState, []HandshakeAction, Alert) { @@ -603,6 +609,7 @@ func (state ServerStateWaitEOED) Next(hm *HandshakeMessage) (HandshakeState, []H clientHandshakeTrafficSecret: state.clientHandshakeTrafficSecret, clientTrafficSecret: state.clientTrafficSecret, serverTrafficSecret: state.serverTrafficSecret, + exporterSecret: state.exporterSecret, } nextState, moreToSend, alert := waitFlight2.Next(nil) toSend = append(toSend, moreToSend...) @@ -618,6 +625,7 @@ type ServerStateWaitFlight2 struct { handshakeHash hash.Hash clientTrafficSecret []byte serverTrafficSecret []byte + exporterSecret []byte } func (state ServerStateWaitFlight2) Next(hm *HandshakeMessage) (HandshakeState, []HandshakeAction, Alert) { @@ -637,6 +645,7 @@ func (state ServerStateWaitFlight2) Next(hm *HandshakeMessage) (HandshakeState, clientHandshakeTrafficSecret: state.clientHandshakeTrafficSecret, clientTrafficSecret: state.clientTrafficSecret, serverTrafficSecret: state.serverTrafficSecret, + exporterSecret: state.exporterSecret, } return nextState, nil, AlertNoAlert } @@ -650,6 +659,7 @@ func (state ServerStateWaitFlight2) Next(hm *HandshakeMessage) (HandshakeState, handshakeHash: state.handshakeHash, clientTrafficSecret: state.clientTrafficSecret, serverTrafficSecret: state.serverTrafficSecret, + exporterSecret: state.exporterSecret, } return nextState, nil, AlertNoAlert } @@ -663,6 +673,7 @@ type ServerStateWaitCert struct { handshakeHash hash.Hash clientTrafficSecret []byte serverTrafficSecret []byte + exporterSecret []byte } func (state ServerStateWaitCert) Next(hm *HandshakeMessage) (HandshakeState, []HandshakeAction, Alert) { @@ -692,6 +703,7 @@ func (state ServerStateWaitCert) Next(hm *HandshakeMessage) (HandshakeState, []H handshakeHash: state.handshakeHash, clientTrafficSecret: state.clientTrafficSecret, serverTrafficSecret: state.serverTrafficSecret, + exporterSecret: state.exporterSecret, } return nextState, nil, AlertNoAlert } @@ -707,6 +719,7 @@ func (state ServerStateWaitCert) Next(hm *HandshakeMessage) (HandshakeState, []H clientTrafficSecret: state.clientTrafficSecret, serverTrafficSecret: state.serverTrafficSecret, clientCertificate: cert, + exporterSecret: state.exporterSecret, } return nextState, nil, AlertNoAlert } @@ -722,6 +735,7 @@ type ServerStateWaitCV struct { handshakeHash hash.Hash clientTrafficSecret []byte serverTrafficSecret []byte + exporterSecret []byte clientCertificate *CertificateBody } @@ -771,6 +785,7 @@ func (state ServerStateWaitCV) Next(hm *HandshakeMessage) (HandshakeState, []Han handshakeHash: state.handshakeHash, clientTrafficSecret: state.clientTrafficSecret, serverTrafficSecret: state.serverTrafficSecret, + exporterSecret: state.exporterSecret, } return nextState, nil, AlertNoAlert } @@ -785,6 +800,7 @@ type ServerStateWaitFinished struct { handshakeHash hash.Hash clientTrafficSecret []byte serverTrafficSecret []byte + exporterSecret []byte } func (state ServerStateWaitFinished) Next(hm *HandshakeMessage) (HandshakeState, []HandshakeAction, Alert) { @@ -831,6 +847,7 @@ func (state ServerStateWaitFinished) Next(hm *HandshakeMessage) (HandshakeState, resumptionSecret: resumptionSecret, clientTrafficSecret: state.clientTrafficSecret, serverTrafficSecret: state.serverTrafficSecret, + exporterSecret: state.exporterSecret, } toSend := []HandshakeAction{ RekeyIn{Label: "application", KeySet: clientTrafficKeys}, diff --git a/state-machine.go b/state-machine.go index 6526f99..75fd64f 100644 --- a/state-machine.go +++ b/state-machine.go @@ -87,6 +87,7 @@ type StateConnected struct { resumptionSecret []byte clientTrafficSecret []byte serverTrafficSecret []byte + exporterSecret []byte } func (state *StateConnected) KeyUpdate(request KeyUpdateRequest) ([]HandshakeAction, Alert) { From 5fc5850cb78851caaf51c13b3b3e167f8f8dbc48 Mon Sep 17 00:00:00 2001 From: EKR Date: Thu, 22 Jun 2017 15:27:04 -0700 Subject: [PATCH 2/8] MT's comments --- conn.go | 4 ++-- conn_test.go | 16 +++++++++++----- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/conn.go b/conn.go index 5d6b53f..8733288 100644 --- a/conn.go +++ b/conn.go @@ -730,8 +730,8 @@ func (c *Conn) GetHsState() string { } func (c *Conn) ComputeExporter(label string, context []byte, keyLength int) ([]byte, error) { - _, ok := c.hState.(StateConnected) - if !ok { + _, connected := c.hState.(StateConnected) + if !connected { return nil, fmt.Errorf("Cannot compute exporter when state is not connected") } diff --git a/conn_test.go b/conn_test.go index 0b7ce86..74e57a7 100644 --- a/conn_test.go +++ b/conn_test.go @@ -274,8 +274,8 @@ func assertKeySetEquals(t *testing.T, k1, k2 keySet) { assertByteEquals(t, k1.key, k2.key) } -func computeExporter(t *testing.T, c *Conn, label string, length int) []byte { - res, err := c.ComputeExporter(label, []byte{}, length) +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 } @@ -307,9 +307,15 @@ func TestBasicFlows(t *testing.T) { assertByteEquals(t, client.state.clientTrafficSecret, server.state.clientTrafficSecret) assertByteEquals(t, client.state.serverTrafficSecret, server.state.serverTrafficSecret) assertByteEquals(t, client.state.exporterSecret, server.state.exporterSecret) - assertByteEquals(t, computeExporter(t, client, "E", 20), computeExporter(t, server, "E", 20)) - assertNotByteEquals(t, computeExporter(t, client, "E", 20), computeExporter(t, server, "E", 21)) - assertNotByteEquals(t, computeExporter(t, client, "E", 20), computeExporter(t, server, "F", 20)) + + 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)) + } } From cec540dfa289a440ea1b0334a6f9f10a83b147a7 Mon Sep 17 00:00:00 2001 From: EKR Date: Mon, 19 Jun 2017 10:39:30 -0700 Subject: [PATCH 3/8] Add some functions to the public API so that they can be used in QUIC implementations. --- client-state-machine.go | 46 +++++++++++++++---------------- common_test.go | 10 +++---- conn.go | 24 ++++++++++++----- conn_test.go | 2 +- crypto.go | 60 ++++++++++++++++++++--------------------- crypto_test.go | 30 ++++++++++----------- negotiation.go | 18 ++++++------- negotiation_test.go | 2 +- server-state-machine.go | 38 +++++++++++++------------- state-machine.go | 22 +++++++-------- 10 files changed, 132 insertions(+), 120 deletions(-) diff --git a/client-state-machine.go b/client-state-machine.go index 274186e..33ce165 100644 --- a/client-state-machine.go +++ b/client-state-machine.go @@ -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) } } @@ -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 @@ -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)) @@ -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) @@ -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, @@ -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) @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -842,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 { diff --git a/common_test.go b/common_test.go index bcf689e..0294595 100644 --- a/common_test.go +++ b/common_test.go @@ -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{}) { diff --git a/conn.go b/conn.go index 8733288..8f29c70 100644 --- a/conn.go +++ b/conn.go @@ -173,9 +173,9 @@ 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(ekr@rtfm.com): implement } // Conn implements the net.Conn interface, as with "crypto/tls" @@ -739,9 +739,21 @@ func (c *Conn) ComputeExporter(label string, context []byte, keyLength int) ([]b return nil, fmt.Errorf("Internal error: no exporter secret") } - h0 := c.state.cryptoParams.hash.New().Sum(nil) + 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 + hc := c.state.cryptoParams.Hash.New().Sum(context) + return HkdfExpandLabel(c.state.cryptoParams.Hash, tmpSecret, "exporter", hc, keyLength), nil +} + +func (c *Conn) GetConnectionState() ConnectionState { + state := ConnectionState{ + HandshakeState: c.GetHsState(), + } + + if c.handshakeComplete { + state.CipherSuite = cipherSuiteMap[c.state.Params.CipherSuite] + } + + return state } diff --git a/conn_test.go b/conn_test.go index 74e57a7..94ec0bc 100644 --- a/conn_test.go +++ b/conn_test.go @@ -315,7 +315,7 @@ func TestBasicFlows(t *testing.T) { 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)) - + } } diff --git a/crypto.go b/crypto.go index beb290e..067cac2 100644 --- a/crypto.go +++ b/crypto.go @@ -29,12 +29,12 @@ var prng = rand.Reader type aeadFactory func(key []byte) (cipher.AEAD, error) -type cipherSuiteParams struct { - suite CipherSuite - cipher aeadFactory // Cipher factory - hash crypto.Hash // Hash function - keyLen int // Key length in octets - ivLen int // IV length in octets +type CipherSuiteParams struct { + Suite CipherSuite + Cipher aeadFactory // Cipher factory + Hash crypto.Hash // Hash function + KeyLen int // Key length in octets + IvLen int // IV length in octets } type signatureAlgorithm uint8 @@ -89,20 +89,20 @@ var ( return cipher.NewGCMWithNonceSize(block, 12) } - cipherSuiteMap = map[CipherSuite]cipherSuiteParams{ + cipherSuiteMap = map[CipherSuite]CipherSuiteParams{ TLS_AES_128_GCM_SHA256: { - suite: TLS_AES_128_GCM_SHA256, - cipher: newAESGCM, - hash: crypto.SHA256, - keyLen: 16, - ivLen: 12, + Suite: TLS_AES_128_GCM_SHA256, + Cipher: newAESGCM, + Hash: crypto.SHA256, + KeyLen: 16, + IvLen: 12, }, TLS_AES_256_GCM_SHA384: { - suite: TLS_AES_256_GCM_SHA384, - cipher: newAESGCM, - hash: crypto.SHA384, - keyLen: 32, - ivLen: 12, + Suite: TLS_AES_256_GCM_SHA384, + Cipher: newAESGCM, + Hash: crypto.SHA384, + KeyLen: 32, + IvLen: 12, }, } @@ -540,7 +540,7 @@ func verify(alg SignatureScheme, publicKey crypto.PublicKey, sigInput []byte, si // From RFC 5869 // PRK = HMAC-Hash(salt, IKM) -func hkdfExtract(hash crypto.Hash, saltIn, input []byte) []byte { +func HkdfExtract(hash crypto.Hash, saltIn, input []byte) []byte { salt := saltIn // if [salt is] not provided, it is set to a string of HashLen zeros @@ -597,7 +597,7 @@ func hkdfEncodeLabel(labelIn string, hashValue []byte, outLen int) []byte { return hkdfLabel } -func hkdfExpand(hash crypto.Hash, prk, info []byte, outLen int) []byte { +func HkdfExpand(hash crypto.Hash, prk, info []byte, outLen int) []byte { out := []byte{} T := []byte{} i := byte(1) @@ -615,9 +615,9 @@ func hkdfExpand(hash crypto.Hash, prk, info []byte, outLen int) []byte { return out[:outLen] } -func hkdfExpandLabel(hash crypto.Hash, secret []byte, label string, hashValue []byte, outLen int) []byte { +func HkdfExpandLabel(hash crypto.Hash, secret []byte, label string, hashValue []byte, outLen int) []byte { info := hkdfEncodeLabel(label, hashValue, outLen) - derived := hkdfExpand(hash, secret, info, outLen) + derived := HkdfExpand(hash, secret, info, outLen) logf(logTypeCrypto, "HKDF Expand: label=[tls13 ] + '%s',requested length=%d\n", label, outLen) logf(logTypeCrypto, "PRK [%d]: %x\n", len(secret), secret) @@ -628,13 +628,13 @@ func hkdfExpandLabel(hash crypto.Hash, secret []byte, label string, hashValue [] return derived } -func deriveSecret(params cipherSuiteParams, secret []byte, label string, messageHash []byte) []byte { - return hkdfExpandLabel(params.hash, secret, label, messageHash, params.hash.Size()) +func deriveSecret(params CipherSuiteParams, secret []byte, label string, messageHash []byte) []byte { + return HkdfExpandLabel(params.Hash, secret, label, messageHash, params.Hash.Size()) } -func computeFinishedData(params cipherSuiteParams, baseKey []byte, input []byte) []byte { - macKey := hkdfExpandLabel(params.hash, baseKey, labelFinished, []byte{}, params.hash.Size()) - mac := hmac.New(params.hash.New, macKey) +func computeFinishedData(params CipherSuiteParams, baseKey []byte, input []byte) []byte { + macKey := HkdfExpandLabel(params.Hash, baseKey, labelFinished, []byte{}, params.Hash.Size()) + mac := hmac.New(params.Hash.New, macKey) mac.Write(input) return mac.Sum(nil) } @@ -645,11 +645,11 @@ type keySet struct { iv []byte } -func makeTrafficKeys(params cipherSuiteParams, secret []byte) keySet { +func makeTrafficKeys(params CipherSuiteParams, secret []byte) keySet { logf(logTypeCrypto, "making traffic keys: secret=%x", secret) return keySet{ - cipher: params.cipher, - key: hkdfExpandLabel(params.hash, secret, "key", []byte{}, params.keyLen), - iv: hkdfExpandLabel(params.hash, secret, "iv", []byte{}, params.ivLen), + cipher: params.Cipher, + key: HkdfExpandLabel(params.Hash, secret, "key", []byte{}, params.KeyLen), + iv: HkdfExpandLabel(params.Hash, secret, "iv", []byte{}, params.IvLen), } } diff --git a/crypto_test.go b/crypto_test.go index 19ac4cd..52c92d6 100644 --- a/crypto_test.go +++ b/crypto_test.go @@ -323,32 +323,32 @@ func TestHKDF(t *testing.T) { hkdfInput := unhex(hkdfInputHex) hkdfSalt := unhex(hkdfSaltHex) hkdfInfo := unhex(hkdfInfoHex) - hkdfExtractOutput := unhex(hkdfExtractOutputHex) - hkdfExtractZeroOutput := unhex(hkdfExtractZeroOutputHex) - hkdfExpandOutput := unhex(hkdfExpandOutputHex) + HkdfExtractOutput := unhex(hkdfExtractOutputHex) + HkdfExtractZeroOutput := unhex(hkdfExtractZeroOutputHex) + HkdfExpandOutput := unhex(hkdfExpandOutputHex) hkdfHash := unhex(hkdfHashHex) hkdfEncodedLabel := unhex(hkdfEncodedLabelHex) - hkdfExpandLabelOutput := unhex(hkdfExpandLabelOutputHex) + HkdfExpandLabelOutput := unhex(hkdfExpandLabelOutputHex) - // Test hkdfExtract is correct with salt - out := hkdfExtract(hash, hkdfSalt, hkdfInput) - assertByteEquals(t, out, hkdfExtractOutput) + // Test HkdfExtract is correct with salt + out := HkdfExtract(hash, hkdfSalt, hkdfInput) + assertByteEquals(t, out, HkdfExtractOutput) - // Test hkdfExtract is correct without salt - out = hkdfExtract(hash, nil, hkdfInput) - assertByteEquals(t, out, hkdfExtractZeroOutput) + // Test HkdfExtract is correct without salt + out = HkdfExtract(hash, nil, hkdfInput) + assertByteEquals(t, out, HkdfExtractZeroOutput) - // Test hkdfExpand is correct - out = hkdfExpand(hash, hkdfExtractOutput, hkdfInfo, hkdfExpandLen) - assertByteEquals(t, out, hkdfExpandOutput) + // Test HkdfExpand is correct + out = HkdfExpand(hash, HkdfExtractOutput, hkdfInfo, hkdfExpandLen) + assertByteEquals(t, out, HkdfExpandOutput) // Test hkdfEncodeLabel is correct out = hkdfEncodeLabel(hkdfLabel, hkdfHash, hkdfExpandLen) assertByteEquals(t, out, hkdfEncodedLabel) // This is pro-forma, just for the coverage - out = hkdfExpandLabel(hash, hkdfSalt, hkdfLabel, hkdfHash, hkdfExpandLen) - assertByteEquals(t, out, hkdfExpandLabelOutput) + out = HkdfExpandLabel(hash, hkdfSalt, hkdfLabel, hkdfHash, hkdfExpandLen) + assertByteEquals(t, out, HkdfExpandLabelOutput) } func random(n int) []byte { diff --git a/negotiation.go b/negotiation.go index 4543b6a..f4ead72 100644 --- a/negotiation.go +++ b/negotiation.go @@ -52,7 +52,7 @@ const ( ticketAgeTolerance uint32 = 5 * 1000 // five seconds in milliseconds ) -func PSKNegotiation(identities []PSKIdentity, binders []PSKBinderEntry, context []byte, psks PreSharedKeyCache) (bool, int, *PreSharedKey, cipherSuiteParams, error) { +func PSKNegotiation(identities []PSKIdentity, binders []PSKBinderEntry, context []byte, psks PreSharedKeyCache) (bool, int, *PreSharedKey, CipherSuiteParams, error) { logf(logTypeNegotiation, "Negotiating PSK offered=[%d] supported=[%d]", len(identities), psks.Size()) for i, id := range identities { identityHex := hex.EncodeToString(id.Identity) @@ -75,14 +75,14 @@ func PSKNegotiation(identities []PSKIdentity, binders []PSKBinderEntry, context logf(logTypeNegotiation, "WARNING potential replay [%x]", psk.Identity) logf(logTypeNegotiation, "Ticket age exceeds tolerance |%d - %d| = [%d] > [%d]", extTicketAge, knownTicketAge, ticketAgeDelta, ticketAgeTolerance) - return false, 0, nil, cipherSuiteParams{}, fmt.Errorf("WARNING Potential replay for identity %x", psk.Identity) + return false, 0, nil, CipherSuiteParams{}, fmt.Errorf("WARNING Potential replay for identity %x", psk.Identity) } } params, ok := cipherSuiteMap[psk.CipherSuite] if !ok { err := fmt.Errorf("tls.cryptoinit: Unsupported ciphersuite from PSK [%04x]", psk.CipherSuite) - return false, 0, nil, cipherSuiteParams{}, err + return false, 0, nil, CipherSuiteParams{}, err } // Compute binder @@ -91,20 +91,20 @@ func PSKNegotiation(identities []PSKIdentity, binders []PSKBinderEntry, context binderLabel = labelResumptionBinder } - h0 := params.hash.New().Sum(nil) - zero := bytes.Repeat([]byte{0}, params.hash.Size()) - earlySecret := hkdfExtract(params.hash, zero, psk.Key) + h0 := params.Hash.New().Sum(nil) + zero := bytes.Repeat([]byte{0}, params.Hash.Size()) + earlySecret := HkdfExtract(params.Hash, zero, psk.Key) binderKey := deriveSecret(params, earlySecret, binderLabel, h0) // context = ClientHello[truncated] // context = ClientHello1 + HelloRetryRequest + ClientHello2[truncated] - ctxHash := params.hash.New() + ctxHash := params.Hash.New() ctxHash.Write(context) binder := computeFinishedData(params, binderKey, ctxHash.Sum(nil)) if !bytes.Equal(binder, binders[i].Binder) { logf(logTypeNegotiation, "Binder check failed for identity %x; [%x] != [%x]", psk.Identity, binder, binders[i].Binder) - return false, 0, nil, cipherSuiteParams{}, fmt.Errorf("Binder check failed identity %x", psk.Identity) + return false, 0, nil, CipherSuiteParams{}, fmt.Errorf("Binder check failed identity %x", psk.Identity) } logf(logTypeNegotiation, "Using PSK with identity %x", psk.Identity) @@ -112,7 +112,7 @@ func PSKNegotiation(identities []PSKIdentity, binders []PSKBinderEntry, context } logf(logTypeNegotiation, "Failed to find a usable PSK") - return false, 0, nil, cipherSuiteParams{}, nil + return false, 0, nil, CipherSuiteParams{}, nil } func PSKModeNegotiation(canDoDH, canDoPSK bool, modes []PSKKeyExchangeMode) (bool, bool) { diff --git a/negotiation_test.go b/negotiation_test.go index 9fc0bb3..5649e0d 100644 --- a/negotiation_test.go +++ b/negotiation_test.go @@ -83,7 +83,7 @@ func TestPSKNegotiation(t *testing.T) { assertEquals(t, ok, true) assertEquals(t, selected, 1) assertNotNil(t, psk, "PSK not set") - assertEquals(t, params.suite, psk.CipherSuite) + assertEquals(t, params.Suite, psk.CipherSuite) assertNotError(t, err, "Valid PSK negotiation failed") // Test negotiation failure on binder value failure diff --git a/server-state-machine.go b/server-state-machine.go index a9edf77..86619b4 100644 --- a/server-state-machine.go +++ b/server-state-machine.go @@ -131,7 +131,7 @@ func (state ServerStateStart) Next(hm *HandshakeMessage) (HandshakeState, []Hand canDoPSK := false var selectedPSK int var psk *PreSharedKey - var params cipherSuiteParams + var params CipherSuiteParams if len(clientPSK.Identities) > 0 { contextBase := []byte{} if state.helloRetryRequest != nil { @@ -191,7 +191,7 @@ func (state ServerStateStart) Next(hm *HandshakeMessage) (HandshakeState, []Hand } params := cipherSuiteMap[connParams.CipherSuite] - h := params.hash.New() + h := params.Hash.New() h.Write(clientHello.Marshal()) firstClientHello := &HandshakeMessage{ msgType: HandshakeTypeMessageHash, @@ -250,12 +250,12 @@ func (state ServerStateStart) Next(hm *HandshakeMessage) (HandshakeState, []Hand connParams.UsingEarlyData = EarlyDataNegotiation(connParams.UsingPSK, gotEarlyData, state.Caps.AllowEarlyData) if connParams.UsingEarlyData { - h := params.hash.New() + h := params.Hash.New() h.Write(clientHello.Marshal()) chHash := h.Sum(nil) - zero := bytes.Repeat([]byte{0}, params.hash.Size()) - earlySecret := hkdfExtract(params.hash, zero, pskSecret) + zero := bytes.Repeat([]byte{0}, params.Hash.Size()) + earlySecret := HkdfExtract(params.Hash, zero, pskSecret) clientEarlyTrafficSecret = deriveSecret(params, earlySecret, labelEarlyTrafficSecret, chHash) } @@ -357,34 +357,34 @@ func (state ServerStateNegotiated) Next(hm *HandshakeMessage) (HandshakeState, [ } // 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(serverHello.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 { - earlySecret = hkdfExtract(params.hash, zero, state.pskSecret) + earlySecret = HkdfExtract(params.Hash, zero, state.pskSecret) } else { - earlySecret = hkdfExtract(params.hash, zero, zero) + earlySecret = HkdfExtract(params.Hash, zero, zero) } if state.dhSecret == nil { state.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, state.dhSecret) + handshakeSecret := HkdfExtract(params.Hash, preHandshakeSecret, state.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 (init!): [%d] %x", len(earlySecret), earlySecret) logf(logTypeCrypto, "handshake secret: [%d] %x", len(handshakeSecret), handshakeSecret) @@ -472,7 +472,7 @@ func (state ServerStateNegotiated) Next(hm *HandshakeMessage) (HandshakeState, [ handshakeHash.Write(certm.Marshal()) certificateVerify := &CertificateVerifyBody{Algorithm: state.certScheme} - logf(logTypeHandshake, "Creating CertVerify: %04x %v", state.certScheme, params.hash) + logf(logTypeHandshake, "Creating CertVerify: %04x %v", state.certScheme, params.Hash) hcv := handshakeHash.Sum(nil) logf(logTypeHandshake, "Handshake Hash to be verified: [%d] %x", len(hcv), hcv) @@ -572,7 +572,7 @@ func (state ServerStateNegotiated) Next(hm *HandshakeMessage) (HandshakeState, [ type ServerStateWaitEOED struct { AuthCertificate func(chain []CertificateEntry) error Params ConnectionParameters - cryptoParams cipherSuiteParams + cryptoParams CipherSuiteParams masterSecret []byte clientHandshakeTrafficSecret []byte handshakeHash hash.Hash @@ -619,7 +619,7 @@ func (state ServerStateWaitEOED) Next(hm *HandshakeMessage) (HandshakeState, []H type ServerStateWaitFlight2 struct { AuthCertificate func(chain []CertificateEntry) error Params ConnectionParameters - cryptoParams cipherSuiteParams + cryptoParams CipherSuiteParams masterSecret []byte clientHandshakeTrafficSecret []byte handshakeHash hash.Hash @@ -667,7 +667,7 @@ func (state ServerStateWaitFlight2) Next(hm *HandshakeMessage) (HandshakeState, type ServerStateWaitCert struct { AuthCertificate func(chain []CertificateEntry) error Params ConnectionParameters - cryptoParams cipherSuiteParams + cryptoParams CipherSuiteParams masterSecret []byte clientHandshakeTrafficSecret []byte handshakeHash hash.Hash @@ -727,7 +727,7 @@ func (state ServerStateWaitCert) Next(hm *HandshakeMessage) (HandshakeState, []H type ServerStateWaitCV struct { AuthCertificate func(chain []CertificateEntry) error Params ConnectionParameters - cryptoParams cipherSuiteParams + cryptoParams CipherSuiteParams masterSecret []byte clientHandshakeTrafficSecret []byte @@ -792,7 +792,7 @@ func (state ServerStateWaitCV) Next(hm *HandshakeMessage) (HandshakeState, []Han type ServerStateWaitFinished struct { Params ConnectionParameters - cryptoParams cipherSuiteParams + cryptoParams CipherSuiteParams masterSecret []byte clientHandshakeTrafficSecret []byte @@ -809,7 +809,7 @@ func (state ServerStateWaitFinished) Next(hm *HandshakeMessage) (HandshakeState, return nil, nil, AlertUnexpectedMessage } - fin := &FinishedBody{VerifyDataLen: state.cryptoParams.hash.Size()} + fin := &FinishedBody{VerifyDataLen: state.cryptoParams.Hash.Size()} _, err := fin.Unmarshal(hm.body) if err != nil { logf(logTypeHandshake, "[ServerStateWaitFinished] Error decoding message %v", err) diff --git a/state-machine.go b/state-machine.go index 75fd64f..f0d7a6f 100644 --- a/state-machine.go +++ b/state-machine.go @@ -83,7 +83,7 @@ type ConnectionParameters struct { type StateConnected struct { Params ConnectionParameters isClient bool - cryptoParams cipherSuiteParams + cryptoParams CipherSuiteParams resumptionSecret []byte clientTrafficSecret []byte serverTrafficSecret []byte @@ -93,12 +93,12 @@ type StateConnected struct { func (state *StateConnected) KeyUpdate(request KeyUpdateRequest) ([]HandshakeAction, Alert) { var trafficKeys keySet if state.isClient { - state.clientTrafficSecret = hkdfExpandLabel(state.cryptoParams.hash, state.clientTrafficSecret, - labelClientApplicationTrafficSecret, []byte{}, state.cryptoParams.hash.Size()) + state.clientTrafficSecret = HkdfExpandLabel(state.cryptoParams.Hash, state.clientTrafficSecret, + labelClientApplicationTrafficSecret, []byte{}, state.cryptoParams.Hash.Size()) trafficKeys = makeTrafficKeys(state.cryptoParams, state.clientTrafficSecret) } else { - state.serverTrafficSecret = hkdfExpandLabel(state.cryptoParams.hash, state.serverTrafficSecret, - labelServerApplicationTrafficSecret, []byte{}, state.cryptoParams.hash.Size()) + state.serverTrafficSecret = HkdfExpandLabel(state.cryptoParams.Hash, state.serverTrafficSecret, + labelServerApplicationTrafficSecret, []byte{}, state.cryptoParams.Hash.Size()) trafficKeys = makeTrafficKeys(state.cryptoParams, state.serverTrafficSecret) } @@ -132,7 +132,7 @@ func (state *StateConnected) NewSessionTicket(length int, lifetime, earlyDataLif labelResumption, tkt.TicketNonce, state.cryptoParams.hash.Size()) newPSK := PreSharedKey{ - CipherSuite: state.cryptoParams.suite, + CipherSuite: state.cryptoParams.Suite, IsResumption: true, Identity: tkt.Ticket, Key: resumptionKey, @@ -171,12 +171,12 @@ func (state StateConnected) Next(hm *HandshakeMessage) (HandshakeState, []Handsh case *KeyUpdateBody: var trafficKeys keySet if !state.isClient { - state.clientTrafficSecret = hkdfExpandLabel(state.cryptoParams.hash, state.clientTrafficSecret, - labelClientApplicationTrafficSecret, []byte{}, state.cryptoParams.hash.Size()) + state.clientTrafficSecret = HkdfExpandLabel(state.cryptoParams.Hash, state.clientTrafficSecret, + labelClientApplicationTrafficSecret, []byte{}, state.cryptoParams.Hash.Size()) trafficKeys = makeTrafficKeys(state.cryptoParams, state.clientTrafficSecret) } else { - state.serverTrafficSecret = hkdfExpandLabel(state.cryptoParams.hash, state.serverTrafficSecret, - labelServerApplicationTrafficSecret, []byte{}, state.cryptoParams.hash.Size()) + state.serverTrafficSecret = HkdfExpandLabel(state.cryptoParams.Hash, state.serverTrafficSecret, + labelServerApplicationTrafficSecret, []byte{}, state.cryptoParams.Hash.Size()) trafficKeys = makeTrafficKeys(state.cryptoParams, state.serverTrafficSecret) } @@ -204,7 +204,7 @@ func (state StateConnected) Next(hm *HandshakeMessage) (HandshakeState, []Handsh labelResumption, body.TicketNonce, state.cryptoParams.hash.Size()) psk := PreSharedKey{ - CipherSuite: state.cryptoParams.suite, + CipherSuite: state.cryptoParams.Suite, IsResumption: true, Identity: body.Ticket, Key: resumptionKey, From 7ad00a64fae40cb96d8a506a3c148170708eb47a Mon Sep 17 00:00:00 2001 From: EKR Date: Wed, 28 Jun 2017 07:06:05 -0700 Subject: [PATCH 4/8] Fix multiple handshake reads from the same record so they don't return wouldblock --- handshake-layer.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/handshake-layer.go b/handshake-layer.go index 757a6b4..5b48208 100644 --- a/handshake-layer.go +++ b/handshake-layer.go @@ -173,10 +173,12 @@ func (h *HandshakeLayer) sendAlert(err Alert) error { func (h *HandshakeLayer) ReadMessage() (*HandshakeMessage, error) { var hdr, body []byte - err := WouldBlock + err := error(nil) for { + logf(logTypeHandshake, "ReadMessage() buffered=%v", len(h.frame.remainder)) if h.frame.needed() > 0 { + logf(logTypeHandshake, "Trying to read a new record") err = h.readRecord() } if err != nil && (h.nonblocking || err != WouldBlock) { From 46ab12c4ea16db8a0a997aec93caf702e20f419f Mon Sep 17 00:00:00 2001 From: EKR Date: Thu, 13 Jul 2017 21:00:06 -0700 Subject: [PATCH 5/8] Expose ALPN info --- conn.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/conn.go b/conn.go index 8f29c70..013ea4c 100644 --- a/conn.go +++ b/conn.go @@ -176,6 +176,7 @@ type ConnectionState struct { 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(ekr@rtfm.com): implement + NextProto string // Selected ALPN proto } // Conn implements the net.Conn interface, as with "crypto/tls" @@ -753,6 +754,7 @@ func (c *Conn) GetConnectionState() ConnectionState { if c.handshakeComplete { state.CipherSuite = cipherSuiteMap[c.state.Params.CipherSuite] + state.NextProto = c.state.Params.NextProto } return state From ab24982caab57699351b75ab7da883bb74f58e33 Mon Sep 17 00:00:00 2001 From: EKR Date: Fri, 14 Jul 2017 09:39:52 -0700 Subject: [PATCH 6/8] Fix up merge conflicts between public and private APIs --- state-machine.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/state-machine.go b/state-machine.go index f0d7a6f..6446ba0 100644 --- a/state-machine.go +++ b/state-machine.go @@ -128,8 +128,8 @@ func (state *StateConnected) NewSessionTicket(length int, lifetime, earlyDataLif return nil, AlertInternalError } - resumptionKey := hkdfExpandLabel(state.cryptoParams.hash, state.resumptionSecret, - labelResumption, tkt.TicketNonce, state.cryptoParams.hash.Size()) + resumptionKey := HkdfExpandLabel(state.cryptoParams.Hash, state.resumptionSecret, + labelResumption, tkt.TicketNonce, state.cryptoParams.Hash.Size()) newPSK := PreSharedKey{ CipherSuite: state.cryptoParams.Suite, @@ -200,8 +200,8 @@ func (state StateConnected) Next(hm *HandshakeMessage) (HandshakeState, []Handsh return nil, nil, AlertUnexpectedMessage } - resumptionKey := hkdfExpandLabel(state.cryptoParams.hash, state.resumptionSecret, - labelResumption, body.TicketNonce, state.cryptoParams.hash.Size()) + resumptionKey := HkdfExpandLabel(state.cryptoParams.Hash, state.resumptionSecret, + labelResumption, body.TicketNonce, state.cryptoParams.Hash.Size()) psk := PreSharedKey{ CipherSuite: state.cryptoParams.Suite, From eebc42c16d426e29c264d3ee3bdd15484cc1b908 Mon Sep 17 00:00:00 2001 From: EKR Date: Tue, 25 Jul 2017 15:31:24 -0700 Subject: [PATCH 7/8] Small fix per @marten-seemann --- handshake-layer.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/handshake-layer.go b/handshake-layer.go index 5b48208..2b04ac5 100644 --- a/handshake-layer.go +++ b/handshake-layer.go @@ -173,7 +173,7 @@ func (h *HandshakeLayer) sendAlert(err Alert) error { func (h *HandshakeLayer) ReadMessage() (*HandshakeMessage, error) { var hdr, body []byte - err := error(nil) + var err error for { logf(logTypeHandshake, "ReadMessage() buffered=%v", len(h.frame.remainder)) From f4de2a122dc2cbad4dd6dd8df896f1139624d3ad Mon Sep 17 00:00:00 2001 From: EKR Date: Tue, 25 Jul 2017 16:13:06 -0700 Subject: [PATCH 8/8] Review comments --- conn.go | 2 +- frame-reader.go | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/conn.go b/conn.go index 013ea4c..38d4873 100644 --- a/conn.go +++ b/conn.go @@ -747,7 +747,7 @@ func (c *Conn) ComputeExporter(label string, context []byte, keyLength int) ([]b return HkdfExpandLabel(c.state.cryptoParams.Hash, tmpSecret, "exporter", hc, keyLength), nil } -func (c *Conn) GetConnectionState() ConnectionState { +func (c *Conn) State() ConnectionState { state := ConnectionState{ HandshakeState: c.GetHsState(), } diff --git a/frame-reader.go b/frame-reader.go index 7889f29..3394387 100644 --- a/frame-reader.go +++ b/frame-reader.go @@ -2,8 +2,6 @@ // This is used for both TLS Records and TLS Handshake Messages package mint -import () - type framing interface { headerLen() int defaultReadLen() int