Elliptic curve + post-quantum key exchange
CECPQ1 is a new key exchange that concatenates the results of an X25519
key agreement and a NEWHOPE key agreement.
Change-Id: Ib919bdc2e1f30f28bf80c4c18f6558017ea386bb
Reviewed-on: https://boringssl-review.googlesource.com/7962
Reviewed-by: David Benjamin <davidben@google.com>
diff --git a/crypto/obj/obj_mac.num b/crypto/obj/obj_mac.num
index 074657a..3a7844c 100644
--- a/crypto/obj/obj_mac.num
+++ b/crypto/obj/obj_mac.num
@@ -946,3 +946,4 @@
dh_std_kdf 946
dh_cofactor_kdf 947
X25519 948
+cecpq1 949
diff --git a/crypto/obj/objects.txt b/crypto/obj/objects.txt
index f3990d0..75f2cbf 100644
--- a/crypto/obj/objects.txt
+++ b/crypto/obj/objects.txt
@@ -1333,3 +1333,6 @@
# NID for X25519 (no corresponding OID).
: X25519
+
+# NID for CECPQ1 (no corresponding OID)
+ : cecpq1
diff --git a/include/openssl/nid.h b/include/openssl/nid.h
index d51a67c..5ed44ee 100644
--- a/include/openssl/nid.h
+++ b/include/openssl/nid.h
@@ -4159,5 +4159,8 @@
#define SN_X25519 "X25519"
#define NID_X25519 948
+#define SN_cecpq1 "cecpq1"
+#define NID_cecpq1 949
+
#endif /* OPENSSL_HEADER_NID_H */
diff --git a/ssl/internal.h b/ssl/internal.h
index 962d835..acd9739 100644
--- a/ssl/internal.h
+++ b/ssl/internal.h
@@ -533,6 +533,7 @@
#define SSL_CURVE_SECP384R1 24
#define SSL_CURVE_SECP521R1 25
#define SSL_CURVE_X25519 29
+#define SSL_CURVE_CECPQ1 65165
/* An SSL_ECDH_METHOD is an implementation of ECDH-like key exchanges for
* TLS. */
@@ -567,6 +568,15 @@
int (*finish)(SSL_ECDH_CTX *ctx, uint8_t **out_secret, size_t *out_secret_len,
uint8_t *out_alert, const uint8_t *peer_key,
size_t peer_key_len);
+
+ /* get_key initializes |out| with a length-prefixed key from |cbs|. It returns
+ * one on success and zero on error. */
+ int (*get_key)(CBS *cbs, CBS *out);
+
+ /* add_key initializes |out_contents| to receive a key. Typically it will then
+ * be passed to |offer| or |accept|. It returns one on success and zero on
+ * error. */
+ int (*add_key)(CBB *cbb, CBB *out_contents);
} /* SSL_ECDH_METHOD */;
/* ssl_nid_to_curve_id looks up the curve corresponding to |nid|. On success, it
@@ -586,6 +596,12 @@
* call it in the zero state. */
void SSL_ECDH_CTX_cleanup(SSL_ECDH_CTX *ctx);
+/* SSL_ECDH_CTX_get_key calls the |get_key| method of |SSL_ECDH_METHOD|. */
+int SSL_ECDH_CTX_get_key(SSL_ECDH_CTX *ctx, CBS *cbs, CBS *out);
+
+/* SSL_ECDH_CTX_add_key calls the |add_key| method of |SSL_ECDH_METHOD|. */
+int SSL_ECDH_CTX_add_key(SSL_ECDH_CTX *ctx, CBB *cbb, CBB *out_contents);
+
/* SSL_ECDH_CTX_offer calls the |offer| method of |SSL_ECDH_METHOD|. */
int SSL_ECDH_CTX_offer(SSL_ECDH_CTX *ctx, CBB *out_public_key);
diff --git a/ssl/s3_clnt.c b/ssl/s3_clnt.c
index 88a30e3..39aea3c 100644
--- a/ssl/s3_clnt.c
+++ b/ssl/s3_clnt.c
@@ -1142,8 +1142,7 @@
CBS point;
if (!CBS_get_u8(&server_key_exchange, &curve_type) ||
curve_type != NAMED_CURVE_TYPE ||
- !CBS_get_u16(&server_key_exchange, &curve_id) ||
- !CBS_get_u8_length_prefixed(&server_key_exchange, &point)) {
+ !CBS_get_u16(&server_key_exchange, &curve_id)) {
al = SSL_AD_DECODE_ERROR;
OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
goto f_err;
@@ -1157,13 +1156,22 @@
goto f_err;
}
- /* Initialize ECDH and save the peer public key for later. */
- size_t peer_key_len;
- if (!SSL_ECDH_CTX_init(&ssl->s3->tmp.ecdh_ctx, curve_id) ||
- !CBS_stow(&point, &ssl->s3->tmp.peer_key, &peer_key_len)) {
+ if (!SSL_ECDH_CTX_init(&ssl->s3->tmp.ecdh_ctx, curve_id)) {
goto err;
}
- /* |point| has a u8 length prefix, so this fits in a |uint16_t|. */
+ if (!SSL_ECDH_CTX_get_key(&ssl->s3->tmp.ecdh_ctx, &server_key_exchange,
+ &point)) {
+ al = SSL_AD_DECODE_ERROR;
+ OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
+ goto f_err;
+ }
+
+ /* Initialize ECDH and save the peer public key for later. */
+ size_t peer_key_len;
+ if (!CBS_stow(&point, &ssl->s3->tmp.peer_key, &peer_key_len)) {
+ goto err;
+ }
+ /* |point| has a u8 or u16 length prefix, so this fits in a |uint16_t|. */
assert(sizeof(ssl->s3->tmp.peer_key_len) == 2 && peer_key_len <= 0xffff);
ssl->s3->tmp.peer_key_len = (uint16_t)peer_key_len;
} else if (!(alg_k & SSL_kPSK)) {
@@ -1616,17 +1624,9 @@
goto err;
}
} else if (alg_k & (SSL_kECDHE|SSL_kDHE)) {
- /* Generate a keypair and serialize the public half. ECDHE uses a u8 length
- * prefix while DHE uses u16. */
+ /* Generate a keypair and serialize the public half. */
CBB child;
- int child_ok;
- if (alg_k & SSL_kECDHE) {
- child_ok = CBB_add_u8_length_prefixed(&cbb, &child);
- } else {
- child_ok = CBB_add_u16_length_prefixed(&cbb, &child);
- }
-
- if (!child_ok) {
+ if (!SSL_ECDH_CTX_add_key(&ssl->s3->tmp.ecdh_ctx, &cbb, &child)) {
goto err;
}
diff --git a/ssl/s3_srvr.c b/ssl/s3_srvr.c
index 01d0be3..17dc15b 100644
--- a/ssl/s3_srvr.c
+++ b/ssl/s3_srvr.c
@@ -1209,7 +1209,7 @@
!BN_bn2cbb_padded(&child, BN_num_bytes(params->p), params->p) ||
!CBB_add_u16_length_prefixed(&cbb, &child) ||
!BN_bn2cbb_padded(&child, BN_num_bytes(params->g), params->g) ||
- !CBB_add_u16_length_prefixed(&cbb, &child) ||
+ !SSL_ECDH_CTX_add_key(&ssl->s3->tmp.ecdh_ctx, &cbb, &child) ||
!SSL_ECDH_CTX_offer(&ssl->s3->tmp.ecdh_ctx, &child)) {
goto err;
}
@@ -1227,7 +1227,7 @@
if (!SSL_ECDH_CTX_init(&ssl->s3->tmp.ecdh_ctx, curve_id) ||
!CBB_add_u8(&cbb, NAMED_CURVE_TYPE) ||
!CBB_add_u16(&cbb, curve_id) ||
- !CBB_add_u8_length_prefixed(&cbb, &child) ||
+ !SSL_ECDH_CTX_add_key(&ssl->s3->tmp.ecdh_ctx, &cbb, &child) ||
!SSL_ECDH_CTX_offer(&ssl->s3->tmp.ecdh_ctx, &child)) {
goto err;
}
@@ -1591,18 +1591,11 @@
OPENSSL_free(decrypt_buf);
decrypt_buf = NULL;
} else if (alg_k & (SSL_kECDHE|SSL_kDHE)) {
- /* Parse the ClientKeyExchange. ECDHE uses a u8 length prefix while DHE uses
- * u16. */
+ /* Parse the ClientKeyExchange. */
CBS peer_key;
- int peer_key_ok;
- if (alg_k & SSL_kECDHE) {
- peer_key_ok = CBS_get_u8_length_prefixed(&client_key_exchange, &peer_key);
- } else {
- peer_key_ok =
- CBS_get_u16_length_prefixed(&client_key_exchange, &peer_key);
- }
-
- if (!peer_key_ok || CBS_len(&client_key_exchange) != 0) {
+ if (!SSL_ECDH_CTX_get_key(&ssl->s3->tmp.ecdh_ctx, &client_key_exchange,
+ &peer_key) ||
+ CBS_len(&client_key_exchange) != 0) {
al = SSL_AD_DECODE_ERROR;
OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
goto f_err;
diff --git a/ssl/ssl_ecdh.c b/ssl/ssl_ecdh.c
index 305a5af..04ecd39 100644
--- a/ssl/ssl_ecdh.c
+++ b/ssl/ssl_ecdh.c
@@ -23,6 +23,7 @@
#include <openssl/ec.h>
#include <openssl/err.h>
#include <openssl/mem.h>
+#include <openssl/newhope.h>
#include <openssl/nid.h>
#include "internal.h"
@@ -220,6 +221,154 @@
return 1;
}
+
+/* Combined X25119 + New Hope (post-quantum) implementation. */
+
+typedef struct {
+ uint8_t x25519_key[32];
+ NEWHOPE_POLY *newhope_sk;
+} cecpq1_data;
+
+#define CECPQ1_OFFERMSG_LENGTH (32 + NEWHOPE_OFFERMSG_LENGTH)
+#define CECPQ1_ACCEPTMSG_LENGTH (32 + NEWHOPE_ACCEPTMSG_LENGTH)
+#define CECPQ1_SECRET_LENGTH (32 + SHA256_DIGEST_LENGTH)
+
+static void ssl_cecpq1_cleanup(SSL_ECDH_CTX *ctx) {
+ if (ctx->data == NULL) {
+ return;
+ }
+ cecpq1_data *data = ctx->data;
+ NEWHOPE_POLY_free(data->newhope_sk);
+ OPENSSL_cleanse(data, sizeof(cecpq1_data));
+ OPENSSL_free(data);
+}
+
+static int ssl_cecpq1_offer(SSL_ECDH_CTX *ctx, CBB *out) {
+ assert(ctx->data == NULL);
+ cecpq1_data *data = OPENSSL_malloc(sizeof(cecpq1_data));
+ if (data == NULL) {
+ OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
+ return 0;
+ }
+ ctx->data = data;
+ data->newhope_sk = NEWHOPE_POLY_new();
+ if (data->newhope_sk == NULL) {
+ OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
+ return 0;
+ }
+
+ uint8_t x25519_public_key[32];
+ X25519_keypair(x25519_public_key, data->x25519_key);
+
+ uint8_t newhope_offermsg[NEWHOPE_OFFERMSG_LENGTH];
+ NEWHOPE_offer(newhope_offermsg, data->newhope_sk);
+
+ if (!CBB_add_bytes(out, x25519_public_key, sizeof(x25519_public_key)) ||
+ !CBB_add_bytes(out, newhope_offermsg, sizeof(newhope_offermsg))) {
+ return 0;
+ }
+ return 1;
+}
+
+static int ssl_cecpq1_accept(SSL_ECDH_CTX *ctx, CBB *cbb, uint8_t **out_secret,
+ size_t *out_secret_len, uint8_t *out_alert,
+ const uint8_t *peer_key, size_t peer_key_len) {
+ if (peer_key_len != CECPQ1_OFFERMSG_LENGTH) {
+ *out_alert = SSL_AD_DECODE_ERROR;
+ return 0;
+ }
+
+ *out_alert = SSL_AD_INTERNAL_ERROR;
+
+ assert(ctx->data == NULL);
+ cecpq1_data *data = OPENSSL_malloc(sizeof(cecpq1_data));
+ if (data == NULL) {
+ OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
+ return 0;
+ }
+ data->newhope_sk = NULL;
+ ctx->data = data;
+
+ uint8_t *secret = OPENSSL_malloc(CECPQ1_SECRET_LENGTH);
+ if (secret == NULL) {
+ OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
+ return 0;
+ }
+
+ /* Generate message to server, and secret key, at once. */
+
+ uint8_t x25519_public_key[32];
+ X25519_keypair(x25519_public_key, data->x25519_key);
+ if (!X25519(secret, data->x25519_key, peer_key)) {
+ *out_alert = SSL_AD_DECODE_ERROR;
+ OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_ECPOINT);
+ goto err;
+ }
+
+ uint8_t newhope_acceptmsg[NEWHOPE_ACCEPTMSG_LENGTH];
+ if (!NEWHOPE_accept(secret + 32, newhope_acceptmsg, peer_key + 32,
+ NEWHOPE_OFFERMSG_LENGTH)) {
+ *out_alert = SSL_AD_DECODE_ERROR;
+ goto err;
+ }
+
+ if (!CBB_add_bytes(cbb, x25519_public_key, sizeof(x25519_public_key)) ||
+ !CBB_add_bytes(cbb, newhope_acceptmsg, sizeof(newhope_acceptmsg))) {
+ goto err;
+ }
+
+ *out_secret = secret;
+ *out_secret_len = CECPQ1_SECRET_LENGTH;
+ return 1;
+
+ err:
+ OPENSSL_cleanse(secret, CECPQ1_SECRET_LENGTH);
+ OPENSSL_free(secret);
+ return 0;
+}
+
+static int ssl_cecpq1_finish(SSL_ECDH_CTX *ctx, uint8_t **out_secret,
+ size_t *out_secret_len, uint8_t *out_alert,
+ const uint8_t *peer_key, size_t peer_key_len) {
+ if (peer_key_len != CECPQ1_ACCEPTMSG_LENGTH) {
+ *out_alert = SSL_AD_DECODE_ERROR;
+ return 0;
+ }
+
+ *out_alert = SSL_AD_INTERNAL_ERROR;
+
+ assert(ctx->data != NULL);
+ cecpq1_data *data = ctx->data;
+
+ uint8_t *secret = OPENSSL_malloc(CECPQ1_SECRET_LENGTH);
+ if (secret == NULL) {
+ OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
+ return 0;
+ }
+
+ if (!X25519(secret, data->x25519_key, peer_key)) {
+ *out_alert = SSL_AD_DECODE_ERROR;
+ OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_ECPOINT);
+ goto err;
+ }
+
+ if (!NEWHOPE_finish(secret + 32, data->newhope_sk, peer_key + 32,
+ NEWHOPE_ACCEPTMSG_LENGTH)) {
+ *out_alert = SSL_AD_DECODE_ERROR;
+ goto err;
+ }
+
+ *out_secret = secret;
+ *out_secret_len = CECPQ1_SECRET_LENGTH;
+ return 1;
+
+ err:
+ OPENSSL_cleanse(secret, CECPQ1_SECRET_LENGTH);
+ OPENSSL_free(secret);
+ return 0;
+}
+
+
/* Legacy DHE-based implementation. */
static void ssl_dhe_cleanup(SSL_ECDH_CTX *ctx) {
@@ -295,6 +444,8 @@
ssl_dhe_offer,
ssl_dhe_accept,
ssl_dhe_finish,
+ CBS_get_u16_length_prefixed,
+ CBB_add_u16_length_prefixed,
};
static const SSL_ECDH_METHOD kMethods[] = {
@@ -306,6 +457,8 @@
ssl_ec_point_offer,
ssl_ec_point_accept,
ssl_ec_point_finish,
+ CBS_get_u8_length_prefixed,
+ CBB_add_u8_length_prefixed,
},
{
NID_secp384r1,
@@ -315,6 +468,8 @@
ssl_ec_point_offer,
ssl_ec_point_accept,
ssl_ec_point_finish,
+ CBS_get_u8_length_prefixed,
+ CBB_add_u8_length_prefixed,
},
{
NID_secp521r1,
@@ -324,6 +479,8 @@
ssl_ec_point_offer,
ssl_ec_point_accept,
ssl_ec_point_finish,
+ CBS_get_u8_length_prefixed,
+ CBB_add_u8_length_prefixed,
},
{
NID_X25519,
@@ -333,6 +490,19 @@
ssl_x25519_offer,
ssl_x25519_accept,
ssl_x25519_finish,
+ CBS_get_u8_length_prefixed,
+ CBB_add_u8_length_prefixed,
+ },
+ {
+ NID_cecpq1,
+ SSL_CURVE_CECPQ1,
+ "CECPQ1",
+ ssl_cecpq1_cleanup,
+ ssl_cecpq1_offer,
+ ssl_cecpq1_accept,
+ ssl_cecpq1_finish,
+ CBS_get_u16_length_prefixed,
+ CBB_add_u16_length_prefixed,
},
};
@@ -392,6 +562,20 @@
ctx->data = params;
}
+int SSL_ECDH_CTX_get_key(SSL_ECDH_CTX *ctx, CBS *cbs, CBS *out) {
+ if (ctx->method == NULL) {
+ return 0;
+ }
+ return ctx->method->get_key(cbs, out);
+}
+
+int SSL_ECDH_CTX_add_key(SSL_ECDH_CTX *ctx, CBB *cbb, CBB *out_contents) {
+ if (ctx->method == NULL) {
+ return 0;
+ }
+ return ctx->method->add_key(cbb, out_contents);
+}
+
void SSL_ECDH_CTX_cleanup(SSL_ECDH_CTX *ctx) {
if (ctx->method == NULL) {
return;
diff --git a/ssl/test/bssl_shim.cc b/ssl/test/bssl_shim.cc
index c3ff33b..b676707 100644
--- a/ssl/test/bssl_shim.cc
+++ b/ssl/test/bssl_shim.cc
@@ -1310,7 +1310,7 @@
}
if (config->enable_all_curves) {
static const int kAllCurves[] = {
- NID_X9_62_prime256v1, NID_secp384r1, NID_secp521r1, NID_X25519,
+ NID_X9_62_prime256v1, NID_secp384r1, NID_secp521r1, NID_X25519, NID_cecpq1
};
if (!SSL_set1_curves(ssl.get(), kAllCurves,
sizeof(kAllCurves) / sizeof(kAllCurves[0]))) {
diff --git a/ssl/test/runner/common.go b/ssl/test/runner/common.go
index 57b7b29..3b085d3 100644
--- a/ssl/test/runner/common.go
+++ b/ssl/test/runner/common.go
@@ -103,6 +103,7 @@
CurveP384 CurveID = 24
CurveP521 CurveID = 25
CurveX25519 CurveID = 29
+ CurveCECPQ1 CurveID = 65165
)
// TLS Elliptic Curve Point Formats
diff --git a/ssl/test/runner/key_agreement.go b/ssl/test/runner/key_agreement.go
index f1d44f2..75998b4 100644
--- a/ssl/test/runner/key_agreement.go
+++ b/ssl/test/runner/key_agreement.go
@@ -21,6 +21,7 @@
"math/big"
"./curve25519"
+ "./newhope"
)
var errClientKeyExchange = errors.New("tls: invalid ClientKeyExchange message")
@@ -348,6 +349,66 @@
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:
@@ -360,6 +421,8 @@
return &ellipticECDHCurve{curve: elliptic.P521()}, true
case CurveX25519:
return &x25519ECDHCurve{}, true
+ case CurveCECPQ1:
+ return &cecpq1Curve{}, true
default:
return nil, false
}
@@ -584,15 +647,19 @@
}
// http://tools.ietf.org/html/rfc4492#section-5.4
- serverECDHParams := make([]byte, 1+2+1+len(publicKey))
- serverECDHParams[0] = 3 // named curve
- serverECDHParams[1] = byte(curveid >> 8)
- serverECDHParams[2] = byte(curveid)
+ var serverECDHParams []byte
+ serverECDHParams = append(serverECDHParams, byte(3)) // named curve
+ serverECDHParams = append(serverECDHParams, byte(curveid>>8))
+ serverECDHParams = append(serverECDHParams, byte(curveid))
if config.Bugs.InvalidSKXCurve {
serverECDHParams[2] ^= 0xff
}
- serverECDHParams[3] = byte(len(publicKey))
- copy(serverECDHParams[4:], publicKey)
+ if curveid == CurveCECPQ1 {
+ // The larger key size requires an extra length byte.
+ serverECDHParams = append(serverECDHParams, byte(len(publicKey)>>8))
+ }
+ serverECDHParams = append(serverECDHParams, byte(len(publicKey)&0xff))
+ serverECDHParams = append(serverECDHParams, publicKey[:]...)
if config.Bugs.InvalidECDHPoint {
serverECDHParams[4] ^= 0xff
}
@@ -601,10 +668,21 @@
}
func (ka *ecdheKeyAgreement) processClientKeyExchange(config *Config, cert *Certificate, ckx *clientKeyExchangeMsg, version uint16) ([]byte, error) {
- if len(ckx.ciphertext) == 0 || int(ckx.ciphertext[0]) != len(ckx.ciphertext)-1 {
+ if len(ckx.ciphertext) == 0 {
return nil, errClientKeyExchange
}
- return ka.curve.finish(ckx.ciphertext[1:])
+ peerKeyLen := int(ckx.ciphertext[0])
+ offset := 1
+ if _, postQuantum := ka.curve.(*cecpq1Curve); postQuantum {
+ // The larger key size requires an extra length byte.
+ peerKeyLen = int(ckx.ciphertext[0])<<8 + int(ckx.ciphertext[1])
+ offset = 2
+ }
+ peerKey := ckx.ciphertext[offset:]
+ if peerKeyLen != len(peerKey) {
+ return nil, errClientKeyExchange
+ }
+ return ka.curve.finish(peerKey)
}
func (ka *ecdheKeyAgreement) processServerKeyExchange(config *Config, clientHello *clientHelloMsg, serverHello *serverHelloMsg, cert *x509.Certificate, skx *serverKeyExchangeMsg) error {
@@ -622,15 +700,22 @@
}
publicLen := int(skx.key[3])
- if publicLen+4 > len(skx.key) {
+ publicOffset := 4
+ if curveid == CurveCECPQ1 {
+ // The larger key size requires an extra length byte.
+ publicLen = int(skx.key[3])<<8 + int(skx.key[4])
+ publicOffset += 1
+ }
+
+ if publicLen+publicOffset > len(skx.key) {
return errServerKeyExchange
}
// Save the peer key for later.
- ka.peerKey = skx.key[4 : 4+publicLen]
+ ka.peerKey = skx.key[publicOffset : publicOffset+publicLen]
// Check the signature.
- serverECDHParams := skx.key[:4+publicLen]
- sig := skx.key[4+publicLen:]
+ serverECDHParams := skx.key[:publicOffset+publicLen]
+ sig := skx.key[publicOffset+publicLen:]
return ka.auth.verifyParameters(config, clientHello, serverHello, cert, serverECDHParams, sig)
}
@@ -645,12 +730,15 @@
}
ckx := new(clientKeyExchangeMsg)
- 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
+ if _, postQuantum := ka.curve.(*cecpq1Curve); postQuantum {
+ // The larger key size requires an extra length byte.
+ ckx.ciphertext = append(ckx.ciphertext, byte(len(publicKey)>>8))
}
+ ckx.ciphertext = append(ckx.ciphertext, byte(len(publicKey)&0xff))
+ if config.Bugs.InvalidECDHPoint {
+ publicKey[0] ^= 0xff
+ }
+ ckx.ciphertext = append(ckx.ciphertext, publicKey[:]...)
return preMasterSecret, ckx, nil
}
diff --git a/ssl/test/runner/runner.go b/ssl/test/runner/runner.go
index eb1efdf..004709b 100644
--- a/ssl/test/runner/runner.go
+++ b/ssl/test/runner/runner.go
@@ -5006,6 +5006,7 @@
{"P-384", CurveP384},
{"P-521", CurveP521},
{"X25519", CurveX25519},
+ {"CECPQ1", CurveCECPQ1},
}
func addCurveTests() {