ScopedNSSTypes.h (16201B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=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 // This header provides smart pointers and various helpers for code that needs 8 // to interact with NSS. 9 10 #ifndef ScopedNSSTypes_h 11 #define ScopedNSSTypes_h 12 13 #include <limits> 14 #include <memory> 15 16 #include "cert.h" 17 #include "cms.h" 18 #include "cryptohi.h" 19 #include "keyhi.h" 20 #include "mozilla/Likely.h" 21 #include "nsDebug.h" 22 #include "nsError.h" 23 #include "NSSErrorsService.h" 24 #include "pk11hpke.h" 25 #include "pk11pub.h" 26 #include "pkcs12.h" 27 #include "prerror.h" 28 #include "prio.h" 29 #include "prmem.h" 30 #include "sechash.h" 31 #include "secmod.h" 32 #include "secpkcs7.h" 33 #include "secport.h" 34 35 #ifndef MOZ_NO_MOZALLOC 36 # include "mozilla/mozalloc_oom.h" 37 #endif 38 39 // Normally this would be included from nsNSSComponent.h, but that file includes 40 // this file. 41 bool EnsureNSSInitializedChromeOrContent(); 42 43 namespace mozilla { 44 45 // NSPR APIs use PRStatus/PR_GetError and NSS APIs use SECStatus/PR_GetError to 46 // report success/failure. This function makes it more convenient and *safer* 47 // to translate NSPR/NSS results to nsresult. It is safer because it 48 // refuses to translate any bad PRStatus/SECStatus into an NS_OK, even when the 49 // NSPR/NSS function forgot to call PR_SetError. The actual enforcement of 50 // this happens in mozilla::psm::GetXPCOMFromNSSError. 51 // IMPORTANT: This must be called immediately after the function returning the 52 // SECStatus result. The recommended usage is: 53 // nsresult rv = MapSECStatus(f(x, y, z)); 54 inline nsresult MapSECStatus(SECStatus rv) { 55 if (rv == SECSuccess) { 56 return NS_OK; 57 } 58 59 return mozilla::psm::GetXPCOMFromNSSError(PR_GetError()); 60 } 61 62 namespace internal { 63 64 inline void PK11_DestroyContext_true(PK11Context* ctx) { 65 PK11_DestroyContext(ctx, true); 66 } 67 68 inline void SECKEYEncryptedPrivateKeyInfo_true( 69 SECKEYEncryptedPrivateKeyInfo* epki) { 70 SECKEY_DestroyEncryptedPrivateKeyInfo(epki, true); 71 } 72 73 // If this was created via PK11_ListFixedKeysInSlot, we may have a list of keys, 74 // in which case we have to free them all (and if not, this will still free the 75 // one key). 76 inline void FreeOneOrMoreSymKeys(PK11SymKey* keys) { 77 PK11SymKey* next; 78 while (keys) { 79 next = PK11_GetNextSymKey(keys); 80 PK11_FreeSymKey(keys); 81 keys = next; 82 } 83 } 84 85 } // namespace internal 86 87 // Emulates MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE, but for UniquePtrs. 88 #define MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(name, Type, Deleter) \ 89 struct name##DeletePolicy { \ 90 void operator()(Type* aValue) { Deleter(aValue); } \ 91 }; \ 92 typedef std::unique_ptr<Type, name##DeletePolicy> name; 93 94 MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniquePK11Context, PK11Context, 95 internal::PK11_DestroyContext_true) 96 MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniquePK11SlotInfo, PK11SlotInfo, 97 PK11_FreeSlot) 98 MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniquePK11SymKey, PK11SymKey, 99 internal::FreeOneOrMoreSymKeys) 100 101 // Common base class for Digest and HMAC. Should not be used directly. 102 // Subclasses must implement a `Begin` function that initializes 103 // `mDigestContext` and calls `SetLength`. 104 class DigestBase { 105 protected: 106 explicit DigestBase() : mLen(0), mDigestContext(nullptr) {} 107 108 public: 109 nsresult Update(Span<const uint8_t> in) { 110 return Update(in.Elements(), in.Length()); 111 } 112 113 nsresult Update(const unsigned char* buf, const uint32_t len) { 114 if (!mDigestContext) { 115 return NS_ERROR_NOT_INITIALIZED; 116 } 117 return MapSECStatus(PK11_DigestOp(mDigestContext.get(), buf, len)); 118 } 119 120 nsresult End(/*out*/ nsTArray<uint8_t>& out) { 121 if (!mDigestContext) { 122 return NS_ERROR_NOT_INITIALIZED; 123 } 124 out.SetLength(mLen); 125 uint32_t len; 126 nsresult rv = MapSECStatus( 127 PK11_DigestFinal(mDigestContext.get(), out.Elements(), &len, mLen)); 128 NS_ENSURE_SUCCESS(rv, rv); 129 mDigestContext = nullptr; 130 NS_ENSURE_TRUE(len == mLen, NS_ERROR_UNEXPECTED); 131 132 return NS_OK; 133 } 134 135 protected: 136 nsresult SetLength(SECOidTag hashType) { 137 switch (hashType) { 138 case SEC_OID_MD5: 139 mLen = MD5_LENGTH; 140 break; 141 case SEC_OID_SHA1: 142 mLen = SHA1_LENGTH; 143 break; 144 case SEC_OID_SHA256: 145 mLen = SHA256_LENGTH; 146 break; 147 case SEC_OID_SHA384: 148 mLen = SHA384_LENGTH; 149 break; 150 case SEC_OID_SHA512: 151 mLen = SHA512_LENGTH; 152 break; 153 default: 154 return NS_ERROR_INVALID_ARG; 155 } 156 return NS_OK; 157 } 158 159 private: 160 uint8_t mLen; 161 162 protected: 163 UniquePK11Context mDigestContext; 164 }; 165 166 /** A more convenient way of dealing with digests calculated into 167 * stack-allocated buffers. NSS must be initialized on the main thread before 168 * use, and the caller must ensure NSS isn't shut down, typically by 169 * being within the lifetime of XPCOM. 170 * 171 * Typical usage, for digesting a buffer in memory: 172 * 173 * nsCOMPtr<nsISupports> nssDummy = do_GetService("@mozilla.org/psm;1", &rv); 174 * nsTArray<uint8_t> digestArray; 175 * nsresult rv = Digest::DigestBuf(SEC_OID_SHA256, mybuffer, myBufferLen, 176 * digestArray); 177 * NS_ENSURE_SUCCESS(rv, rv); 178 * 179 * Less typical usage, for digesting while doing streaming I/O and similar: 180 * 181 * Digest digest; 182 * nsresult rv = digest.Begin(SEC_OID_SHA256); 183 * NS_ENSURE_SUCCESS(rv, rv); 184 * for (...) { 185 * rv = digest.Update(buf, len); 186 * NS_ENSURE_SUCCESS(rv, rv); 187 * } 188 * nsTArray<uint8_t> digestArray; 189 * rv = digest.End(digestArray); 190 * NS_ENSURE_SUCCESS(rv, rv) 191 */ 192 class Digest : public DigestBase { 193 public: 194 explicit Digest() = default; 195 196 static nsresult DigestBuf(SECOidTag hashAlg, Span<const uint8_t> buf, 197 /*out*/ nsTArray<uint8_t>& out) { 198 return Digest::DigestBuf(hashAlg, buf.Elements(), buf.Length(), out); 199 } 200 201 static nsresult DigestBuf(SECOidTag hashAlg, const uint8_t* buf, uint32_t len, 202 /*out*/ nsTArray<uint8_t>& out) { 203 Digest digest; 204 205 nsresult rv = digest.Begin(hashAlg); 206 if (NS_FAILED(rv)) { 207 return rv; 208 } 209 210 rv = digest.Update(buf, len); 211 if (NS_FAILED(rv)) { 212 return rv; 213 } 214 215 rv = digest.End(out); 216 if (NS_FAILED(rv)) { 217 return rv; 218 } 219 220 return rv; 221 } 222 223 nsresult Begin(SECOidTag hashAlg) { 224 if (!EnsureNSSInitializedChromeOrContent()) { 225 return NS_ERROR_FAILURE; 226 } 227 228 switch (hashAlg) { 229 case SEC_OID_SHA1: 230 case SEC_OID_SHA256: 231 case SEC_OID_SHA384: 232 case SEC_OID_SHA512: 233 break; 234 235 default: 236 return NS_ERROR_INVALID_ARG; 237 } 238 239 mDigestContext = UniquePK11Context(PK11_CreateDigestContext(hashAlg)); 240 if (!mDigestContext) { 241 return mozilla::psm::GetXPCOMFromNSSError(PR_GetError()); 242 } 243 244 nsresult rv = SetLength(hashAlg); 245 NS_ENSURE_SUCCESS(rv, rv); 246 return MapSECStatus(PK11_DigestBegin(mDigestContext.get())); 247 } 248 }; 249 250 // A helper class to calculate HMACs over some data given a key. 251 // Only SHA256 and, sadly, MD5 are supported at the moment. 252 // Typical usage: 253 // (ensure NSS is initialized) 254 // (obtain raw bytes for a key, some data to calculate the HMAC for) 255 // HMAC hmac; 256 // nsresult rv = hmac.Begin(SEC_OID_SHA256, Span(key)); 257 // NS_ENSURE_SUCCESS(rv, rv); 258 // rv = hmac.Update(buf, len); 259 // NS_ENSURE_SUCCESS(rv, rv); 260 // nsTArray<uint8_t> calculatedHmac; 261 // rv = hmac.End(calculatedHmac); 262 // NS_ENSURE_SUCCESS(rv, rv); 263 class HMAC : public DigestBase { 264 public: 265 explicit HMAC() = default; 266 267 nsresult Begin(SECOidTag hashAlg, Span<const uint8_t> key) { 268 if (!EnsureNSSInitializedChromeOrContent()) { 269 return NS_ERROR_FAILURE; 270 } 271 CK_MECHANISM_TYPE mechType; 272 switch (hashAlg) { 273 case SEC_OID_SHA256: 274 mechType = CKM_SHA256_HMAC; 275 break; 276 case SEC_OID_MD5: 277 mechType = CKM_MD5_HMAC; 278 break; 279 default: 280 return NS_ERROR_INVALID_ARG; 281 } 282 if (key.Length() > std::numeric_limits<unsigned int>::max()) { 283 return NS_ERROR_INVALID_ARG; 284 } 285 // SECItem's data field is a non-const unsigned char*. The good news is the 286 // data won't be mutated, but the bad news is the constness needs to be 287 // casted away. 288 SECItem keyItem = {siBuffer, const_cast<unsigned char*>(key.Elements()), 289 static_cast<unsigned int>(key.Length())}; 290 UniquePK11SlotInfo slot(PK11_GetInternalSlot()); 291 if (!slot) { 292 return mozilla::psm::GetXPCOMFromNSSError(PR_GetError()); 293 } 294 UniquePK11SymKey symKey( 295 PK11_ImportSymKey(slot.get(), CKM_GENERIC_SECRET_KEY_GEN, 296 PK11_OriginUnwrap, CKA_SIGN, &keyItem, nullptr)); 297 if (!symKey) { 298 return mozilla::psm::GetXPCOMFromNSSError(PR_GetError()); 299 } 300 SECItem emptyData = {siBuffer, nullptr, 0}; 301 mDigestContext = UniquePK11Context(PK11_CreateContextBySymKey( 302 mechType, CKA_SIGN, symKey.get(), &emptyData)); 303 if (!mDigestContext) { 304 return mozilla::psm::GetXPCOMFromNSSError(PR_GetError()); 305 } 306 307 nsresult rv = SetLength(hashAlg); 308 NS_ENSURE_SUCCESS(rv, rv); 309 return MapSECStatus(PK11_DigestBegin(mDigestContext.get())); 310 } 311 }; 312 313 namespace internal { 314 315 inline void PORT_FreeArena_false(PLArenaPool* arena) { 316 // PL_FreeArenaPool can't be used because it doesn't actually free the 317 // memory, which doesn't work well with memory analysis tools. 318 return PORT_FreeArena(arena, false); 319 } 320 321 } // namespace internal 322 323 // Wrapper around NSS's SECItem_AllocItem that handles OOM the same way as 324 // other allocators. 325 inline void SECITEM_AllocItem(SECItem& item, uint32_t len) { 326 if (MOZ_UNLIKELY(!SECITEM_AllocItem(nullptr, &item, len))) { 327 #ifndef MOZ_NO_MOZALLOC 328 mozalloc_handle_oom(len); 329 if (MOZ_UNLIKELY(!SECITEM_AllocItem(nullptr, &item, len))) 330 #endif 331 { 332 MOZ_CRASH(); 333 } 334 } 335 } 336 337 class ScopedAutoSECItem final : public SECItem { 338 public: 339 explicit ScopedAutoSECItem(uint32_t initialAllocatedLen = 0) { 340 data = nullptr; 341 len = 0; 342 if (initialAllocatedLen > 0) { 343 SECITEM_AllocItem(*this, initialAllocatedLen); 344 } 345 } 346 347 void reset() { SECITEM_FreeItem(this, false); } 348 349 ~ScopedAutoSECItem() { reset(); } 350 }; 351 352 class MOZ_RAII AutoSECMODListReadLock final { 353 public: 354 AutoSECMODListReadLock() : mLock(SECMOD_GetDefaultModuleListLock()) { 355 MOZ_ASSERT(mLock, "should have SECMOD lock (has NSS been initialized?)"); 356 SECMOD_GetReadLock(mLock); 357 } 358 359 ~AutoSECMODListReadLock() { SECMOD_ReleaseReadLock(mLock); } 360 361 private: 362 SECMODListLock* mLock; 363 }; 364 365 namespace internal { 366 367 inline void SECITEM_FreeItem_true(SECItem* s) { 368 return SECITEM_FreeItem(s, true); 369 } 370 371 inline void SECITEM_FreeArray_true(SECItemArray* s) { 372 return SECITEM_FreeArray(s, true); 373 } 374 375 inline void SECOID_DestroyAlgorithmID_true(SECAlgorithmID* a) { 376 return SECOID_DestroyAlgorithmID(a, true); 377 } 378 379 inline void VFY_DestroyContext_true(VFYContext* ctx) { 380 VFY_DestroyContext(ctx, true); 381 } 382 383 inline void PK11_HPKE_DestroyContext_true(HpkeContext* cx) { 384 PK11_HPKE_DestroyContext(cx, true); 385 } 386 387 } // namespace internal 388 389 MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniqueCERTCertificate, CERTCertificate, 390 CERT_DestroyCertificate) 391 MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniqueCERTCertificateList, 392 CERTCertificateList, 393 CERT_DestroyCertificateList) 394 MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniqueCERTCertificatePolicies, 395 CERTCertificatePolicies, 396 CERT_DestroyCertificatePoliciesExtension) 397 MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniqueCERTCertificateRequest, 398 CERTCertificateRequest, 399 CERT_DestroyCertificateRequest) 400 MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniqueCERTCertList, CERTCertList, 401 CERT_DestroyCertList) 402 MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniqueCERTName, CERTName, 403 CERT_DestroyName) 404 MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniqueCERTOidSequence, CERTOidSequence, 405 CERT_DestroyOidSequence) 406 MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniqueCERTSubjectPublicKeyInfo, 407 CERTSubjectPublicKeyInfo, 408 SECKEY_DestroySubjectPublicKeyInfo) 409 MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniqueCERTUserNotice, CERTUserNotice, 410 CERT_DestroyUserNotice) 411 MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniqueCERTValidity, CERTValidity, 412 CERT_DestroyValidity) 413 414 MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniqueHASHContext, HASHContext, 415 HASH_Destroy) 416 417 MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniqueNSSCMSMessage, NSSCMSMessage, 418 NSS_CMSMessage_Destroy) 419 MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniqueNSSCMSSignedData, NSSCMSSignedData, 420 NSS_CMSSignedData_Destroy) 421 422 MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniquePK11GenericObject, 423 PK11GenericObject, 424 PK11_DestroyGenericObject) 425 MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniquePK11SlotList, PK11SlotList, 426 PK11_FreeSlotList) 427 428 MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniquePLArenaPool, PLArenaPool, 429 internal::PORT_FreeArena_false) 430 MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniquePORTString, char, PORT_Free) 431 MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniquePRFileDesc, PRFileDesc, PR_Close) 432 MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniquePRString, char, PR_Free) 433 434 MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniqueSECAlgorithmID, SECAlgorithmID, 435 internal::SECOID_DestroyAlgorithmID_true) 436 MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniqueSECItem, SECItem, 437 internal::SECITEM_FreeItem_true) 438 MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniqueSECItemArray, SECItemArray, 439 internal::SECITEM_FreeArray_true) 440 MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniqueSECKEYPrivateKey, SECKEYPrivateKey, 441 SECKEY_DestroyPrivateKey) 442 MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniqueSECKEYPrivateKeyList, 443 SECKEYPrivateKeyList, 444 SECKEY_DestroyPrivateKeyList) 445 MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniqueSECKEYPublicKey, SECKEYPublicKey, 446 SECKEY_DestroyPublicKey) 447 MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniqueSECMODModule, SECMODModule, 448 SECMOD_DestroyModule) 449 450 MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniqueSGNDigestInfo, SGNDigestInfo, 451 SGN_DestroyDigestInfo) 452 453 MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniqueVFYContext, VFYContext, 454 internal::VFY_DestroyContext_true) 455 456 MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniqueSEC_PKCS12DecoderContext, 457 SEC_PKCS12DecoderContext, 458 SEC_PKCS12DecoderFinish) 459 MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniqueSEC_PKCS12ExportContext, 460 SEC_PKCS12ExportContext, 461 SEC_PKCS12DestroyExportContext) 462 MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE( 463 UniqueSECKEYEncryptedPrivateKeyInfo, SECKEYEncryptedPrivateKeyInfo, 464 internal::SECKEYEncryptedPrivateKeyInfo_true) 465 MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniqueHpkeContext, HpkeContext, 466 internal::PK11_HPKE_DestroyContext_true) 467 } // namespace mozilla 468 469 #endif // ScopedNSSTypes_h