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/. */
6 
7 #include "NSSCipherStrategy.h"
8 
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"
17 
18 // NSS includes
19 #include "blapit.h"
20 #include "pk11pub.h"
21 #include "pkcs11t.h"
22 #include "seccomon.h"
23 #include "secmodt.h"
24 
25 namespace mozilla::dom::quota {
26 
27 static_assert(sizeof(NSSCipherStrategy::KeyType) == 32);
28 static_assert(NSSCipherStrategy::BlockPrefixLength == 32);
29 static_assert(NSSCipherStrategy::BasicBlockSize == 16);
30 
GenerateKey()31 Result<NSSCipherStrategy::KeyType, nsresult> NSSCipherStrategy::GenerateKey() {
32   const auto slot = UniquePK11SlotInfo{PK11_GetInternalSlot()};
33   if (slot == nullptr) {
34     return Err(NS_ERROR_FAILURE);
35   }
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);
40   }
41   if (PK11_ExtractKeyValue(symKey.get()) != SECSuccess) {
42     return Err(NS_ERROR_FAILURE);
43   }
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);
48   }
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;
53 }
54 
Init(const CipherMode aMode,const Span<const uint8_t> aKey,const Span<const uint8_t> aInitialIv)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);
59 
60   mMode.init(aMode);
61 
62   mIv.AppendElements(aInitialIv);
63 
64   const auto slot = UniquePK11SlotInfo{PK11_GetInternalSlot()};
65   if (slot == nullptr) {
66     return NS_ERROR_FAILURE;
67   }
68 
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;
77   }
78 
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;
87   }
88 
89   mPK11Context.init(std::move(pk11Context));
90   return NS_OK;
91 }
92 
Cipher(const Span<uint8_t> aIv,const Span<const uint8_t> aIn,const Span<uint8_t> aOut)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());
99   }
100 
101   // XXX make tag a separate parameter
102   constexpr size_t tagLen = 16;
103   const auto tag = Span{aIv}.Last(tagLen);
104   // tag is const on decrypt, but returned on encrypt
105 
106   const auto iv = Span{aIv}.First(12);
107   MOZ_ASSERT(tag.Length() + iv.Length() <= aIv.Length());
108 
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,
114       /* TODO use this for the discriminator */ 0, iv.Elements(), iv.Length(),
115       nullptr, 0, aOut.Elements(), &outLen, aOut.Length(), tag.Elements(),
116       tag.Length(), aIn.Elements(), aIn.Length());
117 
118   if (CipherMode::Encrypt == *mMode) {
119     memcpy(mIv.Elements(), aIv.Elements(), aIv.Length());
120   }
121 
122   return MapSECStatus(rv);
123 }
124 
125 template <size_t N>
MakeRandomData()126 static std::array<uint8_t, N> MakeRandomData() {
127   std::array<uint8_t, N> res;
128 
129   const auto rv = PK11_GenerateRandom(res.data(), res.size());
130   /// XXX Allow return of error code to handle this gracefully.
131   MOZ_RELEASE_ASSERT(rv == SECSuccess);
132 
133   return res;
134 }
135 
136 std::array<uint8_t, NSSCipherStrategy::BlockPrefixLength>
MakeBlockPrefix()137 NSSCipherStrategy::MakeBlockPrefix() {
138   return MakeRandomData<BlockPrefixLength>();
139 }
140 
SerializeKey(const KeyType & aKey)141 Span<const uint8_t> NSSCipherStrategy::SerializeKey(const KeyType& aKey) {
142   return Span(aKey);
143 }
144 
DeserializeKey(const Span<const uint8_t> & aSerializedKey)145 NSSCipherStrategy::KeyType NSSCipherStrategy::DeserializeKey(
146     const Span<const uint8_t>& aSerializedKey) {
147   KeyType res;
148   MOZ_ASSERT(res.size() == aSerializedKey.size());
149   std::copy(aSerializedKey.cbegin(), aSerializedKey.cend(), res.begin());
150   return res;
151 }
152 
153 }  // namespace mozilla::dom::quota
154