| /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
| /* |
| * This file is PRIVATE to SSL. |
| * |
| * This Source Code Form is subject to the terms of the Mozilla Public |
| * License, v. 2.0. If a copy of the MPL was not distributed with this |
| * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
| |
| #include "pk11func.h" |
| #include "ssl.h" |
| #include "sslt.h" |
| #include "sslimpl.h" |
| #include "selfencrypt.h" |
| #include "tls13con.h" |
| #include "tls13err.h" |
| #include "tls13hashstate.h" |
| |
| /* |
| * The cookie is structured as a self-encrypted structure with the |
| * inner value being. |
| * |
| * struct { |
| * uint8 indicator = 0xff; // To disambiguate from tickets. |
| * uint16 cipherSuite; // Selected cipher suite. |
| * uint16 keyShare; // Requested key share group (0=none) |
| * opaque applicationToken<0..65535>; // Application token |
| * opaque ch_hash[rest_of_buffer]; // H(ClientHello) |
| * } CookieInner; |
| */ |
| SECStatus |
| tls13_MakeHrrCookie(sslSocket *ss, const sslNamedGroupDef *selectedGroup, |
| const PRUint8 *appToken, unsigned int appTokenLen, |
| PRUint8 *buf, unsigned int *len, unsigned int maxlen) |
| { |
| SECStatus rv; |
| SSL3Hashes hashes; |
| PRUint8 cookie[1024]; |
| sslBuffer cookieBuf = SSL_BUFFER(cookie); |
| static const PRUint8 indicator = 0xff; |
| |
| /* Encode header. */ |
| rv = sslBuffer_Append(&cookieBuf, &indicator, 1); |
| if (rv != SECSuccess) { |
| return SECFailure; |
| } |
| rv = sslBuffer_AppendNumber(&cookieBuf, ss->ssl3.hs.cipher_suite, 2); |
| if (rv != SECSuccess) { |
| return SECFailure; |
| } |
| rv = sslBuffer_AppendNumber(&cookieBuf, |
| selectedGroup ? selectedGroup->name : 0, 2); |
| if (rv != SECSuccess) { |
| return SECFailure; |
| } |
| |
| /* Application token. */ |
| rv = sslBuffer_AppendVariable(&cookieBuf, appToken, appTokenLen, 2); |
| if (rv != SECSuccess) { |
| return SECFailure; |
| } |
| |
| /* Compute and encode hashes. */ |
| rv = tls13_ComputeHandshakeHashes(ss, &hashes); |
| if (rv != SECSuccess) { |
| return SECFailure; |
| } |
| rv = sslBuffer_Append(&cookieBuf, hashes.u.raw, hashes.len); |
| if (rv != SECSuccess) { |
| return SECFailure; |
| } |
| |
| /* Encrypt right into the buffer. */ |
| rv = ssl_SelfEncryptProtect(ss, cookieBuf.buf, cookieBuf.len, |
| buf, len, maxlen); |
| if (rv != SECSuccess) { |
| return SECFailure; |
| } |
| |
| return SECSuccess; |
| } |
| |
| /* Recover the hash state from the cookie. */ |
| SECStatus |
| tls13_RecoverHashState(sslSocket *ss, |
| unsigned char *cookie, unsigned int cookieLen, |
| ssl3CipherSuite *previousCipherSuite, |
| const sslNamedGroupDef **previousGroup) |
| { |
| SECStatus rv; |
| unsigned char plaintext[1024]; |
| unsigned int plaintextLen = 0; |
| sslBuffer messageBuf = SSL_BUFFER_EMPTY; |
| PRUint64 sentinel; |
| PRUint64 cipherSuite; |
| PRUint64 group; |
| const sslNamedGroupDef *selectedGroup; |
| PRUint64 appTokenLen; |
| |
| rv = ssl_SelfEncryptUnprotect(ss, cookie, cookieLen, |
| plaintext, &plaintextLen, sizeof(plaintext)); |
| if (rv != SECSuccess) { |
| return SECFailure; |
| } |
| |
| sslReader reader = SSL_READER(plaintext, plaintextLen); |
| |
| /* Should start with 0xff. */ |
| rv = sslRead_ReadNumber(&reader, 1, &sentinel); |
| if ((rv != SECSuccess) || (sentinel != 0xff)) { |
| FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_CLIENT_HELLO, illegal_parameter); |
| return SECFailure; |
| } |
| /* The cipher suite should be the same or there are some shenanigans. */ |
| rv = sslRead_ReadNumber(&reader, 2, &cipherSuite); |
| if (rv != SECSuccess) { |
| FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_CLIENT_HELLO, illegal_parameter); |
| return SECFailure; |
| } |
| |
| /* The named group, if any. */ |
| rv = sslRead_ReadNumber(&reader, 2, &group); |
| if (rv != SECSuccess) { |
| FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_CLIENT_HELLO, illegal_parameter); |
| return SECFailure; |
| } |
| selectedGroup = ssl_LookupNamedGroup(group); |
| |
| /* Application token. */ |
| PORT_Assert(ss->xtnData.applicationToken.len == 0); |
| rv = sslRead_ReadNumber(&reader, 2, &appTokenLen); |
| if (rv != SECSuccess) { |
| FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_CLIENT_HELLO, illegal_parameter); |
| return SECFailure; |
| } |
| if (SECITEM_AllocItem(NULL, &ss->xtnData.applicationToken, |
| appTokenLen) == NULL) { |
| FATAL_ERROR(ss, PORT_GetError(), internal_error); |
| return SECFailure; |
| } |
| ss->xtnData.applicationToken.len = appTokenLen; |
| sslReadBuffer appTokenReader = { 0 }; |
| rv = sslRead_Read(&reader, appTokenLen, &appTokenReader); |
| if (rv != SECSuccess) { |
| FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_CLIENT_HELLO, illegal_parameter); |
| return SECFailure; |
| } |
| PORT_Assert(appTokenReader.len == appTokenLen); |
| PORT_Memcpy(ss->xtnData.applicationToken.data, appTokenReader.buf, appTokenLen); |
| |
| /* The remainder is the hash. */ |
| unsigned int hashLen = SSL_READER_REMAINING(&reader); |
| if (hashLen != tls13_GetHashSize(ss)) { |
| FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_CLIENT_HELLO, illegal_parameter); |
| return SECFailure; |
| } |
| |
| /* Now reinject the message. */ |
| SSL_ASSERT_HASHES_EMPTY(ss); |
| rv = ssl_HashHandshakeMessageInt(ss, ssl_hs_message_hash, 0, |
| SSL_READER_CURRENT(&reader), hashLen); |
| if (rv != SECSuccess) { |
| return SECFailure; |
| } |
| |
| /* And finally reinject the HRR. */ |
| rv = tls13_ConstructHelloRetryRequest(ss, cipherSuite, |
| selectedGroup, |
| cookie, cookieLen, |
| &messageBuf); |
| if (rv != SECSuccess) { |
| return SECFailure; |
| } |
| |
| rv = ssl_HashHandshakeMessageInt(ss, ssl_hs_server_hello, 0, |
| SSL_BUFFER_BASE(&messageBuf), |
| SSL_BUFFER_LEN(&messageBuf)); |
| sslBuffer_Clear(&messageBuf); |
| if (rv != SECSuccess) { |
| return SECFailure; |
| } |
| |
| *previousCipherSuite = cipherSuite; |
| *previousGroup = selectedGroup; |
| return SECSuccess; |
| } |