diff options
Diffstat (limited to 'ssl/test/runner/key_agreement.go')
-rw-r--r-- | ssl/test/runner/key_agreement.go | 215 |
1 files changed, 198 insertions, 17 deletions
diff --git a/ssl/test/runner/key_agreement.go b/ssl/test/runner/key_agreement.go index 9ecd2e56..5e5d976d 100644 --- a/ssl/test/runner/key_agreement.go +++ b/ssl/test/runner/key_agreement.go @@ -21,6 +21,7 @@ import ( "math/big" "./curve25519" + "./newhope" ) var errClientKeyExchange = errors.New("tls: invalid ClientKeyExchange message") @@ -252,13 +253,16 @@ func pickTLS12HashForSignature(sigType uint8, clientList, serverList []signature // A ecdhCurve is an instance of ECDH-style key agreement for TLS. type ecdhCurve interface { - // generateKeypair generates a keypair using rand. It returns the - // encoded public key. - generateKeypair(rand io.Reader) (publicKey []byte, err error) + // offer generates a keypair using rand. It returns the encoded |publicKey|. + offer(rand io.Reader) (publicKey []byte, err error) - // computeSecret performs a key exchange against peerKey and returns - // the resulting shared secret. - computeSecret(peerKey []byte) (preMasterSecret []byte, err error) + // accept responds to the |peerKey| generated by |offer| with the acceptor's + // |publicKey|, and returns agreed-upon |preMasterSecret| to the acceptor. + accept(rand io.Reader, peerKey []byte) (publicKey []byte, preMasterSecret []byte, err error) + + // finish returns the computed |preMasterSecret|, given the |peerKey| + // generated by |accept|. + finish(peerKey []byte) (preMasterSecret []byte, err error) } // ellipticECDHCurve implements ecdhCurve with an elliptic.Curve. @@ -267,7 +271,7 @@ type ellipticECDHCurve struct { privateKey []byte } -func (e *ellipticECDHCurve) generateKeypair(rand io.Reader) (publicKey []byte, err error) { +func (e *ellipticECDHCurve) offer(rand io.Reader) (publicKey []byte, err error) { var x, y *big.Int e.privateKey, x, y, err = elliptic.GenerateKey(e.curve, rand) if err != nil { @@ -276,7 +280,19 @@ func (e *ellipticECDHCurve) generateKeypair(rand io.Reader) (publicKey []byte, e return elliptic.Marshal(e.curve, x, y), nil } -func (e *ellipticECDHCurve) computeSecret(peerKey []byte) (preMasterSecret []byte, err error) { +func (e *ellipticECDHCurve) accept(rand io.Reader, peerKey []byte) (publicKey []byte, preMasterSecret []byte, err error) { + publicKey, err = e.offer(rand) + if err != nil { + return nil, nil, err + } + preMasterSecret, err = e.finish(peerKey) + if err != nil { + return nil, nil, err + } + return +} + +func (e *ellipticECDHCurve) finish(peerKey []byte) (preMasterSecret []byte, err error) { x, y := elliptic.Unmarshal(e.curve, peerKey) if x == nil { return nil, errors.New("tls: invalid peer key") @@ -294,7 +310,7 @@ type x25519ECDHCurve struct { privateKey [32]byte } -func (e *x25519ECDHCurve) generateKeypair(rand io.Reader) (publicKey []byte, err error) { +func (e *x25519ECDHCurve) offer(rand io.Reader) (publicKey []byte, err error) { _, err = io.ReadFull(rand, e.privateKey[:]) if err != nil { return @@ -304,7 +320,19 @@ func (e *x25519ECDHCurve) generateKeypair(rand io.Reader) (publicKey []byte, err return out[:], nil } -func (e *x25519ECDHCurve) computeSecret(peerKey []byte) (preMasterSecret []byte, err error) { +func (e *x25519ECDHCurve) accept(rand io.Reader, peerKey []byte) (publicKey []byte, preMasterSecret []byte, err error) { + publicKey, err = e.offer(rand) + if err != nil { + return nil, nil, err + } + preMasterSecret, err = e.finish(peerKey) + if err != nil { + return nil, nil, err + } + return +} + +func (e *x25519ECDHCurve) finish(peerKey []byte) (preMasterSecret []byte, err error) { if len(peerKey) != 32 { return nil, errors.New("tls: invalid peer key") } @@ -321,6 +349,66 @@ func (e *x25519ECDHCurve) computeSecret(peerKey []byte) (preMasterSecret []byte, return out[:], nil } +// cecpq1Curve is combined elliptic curve (X25519) and post-quantum (new hope) key +// agreement. +type cecpq1Curve struct { + x25519 *x25519ECDHCurve + newhope *newhope.Poly +} + +func (e *cecpq1Curve) offer(rand io.Reader) (publicKey []byte, err error) { + var x25519OfferMsg, newhopeOfferMsg []byte + + e.x25519 = new(x25519ECDHCurve) + if x25519OfferMsg, err = e.x25519.offer(rand); err != nil { + return nil, err + } + + newhopeOfferMsg, e.newhope = newhope.Offer(rand) + + return append(x25519OfferMsg, newhopeOfferMsg[:]...), nil +} + +func (e *cecpq1Curve) accept(rand io.Reader, peerKey []byte) (publicKey []byte, preMasterSecret []byte, err error) { + if len(peerKey) != 32+newhope.OfferMsgLen { + return nil, nil, errors.New("cecpq1: invalid offer message") + } + + var x25519AcceptMsg, newhopeAcceptMsg []byte + var x25519Secret []byte + var newhopeSecret newhope.Key + + x25519 := new(x25519ECDHCurve) + if x25519AcceptMsg, x25519Secret, err = x25519.accept(rand, peerKey[:32]); err != nil { + return nil, nil, err + } + + if newhopeSecret, newhopeAcceptMsg, err = newhope.Accept(rand, peerKey[32:]); err != nil { + return nil, nil, err + } + + return append(x25519AcceptMsg, newhopeAcceptMsg[:]...), append(x25519Secret, newhopeSecret[:]...), nil +} + +func (e *cecpq1Curve) finish(peerKey []byte) (preMasterSecret []byte, err error) { + if len(peerKey) != 32+newhope.AcceptMsgLen { + return nil, errors.New("cecpq1: invalid accept message") + } + + var x25519Secret []byte + var newhopeSecret newhope.Key + + if x25519Secret, err = e.x25519.finish(peerKey[:32]); err != nil { + return nil, err + } + + if newhopeSecret, err = e.newhope.Finish(peerKey[32:]); err != nil { + return nil, err + } + + return append(x25519Secret, newhopeSecret[:]...), nil +} + func curveForCurveID(id CurveID) (ecdhCurve, bool) { switch id { case CurveP224: @@ -551,7 +639,7 @@ NextCandidate: return nil, errors.New("tls: preferredCurves includes unsupported curve") } - publicKey, err := ka.curve.generateKeypair(config.rand()) + publicKey, err := ka.curve.offer(config.rand()) if err != nil { return nil, err } @@ -566,6 +654,9 @@ NextCandidate: } serverECDHParams[3] = byte(len(publicKey)) copy(serverECDHParams[4:], publicKey) + if config.Bugs.InvalidECDHPoint { + serverECDHParams[4] ^= 0xff + } return ka.auth.signParameters(config, cert, clientHello, hello, serverECDHParams) } @@ -574,7 +665,7 @@ func (ka *ecdheKeyAgreement) processClientKeyExchange(config *Config, cert *Cert if len(ckx.ciphertext) == 0 || int(ckx.ciphertext[0]) != len(ckx.ciphertext)-1 { return nil, errClientKeyExchange } - return ka.curve.computeSecret(ckx.ciphertext[1:]) + return ka.curve.finish(ckx.ciphertext[1:]) } func (ka *ecdheKeyAgreement) processServerKeyExchange(config *Config, clientHello *clientHelloMsg, serverHello *serverHelloMsg, cert *x509.Certificate, skx *serverKeyExchangeMsg) error { @@ -609,11 +700,7 @@ func (ka *ecdheKeyAgreement) generateClientKeyExchange(config *Config, clientHel return nil, nil, errors.New("missing ServerKeyExchange message") } - publicKey, err := ka.curve.generateKeypair(config.rand()) - if err != nil { - return nil, nil, err - } - preMasterSecret, err := ka.curve.computeSecret(ka.peerKey) + publicKey, preMasterSecret, err := ka.curve.accept(config.rand(), ka.peerKey) if err != nil { return nil, nil, err } @@ -622,6 +709,100 @@ func (ka *ecdheKeyAgreement) generateClientKeyExchange(config *Config, clientHel ckx.ciphertext = make([]byte, 1+len(publicKey)) ckx.ciphertext[0] = byte(len(publicKey)) copy(ckx.ciphertext[1:], publicKey) + if config.Bugs.InvalidECDHPoint { + ckx.ciphertext[1] ^= 0xff + } + + return preMasterSecret, ckx, nil +} + +// cecpq1RSAKeyAgreement is like an ecdheKeyAgreement, but using the cecpq1Curve +// pseudo-curve, and without any parameters (e.g. curve name) other than the +// keys being exchanged. The signature may either be ECDSA or RSA. +type cecpq1KeyAgreement struct { + auth keyAgreementAuthentication + curve ecdhCurve + peerKey []byte +} + +func (ka *cecpq1KeyAgreement) generateServerKeyExchange(config *Config, cert *Certificate, clientHello *clientHelloMsg, hello *serverHelloMsg) (*serverKeyExchangeMsg, error) { + ka.curve = &cecpq1Curve{} + publicKey, err := ka.curve.offer(config.rand()) + if err != nil { + return nil, err + } + + if config.Bugs.CECPQ1BadX25519Part { + publicKey[0] ^= 1 + } + if config.Bugs.CECPQ1BadNewhopePart { + publicKey[32] ^= 1 + publicKey[33] ^= 1 + publicKey[34] ^= 1 + publicKey[35] ^= 1 + } + + var params []byte + params = append(params, byte(len(publicKey)>>8)) + params = append(params, byte(len(publicKey)&0xff)) + params = append(params, publicKey[:]...) + + return ka.auth.signParameters(config, cert, clientHello, hello, params) +} + +func (ka *cecpq1KeyAgreement) processClientKeyExchange(config *Config, cert *Certificate, ckx *clientKeyExchangeMsg, version uint16) ([]byte, error) { + if len(ckx.ciphertext) < 2 { + return nil, errClientKeyExchange + } + peerKeyLen := int(ckx.ciphertext[0])<<8 + int(ckx.ciphertext[1]) + peerKey := ckx.ciphertext[2:] + if peerKeyLen != len(peerKey) { + return nil, errClientKeyExchange + } + return ka.curve.finish(peerKey) +} + +func (ka *cecpq1KeyAgreement) processServerKeyExchange(config *Config, clientHello *clientHelloMsg, serverHello *serverHelloMsg, cert *x509.Certificate, skx *serverKeyExchangeMsg) error { + if len(skx.key) < 2 { + return errServerKeyExchange + } + peerKeyLen := int(skx.key[0])<<8 + int(skx.key[1]) + // Save the peer key for later. + if len(skx.key) < 2+peerKeyLen { + return errServerKeyExchange + } + ka.peerKey = skx.key[2 : 2+peerKeyLen] + if peerKeyLen != len(ka.peerKey) { + return errServerKeyExchange + } + + // Check the signature. + params := skx.key[:2+peerKeyLen] + sig := skx.key[2+peerKeyLen:] + return ka.auth.verifyParameters(config, clientHello, serverHello, cert, params, sig) +} + +func (ka *cecpq1KeyAgreement) generateClientKeyExchange(config *Config, clientHello *clientHelloMsg, cert *x509.Certificate) ([]byte, *clientKeyExchangeMsg, error) { + curve := &cecpq1Curve{} + publicKey, preMasterSecret, err := curve.accept(config.rand(), ka.peerKey) + if err != nil { + return nil, nil, err + } + + if config.Bugs.CECPQ1BadX25519Part { + publicKey[0] ^= 1 + } + if config.Bugs.CECPQ1BadNewhopePart { + publicKey[32] ^= 1 + publicKey[33] ^= 1 + publicKey[34] ^= 1 + publicKey[35] ^= 1 + } + + ckx := new(clientKeyExchangeMsg) + ckx.ciphertext = append(ckx.ciphertext, byte(len(publicKey)>>8)) + ckx.ciphertext = append(ckx.ciphertext, byte(len(publicKey)&0xff)) + ckx.ciphertext = append(ckx.ciphertext, publicKey[:]...) return preMasterSecret, ckx, nil } |