diff options
author | jsing <> | 2024-06-25 14:10:45 +0000 |
---|---|---|
committer | jsing <> | 2024-06-25 14:10:45 +0000 |
commit | e59a7b2ee59c295978f01e92bf5132ee2e8548b6 (patch) | |
tree | 63af5f2e95c5104b6713eeb3528306b47ecc203a /src | |
parent | 7e17989e1f3f79c8497ce57ca420783bb5efba53 (diff) | |
download | openbsd-e59a7b2ee59c295978f01e92bf5132ee2e8548b6.tar.gz openbsd-e59a7b2ee59c295978f01e92bf5132ee2e8548b6.tar.bz2 openbsd-e59a7b2ee59c295978f01e92bf5132ee2e8548b6.zip |
Implement RSA key exchange in constant time.
RSA key exchange is known to have multiple security weaknesses,
including being potentially susceptible to padding oracle and timing
attacks.
The RSA key exchange code that we inherited from OpenSSL was riddled
with timing leaks, many of which we fixed (or minimised) early on.
However, a number of issues still remained, particularly those
related to libcrypto's RSA decryption and padding checks.
Rework the RSA key exchange code such that we decrypt with
RSA_NO_PADDING and then check the padding ourselves in constant
time. In this case, the pre-master secret is of a known length,
hence the padding is also a known length based on the size of the
RSA key. This makes it easy to implement a check that is much safer
than having RSA_private_decrypt() depad for us.
Regardless, we still strongly recommend disabling RSA key exchange
and using other key exchange methods that provide perfect forward
secrecy and do not depend on client generated keys.
Thanks to Marcel Maehren, Nurullah Erinola, Robert Merget, Juraj
Somorovsky, Joerg Schwenk and Hubert Kario for raising these issues
with us at various points in time.
ok tb@
Diffstat (limited to 'src')
-rw-r--r-- | src/lib/libssl/Makefile | 3 | ||||
-rw-r--r-- | src/lib/libssl/ssl_local.h | 4 | ||||
-rw-r--r-- | src/lib/libssl/ssl_srvr.c | 129 |
3 files changed, 73 insertions, 63 deletions
diff --git a/src/lib/libssl/Makefile b/src/lib/libssl/Makefile index 38e5ba30e0..a2b710922d 100644 --- a/src/lib/libssl/Makefile +++ b/src/lib/libssl/Makefile | |||
@@ -1,4 +1,4 @@ | |||
1 | # $OpenBSD: Makefile,v 1.81 2023/11/22 15:55:28 tb Exp $ | 1 | # $OpenBSD: Makefile,v 1.82 2024/06/25 14:10:45 jsing Exp $ |
2 | 2 | ||
3 | .include <bsd.own.mk> | 3 | .include <bsd.own.mk> |
4 | .ifndef NOMAN | 4 | .ifndef NOMAN |
@@ -23,6 +23,7 @@ CFLAGS+= -DLIBRESSL_NAMESPACE | |||
23 | CFLAGS+= -DTLS13_DEBUG | 23 | CFLAGS+= -DTLS13_DEBUG |
24 | .endif | 24 | .endif |
25 | CFLAGS+= -I${.CURDIR} | 25 | CFLAGS+= -I${.CURDIR} |
26 | CFLAGS+= -I${.CURDIR}/../libcrypto | ||
26 | CFLAGS+= -I${.CURDIR}/../libcrypto/hidden | 27 | CFLAGS+= -I${.CURDIR}/../libcrypto/hidden |
27 | CFLAGS+= -I${.CURDIR}/../libcrypto/bio | 28 | CFLAGS+= -I${.CURDIR}/../libcrypto/bio |
28 | CFLAGS+= -I${.CURDIR}/hidden | 29 | CFLAGS+= -I${.CURDIR}/hidden |
diff --git a/src/lib/libssl/ssl_local.h b/src/lib/libssl/ssl_local.h index eeef569fa9..db102212a8 100644 --- a/src/lib/libssl/ssl_local.h +++ b/src/lib/libssl/ssl_local.h | |||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: ssl_local.h,v 1.16 2024/05/19 07:12:50 jsg Exp $ */ | 1 | /* $OpenBSD: ssl_local.h,v 1.17 2024/06/25 14:10:45 jsing Exp $ */ |
2 | /* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) | 2 | /* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) |
3 | * All rights reserved. | 3 | * All rights reserved. |
4 | * | 4 | * |
@@ -167,8 +167,10 @@ | |||
167 | 167 | ||
168 | __BEGIN_HIDDEN_DECLS | 168 | __BEGIN_HIDDEN_DECLS |
169 | 169 | ||
170 | #ifndef CTASSERT | ||
170 | #define CTASSERT(x) extern char _ctassert[(x) ? 1 : -1 ] \ | 171 | #define CTASSERT(x) extern char _ctassert[(x) ? 1 : -1 ] \ |
171 | __attribute__((__unused__)) | 172 | __attribute__((__unused__)) |
173 | #endif | ||
172 | 174 | ||
173 | #ifndef LIBRESSL_HAS_DTLS1_2 | 175 | #ifndef LIBRESSL_HAS_DTLS1_2 |
174 | #define LIBRESSL_HAS_DTLS1_2 | 176 | #define LIBRESSL_HAS_DTLS1_2 |
diff --git a/src/lib/libssl/ssl_srvr.c b/src/lib/libssl/ssl_srvr.c index 6d61a4e4fa..e9f14dc610 100644 --- a/src/lib/libssl/ssl_srvr.c +++ b/src/lib/libssl/ssl_srvr.c | |||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: ssl_srvr.c,v 1.160 2024/02/03 17:39:17 tb Exp $ */ | 1 | /* $OpenBSD: ssl_srvr.c,v 1.161 2024/06/25 14:10:45 jsing Exp $ */ |
2 | /* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) | 2 | /* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) |
3 | * All rights reserved. | 3 | * All rights reserved. |
4 | * | 4 | * |
@@ -163,6 +163,7 @@ | |||
163 | #include <openssl/x509.h> | 163 | #include <openssl/x509.h> |
164 | 164 | ||
165 | #include "bytestring.h" | 165 | #include "bytestring.h" |
166 | #include "crypto_internal.h" | ||
166 | #include "dtls_local.h" | 167 | #include "dtls_local.h" |
167 | #include "ssl_local.h" | 168 | #include "ssl_local.h" |
168 | #include "ssl_sigalgs.h" | 169 | #include "ssl_sigalgs.h" |
@@ -1621,98 +1622,104 @@ ssl3_send_certificate_request(SSL *s) | |||
1621 | static int | 1622 | static int |
1622 | ssl3_get_client_kex_rsa(SSL *s, CBS *cbs) | 1623 | ssl3_get_client_kex_rsa(SSL *s, CBS *cbs) |
1623 | { | 1624 | { |
1624 | unsigned char fakekey[SSL_MAX_MASTER_KEY_LENGTH]; | 1625 | uint8_t fakepms[SSL_MAX_MASTER_KEY_LENGTH]; |
1625 | unsigned char *pms = NULL; | 1626 | uint8_t *pms = NULL; |
1626 | unsigned char *p; | ||
1627 | size_t pms_len = 0; | 1627 | size_t pms_len = 0; |
1628 | size_t pad_len; | ||
1628 | EVP_PKEY *pkey = NULL; | 1629 | EVP_PKEY *pkey = NULL; |
1629 | RSA *rsa = NULL; | 1630 | RSA *rsa = NULL; |
1630 | CBS enc_pms; | 1631 | CBS enc_pms; |
1631 | int decrypt_len; | 1632 | int decrypt_len; |
1632 | int al = -1; | 1633 | uint8_t mask; |
1634 | size_t i; | ||
1635 | int valid = 1; | ||
1636 | int ret = 0; | ||
1637 | |||
1638 | /* | ||
1639 | * Handle key exchange in the form of an RSA-Encrypted Premaster Secret | ||
1640 | * Message. See RFC 5246, section 7.4.7.1. | ||
1641 | */ | ||
1633 | 1642 | ||
1634 | arc4random_buf(fakekey, sizeof(fakekey)); | 1643 | arc4random_buf(fakepms, sizeof(fakepms)); |
1635 | 1644 | ||
1636 | fakekey[0] = s->s3->hs.peer_legacy_version >> 8; | 1645 | fakepms[0] = s->s3->hs.peer_legacy_version >> 8; |
1637 | fakekey[1] = s->s3->hs.peer_legacy_version & 0xff; | 1646 | fakepms[1] = s->s3->hs.peer_legacy_version & 0xff; |
1638 | 1647 | ||
1639 | pkey = s->cert->pkeys[SSL_PKEY_RSA].privatekey; | 1648 | pkey = s->cert->pkeys[SSL_PKEY_RSA].privatekey; |
1640 | if (pkey == NULL || (rsa = EVP_PKEY_get0_RSA(pkey)) == NULL) { | 1649 | if (pkey == NULL || (rsa = EVP_PKEY_get0_RSA(pkey)) == NULL) { |
1641 | al = SSL_AD_HANDSHAKE_FAILURE; | ||
1642 | SSLerror(s, SSL_R_MISSING_RSA_CERTIFICATE); | 1650 | SSLerror(s, SSL_R_MISSING_RSA_CERTIFICATE); |
1643 | goto fatal_err; | 1651 | ssl3_send_alert(s, SSL3_AL_FATAL, SSL_AD_HANDSHAKE_FAILURE); |
1652 | goto err; | ||
1644 | } | 1653 | } |
1645 | 1654 | ||
1655 | /* | ||
1656 | * The minimum size of an encrypted premaster secret is 11 bytes of | ||
1657 | * padding (00 02 <8 or more non-zero bytes> 00) (RFC 8017, section | ||
1658 | * 9.2) and 48 bytes of premaster secret (RFC 5246, section 7.4.7.1). | ||
1659 | * This means an RSA key size of at least 472 bits. | ||
1660 | */ | ||
1646 | pms_len = RSA_size(rsa); | 1661 | pms_len = RSA_size(rsa); |
1647 | if (pms_len < SSL_MAX_MASTER_KEY_LENGTH) | 1662 | if (pms_len < 11 + SSL_MAX_MASTER_KEY_LENGTH) { |
1648 | goto err; | 1663 | SSLerror(s, SSL_R_DECRYPTION_FAILED); |
1649 | if ((pms = malloc(pms_len)) == NULL) | 1664 | ssl3_send_alert(s, SSL3_AL_FATAL, SSL_AD_DECRYPT_ERROR); |
1650 | goto err; | 1665 | goto err; |
1651 | p = pms; | 1666 | } |
1667 | pad_len = pms_len - SSL_MAX_MASTER_KEY_LENGTH; | ||
1652 | 1668 | ||
1653 | if (!CBS_get_u16_length_prefixed(cbs, &enc_pms)) | 1669 | if (!CBS_get_u16_length_prefixed(cbs, &enc_pms)) { |
1654 | goto decode_err; | 1670 | SSLerror(s, SSL_R_BAD_PACKET_LENGTH); |
1655 | if (CBS_len(cbs) != 0 || CBS_len(&enc_pms) != RSA_size(rsa)) { | 1671 | ssl3_send_alert(s, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR); |
1672 | goto err; | ||
1673 | } | ||
1674 | if (CBS_len(&enc_pms) != pms_len || CBS_len(cbs) != 0) { | ||
1656 | SSLerror(s, SSL_R_TLS_RSA_ENCRYPTED_VALUE_LENGTH_IS_WRONG); | 1675 | SSLerror(s, SSL_R_TLS_RSA_ENCRYPTED_VALUE_LENGTH_IS_WRONG); |
1676 | ssl3_send_alert(s, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR); | ||
1657 | goto err; | 1677 | goto err; |
1658 | } | 1678 | } |
1659 | 1679 | ||
1660 | decrypt_len = RSA_private_decrypt(CBS_len(&enc_pms), CBS_data(&enc_pms), | 1680 | if ((pms = calloc(1, pms_len)) == NULL) |
1661 | pms, rsa, RSA_PKCS1_PADDING); | 1681 | goto err; |
1662 | 1682 | ||
1663 | ERR_clear_error(); | 1683 | decrypt_len = RSA_private_decrypt(CBS_len(&enc_pms), CBS_data(&enc_pms), |
1684 | pms, rsa, RSA_NO_PADDING); | ||
1664 | 1685 | ||
1665 | if (decrypt_len != SSL_MAX_MASTER_KEY_LENGTH) { | 1686 | if (decrypt_len != pms_len) { |
1666 | al = SSL_AD_DECODE_ERROR; | 1687 | SSLerror(s, SSL_R_DECRYPTION_FAILED); |
1667 | /* SSLerror(s, SSL_R_BAD_RSA_DECRYPT); */ | 1688 | ssl3_send_alert(s, SSL3_AL_FATAL, SSL_AD_DECRYPT_ERROR); |
1689 | goto err; | ||
1668 | } | 1690 | } |
1669 | 1691 | ||
1670 | if ((al == -1) && !((pms[0] == (s->s3->hs.peer_legacy_version >> 8)) && | 1692 | /* |
1671 | (pms[1] == (s->s3->hs.peer_legacy_version & 0xff)))) { | 1693 | * All processing from here on needs to avoid leaking any information |
1672 | /* | 1694 | * about the decrypted content, in order to prevent oracle attacks and |
1673 | * The premaster secret must contain the same version number | 1695 | * minimise timing attacks. |
1674 | * as the ClientHello to detect version rollback attacks | 1696 | */ |
1675 | * (strangely, the protocol does not offer such protection for | ||
1676 | * DH ciphersuites). | ||
1677 | * | ||
1678 | * The Klima-Pokorny-Rosa extension of Bleichenbacher's attack | ||
1679 | * (http://eprint.iacr.org/2003/052/) exploits the version | ||
1680 | * number check as a "bad version oracle" -- an alert would | ||
1681 | * reveal that the plaintext corresponding to some ciphertext | ||
1682 | * made up by the adversary is properly formatted except that | ||
1683 | * the version number is wrong. To avoid such attacks, we should | ||
1684 | * treat this just like any other decryption error. | ||
1685 | */ | ||
1686 | al = SSL_AD_DECODE_ERROR; | ||
1687 | /* SSLerror(s, SSL_R_BAD_PROTOCOL_VERSION_NUMBER); */ | ||
1688 | } | ||
1689 | 1697 | ||
1690 | if (al != -1) { | 1698 | /* Check padding - 00 02 <8 or more non-zero bytes> 00 */ |
1691 | /* | 1699 | valid &= crypto_ct_eq_u8(pms[0], 0x00); |
1692 | * Some decryption failure -- use random value instead | 1700 | valid &= crypto_ct_eq_u8(pms[1], 0x02); |
1693 | * as countermeasure against Bleichenbacher's attack | 1701 | for (i = 2; i < pad_len - 1; i++) |
1694 | * on PKCS #1 v1.5 RSA padding (see RFC 2246, | 1702 | valid &= crypto_ct_ne_u8(pms[i], 0x00); |
1695 | * section 7.4.7.1). | 1703 | valid &= crypto_ct_eq_u8(pms[pad_len - 1], 0x00); |
1696 | */ | ||
1697 | p = fakekey; | ||
1698 | } | ||
1699 | 1704 | ||
1700 | if (!tls12_derive_master_secret(s, p, SSL_MAX_MASTER_KEY_LENGTH)) | 1705 | /* Ensure client version in premaster secret matches ClientHello version. */ |
1701 | goto err; | 1706 | valid &= crypto_ct_eq_u8(pms[pad_len + 0], s->s3->hs.peer_legacy_version >> 8); |
1707 | valid &= crypto_ct_eq_u8(pms[pad_len + 1], s->s3->hs.peer_legacy_version & 0xff); | ||
1702 | 1708 | ||
1703 | freezero(pms, pms_len); | 1709 | /* Use the premaster secret if padding is correct, if not use the fake. */ |
1710 | mask = crypto_ct_eq_mask_u8(valid, 1); | ||
1711 | for (i = 0; i < SSL_MAX_MASTER_KEY_LENGTH; i++) | ||
1712 | pms[i] = (pms[pad_len + i] & mask) | (fakepms[i] & ~mask); | ||
1704 | 1713 | ||
1705 | return 1; | 1714 | if (!tls12_derive_master_secret(s, pms, SSL_MAX_MASTER_KEY_LENGTH)) |
1715 | goto err; | ||
1716 | |||
1717 | ret = 1; | ||
1706 | 1718 | ||
1707 | decode_err: | ||
1708 | al = SSL_AD_DECODE_ERROR; | ||
1709 | SSLerror(s, SSL_R_BAD_PACKET_LENGTH); | ||
1710 | fatal_err: | ||
1711 | ssl3_send_alert(s, SSL3_AL_FATAL, al); | ||
1712 | err: | 1719 | err: |
1713 | freezero(pms, pms_len); | 1720 | freezero(pms, pms_len); |
1714 | 1721 | ||
1715 | return 0; | 1722 | return ret; |
1716 | } | 1723 | } |
1717 | 1724 | ||
1718 | static int | 1725 | static int |