ContentSignatureVerifier.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562
  1. /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
  2. /* This Source Code Form is subject to the terms of the Mozilla Public
  3. * License, v. 2.0. If a copy of the MPL was not distributed with this
  4. * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  5. #include "ContentSignatureVerifier.h"
  6. #include "BRNameMatchingPolicy.h"
  7. #include "SharedCertVerifier.h"
  8. #include "cryptohi.h"
  9. #include "keyhi.h"
  10. #include "mozilla/Assertions.h"
  11. #include "mozilla/Casting.h"
  12. #include "mozilla/Unused.h"
  13. #include "nsCOMPtr.h"
  14. #include "nsContentUtils.h"
  15. #include "nsISupportsPriority.h"
  16. #include "nsIURI.h"
  17. #include "nsNSSComponent.h"
  18. #include "nsSecurityHeaderParser.h"
  19. #include "nsStreamUtils.h"
  20. #include "nsWhitespaceTokenizer.h"
  21. #include "nsXPCOMStrings.h"
  22. #include "nssb64.h"
  23. #include "pkix/pkix.h"
  24. #include "pkix/pkixtypes.h"
  25. #include "secerr.h"
  26. NS_IMPL_ISUPPORTS(ContentSignatureVerifier,
  27. nsIContentSignatureVerifier,
  28. nsIInterfaceRequestor,
  29. nsIStreamListener)
  30. using namespace mozilla;
  31. using namespace mozilla::pkix;
  32. using namespace mozilla::psm;
  33. static LazyLogModule gCSVerifierPRLog("ContentSignatureVerifier");
  34. #define CSVerifier_LOG(args) MOZ_LOG(gCSVerifierPRLog, LogLevel::Debug, args)
  35. // Content-Signature prefix
  36. const nsLiteralCString kPREFIX = NS_LITERAL_CSTRING("Content-Signature:\x00");
  37. ContentSignatureVerifier::~ContentSignatureVerifier()
  38. {
  39. nsNSSShutDownPreventionLock locker;
  40. if (isAlreadyShutDown()) {
  41. return;
  42. }
  43. destructorSafeDestroyNSSReference();
  44. shutdown(ShutdownCalledFrom::Object);
  45. }
  46. NS_IMETHODIMP
  47. ContentSignatureVerifier::VerifyContentSignature(
  48. const nsACString& aData, const nsACString& aCSHeader,
  49. const nsACString& aCertChain, const nsACString& aName, bool* _retval)
  50. {
  51. NS_ENSURE_ARG(_retval);
  52. nsresult rv = CreateContext(aData, aCSHeader, aCertChain, aName);
  53. if (NS_FAILED(rv)) {
  54. *_retval = false;
  55. CSVerifier_LOG(("CSVerifier: Signature verification failed\n"));
  56. if (rv == NS_ERROR_INVALID_SIGNATURE) {
  57. return NS_OK;
  58. }
  59. return rv;
  60. }
  61. return End(_retval);
  62. }
  63. bool
  64. IsNewLine(char16_t c)
  65. {
  66. return c == '\n' || c == '\r';
  67. }
  68. nsresult
  69. ReadChainIntoCertList(const nsACString& aCertChain, CERTCertList* aCertList,
  70. const nsNSSShutDownPreventionLock& /*proofOfLock*/)
  71. {
  72. bool inBlock = false;
  73. bool certFound = false;
  74. const nsCString header = NS_LITERAL_CSTRING("-----BEGIN CERTIFICATE-----");
  75. const nsCString footer = NS_LITERAL_CSTRING("-----END CERTIFICATE-----");
  76. nsCWhitespaceTokenizerTemplate<IsNewLine> tokenizer(aCertChain);
  77. nsAutoCString blockData;
  78. while (tokenizer.hasMoreTokens()) {
  79. nsDependentCSubstring token = tokenizer.nextToken();
  80. if (token.IsEmpty()) {
  81. continue;
  82. }
  83. if (inBlock) {
  84. if (token.Equals(footer)) {
  85. inBlock = false;
  86. certFound = true;
  87. // base64 decode data, make certs, append to chain
  88. ScopedAutoSECItem der;
  89. if (!NSSBase64_DecodeBuffer(nullptr, &der, blockData.BeginReading(),
  90. blockData.Length())) {
  91. CSVerifier_LOG(("CSVerifier: decoding the signature failed\n"));
  92. return NS_ERROR_FAILURE;
  93. }
  94. UniqueCERTCertificate tmpCert(
  95. CERT_NewTempCertificate(CERT_GetDefaultCertDB(), &der, nullptr, false,
  96. true));
  97. if (!tmpCert) {
  98. return NS_ERROR_FAILURE;
  99. }
  100. // if adding tmpCert succeeds, tmpCert will now be owned by aCertList
  101. SECStatus res = CERT_AddCertToListTail(aCertList, tmpCert.get());
  102. if (res != SECSuccess) {
  103. return MapSECStatus(res);
  104. }
  105. Unused << tmpCert.release();
  106. } else {
  107. blockData.Append(token);
  108. }
  109. } else if (token.Equals(header)) {
  110. inBlock = true;
  111. blockData = "";
  112. }
  113. }
  114. if (inBlock || !certFound) {
  115. // the PEM data did not end; bad data.
  116. CSVerifier_LOG(("CSVerifier: supplied chain contains bad data\n"));
  117. return NS_ERROR_FAILURE;
  118. }
  119. return NS_OK;
  120. }
  121. nsresult
  122. ContentSignatureVerifier::CreateContextInternal(const nsACString& aData,
  123. const nsACString& aCertChain,
  124. const nsACString& aName)
  125. {
  126. MOZ_ASSERT(NS_IsMainThread());
  127. nsNSSShutDownPreventionLock locker;
  128. if (isAlreadyShutDown()) {
  129. CSVerifier_LOG(("CSVerifier: nss is already shutdown\n"));
  130. return NS_ERROR_FAILURE;
  131. }
  132. UniqueCERTCertList certCertList(CERT_NewCertList());
  133. if (!certCertList) {
  134. return NS_ERROR_OUT_OF_MEMORY;
  135. }
  136. nsresult rv = ReadChainIntoCertList(aCertChain, certCertList.get(), locker);
  137. if (NS_FAILED(rv)) {
  138. return rv;
  139. }
  140. CERTCertListNode* node = CERT_LIST_HEAD(certCertList.get());
  141. if (!node || CERT_LIST_END(node, certCertList.get()) || !node->cert) {
  142. return NS_ERROR_FAILURE;
  143. }
  144. SECItem* certSecItem = &node->cert->derCert;
  145. Input certDER;
  146. mozilla::pkix::Result result =
  147. certDER.Init(BitwiseCast<uint8_t*, unsigned char*>(certSecItem->data),
  148. certSecItem->len);
  149. if (result != Success) {
  150. return NS_ERROR_FAILURE;
  151. }
  152. // Check the signerCert chain is good
  153. CSTrustDomain trustDomain(certCertList);
  154. result = BuildCertChain(trustDomain, certDER, Now(),
  155. EndEntityOrCA::MustBeEndEntity,
  156. KeyUsage::noParticularKeyUsageRequired,
  157. KeyPurposeId::id_kp_codeSigning,
  158. CertPolicyId::anyPolicy,
  159. nullptr/*stapledOCSPResponse*/);
  160. if (result != Success) {
  161. // if there was a library error, return an appropriate error
  162. if (IsFatalError(result)) {
  163. return NS_ERROR_FAILURE;
  164. }
  165. // otherwise, assume the signature was invalid
  166. CSVerifier_LOG(("CSVerifier: The supplied chain is bad\n"));
  167. return NS_ERROR_INVALID_SIGNATURE;
  168. }
  169. // Check the SAN
  170. Input hostnameInput;
  171. result = hostnameInput.Init(
  172. BitwiseCast<const uint8_t*, const char*>(aName.BeginReading()),
  173. aName.Length());
  174. if (result != Success) {
  175. return NS_ERROR_FAILURE;
  176. }
  177. BRNameMatchingPolicy nameMatchingPolicy(BRNameMatchingPolicy::Mode::Enforce);
  178. result = CheckCertHostname(certDER, hostnameInput, nameMatchingPolicy);
  179. if (result != Success) {
  180. return NS_ERROR_INVALID_SIGNATURE;
  181. }
  182. mKey.reset(CERT_ExtractPublicKey(node->cert));
  183. // in case we were not able to extract a key
  184. if (!mKey) {
  185. CSVerifier_LOG(("CSVerifier: unable to extract a key\n"));
  186. return NS_ERROR_INVALID_SIGNATURE;
  187. }
  188. // Base 64 decode the signature
  189. ScopedAutoSECItem rawSignatureItem;
  190. if (!NSSBase64_DecodeBuffer(nullptr, &rawSignatureItem, mSignature.get(),
  191. mSignature.Length())) {
  192. CSVerifier_LOG(("CSVerifier: decoding the signature failed\n"));
  193. return NS_ERROR_FAILURE;
  194. }
  195. // get signature object
  196. ScopedAutoSECItem signatureItem;
  197. // We have a raw ecdsa signature r||s so we have to DER-encode it first
  198. // Note that we have to check rawSignatureItem->len % 2 here as
  199. // DSAU_EncodeDerSigWithLen asserts this
  200. if (rawSignatureItem.len == 0 || rawSignatureItem.len % 2 != 0) {
  201. CSVerifier_LOG(("CSVerifier: signature length is bad\n"));
  202. return NS_ERROR_FAILURE;
  203. }
  204. if (DSAU_EncodeDerSigWithLen(&signatureItem, &rawSignatureItem,
  205. rawSignatureItem.len) != SECSuccess) {
  206. CSVerifier_LOG(("CSVerifier: encoding the signature failed\n"));
  207. return NS_ERROR_FAILURE;
  208. }
  209. // this is the only OID we support for now
  210. SECOidTag oid = SEC_OID_ANSIX962_ECDSA_SHA384_SIGNATURE;
  211. mCx = UniqueVFYContext(
  212. VFY_CreateContext(mKey.get(), &signatureItem, oid, nullptr));
  213. if (!mCx) {
  214. return NS_ERROR_INVALID_SIGNATURE;
  215. }
  216. if (VFY_Begin(mCx.get()) != SECSuccess) {
  217. return NS_ERROR_INVALID_SIGNATURE;
  218. }
  219. rv = UpdateInternal(kPREFIX, locker);
  220. if (NS_FAILED(rv)) {
  221. return rv;
  222. }
  223. // add data if we got any
  224. return UpdateInternal(aData, locker);
  225. }
  226. nsresult
  227. ContentSignatureVerifier::DownloadCertChain()
  228. {
  229. MOZ_ASSERT(NS_IsMainThread());
  230. if (mCertChainURL.IsEmpty()) {
  231. return NS_ERROR_INVALID_SIGNATURE;
  232. }
  233. nsCOMPtr<nsIURI> certChainURI;
  234. nsresult rv = NS_NewURI(getter_AddRefs(certChainURI), mCertChainURL);
  235. if (NS_FAILED(rv) || !certChainURI) {
  236. return rv;
  237. }
  238. // If the address is not https, fail.
  239. bool isHttps = false;
  240. rv = certChainURI->SchemeIs("https", &isHttps);
  241. if (NS_FAILED(rv)) {
  242. return rv;
  243. }
  244. if (!isHttps) {
  245. return NS_ERROR_INVALID_SIGNATURE;
  246. }
  247. rv = NS_NewChannel(getter_AddRefs(mChannel), certChainURI,
  248. nsContentUtils::GetSystemPrincipal(),
  249. nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
  250. nsIContentPolicy::TYPE_OTHER);
  251. if (NS_FAILED(rv)) {
  252. return rv;
  253. }
  254. // we need this chain soon
  255. nsCOMPtr<nsISupportsPriority> priorityChannel = do_QueryInterface(mChannel);
  256. if (priorityChannel) {
  257. priorityChannel->AdjustPriority(nsISupportsPriority::PRIORITY_HIGHEST);
  258. }
  259. rv = mChannel->AsyncOpen2(this);
  260. if (NS_FAILED(rv)) {
  261. return rv;
  262. }
  263. return NS_OK;
  264. }
  265. // Create a context for content signature verification using CreateContext below.
  266. // This function doesn't require a cert chain to be passed, but instead aCSHeader
  267. // must contain an x5u value that is then used to download the cert chain.
  268. NS_IMETHODIMP
  269. ContentSignatureVerifier::CreateContextWithoutCertChain(
  270. nsIContentSignatureReceiverCallback *aCallback, const nsACString& aCSHeader,
  271. const nsACString& aName)
  272. {
  273. MOZ_ASSERT(NS_IsMainThread());
  274. MOZ_ASSERT(aCallback);
  275. if (mInitialised) {
  276. return NS_ERROR_ALREADY_INITIALIZED;
  277. }
  278. mInitialised = true;
  279. // we get the raw content-signature header here, so first parse aCSHeader
  280. nsresult rv = ParseContentSignatureHeader(aCSHeader);
  281. if (NS_FAILED(rv)) {
  282. return rv;
  283. }
  284. mCallback = aCallback;
  285. mName.Assign(aName);
  286. // We must download the cert chain now.
  287. // This is async and blocks createContextInternal calls.
  288. return DownloadCertChain();
  289. }
  290. // Create a context for a content signature verification.
  291. // It sets signature, certificate chain and name that should be used to verify
  292. // the data. The data parameter is the first part of the data to verify (this
  293. // can be the empty string).
  294. NS_IMETHODIMP
  295. ContentSignatureVerifier::CreateContext(const nsACString& aData,
  296. const nsACString& aCSHeader,
  297. const nsACString& aCertChain,
  298. const nsACString& aName)
  299. {
  300. if (mInitialised) {
  301. return NS_ERROR_ALREADY_INITIALIZED;
  302. }
  303. mInitialised = true;
  304. // The cert chain is given in aCertChain so we don't have to download anything.
  305. mHasCertChain = true;
  306. // we get the raw content-signature header here, so first parse aCSHeader
  307. nsresult rv = ParseContentSignatureHeader(aCSHeader);
  308. if (NS_FAILED(rv)) {
  309. return rv;
  310. }
  311. return CreateContextInternal(aData, aCertChain, aName);
  312. }
  313. nsresult
  314. ContentSignatureVerifier::UpdateInternal(
  315. const nsACString& aData, const nsNSSShutDownPreventionLock& /*proofOfLock*/)
  316. {
  317. if (!aData.IsEmpty()) {
  318. if (VFY_Update(mCx.get(), (const unsigned char*)nsPromiseFlatCString(aData).get(),
  319. aData.Length()) != SECSuccess){
  320. return NS_ERROR_INVALID_SIGNATURE;
  321. }
  322. }
  323. return NS_OK;
  324. }
  325. /**
  326. * Add data to the context that shold be verified.
  327. */
  328. NS_IMETHODIMP
  329. ContentSignatureVerifier::Update(const nsACString& aData)
  330. {
  331. MOZ_ASSERT(NS_IsMainThread());
  332. nsNSSShutDownPreventionLock locker;
  333. if (isAlreadyShutDown()) {
  334. CSVerifier_LOG(("CSVerifier: nss is already shutdown\n"));
  335. return NS_ERROR_FAILURE;
  336. }
  337. // If we didn't create the context yet, bail!
  338. if (!mHasCertChain) {
  339. MOZ_ASSERT_UNREACHABLE(
  340. "Someone called ContentSignatureVerifier::Update before "
  341. "downloading the cert chain.");
  342. return NS_ERROR_FAILURE;
  343. }
  344. return UpdateInternal(aData, locker);
  345. }
  346. /**
  347. * Finish signature verification and return the result in _retval.
  348. */
  349. NS_IMETHODIMP
  350. ContentSignatureVerifier::End(bool* _retval)
  351. {
  352. NS_ENSURE_ARG(_retval);
  353. MOZ_ASSERT(NS_IsMainThread());
  354. nsNSSShutDownPreventionLock locker;
  355. if (isAlreadyShutDown()) {
  356. CSVerifier_LOG(("CSVerifier: nss is already shutdown\n"));
  357. return NS_ERROR_FAILURE;
  358. }
  359. // If we didn't create the context yet, bail!
  360. if (!mHasCertChain) {
  361. MOZ_ASSERT_UNREACHABLE(
  362. "Someone called ContentSignatureVerifier::End before "
  363. "downloading the cert chain.");
  364. return NS_ERROR_FAILURE;
  365. }
  366. *_retval = (VFY_End(mCx.get()) == SECSuccess);
  367. return NS_OK;
  368. }
  369. nsresult
  370. ContentSignatureVerifier::ParseContentSignatureHeader(
  371. const nsACString& aContentSignatureHeader)
  372. {
  373. MOZ_ASSERT(NS_IsMainThread());
  374. // We only support p384 ecdsa according to spec
  375. NS_NAMED_LITERAL_CSTRING(signature_var, "p384ecdsa");
  376. NS_NAMED_LITERAL_CSTRING(certChainURL_var, "x5u");
  377. nsSecurityHeaderParser parser(aContentSignatureHeader.BeginReading());
  378. nsresult rv = parser.Parse();
  379. if (NS_FAILED(rv)) {
  380. CSVerifier_LOG(("CSVerifier: could not parse ContentSignature header\n"));
  381. return NS_ERROR_FAILURE;
  382. }
  383. LinkedList<nsSecurityHeaderDirective>* directives = parser.GetDirectives();
  384. for (nsSecurityHeaderDirective* directive = directives->getFirst();
  385. directive != nullptr; directive = directive->getNext()) {
  386. CSVerifier_LOG(("CSVerifier: found directive %s\n", directive->mName.get()));
  387. if (directive->mName.Length() == signature_var.Length() &&
  388. directive->mName.EqualsIgnoreCase(signature_var.get(),
  389. signature_var.Length())) {
  390. if (!mSignature.IsEmpty()) {
  391. CSVerifier_LOG(("CSVerifier: found two ContentSignatures\n"));
  392. return NS_ERROR_INVALID_SIGNATURE;
  393. }
  394. CSVerifier_LOG(("CSVerifier: found a ContentSignature directive\n"));
  395. mSignature = directive->mValue;
  396. }
  397. if (directive->mName.Length() == certChainURL_var.Length() &&
  398. directive->mName.EqualsIgnoreCase(certChainURL_var.get(),
  399. certChainURL_var.Length())) {
  400. if (!mCertChainURL.IsEmpty()) {
  401. CSVerifier_LOG(("CSVerifier: found two x5u values\n"));
  402. return NS_ERROR_INVALID_SIGNATURE;
  403. }
  404. CSVerifier_LOG(("CSVerifier: found an x5u directive\n"));
  405. mCertChainURL = directive->mValue;
  406. }
  407. }
  408. // we have to ensure that we found a signature at this point
  409. if (mSignature.IsEmpty()) {
  410. CSVerifier_LOG(("CSVerifier: got a Content-Signature header but didn't find a signature.\n"));
  411. return NS_ERROR_FAILURE;
  412. }
  413. // Bug 769521: We have to change b64 url to regular encoding as long as we
  414. // don't have a b64 url decoder. This should change soon, but in the meantime
  415. // we have to live with this.
  416. mSignature.ReplaceChar('-', '+');
  417. mSignature.ReplaceChar('_', '/');
  418. return NS_OK;
  419. }
  420. /* nsIStreamListener implementation */
  421. NS_IMETHODIMP
  422. ContentSignatureVerifier::OnStartRequest(nsIRequest* aRequest,
  423. nsISupports* aContext)
  424. {
  425. MOZ_ASSERT(NS_IsMainThread());
  426. return NS_OK;
  427. }
  428. NS_IMETHODIMP
  429. ContentSignatureVerifier::OnStopRequest(nsIRequest* aRequest,
  430. nsISupports* aContext, nsresult aStatus)
  431. {
  432. MOZ_ASSERT(NS_IsMainThread());
  433. nsCOMPtr<nsIContentSignatureReceiverCallback> callback;
  434. callback.swap(mCallback);
  435. nsresult rv;
  436. // Check HTTP status code and return if it's not 200.
  437. nsCOMPtr<nsIHttpChannel> http = do_QueryInterface(aRequest, &rv);
  438. uint32_t httpResponseCode;
  439. if (NS_FAILED(rv) || NS_FAILED(http->GetResponseStatus(&httpResponseCode)) ||
  440. httpResponseCode != 200) {
  441. callback->ContextCreated(false);
  442. return NS_OK;
  443. }
  444. if (NS_FAILED(aStatus)) {
  445. callback->ContextCreated(false);
  446. return NS_OK;
  447. }
  448. nsAutoCString certChain;
  449. for (uint32_t i = 0; i < mCertChain.Length(); ++i) {
  450. certChain.Append(mCertChain[i]);
  451. }
  452. // We got the cert chain now. Let's create the context.
  453. rv = CreateContextInternal(NS_LITERAL_CSTRING(""), certChain, mName);
  454. if (NS_FAILED(rv)) {
  455. callback->ContextCreated(false);
  456. return NS_OK;
  457. }
  458. mHasCertChain = true;
  459. callback->ContextCreated(true);
  460. return NS_OK;
  461. }
  462. NS_IMETHODIMP
  463. ContentSignatureVerifier::OnDataAvailable(nsIRequest* aRequest,
  464. nsISupports* aContext,
  465. nsIInputStream* aInputStream,
  466. uint64_t aOffset, uint32_t aCount)
  467. {
  468. MOZ_ASSERT(NS_IsMainThread());
  469. nsAutoCString buffer;
  470. nsresult rv = NS_ConsumeStream(aInputStream, aCount, buffer);
  471. if (NS_FAILED(rv)) {
  472. return rv;
  473. }
  474. if (!mCertChain.AppendElement(buffer, fallible)) {
  475. mCertChain.TruncateLength(0);
  476. return NS_ERROR_OUT_OF_MEMORY;
  477. }
  478. return NS_OK;
  479. }
  480. NS_IMETHODIMP
  481. ContentSignatureVerifier::GetInterface(const nsIID& uuid, void** result)
  482. {
  483. return QueryInterface(uuid, result);
  484. }