Skip to content

Commit e70353c

Browse files
josephjoseph
joseph
authored and
joseph
committed
crypto/tls: add cipher suites TLS_ECDHE_PSK
1 parent 160414c commit e70353c

File tree

6 files changed

+289
-17
lines changed

6 files changed

+289
-17
lines changed

src/crypto/tls/cipher_suites.go

+36
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414
"crypto/rc4"
1515
"crypto/sha1"
1616
"crypto/sha256"
17+
"crypto/sha512"
1718
"fmt"
1819
"hash"
1920
"internal/cpu"
@@ -69,6 +70,10 @@ func CipherSuites() []*CipherSuite {
6970
{TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", supportedOnlyTLS12, false},
7071
{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", supportedOnlyTLS12, false},
7172
{TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", supportedOnlyTLS12, false},
73+
{TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA, "TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA", supportedOnlyTLS12, false},
74+
{TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256, "TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256", supportedOnlyTLS12, false},
75+
{TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA, "TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA", supportedOnlyTLS12, false},
76+
{TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384, "TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384", supportedOnlyTLS12, false},
7277
{TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256", supportedOnlyTLS12, false},
7378
{TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256", supportedOnlyTLS12, false},
7479
}
@@ -128,6 +133,9 @@ const (
128133
// suiteSHA384 indicates that the cipher suite uses SHA384 as the
129134
// handshake hash.
130135
suiteSHA384
136+
// suiteNoCerts indicates that the cipher suite doesn't use certificate exchange
137+
// (anonymous ciphersuites or pre-shared-secret)
138+
suiteNoCerts
131139
)
132140

133141
// A cipherSuite is a TLS 1.0–1.2 cipher suite, and defines the key exchange
@@ -169,6 +177,11 @@ var cipherSuites = []*cipherSuite{ // TODO: replace with a map, since the order
169177
{TLS_RSA_WITH_RC4_128_SHA, 16, 20, 0, rsaKA, 0, cipherRC4, macSHA1, nil},
170178
{TLS_ECDHE_RSA_WITH_RC4_128_SHA, 16, 20, 0, ecdheRSAKA, suiteECDHE, cipherRC4, macSHA1, nil},
171179
{TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, 16, 20, 0, ecdheECDSAKA, suiteECDHE | suiteECSign, cipherRC4, macSHA1, nil},
180+
181+
{TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA, 16, 20, 16, ecdhePSKKA, suiteECDHE | suiteTLS12 | suiteNoCerts, cipherAES, macSHA1, nil},
182+
{TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256, 16, 32, 16, ecdhePSKKA, suiteECDHE | suiteTLS12 | suiteNoCerts, cipherAES, macSHA256, nil},
183+
{TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA, 32, 20, 16, ecdhePSKKA, suiteECDHE | suiteTLS12 | suiteNoCerts, cipherAES, macSHA1, nil},
184+
{TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384, 32, 48, 16, ecdhePSKKA, suiteECDHE | suiteTLS12 | suiteNoCerts, cipherAES, macSHA384, nil},
172185
}
173186

174187
// selectCipherSuite returns the first TLS 1.0–1.2 cipher suite from ids which
@@ -297,6 +310,10 @@ var cipherSuitesPreferenceOrder = []uint16{
297310
// RC4
298311
TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, TLS_ECDHE_RSA_WITH_RC4_128_SHA,
299312
TLS_RSA_WITH_RC4_128_SHA,
313+
314+
// PSK
315+
TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384, TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA,
316+
TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256, TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA,
300317
}
301318

302319
var cipherSuitesPreferenceOrderNoAES = []uint16{
@@ -320,6 +337,10 @@ var cipherSuitesPreferenceOrderNoAES = []uint16{
320337
TLS_RSA_WITH_AES_128_CBC_SHA256,
321338
TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, TLS_ECDHE_RSA_WITH_RC4_128_SHA,
322339
TLS_RSA_WITH_RC4_128_SHA,
340+
341+
// PSK
342+
TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384, TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA,
343+
TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256, TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA,
323344
}
324345

325346
// disabledCipherSuites are not used unless explicitly listed in
@@ -437,6 +458,11 @@ func macSHA256(key []byte) hash.Hash {
437458
return hmac.New(sha256.New, key)
438459
}
439460

461+
// macSHA384 returns a SHA-384 based MAC.
462+
func macSHA384(key []byte) hash.Hash {
463+
return hmac.New(sha512.New384, key)
464+
}
465+
440466
type aead interface {
441467
cipher.AEAD
442468

@@ -619,6 +645,12 @@ func ecdheRSAKA(version uint16) keyAgreement {
619645
}
620646
}
621647

648+
func ecdhePSKKA(version uint16) keyAgreement {
649+
return &ecdhePskKeyAgreement{
650+
version: version,
651+
}
652+
}
653+
622654
// mutualCipherSuite returns a cipherSuite given a list of supported
623655
// ciphersuites and the id requested by the peer.
624656
func mutualCipherSuite(have []uint16, want uint16) *cipherSuite {
@@ -683,6 +715,10 @@ const (
683715
TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 uint16 = 0xc02b
684716
TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 uint16 = 0xc030
685717
TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 uint16 = 0xc02c
718+
TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA uint16 = 0xc035
719+
TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA uint16 = 0xc036
720+
TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256 uint16 = 0xc037
721+
TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384 uint16 = 0xc038
686722
TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 uint16 = 0xcca8
687723
TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 uint16 = 0xcca9
688724

src/crypto/tls/common.go

+3
Original file line numberDiff line numberDiff line change
@@ -723,6 +723,9 @@ type Config struct {
723723
// used for debugging.
724724
KeyLogWriter io.Writer
725725

726+
// Extra is used to hold extra configuration for external cipher-suites
727+
Extra interface{}
728+
726729
// mutex protects sessionTicketKeys and autoSessionTicketKeys.
727730
mutex sync.RWMutex
728731
// sessionTicketKeys contains zero or more ticket keys. If set, it means the

src/crypto/tls/handshake_server.go

+20-16
Original file line numberDiff line numberDiff line change
@@ -370,12 +370,14 @@ func (hs *serverHandshakeState) cipherSuiteOk(c *cipherSuite) bool {
370370
if !hs.ecdheOk {
371371
return false
372372
}
373-
if c.flags&suiteECSign != 0 {
374-
if !hs.ecSignOk {
373+
if c.flags&suiteNoCerts == 0 {
374+
if c.flags&suiteECSign != 0 {
375+
if !hs.ecSignOk {
376+
return false
377+
}
378+
} else if !hs.rsaSignOk {
375379
return false
376380
}
377-
} else if !hs.rsaSignOk {
378-
return false
379381
}
380382
} else if !hs.rsaDecryptOk {
381383
return false
@@ -502,20 +504,22 @@ func (hs *serverHandshakeState) doFullHandshake() error {
502504
return err
503505
}
504506

505-
certMsg := new(certificateMsg)
506-
certMsg.certificates = hs.cert.Certificate
507-
hs.finishedHash.Write(certMsg.marshal())
508-
if _, err := c.writeRecord(recordTypeHandshake, certMsg.marshal()); err != nil {
509-
return err
510-
}
511-
512-
if hs.hello.ocspStapling {
513-
certStatus := new(certificateStatusMsg)
514-
certStatus.response = hs.cert.OCSPStaple
515-
hs.finishedHash.Write(certStatus.marshal())
516-
if _, err := c.writeRecord(recordTypeHandshake, certStatus.marshal()); err != nil {
507+
if hs.suite.flags&suiteNoCerts == 0 { // this suite requires certificate handshake
508+
certMsg := new(certificateMsg)
509+
certMsg.certificates = hs.cert.Certificate
510+
hs.finishedHash.Write(certMsg.marshal())
511+
if _, err := c.writeRecord(recordTypeHandshake, certMsg.marshal()); err != nil {
517512
return err
518513
}
514+
515+
if hs.hello.ocspStapling {
516+
certStatus := new(certificateStatusMsg)
517+
certStatus.response = hs.cert.OCSPStaple
518+
hs.finishedHash.Write(certStatus.marshal())
519+
if _, err := c.writeRecord(recordTypeHandshake, certStatus.marshal()); err != nil {
520+
return err
521+
}
522+
}
519523
}
520524

521525
keyAgreement := hs.suite.ka(c.vers)

src/crypto/tls/key_agreement.go

+218
Original file line numberDiff line numberDiff line change
@@ -355,3 +355,221 @@ func (ka *ecdheKeyAgreement) generateClientKeyExchange(config *Config, clientHel
355355

356356
return ka.preMasterSecret, ka.ckx, nil
357357
}
358+
359+
// ecdhePskKeyAgreement implements a TLS key agreement where the server
360+
// generates an ephemeral EC public/private key pair and signs it. The
361+
// pre-master secret is then calculated using ECDH with Pre-shared key.
362+
type ecdhePskKeyAgreement struct {
363+
version uint16
364+
params ecdheParameters
365+
366+
// ckx and otherSecret are generated in processServerKeyExchange
367+
// and returned in generateClientKeyExchange.
368+
ckx *clientKeyExchangeMsg
369+
otherSecret []byte
370+
pskIdentity string
371+
}
372+
373+
func (ka *ecdhePskKeyAgreement) generateServerKeyExchange(config *Config, cert *Certificate, clientHello *clientHelloMsg, hello *serverHelloMsg) (*serverKeyExchangeMsg, error) {
374+
var curveID CurveID
375+
for _, c := range clientHello.supportedCurves {
376+
if config.supportsCurve(c) {
377+
curveID = c
378+
break
379+
}
380+
}
381+
382+
if curveID == 0 {
383+
return nil, errors.New("tls: no supported elliptic curves offered")
384+
}
385+
if _, ok := curveForCurveID(curveID); curveID != X25519 && !ok {
386+
return nil, errors.New("tls: CurvePreferences includes unsupported curve")
387+
}
388+
389+
params, err := generateECDHEParameters(config.rand(), curveID)
390+
if err != nil {
391+
return nil, err
392+
}
393+
ka.params = params
394+
395+
ecdhePublic := params.PublicKey()
396+
397+
serverECDHEParamsSize := 1 + 2 + 1 + len(ecdhePublic)
398+
skx := new(serverKeyExchangeMsg)
399+
skx.key = make([]byte, 2+serverECDHEParamsSize)
400+
401+
// See RFC 4492, Section 5.4.
402+
serverECDHEParams := skx.key[2:]
403+
serverECDHEParams[0] = 3 // named curve
404+
serverECDHEParams[1] = byte(curveID >> 8)
405+
serverECDHEParams[2] = byte(curveID)
406+
serverECDHEParams[3] = byte(len(ecdhePublic))
407+
copy(serverECDHEParams[4:], ecdhePublic)
408+
409+
return skx, nil
410+
}
411+
412+
func (ka *ecdhePskKeyAgreement) processClientKeyExchange(config *Config, cert *Certificate, ckx *clientKeyExchangeMsg, version uint16) ([]byte, error) {
413+
pskConfig, ok := config.Extra.(PSKConfig)
414+
if !ok {
415+
return nil, errors.New("bad Config - Extra not of type PSKConfig")
416+
}
417+
418+
if pskConfig.GetKey == nil {
419+
return nil, errors.New("bad Config - GetKey required for PSK")
420+
}
421+
422+
if len(ckx.ciphertext) < 2 {
423+
return nil, errors.New("bad ClientKeyExchange")
424+
}
425+
426+
ciphertext := ckx.ciphertext
427+
pskIdentityLen := int(ciphertext[0])<<8 | int(ciphertext[1])
428+
if len(ciphertext) < (pskIdentityLen + 2) {
429+
return nil, errors.New("bad ClientKeyExchange")
430+
}
431+
pskIdentity := string(ciphertext[2 : 2+pskIdentityLen])
432+
ciphertext = ciphertext[2+pskIdentityLen:]
433+
434+
// ciphertext is actually the pskIdentity here
435+
psk, err := pskConfig.GetKey(pskIdentity)
436+
if err != nil {
437+
return nil, err
438+
}
439+
pskLen := len(psk)
440+
441+
if len(ciphertext) < 1 {
442+
return nil, errors.New("bad ClientKeyExchange")
443+
}
444+
445+
publicKeyLen := int(ciphertext[0])
446+
if len(ciphertext) < (publicKeyLen + 1) {
447+
return nil, errors.New("bad ClientKeyExchange")
448+
}
449+
publicKey := ciphertext[1 : 1+publicKeyLen]
450+
451+
otherSecret := ka.params.SharedKey(publicKey)
452+
if otherSecret == nil {
453+
return nil, errClientKeyExchange
454+
}
455+
otherSecretLen := len(otherSecret)
456+
457+
preMasterSecret := make([]byte, 4+pskLen+otherSecretLen)
458+
preMasterSecret[0] = byte(otherSecretLen >> 8)
459+
preMasterSecret[1] = byte(otherSecretLen)
460+
copy(preMasterSecret[2:], otherSecret)
461+
preMasterSecret[2+otherSecretLen] = byte(pskLen >> 8)
462+
preMasterSecret[3+otherSecretLen] = byte(pskLen)
463+
copy(preMasterSecret[4+otherSecretLen:], psk)
464+
465+
return preMasterSecret, nil
466+
}
467+
468+
func (ka *ecdhePskKeyAgreement) processServerKeyExchange(config *Config, clientHello *clientHelloMsg, serverHello *serverHelloMsg, cert *x509.Certificate, skx *serverKeyExchangeMsg) error {
469+
pskConfig, ok := config.Extra.(PSKConfig)
470+
if !ok {
471+
return errors.New("bad Config - Extra not of type PSKConfig")
472+
}
473+
474+
if pskConfig.GetIdentity == nil {
475+
return errors.New("bad PSKConfig - GetIdentity required for PSK")
476+
}
477+
478+
if pskConfig.GetKey == nil {
479+
return errors.New("bad Config - GetKey required for PSK")
480+
}
481+
482+
key := skx.key
483+
484+
if len(key) < 2 {
485+
return errServerKeyExchange
486+
}
487+
pskIdentityFromServerLen := int(key[0])<<8 | int(key[1])
488+
if pskIdentityFromServerLen > 0 {
489+
if len(key) < (pskIdentityFromServerLen + 2) {
490+
return errServerKeyExchange
491+
}
492+
pskIdentityFromServer := string(key[2 : 2+pskIdentityFromServerLen])
493+
key = key[2+pskIdentityFromServerLen:]
494+
_ = pskIdentityFromServer
495+
}
496+
497+
pskIdentity := pskConfig.GetIdentity()
498+
bPskIdentity := []byte(pskIdentity)
499+
pskIdentityLen := len(bPskIdentity)
500+
ka.pskIdentity = pskIdentity
501+
502+
if len(key) < 3 {
503+
return errServerKeyExchange
504+
}
505+
506+
if key[0] != 3 { // named curve
507+
return errors.New("tls: server selected unsupported curve")
508+
}
509+
curveID := CurveID(key[1])<<8 | CurveID(key[2])
510+
511+
publicLen := int(key[3])
512+
if publicLen+4 > len(key) {
513+
return errServerKeyExchange
514+
}
515+
serverECDHEParams := key[:4+publicLen]
516+
publicKey := serverECDHEParams[4:]
517+
518+
if _, ok := curveForCurveID(curveID); curveID != X25519 && !ok {
519+
return errors.New("tls: server selected unsupported curve")
520+
}
521+
522+
params, err := generateECDHEParameters(config.rand(), curveID)
523+
if err != nil {
524+
return err
525+
}
526+
ka.params = params
527+
528+
ka.otherSecret = params.SharedKey(publicKey)
529+
if ka.otherSecret == nil {
530+
return errServerKeyExchange
531+
}
532+
533+
ourPublicKey := params.PublicKey()
534+
ka.ckx = new(clientKeyExchangeMsg)
535+
ka.ckx.ciphertext = make([]byte, 2+pskIdentityLen+1+len(ourPublicKey))
536+
ka.ckx.ciphertext[0] = byte(pskIdentityLen >> 8)
537+
ka.ckx.ciphertext[1] = byte(pskIdentityLen)
538+
copy(ka.ckx.ciphertext[2:], bPskIdentity)
539+
ka.ckx.ciphertext[2+pskIdentityLen] = byte(len(ourPublicKey))
540+
copy(ka.ckx.ciphertext[3+pskIdentityLen:], ourPublicKey)
541+
542+
return nil
543+
}
544+
545+
func (ka *ecdhePskKeyAgreement) generateClientKeyExchange(config *Config, clientHello *clientHelloMsg, cert *x509.Certificate) ([]byte, *clientKeyExchangeMsg, error) {
546+
pskConfig, ok := config.Extra.(PSKConfig)
547+
if !ok {
548+
return nil, nil, errors.New("bad Config - Extra not of type PSKConfig")
549+
}
550+
551+
if pskConfig.GetKey == nil {
552+
return nil, nil, errors.New("bad Config - GetKey required for PSK")
553+
}
554+
555+
if ka.ckx == nil {
556+
return nil, nil, errors.New("tls: missing ServerKeyExchange message")
557+
}
558+
559+
psk, err := pskConfig.GetKey(ka.pskIdentity)
560+
if err != nil {
561+
return nil, nil, err
562+
}
563+
pskLen := len(psk)
564+
565+
otherSecretLen := len(ka.otherSecret)
566+
preMasterSecret := make([]byte, 4+pskLen+otherSecretLen)
567+
preMasterSecret[0] = byte(otherSecretLen >> 8)
568+
preMasterSecret[1] = byte(otherSecretLen)
569+
copy(preMasterSecret[2:], ka.otherSecret)
570+
preMasterSecret[2+otherSecretLen] = byte(pskLen >> 8)
571+
preMasterSecret[3+otherSecretLen] = byte(pskLen)
572+
copy(preMasterSecret[4+otherSecretLen:], psk)
573+
574+
return preMasterSecret, ka.ckx, nil
575+
}

src/crypto/tls/psk.go

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package tls
2+
3+
// Configuration for PSK cipher-suite. The client needs to provide a GetIdentity and GetKey functions to retrieve client id and pre-shared-key
4+
type PSKConfig struct {
5+
// client-only - returns the client identity
6+
GetIdentity func() string
7+
8+
// for server - returns the key associated to a client identity
9+
// for client - returns the key for this client
10+
GetKey func(identity string) ([]byte, error)
11+
}

0 commit comments

Comments
 (0)