Skip to content

Commit

Permalink
Add support for integrity-only cipher suites for TLS v1.3
Browse files Browse the repository at this point in the history
- add test vectors for tls1_3 integrity-only ciphers
- recmethod_local.h: add new member for MAC
- tls13_meth.c: add MAC only to tls 1.3
- tls13_enc.c: extend function to add MAC only
- ssl_local.h: add ssl_cipher_get_evp_md_mac()
- s3_lib.c: add the new ciphers and add #ifndef OPENSSL_NO_INTEGRITY_ONLY_CIPHERS
- ssl_ciph.c : add ssl_cipher_get_evp_md_mac() and use it
- tls13secretstest.c: add dummy test function
- Configure: add integrity-only-ciphers option
- document the new ciphers

Reviewed-by: Matt Caswell <matt@openssl.org>
Reviewed-by: David von Oheimb <david.von.oheimb@siemens.com>
Reviewed-by: Tomas Mraz <tomas@openssl.org>
(Merged from openssl#22903)
  • Loading branch information
rajeev-0 authored and t8m committed May 14, 2024
1 parent 61f3239 commit b6a5e80
Show file tree
Hide file tree
Showing 22 changed files with 423 additions and 137 deletions.
1 change: 1 addition & 0 deletions .github/workflows/run-checker-merge.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ jobs:
no-srp,
no-srtp,
no-ts,
no-integrity-only-ciphers,
enable-weak-ssl-ciphers,
enable-zlib,
enable-pie,
Expand Down
7 changes: 7 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,13 @@ OpenSSL 3.4

*Tim Perry*

* Added support for integrity-only cipher suites TLS_SHA256_SHA256 and
TLS_SHA384_SHA384 in TLS 1.3, as defined in RFC 9150.

This work was sponsored by Siemens AG.

*Rajeev Ranjan*

* Added support for requesting CRL in CMP.

This work was sponsored by Siemens AG.
Expand Down
1 change: 1 addition & 0 deletions Configure
Original file line number Diff line number Diff line change
Expand Up @@ -529,6 +529,7 @@ my @disablables = (
"thread-pool",
"threads",
"tls",
"integrity-only-ciphers",
"trace",
"ts",
"ubsan",
Expand Down
4 changes: 4 additions & 0 deletions INSTALL.md
Original file line number Diff line number Diff line change
Expand Up @@ -1117,6 +1117,10 @@ synonymous with `no-ssl3`. Note this only affects version negotiation.
OpenSSL will still provide the methods for applications to explicitly select
the individual protocol versions.

### no-integrity-only-ciphers

Don't build support for integrity only ciphers in tls.

### no-{protocol}-method

no-{ssl3|tls1|tls1_1|tls1_2|dtls1|dtls1_2}-method
Expand Down
11 changes: 11 additions & 0 deletions doc/man1/openssl-ciphers.pod.in
Original file line number Diff line number Diff line change
Expand Up @@ -738,6 +738,15 @@ Note: the CBC modes mentioned in this RFC are not supported.
TLS_AES_128_CCM_SHA256 TLS_AES_128_CCM_SHA256
TLS_AES_128_CCM_8_SHA256 TLS_AES_128_CCM_8_SHA256

=head2 TLS v1.3 integrity-only cipher suites according to RFC 9150

TLS_SHA256_SHA256 TLS_SHA256_SHA256
TLS_SHA384_SHA384 TLS_SHA384_SHA384

Note: these ciphers are purely HMAC based and do not provide any confidentiality
and thus are disabled by default.
These ciphers are only available at security level 0.

=head2 Older names used by OpenSSL

The following names are accepted by older releases:
Expand Down Expand Up @@ -802,6 +811,8 @@ The B<-convert> option was added in OpenSSL 1.1.1.
Support for standard IANA names in cipher lists was added in
OpenSSL 3.2.0.

The support for TLS v1.3 integrity-only cipher suites was added in OpenSSL 3.4.

=head1 COPYRIGHT

Copyright 2000-2024 The OpenSSL Project Authors. All Rights Reserved.
Expand Down
4 changes: 4 additions & 0 deletions doc/man3/SSL_CTX_set_cipher_list.pod
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@ ciphersuite names in order of preference. Valid TLSv1.3 ciphersuite names are:

=item TLS_AES_128_CCM_8_SHA256

=item TLS_SHA384_SHA384 - integrity-only

=item TLS_SHA256_SHA256 - integrity-only

=back

An empty list is permissible. The default value for the this setting is:
Expand Down
6 changes: 6 additions & 0 deletions include/openssl/tls1.h
Original file line number Diff line number Diff line change
Expand Up @@ -622,6 +622,10 @@ int SSL_CTX_set_tlsext_ticket_key_evp_cb
# define TLS1_3_CK_AES_128_CCM_SHA256 0x03001304
# define TLS1_3_CK_AES_128_CCM_8_SHA256 0x03001305

/* Integrity-only ciphersuites from RFC 9150 */
# define TLS1_3_CK_SHA256_SHA256 0x0300C0B4
# define TLS1_3_CK_SHA384_SHA384 0x0300C0B5

/* Aria ciphersuites from RFC6209 */
# define TLS1_CK_RSA_WITH_ARIA_128_GCM_SHA256 0x0300C050
# define TLS1_CK_RSA_WITH_ARIA_256_GCM_SHA384 0x0300C051
Expand Down Expand Up @@ -699,6 +703,8 @@ int SSL_CTX_set_tlsext_ticket_key_evp_cb
# define TLS1_3_RFC_AES_128_GCM_SHA256 "TLS_AES_128_GCM_SHA256"
# define TLS1_3_RFC_AES_256_GCM_SHA384 "TLS_AES_256_GCM_SHA384"
# define TLS1_3_RFC_CHACHA20_POLY1305_SHA256 "TLS_CHACHA20_POLY1305_SHA256"
# define TLS1_3_RFC_SHA256_SHA256 "TLS_SHA256_SHA256"
# define TLS1_3_RFC_SHA384_SHA384 "TLS_SHA384_SHA384"
# define TLS1_3_RFC_AES_128_CCM_SHA256 "TLS_AES_128_CCM_SHA256"
# define TLS1_3_RFC_AES_128_CCM_8_SHA256 "TLS_AES_128_CCM_8_SHA256"
# define TLS1_RFC_ECDHE_ECDSA_WITH_NULL_SHA "TLS_ECDHE_ECDSA_WITH_NULL_SHA"
Expand Down
7 changes: 5 additions & 2 deletions ssl/record/methods/recmethod_local.h
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,9 @@ struct ossl_record_layer_st
/* cryptographic state */
EVP_CIPHER_CTX *enc_ctx;

/* TLSv1.3 MAC ctx, only used with integrity-only cipher */
EVP_MAC_CTX *mac_ctx;

/* Explicit IV length */
size_t eivlen;

Expand Down Expand Up @@ -333,8 +336,8 @@ struct ossl_record_layer_st
int tlstree;

/* TLSv1.3 fields */
/* static IV */
unsigned char iv[EVP_MAX_IV_LENGTH];
unsigned char *iv; /* static IV */
unsigned char *nonce; /* part of static IV followed by sequence number */
int allow_plain_alerts;

/* TLS "any" fields */
Expand Down
127 changes: 96 additions & 31 deletions ssl/record/methods/tls13_meth.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,42 @@ static int tls13_set_crypto_state(OSSL_RECORD_LAYER *rl, int level,
COMP_METHOD *comp)
{
EVP_CIPHER_CTX *ciph_ctx;
EVP_MAC_CTX *mac_ctx;
EVP_MAC *mac;
OSSL_PARAM params[2], *p = params;
int mode;
int enc = (rl->direction == OSSL_RECORD_DIRECTION_WRITE) ? 1 : 0;

if (ivlen > sizeof(rl->iv)) {
ERR_raise(ERR_LIB_SSL, ERR_R_INTERNAL_ERROR);
rl->iv = OPENSSL_malloc(ivlen);
if (rl->iv == NULL)
return OSSL_RECORD_RETURN_FATAL;
}

rl->nonce = OPENSSL_malloc(ivlen);
if (rl->nonce == NULL)
return OSSL_RECORD_RETURN_FATAL;

memcpy(rl->iv, iv, ivlen);

/* Integrity only */
if (EVP_CIPHER_is_a(ciph, "NULL") && mactype == NID_hmac && md != NULL) {
mac = EVP_MAC_fetch(rl->libctx, "HMAC", rl->propq);
if (mac == NULL
|| (mac_ctx = rl->mac_ctx = EVP_MAC_CTX_new(mac)) == NULL) {
EVP_MAC_free(mac);
ERR_raise(ERR_LIB_SSL, ERR_R_INTERNAL_ERROR);
return OSSL_RECORD_RETURN_FATAL;
}
EVP_MAC_free(mac);
*p++ = OSSL_PARAM_construct_utf8_string(OSSL_MAC_PARAM_DIGEST,
(char *)EVP_MD_name(md), 0);
*p = OSSL_PARAM_construct_end();
if (!EVP_MAC_init(mac_ctx, key, keylen, params)) {
ERR_raise(ERR_LIB_SSL, ERR_R_INTERNAL_ERROR);
return OSSL_RECORD_RETURN_FATAL;
}
goto end;
}

ciph_ctx = rl->enc_ctx = EVP_CIPHER_CTX_new();
if (ciph_ctx == NULL) {
ERR_raise(ERR_LIB_SSL, ERR_R_INTERNAL_ERROR);
Expand All @@ -51,23 +78,26 @@ static int tls13_set_crypto_state(OSSL_RECORD_LAYER *rl, int level,
ERR_raise(ERR_LIB_SSL, ERR_R_INTERNAL_ERROR);
return OSSL_RECORD_RETURN_FATAL;
}

end:
return OSSL_RECORD_RETURN_SUCCESS;
}

static int tls13_cipher(OSSL_RECORD_LAYER *rl, TLS_RL_RECORD *recs,
size_t n_recs, int sending, SSL_MAC_BUF *mac,
size_t macsize)
{
EVP_CIPHER_CTX *ctx;
unsigned char iv[EVP_MAX_IV_LENGTH], recheader[SSL3_RT_HEADER_LENGTH];
size_t ivlen, offset, loop, hdrlen;
EVP_CIPHER_CTX *enc_ctx;
unsigned char recheader[SSL3_RT_HEADER_LENGTH];
unsigned char tag[EVP_MAX_MD_SIZE];
size_t nonce_len, offset, loop, hdrlen, taglen;
unsigned char *staticiv;
unsigned char *nonce;
unsigned char *seq = rl->sequence;
int lenu, lenf;
TLS_RL_RECORD *rec = &recs[0];
WPACKET wpkt;
const EVP_CIPHER *cipher;
EVP_MAC_CTX *mac_ctx = NULL;
int mode;

if (n_recs != 1) {
Expand All @@ -76,29 +106,32 @@ static int tls13_cipher(OSSL_RECORD_LAYER *rl, TLS_RL_RECORD *recs,
return 0;
}

ctx = rl->enc_ctx;
enc_ctx = rl->enc_ctx; /* enc_ctx is ignored when rl->mac_ctx != NULL */
staticiv = rl->iv;
nonce = rl->nonce;

cipher = EVP_CIPHER_CTX_get0_cipher(ctx);
if (cipher == NULL) {
if (enc_ctx == NULL && rl->mac_ctx == NULL) {
RLAYERfatal(rl, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
return 0;
}
mode = EVP_CIPHER_get_mode(cipher);

/*
* If we're sending an alert and ctx != NULL then we must be forcing
* plaintext alerts. If we're reading and ctx != NULL then we allow
* plaintext alerts at certain points in the handshake. If we've got this
* far then we have already validated that a plaintext alert is ok here.
*/
if (ctx == NULL || rec->type == SSL3_RT_ALERT) {
if (rec->type == SSL3_RT_ALERT) {
memmove(rec->data, rec->input, rec->length);
rec->input = rec->data;
return 1;
}

ivlen = EVP_CIPHER_CTX_get_iv_length(ctx);
/* For integrity-only ciphers, nonce_len is same as MAC size */
if (rl->mac_ctx != NULL)
nonce_len = EVP_MAC_CTX_get_mac_size(rl->mac_ctx);
else
nonce_len = EVP_CIPHER_CTX_get_iv_length(enc_ctx);

if (!sending) {
/*
Expand All @@ -110,30 +143,22 @@ static int tls13_cipher(OSSL_RECORD_LAYER *rl, TLS_RL_RECORD *recs,
rec->length -= rl->taglen;
}

/* Set up IV */
if (ivlen < SEQ_NUM_SIZE) {
/* Set up nonce: part of static IV followed by sequence number */
if (nonce_len < SEQ_NUM_SIZE) {
/* Should not happen */
RLAYERfatal(rl, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
return 0;
}
offset = ivlen - SEQ_NUM_SIZE;
memcpy(iv, staticiv, offset);
offset = nonce_len - SEQ_NUM_SIZE;
memcpy(nonce, staticiv, offset);
for (loop = 0; loop < SEQ_NUM_SIZE; loop++)
iv[offset + loop] = staticiv[offset + loop] ^ seq[loop];
nonce[offset + loop] = staticiv[offset + loop] ^ seq[loop];

if (!tls_increment_sequence_ctr(rl)) {
/* RLAYERfatal already called */
return 0;
}

if (EVP_CipherInit_ex(ctx, NULL, NULL, NULL, iv, sending) <= 0
|| (!sending && EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG,
rl->taglen,
rec->data + rec->length) <= 0)) {
RLAYERfatal(rl, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
return 0;
}

/* Set up the AAD */
if (!WPACKET_init_static_len(&wpkt, recheader, sizeof(recheader), 0)
|| !WPACKET_put_bytes_u8(&wpkt, rec->type)
Expand All @@ -147,24 +172,64 @@ static int tls13_cipher(OSSL_RECORD_LAYER *rl, TLS_RL_RECORD *recs,
return 0;
}

if (rl->mac_ctx != NULL) {
int ret = 0;

if ((mac_ctx = EVP_MAC_CTX_dup(rl->mac_ctx)) == NULL
|| !EVP_MAC_update(mac_ctx, nonce, nonce_len)
|| !EVP_MAC_update(mac_ctx, recheader, sizeof(recheader))
|| !EVP_MAC_update(mac_ctx, rec->input, rec->length)
|| !EVP_MAC_final(mac_ctx, tag, &taglen, rl->taglen)) {
RLAYERfatal(rl, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
goto end_mac;
}

if (sending) {
memcpy(rec->data + rec->length, tag, rl->taglen);
rec->length += rl->taglen;
} else if (CRYPTO_memcmp(tag, rec->data + rec->length,
rl->taglen) != 0) {
goto end_mac;
}
ret = 1;
end_mac:
EVP_MAC_CTX_free(mac_ctx);
return ret;
}

cipher = EVP_CIPHER_CTX_get0_cipher(enc_ctx);
if (cipher == NULL) {
RLAYERfatal(rl, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
return 0;
}
mode = EVP_CIPHER_get_mode(cipher);

if (EVP_CipherInit_ex(enc_ctx, NULL, NULL, NULL, nonce, sending) <= 0
|| (!sending && EVP_CIPHER_CTX_ctrl(enc_ctx, EVP_CTRL_AEAD_SET_TAG,
rl->taglen,
rec->data + rec->length) <= 0)) {
RLAYERfatal(rl, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
return 0;
}

/*
* For CCM we must explicitly set the total plaintext length before we add
* any AAD.
*/
if ((mode == EVP_CIPH_CCM_MODE
&& EVP_CipherUpdate(ctx, NULL, &lenu, NULL,
&& EVP_CipherUpdate(enc_ctx, NULL, &lenu, NULL,
(unsigned int)rec->length) <= 0)
|| EVP_CipherUpdate(ctx, NULL, &lenu, recheader,
|| EVP_CipherUpdate(enc_ctx, NULL, &lenu, recheader,
sizeof(recheader)) <= 0
|| EVP_CipherUpdate(ctx, rec->data, &lenu, rec->input,
|| EVP_CipherUpdate(enc_ctx, rec->data, &lenu, rec->input,
(unsigned int)rec->length) <= 0
|| EVP_CipherFinal_ex(ctx, rec->data + lenu, &lenf) <= 0
|| EVP_CipherFinal_ex(enc_ctx, rec->data + lenu, &lenf) <= 0
|| (size_t)(lenu + lenf) != rec->length) {
return 0;
}
if (sending) {
/* Add the tag */
if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_GET_TAG, rl->taglen,
if (EVP_CIPHER_CTX_ctrl(enc_ctx, EVP_CTRL_AEAD_GET_TAG, rl->taglen,
rec->data + rec->length) <= 0) {
RLAYERfatal(rl, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
return 0;
Expand Down
4 changes: 3 additions & 1 deletion ssl/record/methods/tls_common.c
Original file line number Diff line number Diff line change
Expand Up @@ -1434,11 +1434,13 @@ static void tls_int_free(OSSL_RECORD_LAYER *rl)
tls_release_write_buffer(rl);

EVP_CIPHER_CTX_free(rl->enc_ctx);
EVP_MAC_CTX_free(rl->mac_ctx);
EVP_MD_CTX_free(rl->md_ctx);
#ifndef OPENSSL_NO_COMP
COMP_CTX_free(rl->compctx);
#endif

OPENSSL_free(rl->iv);
OPENSSL_free(rl->nonce);
if (rl->version == SSL3_VERSION)
OPENSSL_cleanse(rl->mac_secret, sizeof(rl->mac_secret));

Expand Down
Loading

0 comments on commit b6a5e80

Please sign in to comment.