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>
This commit is contained in:
parent
61d4cdc03d
commit
e25775bcac
@ -946,3 +946,4 @@ dhSinglePass_cofactorDH_sha512kdf_scheme 945
|
||||
dh_std_kdf 946
|
||||
dh_cofactor_kdf 947
|
||||
X25519 948
|
||||
cecpq1 949
|
||||
|
@ -1333,3 +1333,6 @@ secg-scheme 14 3 : dhSinglePass-cofactorDH-sha512kdf-scheme
|
||||
|
||||
# NID for X25519 (no corresponding OID).
|
||||
: X25519
|
||||
|
||||
# NID for CECPQ1 (no corresponding OID)
|
||||
: cecpq1
|
||||
|
@ -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 */
|
||||
|
@ -533,6 +533,7 @@ int ssl3_update_handshake_hash(SSL *ssl, const uint8_t *in, size_t in_len);
|
||||
#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 @@ struct ssl_ecdh_method_st {
|
||||
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 @@ void SSL_ECDH_CTX_init_for_dhe(SSL_ECDH_CTX *ctx, DH *params);
|
||||
* 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);
|
||||
|
||||
|
@ -1142,8 +1142,7 @@ int ssl3_get_server_key_exchange(SSL *ssl) {
|
||||
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 @@ int ssl3_get_server_key_exchange(SSL *ssl) {
|
||||
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 @@ int ssl3_send_client_key_exchange(SSL *ssl) {
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -1209,7 +1209,7 @@ int ssl3_send_server_key_exchange(SSL *ssl) {
|
||||
!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 @@ int ssl3_send_server_key_exchange(SSL *ssl) {
|
||||
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 @@ int ssl3_get_client_key_exchange(SSL *ssl) {
|
||||
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;
|
||||
|
184
ssl/ssl_ecdh.c
184
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 @@ static int ssl_x25519_accept(SSL_ECDH_CTX *ctx, CBB *out_public_key,
|
||||
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 @@ static const SSL_ECDH_METHOD kDHEMethod = {
|
||||
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 @@ static const SSL_ECDH_METHOD kMethods[] = {
|
||||
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 @@ static const SSL_ECDH_METHOD kMethods[] = {
|
||||
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 @@ static const SSL_ECDH_METHOD kMethods[] = {
|
||||
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 @@ static const SSL_ECDH_METHOD kMethods[] = {
|
||||
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 @@ void SSL_ECDH_CTX_init_for_dhe(SSL_ECDH_CTX *ctx, DH *params) {
|
||||
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;
|
||||
|
@ -1310,7 +1310,7 @@ static bool DoExchange(ScopedSSL_SESSION *out_session, SSL_CTX *ssl_ctx,
|
||||
}
|
||||
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]))) {
|
||||
|
@ -103,6 +103,7 @@ const (
|
||||
CurveP384 CurveID = 24
|
||||
CurveP521 CurveID = 25
|
||||
CurveX25519 CurveID = 29
|
||||
CurveCECPQ1 CurveID = 65165
|
||||
)
|
||||
|
||||
// TLS Elliptic Curve Point Formats
|
||||
|
@ -21,6 +21,7 @@ import (
|
||||
"math/big"
|
||||
|
||||
"./curve25519"
|
||||
"./newhope"
|
||||
)
|
||||
|
||||
var errClientKeyExchange = errors.New("tls: invalid ClientKeyExchange message")
|
||||
@ -348,6 +349,66 @@ func (e *x25519ECDHCurve) finish(peerKey []byte) (preMasterSecret []byte, err er
|
||||
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 @@ func curveForCurveID(id CurveID) (ecdhCurve, bool) {
|
||||
return &ellipticECDHCurve{curve: elliptic.P521()}, true
|
||||
case CurveX25519:
|
||||
return &x25519ECDHCurve{}, true
|
||||
case CurveCECPQ1:
|
||||
return &cecpq1Curve{}, true
|
||||
default:
|
||||
return nil, false
|
||||
}
|
||||
@ -584,15 +647,19 @@ NextCandidate:
|
||||
}
|
||||
|
||||
// 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 @@ NextCandidate:
|
||||
}
|
||||
|
||||
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 @@ func (ka *ecdheKeyAgreement) processServerKeyExchange(config *Config, clientHell
|
||||
}
|
||||
|
||||
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 @@ func (ka *ecdheKeyAgreement) generateClientKeyExchange(config *Config, clientHel
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
@ -5006,6 +5006,7 @@ var testCurves = []struct {
|
||||
{"P-384", CurveP384},
|
||||
{"P-521", CurveP521},
|
||||
{"X25519", CurveX25519},
|
||||
{"CECPQ1", CurveCECPQ1},
|
||||
}
|
||||
|
||||
func addCurveTests() {
|
||||
|
Loading…
Reference in New Issue
Block a user