Implement ChannelID for TLS 1.3.
Channel ID for TLS 1.3 uses the same digest construction as
CertificateVerify. This message is signed with the Channel ID key and
put in the same handshake message (with the same format) as in TLS 1.2.
BUG=103
Change-Id: Ia5b2dffe5a39c39db0cecb0aa6bdc328e53accc2
Reviewed-on: https://boringssl-review.googlesource.com/11420
Reviewed-by: David Benjamin <davidben@google.com>
Commit-Queue: David Benjamin <davidben@google.com>
CQ-Verified: CQ bot account: commit-bot@chromium.org <commit-bot@chromium.org>
diff --git a/ssl/t1_lib.c b/ssl/t1_lib.c
index 9655b83..05e1f23 100644
--- a/ssl/t1_lib.c
+++ b/ssl/t1_lib.c
@@ -1633,10 +1633,6 @@
return 1;
}
- if (ssl3_protocol_version(ssl) >= TLS1_3_VERSION) {
- return 0;
- }
-
assert(!SSL_is_dtls(ssl));
assert(ssl->tlsext_channel_id_enabled);
@@ -1665,10 +1661,6 @@
}
static int ext_channel_id_add_serverhello(SSL *ssl, CBB *out) {
- if (ssl3_protocol_version(ssl) >= TLS1_3_VERSION) {
- return 1;
- }
-
if (!ssl->s3->tlsext_channel_id_valid) {
return 1;
}
@@ -3207,7 +3199,157 @@
return 0;
}
+int tls1_verify_channel_id(SSL *ssl) {
+ int ret = 0;
+ uint16_t extension_type;
+ CBS extension, channel_id;
+
+ /* A Channel ID handshake message is structured to contain multiple
+ * extensions, but the only one that can be present is Channel ID. */
+ CBS_init(&channel_id, ssl->init_msg, ssl->init_num);
+ if (!CBS_get_u16(&channel_id, &extension_type) ||
+ !CBS_get_u16_length_prefixed(&channel_id, &extension) ||
+ CBS_len(&channel_id) != 0 ||
+ extension_type != TLSEXT_TYPE_channel_id ||
+ CBS_len(&extension) != TLSEXT_CHANNEL_ID_SIZE) {
+ OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
+ ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+ return 0;
+ }
+
+ EC_GROUP *p256 = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1);
+ if (!p256) {
+ OPENSSL_PUT_ERROR(SSL, SSL_R_NO_P256_SUPPORT);
+ return 0;
+ }
+
+ EC_KEY *key = NULL;
+ EC_POINT *point = NULL;
+ BIGNUM x, y;
+ ECDSA_SIG sig;
+ BN_init(&x);
+ BN_init(&y);
+ sig.r = BN_new();
+ sig.s = BN_new();
+ if (sig.r == NULL || sig.s == NULL) {
+ goto err;
+ }
+
+ const uint8_t *p = CBS_data(&extension);
+ if (BN_bin2bn(p + 0, 32, &x) == NULL ||
+ BN_bin2bn(p + 32, 32, &y) == NULL ||
+ BN_bin2bn(p + 64, 32, sig.r) == NULL ||
+ BN_bin2bn(p + 96, 32, sig.s) == NULL) {
+ goto err;
+ }
+
+ point = EC_POINT_new(p256);
+ if (point == NULL ||
+ !EC_POINT_set_affine_coordinates_GFp(p256, point, &x, &y, NULL)) {
+ goto err;
+ }
+
+ key = EC_KEY_new();
+ if (key == NULL ||
+ !EC_KEY_set_group(key, p256) ||
+ !EC_KEY_set_public_key(key, point)) {
+ goto err;
+ }
+
+ uint8_t digest[EVP_MAX_MD_SIZE];
+ size_t digest_len;
+ if (!tls1_channel_id_hash(ssl, digest, &digest_len)) {
+ goto err;
+ }
+
+ int sig_ok = ECDSA_do_verify(digest, digest_len, &sig, key);
+#if defined(BORINGSSL_UNSAFE_FUZZER_MODE)
+ sig_ok = 1;
+#endif
+ if (!sig_ok) {
+ OPENSSL_PUT_ERROR(SSL, SSL_R_CHANNEL_ID_SIGNATURE_INVALID);
+ ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECRYPT_ERROR);
+ ssl->s3->tlsext_channel_id_valid = 0;
+ goto err;
+ }
+
+ memcpy(ssl->s3->tlsext_channel_id, p, 64);
+ ret = 1;
+
+err:
+ BN_free(&x);
+ BN_free(&y);
+ BN_free(sig.r);
+ BN_free(sig.s);
+ EC_KEY_free(key);
+ EC_POINT_free(point);
+ EC_GROUP_free(p256);
+ return ret;
+}
+
+int tls1_write_channel_id(SSL *ssl, CBB *cbb) {
+ uint8_t digest[EVP_MAX_MD_SIZE];
+ size_t digest_len;
+ if (!tls1_channel_id_hash(ssl, digest, &digest_len)) {
+ return 0;
+ }
+
+ EC_KEY *ec_key = EVP_PKEY_get0_EC_KEY(ssl->tlsext_channel_id_private);
+ if (ec_key == NULL) {
+ OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+ return 0;
+ }
+
+ int ret = 0;
+ BIGNUM *x = BN_new();
+ BIGNUM *y = BN_new();
+ ECDSA_SIG *sig = NULL;
+ if (x == NULL || y == NULL ||
+ !EC_POINT_get_affine_coordinates_GFp(EC_KEY_get0_group(ec_key),
+ EC_KEY_get0_public_key(ec_key),
+ x, y, NULL)) {
+ goto err;
+ }
+
+ sig = ECDSA_do_sign(digest, digest_len, ec_key);
+ if (sig == NULL) {
+ goto err;
+ }
+
+ CBB child;
+ if (!CBB_add_u16(cbb, TLSEXT_TYPE_channel_id) ||
+ !CBB_add_u16_length_prefixed(cbb, &child) ||
+ !BN_bn2cbb_padded(&child, 32, x) ||
+ !BN_bn2cbb_padded(&child, 32, y) ||
+ !BN_bn2cbb_padded(&child, 32, sig->r) ||
+ !BN_bn2cbb_padded(&child, 32, sig->s) ||
+ !CBB_flush(cbb)) {
+ goto err;
+ }
+
+ ret = 1;
+
+err:
+ BN_free(x);
+ BN_free(y);
+ ECDSA_SIG_free(sig);
+ return ret;
+}
+
int tls1_channel_id_hash(SSL *ssl, uint8_t *out, size_t *out_len) {
+ if (ssl3_protocol_version(ssl) >= TLS1_3_VERSION) {
+ uint8_t *msg;
+ size_t msg_len;
+ if (!tls13_get_cert_verify_signature_input(ssl, &msg, &msg_len,
+ ssl_cert_verify_channel_id)) {
+ return 0;
+ }
+ SHA256(msg, msg_len, out);
+ *out_len = SHA256_DIGEST_LENGTH;
+ OPENSSL_free(msg);
+ return 1;
+ }
+
int ret = 0;
EVP_MD_CTX ctx;
@@ -3272,3 +3414,21 @@
return 1;
}
+
+int ssl_do_channel_id_callback(SSL *ssl) {
+ if (ssl->tlsext_channel_id_private != NULL ||
+ ssl->ctx->channel_id_cb == NULL) {
+ return 1;
+ }
+
+ EVP_PKEY *key = NULL;
+ ssl->ctx->channel_id_cb(ssl, &key);
+ if (key == NULL) {
+ /* The caller should try again later. */
+ return 1;
+ }
+
+ int ret = SSL_set1_tls_channel_id(ssl, key);
+ EVP_PKEY_free(key);
+ return ret;
+}