tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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