tor-browser

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

PDFSignatureVerification.cpp (9685B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 #include "cms.h"
      6 #include "nsNSSCertificateDB.h"
      7 #include "nsNSSCertificate.h"
      8 #include "AppSignatureVerification.h"
      9 #include "CryptoTask.h"
     10 
     11 #include "mozpkix/pkix.h"
     12 #include "mozpkix/pkixnss.h"
     13 #include "mozpkix/pkixtypes.h"
     14 #include "mozpkix/pkixutil.h"
     15 
     16 #include "mozilla/dom/Promise.h"
     17 
     18 using namespace mozilla;
     19 using namespace mozilla::pkix;
     20 using namespace mozilla::psm;
     21 
     22 using dom::Promise;
     23 
     24 nsresult ComputeDigest(SECOidTag digestAlgorithm,
     25                       const nsTArray<nsTArray<uint8_t>>& ins,
     26                       /* out */ nsTArray<uint8_t>& calculatedDigest) {
     27  Digest digest;
     28  nsresult rv = digest.Begin(digestAlgorithm);
     29  if (NS_FAILED(rv)) {
     30    return rv;
     31  }
     32 
     33  for (auto& in : ins) {
     34    rv = digest.Update(in.Elements(), in.Length());
     35    if (NS_FAILED(rv)) {
     36      return rv;
     37    }
     38  }
     39  return digest.End(calculatedDigest);
     40 }
     41 
     42 struct VerifySignatureResult {
     43  nsresult signatureVerificationResult;
     44  nsTArray<uint8_t> signerCert;
     45  Time time;
     46 
     47  VerifySignatureResult(nsresult result, Span<const uint8_t> certSpan, Time t)
     48      : signatureVerificationResult(result), time(t) {
     49    signerCert.AppendElements(certSpan.data(), certSpan.Length());
     50  }
     51 };
     52 
     53 void VerifySignature(
     54    NSSCMSSignedData* signedData, const nsTArray<nsTArray<uint8_t>>& data,
     55    /* out */ nsTArray<VerifySignatureResult>& signatureVerificationResults) {
     56  nsTArray<std::tuple<NSSCMSSignerInfo*, SECOidTag>> signerInfos;
     57  // Returns a prioritized list of signerInfos.
     58  GetAllSignerInfosForSupportedDigestAlgorithms(signedData, signerInfos);
     59 
     60  Span<const uint8_t> signerCertSpan;
     61  if (signerInfos.Length() == 0) {
     62    signatureVerificationResults.AppendElement(
     63        VerifySignatureResult(NS_ERROR_CMS_VERIFY_NOT_SIGNED,
     64                              /* no certificate */ signerCertSpan,
     65                              /* default time */ Time(Time::uninitialized)));
     66    return;
     67  }
     68 
     69  nsTArray<Span<const uint8_t>> collectedCerts;
     70  CollectCertificates(signedData, collectedCerts);
     71  if (collectedCerts.Length() == 0) {
     72    signatureVerificationResults.AppendElement(
     73        VerifySignatureResult(NS_ERROR_CMS_VERIFY_NOCERT,
     74                              /* no certificate */ signerCertSpan,
     75                              /* default time */ Time(Time::uninitialized)));
     76    return;
     77  }
     78 
     79  for (const auto& pair : signerInfos) {
     80    Time defaultTime(Time::uninitialized);
     81 
     82    SECOidTag digestAlgorithm = std::get<1>(pair);
     83    signerCertSpan = Span<const uint8_t>();
     84 
     85    nsTArray<uint8_t> calculatedDigest;
     86    nsresult rv = ComputeDigest(digestAlgorithm, data, calculatedDigest);
     87    if (NS_FAILED(rv)) {
     88      signatureVerificationResults.AppendElement(VerifySignatureResult(
     89          rv,
     90          /* no certificate */ signerCertSpan, defaultTime));
     91      continue;
     92    }
     93 
     94    NSSCMSSignerInfo* signerInfo = std::get<0>(pair);
     95    signerCertSpan = GetPKCS7SignerCert(signerInfo, collectedCerts);
     96 
     97    if (signerCertSpan.IsEmpty()) {
     98      signatureVerificationResults.AppendElement(VerifySignatureResult(
     99          NS_ERROR_CMS_VERIFY_NOCERT,
    100          /* no certificate */ signerCertSpan, defaultTime));
    101      continue;
    102    }
    103 
    104    SECItem detachedDigest = {
    105        siBuffer, calculatedDigest.Elements(),
    106        static_cast<unsigned int>(calculatedDigest.Length())};
    107 
    108    rv = VerifySignatureFromCertificate(signerCertSpan, signerInfo,
    109                                        &detachedDigest);
    110 
    111    PRTime signingTime;
    112    if (NSS_CMSSignerInfo_GetSigningTime(signerInfo, &signingTime) ==
    113        SECSuccess) {
    114      signatureVerificationResults.AppendElement(VerifySignatureResult(
    115          rv, signerCertSpan,
    116          TimeFromEpochInSeconds((uint64_t)(signingTime / 1000000))));
    117    } else {
    118      signatureVerificationResults.AppendElement(
    119          VerifySignatureResult(rv, signerCertSpan, defaultTime));
    120    }
    121  }
    122 }
    123 
    124 nsresult VerifyCertificate() { return NS_ERROR_CMS_VERIFY_NOT_YET_ATTEMPTED; }
    125 
    126 class PDFVerificationResultImpl final : public nsIPDFVerificationResult {
    127 public:
    128  NS_DECL_THREADSAFE_ISUPPORTS
    129  NS_DECL_NSIPDFVERIFICATIONRESULT
    130 
    131  PDFVerificationResultImpl(nsresult sigResult, nsresult certResult,
    132                            nsIX509Cert* cert)
    133      : mSignatureResult(sigResult),
    134        mCertificateResult(certResult),
    135        mSignerCertificate(cert) {}
    136 
    137 private:
    138  ~PDFVerificationResultImpl() = default;
    139 
    140  nsresult mSignatureResult;
    141  nsresult mCertificateResult;
    142  nsCOMPtr<nsIX509Cert> mSignerCertificate;
    143 };
    144 
    145 NS_IMPL_ISUPPORTS(PDFVerificationResultImpl, nsIPDFVerificationResult)
    146 
    147 NS_IMETHODIMP
    148 PDFVerificationResultImpl::GetSignatureResult(nsresult* aResult) {
    149  *aResult = mSignatureResult;
    150  return NS_OK;
    151 }
    152 
    153 NS_IMETHODIMP
    154 PDFVerificationResultImpl::GetCertificateResult(nsresult* aResult) {
    155  *aResult = mCertificateResult;
    156  return NS_OK;
    157 }
    158 
    159 NS_IMETHODIMP
    160 PDFVerificationResultImpl::GetSignerCertificate(nsIX509Cert** aCert) {
    161  NS_IF_ADDREF(*aCert = mSignerCertificate);
    162  return NS_OK;
    163 }
    164 
    165 void VerifyPKCS7Object(
    166    const nsTArray<uint8_t>& pkcs7, const nsTArray<nsTArray<uint8_t>>& data,
    167    nsIX509CertDB::PDFSignatureAlgorithm signatureType,
    168    /* out */ nsTArray<RefPtr<PDFVerificationResultImpl>>& pdfVerifResults) {
    169  if (signatureType !=
    170      nsIX509CertDB::PDFSignatureAlgorithm::ADBE_PKCS7_DETACHED) {
    171    pdfVerifResults.AppendElement(new PDFVerificationResultImpl(
    172        NS_ERROR_CMS_VERIFY_ERROR_PROCESSING,
    173        NS_ERROR_CMS_VERIFY_ERROR_PROCESSING, nullptr));
    174    return;
    175  }
    176 
    177  if (pkcs7.Length() == 0 || data.Length() == 0) {
    178    pdfVerifResults.AppendElement(new PDFVerificationResultImpl(
    179        NS_ERROR_CMS_VERIFY_ERROR_PROCESSING,
    180        NS_ERROR_CMS_VERIFY_ERROR_PROCESSING, nullptr));
    181    return;
    182  }
    183 
    184  SECItem certificateItem = {siBuffer, const_cast<uint8_t*>(pkcs7.Elements()),
    185                             static_cast<unsigned int>(pkcs7.Length())};
    186 
    187  UniqueNSSCMSMessage cmsMsg(
    188      NSS_CMSMessage_CreateFromDER(&certificateItem,
    189                                   /* NSSCMSContentCallback */ nullptr,
    190                                   /* cb_arg */ nullptr,
    191                                   /* PK11PasswordFunc */ nullptr,
    192                                   /* pwfn_args */ nullptr,
    193                                   /* NSSCMSGetDecryptKeyCallback */ nullptr,
    194                                   /* decrypt_key_cb_arg */ nullptr));
    195 
    196  NSSCMSSignedData* signedData = GetSignedDataContent(cmsMsg.get());
    197  if (!signedData) {
    198    pdfVerifResults.AppendElement(new PDFVerificationResultImpl(
    199        NS_ERROR_CMS_VERIFY_ERROR_PROCESSING,
    200        NS_ERROR_CMS_VERIFY_ERROR_PROCESSING, nullptr));
    201    return;
    202  }
    203 
    204  nsTArray<VerifySignatureResult> signatureVerificationResults;
    205  VerifySignature(signedData, data, signatureVerificationResults);
    206 
    207  for (auto& result : signatureVerificationResults) {
    208    if (result.signatureVerificationResult != NS_OK) {
    209      pdfVerifResults.AppendElement(new PDFVerificationResultImpl(
    210          result.signatureVerificationResult,
    211          NS_ERROR_CMS_VERIFY_NOT_YET_ATTEMPTED, nullptr));
    212    } else {
    213      // The next patch will contain the certificate verification for each
    214      // signerCert
    215      nsCOMPtr<nsIX509Cert> cert(
    216          new nsNSSCertificate(std::move(result.signerCert)));
    217      pdfVerifResults.AppendElement(new PDFVerificationResultImpl(
    218          result.signatureVerificationResult,
    219          NS_ERROR_CMS_VERIFY_NOT_YET_ATTEMPTED, cert));
    220    }
    221  }
    222 }
    223 
    224 class VerifyPKCS7ObjectTask : public CryptoTask {
    225 public:
    226  VerifyPKCS7ObjectTask(const nsTArray<uint8_t>& aPkcs7,
    227                        const nsTArray<nsTArray<uint8_t>>& aData,
    228                        nsIX509CertDB::PDFSignatureAlgorithm aSignatureType,
    229                        RefPtr<Promise>& aPromise)
    230      : mPkcs7(aPkcs7.Clone()),
    231        mSignatureType(aSignatureType),
    232        mPromise(new nsMainThreadPtrHolder<Promise>(
    233            "VerifyPKCS7ObjectTask::mPromise", aPromise)) {
    234    for (auto& n : aData) {
    235      mData.AppendElement(n.Clone());
    236    }
    237  }
    238 
    239 private:
    240  virtual nsresult CalculateResult() override;
    241  virtual void CallCallback(nsresult rv) override;
    242 
    243  const nsTArray<uint8_t> mPkcs7;
    244  nsTArray<nsTArray<uint8_t>> mData;
    245  nsIX509CertDB::PDFSignatureAlgorithm mSignatureType;
    246  nsTArray<RefPtr<PDFVerificationResultImpl>> mPdfVerifResults;  // out
    247  nsMainThreadPtrHandle<Promise> mPromise;
    248 };
    249 
    250 NS_IMETHODIMP
    251 nsNSSCertificateDB::AsyncVerifyPKCS7Object(
    252    const nsTArray<uint8_t>& pkcs7, const nsTArray<nsTArray<uint8_t>>& data,
    253    nsIX509CertDB::PDFSignatureAlgorithm signatureType, JSContext* aCx,
    254    mozilla::dom::Promise** aPromise) {
    255  NS_ENSURE_ARG_POINTER(aCx);
    256 
    257  nsIGlobalObject* globalObject = xpc::CurrentNativeGlobal(aCx);
    258  if (NS_WARN_IF(!globalObject)) {
    259    return NS_ERROR_UNEXPECTED;
    260  }
    261 
    262  ErrorResult result;
    263  RefPtr<Promise> promise = Promise::Create(globalObject, result);
    264  if (NS_WARN_IF(result.Failed())) {
    265    return result.StealNSResult();
    266  }
    267 
    268  RefPtr<VerifyPKCS7ObjectTask> task(
    269      new VerifyPKCS7ObjectTask(pkcs7, data, signatureType, promise));
    270  nsresult rv = task->Dispatch();
    271  if (NS_FAILED(rv)) {
    272    return rv;
    273  }
    274 
    275  promise.forget(aPromise);
    276  return NS_OK;
    277 }
    278 
    279 nsresult VerifyPKCS7ObjectTask::CalculateResult() {
    280  VerifyPKCS7Object(mPkcs7, mData, mSignatureType, mPdfVerifResults);
    281  return NS_OK;
    282 }
    283 
    284 void VerifyPKCS7ObjectTask::CallCallback(nsresult rv) {
    285  if (NS_FAILED(rv)) {
    286    mPromise->MaybeReject(rv);
    287  } else {
    288    mPromise->MaybeResolve(mPdfVerifResults);
    289  }
    290 }