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/. */
7 // This header provides smart pointers and various helpers for code that needs
8 // to interact with NSS.
10 #ifndef ScopedNSSTypes_h
11 #define ScopedNSSTypes_h
20 #include "mozilla/Likely.h"
21 #include "mozilla/UniquePtr.h"
24 #include "NSSErrorsService.h"
36 #ifndef MOZ_NO_MOZALLOC
37 # include "mozilla/mozalloc_oom.h"
40 // Normally this would be included from nsNSSComponent.h, but that file includes
42 bool EnsureNSSInitializedChromeOrContent();
46 // NSPR APIs use PRStatus/PR_GetError and NSS APIs use SECStatus/PR_GetError to
47 // report success/failure. This function makes it more convenient and *safer*
48 // to translate NSPR/NSS results to nsresult. It is safer because it
49 // refuses to translate any bad PRStatus/SECStatus into an NS_OK, even when the
50 // NSPR/NSS function forgot to call PR_SetError. The actual enforcement of
51 // this happens in mozilla::psm::GetXPCOMFromNSSError.
52 // IMPORTANT: This must be called immediately after the function returning the
53 // SECStatus result. The recommended usage is:
54 // nsresult rv = MapSECStatus(f(x, y, z));
55 inline nsresult
MapSECStatus(SECStatus rv
) {
56 if (rv
== SECSuccess
) {
60 return mozilla::psm::GetXPCOMFromNSSError(PR_GetError());
65 inline void PK11_DestroyContext_true(PK11Context
* ctx
) {
66 PK11_DestroyContext(ctx
, true);
69 inline void SECKEYEncryptedPrivateKeyInfo_true(
70 SECKEYEncryptedPrivateKeyInfo
* epki
) {
71 SECKEY_DestroyEncryptedPrivateKeyInfo(epki
, true);
74 // If this was created via PK11_ListFixedKeysInSlot, we may have a list of keys,
75 // in which case we have to free them all (and if not, this will still free the
77 inline void FreeOneOrMoreSymKeys(PK11SymKey
* keys
) {
80 next
= PK11_GetNextSymKey(keys
);
81 PK11_FreeSymKey(keys
);
86 } // namespace internal
88 // Emulates MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE, but for UniquePtrs.
89 #define MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(name, Type, Deleter) \
90 struct name##DeletePolicy { \
91 void operator()(Type* aValue) { Deleter(aValue); } \
93 typedef std::unique_ptr<Type, name##DeletePolicy> name;
95 MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniquePK11Context
, PK11Context
,
96 internal::PK11_DestroyContext_true
)
97 MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniquePK11SlotInfo
, PK11SlotInfo
,
99 MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniquePK11SymKey
, PK11SymKey
,
100 internal::FreeOneOrMoreSymKeys
)
102 // Common base class for Digest and HMAC. Should not be used directly.
103 // Subclasses must implement a `Begin` function that initializes
104 // `mDigestContext` and calls `SetLength`.
107 explicit DigestBase() : mLen(0), mDigestContext(nullptr) {}
110 nsresult
Update(Span
<const uint8_t> in
) {
111 return Update(in
.Elements(), in
.Length());
114 nsresult
Update(const unsigned char* buf
, const uint32_t len
) {
115 if (!mDigestContext
) {
116 return NS_ERROR_NOT_INITIALIZED
;
118 return MapSECStatus(PK11_DigestOp(mDigestContext
.get(), buf
, len
));
121 nsresult
End(/*out*/ nsTArray
<uint8_t>& out
) {
122 if (!mDigestContext
) {
123 return NS_ERROR_NOT_INITIALIZED
;
127 nsresult rv
= MapSECStatus(
128 PK11_DigestFinal(mDigestContext
.get(), out
.Elements(), &len
, mLen
));
129 NS_ENSURE_SUCCESS(rv
, rv
);
130 mDigestContext
= nullptr;
131 NS_ENSURE_TRUE(len
== mLen
, NS_ERROR_UNEXPECTED
);
137 nsresult
SetLength(SECOidTag hashType
) {
146 mLen
= SHA256_LENGTH
;
149 mLen
= SHA384_LENGTH
;
152 mLen
= SHA512_LENGTH
;
155 return NS_ERROR_INVALID_ARG
;
164 UniquePK11Context mDigestContext
;
167 /** A more convenient way of dealing with digests calculated into
168 * stack-allocated buffers. NSS must be initialized on the main thread before
169 * use, and the caller must ensure NSS isn't shut down, typically by
170 * being within the lifetime of XPCOM.
172 * Typical usage, for digesting a buffer in memory:
174 * nsCOMPtr<nsISupports> nssDummy = do_GetService("@mozilla.org/psm;1", &rv);
175 * nsTArray<uint8_t> digestArray;
176 * nsresult rv = Digest::DigestBuf(SEC_OID_SHA256, mybuffer, myBufferLen,
178 * NS_ENSURE_SUCCESS(rv, rv);
180 * Less typical usage, for digesting while doing streaming I/O and similar:
183 * nsresult rv = digest.Begin(SEC_OID_SHA256);
184 * NS_ENSURE_SUCCESS(rv, rv);
186 * rv = digest.Update(buf, len);
187 * NS_ENSURE_SUCCESS(rv, rv);
189 * nsTArray<uint8_t> digestArray;
190 * rv = digest.End(digestArray);
191 * NS_ENSURE_SUCCESS(rv, rv)
193 class Digest
: public DigestBase
{
195 explicit Digest() = default;
197 static nsresult
DigestBuf(SECOidTag hashAlg
, Span
<const uint8_t> buf
,
198 /*out*/ nsTArray
<uint8_t>& out
) {
199 return Digest::DigestBuf(hashAlg
, buf
.Elements(), buf
.Length(), out
);
202 static nsresult
DigestBuf(SECOidTag hashAlg
, const uint8_t* buf
, uint32_t len
,
203 /*out*/ nsTArray
<uint8_t>& out
) {
206 nsresult rv
= digest
.Begin(hashAlg
);
211 rv
= digest
.Update(buf
, len
);
216 rv
= digest
.End(out
);
224 nsresult
Begin(SECOidTag hashAlg
) {
225 if (!EnsureNSSInitializedChromeOrContent()) {
226 return NS_ERROR_FAILURE
;
237 return NS_ERROR_INVALID_ARG
;
240 mDigestContext
= UniquePK11Context(PK11_CreateDigestContext(hashAlg
));
241 if (!mDigestContext
) {
242 return mozilla::psm::GetXPCOMFromNSSError(PR_GetError());
245 nsresult rv
= SetLength(hashAlg
);
246 NS_ENSURE_SUCCESS(rv
, rv
);
247 return MapSECStatus(PK11_DigestBegin(mDigestContext
.get()));
251 // A helper class to calculate HMACs over some data given a key.
252 // Only SHA256 and, sadly, MD5 are supported at the moment.
254 // (ensure NSS is initialized)
255 // (obtain raw bytes for a key, some data to calculate the HMAC for)
257 // nsresult rv = hmac.Begin(SEC_OID_SHA256, Span(key));
258 // NS_ENSURE_SUCCESS(rv, rv);
259 // rv = hmac.Update(buf, len);
260 // NS_ENSURE_SUCCESS(rv, rv);
261 // nsTArray<uint8_t> calculatedHmac;
262 // rv = hmac.End(calculatedHmac);
263 // NS_ENSURE_SUCCESS(rv, rv);
264 class HMAC
: public DigestBase
{
266 explicit HMAC() = default;
268 nsresult
Begin(SECOidTag hashAlg
, Span
<const uint8_t> key
) {
269 if (!EnsureNSSInitializedChromeOrContent()) {
270 return NS_ERROR_FAILURE
;
272 CK_MECHANISM_TYPE mechType
;
275 mechType
= CKM_SHA256_HMAC
;
278 mechType
= CKM_MD5_HMAC
;
281 return NS_ERROR_INVALID_ARG
;
283 if (key
.Length() > std::numeric_limits
<unsigned int>::max()) {
284 return NS_ERROR_INVALID_ARG
;
286 // SECItem's data field is a non-const unsigned char*. The good news is the
287 // data won't be mutated, but the bad news is the constness needs to be
289 SECItem keyItem
= {siBuffer
, const_cast<unsigned char*>(key
.Elements()),
290 static_cast<unsigned int>(key
.Length())};
291 UniquePK11SlotInfo
slot(PK11_GetInternalSlot());
293 return mozilla::psm::GetXPCOMFromNSSError(PR_GetError());
295 UniquePK11SymKey
symKey(
296 PK11_ImportSymKey(slot
.get(), CKM_GENERIC_SECRET_KEY_GEN
,
297 PK11_OriginUnwrap
, CKA_SIGN
, &keyItem
, nullptr));
299 return mozilla::psm::GetXPCOMFromNSSError(PR_GetError());
301 SECItem emptyData
= {siBuffer
, nullptr, 0};
302 mDigestContext
= UniquePK11Context(PK11_CreateContextBySymKey(
303 mechType
, CKA_SIGN
, symKey
.get(), &emptyData
));
304 if (!mDigestContext
) {
305 return mozilla::psm::GetXPCOMFromNSSError(PR_GetError());
308 nsresult rv
= SetLength(hashAlg
);
309 NS_ENSURE_SUCCESS(rv
, rv
);
310 return MapSECStatus(PK11_DigestBegin(mDigestContext
.get()));
316 inline void PORT_FreeArena_false(PLArenaPool
* arena
) {
317 // PL_FreeArenaPool can't be used because it doesn't actually free the
318 // memory, which doesn't work well with memory analysis tools.
319 return PORT_FreeArena(arena
, false);
322 } // namespace internal
324 // Wrapper around NSS's SECItem_AllocItem that handles OOM the same way as
326 inline void SECITEM_AllocItem(SECItem
& item
, uint32_t len
) {
327 if (MOZ_UNLIKELY(!SECITEM_AllocItem(nullptr, &item
, len
))) {
328 #ifndef MOZ_NO_MOZALLOC
329 mozalloc_handle_oom(len
);
330 if (MOZ_UNLIKELY(!SECITEM_AllocItem(nullptr, &item
, len
)))
338 class ScopedAutoSECItem final
: public SECItem
{
340 explicit ScopedAutoSECItem(uint32_t initialAllocatedLen
= 0) {
343 if (initialAllocatedLen
> 0) {
344 SECITEM_AllocItem(*this, initialAllocatedLen
);
348 void reset() { SECITEM_FreeItem(this, false); }
350 ~ScopedAutoSECItem() { reset(); }
353 class MOZ_RAII AutoSECMODListReadLock final
{
355 AutoSECMODListReadLock() : mLock(SECMOD_GetDefaultModuleListLock()) {
356 MOZ_ASSERT(mLock
, "should have SECMOD lock (has NSS been initialized?)");
357 SECMOD_GetReadLock(mLock
);
360 ~AutoSECMODListReadLock() { SECMOD_ReleaseReadLock(mLock
); }
363 SECMODListLock
* mLock
;
368 inline void SECITEM_FreeItem_true(SECItem
* s
) {
369 return SECITEM_FreeItem(s
, true);
372 inline void SECITEM_FreeArray_true(SECItemArray
* s
) {
373 return SECITEM_FreeArray(s
, true);
376 inline void SECOID_DestroyAlgorithmID_true(SECAlgorithmID
* a
) {
377 return SECOID_DestroyAlgorithmID(a
, true);
380 inline void VFY_DestroyContext_true(VFYContext
* ctx
) {
381 VFY_DestroyContext(ctx
, true);
384 inline void PK11_HPKE_DestroyContext_true(HpkeContext
* cx
) {
385 PK11_HPKE_DestroyContext(cx
, true);
388 } // namespace internal
390 MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniqueCERTCertificate
, CERTCertificate
,
391 CERT_DestroyCertificate
)
392 MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniqueCERTCertificateList
,
394 CERT_DestroyCertificateList
)
395 MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniqueCERTCertificatePolicies
,
396 CERTCertificatePolicies
,
397 CERT_DestroyCertificatePoliciesExtension
)
398 MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniqueCERTCertificateRequest
,
399 CERTCertificateRequest
,
400 CERT_DestroyCertificateRequest
)
401 MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniqueCERTCertList
, CERTCertList
,
402 CERT_DestroyCertList
)
403 MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniqueCERTName
, CERTName
,
405 MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniqueCERTOidSequence
, CERTOidSequence
,
406 CERT_DestroyOidSequence
)
407 MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniqueCERTSubjectPublicKeyInfo
,
408 CERTSubjectPublicKeyInfo
,
409 SECKEY_DestroySubjectPublicKeyInfo
)
410 MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniqueCERTUserNotice
, CERTUserNotice
,
411 CERT_DestroyUserNotice
)
412 MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniqueCERTValidity
, CERTValidity
,
413 CERT_DestroyValidity
)
415 MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniqueHASHContext
, HASHContext
,
418 MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniqueNSSCMSMessage
, NSSCMSMessage
,
419 NSS_CMSMessage_Destroy
)
420 MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniqueNSSCMSSignedData
, NSSCMSSignedData
,
421 NSS_CMSSignedData_Destroy
)
423 MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniquePK11GenericObject
,
425 PK11_DestroyGenericObject
)
426 MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniquePK11SlotList
, PK11SlotList
,
429 MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniquePLArenaPool
, PLArenaPool
,
430 internal::PORT_FreeArena_false
)
431 MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniquePORTString
, char, PORT_Free
)
432 MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniquePRFileDesc
, PRFileDesc
, PR_Close
)
433 MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniquePRString
, char, PR_Free
)
435 MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniqueSECAlgorithmID
, SECAlgorithmID
,
436 internal::SECOID_DestroyAlgorithmID_true
)
437 MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniqueSECItem
, SECItem
,
438 internal::SECITEM_FreeItem_true
)
439 MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniqueSECItemArray
, SECItemArray
,
440 internal::SECITEM_FreeArray_true
)
441 MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniqueSECKEYPrivateKey
, SECKEYPrivateKey
,
442 SECKEY_DestroyPrivateKey
)
443 MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniqueSECKEYPrivateKeyList
,
444 SECKEYPrivateKeyList
,
445 SECKEY_DestroyPrivateKeyList
)
446 MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniqueSECKEYPublicKey
, SECKEYPublicKey
,
447 SECKEY_DestroyPublicKey
)
448 MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniqueSECMODModule
, SECMODModule
,
449 SECMOD_DestroyModule
)
451 MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniqueSGNDigestInfo
, SGNDigestInfo
,
452 SGN_DestroyDigestInfo
)
454 MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniqueVFYContext
, VFYContext
,
455 internal::VFY_DestroyContext_true
)
457 MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniqueSEC_PKCS12DecoderContext
,
458 SEC_PKCS12DecoderContext
,
459 SEC_PKCS12DecoderFinish
)
460 MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniqueSEC_PKCS12ExportContext
,
461 SEC_PKCS12ExportContext
,
462 SEC_PKCS12DestroyExportContext
)
463 MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(
464 UniqueSECKEYEncryptedPrivateKeyInfo
, SECKEYEncryptedPrivateKeyInfo
,
465 internal::SECKEYEncryptedPrivateKeyInfo_true
)
466 MOZ_TYPE_SPECIFIC_UNIQUE_PTR_TEMPLATE(UniqueHpkeContext
, HpkeContext
,
467 internal::PK11_HPKE_DestroyContext_true
)
468 } // namespace mozilla
470 #endif // ScopedNSSTypes_h