NSSCipherStrategy.cpp (4898B)
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 <utility> 13 14 #include "mozilla/Assertions.h" 15 16 // NSS includes 17 #include "blapit.h" 18 #include "nsNSSComponent.h" 19 #include "pk11pub.h" 20 #include "pkcs11t.h" 21 #include "seccomon.h" 22 #include "secmodt.h" 23 24 namespace mozilla::dom::quota { 25 26 static_assert(sizeof(NSSCipherStrategy::KeyType) == 32); 27 static_assert(NSSCipherStrategy::BlockPrefixLength == 32); 28 static_assert(NSSCipherStrategy::BasicBlockSize == 16); 29 30 Result<NSSCipherStrategy::KeyType, nsresult> NSSCipherStrategy::GenerateKey() { 31 const auto slot = UniquePK11SlotInfo{PK11_GetInternalSlot()}; 32 if (slot == nullptr) { 33 return Err(NS_ERROR_FAILURE); 34 } 35 const auto symKey = UniquePK11SymKey{PK11_KeyGen( 36 slot.get(), CKM_CHACHA20_KEY_GEN, nullptr, sizeof(KeyType), nullptr)}; 37 if (symKey == nullptr) { 38 return Err(NS_ERROR_FAILURE); 39 } 40 if (PK11_ExtractKeyValue(symKey.get()) != SECSuccess) { 41 return Err(NS_ERROR_FAILURE); 42 } 43 // No need to free keyData as it is a buffer managed by symKey. 44 SECItem* keyData = PK11_GetKeyData(symKey.get()); 45 if (keyData == nullptr) { 46 return Err(NS_ERROR_FAILURE); 47 } 48 KeyType key; 49 MOZ_RELEASE_ASSERT(keyData->len == key.size()); 50 std::copy(keyData->data, keyData->data + key.size(), key.data()); 51 return key; 52 } 53 54 nsresult NSSCipherStrategy::Init(const CipherMode aMode, 55 const Span<const uint8_t> aKey, 56 const Span<const uint8_t> aInitialIv) { 57 MOZ_ASSERT_IF(CipherMode::Encrypt == aMode, aInitialIv.Length() == 32); 58 MOZ_RELEASE_ASSERT(EnsureNSSInitializedChromeOrContent(), 59 "Could not initialize NSS."); 60 61 mMode.init(aMode); 62 63 mIv.AppendElements(aInitialIv); 64 65 const auto slot = UniquePK11SlotInfo{PK11_GetInternalSlot()}; 66 if (slot == nullptr) { 67 return NS_ERROR_FAILURE; 68 } 69 70 SECItem keyItem; 71 keyItem.data = const_cast<uint8_t*>(aKey.Elements()); 72 keyItem.len = aKey.Length(); 73 const auto symKey = UniquePK11SymKey{ 74 PK11_ImportSymKey(slot.get(), CKM_CHACHA20_POLY1305, PK11_OriginUnwrap, 75 CKA_ENCRYPT, &keyItem, nullptr)}; 76 if (symKey == nullptr) { 77 return NS_ERROR_FAILURE; 78 } 79 80 SECItem empty = {siBuffer, nullptr, 0}; 81 auto pk11Context = UniquePK11Context{PK11_CreateContextBySymKey( 82 CKM_CHACHA20_POLY1305, 83 CKA_NSS_MESSAGE | 84 (CipherMode::Encrypt == aMode ? CKA_ENCRYPT : CKA_DECRYPT), 85 symKey.get(), &empty)}; 86 if (pk11Context == nullptr) { 87 return NS_ERROR_FAILURE; 88 } 89 90 mPK11Context.init(std::move(pk11Context)); 91 return NS_OK; 92 } 93 94 nsresult NSSCipherStrategy::Cipher(const Span<uint8_t> aIv, 95 const Span<const uint8_t> aIn, 96 const Span<uint8_t> aOut) { 97 if (CipherMode::Encrypt == *mMode) { 98 MOZ_RELEASE_ASSERT(aIv.Length() == mIv.Length()); 99 memcpy(aIv.Elements(), mIv.Elements(), aIv.Length()); 100 } 101 102 // XXX make tag a separate parameter 103 constexpr size_t tagLen = 16; 104 const auto tag = aIv.Last(tagLen); 105 // tag is const on decrypt, but returned on encrypt 106 107 const auto iv = aIv.First(12); 108 MOZ_ASSERT(tag.Length() + iv.Length() <= aIv.Length()); 109 110 int outLen; 111 // aIn and aOut may not overlap resp. be the same, so we can't do this 112 // in-place. 113 const SECStatus rv = PK11_AEADOp( 114 mPK11Context->get(), CKG_GENERATE_COUNTER, 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> 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> 137 NSSCipherStrategy::MakeBlockPrefix() { 138 return MakeRandomData<BlockPrefixLength>(); 139 } 140 141 Span<const uint8_t> NSSCipherStrategy::SerializeKey(const KeyType& aKey) { 142 return Span(aKey); 143 } 144 145 Maybe<NSSCipherStrategy::KeyType> NSSCipherStrategy::DeserializeKey( 146 const Span<const uint8_t>& aSerializedKey) { 147 KeyType res; 148 if (res.size() != aSerializedKey.size()) { 149 return Nothing(); 150 } 151 std::copy(aSerializedKey.cbegin(), aSerializedKey.cend(), res.begin()); 152 return Some(res); 153 } 154 155 } // namespace mozilla::dom::quota