Skip to content

Commit

Permalink
crypto/tls: add cipher suites TLS_ECDHE_PSK
Browse files Browse the repository at this point in the history
  • Loading branch information
jclab-joseph committed Jun 30, 2022
1 parent 160414c commit e296a6b
Show file tree
Hide file tree
Showing 8 changed files with 376 additions and 68 deletions.
51 changes: 45 additions & 6 deletions src/crypto/tls/cipher_suites.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"crypto/rc4"
"crypto/sha1"
"crypto/sha256"
"crypto/sha512"
"fmt"
"hash"
"internal/cpu"
Expand Down Expand Up @@ -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},
}
}

Expand All @@ -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},
}
}

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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,
Expand All @@ -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
Expand All @@ -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,
Expand All @@ -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
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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
Expand Down
4 changes: 4 additions & 0 deletions src/crypto/tls/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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,
}
Expand Down
102 changes: 59 additions & 43 deletions src/crypto/tls/handshake_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -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")
}
}
}

Expand All @@ -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
Expand Down Expand Up @@ -578,15 +588,21 @@ 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 {
return err
}
}

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
Expand Down
36 changes: 20 additions & 16 deletions src/crypto/tls/handshake_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down
4 changes: 3 additions & 1 deletion src/crypto/tls/handshake_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading

0 comments on commit e296a6b

Please sign in to comment.