Bug 1833854 - Part 4: Move all code that deals with maintaining invariants into a...
[gecko.git] / dom / quota / NSSCipherStrategy.cpp
blob6e74456d5d83a243d64fe339c983dc2e4a4e743c
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "NSSCipherStrategy.h"
9 #include <algorithm>
10 #include <cstdlib>
11 #include <cstring>
12 #include <memory>
13 #include <type_traits>
14 #include <utility>
15 #include "mozilla/Assertions.h"
16 #include "mozilla/ResultExtensions.h"
18 // NSS includes
19 #include "blapit.h"
20 #include "pk11pub.h"
21 #include "pkcs11t.h"
22 #include "seccomon.h"
23 #include "secmodt.h"
25 namespace mozilla::dom::quota {
27 static_assert(sizeof(NSSCipherStrategy::KeyType) == 32);
28 static_assert(NSSCipherStrategy::BlockPrefixLength == 32);
29 static_assert(NSSCipherStrategy::BasicBlockSize == 16);
31 Result<NSSCipherStrategy::KeyType, nsresult> NSSCipherStrategy::GenerateKey() {
32 const auto slot = UniquePK11SlotInfo{PK11_GetInternalSlot()};
33 if (slot == nullptr) {
34 return Err(NS_ERROR_FAILURE);
36 const auto symKey = UniquePK11SymKey{PK11_KeyGen(
37 slot.get(), CKM_CHACHA20_KEY_GEN, nullptr, sizeof(KeyType), nullptr)};
38 if (symKey == nullptr) {
39 return Err(NS_ERROR_FAILURE);
41 if (PK11_ExtractKeyValue(symKey.get()) != SECSuccess) {
42 return Err(NS_ERROR_FAILURE);
44 // No need to free keyData as it is a buffer managed by symKey.
45 SECItem* keyData = PK11_GetKeyData(symKey.get());
46 if (keyData == nullptr) {
47 return Err(NS_ERROR_FAILURE);
49 KeyType key;
50 MOZ_RELEASE_ASSERT(keyData->len == key.size());
51 std::copy(keyData->data, keyData->data + key.size(), key.data());
52 return key;
55 nsresult NSSCipherStrategy::Init(const CipherMode aMode,
56 const Span<const uint8_t> aKey,
57 const Span<const uint8_t> aInitialIv) {
58 MOZ_ASSERT_IF(CipherMode::Encrypt == aMode, aInitialIv.Length() == 32);
60 mMode.init(aMode);
62 mIv.AppendElements(aInitialIv);
64 const auto slot = UniquePK11SlotInfo{PK11_GetInternalSlot()};
65 if (slot == nullptr) {
66 return NS_ERROR_FAILURE;
69 SECItem keyItem;
70 keyItem.data = const_cast<uint8_t*>(aKey.Elements());
71 keyItem.len = aKey.Length();
72 const auto symKey = UniquePK11SymKey{
73 PK11_ImportSymKey(slot.get(), CKM_CHACHA20_POLY1305, PK11_OriginUnwrap,
74 CKA_ENCRYPT, &keyItem, nullptr)};
75 if (symKey == nullptr) {
76 return NS_ERROR_FAILURE;
79 SECItem empty = {siBuffer, nullptr, 0};
80 auto pk11Context = UniquePK11Context{PK11_CreateContextBySymKey(
81 CKM_CHACHA20_POLY1305,
82 CKA_NSS_MESSAGE |
83 (CipherMode::Encrypt == aMode ? CKA_ENCRYPT : CKA_DECRYPT),
84 symKey.get(), &empty)};
85 if (pk11Context == nullptr) {
86 return NS_ERROR_FAILURE;
89 mPK11Context.init(std::move(pk11Context));
90 return NS_OK;
93 nsresult NSSCipherStrategy::Cipher(const Span<uint8_t> aIv,
94 const Span<const uint8_t> aIn,
95 const Span<uint8_t> aOut) {
96 if (CipherMode::Encrypt == *mMode) {
97 MOZ_RELEASE_ASSERT(aIv.Length() == mIv.Length());
98 memcpy(aIv.Elements(), mIv.Elements(), aIv.Length());
101 // XXX make tag a separate parameter
102 constexpr size_t tagLen = 16;
103 const auto tag = aIv.Last(tagLen);
104 // tag is const on decrypt, but returned on encrypt
106 const auto iv = aIv.First(12);
107 MOZ_ASSERT(tag.Length() + iv.Length() <= aIv.Length());
109 int outLen;
110 // aIn and aOut may not overlap resp. be the same, so we can't do this
111 // in-place.
112 const SECStatus rv = PK11_AEADOp(
113 mPK11Context->get(), CKG_GENERATE_COUNTER, 0, iv.Elements(), iv.Length(),
114 nullptr, 0, aOut.Elements(), &outLen, aOut.Length(), tag.Elements(),
115 tag.Length(), aIn.Elements(), aIn.Length());
117 if (CipherMode::Encrypt == *mMode) {
118 memcpy(mIv.Elements(), aIv.Elements(), aIv.Length());
121 return MapSECStatus(rv);
124 template <size_t N>
125 static std::array<uint8_t, N> MakeRandomData() {
126 std::array<uint8_t, N> res;
128 const auto rv = PK11_GenerateRandom(res.data(), res.size());
129 /// XXX Allow return of error code to handle this gracefully.
130 MOZ_RELEASE_ASSERT(rv == SECSuccess);
132 return res;
135 std::array<uint8_t, NSSCipherStrategy::BlockPrefixLength>
136 NSSCipherStrategy::MakeBlockPrefix() {
137 return MakeRandomData<BlockPrefixLength>();
140 Span<const uint8_t> NSSCipherStrategy::SerializeKey(const KeyType& aKey) {
141 return Span(aKey);
144 NSSCipherStrategy::KeyType NSSCipherStrategy::DeserializeKey(
145 const Span<const uint8_t>& aSerializedKey) {
146 KeyType res;
147 MOZ_ASSERT(res.size() == aSerializedKey.size());
148 std::copy(aSerializedKey.cbegin(), aSerializedKey.cend(), res.begin());
149 return res;
152 } // namespace mozilla::dom::quota