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"
13 #include <type_traits>
15 #include "mozilla/Assertions.h"
16 #include "mozilla/ResultExtensions.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
);
50 MOZ_RELEASE_ASSERT(keyData
->len
== key
.size());
51 std::copy(keyData
->data
, keyData
->data
+ key
.size(), key
.data());
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);
62 mIv
.AppendElements(aInitialIv
);
64 const auto slot
= UniquePK11SlotInfo
{PK11_GetInternalSlot()};
65 if (slot
== nullptr) {
66 return NS_ERROR_FAILURE
;
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
,
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
));
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());
110 // aIn and aOut may not overlap resp. be the same, so we can't do this
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
);
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
);
135 std::array
<uint8_t, NSSCipherStrategy::BlockPrefixLength
>
136 NSSCipherStrategy::MakeBlockPrefix() {
137 return MakeRandomData
<BlockPrefixLength
>();
140 Span
<const uint8_t> NSSCipherStrategy::SerializeKey(const KeyType
& aKey
) {
144 NSSCipherStrategy::KeyType
NSSCipherStrategy::DeserializeKey(
145 const Span
<const uint8_t>& aSerializedKey
) {
147 MOZ_ASSERT(res
.size() == aSerializedKey
.size());
148 std::copy(aSerializedKey
.cbegin(), aSerializedKey
.cend(), res
.begin());
152 } // namespace mozilla::dom::quota