From e28c12c968be3d7d5f625447f8b9c3bc3979464b Mon Sep 17 00:00:00 2001 From: Marten Seemann Date: Thu, 11 Jan 2018 13:18:16 +0700 Subject: [PATCH 1/3] expose the server certificate chain in the connection state --- client-state-machine.go | 24 ++++++++++++++++-------- conn.go | 3 ++- conn_test.go | 21 +++++++++++++++++++++ state-machine.go | 3 +++ 4 files changed, 42 insertions(+), 9 deletions(-) diff --git a/client-state-machine.go b/client-state-machine.go index ddf9ee9..738e9e3 100644 --- a/client-state-machine.go +++ b/client-state-machine.go @@ -3,6 +3,7 @@ package mint import ( "bytes" "crypto" + "crypto/x509" "hash" "time" ) @@ -821,6 +822,10 @@ func (state ClientStateWaitCV) Next(hr handshakeMessageReader) (HandshakeState, } else { logf(logTypeHandshake, "[ClientStateWaitCV] WARNING: No verification of server certificate") } + serverCertChain := make([]*x509.Certificate, len(state.serverCertificate.CertificateList)) + for i, certEntry := range state.serverCertificate.CertificateList { + serverCertChain[i] = certEntry.CertData + } state.handshakeHash.Write(hm.Marshal()) @@ -831,6 +836,7 @@ func (state ClientStateWaitCV) Next(hr handshakeMessageReader) (HandshakeState, cryptoParams: state.cryptoParams, handshakeHash: state.handshakeHash, certificates: state.certificates, + serverCertificateChain: serverCertChain, serverCertificateRequest: state.serverCertificateRequest, masterSecret: state.masterSecret, clientHandshakeTrafficSecret: state.clientHandshakeTrafficSecret, @@ -846,6 +852,7 @@ type ClientStateWaitFinished struct { handshakeHash hash.Hash certificates []*Certificate + serverCertificateChain []*x509.Certificate serverCertificateRequest *CertificateRequestBody masterSecret []byte @@ -1025,14 +1032,15 @@ func (state ClientStateWaitFinished) Next(hr handshakeMessageReader) (HandshakeS logf(logTypeHandshake, "[ClientStateWaitFinished] -> [StateConnected]") nextState := StateConnected{ - Params: state.Params, - hsCtx: state.hsCtx, - isClient: true, - cryptoParams: state.cryptoParams, - resumptionSecret: resumptionSecret, - clientTrafficSecret: clientTrafficSecret, - serverTrafficSecret: serverTrafficSecret, - exporterSecret: exporterSecret, + Params: state.Params, + hsCtx: state.hsCtx, + isClient: true, + cryptoParams: state.cryptoParams, + resumptionSecret: resumptionSecret, + clientTrafficSecret: clientTrafficSecret, + serverTrafficSecret: serverTrafficSecret, + exporterSecret: exporterSecret, + PeerCertificateChain: state.serverCertificateChain, } return nextState, toSend, AlertNoAlert } diff --git a/conn.go b/conn.go index acea497..114abd7 100644 --- a/conn.go +++ b/conn.go @@ -232,7 +232,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 TODO(ekr@rtfm.com): implement + PeerCertificates []*x509.Certificate // certificate chain presented by remote peer NextProto string // Selected ALPN proto } @@ -875,6 +875,7 @@ func (c *Conn) State() ConnectionState { if c.handshakeComplete { state.CipherSuite = cipherSuiteMap[c.state.Params.CipherSuite] state.NextProto = c.state.Params.NextProto + state.PeerCertificates = c.state.PeerCertificateChain } return state diff --git a/conn_test.go b/conn_test.go index 04d9277..27c33d6 100644 --- a/conn_test.go +++ b/conn_test.go @@ -825,6 +825,27 @@ func TestExternalExtensions(t *testing.T) { }) } +func TestConnectionState(t *testing.T) { + cConn, sConn := pipe() + client := Client(cConn, basicConfig) + server := Server(sConn, basicConfig) + + done := make(chan bool) + go func(t *testing.T) { + serverAlert := server.Handshake() + assertEquals(t, serverAlert, AlertNoAlert) + done <- true + }(t) + + clientAlert := client.Handshake() + assertEquals(t, clientAlert, AlertNoAlert) + <-done + + connectionState := client.State() + assertEquals(t, connectionState.CipherSuite.Suite, basicConfig.CipherSuites[0]) + assertDeepEquals(t, connectionState.PeerCertificates, []*x509.Certificate{serverCert}) +} + func TestDTLS(t *testing.T) { cConn, sConn := pipe() diff --git a/state-machine.go b/state-machine.go index f3d3593..c23b160 100644 --- a/state-machine.go +++ b/state-machine.go @@ -1,6 +1,7 @@ package mint import ( + "crypto/x509" "time" ) @@ -114,6 +115,8 @@ type StateConnected struct { clientTrafficSecret []byte serverTrafficSecret []byte exporterSecret []byte + + PeerCertificateChain []*x509.Certificate } var _ HandshakeState = &StateConnected{} From 100622617996de882b023723414c4b0e494c5027 Mon Sep 17 00:00:00 2001 From: Marten Seemann Date: Thu, 11 Jan 2018 15:37:19 +0700 Subject: [PATCH 2/3] expose the client certificate chain in the connection state --- conn_test.go | 147 +++++++++++++++++++--------------------- server-state-machine.go | 25 ++++--- 2 files changed, 85 insertions(+), 87 deletions(-) diff --git a/conn_test.go b/conn_test.go index 27c33d6..0155741 100644 --- a/conn_test.go +++ b/conn_test.go @@ -2,9 +2,13 @@ package mint import ( "bytes" + "crypto/rand" + "crypto/rsa" "crypto/x509" + "crypto/x509/pkix" "fmt" "io" + "math/big" "net" "sync" "testing" @@ -109,75 +113,48 @@ func newBufferedConn(p net.Conn) *bufferedConn { } var ( - serverName = "example.com" - - // Certificate generated by Go - // * CN: "example.com" - // * SANs: "example.com", "www.example.com" - // * Random 2048-bit public key - // * Self-signed - serverCertHex = "308202f7308201dfa00302010202012a300d06092a864886f70d01010b05003016311430" + - "120603550403130b6578616d706c652e636f6d301e170d3135303130313030303030305a" + - "170d3235303130313030303030305a3016311430120603550403130b6578616d706c652e" + - "636f6d30820122300d06092a864886f70d01010105000382010f003082010a0282010100" + - "a558ff3c12b8c4906b7f638878c71963ac95548c5d36975bc575de8775a141408c449c3e" + - "7fe7eddf93329dd894ecb2705b7f79caa06f1477b7bd2d3ff32f43076dd32a7f9f97ed4d" + - "4593db3f28adbea7794c14d8d206832652e93959e2b8d2b4781fadcf55c852641482f7fc" + - "6b9e7e751442a0818c21c9cacc28e7594606ff692392510df57ce26d9c0d052f84e236b9" + - "9e3f81daa98c554607432e3bb26a5fe3fa2b5fc5e5c1fcb1d76050328b92edc80238773d" + - "16547ccc24c0784933d86b3f8d0ee33d90a1b47ecbfbaad12e77155f1b4e84b3e5c4d565" + - "1717832fcbf82886eb6f925435b4ca9f87ec207b4338f03a846fbf0f68ea0e674bf50a21" + - "d9165b690203010001a350304e300e0603551d0f0101ff04040302028430130603551d25" + - "040c300a06082b0601050507030130270603551d110420301e820b6578616d706c652e63" + - "6f6d820f7777772e6578616d706c652e636f6d300d06092a864886f70d01010b05000382" + - "01010024ed08531171df6256ed5668b962ca3740e61c20d014b3aae8ac436b200ee89b50" + - "dc5e5a74859fdf1d0f1136ad16d5fe018dac83d0e4dcfd1b5951e1502e23b7b740621b10" + - "b3376f8b3bc5494c25917b4103d88670390e33c2b089e66326316e4bbd75fd6e5dced78f" + - "79caf97d83b981950ed10449f61d826af4a6eb70e291fccdaa76145f7ba085d27698197f" + - "60e944646640ea18d5439955d91a80d4dfb1e4c12f539da9423a33f479ee19a0fa9c5339" + - "1e0d164633bea4346dc0c8081172d67ee7bca4bd5463cc147d8c062ebb31be6e9c39518c" + - "37f5607a2d6f36114800f6c6f509893fa352a468b30ad874ae56db769f1786567e9c96c1" + - "6b4a4b2a25dda3" - serverCertDER = unhex(serverCertHex) - serverCert, _ = x509.ParseCertificate(serverCertDER) - - // The corresponding private key - serverKeyHex = "308204a40201000282010100a558ff3c12b8c4906b7f638878c71963ac95548c5d36975b" + - "c575de8775a141408c449c3e7fe7eddf93329dd894ecb2705b7f79caa06f1477b7bd2d3f" + - "f32f43076dd32a7f9f97ed4d4593db3f28adbea7794c14d8d206832652e93959e2b8d2b4" + - "781fadcf55c852641482f7fc6b9e7e751442a0818c21c9cacc28e7594606ff692392510d" + - "f57ce26d9c0d052f84e236b99e3f81daa98c554607432e3bb26a5fe3fa2b5fc5e5c1fcb1" + - "d76050328b92edc80238773d16547ccc24c0784933d86b3f8d0ee33d90a1b47ecbfbaad1" + - "2e77155f1b4e84b3e5c4d5651717832fcbf82886eb6f925435b4ca9f87ec207b4338f03a" + - "846fbf0f68ea0e674bf50a21d9165b6902030100010282010074f08262ec22bcf21ef4d3" + - "621b79445d981b6cd670be4141e85f3a68b72abac979eab44e078bf25222fab3640fbf6f" + - "5bc37a5e9a8de8c1a301d1cb84e4ead20f18ff35995937cbded08c878d1da9f3a2e2488a" + - "9de5bc3159135e5aef5547bdcd60ff969f825dd0d77322455cc2882f8b822eb4f1aa37e3" + - "4d88228dac37b88f3d9b671ef6b05e2f47b562265e0d09fefb01c190c7fb4b3682231cd8" + - "564c59b6cc788ff742fb040562110b1f849f1535164503b0a402399e2c6cf1c0847dd50a" + - "a917b62fc3215e4eb43d7d07fa9731a51e01f0f7b694dd002b48c0bad04b9ff34e576393" + - "c0a213a12dda4bf43a7dd4ee0563c5e0de2025eb76e049cd771c96330102818100c590bd" + - "8f226cec50c818afb3ebe7ceeacabb107ac73ac159b1eca1a194ea550a0609c432a183e2" + - "fee62dafdc0201426f90cb46f9b2fc7a9bcc2365b58177529cf78c209eb6a3afd1896466" + - "63e8462729e8bf902dc1c42c7d46c1c0c99c632f0560418604b4260a1ed8d165375c674c" + - "806c2a8e202d0b7c5a8b8717309106fb3102818100d640cae7b6adea649a8c11863a3ba8" + - "098025a972d130aecaa4db08154fd0feb8af79bf7009c1ea2a790752464e923b53b41ff4" + - "3ff84e6ddb94bfc5b157e6a21e1fefe11cc082e7e8b31d07eab5e13d7a84cdeeba24d283" + - "699a8fa5138e753e88856a033ab2153c1a8200caac28377a1d09d6318ac2e946cef879a0" + - "5acbd8e5b902818100bfe142ea189257b66190f05d3bba8951aa92a27fccadf90a076f7e" + - "cff354e040fafa534ea565f57a81ce4fa5cb60b3c8ad8570aaa5b6e7d217232dee6a0e9c" + - "f30cce510434f8a79347f0762d84735628330092a48e33dccdd381ec9f233f8574a03723" + - "55c02dcdd885d6618ab23935a8e8e52fe27a3d548a90472533ab376f910281805253fd64" + - "02875bbd22c1d5ee0d2c654a994a5f8d7622cdd7a27763e8c48ddb835e325b44930b478e" + - "e088d6ad9b7d877c87878bd494f696323d3b5f9ce0d907cca99b049686c706941d577776" + - "524365db5172cc5c0cd0339cfdbe5ac164095b691c52fb40afb3872fec6a9f767dd1ab83" + - "c306e26c9eaf02fd7eef4595fe24af4902818100b5a2294d7567283f3f4bf54be7b98785" + - "fc564f24ff2d67215ecdc7955cbf05260f48c9608a59a8ebfbedc62b4d110c1704ade704" + - "cb27a591f69752d1d6ebe21291aec29b301efe47eced0187125f741ce52b3826beac3778" + - "f3560448e91644fd52460f8c3afa1596c01e6cd2c37120d8122c09edf326988b48d98c27" + - "f788eb83" - serverKeyDER = unhex(serverKeyHex) - serverKey, _ = x509.ParsePKCS1PrivateKey(serverKeyDER) + serverKey, clientKey *rsa.PrivateKey + serverCert, clientCert *x509.Certificate + certificates, clientCertificates []*Certificate + + psk PreSharedKey + psks *PSKMapCache + + basicConfig, dtlsConfig, nbConfig, hrrConfig, alpnConfig, clientAuthConfigServer, clientAuthConfigClient, pskConfig, pskECDHEConfig, pskDHEConfig, resumptionConfig, ffdhConfig, x25519Config *Config +) + +const serverName = "example.com" + +func genCerts() (*rsa.PrivateKey, *x509.Certificate, error) { + key, err := rsa.GenerateKey(rand.Reader, 2048) + if err != nil { + return nil, nil, err + } + template := &x509.Certificate{ + SerialNumber: big.NewInt(1), + NotBefore: time.Now(), + NotAfter: time.Now().Add(time.Hour), + Subject: pkix.Name{CommonName: serverName}, + DNSNames: []string{serverName}, + } + certDER, err := x509.CreateCertificate(rand.Reader, template, template, key.Public(), key) + if err != nil { + return nil, nil, err + } + cert, err := x509.ParseCertificate(certDER) + return key, cert, err +} + +func init() { + var err error + serverKey, serverCert, err = genCerts() + if err != nil { + panic(err) + } + clientKey, clientCert, err = genCerts() + if err != nil { + panic(err) + } psk = PreSharedKey{ CipherSuite: TLS_AES_128_GCM_SHA256, @@ -191,6 +168,12 @@ var ( PrivateKey: serverKey, }, } + clientCertificates = []*Certificate{ + { + Chain: []*x509.Certificate{clientCert}, + PrivateKey: clientKey, + }, + } psks = &PSKMapCache{ serverName: psk, "00010203": psk, @@ -225,11 +208,14 @@ var ( NextProtos: []string{"http/1.1", "h2"}, } - clientAuthConfig = &Config{ - ServerName: serverName, + clientAuthConfigServer = &Config{ RequireClientAuth: true, Certificates: certificates, } + clientAuthConfigClient = &Config{ + ServerName: serverName, + Certificates: clientCertificates, + } pskConfig = &Config{ ServerName: serverName, @@ -272,7 +258,7 @@ var ( CipherSuites: []CipherSuite{TLS_AES_128_GCM_SHA256}, Groups: []NamedGroup{X25519}, } -) +} func assertKeySetEquals(t *testing.T, k1, k2 keySet) { t.Helper() @@ -342,8 +328,8 @@ func TestBasicFlows(t *testing.T) { func TestClientAuth(t *testing.T) { cConn, sConn := pipe() - client := Client(cConn, clientAuthConfig) - server := Server(sConn, clientAuthConfig) + client := Client(cConn, clientAuthConfigClient) + server := Server(sConn, clientAuthConfigServer) var clientAlert, serverAlert Alert @@ -827,8 +813,8 @@ func TestExternalExtensions(t *testing.T) { func TestConnectionState(t *testing.T) { cConn, sConn := pipe() - client := Client(cConn, basicConfig) - server := Server(sConn, basicConfig) + client := Client(cConn, clientAuthConfigClient) + server := Server(sConn, clientAuthConfigServer) done := make(chan bool) go func(t *testing.T) { @@ -841,9 +827,12 @@ func TestConnectionState(t *testing.T) { assertEquals(t, clientAlert, AlertNoAlert) <-done - connectionState := client.State() - assertEquals(t, connectionState.CipherSuite.Suite, basicConfig.CipherSuites[0]) - assertDeepEquals(t, connectionState.PeerCertificates, []*x509.Certificate{serverCert}) + clientConnectionState := client.State() + assertEquals(t, clientConnectionState.CipherSuite.Suite, clientAuthConfigClient.CipherSuites[0]) + assertDeepEquals(t, clientConnectionState.PeerCertificates, []*x509.Certificate{serverCert}) + serverConnectionState := server.State() + assertEquals(t, serverConnectionState.CipherSuite.Suite, clientAuthConfigServer.CipherSuites[0]) + assertDeepEquals(t, serverConnectionState.PeerCertificates, []*x509.Certificate{clientCert}) } func TestDTLS(t *testing.T) { diff --git a/server-state-machine.go b/server-state-machine.go index 6c2d7b3..3d8ec91 100644 --- a/server-state-machine.go +++ b/server-state-machine.go @@ -2,6 +2,7 @@ package mint import ( "bytes" + "crypto/x509" "fmt" "hash" "reflect" @@ -994,6 +995,10 @@ func (state ServerStateWaitCV) Next(hr handshakeMessageReader) (HandshakeState, } else { logf(logTypeHandshake, "[ServerStateWaitCV] WARNING: No verification of client certificate") } + clientCertChain := make([]*x509.Certificate, len(state.clientCertificate.CertificateList)) + for i, certEntry := range state.clientCertificate.CertificateList { + clientCertChain[i] = certEntry.CertData + } // If it passes, record the certificateVerify in the transcript hash state.handshakeHash.Write(hm.Marshal()) @@ -1005,6 +1010,7 @@ func (state ServerStateWaitCV) Next(hr handshakeMessageReader) (HandshakeState, cryptoParams: state.cryptoParams, masterSecret: state.masterSecret, clientHandshakeTrafficSecret: state.clientHandshakeTrafficSecret, + clientCertificateChain: clientCertChain, handshakeHash: state.handshakeHash, clientTrafficSecret: state.clientTrafficSecret, serverTrafficSecret: state.serverTrafficSecret, @@ -1021,6 +1027,8 @@ type ServerStateWaitFinished struct { masterSecret []byte clientHandshakeTrafficSecret []byte + clientCertificateChain []*x509.Certificate + handshakeHash hash.Hash clientTrafficSecret []byte serverTrafficSecret []byte @@ -1074,14 +1082,15 @@ func (state ServerStateWaitFinished) Next(hr handshakeMessageReader) (HandshakeS logf(logTypeHandshake, "[ServerStateWaitFinished] -> [StateConnected]") nextState := StateConnected{ - Params: state.Params, - hsCtx: state.hsCtx, - isClient: false, - cryptoParams: state.cryptoParams, - resumptionSecret: resumptionSecret, - clientTrafficSecret: state.clientTrafficSecret, - serverTrafficSecret: state.serverTrafficSecret, - exporterSecret: state.exporterSecret, + Params: state.Params, + hsCtx: state.hsCtx, + isClient: false, + cryptoParams: state.cryptoParams, + resumptionSecret: resumptionSecret, + clientTrafficSecret: state.clientTrafficSecret, + serverTrafficSecret: state.serverTrafficSecret, + exporterSecret: state.exporterSecret, + PeerCertificateChain: state.clientCertificateChain, } toSend := []HandshakeAction{ RekeyIn{epoch: EpochApplicationData, KeySet: clientTrafficKeys}, From b45faa8d00fcd925c32ea4b10f739eacff740d45 Mon Sep 17 00:00:00 2001 From: Marten Seemann Date: Mon, 29 Jan 2018 08:39:12 +0700 Subject: [PATCH 3/3] use newSelfSigned in the conn tests --- conn_test.go | 35 ++++++++++++----------------------- 1 file changed, 12 insertions(+), 23 deletions(-) diff --git a/conn_test.go b/conn_test.go index 0155741..e466533 100644 --- a/conn_test.go +++ b/conn_test.go @@ -5,10 +5,8 @@ import ( "crypto/rand" "crypto/rsa" "crypto/x509" - "crypto/x509/pkix" "fmt" "io" - "math/big" "net" "sync" "testing" @@ -123,35 +121,26 @@ var ( basicConfig, dtlsConfig, nbConfig, hrrConfig, alpnConfig, clientAuthConfigServer, clientAuthConfigClient, pskConfig, pskECDHEConfig, pskDHEConfig, resumptionConfig, ffdhConfig, x25519Config *Config ) -const serverName = "example.com" +const ( + serverName = "example.com" + clientName = "example.org" +) -func genCerts() (*rsa.PrivateKey, *x509.Certificate, error) { - key, err := rsa.GenerateKey(rand.Reader, 2048) +func init() { + var err error + serverKey, err := rsa.GenerateKey(rand.Reader, 2048) if err != nil { - return nil, nil, err - } - template := &x509.Certificate{ - SerialNumber: big.NewInt(1), - NotBefore: time.Now(), - NotAfter: time.Now().Add(time.Hour), - Subject: pkix.Name{CommonName: serverName}, - DNSNames: []string{serverName}, + panic(err) } - certDER, err := x509.CreateCertificate(rand.Reader, template, template, key.Public(), key) + serverCert, err = newSelfSigned(serverName, RSA_PKCS1_SHA256, serverKey) if err != nil { - return nil, nil, err + panic(err) } - cert, err := x509.ParseCertificate(certDER) - return key, cert, err -} - -func init() { - var err error - serverKey, serverCert, err = genCerts() + clientKey, err := rsa.GenerateKey(rand.Reader, 2048) if err != nil { panic(err) } - clientKey, clientCert, err = genCerts() + clientCert, err = newSelfSigned(clientName, RSA_PKCS1_SHA256, clientKey) if err != nil { panic(err) }