diff options
Diffstat (limited to 'dom/quota/NSSCipherStrategy.cpp')
-rw-r--r-- | dom/quota/NSSCipherStrategy.cpp | 154 |
1 files changed, 154 insertions, 0 deletions
diff --git a/dom/quota/NSSCipherStrategy.cpp b/dom/quota/NSSCipherStrategy.cpp new file mode 100644 index 0000000000..9d1da39ad8 --- /dev/null +++ b/dom/quota/NSSCipherStrategy.cpp @@ -0,0 +1,154 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=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 "NSSCipherStrategy.h" + +#include <algorithm> +#include <cstdlib> +#include <cstring> +#include <memory> +#include <type_traits> +#include <utility> +#include "mozilla/Assertions.h" +#include "mozilla/ResultExtensions.h" + +// NSS includes +#include "blapit.h" +#include "pk11pub.h" +#include "pkcs11t.h" +#include "seccomon.h" +#include "secmodt.h" + +namespace mozilla::dom::quota { + +static_assert(sizeof(NSSCipherStrategy::KeyType) == 32); +static_assert(NSSCipherStrategy::BlockPrefixLength == 32); +static_assert(NSSCipherStrategy::BasicBlockSize == 16); + +Result<NSSCipherStrategy::KeyType, nsresult> NSSCipherStrategy::GenerateKey() { + const auto slot = UniquePK11SlotInfo{PK11_GetInternalSlot()}; + if (slot == nullptr) { + return Err(NS_ERROR_FAILURE); + } + const auto symKey = UniquePK11SymKey{PK11_KeyGen( + slot.get(), CKM_CHACHA20_KEY_GEN, nullptr, sizeof(KeyType), nullptr)}; + if (symKey == nullptr) { + return Err(NS_ERROR_FAILURE); + } + if (PK11_ExtractKeyValue(symKey.get()) != SECSuccess) { + return Err(NS_ERROR_FAILURE); + } + // No need to free keyData as it is a buffer managed by symKey. + SECItem* keyData = PK11_GetKeyData(symKey.get()); + if (keyData == nullptr) { + return Err(NS_ERROR_FAILURE); + } + KeyType key; + MOZ_RELEASE_ASSERT(keyData->len == key.size()); + std::copy(keyData->data, keyData->data + key.size(), key.data()); + return key; +} + +nsresult NSSCipherStrategy::Init(const CipherMode aMode, + const Span<const uint8_t> aKey, + const Span<const uint8_t> aInitialIv) { + MOZ_ASSERT_IF(CipherMode::Encrypt == aMode, aInitialIv.Length() == 32); + + mMode.init(aMode); + + mIv.AppendElements(aInitialIv); + + const auto slot = UniquePK11SlotInfo{PK11_GetInternalSlot()}; + if (slot == nullptr) { + return NS_ERROR_FAILURE; + } + + SECItem keyItem; + keyItem.data = const_cast<uint8_t*>(aKey.Elements()); + keyItem.len = aKey.Length(); + const auto symKey = UniquePK11SymKey{ + PK11_ImportSymKey(slot.get(), CKM_CHACHA20_POLY1305, PK11_OriginUnwrap, + CKA_ENCRYPT, &keyItem, nullptr)}; + if (symKey == nullptr) { + return NS_ERROR_FAILURE; + } + + SECItem empty = {siBuffer, nullptr, 0}; + auto pk11Context = UniquePK11Context{PK11_CreateContextBySymKey( + CKM_CHACHA20_POLY1305, + CKA_NSS_MESSAGE | + (CipherMode::Encrypt == aMode ? CKA_ENCRYPT : CKA_DECRYPT), + symKey.get(), &empty)}; + if (pk11Context == nullptr) { + return NS_ERROR_FAILURE; + } + + mPK11Context.init(std::move(pk11Context)); + return NS_OK; +} + +nsresult NSSCipherStrategy::Cipher(const Span<uint8_t> aIv, + const Span<const uint8_t> aIn, + const Span<uint8_t> aOut) { + if (CipherMode::Encrypt == *mMode) { + MOZ_RELEASE_ASSERT(aIv.Length() == mIv.Length()); + memcpy(aIv.Elements(), mIv.Elements(), aIv.Length()); + } + + // XXX make tag a separate parameter + constexpr size_t tagLen = 16; + const auto tag = aIv.Last(tagLen); + // tag is const on decrypt, but returned on encrypt + + const auto iv = aIv.First(12); + MOZ_ASSERT(tag.Length() + iv.Length() <= aIv.Length()); + + int outLen; + // aIn and aOut may not overlap resp. be the same, so we can't do this + // in-place. + const SECStatus rv = PK11_AEADOp( + mPK11Context->get(), CKG_GENERATE_COUNTER, 0, iv.Elements(), iv.Length(), + nullptr, 0, aOut.Elements(), &outLen, aOut.Length(), tag.Elements(), + tag.Length(), aIn.Elements(), aIn.Length()); + + if (CipherMode::Encrypt == *mMode) { + memcpy(mIv.Elements(), aIv.Elements(), aIv.Length()); + } + + return MapSECStatus(rv); +} + +template <size_t N> +static std::array<uint8_t, N> MakeRandomData() { + std::array<uint8_t, N> res; + + const auto rv = PK11_GenerateRandom(res.data(), res.size()); + /// XXX Allow return of error code to handle this gracefully. + MOZ_RELEASE_ASSERT(rv == SECSuccess); + + return res; +} + +std::array<uint8_t, NSSCipherStrategy::BlockPrefixLength> +NSSCipherStrategy::MakeBlockPrefix() { + return MakeRandomData<BlockPrefixLength>(); +} + +Span<const uint8_t> NSSCipherStrategy::SerializeKey(const KeyType& aKey) { + return Span(aKey); +} + +Maybe<NSSCipherStrategy::KeyType> NSSCipherStrategy::DeserializeKey( + const Span<const uint8_t>& aSerializedKey) { + KeyType res; + if (res.size() != aSerializedKey.size()) { + return Nothing(); + } + std::copy(aSerializedKey.cbegin(), aSerializedKey.cend(), res.begin()); + return Some(res); +} + +} // namespace mozilla::dom::quota |