Source code
Revision control
Copy as Markdown
Other Tools
Test Info: Warnings
- This test gets skipped with pattern: os == 'win' && msix
- Manifest: security/manager/ssl/tests/unit/xpcshell.toml
// -*- 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
"use strict";
// This test tests that the nsIX509Cert.dbKey and nsIX509CertDB.findCertByDBKey
// APIs work as expected. That is, getting a certificate's dbKey and using it
// in findCertByDBKey should return the same certificate. Also, for backwards
// compatibility, findCertByDBKey should ignore any whitespace in its input
// (even though now nsIX509Cert.dbKey will never have whitespace in it).
function hexStringToBytes(hex) {
let bytes = [];
for (let hexByteStr of hex.split(":")) {
bytes.push(parseInt(hexByteStr, 16));
}
return bytes;
}
function encodeCommonNameAsBytes(commonName) {
// The encoding will look something like this (in hex):
// 30 (SEQUENCE) <length of contents>
// 31 (SET) <length of contents>
// 30 (SEQUENCE) <length of contents>
// 06 (OID) 03 (length)
// 55 04 03 (id-at-commonName)
// 0C (UTF8String) <length of common name>
// <common name bytes>
// To make things simple, it would be nice to have the length of each
// component be less than 128 bytes (so we can have single-byte lengths).
// For this to hold, the maximum length of the contents of the outermost
// SEQUENCE must be 127. Everything not in the contents of the common name
// will take up 11 bytes, so the value of the common name itself can be at
// most 116 bytes.
Assert.lessOrEqual(
commonName.length,
116,
"test assumption: common name can't be longer than 116 bytes (makes " +
"DER encoding easier)"
);
let commonNameOIDBytes = [0x06, 0x03, 0x55, 0x04, 0x03];
let commonNameBytes = [0x0c, commonName.length];
for (let i = 0; i < commonName.length; i++) {
commonNameBytes.push(commonName.charCodeAt(i));
}
let bytes = commonNameOIDBytes.concat(commonNameBytes);
bytes.unshift(bytes.length);
bytes.unshift(0x30); // SEQUENCE
bytes.unshift(bytes.length);
bytes.unshift(0x31); // SET
bytes.unshift(bytes.length);
bytes.unshift(0x30); // SEQUENCE
return bytes;
}
function testInvalidDBKey(certDB, dbKey) {
throws(
() => certDB.findCertByDBKey(dbKey),
/NS_ERROR_ILLEGAL_INPUT/,
`findCertByDBKey(${dbKey}) should raise NS_ERROR_ILLEGAL_INPUT`
);
}
function testDBKeyForNonexistentCert(certDB, dbKey) {
let cert = certDB.findCertByDBKey(dbKey);
ok(!cert, "shouldn't find cert for given dbKey");
}
function byteArrayToByteString(bytes) {
let byteString = "";
for (let b of bytes) {
byteString += String.fromCharCode(b);
}
return byteString;
}
function run_test() {
do_get_profile();
let certDB = Cc["@mozilla.org/security/x509certdb;1"].getService(
Ci.nsIX509CertDB
);
let cert = constructCertFromFile("bad_certs/test-ca.pem");
equal(
cert.issuerName,
"CN=" + cert.issuerCommonName,
"test assumption: this certificate's issuer distinguished name " +
"consists only of a common name"
);
let issuerBytes = encodeCommonNameAsBytes(cert.issuerCommonName);
Assert.less(
issuerBytes.length,
256,
"test assumption: length of encoded issuer is less than 256 bytes"
);
let serialNumberBytes = hexStringToBytes(cert.serialNumber);
Assert.less(
serialNumberBytes.length,
256,
"test assumption: length of encoded serial number is less than 256 bytes"
);
let dbKeyHeader = [
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
serialNumberBytes.length,
0,
0,
0,
issuerBytes.length,
];
let expectedDbKeyBytes = dbKeyHeader.concat(serialNumberBytes, issuerBytes);
let expectedDbKey = btoa(byteArrayToByteString(expectedDbKeyBytes));
equal(
cert.dbKey,
expectedDbKey,
"actual and expected dbKey values should match"
);
let certFromDbKey = certDB.findCertByDBKey(expectedDbKey);
ok(
areCertsEqual(certFromDbKey, cert),
"nsIX509CertDB.findCertByDBKey should find the right certificate"
);
Assert.greater(
expectedDbKey.length,
64,
"test assumption: dbKey should be longer than 64 characters"
);
let expectedDbKeyWithCRLF = expectedDbKey.replace(/(.{64})/, "$1\r\n");
Assert.equal(
expectedDbKeyWithCRLF.indexOf("\r\n"),
64,
"test self-check: adding CRLF to dbKey should succeed"
);
certFromDbKey = certDB.findCertByDBKey(expectedDbKeyWithCRLF);
ok(
areCertsEqual(certFromDbKey, cert),
"nsIX509CertDB.findCertByDBKey should work with dbKey with CRLF"
);
let expectedDbKeyWithSpaces = expectedDbKey.replace(/(.{64})/, "$1 ");
Assert.equal(
expectedDbKeyWithSpaces.indexOf(" "),
64,
"test self-check: adding spaces to dbKey should succeed"
);
certFromDbKey = certDB.findCertByDBKey(expectedDbKeyWithSpaces);
ok(
areCertsEqual(certFromDbKey, cert),
"nsIX509CertDB.findCertByDBKey should work with dbKey with spaces"
);
// Test some invalid dbKey values.
testInvalidDBKey(certDB, "AAAA"); // Not long enough.
// No header.
testInvalidDBKey(
certDB,
btoa(
byteArrayToByteString(
[0, 0, 0, serialNumberBytes.length, 0, 0, 0, issuerBytes.length].concat(
serialNumberBytes,
issuerBytes
)
)
)
);
testInvalidDBKey(
certDB,
btoa(
byteArrayToByteString([
0,
0,
0,
0,
0,
0,
0,
0,
255,
255,
255,
255, // serial number length is way too long
255,
255,
255,
255, // issuer length is way too long
0,
0,
0,
0,
])
)
);
// Truncated issuer.
testInvalidDBKey(
certDB,
btoa(
byteArrayToByteString([
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 10, 1, 1, 2, 3,
])
)
);
// Issuer doesn't decode to valid common name.
testDBKeyForNonexistentCert(
certDB,
btoa(
byteArrayToByteString([
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 3, 1, 1, 2, 3,
])
)
);
// zero-length serial number and issuer -> no such certificate
testDBKeyForNonexistentCert(
certDB,
btoa(
byteArrayToByteString([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
)
);
}