diff --git a/src/include/openssl/ssl.h b/src/include/openssl/ssl.h index 53aa9b453..87309c3e1 100644 --- a/src/include/openssl/ssl.h +++ b/src/include/openssl/ssl.h @@ -138,6 +138,25 @@ * OTHER ENTITY BASED ON INFRINGEMENT OF INTELLECTUAL PROPERTY RIGHTS OR * OTHERWISE. */ +/* ==================================================================== + * Copyright 2020 Apple Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the “Software”), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following conditions: + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ #ifndef OPENSSL_HEADER_SSL_H #define OPENSSL_HEADER_SSL_H @@ -1136,6 +1155,16 @@ OPENSSL_EXPORT int SSL_CTX_set_chain_and_key( SSL_CTX *ctx, CRYPTO_BUFFER *const *certs, size_t num_certs, EVP_PKEY *privkey, const SSL_PRIVATE_KEY_METHOD *privkey_method); +// SSL_CTX_set_nullchain_and_key sets the private key for a +// TLS client or server. Reference to the given |EVP_PKEY| +// object is added as needed. Exactly one of |privkey| or |privkey_method| +// may be non-NULL. Returns one on success and zero on error. +// Note the lack of a corresponding public-key certificate. +// See SSL_CTX_set_server_raw_public_key_certificate. +OPENSSL_EXPORT int SSL_CTX_set_nullchain_and_key( + SSL_CTX *ctx, + EVP_PKEY *privkey, const SSL_PRIVATE_KEY_METHOD *privkey_method); + // SSL_set_chain_and_key sets the certificate chain and private key for a TLS // client or server. References to the given |CRYPTO_BUFFER| and |EVP_PKEY| // objects are added as needed. Exactly one of |privkey| or |privkey_method| @@ -1144,6 +1173,16 @@ OPENSSL_EXPORT int SSL_set_chain_and_key( SSL *ssl, CRYPTO_BUFFER *const *certs, size_t num_certs, EVP_PKEY *privkey, const SSL_PRIVATE_KEY_METHOD *privkey_method); +// SSL_set_nullchain_and_key sets the private key for a TLS +// client or server. Reference to the given |EVP_PKEY| +// object is added as needed. Exactly one of |privkey| or |privkey_method| +// may be non-NULL. Returns one on success and zero on error. +// Note the lack of a corresponding public-key certificate. +// See SSL_set_server_raw_public_key_certificate. +OPENSSL_EXPORT int SSL_set_nullchain_and_key( + SSL *ssl, EVP_PKEY *privkey, + const SSL_PRIVATE_KEY_METHOD *privkey_method); + // SSL_CTX_get0_chain returns the list of |CRYPTO_BUFFER|s that were set by // |SSL_CTX_set_chain_and_key|. Reference counts are not incremented by this // call. The return value may be |NULL| if no chain has been set. @@ -3023,6 +3062,21 @@ OPENSSL_EXPORT void SSL_get0_peer_application_settings(const SSL *ssl, OPENSSL_EXPORT int SSL_has_application_settings(const SSL *ssl); +// Server Certificate Type. + +#define TLSEXT_CERTIFICATETYPE_X509 0 +#define TLSEXT_CERTIFICATETYPE_RAW_PUBLIC_KEY 2 + +OPENSSL_EXPORT int SSL_CTX_set_server_raw_public_key_certificate( + SSL_CTX *ctx, const uint8_t *raw_public_key, unsigned raw_public_key_len); + +OPENSSL_EXPORT int SSL_CTX_has_server_raw_public_key_certificate(SSL_CTX *ctx); + +OPENSSL_EXPORT int SSL_set_server_raw_public_key_certificate( + SSL *ssl, const uint8_t *raw_public_key, unsigned raw_public_key_len); + +OPENSSL_EXPORT int SSL_has_server_raw_public_key_certificate(SSL *ssl); + // Certificate compression. // // Certificates in TLS 1.3 can be compressed (RFC 8879). BoringSSL supports this diff --git a/src/include/openssl/tls1.h b/src/include/openssl/tls1.h index 772fb87a3..be605c1aa 100644 --- a/src/include/openssl/tls1.h +++ b/src/include/openssl/tls1.h @@ -146,6 +146,25 @@ * OTHER ENTITY BASED ON INFRINGEMENT OF INTELLECTUAL PROPERTY RIGHTS OR * OTHERWISE. */ +/* ==================================================================== + * Copyright 2020 Apple Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the “Software”), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following conditions: + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ #ifndef OPENSSL_HEADER_TLS1_H #define OPENSSL_HEADER_TLS1_H @@ -197,6 +216,9 @@ extern "C" { // ExtensionType value from RFC 7301 #define TLSEXT_TYPE_application_layer_protocol_negotiation 16 +// ExtensionType value from RFC 7250 +#define TLSEXT_TYPE_server_certificate_type 20 + // ExtensionType value from RFC 7685 #define TLSEXT_TYPE_padding 21 diff --git a/src/ssl/extensions.cc b/src/ssl/extensions.cc index 5ee280221..2692e5478 100644 --- a/src/ssl/extensions.cc +++ b/src/ssl/extensions.cc @@ -105,6 +105,25 @@ * This product includes cryptographic software written by Eric Young * (eay@cryptsoft.com). This product includes software written by Tim * Hudson (tjh@cryptsoft.com). */ +/* ==================================================================== + * Copyright 2020 Apple Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the “Software”), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following conditions: + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ #include @@ -3094,6 +3113,146 @@ bool ssl_negotiate_alps(SSL_HANDSHAKE *hs, uint8_t *out_alert, return true; } +// Server Certificate Type + +static bool ext_server_certificate_type_add_clienthello(const SSL_HANDSHAKE *hs, + CBB *out, + CBB *out_compressible, + ssl_client_hello_type_t type) { + + if (hs->max_version <= TLS1_2_VERSION) { + return true; + } + + if (hs->config->server_certificate_type_list.empty()) { + return true; + } + + CBB contents, server_certificate_types; + if (!CBB_add_u16(out, TLSEXT_TYPE_server_certificate_type) || + !CBB_add_u16_length_prefixed(out, &contents) || + !CBB_add_u8_length_prefixed(&contents, &server_certificate_types) || + !CBB_add_bytes(&server_certificate_types, + hs->config->server_certificate_type_list.data(), + hs->config->server_certificate_type_list.size()) || + !CBB_flush(out)) { + return false; + } + + return true; +} + +static bool ssl_is_certificate_type_allowed(CBS *certificate_type_list, + uint8_t certificate_type) +{ + uint8_t supported_certificate_type; + while (CBS_len(certificate_type_list) > 0) { + if (!CBS_get_u8(certificate_type_list, + &supported_certificate_type)) { + break; + } + + if (supported_certificate_type != certificate_type) { + continue; + } + + return true; + } + + return false; +} + +static bool ext_server_certificate_type_parse_serverhello(SSL_HANDSHAKE *hs, + uint8_t *out_alert, + CBS *content) +{ + if (hs->max_version <= TLS1_2_VERSION || + hs->config->server_certificate_type_list.empty()) { + return true; + } + + // Strict + if (!content) { + OPENSSL_PUT_ERROR(SSL, SSL_R_UNKNOWN_CERTIFICATE_TYPE); + *out_alert = SSL_AD_ILLEGAL_PARAMETER; + return false; + } + + CBS certificate_type_list = + MakeConstSpan(hs->config->server_certificate_type_list); + + uint8_t certificate_type; + if (CBS_get_u8(content, &certificate_type) && + ssl_is_certificate_type_allowed(&certificate_type_list, + certificate_type)) { + hs->server_certificate_type = certificate_type; + hs->server_certificate_type_negotiated = 1; + return true; + } + + OPENSSL_PUT_ERROR(SSL, SSL_R_UNKNOWN_CERTIFICATE_TYPE); + *out_alert = SSL_AD_ILLEGAL_PARAMETER; + return false; +} + +static bool ext_server_certificate_type_parse_clienthello(SSL_HANDSHAKE *hs, + uint8_t *out_alert, + CBS *content) +{ + if (!content) { + return true; + } + + if (hs->max_version <= TLS1_2_VERSION || + hs->config->server_certificate_type_list.empty()) { + return true; + } + + CBS certificate_type_list = + MakeConstSpan(hs->config->server_certificate_type_list); + + CBS type_list; + if (!CBS_get_u8_length_prefixed(content, &type_list)) { + type_list.len = 0; + } + + uint8_t type; + while(CBS_len(&type_list) > 0) { + if (!CBS_get_u8(&type_list, &type)) { + break; + } + + if (!ssl_is_certificate_type_allowed(&certificate_type_list, type)) { + continue; + } + + hs->server_certificate_type = type; + hs->server_certificate_type_negotiated = 1; + return true; + } + + *out_alert = SSL_AD_ILLEGAL_PARAMETER; + return false; +} + +static bool ext_server_certificate_type_add_serverhello(SSL_HANDSHAKE *hs, + CBB *out) +{ + if (!hs->server_certificate_type_negotiated) { + return true; + } + + CBB contents; + if (!CBB_add_u16(out, TLSEXT_TYPE_server_certificate_type) || + !CBB_add_u16_length_prefixed(out, &contents) || + !CBB_add_u8(&contents, hs->server_certificate_type) || + !CBB_flush(out)) { + return false; + } + + return true; +} + // kExtensions contains all the supported extensions. static const struct tls_extension kExtensions[] = { { @@ -3267,6 +3426,13 @@ static const struct tls_extension kExtensions[] = { ignore_parse_clienthello, ext_alps_add_serverhello, }, + { + TLSEXT_TYPE_server_certificate_type, + ext_server_certificate_type_add_clienthello, + ext_server_certificate_type_parse_serverhello, + ext_server_certificate_type_parse_clienthello, + ext_server_certificate_type_add_serverhello, + }, }; #define kNumExtensions (sizeof(kExtensions) / sizeof(struct tls_extension)) diff --git a/src/ssl/handshake.cc b/src/ssl/handshake.cc index 8d5a23872..b9ac70dfe 100644 --- a/src/ssl/handshake.cc +++ b/src/ssl/handshake.cc @@ -109,6 +109,25 @@ * Copyright 2002 Sun Microsystems, Inc. ALL RIGHTS RESERVED. * ECC cipher suite support in OpenSSL originally developed by * SUN MICROSYSTEMS, INC., and contributed to the OpenSSL project. */ +/* ==================================================================== + * Copyright 2020 Apple Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the “Software”), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following conditions: + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ #include @@ -150,6 +169,7 @@ SSL_HANDSHAKE::SSL_HANDSHAKE(SSL *ssl_arg) cert_compression_negotiated(false), + server_certificate_type_negotiated(false), apply_jdk11_workaround(false), can_release_private_key(false), channel_id_negotiated(false) { assert(ssl); @@ -365,7 +385,21 @@ enum ssl_verify_result_t ssl_verify_peer_cert(SSL_HANDSHAKE *hs) { uint8_t alert = SSL_AD_CERTIFICATE_UNKNOWN; enum ssl_verify_result_t ret; - if (hs->config->custom_verify_callback != nullptr) { + if (hs->server_certificate_type_negotiated && + hs->server_certificate_type == TLSEXT_CERTIFICATETYPE_RAW_PUBLIC_KEY) { + ret = ssl_verify_invalid; + EVP_PKEY *peer_pubkey = hs->peer_pubkey.get(); + CBS spki = MakeConstSpan(ssl->config->server_raw_public_key_certificate); + EVP_PKEY *pubkey = EVP_parse_public_key(&spki); + if (!pubkey) { + OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR); + alert = SSL_AD_INTERNAL_ERROR; + } else if (EVP_PKEY_cmp(peer_pubkey, pubkey) == 1 /* Equal */) { + ret = ssl_verify_ok; + } else { + alert = SSL_AD_BAD_CERTIFICATE; + } + } else if (hs->config->custom_verify_callback != nullptr) { ret = hs->config->custom_verify_callback(ssl, &alert); switch (ret) { case ssl_verify_ok: diff --git a/src/ssl/internal.h b/src/ssl/internal.h index 1e6da2153..f04888384 100644 --- a/src/ssl/internal.h +++ b/src/ssl/internal.h @@ -138,6 +138,25 @@ * OTHER ENTITY BASED ON INFRINGEMENT OF INTELLECTUAL PROPERTY RIGHTS OR * OTHERWISE. */ +/* ==================================================================== + * Copyright 2020 Apple Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the “Software”), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following conditions: + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ #ifndef OPENSSL_HEADER_SSL_INTERNAL_H #define OPENSSL_HEADER_SSL_INTERNAL_H @@ -1286,6 +1305,8 @@ int ssl_write_buffer_flush(SSL *ssl); // configured. bool ssl_has_certificate(const SSL_HANDSHAKE *hs); +bool ssl_has_raw_public_key_certificate(const SSL_HANDSHAKE *hs); + // ssl_parse_cert_chain parses a certificate list from |cbs| in the format used // by a TLS Certificate message. On success, it advances |cbs| and returns // true. Otherwise, it returns false and sets |*out_alert| to an alert to send @@ -1887,6 +1908,8 @@ struct SSL_HANDSHAKE { // |cert_compression_negotiated| is true. uint16_t cert_compression_alg_id; + uint8_t server_certificate_type; + // ech_hpke_ctx is the HPKE context used in ECH. On the server, it is // initialized if |ech_status| is |ssl_ech_accepted|. On the client, it is // initialized if |selected_ech_config| is not nullptr. @@ -2037,6 +2060,8 @@ struct SSL_HANDSHAKE { // cert_compression_negotiated is true iff |cert_compression_alg_id| is valid. bool cert_compression_negotiated : 1; + bool server_certificate_type_negotiated : 1; + // apply_jdk11_workaround is true if the peer is probably a JDK 11 client // which implemented TLS 1.3 incorrectly. bool apply_jdk11_workaround : 1; @@ -3049,6 +3074,9 @@ struct SSL_CONFIG { // along with their corresponding ALPS values. GrowableArray alps_configs; + Array server_certificate_type_list; + Array server_raw_public_key_certificate; + // Contains the QUIC transport params that this endpoint will send. Array quic_transport_params; @@ -3648,6 +3676,9 @@ struct ssl_ctx_st { // format. bssl::Array alpn_client_proto_list; + bssl::Array server_certificate_type_list; + bssl::Array server_raw_public_key_certificate; + // SRTP profiles we are willing to do from RFC 5764 bssl::UniquePtr srtp_profiles; diff --git a/src/ssl/ssl_cert.cc b/src/ssl/ssl_cert.cc index aa46a8bb6..d90840fce 100644 --- a/src/ssl/ssl_cert.cc +++ b/src/ssl/ssl_cert.cc @@ -111,6 +111,25 @@ * Copyright 2002 Sun Microsystems, Inc. ALL RIGHTS RESERVED. * ECC cipher suite support in OpenSSL originally developed by * SUN MICROSYSTEMS, INC., and contributed to the OpenSSL project. */ +/* ==================================================================== + * Copyright 2020 Apple Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the “Software”), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following conditions: + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ #include @@ -302,6 +321,25 @@ static int cert_set_chain_and_key( return 1; } +static int cert_set_key( + CERT *cert, + EVP_PKEY *privkey, const SSL_PRIVATE_KEY_METHOD *privkey_method) { + if (privkey == NULL && privkey_method == NULL) { + OPENSSL_PUT_ERROR(SSL, ERR_R_PASSED_NULL_PARAMETER); + return 0; + } + + if (privkey != NULL && privkey_method != NULL) { + OPENSSL_PUT_ERROR(SSL, SSL_R_CANNOT_HAVE_BOTH_PRIVKEY_AND_METHOD); + return 0; + } + + cert->privatekey = UpRef(privkey); + cert->key_method = privkey_method; + + return 1; +} + bool ssl_set_cert(CERT *cert, UniquePtr buffer) { switch (check_leaf_cert_and_privkey(buffer.get(), cert->privatekey.get())) { case leaf_cert_and_privkey_error: @@ -343,6 +381,12 @@ bool ssl_has_certificate(const SSL_HANDSHAKE *hs) { ssl_has_private_key(hs); } +bool ssl_has_raw_public_key_certificate(const SSL_HANDSHAKE *hs) { + return hs->server_certificate_type_negotiated && + hs->server_certificate_type == TLSEXT_CERTIFICATETYPE_RAW_PUBLIC_KEY && + ssl_has_private_key(hs); +} + bool ssl_parse_cert_chain(uint8_t *out_alert, UniquePtr *out_chain, UniquePtr *out_pubkey, @@ -721,11 +765,20 @@ bool ssl_check_leaf_certificate(SSL_HANDSHAKE *hs, EVP_PKEY *pkey, bool ssl_on_certificate_selected(SSL_HANDSHAKE *hs) { SSL *const ssl = hs->ssl; - if (!ssl_has_certificate(hs)) { + if (!ssl_has_certificate(hs) && + !ssl_has_raw_public_key_certificate(hs)) { // Nothing to do. return true; } + if (ssl_has_raw_public_key_certificate(hs)) { + CBS spki = MakeConstSpan( + ssl->config->server_raw_public_key_certificate.data(), + ssl->config->server_raw_public_key_certificate.size()); + hs->local_pubkey = UniquePtr(EVP_parse_public_key(&spki)); + return hs->local_pubkey != NULL; + } + if (!ssl->ctx->x509_method->ssl_auto_chain_if_needed(hs)) { return false; } @@ -880,6 +933,15 @@ int SSL_set_chain_and_key(SSL *ssl, CRYPTO_BUFFER *const *certs, privkey, privkey_method); } +int SSL_set_nullchain_and_key(SSL *ssl, + EVP_PKEY *privkey, + const SSL_PRIVATE_KEY_METHOD *privkey_method) { + if (!ssl->config) { + return 0; + } + return cert_set_key(ssl->config->cert.get(), privkey, privkey_method); +} + int SSL_CTX_set_chain_and_key(SSL_CTX *ctx, CRYPTO_BUFFER *const *certs, size_t num_certs, EVP_PKEY *privkey, const SSL_PRIVATE_KEY_METHOD *privkey_method) { @@ -887,6 +949,12 @@ int SSL_CTX_set_chain_and_key(SSL_CTX *ctx, CRYPTO_BUFFER *const *certs, privkey_method); } +int SSL_CTX_set_nullchain_and_key(SSL_CTX *ctx, + EVP_PKEY *privkey, + const SSL_PRIVATE_KEY_METHOD *privkey_method) { + return cert_set_key(ctx->cert.get(), privkey, privkey_method); +} + const STACK_OF(CRYPTO_BUFFER)* SSL_CTX_get0_chain(const SSL_CTX *ctx) { return ctx->cert->chain.get(); } diff --git a/src/ssl/ssl_lib.cc b/src/ssl/ssl_lib.cc index 838761af5..e4f1a12b7 100644 --- a/src/ssl/ssl_lib.cc +++ b/src/ssl/ssl_lib.cc @@ -137,6 +137,25 @@ * SPECIFICALLY DISCLAIMS ANY LIABILITY FOR CLAIMS BROUGHT BY YOU OR ANY * OTHER ENTITY BASED ON INFRINGEMENT OF INTELLECTUAL PROPERTY RIGHTS OR * OTHERWISE. */ +/* ==================================================================== + * Copyright 2020 Apple Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the “Software”), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following conditions: + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ #include @@ -687,6 +706,11 @@ SSL *SSL_new(SSL_CTX *ctx) { ssl->config->handoff = ctx->handoff; ssl->quic_method = ctx->quic_method; + ssl->config->server_certificate_type_list.CopyFrom( + ctx->server_certificate_type_list); + ssl->config->server_raw_public_key_certificate.CopyFrom( + ctx->server_raw_public_key_certificate); + if (!ssl->method->ssl_new(ssl.get()) || !ssl->ctx->x509_method->ssl_new(ssl->s3->hs.get())) { return nullptr; @@ -3140,6 +3164,53 @@ int SSL_CTX_set_tlsext_status_arg(SSL_CTX *ctx, void *arg) { return 1; } +int SSL_CTX_set_server_raw_public_key_certificate(SSL_CTX *ctx, + const uint8_t *raw_public_key, unsigned raw_public_key_len) { + if (!ctx->server_raw_public_key_certificate.CopyFrom( + MakeConstSpan(raw_public_key, raw_public_key_len))) { + return 0; /* Failure */ + } + + if (!ctx->server_certificate_type_list.Init(1)) { + return 0; + } + ctx->server_certificate_type_list[0] = TLSEXT_CERTIFICATETYPE_RAW_PUBLIC_KEY; + + return 1; /* Success */ +} + +int SSL_CTX_has_server_raw_public_key_certificate(SSL_CTX *ctx) { + return !ctx->server_raw_public_key_certificate.empty(); +} + +int SSL_set_server_raw_public_key_certificate(SSL *ssl, + const uint8_t *raw_public_key, unsigned raw_public_key_len) { + if (!ssl->config) { + return 0; /* Failure */ + } + + if (!ssl->config->server_raw_public_key_certificate.CopyFrom( + MakeConstSpan(raw_public_key, raw_public_key_len))) { + return 0; + } + + if (!ssl->config->server_certificate_type_list.Init(1)) { + return 0; + } + ssl->config->server_certificate_type_list[0] = + TLSEXT_CERTIFICATETYPE_RAW_PUBLIC_KEY; + + return 1; /* Success */ +} + +int SSL_has_server_raw_public_key_certificate(SSL *ssl) { + if (!ssl->config) { + return 0; /* Failure */ + } + + return !ssl->config->server_raw_public_key_certificate.empty(); +} + namespace fips202205 { // (References are to SP 800-52r2): diff --git a/src/ssl/tls13_both.cc b/src/ssl/tls13_both.cc index 5ab5a1c93..79135613e 100644 --- a/src/ssl/tls13_both.cc +++ b/src/ssl/tls13_both.cc @@ -11,6 +11,25 @@ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +/* ==================================================================== + * Copyright 2020 Apple Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the “Software”), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following conditions: + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ #include @@ -197,7 +216,16 @@ bool tls13_process_certificate(SSL_HANDSHAKE *hs, const SSLMessage &msg, return false; } - if (sk_CRYPTO_BUFFER_num(certs.get()) == 0) { + if (hs->server_certificate_type_negotiated && + hs->server_certificate_type == TLSEXT_CERTIFICATETYPE_RAW_PUBLIC_KEY) { + pkey = UniquePtr(EVP_parse_public_key(&certificate)); + if (!pkey) { + ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR); + OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR); + return false; + } + } + else if (sk_CRYPTO_BUFFER_num(certs.get()) == 0) { pkey = ssl_cert_parse_pubkey(&certificate); if (!pkey) { ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR); @@ -299,7 +327,10 @@ bool tls13_process_certificate(SSL_HANDSHAKE *hs, const SSLMessage &msg, } if (sk_CRYPTO_BUFFER_num(hs->new_session->certs.get()) == 0) { - if (!allow_anonymous) { + if (!allow_anonymous && + !(hs->server_certificate_type_negotiated && + hs->server_certificate_type == + TLSEXT_CERTIFICATETYPE_RAW_PUBLIC_KEY)) { OPENSSL_PUT_ERROR(SSL, SSL_R_PEER_DID_NOT_RETURN_A_CERTIFICATE); ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_CERTIFICATE_REQUIRED); return false; @@ -416,6 +447,20 @@ bool tls13_add_certificate(SSL_HANDSHAKE *hs) { return false; } + if (hs->server_certificate_type_negotiated && + hs->server_certificate_type == TLSEXT_CERTIFICATETYPE_RAW_PUBLIC_KEY) { + CBB leaf, extensions; + if (!CBB_add_u24_length_prefixed(&certificate_list, &leaf) || + !CBB_add_bytes(&leaf, + ssl->config->server_raw_public_key_certificate.data(), + ssl->config->server_raw_public_key_certificate.size()) || + !CBB_add_u16_length_prefixed(&certificate_list, &extensions)) { + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); + return false; + } + return ssl_add_message_cbb(ssl, cbb.get()); + } + if (!ssl_has_certificate(hs)) { return ssl_add_message_cbb(ssl, cbb.get()); } diff --git a/src/ssl/tls13_server.cc b/src/ssl/tls13_server.cc index 9d26f4e00..a92689761 100644 --- a/src/ssl/tls13_server.cc +++ b/src/ssl/tls13_server.cc @@ -11,6 +11,25 @@ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +/* ==================================================================== + * Copyright 2020 Apple Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the “Software”), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following conditions: + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ #include @@ -860,7 +879,8 @@ static enum ssl_hs_wait_t do_send_server_hello(SSL_HANDSHAKE *hs) { // Send the server Certificate message, if necessary. if (!ssl->s3->session_reused) { - if (!ssl_has_certificate(hs)) { + if (!ssl_has_certificate(hs) && + !ssl_has_raw_public_key_certificate(hs)) { OPENSSL_PUT_ERROR(SSL, SSL_R_NO_CERTIFICATE_SET); return ssl_hs_error; }