tor-commits
Threads by month
- ----- 2025 -----
- June
- May
- April
- March
- February
- January
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
October 2014
- 26 participants
- 1551 discussions

[translation/torbutton-torbuttondtd] Update translations for torbutton-torbuttondtd
by translation@torproject.org 30 Oct '14
by translation@torproject.org 30 Oct '14
30 Oct '14
commit 06ddb24b2b8c1aa154815469308e3f442651d7a4
Author: Translation commit bot <translation(a)torproject.org>
Date: Thu Oct 30 07:15:47 2014 +0000
Update translations for torbutton-torbuttondtd
---
sl_SI/torbutton.dtd | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/sl_SI/torbutton.dtd b/sl_SI/torbutton.dtd
index f317a05..8fb6d92 100644
--- a/sl_SI/torbutton.dtd
+++ b/sl_SI/torbutton.dtd
@@ -103,13 +103,13 @@
<!ENTITY torbutton.prefs.block_nontor_file_net "Blokiraj Ne-Tor dostop do omrežja iz datoteke:// urls">
<!ENTITY torbutton.prefs.restore_defaults "Obnovi privzeto">
<!ENTITY torbutton.prefs.test_settings "Testiranje nastavitev">
-<!ENTITY torbutton.prefs.test_auto "Test my Tor settings after the first time I toggle on every Firefox start">
-<!ENTITY torbutton.prefs.disable_livemarks "Disable livemarks updates during Tor usage">
-<!ENTITY torbutton.prefs.tor_memory_jar "Do not write Tor cookies to disk">
-<!ENTITY torbutton.prefs.nontor_memory_jar "Do not write Non-Tor cookies to disk">
-<!ENTITY torbutton.prefs.session_restore "Have the session store save and restore these tabs:">
-<!ENTITY torbutton.prefs.nontor_tabs "Tabs loaded in Non-Tor">
-<!ENTITY torbutton.prefs.tor_tabs "Tabs loaded in Tor">
+<!ENTITY torbutton.prefs.test_auto "Test mojih Tor nastavitev po prvem preklopu z vsakim zagonom Firefox">
+<!ENTITY torbutton.prefs.disable_livemarks "Onemogoči livemarks posodobitve med Tor uporabo">
+<!ENTITY torbutton.prefs.tor_memory_jar "Ne zapisuj Tor piškotke na disk">
+<!ENTITY torbutton.prefs.nontor_memory_jar "Ne zapisuj Ne-Tor piškotke na disk">
+<!ENTITY torbutton.prefs.session_restore "Ima seja shrambe shranjevanje in obnavljanje teh tabulatorjev:">
+<!ENTITY torbutton.prefs.nontor_tabs "Naloženi tabulatorji v Ne-Tor">
+<!ENTITY torbutton.prefs.tor_tabs "Naloženi tabulatorji v Tor">
<!ENTITY torbutton.prefs.socks_vfour "SOCKS v4">
<!ENTITY torbutton.prefs.socks_vfive "SOCKS v5">
<!ENTITY torbutton.prefs.no_proxies_on "Ni proxija za">
@@ -131,7 +131,7 @@
<!ENTITY torbutton.cookiedialog.removeAllBut "Remove All But Protected">
<!ENTITY torbutton.cookiedialog.saveAllCookies "Protect New Cookies">
<!ENTITY torbutton.cookiedialog.doNotSaveAllCookies "Do Not Protect New Cookies">
-<!ENTITY torbutton.prefs.disable_livemarks "Disable livemarks updates during Tor usage">
+<!ENTITY torbutton.prefs.disable_livemarks "Onemogoči livemarks posodobitve med Tor uporabo">
<!ENTITY torbutton.prefs.dtd_recommended "(recommended)">
<!ENTITY torbutton.prefs.dtd_optional "(optional)">
<!ENTITY torbutton.prefs.dtd_crucial "(crucial)">
1
0
commit 7b06135737d39fe1a1e1a5cb67a2461d22ab571a
Author: Mike Perry <mikeperry-git(a)torproject.org>
Date: Wed Oct 29 22:00:02 2014 -0700
Update design doc for TBB 4.0.
---
projects/torbrowser/design/index.html.en | 701 +++++++++++++++++-------------
1 file changed, 407 insertions(+), 294 deletions(-)
diff --git a/projects/torbrowser/design/index.html.en b/projects/torbrowser/design/index.html.en
index de7b344..65b6e98 100644
--- a/projects/torbrowser/design/index.html.en
+++ b/projects/torbrowser/design/index.html.en
@@ -1,10 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-<html xmlns="http://www.w3.org/1999/xhtml"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /><title>The Design and Implementation of the Tor Browser [DRAFT]</title><meta name="generator" content="DocBook XSL Stylesheets V1.76.1" /></head><body><div class="article" title="The Design and Implementation of the Tor Browser [DRAFT]"><div class="titlepage"><div><div><h2 class="title"><a id="design"></a>The Design and Implementation of the Tor Browser [DRAFT]</h2></div><div><div class="author"><h3 class="author"><span class="firstname">Mike</span> <span class="surname">Perry</span></h3><div class="affiliation"><div class="address"><p><code class="email"><<a class="email" href="mailto:mikeperry#torproject org">mikeperry#torproject org</a>></code></p></div></div></div></div><div><div class="author"><h3 class="author"><span class="firstname">Erinn</span> <span class="surname">Clark</span></h3><div class="affiliation"><div class="address"><p><code class="email">
<<a class="email" href="mailto:erinn#torproject org">erinn#torproject org</a>></code></p></div></div></div></div><div><div class="author"><h3 class="author"><span class="firstname">Steven</span> <span class="surname">Murdoch</span></h3><div class="affiliation"><div class="address"><p><code class="email"><<a class="email" href="mailto:sjmurdoch#torproject org">sjmurdoch#torproject org</a>></code></p></div></div></div></div><div><p class="pubdate">March 15, 2013</p></div></div><hr /></div><div class="toc"><p><strong>Table of Contents</strong></p><dl><dt><span class="sect1"><a href="#idp2182160">1. Introduction</a></span></dt><dd><dl><dt><span class="sect2"><a href="#components">1.1. Browser Component Overview</a></span></dt></dl></dd><dt><span class="sect1"><a href="#DesignRequirements">2. Design Requirements and Philosophy</a></span></dt><dd><dl><dt><span class="sect2"><a href="#security">2.1. Security Requirements</a></span></dt><dt><span class="sect2"><a href="#privac
y">2.2. Privacy Requirements</a></span></dt><dt><span class="sect2"><a href="#philosophy">2.3. Philosophy</a></span></dt></dl></dd><dt><span class="sect1"><a href="#adversary">3. Adversary Model</a></span></dt><dd><dl><dt><span class="sect2"><a href="#adversary-goals">3.1. Adversary Goals</a></span></dt><dt><span class="sect2"><a href="#adversary-positioning">3.2. Adversary Capabilities - Positioning</a></span></dt><dt><span class="sect2"><a href="#attacks">3.3. Adversary Capabilities - Attacks</a></span></dt></dl></dd><dt><span class="sect1"><a href="#Implementation">4. Implementation</a></span></dt><dd><dl><dt><span class="sect2"><a href="#proxy-obedience">4.1. Proxy Obedience</a></span></dt><dt><span class="sect2"><a href="#state-separation">4.2. State Separation</a></span></dt><dt><span class="sect2"><a href="#disk-avoidance">4.3. Disk Avoidance</a></span></dt><dt><span class="sect2"><a href="#app-data-isolation">4.4. Application Data Isolation</a></span></dt><dt><span class="se
ct2"><a href="#identifier-linkability">4.5. Cross-Origin Identifier Unlinkability</a></span></dt><dt><span class="sect2"><a href="#fingerprinting-linkability">4.6. Cross-Origin Fingerprinting Unlinkability</a></span></dt><dt><span class="sect2"><a href="#new-identity">4.7. Long-Term Unlinkability via "New Identity" button</a></span></dt><dt><span class="sect2"><a href="#other-security">4.8. Other Security Measures</a></span></dt><dt><span class="sect2"><a href="#firefox-patches">4.9. Description of Firefox Patches</a></span></dt></dl></dd><dt><span class="appendix"><a href="#Transparency">A. Towards Transparency in Navigation Tracking</a></span></dt><dd><dl><dt><span class="sect1"><a href="#deprecate">A.1. Deprecation Wishlist</a></span></dt><dt><span class="sect1"><a href="#idp5896048">A.2. Promising Standards</a></span></dt></dl></dd></dl></div><div class="sect1" title="1. Introduction"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="idp2182160"></a>
1. Introduction</h2></div></div></div><p>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /><title>The Design and Implementation of the Tor Browser [DRAFT]</title><meta name="generator" content="DocBook XSL Stylesheets V1.78.1" /></head><body><div class="article"><div class="titlepage"><div><div><h2 class="title"><a id="design"></a>The Design and Implementation of the Tor Browser [DRAFT]</h2></div><div><div class="author"><h3 class="author"><span class="firstname">Mike</span> <span class="surname">Perry</span></h3><div class="affiliation"><div class="address"><p><code class="email"><<a class="email" href="mailto:mikeperry#torproject org">mikeperry#torproject org</a>></code></p></div></div></div></div><div><div class="author"><h3 class="author"><span class="firstname">Erinn</span> <span class="surname">Clark</span></h3><div class="a
ffiliation"><div class="address"><p><code class="email"><<a class="email" href="mailto:erinn#torproject org">erinn#torproject org</a>></code></p></div></div></div></div><div><div class="author"><h3 class="author"><span class="firstname">Steven</span> <span class="surname">Murdoch</span></h3><div class="affiliation"><div class="address"><p><code class="email"><<a class="email" href="mailto:sjmurdoch#torproject org">sjmurdoch#torproject org</a>></code></p></div></div></div></div><div><p class="pubdate">October 30th, 2014</p></div></div><hr /></div><div class="toc"><p><strong>Table of Contents</strong></p><dl class="toc"><dt><span class="sect1"><a href="#idp35210336">1. Introduction</a></span></dt><dd><dl><dt><span class="sect2"><a href="#components">1.1. Browser Component Overview</a></span></dt></dl></dd><dt><span class="sect1"><a href="#DesignRequirements">2. Design Requirements and Philosophy</a></span></dt><dd><dl><dt><span class="sect2"><a href="#security">2.1. Secu
rity Requirements</a></span></dt><dt><span class="sect2"><a href="#privacy">2.2. Privacy Requirements</a></span></dt><dt><span class="sect2"><a href="#philosophy">2.3. Philosophy</a></span></dt></dl></dd><dt><span class="sect1"><a href="#adversary">3. Adversary Model</a></span></dt><dd><dl><dt><span class="sect2"><a href="#adversary-goals">3.1. Adversary Goals</a></span></dt><dt><span class="sect2"><a href="#adversary-positioning">3.2. Adversary Capabilities - Positioning</a></span></dt><dt><span class="sect2"><a href="#attacks">3.3. Adversary Capabilities - Attacks</a></span></dt></dl></dd><dt><span class="sect1"><a href="#Implementation">4. Implementation</a></span></dt><dd><dl><dt><span class="sect2"><a href="#proxy-obedience">4.1. Proxy Obedience</a></span></dt><dt><span class="sect2"><a href="#state-separation">4.2. State Separation</a></span></dt><dt><span class="sect2"><a href="#disk-avoidance">4.3. Disk Avoidance</a></span></dt><dt><span class="sect2"><a href="#app-data-isol
ation">4.4. Application Data Isolation</a></span></dt><dt><span class="sect2"><a href="#identifier-linkability">4.5. Cross-Origin Identifier Unlinkability</a></span></dt><dt><span class="sect2"><a href="#fingerprinting-linkability">4.6. Cross-Origin Fingerprinting Unlinkability</a></span></dt><dt><span class="sect2"><a href="#new-identity">4.7. Long-Term Unlinkability via "New Identity" button</a></span></dt><dt><span class="sect2"><a href="#other-security">4.8. Other Security Measures</a></span></dt></dl></dd><dt><span class="sect1"><a href="#BuildSecurity">5. Build Security and Package Integrity</a></span></dt><dd><dl><dt><span class="sect2"><a href="#idp37001088">5.1. Achieving Binary Reproducibility</a></span></dt><dt><span class="sect2"><a href="#idp37036336">5.2. Package Signatures and Verification</a></span></dt><dt><span class="sect2"><a href="#idp37040272">5.3. Anonymous Verification</a></span></dt></dl></dd><dt><span class="appendix"><a href="#Transparency">A. Towards Tran
sparency in Navigation Tracking</a></span></dt><dd><dl><dt><span class="sect1"><a href="#deprecate">A.1. Deprecation Wishlist</a></span></dt><dt><span class="sect1"><a href="#idp37071376">A.2. Promising Standards</a></span></dt></dl></dd></dl></div><div class="sect1"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="idp35210336"></a>1. Introduction</h2></div></div></div><p>
This document describes the <a class="link" href="#adversary" title="3. Adversary Model">adversary model</a>,
<a class="link" href="#DesignRequirements" title="2. Design Requirements and Philosophy">design requirements</a>, and <a class="link" href="#Implementation" title="4. Implementation">implementation</a> of the Tor Browser. It is current as of Tor Browser
-2.3.25-5 and Torbutton 1.5.1.
+4.0.
</p><p>
@@ -13,27 +12,39 @@ describe a reference implementation of a Private Browsing Mode that defends
against active network adversaries, in addition to the passive forensic local
adversary currently addressed by the major browsers.
- </p><div class="sect2" title="1.1. Browser Component Overview"><div class="titlepage"><div><div><h3 class="title"><a id="components"></a>1.1. Browser Component Overview</h3></div></div></div><p>
+ </p><div class="sect2"><div class="titlepage"><div><div><h3 class="title"><a id="components"></a>1.1. Browser Component Overview</h3></div></div></div><p>
The Tor Browser is based on <a class="ulink" href="https://www.mozilla.org/en-US/firefox/organizations/" target="_top">Mozilla's Extended
-Support Release (ESR) Firefox branch</a>. We have a <a class="link" href="#firefox-patches" title="4.9. Description of Firefox Patches">series of patches</a> against this browser to
-enhance privacy and security. Browser behavior is additionally augmented
-through the <a class="ulink" href="https://gitweb.torproject.org/torbutton.git/tree/master" target="_top">Torbutton
-extension</a>, though we are in the process of moving this
-functionality into direct Firefox patches. We also <a class="ulink" href="https://gitweb.torproject.org/torbrowser.git/blob/HEAD:/build-scripts/confi…" target="_top">change
+Support Release (ESR) Firefox branch</a>. We have a <a class="ulink" href="https://gitweb.torproject.org/tor-browser.git" target="_top">series of patches</a>
+against this browser to enhance privacy and security. Browser behavior is
+additionally augmented through the <a class="ulink" href="https://gitweb.torproject.org/torbutton.git/tree/master" target="_top">Torbutton
+extension</a>, though we are in the process of moving this functionality
+into direct Firefox patches. We also <a class="ulink" href="https://gitweb.torproject.org/tor-browser.git/blob/refs/heads/tor-browser-3…" target="_top">change
a number of Firefox preferences</a> from their defaults.
</p><p>
+Tor process management and configuration is accomplished through the <a class="ulink" href="https://gitweb.torproject.org/tor-launcher.git" target="_top">Tor Launcher</a>
+addon, which provides the initial Tor configuration splash screen and
+bootstrap progress bar. Tor Launcher is also compatible with Thunderbird,
+InstantBird, and XULRunner.
+
+ </p><p>
To help protect against potential Tor Exit Node eavesdroppers, we include
<a class="ulink" href="https://www.eff.org/https-everywhere" target="_top">HTTPS-Everywhere</a>. To
provide users with optional defense-in-depth against Javascript and other
-potential exploit vectors, we also include <a class="ulink" href="http://noscript.net/" target="_top">NoScript</a>. To protect against
-PDF-based Tor proxy bypass and to improve usability, we include the <a class="ulink" href="https://addons.mozilla.org/en-us/firefox/addon/pdfjs/" target="_top">PDF.JS</a>
-extension. We also modify <a class="ulink" href="https://gitweb.torproject.org/torbrowser.git/blob/HEAD:/build-scripts/confi…" target="_top">several
+potential exploit vectors, we also include <a class="ulink" href="http://noscript.net/" target="_top">NoScript</a>. We also modify <a class="ulink" href="https://gitweb.torproject.org/builders/tor-browser-bundle.git/blob/refs/hea…" target="_top">several
extension preferences</a> from their defaults.
- </p></div></div><div class="sect1" title="2. Design Requirements and Philosophy"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="DesignRequirements"></a>2. Design Requirements and Philosophy</h2></div></div></div><p>
+ </p><p>
+
+To provide censorship circumvention in areas where the public Tor network is
+blocked either by IP, or by protocol fingerprint, we include several <a class="ulink" href="https://trac.torproject.org/projects/tor/wiki/doc/AChildsGardenOfPluggableT…" target="_top">Pluggable
+Transports</a> in the distribution. As of this writing, we include <a class="ulink" href="https://gitweb.torproject.org/pluggable-transports/obfsproxy.git/blob/HEAD:…" target="_top">Obfsproxy</a>,
+<a class="ulink" href="https://trac.torproject.org/projects/tor/wiki/doc/meek" target="_top">meek</a>,
+<a class="ulink" href="https://fteproxy.org/" target="_top">FTE</a>, and <a class="ulink" href="https://crypto.stanford.edu/flashproxy/" target="_top">FlashProxy</a>.
+
+ </p></div></div><div class="sect1"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="DesignRequirements"></a>2. Design Requirements and Philosophy</h2></div></div></div><p>
The Tor Browser Design Requirements are meant to describe the properties of a
Private Browsing Mode that defends against both network and local forensic
@@ -59,7 +70,7 @@ browser distribution.
"OPTIONAL" in this document are to be interpreted as described in
<a class="ulink" href="https://www.ietf.org/rfc/rfc2119.txt" target="_top">RFC 2119</a>.
- </p><div class="sect2" title="2.1. Security Requirements"><div class="titlepage"><div><div><h3 class="title"><a id="security"></a>2.1. Security Requirements</h3></div></div></div><p>
+ </p><div class="sect2"><div class="titlepage"><div><div><h3 class="title"><a id="security"></a>2.1. Security Requirements</h3></div></div></div><p>
The security requirements are primarily concerned with ensuring the safe use
of Tor. Violations in these properties typically result in serious risk for
@@ -100,7 +111,7 @@ to permissions issues with access to swap, implementations MAY choose to leave
it out of scope, and/or leave it to the Operating System/platform to implement
ephemeral-keyed encrypted swap.
-</p></li></ol></div></div><div class="sect2" title="2.2. Privacy Requirements"><div class="titlepage"><div><div><h3 class="title"><a id="privacy"></a>2.2. Privacy Requirements</h3></div></div></div><p>
+</p></li></ol></div></div><div class="sect2"><div class="titlepage"><div><div><h3 class="title"><a id="privacy"></a>2.2. Privacy Requirements</h3></div></div></div><p>
The privacy requirements are primarily concerned with reducing linkability:
the ability for a user's activity on one site to be linked with their activity
@@ -144,7 +155,7 @@ its authentication tokens and browser state and obtain a fresh identity.
Additionally, the browser SHOULD clear linkable state by default automatically
upon browser restart, except at user option.
- </p></li></ol></div></div><div class="sect2" title="2.3. Philosophy"><div class="titlepage"><div><div><h3 class="title"><a id="philosophy"></a>2.3. Philosophy</h3></div></div></div><p>
+ </p></li></ol></div></div><div class="sect2"><div class="titlepage"><div><div><h3 class="title"><a id="philosophy"></a>2.3. Philosophy</h3></div></div></div><p>
In addition to the above design requirements, the technology decisions about
Tor Browser are also guided by some philosophical positions about technology.
@@ -207,7 +218,7 @@ url bar origin</a> to eliminate the possibility of linkability
between domains. For example, when a plugin object (or a Javascript access of
window.plugins) is present in a page, the user should be given the choice of
allowing that plugin object for that url bar origin only. The same
-goes for exemptions to third party cookie policy, geo-location, and any other
+goes for exemptions to third party cookie policy, geolocation, and any other
privacy permissions.
</p><p>
If the user has indicated they wish to record local history storage, these
@@ -243,18 +254,18 @@ We believe that if we do not stay current with the support of new web
technologies, we cannot hope to substantially influence or be involved in
their proper deployment or privacy realization. However, we will likely disable
high-risk features pending analysis, audit, and mitigation.
- </p></li></ol></div></div></div><div class="sect1" title="3. Adversary Model"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="adversary"></a>3. Adversary Model</h2></div></div></div><p>
+ </p></li></ol></div></div></div><div class="sect1"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="adversary"></a>3. Adversary Model</h2></div></div></div><p>
A Tor web browser adversary has a number of goals, capabilities, and attack
types that can be used to illustrate the design requirements for the
Tor Browser. Let's start with the goals.
- </p><div class="sect2" title="3.1. Adversary Goals"><div class="titlepage"><div><div><h3 class="title"><a id="adversary-goals"></a>3.1. Adversary Goals</h3></div></div></div><div class="orderedlist"><ol class="orderedlist" type="1"><li class="listitem"><span class="command"><strong>Bypassing proxy settings</strong></span><p>The adversary's primary goal is direct compromise and bypass of
+ </p><div class="sect2"><div class="titlepage"><div><div><h3 class="title"><a id="adversary-goals"></a>3.1. Adversary Goals</h3></div></div></div><div class="orderedlist"><ol class="orderedlist" type="1"><li class="listitem"><span class="command"><strong>Bypassing proxy settings</strong></span><p>The adversary's primary goal is direct compromise and bypass of
Tor, causing the user to directly connect to an IP of the adversary's
choosing.</p></li><li class="listitem"><span class="command"><strong>Correlation of Tor vs Non-Tor Activity</strong></span><p>If direct proxy bypass is not possible, the adversary will likely
happily settle for the ability to correlate something a user did via Tor with
their non-Tor activity. This can be done with cookies, cache identifiers,
-javascript events, and even CSS. Sometimes the fact that a user uses Tor may
+Javascript events, and even CSS. Sometimes the fact that a user uses Tor may
be enough for some authorities.</p></li><li class="listitem"><span class="command"><strong>History disclosure</strong></span><p>
The adversary may also be interested in history disclosure: the ability to
query a user's history to see if they have issued certain censored search
@@ -282,7 +293,7 @@ In some cases, the adversary may opt for a heavy-handed approach, such as
seizing the computers of all Tor users in an area (especially after narrowing
the field by the above two pieces of information). History records and cache
data are the primary goals here.
- </p></li></ol></div></div><div class="sect2" title="3.2. Adversary Capabilities - Positioning"><div class="titlepage"><div><div><h3 class="title"><a id="adversary-positioning"></a>3.2. Adversary Capabilities - Positioning</h3></div></div></div><p>
+ </p></li></ol></div></div><div class="sect2"><div class="titlepage"><div><div><h3 class="title"><a id="adversary-positioning"></a>3.2. Adversary Capabilities - Positioning</h3></div></div></div><p>
The adversary can position themselves at a number of different locations in
order to execute their attacks.
</p><div class="orderedlist"><ol class="orderedlist" type="1"><li class="listitem"><span class="command"><strong>Exit Node or Upstream Router</strong></span><p>
@@ -311,7 +322,7 @@ Users in Internet cafes, for example, face such a threat. In addition, in
countries where simply using tools like Tor is illegal, users may face
confiscation of their computer equipment for excessive Tor usage or just
general suspicion.
- </p></li></ol></div></div><div class="sect2" title="3.3. Adversary Capabilities - Attacks"><div class="titlepage"><div><div><h3 class="title"><a id="attacks"></a>3.3. Adversary Capabilities - Attacks</h3></div></div></div><p>
+ </p></li></ol></div></div><div class="sect2"><div class="titlepage"><div><div><h3 class="title"><a id="attacks"></a>3.3. Adversary Capabilities - Attacks</h3></div></div></div><p>
The adversary can perform the following attacks from a number of different
positions to accomplish various aspects of their goals. It should be noted
@@ -340,7 +351,7 @@ with cookies as well.
</p><p>
-These types of attacks are attempts at subverting our <a class="link" href="#identifier-linkability" title="4.5. Cross-Origin Identifier Unlinkability">Cross-Origin Identifier Unlinkability</a> and <a class="link" href="#new-identity" title="4.7. Long-Term Unlinkability via "New Identity" button">Long-Term Unlikability</a> design requirements.
+These types of attacks are attempts at subverting our <a class="link" href="#identifier-linkability" title="4.5. Cross-Origin Identifier Unlinkability">Cross-Origin Identifier Unlinkability</a> and <a class="link" href="#new-identity" title="4.7. Long-Term Unlinkability via "New Identity" button">Long-Term Unlinkability</a> design requirements.
</p></li><li class="listitem"><a id="fingerprinting"></a><span class="command"><strong>Fingerprint users based on browser
attributes</strong></span><p>
@@ -350,7 +361,7 @@ of the browser. This information can be used to reduce anonymity set, or even
uniquely fingerprint individual users. Attacks of this nature are typically
aimed at tracking users across sites without their consent, in an attempt to
subvert our <a class="link" href="#fingerprinting-linkability" title="4.6. Cross-Origin Fingerprinting Unlinkability">Cross-Origin
-Fingerprinting Unlinkability</a> and <a class="link" href="#new-identity" title="4.7. Long-Term Unlinkability via "New Identity" button">Long-Term Unlikability</a> design requirements.
+Fingerprinting Unlinkability</a> and <a class="link" href="#new-identity" title="4.7. Long-Term Unlinkability via "New Identity" button">Long-Term Unlinkability</a> design requirements.
</p><p>
@@ -465,7 +476,7 @@ complexity (and thus hinder a real world adversary who attempts this attack)
are large numbers of dynamically generated pages, partially cached content,
and also the non-web activity of entire Tor network. This yields an effective
number of "web pages" many orders of magnitude larger than even <a class="ulink" href="http://lorre.uni.lu/~andriy/papers/acmccs-wpes11-fingerprinting.pdf" target="_top">Panchenko's
-"Open World" scenario</a>, which suffered continous near-constant decline
+"Open World" scenario</a>, which suffered continuous near-constant decline
in the true positive rate as the "Open World" size grew (see figure 4). This
large level of classification complexity is further confounded by a noisy and
low resolution featureset - one which is also relatively easy for the defender
@@ -510,12 +521,12 @@ has taken place. This adversary motivates our
An adversary with arbitrary code execution typically has more power, though.
It can be quite hard to really significantly limit the capabilities of such an
-adversary. <a class="ulink" href="https://tails.boum.org/contribute/design/" target="_top">The Tails system</a> can
+adversary. <a class="ulink" href="http://tails.boum.org/contribute/design/" target="_top">The Tails system</a> can
provide some defense against this adversary through the use of readonly media
and frequent reboots, but even this can be circumvented on machines without
Secure Boot through the use of BIOS rootkits.
- </p></li></ol></div></div></div><div class="sect1" title="4. Implementation"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="Implementation"></a>4. Implementation</h2></div></div></div><p>
+ </p></li></ol></div></div></div><div class="sect1"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="Implementation"></a>4. Implementation</h2></div></div></div><p>
The Implementation section is divided into subsections, each of which
corresponds to a <a class="link" href="#DesignRequirements" title="2. Design Requirements and Philosophy">Design Requirement</a>.
@@ -531,38 +542,45 @@ between the <span class="command"><strong>Design Goal</strong></span> and the <s
Status</strong></span> for each property. Corresponding bugs in the <a class="ulink" href="https://trac.torproject.org/projects/tor/report" target="_top">Tor bug tracker</a>
are typically linked for these cases.
- </p><div class="sect2" title="4.1. Proxy Obedience"><div class="titlepage"><div><div><h3 class="title"><a id="proxy-obedience"></a>4.1. Proxy Obedience</h3></div></div></div><p>
+ </p><div class="sect2"><div class="titlepage"><div><div><h3 class="title"><a id="proxy-obedience"></a>4.1. Proxy Obedience</h3></div></div></div><p>
Proxy obedience is assured through the following:
</p><div class="orderedlist"><ol class="orderedlist" type="1"><li class="listitem">Firefox proxy settings, patches, and build flags
<p>
-Our <a class="ulink" href="https://gitweb.torproject.org/torbrowser.git/blob/HEAD:/build-scripts/confi…" target="_top">Firefox
-preferences file</a> sets the Firefox proxy settings to use Tor directly as a
-SOCKS proxy. It sets <span class="command"><strong>network.proxy.socks_remote_dns</strong></span>,
+
+Our <a class="ulink" href="https://gitweb.torproject.org/tor-browser.git/blob/refs/heads/tor-browser-3…" target="_top">Firefox
+preferences file</a> sets the Firefox proxy settings to use Tor directly
+as a SOCKS proxy. It sets <span class="command"><strong>network.proxy.socks_remote_dns</strong></span>,
<span class="command"><strong>network.proxy.socks_version</strong></span>,
<span class="command"><strong>network.proxy.socks_port</strong></span>, and
<span class="command"><strong>network.dns.disablePrefetch</strong></span>.
+
</p><p>
-We also patch Firefox in order to <a class="ulink" href="https://gitweb.torproject.org/torbrowser.git/blob/maint-2.4:/src/current-pa…" target="_top">prevent
-a DNS leak due to a WebSocket rate-limiting check</a>. As stated in the
-patch, we believe the direct DNS resolution performed by this check is in
-violation of the W3C standard, but <a class="ulink" href="https://bugzilla.mozilla.org/show_bug.cgi?id=751465" target="_top">this DNS proxy leak
-remains present in stock Firefox releases</a>.
+To prevent proxy bypass by WebRTC calls, we disable WebRTC at compile time
+with the <span class="command"><strong>--disable-webrtc</strong></span> configure switch, as well
+as set the pref <span class="command"><strong>media.peerconnection.enabled</strong></span> to false.
</p><p>
-During the transition to Firefox 17-ESR, a code audit was undertaken to verify
-that there were no system calls or XPCOM activity in the source tree that did
-not use the browser proxy settings. The only violation we found was that
-WebRTC was capable of creating UDP sockets and was compiled in by default. We
-subsequently disabled it using the Firefox build option
-<span class="command"><strong>--disable-webrtc</strong></span>.
+We also patch Firefox in order to provide several defense-in-depth mechanisms
+for proxy safety. Notably, we <a class="ulink" href="https://gitweb.torproject.org/tor-browser.git/commitdiff/8527bec0ad59fb3d88…" target="_top">patch
+the DNS service</a> to prevent any browser or addon DNS resolution, and we
+also <a class="ulink" href="https://gitweb.torproject.org/tor-browser.git/commitdiff/04c046e11f6622f44c…" target="_top">patch
+OCSP and PKIX code</a> to prevent any use of the non-proxied command-line
+tool utility functions from being functional while linked in to the browser.
+In both cases, we could find no direct paths to these routines in the browser,
+but it seemed better safe than sorry.
</p><p>
+During every Extended Support Release transition, we perform <a class="ulink" href="https://gitweb.torproject.org/tor-browser-spec.git/tree/HEAD:/audits" target="_top">in-depth
+code audits</a> to verify that there were no system calls or XPCOM
+activity in the source tree that did not use the browser proxy settings.
+ </p><p>
+
We have verified that these settings and patches properly proxy HTTPS, OCSP,
-HTTP, FTP, gopher (now defunct), DNS, SafeBrowsing Queries, all javascript
+HTTP, FTP, gopher (now defunct), DNS, SafeBrowsing Queries, all JavaScript
activity, including HTML5 audio and video objects, addon updates, wifi
geolocation queries, searchbox queries, XPCOM addon HTTPS/HTTP activity,
WebSockets, and live bookmark updates. We have also verified that IPv6
@@ -590,10 +608,11 @@ If the user does enable plugins in this way, plugin-handled objects are still
restricted from automatic load through Firefox's click-to-play preference
<span class="command"><strong>plugins.click_to_play</strong></span>.
</p><p>
+
In addition, to reduce any unproxied activity by arbitrary plugins at load
time, and to reduce the fingerprintability of the installed plugin list, we
-also patch the Firefox source code to <a class="ulink" href="https://gitweb.torproject.org/torbrowser.git/blob/maint-2.4:/src/current-pa…" target="_top">prevent the load of any plugins except
-for Flash and Gnash</a>.
+also patch the Firefox source code to <a class="ulink" href="https://gitweb.torproject.org/tor-browser.git/commitdiff/2ecf6c33618ecee554…" target="_top">
+prevent the load of any plugins except for Flash and Gnash</a>.
</p></li><li class="listitem">External App Blocking and Drag Event Filtering
<p>
@@ -611,9 +630,8 @@ Additionally, modern desktops now pre-emptively fetch any URLs in Drag and
Drop events as soon as the drag is initiated. This download happens
independent of the browser's Tor settings, and can be triggered by something
as simple as holding the mouse button down for slightly too long while
-clicking on an image link. We had to patch Firefox to <a class="ulink" href="https://gitweb.torproject.org/torbrowser.git/blob/maint-2.4:/src/current-pa…" target="_top">emit
-an observer event during dragging</a> to allow us to filter the drag
-events from Torbutton before the OS downloads the URLs the events contained.
+clicking on an image link. We filter drag and drop events events <a class="ulink" href="https://gitweb.torproject.org/torbutton.git/blob_plain/HEAD:/src/components…" target="_top">from
+Torbutton</a> before the OS downloads the URLs the events contained.
</p></li><li class="listitem">Disabling system extensions and clearing the addon whitelist
<p>
@@ -626,41 +644,39 @@ system-level addons from the browser through the use of
<span class="command"><strong>extensions.enabledScopes</strong></span> and
<span class="command"><strong>extensions.autoDisableScopes</strong></span>.
- </p></li></ol></div></div><div class="sect2" title="4.2. State Separation"><div class="titlepage"><div><div><h3 class="title"><a id="state-separation"></a>4.2. State Separation</h3></div></div></div><p>
+ </p></li></ol></div></div><div class="sect2"><div class="titlepage"><div><div><h3 class="title"><a id="state-separation"></a>4.2. State Separation</h3></div></div></div><p>
Tor Browser State is separated from existing browser state through use of a
custom Firefox profile, and by setting the $HOME environment variable to the
root of the bundle's directory. The browser also does not load any
system-wide extensions (through the use of
<span class="command"><strong>extensions.enabledScopes</strong></span> and
-<span class="command"><strong>extensions.autoDisableScopes</strong></span>. Furthermore, plugins are
+<span class="command"><strong>extensions.autoDisableScopes</strong></span>). Furthermore, plugins are
disabled, which prevents Flash cookies from leaking from a pre-existing Flash
directory.
- </p></div><div class="sect2" title="4.3. Disk Avoidance"><div class="titlepage"><div><div><h3 class="title"><a id="disk-avoidance"></a>4.3. Disk Avoidance</h3></div></div></div><div class="sect3" title="Design Goal:"><div class="titlepage"><div><div><h4 class="title"><a id="idp5639136"></a>Design Goal:</h4></div></div></div><div class="blockquote"><blockquote class="blockquote">
+ </p></div><div class="sect2"><div class="titlepage"><div><div><h3 class="title"><a id="disk-avoidance"></a>4.3. Disk Avoidance</h3></div></div></div><div class="sect3"><div class="titlepage"><div><div><h4 class="title"><a id="idp36779392"></a>Design Goal:</h4></div></div></div><div class="blockquote"><blockquote class="blockquote">
The User Agent MUST (at user option) prevent all disk records of browser activity.
The user should be able to optionally enable URL history and other history
features if they so desire.
- </blockquote></div></div><div class="sect3" title="Implementation Status:"><div class="titlepage"><div><div><h4 class="title"><a id="idp5640496"></a>Implementation Status:</h4></div></div></div><div class="blockquote"><blockquote class="blockquote">
+ </blockquote></div></div><div class="sect3"><div class="titlepage"><div><div><h4 class="title"><a id="idp36780752"></a>Implementation Status:</h4></div></div></div><div class="blockquote"><blockquote class="blockquote">
We achieve this goal through several mechanisms. First, we set the Firefox
Private Browsing preference
<span class="command"><strong>browser.privatebrowsing.autostart</strong></span>. In addition, four Firefox patches are needed to prevent disk writes, even if
Private Browsing Mode is enabled. We need to
-<a class="ulink" href="https://gitweb.torproject.org/torbrowser.git/blob/maint-2.4:/src/current-pa…" target="_top">prevent
-the permissions manager from recording HTTPS STS state</a>,
-<a class="ulink" href="https://gitweb.torproject.org/torbrowser.git/blob/maint-2.4:/src/current-pa…" target="_top">prevent
-intermediate SSL certificates from being recorded</a>,
-<a class="ulink" href="https://gitweb.torproject.org/torbrowser.git/blob/maint-2.4:/src/current-pa…" target="_top">prevent
-download history from being recorded</a>, and
-<a class="ulink" href="https://gitweb.torproject.org/torbrowser.git/blob/maint-2.4:/src/current-pa…" target="_top">prevent
-the content preferences service from recording site zoom</a>.
-
-For more details on these patches, <a class="link" href="#firefox-patches" title="4.9. Description of Firefox Patches">see the
-Firefox Patches section</a>.
+<a class="ulink" href="https://gitweb.torproject.org/tor-browser.git/commitdiff/4ebc3cda4b704c0149…" target="_top">prevent
+the permissions manager from recording HTTPS STS state</a>, <a class="ulink" href="https://gitweb.torproject.org/tor-browser.git/commitdiff/8904bfc10cd537bd35…" target="_top">prevent
+intermediate SSL certificates from being recorded</a>, <a class="ulink" href="https://gitweb.torproject.org/tor-browser.git/commitdiff/86f6bc9dc28b6f8d7e…" target="_top">prevent
+the clipboard cache from being written to disk for large pastes</a>, and
+<a class="ulink" href="https://gitweb.torproject.org/tor-browser.git/commitdiff/d5da6f8b7de089335e…" target="_top">prevent
+the content preferences service from recording site zoom</a>. We also had
+to disable the media cache with the pref <span class="command"><strong>media.cache_size</strong></span>,
+to prevent HTML5 videos from being written to the OS temporary directory,
+which happened regardless of the private browsing mode setting.
</blockquote></div><div class="blockquote"><blockquote class="blockquote">
@@ -681,11 +697,7 @@ auditing work to ensure that yet.
</blockquote></div><div class="blockquote"><blockquote class="blockquote">
-Torbutton also <a class="ulink" href="https://gitweb.torproject.org/torbutton.git/blob/HEAD:/src/components/tbSes…" target="_top">contains
-code</a> to prevent the Firefox session store from writing to disk.
- </blockquote></div><div class="blockquote"><blockquote class="blockquote">
-
-For more details on disk leak bugs and enhancements, see the <a class="ulink" href="https://trac.torproject.org/projects/tor/query?keywords=~tbb-disk-leak&…" target="_top">tbb-disk-leak tag in our bugtracker</a></blockquote></div></div></div><div class="sect2" title="4.4. Application Data Isolation"><div class="titlepage"><div><div><h3 class="title"><a id="app-data-isolation"></a>4.4. Application Data Isolation</h3></div></div></div><p>
+For more details on disk leak bugs and enhancements, see the <a class="ulink" href="https://trac.torproject.org/projects/tor/query?keywords=~tbb-disk-leak&…" target="_top">tbb-disk-leak tag in our bugtracker</a></blockquote></div></div></div><div class="sect2"><div class="titlepage"><div><div><h3 class="title"><a id="app-data-isolation"></a>4.4. Application Data Isolation</h3></div></div></div><p>
Tor Browser Bundle MUST NOT cause any information to be written outside of the
bundle directory. This is to ensure that the user is able to completely and
@@ -699,7 +711,7 @@ To ensure TBB directory isolation, we set
<span class="command"><strong>browser.shell.checkDefaultBrowser</strong></span>, and
<span class="command"><strong>browser.download.manager.addToRecentDocs</strong></span>. We also set the
$HOME environment variable to be the TBB extraction directory.
- </p></div><div class="sect2" title="4.5. Cross-Origin Identifier Unlinkability"><div class="titlepage"><div><div><h3 class="title"><a id="identifier-linkability"></a>4.5. Cross-Origin Identifier Unlinkability</h3></div></div></div><p>
+ </p></div><div class="sect2"><div class="titlepage"><div><div><h3 class="title"><a id="identifier-linkability"></a>4.5. Cross-Origin Identifier Unlinkability</h3></div></div></div><p>
The Tor Browser MUST prevent a user's activity on one site from being linked
to their activity on another site. When this goal cannot yet be met with an
@@ -723,7 +735,7 @@ the url bar origin for which browser state exists, possibly with a
context-menu option to drill down into specific types of state or permissions.
An example of this simplification can be seen in Figure 1.
- </p><div class="figure"><a id="idp5664576"></a><p class="title"><strong>Figure 1. Improving the Privacy UI</strong></p><div class="figure-contents"><div class="mediaobject" align="center"><img src="NewCookieManager.png" align="middle" alt="Improving the Privacy UI" /></div><div class="caption"><p></p>
+ </p><div class="figure"><a id="idp36803456"></a><p class="title"><strong>Figure 1. Improving the Privacy UI</strong></p><div class="figure-contents"><div class="mediaobject" align="center"><img src="NewCookieManager.png" align="middle" alt="Improving the Privacy UI" /></div><div class="caption"><p></p>
This example UI is a mock-up of how isolating identifiers to the URL bar
origin can simplify the privacy UI for all data - not just cookies. Once
@@ -937,7 +949,7 @@ functionality.
</p></li></ol></div><p>
For more details on identifier linkability bugs and enhancements, see the <a class="ulink" href="https://trac.torproject.org/projects/tor/query?keywords=~tbb-linkability&am…" target="_top">tbb-linkability tag in our bugtracker</a>
- </p></div><div class="sect2" title="4.6. Cross-Origin Fingerprinting Unlinkability"><div class="titlepage"><div><div><h3 class="title"><a id="fingerprinting-linkability"></a>4.6. Cross-Origin Fingerprinting Unlinkability</h3></div></div></div><p>
+ </p></div><div class="sect2"><div class="titlepage"><div><div><h3 class="title"><a id="fingerprinting-linkability"></a>4.6. Cross-Origin Fingerprinting Unlinkability</h3></div></div></div><p>
In order to properly address the fingerprinting adversary on a technical
level, we need a metric to measure linkability of the various browser
@@ -950,11 +962,14 @@ determine how many bits of identifying information each attribute provided.
</p><p>
-Many browser features have been added since the EFF first ran their experiment
-and collected their data. To avoid an infinite sinkhole, we reduce the efforts
-for fingerprinting resistance by only concerning ourselves with reducing the
-fingerprintable differences <span class="emphasis"><em>among</em></span> Tor Browser users. We
-do not believe it is possible to solve cross-browser fingerprinting issues.
+Because fingerprinting is problem that potentially touches every aspect of the
+browser, we reduce the efforts for fingerprinting resistance by only
+concerning ourselves with reducing the fingerprintable differences
+<span class="emphasis"><em>among</em></span> Tor Browser users. We do not believe it is possible
+to solve cross-browser fingerprinting issues. Similarly, we prioritize issues
+that differentiate only MacOS, Windows, and Linux lower than those that
+differentiate aspects of the hardware, third party installed software, and
+configuration differences in those operating systems.
</p><p>
@@ -970,7 +985,15 @@ contrast to historical data. We have been <a class="ulink" href="https://trac.to
the EFF</a> that it is worthwhile to release the source code to
Panopticlick to allow us to run our own version for this reason.
- </p><div class="sect3" title="Fingerprinting defenses in the Tor Browser"><div class="titlepage"><div><div><h4 class="title"><a id="fingerprinting-defenses"></a>Fingerprinting defenses in the Tor Browser</h4></div></div></div><div class="orderedlist"><ol class="orderedlist" type="1"><li class="listitem">Plugins
+ </p><div class="sect3"><div class="titlepage"><div><div><h4 class="title"><a id="fingerprinting-defenses"></a>Fingerprinting defenses in the Tor Browser</h4></div></div></div><p>
+
+The following defenses are listed roughly in order of most severe
+fingerprinting threat first, though we are desperately in need of updated
+measurements to determine this with certainty. Where our actual implementation
+differs from an ideal solution, we separately describe our <span class="command"><strong>Design
+Goal</strong></span> and our <span class="command"><strong>Implementation Status</strong></span>.
+
+ </p><div class="orderedlist"><ol class="orderedlist" type="1"><li class="listitem">Plugins
<p>
Plugins add to fingerprinting risk via two main vectors: their mere presence in
@@ -984,7 +1007,9 @@ be allowed to load objects until the user has clicked through a click-to-play
barrier. Additionally, version information should be reduced or obfuscated
until the plugin object is loaded. For flash, we wish to <a class="ulink" href="https://trac.torproject.org/projects/tor/ticket/3974" target="_top">provide a
settings.sol file</a> to disable Flash cookies, and to restrict P2P
-features that are likely to bypass proxy settings.
+features that are likely to bypass proxy settings. We'd also like to restrict
+access to fonts and other system information (such as IP address and MAC
+address) in such a sandbox.
</p><p><span class="command"><strong>Implementation Status:</strong></span>
@@ -1015,11 +1040,49 @@ image can be used almost identically to a tracking cookie by the web server.
</p><p>
-To reduce the threat from this vector, we have patched Firefox to <a class="ulink" href="https://gitweb.torproject.org/torbrowser.git/blob/maint-2.4:/src/current-pa…" target="_top">prompt
-before returning valid image data</a> to the Canvas APIs. If the user
-hasn't previously allowed the site in the URL bar to access Canvas image data,
-pure white image data is returned to the Javascript APIs.
+In some sense, the canvas can be seen as the union of many other
+fingerprinting vectors. If WebGL were normalized through software rendering,
+and the browser shipped a fixed collection of fonts, it might not be necessary
+to create a canvas permission. However, until then, to reduce the threat from
+this vector, we have patched Firefox to <a class="ulink" href="https://gitweb.torproject.org/tor-browser.git/commitdiff/3b53f525cfb68880e6…" target="_top">prompt
+before returning valid image data</a> to the Canvas APIs, and for <a class="ulink" href="https://gitweb.torproject.org/tor-browser.git/commitdiff/fb9f463fe3a69499d6…" target="_top">access
+to isPointInPath and related functions</a>. If the user hasn't previously
+allowed the site in the URL bar to access Canvas image data, pure white image
+data is returned to the Javascript APIs.
+
+ </p><p>
+ </p></li><li class="listitem">Open TCP Port Fingerprinting
+ <p>
+
+In Firefox, by using either WebSockets or XHR, it is possible for remote
+content to <a class="ulink" href="http://www.andlabs.org/tools/jsrecon.html" target="_top">enumerate
+the list of TCP ports open on 127.0.0.1</a>. In other browsers, this can
+be accomplished by DOM events on image or script tags. This open vs filtered
+vs closed port list can provide a very unique fingerprint of a machine.
+
+ </p><p>In Tor Browser, we prevent access to
+127.0.0.1/localhost by ensuring that even these requests are still sent by
+Firefox to our SOCKS proxy (ie we set
+<span class="command"><strong>network.proxy.no_proxies_on</strong></span> to the empty string). The local
+Tor client then rejects them, since it is configured to proxy for internal IP
+addresses by default.
+ </p></li><li class="listitem">USB Device ID enumeration
+ <p>
+
+The <a class="ulink" href="https://developer.mozilla.org/en-US/docs/Web/Guide/API/Gamepad" target="_top">GamePad
+API</a> provides web pages with the <a class="ulink" href="https://dvcs.w3.org/hg/gamepad/raw-file/default/gamepad.html#widl-Gamepad-id" target="_top">USB
+device id, product id, and driver name</a> of all connected game
+controllers, as well as detailed information about their capabilities. This API
+should be behind a site permission in Private Browsing Modes, or should present a generic
+controller type (perhaps a two button controller that can be mapped to the keyboard) in all cases.
+We simply disable it via the pref <span class="command"><strong>dom.gamepad.enabled</strong></span>.
+ </p></li><li class="listitem">Invasive Authentication Mechanisms (NTLM and SPNEGO)
+ <p>
+Both NTLM and SPNEGO authentication mechanisms can leak the hostname, and in
+some cases the machine username. These authentication mechanisms should either
+be disabled, or placed behind a site permission before their use. We simply
+disable them.
</p></li><li class="listitem">WebGL
<p>
@@ -1040,6 +1103,12 @@ provided by the following WebGL API calls: <span class="command"><strong>getPara
<span class="command"><strong>getSupportedExtensions()</strong></span>, and
<span class="command"><strong>getExtension()</strong></span>.
+ </p><p>
+
+Another option for WebGL might be to use software-only rendering, using a
+library such as <a class="ulink" href="http://www.mesa3d.org/" target="_top">Mesa</a>. The use of
+such a library would avoid hardware-specific rendering differences.
+
</p></li><li class="listitem">Fonts
<p>
@@ -1050,28 +1119,33 @@ Javascript to query for the existence of specific fonts. With a large enough
pre-built list to query, a large amount of fingerprintable information may
still be available.
- </p><p>
-
-The sure-fire way to address font linkability is to ship the browser with a
-font for every language, typeface, and style in use in the world, and to only
-use those fonts at the exclusion of system fonts. However, this set may be
-impractically large. It is possible that a smaller <a class="ulink" href="https://secure.wikimedia.org/wikipedia/en/wiki/Unicode_typeface#List_of_Uni…" target="_top">common
-subset</a> may be found that provides total coverage. However, we believe
-that with strong url bar origin identifier isolation, a simpler approach can reduce the
-number of bits available to the adversary while avoiding the rendering and
-language issues of supporting a global font set.
-
+ </p><p><span class="command"><strong>Design Goal:</strong></span> The sure-fire way to address font
+linkability is to ship the browser with a font for every language, typeface,
+and style, and to only use those fonts at the exclusion of system fonts. We are
+<a class="ulink" href="https://trac.torproject.org/projects/tor/ticket/13313" target="_top">currently
+investigating</a> this approach, and our current favorite font sets for
+this purpose are the <a class="ulink" href="http://www.droidfonts.com/droidfonts/" target="_top">Droid
+fonts</a>, the <a class="ulink" href="http://hangeul.naver.com/" target="_top">Nanum fonts</a>,
+and <a class="ulink" href="https://fedorahosted.org/lohit/" target="_top">Lohit fonts</a>. The Droid
+font set is fairly complete by itself, but Nanum and Lohit have smaller
+versions of many South Asian languages. When combined in a way that chooses the
+smallest font implementations for each locale, these three font sets provide
+which provide coverage for the all languages used on Wikipedia with more than
+10,000 articles, and several others as well, in approximately 3MB of compressed
+overhead. The <a class="ulink" href="https://www.google.com/get/noto/" target="_top">Noto font
+set</a> is another font set that aims for complete coverage, but is
+considerably larger than the combination of the Droid, Nanum, and Lohit fonts.
</p><p><span class="command"><strong>Implementation Status:</strong></span>
-We disable plugins, which prevents font enumeration. Additionally, we limit
-both the number of font queries from CSS, as well as the total number of
-fonts that can be used in a document <a class="ulink" href="https://gitweb.torproject.org/torbrowser.git/blob/maint-2.4:/src/current-pa…" target="_top">with
+In the meantime while we investigate shipping our own fonts, we disable
+plugins, which prevents font enumeration. Additionally, we limit both the
+number of font queries from CSS, as well as the total number of fonts that can
+be used in a document <a class="ulink" href="https://gitweb.torproject.org/torbrowser.git/blob/maint-2.4:/src/current-pa…" target="_top">with
a Firefox patch</a>. We create two prefs,
<span class="command"><strong>browser.display.max_font_attempts</strong></span> and
<span class="command"><strong>browser.display.max_font_count</strong></span> for this purpose. Once these
limits are reached, the browser behaves as if
-<span class="command"><strong>browser.display.use_document_fonts</strong></span> was set. We are
-still working to determine optimal values for these prefs.
+<span class="command"><strong>browser.display.use_document_fonts</strong></span> was set.
</p><p>
@@ -1079,43 +1153,69 @@ To improve rendering, we exempt remote <a class="ulink" href="https://developer.
fonts</a> from these counts, and if a font-family CSS rule lists a remote
font (in any order), we use that font instead of any of the named local fonts.
- </p></li><li class="listitem">Desktop resolution, CSS Media Queries, and System Colors
+ </p></li><li class="listitem">Monitor and Desktop resolution
<p>
Both CSS and Javascript have access to a lot of information about the screen
resolution, usable desktop size, OS widget size, toolbar size, title bar size,
-system theme colors, and other desktop features that are not at all relevant
+screen orientation, and other desktop features that are not at all relevant
to rendering and serve only to provide information for fingerprinting.
</p><p><span class="command"><strong>Design Goal:</strong></span>
Our design goal here is to reduce the resolution information down to the bare
-minimum required for properly rendering inside a content window. We intend to
+minimum required for properly rendering inside a content window. We intend to
report all rendering information correctly with respect to the size and
properties of the content window, but report an effective size of 0 for all
-border material, and also report that the desktop is only as big as the
-inner content window. Additionally, new browser windows are sized such that
-their content windows are one of a few fixed sizes based on the user's
-desktop resolution.
+border material, and also report that the desktop is only as big as the inner
+content window. Additionally, new browser windows are sized such that their
+content windows are one of a few fixed sizes based on the user's desktop
+resolution. In addition, to further reduce resolution-based fingerprinting, we
+are <a class="ulink" href="https://trac.torproject.org/projects/tor/ticket/7256" target="_top">investigating
+zoom/viewport-based mechanisms</a> that might allow us to always report the
+same desktop resolution regardless of the actual size of the content window,
+and simply scale to make up the difference. Until then, the user should also
+be informed that maximizing their windows can lead to fingerprintability under
+this scheme.
</p><p><span class="command"><strong>Implementation Status:</strong></span>
-We have implemented the above strategy using a window observer to <a class="ulink" href="https://gitweb.torproject.org/torbutton.git/blob/HEAD:/src/chrome/content/t…" target="_top">resize
+
+We have implemented the above strategy using a window observer to <a class="ulink" href="https://gitweb.torproject.org/torbutton.git/blob/HEAD:/src/chrome/content/t…" target="_top">resize
new windows based on desktop resolution</a>. Additionally, we patch
-Firefox to use the client content window size <a class="ulink" href="https://gitweb.torproject.org/torbrowser.git/blob/maint-2.4:/src/current-pa…" target="_top">for
-window.screen</a> and <a class="ulink" href="https://gitweb.torproject.org/torbrowser.git/blob/maint-2.4:/src/current-pa…" target="_top">for
-CSS Media Queries</a>. Similarly, we <a class="ulink" href="https://gitweb.torproject.org/torbrowser.git/blob/maint-2.4:/src/current-pa…" target="_top">patch
-DOM events to return content window relative points</a>. We also patch
-Firefox to <a class="ulink" href="https://gitweb.torproject.org/torbrowser.git/blob/maint-2.4:/src/current-pa…" target="_top">report
-a fixed set of system colors to content window CSS</a>.
+Firefox to use the client content window size <a class="ulink" href="https://gitweb.torproject.org/tor-browser.git/commitdiff/8fc2421becd0ab0cfb…" target="_top">for
+window.screen</a>. Similarly, we <a class="ulink" href="https://gitweb.torproject.org/tor-browser.git/commitdiff/81e7fc3a10d27b1d8f…" target="_top">patch
+DOM events to return content window relative points</a>. We also force
+popups to open in new tabs (via
+<span class="command"><strong>browser.link.open_newwindow.restriction</strong></span>), to avoid
+full-screen popups inferring information about the browser resolution. In
+addition, we prevent auto-maximizing on browser start, and are investigating a
+user-friendly way of informing users that maximized windows are detrimental
+to privacy in this mode.
+
+ </p></li><li class="listitem">CSS Media Queries
+ <p>
- </p><p>
+Even without Javascript, CSS has access to a lot of information about the screen
+resolution, usable desktop size, OS widget size, toolbar size, title bar size,
+system theme colors, and other desktop features that are not at all relevant
+to rendering and serve only to provide information for fingerprinting. Most of this information comes from
+<a class="ulink" href="https://developer.mozilla.org/en-US/docs/Web/Guide/CSS/Media_queries" target="_top">CSS Media Queries</a>, but
+Mozilla has exposed <a class="ulink" href="https://developer.mozilla.org/en-US/docs/Web/CSS/color_value#System_Colors" target="_top">several user and OS theme defined color values</a> to CSS as well.
+
+ </p><p><span class="command"><strong>Design Goal:</strong></span>
+In Private Browsing Mode, CSS should not be able infer anything that the user
+has configured about their computer. Additionally, it should not be able to
+infer machine-specific details such as screen orientation or type.
+
+ </p><p><span class="command"><strong>Implementation Status:</strong></span>
-To further reduce resolution-based fingerprinting, we are <a class="ulink" href="https://trac.torproject.org/projects/tor/ticket/7256" target="_top">investigating
-zoom/viewport-based mechanisms</a> that might allow us to always report
-the same desktop resolution regardless of the actual size of the content
-window, and simply scale to make up the difference. However, the complexity
-and rendering impact of such a change is not yet known.
+We patch
+Firefox to <a class="ulink" href="https://gitweb.torproject.org/tor-browser.git/commitdiff/30dc2c4290698af81c…" target="_top">report
+a fixed set of system colors to content window CSS</a>, and <a class="ulink" href="https://gitweb.torproject.org/tor-browser.git/commitdiff/8f6e979d30598569de…" target="_top">prevent
+detection of font smoothing on OSX</a>. We also always
+<a class="ulink" href="https://gitweb.torproject.org/tor-browser.git/commitdiff/09561f0e5452305b9e…" target="_top">report
+landscape-primary</a> for the screen orientation.
</p></li><li class="listitem">User Agent and HTTP Headers
<p><span class="command"><strong>Design Goal:</strong></span>
@@ -1132,8 +1232,29 @@ which we leverage. We also set similar prefs for controlling the
Accept-Language and Accept-Charset headers, which we spoof to English by default. Additionally, we
<a class="ulink" href="https://gitweb.torproject.org/torbrowser.git/blob/maint-2.4:/src/current-pa…" target="_top">remove
content script access</a> to Components.interfaces, which <a class="ulink" href="http://pseudo-flaw.net/tor/torbutton/fingerprint-firefox.html" target="_top">can be
-used</a> to fingerprint OS, platform, and Firefox minor version. </p></li><li class="listitem">Timezone and clock offset
- <p><span class="command"><strong>Design Goal:</strong></span>
+used</a> to fingerprint OS, platform, and Firefox minor version. </p></li><li class="listitem">Locale Fingerprinting
+ <p>
+
+In Tor Browser, we provide non-English users the option of concealing their OS
+and browser locale from websites. It is debatable if this should be as high of
+a priority as information specific to the user's computer, but for
+completeness, we attempt to maintain this property.
+
+ </p><p><span class="command"><strong>Implementation Status:</strong></span>
+
+We set the fallback character set to set to windows-1252 for all locales, via
+<span class="command"><strong>intl.charset.default</strong></span>. We also patch Firefox to allow us to
+<a class="ulink" href="https://gitweb.torproject.org/tor-browser.git/commitdiff/fe42a78575df7f460f…" target="_top">instruct
+the JS engine</a> to use en-US as its internal C locale for all Date, Math,
+and exception handling.
+
+ </p></li><li class="listitem">Timezone and clock offset
+ <p>
+
+While the latency in Tor connections varies anywhere from milliseconds to
+several seconds, it is still possible for the remote site to detect large
+differences between the user's clock and an official reference timesource.
+ </p><p><span class="command"><strong>Design Goal:</strong></span>
All Tor Browser users MUST report the same timezone to websites. Currently, we
choose UTC for this purpose, although an equally valid argument could be made
@@ -1141,7 +1262,9 @@ for EDT/EST due to the large English-speaking population density (coupled with
the fact that we spoof a US English user agent). Additionally, the Tor
software should detect if the users clock is significantly divergent from the
clocks of the relays that it connects to, and use this to reset the clock
-values used in Tor Browser to something reasonably accurate.
+values used in Tor Browser to something reasonably accurate. Alternatively,
+the browser can obtain this clock skew via a mechanism similar to that used in
+<a class="ulink" href="" target="_top">tlsdate</a>.
</p><p><span class="command"><strong>Implementation Status:</strong></span>
@@ -1204,17 +1327,17 @@ fingerprinting: timestamp quantization and jitter.
We have no implementation as of yet.
</p></li></ol></div></div><p>
For more details on identifier linkability bugs and enhancements, see the <a class="ulink" href="https://trac.torproject.org/projects/tor/query?keywords=~tbb-fingerprinting…" target="_top">tbb-fingerprinting tag in our bugtracker</a>
- </p></div><div class="sect2" title="4.7. Long-Term Unlinkability via "New Identity" button"><div class="titlepage"><div><div><h3 class="title"><a id="new-identity"></a>4.7. Long-Term Unlinkability via "New Identity" button</h3></div></div></div><p>
+ </p></div><div class="sect2"><div class="titlepage"><div><div><h3 class="title"><a id="new-identity"></a>4.7. Long-Term Unlinkability via "New Identity" button</h3></div></div></div><p>
In order to avoid long-term linkability, we provide a "New Identity" context
menu option in Torbutton. This context menu option is active if Torbutton can
read the environment variables $TOR_CONTROL_PASSWD and $TOR_CONTROL_PORT.
- </p><div class="sect3" title="Design Goal:"><div class="titlepage"><div><div><h4 class="title"><a id="idp5782640"></a>Design Goal:</h4></div></div></div><div class="blockquote"><blockquote class="blockquote">
+ </p><div class="sect3"><div class="titlepage"><div><div><h4 class="title"><a id="idp36963888"></a>Design Goal:</h4></div></div></div><div class="blockquote"><blockquote class="blockquote">
All linkable identifiers and browser state MUST be cleared by this feature.
- </blockquote></div></div><div class="sect3" title="Implementation Status:"><div class="titlepage"><div><div><h4 class="title"><a id="idp5783888"></a>Implementation Status:</h4></div></div></div><div class="blockquote"><blockquote class="blockquote"><p>
+ </blockquote></div></div><div class="sect3"><div class="titlepage"><div><div><h4 class="title"><a id="idp36965136"></a>Implementation Status:</h4></div></div></div><div class="blockquote"><blockquote class="blockquote"><p>
First, Torbutton disables Javascript in all open tabs and windows by using
both the <a class="ulink" href="https://developer.mozilla.org/en-US/docs/XPCOM_Interface_Reference/nsIDocSh…" target="_top">browser.docShell.allowJavascript</a>
@@ -1232,8 +1355,9 @@ After closing all tabs, we then emit "<a class="ulink" href="https://developer.m
state), and then manually clear the following state: searchbox and findbox
text, HTTP auth, SSL state, OCSP state, site-specific content preferences
(including HSTS state), content and image cache, offline cache, Cookies, DOM
-storage, DOM local storage, the safe browsing key, and the Google wifi geolocation
-token (if it exists).
+storage, crypto tokens, DOM local storage, the safe browsing key, and the
+Google wifi geolocation token (if it exists). We also clear NoScript's site
+and temporary permissions.
</p><p>
@@ -1246,7 +1370,7 @@ closed (this does not spawn a new Firefox process, only a new window).
</p></blockquote></div><div class="blockquote"><blockquote class="blockquote">
If the user chose to "protect" any cookies by using the Torbutton Cookie
Protections UI, those cookies are not cleared as part of the above.
- </blockquote></div></div></div><div class="sect2" title="4.8. Other Security Measures"><div class="titlepage"><div><div><h3 class="title"><a id="other-security"></a>4.8. Other Security Measures</h3></div></div></div><p>
+ </blockquote></div></div></div><div class="sect2"><div class="titlepage"><div><div><h3 class="title"><a id="other-security"></a>4.8. Other Security Measures</h3></div></div></div><p>
In addition to the above mechanisms that are devoted to preserving privacy
while browsing, we also have a number of technical mechanisms to address other
@@ -1258,7 +1382,7 @@ privacy and security issues.
Fingerprinting</a> is a statistical attack to attempt to recognize specific
encrypted website activity.
- </p><div class="sect3" title="Design Goal:"><div class="titlepage"><div><div><h4 class="title"><a id="idp5797920"></a>Design Goal:</h4></div></div></div><div class="blockquote"><blockquote class="blockquote"><p>
+ </p><div class="sect3"><div class="titlepage"><div><div><h4 class="title"><a id="idp36979248"></a>Design Goal:</h4></div></div></div><div class="blockquote"><blockquote class="blockquote"><p>
We want to deploy a mechanism that reduces the accuracy of <a class="ulink" href="https://en.wikipedia.org/wiki/Feature_selection" target="_top">useful features</a> available
for classification. This mechanism would either impact the true and false
@@ -1280,7 +1404,7 @@ Congestion-Sensitive BUFLO</a>. It may be also possible to <a class="ulink" href
defenses</a> such that they only use existing spare Guard bandwidth capacity in the Tor
network, making them also effectively no-overhead.
- </p></blockquote></div></div><div class="sect3" title="Implementation Status:"><div class="titlepage"><div><div><h4 class="title"><a id="idp5804816"></a>Implementation Status:</h4></div></div></div><div class="blockquote"><blockquote class="blockquote"><p>
+ </p></blockquote></div></div><div class="sect3"><div class="titlepage"><div><div><h4 class="title"><a id="idp36986144"></a>Implementation Status:</h4></div></div></div><div class="blockquote"><blockquote class="blockquote"><p>
Currently, we patch Firefox to <a class="ulink" href="https://gitweb.torproject.org/torbrowser.git/blob/maint-2.4:/src/current-pa…" target="_top">randomize
pipeline order and depth</a>. Unfortunately, pipelining is very fragile.
Many sites do not support it, and even sites that advertise support for
@@ -1330,199 +1454,188 @@ homepage to point to a <a class="ulink" href="https://check.torproject.org/?lang
informs the user</a> that their browser is out of
date.
- </p></li></ol></div></div><div class="sect2" title="4.9. Description of Firefox Patches"><div class="titlepage"><div><div><h3 class="title"><a id="firefox-patches"></a>4.9. Description of Firefox Patches</h3></div></div></div><p>
-
-The set of patches we have against Firefox can be found in the <a class="ulink" href="https://gitweb.torproject.org/torbrowser.git/tree/maint-2.4:/src/current-pa…" target="_top">current-patches directory of the torbrowser git repository</a>. They are:
-
- </p><div class="orderedlist"><ol class="orderedlist" type="1"><li class="listitem"><a class="ulink" href="https://gitweb.torproject.org/torbrowser.git/blob/maint-2.4:/src/current-pa…" target="_top">Block
-Components.interfaces</a><p>
-
-In order to reduce fingerprinting, we block access to this interface from
-content script. Components.interfaces can be used for fingerprinting the
-platform, OS, and Firebox version, but not much else.
-
- </p></li><li class="listitem"><a class="ulink" href="https://gitweb.torproject.org/torbrowser.git/blob/maint-2.4:/src/current-pa…" target="_top">Make
-Permissions Manager memory only</a><p>
-
-This patch exposes a pref 'permissions.memory_only' that properly isolates the
-permissions manager to memory, which is responsible for all user specified
-site permissions, as well as stored <a class="ulink" href="https://secure.wikimedia.org/wikipedia/en/wiki/HTTP_Strict_Transport_Securi…" target="_top">HSTS</a>
-policy from visited sites.
-
-The pref does successfully clear the permissions manager memory if toggled. It
-does not need to be set in prefs.js, and can be handled by Torbutton.
-
- </p></li><li class="listitem"><a class="ulink" href="https://gitweb.torproject.org/torbrowser.git/blob/maint-2.4:/src/current-pa…" target="_top">Make
-Intermediate Cert Store memory-only</a><p>
-
-The intermediate certificate store records the intermediate SSL certificates
-the browser has seen to date. Because these intermediate certificates are used
-by a limited number of domains (and in some cases, only a single domain),
-the intermediate certificate store can serve as a low-resolution record of
-browsing history.
-
- </p><p><span class="command"><strong>Design Goal:</strong></span>
-
-As an additional design goal, we would like to later alter this patch to allow this
-information to be cleared from memory. The implementation does not currently
-allow this.
-
- </p></li><li class="listitem"><a class="ulink" href="https://gitweb.torproject.org/torbrowser.git/blob/maint-2.4:/src/current-pa…" target="_top">Add
-a string-based cacheKey property for domain isolation</a><p>
-
-To <a class="ulink" href="https://trac.torproject.org/projects/tor/ticket/3666" target="_top">increase the
-security of cache isolation</a> and to <a class="ulink" href="https://trac.torproject.org/projects/tor/ticket/3754" target="_top">solve strange and
-unknown conflicts with OCSP</a>, we had to patch
-Firefox to provide a cacheDomain cache attribute. We use the url bar
-FQDN as input to this field.
-
- </p></li><li class="listitem"><a class="ulink" href="https://gitweb.torproject.org/torbrowser.git/blob/maint-2.4:/src/current-pa…" target="_top">Block
-all plugins except flash</a><p>
-We cannot use the <a class="ulink" href="http://www.oxymoronical.com/experiments/xpcomref/applications/Firefox/3.5/c…" target="_top">
-(a)mozilla.org/extensions/blocklist;1</a> service, because we
-actually want to stop plugins from ever entering the browser's process space
-and/or executing code (for example, AV plugins that collect statistics/analyze
-URLs, magical toolbars that phone home or "help" the user, Skype buttons that
-ruin our day, and censorship filters). Hence we rolled our own.
- </p></li><li class="listitem"><a class="ulink" href="https://gitweb.torproject.org/torbrowser.git/blob/maint-2.4:/src/current-pa…" target="_top">Make content-prefs service memory only</a><p>
-This patch prevents random URLs from being inserted into content-prefs.sqlite in
-the profile directory as content prefs change (includes site-zoom and perhaps
-other site prefs?).
- </p></li><li class="listitem"><a class="ulink" href="https://gitweb.torproject.org/torbrowser.git/blob/maint-2.4:/src/current-pa…" target="_top">Make Tor Browser exit when not launched from Vidalia</a><p>
-
-It turns out that on Windows 7 and later systems, the Taskbar attempts to
-automatically learn the most frequent apps used by the user, and it recognizes
-Tor Browser as a separate app from Vidalia. This can cause users to try to
-launch Tor Browser without Vidalia or a Tor instance running. Worse, the Tor
-Browser will automatically find their default Firefox profile, and properly
-connect directly without using Tor. This patch is a simple hack to cause Tor
-Browser to immediately exit in this case.
+ </p></li></ol></div></div></div><div class="sect1"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="BuildSecurity"></a>5. Build Security and Package Integrity</h2></div></div></div><p>
- </p></li><li class="listitem"><a class="ulink" href="https://gitweb.torproject.org/torbrowser.git/blob/maint-2.4:/src/current-pa…" target="_top">Disable SSL Session ID tracking</a><p>
+In the age of state-sponsored malware, <a class="ulink" href="https://blog.torproject.org/blog/deterministic-builds-part-one-cyberwar-and…" target="_top">we
+believe</a> it is impossible to expect to keep a single build machine or
+software signing key secure, given the class of adversaries that Tor has to
+contend with. For this reason, we have deployed a build system
+that allows anyone to use our source code to reproduce byte-for-byte identical
+binary packages to the ones that we distribute.
-This patch is a simple 1-line hack to prevent SSL connections from caching
-(and then later transmitting) their Session IDs. There was no preference to
-govern this behavior, so we had to hack it by altering the SSL new connection
-defaults.
+ </p><div class="sect2"><div class="titlepage"><div><div><h3 class="title"><a id="idp37001088"></a>5.1. Achieving Binary Reproducibility</h3></div></div></div><p>
- </p></li><li class="listitem"><a class="ulink" href="https://gitweb.torproject.org/torbrowser.git/blob/maint-2.4:/src/current-pa…" target="_top">Provide an observer event to close persistent connections</a><p>
+The GNU toolchain has been working on providing reproducible builds for some
+time, however a large software project such as Firefox typically ends up
+embedding a large number of details about the machine it was built on, both
+intentionally and inadvertently. Additionally, manual changes to the build
+machine configuration can accumulate over time and are difficult for others to
+replicate externally, which leads to difficulties with binary reproducibility.
-This patch creates an observer event in the HTTP connection manager to close
-all keep-alive connections that still happen to be open. This event is emitted
-by the <a class="link" href="#new-identity" title="4.7. Long-Term Unlinkability via "New Identity" button">New Identity</a> button.
-
- </p></li><li class="listitem"><a class="ulink" href="https://gitweb.torproject.org/torbrowser.git/blob/maint-2.4:/src/current-pa…" target="_top">Limit Device and System Specific Media Queries</a><p>
-
-<a class="ulink" href="https://developer.mozilla.org/en-US/docs/CSS/Media_queries" target="_top">CSS
-Media Queries</a> have a fingerprinting capability approaching that of
-Javascript. This patch causes such Media Queries to evaluate as if the device
-resolution was equal to the content window resolution.
-
- </p></li><li class="listitem"><a class="ulink" href="https://gitweb.torproject.org/torbrowser.git/blob/maint-2.4:/src/current-pa…" target="_top">Limit the number of fonts per document</a><p>
-
-Font availability can be <a class="ulink" href="http://flippingtypical.com/" target="_top">queried by
-CSS and Javascript</a> and is a fingerprinting vector. This patch limits
-the number of times CSS and Javascript can cause font-family rules to
-evaluate. Remote @font-face fonts are exempt from the limits imposed by this
-patch, and remote fonts are given priority over local fonts whenever both
-appear in the same font-family rule. We do this by explicitly altering the
-nsRuleNode rule represenation itself to remove the local font families before
-the rule hits the font renderer.
-
- </p></li><li class="listitem"><a class="ulink" href="https://gitweb.torproject.org/torbrowser.git/blob/maint-2.4:/src/current-pa…" target="_top">Rebrand Firefox to Tor Browser</a><p>
-
-This patch updates our branding in compliance with Mozilla's trademark policy.
-
- </p></li><li class="listitem"><a class="ulink" href="https://gitweb.torproject.org/torbrowser.git/blob/maint-2.4:/src/current-pa…" target="_top">Make Download Manager Memory Only</a><p>
-
-This patch prevents disk leaks from the download manager. The original
-behavior is to write the download history to disk and then delete it, even if
-you disable download history from your Firefox preferences.
+ </p><p>
+For this reason, we decided to leverage the work done by the <a class="ulink" href="http://gitian.org/" target="_top">Gitian Project</a> from the Bitcoin community.
+Gitian is a wrapper around Ubuntu's virtualization tools that allows you to
+specify an Ubuntu version, architecture, a set of additional packages, a set
+of input files, and a bash build scriptlet in an YAML document called a
+"Gitian Descriptor". This document is used to install a qemu-kvm image, and
+execute your build scriptlet inside it.
+ </p><p>
- </p></li><li class="listitem"><a class="ulink" href="https://gitweb.torproject.org/torbrowser.git/blob/maint-2.4:/src/current-pa…" target="_top">Add DDG and StartPage to Omnibox</a><p>
+We have created a <a class="ulink" href="https://gitweb.torproject.org/builders/tor-browser-bundle.git/tree/refs/hea…" target="_top">set
+of wrapper scripts</a> around Gitian to automate dependency download and
+authentication, as well as transfer intermediate build outputs between the
+stages of the build process. Because Gitian creates an Ubuntu build
+environment, we must use cross-compilation to create packages for Windows and
+Mac OS. For Windows, we use mingw-w64 as our cross compiler. For Mac OS, we
+use toolchain4 in combination with a binary redistribution of the Mac OS 10.6
+SDK.
-This patch adds DuckDuckGo and StartPage to the Search Box, and sets our
-default search engine to StartPage. We deployed this patch due to excessive
-Captchas and complete 403 bans from Google.
+ </p><p>
- </p></li><li class="listitem"><a class="ulink" href="https://gitweb.torproject.org/torbrowser.git/blob/maint-2.4:/src/current-pa…" target="_top">Make nsICacheService.EvictEntries() Synchronous</a><p>
+The use of the Gitian system eliminates build non-determinism by normalizing
+the build environment's hostname, username, build path, uname output,
+toolchain versions, and time. On top of what Gitian provides, we also had to
+address the following additional sources of non-determinism:
-This patch eliminates a race condition with "New Identity". Without it,
-cache-based Evercookies survive for up to a minute after clearing the cache
-on some platforms.
+ </p><div class="orderedlist"><ol class="orderedlist" type="1"><li class="listitem">Filesystem and archive reordering
+ <p>
- </p></li><li class="listitem"><a class="ulink" href="https://gitweb.torproject.org/torbrowser.git/blob/maint-2.4:/src/current-pa…" target="_top">Prevent WebSockets DNS Leak</a><p>
+The most prevalent source of non-determinism in the components of Tor Browser
+by far was various ways that archives (such as zip, tar, jar/ja, DMG, and
+Firefox manifest lists) could be reordered. Many file archivers walk the
+filesystem in inode structure order by default, which will result in ordering
+differences between two different archive invocations, especially on machines
+of different disk and hardware configurations.
-This patch prevents a DNS leak when using WebSockets. It also prevents other
-similar types of DNS leaks.
+ </p><p>
- </p></li><li class="listitem"><a class="ulink" href="https://gitweb.torproject.org/torbrowser.git/blob/maint-2.4:/src/current-pa…" target="_top">Randomize HTTP pipeline order and depth</a><p>
-As an
-<a class="ulink" href="https://blog.torproject.org/blog/experimental-defense-website-traffic-finge…" target="_top">experimental
-defense against Website Traffic Fingerprinting</a>, we patch the standard
-HTTP pipelining code to randomize the number of requests in a
-pipeline, as well as their order.
- </p></li><li class="listitem"><a class="ulink" href="https://gitweb.torproject.org/torbrowser.git/blob/maint-2.4:/src/current-pa…" target="_top">Emit
-an observer event to filter the Drag and Drop URL list</a><p>
+The fix for this is to perform an additional sorting step on the input list
+for archives, but care must be taken to instruct libc and other sorting routines
+to use a fixed locale to determine lexicographic ordering, or machines with
+different locale settings will produce different sort results. We chose the
+'C' locale for this purpose. We created wrapper scripts for <a class="ulink" href="https://gitweb.torproject.org/builders/tor-browser-bundle.git/blob/HEAD:/gi…" target="_top">tar</a>,
+<a class="ulink" href="https://gitweb.torproject.org/builders/tor-browser-bundle.git/blob/HEAD:/gi…" target="_top">zip</a>,
+and <a class="ulink" href="https://gitweb.torproject.org/builders/tor-browser-bundle.git/blob/HEAD:/gi…" target="_top">DMG</a>
+to aid in reproducible archive creation.
+
+ </p></li><li class="listitem">Uninitialized memory in toolchain/archivers
+ <p>
+
+We ran into difficulties with both binutils and the DMG archive script using
+uninitialized memory in certain data structures that ended up written to disk.
+Our binutils fixes were merged upstream, but the DMG archive fix remains an
+<a class="ulink" href="https://gitweb.torproject.org/builders/tor-browser-bundle.git/blob/HEAD:/gi…" target="_top">independent
+patch</a>.
+
+ </p></li><li class="listitem">Fine-grained timestamps and timezone leaks
+ <p>
+
+The standard way of controlling timestamps in Gitian is to use libfaketime,
+which hooks time-related library calls to provide a fixed timestamp. However,
+libfaketime does not spoof the millisecond and microsecond components of
+timestamps, which found their way into pyc files and also in explicit Firefox
+build process timestamp embedding.
+ </p><p>
-This patch allows us to block external Drag and Drop events from Torbutton.
-We need to block Drag and Drop because Mac OS and Ubuntu both immediately load
-any URLs they find in your drag buffer before you even drop them (without
-using your browser's proxy settings, of course). This can lead to proxy bypass
-during user activity that is as basic as holding down the mouse button for
-slightly too long while clicking on an image link.
+We addressed the Firefox issues with direct patches to their build process,
+which have since been merged. However, pyc timestamps had to be address with
+an additional <a class="ulink" href="https://gitweb.torproject.org/builders/tor-browser-bundle.git/blob/HEAD:/gi…" target="_top">helper
+script</a>.
+ </p><p>
- </p></li><li class="listitem"><a class="ulink" href="https://gitweb.torproject.org/torbrowser.git/blob/maint-2.4:/src/current-pa…" target="_top">Add mozIThirdPartyUtil.getFirstPartyURI() API</a><p>
+The timezone leaks were addressed by setting the <span class="command"><strong>TZ</strong></span>
+environment variable to UTC in our descriptors.
-This patch provides an API that allows us to more easily isolate identifiers
-to the URL bar domain.
+ </p></li><li class="listitem">Deliberately generated entropy
+ <p>
- </p></li><li class="listitem"><a class="ulink" href="https://gitweb.torproject.org/torbrowser.git/blob/maint-2.4:/src/current-pa…" target="_top">Add canvas image extraction prompt</a><p>
+In two circumstances, deliberately generated entropy was introduced in various
+components of the build process. First, the BuildID Debuginfo identifier
+(which associates detached debug files with their corresponding stripped
+executables) was introducing entropy from some unknown source. We removed this
+header using objcopy invocations in our build scriptlets, and opted to use GNU
+DebugLink instead of BuildID for this association.
-This patch prompts the user before returning canvas image data. Canvas image
-data can be used to create an extremely stable, high-entropy fingerprint based
-on the unique rendering behavior of video cards, OpenGL behavior,
-system fonts, and supporting library versions.
+ </p><p>
- </p></li><li class="listitem"><a class="ulink" href="https://gitweb.torproject.org/torbrowser.git/blob/maint-2.4:/src/current-pa…" target="_top">Return client window coordinates for mouse events</a><p>
+Second, on Linux, Firefox builds detached signatures of its cryptographic
+libraries using a temporary key for FIPS-140 certification. A rather insane
+subsection of the FIPS-140 certification standard requires that you distribute
+signatures for all of your cryptographic libraries. The Firefox build process
+meets this requirement by generating a temporary key, using it to sign the
+libraries, and discarding the private portion of that key. Because there are
+many other ways to intercept the crypto outside of modifying the actual DLL
+images, we opted to simply remove these signature files from distribution.
+There simply is no way to verify code integrity on a running system without
+both OS and co-processor assistance. Download package signatures make sense of
+course, but we handle those another way (as mentioned above).
-This patch causes mouse events to return coordinates relative to the content
-window instead of the desktop.
- </p></li><li class="listitem"><a class="ulink" href="https://gitweb.torproject.org/torbrowser.git/blob/maint-2.4:/src/current-pa…" target="_top">Do not expose physical screen info to window.screen</a><p>
+ </p></li><li class="listitem">LXC-specific leaks
+ <p>
-This patch causes window.screen to return the display resolution size of the
-content window instead of the desktop resolution size.
+Gitian provides an option to use LXC containers instead of full qemu-kvm
+virtualization. Unfortunately, these containers can allow additional details
+about the host OS to leak. In particular, umask settings as well as the
+hostname and Linux kernel version can leak from the host OS into the LXC
+container. We addressed umask by setting it explicitly in our Gitian
+descriptor scriptlet, and addressed the hostname and kernel version leaks by
+directly patching the aspects of the Firefox build process that included this
+information into the build.
+ </p></li></ol></div></div><div class="sect2"><div class="titlepage"><div><div><h3 class="title"><a id="idp37036336"></a>5.2. Package Signatures and Verification</h3></div></div></div><p>
+
+The build process produces a single sha256sums.txt file that contains a sorted
+list the SHA-256 hashes of every package produced for that build version. Each
+official builder uploads this file and a GPG signature of it to a directory
+on a Tor Project's web server. The build scripts have an optional matching
+step that downloads these signatures, verifies them, and ensures that the
+local builds match this file.
- </p></li><li class="listitem"><a class="ulink" href="https://gitweb.torproject.org/torbrowser.git/blob/maint-2.4:/src/current-pa…" target="_top">Do not expose system colors to CSS or canvas</a><p>
+ </p><p>
-This patch prevents CSS and Javascript from discovering your desktop color
-scheme and/or theme.
+When builds are published officially, the single sha256sums.txt file is
+accompanied by a detached GPG signature from each official builder that
+produced a matching build. The packages are additionally signed with detached
+GPG signatures from an official signing key.
- </p></li><li class="listitem"><a class="ulink" href="https://gitweb.torproject.org/torbrowser.git/blob/maint-2.4:/src/current-pa…" target="_top">Isolate the Image Cache per url bar domain</a><p>
+ </p><p>
-This patch prevents cached images from being used to store third party tracking
-identifiers.
+The fact that the entire set of packages for a given version can be
+authenticated by a single hash of the sha256sums.txt file will also allow us
+to create a number of auxiliary authentication mechanisms for our packages,
+beyond just trusting a single offline build machine and a single cryptographic
+key's integrity. Interesting examples include providing multiple independent
+cryptographic signatures for packages, listing the package hashes in the Tor
+consensus, and encoding the package hashes in the Bitcoin blockchain.
- </p></li><li class="listitem"><a class="ulink" href="https://gitweb.torproject.org/torbrowser.git/blob/maint-2.4:/src/current-pa…" target="_top">nsIHTTPChannel.redirectTo() API</a><p>
+ </p><p>
-This patch provides HTTPS-Everywhere with an API to perform redirections more
-securely and without addon conflicts.
+At the time of this writing, we do not yet support native code signing for Mac
+OS or Windows. Because these signatures are embedded in the actual packages,
+and by their nature are based on non-public key material, providing native
+code-signed packages while still preserving ease of reproducibility
+verification has not yet been achieved.
- </p></li><li class="listitem"><a class="ulink" href="https://gitweb.torproject.org/torbrowser.git/blob/maint-2.4:/src/current-pa…" target="_top">Isolate DOM Storage to first party URI</a><p>
+ </p></div><div class="sect2"><div class="titlepage"><div><div><h3 class="title"><a id="idp37040272"></a>5.3. Anonymous Verification</h3></div></div></div><p>
-This patch prevents DOM Storage from being used to store third party tracking
-identifiers.
+Due to the fact that bit-identical packages can be produced by anyone, the
+security of this build system extends beyond the security of the official
+build machines. In fact, it is still possible for build integrity to be
+achieved even if all official build machines are compromised.
- </p></li><li class="listitem"><a class="ulink" href="https://gitweb.torproject.org/torbrowser.git/blob/maint-2.4:/src/current-pa…" target="_top">Remove
-"This plugin is disabled" barrier</a><p>
+ </p><p>
-This patch removes a barrier that was informing users that plugins were
-disabled and providing them with a link to enable them. We felt this was poor
-user experience, especially since the barrier was displayed even for sites
-with dual Flash+HTML5 video players, such as YouTube.
+By default, all tor-specific dependencies and inputs to the build process are
+downloaded over Tor, which allows build verifiers to remain anonymous and
+hidden. Because of this, any individual can use our anonymity network to
+privately download our source code, verify it against public signed, audited,
+and mirrored git repositories, and reproduce our builds exactly, without being
+subject to targeted attacks. If they notice any differences, they can alert
+the public builders/signers, hopefully using a pseudonym or our anonymous
+bugtracker account, to avoid revealing the fact that they are a build
+verifier.
- </p></li></ol></div></div></div><div class="appendix" title="A. Towards Transparency in Navigation Tracking"><h2 class="title" style="clear: both"><a id="Transparency"></a>A. Towards Transparency in Navigation Tracking</h2><p>
+ </p></div></div><div class="appendix"><h2 class="title" style="clear: both"><a id="Transparency"></a>A. Towards Transparency in Navigation Tracking</h2><p>
The <a class="link" href="#privacy" title="2.2. Privacy Requirements">privacy properties</a> of Tor Browser are based
upon the assumption that link-click navigation indicates user consent to
@@ -1554,7 +1667,7 @@ also describe auditable alternatives and promising web draft standards that woul
preserve this functionality while still providing transparency when tracking is
occurring.
-</p><div class="sect1" title="A.1. Deprecation Wishlist"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="deprecate"></a>A.1. Deprecation Wishlist</h2></div></div></div><div class="orderedlist"><ol class="orderedlist" type="1"><li class="listitem">The Referer Header
+</p><div class="sect1"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="deprecate"></a>A.1. Deprecation Wishlist</h2></div></div></div><div class="orderedlist"><ol class="orderedlist" type="1"><li class="listitem">The Referer Header
<p>
We haven't disabled or restricted the Referer ourselves because of the
@@ -1617,7 +1730,7 @@ possible for us to <a class="ulink" href="https://trac.torproject.org/projects/t
ourselves</a>, as they are comparatively rare and can be handled with site
permissions.
- </p></li></ol></div></div><div class="sect1" title="A.2. Promising Standards"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="idp5896048"></a>A.2. Promising Standards</h2></div></div></div><div class="orderedlist"><ol class="orderedlist" type="1"><li class="listitem"><a class="ulink" href="http://web-send.org" target="_top">Web-Send Introducer</a><p>
+ </p></li></ol></div></div><div class="sect1"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="idp37071376"></a>A.2. Promising Standards</h2></div></div></div><div class="orderedlist"><ol class="orderedlist" type="1"><li class="listitem"><a class="ulink" href="http://web-send.org" target="_top">Web-Send Introducer</a><p>
Web-Send is a browser-based link sharing and federated login widget that is
designed to operate without relying on third-party tracking or abusing other
@@ -1633,4 +1746,4 @@ not directly provide the link sharing capabilities that Web-Send does, it is a
better solution to the privacy issues associated with federated login than
Web-Send is.
- </p></li></ol></div></div></div></div></body></html>
+ </p></li></ol></div></div></div></div></body></html>
\ No newline at end of file
1
0
commit c95e661c9acdfe2d746099709cf544f8d8613c40
Author: Mike Perry <mikeperry-git(a)torproject.org>
Date: Wed Oct 29 21:39:41 2014 -0700
More cleanups.
---
design-doc/design.xml | 61 +++++++++++++++++++++++--------------------------
1 file changed, 28 insertions(+), 33 deletions(-)
diff --git a/design-doc/design.xml b/design-doc/design.xml
index 7c89257..b8c67d9 100644
--- a/design-doc/design.xml
+++ b/design-doc/design.xml
@@ -23,7 +23,7 @@
<address><email>sjmurdoch#torproject org</email></address>
</affiliation>
</author>
- <pubdate>October 20th, 2014</pubdate>
+ <pubdate>October 30th, 2014</pubdate>
</articleinfo>
<!--
@@ -1475,6 +1475,15 @@ Panopticlick to allow us to run our own version for this reason.
</para>
<sect3 id="fingerprinting-defenses">
<title>Fingerprinting defenses in the Tor Browser</title>
+ <para>
+
+The following defenses are listed roughly in order of most severe
+fingerprinting threat first, though we are desperately in need of updated
+measurements to determine this with certainty. Where our actual implementation
+differs from an ideal solution, we separately describe our <command>Design
+Goal</command> and our <command>Implementation Status</command>.
+
+ </para>
<orderedlist>
<listitem>Plugins
<para>
@@ -1547,18 +1556,18 @@ data is returned to the Javascript APIs.
<para>
</para>
</listitem>
- <listitem>Open Local Port Fingerprinting
+ <listitem>Open TCP Port Fingerprinting
<para>
In Firefox, by using either WebSockets or XHR, it is possible for remote
content to <ulink url="http://www.andlabs.org/tools/jsrecon.html">enumerate
the list of TCP ports open on 127.0.0.1</ulink>. In other browsers, this can
-be accomplished by DOM events on image tags. This open vs filtered vs closed
-port list can provide a very unique fingerprint of a machine.
+be accomplished by DOM events on image or script tags. This open vs filtered
+vs closed port list can provide a very unique fingerprint of a machine.
</para>
- <para><command>Implementation Status:</command> We prevent access to
+ <para>In Tor Browser, we prevent access to
127.0.0.1/localhost by ensuring that even these requests are still sent by
Firefox to our SOCKS proxy (ie we set
<command>network.proxy.no_proxies_on</command> to the empty string). The local
@@ -1615,8 +1624,9 @@ provided by the following WebGL API calls: <command>getParameter()</command>,
</para>
<para>
-Another alternative for WebGL might be to fall back to software rendering only
-for private motes.
+Another option for WebGL might be to use software-only rendering, using a
+library such as <ulink url="http://www.mesa3d.org/">Mesa</ulink>. The use of
+such a library would avoid hardware-specific rendering differences.
</para>
</listitem>
@@ -1796,7 +1806,14 @@ and exception handling.
</listitem>
<listitem>Timezone and clock offset
- <para><command>Design Goal:</command>
+ <para>
+
+While the latency in Tor connections varies anywhere from milliseconds to
+several seconds, it is still possible for the remote site to detect large
+differences between the user's clock and an official reference timesource.
+ </para>
+
+ <para><command>Design Goal:</command>
All Tor Browser users MUST report the same timezone to websites. Currently, we
choose UTC for this purpose, although an equally valid argument could be made
@@ -1804,7 +1821,9 @@ for EDT/EST due to the large English-speaking population density (coupled with
the fact that we spoof a US English user agent). Additionally, the Tor
software should detect if the users clock is significantly divergent from the
clocks of the relays that it connects to, and use this to reset the clock
-values used in Tor Browser to something reasonably accurate.
+values used in Tor Browser to something reasonably accurate. Alternatively,
+the browser can obtain this clock skew via a mechanism similar to that used in
+<ulink linkend="https://github.com/ioerror/tlsdate">tlsdate</ulink>.
</para>
<para><command>Implementation Status:</command>
@@ -1817,30 +1836,6 @@ use.
</para>
</listitem>
- <listitem>Timezone and Clock skew fingerprinting
- <para>
-
-While the latency in Tor connections varies anywhere from milliseconds to
-several seconds, it is still possible for the remote site to detect large
-differences between the user's clock and an official reference timesource.
- </para>
-
- <para><command>Design Goal:</command> Ideally, the browser would be able
-to correct the source of this clock drift using an external time source,
-either through something like <ulink
-linkend="https://github.com/ioerror/tlsdate">tlsdate</ulink>, or directly
-through the Tor protocol. Additionally, the timezone should be set to UTC.
-
- </para>
- <para><command>Implementation Status:</command>
-
-Right now, we currently set the timezone to UTC via the
-<command>TZ</command> environment variable, and randomize the TLS Hello
-timestamp. However, we have not yet integrated tlsdate or an external
-timesource.
-
- </para>
- </listitem>
<listitem>Javascript performance fingerprinting
<para>
1
0
commit d856497cb5e0b5f51ecf642edbc1c2053b00d0f8
Author: Mike Perry <mikeperry-git(a)torproject.org>
Date: Wed Oct 29 21:15:01 2014 -0700
Speel check.
---
design-doc/design.xml | 18 +++++++++---------
1 file changed, 9 insertions(+), 9 deletions(-)
diff --git a/design-doc/design.xml b/design-doc/design.xml
index a49de34..7c89257 100644
--- a/design-doc/design.xml
+++ b/design-doc/design.xml
@@ -372,7 +372,7 @@ url bar origin</ulink> to eliminate the possibility of linkability
between domains. For example, when a plugin object (or a Javascript access of
window.plugins) is present in a page, the user should be given the choice of
allowing that plugin object for that url bar origin only. The same
-goes for exemptions to third party cookie policy, geo-location, and any other
+goes for exemptions to third party cookie policy, geolocation, and any other
privacy permissions.
</para>
<para>
@@ -494,7 +494,7 @@ choosing.</para>
<para>If direct proxy bypass is not possible, the adversary will likely
happily settle for the ability to correlate something a user did via Tor with
their non-Tor activity. This can be done with cookies, cache identifiers,
-javascript events, and even CSS. Sometimes the fact that a user uses Tor may
+Javascript events, and even CSS. Sometimes the fact that a user uses Tor may
be enough for some authorities.</para>
</listitem>
<listitem><command>History disclosure</command>
@@ -626,7 +626,7 @@ with cookies as well.
These types of attacks are attempts at subverting our <link
linkend="identifier-linkability">Cross-Origin Identifier Unlinkability</link> and <link
-linkend="new-identity">Long-Term Unlikability</link> design requirements.
+linkend="new-identity">Long-Term Unlinkability</link> design requirements.
</para>
</listitem>
@@ -640,7 +640,7 @@ uniquely fingerprint individual users. Attacks of this nature are typically
aimed at tracking users across sites without their consent, in an attempt to
subvert our <link linkend="fingerprinting-linkability">Cross-Origin
Fingerprinting Unlinkability</link> and <link
-linkend="new-identity">Long-Term Unlikability</link> design requirements.
+linkend="new-identity">Long-Term Unlinkability</link> design requirements.
</para>
@@ -792,7 +792,7 @@ are large numbers of dynamically generated pages, partially cached content,
and also the non-web activity of entire Tor network. This yields an effective
number of "web pages" many orders of magnitude larger than even <ulink
url="http://lorre.uni.lu/~andriy/papers/acmccs-wpes11-fingerprinting.pdf">Panchenko's
-"Open World" scenario</ulink>, which suffered continous near-constant decline
+"Open World" scenario</ulink>, which suffered continuous near-constant decline
in the true positive rate as the "Open World" size grew (see figure 4). This
large level of classification complexity is further confounded by a noisy and
low resolution featureset - one which is also relatively easy for the defender
@@ -929,7 +929,7 @@ activity in the source tree that did not use the browser proxy settings.
<para>
We have verified that these settings and patches properly proxy HTTPS, OCSP,
-HTTP, FTP, gopher (now defunct), DNS, SafeBrowsing Queries, all javascript
+HTTP, FTP, gopher (now defunct), DNS, SafeBrowsing Queries, all JavaScript
activity, including HTML5 audio and video objects, addon updates, wifi
geolocation queries, searchbox queries, XPCOM addon HTTPS/HTTP activity,
WebSockets, and live bookmark updates. We have also verified that IPv6
@@ -1584,7 +1584,7 @@ We simply disable it via the pref <command>dom.gamepad.enabled</command>.
</listitem>
<listitem>Invasive Authentication Mechanisms (NTLM and SPNEGO)
<para>
-Both NTLM and SPNEGO authentication mechansisms can leak the hostname, and in
+Both NTLM and SPNEGO authentication mechanisms can leak the hostname, and in
some cases the machine username. These authentication mechanisms should either
be disabled, or placed behind a site permission before their use. We simply
disable them.
@@ -1716,7 +1716,7 @@ popups to open in new tabs (via
<command>browser.link.open_newwindow.restriction</command>), to avoid
full-screen popups inferring information about the browser resolution. In
addition, we prevent auto-maximizing on browser start, and are investigating a
-user-friendly way of informing users that maximized windows are deterimental
+user-friendly way of informing users that maximized windows are detrimental
to privacy in this mode.
</para>
@@ -2609,7 +2609,7 @@ libraries, and discarding the private portion of that key. Because there are
many other ways to intercept the crypto outside of modifying the actual DLL
images, we opted to simply remove these signature files from distribution.
There simply is no way to verify code integrity on a running system without
-both OS and coprocessor assistance. Download package signatures make sense of
+both OS and co-processor assistance. Download package signatures make sense of
course, but we handle those another way (as mentioned above).
1
0

30 Oct '14
commit db382d7cdef7988a5022deb0ea44d5e65a14ed6a
Author: Mike Perry <mikeperry-git(a)torproject.org>
Date: Wed Oct 29 20:53:37 2014 -0700
Finish fingerprinting updates.
---
design-doc/design.xml | 114 ++++++++++++++++++++++++++++++++-----------------
1 file changed, 76 insertions(+), 38 deletions(-)
diff --git a/design-doc/design.xml b/design-doc/design.xml
index b469f2a..a49de34 100644
--- a/design-doc/design.xml
+++ b/design-doc/design.xml
@@ -1531,7 +1531,11 @@ image can be used almost identically to a tracking cookie by the web server.
</para>
<para>
-To reduce the threat from this vector, we have patched Firefox to <ulink
+In some sense, the canvas can be seen as the union of many other
+fingerprinting vectors. If WebGL were normalized through software rendering,
+and the browser shipped a fixed collection of fonts, it might not be necessary
+to create a canvas permission. However, until then, to reduce the threat from
+this vector, we have patched Firefox to <ulink
url="https://gitweb.torproject.org/tor-browser.git/commitdiff/3b53f525cfb68880e6…">prompt
before returning valid image data</ulink> to the Canvas APIs, and for <ulink
url="https://gitweb.torproject.org/tor-browser.git/commitdiff/fb9f463fe3a69499d6…">access
@@ -1540,6 +1544,8 @@ allowed the site in the URL bar to access Canvas image data, pure white image
data is returned to the Javascript APIs.
</para>
+ <para>
+ </para>
</listitem>
<listitem>Open Local Port Fingerprinting
<para>
@@ -1563,13 +1569,17 @@ addresses by default.
</listitem>
<listitem>USB Device ID enumeration
<para>
-The GamePad API <ulink
-url="https://developer.mozilla.org/en-US/docs/Web/Guide/API/Gamepad#querying">provides
-web pages with the USB device id, product id, and driver name</ulink> of all
-connected game controllers, as well as detailed information about their
-capabilities. This API should be behind a site permission in Private Browsing
-Modes. We simply disable it via the pref
-<command>dom.gamepad.enabled</command>.
+
+The <ulink
+url="https://developer.mozilla.org/en-US/docs/Web/Guide/API/Gamepad">GamePad
+API</ulink> provides web pages with the <ulink
+url="https://dvcs.w3.org/hg/gamepad/raw-file/default/gamepad.html#widl-Gamepad-id">USB
+device id, product id, and driver name</ulink> of all connected game
+controllers, as well as detailed information about their capabilities. This API
+should be behind a site permission in Private Browsing Modes, or should present a generic
+controller type (perhaps a two button controller that can be mapped to the keyboard) in all cases.
+We simply disable it via the pref <command>dom.gamepad.enabled</command>.
+
</para>
</listitem>
<listitem>Invasive Authentication Mechanisms (NTLM and SPNEGO)
@@ -1603,6 +1613,12 @@ provided by the following WebGL API calls: <command>getParameter()</command>,
<command>getExtension()</command>.
</para>
+ <para>
+
+Another alternative for WebGL might be to fall back to software rendering only
+for private motes.
+
+ </para>
</listitem>
<listitem>Fonts
<para>
@@ -1615,19 +1631,25 @@ pre-built list to query, a large amount of fingerprintable information may
still be available.
</para>
- <para>
-
-The sure-fire way to address font linkability is to ship the browser with a
-font for every language, typeface, and style in use in the world, and to only
-use those fonts at the exclusion of system fonts. However, this set may be
-impractically large. It is possible that a smaller <ulink
-url="https://secure.wikimedia.org/wikipedia/en/wiki/Unicode_typeface#List_of_Uni…">common
-subset</ulink> may be found that provides total coverage. Right now, it
-appears that the major languages on Wikipedia can be supported for about 3MB
-of additional distribution size, using the DejaVu font set.
-
+ <para><command>Design Goal:</command> The sure-fire way to address font
+linkability is to ship the browser with a font for every language, typeface,
+and style, and to only use those fonts at the exclusion of system fonts. We are
+<ulink url="https://trac.torproject.org/projects/tor/ticket/13313">currently
+investigating</ulink> this approach, and our current favorite font sets for
+this purpose are the <ulink url="http://www.droidfonts.com/droidfonts/">Droid
+fonts</ulink>, the <ulink url="http://hangeul.naver.com/">Nanum fonts</ulink>,
+and <ulink url="https://fedorahosted.org/lohit/">Lohit fonts</ulink>. The Droid
+font set is fairly complete by itself, but Nanum and Lohit have smaller
+versions of many South Asian languages. When combined in a way that chooses the
+smallest font implementations for each locale, these three font sets provide
+which provide coverage for the all languages used on Wikipedia with more than
+10,000 articles, and several others as well, in approximately 3MB of compressed
+overhead. The <ulink url="https://www.google.com/get/noto/">Noto font
+set</ulink> is another font set that aims for complete coverage, but is
+considerably larger than the combination of the Droid, Nanum, and Lohit fonts.
</para>
+
<para><command>Implementation Status:</command>
In the meantime while we investigate shipping our own fonts, we disable
@@ -1639,8 +1661,7 @@ a Firefox patch</ulink>. We create two prefs,
<command>browser.display.max_font_attempts</command> and
<command>browser.display.max_font_count</command> for this purpose. Once these
limits are reached, the browser behaves as if
-<command>browser.display.use_document_fonts</command> was set. We are still
-working to determine optimal values for these prefs.
+<command>browser.display.use_document_fonts</command> was set.
</para>
<para>
@@ -1670,13 +1691,14 @@ properties of the content window, but report an effective size of 0 for all
border material, and also report that the desktop is only as big as the inner
content window. Additionally, new browser windows are sized such that their
content windows are one of a few fixed sizes based on the user's desktop
-resolution. The user should also be informed that maximizing their windows can
-lead to fingerprintability under this scheme. To further reduce
-resolution-based fingerprinting, we are <ulink
+resolution. In addition, to further reduce resolution-based fingerprinting, we
+are <ulink
url="https://trac.torproject.org/projects/tor/ticket/7256">investigating
-zoom/viewport-based mechanisms</ulink> that might allow us to always report
-the same desktop resolution regardless of the actual size of the content
-window, and simply scale to make up the difference.
+zoom/viewport-based mechanisms</ulink> that might allow us to always report the
+same desktop resolution regardless of the actual size of the content window,
+and simply scale to make up the difference. Until then, the user should also
+be informed that maximizing their windows can lead to fingerprintability under
+this scheme.
</para>
<para><command>Implementation Status:</command>
@@ -1702,15 +1724,15 @@ to privacy in this mode.
<listitem>CSS Media Queries
<para>
-Both CSS and Javascript have access to a lot of information about the screen
+Even without Javascript, CSS has access to a lot of information about the screen
resolution, usable desktop size, OS widget size, toolbar size, title bar size,
system theme colors, and other desktop features that are not at all relevant
-to rendering and serve only to provide information for fingerprinting.
+to rendering and serve only to provide information for fingerprinting. Most of this information comes from
+<ulink url="https://developer.mozilla.org/en-US/docs/Web/Guide/CSS/Media_queries">CSS Media Queries</ulink>, but
+Mozilla has exposed <ulink url="https://developer.mozilla.org/en-US/docs/Web/CSS/color_value#System_Colors">several user and OS theme defined color values</ulink> to CSS as well.
</para>
<para><command>Design Goal:</command>
-<!-- XXX: Link to CSS spec for this stuff -->
-
In Private Browsing Mode, CSS should not be able infer anything that the user
has configured about their computer. Additionally, it should not be able to
infer machine-specific details such as screen orientation or type.
@@ -1751,11 +1773,28 @@ url="http://pseudo-flaw.net/tor/torbutton/fingerprint-firefox.html">can be
used</ulink> to fingerprint OS, platform, and Firefox minor version. </para>
</listitem>
+
<listitem>Locale Fingerprinting
<para>
-XXX: 2. bug 10703: force the default charset to avoid locale fingerprinting
+
+In Tor Browser, we provide non-English users the option of concealing their OS
+and browser locale from websites. It is debatable if this should be as high of
+a priority as information specific to the user's computer, but for
+completeness, we attempt to maintain this property.
+
+ </para>
+ <para><command>Implementation Status:</command>
+
+We set the fallback character set to set to windows-1252 for all locales, via
+<command>intl.charset.default</command>. We also patch Firefox to allow us to
+<ulink
+url="https://gitweb.torproject.org/tor-browser.git/commitdiff/fe42a78575df7f460f…">instruct
+the JS engine</ulink> to use en-US as its internal C locale for all Date, Math,
+and exception handling.
+
</para>
</listitem>
+
<listitem>Timezone and clock offset
<para><command>Design Goal:</command>
@@ -1786,10 +1825,11 @@ several seconds, it is still possible for the remote site to detect large
differences between the user's clock and an official reference timesource.
</para>
- <para><command>Design Goal:</command> Ideally, the browser would be
-able to correct the source of this clock drift using an external time source,
-either through something like tlsdate, or directly through the Tor protocol.
-Additionally, the timezone should be set to UTC.
+ <para><command>Design Goal:</command> Ideally, the browser would be able
+to correct the source of this clock drift using an external time source,
+either through something like <ulink
+linkend="https://github.com/ioerror/tlsdate">tlsdate</ulink>, or directly
+through the Tor protocol. Additionally, the timezone should be set to UTC.
</para>
<para><command>Implementation Status:</command>
@@ -1829,8 +1869,6 @@ optimum trade-off between quantization+jitter and amortization time.
</para>
<para><command>Implementation Status:</command>
-<!-- XXX: Disabled network performance timers too -->
-
Currently, the only mitigation against performance fingerprinting is to
disable <ulink url="http://www.w3.org/TR/navigation-timing/">Navigation
Timing</ulink> through the Firefox preference
1
0

[torbutton/master] Bug #5926: Allow spoofing of javascript locale to en-US.
by mikeperry@torproject.org 30 Oct '14
by mikeperry@torproject.org 30 Oct '14
30 Oct '14
commit da3286e36773c5ed7e0519dbc4956ef15ee407ff
Author: Arthur Edelstein <arthuredelstein(a)gmail.com>
Date: Fri Oct 10 15:41:19 2014 -0700
Bug #5926: Allow spoofing of javascript locale to en-US.
---
src/chrome/content/torbutton.js | 3 +++
1 file changed, 3 insertions(+)
diff --git a/src/chrome/content/torbutton.js b/src/chrome/content/torbutton.js
index 44020ec..7fddf07 100644
--- a/src/chrome/content/torbutton.js
+++ b/src/chrome/content/torbutton.js
@@ -2030,6 +2030,7 @@ function torbutton_update_fingerprinting_prefs() {
m_tb_prefs.setCharPref("intl.accept_languages", "en-us, en");
m_tb_prefs.setCharPref("intl.accept_charsets", "iso-8859-1,*,utf-8");
m_tb_prefs.setCharPref("intl.charsetmenu.browser.cache", "UTF-8");
+ m_tb_prefs.setCharPref("javascript.default_locale", "en-US");
} else {
if(m_tb_prefs.prefHasUserValue("intl.accept_languages"))
m_tb_prefs.clearUserPref("intl.accept_languages");
@@ -2037,6 +2038,8 @@ function torbutton_update_fingerprinting_prefs() {
m_tb_prefs.clearUserPref("intl.charsetmenu.browser.cache");
if(m_tb_prefs.prefHasUserValue("intl.accept_charsets"))
m_tb_prefs.clearUserPref("intl.accept_charsets");
+ if(m_tb_prefs.prefHasUserValue("javascript.default_locale"))
+ m_tb_prefs.clearUserPref("javascript.default_locale");
}
} else {
m_tb_prefs.setIntPref("browser.display.max_font_attempts",-1);
1
0

[tor-browser/tor-browser-31.2.0esr-4.5-1] Bug #11955 Backport certificate pinning
by mikeperry@torproject.org 30 Oct '14
by mikeperry@torproject.org 30 Oct '14
30 Oct '14
commit cd9887977227df33462e755200ac6f6ade351bea
Author: Camilo Viecco <cviecco(a)mozilla.com>
Date: Thu Jun 20 10:35:43 2013 -0700
Bug #11955 Backport certificate pinning
Includes the following Mozilla patches, some modified for Tor Browser:
Bug 744204 - Allow Key pining part 1 - Built-in Pinning Service. r=keeler
Bug 744204 - Allow Certificate key pinning Part 2 - Certverifier Interface. r=keeler
--HG--
extra : rebase_source : 2f9748ba0b241c697e22b7ff72f2f5a0fad4a2ca
Bug 998057: Add test pinset to the pin generator (r=cviecco)
--HG--
rename : security/manager/ssl/tests/unit/tlsserver/default-ee.der => security/manager/boot/src/default-ee.der
Bug 998057: Add tests for certificate pinning (r=cviecco,dkeeler)
Bug 1002696 - Minimum set of changes to make genHPKPStaticPins.js productionizable. r=cviecco, dkeeler
--HG--
rename : security/manager/boot/src/PreloadedHPKPins.json => security/manager/tools/PreloadedHPKPins.json
rename : security/manager/boot/src/genHPKPStaticPins.js => security/manager/tools/genHPKPStaticPins.js
Bug 951315 - Add telemetry to PK pinning. r=dkeeler
Bug 1006107 - Disable pining by default, setup pinning for *.addons.mozilla.org. r=dkeeler
Tor project: only patching two files:
security/manager/ssl/src/nsNSSComponent.cpp
netwerk/base/public/security-prefs.js
--HG--
extra : rebase_source : 93b1dbd5dc31490424060729a3941deffa8ee1d5
Bug 772756: Implement sha1 support, import Chrome's pinsets wholesale, add test mode (r=cviecco,keeler)
Tor project, we only patch:
security/manager/ssl/tests/unit/test_pinning.js
security/manager/ssl/tests/unit/tlsserver/cmd/BadCertServer.cpp
security/manager/ssl/tests/unit/tlsserver/default-ee.der
security/manager/ssl/tests/unit/tlsserver/generate_certs.sh
security/manager/ssl/tests/unit/tlsserver/other-test-ca.der
security/manager/ssl/tests/unit/tlsserver/test-ca.der
Bug 1009720: Telemetry for CERT_PINNING_TEST_RESULTS (r=keeler)
Bug 1007844: Implement per-host telemetry for pin violations for AMO and aus4 (r=keeler)
Only patching toolkit/components/telemetry/Histograms.json
Bug 1011269: Add CertVerifier::pinningEnforceTestMode (r=keeler)
Tor project, only commit:
security/certverifier/CertVerifier.cpp
security/certverifier/CertVerifier.h
security/manager/ssl/src/nsNSSComponent.cpp
Bug 1012882: Restrict pinning to desktop (r=keeler)
Tor Bug #11955: Backport certificate pinning
Bring the following files up to date:
security/manager/boot/src/PublicKeyPinningService.cpp
security/manager/boot/src/PublicKeyPinningService.h
security/manager/boot/src/StaticHPKPins.h
security/manager/ssl/tests/unit/test_pinning.js
security/manager/tools/PreloadedHPKPins.json
security/manager/tools/genHPKPStaticPins.js
security/pkix/include/pkix/Time.h
security/pkix/lib/pkixtime.cpp
---
.gitignore | 2 +-
browser/app/profile/firefox.js | 3 +
modules/libpref/src/init/all.js | 3 +
security/apps/AppTrustDomain.h | 2 +
security/certverifier/CertVerifier.cpp | 225 +++-
security/certverifier/CertVerifier.h | 17 +-
security/certverifier/NSSCertDBTrustDomain.cpp | 35 +-
security/certverifier/NSSCertDBTrustDomain.h | 6 +-
security/certverifier/moz.build | 1 +
.../manager/boot/src/PublicKeyPinningService.cpp | 306 ++++++
.../boot/src/PublicKeyPinningService.cpp.rej | 11 +
.../manager/boot/src/PublicKeyPinningService.h | 35 +
security/manager/boot/src/StaticHPKPins.h | 1095 ++++++++++++++++++++
security/manager/boot/src/moz.build | 6 +
.../manager/ssl/src/SSLServerCertVerification.cpp | 5 +-
security/manager/ssl/src/SharedCertVerifier.h | 6 +-
security/manager/ssl/src/nsCMS.cpp | 5 +-
security/manager/ssl/src/nsNSSCertificate.cpp | 13 +-
security/manager/ssl/src/nsNSSCertificateDB.cpp | 19 +-
security/manager/ssl/src/nsNSSComponent.cpp | 12 +-
security/manager/ssl/src/nsUsageArrayHelper.cpp | 5 +-
security/manager/ssl/tests/unit/head_psm.js | 1 +
.../manager/ssl/tests/unit/test_cert_overrides.js | 2 +-
security/manager/ssl/tests/unit/test_pinning.js | 182 ++++
security/manager/ssl/tests/unit/tlsserver/cert8.db | Bin 65536 -> 65536 bytes
.../ssl/tests/unit/tlsserver/cmd/BadCertServer.cpp | 11 +
.../ssl/tests/unit/tlsserver/default-ee.der | Bin 527 -> 639 bytes
.../ssl/tests/unit/tlsserver/generate_certs.sh | 17 +-
security/manager/ssl/tests/unit/tlsserver/key3.db | Bin 49152 -> 57344 bytes
.../ssl/tests/unit/tlsserver/other-test-ca.der | Bin 452 -> 452 bytes
.../manager/ssl/tests/unit/tlsserver/secmod.db | Bin 16384 -> 16384 bytes
.../manager/ssl/tests/unit/tlsserver/test-ca.der | Bin 440 -> 440 bytes
security/manager/ssl/tests/unit/xpcshell.ini | 4 +
security/manager/tools/PreloadedHPKPins.json | 247 +++++
security/manager/tools/genHPKPStaticPins.js | 576 ++++++++++
security/pkix/include/pkix/Result.h | 174 ++++
security/pkix/include/pkix/Time.h | 126 +++
security/pkix/include/pkix/pkixtypes.h | 5 +
security/pkix/lib/pkixbuild.cpp | 24 +
security/pkix/lib/pkixtime.cpp | 70 ++
toolkit/components/telemetry/Histograms.json | 32 +
41 files changed, 3198 insertions(+), 85 deletions(-)
diff --git a/.gitignore b/.gitignore
index 8df1754..6d17511 100644
--- a/.gitignore
+++ b/.gitignore
@@ -13,7 +13,7 @@ ID
.*.sw[a-z]
# User files that may appear at the root
-/.mozconfig*
+#/.mozconfig*
/mozconfig
/configure
/config.cache
diff --git a/browser/app/profile/firefox.js b/browser/app/profile/firefox.js
index 2ec7f9a..b61756d 100644
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1484,6 +1484,9 @@ pref("security.csp.speccompliant", true);
// Block insecure active content on https pages
pref("security.mixed_content.block_active_content", true);
+// 1 = allow MITM for certificate pinning checks.
+pref("security.cert_pinning.enforcement_level", 1);
+
// Override the Gecko-default value of false for Firefox.
pref("plain_text.wrap_long_lines", true);
diff --git a/modules/libpref/src/init/all.js b/modules/libpref/src/init/all.js
index 8efad8f..8a389e3 100644
--- a/modules/libpref/src/init/all.js
+++ b/modules/libpref/src/init/all.js
@@ -1566,6 +1566,9 @@ pref("security.csp.experimentalEnabled", false);
pref("security.mixed_content.block_active_content", false);
pref("security.mixed_content.block_display_content", false);
+// Disable pinning checks by default.
+pref("security.cert_pinning.enforcement_level", 0);
+
// Modifier key prefs: default to Windows settings,
// menu access key = alt, accelerator key = control.
// Use 17 for Ctrl, 18 for Alt, 224 for Meta, 91 for Win, 0 for none. Mac settings in macprefs.js
diff --git a/security/apps/AppTrustDomain.h b/security/apps/AppTrustDomain.h
index 875c1db..d7e4734 100644
--- a/security/apps/AppTrustDomain.h
+++ b/security/apps/AppTrustDomain.h
@@ -35,6 +35,8 @@ public:
/*const*/ CERTCertificate* issuerCertToDup,
PRTime time,
/*optional*/ const SECItem* stapledOCSPresponse);
+ SECStatus IsChainValid(const CERTCertList* certChain) { return SECSuccess; }
+
private:
void* mPinArg; // non-owning!
mozilla::pkix::ScopedCERTCertificate mTrustedRoot;
diff --git a/security/certverifier/CertVerifier.cpp b/security/certverifier/CertVerifier.cpp
index b8b84d7..b2db9fc 100644
--- a/security/certverifier/CertVerifier.cpp
+++ b/security/certverifier/CertVerifier.cpp
@@ -11,9 +11,11 @@
#include "pkix/pkix.h"
#include "ExtendedValidation.h"
#include "NSSCertDBTrustDomain.h"
+#include "PublicKeyPinningService.h"
#include "cert.h"
#include "ocsp.h"
#include "secerr.h"
+#include "pk11pub.h"
#include "prerror.h"
#include "sslerr.h"
@@ -38,7 +40,8 @@ CertVerifier::CertVerifier(implementation_config ic,
#endif
ocsp_download_config odc,
ocsp_strict_config osc,
- ocsp_get_config ogc)
+ ocsp_get_config ogc,
+ pinning_enforcement_config pel)
: mImplementation(ic)
#ifndef NSS_NO_LIBPKIX
, mMissingCertDownloadEnabled(mcdc == missing_cert_download_on)
@@ -47,6 +50,7 @@ CertVerifier::CertVerifier(implementation_config ic,
, mOCSPDownloadEnabled(odc == ocsp_on)
, mOCSPStrict(osc == ocsp_strict)
, mOCSPGETEnabled(ogc == ocsp_get_enabled)
+ , mPinningEnforcementLevel(pel)
{
}
@@ -64,7 +68,6 @@ InitCertVerifierLog()
#endif
}
-#if 0
// Once we migrate to mozilla::pkix or change the overridable error
// logic this will become unnecesary.
static SECStatus
@@ -95,23 +98,102 @@ insertErrorIntoVerifyLog(CERTCertificate* cert, const PRErrorCode err,
return SECSuccess;
}
-#endif
+
+SECStatus
+IsCertBuiltInRoot(CERTCertificate* cert, bool& result) {
+ result = false;
+ ScopedPtr<PK11SlotList, PK11_FreeSlotList> slots;
+ slots = PK11_GetAllSlotsForCert(cert, nullptr);
+ if (!slots) {
+ if (PORT_GetError() == SEC_ERROR_NO_TOKEN) {
+ // no list
+ return SECSuccess;
+ }
+ return SECFailure;
+ }
+ for (PK11SlotListElement* le = slots->head; le; le = le->next) {
+ char* token = PK11_GetTokenName(le->slot);
+ PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
+ ("BuiltInRoot? subject=%s token=%s",cert->subjectName, token));
+ if (strcmp("Builtin Object Token", token) == 0) {
+ result = true;
+ return SECSuccess;
+ }
+ }
+ return SECSuccess;
+}
+
+struct ChainValidationCallbackState
+{
+ const char* hostname;
+ const CertVerifier::pinning_enforcement_config pinningEnforcementLevel;
+ const SECCertificateUsage usage;
+ const PRTime time;
+};
SECStatus chainValidationCallback(void* state, const CERTCertList* certList,
PRBool* chainOK)
{
+ ChainValidationCallbackState* callbackState =
+ reinterpret_cast<ChainValidationCallbackState*>(state);
+
*chainOK = PR_FALSE;
- PR_LOG(gCertVerifierLog, PR_LOG_DEBUG, ("verifycert: Inside the Callback \n"));
+ PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
+ ("verifycert: Inside the Callback \n"));
// On sanity failure we fail closed.
if (!certList) {
- PR_LOG(gCertVerifierLog, PR_LOG_DEBUG, ("verifycert: Short circuit, callback, "
- "sanity check failed \n"));
+ PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
+ ("verifycert: Short circuit, callback, sanity check failed \n"));
+ PR_SetError(PR_INVALID_STATE_ERROR, 0);
+ return SECFailure;
+ }
+ if (!callbackState) {
+ PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
+ ("verifycert: Short circuit, callback, no state! \n"));
PR_SetError(PR_INVALID_STATE_ERROR, 0);
return SECFailure;
}
- *chainOK = PR_TRUE;
+
+ if (callbackState->usage != certificateUsageSSLServer ||
+ callbackState->pinningEnforcementLevel == CertVerifier::pinningDisabled) {
+ PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
+ ("verifycert: Callback shortcut pel=%d \n",
+ callbackState->pinningEnforcementLevel));
+ *chainOK = PR_TRUE;
+ return SECSuccess;
+ }
+
+ for (CERTCertListNode* node = CERT_LIST_HEAD(certList);
+ !CERT_LIST_END(node, certList);
+ node = CERT_LIST_NEXT(node)) {
+ CERTCertificate* currentCert = node->cert;
+ if (CERT_LIST_END(CERT_LIST_NEXT(node), certList)) {
+ bool isBuiltInRoot = false;
+ SECStatus srv = IsCertBuiltInRoot(currentCert, isBuiltInRoot);
+ if (srv != SECSuccess) {
+ PR_LOG(gCertVerifierLog, PR_LOG_DEBUG, ("Is BuiltInRoot failure"));
+ return srv;
+ }
+ // If desired, the user can enable "allow user CA MITM mode", in which
+ // case key pinning is not enforced for certificates that chain to trust
+ // anchors that are not in Mozilla's root program
+ if (!isBuiltInRoot &&
+ (callbackState->pinningEnforcementLevel ==
+ CertVerifier::pinningAllowUserCAMITM)) {
+ *chainOK = PR_TRUE;
+ return SECSuccess;
+ }
+ }
+ }
+
+ const bool enforceTestMode = (callbackState->pinningEnforcementLevel ==
+ CertVerifier::pinningEnforceTestMode);
+ *chainOK = PublicKeyPinningService::
+ ChainHasValidPins(certList, callbackState->hostname, callbackState->time,
+ enforceTestMode);
+
return SECSuccess;
}
@@ -120,42 +202,41 @@ ClassicVerifyCert(CERTCertificate* cert,
const SECCertificateUsage usage,
const PRTime time,
void* pinArg,
+ ChainValidationCallbackState* callbackState,
/*optional out*/ ScopedCERTCertList* validationChain,
/*optional out*/ CERTVerifyLog* verifyLog)
{
SECStatus rv;
SECCertUsage enumUsage;
- if (validationChain) {
- switch(usage){
- case certificateUsageSSLClient:
- enumUsage = certUsageSSLClient;
- break;
- case certificateUsageSSLServer:
- enumUsage = certUsageSSLServer;
- break;
- case certificateUsageSSLCA:
- enumUsage = certUsageSSLCA;
- break;
- case certificateUsageEmailSigner:
- enumUsage = certUsageEmailSigner;
- break;
- case certificateUsageEmailRecipient:
- enumUsage = certUsageEmailRecipient;
- break;
- case certificateUsageObjectSigner:
- enumUsage = certUsageObjectSigner;
- break;
- case certificateUsageVerifyCA:
- enumUsage = certUsageVerifyCA;
- break;
- case certificateUsageStatusResponder:
- enumUsage = certUsageStatusResponder;
- break;
- default:
- PR_NOT_REACHED("unexpected usage");
- PORT_SetError(SEC_ERROR_INVALID_ARGS);
- return SECFailure;
- }
+ switch (usage) {
+ case certificateUsageSSLClient:
+ enumUsage = certUsageSSLClient;
+ break;
+ case certificateUsageSSLServer:
+ enumUsage = certUsageSSLServer;
+ break;
+ case certificateUsageSSLCA:
+ enumUsage = certUsageSSLCA;
+ break;
+ case certificateUsageEmailSigner:
+ enumUsage = certUsageEmailSigner;
+ break;
+ case certificateUsageEmailRecipient:
+ enumUsage = certUsageEmailRecipient;
+ break;
+ case certificateUsageObjectSigner:
+ enumUsage = certUsageObjectSigner;
+ break;
+ case certificateUsageVerifyCA:
+ enumUsage = certUsageVerifyCA;
+ break;
+ case certificateUsageStatusResponder:
+ enumUsage = certUsageStatusResponder;
+ break;
+ default:
+ PR_NOT_REACHED("unexpected usage");
+ PORT_SetError(SEC_ERROR_INVALID_ARGS);
+ return SECFailure;
}
if (usage == certificateUsageSSLServer) {
// SSL server cert verification has always used CERT_VerifyCert, so we
@@ -168,13 +249,38 @@ ClassicVerifyCert(CERTCertificate* cert,
rv = CERT_VerifyCertificate(CERT_GetDefaultCertDB(), cert, true,
usage, time, pinArg, verifyLog, nullptr);
}
- if (rv == SECSuccess && validationChain) {
- PR_LOG(gCertVerifierLog, PR_LOG_DEBUG, ("VerifyCert: getting chain in 'classic' \n"));
- *validationChain = CERT_GetCertChainFromCert(cert, time, enumUsage);
- if (!*validationChain) {
- rv = SECFailure;
+
+ if (rv == SECSuccess &&
+ (validationChain || usage == certificateUsageSSLServer)) {
+ PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
+ ("VerifyCert: getting chain in 'classic' \n"));
+ ScopedCERTCertList certChain(CERT_GetCertChainFromCert(cert, time,
+ enumUsage));
+ if (!certChain) {
+ return SECFailure;
+ }
+ if (usage == certificateUsageSSLServer) {
+ PRBool chainOK = PR_FALSE;
+ SECStatus srv = chainValidationCallback(callbackState, certChain.get(),
+ &chainOK);
+ if (srv != SECSuccess) {
+ return srv;
+ }
+ if (chainOK != PR_TRUE) {
+ if (verifyLog) {
+ insertErrorIntoVerifyLog(cert,
+ SEC_ERROR_APPLICATION_CALLBACK_ERROR,
+ verifyLog);
+ }
+ PR_SetError(SEC_ERROR_APPLICATION_CALLBACK_ERROR, 0); // same as libpkix
+ return SECFailure;
+ }
+ }
+ if (rv == SECSuccess && validationChain) {
+ *validationChain = certChain.release();
}
}
+
return rv;
}
@@ -227,6 +333,7 @@ CertVerifier::MozillaPKIXVerifyCert(
const PRTime time,
void* pinArg,
const Flags flags,
+ ChainValidationCallbackState* callbackState,
/*optional*/ const SECItem* stapledOCSPResponse,
/*optional out*/ mozilla::pkix::ScopedCERTCertList* validationChain,
/*optional out*/ SECOidTag* evOidPolicy)
@@ -249,6 +356,10 @@ CertVerifier::MozillaPKIXVerifyCert(
return SECFailure;
}
+ CERTChainVerifyCallback callbackContainer;
+ callbackContainer.isChainValid = chainValidationCallback;
+ callbackContainer.isChainValidArg = callbackState;
+
NSSCertDBTrustDomain::OCSPFetching ocspFetching
= !mOCSPDownloadEnabled ||
(flags & FLAG_LOCAL_ONLY) ? NSSCertDBTrustDomain::NeverFetchOCSP
@@ -295,7 +406,7 @@ CertVerifier::MozillaPKIXVerifyCert(
ocspFetching == NSSCertDBTrustDomain::NeverFetchOCSP
? NSSCertDBTrustDomain::LocalOnlyOCSPForEV
: NSSCertDBTrustDomain::FetchOCSPForEV,
- mOCSPCache, pinArg);
+ mOCSPCache, pinArg, &callbackContainer);
rv = BuildCertChainForOneKeyUsage(trustDomain, cert, time,
KeyUsage::digitalSignature, // ECDHE/DHE
KeyUsage::keyEncipherment, // RSA
@@ -321,7 +432,7 @@ CertVerifier::MozillaPKIXVerifyCert(
// Now try non-EV.
NSSCertDBTrustDomain trustDomain(trustSSL, ocspFetching, mOCSPCache,
- pinArg);
+ pinArg, &callbackContainer);
rv = BuildCertChainForOneKeyUsage(trustDomain, cert, time,
KeyUsage::digitalSignature, // (EC)DHE
KeyUsage::keyEncipherment, // RSA
@@ -443,19 +554,25 @@ CertVerifier::MozillaPKIXVerifyCert(
SECStatus
CertVerifier::VerifyCert(CERTCertificate* cert,
- /*optional*/ const SECItem* stapledOCSPResponse,
const SECCertificateUsage usage,
const PRTime time,
void* pinArg,
+ const char* hostname,
const Flags flags,
+ /*optional in*/ const SECItem* stapledOCSPResponse,
/*optional out*/ ScopedCERTCertList* validationChain,
/*optional out*/ SECOidTag* evOidPolicy,
/*optional out*/ CERTVerifyLog* verifyLog)
{
+ ChainValidationCallbackState callbackState = { hostname,
+ mPinningEnforcementLevel,
+ usage,
+ time };
+
if (mImplementation == mozillapkix) {
return MozillaPKIXVerifyCert(cert, usage, time, pinArg, flags,
- stapledOCSPResponse, validationChain,
- evOidPolicy);
+ &callbackState, stapledOCSPResponse,
+ validationChain, evOidPolicy);
}
if (!cert)
@@ -581,7 +698,7 @@ CertVerifier::VerifyCert(CERTCertificate* cert,
CERTChainVerifyCallback callbackContainer;
if (usage == certificateUsageSSLServer) {
callbackContainer.isChainValid = chainValidationCallback;
- callbackContainer.isChainValidArg = nullptr;
+ callbackContainer.isChainValidArg = &callbackState;
cvin[i].type = cert_pi_chainVerifyCallback;
cvin[i].value.pointer.chainVerifyCallback = &callbackContainer;
++i;
@@ -685,8 +802,8 @@ CertVerifier::VerifyCert(CERTCertificate* cert,
if (mImplementation == classic) {
// XXX: we do not care about the localOnly flag (currently) as the
// caller that wants localOnly should disable and reenable the fetching.
- return ClassicVerifyCert(cert, usage, time, pinArg, validationChain,
- verifyLog);
+ return ClassicVerifyCert(cert, usage, time, pinArg, &callbackState,
+ validationChain, verifyLog);
}
#ifdef NSS_NO_LIBPKIX
@@ -826,9 +943,9 @@ CertVerifier::VerifySSLServerCert(CERTCertificate* peerCert,
// CreateCertErrorRunnable assumes that CERT_VerifyCertName is only called
// if VerifyCert succeeded.
ScopedCERTCertList validationChain;
- SECStatus rv = VerifyCert(peerCert, stapledOCSPResponse,
- certificateUsageSSLServer, time,
- pinarg, 0, &validationChain, evOidPolicy);
+ SECStatus rv = VerifyCert(peerCert, certificateUsageSSLServer, time, pinarg,
+ hostname, 0, stapledOCSPResponse, &validationChain,
+ evOidPolicy, nullptr);
if (rv != SECSuccess) {
return rv;
}
diff --git a/security/certverifier/CertVerifier.h b/security/certverifier/CertVerifier.h
index 09ed4b0..270e9a1 100644
--- a/security/certverifier/CertVerifier.h
+++ b/security/certverifier/CertVerifier.h
@@ -12,6 +12,8 @@
namespace mozilla { namespace psm {
+struct ChainValidationCallbackState;
+
class CertVerifier
{
public:
@@ -24,11 +26,12 @@ public:
// *evOidPolicy == SEC_OID_UNKNOWN means the cert is NOT EV
// Only one usage per verification is supported.
SECStatus VerifyCert(CERTCertificate* cert,
- /*optional*/ const SECItem* stapledOCSPResponse,
const SECCertificateUsage usage,
const PRTime time,
void* pinArg,
+ const char* hostname,
const Flags flags = 0,
+ /*optional in*/ const SECItem* stapledOCSPResponse = nullptr,
/*optional out*/ mozilla::pkix::ScopedCERTCertList* validationChain = nullptr,
/*optional out*/ SECOidTag* evOidPolicy = nullptr ,
/*optional out*/ CERTVerifyLog* verifyLog = nullptr);
@@ -52,6 +55,13 @@ public:
mozillapkix = 2
};
+ enum pinning_enforcement_config {
+ pinningDisabled = 0,
+ pinningAllowUserCAMITM = 1,
+ pinningStrict = 2,
+ pinningEnforceTestMode = 3
+ };
+
enum missing_cert_download_config { missing_cert_download_off = 0, missing_cert_download_on };
enum crl_download_config { crl_local_only = 0, crl_download_allowed };
enum ocsp_download_config { ocsp_off = 0, ocsp_on };
@@ -65,7 +75,8 @@ public:
missing_cert_download_config ac, crl_download_config cdc,
#endif
ocsp_download_config odc, ocsp_strict_config osc,
- ocsp_get_config ogc);
+ ocsp_get_config ogc,
+ pinning_enforcement_config pinningEnforcementLevel);
~CertVerifier();
void ClearOCSPCache() { mOCSPCache.Clear(); }
@@ -78,6 +89,7 @@ public:
const bool mOCSPDownloadEnabled;
const bool mOCSPStrict;
const bool mOCSPGETEnabled;
+ const pinning_enforcement_config mPinningEnforcementLevel;
private:
SECStatus MozillaPKIXVerifyCert(CERTCertificate* cert,
@@ -85,6 +97,7 @@ private:
const PRTime time,
void* pinArg,
const Flags flags,
+ ChainValidationCallbackState* callbackState,
/*optional*/ const SECItem* stapledOCSPResponse,
/*optional out*/ mozilla::pkix::ScopedCERTCertList* validationChain,
/*optional out*/ SECOidTag* evOidPolicy);
diff --git a/security/certverifier/NSSCertDBTrustDomain.cpp b/security/certverifier/NSSCertDBTrustDomain.cpp
index 9bed2ce..fd2e363 100644
--- a/security/certverifier/NSSCertDBTrustDomain.cpp
+++ b/security/certverifier/NSSCertDBTrustDomain.cpp
@@ -43,11 +43,13 @@ typedef ScopedPtr<SECMODModule, SECMOD_DestroyModule> ScopedSECMODModule;
NSSCertDBTrustDomain::NSSCertDBTrustDomain(SECTrustType certDBTrustType,
OCSPFetching ocspFetching,
OCSPCache& ocspCache,
- void* pinArg)
+ void* pinArg,
+ CERTChainVerifyCallback* checkChainCallback)
: mCertDBTrustType(certDBTrustType)
, mOCSPFetching(ocspFetching)
, mOCSPCache(ocspCache)
, mPinArg(pinArg)
+ , mCheckChainCallback(checkChainCallback)
{
}
@@ -475,6 +477,37 @@ NSSCertDBTrustDomain::VerifyAndMaybeCacheEncodedOCSPResponse(
return rv;
}
+SECStatus
+NSSCertDBTrustDomain::IsChainValid(const CERTCertList* certChain) {
+ SECStatus rv = SECFailure;
+
+ PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
+ ("NSSCertDBTrustDomain: Top of IsChainValid mCheckCallback=%p",
+ mCheckChainCallback));
+
+ if (!mCheckChainCallback) {
+ return SECSuccess;
+ }
+ if (!mCheckChainCallback->isChainValid) {
+ PR_SetError(SEC_ERROR_INVALID_ARGS, 0);
+ return SECFailure;
+ }
+ PRBool chainOK;
+ rv = (mCheckChainCallback->isChainValid)(mCheckChainCallback->isChainValidArg,
+ certChain, &chainOK);
+ if (rv != SECSuccess) {
+ return rv;
+ }
+ // rv = SECSuccess only implies successful call, now is time
+ // to check the chain check status
+ // we should only return success if the chain is valid
+ if (chainOK) {
+ return SECSuccess;
+ }
+ PR_SetError(SEC_ERROR_APPLICATION_CALLBACK_ERROR, 0);
+ return SECFailure;
+}
+
namespace {
static char*
diff --git a/security/certverifier/NSSCertDBTrustDomain.h b/security/certverifier/NSSCertDBTrustDomain.h
index 979c3e2..c2f211d 100644
--- a/security/certverifier/NSSCertDBTrustDomain.h
+++ b/security/certverifier/NSSCertDBTrustDomain.h
@@ -57,7 +57,8 @@ public:
LocalOnlyOCSPForEV = 4,
};
NSSCertDBTrustDomain(SECTrustType certDBTrustType, OCSPFetching ocspFetching,
- OCSPCache& ocspCache, void* pinArg);
+ OCSPCache& ocspCache, void* pinArg,
+ CERTChainVerifyCallback* checkChainCallback = nullptr);
virtual SECStatus FindPotentialIssuers(
const SECItem* encodedIssuerName,
@@ -78,6 +79,8 @@ public:
PRTime time,
/*optional*/ const SECItem* stapledOCSPResponse);
+ virtual SECStatus IsChainValid(const CERTCertList* certChain);
+
private:
enum EncodedResponseSource {
ResponseIsFromNetwork = 1,
@@ -93,6 +96,7 @@ private:
const OCSPFetching mOCSPFetching;
OCSPCache& mOCSPCache; // non-owning!
void* mPinArg; // non-owning!
+ CERTChainVerifyCallback* mCheckChainCallback; // non-owning!
};
} } // namespace mozilla::psm
diff --git a/security/certverifier/moz.build b/security/certverifier/moz.build
index 5e99b12..434c3cc 100644
--- a/security/certverifier/moz.build
+++ b/security/certverifier/moz.build
@@ -17,6 +17,7 @@ if not CONFIG['NSS_NO_EV_CERTS']:
]
LOCAL_INCLUDES += [
+ '../manager/boot/src',
'../manager/ssl/src',
'../pkix/include',
]
diff --git a/security/manager/boot/src/PublicKeyPinningService.cpp b/security/manager/boot/src/PublicKeyPinningService.cpp
new file mode 100644
index 0000000..82abaaf
--- /dev/null
+++ b/security/manager/boot/src/PublicKeyPinningService.cpp
@@ -0,0 +1,306 @@
+/* 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 "PublicKeyPinningService.h"
+#include "pkix/nullptr.h"
+#include "StaticHPKPins.h" // autogenerated by genHPKPStaticpins.js
+
+#include "cert.h"
+#include "mozilla/Base64.h"
+#include "mozilla/Telemetry.h"
+#include "nsString.h"
+#include "nssb64.h"
+#include "pkix/pkixtypes.h"
+#include "prlog.h"
+#include "ScopedNSSTypes.h"
+#include "seccomon.h"
+#include "sechash.h"
+
+using namespace mozilla;
+using namespace mozilla::pkix;
+using namespace mozilla::psm;
+
+#if defined(PR_LOGGING)
+PRLogModuleInfo* gPublicKeyPinningLog =
+ PR_NewLogModule("PublicKeyPinningService");
+#endif
+
+/**
+ Computes in the location specified by base64Out the SHA256 digest
+ of the DER Encoded subject Public Key Info for the given cert
+*/
+static SECStatus
+GetBase64HashSPKI(const CERTCertificate* cert, SECOidTag hashType,
+ nsACString& hashSPKIDigest)
+{
+ hashSPKIDigest.Truncate();
+ Digest digest;
+ nsresult rv = digest.DigestBuf(hashType, cert->derPublicKey.data,
+ cert->derPublicKey.len);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return SECFailure;
+ }
+ rv = Base64Encode(nsDependentCSubstring(
+ reinterpret_cast<const char*>(digest.get().data),
+ digest.get().len),
+ hashSPKIDigest);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return SECFailure;
+ }
+ return SECSuccess;
+}
+
+/*
+ * Returns true if a given cert matches any hashType fingerprints from the
+ * given pinset, false otherwise.
+ */
+static bool
+EvalCertWithHashType(const CERTCertificate* cert, SECOidTag hashType,
+ const StaticFingerprints* fingerprints)
+{
+ if (!fingerprints) {
+ PR_LOG(gPublicKeyPinningLog, PR_LOG_DEBUG,
+ ("pkpin: No hashes found for hash type: %d\n", hashType));
+ return false;
+ }
+
+ nsAutoCString base64Out;
+ SECStatus srv = GetBase64HashSPKI(cert, hashType, base64Out);
+ if (srv != SECSuccess) {
+ PR_LOG(gPublicKeyPinningLog, PR_LOG_DEBUG,
+ ("pkpin: GetBase64HashSPKI failed!\n"));
+ return false;
+ }
+
+ for (size_t i = 0; i < fingerprints->size; i++) {
+ if (base64Out.Equals(fingerprints->data[i])) {
+ PR_LOG(gPublicKeyPinningLog, PR_LOG_DEBUG,
+ ("pkpin: found pin base_64 ='%s'\n", base64Out.get()));
+ return true;
+ }
+ }
+ return false;
+}
+
+/*
+ * Returns true if a given chain matches any hashType fingerprints from the
+ * given pinset, false otherwise.
+ */
+static bool
+EvalChainWithHashType(const CERTCertList* certList, SECOidTag hashType,
+ const StaticPinset* pinset)
+{
+ CERTCertificate* currentCert;
+
+ const StaticFingerprints* fingerprints = nullptr;
+ if (hashType == SEC_OID_SHA256) {
+ fingerprints = pinset->sha256;
+ } else if (hashType == SEC_OID_SHA1) {
+ fingerprints = pinset->sha1;
+ }
+ if (!fingerprints) {
+ return false;
+ }
+
+ CERTCertListNode* node;
+ for (node = CERT_LIST_HEAD(certList); !CERT_LIST_END(node, certList);
+ node = CERT_LIST_NEXT(node)) {
+ currentCert = node->cert;
+ PR_LOG(gPublicKeyPinningLog, PR_LOG_DEBUG,
+ ("pkpin: certArray subject: '%s'\n",
+ currentCert->subjectName));
+ PR_LOG(gPublicKeyPinningLog, PR_LOG_DEBUG,
+ ("pkpin: certArray common_name: '%s'\n",
+ CERT_GetCommonName(&(currentCert->issuer))));
+ if (EvalCertWithHashType(currentCert, hashType, fingerprints)) {
+ return true;
+ }
+ }
+ PR_LOG(gPublicKeyPinningLog, PR_LOG_DEBUG, ("pkpin: no matches found\n"));
+ return false;
+}
+
+/**
+ * Given a pinset and certlist, return true if one of the certificates on
+ * the list matches a fingerprint in the pinset, false otherwise.
+ */
+static bool
+EvalChainWithPinset(const CERTCertList* certList,
+ const StaticPinset* pinset) {
+ // SHA256 is more trustworthy, try that first.
+ if (EvalChainWithHashType(certList, SEC_OID_SHA256, pinset)) {
+ return true;
+ }
+ return EvalChainWithHashType(certList, SEC_OID_SHA1, pinset);
+}
+
+/**
+ Comparator for the is public key pinned host.
+*/
+static int
+TransportSecurityPreloadCompare(const void *key, const void *entry) {
+ const char *keyStr = reinterpret_cast<const char *>(key);
+ const TransportSecurityPreload *preloadEntry =
+ reinterpret_cast<const TransportSecurityPreload *>(entry);
+
+ return strcmp(keyStr, preloadEntry->mHost);
+}
+
+/**
+ * Check PKPins on the given certlist against the specified hostname
+ */
+static bool
+CheckPinsForHostname(const CERTCertList *certList, const char *hostname,
+ bool enforceTestMode)
+{
+ if (!certList) {
+ return false;
+ }
+ if (!hostname || hostname[0] == 0) {
+ return false;
+ }
+
+ TransportSecurityPreload *foundEntry = nullptr;
+ char *evalHost = const_cast<char*>(hostname);
+ char *evalPart;
+ // Notice how the (xx = strchr) prevents pins for unqualified domain names.
+ while (!foundEntry && (evalPart = strchr(evalHost, '.'))) {
+ PR_LOG(gPublicKeyPinningLog, PR_LOG_DEBUG,
+ ("pkpin: Querying pinsets for host: '%s'\n", evalHost));
+ foundEntry = (TransportSecurityPreload *)bsearch(evalHost,
+ kPublicKeyPinningPreloadList,
+ sizeof(kPublicKeyPinningPreloadList) / sizeof(TransportSecurityPreload),
+ sizeof(TransportSecurityPreload),
+ TransportSecurityPreloadCompare);
+ if (foundEntry) {
+ PR_LOG(gPublicKeyPinningLog, PR_LOG_DEBUG,
+ ("pkpin: Found pinset for host: '%s'\n", evalHost));
+ if (evalHost != hostname) {
+ if (!foundEntry->mIncludeSubdomains) {
+ // Does not apply to this host, continue iterating
+ foundEntry = nullptr;
+ }
+ }
+ } else {
+ PR_LOG(gPublicKeyPinningLog, PR_LOG_DEBUG,
+ ("pkpin: Didn't find pinset for host: '%s'\n", evalHost));
+ }
+ // Add one for '.'
+ evalHost = evalPart + 1;
+ }
+
+ if (foundEntry && foundEntry->pinset) {
+ bool result = EvalChainWithPinset(certList, foundEntry->pinset);
+ bool retval = result;
+ Telemetry::ID histogram = foundEntry->mIsMoz
+ ? Telemetry::CERT_PINNING_MOZ_RESULTS
+ : Telemetry::CERT_PINNING_RESULTS;
+ if (foundEntry->mTestMode) {
+ histogram = foundEntry->mIsMoz
+ ? Telemetry::CERT_PINNING_MOZ_TEST_RESULTS
+ : Telemetry::CERT_PINNING_TEST_RESULTS;
+ if (!enforceTestMode) {
+ retval = true;
+ }
+ }
+ // We can collect per-host pinning violations for this host because it is
+ // operationally critical to Firefox.
+ if (foundEntry->mId != kUnknownId) {
+ int32_t bucket = foundEntry->mId * 2 + (result ? 1 : 0);
+ histogram = foundEntry->mTestMode
+ ? Telemetry::CERT_PINNING_MOZ_TEST_RESULTS_BY_HOST
+ : Telemetry::CERT_PINNING_MOZ_RESULTS_BY_HOST;
+ Telemetry::Accumulate(histogram, bucket);
+ } else {
+ Telemetry::Accumulate(histogram, result ? 1 : 0);
+ }
+ PR_LOG(gPublicKeyPinningLog, PR_LOG_DEBUG,
+ ("pkpin: Pin check %s for %s host '%s' (mode=%s)\n",
+ result ? "passed" : "failed",
+ foundEntry->mIsMoz ? "mozilla" : "non-mozilla",
+ hostname, foundEntry->mTestMode ? "test" : "production"));
+ return retval;
+ }
+ return true; // No pinning information for this hostname
+}
+
+/**
+ * Extract all the DNS names for a host (including CN) and evaluate the
+ * certifiate pins against all of them (Currently is an OR so we stop
+ * evaluating at the first OK pin).
+ */
+static bool
+CheckChainAgainstAllNames(const CERTCertList* certList, bool enforceTestMode)
+{
+ PR_LOG(gPublicKeyPinningLog, PR_LOG_DEBUG,
+ ("pkpin: top of checkChainAgainstAllNames"));
+ CERTCertListNode* node = CERT_LIST_HEAD(certList);
+ if (!node) {
+ return false;
+ }
+ CERTCertificate* cert = node->cert;
+ if (!cert) {
+ return false;
+ }
+
+ ScopedPLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
+ if (!arena) {
+ return false;
+ }
+
+ bool hasValidPins = false;
+ CERTGeneralName* nameList;
+ CERTGeneralName* currentName;
+ nameList = CERT_GetConstrainedCertificateNames(cert, arena.get(), PR_TRUE);
+ if (!nameList) {
+ return false;
+ }
+
+ currentName = nameList;
+ do {
+ if (currentName->type == certDNSName
+ && currentName->name.other.data[0] != 0) {
+ // no need to cleaup, as the arena cleanup will do
+ char *hostName = (char *)PORT_ArenaAlloc(arena.get(),
+ currentName->name.other.len + 1);
+ if (!hostName) {
+ break;
+ }
+ // We use a temporary buffer as the hostname as returned might not be
+ // null terminated.
+ hostName[currentName->name.other.len] = 0;
+ memcpy(hostName, currentName->name.other.data,
+ currentName->name.other.len);
+ if (!hostName[0]) {
+ // cannot call CheckPinsForHostname on empty or null hostname
+ break;
+ }
+ if (CheckPinsForHostname(certList, hostName, enforceTestMode)) {
+ hasValidPins = true;
+ break;
+ }
+ }
+ currentName = CERT_GetNextGeneralName(currentName);
+ } while (currentName != nameList);
+
+ return hasValidPins;
+}
+
+bool
+PublicKeyPinningService::ChainHasValidPins(const CERTCertList* certList,
+ const char* hostname,
+ const PRTime time,
+ bool enforceTestMode)
+{
+ if (!certList) {
+ return false;
+ }
+ if (time > kPreloadPKPinsExpirationTime) {
+ return true;
+ }
+ if (!hostname || hostname[0] == 0) {
+ return CheckChainAgainstAllNames(certList, enforceTestMode);
+ }
+ return CheckPinsForHostname(certList, hostname, enforceTestMode);
+}
diff --git a/security/manager/boot/src/PublicKeyPinningService.cpp.rej b/security/manager/boot/src/PublicKeyPinningService.cpp.rej
new file mode 100644
index 0000000..e88f91b
--- /dev/null
+++ b/security/manager/boot/src/PublicKeyPinningService.cpp.rej
@@ -0,0 +1,11 @@
+diff a/security/manager/boot/src/PublicKeyPinningService.cpp b/security/manager/boot/src/PublicKeyPinningService.cpp (rejected hunks)
+@@ -296,7 +296,8 @@ PublicKeyPinningService::ChainHasValidPins(const CERTCertList* certList,
+ if (!certList) {
+ return false;
+ }
+- if (time > TimeFromElapsedSecondsAD(kPreloadPKPinsExpirationTime)) {
++ if (time > TimeFromEpochInSeconds(kPreloadPKPinsExpirationTime /
++ PR_USEC_PER_SEC)) {
+ return true;
+ }
+ if (!hostname || hostname[0] == 0) {
diff --git a/security/manager/boot/src/PublicKeyPinningService.h b/security/manager/boot/src/PublicKeyPinningService.h
new file mode 100644
index 0000000..978c5ec
--- /dev/null
+++ b/security/manager/boot/src/PublicKeyPinningService.h
@@ -0,0 +1,35 @@
+/* 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 PublicKeyPinningService_h
+#define PublicKeyPinningService_h
+
+#include "cert.h"
+
+namespace mozilla {
+namespace psm {
+
+class PublicKeyPinningService
+{
+public:
+ /**
+ * Returns true if the given (host, certList) passes pinning checks,
+ * false otherwise. If the host is pinned, return true if one of the keys in
+ * the given certificate chain matches the pin set specified by the
+ * hostname. If the hostname is null or empty evaluate against all the
+ * possible names for the EE cert (Common Name (CN) plus all DNS Name:
+ * subject Alt Name entries). The certList's head is the EE cert and the
+ * tail is the trust anchor.
+ * Note: if an alt name is a wildcard, it won't necessarily find a pinset
+ * that would otherwise be valid for it
+ */
+ static bool ChainHasValidPins(const CERTCertList* certList,
+ const char* hostname,
+ const PRTime,
+ bool enforceTestMode);
+};
+
+}} // namespace mozilla::psm
+
+#endif // PublicKeyPinningServiceService_h
diff --git a/security/manager/boot/src/StaticHPKPins.h b/security/manager/boot/src/StaticHPKPins.h
new file mode 100644
index 0000000..4506489
--- /dev/null
+++ b/security/manager/boot/src/StaticHPKPins.h
@@ -0,0 +1,1095 @@
+/* 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 is an automatically generated file. If you're not */
+/* PublicKeyPinningService.cpp, you shouldn't be #including it. */
+/*****************************************************************************/
+#include <stdint.h>
+/* AddTrust External Root */
+static const char kAddTrust_External_RootFingerprint[] =
+ "lCppFqbkrlJ3EcVFAkeip0+44VaoJUymbnOaEUk7tEU=";
+
+/* AddTrust Low-Value Services Root */
+static const char kAddTrust_Low_Value_Services_RootFingerprint[] =
+ "BStocQfshOhzA4JFLsKidFF0XXSFpX1vRk4Np6G2ryo=";
+
+/* AddTrust Public Services Root */
+static const char kAddTrust_Public_Services_RootFingerprint[] =
+ "OGHXtpYfzbISBFb/b8LrdwSxp0G0vZM6g3b14ZFcppg=";
+
+/* AddTrust Qualified Certificates Root */
+static const char kAddTrust_Qualified_Certificates_RootFingerprint[] =
+ "xzr8Lrp3DQy8HuQfJStS6Kk9ErctzOwDHY2DnL+Bink=";
+
+/* AffirmTrust Commercial */
+static const char kAffirmTrust_CommercialFingerprint[] =
+ "bEZLmlsjOl6HTadlwm8EUBDS3c/0V5TwtMfkqvpQFJU=";
+
+/* AffirmTrust Networking */
+static const char kAffirmTrust_NetworkingFingerprint[] =
+ "lAcq0/WPcPkwmOWl9sBMlscQvYSdgxhJGa6Q64kK5AA=";
+
+/* AffirmTrust Premium */
+static const char kAffirmTrust_PremiumFingerprint[] =
+ "x/Q7TPW3FWgpT4IrU3YmBfbd0Vyt7Oc56eLDy6YenWc=";
+
+/* AffirmTrust Premium ECC */
+static const char kAffirmTrust_Premium_ECCFingerprint[] =
+ "MhmwkRT/SVo+tusAwu/qs0ACrl8KVsdnnqCHo/oDfk8=";
+
+/* America Online Root Certification Authority 1 */
+static const char kAmerica_Online_Root_Certification_Authority_1Fingerprint[] =
+ "I4SdCUkj1EpIgbY6sYXpvhWqyO8sMETZNLx/JuLSzWk=";
+
+/* America Online Root Certification Authority 2 */
+static const char kAmerica_Online_Root_Certification_Authority_2Fingerprint[] =
+ "/PfamDYD6IhiAw2WE32OEwMbrftNVsH9TKzDOfa9uyo=";
+
+/* Baltimore CyberTrust Root */
+static const char kBaltimore_CyberTrust_RootFingerprint[] =
+ "Y9mvm0exBk1JoQ57f9Vm28jKo5lFm/woKcVxrYxu80o=";
+
+/* COMODO Certification Authority */
+static const char kCOMODO_Certification_AuthorityFingerprint[] =
+ "AG1751Vd2CAmRCxPGieoDomhmJy4ezREjtIZTBgZbV4=";
+
+/* COMODO ECC Certification Authority */
+static const char kCOMODO_ECC_Certification_AuthorityFingerprint[] =
+ "58qRu/uxh4gFezqAcERupSkRYBlBAvfcw7mEjGPLnNU=";
+
+/* Comodo AAA Services root */
+static const char kComodo_AAA_Services_rootFingerprint[] =
+ "vRU+17BDT2iGsXvOi76E7TQMcTLXAqj0+jGPdW7L1vM=";
+
+/* Comodo Secure Services root */
+static const char kComodo_Secure_Services_rootFingerprint[] =
+ "RpHL/ehKa2BS3b4VK7DCFq4lqG5XR4E9vA8UfzOFcL4=";
+
+/* Comodo Trusted Services root */
+static const char kComodo_Trusted_Services_rootFingerprint[] =
+ "4tiR77c4ZpEF1TDeXtcuKyrD9KZweLU0mz/ayklvXrg=";
+
+/* Cybertrust Global Root */
+static const char kCybertrust_Global_RootFingerprint[] =
+ "foeCwVDOOVL4AuY2AjpdPpW7XWjjPoWtsroXgSXOvxU=";
+
+/* DigiCert Assured ID Root CA */
+static const char kDigiCert_Assured_ID_Root_CAFingerprint[] =
+ "I/Lt/z7ekCWanjD0Cvj5EqXls2lOaThEA0H2Bg4BT/o=";
+
+/* DigiCert ECC Secure Server CA */
+static const char kDigiCert_ECC_Secure_Server_CAFingerprint[] =
+ "PZXN3lRAy+8tBKk2Ox6F7jIlnzr2Yzmwqc3JnyfXoCw=";
+
+/* DigiCert Global Root CA */
+static const char kDigiCert_Global_Root_CAFingerprint[] =
+ "r/mIkG3eEpVdm+u/ko/cwxzOMo1bk4TyHIlByibiA5E=";
+
+/* DigiCert High Assurance EV Root CA */
+static const char kDigiCert_High_Assurance_EV_Root_CAFingerprint[] =
+ "WoiWRyIOVNa9ihaBciRSC7XHjliYS9VwUGOIud4PB18=";
+
+/* End Entity Test Cert */
+static const char kEnd_Entity_Test_CertFingerprint[] =
+ "pVVgLk2kFI2WWRPwDMIX6YmzFhEW4DXQV/U5gP+feGA=";
+
+/* Entrust Root Certification Authority */
+static const char kEntrust_Root_Certification_AuthorityFingerprint[] =
+ "bb+uANN7nNc/j7R95lkXrwDg3d9C286sIMF8AnXuIJU=";
+
+/* Entrust.net Premium 2048 Secure Server CA */
+static const char kEntrust_net_Premium_2048_Secure_Server_CAFingerprint[] =
+ "HqPF5D7WbC2imDpCpKebHpBnhs6fG1hiFBmgBGOofTg=";
+
+/* Equifax Secure CA */
+static const char kEquifax_Secure_CAFingerprint[] =
+ "/1aAzXOlcD2gSBegdf1GJQanNQbEuBoVg+9UlHjSZHY=";
+
+/* Equifax Secure Global eBusiness CA */
+static const char kEquifax_Secure_Global_eBusiness_CAFingerprint[] =
+ "pvH5v4oKndwID7SbHvw9GhwsMtwOE2pbAMlzFvKj3BE=";
+
+/* Equifax Secure eBusiness CA 1 */
+static const char kEquifax_Secure_eBusiness_CA_1Fingerprint[] =
+ "JsGNxu6m9jL2drzrodjCtINS8pwtX82oeOCdy4Mt1uU=";
+
+/* GOOGLE_PIN_AlphaSSL_G2 */
+static const char kGOOGLE_PIN_AlphaSSL_G2Fingerprint[] =
+ "yxgiWGK++SFB9ySwt3M3qpn5HO0ZLFY5D+h+G/vcT/c=";
+
+/* GOOGLE_PIN_CryptoCat1 */
+static const char kGOOGLE_PIN_CryptoCat1Fingerprint[] =
+ "vKaqtTLWmVuXPVJE+0OqN5sRc4VCcSQHI/W3XTDVR24=";
+
+/* GOOGLE_PIN_EntrustRootEC1 */
+static const char kGOOGLE_PIN_EntrustRootEC1Fingerprint[] =
+ "/qK31kX7pz11PB7Jp4cMQOH3sMVh6Se5hb9xGGbjbyI=";
+
+/* GOOGLE_PIN_Entrust_G2 */
+static const char kGOOGLE_PIN_Entrust_G2Fingerprint[] =
+ "du6FkDdMcVQ3u8prumAo6t3i3G27uMP2EOhR8R0at/U=";
+
+/* GOOGLE_PIN_Entrust_SSL */
+static const char kGOOGLE_PIN_Entrust_SSLFingerprint[] =
+ "nsxRNo6G40YPZsKV5JQt1TCA8nseQQr/LRqp1Oa8fnw=";
+
+/* GOOGLE_PIN_GoDaddySecure */
+static const char kGOOGLE_PIN_GoDaddySecureFingerprint[] =
+ "MrZLZnJ6IGPkBm87lYywqu5Xal7O/ZUzmbuIdHMdlYc=";
+
+/* GOOGLE_PIN_Libertylavabitcom */
+static const char kGOOGLE_PIN_LibertylavabitcomFingerprint[] =
+ "WnKzsDXgqPtS1KvtImrhQPqcxfpmfssuI2cSJt4LMks=";
+
+/* GOOGLE_PIN_RapidSSL */
+static const char kGOOGLE_PIN_RapidSSLFingerprint[] =
+ "lT09gPUeQfbYrlxRtpsHrjDblj9Rpz+u7ajfCrg4qDM=";
+
+/* GOOGLE_PIN_Tor2web */
+static const char kGOOGLE_PIN_Tor2webFingerprint[] =
+ "99ogQzjMuUTBkG1ZP7FME0K4kvBEti8Buzu4nZjRItM=";
+
+/* GTE CyberTrust Global Root */
+static const char kGTE_CyberTrust_Global_RootFingerprint[] =
+ "EGn6R6CqT4z3ERscrqNl7q7RC//zJmDe9uBhS/rnCHU=";
+
+/* GeoTrust Global CA */
+static const char kGeoTrust_Global_CAFingerprint[] =
+ "h6801m+z8v3zbgkRHpq6L29Esgfzhj89C1SyUCOQmqU=";
+
+/* GeoTrust Global CA 2 */
+static const char kGeoTrust_Global_CA_2Fingerprint[] =
+ "F3VaXClfPS1y5vAxofB/QAxYi55YKyLxfq4xoVkNEYU=";
+
+/* GeoTrust Primary Certification Authority */
+static const char kGeoTrust_Primary_Certification_AuthorityFingerprint[] =
+ "SQVGZiOrQXi+kqxcvWWE96HhfydlLVqFr4lQTqI5qqo=";
+
+/* GeoTrust Primary Certification Authority - G2 */
+static const char kGeoTrust_Primary_Certification_Authority___G2Fingerprint[] =
+ "vPtEqrmtAhAVcGtBIep2HIHJ6IlnWQ9vlK50TciLePs=";
+
+/* GeoTrust Primary Certification Authority - G3 */
+static const char kGeoTrust_Primary_Certification_Authority___G3Fingerprint[] =
+ "q5hJUnat8eyv8o81xTBIeB5cFxjaucjmelBPT2pRMo8=";
+
+/* GeoTrust Universal CA */
+static const char kGeoTrust_Universal_CAFingerprint[] =
+ "lpkiXF3lLlbN0y3y6W0c/qWqPKC7Us2JM8I7XCdEOCA=";
+
+/* GeoTrust Universal CA 2 */
+static const char kGeoTrust_Universal_CA_2Fingerprint[] =
+ "fKoDRlEkWQxgHlZ+UhSOlSwM/+iQAFMP4NlbbVDqrkE=";
+
+/* GlobalSign Root CA */
+static const char kGlobalSign_Root_CAFingerprint[] =
+ "K87oWBWM9UZfyddvDfoxL+8lpNyoUB2ptGtn0fv6G2Q=";
+
+/* GlobalSign Root CA - R2 */
+static const char kGlobalSign_Root_CA___R2Fingerprint[] =
+ "iie1VXtL7HzAMF+/PVPR9xzT80kQxdZeJ+zduCB3uj0=";
+
+/* GlobalSign Root CA - R3 */
+static const char kGlobalSign_Root_CA___R3Fingerprint[] =
+ "cGuxAXyFXFkWm61cF4HPWX8S0srS9j0aSqN0k4AP+4A=";
+
+/* Go Daddy Class 2 CA */
+static const char kGo_Daddy_Class_2_CAFingerprint[] =
+ "VjLZe/p3W/PJnd6lL8JVNBCGQBZynFLdZSTIqcO0SJ8=";
+
+/* Go Daddy Root Certificate Authority - G2 */
+static const char kGo_Daddy_Root_Certificate_Authority___G2Fingerprint[] =
+ "Ko8tivDrEjiY90yGasP6ZpBU4jwXvHqVvQI0GS3GNdA=";
+
+/* GoogleBackup2048 */
+static const char kGoogleBackup2048Fingerprint[] =
+ "vq7OyjSnqOco9nyMCDGdy77eijM=";
+
+/* GoogleG2 */
+static const char kGoogleG2Fingerprint[] =
+ "Q9rWMO5T+KmAym79hfRqo3mQ4Oo=";
+
+/* Network Solutions Certificate Authority */
+static const char kNetwork_Solutions_Certificate_AuthorityFingerprint[] =
+ "MtGA7THJNVieydu7ciEjuIO1/C3BD5/KOpXXfhv8tTQ=";
+
+/* Starfield Class 2 CA */
+static const char kStarfield_Class_2_CAFingerprint[] =
+ "FfFKxFycfaIz00eRZOgTf+Ne4POK6FgYPwhBDqgqxLQ=";
+
+/* Starfield Root Certificate Authority - G2 */
+static const char kStarfield_Root_Certificate_Authority___G2Fingerprint[] =
+ "gI1os/q0iEpflxrOfRBVDXqVoWN3Tz7Dav/7IT++THQ=";
+
+/* Starfield Services Root Certificate Authority - G2 */
+static const char kStarfield_Services_Root_Certificate_Authority___G2Fingerprint[] =
+ "KwccWaCgrnaw6tsrrSO61FgLacNgG2MMLq8GE6+oP5I=";
+
+/* StartCom Certification Authority */
+static const char kStartCom_Certification_AuthorityFingerprint[] =
+ "5C8kvU039KouVrl52D0eZSGf4Onjo4Khs8tmyTlV3nU=";
+
+/* StartCom Certification Authority G2 */
+static const char kStartCom_Certification_Authority_G2Fingerprint[] =
+ "FSg5faISiQqDCwuVpZlozvI0dzd531GBzxD6ZHU0u2U=";
+
+/* TC TrustCenter Class 2 CA II */
+static const char kTC_TrustCenter_Class_2_CA_IIFingerprint[] =
+ "rPZeHWLLWKK6/W/6tA+4hpnEc5fPXLSD1C1pytNM1Is=";
+
+/* TC TrustCenter Class 3 CA II */
+static const char kTC_TrustCenter_Class_3_CA_IIFingerprint[] =
+ "k5KuIUmSSt435kXbof9L3dzaKykbYJdmnSr6XHo3Jhk=";
+
+/* TC TrustCenter Universal CA I */
+static const char kTC_TrustCenter_Universal_CA_IFingerprint[] =
+ "st71NirT+s0EvSkEekOET3ZwNOpIkvgOVr7mkCQ+JQI=";
+
+/* TC TrustCenter Universal CA III */
+static const char kTC_TrustCenter_Universal_CA_IIIFingerprint[] =
+ "q1zbM1Y5c1bW5pGXPCW4YYtl12qQSG6nqKXBd2f0Zzo=";
+
+/* TestSPKI */
+static const char kTestSPKIFingerprint[] =
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAA=";
+
+/* Thawte Premium Server CA */
+static const char kThawte_Premium_Server_CAFingerprint[] =
+ "9TwiBZgX3Zb0AGUWOdL4V+IQcKWavtkHlADZ9pVQaQA=";
+
+/* Thawte Server CA */
+static const char kThawte_Server_CAFingerprint[] =
+ "nG9qEjy6pO402+zu4kyX1ziHjLQj88InOQNCT10fbdU=";
+
+/* Tor1 */
+static const char kTor1Fingerprint[] =
+ "juNxSTv9UANmpC9kF5GKpmWNx3Y=";
+
+/* Tor2 */
+static const char kTor2Fingerprint[] =
+ "lia43lPolzSPVIq34Dw57uYcLD8=";
+
+/* Tor3 */
+static const char kTor3Fingerprint[] =
+ "rzEyQIKOh77j87n5bjWUNguXF8Y=";
+
+/* Twitter1 */
+static const char kTwitter1Fingerprint[] =
+ "Vv7zwhR9TtOIN/29MFI4cgHld40=";
+
+/* UTN DATACorp SGC Root CA */
+static const char kUTN_DATACorp_SGC_Root_CAFingerprint[] =
+ "QAL80xHQczFWfnG82XHkYEjI3OjRZZcRdTs9qiommvo=";
+
+/* UTN USERFirst Email Root CA */
+static const char kUTN_USERFirst_Email_Root_CAFingerprint[] =
+ "Laj56jRU0hFGRko/nQKNxMf7tXscUsc8KwVyovWZotM=";
+
+/* UTN USERFirst Hardware Root CA */
+static const char kUTN_USERFirst_Hardware_Root_CAFingerprint[] =
+ "TUDnr0MEoJ3of7+YliBMBVFB4/gJsv5zO7IxD9+YoWI=";
+
+/* UTN USERFirst Object Root CA */
+static const char kUTN_USERFirst_Object_Root_CAFingerprint[] =
+ "D+FMJksXu28NZT56cOs2Pb9UvhWAOe3a5cJXEd9IwQM=";
+
+/* VeriSign Class 3 Public Primary Certification Authority - G4 */
+static const char kVeriSign_Class_3_Public_Primary_Certification_Authority___G4Fingerprint[] =
+ "UZJDjsNp1+4M5x9cbbdflB779y5YRBcV6Z6rBMLIrO4=";
+
+/* VeriSign Class 3 Public Primary Certification Authority - G5 */
+static const char kVeriSign_Class_3_Public_Primary_Certification_Authority___G5Fingerprint[] =
+ "JbQbUG5JMJUoI6brnx0x3vZF6jilxsapbXGVfjhN8Fg=";
+
+/* VeriSign Universal Root Certification Authority */
+static const char kVeriSign_Universal_Root_Certification_AuthorityFingerprint[] =
+ "lnsM2T/O9/J84sJFdnrpsFp3awZJ+ZZbYpCWhGloaHI=";
+
+/* Verisign Class 1 Public Primary Certification Authority */
+static const char kVerisign_Class_1_Public_Primary_Certification_AuthorityFingerprint[] =
+ "LclHC+Y+9KzxvYKGCUArt7h72ZY4pkOTTohoLRvowwg=";
+
+/* Verisign Class 1 Public Primary Certification Authority - G3 */
+static const char kVerisign_Class_1_Public_Primary_Certification_Authority___G3Fingerprint[] =
+ "IgduWu9Eu5pBaii30cRDItcFn2D+/6XK9sW+hEeJEwM=";
+
+/* Verisign Class 2 Public Primary Certification Authority - G2 */
+static const char kVerisign_Class_2_Public_Primary_Certification_Authority___G2Fingerprint[] =
+ "2oALgLKofTmeZvoZ1y/fSZg7R9jPMix8eVA6DH4o/q8=";
+
+/* Verisign Class 2 Public Primary Certification Authority - G3 */
+static const char kVerisign_Class_2_Public_Primary_Certification_Authority___G3Fingerprint[] =
+ "cAajgxHlj7GTSEIzIYIQxmEloOSoJq7VOaxWHfv72QM=";
+
+/* Verisign Class 3 Public Primary Certification Authority */
+static const char kVerisign_Class_3_Public_Primary_Certification_AuthorityFingerprint[] =
+ "sRJBQqWhpaKIGcc1NA7/jJ4vgWj+47oYfyU7waOS1+I=";
+
+/* Verisign Class 3 Public Primary Certification Authority - G2 */
+static const char kVerisign_Class_3_Public_Primary_Certification_Authority___G2Fingerprint[] =
+ "AjyBzOjnxk+pQtPBUEhwfTXZu1uH9PVExb8bxWQ68vo=";
+
+/* Verisign Class 3 Public Primary Certification Authority - G3 */
+static const char kVerisign_Class_3_Public_Primary_Certification_Authority___G3Fingerprint[] =
+ "SVqWumuteCQHvVIaALrOZXuzVVVeS7f4FGxxu6V+es4=";
+
+/* Verisign Class 4 Public Primary Certification Authority - G3 */
+static const char kVerisign_Class_4_Public_Primary_Certification_Authority___G3Fingerprint[] =
+ "VnuCEf0g09KD7gzXzgZyy52ZvFtIeljJ1U7Gf3fUqPU=";
+
+/* XRamp Global CA Root */
+static const char kXRamp_Global_CA_RootFingerprint[] =
+ "BRz5+pXkDpuD7a7aaWH2Fox4ecRmAXJHnN1RqwPOpis=";
+
+/* thawte Primary Root CA */
+static const char kthawte_Primary_Root_CAFingerprint[] =
+ "HXXQgxueCIU5TTLHob/bPbwcKOKw6DkfsTWYHbxbqTY=";
+
+/* thawte Primary Root CA - G2 */
+static const char kthawte_Primary_Root_CA___G2Fingerprint[] =
+ "Z9xPMvoQ59AaeaBzqgyeAhLsL/w9d54Kp/nA8OHCyJM=";
+
+/* thawte Primary Root CA - G3 */
+static const char kthawte_Primary_Root_CA___G3Fingerprint[] =
+ "GQbGEk27Q4V40A4GbVBUxsN/D6YCjAVUXgmU7drshik=";
+
+/* Pinsets are each an ordered list by the actual value of the fingerprint */
+struct StaticFingerprints {
+ const size_t size;
+ const char* const* data;
+};
+
+struct StaticPinset {
+ const StaticFingerprints* sha1;
+ const StaticFingerprints* sha256;
+};
+
+/* PreloadedHPKPins.json pinsets */
+static const char* kPinset_facebook_sha256_Data[] = {
+ kDigiCert_ECC_Secure_Server_CAFingerprint,
+ kVerisign_Class_3_Public_Primary_Certification_Authority___G3Fingerprint,
+ kDigiCert_High_Assurance_EV_Root_CAFingerprint,
+};
+static const StaticFingerprints kPinset_facebook_sha256 = {
+ sizeof(kPinset_facebook_sha256_Data) / sizeof(const char*),
+ kPinset_facebook_sha256_Data
+};
+
+static const StaticPinset kPinset_facebook = {
+ nullptr,
+ &kPinset_facebook_sha256
+};
+
+static const char* kPinset_google_root_pems_sha256_Data[] = {
+ kEquifax_Secure_CAFingerprint,
+ kAmerica_Online_Root_Certification_Authority_2Fingerprint,
+ kComodo_Trusted_Services_rootFingerprint,
+ kCOMODO_ECC_Certification_AuthorityFingerprint,
+ kStartCom_Certification_AuthorityFingerprint,
+ kStartCom_Certification_AuthorityFingerprint,
+ kThawte_Premium_Server_CAFingerprint,
+ kCOMODO_Certification_AuthorityFingerprint,
+ kVerisign_Class_3_Public_Primary_Certification_Authority___G2Fingerprint,
+ kXRamp_Global_CA_RootFingerprint,
+ kAddTrust_Low_Value_Services_RootFingerprint,
+ kGeoTrust_Global_CA_2Fingerprint,
+ kStartCom_Certification_Authority_G2Fingerprint,
+ kStarfield_Class_2_CAFingerprint,
+ kthawte_Primary_Root_CA___G3Fingerprint,
+ kthawte_Primary_Root_CAFingerprint,
+ kEntrust_net_Premium_2048_Secure_Server_CAFingerprint,
+ kDigiCert_Assured_ID_Root_CAFingerprint,
+ kAmerica_Online_Root_Certification_Authority_1Fingerprint,
+ kVeriSign_Class_3_Public_Primary_Certification_Authority___G5Fingerprint,
+ kEquifax_Secure_eBusiness_CA_1Fingerprint,
+ kGlobalSign_Root_CAFingerprint,
+ kGo_Daddy_Root_Certificate_Authority___G2Fingerprint,
+ kStarfield_Services_Root_Certificate_Authority___G2Fingerprint,
+ kAffirmTrust_Premium_ECCFingerprint,
+ kNetwork_Solutions_Certificate_AuthorityFingerprint,
+ kAddTrust_Public_Services_RootFingerprint,
+ kUTN_DATACorp_SGC_Root_CAFingerprint,
+ kComodo_Secure_Services_rootFingerprint,
+ kGeoTrust_Primary_Certification_AuthorityFingerprint,
+ kVerisign_Class_3_Public_Primary_Certification_Authority___G3Fingerprint,
+ kUTN_USERFirst_Hardware_Root_CAFingerprint,
+ kVeriSign_Class_3_Public_Primary_Certification_Authority___G4Fingerprint,
+ kGo_Daddy_Class_2_CAFingerprint,
+ kVerisign_Class_4_Public_Primary_Certification_Authority___G3Fingerprint,
+ kDigiCert_High_Assurance_EV_Root_CAFingerprint,
+ kBaltimore_CyberTrust_RootFingerprint,
+ kthawte_Primary_Root_CA___G2Fingerprint,
+ kAffirmTrust_CommercialFingerprint,
+ kEntrust_Root_Certification_AuthorityFingerprint,
+ kGlobalSign_Root_CA___R3Fingerprint,
+ kGeoTrust_Universal_CA_2Fingerprint,
+ kCybertrust_Global_RootFingerprint,
+ kStarfield_Root_Certificate_Authority___G2Fingerprint,
+ kGeoTrust_Global_CAFingerprint,
+ kGlobalSign_Root_CA___R2Fingerprint,
+ kTC_TrustCenter_Class_3_CA_IIFingerprint,
+ kAffirmTrust_NetworkingFingerprint,
+ kAddTrust_External_RootFingerprint,
+ kVeriSign_Universal_Root_Certification_AuthorityFingerprint,
+ kGeoTrust_Universal_CAFingerprint,
+ kThawte_Server_CAFingerprint,
+ kEquifax_Secure_Global_eBusiness_CAFingerprint,
+ kTC_TrustCenter_Universal_CA_IIIFingerprint,
+ kGeoTrust_Primary_Certification_Authority___G3Fingerprint,
+ kDigiCert_Global_Root_CAFingerprint,
+ kTC_TrustCenter_Class_2_CA_IIFingerprint,
+ kVerisign_Class_3_Public_Primary_Certification_AuthorityFingerprint,
+ kVerisign_Class_3_Public_Primary_Certification_AuthorityFingerprint,
+ kTC_TrustCenter_Universal_CA_IFingerprint,
+ kGeoTrust_Primary_Certification_Authority___G2Fingerprint,
+ kComodo_AAA_Services_rootFingerprint,
+ kAffirmTrust_PremiumFingerprint,
+ kAddTrust_Qualified_Certificates_RootFingerprint,
+};
+static const StaticFingerprints kPinset_google_root_pems_sha256 = {
+ sizeof(kPinset_google_root_pems_sha256_Data) / sizeof(const char*),
+ kPinset_google_root_pems_sha256_Data
+};
+
+static const StaticPinset kPinset_google_root_pems = {
+ nullptr,
+ &kPinset_google_root_pems_sha256
+};
+
+static const char* kPinset_mozilla_sha256_Data[] = {
+ kGeoTrust_Global_CA_2Fingerprint,
+ kthawte_Primary_Root_CA___G3Fingerprint,
+ kthawte_Primary_Root_CAFingerprint,
+ kDigiCert_Assured_ID_Root_CAFingerprint,
+ kVerisign_Class_1_Public_Primary_Certification_Authority___G3Fingerprint,
+ kVeriSign_Class_3_Public_Primary_Certification_Authority___G5Fingerprint,
+ kGeoTrust_Primary_Certification_AuthorityFingerprint,
+ kVerisign_Class_3_Public_Primary_Certification_Authority___G3Fingerprint,
+ kVeriSign_Class_3_Public_Primary_Certification_Authority___G4Fingerprint,
+ kVerisign_Class_4_Public_Primary_Certification_Authority___G3Fingerprint,
+ kDigiCert_High_Assurance_EV_Root_CAFingerprint,
+ kBaltimore_CyberTrust_RootFingerprint,
+ kthawte_Primary_Root_CA___G2Fingerprint,
+ kVerisign_Class_2_Public_Primary_Certification_Authority___G3Fingerprint,
+ kGeoTrust_Universal_CA_2Fingerprint,
+ kGeoTrust_Global_CAFingerprint,
+ kVeriSign_Universal_Root_Certification_AuthorityFingerprint,
+ kGeoTrust_Universal_CAFingerprint,
+ kGeoTrust_Primary_Certification_Authority___G3Fingerprint,
+ kDigiCert_Global_Root_CAFingerprint,
+ kGeoTrust_Primary_Certification_Authority___G2Fingerprint,
+};
+static const StaticFingerprints kPinset_mozilla_sha256 = {
+ sizeof(kPinset_mozilla_sha256_Data) / sizeof(const char*),
+ kPinset_mozilla_sha256_Data
+};
+
+static const StaticPinset kPinset_mozilla = {
+ nullptr,
+ &kPinset_mozilla_sha256
+};
+
+static const char* kPinset_mozilla_services_sha256_Data[] = {
+ kDigiCert_Global_Root_CAFingerprint,
+};
+static const StaticFingerprints kPinset_mozilla_services_sha256 = {
+ sizeof(kPinset_mozilla_services_sha256_Data) / sizeof(const char*),
+ kPinset_mozilla_services_sha256_Data
+};
+
+static const StaticPinset kPinset_mozilla_services = {
+ nullptr,
+ &kPinset_mozilla_services_sha256
+};
+
+static const char* kPinset_mozilla_test_sha256_Data[] = {
+ kEnd_Entity_Test_CertFingerprint,
+};
+static const StaticFingerprints kPinset_mozilla_test_sha256 = {
+ sizeof(kPinset_mozilla_test_sha256_Data) / sizeof(const char*),
+ kPinset_mozilla_test_sha256_Data
+};
+
+static const StaticPinset kPinset_mozilla_test = {
+ nullptr,
+ &kPinset_mozilla_test_sha256
+};
+
+/* Chrome static pinsets */
+static const char* kPinset_test_sha1_Data[] = {
+ kTestSPKIFingerprint,
+};
+static const StaticFingerprints kPinset_test_sha1 = {
+ sizeof(kPinset_test_sha1_Data) / sizeof(const char*),
+ kPinset_test_sha1_Data
+};
+
+static const StaticPinset kPinset_test = {
+ &kPinset_test_sha1,
+ nullptr
+};
+
+static const char* kPinset_google_sha1_Data[] = {
+ kGoogleG2Fingerprint,
+ kGoogleBackup2048Fingerprint,
+};
+static const StaticFingerprints kPinset_google_sha1 = {
+ sizeof(kPinset_google_sha1_Data) / sizeof(const char*),
+ kPinset_google_sha1_Data
+};
+
+static const StaticPinset kPinset_google = {
+ &kPinset_google_sha1,
+ nullptr
+};
+
+static const char* kPinset_tor_sha1_Data[] = {
+ kTor1Fingerprint,
+ kTor2Fingerprint,
+ kTor3Fingerprint,
+};
+static const StaticFingerprints kPinset_tor_sha1 = {
+ sizeof(kPinset_tor_sha1_Data) / sizeof(const char*),
+ kPinset_tor_sha1_Data
+};
+
+static const char* kPinset_tor_sha256_Data[] = {
+ kDigiCert_High_Assurance_EV_Root_CAFingerprint,
+ kGOOGLE_PIN_RapidSSLFingerprint,
+};
+static const StaticFingerprints kPinset_tor_sha256 = {
+ sizeof(kPinset_tor_sha256_Data) / sizeof(const char*),
+ kPinset_tor_sha256_Data
+};
+
+static const StaticPinset kPinset_tor = {
+ &kPinset_tor_sha1,
+ &kPinset_tor_sha256
+};
+
+static const char* kPinset_twitterCom_sha1_Data[] = {
+ kTwitter1Fingerprint,
+};
+static const StaticFingerprints kPinset_twitterCom_sha1 = {
+ sizeof(kPinset_twitterCom_sha1_Data) / sizeof(const char*),
+ kPinset_twitterCom_sha1_Data
+};
+
+static const char* kPinset_twitterCom_sha256_Data[] = {
+ kVerisign_Class_2_Public_Primary_Certification_Authority___G2Fingerprint,
+ kVerisign_Class_3_Public_Primary_Certification_Authority___G2Fingerprint,
+ kGeoTrust_Global_CA_2Fingerprint,
+ kDigiCert_Assured_ID_Root_CAFingerprint,
+ kVerisign_Class_1_Public_Primary_Certification_Authority___G3Fingerprint,
+ kVeriSign_Class_3_Public_Primary_Certification_Authority___G5Fingerprint,
+ kVerisign_Class_1_Public_Primary_Certification_AuthorityFingerprint,
+ kGeoTrust_Primary_Certification_AuthorityFingerprint,
+ kVerisign_Class_3_Public_Primary_Certification_Authority___G3Fingerprint,
+ kVeriSign_Class_3_Public_Primary_Certification_Authority___G4Fingerprint,
+ kVerisign_Class_4_Public_Primary_Certification_Authority___G3Fingerprint,
+ kDigiCert_High_Assurance_EV_Root_CAFingerprint,
+ kVerisign_Class_2_Public_Primary_Certification_Authority___G3Fingerprint,
+ kGeoTrust_Universal_CA_2Fingerprint,
+ kGeoTrust_Global_CAFingerprint,
+ kVeriSign_Universal_Root_Certification_AuthorityFingerprint,
+ kGeoTrust_Universal_CAFingerprint,
+ kGeoTrust_Primary_Certification_Authority___G3Fingerprint,
+ kDigiCert_Global_Root_CAFingerprint,
+ kVerisign_Class_3_Public_Primary_Certification_AuthorityFingerprint,
+ kGeoTrust_Primary_Certification_Authority___G2Fingerprint,
+};
+static const StaticFingerprints kPinset_twitterCom_sha256 = {
+ sizeof(kPinset_twitterCom_sha256_Data) / sizeof(const char*),
+ kPinset_twitterCom_sha256_Data
+};
+
+static const StaticPinset kPinset_twitterCom = {
+ &kPinset_twitterCom_sha1,
+ &kPinset_twitterCom_sha256
+};
+
+static const char* kPinset_twitterCDN_sha1_Data[] = {
+ kTwitter1Fingerprint,
+};
+static const StaticFingerprints kPinset_twitterCDN_sha1 = {
+ sizeof(kPinset_twitterCDN_sha1_Data) / sizeof(const char*),
+ kPinset_twitterCDN_sha1_Data
+};
+
+static const char* kPinset_twitterCDN_sha256_Data[] = {
+ kVerisign_Class_2_Public_Primary_Certification_Authority___G2Fingerprint,
+ kComodo_Trusted_Services_rootFingerprint,
+ kCOMODO_Certification_AuthorityFingerprint,
+ kVerisign_Class_3_Public_Primary_Certification_Authority___G2Fingerprint,
+ kAddTrust_Low_Value_Services_RootFingerprint,
+ kUTN_USERFirst_Object_Root_CAFingerprint,
+ kGTE_CyberTrust_Global_RootFingerprint,
+ kGeoTrust_Global_CA_2Fingerprint,
+ kEntrust_net_Premium_2048_Secure_Server_CAFingerprint,
+ kDigiCert_Assured_ID_Root_CAFingerprint,
+ kVerisign_Class_1_Public_Primary_Certification_Authority___G3Fingerprint,
+ kVeriSign_Class_3_Public_Primary_Certification_Authority___G5Fingerprint,
+ kGlobalSign_Root_CAFingerprint,
+ kUTN_USERFirst_Email_Root_CAFingerprint,
+ kVerisign_Class_1_Public_Primary_Certification_AuthorityFingerprint,
+ kAddTrust_Public_Services_RootFingerprint,
+ kUTN_DATACorp_SGC_Root_CAFingerprint,
+ kComodo_Secure_Services_rootFingerprint,
+ kGeoTrust_Primary_Certification_AuthorityFingerprint,
+ kVerisign_Class_3_Public_Primary_Certification_Authority___G3Fingerprint,
+ kUTN_USERFirst_Hardware_Root_CAFingerprint,
+ kVeriSign_Class_3_Public_Primary_Certification_Authority___G4Fingerprint,
+ kVerisign_Class_4_Public_Primary_Certification_Authority___G3Fingerprint,
+ kDigiCert_High_Assurance_EV_Root_CAFingerprint,
+ kBaltimore_CyberTrust_RootFingerprint,
+ kEntrust_Root_Certification_AuthorityFingerprint,
+ kVerisign_Class_2_Public_Primary_Certification_Authority___G3Fingerprint,
+ kGlobalSign_Root_CA___R3Fingerprint,
+ kGOOGLE_PIN_Entrust_G2Fingerprint,
+ kGeoTrust_Universal_CA_2Fingerprint,
+ kGeoTrust_Global_CAFingerprint,
+ kGlobalSign_Root_CA___R2Fingerprint,
+ kAddTrust_External_RootFingerprint,
+ kVeriSign_Universal_Root_Certification_AuthorityFingerprint,
+ kGeoTrust_Universal_CAFingerprint,
+ kGOOGLE_PIN_Entrust_SSLFingerprint,
+ kGeoTrust_Primary_Certification_Authority___G3Fingerprint,
+ kDigiCert_Global_Root_CAFingerprint,
+ kVerisign_Class_3_Public_Primary_Certification_AuthorityFingerprint,
+ kGeoTrust_Primary_Certification_Authority___G2Fingerprint,
+ kComodo_AAA_Services_rootFingerprint,
+ kAddTrust_Qualified_Certificates_RootFingerprint,
+};
+static const StaticFingerprints kPinset_twitterCDN_sha256 = {
+ sizeof(kPinset_twitterCDN_sha256_Data) / sizeof(const char*),
+ kPinset_twitterCDN_sha256_Data
+};
+
+static const StaticPinset kPinset_twitterCDN = {
+ &kPinset_twitterCDN_sha1,
+ &kPinset_twitterCDN_sha256
+};
+
+static const char* kPinset_tor2web_sha256_Data[] = {
+ kGOOGLE_PIN_Tor2webFingerprint,
+ kGOOGLE_PIN_AlphaSSL_G2Fingerprint,
+};
+static const StaticFingerprints kPinset_tor2web_sha256 = {
+ sizeof(kPinset_tor2web_sha256_Data) / sizeof(const char*),
+ kPinset_tor2web_sha256_Data
+};
+
+static const StaticPinset kPinset_tor2web = {
+ nullptr,
+ &kPinset_tor2web_sha256
+};
+
+static const char* kPinset_cryptoCat_sha256_Data[] = {
+ kDigiCert_High_Assurance_EV_Root_CAFingerprint,
+ kGOOGLE_PIN_CryptoCat1Fingerprint,
+};
+static const StaticFingerprints kPinset_cryptoCat_sha256 = {
+ sizeof(kPinset_cryptoCat_sha256_Data) / sizeof(const char*),
+ kPinset_cryptoCat_sha256_Data
+};
+
+static const StaticPinset kPinset_cryptoCat = {
+ nullptr,
+ &kPinset_cryptoCat_sha256
+};
+
+static const char* kPinset_lavabit_sha256_Data[] = {
+ kGOOGLE_PIN_LibertylavabitcomFingerprint,
+};
+static const StaticFingerprints kPinset_lavabit_sha256 = {
+ sizeof(kPinset_lavabit_sha256_Data) / sizeof(const char*),
+ kPinset_lavabit_sha256_Data
+};
+
+static const StaticPinset kPinset_lavabit = {
+ nullptr,
+ &kPinset_lavabit_sha256
+};
+
+static const char* kPinset_dropbox_sha256_Data[] = {
+ kGOOGLE_PIN_EntrustRootEC1Fingerprint,
+ kThawte_Premium_Server_CAFingerprint,
+ kthawte_Primary_Root_CA___G3Fingerprint,
+ kthawte_Primary_Root_CAFingerprint,
+ kEntrust_net_Premium_2048_Secure_Server_CAFingerprint,
+ kDigiCert_Assured_ID_Root_CAFingerprint,
+ kGo_Daddy_Root_Certificate_Authority___G2Fingerprint,
+ kGOOGLE_PIN_GoDaddySecureFingerprint,
+ kGeoTrust_Primary_Certification_AuthorityFingerprint,
+ kGo_Daddy_Class_2_CAFingerprint,
+ kDigiCert_High_Assurance_EV_Root_CAFingerprint,
+ kthawte_Primary_Root_CA___G2Fingerprint,
+ kEntrust_Root_Certification_AuthorityFingerprint,
+ kGOOGLE_PIN_Entrust_G2Fingerprint,
+ kGeoTrust_Global_CAFingerprint,
+ kGeoTrust_Primary_Certification_Authority___G3Fingerprint,
+ kDigiCert_Global_Root_CAFingerprint,
+ kGeoTrust_Primary_Certification_Authority___G2Fingerprint,
+};
+static const StaticFingerprints kPinset_dropbox_sha256 = {
+ sizeof(kPinset_dropbox_sha256_Data) / sizeof(const char*),
+ kPinset_dropbox_sha256_Data
+};
+
+static const StaticPinset kPinset_dropbox = {
+ nullptr,
+ &kPinset_dropbox_sha256
+};
+
+/* Domainlist */
+struct TransportSecurityPreload {
+ const char* mHost;
+ const bool mIncludeSubdomains;
+ const bool mTestMode;
+ const bool mIsMoz;
+ const int32_t mId;
+ const StaticPinset *pinset;
+};
+
+/* Sort hostnames for binary search. */
+static const TransportSecurityPreload kPublicKeyPinningPreloadList[] = {
+ { "2mdn.net", true, false, false, -1, &kPinset_google_root_pems },
+ { "accounts.firefox.com", true, false, false, 4, &kPinset_mozilla_services },
+ { "accounts.google.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "addons.mozilla.net", true, false, true, 2, &kPinset_mozilla },
+ { "addons.mozilla.org", true, false, true, 1, &kPinset_mozilla },
+ { "admin.google.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "android.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "api.accounts.firefox.com", true, false, false, 5, &kPinset_mozilla_services },
+ { "api.twitter.com", true, false, false, -1, &kPinset_twitterCDN },
+ { "apis.google.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "appengine.google.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "appspot.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "aus4.mozilla.org", true, true, true, 3, &kPinset_mozilla },
+ { "blog.torproject.org", true, false, false, -1, &kPinset_tor },
+ { "business.twitter.com", true, false, false, -1, &kPinset_twitterCom },
+ { "cdn.mozilla.net", true, false, true, -1, &kPinset_mozilla },
+ { "cdn.mozilla.org", true, false, true, -1, &kPinset_mozilla },
+ { "chart.apis.google.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "check.torproject.org", true, false, false, -1, &kPinset_tor },
+ { "checkout.google.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "chrome-devtools-frontend.appspot.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "chrome.google.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "chromiumcodereview.appspot.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "cloud.google.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "code.google.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "codereview.appspot.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "codereview.chromium.org", true, false, false, -1, &kPinset_google_root_pems },
+ { "crypto.cat", false, true, false, -1, &kPinset_cryptoCat },
+ { "dev.twitter.com", true, false, false, -1, &kPinset_twitterCom },
+ { "dist.torproject.org", true, false, false, -1, &kPinset_tor },
+ { "dl.google.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "docs.google.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "domains.google.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "doubleclick.net", true, false, false, -1, &kPinset_google_root_pems },
+ { "drive.google.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "dropbox.com", false, false, false, -1, &kPinset_dropbox },
+ { "encrypted.google.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "exclude-subdomains.pinning.example.com", false, false, false, 0, &kPinset_mozilla_test },
+ { "facebook.com", true, true, false, -1, &kPinset_facebook },
+ { "g.co", true, false, false, -1, &kPinset_google_root_pems },
+ { "glass.google.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "gmail.com", false, false, false, -1, &kPinset_google_root_pems },
+ { "goo.gl", true, false, false, -1, &kPinset_google_root_pems },
+ { "google-analytics.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.ac", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.ad", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.ae", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.af", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.ag", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.am", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.as", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.at", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.az", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.ba", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.be", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.bf", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.bg", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.bi", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.bj", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.bs", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.by", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.ca", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.cat", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.cc", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.cd", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.cf", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.cg", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.ch", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.ci", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.cl", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.cm", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.cn", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.co.ao", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.co.bw", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.co.ck", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.co.cr", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.co.hu", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.co.id", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.co.il", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.co.im", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.co.in", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.co.je", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.co.jp", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.co.ke", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.co.kr", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.co.ls", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.co.ma", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.co.mz", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.co.nz", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.co.th", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.co.tz", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.co.ug", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.co.uk", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.co.uz", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.co.ve", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.co.vi", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.co.za", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.co.zm", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.co.zw", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.af", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.ag", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.ai", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.ar", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.au", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.bd", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.bh", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.bn", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.bo", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.br", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.by", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.bz", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.cn", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.co", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.cu", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.cy", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.do", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.ec", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.eg", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.et", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.fj", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.ge", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.gh", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.gi", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.gr", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.gt", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.hk", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.iq", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.jm", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.jo", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.kh", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.kw", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.lb", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.ly", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.mt", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.mx", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.my", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.na", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.nf", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.ng", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.ni", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.np", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.nr", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.om", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.pa", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.pe", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.ph", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.pk", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.pl", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.pr", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.py", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.qa", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.ru", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.sa", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.sb", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.sg", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.sl", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.sv", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.tj", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.tn", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.tr", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.tw", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.ua", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.uy", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.vc", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.ve", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.com.vn", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.cv", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.cz", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.de", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.dj", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.dk", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.dm", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.dz", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.ee", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.es", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.fi", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.fm", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.fr", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.ga", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.ge", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.gg", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.gl", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.gm", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.gp", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.gr", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.gy", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.hk", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.hn", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.hr", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.ht", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.hu", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.ie", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.im", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.info", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.iq", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.is", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.it", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.it.ao", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.je", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.jo", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.jobs", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.jp", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.kg", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.ki", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.kz", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.la", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.li", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.lk", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.lt", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.lu", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.lv", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.md", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.me", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.mg", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.mk", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.ml", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.mn", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.ms", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.mu", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.mv", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.mw", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.ne", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.ne.jp", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.net", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.nl", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.no", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.nr", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.nu", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.off.ai", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.pk", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.pl", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.pn", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.ps", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.pt", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.ro", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.rs", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.ru", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.rw", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.sc", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.se", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.sh", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.si", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.sk", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.sm", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.sn", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.so", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.st", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.td", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.tg", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.tk", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.tl", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.tm", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.tn", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.to", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.tt", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.us", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.uz", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.vg", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.vu", true, false, false, -1, &kPinset_google_root_pems },
+ { "google.ws", true, false, false, -1, &kPinset_google_root_pems },
+ { "googleadservices.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "googleapis.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "googlecode.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "googlecommerce.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "googlegroups.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "googlemail.com", false, false, false, -1, &kPinset_google_root_pems },
+ { "googleplex.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "googlesyndication.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "googletagmanager.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "googletagservices.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "googleusercontent.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "goto.google.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "groups.google.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "gstatic.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "history.google.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "hostedtalkgadget.google.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "include-subdomains.pinning.example.com", true, false, false, -1, &kPinset_mozilla_test },
+ { "liberty.lavabit.com", true, true, false, -1, &kPinset_lavabit },
+ { "login.corp.google.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "mail.google.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "market.android.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "media.mozilla.com", true, false, true, -1, &kPinset_mozilla },
+ { "mobile.twitter.com", true, false, false, -1, &kPinset_twitterCom },
+ { "oauth.twitter.com", true, false, false, -1, &kPinset_twitterCom },
+ { "pinningtest.appspot.com", true, false, false, -1, &kPinset_test },
+ { "platform.twitter.com", true, false, false, -1, &kPinset_twitterCDN },
+ { "play.google.com", false, false, false, -1, &kPinset_google_root_pems },
+ { "plus.google.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "plus.sandbox.google.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "profiles.google.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "script.google.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "security.google.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "services.mozilla.com", true, true, false, -1, &kPinset_mozilla_services },
+ { "sites.google.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "spreadsheets.google.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "ssl.google-analytics.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "talk.google.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "talkgadget.google.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "test-mode.pinning.example.com", true, true, false, -1, &kPinset_mozilla_test },
+ { "tor2web.org", true, true, false, -1, &kPinset_tor2web },
+ { "torproject.org", false, false, false, -1, &kPinset_tor },
+ { "translate.googleapis.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "twimg.com", true, false, false, -1, &kPinset_twitterCDN },
+ { "twitter.com", true, false, false, -1, &kPinset_twitterCDN },
+ { "urchin.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "w-spotlight.appspot.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "wallet.google.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "webfilings-eu-mirror.appspot.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "webfilings-eu.appspot.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "webfilings-mirror-hrd.appspot.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "webfilings.appspot.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "wf-bigsky-master.appspot.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "wf-demo-eu.appspot.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "wf-demo-hrd.appspot.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "wf-dogfood-hrd.appspot.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "wf-pentest.appspot.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "wf-staging-hr.appspot.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "wf-training-hrd.appspot.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "wf-training-master.appspot.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "wf-trial-hrd.appspot.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "www.dropbox.com", true, false, false, -1, &kPinset_dropbox },
+ { "www.gmail.com", false, false, false, -1, &kPinset_google_root_pems },
+ { "www.googlemail.com", false, false, false, -1, &kPinset_google_root_pems },
+ { "www.torproject.org", true, false, false, -1, &kPinset_tor },
+ { "www.twitter.com", true, false, false, -1, &kPinset_twitterCom },
+ { "xbrlsuccess.appspot.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "youtu.be", true, false, false, -1, &kPinset_google_root_pems },
+ { "youtube-nocookie.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "youtube.com", true, false, false, -1, &kPinset_google_root_pems },
+ { "ytimg.com", true, false, false, -1, &kPinset_google_root_pems },
+};
+
+// Pinning Preload List Length = 331;
+
+static const int32_t kUnknownId = -1;
+
+static const PRTime kPreloadPKPinsExpirationTime = INT64_C(1419674828470000);
diff --git a/security/manager/boot/src/moz.build b/security/manager/boot/src/moz.build
index b3d2127..50a345b 100644
--- a/security/manager/boot/src/moz.build
+++ b/security/manager/boot/src/moz.build
@@ -10,6 +10,7 @@ UNIFIED_SOURCES += [
'nsSecurityHeaderParser.cpp',
'nsSecurityWarningDialogs.cpp',
'nsSiteSecurityService.cpp',
+ 'PublicKeyPinningService.cpp',
]
# nsSecureBrowserUIImpl.cpp cannot be built in unified mode because it forces NSPR logging.
@@ -17,6 +18,11 @@ SOURCES += [
'nsSecureBrowserUIImpl.cpp',
]
+
+LOCAL_INCLUDES += [
+ '../../../pkix/include',
+]
+
FAIL_ON_WARNINGS = True
MSVC_ENABLE_PGO = True
diff --git a/security/manager/ssl/src/SSLServerCertVerification.cpp b/security/manager/ssl/src/SSLServerCertVerification.cpp
index cf15586..18d9f55 100644
--- a/security/manager/ssl/src/SSLServerCertVerification.cpp
+++ b/security/manager/ssl/src/SSLServerCertVerification.cpp
@@ -637,8 +637,9 @@ NSSDetermineCertOverrideErrors(CertVerifier& certVerifier,
// possible failure.
// XXX TODO: convert to VerifySSLServerCert
// XXX TODO: get rid of error log
- certVerifier.VerifyCert(cert, stapledOCSPResponse, certificateUsageSSLServer,
- now, infoObject, 0, nullptr, nullptr, verify_log);
+ certVerifier.VerifyCert(cert, certificateUsageSSLServer,
+ now, infoObject, infoObject->GetHostNameRaw(),
+ 0, stapledOCSPResponse, nullptr, nullptr, verify_log);
// Check the name field against the desired hostname.
if (CERT_VerifyCertName(cert, infoObject->GetHostNameRaw()) != SECSuccess) {
diff --git a/security/manager/ssl/src/SharedCertVerifier.h b/security/manager/ssl/src/SharedCertVerifier.h
index 32a92a3..c7c1936 100644
--- a/security/manager/ssl/src/SharedCertVerifier.h
+++ b/security/manager/ssl/src/SharedCertVerifier.h
@@ -24,12 +24,14 @@ public:
missing_cert_download_config ac, crl_download_config cdc,
#endif
ocsp_download_config odc, ocsp_strict_config osc,
- ocsp_get_config ogc)
+ ocsp_get_config ogc,
+ pinning_enforcement_config pinningEnforcementLevel)
: mozilla::psm::CertVerifier(ic,
#ifndef NSS_NO_LIBPKIX
ac, cdc,
#endif
- odc, osc, ogc)
+ odc, osc, ogc,
+ pinningEnforcementLevel)
{
}
};
diff --git a/security/manager/ssl/src/nsCMS.cpp b/security/manager/ssl/src/nsCMS.cpp
index 70a3c49..ee76c07 100644
--- a/security/manager/ssl/src/nsCMS.cpp
+++ b/security/manager/ssl/src/nsCMS.cpp
@@ -264,9 +264,10 @@ nsresult nsCMSMessage::CommonVerifySignature(unsigned char* aDigestData, uint32_
NS_ENSURE_TRUE(certVerifier, NS_ERROR_UNEXPECTED);
{
- SECStatus srv = certVerifier->VerifyCert(si->cert, nullptr,
+ SECStatus srv = certVerifier->VerifyCert(si->cert,
certificateUsageEmailSigner,
- PR_Now(), nullptr /*XXX pinarg*/);
+ PR_Now(), nullptr /*XXX pinarg*/,
+ nullptr /*hostname*/);
if (srv != SECSuccess) {
PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
("nsCMSMessage::CommonVerifySignature - signing cert not trusted now\n"));
diff --git a/security/manager/ssl/src/nsNSSCertificate.cpp b/security/manager/ssl/src/nsNSSCertificate.cpp
index 88fb8ed..70b9c89 100644
--- a/security/manager/ssl/src/nsNSSCertificate.cpp
+++ b/security/manager/ssl/src/nsNSSCertificate.cpp
@@ -829,10 +829,12 @@ nsNSSCertificate::GetChain(nsIArray** _rvChain)
// We want to test all usages, but we start with server because most of the
// time Firefox users care about server certs.
- certVerifier->VerifyCert(mCert.get(), nullptr,
+ certVerifier->VerifyCert(mCert.get(),
certificateUsageSSLServer, PR_Now(),
nullptr, /*XXX fixme*/
+ nullptr, /* hostname */
CertVerifier::FLAG_LOCAL_ONLY,
+ nullptr, /* stapledOCSPResponse */
&nssChain);
// This is the whitelist of all non-SSLServer usages that are supported by
// verifycert.
@@ -851,10 +853,12 @@ nsNSSCertificate::GetChain(nsIArray** _rvChain)
PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
("pipnss: PKIX attempting chain(%d) for '%s'\n",
usage, mCert->nickname));
- certVerifier->VerifyCert(mCert.get(), nullptr,
+ certVerifier->VerifyCert(mCert.get(),
usage, PR_Now(),
nullptr, /*XXX fixme*/
+ nullptr, /*hostname*/
CertVerifier::FLAG_LOCAL_ONLY,
+ nullptr, /* stapledOCSPResponse */
&nssChain);
}
@@ -1467,10 +1471,11 @@ nsNSSCertificate::hasValidEVOidTag(SECOidTag& resultOidTag, bool& validEV)
uint32_t flags = mozilla::psm::CertVerifier::FLAG_LOCAL_ONLY |
mozilla::psm::CertVerifier::FLAG_MUST_BE_EV;
- SECStatus rv = certVerifier->VerifyCert(mCert.get(), nullptr,
+ SECStatus rv = certVerifier->VerifyCert(mCert.get(),
certificateUsageSSLServer, PR_Now(),
nullptr /* XXX pinarg */,
- flags, nullptr, &resultOidTag);
+ nullptr /* hostname */,
+ flags, nullptr /* stapledOCSPResponse */ , nullptr, &resultOidTag);
if (rv != SECSuccess) {
resultOidTag = SEC_OID_UNKNOWN;
diff --git a/security/manager/ssl/src/nsNSSCertificateDB.cpp b/security/manager/ssl/src/nsNSSCertificateDB.cpp
index 40b03c1..c9fd8ba 100644
--- a/security/manager/ssl/src/nsNSSCertificateDB.cpp
+++ b/security/manager/ssl/src/nsNSSCertificateDB.cpp
@@ -634,9 +634,10 @@ nsNSSCertificateDB::ImportEmailCertificate(uint8_t * data, uint32_t length,
mozilla::pkix::ScopedCERTCertList certChain;
- SECStatus rv = certVerifier->VerifyCert(node->cert, nullptr,
+ SECStatus rv = certVerifier->VerifyCert(node->cert,
certificateUsageEmailRecipient,
- now, ctx, 0, &certChain);
+ now, ctx, nullptr, 0,
+ nullptr, &certChain);
if (rv != SECSuccess) {
nsCOMPtr<nsIX509Cert> certToShow = nsNSSCertificate::Create(node->cert);
@@ -801,9 +802,10 @@ nsNSSCertificateDB::ImportValidCACertsInList(CERTCertList *certList, nsIInterfac
!CERT_LIST_END(node,certList);
node = CERT_LIST_NEXT(node)) {
mozilla::pkix::ScopedCERTCertList certChain;
- SECStatus rv = certVerifier->VerifyCert(node->cert, nullptr,
+ SECStatus rv = certVerifier->VerifyCert(node->cert,
certificateUsageVerifyCA,
- PR_Now(), ctx, 0, &certChain);
+ PR_Now(), ctx, nullptr, 0, nullptr,
+ &certChain);
if (rv != SECSuccess) {
nsCOMPtr<nsIX509Cert> certToShow = nsNSSCertificate::Create(node->cert);
DisplayCertificateAlert(ctx, "NotImportingUnverifiedCert", certToShow, proofOfLock);
@@ -1381,9 +1383,10 @@ nsNSSCertificateDB::FindCertByEmailAddress(nsISupports *aToken, const char *aEma
!CERT_LIST_END(node, certlist);
node = CERT_LIST_NEXT(node)) {
- SECStatus srv = certVerifier->VerifyCert(node->cert, nullptr,
+ SECStatus srv = certVerifier->VerifyCert(node->cert,
certificateUsageEmailRecipient,
- PR_Now(), nullptr /*XXX pinarg*/);
+ PR_Now(), nullptr /*XXX pinarg*/,
+ nullptr /*hostname*/);
if (srv == SECSuccess) {
break;
}
@@ -1772,10 +1775,12 @@ nsNSSCertificateDB::VerifyCertNow(nsIX509Cert* aCert,
SECOidTag evOidPolicy;
SECStatus srv;
- srv = certVerifier->VerifyCert(nssCert, nullptr,
+ srv = certVerifier->VerifyCert(nssCert,
aUsage, PR_Now(),
nullptr, // Assume no context
+ nullptr, // hostname
aFlags,
+ nullptr, // stapledOCSPResponse
&resultChain,
&evOidPolicy);
diff --git a/security/manager/ssl/src/nsNSSComponent.cpp b/security/manager/ssl/src/nsNSSComponent.cpp
index 44b3808..c4bd566 100644
--- a/security/manager/ssl/src/nsNSSComponent.cpp
+++ b/security/manager/ssl/src/nsNSSComponent.cpp
@@ -996,6 +996,13 @@ void nsNSSComponent::setValidationOptions(bool isInitialSetting,
}
}
+ // Default pinning enforcement level is disabled.
+ CertVerifier::pinning_enforcement_config
+ pinningEnforcementLevel =
+ static_cast<CertVerifier::pinning_enforcement_config>
+ (Preferences::GetInt("security.cert_pinning.enforcement_level",
+ CertVerifier::pinningDisabled));
+
CertVerifier::ocsp_download_config odc;
CertVerifier::ocsp_strict_config osc;
CertVerifier::ocsp_get_config ogc;
@@ -1009,7 +1016,7 @@ void nsNSSComponent::setValidationOptions(bool isInitialSetting,
crlDownloading ?
CertVerifier::crl_download_allowed : CertVerifier::crl_local_only,
#endif
- odc, osc, ogc);
+ odc, osc, ogc, pinningEnforcementLevel);
// mozilla::pkix has its own OCSP cache, so disable the NSS cache
// if appropriate.
@@ -1644,7 +1651,8 @@ nsNSSComponent::Observe(nsISupports* aSubject, const char* aTopic,
|| prefName.Equals("security.OCSP.GET.enabled")
|| prefName.Equals("security.ssl.enable_ocsp_stapling")
|| prefName.Equals("security.use_mozillapkix_verification")
- || prefName.Equals("security.use_libpkix_verification")) {
+ || prefName.Equals("security.use_libpkix_verification")
+ || prefName.Equals("security.cert_pinning.enforcement_level")) {
MutexAutoLock lock(mutex);
setValidationOptions(false, lock);
} else if (prefName.Equals("network.ntlm.send-lm-response")) {
diff --git a/security/manager/ssl/src/nsUsageArrayHelper.cpp b/security/manager/ssl/src/nsUsageArrayHelper.cpp
index 7f7249f..dd65ae5 100644
--- a/security/manager/ssl/src/nsUsageArrayHelper.cpp
+++ b/security/manager/ssl/src/nsUsageArrayHelper.cpp
@@ -105,8 +105,9 @@ nsUsageArrayHelper::check(uint32_t previousCheckResult,
MOZ_CRASH("unknown cert usage passed to check()");
}
- SECStatus rv = certVerifier->VerifyCert(mCert, nullptr, aCertUsage,
- time, nullptr /*XXX:wincx*/, flags);
+ SECStatus rv = certVerifier->VerifyCert(mCert, aCertUsage, time,
+ nullptr /*XXX:wincx*/,
+ nullptr /*hostname*/, flags);
if (rv == SECSuccess) {
typestr.Append(suffix);
diff --git a/security/manager/ssl/tests/unit/head_psm.js b/security/manager/ssl/tests/unit/head_psm.js
index 58e838b..ae25ea9e 100644
--- a/security/manager/ssl/tests/unit/head_psm.js
+++ b/security/manager/ssl/tests/unit/head_psm.js
@@ -52,6 +52,7 @@ const SEC_ERROR_OCSP_INVALID_SIGNING_CERT = SEC_ERROR_BASE + 144;
const SEC_ERROR_POLICY_VALIDATION_FAILED = SEC_ERROR_BASE + 160; // -8032
const SEC_ERROR_OCSP_BAD_SIGNATURE = SEC_ERROR_BASE + 157;
const SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED = SEC_ERROR_BASE + 176;
+const SEC_ERROR_APPLICATION_CALLBACK_ERROR = SEC_ERROR_BASE + 178;
const SSL_ERROR_BAD_CERT_DOMAIN = SSL_ERROR_BASE + 12;
diff --git a/security/manager/ssl/tests/unit/test_cert_overrides.js b/security/manager/ssl/tests/unit/test_cert_overrides.js
index 11e14f3b..91fc77eb 100644
--- a/security/manager/ssl/tests/unit/test_cert_overrides.js
+++ b/security/manager/ssl/tests/unit/test_cert_overrides.js
@@ -232,7 +232,7 @@ function add_distrust_override_test(certFileName, hostName,
let certToDistrust = constructCertFromFile(certFileName);
add_test(function () {
- // "pu" means the cert is actively distrusted.
+ // Add an entry to the NSS certDB that says to distrust the cert
setCertTrust(certToDistrust, "pu,,");
clearSessionCache();
run_next_test();
diff --git a/security/manager/ssl/tests/unit/test_pinning.js b/security/manager/ssl/tests/unit/test_pinning.js
new file mode 100644
index 0000000..dc779dd
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_pinning.js
@@ -0,0 +1,182 @@
+// -*- indent-tabs-mode: nil; js-indent-level: 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/.
+//
+// For all cases, the acceptable pinset includes only certificates pinned to
+// Test End Entity Cert (signed by issuer testCA). Other certificates
+// are issued by otherCA, which is never in the pinset but is a user-specified
+// trust anchor. This test covers multiple cases:
+//
+// Pinned domain include-subdomains.pinning.example.com includes subdomains
+// - PASS: include-subdomains.pinning.example.com serves a correct cert
+// - PASS: good.include-subdomains.pinning.example.com serves a correct cert
+// - FAIL (strict): bad.include-subdomains.pinning.example.com serves a cert
+// not in the pinset
+// - PASS (mitm): bad.include-subdomains.pinning.example.com serves a cert not
+// in the pinset, but issued by a user-specified trust domain
+//
+// Pinned domain exclude-subdomains.pinning.example.com excludes subdomains
+// - PASS: exclude-subdomains.pinning.example.com serves a correct cert
+// - FAIL: exclude-subdomains.pinning.example.com services an incorrect cert
+// (TODO: test using verifyCertnow)
+// - PASS: sub.exclude-subdomains.pinning.example.com serves an incorrect cert
+
+"use strict";
+
+do_get_profile(); // must be called before getting nsIX509CertDB
+const certdb = Cc["@mozilla.org/security/x509certdb;1"]
+ .getService(Ci.nsIX509CertDB);
+
+function test_strict() {
+ // In strict mode, we always evaluate pinning data, regardless of whether the
+ // issuer is a built-in trust anchor. We only enforce pins that are not in
+ // test mode.
+ add_test(function() {
+ Services.prefs.setIntPref("security.cert_pinning.enforcement_level", 2);
+ run_next_test();
+ });
+
+ // If a host should be pinned but other errors (particularly overridable
+ // errors) like 'unknown issuer' are encountered, the pinning error takes
+ // precedence. This prevents overrides for such hosts.
+ add_connection_test("unknownissuer.include-subdomains.pinning.example.com",
+ getXPCOMStatusFromNSS(MOZILLA_PKIX_ERROR_KEY_PINNING_FAILURE));
+
+ // Issued by otherCA, which is not in the pinset for pinning.example.com.
+ add_connection_test("bad.include-subdomains.pinning.example.com",
+ getXPCOMStatusFromNSS(MOZILLA_PKIX_ERROR_KEY_PINNING_FAILURE));
+
+ // These domains serve certs that match the pinset.
+ add_connection_test("include-subdomains.pinning.example.com", Cr.NS_OK);
+ add_connection_test("good.include-subdomains.pinning.example.com", Cr.NS_OK);
+ add_connection_test("exclude-subdomains.pinning.example.com", Cr.NS_OK);
+
+ // This domain serves a cert that doesn't match the pinset, but subdomains
+ // are excluded.
+ add_connection_test("sub.exclude-subdomains.pinning.example.com", Cr.NS_OK);
+
+ // This domain's pinset is exactly the same as
+ // include-subdomains.pinning.example.com, serves the same cert as
+ // bad.include-subdomains.pinning.example.com, but it should pass because
+ // it's in test_mode.
+ add_connection_test("test-mode.pinning.example.com", Cr.NS_OK);
+}
+
+function test_mitm() {
+ // In MITM mode, we allow pinning to pass if the chain resolves to any
+ // user-specified trust anchor, even if it is not in the pinset.
+ add_test(function() {
+ Services.prefs.setIntPref("security.cert_pinning.enforcement_level", 1);
+ run_next_test();
+ });
+
+ add_connection_test("include-subdomains.pinning.example.com", Cr.NS_OK);
+ add_connection_test("good.include-subdomains.pinning.example.com", Cr.NS_OK);
+
+ add_connection_test("unknownissuer.include-subdomains.pinning.example.com",
+ getXPCOMStatusFromNSS(SEC_ERROR_UNKNOWN_ISSUER));
+
+ // In this case, even though otherCA is not in the pinset, it is a
+ // user-specified trust anchor and the pinning check succeeds.
+ add_connection_test("bad.include-subdomains.pinning.example.com", Cr.NS_OK);
+
+ add_connection_test("exclude-subdomains.pinning.example.com", Cr.NS_OK);
+ add_connection_test("sub.exclude-subdomains.pinning.example.com", Cr.NS_OK);
+ add_connection_test("test-mode.pinning.example.com", Cr.NS_OK);
+};
+
+function test_disabled() {
+ // Disable pinning.
+ add_test(function() {
+ Services.prefs.setIntPref("security.cert_pinning.enforcement_level", 0);
+ run_next_test();
+ });
+
+ add_connection_test("include-subdomains.pinning.example.com", Cr.NS_OK);
+ add_connection_test("good.include-subdomains.pinning.example.com", Cr.NS_OK);
+ add_connection_test("bad.include-subdomains.pinning.example.com", Cr.NS_OK);
+ add_connection_test("exclude-subdomains.pinning.example.com", Cr.NS_OK);
+ add_connection_test("sub.exclude-subdomains.pinning.example.com", Cr.NS_OK);
+ add_connection_test("test-mode.pinning.example.com", Cr.NS_OK);
+
+ add_connection_test("unknownissuer.include-subdomains.pinning.example.com",
+ getXPCOMStatusFromNSS(SEC_ERROR_UNKNOWN_ISSUER));
+}
+
+function test_enforce_test_mode() {
+ // In enforce test mode, we always enforce all pins, even test pins.
+ add_test(function() {
+ Services.prefs.setIntPref("security.cert_pinning.enforcement_level", 3);
+ run_next_test();
+ });
+
+ add_connection_test("unknownissuer.include-subdomains.pinning.example.com",
+ getXPCOMStatusFromNSS(MOZILLA_PKIX_ERROR_KEY_PINNING_FAILURE));
+
+ // Issued by otherCA, which is not in the pinset for pinning.example.com.
+ add_connection_test("bad.include-subdomains.pinning.example.com",
+ getXPCOMStatusFromNSS(MOZILLA_PKIX_ERROR_KEY_PINNING_FAILURE));
+
+ // These domains serve certs that match the pinset.
+ add_connection_test("include-subdomains.pinning.example.com", Cr.NS_OK);
+ add_connection_test("good.include-subdomains.pinning.example.com", Cr.NS_OK);
+ add_connection_test("exclude-subdomains.pinning.example.com", Cr.NS_OK);
+
+ // This domain serves a cert that doesn't match the pinset, but subdomains
+ // are excluded.
+ add_connection_test("sub.exclude-subdomains.pinning.example.com", Cr.NS_OK);
+
+ // This domain's pinset is exactly the same as
+ // include-subdomains.pinning.example.com, serves the same cert as
+ // bad.include-subdomains.pinning.example.com, is in test-mode, but we are
+ // enforcing test mode pins.
+ add_connection_test("test-mode.pinning.example.com",
+ getXPCOMStatusFromNSS(MOZILLA_PKIX_ERROR_KEY_PINNING_FAILURE));
+}
+
+function check_pinning_telemetry() {
+ let service = Cc["@mozilla.org/base/telemetry;1"].getService(Ci.nsITelemetry);
+ let prod_histogram = service.getHistogramById("CERT_PINNING_RESULTS")
+ .snapshot();
+ let test_histogram = service.getHistogramById("CERT_PINNING_TEST_RESULTS")
+ .snapshot();
+ // Because all of our test domains are pinned to user-specified trust
+ // anchors, effectively only strict mode and enforce test-mode get evaluated
+ do_check_eq(prod_histogram.counts[0], 4); // Failure count
+ do_check_eq(prod_histogram.counts[1], 4); // Success count
+ do_check_eq(test_histogram.counts[0], 2); // Failure count
+ do_check_eq(test_histogram.counts[1], 0); // Success count
+
+ let moz_prod_histogram = service.getHistogramById("CERT_PINNING_MOZ_RESULTS")
+ .snapshot();
+ let moz_test_histogram =
+ service.getHistogramById("CERT_PINNING_MOZ_TEST_RESULTS").snapshot();
+ do_check_eq(moz_prod_histogram.counts[0], 0); // Failure count
+ do_check_eq(moz_prod_histogram.counts[1], 0); // Success count
+ do_check_eq(moz_test_histogram.counts[0], 0); // Failure count
+ do_check_eq(moz_test_histogram.counts[1], 0); // Success count
+
+ let per_host_histogram =
+ service.getHistogramById("CERT_PINNING_MOZ_RESULTS_BY_HOST").snapshot();
+ do_check_eq(per_host_histogram.counts[0], 0); // Failure count
+ do_check_eq(per_host_histogram.counts[1], 2); // Success count
+ run_next_test();
+}
+
+function run_test() {
+ add_tls_server_setup("BadCertServer");
+
+ // Add a user-specified trust anchor.
+ addCertFromFile(certdb, "tlsserver/other-test-ca.der", "CTu,u,u");
+
+ test_strict();
+ test_mitm();
+ test_disabled();
+ test_enforce_test_mode();
+
+ add_test(function () {
+ check_pinning_telemetry();
+ });
+ run_next_test();
+}
diff --git a/security/manager/ssl/tests/unit/tlsserver/cert8.db b/security/manager/ssl/tests/unit/tlsserver/cert8.db
index 13c101f..7da5d7f 100644
Binary files a/security/manager/ssl/tests/unit/tlsserver/cert8.db and b/security/manager/ssl/tests/unit/tlsserver/cert8.db differ
diff --git a/security/manager/ssl/tests/unit/tlsserver/cmd/BadCertServer.cpp b/security/manager/ssl/tests/unit/tlsserver/cmd/BadCertServer.cpp
index a4c59a4..5df134a 100644
--- a/security/manager/ssl/tests/unit/tlsserver/cmd/BadCertServer.cpp
+++ b/security/manager/ssl/tests/unit/tlsserver/cmd/BadCertServer.cpp
@@ -24,6 +24,7 @@ struct BadCertHost
const char *mCertName;
};
+// Hostname, cert nickname pairs.
const BadCertHost sBadCertHosts[] =
{
{ "expired.example.com", "expired" },
@@ -42,6 +43,16 @@ const BadCertHost sBadCertHosts[] =
{ "inadequatekeyusage.example.com", "inadequatekeyusage" },
{ "selfsigned-inadequateEKU.example.com", "selfsigned-inadequateEKU" },
{ "self-signed-end-entity-with-cA-true.example.com", "self-signed-EE-with-cA-true" },
+ // All of include-subdomains.pinning.example.com is pinned to End Entity
+ // Test Cert with nick localhostAndExampleCom. Any other nick will only
+ // pass pinning when security.cert_pinning.enforcement.level != strict and
+ // otherCA is added as a user-specified trust anchor. See StaticHPKPins.h.
+ { "include-subdomains.pinning.example.com", "localhostAndExampleCom" },
+ { "good.include-subdomains.pinning.example.com", "localhostAndExampleCom" },
+ { "bad.include-subdomains.pinning.example.com", "otherIssuerEE" },
+ { "exclude-subdomains.pinning.example.com", "localhostAndExampleCom" },
+ { "sub.exclude-subdomains.pinning.example.com", "otherIssuerEE" },
+ { "test-mode.pinning.example.com", "otherIssuerEE" },
{ nullptr, nullptr }
};
diff --git a/security/manager/ssl/tests/unit/tlsserver/default-ee.der b/security/manager/ssl/tests/unit/tlsserver/default-ee.der
index ac98037..9b2359d 100644
Binary files a/security/manager/ssl/tests/unit/tlsserver/default-ee.der and b/security/manager/ssl/tests/unit/tlsserver/default-ee.der differ
diff --git a/security/manager/ssl/tests/unit/tlsserver/generate_certs.sh b/security/manager/ssl/tests/unit/tlsserver/generate_certs.sh
index ef388bb..6075a5c 100755
--- a/security/manager/ssl/tests/unit/tlsserver/generate_certs.sh
+++ b/security/manager/ssl/tests/unit/tlsserver/generate_certs.sh
@@ -12,7 +12,10 @@
#
# NB: This will cause the following files to be overwritten if they are in
# the output directory:
-# cert8.db, key3.db, secmod.db, ocsp-ca.der, ocsp-other-ca.der
+# cert8.db, key3.db, secmod.db, ocsp-ca.der, ocsp-other-ca.der, default-ee.der
+# NB: You must run genHPKPStaticPins.js after running this file, since its
+# output (StaticHPKPins.h) depends on default-ee.der
+
set -x
set -e
@@ -25,11 +28,13 @@ OBJDIR=${1}
OUTPUT_DIR=${2}
RUN_MOZILLA="$OBJDIR/dist/bin/run-mozilla.sh"
CERTUTIL="$OBJDIR/dist/bin/certutil"
+# On BSD, mktemp requires either a template or a prefix.
+MKTEMP="mktemp temp.XXXX"
-NOISE_FILE=`mktemp`
+NOISE_FILE=`$MKTEMP`
# Make a good effort at putting something unique in the noise file.
date +%s%N > "$NOISE_FILE"
-PASSWORD_FILE=`mktemp`
+PASSWORD_FILE=`$MKTEMP`
function cleanup {
rm -f "$NOISE_FILE" "$PASSWORD_FILE"
@@ -134,7 +139,11 @@ function make_delegated {
make_CA testCA 'CN=Test CA' test-ca.der
make_CA otherCA 'CN=Other test CA' other-test-ca.der
-make_EE localhostAndExampleCom 'CN=Test End-entity' testCA "localhost,*.example.com"
+
+make_EE localhostAndExampleCom 'CN=Test End-entity' testCA "localhost,*.example.com,*.pinning.example.com,*.include-subdomains.pinning.…"
+# Make an EE cert issued by otherCA
+make_EE otherIssuerEE 'CN=Wrong CA Pin Test End-Entity' otherCA "*.include-subdomains.pinning.example.com,*.exclude-subdomains.pinning.example.com,*.pinning.example.com"
+
$RUN_MOZILLA $CERTUTIL -d $OUTPUT_DIR -L -n localhostAndExampleCom -r > $OUTPUT_DIR/default-ee.der
# A cert that is like localhostAndExampleCom, but with a different serial number for
# testing the "OCSP response is from the right issuer, but it is for the wrong cert"
diff --git a/security/manager/ssl/tests/unit/tlsserver/key3.db b/security/manager/ssl/tests/unit/tlsserver/key3.db
index 283e8fb..2d4dd29 100644
Binary files a/security/manager/ssl/tests/unit/tlsserver/key3.db and b/security/manager/ssl/tests/unit/tlsserver/key3.db differ
diff --git a/security/manager/ssl/tests/unit/tlsserver/other-test-ca.der b/security/manager/ssl/tests/unit/tlsserver/other-test-ca.der
index 794fb9c..742bb86 100644
Binary files a/security/manager/ssl/tests/unit/tlsserver/other-test-ca.der and b/security/manager/ssl/tests/unit/tlsserver/other-test-ca.der differ
diff --git a/security/manager/ssl/tests/unit/tlsserver/secmod.db b/security/manager/ssl/tests/unit/tlsserver/secmod.db
index a5f2f60..7a0e2b5 100644
Binary files a/security/manager/ssl/tests/unit/tlsserver/secmod.db and b/security/manager/ssl/tests/unit/tlsserver/secmod.db differ
diff --git a/security/manager/ssl/tests/unit/tlsserver/test-ca.der b/security/manager/ssl/tests/unit/tlsserver/test-ca.der
index f4c4863..356de5b 100644
Binary files a/security/manager/ssl/tests/unit/tlsserver/test-ca.der and b/security/manager/ssl/tests/unit/tlsserver/test-ca.der differ
diff --git a/security/manager/ssl/tests/unit/xpcshell.ini b/security/manager/ssl/tests/unit/xpcshell.ini
index 7935c2d..24b0ffc 100644
--- a/security/manager/ssl/tests/unit/xpcshell.ini
+++ b/security/manager/ssl/tests/unit/xpcshell.ini
@@ -78,3 +78,7 @@ requesttimeoutfactor = 4
run-sequentially = hardcoded ports
# Bug 676972: this test times out on Android and B2G
skip-if = os == "android" || buildapp == "b2g"
+[test_pinning.js]
+run-sequentially = hardcoded ports
+# Bug 676972: test fails consistently on Android and B2G
+fail-if = os == "android" || buildapp == "b2g"
\ No newline at end of file
diff --git a/security/manager/tools/PreloadedHPKPins.json b/security/manager/tools/PreloadedHPKPins.json
new file mode 100644
index 0000000..ed3b9b7
--- /dev/null
+++ b/security/manager/tools/PreloadedHPKPins.json
@@ -0,0 +1,247 @@
+// -*- Mode: javascript; 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/.
+
+// The top-level element is a dictionary with two keys: "pinsets" maps details
+// of certificate pinning to a name and "entries" contains the HPKP details for
+// each host.
+//
+// "pinsets" is a list of objects. Each object has the following members:
+// name: (string) the name of the pinset
+// sha256_hashes: (list of strings) the set of allowed SPKIs hashes
+//
+// For a given pinset, a certificate is accepted if at least one of the
+// Subject Public Key Infos (SPKIs) is found in the chain. SPKIs are specified
+// as names, which must match up with the name given in the Mozilla root store.
+//
+// "entries" is a list of objects. Each object has the following members:
+// name: (string) the DNS name of the host in question
+// include_subdomains: (optional bool) whether subdomains of |name| are also covered
+// pins: (string) the |name| member of an object in |pinsets|
+//
+// "extra_certs" is a list of base64-encoded certificates. These are used in
+// pinsets that reference certificates not in our root program (for example,
+// Facebook).
+
+// equifax -> aus3
+// Geotrust Primary -> www.mozilla.org
+// Geotrust Global -> *. addons.mozilla.org
+{
+ "chromium_data" : {
+ "cert_file_url": "https://src.chromium.org/chrome/trunk/src/net/http/transport_security_state…",
+ "json_file_url": "https://src.chromium.org/chrome/trunk/src/net/http/transport_security_state…",
+ "substitute_pinsets": {
+ // Use the larger google_root_pems pinset instead of google
+ "google": "google_root_pems"
+ },
+ "production_pinsets": [
+ "google_root_pems"
+ ],
+ "production_domains": [
+ // Chrome's test domain.
+ "pinningtest.appspot.com",
+ // Dropbox
+ "dropbox.com",
+ "www.dropbox.com",
+ // Twitter
+ "api.twitter.com",
+ "business.twitter.com",
+ "dev.twitter.com",
+ "mobile.twitter.com",
+ "oauth.twitter.com",
+ "platform.twitter.com",
+ "twimg.com",
+ "www.twitter.com",
+ // Tor
+ "torproject.org",
+ "blog.torproject.org",
+ "check.torproject.org",
+ "dist.torproject.org",
+ "www.torproject.org"
+ ],
+ "exclude_domains" : [
+ // Chrome's entry for twitter.com doesn't include subdomains, so replace
+ // it with our own entry below which also uses an expanded pinset.
+ "twitter.com"
+ ]
+ },
+ "pinsets": [
+ {
+ // From bug 772756, mozilla uses GeoTrust, Digicert and Thawte. Our
+ // cdn sites use Verisign and Baltimore. We exclude 1024-bit root certs
+ // from all providers. geotrust ca info:
+ // http://www.geotrust.com/resources/root-certificates/index.html
+ "name": "mozilla",
+ "sha256_hashes": [
+ "Baltimore CyberTrust Root",
+ "DigiCert Assured ID Root CA",
+ "DigiCert Global Root CA",
+ "DigiCert High Assurance EV Root CA",
+ "GeoTrust Global CA",
+ "GeoTrust Global CA 2",
+ "GeoTrust Primary Certification Authority",
+ "GeoTrust Primary Certification Authority - G2",
+ "GeoTrust Primary Certification Authority - G3",
+ "GeoTrust Universal CA",
+ "GeoTrust Universal CA 2",
+ "thawte Primary Root CA",
+ "thawte Primary Root CA - G2",
+ "thawte Primary Root CA - G3",
+ "Verisign Class 1 Public Primary Certification Authority - G3",
+ "Verisign Class 2 Public Primary Certification Authority - G3",
+ "Verisign Class 3 Public Primary Certification Authority - G3",
+ "VeriSign Class 3 Public Primary Certification Authority - G4",
+ "VeriSign Class 3 Public Primary Certification Authority - G5",
+ "Verisign Class 4 Public Primary Certification Authority - G3",
+ "VeriSign Universal Root Certification Authority"
+ ]
+ },
+ {
+ "name": "mozilla_services",
+ "sha256_hashes": [
+ "DigiCert Global Root CA"
+ ]
+ },
+ // For pinning tests on pinning.example.com, the certificate must be 'End
+ // Entity Test Cert'
+ {
+ "name": "mozilla_test",
+ "sha256_hashes": [
+ "End Entity Test Cert"
+ ]
+ },
+ // Google's root PEMs. Chrome pins only to their intermediate certs, but
+ // they'd like us to be more liberal. For the initial list, we are using
+ // the certs from http://pki.google.com/roots.pem.
+ // We have no built-in for commented out CAs.
+ {
+ "name": "google_root_pems",
+ "sha256_hashes": [
+ "AddTrust External Root",
+ "AddTrust Low-Value Services Root",
+ "AddTrust Public Services Root",
+ "AddTrust Qualified Certificates Root",
+ "AffirmTrust Commercial",
+ "AffirmTrust Networking",
+ "AffirmTrust Premium",
+ "AffirmTrust Premium ECC",
+ "America Online Root Certification Authority 1",
+ "America Online Root Certification Authority 2",
+ "Baltimore CyberTrust Root",
+ "Comodo AAA Services root",
+ "COMODO Certification Authority",
+ "COMODO ECC Certification Authority",
+ "Comodo Secure Services root",
+ "Comodo Trusted Services root",
+ "Cybertrust Global Root",
+ "DigiCert Assured ID Root CA",
+ "DigiCert Global Root CA",
+ "DigiCert High Assurance EV Root CA",
+ "Entrust.net Premium 2048 Secure Server CA",
+ // "Entrust.net Secure Server CA",
+ "Entrust Root Certification Authority",
+ "Equifax Secure CA",
+ "Equifax Secure eBusiness CA 1",
+ // "Equifax Secure eBusiness CA 2",
+ "Equifax Secure Global eBusiness CA",
+ "GeoTrust Global CA",
+ "GeoTrust Global CA 2",
+ "GeoTrust Primary Certification Authority",
+ "GeoTrust Primary Certification Authority - G2",
+ "GeoTrust Primary Certification Authority - G3",
+ "GeoTrust Universal CA",
+ "GeoTrust Universal CA 2",
+ "GlobalSign Root CA",
+ "GlobalSign Root CA - R2",
+ "GlobalSign Root CA - R3",
+ "Go Daddy Class 2 CA",
+ "Go Daddy Root Certificate Authority - G2",
+ // "GTE CyberTrust Global Root",
+ "Network Solutions Certificate Authority",
+ // "RSA Root Certificate 1",
+ "Starfield Class 2 CA",
+ "Starfield Root Certificate Authority - G2",
+ "Starfield Services Root Certificate Authority - G2",
+ "StartCom Certification Authority",
+ "StartCom Certification Authority",
+ "StartCom Certification Authority G2",
+ "TC TrustCenter Class 2 CA II",
+ "TC TrustCenter Class 3 CA II",
+ "TC TrustCenter Universal CA I",
+ "TC TrustCenter Universal CA III",
+ "Thawte Premium Server CA",
+ "thawte Primary Root CA",
+ "thawte Primary Root CA - G2",
+ "thawte Primary Root CA - G3",
+ "Thawte Server CA",
+ "UTN DATACorp SGC Root CA",
+ "UTN USERFirst Hardware Root CA",
+ // "ValiCert Class 1 VA",
+ // "ValiCert Class 2 VA",
+ "Verisign Class 3 Public Primary Certification Authority",
+ "Verisign Class 3 Public Primary Certification Authority",
+ "Verisign Class 3 Public Primary Certification Authority - G2",
+ "Verisign Class 3 Public Primary Certification Authority - G3",
+ "VeriSign Class 3 Public Primary Certification Authority - G4",
+ "VeriSign Class 3 Public Primary Certification Authority - G5",
+ "Verisign Class 4 Public Primary Certification Authority - G3",
+ "VeriSign Universal Root Certification Authority",
+ "XRamp Global CA Root"
+ ]
+ },
+ {
+ "name": "facebook",
+ "sha256_hashes": [
+ "Verisign Class 3 Public Primary Certification Authority - G3",
+ "DigiCert High Assurance EV Root CA",
+ "DigiCert ECC Secure Server CA"
+ ]
+ }
+ ],
+
+ "entries": [
+ // Only domains that are operationally crucial to Firefox can have per-host
+ // telemetry reporting (the "id") field
+ { "name": "addons.mozilla.org", "include_subdomains": true,
+ "pins": "mozilla", "test_mode": false, "id": 1 },
+ { "name": "addons.mozilla.net", "include_subdomains": true,
+ "pins": "mozilla", "test_mode": false, "id": 2 },
+ { "name": "aus4.mozilla.org", "include_subdomains": true,
+ "pins": "mozilla", "test_mode": true, "id": 3 },
+ { "name": "accounts.firefox.com", "include_subdomains": true,
+ "pins": "mozilla_services", "test_mode": false, "id": 4 },
+ { "name": "api.accounts.firefox.com", "include_subdomains": true,
+ "pins": "mozilla_services", "test_mode": false, "id": 5 },
+ { "name": "cdn.mozilla.net", "include_subdomains": true,
+ "pins": "mozilla", "test_mode": false },
+ { "name": "cdn.mozilla.org", "include_subdomains": true,
+ "pins": "mozilla", "test_mode": false },
+ { "name": "media.mozilla.com", "include_subdomains": true,
+ "pins": "mozilla", "test_mode": false },
+ { "name": "services.mozilla.com", "include_subdomains": true,
+ "pins": "mozilla_services", "test_mode": true },
+ { "name": "include-subdomains.pinning.example.com",
+ "include_subdomains": true, "pins": "mozilla_test",
+ "test_mode": false },
+ // Example domain to collect per-host stats for telemetry tests.
+ { "name": "exclude-subdomains.pinning.example.com",
+ "include_subdomains": false, "pins": "mozilla_test",
+ "test_mode": false, "id": 0 },
+ { "name": "test-mode.pinning.example.com", "include_subdomains": true,
+ "pins": "mozilla_test", "test_mode": true },
+ // Expand twitter's pinset to include all of *.twitter.com and use
+ // twitterCDN. More specific rules take precedence because we search for
+ // exact domain name first.
+ { "name": "twitter.com", "include_subdomains": true,
+ "pins": "twitterCDN", "test_mode": false },
+ // Facebook (not pinned by Chrome)
+ { "name": "facebook.com", "include_subdomains": true,
+ "pins": "facebook", "test_mode": true }
+ ],
+
+ "extra_certificates": [
+ // DigiCert ECC Secure Server CA (for Facebook)
+ "MIIDrDCCApSgAwIBAgIQCssoukZe5TkIdnRw883GEjANBgkqhkiG9w0BAQwFADBhMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBDQTAeFw0xMzAzMDgxMjAwMDBaFw0yMzAzMDgxMjAwMDBaMEwxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxJjAkBgNVBAMTHURpZ2lDZXJ0IEVDQyBTZWN1cmUgU2VydmVyIENBMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE4ghC6nfYJN6gLGSkE85AnCNyqQIKDjc/ITa4jVMU9tWRlUvzlgKNcR7E2Munn17voOZ/WpIRllNv68DLP679Wz9HJOeaBy6Wvqgvu1cYr3GkvXg6HuhbPGtkESvMNCuMo4IBITCCAR0wEgYDVR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwNAYIKwYBBQUHAQEEKDAmMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wQgYDVR0fBDswOTA3oDWgM4YxaHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0R2xvYmFsUm9vdENBLmNybDA9BgNVHSAENjA0MDIGBFUdIAAwKjAoBggrBgEFBQcCARYcaHR0cHM6Ly93d3cuZGlnaWNlcnQuY29tL0NQUzAdBgNVHQ4EFgQUo53mH/naOU/AbuiRy5Wl2jHiCp8wHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUwDQYJKoZIhvcNAQEMBQADggEBAMeKoENL7HTJxavVHzA1Nm6YVntIrAVjrnuaVyRXzG/63qttnMe2uuzO58pzZNvfBDcKAEmzP58
mrZGMIOgfiA4q+2Y3yDDo0sIkp0VILeoBUEoxlBPfjV/aKrtJPGHzecicZpIalir0ezZYoyxBEHQa0+1IttK7igZFcTMQMHp6mCHdJLnsnLWSB62DxsRq+HfmNb4TDydkskO/g+l3VtsIh5RHFPVfKK+jaEyDj2D3loB5hWp2Jp2VDCADjT7ueihlZGak2YPqmXTNbk19HOuNssWvFhtOyPNV6og4ETQdEa8/B6hPatJ0ES8q/HO3X8IVQwVs1n3aAr0im0/T+Xc="
+ ]
+}
diff --git a/security/manager/tools/genHPKPStaticPins.js b/security/manager/tools/genHPKPStaticPins.js
new file mode 100644
index 0000000..d16d017
--- /dev/null
+++ b/security/manager/tools/genHPKPStaticPins.js
@@ -0,0 +1,576 @@
+/* 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/. */
+
+// How to run this file:
+// 1. [obtain firefox source code]
+// 2. [build/obtain firefox binaries]
+// 3. run `[path to]/run-mozilla.sh [path to]/xpcshell \
+// [path to]/genHPKPStaticpins.js \
+// [absolute path to]/PreloadedHPKPins.json \
+// [absolute path to]/default-ee.der \
+// [absolute path to]/StaticHPKPins.h
+
+if (arguments.length != 3) {
+ throw "Usage: genHPKPStaticPins.js " +
+ "<absolute path to PreloadedHPKPins.json> " +
+ "<absolute path to default-ee.der> " +
+ "<absolute path to StaticHPKPins.h>";
+}
+
+const { 'classes': Cc, 'interfaces': Ci, 'utils': Cu, 'results': Cr } = Components;
+
+let { NetUtil } = Cu.import("resource://gre/modules/NetUtil.jsm", {});
+let { FileUtils } = Cu.import("resource://gre/modules/FileUtils.jsm", {});
+let { Services } = Cu.import("resource://gre/modules/Services.jsm", {});
+
+let gCertDB = Cc["@mozilla.org/security/x509certdb;1"]
+ .getService(Ci.nsIX509CertDB);
+
+const BUILT_IN_NICK_PREFIX = "Builtin Object Token:";
+const SHA1_PREFIX = "sha1/";
+const SHA256_PREFIX = "sha256/";
+const GOOGLE_PIN_PREFIX = "GOOGLE_PIN_";
+
+// Pins expire in 14 weeks (6 weeks on Beta + 8 weeks on stable)
+const PINNING_MINIMUM_REQUIRED_MAX_AGE = 60 * 60 * 24 * 7 * 14;
+
+const FILE_HEADER = "/* This Source Code Form is subject to the terms of the Mozilla Public\n" +
+" * License, v. 2.0. If a copy of the MPL was not distributed with this\n" +
+" * file, You can obtain one at http://mozilla.org/MPL/2.0/. */\n" +
+"\n" +
+"/*****************************************************************************/\n" +
+"/* This is an automatically generated file. If you're not */\n" +
+"/* PublicKeyPinningService.cpp, you shouldn't be #including it. */\n" +
+"/*****************************************************************************/\n" +
+"#include <stdint.h>" +
+"\n";
+
+const DOMAINHEADER = "/* Domainlist */\n" +
+ "struct TransportSecurityPreload {\n" +
+ " const char* mHost;\n" +
+ " const bool mIncludeSubdomains;\n" +
+ " const bool mTestMode;\n" +
+ " const bool mIsMoz;\n" +
+ " const int32_t mId;\n" +
+ " const StaticPinset *pinset;\n" +
+ "};\n\n";
+
+const PINSETDEF = "/* Pinsets are each an ordered list by the actual value of the fingerprint */\n" +
+ "struct StaticFingerprints {\n" +
+ " const size_t size;\n" +
+ " const char* const* data;\n" +
+ "};\n\n" +
+ "struct StaticPinset {\n" +
+ " const StaticFingerprints* sha1;\n" +
+ " const StaticFingerprints* sha256;\n" +
+ "};\n\n";
+
+// Command-line arguments
+var gStaticPins = parseJson(arguments[0]);
+var gTestCertFile = arguments[1];
+
+// Open the output file.
+let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile);
+file.initWithPath(arguments[2]);
+let gFileOutputStream = FileUtils.openSafeFileOutputStream(file);
+
+function writeString(string) {
+ gFileOutputStream.write(string, string.length);
+}
+
+function readFileToString(filename) {
+ let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile);
+ file.initWithPath(filename);
+ let stream = Cc["@mozilla.org/network/file-input-stream;1"]
+ .createInstance(Ci.nsIFileInputStream);
+ stream.init(file, -1, 0, 0);
+ let buf = NetUtil.readInputStreamToString(stream, stream.available());
+ return buf;
+}
+
+function stripComments(buf) {
+ var lines = buf.split("\n");
+ let entryRegex = /^\s*\/\//;
+ let data = "";
+ for (let i = 0; i < lines.length; ++i) {
+ let match = entryRegex.exec(lines[i]);
+ if (!match) {
+ data = data + lines[i];
+ }
+ }
+ return data;
+}
+
+function isBuiltinToken(tokenName) {
+ return tokenName == "Builtin Object Token";
+}
+
+function isCertBuiltIn(cert) {
+ let tokenNames = cert.getAllTokenNames({});
+ if (!tokenNames) {
+ return false;
+ }
+ if (tokenNames.some(isBuiltinToken)) {
+ return true;
+ }
+ return false;
+}
+
+function download(filename) {
+ var req = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"]
+ .createInstance(Ci.nsIXMLHttpRequest);
+ req.open("GET", filename, false); // doing the request synchronously
+ try {
+ req.send();
+ }
+ catch (e) {
+ throw "ERROR: problem downloading '" + filename + "': " + e;
+ }
+
+ if (req.status != 200) {
+ throw("ERROR: problem downloading '" + filename + "': status " +
+ req.status);
+ }
+ return req.responseText;
+}
+
+function downloadAsJson(filename) {
+ // we have to filter out '//' comments
+ var result = download(filename).replace(/\/\/[^\n]*\n/g, "");
+ var data = null;
+ try {
+ data = JSON.parse(result);
+ }
+ catch (e) {
+ throw "ERROR: could not parse data from '" + filename + "': " + e;
+ }
+ return data;
+}
+
+// Returns a Subject Public Key Digest from the given pem, if it exists.
+function getSKDFromPem(pem) {
+ let cert = gCertDB.constructX509FromBase64(pem, pem.length);
+ return cert.sha256SubjectPublicKeyInfoDigest;
+}
+
+// Downloads the static certs file and tries to map Google Chrome nicknames
+// to Mozilla nicknames, as well as storing any hashes for pins for which we
+// don't have root PEMs. Each entry consists of a line containing the name of
+// the pin followed either by a hash in the format "sha1/" + base64(hash), or
+// a PEM encoded certificate. For certificates that we have in our database,
+// return a map of Google's nickname to ours. For ones that aren't return a
+// map of Google's nickname to sha1 values. This code is modeled after agl's
+// https://github.com/agl/transport-security-state-generate, which doesn't
+// live in the Chromium repo because go is not an official language in
+// Chromium.
+// For all of the entries in this file:
+// - If the entry has a hash format, find the Mozilla pin name (cert nickname)
+// and stick the hash into certSKDToName
+// - If the entry has a PEM format, parse the PEM, find the Mozilla pin name
+// and stick the hash in certSKDToName
+// We MUST be able to find a corresponding cert nickname for the Chrome names,
+// otherwise we skip all pinsets referring to that Chrome name.
+function downloadAndParseChromeCerts(filename, certSKDToName) {
+ // Prefixes that we care about.
+ const BEGIN_CERT = "-----BEGIN CERTIFICATE-----";
+ const END_CERT = "-----END CERTIFICATE-----";
+
+ // Parsing states.
+ const PRE_NAME = 0;
+ const POST_NAME = 1;
+ const IN_CERT = 2;
+ let state = PRE_NAME;
+
+ let lines = download(filename).split("\n");
+ let name = "";
+ let pemCert = "";
+ let hash = "";
+ let chromeNameToHash = {};
+ let chromeNameToMozName = {}
+ for (let i = 0; i < lines.length; ++i) {
+ let line = lines[i];
+ // Skip comments and newlines.
+ if (line.length == 0 || line[0] == '#') {
+ continue;
+ }
+ switch(state) {
+ case PRE_NAME:
+ chromeName = line;
+ state = POST_NAME;
+ break;
+ case POST_NAME:
+ if (line.startsWith(SHA1_PREFIX) ||
+ line.startsWith(SHA256_PREFIX)) {
+ if (line.startsWith(SHA1_PREFIX)) {
+ hash = line.substring(SHA1_PREFIX.length);
+ } else if (line.startsWith(SHA256_PREFIX)) {
+ hash = line.substring(SHA256_PREFIX);
+ }
+ // Store the entire prefixed hash, so we can disambiguate sha1 from
+ // sha256 later.
+ chromeNameToHash[chromeName] = line;
+ certNameToSKD[chromeName] = hash;
+ certSKDToName[hash] = chromeName;
+ state = PRE_NAME;
+ } else if (line.startsWith(BEGIN_CERT)) {
+ state = IN_CERT;
+ } else {
+ throw "ERROR: couldn't parse Chrome certificate file " + line;
+ }
+ break;
+ case IN_CERT:
+ if (line.startsWith(END_CERT)) {
+ state = PRE_NAME;
+ hash = getSKDFromPem(pemCert);
+ pemCert = "";
+ if (hash in certSKDToName) {
+ mozName = certSKDToName[hash];
+ } else {
+ // Not one of our built-in certs. Prefix the name with
+ // GOOGLE_PIN_.
+ mozName = GOOGLE_PIN_PREFIX + chromeName;
+ dump("Can't find hash in builtin certs for Chrome nickname " +
+ chromeName + ", inserting " + mozName + "\n");
+ certSKDToName[hash] = mozName;
+ certNameToSKD[mozName] = hash;
+ }
+ chromeNameToMozName[chromeName] = mozName;
+ } else {
+ pemCert += line;
+ }
+ break;
+ default:
+ throw "ERROR: couldn't parse Chrome certificate file " + line;
+ }
+ }
+ return [ chromeNameToHash, chromeNameToMozName ];
+}
+
+// We can only import pinsets from chrome if for every name in the pinset:
+// - We have a hash from Chrome's static certificate file
+// - We have a builtin cert
+// If the pinset meets these requirements, we store a map array of pinset
+// objects:
+// {
+// pinset_name : {
+// // Array of names with entries in certNameToSKD
+// sha1_hashes: [],
+// sha256_hashes: []
+// }
+// }
+// and an array of imported pinset entries:
+// { name: string, include_subdomains: boolean, test_mode: boolean,
+// pins: pinset_name }
+function downloadAndParseChromePins(filename,
+ chromeNameToHash,
+ chromeNameToMozName,
+ certNameToSKD,
+ certSKDToName) {
+ let chromePreloads = downloadAsJson(filename);
+ let chromePins = chromePreloads.pinsets;
+ let chromeImportedPinsets = {};
+ let chromeImportedEntries = [];
+
+ chromePins.forEach(function(pin) {
+ let valid = true;
+ let pinset = { name: pin.name, sha1_hashes: [], sha256_hashes: [] };
+ // Translate the Chrome pinset format to ours
+ pin.static_spki_hashes.forEach(function(name) {
+ if (name in chromeNameToHash) {
+ let hash = chromeNameToHash[name];
+ if (hash.startsWith(SHA1_PREFIX)) {
+ hash = hash.substring(SHA1_PREFIX.length);
+ pinset.sha1_hashes.push(certSKDToName[hash]);
+ } else if (hash.startsWith(SHA256_PREFIX)) {
+ hash = hash.substring(SHA256_PREFIX.length);
+ pinset.sha256_hashes.push(certSKDToName[hash]);
+ } else {
+ throw("Unsupported hash type: " + chromeNameToHash[name]);
+ }
+ // We should have already added hashes for all of these when we
+ // imported the certificate file.
+ if (!certNameToSKD[name]) {
+ throw("No hash for name: " + name);
+ }
+ } else if (name in chromeNameToMozName) {
+ pinset.sha256_hashes.push(chromeNameToMozName[name]);
+ } else {
+ dump("Skipping Chrome pinset " + pinset.name + ", couldn't find " +
+ "builtin " + name + " from cert file\n");
+ valid = false;
+ }
+ });
+ if (valid) {
+ chromeImportedPinsets[pinset.name] = pinset;
+ }
+ });
+
+ // Grab the domain entry lists. Chrome's entry format is similar to
+ // ours, except theirs includes a HSTS mode.
+ const cData = gStaticPins.chromium_data;
+ let entries = chromePreloads.entries;
+ entries.forEach(function(entry) {
+ let pinsetName = cData.substitute_pinsets[entry.pins];
+ if (!pinsetName) {
+ pinsetName = entry.pins;
+ }
+ let isProductionDomain =
+ (cData.production_domains.indexOf(entry.name) != -1);
+ let isProductionPinset =
+ (cData.production_pinsets.indexOf(pinsetName) != -1);
+ let excludeDomain =
+ (cData.exclude_domains.indexOf(entry.name) != -1);
+ let isTestMode = !isProductionPinset && !isProductionDomain;
+ if (entry.pins && !excludeDomain && chromeImportedPinsets[entry.pins]) {
+ chromeImportedEntries.push({
+ name: entry.name,
+ include_subdomains: entry.include_subdomains,
+ test_mode: isTestMode,
+ is_moz: false,
+ pins: pinsetName });
+ }
+ });
+ return [ chromeImportedPinsets, chromeImportedEntries ];
+}
+
+// Returns a pair of maps [certNameToSKD, certSKDToName] between cert
+// nicknames and digests of the SPKInfo for the mozilla trust store
+function loadNSSCertinfo(derTestFile, extraCertificates) {
+ let allCerts = gCertDB.getCerts();
+ let enumerator = allCerts.getEnumerator();
+ let certNameToSKD = {};
+ let certSKDToName = {};
+ while (enumerator.hasMoreElements()) {
+ let cert = enumerator.getNext().QueryInterface(Ci.nsIX509Cert);
+ if (!isCertBuiltIn(cert)) {
+ continue;
+ }
+ let name = cert.nickname.substr(BUILT_IN_NICK_PREFIX.length);
+ let SKD = cert.sha256SubjectPublicKeyInfoDigest;
+ certNameToSKD[name] = SKD;
+ certSKDToName[SKD] = name;
+ }
+
+ for (let cert of extraCertificates) {
+ let name = cert.commonName;
+ let SKD = cert.sha256SubjectPublicKeyInfoDigest;
+ certNameToSKD[name] = SKD;
+ certSKDToName[SKD] = name;
+ }
+
+ {
+ // A certificate for *.example.com.
+ let der = readFileToString(derTestFile);
+ let testCert = gCertDB.constructX509(der, der.length);
+ // We can't include this cert in the previous loop, because it skips
+ // non-builtin certs and the nickname is not built-in to the cert.
+ let name = "End Entity Test Cert";
+ let SKD = testCert.sha256SubjectPublicKeyInfoDigest;
+ certNameToSKD[name] = SKD;
+ certSKDToName[SKD] = name;
+ }
+ return [certNameToSKD, certSKDToName];
+}
+
+function parseJson(filename) {
+ let json = stripComments(readFileToString(filename));
+ return JSON.parse(json);
+}
+
+function nameToAlias(certName) {
+ // change the name to a string valid as a c identifier
+ // remove non-ascii characters
+ certName = certName.replace( /[^[:ascii:]]/g, "_");
+ // replace non word characters
+ certName = certName.replace(/[^A-Za-z0-9]/g ,"_");
+
+ return "k" + certName + "Fingerprint";
+}
+
+function compareByName (a, b) {
+ return a.name.localeCompare(b.name);
+}
+
+function genExpirationTime() {
+ let now = new Date();
+ let nowMillis = now.getTime();
+ let expirationMillis = nowMillis + (PINNING_MINIMUM_REQUIRED_MAX_AGE * 1000);
+ let expirationMicros = expirationMillis * 1000;
+ return "static const PRTime kPreloadPKPinsExpirationTime = INT64_C(" +
+ expirationMicros +");\n";
+}
+
+function writeFullPinset(certNameToSKD, certSKDToName, pinset) {
+ // We aren't guaranteed to have sha1 hashes in our own imported pins.
+ let prefix = "kPinset_" + pinset.name;
+ let sha1Name = "nullptr";
+ let sha256Name = "nullptr";
+ if (pinset.sha1_hashes && pinset.sha1_hashes.length > 0) {
+ writeFingerprints(certNameToSKD, certSKDToName, pinset.name,
+ pinset.sha1_hashes, "sha1");
+ sha1Name = "&" + prefix + "_sha1";
+ }
+ if (pinset.sha256_hashes && pinset.sha256_hashes.length > 0) {
+ writeFingerprints(certNameToSKD, certSKDToName, pinset.name,
+ pinset.sha256_hashes, "sha256");
+ sha256Name = "&" + prefix + "_sha256";
+ }
+ writeString("static const StaticPinset " + prefix + " = {\n" +
+ " " + sha1Name + ",\n " + sha256Name + "\n};\n\n");
+}
+
+function writeFingerprints(certNameToSKD, certSKDToName, name, hashes, type) {
+ let varPrefix = "kPinset_" + name + "_" + type;
+ writeString("static const char* " + varPrefix + "_Data[] = {\n");
+ let SKDList = [];
+ for (let certName of hashes) {
+ if (!(certName in certNameToSKD)) {
+ throw "Can't find " + certName + " in certNameToSKD";
+ }
+ SKDList.push(certNameToSKD[certName]);
+ }
+ for (let skd of SKDList.sort()) {
+ writeString(" " + nameToAlias(certSKDToName[skd]) + ",\n");
+ }
+ if (hashes.length == 0) {
+ // ANSI C requires that an initialiser list be non-empty.
+ writeString(" 0\n");
+ }
+ writeString("};\n");
+ writeString("static const StaticFingerprints " + varPrefix + " = {\n " +
+ "sizeof(" + varPrefix + "_Data) / sizeof(const char*),\n " + varPrefix +
+ "_Data\n};\n\n");
+}
+
+function writeEntry(entry) {
+ let printVal = " { \"" + entry.name + "\",\ ";
+ if (entry.include_subdomains) {
+ printVal += "true, ";
+ } else {
+ printVal += "false, ";
+ }
+ // Default to test mode if not specified.
+ let testMode = true;
+ if (entry.hasOwnProperty("test_mode")) {
+ testMode = entry.test_mode;
+ }
+ if (testMode) {
+ printVal += "true, ";
+ } else {
+ printVal += "false, ";
+ }
+ if (entry.is_moz || (entry.pins == "mozilla")) {
+ printVal += "true, ";
+ } else {
+ printVal += "false, ";
+ }
+ if (entry.id >= 256) {
+ throw("Not enough buckets in histogram");
+ }
+ if (entry.id >= 0) {
+ printVal += entry.id + ", ";
+ } else {
+ printVal += "-1, ";
+ }
+ printVal += "&kPinset_" + entry.pins;
+ printVal += " },\n";
+ writeString(printVal);
+}
+
+function writeDomainList(chromeImportedEntries) {
+ writeString("/* Sort hostnames for binary search. */\n");
+ writeString("static const TransportSecurityPreload " +
+ "kPublicKeyPinningPreloadList[] = {\n");
+ let count = 0;
+ let sortedEntries = gStaticPins.entries;
+ sortedEntries.push.apply(sortedEntries, chromeImportedEntries);
+ for (let entry of sortedEntries.sort(compareByName)) {
+ count++;
+ writeEntry(entry);
+ }
+ writeString("};\n");
+
+ writeString("\n// Pinning Preload List Length = " + count + ";\n");
+ writeString("\nstatic const int32_t kUnknownId = -1;\n");
+}
+
+function writeFile(certNameToSKD, certSKDToName,
+ chromeImportedPinsets, chromeImportedEntries) {
+ // Compute used pins from both Chrome's and our pinsets, so we can output
+ // them later.
+ usedFingerprints = {};
+ gStaticPins.pinsets.forEach(function(pinset) {
+ // We aren't guaranteed to have sha1_hashes in our own JSON.
+ if (pinset.sha1_hashes) {
+ pinset.sha1_hashes.forEach(function(name) {
+ usedFingerprints[name] = true;
+ });
+ }
+ if (pinset.sha256_hashes) {
+ pinset.sha256_hashes.forEach(function(name) {
+ usedFingerprints[name] = true;
+ });
+ }
+ });
+ for (let key in chromeImportedPinsets) {
+ let pinset = chromeImportedPinsets[key];
+ pinset.sha1_hashes.forEach(function(name) {
+ usedFingerprints[name] = true;
+ });
+ pinset.sha256_hashes.forEach(function(name) {
+ usedFingerprints[name] = true;
+ });
+ }
+
+ writeString(FILE_HEADER);
+
+ // Write actual fingerprints.
+ Object.keys(usedFingerprints).sort().forEach(function(certName) {
+ if (certName) {
+ writeString("/* " + certName + " */\n");
+ writeString("static const char " + nameToAlias(certName) + "[] =\n");
+ writeString(" \"" + certNameToSKD[certName] + "\";\n");
+ writeString("\n");
+ }
+ });
+
+ // Write the pinsets
+ writeString(PINSETDEF);
+ writeString("/* PreloadedHPKPins.json pinsets */\n");
+ gStaticPins.pinsets.sort(compareByName).forEach(function(pinset) {
+ writeFullPinset(certNameToSKD, certSKDToName, pinset);
+ });
+ writeString("/* Chrome static pinsets */\n");
+ for (let key in chromeImportedPinsets) {
+ writeFullPinset(certNameToSKD, certSKDToName, chromeImportedPinsets[key]);
+ }
+
+ // Write the domainlist entries.
+ writeString(DOMAINHEADER);
+ writeDomainList(chromeImportedEntries);
+ writeString("\n");
+ writeString(genExpirationTime());
+}
+
+function loadExtraCertificates(certStringList) {
+ let constructedCerts = [];
+ for (let certString of certStringList) {
+ constructedCerts.push(gCertDB.constructX509FromBase64(certString));
+ }
+ return constructedCerts;
+}
+
+let extraCertificates = loadExtraCertificates(gStaticPins.extra_certificates);
+let [ certNameToSKD, certSKDToName ] = loadNSSCertinfo(gTestCertFile,
+ extraCertificates);
+let [ chromeNameToHash, chromeNameToMozName ] = downloadAndParseChromeCerts(
+ gStaticPins.chromium_data.cert_file_url, certSKDToName);
+let [ chromeImportedPinsets, chromeImportedEntries ] =
+ downloadAndParseChromePins(gStaticPins.chromium_data.json_file_url,
+ chromeNameToHash, chromeNameToMozName, certNameToSKD, certSKDToName);
+
+writeFile(certNameToSKD, certSKDToName, chromeImportedPinsets,
+ chromeImportedEntries);
+
+FileUtils.closeSafeFileOutputStream(gFileOutputStream);
diff --git a/security/pkix/include/pkix/Result.h b/security/pkix/include/pkix/Result.h
new file mode 100644
index 0000000..e82e6428
--- /dev/null
+++ b/security/pkix/include/pkix/Result.h
@@ -0,0 +1,174 @@
+/* -*- 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 code is made available to you under your choice of the following sets
+ * of licensing terms:
+ */
+/* 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/.
+ */
+/* Copyright 2013 Mozilla Contributors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef mozilla_pkix__Result_h
+#define mozilla_pkix__Result_h
+
+#include <cassert>
+
+#include "pkix/enumclass.h"
+
+namespace mozilla { namespace pkix {
+
+static const unsigned int FATAL_ERROR_FLAG = 0x800;
+
+// The first argument to MOZILLA_PKIX_MAP() is used for building the mapping
+// from error code to error name in MapResultToName.
+//
+// The second argument is for defining the value for the enum literal in the
+// Result enum class.
+//
+// The third argument to MOZILLA_PKIX_MAP() is used, along with the first
+// argument, for maintaining the mapping of mozilla::pkix error codes to
+// NSS/NSPR error codes in pkixnss.cpp.
+#define MOZILLA_PKIX_MAP_LIST \
+ MOZILLA_PKIX_MAP(Success, 0, 0) \
+ MOZILLA_PKIX_MAP(ERROR_BAD_DER, 1, \
+ SEC_ERROR_BAD_DER) \
+ MOZILLA_PKIX_MAP(ERROR_CA_CERT_INVALID, 2, \
+ SEC_ERROR_CA_CERT_INVALID) \
+ MOZILLA_PKIX_MAP(ERROR_BAD_SIGNATURE, 3, \
+ SEC_ERROR_BAD_SIGNATURE) \
+ MOZILLA_PKIX_MAP(ERROR_CERT_BAD_ACCESS_LOCATION, 4, \
+ SEC_ERROR_CERT_BAD_ACCESS_LOCATION) \
+ MOZILLA_PKIX_MAP(ERROR_CERT_NOT_IN_NAME_SPACE, 5, \
+ SEC_ERROR_CERT_NOT_IN_NAME_SPACE) \
+ MOZILLA_PKIX_MAP(ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED, 6, \
+ SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED) \
+ MOZILLA_PKIX_MAP(ERROR_CONNECT_REFUSED, 7, \
+ PR_CONNECT_REFUSED_ERROR) \
+ MOZILLA_PKIX_MAP(ERROR_EXPIRED_CERTIFICATE, 8, \
+ SEC_ERROR_EXPIRED_CERTIFICATE) \
+ MOZILLA_PKIX_MAP(ERROR_EXTENSION_VALUE_INVALID, 9, \
+ SEC_ERROR_EXTENSION_VALUE_INVALID) \
+ MOZILLA_PKIX_MAP(ERROR_INADEQUATE_CERT_TYPE, 10, \
+ SEC_ERROR_INADEQUATE_CERT_TYPE) \
+ MOZILLA_PKIX_MAP(ERROR_INADEQUATE_KEY_USAGE, 11, \
+ SEC_ERROR_INADEQUATE_KEY_USAGE) \
+ MOZILLA_PKIX_MAP(ERROR_INVALID_ALGORITHM, 12, \
+ SEC_ERROR_INVALID_ALGORITHM) \
+ MOZILLA_PKIX_MAP(ERROR_INVALID_TIME, 13, \
+ SEC_ERROR_INVALID_TIME) \
+ MOZILLA_PKIX_MAP(ERROR_KEY_PINNING_FAILURE, 14, \
+ MOZILLA_PKIX_ERROR_KEY_PINNING_FAILURE) \
+ MOZILLA_PKIX_MAP(ERROR_PATH_LEN_CONSTRAINT_INVALID, 15, \
+ SEC_ERROR_PATH_LEN_CONSTRAINT_INVALID) \
+ MOZILLA_PKIX_MAP(ERROR_POLICY_VALIDATION_FAILED, 16, \
+ SEC_ERROR_POLICY_VALIDATION_FAILED) \
+ MOZILLA_PKIX_MAP(ERROR_REVOKED_CERTIFICATE, 17, \
+ SEC_ERROR_REVOKED_CERTIFICATE) \
+ MOZILLA_PKIX_MAP(ERROR_UNKNOWN_CRITICAL_EXTENSION, 18, \
+ SEC_ERROR_UNKNOWN_CRITICAL_EXTENSION) \
+ MOZILLA_PKIX_MAP(ERROR_UNKNOWN_ERROR, 19, \
+ PR_UNKNOWN_ERROR) \
+ MOZILLA_PKIX_MAP(ERROR_UNKNOWN_ISSUER, 20, \
+ SEC_ERROR_UNKNOWN_ISSUER) \
+ MOZILLA_PKIX_MAP(ERROR_UNTRUSTED_CERT, 21, \
+ SEC_ERROR_UNTRUSTED_CERT) \
+ MOZILLA_PKIX_MAP(ERROR_UNTRUSTED_ISSUER, 22, \
+ SEC_ERROR_UNTRUSTED_ISSUER) \
+ MOZILLA_PKIX_MAP(ERROR_OCSP_BAD_SIGNATURE, 23, \
+ SEC_ERROR_OCSP_BAD_SIGNATURE) \
+ MOZILLA_PKIX_MAP(ERROR_OCSP_INVALID_SIGNING_CERT, 24, \
+ SEC_ERROR_OCSP_INVALID_SIGNING_CERT) \
+ MOZILLA_PKIX_MAP(ERROR_OCSP_MALFORMED_REQUEST, 25, \
+ SEC_ERROR_OCSP_MALFORMED_REQUEST) \
+ MOZILLA_PKIX_MAP(ERROR_OCSP_MALFORMED_RESPONSE, 26, \
+ SEC_ERROR_OCSP_MALFORMED_RESPONSE) \
+ MOZILLA_PKIX_MAP(ERROR_OCSP_OLD_RESPONSE, 27, \
+ SEC_ERROR_OCSP_OLD_RESPONSE) \
+ MOZILLA_PKIX_MAP(ERROR_OCSP_REQUEST_NEEDS_SIG, 28, \
+ SEC_ERROR_OCSP_REQUEST_NEEDS_SIG) \
+ MOZILLA_PKIX_MAP(ERROR_OCSP_RESPONDER_CERT_INVALID, 29, \
+ SEC_ERROR_OCSP_RESPONDER_CERT_INVALID) \
+ MOZILLA_PKIX_MAP(ERROR_OCSP_SERVER_ERROR, 30, \
+ SEC_ERROR_OCSP_SERVER_ERROR) \
+ MOZILLA_PKIX_MAP(ERROR_OCSP_TRY_SERVER_LATER, 31, \
+ SEC_ERROR_OCSP_TRY_SERVER_LATER) \
+ MOZILLA_PKIX_MAP(ERROR_OCSP_UNAUTHORIZED_REQUEST, 32, \
+ SEC_ERROR_OCSP_UNAUTHORIZED_REQUEST) \
+ MOZILLA_PKIX_MAP(ERROR_OCSP_UNKNOWN_RESPONSE_STATUS, 33, \
+ SEC_ERROR_OCSP_UNKNOWN_RESPONSE_STATUS) \
+ MOZILLA_PKIX_MAP(ERROR_OCSP_UNKNOWN_CERT, 34, \
+ SEC_ERROR_OCSP_UNKNOWN_CERT) \
+ MOZILLA_PKIX_MAP(ERROR_OCSP_FUTURE_RESPONSE, 35, \
+ SEC_ERROR_OCSP_FUTURE_RESPONSE) \
+ MOZILLA_PKIX_MAP(ERROR_INVALID_KEY, 36, \
+ SEC_ERROR_INVALID_KEY) \
+ MOZILLA_PKIX_MAP(ERROR_UNSUPPORTED_KEYALG, 37, \
+ SEC_ERROR_UNSUPPORTED_KEYALG) \
+ MOZILLA_PKIX_MAP(ERROR_EXPIRED_ISSUER_CERTIFICATE, 38, \
+ SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE) \
+ MOZILLA_PKIX_MAP(ERROR_CA_CERT_USED_AS_END_ENTITY, 39, \
+ MOZILLA_PKIX_ERROR_CA_CERT_USED_AS_END_ENTITY) \
+ MOZILLA_PKIX_MAP(ERROR_INADEQUATE_KEY_SIZE, 40, \
+ MOZILLA_PKIX_ERROR_INADEQUATE_KEY_SIZE) \
+ MOZILLA_PKIX_MAP(FATAL_ERROR_INVALID_ARGS, FATAL_ERROR_FLAG | 1, \
+ SEC_ERROR_INVALID_ARGS) \
+ MOZILLA_PKIX_MAP(FATAL_ERROR_INVALID_STATE, FATAL_ERROR_FLAG | 2, \
+ PR_INVALID_STATE_ERROR) \
+ MOZILLA_PKIX_MAP(FATAL_ERROR_LIBRARY_FAILURE, FATAL_ERROR_FLAG | 3, \
+ SEC_ERROR_LIBRARY_FAILURE) \
+ MOZILLA_PKIX_MAP(FATAL_ERROR_NO_MEMORY, FATAL_ERROR_FLAG | 4, \
+ SEC_ERROR_NO_MEMORY) \
+ /* nothing here */
+
+MOZILLA_PKIX_ENUM_CLASS Result
+{
+#define MOZILLA_PKIX_MAP(name, value, nss_name) name = value,
+ MOZILLA_PKIX_MAP_LIST
+#undef MOZILLA_PKIX_MAP
+};
+
+// Returns the stringified name of the given result, e.g. "Result::Success",
+// or nullptr if result is unknown (invalid).
+const char* MapResultToName(Result result);
+
+// We write many comparisons as (x != Success), and this shortened name makes
+// those comparisons clearer, especially because the shortened name often
+// results in less line wrapping.
+//
+// Visual Studio before VS2013 does not support "enum class," so
+// Result::Success will already be visible in this scope, and compilation will
+// fail if we try to define a variable with that name here.
+#if !defined(_MSC_VER) || (_MSC_VER >= 1700)
+static const Result Success = Result::Success;
+#endif
+
+inline bool
+IsFatalError(Result rv)
+{
+ return (static_cast<unsigned int>(rv) & FATAL_ERROR_FLAG) != 0;
+}
+
+inline Result
+NotReached(const char* /*explanation*/, Result result)
+{
+ assert(false);
+ return result;
+}
+
+} } // namespace mozilla::pkix
+
+#endif // mozilla_pkix__Result_h
diff --git a/security/pkix/include/pkix/Time.h b/security/pkix/include/pkix/Time.h
new file mode 100644
index 0000000..b8d6ee9
--- /dev/null
+++ b/security/pkix/include/pkix/Time.h
@@ -0,0 +1,126 @@
+/* -*- 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 code is made available to you under your choice of the following sets
+ * of licensing terms:
+ */
+/* 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/.
+ */
+/* Copyright 2014 Mozilla Contributors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef mozilla_pkix__Time_h
+#define mozilla_pkix__Time_h
+
+#include <ctime>
+#include <limits>
+#include <stdint.h>
+
+#include "pkix/Result.h"
+
+namespace mozilla { namespace pkix {
+
+// Time with a range from the first second of year 0 (AD) through at least the
+// last second of year 9999, which is the range of legal times in X.509 and
+// OCSP. This type has second-level precision. The time zone is always UTC.
+//
+// Pass by value, not by reference.
+class Time
+{
+public:
+ // Construct an uninitilized instance.
+ //
+ // This will fail to compile because there is no default constructor:
+ // Time x;
+ //
+ // This will succeed, leaving the time uninitialized:
+ // Time x(Time::uninitialized);
+ enum Uninitialized { uninitialized };
+ explicit Time(Uninitialized) { }
+
+ bool operator==(const Time& other) const
+ {
+ return elapsedSecondsAD == other.elapsedSecondsAD;
+ }
+ bool operator>(const Time& other) const
+ {
+ return elapsedSecondsAD > other.elapsedSecondsAD;
+ }
+ bool operator>=(const Time& other) const
+ {
+ return elapsedSecondsAD >= other.elapsedSecondsAD;
+ }
+ bool operator<(const Time& other) const
+ {
+ return elapsedSecondsAD < other.elapsedSecondsAD;
+ }
+ bool operator<=(const Time& other) const
+ {
+ return elapsedSecondsAD <= other.elapsedSecondsAD;
+ }
+
+ Result AddSeconds(uint64_t seconds)
+ {
+ if (std::numeric_limits<uint64_t>::max() - elapsedSecondsAD
+ < seconds) {
+ return Result::FATAL_ERROR_INVALID_ARGS; // integer overflow
+ }
+ elapsedSecondsAD += seconds;
+ return Success;
+ }
+
+ Result SubtractSeconds(uint64_t seconds)
+ {
+ if (seconds > elapsedSecondsAD) {
+ return Result::FATAL_ERROR_INVALID_ARGS; // integer overflow
+ }
+ elapsedSecondsAD -= seconds;
+ return Success;
+ }
+
+ static const uint64_t ONE_DAY_IN_SECONDS
+ = UINT64_C(24) * UINT64_C(60) * UINT64_C(60);
+
+private:
+ // This constructor is hidden to prevent accidents like this:
+ //
+ // Time foo(time_t t)
+ // {
+ // // WRONG! 1970-01-01-00:00:00 == time_t(0), but not Time(0)!
+ // return Time(t);
+ // }
+ explicit Time(uint64_t elapsedSecondsAD)
+ : elapsedSecondsAD(elapsedSecondsAD)
+ {
+ }
+ friend Time TimeFromElapsedSecondsAD(uint64_t);
+
+ uint64_t elapsedSecondsAD;
+};
+
+inline Time TimeFromElapsedSecondsAD(uint64_t elapsedSecondsAD)
+{
+ return Time(elapsedSecondsAD);
+}
+
+Time Now();
+
+// Note the epoch is the unix epoch (ie 00:00:00 UTC, 1 January 1970)
+Time TimeFromEpochInSeconds(uint64_t secondsSinceEpoch);
+
+} } // namespace mozilla::pkix
+
+#endif // mozilla_pkix__Time_h
diff --git a/security/pkix/include/pkix/pkixtypes.h b/security/pkix/include/pkix/pkixtypes.h
index 0a64b9d..d38603b 100644
--- a/security/pkix/include/pkix/pkixtypes.h
+++ b/security/pkix/include/pkix/pkixtypes.h
@@ -120,6 +120,11 @@ public:
PRTime time,
/*optional*/ const SECItem* stapledOCSPresponse) = 0;
+ // Called as soon as we think we have a valid chain but before revocation
+ // checks are done. Called to compute additional chain level checks, by the
+ // TrustDomain.
+ virtual SECStatus IsChainValid(const CERTCertList* certChain) = 0;
+
protected:
TrustDomain() { }
diff --git a/security/pkix/lib/pkixbuild.cpp b/security/pkix/lib/pkixbuild.cpp
index 22e3f61..c078eda 100644
--- a/security/pkix/lib/pkixbuild.cpp
+++ b/security/pkix/lib/pkixbuild.cpp
@@ -225,6 +225,30 @@ BuildForward(TrustDomain& trustDomain,
}
if (trustLevel == TrustDomain::TrustAnchor) {
+ ScopedCERTCertList certChain(CERT_NewCertList());
+ if (!certChain) {
+ PR_SetError(SEC_ERROR_NO_MEMORY, 0);
+ return MapSECStatus(SECFailure);
+ }
+
+ rv = subject.PrependNSSCertToList(certChain.get());
+ if (rv != Success) {
+ return rv;
+ }
+ BackCert* child = subject.childCert;
+ while (child) {
+ rv = child->PrependNSSCertToList(certChain.get());
+ if (rv != Success) {
+ return rv;
+ }
+ child = child->childCert;
+ }
+
+ SECStatus srv = trustDomain.IsChainValid(certChain.get());
+ if (srv != SECSuccess) {
+ return MapSECStatus(srv);
+ }
+
// End of the recursion. Create the result list and add the trust anchor to
// it.
results = CERT_NewCertList();
diff --git a/security/pkix/lib/pkixtime.cpp b/security/pkix/lib/pkixtime.cpp
new file mode 100644
index 0000000..499784e
--- /dev/null
+++ b/security/pkix/lib/pkixtime.cpp
@@ -0,0 +1,70 @@
+/* -*- 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 code is made available to you under your choice of the following sets
+ * of licensing terms:
+ */
+/* 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/.
+ */
+/* Copyright 2014 Mozilla Contributors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "pkix/Time.h"
+#include "pkixutil.h"
+#ifdef WIN32
+#include "windows.h"
+#else
+#include "sys/time.h"
+#endif
+
+namespace mozilla { namespace pkix {
+
+Time
+Now()
+{
+ uint64_t seconds;
+
+#ifdef WIN32
+ // "Contains a 64-bit value representing the number of 100-nanosecond
+ // intervals since January 1, 1601 (UTC)."
+ // - http://msdn.microsoft.com/en-us/library/windows/desktop/ms724284(v=vs.85).a…
+ FILETIME ft;
+ GetSystemTimeAsFileTime(&ft);
+ uint64_t ft64 = (static_cast<uint64_t>(ft.dwHighDateTime) << 32) |
+ ft.dwLowDateTime;
+ seconds = (DaysBeforeYear(1601) * Time::ONE_DAY_IN_SECONDS) +
+ ft64 / (1000u * 1000u * 1000u / 100u);
+#else
+ // "The gettimeofday() function shall obtain the current time, expressed as
+ // seconds and microseconds since the Epoch."
+ // - http://pubs.opengroup.org/onlinepubs/009695399/functions/gettimeofday.html
+ timeval tv;
+ (void) gettimeofday(&tv, nullptr);
+ seconds = (DaysBeforeYear(1970) * Time::ONE_DAY_IN_SECONDS) + tv.tv_sec;
+#endif
+
+ return TimeFromElapsedSecondsAD(seconds);
+}
+
+Time
+TimeFromEpochInSeconds(uint64_t secondsSinceEpoch)
+{
+ uint64_t seconds = (DaysBeforeYear(1970) * Time::ONE_DAY_IN_SECONDS) +
+ secondsSinceEpoch;
+ return TimeFromElapsedSecondsAD(seconds);
+}
+
+} } // namespace mozilla::pkix
diff --git a/toolkit/components/telemetry/Histograms.json b/toolkit/components/telemetry/Histograms.json
index 01a27a1..bf96ccd 100644
--- a/toolkit/components/telemetry/Histograms.json
+++ b/toolkit/components/telemetry/Histograms.json
@@ -5929,5 +5929,37 @@
"high": "5000",
"n_buckets": 10,
"extended_statistics_ok": true
+ },
+ "CERT_PINNING_RESULTS": {
+ "expires_in_version": "never",
+ "kind": "boolean",
+ "description": "Certificate pinning results (0 = failure, 1 = success)"
+ },
+ "CERT_PINNING_TEST_RESULTS": {
+ "expires_in_version": "never",
+ "kind": "boolean",
+ "description": "Certificate pinning test results (0 = failure, 1 = success)"
+ },
+ "CERT_PINNING_MOZ_RESULTS": {
+ "expires_in_version": "never",
+ "kind": "boolean",
+ "description": "Certificate pinning results for Mozilla sites (0 = failure, 1 = success)"
+ },
+ "CERT_PINNING_MOZ_TEST_RESULTS": {
+ "expires_in_version": "never",
+ "kind": "boolean",
+ "description": "Certificate pinning test results for Mozilla sites (0 = failure, 1 = success)"
+ },
+ "CERT_PINNING_MOZ_RESULTS_BY_HOST": {
+ "expires_in_version": "never",
+ "kind": "enumerated",
+ "n_values": 512,
+ "description": "Certificate pinning results by host for Mozilla operational sites"
+ },
+ "CERT_PINNING_MOZ_TEST_RESULTS_BY_HOST": {
+ "expires_in_version": "never",
+ "kind": "enumerated",
+ "n_values": 512,
+ "description": "Certificate pinning test results by host for Mozilla operational sites"
}
}
1
0

[tor-browser/tor-browser-31.2.0esr-4.5-1] fixup! TB3: Tor Browser's official .mozconfigs.
by mikeperry@torproject.org 30 Oct '14
by mikeperry@torproject.org 30 Oct '14
30 Oct '14
commit 672d66cc8b67815fb506ab78c231468098f8462c
Author: Mike Perry <mikeperry-git(a)torproject.org>
Date: Wed Oct 29 15:19:19 2014 -0700
fixup! TB3: Tor Browser's official .mozconfigs.
Re-enable ICU.
---
.mozconfig-mac | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.mozconfig-mac b/.mozconfig-mac
index 4abcc7c..9a38951 100644
--- a/.mozconfig-mac
+++ b/.mozconfig-mac
@@ -42,7 +42,7 @@ ac_add_options --enable-update-packaging
ac_add_options --disable-verify-mar
# ICU seems still to have cross-compiling issues:
-ac_add_options --without-intl-api
+#ac_add_options --without-intl-api
ac_add_options --disable-crashreporter
ac_add_options --disable-maintenance-service
ac_add_options --disable-webrtc
1
0

[translation/gettor_completed] Update translations for gettor_completed
by translation@torproject.org 29 Oct '14
by translation@torproject.org 29 Oct '14
29 Oct '14
commit 137f6a4fd9cd576f622c5cf2abd9007e7dfede6a
Author: Translation commit bot <translation(a)torproject.org>
Date: Wed Oct 29 20:15:09 2014 +0000
Update translations for gettor_completed
---
ru/gettor.po | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/ru/gettor.po b/ru/gettor.po
index 9b902d9..500304a 100644
--- a/ru/gettor.po
+++ b/ru/gettor.po
@@ -16,7 +16,7 @@ msgstr ""
"Project-Id-Version: The Tor Project\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2013-01-19 13:40+0100\n"
-"PO-Revision-Date: 2014-10-29 13:30+0000\n"
+"PO-Revision-Date: 2014-10-29 20:00+0000\n"
"Last-Translator: Andrey Yoker Ogurchikov <domovoy.yoker(a)gmail.com>\n"
"Language-Team: Russian (http://www.transifex.com/projects/p/torproject/language/ru/)\n"
"MIME-Version: 1.0\n"
@@ -307,7 +307,7 @@ msgstr "Если он не приходит, возможно пакет сли
msgid ""
"Unfortunately we are currently experiencing problems and we can't fulfill\n"
"your request right now. Please be patient as we try to resolve this issue."
-msgstr "К сожалению сейчас мы испытываем определенные проблемы и не можем выполнить ваш запрос на данный момент.\nПожалуйста, будьте терпеливы, пока мы пытаемся решить эту проблему."
+msgstr "К сожалению, сейчас мы испытываем определенные проблемы и не можем выполнить ваш запрос.\nПожалуйста, будьте терпеливы, мы пытаемся решить эту проблему."
#: lib/gettor/i18n.py:197
msgid ""
1
0

29 Oct '14
commit 36a63e5a93e5c1a2bbf5858fbda2e2dc56a191d8
Author: Translation commit bot <translation(a)torproject.org>
Date: Wed Oct 29 20:15:05 2014 +0000
Update translations for gettor
---
ru/gettor.po | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/ru/gettor.po b/ru/gettor.po
index 9b902d9..500304a 100644
--- a/ru/gettor.po
+++ b/ru/gettor.po
@@ -16,7 +16,7 @@ msgstr ""
"Project-Id-Version: The Tor Project\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2013-01-19 13:40+0100\n"
-"PO-Revision-Date: 2014-10-29 13:30+0000\n"
+"PO-Revision-Date: 2014-10-29 20:00+0000\n"
"Last-Translator: Andrey Yoker Ogurchikov <domovoy.yoker(a)gmail.com>\n"
"Language-Team: Russian (http://www.transifex.com/projects/p/torproject/language/ru/)\n"
"MIME-Version: 1.0\n"
@@ -307,7 +307,7 @@ msgstr "Если он не приходит, возможно пакет сли
msgid ""
"Unfortunately we are currently experiencing problems and we can't fulfill\n"
"your request right now. Please be patient as we try to resolve this issue."
-msgstr "К сожалению сейчас мы испытываем определенные проблемы и не можем выполнить ваш запрос на данный момент.\nПожалуйста, будьте терпеливы, пока мы пытаемся решить эту проблему."
+msgstr "К сожалению, сейчас мы испытываем определенные проблемы и не можем выполнить ваш запрос.\nПожалуйста, будьте терпеливы, мы пытаемся решить эту проблему."
#: lib/gettor/i18n.py:197
msgid ""
1
0