зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1293231 - Certificate Transparency - basic telemetry reports; r=Cykesiopka,keeler
MozReview-Commit-ID: EGvuZADObJo --HG-- extra : rebase_source : 9a059c9f8e2fdf9bfc693b0b5649808b1beeb67b
This commit is contained in:
Родитель
8cc8c5166b
Коммит
976d5c3f1d
|
@ -679,6 +679,7 @@ SSL_OptionSet
|
|||
SSL_OptionSetDefault
|
||||
SSL_PeerCertificate
|
||||
SSL_PeerCertificateChain
|
||||
SSL_PeerSignedCertTimestamps
|
||||
SSL_PeerStapledOCSPResponses
|
||||
SSL_ResetHandshake
|
||||
SSL_SendAdditionalKeyShares
|
||||
|
|
|
@ -86,6 +86,11 @@ pref("security.pki.netscape_step_up_policy", 1);
|
|||
pref("security.pki.netscape_step_up_policy", 2);
|
||||
#endif
|
||||
|
||||
// Configures Certificate Transparency support mode:
|
||||
// 0: Fully disabled.
|
||||
// 1: Only collect telemetry. CT qualification checks are not performed.
|
||||
pref("security.pki.certificate_transparency.mode", 1);
|
||||
|
||||
pref("security.webauth.u2f", false);
|
||||
pref("security.webauth.u2f_enable_softtoken", false);
|
||||
pref("security.webauth.u2f_enable_usbtoken", false);
|
||||
|
|
|
@ -0,0 +1,155 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/* This file is generated by print_log_list.py from
|
||||
* https://github.com/google/certificate-transparency/ */
|
||||
|
||||
#ifndef CTKnownLogs_h
|
||||
#define CTKnownLogs_h
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
struct CTLogInfo {
|
||||
const char* const logName;
|
||||
const char* const logUrl;
|
||||
const char* const logKey;
|
||||
const size_t logKeyLength;
|
||||
};
|
||||
|
||||
const CTLogInfo kCTLogList[] = {
|
||||
{ "Google 'Pilot' log",
|
||||
"https://ct.googleapis.com/pilot/",
|
||||
"\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48"
|
||||
"\xce\x3d\x03\x01\x07\x03\x42\x00\x04\x7d\xa8\x4b\x12\x29\x80\xa3\x3d\xad"
|
||||
"\xd3\x5a\x77\xb8\xcc\xe2\x88\xb3\xa5\xfd\xf1\xd3\x0c\xcd\x18\x0c\xe8\x41"
|
||||
"\x46\xe8\x81\x01\x1b\x15\xe1\x4b\xf1\x1b\x62\xdd\x36\x0a\x08\x18\xba\xed"
|
||||
"\x0b\x35\x84\xd0\x9e\x40\x3c\x2d\x9e\x9b\x82\x65\xbd\x1f\x04\x10\x41\x4c"
|
||||
"\xa0",
|
||||
91 },
|
||||
{ "Google 'Aviator' log",
|
||||
"https://ct.googleapis.com/aviator/",
|
||||
"\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48"
|
||||
"\xce\x3d\x03\x01\x07\x03\x42\x00\x04\xd7\xf4\xcc\x69\xb2\xe4\x0e\x90\xa3"
|
||||
"\x8a\xea\x5a\x70\x09\x4f\xef\x13\x62\xd0\x8d\x49\x60\xff\x1b\x40\x50\x07"
|
||||
"\x0c\x6d\x71\x86\xda\x25\x49\x8d\x65\xe1\x08\x0d\x47\x34\x6b\xbd\x27\xbc"
|
||||
"\x96\x21\x3e\x34\xf5\x87\x76\x31\xb1\x7f\x1d\xc9\x85\x3b\x0d\xf7\x1f\x3f"
|
||||
"\xe9",
|
||||
91 },
|
||||
{ "DigiCert Log Server",
|
||||
"https://ct1.digicert-ct.com/log/",
|
||||
"\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48"
|
||||
"\xce\x3d\x03\x01\x07\x03\x42\x00\x04\x02\x46\xc5\xbe\x1b\xbb\x82\x40\x16"
|
||||
"\xe8\xc1\xd2\xac\x19\x69\x13\x59\xf8\xf8\x70\x85\x46\x40\xb9\x38\xb0\x23"
|
||||
"\x82\xa8\x64\x4c\x7f\xbf\xbb\x34\x9f\x4a\x5f\x28\x8a\xcf\x19\xc4\x00\xf6"
|
||||
"\x36\x06\x93\x65\xed\x4c\xf5\xa9\x21\x62\x5a\xd8\x91\xeb\x38\x24\x40\xac"
|
||||
"\xe8",
|
||||
91 },
|
||||
{ "Google 'Rocketeer' log",
|
||||
"https://ct.googleapis.com/rocketeer/",
|
||||
"\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48"
|
||||
"\xce\x3d\x03\x01\x07\x03\x42\x00\x04\x20\x5b\x18\xc8\x3c\xc1\x8b\xb3\x31"
|
||||
"\x08\x00\xbf\xa0\x90\x57\x2b\xb7\x47\x8c\x6f\xb5\x68\xb0\x8e\x90\x78\xe9"
|
||||
"\xa0\x73\xea\x4f\x28\x21\x2e\x9c\xc0\xf4\x16\x1b\xaa\xf9\xd5\xd7\xa9\x80"
|
||||
"\xc3\x4e\x2f\x52\x3c\x98\x01\x25\x46\x24\x25\x28\x23\x77\x2d\x05\xc2\x40"
|
||||
"\x7a",
|
||||
91 },
|
||||
{ "Certly.IO log",
|
||||
"https://log.certly.io/",
|
||||
"\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48"
|
||||
"\xce\x3d\x03\x01\x07\x03\x42\x00\x04\x0b\x23\xcb\x85\x62\x98\x61\x48\x04"
|
||||
"\x73\xeb\x54\x5d\xf3\xd0\x07\x8c\x2d\x19\x2d\x8c\x36\xf5\xeb\x8f\x01\x42"
|
||||
"\x0a\x7c\x98\x26\x27\xc1\xb5\xdd\x92\x93\xb0\xae\xf8\x9b\x3d\x0c\xd8\x4c"
|
||||
"\x4e\x1d\xf9\x15\xfb\x47\x68\x7b\xba\x66\xb7\x25\x9c\xd0\x4a\xc2\x66\xdb"
|
||||
"\x48",
|
||||
91 },
|
||||
{ "Izenpe log",
|
||||
"https://ct.izenpe.com/",
|
||||
"\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48"
|
||||
"\xce\x3d\x03\x01\x07\x03\x42\x00\x04\x27\x64\x39\x0c\x2d\xdc\x50\x18\xf8"
|
||||
"\x21\x00\xa2\x0e\xed\x2c\xea\x3e\x75\xba\x9f\x93\x64\x09\x00\x11\xc4\x11"
|
||||
"\x17\xab\x5c\xcf\x0f\x74\xac\xb5\x97\x90\x93\x00\x5b\xb8\xeb\xf7\x27\x3d"
|
||||
"\xd9\xb2\x0a\x81\x5f\x2f\x0d\x75\x38\x94\x37\x99\x1e\xf6\x07\x76\xe0\xee"
|
||||
"\xbe",
|
||||
91 },
|
||||
{ "Symantec log",
|
||||
"https://ct.ws.symantec.com/",
|
||||
"\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48"
|
||||
"\xce\x3d\x03\x01\x07\x03\x42\x00\x04\x96\xea\xac\x1c\x46\x0c\x1b\x55\xdc"
|
||||
"\x0d\xfc\xb5\x94\x27\x46\x57\x42\x70\x3a\x69\x18\xe2\xbf\x3b\xc4\xdb\xab"
|
||||
"\xa0\xf4\xb6\x6c\xc0\x53\x3f\x4d\x42\x10\x33\xf0\x58\x97\x8f\x6b\xbe\x72"
|
||||
"\xf4\x2a\xec\x1c\x42\xaa\x03\x2f\x1a\x7e\x28\x35\x76\x99\x08\x3d\x21\x14"
|
||||
"\x86",
|
||||
91 },
|
||||
{ "Venafi log",
|
||||
"https://ctlog.api.venafi.com/",
|
||||
"\x30\x82\x01\x22\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01\x05"
|
||||
"\x00\x03\x82\x01\x0f\x00\x30\x82\x01\x0a\x02\x82\x01\x01\x00\xa2\x5a\x48"
|
||||
"\x1f\x17\x52\x95\x35\xcb\xa3\x5b\x3a\x1f\x53\x82\x76\x94\xa3\xff\x80\xf2"
|
||||
"\x1c\x37\x3c\xc0\xb1\xbd\xc1\x59\x8b\xab\x2d\x65\x93\xd7\xf3\xe0\x04\xd5"
|
||||
"\x9a\x6f\xbf\xd6\x23\x76\x36\x4f\x23\x99\xcb\x54\x28\xad\x8c\x15\x4b\x65"
|
||||
"\x59\x76\x41\x4a\x9c\xa6\xf7\xb3\x3b\x7e\xb1\xa5\x49\xa4\x17\x51\x6c\x80"
|
||||
"\xdc\x2a\x90\x50\x4b\x88\x24\xe9\xa5\x12\x32\x93\x04\x48\x90\x02\xfa\x5f"
|
||||
"\x0e\x30\x87\x8e\x55\x76\x05\xee\x2a\x4c\xce\xa3\x6a\x69\x09\x6e\x25\xad"
|
||||
"\x82\x76\x0f\x84\x92\xfa\x38\xd6\x86\x4e\x24\x8f\x9b\xb0\x72\xcb\x9e\xe2"
|
||||
"\x6b\x3f\xe1\x6d\xc9\x25\x75\x23\x88\xa1\x18\x58\x06\x23\x33\x78\xda\x00"
|
||||
"\xd0\x38\x91\x67\xd2\xa6\x7d\x27\x97\x67\x5a\xc1\xf3\x2f\x17\xe6\xea\xd2"
|
||||
"\x5b\xe8\x81\xcd\xfd\x92\x68\xe7\xf3\x06\xf0\xe9\x72\x84\xee\x01\xa5\xb1"
|
||||
"\xd8\x33\xda\xce\x83\xa5\xdb\xc7\xcf\xd6\x16\x7e\x90\x75\x18\xbf\x16\xdc"
|
||||
"\x32\x3b\x6d\x8d\xab\x82\x17\x1f\x89\x20\x8d\x1d\x9a\xe6\x4d\x23\x08\xdf"
|
||||
"\x78\x6f\xc6\x05\xbf\x5f\xae\x94\x97\xdb\x5f\x64\xd4\xee\x16\x8b\xa3\x84"
|
||||
"\x6c\x71\x2b\xf1\xab\x7f\x5d\x0d\x32\xee\x04\xe2\x90\xec\x41\x9f\xfb\x39"
|
||||
"\xc1\x02\x03\x01\x00\x01",
|
||||
294 },
|
||||
{ "Symantec 'Vega' log",
|
||||
"https://vega.ws.symantec.com/",
|
||||
"\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48"
|
||||
"\xce\x3d\x03\x01\x07\x03\x42\x00\x04\xea\x95\x9e\x02\xff\xee\xf1\x33\x6d"
|
||||
"\x4b\x87\xbc\xcd\xfd\x19\x17\x62\xff\x94\xd3\xd0\x59\x07\x3f\x02\x2d\x1c"
|
||||
"\x90\xfe\xc8\x47\x30\x3b\xf1\xdd\x0d\xb8\x11\x0c\x5d\x1d\x86\xdd\xab\xd3"
|
||||
"\x2b\x46\x66\xfb\x6e\x65\xb7\x3b\xfd\x59\x68\xac\xdf\xa6\xf8\xce\xd2\x18"
|
||||
"\x4d",
|
||||
91 },
|
||||
{ "CNNIC CT log",
|
||||
"https://ctserver.cnnic.cn/",
|
||||
"\x30\x82\x01\x22\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01\x05"
|
||||
"\x00\x03\x82\x01\x0f\x00\x30\x82\x01\x0a\x02\x82\x01\x01\x00\xbf\xb5\x08"
|
||||
"\x61\x9a\x29\x32\x04\xd3\x25\x63\xe9\xd8\x85\xe1\x86\xe0\x1f\xd6\x5e\x9a"
|
||||
"\xf7\x33\x3b\x80\x1b\xe7\xb6\x3e\x5f\x2d\xa1\x66\xf6\x95\x4a\x84\xa6\x21"
|
||||
"\x56\x79\xe8\xf7\x85\xee\x5d\xe3\x7c\x12\xc0\xe0\x89\x22\x09\x22\x3e\xba"
|
||||
"\x16\x95\x06\xbd\xa8\xb9\xb1\xa9\xb2\x7a\xd6\x61\x2e\x87\x11\xb9\x78\x40"
|
||||
"\x89\x75\xdb\x0c\xdc\x90\xe0\xa4\x79\xd6\xd5\x5e\x6e\xd1\x2a\xdb\x34\xf4"
|
||||
"\x99\x3f\x65\x89\x3b\x46\xc2\x29\x2c\x15\x07\x1c\xc9\x4b\x1a\x54\xf8\x6c"
|
||||
"\x1e\xaf\x60\x27\x62\x0a\x65\xd5\x9a\xb9\x50\x36\x16\x6e\x71\xf6\x1f\x01"
|
||||
"\xf7\x12\xa7\xfc\xbf\xf6\x21\xa3\x29\x90\x86\x2d\x77\xde\xbb\x4c\xd4\xcf"
|
||||
"\xfd\xd2\xcf\x82\x2c\x4d\xd4\xf2\xc2\x2d\xac\xa9\xbe\xea\xc3\x19\x25\x43"
|
||||
"\xb2\xe5\x9a\x6c\x0d\xc5\x1c\xa5\x8b\xf7\x3f\x30\xaf\xb9\x01\x91\xb7\x69"
|
||||
"\x12\x12\xe5\x83\x61\xfe\x34\x00\xbe\xf6\x71\x8a\xc7\xeb\x50\x92\xe8\x59"
|
||||
"\xfe\x15\x91\xeb\x96\x97\xf8\x23\x54\x3f\x2d\x8e\x07\xdf\xee\xda\xb3\x4f"
|
||||
"\xc8\x3c\x9d\x6f\xdf\x3c\x2c\x43\x57\xa1\x47\x0c\x91\x04\xf4\x75\x4d\xda"
|
||||
"\x89\x81\xa4\x14\x06\x34\xb9\x98\xc3\xda\xf1\xfd\xed\x33\x36\xd3\x16\x2d"
|
||||
"\x35\x02\x03\x01\x00\x01",
|
||||
294 },
|
||||
{ "WoSign log",
|
||||
"https://ctlog.wosign.com/",
|
||||
"\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48"
|
||||
"\xce\x3d\x03\x01\x07\x03\x42\x00\x04\xcc\x11\x88\x7b\x2d\x66\xcb\xae\x8f"
|
||||
"\x4d\x30\x66\x27\x19\x25\x22\x93\x21\x46\xb4\x2f\x01\xd3\xc6\xf9\x2b\xd5"
|
||||
"\xc8\xba\x73\x9b\x06\xa2\xf0\x8a\x02\x9c\xd0\x6b\x46\x18\x30\x85\xba\xe9"
|
||||
"\x24\x8b\x0e\xd1\x5b\x70\x28\x0c\x7e\xf1\x3a\x45\x7f\x5a\xf3\x82\x42\x60"
|
||||
"\x31",
|
||||
91 },
|
||||
{ "StartCom log",
|
||||
"https://ct.startssl.com/",
|
||||
"\x30\x59\x30\x13\x06\x07\x2a\x86\x48\xce\x3d\x02\x01\x06\x08\x2a\x86\x48"
|
||||
"\xce\x3d\x03\x01\x07\x03\x42\x00\x04\x48\xf3\x59\xf3\xf6\x05\x18\xd3\xdb"
|
||||
"\xb2\xed\x46\x7e\xcf\xc8\x11\xb5\x57\xb1\xa8\xd6\x4c\xe6\x9f\xb7\x4a\x1a"
|
||||
"\x14\x86\x43\xa9\x48\xb0\xcb\x5a\x3f\x3c\x4a\xca\xdf\xc4\x82\x14\x55\x9a"
|
||||
"\xf8\xf7\x8e\x40\x55\xdc\xf4\xd2\xaf\xea\x75\x74\xfb\x4e\x7f\x60\x86\x2e"
|
||||
"\x51",
|
||||
91 }
|
||||
};
|
||||
|
||||
#endif // CTKnownLogs_h
|
|
@ -509,6 +509,10 @@ DecodeSignedCertificateTimestamp(Reader& reader,
|
|||
}
|
||||
result.timestamp = timestamp;
|
||||
|
||||
result.origin = SignedCertificateTimestamp::Origin::Unknown;
|
||||
result.verificationStatus =
|
||||
SignedCertificateTimestamp::VerificationStatus::None;
|
||||
|
||||
output = Move(result);
|
||||
return Success;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "CTVerifyResult.h"
|
||||
|
||||
namespace mozilla { namespace ct {
|
||||
|
||||
void
|
||||
CTVerifyResult::Reset()
|
||||
{
|
||||
scts.clear();
|
||||
decodingErrors = 0;
|
||||
}
|
||||
|
||||
} } // namespace mozilla::ct
|
|
@ -0,0 +1,41 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef CTVerifyResult_h
|
||||
#define CTVerifyResult_h
|
||||
|
||||
#include "mozilla/Vector.h"
|
||||
#include "SignedCertificateTimestamp.h"
|
||||
|
||||
namespace mozilla { namespace ct {
|
||||
|
||||
typedef Vector<SignedCertificateTimestamp> SCTList;
|
||||
|
||||
// Holds Signed Certificate Timestamps verification results.
|
||||
class CTVerifyResult
|
||||
{
|
||||
public:
|
||||
// SCTs that were processed during the verification. For each SCT,
|
||||
// the verification result is stored in its |verificationStatus| field.
|
||||
SCTList scts;
|
||||
|
||||
// The verifier makes the best effort to extract the available SCTs
|
||||
// from the binary sources provided to it.
|
||||
// If some SCT cannot be extracted due to encoding errors, the verifier
|
||||
// proceeds to the next available one. In other words, decoding errors are
|
||||
// effectively ignored.
|
||||
// Note that a serialized SCT may fail to decode for a "legitimate" reason,
|
||||
// e.g. if the SCT is from a future version of the Certificate Transparency
|
||||
// standard.
|
||||
// |decodingErrors| field counts the errors of the above kind.
|
||||
size_t decodingErrors;
|
||||
|
||||
void Reset();
|
||||
};
|
||||
|
||||
} } // namespace mozilla::ct
|
||||
|
||||
#endif // CTVerifyResult_h
|
|
@ -9,10 +9,13 @@
|
|||
#include <stdint.h>
|
||||
|
||||
#include "BRNameMatchingPolicy.h"
|
||||
#include "CTKnownLogs.h"
|
||||
#include "ExtendedValidation.h"
|
||||
#include "MultiLogCTVerifier.h"
|
||||
#include "NSSCertDBTrustDomain.h"
|
||||
#include "NSSErrorsService.h"
|
||||
#include "cert.h"
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/Casting.h"
|
||||
#include "nsNSSComponent.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
|
@ -24,6 +27,7 @@
|
|||
#include "secmod.h"
|
||||
#include "sslerr.h"
|
||||
|
||||
using namespace mozilla::ct;
|
||||
using namespace mozilla::pkix;
|
||||
using namespace mozilla::psm;
|
||||
|
||||
|
@ -42,7 +46,8 @@ CertVerifier::CertVerifier(OcspDownloadConfig odc,
|
|||
PinningMode pinningMode,
|
||||
SHA1Mode sha1Mode,
|
||||
BRNameMatchingPolicy::Mode nameMatchingMode,
|
||||
NetscapeStepUpPolicy netscapeStepUpPolicy)
|
||||
NetscapeStepUpPolicy netscapeStepUpPolicy,
|
||||
CertificateTransparencyMode ctMode)
|
||||
: mOCSPDownloadConfig(odc)
|
||||
, mOCSPStrict(osc == ocspStrict)
|
||||
, mOCSPGETEnabled(ogc == ocspGetEnabled)
|
||||
|
@ -51,7 +56,9 @@ CertVerifier::CertVerifier(OcspDownloadConfig odc,
|
|||
, mSHA1Mode(sha1Mode)
|
||||
, mNameMatchingMode(nameMatchingMode)
|
||||
, mNetscapeStepUpPolicy(netscapeStepUpPolicy)
|
||||
, mCTMode(ctMode)
|
||||
{
|
||||
LoadKnownCTLogs();
|
||||
}
|
||||
|
||||
CertVerifier::~CertVerifier()
|
||||
|
@ -121,17 +128,17 @@ BuildCertChainForOneKeyUsage(NSSCertDBTrustDomain& trustDomain, Input certDER,
|
|||
/*optional out*/ CertVerifier::OCSPStaplingStatus*
|
||||
ocspStaplingStatus)
|
||||
{
|
||||
trustDomain.ResetOCSPStaplingStatus();
|
||||
trustDomain.ResetAccumulatedState();
|
||||
Result rv = BuildCertChain(trustDomain, certDER, time,
|
||||
EndEntityOrCA::MustBeEndEntity, ku1,
|
||||
eku, requiredPolicy, stapledOCSPResponse);
|
||||
if (rv == Result::ERROR_INADEQUATE_KEY_USAGE) {
|
||||
trustDomain.ResetOCSPStaplingStatus();
|
||||
trustDomain.ResetAccumulatedState();
|
||||
rv = BuildCertChain(trustDomain, certDER, time,
|
||||
EndEntityOrCA::MustBeEndEntity, ku2,
|
||||
eku, requiredPolicy, stapledOCSPResponse);
|
||||
if (rv == Result::ERROR_INADEQUATE_KEY_USAGE) {
|
||||
trustDomain.ResetOCSPStaplingStatus();
|
||||
trustDomain.ResetAccumulatedState();
|
||||
rv = BuildCertChain(trustDomain, certDER, time,
|
||||
EndEntityOrCA::MustBeEndEntity, ku3,
|
||||
eku, requiredPolicy, stapledOCSPResponse);
|
||||
|
@ -146,6 +153,152 @@ BuildCertChainForOneKeyUsage(NSSCertDBTrustDomain& trustDomain, Input certDER,
|
|||
return rv;
|
||||
}
|
||||
|
||||
void
|
||||
CertVerifier::LoadKnownCTLogs()
|
||||
{
|
||||
mCTVerifier = MakeUnique<MultiLogCTVerifier>();
|
||||
for (const CTLogInfo& log : kCTLogList) {
|
||||
Input publicKey;
|
||||
Result rv = publicKey.Init(
|
||||
BitwiseCast<const uint8_t*, const char*>(log.logKey), log.logKeyLength);
|
||||
if (rv != Success) {
|
||||
MOZ_ASSERT_UNREACHABLE("Failed reading a log key for a known CT Log");
|
||||
continue;
|
||||
}
|
||||
rv = mCTVerifier->AddLog(publicKey);
|
||||
if (rv != Success) {
|
||||
MOZ_ASSERT_UNREACHABLE("Failed initializing a known CT Log");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Result
|
||||
CertVerifier::VerifySignedCertificateTimestamps(
|
||||
NSSCertDBTrustDomain& trustDomain, const UniqueCERTCertList& builtChain,
|
||||
Input sctsFromTLS, Time time,
|
||||
/*optional out*/ CertificateTransparencyInfo* ctInfo)
|
||||
{
|
||||
if (ctInfo) {
|
||||
ctInfo->Reset();
|
||||
}
|
||||
if (mCTMode == CertificateTransparencyMode::Disabled) {
|
||||
return Success;
|
||||
}
|
||||
if (ctInfo) {
|
||||
ctInfo->enabled = true;
|
||||
}
|
||||
|
||||
if (!builtChain || CERT_LIST_EMPTY(builtChain)) {
|
||||
return Result::FATAL_ERROR_INVALID_ARGS;
|
||||
}
|
||||
|
||||
bool gotScts = false;
|
||||
Input embeddedSCTs = trustDomain.GetSCTListFromCertificate();
|
||||
if (embeddedSCTs.GetLength() > 0) {
|
||||
gotScts = true;
|
||||
MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
|
||||
("Got embedded SCT data of length %zu\n",
|
||||
static_cast<size_t>(embeddedSCTs.GetLength())));
|
||||
}
|
||||
Input sctsFromOCSP = trustDomain.GetSCTListFromOCSPStapling();
|
||||
if (sctsFromOCSP.GetLength() > 0) {
|
||||
gotScts = true;
|
||||
MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
|
||||
("Got OCSP SCT data of length %zu\n",
|
||||
static_cast<size_t>(sctsFromOCSP.GetLength())));
|
||||
}
|
||||
if (sctsFromTLS.GetLength() > 0) {
|
||||
gotScts = true;
|
||||
MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
|
||||
("Got TLS SCT data of length %zu\n",
|
||||
static_cast<size_t>(sctsFromTLS.GetLength())));
|
||||
}
|
||||
if (!gotScts) {
|
||||
return Success;
|
||||
}
|
||||
|
||||
CERTCertListNode* endEntityNode = CERT_LIST_HEAD(builtChain);
|
||||
if (!endEntityNode) {
|
||||
return Result::FATAL_ERROR_INVALID_ARGS;
|
||||
}
|
||||
CERTCertListNode* issuerNode = CERT_LIST_NEXT(endEntityNode);
|
||||
if (!issuerNode) {
|
||||
// Issuer certificate is required for SCT verification.
|
||||
return Success;
|
||||
}
|
||||
|
||||
CERTCertificate* endEntity = endEntityNode->cert;
|
||||
CERTCertificate* issuer = issuerNode->cert;
|
||||
if (!endEntity || !issuer) {
|
||||
return Result::FATAL_ERROR_INVALID_ARGS;
|
||||
}
|
||||
|
||||
Input endEntityDER;
|
||||
Result rv = endEntityDER.Init(endEntity->derCert.data,
|
||||
endEntity->derCert.len);
|
||||
if (rv != Success) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
Input issuerPublicKeyDER;
|
||||
rv = issuerPublicKeyDER.Init(issuer->derPublicKey.data,
|
||||
issuer->derPublicKey.len);
|
||||
if (rv != Success) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
CTVerifyResult result;
|
||||
rv = mCTVerifier->Verify(endEntityDER, issuerPublicKeyDER,
|
||||
embeddedSCTs, sctsFromOCSP, sctsFromTLS, time,
|
||||
result);
|
||||
if (rv != Success) {
|
||||
MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
|
||||
("SCT verification failed with fatal error %i\n", rv));
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (MOZ_LOG_TEST(gCertVerifierLog, LogLevel::Debug)) {
|
||||
size_t verifiedCount = 0;
|
||||
size_t unknownLogCount = 0;
|
||||
size_t invalidSignatureCount = 0;
|
||||
size_t invalidTimestampCount = 0;
|
||||
for (const SignedCertificateTimestamp& sct : result.scts) {
|
||||
switch (sct.verificationStatus) {
|
||||
case SignedCertificateTimestamp::VerificationStatus::OK:
|
||||
verifiedCount++;
|
||||
break;
|
||||
case SignedCertificateTimestamp::VerificationStatus::UnknownLog:
|
||||
unknownLogCount++;
|
||||
break;
|
||||
case SignedCertificateTimestamp::VerificationStatus::InvalidSignature:
|
||||
invalidSignatureCount++;
|
||||
break;
|
||||
case SignedCertificateTimestamp::VerificationStatus::InvalidTimestamp:
|
||||
invalidTimestampCount++;
|
||||
break;
|
||||
case SignedCertificateTimestamp::VerificationStatus::None:
|
||||
default:
|
||||
MOZ_ASSERT_UNREACHABLE("Unexpected SCT verificationStatus");
|
||||
}
|
||||
}
|
||||
MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
|
||||
("SCT verification result: "
|
||||
"verified=%zu unknownLog=%zu "
|
||||
"invalidSignature=%zu invalidTimestamp=%zu "
|
||||
"decodingErrors=%zu\n",
|
||||
verifiedCount, unknownLogCount,
|
||||
invalidSignatureCount, invalidTimestampCount,
|
||||
result.decodingErrors));
|
||||
}
|
||||
|
||||
if (ctInfo) {
|
||||
ctInfo->processedSCTs = true;
|
||||
ctInfo->verifyResult = Move(result);
|
||||
}
|
||||
return Success;
|
||||
}
|
||||
|
||||
bool
|
||||
CertVerifier::SHA1ModeMoreRestrictiveThanGivenMode(SHA1Mode mode)
|
||||
{
|
||||
|
@ -175,11 +328,13 @@ CertVerifier::VerifyCert(CERTCertificate* cert, SECCertificateUsage usage,
|
|||
/*out*/ UniqueCERTCertList& builtChain,
|
||||
/*optional*/ const Flags flags,
|
||||
/*optional*/ const SECItem* stapledOCSPResponseSECItem,
|
||||
/*optional*/ const SECItem* sctsFromTLSSECItem,
|
||||
/*optional out*/ SECOidTag* evOidPolicy,
|
||||
/*optional out*/ OCSPStaplingStatus* ocspStaplingStatus,
|
||||
/*optional out*/ KeySizeStatus* keySizeStatus,
|
||||
/*optional out*/ SHA1ModeResult* sha1ModeResult,
|
||||
/*optional out*/ PinningTelemetryInfo* pinningTelemetryInfo)
|
||||
/*optional out*/ PinningTelemetryInfo* pinningTelemetryInfo,
|
||||
/*optional out*/ CertificateTransparencyInfo* ctInfo)
|
||||
{
|
||||
MOZ_LOG(gCertVerifierLog, LogLevel::Debug, ("Top of VerifyCert\n"));
|
||||
|
||||
|
@ -255,6 +410,15 @@ CertVerifier::VerifyCert(CERTCertificate* cert, SECCertificateUsage usage,
|
|||
stapledOCSPResponse = &stapledOCSPResponseInput;
|
||||
}
|
||||
|
||||
Input sctsFromTLSInput;
|
||||
if (sctsFromTLSSECItem) {
|
||||
rv = sctsFromTLSInput.Init(sctsFromTLSSECItem->data,
|
||||
sctsFromTLSSECItem->len);
|
||||
// Silently discard the error of the extension being too big,
|
||||
// do not fail the verification.
|
||||
MOZ_ASSERT(rv == Success);
|
||||
}
|
||||
|
||||
switch (usage) {
|
||||
case certificateUsageSSLClient: {
|
||||
// XXX: We don't really have a trust bit for SSL client authentication so
|
||||
|
@ -368,6 +532,12 @@ CertVerifier::VerifyCert(CERTCertificate* cert, SECCertificateUsage usage,
|
|||
if (sha1ModeResult) {
|
||||
*sha1ModeResult = sha1ModeResults[i];
|
||||
}
|
||||
rv = VerifySignedCertificateTimestamps(trustDomain, builtChain,
|
||||
sctsFromTLSInput, time,
|
||||
ctInfo);
|
||||
if (rv != Success) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (rv == Success) {
|
||||
|
@ -449,6 +619,12 @@ CertVerifier::VerifyCert(CERTCertificate* cert, SECCertificateUsage usage,
|
|||
if (sha1ModeResult) {
|
||||
*sha1ModeResult = sha1ModeResults[j];
|
||||
}
|
||||
rv = VerifySignedCertificateTimestamps(trustDomain, builtChain,
|
||||
sctsFromTLSInput, time,
|
||||
ctInfo);
|
||||
if (rv != Success) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -631,6 +807,7 @@ CertVerifier::VerifyCert(CERTCertificate* cert, SECCertificateUsage usage,
|
|||
SECStatus
|
||||
CertVerifier::VerifySSLServerCert(const UniqueCERTCertificate& peerCert,
|
||||
/*optional*/ const SECItem* stapledOCSPResponse,
|
||||
/*optional*/ const SECItem* sctsFromTLS,
|
||||
Time time,
|
||||
/*optional*/ void* pinarg,
|
||||
const char* hostname,
|
||||
|
@ -641,7 +818,8 @@ CertVerifier::VerifySSLServerCert(const UniqueCERTCertificate& peerCert,
|
|||
/*optional out*/ OCSPStaplingStatus* ocspStaplingStatus,
|
||||
/*optional out*/ KeySizeStatus* keySizeStatus,
|
||||
/*optional out*/ SHA1ModeResult* sha1ModeResult,
|
||||
/*optional out*/ PinningTelemetryInfo* pinningTelemetryInfo)
|
||||
/*optional out*/ PinningTelemetryInfo* pinningTelemetryInfo,
|
||||
/*optional out*/ CertificateTransparencyInfo* ctInfo)
|
||||
{
|
||||
PR_ASSERT(peerCert);
|
||||
// XXX: PR_ASSERT(pinarg)
|
||||
|
@ -661,9 +839,10 @@ CertVerifier::VerifySSLServerCert(const UniqueCERTCertificate& peerCert,
|
|||
// if VerifyCert succeeded.
|
||||
SECStatus rv = VerifyCert(peerCert.get(), certificateUsageSSLServer, time,
|
||||
pinarg, hostname, builtChain, flags,
|
||||
stapledOCSPResponse, evOidPolicy,
|
||||
ocspStaplingStatus, keySizeStatus,
|
||||
sha1ModeResult, pinningTelemetryInfo);
|
||||
stapledOCSPResponse, sctsFromTLS,
|
||||
evOidPolicy, ocspStaplingStatus, keySizeStatus,
|
||||
sha1ModeResult, pinningTelemetryInfo,
|
||||
ctInfo);
|
||||
if (rv != SECSuccess) {
|
||||
return rv;
|
||||
}
|
||||
|
|
|
@ -8,11 +8,22 @@
|
|||
#define CertVerifier_h
|
||||
|
||||
#include "BRNameMatchingPolicy.h"
|
||||
#include "CTVerifyResult.h"
|
||||
#include "OCSPCache.h"
|
||||
#include "ScopedNSSTypes.h"
|
||||
#include "mozilla/Telemetry.h"
|
||||
#include "mozilla/UniquePtr.h"
|
||||
#include "pkix/pkixtypes.h"
|
||||
|
||||
namespace mozilla { namespace ct {
|
||||
|
||||
// Including MultiLogCTVerifier.h would bring along all of its dependent
|
||||
// headers and force us to export them in moz.build. Just forward-declare
|
||||
// the class here instead.
|
||||
class MultiLogCTVerifier;
|
||||
|
||||
} } // namespace mozilla::ct
|
||||
|
||||
namespace mozilla { namespace psm {
|
||||
|
||||
// These values correspond to the CERT_CHAIN_KEY_SIZE_STATUS telemetry.
|
||||
|
@ -49,6 +60,21 @@ public:
|
|||
void Reset() { accumulateForRoot = false; accumulateResult = false; }
|
||||
};
|
||||
|
||||
class CertificateTransparencyInfo
|
||||
{
|
||||
public:
|
||||
// Was CT enabled?
|
||||
bool enabled;
|
||||
// Did we receive and process any binary SCT data from the supported sources?
|
||||
bool processedSCTs;
|
||||
// Verification result of the processed SCTs.
|
||||
mozilla::ct::CTVerifyResult verifyResult;
|
||||
|
||||
void Reset() { enabled = false; processedSCTs = false; verifyResult.Reset(); }
|
||||
};
|
||||
|
||||
class NSSCertDBTrustDomain;
|
||||
|
||||
class CertVerifier
|
||||
{
|
||||
public:
|
||||
|
@ -79,15 +105,18 @@ public:
|
|||
/*out*/ UniqueCERTCertList& builtChain,
|
||||
Flags flags = 0,
|
||||
/*optional in*/ const SECItem* stapledOCSPResponse = nullptr,
|
||||
/*optional in*/ const SECItem* sctsFromTLS = nullptr,
|
||||
/*optional out*/ SECOidTag* evOidPolicy = nullptr,
|
||||
/*optional out*/ OCSPStaplingStatus* ocspStaplingStatus = nullptr,
|
||||
/*optional out*/ KeySizeStatus* keySizeStatus = nullptr,
|
||||
/*optional out*/ SHA1ModeResult* sha1ModeResult = nullptr,
|
||||
/*optional out*/ PinningTelemetryInfo* pinningTelemetryInfo = nullptr);
|
||||
/*optional out*/ PinningTelemetryInfo* pinningTelemetryInfo = nullptr,
|
||||
/*optional out*/ CertificateTransparencyInfo* ctInfo = nullptr);
|
||||
|
||||
SECStatus VerifySSLServerCert(
|
||||
const UniqueCERTCertificate& peerCert,
|
||||
/*optional*/ const SECItem* stapledOCSPResponse,
|
||||
/*optional*/ const SECItem* sctsFromTLS,
|
||||
mozilla::pkix::Time time,
|
||||
/*optional*/ void* pinarg,
|
||||
const char* hostname,
|
||||
|
@ -98,7 +127,8 @@ public:
|
|||
/*optional out*/ OCSPStaplingStatus* ocspStaplingStatus = nullptr,
|
||||
/*optional out*/ KeySizeStatus* keySizeStatus = nullptr,
|
||||
/*optional out*/ SHA1ModeResult* sha1ModeResult = nullptr,
|
||||
/*optional out*/ PinningTelemetryInfo* pinningTelemetryInfo = nullptr);
|
||||
/*optional out*/ PinningTelemetryInfo* pinningTelemetryInfo = nullptr,
|
||||
/*optional out*/ CertificateTransparencyInfo* ctInfo = nullptr);
|
||||
|
||||
enum PinningMode {
|
||||
pinningDisabled = 0,
|
||||
|
@ -126,11 +156,17 @@ public:
|
|||
enum OcspStrictConfig { ocspRelaxed = 0, ocspStrict };
|
||||
enum OcspGetConfig { ocspGetDisabled = 0, ocspGetEnabled = 1 };
|
||||
|
||||
enum class CertificateTransparencyMode {
|
||||
Disabled = 0,
|
||||
TelemetryOnly = 1,
|
||||
};
|
||||
|
||||
CertVerifier(OcspDownloadConfig odc, OcspStrictConfig osc,
|
||||
OcspGetConfig ogc, uint32_t certShortLifetimeInDays,
|
||||
PinningMode pinningMode, SHA1Mode sha1Mode,
|
||||
BRNameMatchingPolicy::Mode nameMatchingMode,
|
||||
NetscapeStepUpPolicy netscapeStepUpPolicy);
|
||||
NetscapeStepUpPolicy netscapeStepUpPolicy,
|
||||
CertificateTransparencyMode ctMode);
|
||||
~CertVerifier();
|
||||
|
||||
void ClearOCSPCache() { mOCSPCache.Clear(); }
|
||||
|
@ -143,10 +179,23 @@ public:
|
|||
const SHA1Mode mSHA1Mode;
|
||||
const BRNameMatchingPolicy::Mode mNameMatchingMode;
|
||||
const NetscapeStepUpPolicy mNetscapeStepUpPolicy;
|
||||
const CertificateTransparencyMode mCTMode;
|
||||
|
||||
private:
|
||||
OCSPCache mOCSPCache;
|
||||
|
||||
// We only have a forward declaration of MultiLogCTVerifier (see above),
|
||||
// so we keep a pointer to it and allocate dynamically.
|
||||
UniquePtr<mozilla::ct::MultiLogCTVerifier> mCTVerifier;
|
||||
|
||||
void LoadKnownCTLogs();
|
||||
mozilla::pkix::Result VerifySignedCertificateTimestamps(
|
||||
NSSCertDBTrustDomain& trustDomain,
|
||||
const UniqueCERTCertList& builtChain,
|
||||
mozilla::pkix::Input sctsFromTLS,
|
||||
mozilla::pkix::Time time,
|
||||
/*optional out*/ CertificateTransparencyInfo* ctInfo);
|
||||
|
||||
// Returns true if the configured SHA1 mode is more restrictive than the given
|
||||
// mode. SHA1Mode::Forbidden is more restrictive than any other mode except
|
||||
// Forbidden. Next is ImportedRoot, then ImportedRootOrBefore2016, then
|
||||
|
|
|
@ -8,55 +8,26 @@
|
|||
|
||||
#include "CTObjectsExtractor.h"
|
||||
#include "CTSerialization.h"
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/Move.h"
|
||||
|
||||
namespace mozilla { namespace ct {
|
||||
|
||||
using namespace mozilla::pkix;
|
||||
|
||||
// The possible verification statuses for a Signed Certificate Timestamp.
|
||||
enum class SCTVerifyStatus {
|
||||
UnknownLog, // The SCT is from an unknown log and can not be verified.
|
||||
Invalid, // The SCT is from a known log, but the signature is invalid.
|
||||
OK // The SCT is from a known log, and the signature is valid.
|
||||
};
|
||||
|
||||
// Note: this moves |sct| to the target list in |result|, invalidating |sct|.
|
||||
static Result
|
||||
StoreVerifiedSct(CTVerifyResult& result,
|
||||
SignedCertificateTimestamp&& sct,
|
||||
SCTVerifyStatus status)
|
||||
SignedCertificateTimestamp::VerificationStatus status)
|
||||
{
|
||||
SCTList* target;
|
||||
switch (status) {
|
||||
case SCTVerifyStatus::UnknownLog:
|
||||
target = &result.unknownLogsScts;
|
||||
break;
|
||||
case SCTVerifyStatus::Invalid:
|
||||
target = &result.invalidScts;
|
||||
break;
|
||||
case SCTVerifyStatus::OK:
|
||||
target = &result.verifiedScts;
|
||||
break;
|
||||
default:
|
||||
MOZ_ASSERT_UNREACHABLE("Unexpected SCTVerifyStatus type");
|
||||
return Result::FATAL_ERROR_LIBRARY_FAILURE;
|
||||
}
|
||||
if (!target->append(Move(sct))) {
|
||||
sct.verificationStatus = status;
|
||||
if (!result.scts.append(Move(sct))) {
|
||||
return Result::FATAL_ERROR_NO_MEMORY;
|
||||
}
|
||||
return Success;
|
||||
}
|
||||
|
||||
void
|
||||
CTVerifyResult::Reset()
|
||||
{
|
||||
verifiedScts.clear();
|
||||
invalidScts.clear();
|
||||
unknownLogsScts.clear();
|
||||
decodingErrors = 0;
|
||||
}
|
||||
|
||||
Result
|
||||
MultiLogCTVerifier::AddLog(Input publicKey)
|
||||
{
|
||||
|
@ -77,7 +48,7 @@ MultiLogCTVerifier::Verify(Input cert,
|
|||
Input sctListFromCert,
|
||||
Input sctListFromOCSPResponse,
|
||||
Input sctListFromTLSExtension,
|
||||
uint64_t time,
|
||||
Time time,
|
||||
CTVerifyResult& result)
|
||||
{
|
||||
MOZ_ASSERT(cert.GetLength() > 0);
|
||||
|
@ -133,7 +104,7 @@ Result
|
|||
MultiLogCTVerifier::VerifySCTs(Input encodedSctList,
|
||||
const LogEntry& expectedEntry,
|
||||
SignedCertificateTimestamp::Origin origin,
|
||||
uint64_t time,
|
||||
Time time,
|
||||
CTVerifyResult& result)
|
||||
{
|
||||
Reader listReader;
|
||||
|
@ -171,7 +142,7 @@ MultiLogCTVerifier::VerifySCTs(Input encodedSctList,
|
|||
Result
|
||||
MultiLogCTVerifier::VerifySingleSCT(SignedCertificateTimestamp&& sct,
|
||||
const LogEntry& expectedEntry,
|
||||
uint64_t time,
|
||||
Time time,
|
||||
CTVerifyResult& result)
|
||||
{
|
||||
CTLogVerifier* matchingLog = nullptr;
|
||||
|
@ -184,28 +155,39 @@ MultiLogCTVerifier::VerifySingleSCT(SignedCertificateTimestamp&& sct,
|
|||
|
||||
if (!matchingLog) {
|
||||
// SCT does not match any known log.
|
||||
return StoreVerifiedSct(result, Move(sct), SCTVerifyStatus::UnknownLog);
|
||||
return StoreVerifiedSct(result, Move(sct),
|
||||
SignedCertificateTimestamp::VerificationStatus::UnknownLog);
|
||||
}
|
||||
|
||||
if (!matchingLog->SignatureParametersMatch(sct.signature)) {
|
||||
// SCT signature parameters do not match the log's.
|
||||
return StoreVerifiedSct(result, Move(sct), SCTVerifyStatus::Invalid);
|
||||
return StoreVerifiedSct(result, Move(sct),
|
||||
SignedCertificateTimestamp::VerificationStatus::InvalidSignature);
|
||||
}
|
||||
|
||||
Result rv = matchingLog->Verify(expectedEntry, sct);
|
||||
if (rv != Success) {
|
||||
if (rv == Result::ERROR_BAD_SIGNATURE) {
|
||||
return StoreVerifiedSct(result, Move(sct), SCTVerifyStatus::Invalid);
|
||||
return StoreVerifiedSct(result, Move(sct),
|
||||
SignedCertificateTimestamp::VerificationStatus::InvalidSignature);
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
// |sct.timestamp| is measured in milliseconds since the epoch,
|
||||
// ignoring leap seconds. When converting it to a second-level precision
|
||||
// pkix::Time, we need to round it either up or down. In our case, rounding up
|
||||
// is more "secure", although practically it does not matter.
|
||||
Time sctTime = TimeFromEpochInSeconds((sct.timestamp + 999u) / 1000u);
|
||||
|
||||
// SCT verified ok, just make sure the timestamp is legitimate.
|
||||
if (sct.timestamp > time) {
|
||||
return StoreVerifiedSct(result, Move(sct), SCTVerifyStatus::Invalid);
|
||||
if (sctTime > time) {
|
||||
return StoreVerifiedSct(result, Move(sct),
|
||||
SignedCertificateTimestamp::VerificationStatus::InvalidTimestamp);
|
||||
}
|
||||
|
||||
return StoreVerifiedSct(result, Move(sct), SCTVerifyStatus::OK);
|
||||
return StoreVerifiedSct(result, Move(sct),
|
||||
SignedCertificateTimestamp::VerificationStatus::OK);
|
||||
}
|
||||
|
||||
} } // namespace mozilla::ct
|
||||
|
|
|
@ -8,45 +8,15 @@
|
|||
#define MultiLogCTVerifier_h
|
||||
|
||||
#include "CTLogVerifier.h"
|
||||
#include "CTVerifyResult.h"
|
||||
#include "mozilla/Vector.h"
|
||||
#include "pkix/Input.h"
|
||||
#include "pkix/Result.h"
|
||||
#include "pkix/Time.h"
|
||||
#include "SignedCertificateTimestamp.h"
|
||||
|
||||
namespace mozilla { namespace ct {
|
||||
|
||||
typedef Vector<SignedCertificateTimestamp> SCTList;
|
||||
|
||||
// Holds Signed Certificate Timestamps, arranged by their verification results.
|
||||
class CTVerifyResult
|
||||
{
|
||||
public:
|
||||
// SCTs from known logs where the signature verified correctly.
|
||||
SCTList verifiedScts;
|
||||
|
||||
// SCTs from known logs where the signature failed to verify.
|
||||
SCTList invalidScts;
|
||||
|
||||
// SCTs from unknown logs and as such are unverifiable.
|
||||
SCTList unknownLogsScts;
|
||||
|
||||
// For a certificate to pass Certificate Transparency verification, at least
|
||||
// one of the provided SCTs must validate. The verifier makes the best effort
|
||||
// to extract the available SCTs from the binary sources provided to it.
|
||||
// If some SCT cannot be extracted due to encoding errors, the verifier
|
||||
// proceeds to the next available one. In other words, decoding errors are
|
||||
// effectively ignored.
|
||||
// Note that a serialized SCT may fail to decode for a "legitimate" reason,
|
||||
// e.g. if the SCT is from a future version of the Certificate Transparency
|
||||
// standard.
|
||||
// |decodingErrors| field counts the errors of the above kind.
|
||||
// This field is purely informational; there is probably nothing to do with it
|
||||
// in release builds, but it is useful in unit tests.
|
||||
size_t decodingErrors;
|
||||
|
||||
void Reset();
|
||||
};
|
||||
|
||||
// A Certificate Transparency verifier that can verify Signed Certificate
|
||||
// Timestamps from multiple logs.
|
||||
class MultiLogCTVerifier
|
||||
|
@ -80,9 +50,6 @@ public:
|
|||
// |sctListFromTLSExtension| is the SCT list from the TLS extension. Empty
|
||||
// if no extension was present.
|
||||
// |time| the current time. Used to make sure SCTs are not in the future.
|
||||
// Measured in milliseconds since the epoch, ignoring leap seconds
|
||||
// (same format as used by the "timestamp" field of
|
||||
// SignedCertificateTimestamp).
|
||||
// |result| will be filled with the SCTs present, divided into categories
|
||||
// based on the verification result.
|
||||
pkix::Result Verify(pkix::Input cert,
|
||||
|
@ -90,7 +57,7 @@ public:
|
|||
pkix::Input sctListFromCert,
|
||||
pkix::Input sctListFromOCSPResponse,
|
||||
pkix::Input sctListFromTLSExtension,
|
||||
uint64_t time,
|
||||
pkix::Time time,
|
||||
CTVerifyResult& result);
|
||||
|
||||
private:
|
||||
|
@ -100,14 +67,14 @@ private:
|
|||
pkix::Result VerifySCTs(pkix::Input encodedSctList,
|
||||
const LogEntry& expectedEntry,
|
||||
SignedCertificateTimestamp::Origin origin,
|
||||
uint64_t time,
|
||||
pkix::Time time,
|
||||
CTVerifyResult& result);
|
||||
|
||||
// Verifies a single, parsed SCT against all known logs.
|
||||
// Note: moves |sct| to the target list in |result|, invalidating |sct|.
|
||||
pkix::Result VerifySingleSCT(SignedCertificateTimestamp&& sct,
|
||||
const ct::LogEntry& expectedEntry,
|
||||
uint64_t time,
|
||||
pkix::Time time,
|
||||
CTVerifyResult& result);
|
||||
|
||||
// The list of known logs.
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include "PublicKeyPinningService.h"
|
||||
#include "cert.h"
|
||||
#include "certdb.h"
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/Casting.h"
|
||||
#include "mozilla/UniquePtr.h"
|
||||
#include "mozilla/Unused.h"
|
||||
|
@ -73,6 +74,8 @@ NSSCertDBTrustDomain::NSSCertDBTrustDomain(SECTrustType certDBTrustType,
|
|||
, mHostname(hostname)
|
||||
, mCertBlocklist(do_GetService(NS_CERTBLOCKLIST_CONTRACTID))
|
||||
, mOCSPStaplingStatus(CertVerifier::OCSP_STAPLING_NEVER_CHECKED)
|
||||
, mSCTListFromCertificate()
|
||||
, mSCTListFromOCSPStapling()
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -963,9 +966,59 @@ NSSCertDBTrustDomain::NetscapeStepUpMatchesServerAuth(Time notBefore,
|
|||
}
|
||||
|
||||
void
|
||||
NSSCertDBTrustDomain::NoteAuxiliaryExtension(AuxiliaryExtension /*extension*/,
|
||||
Input /*extensionData*/)
|
||||
NSSCertDBTrustDomain::ResetAccumulatedState()
|
||||
{
|
||||
mOCSPStaplingStatus = CertVerifier::OCSP_STAPLING_NEVER_CHECKED;
|
||||
mSCTListFromOCSPStapling = nullptr;
|
||||
mSCTListFromCertificate = nullptr;
|
||||
}
|
||||
|
||||
static Input
|
||||
SECItemToInput(const UniqueSECItem& item)
|
||||
{
|
||||
Input result;
|
||||
if (item) {
|
||||
MOZ_ASSERT(item->type == siBuffer);
|
||||
Result rv = result.Init(item->data, item->len);
|
||||
// As used here, |item| originally comes from an Input,
|
||||
// so there should be no issues converting it back.
|
||||
MOZ_ASSERT(rv == Success);
|
||||
Unused << rv; // suppresses warnings in release builds
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
Input
|
||||
NSSCertDBTrustDomain::GetSCTListFromCertificate() const
|
||||
{
|
||||
return SECItemToInput(mSCTListFromCertificate);
|
||||
}
|
||||
|
||||
Input
|
||||
NSSCertDBTrustDomain::GetSCTListFromOCSPStapling() const
|
||||
{
|
||||
return SECItemToInput(mSCTListFromOCSPStapling);
|
||||
}
|
||||
|
||||
void
|
||||
NSSCertDBTrustDomain::NoteAuxiliaryExtension(AuxiliaryExtension extension,
|
||||
Input extensionData)
|
||||
{
|
||||
UniqueSECItem* out = nullptr;
|
||||
switch (extension) {
|
||||
case AuxiliaryExtension::EmbeddedSCTList:
|
||||
out = &mSCTListFromCertificate;
|
||||
break;
|
||||
case AuxiliaryExtension::SCTListFromOCSPResponse:
|
||||
out = &mSCTListFromOCSPStapling;
|
||||
break;
|
||||
default:
|
||||
MOZ_ASSERT_UNREACHABLE("unhandled AuxiliaryExtension");
|
||||
}
|
||||
if (out) {
|
||||
SECItem extensionDataItem = UnsafeMapInputToSECItem(extensionData);
|
||||
out->reset(SECITEM_DupItem(&extensionDataItem));
|
||||
}
|
||||
}
|
||||
|
||||
SECStatus
|
||||
|
|
|
@ -145,14 +145,22 @@ public:
|
|||
mozilla::pkix::AuxiliaryExtension extension,
|
||||
mozilla::pkix::Input extensionData) override;
|
||||
|
||||
// Resets the OCSP stapling status and SCT lists accumulated during
|
||||
// the chain building.
|
||||
void ResetAccumulatedState();
|
||||
|
||||
CertVerifier::OCSPStaplingStatus GetOCSPStaplingStatus() const
|
||||
{
|
||||
return mOCSPStaplingStatus;
|
||||
}
|
||||
void ResetOCSPStaplingStatus()
|
||||
{
|
||||
mOCSPStaplingStatus = CertVerifier::OCSP_STAPLING_NEVER_CHECKED;
|
||||
}
|
||||
|
||||
// SCT lists (see Certificate Transparency) extracted during
|
||||
// certificate verification. Note that the returned Inputs are invalidated
|
||||
// the next time a chain is built and by ResetAccumulatedState method
|
||||
// (and when the TrustDomain object is destroyed).
|
||||
|
||||
mozilla::pkix::Input GetSCTListFromCertificate() const;
|
||||
mozilla::pkix::Input GetSCTListFromOCSPStapling() const;
|
||||
|
||||
private:
|
||||
enum EncodedResponseSource {
|
||||
|
@ -180,6 +188,9 @@ private:
|
|||
const char* mHostname; // non-owning - only used for pinning checks
|
||||
nsCOMPtr<nsICertBlocklist> mCertBlocklist;
|
||||
CertVerifier::OCSPStaplingStatus mOCSPStaplingStatus;
|
||||
// Certificate Transparency data extracted during certificate verification
|
||||
UniqueSECItem mSCTListFromCertificate;
|
||||
UniqueSECItem mSCTListFromOCSPStapling;
|
||||
};
|
||||
|
||||
} } // namespace mozilla::psm
|
||||
|
|
|
@ -78,15 +78,6 @@ struct SignedCertificateTimestamp
|
|||
V1 = 0,
|
||||
};
|
||||
|
||||
// Source of the SCT - supplementary, not defined in CT RFC.
|
||||
// Note: The numeric values are used within histograms and should not change
|
||||
// or be re-assigned.
|
||||
enum class Origin {
|
||||
Embedded = 0,
|
||||
TLSExtension = 1,
|
||||
OCSPResponse = 2,
|
||||
};
|
||||
|
||||
Version version;
|
||||
Buffer logId;
|
||||
// "timestamp" is the current time in milliseconds, measured since the epoch,
|
||||
|
@ -94,7 +85,32 @@ struct SignedCertificateTimestamp
|
|||
uint64_t timestamp;
|
||||
Buffer extensions;
|
||||
DigitallySigned signature;
|
||||
|
||||
// Supplementary fields, not defined in CT RFC. Set during the various
|
||||
// stages of processing the received SCTs.
|
||||
|
||||
enum class Origin {
|
||||
Unknown,
|
||||
Embedded,
|
||||
TLSExtension,
|
||||
OCSPResponse
|
||||
};
|
||||
|
||||
enum class VerificationStatus {
|
||||
None,
|
||||
// The SCT is from a known log, and the signature is valid.
|
||||
OK,
|
||||
// The SCT is from an unknown log and can not be verified.
|
||||
UnknownLog,
|
||||
// The SCT is from a known log, but the signature is invalid.
|
||||
InvalidSignature,
|
||||
// The SCT signature is valid, but the timestamp is in the future.
|
||||
// Such SCT are considered invalid (see RFC 6962, Section 5.2).
|
||||
InvalidTimestamp
|
||||
};
|
||||
|
||||
Origin origin;
|
||||
VerificationStatus verificationStatus;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -7,7 +7,10 @@
|
|||
EXPORTS += [
|
||||
'BRNameMatchingPolicy.h',
|
||||
'CertVerifier.h',
|
||||
'CTVerifyResult.h',
|
||||
'OCSPCache.h',
|
||||
'SignedCertificateTimestamp.h',
|
||||
'SignedTreeHead.h',
|
||||
]
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
|
@ -16,6 +19,7 @@ UNIFIED_SOURCES += [
|
|||
'CTLogVerifier.cpp',
|
||||
'CTObjectsExtractor.cpp',
|
||||
'CTSerialization.cpp',
|
||||
'CTVerifyResult.cpp',
|
||||
'MultiLogCTVerifier.cpp',
|
||||
'NSSCertDBTrustDomain.cpp',
|
||||
'OCSPCache.cpp',
|
||||
|
|
|
@ -24,6 +24,10 @@ using namespace mozilla::pkix;
|
|||
class MultiLogCTVerifierTest : public ::testing::Test
|
||||
{
|
||||
public:
|
||||
MultiLogCTVerifierTest()
|
||||
: mNow(Time::uninitialized)
|
||||
{}
|
||||
|
||||
void SetUp() override
|
||||
{
|
||||
// Does nothing if NSS is already initialized.
|
||||
|
@ -39,17 +43,17 @@ public:
|
|||
mIntermediateCertSPKI = ExtractCertSPKI(mIntermediateCert);
|
||||
|
||||
// Set the current time making sure all test timestamps are in the past.
|
||||
mNow = UINT64_MAX;
|
||||
mNow = TimeFromEpochInSeconds(1451606400u); // Date.parse("2016-01-01")/1000
|
||||
}
|
||||
|
||||
void CheckForSingleVerifiedSCTInResult(const CTVerifyResult& result,
|
||||
SignedCertificateTimestamp::Origin origin)
|
||||
{
|
||||
EXPECT_EQ(0U, result.decodingErrors);
|
||||
EXPECT_TRUE(result.invalidScts.empty());
|
||||
EXPECT_TRUE(result.unknownLogsScts.empty());
|
||||
ASSERT_EQ(1U, result.verifiedScts.length());
|
||||
EXPECT_EQ(origin, result.verifiedScts[0].origin);
|
||||
ASSERT_EQ(1U, result.scts.length());
|
||||
EXPECT_EQ(SignedCertificateTimestamp::VerificationStatus::OK,
|
||||
result.scts[0].verificationStatus);
|
||||
EXPECT_EQ(origin, result.scts[0].origin);
|
||||
}
|
||||
|
||||
// Writes an SCTList containing a single |sct| into |output|.
|
||||
|
@ -93,7 +97,7 @@ protected:
|
|||
Buffer mCaCertSPKI;
|
||||
Buffer mIntermediateCert;
|
||||
Buffer mIntermediateCertSPKI;
|
||||
uint64_t mNow;
|
||||
Time mNow;
|
||||
};
|
||||
|
||||
// Test that an embedded SCT can be extracted and the extracted SCT contains
|
||||
|
@ -196,7 +200,9 @@ TEST_F(MultiLogCTVerifierTest, VerifiesSCTFromMultipleSources)
|
|||
|
||||
// The result should contain verified SCTs from TLS and OCSP origins.
|
||||
EnumSet<SignedCertificateTimestamp::Origin> origins;
|
||||
for (auto& sct : result.verifiedScts) {
|
||||
for (const SignedCertificateTimestamp& sct : result.scts) {
|
||||
EXPECT_EQ(SignedCertificateTimestamp::VerificationStatus::OK,
|
||||
sct.verificationStatus);
|
||||
origins += sct.origin;
|
||||
}
|
||||
EXPECT_FALSE(
|
||||
|
@ -218,8 +224,10 @@ TEST_F(MultiLogCTVerifierTest, IdentifiesSCTFromUnknownLog)
|
|||
Input(), Input(), InputForBuffer(sctList),
|
||||
mNow, result));
|
||||
|
||||
EXPECT_EQ(1U, result.unknownLogsScts.length());
|
||||
EXPECT_EQ(0U, result.decodingErrors);
|
||||
ASSERT_EQ(1U, result.scts.length());
|
||||
EXPECT_EQ(SignedCertificateTimestamp::VerificationStatus::UnknownLog,
|
||||
result.scts[0].verificationStatus);
|
||||
}
|
||||
|
||||
} } // namespace mozilla::ct
|
||||
|
|
|
@ -739,7 +739,8 @@ public:
|
|||
nsNSSSocketInfo* infoObject,
|
||||
const UniqueCERTCertificate& serverCert,
|
||||
const UniqueCERTCertList& peerCertChain,
|
||||
SECItem* stapledOCSPResponse,
|
||||
const SECItem* stapledOCSPResponse,
|
||||
const SECItem* sctsFromTLSExtension,
|
||||
uint32_t providerFlags,
|
||||
Time time,
|
||||
PRTime prtime);
|
||||
|
@ -752,7 +753,8 @@ private:
|
|||
nsNSSSocketInfo* infoObject,
|
||||
const UniqueCERTCertificate& cert,
|
||||
UniqueCERTCertList peerCertChain,
|
||||
SECItem* stapledOCSPResponse,
|
||||
const SECItem* stapledOCSPResponse,
|
||||
const SECItem* sctsFromTLSExtension,
|
||||
uint32_t providerFlags,
|
||||
Time time,
|
||||
PRTime prtime);
|
||||
|
@ -766,12 +768,14 @@ private:
|
|||
const PRTime mPRTime;
|
||||
const TimeStamp mJobStartTime;
|
||||
const UniqueSECItem mStapledOCSPResponse;
|
||||
const UniqueSECItem mSCTsFromTLSExtension;
|
||||
};
|
||||
|
||||
SSLServerCertVerificationJob::SSLServerCertVerificationJob(
|
||||
const RefPtr<SharedCertVerifier>& certVerifier, const void* fdForLogging,
|
||||
nsNSSSocketInfo* infoObject, const UniqueCERTCertificate& cert,
|
||||
UniqueCERTCertList peerCertChain, SECItem* stapledOCSPResponse,
|
||||
UniqueCERTCertList peerCertChain, const SECItem* stapledOCSPResponse,
|
||||
const SECItem* sctsFromTLSExtension,
|
||||
uint32_t providerFlags, Time time, PRTime prtime)
|
||||
: mCertVerifier(certVerifier)
|
||||
, mFdForLogging(fdForLogging)
|
||||
|
@ -783,6 +787,7 @@ SSLServerCertVerificationJob::SSLServerCertVerificationJob(
|
|||
, mPRTime(prtime)
|
||||
, mJobStartTime(TimeStamp::Now())
|
||||
, mStapledOCSPResponse(SECITEM_DupItem(stapledOCSPResponse))
|
||||
, mSCTsFromTLSExtension(SECITEM_DupItem(sctsFromTLSExtension))
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -1214,13 +1219,88 @@ GatherSuccessfulValidationTelemetry(const UniqueCERTCertList& certList)
|
|||
GatherEndEntityTelemetry(certList);
|
||||
}
|
||||
|
||||
void
|
||||
GatherTelemetryForSingleSCT(const ct::SignedCertificateTimestamp& sct)
|
||||
{
|
||||
// See SSL_SCTS_ORIGIN in Histograms.json.
|
||||
uint32_t origin = 0;
|
||||
switch (sct.origin) {
|
||||
case ct::SignedCertificateTimestamp::Origin::Embedded:
|
||||
origin = 1;
|
||||
break;
|
||||
case ct::SignedCertificateTimestamp::Origin::TLSExtension:
|
||||
origin = 2;
|
||||
break;
|
||||
case ct::SignedCertificateTimestamp::Origin::OCSPResponse:
|
||||
origin = 3;
|
||||
break;
|
||||
default:
|
||||
MOZ_ASSERT_UNREACHABLE("Unexpected SCT::Origin type");
|
||||
}
|
||||
Telemetry::Accumulate(Telemetry::SSL_SCTS_ORIGIN, origin);
|
||||
|
||||
// See SSL_SCTS_VERIFICATION_STATUS in Histograms.json.
|
||||
uint32_t verificationStatus = 0;
|
||||
switch (sct.verificationStatus) {
|
||||
case ct::SignedCertificateTimestamp::VerificationStatus::OK:
|
||||
verificationStatus = 1;
|
||||
break;
|
||||
case ct::SignedCertificateTimestamp::VerificationStatus::UnknownLog:
|
||||
verificationStatus = 2;
|
||||
break;
|
||||
case ct::SignedCertificateTimestamp::VerificationStatus::InvalidSignature:
|
||||
verificationStatus = 3;
|
||||
break;
|
||||
case ct::SignedCertificateTimestamp::VerificationStatus::InvalidTimestamp:
|
||||
verificationStatus = 4;
|
||||
break;
|
||||
default:
|
||||
MOZ_ASSERT_UNREACHABLE("Unexpected SCT::VerificationStatus type");
|
||||
}
|
||||
Telemetry::Accumulate(Telemetry::SSL_SCTS_VERIFICATION_STATUS,
|
||||
verificationStatus);
|
||||
}
|
||||
|
||||
void
|
||||
GatherCertificateTransparencyTelemetry(const UniqueCERTCertList& certList,
|
||||
const CertificateTransparencyInfo& info)
|
||||
{
|
||||
if (!info.enabled) {
|
||||
// No telemetry is gathered when CT is disabled.
|
||||
return;
|
||||
}
|
||||
|
||||
if (!info.processedSCTs) {
|
||||
// We didn't receive any SCT data for this connection.
|
||||
Telemetry::Accumulate(Telemetry::SSL_SCTS_PER_CONNECTION, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
for (const ct::SignedCertificateTimestamp& sct : info.verifyResult.scts) {
|
||||
GatherTelemetryForSingleSCT(sct);
|
||||
}
|
||||
|
||||
// Decoding errors are reported to the 0th bucket
|
||||
// of the SSL_SCTS_VERIFICATION_STATUS enumerated probe.
|
||||
for (size_t i = 0; i < info.verifyResult.decodingErrors; ++i) {
|
||||
Telemetry::Accumulate(Telemetry::SSL_SCTS_VERIFICATION_STATUS, 0);
|
||||
}
|
||||
|
||||
// Handle the histogram of SCTs counts.
|
||||
uint32_t sctsCount = static_cast<uint32_t>(info.verifyResult.scts.length());
|
||||
// Note that sctsCount can be 0 in case we've received SCT binary data,
|
||||
// but it failed to parse (e.g. due to unsupported CT protocol version).
|
||||
Telemetry::Accumulate(Telemetry::SSL_SCTS_PER_CONNECTION, sctsCount);
|
||||
}
|
||||
|
||||
// Note: Takes ownership of |peerCertChain| if SECSuccess is not returned.
|
||||
SECStatus
|
||||
AuthCertificate(CertVerifier& certVerifier,
|
||||
nsNSSSocketInfo* infoObject,
|
||||
const UniqueCERTCertificate& cert,
|
||||
UniqueCERTCertList& peerCertChain,
|
||||
SECItem* stapledOCSPResponse,
|
||||
const SECItem* stapledOCSPResponse,
|
||||
const SECItem* sctsFromTLSExtension,
|
||||
uint32_t providerFlags,
|
||||
Time time)
|
||||
{
|
||||
|
@ -1241,6 +1321,7 @@ AuthCertificate(CertVerifier& certVerifier,
|
|||
KeySizeStatus keySizeStatus = KeySizeStatus::NeverChecked;
|
||||
SHA1ModeResult sha1ModeResult = SHA1ModeResult::NeverChecked;
|
||||
PinningTelemetryInfo pinningTelemetryInfo;
|
||||
CertificateTransparencyInfo certificateTransparencyInfo;
|
||||
|
||||
int flags = 0;
|
||||
if (!infoObject->SharedState().IsOCSPStaplingEnabled() ||
|
||||
|
@ -1249,12 +1330,13 @@ AuthCertificate(CertVerifier& certVerifier,
|
|||
}
|
||||
|
||||
rv = certVerifier.VerifySSLServerCert(cert, stapledOCSPResponse,
|
||||
time, infoObject,
|
||||
sctsFromTLSExtension, time, infoObject,
|
||||
infoObject->GetHostNameRaw(),
|
||||
certList, saveIntermediates, flags,
|
||||
&evOidPolicy, &ocspStaplingStatus,
|
||||
&keySizeStatus, &sha1ModeResult,
|
||||
&pinningTelemetryInfo);
|
||||
&pinningTelemetryInfo,
|
||||
&certificateTransparencyInfo);
|
||||
PRErrorCode savedErrorCode;
|
||||
if (rv != SECSuccess) {
|
||||
savedErrorCode = PR_GetError();
|
||||
|
@ -1305,6 +1387,8 @@ AuthCertificate(CertVerifier& certVerifier,
|
|||
|
||||
if (rv == SECSuccess) {
|
||||
GatherSuccessfulValidationTelemetry(certList);
|
||||
GatherCertificateTransparencyTelemetry(certList,
|
||||
certificateTransparencyInfo);
|
||||
|
||||
// The connection may get terminated, for example, if the server requires
|
||||
// a client cert. Let's provide a minimal SSLStatus
|
||||
|
@ -1357,7 +1441,8 @@ SSLServerCertVerificationJob::Dispatch(
|
|||
nsNSSSocketInfo* infoObject,
|
||||
const UniqueCERTCertificate& serverCert,
|
||||
const UniqueCERTCertList& peerCertChain,
|
||||
SECItem* stapledOCSPResponse,
|
||||
const SECItem* stapledOCSPResponse,
|
||||
const SECItem* sctsFromTLSExtension,
|
||||
uint32_t providerFlags,
|
||||
Time time,
|
||||
PRTime prtime)
|
||||
|
@ -1384,8 +1469,8 @@ SSLServerCertVerificationJob::Dispatch(
|
|||
RefPtr<SSLServerCertVerificationJob> job(
|
||||
new SSLServerCertVerificationJob(certVerifier, fdForLogging, infoObject,
|
||||
serverCert, Move(peerCertChainCopy),
|
||||
stapledOCSPResponse, providerFlags,
|
||||
time, prtime));
|
||||
stapledOCSPResponse, sctsFromTLSExtension,
|
||||
providerFlags, time, prtime));
|
||||
|
||||
nsresult nrv;
|
||||
if (!gCertVerificationThreadPool) {
|
||||
|
@ -1436,6 +1521,7 @@ SSLServerCertVerificationJob::Run()
|
|||
PR_SetError(0, 0);
|
||||
SECStatus rv = AuthCertificate(*mCertVerifier, mInfoObject, mCert,
|
||||
mPeerCertChain, mStapledOCSPResponse.get(),
|
||||
mSCTsFromTLSExtension.get(),
|
||||
mProviderFlags, mTime);
|
||||
MOZ_ASSERT(mPeerCertChain || rv != SECSuccess,
|
||||
"AuthCertificate() should take ownership of chain on failure");
|
||||
|
@ -1586,6 +1672,14 @@ AuthCertificateHook(void* arg, PRFileDesc* fd, PRBool checkSig, PRBool isServer)
|
|||
stapledOCSPResponse = &csa->items[0];
|
||||
}
|
||||
|
||||
const SECItem* sctsFromTLSExtension = SSL_PeerSignedCertTimestamps(fd);
|
||||
if (sctsFromTLSExtension && sctsFromTLSExtension->len == 0) {
|
||||
// SSL_PeerSignedCertTimestamps returns null on error and empty item
|
||||
// when no extension was returned by the server. We always use null when
|
||||
// no extension was received (for whatever reason), ignoring errors.
|
||||
sctsFromTLSExtension = nullptr;
|
||||
}
|
||||
|
||||
uint32_t providerFlags = 0;
|
||||
socketInfo->GetProviderFlags(&providerFlags);
|
||||
|
||||
|
@ -1599,7 +1693,7 @@ AuthCertificateHook(void* arg, PRFileDesc* fd, PRBool checkSig, PRBool isServer)
|
|||
SECStatus rv = SSLServerCertVerificationJob::Dispatch(
|
||||
certVerifier, static_cast<const void*>(fd), socketInfo,
|
||||
serverCert, peerCertChain, stapledOCSPResponse,
|
||||
providerFlags, now, prnow);
|
||||
sctsFromTLSExtension, providerFlags, now, prnow);
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
@ -1610,7 +1704,7 @@ AuthCertificateHook(void* arg, PRFileDesc* fd, PRBool checkSig, PRBool isServer)
|
|||
|
||||
SECStatus rv = AuthCertificate(*certVerifier, socketInfo, serverCert,
|
||||
peerCertChain, stapledOCSPResponse,
|
||||
providerFlags, now);
|
||||
sctsFromTLSExtension, providerFlags, now);
|
||||
MOZ_ASSERT(peerCertChain || rv != SECSuccess,
|
||||
"AuthCertificate() should take ownership of chain on failure");
|
||||
if (rv == SECSuccess) {
|
||||
|
|
|
@ -22,10 +22,11 @@ public:
|
|||
OcspGetConfig ogc, uint32_t certShortLifetimeInDays,
|
||||
PinningMode pinningMode, SHA1Mode sha1Mode,
|
||||
BRNameMatchingPolicy::Mode nameMatchingMode,
|
||||
NetscapeStepUpPolicy netscapeStepUpPolicy)
|
||||
NetscapeStepUpPolicy netscapeStepUpPolicy,
|
||||
CertificateTransparencyMode ctMode)
|
||||
: mozilla::psm::CertVerifier(odc, osc, ogc, certShortLifetimeInDays,
|
||||
pinningMode, sha1Mode, nameMatchingMode,
|
||||
netscapeStepUpPolicy)
|
||||
netscapeStepUpPolicy, ctMode)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
|
|
@ -43,6 +43,10 @@ public:
|
|||
{
|
||||
mOCSPMustStapleEnabled = mustStapleEnabled;
|
||||
}
|
||||
void SetSignedCertTimestampsEnabled(bool signedCertTimestampsEnabled)
|
||||
{
|
||||
mSignedCertTimestampsEnabled = signedCertTimestampsEnabled;
|
||||
}
|
||||
|
||||
// The following methods may be called from any thread
|
||||
bool SocketCreated();
|
||||
|
@ -50,6 +54,10 @@ public:
|
|||
static void NoteCertOverrideServiceInstantiated();
|
||||
bool IsOCSPStaplingEnabled() const { return mOCSPStaplingEnabled; }
|
||||
bool IsOCSPMustStapleEnabled() const { return mOCSPMustStapleEnabled; }
|
||||
bool IsSignedCertTimestampsEnabled() const
|
||||
{
|
||||
return mSignedCertTimestampsEnabled;
|
||||
}
|
||||
|
||||
private:
|
||||
~SharedSSLState();
|
||||
|
@ -67,6 +75,7 @@ private:
|
|||
bool mSocketCreated;
|
||||
bool mOCSPStaplingEnabled;
|
||||
bool mOCSPMustStapleEnabled;
|
||||
bool mSignedCertTimestampsEnabled;
|
||||
};
|
||||
|
||||
SharedSSLState* PublicSSLState();
|
||||
|
|
|
@ -1317,7 +1317,10 @@ nsNSSCertificate::hasValidEVOidTag(SECOidTag& resultOidTag, bool& validEV)
|
|||
nullptr /* XXX pinarg */,
|
||||
nullptr /* hostname */,
|
||||
unusedBuiltChain,
|
||||
flags, nullptr /* stapledOCSPResponse */, &resultOidTag);
|
||||
flags,
|
||||
nullptr /* stapledOCSPResponse */,
|
||||
nullptr /* sctsFromTLSExtension */,
|
||||
&resultOidTag);
|
||||
|
||||
if (rv != SECSuccess) {
|
||||
resultOidTag = SEC_OID_UNKNOWN;
|
||||
|
|
|
@ -1493,6 +1493,7 @@ VerifyCertAtTime(nsIX509Cert* aCert,
|
|||
if (aHostname && aUsage == certificateUsageSSLServer) {
|
||||
srv = certVerifier->VerifySSLServerCert(nssCert,
|
||||
nullptr, // stapledOCSPResponse
|
||||
nullptr, // sctsFromTLSExtension
|
||||
aTime,
|
||||
nullptr, // Assume no context
|
||||
aHostname,
|
||||
|
@ -1507,6 +1508,7 @@ VerifyCertAtTime(nsIX509Cert* aCert,
|
|||
resultChain,
|
||||
aFlags,
|
||||
nullptr, // stapledOCSPResponse
|
||||
nullptr, // sctsFromTLSExtension
|
||||
&evOidPolicy);
|
||||
}
|
||||
|
||||
|
|
|
@ -1502,6 +1502,25 @@ void nsNSSComponent::setValidationOptions(bool isInitialSetting,
|
|||
PublicSSLState()->SetOCSPMustStapleEnabled(ocspMustStapleEnabled);
|
||||
PrivateSSLState()->SetOCSPMustStapleEnabled(ocspMustStapleEnabled);
|
||||
|
||||
const CertVerifier::CertificateTransparencyMode defaultCTMode =
|
||||
CertVerifier::CertificateTransparencyMode::TelemetryOnly;
|
||||
CertVerifier::CertificateTransparencyMode ctMode =
|
||||
static_cast<CertVerifier::CertificateTransparencyMode>
|
||||
(Preferences::GetInt("security.pki.certificate_transparency.mode",
|
||||
static_cast<int32_t>(defaultCTMode)));
|
||||
switch (ctMode) {
|
||||
case CertVerifier::CertificateTransparencyMode::Disabled:
|
||||
case CertVerifier::CertificateTransparencyMode::TelemetryOnly:
|
||||
break;
|
||||
default:
|
||||
ctMode = defaultCTMode;
|
||||
break;
|
||||
}
|
||||
bool sctsEnabled =
|
||||
ctMode != CertVerifier::CertificateTransparencyMode::Disabled;
|
||||
PublicSSLState()->SetSignedCertTimestampsEnabled(sctsEnabled);
|
||||
PrivateSSLState()->SetSignedCertTimestampsEnabled(sctsEnabled);
|
||||
|
||||
CertVerifier::PinningMode pinningMode =
|
||||
static_cast<CertVerifier::PinningMode>
|
||||
(Preferences::GetInt("security.cert_pinning.enforcement_level",
|
||||
|
@ -1571,7 +1590,8 @@ void nsNSSComponent::setValidationOptions(bool isInitialSetting,
|
|||
certShortLifetimeInDays,
|
||||
pinningMode, sha1Mode,
|
||||
nameMatchingMode,
|
||||
netscapeStepUpPolicy);
|
||||
netscapeStepUpPolicy,
|
||||
ctMode);
|
||||
}
|
||||
|
||||
// Enable the TLS versions given in the prefs, defaulting to TLS 1.0 (min) and
|
||||
|
@ -1982,6 +2002,7 @@ nsNSSComponent::Observe(nsISupports* aSubject, const char* aTopic,
|
|||
prefName.EqualsLiteral("security.pki.cert_short_lifetime_in_days") ||
|
||||
prefName.EqualsLiteral("security.ssl.enable_ocsp_stapling") ||
|
||||
prefName.EqualsLiteral("security.ssl.enable_ocsp_must_staple") ||
|
||||
prefName.EqualsLiteral("security.pki.certificate_transparency.mode") ||
|
||||
prefName.EqualsLiteral("security.cert_pinning.enforcement_level") ||
|
||||
prefName.EqualsLiteral("security.pki.sha1_enforcement_level") ||
|
||||
prefName.EqualsLiteral("security.pki.name_matching_mode") ||
|
||||
|
|
|
@ -443,10 +443,15 @@ nsNSSSocketInfo::IsAcceptableForHost(const nsACString& hostname, bool* _retval)
|
|||
nsAutoCString hostnameFlat(PromiseFlatCString(hostname));
|
||||
CertVerifier::Flags flags = CertVerifier::FLAG_LOCAL_ONLY;
|
||||
UniqueCERTCertList unusedBuiltChain;
|
||||
SECStatus rv = certVerifier->VerifySSLServerCert(nssCert, nullptr,
|
||||
SECStatus rv = certVerifier->VerifySSLServerCert(nssCert,
|
||||
nullptr, // stapledOCSPResponse
|
||||
nullptr, // sctsFromTLSExtension
|
||||
mozilla::pkix::Now(),
|
||||
nullptr, hostnameFlat.get(),
|
||||
unusedBuiltChain, false, flags);
|
||||
nullptr, // pinarg
|
||||
hostnameFlat.get(),
|
||||
unusedBuiltChain,
|
||||
false, // save intermediates
|
||||
flags);
|
||||
if (rv != SECSuccess) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -2512,6 +2517,12 @@ nsSSLIOLayerSetOptions(PRFileDesc* fd, bool forSTARTTLS,
|
|||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
bool sctsEnabled = infoObject->SharedState().IsSignedCertTimestampsEnabled();
|
||||
if (SECSuccess != SSL_OptionSet(fd, SSL_ENABLE_SIGNED_CERT_TIMESTAMPS,
|
||||
sctsEnabled)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
if (SECSuccess != SSL_OptionSet(fd, SSL_HANDSHAKE_AS_CLIENT, true)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
|
|
@ -717,7 +717,9 @@ nsSiteSecurityService::ProcessPKPHeader(nsIURI* aSourceURI,
|
|||
// anyway).
|
||||
CertVerifier::Flags flags = CertVerifier::FLAG_LOCAL_ONLY |
|
||||
CertVerifier::FLAG_TLS_IGNORE_STATUS_REQUEST;
|
||||
if (certVerifier->VerifySSLServerCert(nssCert, nullptr, // stapled ocsp
|
||||
if (certVerifier->VerifySSLServerCert(nssCert,
|
||||
nullptr, // stapledOCSPResponse
|
||||
nullptr, // sctsFromTLSExtension
|
||||
now, nullptr, // pinarg
|
||||
host.get(), // hostname
|
||||
certList,
|
||||
|
|
|
@ -8070,6 +8070,33 @@
|
|||
"n_buckets": 10,
|
||||
"description": "How many permanent certificate overrides a user has stored."
|
||||
},
|
||||
"SSL_SCTS_ORIGIN": {
|
||||
"alert_emails": ["seceng-telemetry@mozilla.com"],
|
||||
"expires_in_version": "never",
|
||||
"kind": "enumerated",
|
||||
"n_values": 10,
|
||||
"bug_numbers": [1293231],
|
||||
"releaseChannelCollection": "opt-out",
|
||||
"description": "Origin of Signed Certificate Timestamps received (1=Embedded, 2=TLS handshake extension, 3=Stapled OCSP response)"
|
||||
},
|
||||
"SSL_SCTS_PER_CONNECTION": {
|
||||
"alert_emails": ["seceng-telemetry@mozilla.com"],
|
||||
"expires_in_version": "never",
|
||||
"kind": "enumerated",
|
||||
"n_values": 10,
|
||||
"bug_numbers": [1293231],
|
||||
"releaseChannelCollection": "opt-out",
|
||||
"description": "Histogram of Signed Certificate Timestamps per SSL connection, from all sources (embedded / OCSP Stapling / TLS handshake). Bucket 0 counts the cases when no SCTs were received, or none were extracted due to parsing errors."
|
||||
},
|
||||
"SSL_SCTS_VERIFICATION_STATUS": {
|
||||
"alert_emails": ["seceng-telemetry@mozilla.com"],
|
||||
"expires_in_version": "never",
|
||||
"kind": "enumerated",
|
||||
"n_values": 10,
|
||||
"bug_numbers": [1293231],
|
||||
"releaseChannelCollection": "opt-out",
|
||||
"description": "Verification status of Signed Certificate Timestamps received (0=Decoding error, 1=SCT verified, 2=SCT from unknown log, 3=Invalid SCT signature, 4=SCT timestamp is in the future)"
|
||||
},
|
||||
"SSL_SERVER_AUTH_EKU": {
|
||||
"alert_emails": ["seceng-telemetry@mozilla.com"],
|
||||
"expires_in_version": "never",
|
||||
|
|
Загрузка…
Ссылка в новой задаче