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 }