Bug 1252882 - Content-Signature Service - some tests r=keeler,r=fkiefer
authorMark Goodwin <mgoodwin@mozilla.com>
Fri, 08 Apr 2016 14:27:52 +0100
changeset 331122 8206ecdf17e8c08cf6127d1b2bcc572c83f40bbe
parent 331121 7baeeb594c97bf30c780108f7ce6e0bb2c458cb3
child 331123 cb6b876450fb64170ba9d4b287351401c0b06c4a
push id6048
push userkmoir@mozilla.com
push dateMon, 06 Jun 2016 19:02:08 +0000
treeherdermozilla-beta@46d72a56c57d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskeeler, fkiefer
bugs1252882
milestone48.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1252882 - Content-Signature Service - some tests r=keeler,r=fkiefer MozReview-Commit-ID: AQGAABvRbNZ
security/manager/ssl/tests/unit/test_content_signing.js
security/manager/ssl/tests/unit/test_content_signing/content_signing_int.pem
security/manager/ssl/tests/unit/test_content_signing/content_signing_int.pem.certspec
security/manager/ssl/tests/unit/test_content_signing/content_signing_onecrl_RSA_ee.pem
security/manager/ssl/tests/unit/test_content_signing/content_signing_onecrl_RSA_ee.pem.certspec
security/manager/ssl/tests/unit/test_content_signing/content_signing_onecrl_ee.pem
security/manager/ssl/tests/unit/test_content_signing/content_signing_onecrl_ee.pem.certspec
security/manager/ssl/tests/unit/test_content_signing/content_signing_onecrl_wrong_key_ee.pem
security/manager/ssl/tests/unit/test_content_signing/content_signing_onecrl_wrong_key_ee.pem.certspec
security/manager/ssl/tests/unit/test_content_signing/content_signing_remote_newtab_ee.pem
security/manager/ssl/tests/unit/test_content_signing/content_signing_remote_newtab_ee.pem.certspec
security/manager/ssl/tests/unit/test_content_signing/content_signing_root.pem
security/manager/ssl/tests/unit/test_content_signing/content_signing_root.pem.certspec
security/manager/ssl/tests/unit/test_content_signing/moz.build
security/manager/ssl/tests/unit/test_content_signing/pysign.py
security/manager/ssl/tests/unit/test_content_signing/test.txt
security/manager/ssl/tests/unit/test_content_signing/test.txt.signature
security/manager/ssl/tests/unit/xpcshell.ini
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_content_signing.js
@@ -0,0 +1,235 @@
+/* -*- 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/. */
+"use strict";
+
+// These tests ensure content signatures are working correctly.
+
+// First, we need to set up some data
+const PREF_SIGNATURE_ROOT = "security.content.signature.root_hash";
+
+const TEST_DATA_DIR = "test_content_signing/";
+
+function getSignatureVerifier() {
+  return Cc["@mozilla.org/security/contentsignatureverifier;1"]
+           .createInstance(Ci.nsIContentSignatureVerifier);
+}
+
+function setRoot(filename) {
+  let cert = constructCertFromFile(filename);
+  Services.prefs.setCharPref(PREF_SIGNATURE_ROOT, cert.sha256Fingerprint);
+}
+
+function loadChain(prefix, names) {
+  let chain = [];
+  for (let name of names) {
+    let filename = `${prefix}_${name}.pem`;
+    chain.push(readFile(do_get_file(filename)));
+  }
+  return chain;
+}
+
+function run_test() {
+  // set up some data
+  const DATA = readFile(do_get_file(TEST_DATA_DIR + 'test.txt'));
+  const GOOD_SIGNATURE = "p384ecdsa=" +
+      readFile(do_get_file(TEST_DATA_DIR + 'test.txt.signature'))
+      .trim();
+
+  const BAD_SIGNATURE = "p384ecdsa=WqRXFQ7tnlVufpg7A-ZavXvWd2Zln0o4woHBy26C2r" +
+                        "UWM4GJke4pE8ecHiXoi-7KnZXty6Pe3s4o3yAIyKDP9jUC52Ek1G" +
+                        "q25j_X703nP5rk5gM1qz5Fe-qCWakPPl6L";
+
+  setRoot(TEST_DATA_DIR + "content_signing_root.pem");
+
+  let remoteNewTabChain = loadChain(TEST_DATA_DIR + "content_signing",
+                                    ["remote_newtab_ee", "int", "root"]);
+
+  let oneCRLChain = loadChain(TEST_DATA_DIR + "content_signing",
+                              ["onecrl_ee", "int", "root"]);
+
+  let oneCRLBadKeyChain = loadChain(TEST_DATA_DIR + "content_signing",
+                                    ["onecrl_wrong_key_ee", "int", "root"]);
+
+  let oneCRLRSAKeyChain = loadChain(TEST_DATA_DIR + "content_signing",
+                                    ["onecrl_RSA_ee", "int", "root"]);
+
+  // Check good signatures from good certificates with the correct SAN
+  let chain1 = oneCRLChain.join("\n");
+  let verifier = getSignatureVerifier();
+  ok(verifier.verifyContentSignature(DATA, GOOD_SIGNATURE, chain1,
+                                     verifier.ONECRL),
+     "A OneCRL signature should verify with the OneCRL chain");
+  let chain2 = remoteNewTabChain.join("\n");
+  verifier = getSignatureVerifier();
+  ok(verifier.verifyContentSignature(DATA, GOOD_SIGNATURE, chain2,
+                                     verifier.ABOUT_NEWTAB),
+     "A newtab signature should verify with the newtab chain");
+
+  // Check a bad signature when a good chain is provided
+  chain1 = oneCRLChain.join("\n");
+  verifier = getSignatureVerifier();
+  ok(!verifier.verifyContentSignature(DATA, BAD_SIGNATURE, chain1,
+                                      verifier.ONECRL),
+     "A bad signature should not verify");
+
+  // Check a good signature from cert with good SAN but a different key than the
+  // one used to create the signature
+  let badKeyChain = oneCRLBadKeyChain.join("\n");
+  verifier = getSignatureVerifier();
+  ok(!verifier.verifyContentSignature(DATA, GOOD_SIGNATURE, badKeyChain,
+                                      verifier.ONECRL),
+     "A signature should not verify if the signing key is wrong");
+
+  // Check a good signature from cert with good SAN but a different key than the
+  // one used to create the signature (this time, an RSA key)
+  let rsaKeyChain = oneCRLBadKeyChain.join("\n");
+  verifier = getSignatureVerifier();
+  ok(!verifier.verifyContentSignature(DATA, GOOD_SIGNATURE, rsaKeyChain,
+                                      verifier.ONECRL),
+     "A signature should not verify if the signing key is wrong (RSA)");
+
+  // Check a good signature from cert with good SAN but with chain missing root
+  let missingRoot = [oneCRLChain[0], oneCRLChain[1]].join("\n");
+  verifier = getSignatureVerifier();
+  ok(!verifier.verifyContentSignature(DATA, GOOD_SIGNATURE, missingRoot,
+                                      verifier.ONECRL),
+     "A signature should not verify if the chain is incomplete (missing root)");
+
+  // Check a good signature from cert with good SAN but with no path to root
+  let missingInt = [oneCRLChain[0], oneCRLChain[2]].join("\n");
+  verifier = getSignatureVerifier();
+  ok(!verifier.verifyContentSignature(DATA, GOOD_SIGNATURE, missingInt,
+                                      verifier.ONECRL),
+     "A signature should not verify if the chain is incomplete (missing int)");
+
+  // Check good signatures from good certificates with incorrect SANs
+  chain1 = oneCRLChain.join("\n");
+  verifier = getSignatureVerifier();
+  ok(!verifier.verifyContentSignature(DATA, GOOD_SIGNATURE, chain1,
+                                      verifier.ABOUT_NEWTAB),
+     "A OneCRL signature should not verify if the signer has the newtab SAN");
+
+  chain2 = remoteNewTabChain.join("\n");
+  verifier = getSignatureVerifier();
+  ok(!verifier.verifyContentSignature(DATA, GOOD_SIGNATURE, chain2,
+                                      verifier.ONECRL),
+     "A newtab signature should not verify if the signer has the OneCRL SAN");
+
+  // Check malformed signature data
+  chain1 = oneCRLChain.join("\n");
+  let bad_signatures = [
+    // wrong length
+    "p384ecdsa=WqRXFQ7tnlVufpg7A-ZavXvWd2Zln0o4woHBy26C2rUWM4GJke4pE8ecHiXoi-" +
+    "7KnZXty6Pe3s4o3yAIyKDP9jUC52Ek1Gq25j_X703nP5rk5gM1qz5Fe-qCWakPPl6L==",
+    // incorrectly encoded
+    "p384ecdsa='WqRXFQ7tnlVufpg7A-ZavXvWd2Zln0o4woHBy26C2rUWM4GJke4pE8ecHiXoi" +
+    "-7KnZXty6Pe3s4o3yAIyKDP9jUC52Ek1Gq25j_X703nP5rk5gM1qz5Fe-qCWakPPl6L=",
+    // missing directive
+    "other_directive=WqRXFQ7tnlVufpg7A-ZavXvWd2Zln0o4woHBy26C2rUWM4GJke4pE8ec" +
+    "HiXoi-7KnZXty6Pe3s4o3yAIyKDP9jUC52Ek1Gq25j_X703nP5rk5gM1qz5Fe-qCWakPPl6L",
+    // actually sha256 with RSA
+    "p384ecdsa=XS_jiQsS5qlzQyUKaA1nAnQn_OvxhvDfKybflB8Xe5gNH1wNmPGK1qN-jpeTfK" +
+    "6ob3l3gCTXrsMnOXMeht0kPP3wLfVgXbuuO135pQnsv0c-ltRMWLe56Cm4S4Z6E7WWKLPWaj" +
+    "jhAcG5dZxjffP9g7tuPP4lTUJztyc4d1z_zQZakEG7R0vN7P5_CaX9MiMzP4R7nC3H4Ba6yi" +
+    "yjlGvsZwJ_C5zDQzWWs95czUbMzbDScEZ_7AWnidw91jZn-fUK3xLb6m-Zb_b4GAqZ-vnXIf" +
+    "LpLB1Nzal42BQZn7i4rhAldYdcVvy7rOMlsTUb5Zz6vpVW9LCT9lMJ7Sq1xbU-0g=="
+    ];
+  for (let badSig of bad_signatures) {
+    throws(() => {
+      verifier = getSignatureVerifier();
+      verifier.verifyContentSignature(DATA, badSig, chain1,
+                                      verifier.ONECRL);
+    }, /NS_ERROR/, `Bad or malformed signature "${badSig}" should be rejected`);
+  }
+
+  // Check malformed and missing certificate chain data
+  let chainSuffix = [oneCRLChain[1], oneCRLChain[2]].join("\n");
+  let badChains = [
+    // no data
+    "",
+    // completely wrong data
+    "blah blah \n blah",
+    ];
+
+  let badSections = [
+    // data that looks like PEM but isn't
+    "-----BEGIN CERTIFICATE-----\nBSsPRlYp5+gaFMRIczwUzaioRfteCjr94xyz0g==\n",
+    "-----BEGIN CERTIFICATE-----\nBSsPRlYp5+gaFMRIczwUzaioRfteCjr94xyz0g==\n-----END CERTIFICATE-----",
+    // data that will start to parse but won't base64decode
+    "-----BEGIN CERTIFICATE-----\nnon-base64-stuff\n-----END CERTIFICATE-----",
+    // data with garbage outside of PEM sections
+    "this data is garbage\n-----BEGIN CERTIFICATE-----\nnon-base64-stuff\n" +
+    "-----END CERTIFICATE-----",
+    ];
+
+  for (let badSection of badSections) {
+    // ensure we test each bad section on its own...
+    badChains.push(badSection);
+    // ... and as part of a chain with good certificates
+    badChains.push(badSection + '\n' + chainSuffix);
+  }
+
+  for (let badChain of badChains) {
+    throws(() => {
+      verifier = getSignatureVerifier();
+      verifier.verifyContentSignature(DATA, GOOD_SIGNATURE, badChain,
+                                      verifier.ONECRL);
+    }, /NS_ERROR/, `Bad chain data starting "${badChain.substring(0, 80)}" ` +
+                   "should be rejected");
+  }
+
+  // Check the streaming interface works OK when a good chain / data
+  // combination is provided
+  chain1 = oneCRLChain.join("\n");
+  verifier = getSignatureVerifier();
+  verifier.createContext("", GOOD_SIGNATURE, chain1, verifier.ONECRL);
+  verifier.update(DATA);
+  ok(verifier.end(),
+     "A good signature should verify using the stream interface");
+
+  // Check that the streaming interface works with multiple update calls
+  verifier = getSignatureVerifier();
+  verifier.createContext("", GOOD_SIGNATURE, chain1, verifier.ONECRL);
+  for (let c of DATA) {
+    verifier.update(c);
+  }
+  ok(verifier.end(),
+     "A good signature should verify using multiple updates");
+
+  // Check that the streaming interface works with multiple update calls and
+  // some data provided in CreateContext
+  verifier = getSignatureVerifier();
+  let start = DATA.substring(0, 5);
+  let rest = DATA.substring(start.length);
+  verifier.createContext(start, GOOD_SIGNATURE, chain1, verifier.ONECRL);
+  for (let c of rest) {
+    verifier.update(c);
+  }
+  ok(verifier.end(),
+     "A good signature should verify using data in CreateContext and updates");
+
+  // Check that a bad chain / data combination fails
+  verifier = getSignatureVerifier();
+  verifier.createContext("", GOOD_SIGNATURE, chain1, verifier.ONECRL);
+  ok(!verifier.end(),
+     "A bad signature should fail using the stream interface");
+
+  // Check that re-creating a context throws ...
+  verifier = getSignatureVerifier();
+  verifier.createContext("", GOOD_SIGNATURE, chain1, verifier.ONECRL);
+
+  // ... firstly, creating a context explicitly
+  throws(() => {
+    verifier.createContext(DATA, GOOD_SIGNATURE, chain1, verifier.ONECRL);
+  }, /NS_ERROR/, "Ensure a verifier cannot be re-used with createContext");
+
+  // ... secondly, by calling verifyContentSignature
+  throws(() => {
+    verifier.verifyContentSignature(DATA, GOOD_SIGNATURE, chain1,
+                                    verifier.ONECRL);
+  }, /NS_ERROR/, "Ensure a verifier cannot be re-used with verifyContentSignature");
+
+  run_next_test();
+}
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_content_signing/content_signing_int.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC0TCCAbugAwIBAgIULYyr3v/0zZ+XiR22NH7hOcnj2FcwCwYJKoZIhvcNAQEL
+MA0xCzAJBgNVBAMMAmNhMCIYDzIwMTQxMTI3MDAwMDAwWhgPMjAxNzAyMDQwMDAw
+MDBaMBExDzANBgNVBAMMBmludC1DQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
+AQoCggEBALqIUahEjhbWQf1utogGNhA9PBPZ6uQ1SrTs9WhXbCR7wcclqODYH72x
+nAabbhqG8mvir1p1a2pkcQh6pVqnRYf3HNUknAJ+zUP8HmnQOCApk6sgw0nk27lM
+wmtsDu0Vgg/xfq1pGrHTAjqLKkHup3DgDw2N/WYLK7AkkqR9uYhheZCxV5A90jvF
+4LhIH6g304hD7ycW2FW3ZlqqfgKQLzp7EIAGJMwcbJetlmFbt+KWEsB1MaMMkd20
+yvf8rR0l0wnvuRcOp2jhs3svIm9p47SKlWEd7ibWJZ2rkQhONsscJAQsvxaLL+Xx
+j5kXMbiz/kkj+nJRxDHVA6zaGAo17Y0CAwEAAaMlMCMwDAYDVR0TBAUwAwEB/zAT
+BgNVHSUEDDAKBggrBgEFBQcDAzALBgkqhkiG9w0BAQsDggEBADfRBKSM08JF6vqz
+0EA+KNc0XIEAWApuHuwX6XXWeLgo6QN4E/9qfrsaO+C366WT+JDsjDOi40wW46SA
+XbguxtZQeZasNDUWp/leZix4RSJoHB7OllG1rgZJfN76zKVaXRGUmyQObkMMOJZe
+wIA0OBURT8ik9Z89pD0IWrqscds71Edfjt0hHgg63wVvIaklReZXvFOD3VmSCPNn
+2wB6ZzECcbhJpnzxZdsoMSGH0C6apYnNNTjqZjO90JVm/Ph/7nbi/KncYXA6ccl6
+Jz2mfiAquWIua2+CzBGbqjZVSATTpWCp+cXQJE1xka+hWUaL5HPTq1bTULRFlauZ
+HGl5lJk=
+-----END CERTIFICATE-----
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_content_signing/content_signing_int.pem.certspec
@@ -0,0 +1,4 @@
+issuer:ca
+subject:int-CA
+extension:basicConstraints:cA,
+extension:extKeyUsage:codeSigning
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_content_signing/content_signing_onecrl_RSA_ee.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC8DCCAdqgAwIBAgIUKcUEg+FhMf2HlBoz5wkUmLmAb6MwCwYJKoZIhvcNAQEL
+MBExDzANBgNVBAMMBmludC1DQTAiGA8yMDE0MTEyNzAwMDAwMFoYDzIwMTcwMjA0
+MDAwMDAwWjAUMRIwEAYDVQQDDAllZS1pbnQtQ0EwggEiMA0GCSqGSIb3DQEBAQUA
+A4IBDwAwggEKAoIBAQC6iFGoRI4W1kH9braIBjYQPTwT2erkNUq07PVoV2wke8HH
+Jajg2B+9sZwGm24ahvJr4q9adWtqZHEIeqVap0WH9xzVJJwCfs1D/B5p0DggKZOr
+IMNJ5Nu5TMJrbA7tFYIP8X6taRqx0wI6iypB7qdw4A8Njf1mCyuwJJKkfbmIYXmQ
+sVeQPdI7xeC4SB+oN9OIQ+8nFthVt2Zaqn4CkC86exCABiTMHGyXrZZhW7filhLA
+dTGjDJHdtMr3/K0dJdMJ77kXDqdo4bN7LyJvaeO0ipVhHe4m1iWdq5EITjbLHCQE
+LL8Wiy/l8Y+ZFzG4s/5JI/pyUcQx1QOs2hgKNe2NAgMBAAGjPTA7MBMGA1UdJQQM
+MAoGCCsGAQUFBwMDMCQGA1UdEQQdMBuCGW9uZUNSTC1zaWduZXIubW96aWxsYS5v
+cmcwCwYJKoZIhvcNAQELA4IBAQByIDFxtRWPyUUlbllCWQ4+2rKrSvEJRoa2DPK9
+OZR0Gxs1ebXCMf7DuXDDNoHCbPnPSykIM00JXkyGl0yd6lS5PyeujPmYYNf7/5sQ
+OEG7HbHun7BATbZqGKFMOSAhT1af0hAKf0dvtcTq34sI3LMDXr93dimNvmJQ3lR4
+Z1hKF9mZPl0AH6avVDj7h/vhy/kZpjGjOpddqNxt/oZFa9E2EnN9RKQZ+D6agkvq
+NxN0E+WBnZWs9Znn2N+wfCrFkacz/k9MhomEaPG9z6opGJs3BkGU56t6I1RDt9+m
+EeOoQUuKnTWkDgPmBNGOV3iacyCdpj1VtjqeAP2n5CDM7hC3
+-----END CERTIFICATE-----
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_content_signing/content_signing_onecrl_RSA_ee.pem.certspec
@@ -0,0 +1,4 @@
+issuer:int-CA
+subject:ee-int-CA
+extension:extKeyUsage:codeSigning
+extension:subjectAlternativeName:oneCRL-signer.mozilla.org
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_content_signing/content_signing_onecrl_ee.pem
@@ -0,0 +1,15 @@
+-----BEGIN CERTIFICATE-----
+MIICQjCCASygAwIBAgIUKcUEg+FhMf2HlBoz5wkUmLmAb6MwCwYJKoZIhvcNAQEL
+MBExDzANBgNVBAMMBmludC1DQTAiGA8yMDE0MTEyNzAwMDAwMFoYDzIwMTcwMjA0
+MDAwMDAwWjAUMRIwEAYDVQQDDAllZS1pbnQtQ0EwdjAQBgcqhkjOPQIBBgUrgQQA
+IgNiAAShaHJDNitcexiJ83kVRhWhxz+0je6GPgIpFdtgjiUt5LcTLajOmOgxU05q
+nAwLCcjWOa3oMgbluoE0c6EfozDgXajJbkOD/ieHPalxA74oiM/wAvBa9xof3cyD
+dKpuqc6jPTA7MBMGA1UdJQQMMAoGCCsGAQUFBwMDMCQGA1UdEQQdMBuCGW9uZUNS
+TC1zaWduZXIubW96aWxsYS5vcmcwCwYJKoZIhvcNAQELA4IBAQAES4IF3AN7ymjX
+AcHGUFyTsXmHc1HoiiQjU8lLByCqHgpOUta2uXprv4r0INn5ucS7QaBntEu27Xi7
+kuFVwoqT2vgvqJ9Wrj/CZGbPXQbgIl13r+q09C/NSQTQNDYd/UI1z2nObRQdSWON
+OyVlm//Z+2x1MTw1ejsZEX3TJH2c19GG16aw1an0nrocpS8IiTmGcT80HHOHFd2a
+06xSlC1sTfsQJiE7EvMrIwwAuPXhWWbYWS3XMWt8ELE0hF1ZArq+7dBM+hjA/THR
+Bk/ufbGwObRvtMsWBj2RYNklV3qxGGKiFA3+3uz8lF0bZ6OSsZlpaSFK87Z1mgER
+53WoVgJk
+-----END CERTIFICATE-----
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_content_signing/content_signing_onecrl_ee.pem.certspec
@@ -0,0 +1,5 @@
+issuer:int-CA
+subject:ee-int-CA
+subjectKey:secp384r1
+extension:extKeyUsage:codeSigning
+extension:subjectAlternativeName:oneCRL-signer.mozilla.org
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_content_signing/content_signing_onecrl_wrong_key_ee.pem
@@ -0,0 +1,14 @@
+-----BEGIN CERTIFICATE-----
+MIICJTCCAQ+gAwIBAgIUKcUEg+FhMf2HlBoz5wkUmLmAb6MwCwYJKoZIhvcNAQEL
+MBExDzANBgNVBAMMBmludC1DQTAiGA8yMDE0MTEyNzAwMDAwMFoYDzIwMTcwMjA0
+MDAwMDAwWjAUMRIwEAYDVQQDDAllZS1pbnQtQ0EwWTATBgcqhkjOPQIBBggqhkjO
+PQMBBwNCAARPv7u7YeD4+bGmClmshwTi7AULQj489y6SPyxPeUtFXCpp0jNFbDbE
+EZ0HBuAO7cjRk5DXmRt7LQejBOqgSqbAoz0wOzATBgNVHSUEDDAKBggrBgEFBQcD
+AzAkBgNVHREEHTAbghlvbmVDUkwtc2lnbmVyLm1vemlsbGEub3JnMAsGCSqGSIb3
+DQEBCwOCAQEAR6mj+xtPF6evUnm2M4FVJgkAK+A+LQ7lGDmvneik9Ma10tO1w9o6
+Vu7hWhekuMpyLc2dZrK0ImwNnmvqLHxz2cvN8vFRhmVaeS3mVKaxLL0oGI2V3jRq
+ofxQES9zyM6QbzBAWweRW/DbsmjF2k4CiO2AykHSkCiKr4p7UFUhqhH86HHW7KOl
+rwVX9BlIgEL3ooOprT3+VwGIWrN0DhSiV6BepqyxOhyH2DCdZCHHJCx5sttJbQPI
+/dwSfmJtr5s+rh17N63JOD6h/v0S1ZurL7xgrolKeEJ9aZ0w4pwwyOB/ZNKsC/Pp
+MBPm25DhBPxeN7smAHQXVkEQKJeCQX6xeg==
+-----END CERTIFICATE-----
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_content_signing/content_signing_onecrl_wrong_key_ee.pem.certspec
@@ -0,0 +1,5 @@
+issuer:int-CA
+subject:ee-int-CA
+subjectKey:secp256r1
+extension:extKeyUsage:codeSigning
+extension:subjectAlternativeName:oneCRL-signer.mozilla.org
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_content_signing/content_signing_remote_newtab_ee.pem
@@ -0,0 +1,15 @@
+-----BEGIN CERTIFICATE-----
+MIICSTCCATOgAwIBAgIUWQzTTfKLNZgX5ngi/ENiI2DO2kowCwYJKoZIhvcNAQEL
+MBExDzANBgNVBAMMBmludC1DQTAiGA8yMDE0MTEyNzAwMDAwMFoYDzIwMTcwMjA0
+MDAwMDAwWjAUMRIwEAYDVQQDDAllZS1pbnQtQ0EwdjAQBgcqhkjOPQIBBgUrgQQA
+IgNiAAShaHJDNitcexiJ83kVRhWhxz+0je6GPgIpFdtgjiUt5LcTLajOmOgxU05q
+nAwLCcjWOa3oMgbluoE0c6EfozDgXajJbkOD/ieHPalxA74oiM/wAvBa9xof3cyD
+dKpuqc6jRDBCMBMGA1UdJQQMMAoGCCsGAQUFBwMDMCsGA1UdEQQkMCKCIHJlbW90
+ZS1uZXd0YWItc2lnbmVyLm1vemlsbGEub3JnMAsGCSqGSIb3DQEBCwOCAQEAc2nE
+feYpA8WFyiPfZi56NgVgc8kXSKRNgplDtBHXK7gT7ICNQTSKkt+zHxnS9tAoXoix
+OGKsyp/8LNIYGMr4vHVNyOGnxuiLzAYjmDxXhp3t36xOFlU5Y7UaKf9G4feMXrNH
++q1SPYlP84keo1MaC5yhTZTTmJMKkRBsCbIVhfDnL3BUczxVZmk9F+7qK/trL222
+RoAaTZW5hdXUZrX630CYs1sQHWgL0B5rg2y9bwFk7toQ34JbjS0Z25e/MZUtFz19
+5tSjAZQHlLE6fAYZ3knrxF9xVMJCZf7gQqVphJzBtgy9yvTAtlMsrf6XS6sRRngz
+27HBxIpd4tYniYrtfg==
+-----END CERTIFICATE-----
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_content_signing/content_signing_remote_newtab_ee.pem.certspec
@@ -0,0 +1,5 @@
+issuer:int-CA
+subject:ee-int-CA
+subjectKey:secp384r1
+extension:extKeyUsage:codeSigning
+extension:subjectAlternativeName:remote-newtab-signer.mozilla.org
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_content_signing/content_signing_root.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIICzTCCAbegAwIBAgIUIVkGGA8HiO3RIKGjdOjVi+d6EVkwCwYJKoZIhvcNAQEL
+MA0xCzAJBgNVBAMMAmNhMCIYDzIwMTQxMTI3MDAwMDAwWhgPMjAxNzAyMDQwMDAw
+MDBaMA0xCzAJBgNVBAMMAmNhMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
+AQEAuohRqESOFtZB/W62iAY2ED08E9nq5DVKtOz1aFdsJHvBxyWo4NgfvbGcBptu
+Gobya+KvWnVramRxCHqlWqdFh/cc1SScAn7NQ/weadA4ICmTqyDDSeTbuUzCa2wO
+7RWCD/F+rWkasdMCOosqQe6ncOAPDY39ZgsrsCSSpH25iGF5kLFXkD3SO8XguEgf
+qDfTiEPvJxbYVbdmWqp+ApAvOnsQgAYkzBxsl62WYVu34pYSwHUxowyR3bTK9/yt
+HSXTCe+5Fw6naOGzey8ib2njtIqVYR3uJtYlnauRCE42yxwkBCy/Fosv5fGPmRcx
+uLP+SSP6clHEMdUDrNoYCjXtjQIDAQABoyUwIzAMBgNVHRMEBTADAQH/MBMGA1Ud
+JQQMMAoGCCsGAQUFBwMDMAsGCSqGSIb3DQEBCwOCAQEAlpbRzRIPnf43AwGfMvKP
+zOtntRy2nE9GlmY9I00uioHUnUrPLs8aw3UDtyiDWMGqcYysXGx9EX2Vk0POS4gf
+G6PA95F6GxTtbzIEZmTPVuzA/cfc9HU3HXDPqh+dySJ8/Ta4c4vX1lgeGGAvstNe
+q+9DaCGXs8MqMF8KtXNmOm3eS9q622hKEvTVEoxqj1t365kwKHaNpbObddQ6Xcny
+akvfh2L+8QbJSflcm8fL/JTup/2/cRG1ytOsaiXEr9JBEITOtQO0Ot/4Qzq+MJjv
+weaJ3hZ0c+cTy3tEvt+I7+lnW4Q5dB7aLR2/BZfLubhxz1SUVMuHfLH64fc0Uf1Q
+Nw==
+-----END CERTIFICATE-----
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_content_signing/content_signing_root.pem.certspec
@@ -0,0 +1,4 @@
+issuer:ca
+subject:ca
+extension:basicConstraints:cA,
+extension:extKeyUsage:codeSigning
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_content_signing/moz.build
@@ -0,0 +1,17 @@
+# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+# Temporarily disabled. See bug 1256495.
+#test_certificates = (
+#    'content_signing_root.pem',
+#    'content_signing_int.pem',
+#    'content_signing_onecrl_ee.pem',
+#    'content_signing_onecrl_wrong_key_ee.pem',
+#    'content_signing_remote_newtab_ee.pem',
+#)
+#
+#for test_certificate in test_certificates:
+#    GeneratedTestCertificate(test_certificate)
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_content_signing/pysign.py
@@ -0,0 +1,29 @@
+#!/usr/bin/env python
+#
+# 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/.
+
+"""
+Create an ECDSA signature on the P-384 curve using the SHA-384 hash of data from
+stdin. The key used for the signature is the secp384r1Encoded key used in pykey
+and pycert.
+
+The certificates for the content signature tests make use of this program.
+You can use pysign.py like this:
+
+cat test.txt | python pysign.py > test.txt.signature
+"""
+
+import base64
+import binascii
+import ecc
+import pykey
+
+from sys import stdin
+
+data = stdin.read()
+
+key = ecc.Key.Key.decode(binascii.unhexlify(pykey.ECCKey.secp384r1Encoded))
+sig = key.sign("Content-Signature:\00" + data, 'sha384')
+print base64.b64encode(sig).replace('+', '-').replace('/', '_')
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_content_signing/test.txt
@@ -0,0 +1,1 @@
+This is a test file to test content-signature verification with a PKI.
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_content_signing/test.txt.signature
@@ -0,0 +1,1 @@
+hSvmvvA7_QLedDsjRJGBevqLwjPILx1EtWSPP4A0fepaWWPuuZRB8VfDT2j07bKDacRsbmJjmvg_R4CpKmnoWF8-2w5lSszlFFDqYSvQVQxpKhu-HMM_qquu_l0KecQ2
--- a/security/manager/ssl/tests/unit/xpcshell.ini
+++ b/security/manager/ssl/tests/unit/xpcshell.ini
@@ -10,16 +10,17 @@ support-files =
   test_cert_embedded_null/**
   test_cert_keyUsage/**
   test_cert_sha1/**
   test_cert_signatures/**
   test_cert_trust/**
   test_cert_version/**
   test_certDB_import/**
   test_certviewer_invalid_oids/**
+  test_content_signing/**
   test_ev_certs/**
   test_getchain/**
   test_intermediate_basic_usage_constraints/**
   test_keysize/**
   test_keysize_ev/**
   test_name_constraints/**
   test_ocsp_fetch_method/**
   test_ocsp_url/**
@@ -48,16 +49,17 @@ run-sequentially = hardcoded ports
 [test_cert_sha1.js]
 [test_cert_signatures.js]
 [test_cert_trust.js]
 [test_cert_version.js]
 [test_certDB_import.js]
 [test_certviewer_invalid_oids.js]
 skip-if = toolkit == 'android' || buildapp == 'b2g'
 [test_constructX509FromBase64.js]
+[test_content_signing.js]
 [test_datasignatureverifier.js]
 [test_ev_certs.js]
 run-sequentially = hardcoded ports
 [test_getchain.js]
 [test_hash_algorithms.js]
 [test_hash_algorithms_wrap.js]
 # bug 1124289 - run_test_in_child violates the sandbox on b2g and android
 skip-if = toolkit == 'android' || toolkit == 'gonk'