Bug 1305289 - Certificate Transparency - basic UI indicator; r=Dolske,keeler
MozReview-Commit-ID: b0SUW2WNJT
--- a/browser/base/content/pageinfo/pageInfo.xul
+++ b/browser/base/content/pageinfo/pageInfo.xul
@@ -412,16 +412,17 @@
<!-- Technical Details section -->
<groupbox id="security-technical-groupbox" flex="1">
<caption id="security-technical" label="&securityView.technical.header;" />
<vbox id="security-technical-box" flex="1">
<label id="security-technical-shortform" class="fieldValue"/>
<description id="security-technical-longform1" class="fieldLabel"/>
<description id="security-technical-longform2" class="fieldLabel"/>
+ <description id="security-technical-certificate-transparency" class="fieldLabel"/>
</vbox>
</groupbox>
<hbox pack="end">
<button command="cmd_help" label="&helpButton.label;" dlgtype="help"/>
</hbox>
</vbox>
<!-- Others added by overlay -->
</deck>
--- a/browser/base/content/pageinfo/security.js
+++ b/browser/base/content/pageinfo/security.js
@@ -60,17 +60,18 @@ var security = {
hostName : hostName,
cAName : issuerName,
encryptionAlgorithm : undefined,
encryptionStrength : undefined,
version: undefined,
isBroken : isBroken,
isMixed : isMixed,
isEV : isEV,
- cert : cert
+ cert : cert,
+ certificateTransparency : undefined
};
var version;
try {
retval.encryptionAlgorithm = status.cipherName;
retval.encryptionStrength = status.secretKeyLength;
version = status.protocolVersion;
}
@@ -90,28 +91,50 @@ var security = {
case nsISSLStatus.TLS_VERSION_1_2:
retval.version = "TLS 1.2"
break;
case nsISSLStatus.TLS_VERSION_1_3:
retval.version = "TLS 1.3"
break;
}
+ // Select status text to display for Certificate Transparency.
+ switch (status.certificateTransparencyStatus) {
+ case nsISSLStatus.CERTIFICATE_TRANSPARENCY_NOT_APPLICABLE:
+ // CT compliance checks were not performed,
+ // do not display any status text.
+ retval.certificateTransparency = null;
+ break;
+ case nsISSLStatus.CERTIFICATE_TRANSPARENCY_NONE:
+ retval.certificateTransparency = "None";
+ break;
+ case nsISSLStatus.CERTIFICATE_TRANSPARENCY_OK:
+ retval.certificateTransparency = "OK";
+ break;
+ case nsISSLStatus.CERTIFICATE_TRANSPARENCY_UNKNOWN_LOG:
+ retval.certificateTransparency = "UnknownLog";
+ break;
+ case nsISSLStatus.CERTIFICATE_TRANSPARENCY_INVALID:
+ retval.certificateTransparency = "Invalid";
+ break;
+ }
+
return retval;
}
return {
hostName : hostName,
cAName : "",
encryptionAlgorithm : "",
encryptionStrength : 0,
version: "",
isBroken : isBroken,
isMixed : isMixed,
isEV : isEV,
- cert : null
+ cert : null,
+ certificateTransparency : null
};
},
// Find the secureBrowserUI object (if present)
_getSecurityUI : function() {
if (window.opener.gBrowser)
return window.opener.gBrowser.securityUI;
return null;
@@ -278,16 +301,26 @@ function securityOnLoad(uri, windowInfo)
msg1 = pkiBundle.getFormattedString("pageInfo_Privacy_None1", [info.hostName]);
else
msg1 = pkiBundle.getString("pageInfo_Privacy_None4");
msg2 = pkiBundle.getString("pageInfo_Privacy_None2");
}
setText("security-technical-shortform", hdr);
setText("security-technical-longform1", msg1);
setText("security-technical-longform2", msg2);
+
+ const ctStatus =
+ document.getElementById("security-technical-certificate-transparency");
+ if (info.certificateTransparency) {
+ ctStatus.hidden = false;
+ ctStatus.value = pkiBundle.getString(
+ "pageInfo_CertificateTransparency_" + info.certificateTransparency);
+ } else {
+ ctStatus.hidden = true;
+ }
}
function setText(id, value)
{
var element = document.getElementById(id);
if (!element)
return;
if (element.localName == "textbox" || element.localName == "label")
--- a/security/manager/locales/en-US/chrome/pippki/pippki.properties
+++ b/security/manager/locales/en-US/chrome/pippki/pippki.properties
@@ -104,16 +104,20 @@ pageInfo_Privacy_None4=The page you are
# %3$S is protocol version like "SSL 3" or "TLS 1.2"
pageInfo_EncryptionWithBitsAndProtocol=Connection Encrypted (%1$S, %2$S bit keys, %3$S)
pageInfo_BrokenEncryption=Broken Encryption (%1$S, %2$S bit keys, %3$S)
pageInfo_Privacy_Encrypted1=The page you are viewing was encrypted before being transmitted over the Internet.
pageInfo_Privacy_Encrypted2=Encryption makes it difficult for unauthorized people to view information traveling between computers. It is therefore unlikely that anyone read this page as it traveled across the network.
pageInfo_MixedContent=Connection Partially Encrypted
pageInfo_MixedContent2=Parts of the page you are viewing were not encrypted before being transmitted over the Internet.
pageInfo_WeakCipher=Your connection to this website uses weak encryption and is not private. Other people can view your information or modify the website’s behavior.
+pageInfo_CertificateTransparency_None=This website does not supply Certificate Transparency audit records.
+pageInfo_CertificateTransparency_OK=This website supplies publicly auditable Certificate Transparency records.
+pageInfo_CertificateTransparency_UnknownLog=This website claims to have Certificate Transparency audit records, but the records were issued by an unknown party and cannot be verified.
+pageInfo_CertificateTransparency_Invalid=This website supplies Certificate Transparency audit records, but the records failed verification.
# Cert Viewer
# LOCALIZATION NOTE(certViewerTitle): Title used for the Certificate Viewer.
# %1$S is a string representative of the certificate being viewed.
certViewerTitle=Certificate Viewer: “%1$S”
notPresent=<Not Part Of Certificate>
# Token Manager
--- a/security/manager/ssl/SSLServerCertVerification.cpp
+++ b/security/manager/ssl/SSLServerCertVerification.cpp
@@ -1412,16 +1412,18 @@ AuthCertificate(CertVerifier& certVerifi
} else {
evStatus = nsNSSCertificate::ev_status_valid;
}
status->SetServerCert(nsc, evStatus);
MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
("AuthCertificate setting NEW cert %p\n", nsc.get()));
}
+
+ status->SetCertificateTransparencyInfo(certificateTransparencyInfo);
}
if (rv != Success) {
// Certificate validation failed; store the peer certificate chain on
// infoObject so it can be used for error reporting.
infoObject->SetFailedCertChain(Move(peerCertChain));
PR_SetError(MapResultToPRErrorCode(rv), 0);
}
--- a/security/manager/ssl/nsISSLStatus.idl
+++ b/security/manager/ssl/nsISSLStatus.idl
@@ -18,16 +18,23 @@ interface nsISSLStatus : nsISupports {
const short SSL_VERSION_3 = 0;
const short TLS_VERSION_1 = 1;
const short TLS_VERSION_1_1 = 2;
const short TLS_VERSION_1_2 = 3;
const short TLS_VERSION_1_3 = 4;
readonly attribute unsigned short protocolVersion;
+ const short CERTIFICATE_TRANSPARENCY_NOT_APPLICABLE = 0;
+ const short CERTIFICATE_TRANSPARENCY_NONE = 1;
+ const short CERTIFICATE_TRANSPARENCY_OK = 2;
+ const short CERTIFICATE_TRANSPARENCY_UNKNOWN_LOG = 3;
+ const short CERTIFICATE_TRANSPARENCY_INVALID = 4;
+ readonly attribute unsigned short certificateTransparencyStatus;
+
readonly attribute boolean isDomainMismatch;
readonly attribute boolean isNotValidAtThisTime;
/* Note: To distinguish between
* "unstrusted because missing or untrusted issuer"
* and
* "untrusted because self signed"
* query nsIX509Cert::isSelfSigned
--- a/security/manager/ssl/nsSSLStatus.cpp
+++ b/security/manager/ssl/nsSSLStatus.cpp
@@ -1,19 +1,21 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* 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 "mozilla/Casting.h"
#include "nsSSLStatus.h"
#include "plstr.h"
#include "nsIClassInfoImpl.h"
#include "nsIObjectOutputStream.h"
#include "nsIObjectInputStream.h"
+#include "SignedCertificateTimestamp.h"
#include "ssl.h"
NS_IMETHODIMP
nsSSLStatus::GetServerCert(nsIX509Cert** aServerCert)
{
NS_ENSURE_ARG_POINTER(aServerCert);
nsCOMPtr<nsIX509Cert> cert = mServerCert;
@@ -82,16 +84,26 @@ nsSSLStatus::GetProtocolVersion(uint16_t
return NS_ERROR_NOT_AVAILABLE;
}
*aProtocolVersion = mProtocolVersion;
return NS_OK;
}
NS_IMETHODIMP
+nsSSLStatus::GetCertificateTransparencyStatus(
+ uint16_t* aCertificateTransparencyStatus)
+{
+ NS_ENSURE_ARG_POINTER(aCertificateTransparencyStatus);
+
+ *aCertificateTransparencyStatus = mCertificateTransparencyStatus;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
nsSSLStatus::GetIsDomainMismatch(bool* aIsDomainMismatch)
{
NS_ENSURE_ARG_POINTER(aIsDomainMismatch);
*aIsDomainMismatch = mHaveCertErrorBits && mIsDomainMismatch;
return NS_OK;
}
@@ -145,18 +157,29 @@ nsSSLStatus::Read(nsIObjectInputStream*
mServerCert = do_QueryInterface(cert);
if (!mServerCert) {
return NS_NOINTERFACE;
}
rv = aStream->Read16(&mCipherSuite);
NS_ENSURE_SUCCESS(rv, rv);
- rv = aStream->Read16(&mProtocolVersion);
+
+ // The code below is a workaround to allow serializing new fields
+ // while preserving binary compatibility with older streams. For more details
+ // on the binary compatibility requirement, refer to bug 1248628.
+ // Here, we take advantage of the fact that mProtocolVersion was originally
+ // stored as a 16 bits integer, but the highest 8 bits were never used.
+ // These bits are now used for stream versioning.
+ uint16_t protocolVersionAndStreamFormatVersion;
+ rv = aStream->Read16(&protocolVersionAndStreamFormatVersion);
NS_ENSURE_SUCCESS(rv, rv);
+ mProtocolVersion = protocolVersionAndStreamFormatVersion & 0xFF;
+ const uint8_t streamFormatVersion =
+ (protocolVersionAndStreamFormatVersion >> 8) & 0xFF;
rv = aStream->ReadBoolean(&mIsDomainMismatch);
NS_ENSURE_SUCCESS(rv, rv);
rv = aStream->ReadBoolean(&mIsNotValidAtThisTime);
NS_ENSURE_SUCCESS(rv, rv);
rv = aStream->ReadBoolean(&mIsUntrusted);
NS_ENSURE_SUCCESS(rv, rv);
rv = aStream->ReadBoolean(&mIsEV);
@@ -164,30 +187,43 @@ nsSSLStatus::Read(nsIObjectInputStream*
rv = aStream->ReadBoolean(&mHasIsEVStatus);
NS_ENSURE_SUCCESS(rv, rv);
rv = aStream->ReadBoolean(&mHaveCipherSuiteAndProtocol);
NS_ENSURE_SUCCESS(rv, rv);
rv = aStream->ReadBoolean(&mHaveCertErrorBits);
NS_ENSURE_SUCCESS(rv, rv);
+ // Added in version 1 (see bug 1305289).
+ if (streamFormatVersion >= 1) {
+ rv = aStream->Read16(&mCertificateTransparencyStatus);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
return NS_OK;
}
NS_IMETHODIMP
nsSSLStatus::Write(nsIObjectOutputStream* aStream)
{
+ // The current version of the binary stream format.
+ const uint8_t STREAM_FORMAT_VERSION = 1;
+
nsresult rv = aStream->WriteCompoundObject(mServerCert,
NS_GET_IID(nsIX509Cert),
true);
NS_ENSURE_SUCCESS(rv, rv);
rv = aStream->Write16(mCipherSuite);
NS_ENSURE_SUCCESS(rv, rv);
- rv = aStream->Write16(mProtocolVersion);
+
+ uint16_t protocolVersionAndStreamFormatVersion =
+ mozilla::AssertedCast<uint8_t>(mProtocolVersion) |
+ (STREAM_FORMAT_VERSION << 8);
+ rv = aStream->Write16(protocolVersionAndStreamFormatVersion);
NS_ENSURE_SUCCESS(rv, rv);
rv = aStream->WriteBoolean(mIsDomainMismatch);
NS_ENSURE_SUCCESS(rv, rv);
rv = aStream->WriteBoolean(mIsNotValidAtThisTime);
NS_ENSURE_SUCCESS(rv, rv);
rv = aStream->WriteBoolean(mIsUntrusted);
NS_ENSURE_SUCCESS(rv, rv);
@@ -196,16 +232,20 @@ nsSSLStatus::Write(nsIObjectOutputStream
rv = aStream->WriteBoolean(mHasIsEVStatus);
NS_ENSURE_SUCCESS(rv, rv);
rv = aStream->WriteBoolean(mHaveCipherSuiteAndProtocol);
NS_ENSURE_SUCCESS(rv, rv);
rv = aStream->WriteBoolean(mHaveCertErrorBits);
NS_ENSURE_SUCCESS(rv, rv);
+ // Added in version 1.
+ rv = aStream->Write16(mCertificateTransparencyStatus);
+ NS_ENSURE_SUCCESS(rv, rv);
+
return NS_OK;
}
NS_IMETHODIMP
nsSSLStatus::GetInterfaces(uint32_t* aCount, nsIID*** aArray)
{
*aCount = 0;
*aArray = nullptr;
@@ -257,16 +297,18 @@ nsSSLStatus::GetClassIDNoAlloc(nsCID* aC
{
*aClassIDNoAlloc = kSSLStatusCID;
return NS_OK;
}
nsSSLStatus::nsSSLStatus()
: mCipherSuite(0)
, mProtocolVersion(0)
+, mCertificateTransparencyStatus(nsISSLStatus::
+ CERTIFICATE_TRANSPARENCY_NOT_APPLICABLE)
, mIsDomainMismatch(false)
, mIsNotValidAtThisTime(false)
, mIsUntrusted(false)
, mIsEV(false)
, mHasIsEVStatus(false)
, mHaveCipherSuiteAndProtocol(false)
, mHaveCertErrorBits(false)
{
@@ -295,8 +337,60 @@ nsSSLStatus::SetServerCert(nsNSSCertific
nsresult rv = aServerCert->GetIsExtendedValidation(&mIsEV);
if (NS_FAILED(rv)) {
return;
}
mHasIsEVStatus = true;
}
#endif
}
+
+void
+nsSSLStatus::SetCertificateTransparencyInfo(
+ const mozilla::psm::CertificateTransparencyInfo& info)
+{
+ using mozilla::ct::SignedCertificateTimestamp;
+
+ if (!info.enabled) {
+ // CT disabled.
+ mCertificateTransparencyStatus =
+ nsISSLStatus::CERTIFICATE_TRANSPARENCY_NOT_APPLICABLE;
+ return;
+ }
+
+ if (!info.processedSCTs) {
+ // No SCTs processed on the connection.
+ mCertificateTransparencyStatus =
+ nsISSLStatus::CERTIFICATE_TRANSPARENCY_NONE;
+ return;
+ }
+
+ bool hasOKSCTs = false;
+ bool hasUnknownLogSCTs = false;
+ bool hasInvalidSCTs = false;
+ for (const SignedCertificateTimestamp& sct : info.verifyResult.scts) {
+ switch (sct.verificationStatus) {
+ case SignedCertificateTimestamp::VerificationStatus::OK:
+ hasOKSCTs = true;
+ break;
+ case SignedCertificateTimestamp::VerificationStatus::UnknownLog:
+ hasUnknownLogSCTs = true;
+ break;
+ case SignedCertificateTimestamp::VerificationStatus::InvalidSignature:
+ case SignedCertificateTimestamp::VerificationStatus::InvalidTimestamp:
+ hasInvalidSCTs = true;
+ break;
+ default:
+ MOZ_ASSERT_UNREACHABLE("Unexpected SCT::VerificationStatus type");
+ }
+ }
+
+ if (hasOKSCTs) {
+ mCertificateTransparencyStatus =
+ nsISSLStatus::CERTIFICATE_TRANSPARENCY_OK;
+ } else if (hasUnknownLogSCTs) {
+ mCertificateTransparencyStatus =
+ nsISSLStatus::CERTIFICATE_TRANSPARENCY_UNKNOWN_LOG;
+ } else if (hasInvalidSCTs) {
+ mCertificateTransparencyStatus =
+ nsISSLStatus::CERTIFICATE_TRANSPARENCY_INVALID;
+ }
+}
--- a/security/manager/ssl/nsSSLStatus.h
+++ b/security/manager/ssl/nsSSLStatus.h
@@ -2,16 +2,17 @@
*
* 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 _NSSSLSTATUS_H
#define _NSSSLSTATUS_H
+#include "CertVerifier.h" // For CertificateTransparencyInfo
#include "nsISSLStatus.h"
#include "nsCOMPtr.h"
#include "nsXPIDLString.h"
#include "nsIX509Cert.h"
#include "nsISerializable.h"
#include "nsIClassInfo.h"
#include "nsNSSCertificate.h" // For EVStatus
@@ -32,19 +33,23 @@ public:
void SetServerCert(nsNSSCertificate* aServerCert,
nsNSSCertificate::EVStatus aEVStatus);
bool HasServerCert() {
return mServerCert != nullptr;
}
+ void SetCertificateTransparencyInfo(
+ const mozilla::psm::CertificateTransparencyInfo& info);
+
/* public for initilization in this file */
uint16_t mCipherSuite;
uint16_t mProtocolVersion;
+ uint16_t mCertificateTransparencyStatus;
bool mIsDomainMismatch;
bool mIsNotValidAtThisTime;
bool mIsUntrusted;
bool mIsEV;
bool mHasIsEVStatus;
bool mHaveCipherSuiteAndProtocol;