/* -*- 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(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(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