1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332
|
/* -*- 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 "tls13ech.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)
* PRUint8 echConfigId; // ECH config_id
* HpkeKdfId kdfId; // ECH KDF (uint16)
* HpkeAeadId aeadId; // ECH AEAD (uint16)
* opaque echHpkeCtx<0..65535>; // ECH serialized HPKE context
* opaque applicationToken<0..65535>; // Application token
* opaque ch_hash[rest_of_buffer]; // H(ClientHello)
* } CookieInner;
*
* An empty echConfigId means that ECH was not offered in the first ClientHello.
* An empty echHrrPsk means that ECH was not accepted in CH1.
*/
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;
SECItem *echHpkeCtx = NULL;
/* 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;
}
if (ss->xtnData.ech) {
/* Record that we received ECH. See sslEchCookieData */
rv = sslBuffer_AppendNumber(&cookieBuf, PR_TRUE, 1);
if (rv != SECSuccess) {
return SECFailure;
}
rv = sslBuffer_AppendNumber(&cookieBuf, ss->xtnData.ech->configId,
1);
if (rv != SECSuccess) {
return SECFailure;
}
rv = sslBuffer_AppendNumber(&cookieBuf, ss->xtnData.ech->kdfId, 2);
if (rv != SECSuccess) {
return SECFailure;
}
rv = sslBuffer_AppendNumber(&cookieBuf, ss->xtnData.ech->aeadId, 2);
if (rv != SECSuccess) {
return SECFailure;
}
/* We need to send a ECH HRR Extension containing a signal for the client,
* we must store the signal in the cookie so we can reconstruct the transcript
* later. To avoid leaking whether ECH was accepted in the length of the cookie
* we include the empty signal in the cookie regardless.
*/
PR_ASSERT(SSL_BUFFER_LEN(&ss->ssl3.hs.greaseEchBuf) == TLS13_ECH_SIGNAL_LEN);
rv = sslBuffer_AppendBuffer(&cookieBuf, &ss->ssl3.hs.greaseEchBuf);
if (rv != SECSuccess) {
return SECFailure;
}
/* There might be no HPKE Context, e.g. when we lack a matching ECHConfig. */
if (ss->ssl3.hs.echHpkeCtx) {
rv = PK11_HPKE_ExportContext(ss->ssl3.hs.echHpkeCtx, NULL, &echHpkeCtx);
if (rv != SECSuccess) {
return SECFailure;
}
rv = sslBuffer_AppendVariable(&cookieBuf, echHpkeCtx->data, echHpkeCtx->len, 2);
SECITEM_ZfreeItem(echHpkeCtx, PR_TRUE);
} else {
/* Zero length HPKE context. */
rv = sslBuffer_AppendNumber(&cookieBuf, 0, 2);
}
if (rv != SECSuccess) {
return SECFailure;
}
} else {
rv = sslBuffer_AppendNumber(&cookieBuf, PR_FALSE, 1);
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;
}
/* Given a cookie and cookieLen, decrypt and parse, returning
* any values that were requested via the "previous_" params. If
* recoverState is true, the transcript state and application
* token are restored. Note that previousEchKdfId, previousEchAeadId,
* previousEchConfigId, and previousEchHpkeCtx are not modified if ECH was not
* previously negotiated (i.e., previousEchOffered is PR_FALSE). */
SECStatus
tls13_HandleHrrCookie(sslSocket *ss,
unsigned char *cookie, unsigned int cookieLen,
ssl3CipherSuite *previousCipherSuite,
const sslNamedGroupDef **previousGroup,
PRBool *previousOfferedEch,
sslEchCookieData *echData,
PRBool recoverState)
{
SECStatus rv;
unsigned char plaintext[1024];
unsigned int plaintextLen = 0;
sslBuffer messageBuf = SSL_BUFFER_EMPTY;
sslReadBuffer echHpkeBuf = { 0 };
PRBool receivedEch;
PRUint64 sentinel;
PRUint64 cipherSuite;
sslEchCookieData parsedEchData = { 0 };
sslReadBuffer greaseReadBuf = { 0 };
PRUint64 group;
PRUint64 tmp64;
const sslNamedGroupDef *selectedGroup;
PRUint64 appTokenLen;
rv = ssl_SelfEncryptUnprotect(ss, cookie, cookieLen,
plaintext, &plaintextLen, sizeof(plaintext));
if (rv != SECSuccess) {
SSL_TRC(100, ("Error decrypting cookie."));
return SECFailure;
}
sslReader reader = SSL_READER(plaintext, plaintextLen);
/* Should start with the sentinel value. */
rv = sslRead_ReadNumber(&reader, 1, &sentinel);
if ((rv != SECSuccess) || (sentinel != TLS13_COOKIE_SENTINEL)) {
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);
/* Was ECH received. */
rv = sslRead_ReadNumber(&reader, 1, &tmp64);
if (rv != SECSuccess) {
FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_CLIENT_HELLO, illegal_parameter);
return SECFailure;
}
receivedEch = tmp64 == PR_TRUE;
*previousOfferedEch = receivedEch;
if (receivedEch) {
/* ECH config ID */
rv = sslRead_ReadNumber(&reader, 1, &tmp64);
if (rv != SECSuccess) {
FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_CLIENT_HELLO, illegal_parameter);
return SECFailure;
}
parsedEchData.configId = (PRUint8)tmp64;
/* ECH Ciphersuite */
rv = sslRead_ReadNumber(&reader, 2, &tmp64);
if (rv != SECSuccess) {
FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_CLIENT_HELLO, illegal_parameter);
return SECFailure;
}
parsedEchData.kdfId = (HpkeKdfId)tmp64;
rv = sslRead_ReadNumber(&reader, 2, &tmp64);
if (rv != SECSuccess) {
FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_CLIENT_HELLO, illegal_parameter);
return SECFailure;
}
parsedEchData.aeadId = (HpkeAeadId)tmp64;
/* ECH accept_confirmation signal. */
rv = sslRead_Read(&reader, TLS13_ECH_SIGNAL_LEN, &greaseReadBuf);
if (rv != SECSuccess) {
FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_CLIENT_HELLO, illegal_parameter);
return SECFailure;
}
PORT_Memcpy(parsedEchData.signal, greaseReadBuf.buf, TLS13_ECH_SIGNAL_LEN);
/* ECH HPKE context may be empty. */
rv = sslRead_ReadVariable(&reader, 2, &echHpkeBuf);
if (rv != SECSuccess) {
FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_CLIENT_HELLO, illegal_parameter);
return SECFailure;
}
if (echData && echHpkeBuf.len) {
const SECItem hpkeItem = { siBuffer, CONST_CAST(unsigned char, echHpkeBuf.buf),
echHpkeBuf.len };
parsedEchData.hpkeCtx = PK11_HPKE_ImportContext(&hpkeItem, NULL);
if (!parsedEchData.hpkeCtx) {
FATAL_ERROR(ss, PORT_GetError(), illegal_parameter);
return SECFailure;
}
}
}
/* Application token. */
rv = sslRead_ReadNumber(&reader, 2, &appTokenLen);
if (rv != SECSuccess) {
FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_CLIENT_HELLO, illegal_parameter);
return SECFailure;
}
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);
if (recoverState) {
PORT_Assert(ss->xtnData.applicationToken.len == 0);
if (SECITEM_AllocItem(NULL, &ss->xtnData.applicationToken,
appTokenLen) == NULL) {
FATAL_ERROR(ss, PORT_GetError(), internal_error);
return SECFailure;
}
PORT_Memcpy(ss->xtnData.applicationToken.data, appTokenReader.buf, appTokenLen);
ss->xtnData.applicationToken.len = 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,
ssl3_UpdateHandshakeHashes);
if (rv != SECSuccess) {
return SECFailure;
}
/* And finally reinject the HRR. */
rv = tls13_ConstructHelloRetryRequest(ss, cipherSuite,
selectedGroup,
cookie, cookieLen,
parsedEchData.signal,
&messageBuf);
if (rv != SECSuccess) {
return SECFailure;
}
rv = ssl_HashHandshakeMessageInt(ss, ssl_hs_server_hello, 0,
SSL_BUFFER_BASE(&messageBuf),
SSL_BUFFER_LEN(&messageBuf),
ssl3_UpdateHandshakeHashes);
sslBuffer_Clear(&messageBuf);
if (rv != SECSuccess) {
return SECFailure;
}
}
if (previousCipherSuite) {
*previousCipherSuite = cipherSuite;
}
if (previousGroup) {
*previousGroup = selectedGroup;
}
if (echData) {
PORT_Memcpy(echData, &parsedEchData, sizeof(parsedEchData));
}
return SECSuccess;
}
|