blob: 6187660a5cc5a2e91f487c571817e88c581b0c30 [file] [log] [blame]
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* 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 "tls_protect.h"
#include "sslproto.h"
#include "tls_filter.h"
namespace nss_test {
static uint64_t FirstSeqno(bool dtls, uint16_t epoc) {
if (dtls) {
return static_cast<uint64_t>(epoc) << 48;
}
return 0;
}
TlsCipherSpec::TlsCipherSpec(bool dtls, uint16_t epoc)
: dtls_(dtls),
epoch_(epoc),
in_seqno_(FirstSeqno(dtls, epoc)),
out_seqno_(FirstSeqno(dtls, epoc)) {}
bool TlsCipherSpec::SetKeys(SSLCipherSuiteInfo* cipherinfo,
PK11SymKey* secret) {
SSLAeadContext* aead_ctx;
SSLProtocolVariant variant =
dtls_ ? ssl_variant_datagram : ssl_variant_stream;
SECStatus rv =
SSL_MakeVariantAead(SSL_LIBRARY_VERSION_TLS_1_3, cipherinfo->cipherSuite,
variant, secret, "", 0, // Use the default labels.
&aead_ctx);
if (rv != SECSuccess) {
return false;
}
aead_.reset(aead_ctx);
SSLMaskingContext* mask_ctx;
const char kHkdfPurposeSn[] = "sn";
rv = SSL_CreateVariantMaskingContext(
SSL_LIBRARY_VERSION_TLS_1_3, cipherinfo->cipherSuite, variant, secret,
kHkdfPurposeSn, strlen(kHkdfPurposeSn), &mask_ctx);
if (rv != SECSuccess) {
return false;
}
mask_.reset(mask_ctx);
return true;
}
bool TlsCipherSpec::Unprotect(const TlsRecordHeader& header,
const DataBuffer& ciphertext,
DataBuffer* plaintext,
TlsRecordHeader* out_header) {
if (!aead_ || !out_header) {
return false;
}
*out_header = header;
// Make space.
plaintext->Allocate(ciphertext.len());
unsigned int len;
uint64_t seqno = dtls_ ? header.sequence_number() : in_seqno_;
SECStatus rv;
if (header.is_dtls13_ciphertext()) {
if (!mask_ || !out_header) {
return false;
}
PORT_Assert(ciphertext.len() >= 16);
DataBuffer mask(2);
rv = SSL_CreateMask(mask_.get(), ciphertext.data(), ciphertext.len(),
mask.data(), mask.len());
if (rv != SECSuccess) {
return false;
}
if (!out_header->MaskSequenceNumber(mask)) {
return false;
}
seqno = out_header->sequence_number();
}
auto header_bytes = out_header->header();
rv = SSL_AeadDecrypt(aead_.get(), seqno, header_bytes.data(),
header_bytes.len(), ciphertext.data(), ciphertext.len(),
plaintext->data(), &len, plaintext->len());
if (rv != SECSuccess) {
return false;
}
RecordUnprotected(seqno);
plaintext->Truncate(static_cast<size_t>(len));
return true;
}
bool TlsCipherSpec::Protect(const TlsRecordHeader& header,
const DataBuffer& plaintext, DataBuffer* ciphertext,
TlsRecordHeader* out_header) {
if (!aead_ || !out_header) {
return false;
}
*out_header = header;
// Make a padded buffer.
ciphertext->Allocate(plaintext.len() +
32); // Room for any plausible auth tag
unsigned int len;
DataBuffer header_bytes;
(void)header.WriteHeader(&header_bytes, 0, plaintext.len() + 16);
uint64_t seqno = dtls_ ? header.sequence_number() : out_seqno_;
SECStatus rv =
SSL_AeadEncrypt(aead_.get(), seqno, header_bytes.data(),
header_bytes.len(), plaintext.data(), plaintext.len(),
ciphertext->data(), &len, ciphertext->len());
if (rv != SECSuccess) {
return false;
}
if (header.is_dtls13_ciphertext()) {
if (!mask_ || !out_header) {
return false;
}
PORT_Assert(ciphertext->len() >= 16);
DataBuffer mask(2);
rv = SSL_CreateMask(mask_.get(), ciphertext->data(), ciphertext->len(),
mask.data(), mask.len());
if (rv != SECSuccess) {
return false;
}
if (!out_header->MaskSequenceNumber(mask)) {
return false;
}
}
RecordProtected();
ciphertext->Truncate(len);
return true;
}
} // namespace nss_test