diff --git a/src/crypto/tls/cipher_suites.go b/src/crypto/tls/cipher_suites.go index 9a1fa3104b7acc..5c2e3732df8a99 100644 --- a/src/crypto/tls/cipher_suites.go +++ b/src/crypto/tls/cipher_suites.go @@ -14,6 +14,7 @@ import ( "crypto/rc4" "crypto/sha1" "crypto/sha256" + "crypto/sha512" "fmt" "hash" "internal/cpu" @@ -69,8 +70,12 @@ func CipherSuites() []*CipherSuite { {TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", supportedOnlyTLS12, false}, {TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", supportedOnlyTLS12, false}, {TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", supportedOnlyTLS12, false}, + {TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA, "TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA", supportedOnlyTLS12, false}, + {TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA, "TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA", supportedOnlyTLS12, false}, + {TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384, "TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384", supportedOnlyTLS12, false}, {TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256", supportedOnlyTLS12, false}, {TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256", supportedOnlyTLS12, false}, + {TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256, "TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256", supportedOnlyTLS12, false}, } } @@ -91,6 +96,7 @@ func InsecureCipherSuites() []*CipherSuite { {TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, "TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA", supportedUpToTLS12, true}, {TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256", supportedOnlyTLS12, true}, {TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256", supportedOnlyTLS12, true}, + {TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256, "TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256", supportedOnlyTLS12, true}, } } @@ -128,6 +134,9 @@ const ( // suiteSHA384 indicates that the cipher suite uses SHA384 as the // handshake hash. suiteSHA384 + // suiteNoCerts indicates that the cipher suite doesn't use certificate exchange + // (anonymous ciphersuites or pre-shared-secret) + suiteNoCerts ) // A cipherSuite is a TLS 1.0–1.2 cipher suite, and defines the key exchange @@ -169,6 +178,12 @@ var cipherSuites = []*cipherSuite{ // TODO: replace with a map, since the order {TLS_RSA_WITH_RC4_128_SHA, 16, 20, 0, rsaKA, 0, cipherRC4, macSHA1, nil}, {TLS_ECDHE_RSA_WITH_RC4_128_SHA, 16, 20, 0, ecdheRSAKA, suiteECDHE, cipherRC4, macSHA1, nil}, {TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, 16, 20, 0, ecdheECDSAKA, suiteECDHE | suiteECSign, cipherRC4, macSHA1, nil}, + + {TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA, 16, 20, 16, ecdhePSKKA, suiteECDHE | suiteTLS12 | suiteNoCerts, cipherAES, macSHA1, nil}, + {TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256, 16, 32, 16, ecdhePSKKA, suiteECDHE | suiteTLS12 | suiteNoCerts, cipherAES, macSHA256, nil}, + {TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA, 32, 20, 16, ecdhePSKKA, suiteECDHE | suiteTLS12 | suiteNoCerts, cipherAES, macSHA1, nil}, + {TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384, 32, 48, 16, ecdhePSKKA, suiteECDHE | suiteTLS12 | suiteSHA384 | suiteNoCerts, cipherAES, macSHA384, nil}, + {TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256, 32, 0, 12, ecdhePSKKA, suiteECDHE | suiteTLS12 | suiteNoCerts, nil, nil, aeadChaCha20Poly1305}, } // selectCipherSuite returns the first TLS 1.0–1.2 cipher suite from ids which @@ -272,11 +287,12 @@ var cipherSuitesPreferenceOrder = []uint16{ // AEADs w/ ECDHE TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, - TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305, TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305, + TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305, TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305, TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256, // CBC w/ ECDHE - TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, - TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, + TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA, + TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA, + TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384, // AEADs w/o ECDHE TLS_RSA_WITH_AES_128_GCM_SHA256, @@ -292,6 +308,7 @@ var cipherSuitesPreferenceOrder = []uint16{ // CBC_SHA256 TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, + TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256, TLS_RSA_WITH_AES_128_CBC_SHA256, // RC4 @@ -301,22 +318,27 @@ var cipherSuitesPreferenceOrder = []uint16{ var cipherSuitesPreferenceOrderNoAES = []uint16{ // ChaCha20Poly1305 - TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305, TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305, + TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305, TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305, TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256, // AES-GCM w/ ECDHE TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, // The rest of cipherSuitesPreferenceOrder. - TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, - TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, + TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA, + TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA, + TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384, + TLS_RSA_WITH_AES_128_GCM_SHA256, TLS_RSA_WITH_AES_256_GCM_SHA384, TLS_RSA_WITH_AES_128_CBC_SHA, TLS_RSA_WITH_AES_256_CBC_SHA, + TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, TLS_RSA_WITH_3DES_EDE_CBC_SHA, + TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, + TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256, TLS_RSA_WITH_AES_128_CBC_SHA256, TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, TLS_ECDHE_RSA_WITH_RC4_128_SHA, TLS_RSA_WITH_RC4_128_SHA, @@ -327,6 +349,7 @@ var cipherSuitesPreferenceOrderNoAES = []uint16{ var disabledCipherSuites = []uint16{ // CBC_SHA256 TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, + TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256, TLS_RSA_WITH_AES_128_CBC_SHA256, // RC4 @@ -437,6 +460,11 @@ func macSHA256(key []byte) hash.Hash { return hmac.New(sha256.New, key) } +// macSHA384 returns a SHA-384 based MAC. +func macSHA384(key []byte) hash.Hash { + return hmac.New(sha512.New384, key) +} + type aead interface { cipher.AEAD @@ -619,6 +647,12 @@ func ecdheRSAKA(version uint16) keyAgreement { } } +func ecdhePSKKA(version uint16) keyAgreement { + return &ecdhePskKeyAgreement{ + version: version, + } +} + // mutualCipherSuite returns a cipherSuite given a list of supported // ciphersuites and the id requested by the peer. func mutualCipherSuite(have []uint16, want uint16) *cipherSuite { @@ -683,8 +717,13 @@ const ( TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 uint16 = 0xc02b TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 uint16 = 0xc030 TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 uint16 = 0xc02c + TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA uint16 = 0xc035 + TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA uint16 = 0xc036 + TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256 uint16 = 0xc037 + TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384 uint16 = 0xc038 TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 uint16 = 0xcca8 TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 uint16 = 0xcca9 + TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256 uint16 = 0xccac // TLS 1.3 cipher suites. TLS_AES_128_GCM_SHA256 uint16 = 0x1301 diff --git a/src/crypto/tls/common.go b/src/crypto/tls/common.go index 1861efce66c96d..dd921bae87271c 100644 --- a/src/crypto/tls/common.go +++ b/src/crypto/tls/common.go @@ -723,6 +723,9 @@ type Config struct { // used for debugging. KeyLogWriter io.Writer + // Extra is used to hold extra configuration for external cipher-suites + Extra interface{} + // mutex protects sessionTicketKeys and autoSessionTicketKeys. mutex sync.RWMutex // sessionTicketKeys contains zero or more ticket keys. If set, it means the @@ -812,6 +815,7 @@ func (c *Config) Clone() *Config { DynamicRecordSizingDisabled: c.DynamicRecordSizingDisabled, Renegotiation: c.Renegotiation, KeyLogWriter: c.KeyLogWriter, + Extra: c.Extra, sessionTicketKeys: c.sessionTicketKeys, autoSessionTicketKeys: c.autoSessionTicketKeys, } diff --git a/src/crypto/tls/handshake_client.go b/src/crypto/tls/handshake_client.go index e61e3eb5409690..931cf23a0a9ed6 100644 --- a/src/crypto/tls/handshake_client.go +++ b/src/crypto/tls/handshake_client.go @@ -477,57 +477,63 @@ func (hs *clientHandshakeState) doFullHandshake() error { if err != nil { return err } - certMsg, ok := msg.(*certificateMsg) - if !ok || len(certMsg.certificates) == 0 { - c.sendAlert(alertUnexpectedMessage) - return unexpectedMessageError(certMsg, msg) - } - hs.finishedHash.Write(certMsg.marshal()) - msg, err = c.readHandshake() - if err != nil { - return err - } - - cs, ok := msg.(*certificateStatusMsg) - if ok { - // RFC4366 on Certificate Status Request: - // The server MAY return a "certificate_status" message. - - if !hs.serverHello.ocspStapling { - // If a server returns a "CertificateStatus" message, then the - // server MUST have included an extension of type "status_request" - // with empty "extension_data" in the extended server hello. + cipherSuite := cipherSuiteByID(c.cipherSuite) + useCerts := (cipherSuite.flags & suiteNoCerts) == 0 + if useCerts { + certMsg, ok := msg.(*certificateMsg) + if !ok || len(certMsg.certificates) == 0 { c.sendAlert(alertUnexpectedMessage) - return errors.New("tls: received unexpected CertificateStatus message") + return unexpectedMessageError(certMsg, msg) } - hs.finishedHash.Write(cs.marshal()) - - c.ocspResponse = cs.response + hs.finishedHash.Write(certMsg.marshal()) msg, err = c.readHandshake() if err != nil { return err } - } - if c.handshakes == 0 { - // If this is the first handshake on a connection, process and - // (optionally) verify the server's certificates. - if err := c.verifyServerCertificate(certMsg.certificates); err != nil { - return err + cs, ok := msg.(*certificateStatusMsg) + if ok { + // RFC4366 on Certificate Status Request: + // The server MAY return a "certificate_status" message. + + if !hs.serverHello.ocspStapling { + // If a server returns a "CertificateStatus" message, then the + // server MUST have included an extension of type "status_request" + // with empty "extension_data" in the extended server hello. + + c.sendAlert(alertUnexpectedMessage) + return errors.New("tls: received unexpected CertificateStatus message") + } + hs.finishedHash.Write(cs.marshal()) + + c.ocspResponse = cs.response + + msg, err = c.readHandshake() + if err != nil { + return err + } } - } else { - // This is a renegotiation handshake. We require that the - // server's identity (i.e. leaf certificate) is unchanged and - // thus any previous trust decision is still valid. - // - // See https://mitls.org/pages/attacks/3SHAKE for the - // motivation behind this requirement. - if !bytes.Equal(c.peerCertificates[0].Raw, certMsg.certificates[0]) { - c.sendAlert(alertBadCertificate) - return errors.New("tls: server's identity changed during renegotiation") + + if c.handshakes == 0 { + // If this is the first handshake on a connection, process and + // (optionally) verify the server's certificates. + if err := c.verifyServerCertificate(certMsg.certificates); err != nil { + return err + } + } else { + // This is a renegotiation handshake. We require that the + // server's identity (i.e. leaf certificate) is unchanged and + // thus any previous trust decision is still valid. + // + // See https://mitls.org/pages/attacks/3SHAKE for the + // motivation behind this requirement. + if !bytes.Equal(c.peerCertificates[0].Raw, certMsg.certificates[0]) { + c.sendAlert(alertBadCertificate) + return errors.New("tls: server's identity changed during renegotiation") + } } } @@ -536,7 +542,11 @@ func (hs *clientHandshakeState) doFullHandshake() error { skx, ok := msg.(*serverKeyExchangeMsg) if ok { hs.finishedHash.Write(skx.marshal()) - err = keyAgreement.processServerKeyExchange(c.config, hs.hello, hs.serverHello, c.peerCertificates[0], skx) + if useCerts { + err = keyAgreement.processServerKeyExchange(c.config, hs.hello, hs.serverHello, c.peerCertificates[0], skx) + } else { + err = keyAgreement.processServerKeyExchange(c.config, hs.hello, hs.serverHello, nil, skx) + } if err != nil { c.sendAlert(alertUnexpectedMessage) return err @@ -578,7 +588,7 @@ func (hs *clientHandshakeState) doFullHandshake() error { // Certificate message, even if it's empty because we don't have a // certificate to send. if certRequested { - certMsg = new(certificateMsg) + certMsg := new(certificateMsg) certMsg.certificates = chainToSend.Certificate hs.finishedHash.Write(certMsg.marshal()) if _, err := c.writeRecord(recordTypeHandshake, certMsg.marshal()); err != nil { @@ -586,7 +596,13 @@ func (hs *clientHandshakeState) doFullHandshake() error { } } - preMasterSecret, ckx, err := keyAgreement.generateClientKeyExchange(c.config, hs.hello, c.peerCertificates[0]) + var preMasterSecret []byte + var ckx *clientKeyExchangeMsg + if useCerts { + preMasterSecret, ckx, err = keyAgreement.generateClientKeyExchange(c.config, hs.hello, c.peerCertificates[0]) + } else { + preMasterSecret, ckx, err = keyAgreement.generateClientKeyExchange(c.config, hs.hello, nil) + } if err != nil { c.sendAlert(alertInternalError) return err diff --git a/src/crypto/tls/handshake_server.go b/src/crypto/tls/handshake_server.go index 7606305c1dfbcc..082d4601eb49fd 100644 --- a/src/crypto/tls/handshake_server.go +++ b/src/crypto/tls/handshake_server.go @@ -370,12 +370,14 @@ func (hs *serverHandshakeState) cipherSuiteOk(c *cipherSuite) bool { if !hs.ecdheOk { return false } - if c.flags&suiteECSign != 0 { - if !hs.ecSignOk { + if c.flags&suiteNoCerts == 0 { + if c.flags&suiteECSign != 0 { + if !hs.ecSignOk { + return false + } + } else if !hs.rsaSignOk { return false } - } else if !hs.rsaSignOk { - return false } } else if !hs.rsaDecryptOk { return false @@ -502,20 +504,22 @@ func (hs *serverHandshakeState) doFullHandshake() error { return err } - certMsg := new(certificateMsg) - certMsg.certificates = hs.cert.Certificate - hs.finishedHash.Write(certMsg.marshal()) - if _, err := c.writeRecord(recordTypeHandshake, certMsg.marshal()); err != nil { - return err - } - - if hs.hello.ocspStapling { - certStatus := new(certificateStatusMsg) - certStatus.response = hs.cert.OCSPStaple - hs.finishedHash.Write(certStatus.marshal()) - if _, err := c.writeRecord(recordTypeHandshake, certStatus.marshal()); err != nil { + if hs.suite.flags&suiteNoCerts == 0 { // this suite requires certificate handshake + certMsg := new(certificateMsg) + certMsg.certificates = hs.cert.Certificate + hs.finishedHash.Write(certMsg.marshal()) + if _, err := c.writeRecord(recordTypeHandshake, certMsg.marshal()); err != nil { return err } + + if hs.hello.ocspStapling { + certStatus := new(certificateStatusMsg) + certStatus.response = hs.cert.OCSPStaple + hs.finishedHash.Write(certStatus.marshal()) + if _, err := c.writeRecord(recordTypeHandshake, certStatus.marshal()); err != nil { + return err + } + } } keyAgreement := hs.suite.ka(c.vers) diff --git a/src/crypto/tls/handshake_test.go b/src/crypto/tls/handshake_test.go index bacc8b7d4fed57..4e7634302ef85d 100644 --- a/src/crypto/tls/handshake_test.go +++ b/src/crypto/tls/handshake_test.go @@ -320,7 +320,9 @@ func (zeroSource) Read(b []byte) (n int, err error) { func allCipherSuites() []uint16 { ids := make([]uint16, len(cipherSuites)) for i, suite := range cipherSuites { - ids[i] = suite.id + if suite.flags&suiteNoCerts == 0 { + ids[i] = suite.id + } } return ids diff --git a/src/crypto/tls/key_agreement.go b/src/crypto/tls/key_agreement.go index c28a64f3a8b8c5..323117dde031c4 100644 --- a/src/crypto/tls/key_agreement.go +++ b/src/crypto/tls/key_agreement.go @@ -355,3 +355,219 @@ func (ka *ecdheKeyAgreement) generateClientKeyExchange(config *Config, clientHel return ka.preMasterSecret, ka.ckx, nil } + +// ecdhePskKeyAgreement implements a TLS key agreement where the server +// generates an ephemeral EC public/private key pair and signs it. The +// pre-master secret is then calculated using ECDH with Pre-shared key. +type ecdhePskKeyAgreement struct { + version uint16 + params ecdheParameters + + // ckx and otherSecret are generated in processServerKeyExchange + // and returned in generateClientKeyExchange. + ckx *clientKeyExchangeMsg + otherSecret []byte + pskIdentity string +} + +func (ka *ecdhePskKeyAgreement) generateServerKeyExchange(config *Config, cert *Certificate, clientHello *clientHelloMsg, hello *serverHelloMsg) (*serverKeyExchangeMsg, error) { + var curveID CurveID + for _, c := range clientHello.supportedCurves { + if config.supportsCurve(c) { + curveID = c + break + } + } + + if curveID == 0 { + return nil, errors.New("tls: no supported elliptic curves offered") + } + if _, ok := curveForCurveID(curveID); curveID != X25519 && !ok { + return nil, errors.New("tls: CurvePreferences includes unsupported curve") + } + + params, err := generateECDHEParameters(config.rand(), curveID) + if err != nil { + return nil, err + } + ka.params = params + + ecdhePublic := params.PublicKey() + + serverECDHEParamsSize := 1 + 2 + 1 + len(ecdhePublic) + skx := new(serverKeyExchangeMsg) + skx.key = make([]byte, 2+serverECDHEParamsSize) + + // See RFC 4492, Section 5.4. + serverECDHEParams := skx.key[2:] + serverECDHEParams[0] = 3 // named curve + serverECDHEParams[1] = byte(curveID >> 8) + serverECDHEParams[2] = byte(curveID) + serverECDHEParams[3] = byte(len(ecdhePublic)) + copy(serverECDHEParams[4:], ecdhePublic) + + return skx, nil +} + +func (ka *ecdhePskKeyAgreement) processClientKeyExchange(config *Config, cert *Certificate, ckx *clientKeyExchangeMsg, version uint16) ([]byte, error) { + pskConfig, ok := config.Extra.(PSKConfig) + if !ok { + return nil, errors.New("bad Config - Extra not of type PSKConfig") + } + + if pskConfig.GetKey == nil { + return nil, errors.New("bad Config - GetKey required for PSK") + } + + if len(ckx.ciphertext) < 2 { + return nil, errors.New("bad ClientKeyExchange") + } + + ciphertext := ckx.ciphertext + pskIdentityLen := int(ciphertext[0])<<8 | int(ciphertext[1]) + if len(ciphertext) < (pskIdentityLen + 2) { + return nil, errors.New("bad ClientKeyExchange") + } + pskIdentity := string(ciphertext[2 : 2+pskIdentityLen]) + ciphertext = ciphertext[2+pskIdentityLen:] + + // ciphertext is actually the pskIdentity here + psk, err := pskConfig.GetKey(pskIdentity) + if err != nil { + return nil, err + } + pskLen := len(psk) + + if len(ciphertext) < 1 { + return nil, errors.New("bad ClientKeyExchange") + } + + publicKeyLen := int(ciphertext[0]) + if len(ciphertext) < (publicKeyLen + 1) { + return nil, errors.New("bad ClientKeyExchange") + } + publicKey := ciphertext[1 : 1+publicKeyLen] + + otherSecret := ka.params.SharedKey(publicKey) + if otherSecret == nil { + return nil, errClientKeyExchange + } + otherSecretLen := len(otherSecret) + + preMasterSecret := make([]byte, 4+pskLen+otherSecretLen) + preMasterSecret[0] = byte(otherSecretLen >> 8) + preMasterSecret[1] = byte(otherSecretLen) + copy(preMasterSecret[2:], otherSecret) + preMasterSecret[2+otherSecretLen] = byte(pskLen >> 8) + preMasterSecret[3+otherSecretLen] = byte(pskLen) + copy(preMasterSecret[4+otherSecretLen:], psk) + + return preMasterSecret, nil +} + +func (ka *ecdhePskKeyAgreement) processServerKeyExchange(config *Config, clientHello *clientHelloMsg, serverHello *serverHelloMsg, cert *x509.Certificate, skx *serverKeyExchangeMsg) error { + pskConfig, ok := config.Extra.(PSKConfig) + if !ok { + return errors.New("bad Config - Extra not of type PSKConfig") + } + + if pskConfig.GetIdentity == nil { + return errors.New("bad PSKConfig - GetIdentity required for PSK") + } + + if pskConfig.GetKey == nil { + return errors.New("bad Config - GetKey required for PSK") + } + + key := skx.key + + if len(key) < 2 { + return errServerKeyExchange + } + pskIdentityFromServerLen := int(key[0])<<8 | int(key[1]) + if len(key) < (pskIdentityFromServerLen + 2) { + return errServerKeyExchange + } + pskIdentityFromServer := string(key[2 : 2+pskIdentityFromServerLen]) + key = key[2:] + _ = pskIdentityFromServer + + pskIdentity := pskConfig.GetIdentity() + bPskIdentity := []byte(pskIdentity) + pskIdentityLen := len(bPskIdentity) + ka.pskIdentity = pskIdentity + + if len(key) < 3 { + return errServerKeyExchange + } + + if key[0] != 3 { // named curve + return errors.New("tls: server selected unsupported curve") + } + curveID := CurveID(key[1])<<8 | CurveID(key[2]) + + publicLen := int(key[3]) + if publicLen+4 > len(key) { + return errServerKeyExchange + } + serverECDHEParams := key[:4+publicLen] + publicKey := serverECDHEParams[4:] + + if _, ok := curveForCurveID(curveID); curveID != X25519 && !ok { + return errors.New("tls: server selected unsupported curve") + } + + params, err := generateECDHEParameters(config.rand(), curveID) + if err != nil { + return err + } + ka.params = params + + ka.otherSecret = params.SharedKey(publicKey) + if ka.otherSecret == nil { + return errServerKeyExchange + } + + ourPublicKey := params.PublicKey() + ka.ckx = new(clientKeyExchangeMsg) + ka.ckx.ciphertext = make([]byte, 2+pskIdentityLen+1+len(ourPublicKey)) + ka.ckx.ciphertext[0] = byte(pskIdentityLen >> 8) + ka.ckx.ciphertext[1] = byte(pskIdentityLen) + copy(ka.ckx.ciphertext[2:], bPskIdentity) + ka.ckx.ciphertext[2+pskIdentityLen] = byte(len(ourPublicKey)) + copy(ka.ckx.ciphertext[3+pskIdentityLen:], ourPublicKey) + + return nil +} + +func (ka *ecdhePskKeyAgreement) generateClientKeyExchange(config *Config, clientHello *clientHelloMsg, cert *x509.Certificate) ([]byte, *clientKeyExchangeMsg, error) { + pskConfig, ok := config.Extra.(PSKConfig) + if !ok { + return nil, nil, errors.New("bad Config - Extra not of type PSKConfig") + } + + if pskConfig.GetKey == nil { + return nil, nil, errors.New("bad Config - GetKey required for PSK") + } + + if ka.ckx == nil { + return nil, nil, errors.New("tls: missing ServerKeyExchange message") + } + + psk, err := pskConfig.GetKey(ka.pskIdentity) + if err != nil { + return nil, nil, err + } + pskLen := len(psk) + + otherSecretLen := len(ka.otherSecret) + preMasterSecret := make([]byte, 4+pskLen+otherSecretLen) + preMasterSecret[0] = byte(otherSecretLen >> 8) + preMasterSecret[1] = byte(otherSecretLen) + copy(preMasterSecret[2:], ka.otherSecret) + preMasterSecret[2+otherSecretLen] = byte(pskLen >> 8) + preMasterSecret[3+otherSecretLen] = byte(pskLen) + copy(preMasterSecret[4+otherSecretLen:], psk) + + return preMasterSecret, ka.ckx, nil +} diff --git a/src/crypto/tls/psk.go b/src/crypto/tls/psk.go new file mode 100755 index 00000000000000..5a4f3e84728d96 --- /dev/null +++ b/src/crypto/tls/psk.go @@ -0,0 +1,11 @@ +package tls + +// Configuration for PSK cipher-suite. The client needs to provide a GetIdentity and GetKey functions to retrieve client id and pre-shared-key +type PSKConfig struct { + // client-only - returns the client identity + GetIdentity func() string + + // for server - returns the key associated to a client identity + // for client - returns the key for this client + GetKey func(identity string) ([]byte, error) +} diff --git a/src/crypto/tls/tls_test.go b/src/crypto/tls/tls_test.go index d8a43add1796f6..0cd0e35c9ceb11 100644 --- a/src/crypto/tls/tls_test.go +++ b/src/crypto/tls/tls_test.go @@ -825,7 +825,7 @@ func TestCloneNonFuncFields(t *testing.T) { f.Set(reflect.ValueOf([]CurveID{CurveP256})) case "Renegotiation": f.Set(reflect.ValueOf(RenegotiateOnceAsClient)) - case "mutex", "autoSessionTicketKeys", "sessionTicketKeys": + case "Extra", "mutex", "autoSessionTicketKeys", "sessionTicketKeys": continue // these are unexported fields that are handled separately default: t.Errorf("all fields must be accounted for, but saw unknown field %q", fn) @@ -1541,6 +1541,18 @@ func TestCipherSuites(t *testing.T) { } else if aSuite.flags&suiteECSign == 0 && bSuite.flags&suiteECSign != 0 { return false } + // * < NoCerts + if aSuite.flags&suiteNoCerts == 0 && bSuite.flags&suiteNoCerts != 0 { + return true + } else if aSuite.flags&suiteNoCerts != 0 && bSuite.flags&suiteNoCerts == 0 { + return false + } + // SHA < SHA384 + if strings.HasSuffix(aName, "SHA") && strings.Contains(bName, "SHA384") { + return true + } else if strings.Contains(aName, "SHA384") && strings.HasSuffix(bName, "SHA") { + return false + } t.Fatalf("two ciphersuites are equal by all criteria: %v and %v", aName, bName) panic("unreachable") } @@ -1569,7 +1581,11 @@ func http2isBadCipher(cipher uint16) bool { TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, - TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256: + TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, + TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA, + TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA, + TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256, + TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384: return true default: return false