diff --git a/tests/legacy/xhr.html b/tests/legacy/xhr.html
index 5aa868f54..28ba5dcb1 100644
--- a/tests/legacy/xhr.html
+++ b/tests/legacy/xhr.html
@@ -5,6 +5,7 @@
+
-
+
Could not load the flash SocketPool.
diff --git a/tests/legacy/xhr.js b/tests/legacy/xhr.js
index 1beb48799..fbe202cda 100644
--- a/tests/legacy/xhr.js
+++ b/tests/legacy/xhr.js
@@ -60,7 +60,7 @@ jQuery(function($)
{
$('#start').attr('disabled', 'disabled');
// meta! use tasks to run the task tests
- forge.task.start({
+ forge_task.start({
type: 'test',
run: function(task) {
task.next('starting', function(task) {
diff --git a/tests/security/cve-2025-12816.js b/tests/security/cve-2025-12816.js
new file mode 100644
index 000000000..bf26f26bb
--- /dev/null
+++ b/tests/security/cve-2025-12816.js
@@ -0,0 +1,75 @@
+'use strict';
+
+var assert = require('assert');
+var md = require('../../lib/md');
+var pkcs12 = require('../../lib/pkcs12');
+var asn1 = require('../../lib/asn1');
+var pki = require('../../lib/pki');
+
+/**
+ * Build a minimal certificate-only PKCS#12 (PFX) with a MAC.
+ */
+function buildCertOnlyPfxWithMac(password) {
+ var keys = pki.rsa.generateKeyPair({bits: 1024, e: 0x10001});
+ var cert = pki.createCertificate();
+ cert.publicKey = keys.publicKey;
+ cert.serialNumber = '01';
+ cert.validity.notBefore = new Date();
+ cert.validity.notAfter = new Date();
+ cert.validity.notAfter.setFullYear(cert.validity.notBefore.getFullYear() + 1);
+ cert.setSubject([{name: 'commonName', value: 'p12-demo'}]);
+ cert.setIssuer([{name: 'commonName', value: 'p12-demo'}]);
+ cert.sign(keys.privateKey, md.sha256.create());
+
+ return pkcs12.toPkcs12Asn1(
+ null,
+ cert,
+ password,
+ {useMac: true, count: 2048, saltSize: 8}
+ );
+}
+
+/**
+ * Replace macData node with arbitrary junk.
+ */
+function corruptMacData(pfxAsn1) {
+ var clone = asn1.fromDer(asn1.toDer(pfxAsn1).getBytes());
+ // Replace 3rd element (macData) with garbage node
+ clone.value[2] = asn1.create(
+ asn1.Class.UNIVERSAL,
+ asn1.Type.OCTETSTRING,
+ false,
+ 'JUNK'
+ );
+ return clone;
+}
+
+describe('PKCS#12 MAC corruption', function() {
+ var correctPw = 'correct-horse-battery-staple';
+ var wrongPw = 'wrong-password';
+ var legit;
+
+ before(function() {
+ legit = buildCertOnlyPfxWithMac(correctPw);
+ });
+
+ it('should accept valid PFX with correct password', function() {
+ assert.doesNotThrow(function() {
+ var obj = pkcs12.pkcs12FromAsn1(legit, true, correctPw);
+ assert(obj, 'pkcs12FromAsn1 should return an object');
+ });
+ });
+
+ it('should reject valid PFX with wrong password (MAC mismatch)', function() {
+ assert.throws(function() {
+ pkcs12.pkcs12FromAsn1(legit, true, wrongPw);
+ }, /PKCS#12 MAC could not be verified. Invalid password?/);
+ });
+
+ it('should reject tampered PFX with corrupted macData', function() {
+ var tampered = corruptMacData(legit);
+ assert.throws(function() {
+ pkcs12.pkcs12FromAsn1(tampered, true, wrongPw);
+ }, /Invalid PKCS#12. macData field present but MAC was not validated./);
+ });
+});
diff --git a/tests/security/ghsa-554w-wpv2-vw27.js b/tests/security/ghsa-554w-wpv2-vw27.js
new file mode 100644
index 000000000..7fd743f2c
--- /dev/null
+++ b/tests/security/ghsa-554w-wpv2-vw27.js
@@ -0,0 +1,81 @@
+/*
+ * Regression Test for GHSA-554w-wpv2-vw27
+ * Verifies that the parser enforces a maximum recursion depth
+ * instead of crashing with a call stack overflow.
+ */
+var assert = require('assert');
+var asn1 = require('../../lib/asn1');
+var util = require('../../lib/util');
+
+describe('GHSA-554w-wpv2-vw27 Security Patch', function() {
+
+ function createNestedDer(depth) {
+ var obj = asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false, '\x00');
+ for(var i = 0; i < depth; i++) {
+ obj = asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [obj]);
+ }
+ return asn1.toDer(obj).getBytes();
+ }
+
+ beforeEach(function() {
+ // check max depth is the default
+ assert.equal(asn1.maxDepth, 256);
+ });
+
+ it('should throw a manageable error when default recursion depth is exceeded', function() {
+ // create a payload just above the default limit (256)
+ var DANGEROUS_DEPTH = 257;
+ var der = createNestedDer(DANGEROUS_DEPTH);
+ var buf = util.createBuffer(der);
+
+ // assert that it throws the correct error
+ assert.throws(function() {
+ asn1.fromDer(buf, {strict: true});
+ }, /ASN.1 parsing error: Max depth exceeded./);
+ });
+
+ it('should throw a manageable error when optional recursion depth is exceeded', function() {
+ // create a payload just above the optional defined limit (128)
+ var DANGEROUS_DEPTH = 129;
+ var der = createNestedDer(DANGEROUS_DEPTH);
+ var buf = util.createBuffer(der);
+
+ // assert that it throws the correct error
+ assert.throws(function() {
+ asn1.fromDer(buf, {strict: true, maxDepth: 128});
+ }, /ASN.1 parsing error: Max depth exceeded./);
+ });
+
+ it('should still parse valid nested structures with new default limits', function() {
+ var oldMaxDepth = asn1.maxDepth;
+ asn1.maxDepth = 258;
+
+ // create a payload just above the default limit (256)
+ var DANGEROUS_DEPTH = 257;
+ var der = createNestedDer(DANGEROUS_DEPTH);
+ var buf = util.createBuffer(der);
+
+ // verify with new default depth
+ asn1.fromDer(buf, {strict: true});
+
+ asn1.maxDepth = oldMaxDepth;
+ });
+
+ it('should still parse valid nested structures within default limits', function() {
+ // verify we didn't break default depth functionality
+ var SAFE_DEPTH = 20;
+ var der = createNestedDer(SAFE_DEPTH);
+ var buf = util.createBuffer(der);
+
+ asn1.fromDer(buf, {strict: true});
+ });
+
+ it('should still parse valid nested structures within optional limits', function() {
+ // verify we didn't break optional depth functionality
+ var SAFE_DEPTH = 20;
+ var der = createNestedDer(SAFE_DEPTH);
+ var buf = util.createBuffer(der);
+
+ asn1.fromDer(buf, {strict: true, maxDepth: 128});
+ });
+});
diff --git a/tests/security/index.js b/tests/security/index.js
new file mode 100644
index 000000000..5ce5600a1
--- /dev/null
+++ b/tests/security/index.js
@@ -0,0 +1,3 @@
+// tests related to security, vulnerability reports, etc
+require('./cve-2025-12816.js');
+require('./ghsa-554w-wpv2-vw27.js');
diff --git a/tests/server.js b/tests/server.js
index 85af534f2..5537b7b99 100644
--- a/tests/server.js
+++ b/tests/server.js
@@ -30,6 +30,8 @@ function contentServer(callback) {
// forge
app.use('/forge', express.static(path.join(__dirname, '..', 'dist')));
+ app.use('/support', express.static(path.join(__dirname, 'support')));
+ app.use('/issues', express.static(path.join(__dirname, 'issues')));
// unit tests support
app.use('/mocha',
diff --git a/lib/task.js b/tests/support/task.js
similarity index 97%
rename from lib/task.js
rename to tests/support/task.js
index df4866001..0d614a207 100644
--- a/lib/task.js
+++ b/tests/support/task.js
@@ -7,13 +7,10 @@
*
* Copyright (c) 2009-2013 Digital Bazaar, Inc.
*/
-var forge = require('./forge');
-require('./debug');
-require('./log');
-require('./util');
+// 'forge' should be a global
// logging category
-var cat = 'forge.task';
+var cat = 'forge.tests.task';
// verbose level
// 0: off, 1: a little, 2: a whole lot
@@ -27,13 +24,9 @@ var sVL = 0;
// track tasks for debugging
var sTasks = {};
var sNextTaskId = 0;
-// debug access
-forge.debug.set(cat, 'tasks', sTasks);
// a map of task type to task queue
var sTaskQueues = {};
-// debug access
-forge.debug.set(cat, 'queues', sTaskQueues);
// name for unnamed tasks
var sNoTaskName = '?';
@@ -73,7 +66,7 @@ var ERROR = 'error';
* SLEEP: sleep for a period of time
* WAKEUP: wakeup early from SLEEPING state
* CANCEL: cancel further tasks
- * FAIL: a failure occured
+ * FAIL: a failure occurred
*/
var STOP = 'stop';
var START = 'start';
@@ -277,7 +270,7 @@ Task.prototype.parallel = function(name, subrun) {
// closure and changes as the loop changes -- causing i
// to always be set to its highest value
var startParallelTask = function(pname, pi) {
- forge.task.start({
+ forge_task.start({
type: pname,
run: function(task) {
subrun[pi](task);
@@ -345,7 +338,7 @@ Task.prototype.block = function(n) {
* running once enough permits have been released via unblock() calls.
*
* If multiple processes need to synchronize with a single task then
- * use a condition variable (see forge.task.createCondition). It is
+ * use a condition variable (see task.createCondition). It is
* an error to unblock a task more times than it has been blocked.
*
* @param n number of permits to release (default: 1).
@@ -381,7 +374,7 @@ Task.prototype.sleep = function(n) {
/**
* Waits on a condition variable until notified. The next task will
* not be scheduled until notification. A condition variable can be
- * created with forge.task.createCondition().
+ * created with task.createCondition().
*
* Once cond.notify() is called, the task will continue.
*
@@ -618,7 +611,7 @@ var finish = function(task, suppressCallbacks) {
};
/* Tasks API */
-module.exports = forge.task = forge.task || {};
+window.forge_task = {};
/**
* Starts a new task that will run the passed function asynchronously.
@@ -642,7 +635,7 @@ module.exports = forge.task = forge.task || {};
*
* @param options the object as described above.
*/
-forge.task.start = function(options) {
+forge_task.start = function(options) {
// create a new task
var task = new Task({
run: options.run,
@@ -673,7 +666,7 @@ forge.task.start = function(options) {
*
* @param type the type of task to cancel.
*/
-forge.task.cancel = function(type) {
+forge_task.cancel = function(type) {
// find the task queue
if(type in sTaskQueues) {
// empty all but the current task from the queue
@@ -688,7 +681,7 @@ forge.task.cancel = function(type) {
*
* @return the condition variable.
*/
-forge.task.createCondition = function() {
+forge_task.createCondition = function() {
var cond = {
// all tasks that are blocked
tasks: {}
diff --git a/tests/unit/asn1.js b/tests/unit/asn1.js
index 65a1db510..a99783cb9 100644
--- a/tests/unit/asn1.js
+++ b/tests/unit/asn1.js
@@ -10,11 +10,50 @@ var UTIL = require('../../lib/util');
ASSERT.equal(ASN1.oidToDer('1.2.840.113549').toHex(), '2a864886f70d');
});
+ it('should convert a 32b OID to DER', function() {
+ ASSERT.equal(ASN1.oidToDer('1.2.4294967295').toHex(), '2a8fffffff7f');
+ });
+
+ it('should not convert a >32b OID to DER', function() {
+ ASSERT.throws(
+ function() {
+ ASN1.oidToDer('1.2.4294967296');
+ },
+ /OID value too large; max is 32-bits./
+ );
+ });
+
it('should convert an OID from DER', function() {
var der = UTIL.hexToBytes('2a864886f70d');
ASSERT.equal(ASN1.derToOid(der), '1.2.840.113549');
});
+ it('should convert a 32b OID from DER', function() {
+ var der = UTIL.hexToBytes('2a8fffffff7f');
+ ASSERT.equal(ASN1.derToOid(der), '1.2.4294967295');
+ });
+
+ it('should convert a >32b OID from DER', function() {
+ var der = UTIL.hexToBytes('2a9080808001');
+ ASSERT.equal(ASN1.derToOid(der), '1.2.4294967297');
+ });
+
+ it('should convert a max safe int OID from DER', function() {
+ var der = UTIL.hexToBytes('2a8fffffffffffff7f');
+ ASSERT.equal(ASN1.derToOid(der), '1.2.9007199254740991');
+ });
+
+ it('should not convert a >max safe int OID from DER', function() {
+ ASSERT.throws(
+ function() {
+ // '1.2.9007199254740992'
+ var der = UTIL.hexToBytes('2a9080808080808000');
+ console.log(ASN1.derToOid(der));
+ },
+ /OID value too large; max is 53-bits./
+ );
+ });
+
it('should convert INTEGER 0 to DER', function() {
ASSERT.equal(ASN1.integerToDer(0).toHex(), '00');
});
@@ -441,7 +480,7 @@ var UTIL = require('../../lib/util');
der = ASN1.toDer(asn1);
if(options.roundtrip) {
// byte comparisons for round-trip testing can fail due to
- // symantically safe changes such as changing the length encoding.
+ // semantically safe changes such as changing the length encoding.
// test a roundtrip for data where it makes sense.
ASSERT.equal(
UTIL.bytesToHex(bytes),
@@ -1047,7 +1086,7 @@ var UTIL = require('../../lib/util');
// could extend out to any structure size:
_add(b, '02 06 FF FF FF FF FF FF');
// the roundtrip issue can exist for long lengths that could
- // compress to short lenghts, this could be output as '02 02 01 23':
+ // compress to short lengths, this could be output as '02 02 01 23':
_add(b, '02 81 02 01 23');
// also an issue for indefinite length structures that will
// have a known length later:
@@ -1278,7 +1317,7 @@ var UTIL = require('../../lib/util');
UTIL.bytesToHex(derOut),
UTIL.bytesToHex(_h2b(hout)));
}
- // optimial
+ // optimal
_test('02 01 01', '02 01 01');
_test('02 01 FF', '02 01 FF');
_test('02 02 00 FF', '02 02 00 FF');
diff --git a/tests/unit/des.js b/tests/unit/des.js
index 77ac035e4..093756d2b 100644
--- a/tests/unit/des.js
+++ b/tests/unit/des.js
@@ -31,6 +31,22 @@ var UTIL = require('../../lib/util');
ASSERT.equal(decipher.output.getBytes(), 'foobar');
});
+ it('should check des-cbc short IV', function() {
+ var key = new UTIL.createBuffer(
+ UTIL.hexToBytes('a1c06b381adf3651'));
+ var iv = new UTIL.createBuffer(
+ UTIL.hexToBytes('818bcf76efc596'));
+
+ var error = null;
+ try {
+ var cipher = CIPHER.createCipher('DES-CBC', key);
+ cipher.start({iv: iv});
+ } catch(e) {
+ error = e;
+ }
+ ASSERT.ok(error, 'blocksize check should have failed');
+ });
+
// OpenSSL equivalent:
// openssl enc -des -K a1c06b381adf3651 -iv 818bcf76efc59662 -nosalt
it('should des-cbc encrypt: foobar', function() {
@@ -61,6 +77,90 @@ var UTIL = require('../../lib/util');
ASSERT.equal(decipher.output.getBytes(), 'foobar');
});
+ // play.golang.org/p/LX_dP0cFuEt
+ it('should des-ctr encrypt: foobar', function() {
+ var key = new UTIL.createBuffer(
+ UTIL.hexToBytes('a1c06b381adf3651'));
+ var iv = new UTIL.createBuffer(
+ UTIL.hexToBytes('818bcf76efc59662'));
+
+ var cipher = CIPHER.createCipher('DES-CTR', key);
+ cipher.start({iv: iv});
+ cipher.update(UTIL.createBuffer('foobar'));
+ cipher.finish();
+ ASSERT.equal(cipher.output.toHex(), '3a97fa79e631');
+ });
+
+ // play.golang.org/p/6_MQBYzn04c
+ it('should des-ctr decrypt: foobar', function() {
+ var key = new UTIL.createBuffer(
+ UTIL.hexToBytes('beefdeadbeefdead'));
+ var iv = new UTIL.createBuffer(
+ UTIL.hexToBytes('deadbeefdeadbeef'));
+
+ var cipher = CIPHER.createDecipher('DES-CTR', key);
+ cipher.start({iv: iv});
+ cipher.update(UTIL.createBuffer(UTIL.hexToBytes('6df74b7b4437')));
+ cipher.finish();
+ ASSERT.equal(cipher.output.getBytes(), 'foobar');
+ });
+
+ // play.golang.org/p/i892aR7YsGK
+ it('should des-ctr encrypt: dead parrot', function() {
+ var key = new UTIL.createBuffer(
+ UTIL.hexToBytes('a1c06b381adf3651'));
+ var iv = new UTIL.createBuffer(
+ UTIL.hexToBytes('818bcf76efc59662'));
+
+ var cipher = CIPHER.createCipher('DES-CTR', key);
+ cipher.start({iv: iv});
+ cipher.update(UTIL.createBuffer('dead parrot'));
+ cipher.finish();
+ ASSERT.equal(cipher.output.toHex(), '389df47fa733dcf4b99b7c');
+ });
+
+ // play.golang.org/p/6L0LqPS9ARt
+ it('should des-ctr decrypt: 79f1527c5737f774f85c1a9399755d895ae7', function() {
+ var key = new UTIL.createBuffer(
+ UTIL.hexToBytes('beefdeadbeefdead'));
+ var iv = new UTIL.createBuffer(
+ UTIL.hexToBytes('deadbeefdeadbeef'));
+
+ var cipher = CIPHER.createDecipher('DES-CTR', key);
+ cipher.start({iv: iv});
+ cipher.update(UTIL.createBuffer(UTIL.hexToBytes('79f1527c5737f774f85c1a9399755d895ae7')));
+ cipher.finish();
+ ASSERT.equal(cipher.output.getBytes(), 'riverrun, past Eve');
+ });
+
+ // play.golang.org/p/WsSx6BXJniU
+ it('should des-ctr encrypt: 69742773206e6f742073696c6c7920656e6f756768', function() {
+ var key = new UTIL.createBuffer(
+ UTIL.hexToBytes('a1c06b381adf3651'));
+ var iv = new UTIL.createBuffer(
+ UTIL.hexToBytes('818bcf76efc59662'));
+
+ var cipher = CIPHER.createCipher('DES-CTR', key);
+ cipher.start({iv: iv});
+ cipher.update(UTIL.createBuffer(UTIL.hexToBytes('69742773206e6f742073696c6c7920656e6f756768')));
+ cipher.finish();
+ ASSERT.equal(cipher.output.toHex(), '358cb268a72dd2f2eb87615060bd3a490e85136873');
+ });
+
+ // play.golang.org/p/y01inAlMCEM
+ it('should des-ctr decrypt: 0a80bd81a4dc1303a62f', function() {
+ var key = new UTIL.createBuffer(
+ UTIL.hexToBytes('beefdeadbeefdead'));
+ var iv = new UTIL.createBuffer(
+ UTIL.hexToBytes('deadbeefdeadbeef'));
+
+ var cipher = CIPHER.createDecipher('DES-CTR', key);
+ cipher.start({iv: iv});
+ cipher.update(UTIL.createBuffer(UTIL.hexToBytes('0a80bd81a4dc1303a62f')));
+ cipher.finish();
+ ASSERT.equal(cipher.output.toHex(), '01189998819991197253');
+ });
+
// OpenSSL equivalent:
// openssl enc -des-ede3 -K a1c06b381adf36517e84575552777779da5e3d9f994b05b5 -nosalt
it('should 3des-ecb encrypt: foobar', function() {
diff --git a/tests/unit/forge.js b/tests/unit/forge.js
new file mode 100644
index 000000000..fdbe58a2f
--- /dev/null
+++ b/tests/unit/forge.js
@@ -0,0 +1,2 @@
+// test loading the entire module
+require('../../lib/index.js');
diff --git a/tests/unit/index.js b/tests/unit/index.js
index 4eb72d765..dfc441efc 100644
--- a/tests/unit/index.js
+++ b/tests/unit/index.js
@@ -1,3 +1,4 @@
+require('./forge');
require('./util');
require('./md5');
require('./sha1');
@@ -22,3 +23,4 @@ require('./pkcs7');
require('./pkcs12');
require('./tls');
require('./ssh');
+require('../security');
diff --git a/tests/unit/pem.js b/tests/unit/pem.js
index dd989596f..e31dcf342 100644
--- a/tests/unit/pem.js
+++ b/tests/unit/pem.js
@@ -70,6 +70,66 @@ var PEM = require('../../lib/pem');
'0vhM5TEmmNWz0anPVabqDj9TA0z5MsDJQcn5NmO9xnw=\r\n' +
'-----END RSA PRIVATE KEY-----\r\n';
+ var _csrWithNew = '-----BEGIN NEW CERTIFICATE REQUEST-----\r\n' +
+ 'MIIE9jCCAt4CAQAwfjELMAkGA1UEBhMCVVMxETAPBgNVBAgMCFZpcmdpbmlhMRMw\r\n' +
+ 'EQYDVQQHDApCbGFja3NidXJnMR0wGwYDVQQKDBREaWdpdGFsIEJhemFhciwgSW5j\r\n' +
+ 'LjEMMAoGA1UECwwDT1NTMRowGAYDVQQDDBFkaWdpdGFsYmF6YWFyLmNvbTCCAiIw\r\n' +
+ 'DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAKbqOZ0oC5L+GFnuvwuWnq5J/wxQ\r\n' +
+ '6upw5qvA+zfHZYkqdC170OYKsfC67/W6591631xGhVden26/BdxilpeSX1hFVqPF\r\n' +
+ 'IND7KJvo039QdFQzmzBgqcY5cr11OT9jYjoQMPCehRmbmv6RNaKqTdITMrGZMFzk\r\n' +
+ 'HFWfshuY71A0+wlz2pOzi79qL7tdcm5s6Whge3/0AAZi19Ze148vCH+HHnbQ7jMH\r\n' +
+ 'bGJlFZhvGYd2D/clCVnG4w4mCX6scMBZXtf4k1qZAuyhEpTJl8vxCExQs2iCN8lw\r\n' +
+ '4tEJH979MQsTDCNf5EZOBzMa4tJtybvQcmFQT2Xjb/8qYT0GyBP+XyJ6nmY3S0R2\r\n' +
+ 'xZtIsuKlayTw1GG/cYg3OC73G1lbVFLYLh1R+nEs14XX5Dj3J0zTxLeWewFIL7FP\r\n' +
+ 'D77oRqTHoHNIWz3SJ3S0OTqCYr+5h4vjUOCyXdjCZMZSFOWfCjcMIqcUsysj05gL\r\n' +
+ 'YBw5z+ZUn17zEEKBuq1tjS1UInbLPBbDMYc1P0NAO5UltdpOs0FPXWgHtzpVoYgZ\r\n' +
+ '7W2mXSTgP3xfVicWK6SBP0ejJmcgt4eB5gKidfg0t1BbB/4TgHLrDgGZapVA4DrX\r\n' +
+ 'agUxalhOrvV0Pm3zWdn6DNGNQbtm0xOebzEFL2bDRangK3OnA4EtOMj39cK2f4bY\r\n' +
+ '6ENG38DrC/ctvFmHAgMBAAGgMzAxBgkqhkiG9w0BCQ4xJDAiMAsGA1UdDwQEAwIE\r\n' +
+ 'MDATBgNVHSUEDDAKBggrBgEFBQcDATANBgkqhkiG9w0BAQsFAAOCAgEAGXNXqKmv\r\n' +
+ 'Dzkvm+ZTTmwsjf8zlCp1M+QtPSvCMGGUJtqwIFarIKc1H5ZIyfh3p+ws1xDFw0ZK\r\n' +
+ 'xPyIleeCqMVPAL9me4l8oaQ2IoQ917rmcsdfbPh3/8JkU5rotoRBW0JtsMTx5A6U\r\n' +
+ '7FluYFeKVTM1GZo3TpMhG7NZFePtIJfP/hPwtNnIrBkMOLmvyfN68UO1uhazx5/a\r\n' +
+ 'Uanp1JF9+05hwNSIL/R6TC/RQdeA5b3fycDPfhHhot7Bs/FczgF6I7Qrmyb4pzmR\r\n' +
+ 'e0knYlOucs0CsV/qj2K2Iouu0lWA0nZQQsbBtvN8dExYZpGPl4LJqNGYF4rLsoep\r\n' +
+ 'VyDD79rwCM6oqYbQ6GXQJdzXnQoAJTTFyg8bGmj9osBaSb8WKfz1VspnHzsbryxT\r\n' +
+ 'LPCI9Drg9kB28f7PGN0KWZnmWgD2qV/UuVPjxNhHTC8nEHCQP0gPeHrRgCyhDT4n\r\n' +
+ 'WPluKuX1B+xO5aOXOSmKcHNufDrN1l/ErhOvYeAimPq1Ag74Z946s27fO0M00kHK\r\n' +
+ '+ex8zj29okA0QSsJuCVbOA1tFlyoRd7apN/z1mpcvpb+TDZgdH/HFyrMK1bH2J5u\r\n' +
+ 'I1iuhuP3g2HSdjLC0wuUA4u73WcbcH7X9tnAHymFgGa5pNUlRPllbIRWvCM+7UaY\r\n' +
+ 'x6n+naGYblpSHXiboXRsuGWUtTjvqNVdOxA=\r\n' +
+ '-----END NEW CERTIFICATE REQUEST-----\r\n';
+
+ var _csrWithoutNew = '-----BEGIN CERTIFICATE REQUEST-----\r\n' +
+ 'MIIE9jCCAt4CAQAwfjELMAkGA1UEBhMCVVMxETAPBgNVBAgMCFZpcmdpbmlhMRMw\r\n' +
+ 'EQYDVQQHDApCbGFja3NidXJnMR0wGwYDVQQKDBREaWdpdGFsIEJhemFhciwgSW5j\r\n' +
+ 'LjEMMAoGA1UECwwDT1NTMRowGAYDVQQDDBFkaWdpdGFsYmF6YWFyLmNvbTCCAiIw\r\n' +
+ 'DQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAKbqOZ0oC5L+GFnuvwuWnq5J/wxQ\r\n' +
+ '6upw5qvA+zfHZYkqdC170OYKsfC67/W6591631xGhVden26/BdxilpeSX1hFVqPF\r\n' +
+ 'IND7KJvo039QdFQzmzBgqcY5cr11OT9jYjoQMPCehRmbmv6RNaKqTdITMrGZMFzk\r\n' +
+ 'HFWfshuY71A0+wlz2pOzi79qL7tdcm5s6Whge3/0AAZi19Ze148vCH+HHnbQ7jMH\r\n' +
+ 'bGJlFZhvGYd2D/clCVnG4w4mCX6scMBZXtf4k1qZAuyhEpTJl8vxCExQs2iCN8lw\r\n' +
+ '4tEJH979MQsTDCNf5EZOBzMa4tJtybvQcmFQT2Xjb/8qYT0GyBP+XyJ6nmY3S0R2\r\n' +
+ 'xZtIsuKlayTw1GG/cYg3OC73G1lbVFLYLh1R+nEs14XX5Dj3J0zTxLeWewFIL7FP\r\n' +
+ 'D77oRqTHoHNIWz3SJ3S0OTqCYr+5h4vjUOCyXdjCZMZSFOWfCjcMIqcUsysj05gL\r\n' +
+ 'YBw5z+ZUn17zEEKBuq1tjS1UInbLPBbDMYc1P0NAO5UltdpOs0FPXWgHtzpVoYgZ\r\n' +
+ '7W2mXSTgP3xfVicWK6SBP0ejJmcgt4eB5gKidfg0t1BbB/4TgHLrDgGZapVA4DrX\r\n' +
+ 'agUxalhOrvV0Pm3zWdn6DNGNQbtm0xOebzEFL2bDRangK3OnA4EtOMj39cK2f4bY\r\n' +
+ '6ENG38DrC/ctvFmHAgMBAAGgMzAxBgkqhkiG9w0BCQ4xJDAiMAsGA1UdDwQEAwIE\r\n' +
+ 'MDATBgNVHSUEDDAKBggrBgEFBQcDATANBgkqhkiG9w0BAQsFAAOCAgEAGXNXqKmv\r\n' +
+ 'Dzkvm+ZTTmwsjf8zlCp1M+QtPSvCMGGUJtqwIFarIKc1H5ZIyfh3p+ws1xDFw0ZK\r\n' +
+ 'xPyIleeCqMVPAL9me4l8oaQ2IoQ917rmcsdfbPh3/8JkU5rotoRBW0JtsMTx5A6U\r\n' +
+ '7FluYFeKVTM1GZo3TpMhG7NZFePtIJfP/hPwtNnIrBkMOLmvyfN68UO1uhazx5/a\r\n' +
+ 'Uanp1JF9+05hwNSIL/R6TC/RQdeA5b3fycDPfhHhot7Bs/FczgF6I7Qrmyb4pzmR\r\n' +
+ 'e0knYlOucs0CsV/qj2K2Iouu0lWA0nZQQsbBtvN8dExYZpGPl4LJqNGYF4rLsoep\r\n' +
+ 'VyDD79rwCM6oqYbQ6GXQJdzXnQoAJTTFyg8bGmj9osBaSb8WKfz1VspnHzsbryxT\r\n' +
+ 'LPCI9Drg9kB28f7PGN0KWZnmWgD2qV/UuVPjxNhHTC8nEHCQP0gPeHrRgCyhDT4n\r\n' +
+ 'WPluKuX1B+xO5aOXOSmKcHNufDrN1l/ErhOvYeAimPq1Ag74Z946s27fO0M00kHK\r\n' +
+ '+ex8zj29okA0QSsJuCVbOA1tFlyoRd7apN/z1mpcvpb+TDZgdH/HFyrMK1bH2J5u\r\n' +
+ 'I1iuhuP3g2HSdjLC0wuUA4u73WcbcH7X9tnAHymFgGa5pNUlRPllbIRWvCM+7UaY\r\n' +
+ 'x6n+naGYblpSHXiboXRsuGWUtTjvqNVdOxA=\r\n' +
+ '-----END CERTIFICATE REQUEST-----\r\n';
+
describe('pem', function() {
it('should decode and re-encode PEM messages', function() {
var msgs = PEM.decode(_input);
@@ -81,5 +141,19 @@ var PEM = require('../../lib/pem');
ASSERT.equal(output, _input);
});
+
+ it('should decode a CSR from PEM with NEW in the labels', function() {
+ var csrs = PEM.decode(_csrWithNew);
+ for(var i = 0; i < csrs.length; ++i) {
+ ASSERT.equal(csrs[i].type, 'CERTIFICATE REQUEST');
+ }
+ });
+
+ it('should decode a CSR from PEM without NEW in the labels', function() {
+ var csrs = PEM.decode(_csrWithoutNew);
+ for(var i = 0; i < csrs.length; ++i) {
+ ASSERT.equal(csrs[i].type, 'CERTIFICATE REQUEST');
+ }
+ });
});
})();
diff --git a/tests/unit/rsa.js b/tests/unit/rsa.js
index 0cdd28e01..d68946d8f 100644
--- a/tests/unit/rsa.js
+++ b/tests/unit/rsa.js
@@ -1,5 +1,6 @@
var ASSERT = require('assert');
var FORGE = require('../../lib/forge');
+var JSBN = require('../../lib/jsbn');
var MD = require('../../lib/md.all');
var MGF = require('../../lib/mgf');
var PKI = require('../../lib/pki');
@@ -580,7 +581,7 @@ var UTIL = require('../../lib/util');
/* Second step, use private key decryption to verify successful
encryption. The encrypted message differs every time, since it is
padded with random data. Therefore just rely on the decryption
- routine to work, which is tested seperately against an externally
+ routine to work, which is tested separately against an externally
provided encrypted message. */
key = PKI.privateKeyFromPem(params.privateKeyPem);
ASSERT.equal(key.decrypt(data), message);
@@ -773,5 +774,382 @@ var UTIL = require('../../lib/util');
});
}
})();
+
+ describe('signature verification', function() {
+
+ // NOTE: Tests in this section, and associated fixes, are largely derived
+ // from a detailed vulnerability report provided by Moosa Yahyazadeh
+ // (moosa-yahyazadeh@uiowa.edu).
+
+ // params for tests
+
+ // public modulus / 256 bytes
+ var N = new JSBN.BigInteger(
+ 'E932AC92252F585B3A80A4DD76A897C8B7652952FE788F6EC8DD640587A1EE56' +
+ '47670A8AD4C2BE0F9FA6E49C605ADF77B5174230AF7BD50E5D6D6D6D28CCF0A8' +
+ '86A514CC72E51D209CC772A52EF419F6A953F3135929588EBE9B351FCA61CED7' +
+ '8F346FE00DBB6306E5C2A4C6DFC3779AF85AB417371CF34D8387B9B30AE46D7A' +
+ '5FF5A655B8D8455F1B94AE736989D60A6F2FD5CADBFFBD504C5A756A2E6BB5CE' +
+ 'CC13BCA7503F6DF8B52ACE5C410997E98809DB4DC30D943DE4E812A47553DCE5' +
+ '4844A78E36401D13F77DC650619FED88D8B3926E3D8E319C80C744779AC5D6AB' +
+ 'E252896950917476ECE5E8FC27D5F053D6018D91B502C4787558A002B9283DA7',
+ 16);
+
+ // private exponent
+ var d = new JSBN.BigInteger(
+ '009b771db6c374e59227006de8f9c5ba85cf98c63754505f9f30939803afc149' +
+ '8eda44b1b1e32c7eb51519edbd9591ea4fce0f8175ca528e09939e48f37088a0' +
+ '7059c36332f74368c06884f718c9f8114f1b8d4cb790c63b09d46778bfdc4134' +
+ '8fb4cd9feab3d24204992c6dd9ea824fbca591cd64cf68a233ad0526775c9848' +
+ 'fafa31528177e1f8df9181a8b945081106fd58bd3d73799b229575c4f3b29101' +
+ 'a03ee1f05472b3615784d9244ce0ed639c77e8e212ab52abddf4a928224b6b6f' +
+ '74b7114786dd6071bd9113d7870c6b52c0bc8b9c102cfe321dac357e030ed6c5' +
+ '80040ca41c13d6b4967811807ef2a225983ea9f88d67faa42620f42a4f5bdbe0' +
+ '3b',
+ 16);
+
+ // public exponent
+ var e = new JSBN.BigInteger('3');
+
+ // hash function
+ // H = SHA-256 (OID = 0x608648016503040201)
+
+ // message
+ var m = 'hello world!';
+
+ // to-be-signed RSA PKCS#1 v1.5 signature scheme input structure
+ // I
+
+ // signature value obtained by I^d mod N
+ // S
+
+ function _checkBadTailingGarbage(publicKey, S) {
+ var md = MD.sha256.create();
+ md.update(m);
+
+ ASSERT.throws(function() {
+ publicKey.verify(md.digest().getBytes(), S);
+ },
+ /^Error: Unparsed DER bytes remain after ASN.1 parsing.$/);
+ }
+
+ function _checkBadDigestInfo(publicKey, S, skipTailingGarbage) {
+ var md = MD.sha256.create();
+ md.update(m);
+
+ ASSERT.throws(function() {
+ publicKey.verify(md.digest().getBytes(), S, undefined, {
+ _parseAllDigestBytes: !skipTailingGarbage
+ });
+ },
+ /^Error: ASN.1 object does not contain a valid RSASSA-PKCS1-v1_5 DigestInfo value.$/);
+ }
+
+ function _checkGoodDigestInfo(publicKey, S, skipTailingGarbage) {
+ var md = MD.sha256.create();
+ md.update(m);
+
+ ASSERT.ok(publicKey.verify(md.digest().getBytes(), S, undefined, {
+ _parseAllDigestBytes: !skipTailingGarbage
+ }));
+ }
+
+ it('should check DigestInfo structure', function() {
+ var publicKey = RSA.setPublicKey(N, e);
+ // 0xff bytes stolen from padding
+ // unchecked portion of PKCS#1 encoded message used to forge a
+ // signature when low public exponent is being used.
+ // See "Bleichenbacher's RSA signature forgery based on implementation
+ // error" by Hal Finney
+ // https://mailarchive.ietf.org/arch/msg/openpgp/5rnE9ZRN1AokBVj3VqblGlP63QE/
+
+ // 91 garbage byte injected as the value of a TLV replaced digest
+ // algorithm structure
+ var I = UTIL.binary.hex.decode(
+ '0001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff' +
+ 'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff' +
+ 'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff' +
+ 'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0030' +
+ '7f065b8888888888888888888888888888888888888888888888888888888888' +
+ '8888888888888888888888888888888888888888888888888888888888888888' +
+ '8888888888888888888888888888888888888888888888888888888888880420' +
+ '7509e5bda0c762d2bac7f90d758b5b2263fa01ccbc542ab5e3df163be08e6ca9');
+ var S = UTIL.binary.hex.decode(
+ 'e7410e05bdc38d1c72fab784be41df3d3de2ae83894d9ec86cb5fe343d5dc7d4' +
+ '5df2a36fc60363faf32f0d37ab457648af40a48a6c53ae7af0575e92cb1ffc23' +
+ '6d55e1325af8c71b3ac313f2630fb498b8e1546093aca1ed56026a96cb525d99' +
+ '1159a2d6ccbfd5ef63ae718f8ace2469e357ccf3f6a048bbf9760f5fb36b9dd3' +
+ '8fb330eab504f05078b83f5d8bd95dce8fccc6b46babd56f678300f2b39083e5' +
+ '3e04e79f503358a6222f8dd66b561fea3a51ecf3be16c9e2ea6ba8aaed9fbe6b' +
+ 'a510ff752e4529385f759d4d6120b15f65534248ed5bbb1307a7d0a983832969' +
+ '7f5fbae91f48e478dcbb77190f0d173b6cb8b1299cf4202570d25d11a7862b47');
+
+ _checkBadDigestInfo(publicKey, S);
+ });
+
+ it('should check tailing garbage and DigestInfo [1]', function() {
+ var publicKey = RSA.setPublicKey(N, e);
+ // bytes stolen from padding and unchecked tailing bytes used to forge
+ // a signature when low public exponent is used
+
+ // 204 tailing garbage bytes injected after DigestInfo structure
+ var I = UTIL.binary.hex.decode(
+ '000100302f300b060960864801650304020104207509e5bda0c762d2bac7f90d' +
+ '758b5b2263fa01ccbc542ab5e3df163be08e6ca9888888888888888888888888' +
+ '8888888888888888888888888888888888888888888888888888888888888888' +
+ '8888888888888888888888888888888888888888888888888888888888888888' +
+ '8888888888888888888888888888888888888888888888888888888888888888' +
+ '8888888888888888888888888888888888888888888888888888888888888888' +
+ '8888888888888888888888888888888888888888888888888888888888888888' +
+ '8888888888888888888888888888888888888888888888888888888888888888');
+ var S = UTIL.binary.hex.decode(
+ 'c2ad2fa23c246ee98c453d69023e7ec05956b48bd0e287341ba9d342ad49b0ff' +
+ 'f2bcbb9adc50f1ccbfc54106305cc74a88db89ff94901a08359893a08426373e' +
+ '7949a8794798233445af6c48bc6ccbe278bdeb62c31e40c3bf0014af2faadcc9' +
+ 'ed7885756789a5b95c2a355fbb3f04412f42e0f9ed335ab51af8f091a62aaaaf' +
+ '6577422220917daaece3ca2f4e66dc4e0574356762592052b406768c31c25cf4' +
+ 'c1754e6da9dc3440e238c4f9b25cccc174dd1b17b027e0f9ce2763b86f0e6871' +
+ '690ddd018d2e774bc968c9c6e907a000daf5044ba31a0b9eefbd7b4b1ec466d2' +
+ '0bc1dd3f020cb1091af6b476416da3024ea046b09fbbbc4d2355da9a2bc6ddb9');
+
+ _checkBadTailingGarbage(publicKey, S);
+ _checkGoodDigestInfo(publicKey, S, true);
+ });
+
+ it('should check tailing garbage and DigestInfo [2]', function() {
+ var publicKey = RSA.setPublicKey(N, e);
+ // bytes stolen from padding and unchecked tailing bytes used to forge
+ // a signature when low public exponent is used
+
+ // 215 tailing garbage bytes injected after DigestInfo structure
+ // unchecked digest algorithm structure
+ // combined with earlier issue
+ var I = UTIL.binary.hex.decode(
+ '0001003024010004207509e5bda0c762d2bac7f90d758b5b2263fa01ccbc542a' +
+ 'b5e3df163be08e6ca98888888888888888888888888888888888888888888888' +
+ '8888888888888888888888888888888888888888888888888888888888888888' +
+ '8888888888888888888888888888888888888888888888888888888888888888' +
+ '8888888888888888888888888888888888888888888888888888888888888888' +
+ '8888888888888888888888888888888888888888888888888888888888888888' +
+ '8888888888888888888888888888888888888888888888888888888888888888' +
+ '8888888888888888888888888888888888888888888888888888888888888888');
+ var S = UTIL.binary.hex.decode(
+ 'a7c5812d7fc0eef766a481aac18c8c48483daf9b5ffb6614bd98ebe4ecb746dd' +
+ '493cf5dd2cbe16ecaa0b52109b744930eda49316605fc823fd57a68b5b2c62e8' +
+ 'c1b158b26e1547a2e33cdd79427d7c513f07d02261ffe43db197d8cddca2b5b4' +
+ '3c1df85aaed6e91aadd44a46bff7f5c70f1acc1a193917e3908444632f30e69c' +
+ 'fe95d8036d3b6ad318eefd3952804f16613c969e6d13604bb4e723dfad24c42c' +
+ '8d9b5b16a9f5a4b40dcf17b167d319017740f9cc0836436c14d51c3d8a697f1f' +
+ 'a2b65196deb5c21b1559c7dea7f598007fa7320909825009f8bf376491c298d8' +
+ '155a382e967042db952e995d14b2f961e1b22f911d1b77895def1c7ef229c87e');
+
+ _checkBadTailingGarbage(publicKey, S);
+ _checkBadDigestInfo(publicKey, S, true);
+ });
+
+ it('should check tailing garbage and DigestInfo [e=3]', function() {
+ // signature forged without knowledge of private key for given message
+ // and low exponent e=3
+
+ // test data computed from a script
+ var N = new JSBN.BigInteger(
+ '2943851338959486749023220128247883872673446416188780128906858510' +
+ '0507839535636256317277708295678804401391394313946142335874609638' +
+ '6660819509361141525748702240343825617847432837639613499808068190' +
+ '7802897559477710338828027239284411238090037450817022107555351764' +
+ '1170327441791034393719271744724924194371070527213991317221667249' +
+ '0779727008421990374037994805699108447010306443226160454080397152' +
+ '7839457232809919202392450307767317822761454935119120485180507635' +
+ '9472439160130994385433568113626206477097769842080459156024112389' +
+ '4062006872333417793816670825914214968706669312685485046743622307' +
+ '25756397511775557878046572472650613407143');
+ var e = new JSBN.BigInteger('3');
+ var publicKey = RSA.setPublicKey(N, e);
+
+ var S = UTIL.binary.hex.decode(
+ '0000000000000000000000000000000000000000000000000000000000000000' +
+ '0000000000000000000000000000000000000000000000000000000000000000' +
+ '0000000000000000000000000000000000000000000000000000000000000000' +
+ '0000000000000000000000000000000000000000000000000000000000000000' +
+ '0000000000000000000000000000000000000000000000000000000000000000' +
+ '00000000000000000000002853ccc2cd32a8d430dd3bde37e70782ac82cdb7bc' +
+ 'e3c044219b50aefd689c20d3b840299f28e2fde6c67c8a7f9e528ac222fae947' +
+ 'a6dee0d812e3c3b3452171717396e8bedc3132d92d8317e3593642640d1431ef');
+
+ _checkBadTailingGarbage(publicKey, S);
+ _checkBadDigestInfo(publicKey, S, true);
+ });
+
+ it('should check tailing garbage and DigestInfo [e=5]', function() {
+ // signature forged without knowledge of private key for given message
+ // and low exponent e=5
+
+ // test data computed from a script
+ var N = new JSBN.BigInteger(
+ '2943851338959486749023220128247883872673446416188780128906858510' +
+ '0507839535636256317277708295678804401391394313946142335874609638' +
+ '6660819509361141525748702240343825617847432837639613499808068190' +
+ '7802897559477710338828027239284411238090037450817022107555351764' +
+ '1170327441791034393719271744724924194371070527213991317221667249' +
+ '0779727008421990374037994805699108447010306443226160454080397152' +
+ '7839457232809919202392450307767317822761454935119120485180507635' +
+ '9472439160130994385433568113626206477097769842080459156024112389' +
+ '4062006872333417793816670825914214968706669312685485046743622307' +
+ '25756397511775557878046572472650613407143');
+ var e = new JSBN.BigInteger('5');
+ var publicKey = RSA.setPublicKey(N, e);
+
+ var S = UTIL.binary.hex.decode(
+ '0000000000000000000000000000000000000000000000000000000000000000' +
+ '0000000000000000000000000000000000000000000000000000000000000000' +
+ '0000000000000000000000000000000000000000000000000000000000000000' +
+ '0000000000000000000000000000000000000000000000000000000000000000' +
+ '0000000000000000000000000000000000000000000000000000000000000000' +
+ '0000000000000000000000000000000000000000000000000000000000000000' +
+ '000000000000000000000000005475fe2681d7125972bd2c2f2c7ab7b8003b03' +
+ 'd4a487d6dee07c14eb5212a9fe0071b93f84ba5bb4b0cfaf20c976b11d902013');
+
+ _checkBadTailingGarbage(publicKey, S);
+ _checkBadDigestInfo(publicKey, S, true);
+ });
+
+ it('should check tailing garbage and DigestInfo [e=17]', function() {
+ // signature forged without knowledge of private key for given message
+ // and low exponent e=17
+
+ // test data computed from a script
+ var N = new JSBN.BigInteger(
+ '9283656416612985262941143827717696579056959956800096804440022580' +
+ '8979605519224532102091105159037909758713334182004379540747102163' +
+ '0328875171430160513961779154294247563032373839871165519961382202' +
+ '8118288833646515747631246999476620608496831766892861810215014002' +
+ '6197665341672524640393361361575818164897153768964295647456396149' +
+ '0989544033629566558036444831495046301215543198107208071526376318' +
+ '9614817392787691228850316867637768748063173527415482321108924014' +
+ '0172719575883597580010690402077593789150581979877629529469651667' +
+ '0437057465296389148672556848624501468669295285428387365416747516' +
+ '1806526300547653933352115280843297169178217266705491556199868750' +
+ '3004910766820506445410432860104193197231996634882562129969319354' +
+ '2460060799067674344247887198933507132592770898312271636011037138' +
+ '9847292565155151851533347436854797090854109022697775636916157198' +
+ '8470890850961835279273782642105981947430594900197891694944702901' +
+ '0362775778664826653636547333219983468955600305523140183269580452' +
+ '7928125033990422010817859727072181449684606236639224708148897385' +
+ '6473081641220112881037032407068024585466913055187295801749427746' +
+ '8722193869883705529583737211815974801292292728082721785855274147' +
+ '9919792200010181565600099271483749952360303834740314188025547140' +
+ '4368096941701515529809239068018840617766710102093620675455198522' +
+ '9636814788735090951246816765035721775759652424641736739668936540' +
+ '4502328148572893125899985056273755530380627654934084609415976292' +
+ '9123186604266210829116435949633497856328752368587226250956046322' +
+ '5096226739991402761266388226652661345282274508037924611589455395' +
+ '6555120130786293751868059518231813715612891296160287687335835654' +
+ '3979850800254668550551247800296013251153132326459614458561196296' +
+ '9372672455541953777622436993987703564293487820434112162562492086' +
+ '8651475984366477254452308612460939500200990849949906321025068481' +
+ '9019640785570574553040761725312997166593985384222496507953730319' +
+ '8339986953399517682750248394628026225887174258267456078564070387' +
+ '3276539895054169432261639890044193773631304665663877617572725639' +
+ '9608670862191314058068741469812649057261850985814174869283757023' +
+ '5128900627675422927964369356691123905362222855545719945605604307' +
+ '2632528510813096225692258119794268564646732338755890857736163737' +
+ '9885700134409359441713832300526017978115395080312777381770201653' +
+ '4081581157881295739782000814998795398671806283018844936919299070' +
+ '5625387639000374694851356996772485803653791257029031861749956519' +
+ '3846941219138832785295572786934547608717304766525989212989524778' +
+ '5416834855450881318585909376917039');
+ var e = new JSBN.BigInteger('17');
+ var publicKey = RSA.setPublicKey(N, e);
+
+ var S = UTIL.binary.hex.decode(
+ '0000000000000000000000000000000000000000000000000000000000000000' +
+ '0000000000000000000000000000000000000000000000000000000000000000' +
+ '0000000000000000000000000000000000000000000000000000000000000000' +
+ '0000000000000000000000000000000000000000000000000000000000000000' +
+ '0000000000000000000000000000000000000000000000000000000000000000' +
+ '0000000000000000000000000000000000000000000000000000000000000000' +
+ '0000000000000000000000000000000000000000000000000000000000000000' +
+ '0000000000000000000000000000000000000000000000000000000000000000' +
+ '0000000000000000000000000000000000000000000000000000000000000000' +
+ '0000000000000000000000000000000000000000000000000000000000000000' +
+ '0000000000000000000000000000000000000000000000000000000000000000' +
+ '0000000000000000000000000000000000000000000000000000000000000000' +
+ '0000000000000000000000000000000000000000000000000000000000000000' +
+ '0000000000000000000000000000000000000000000000000000000000000000' +
+ '0000000000000000000000000000000000000000000000000000000000000000' +
+ '0000000000000000000000000000000000000000000000000000000000000000' +
+ '0000000000000000000000000000000000000000000000000000000000000000' +
+ '0000000000000000000000000000000000000000000000000000000000000000' +
+ '0000000000000000000000000000000000000000000000000000000000000000' +
+ '0000000000000000000000000000000000000000000000000000000000000000' +
+ '0000000000000000000000000000000000000000000000000000000000000000' +
+ '0000000000000000000000000000000000000000000000000000000000000000' +
+ '0000000000000000000000000000000000000000000000000000000000000000' +
+ '0000000000000000000000000000000000000000000000000000000000000000' +
+ '0000000000000000000000000000000000000000000000000000000000000000' +
+ '0000000000000000000000000000000000000000000000000000000000000000' +
+ '0000000000000000000000000000000000000000000000000000000000000000' +
+ '0000000000000000000000000000000000000000000000000000000000000000' +
+ '0000000000000000000000000000000000000000000000000000000000000000' +
+ '0000000000000000000000000000000000000000000000000000000000000000' +
+ '00000001eb90acbec1bf590ba1e50960db8381fb5bdc363d46379d09956560a6' +
+ '16b88616ce7fa4309dc45f47f5fa47d61bf66baa3d11732ce71768ded295f962');
+
+ _checkBadTailingGarbage(publicKey, S);
+ _checkBadDigestInfo(publicKey, S, true);
+ });
+
+ it('should check DigestInfo type octet [1]', function() {
+ var publicKey = RSA.setPublicKey(N, e);
+ // incorrect value for digest algorithm's type octet
+ // 0x0c instead of correct 0x06
+ var I = UTIL.binary.hex.decode(
+ '0001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff' +
+ 'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff' +
+ 'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff' +
+ 'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff' +
+ 'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff' +
+ 'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff' +
+ 'ffffffffffffffffffffffff0030310c0d060960864801650304020105000420' +
+ '7509e5bda0c762d2bac7f90d758b5b2263fa01ccbc542ab5e3df163be08e6ca9');
+ var S = UTIL.binary.hex.decode(
+ 'd8298a199e1b6ac18f3c0067a004bd9ff7af87be6ad857d73cc3d24ef06195b8' +
+ '2aaddb0194f8e61fc31453b9163062255e8baf9c480200d0991a5f764f63d5f6' +
+ 'afd283b9cd6afe54f0b7f738707b4eb6b8807539bb627e74db87a50413ab18e5' +
+ '04e37975aad1edc612bc8ecad53b81ea249deb5a2acc27e6419c61ab9acec660' +
+ '8f5ae6a2985ba0b6f42d831bc6cce4b044864154b935cf179967d129e0ad8eda' +
+ '9bfbb638121c3ff13c64d439632e62250d4be928a3deb112ef76a025c5d91805' +
+ '1e601878eac0049fc9d82be9ae3475deb7ca515c830c20b91b7bedf2184fef66' +
+ 'aea0bde62ccd1659afbfd1342322b095309451b1a87e007e640e368fb68a13c9');
+
+ _checkBadDigestInfo(publicKey, S);
+ });
+
+ it('should check DigestInfo type octet [2]', function() {
+ var publicKey = RSA.setPublicKey(N, e);
+ // incorrect value for hash value's type octet
+ // 0x0a instead of correct 0x04
+ var I = UTIL.binary.hex.decode(
+ '0001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff' +
+ 'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff' +
+ 'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff' +
+ 'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff' +
+ 'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff' +
+ 'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff' +
+ 'ffffffffffffffffffffffff003031300d060960864801650304020105000a20' +
+ '7509e5bda0c762d2bac7f90d758b5b2263fa01ccbc542ab5e3df163be08e6ca9');
+ var S = UTIL.binary.hex.decode(
+ 'c1acdd3aef5f0439c254980295fc0d81b628df00726310a1041d79b5dd94c11d' +
+ '3bcaf0236763c77c25d9ab49522ed2a7d6ea3a4e483a29838acd48f2d60a7902' +
+ '75f4cd46e4b1d09c527a426ec373e8a21746ad3ea541d3b85ba4c303ff793ea8' +
+ 'a0a3458e93a7ec42ed66f675d7c299b0817ac95f7f45b2f48c09b3c070171f31' +
+ 'a33ac789da9943da5dabcda1c95b42531d45484ac1efde0fe0519077debb9318' +
+ '3e63de8f80d7f3cbfecb03cbb44ac4a2d56699e33fca0663b79ca627755fc4fc' +
+ '684b4ab358a0b4ac5b7e9d0cc18b6ab6300b40781502a1c03d34f31dd19d8119' +
+ '5f8a44bc03a2595a706f06f0cb39b8e3f4afe06675fe7439b057f1200a06f4fd');
+
+ _checkBadDigestInfo(publicKey, S);
+ });
+ });
});
})();
diff --git a/tests/unit/x509.js b/tests/unit/x509.js
index 43a9ea61b..474e97a89 100644
--- a/tests/unit/x509.js
+++ b/tests/unit/x509.js
@@ -1206,6 +1206,70 @@ var UTIL = require('../../lib/util');
ASSERT.ok(issuer.verify(cert));
});
+ it('should calculate a certificate subject and issuer hash', function() {
+ var certPem = '-----BEGIN CERTIFICATE-----\r\n' +
+ 'MIIDZDCCAs2gAwIBAgIKQ8fjjgAAAABh3jANBgkqhkiG9w0BAQUFADBGMQswCQYD\r\n' +
+ 'VQQGEwJVUzETMBEGA1UEChMKR29vZ2xlIEluYzEiMCAGA1UEAxMZR29vZ2xlIElu\r\n' +
+ 'dGVybmV0IEF1dGhvcml0eTAeFw0xMjA2MjcxMzU5MTZaFw0xMzA2MDcxOTQzMjda\r\n' +
+ 'MGcxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1N\r\n' +
+ 'b3VudGFpbiBWaWV3MRMwEQYDVQQKEwpHb29nbGUgSW5jMRYwFAYDVQQDEw13d3cu\r\n' +
+ 'Z29vZ2xlLmRlMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCw2Hw3vNy5QMSd\r\n' +
+ '0/iMCS8lwZk9lnEk2NmrJt6vGJfRGlBprtHp5lpMFMoi+x8m8EwGVxXHGp7hLyN/\r\n' +
+ 'gXuUjL7/DY9fxxx9l77D+sDZz7jfUfWmhS03Ra1FbT6myF8miVZFChJ8XgWzioJY\r\n' +
+ 'gyNdRUC9149yrXdPWrSmSVaT0+tUCwIDAQABo4IBNjCCATIwHQYDVR0lBBYwFAYI\r\n' +
+ 'KwYBBQUHAwEGCCsGAQUFBwMCMB0GA1UdDgQWBBTiQGhrO3785rMPIKZ/zQEl5RyS\r\n' +
+ '0TAfBgNVHSMEGDAWgBS/wDDr9UMRPme6npH7/Gra42sSJDBbBgNVHR8EVDBSMFCg\r\n' +
+ 'TqBMhkpodHRwOi8vd3d3LmdzdGF0aWMuY29tL0dvb2dsZUludGVybmV0QXV0aG9y\r\n' +
+ 'aXR5L0dvb2dsZUludGVybmV0QXV0aG9yaXR5LmNybDBmBggrBgEFBQcBAQRaMFgw\r\n' +
+ 'VgYIKwYBBQUHMAKGSmh0dHA6Ly93d3cuZ3N0YXRpYy5jb20vR29vZ2xlSW50ZXJu\r\n' +
+ 'ZXRBdXRob3JpdHkvR29vZ2xlSW50ZXJuZXRBdXRob3JpdHkuY3J0MAwGA1UdEwEB\r\n' +
+ '/wQCMAAwDQYJKoZIhvcNAQEFBQADgYEAVJ0qt/MBvHEPuWHeH51756qy+lBNygLA\r\n' +
+ 'Xp5Gq+xHUTOzRty61BR05zv142hYAGWvpvnEOJ/DI7V3QlXK8a6dQ+du97obQJJx\r\n' +
+ '7ekqtfxVzmlSb23halYSoXmWgP8Tq0VUDsgsSLE7fS8JuO1soXUVKj1/6w189HL6\r\n' +
+ 'LsngXwZSuL0=\r\n' +
+ '-----END CERTIFICATE-----\r\n';
+ var issuerPem = '-----BEGIN CERTIFICATE-----\r\n' +
+ 'MIICsDCCAhmgAwIBAgIDC2dxMA0GCSqGSIb3DQEBBQUAME4xCzAJBgNVBAYTAlVT\r\n' +
+ 'MRAwDgYDVQQKEwdFcXVpZmF4MS0wKwYDVQQLEyRFcXVpZmF4IFNlY3VyZSBDZXJ0\r\n' +
+ 'aWZpY2F0ZSBBdXRob3JpdHkwHhcNMDkwNjA4MjA0MzI3WhcNMTMwNjA3MTk0MzI3\r\n' +
+ 'WjBGMQswCQYDVQQGEwJVUzETMBEGA1UEChMKR29vZ2xlIEluYzEiMCAGA1UEAxMZ\r\n' +
+ 'R29vZ2xlIEludGVybmV0IEF1dGhvcml0eTCBnzANBgkqhkiG9w0BAQEFAAOBjQAw\r\n' +
+ 'gYkCgYEAye23pIucV+eEPkB9hPSP0XFjU5nneXQUr0SZMyCSjXvlKAy6rWxJfoNf\r\n' +
+ 'NFlOCnowzdDXxFdF7dWq1nMmzq0yE7jXDx07393cCDaob1FEm8rWIFJztyaHNWrb\r\n' +
+ 'qeXUWaUr/GcZOfqTGBhs3t0lig4zFEfC7wFQeeT9adGnwKziV28CAwEAAaOBozCB\r\n' +
+ 'oDAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFL/AMOv1QxE+Z7qekfv8atrjaxIk\r\n' +
+ 'MB8GA1UdIwQYMBaAFEjmaPkr0rKV10fYIyAQTzOYkJ/UMBIGA1UdEwEB/wQIMAYB\r\n' +
+ 'Af8CAQAwOgYDVR0fBDMwMTAvoC2gK4YpaHR0cDovL2NybC5nZW90cnVzdC5jb20v\r\n' +
+ 'Y3Jscy9zZWN1cmVjYS5jcmwwDQYJKoZIhvcNAQEFBQADgYEAuIojxkiWsRF8YHde\r\n' +
+ 'BZqrocb6ghwYB8TrgbCoZutJqOkM0ymt9e8kTP3kS8p/XmOrmSfLnzYhLLkQYGfN\r\n' +
+ '0rTw8Ktx5YtaiScRhKqOv5nwnQkhClIZmloJ0pC3+gz4fniisIWvXEyZ2VxVKfml\r\n' +
+ 'UUIuOss4jHg7y/j7lYe8vJD5UDI=\r\n' +
+ '-----END CERTIFICATE-----\r\n';
+ var cert = PKI.certificateFromPem(certPem, true);
+ var issuer = PKI.certificateFromPem(issuerPem);
+ ASSERT.strictEqual(issuer.subject.hash, 'd43b6713ab1a8679f0b70e169e9df889ed387a4b');
+ ASSERT.strictEqual(cert.subject.hash, 'fd90a93e35c96cd6959f45ec60ca76faa4ce8926');
+ ASSERT.strictEqual(cert.issuer.hash, 'd43b6713ab1a8679f0b70e169e9df889ed387a4b');
+ });
+
+ it('should verify certificate with sha1WithRSASignature signature', function() {
+ var certPem = '-----BEGIN CERTIFICATE-----\r\n' +
+ 'MIIBwjCCAS+gAwIBAgIQj2d4hVEz0L1DYFVhA9CxCzAJBgUrDgMCHQUAMA8xDTAL\r\n' +
+ 'BgNVBAMTBFZQUzEwHhcNMDcwODE4MDkyODUzWhcNMDgwODE3MDkyODUzWjAPMQ0w\r\n' +
+ 'CwYDVQQDEwRWUFMxMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDaqKn40uaU\r\n' +
+ 'DbFL1NXXZ8/b4ZqDJ6eSI5lysMZHfZDs60G3ocbNKofBvURIutabrFuBCB2S5f/z\r\n' +
+ 'ICan0LR4uFpGuZ2I/PuVaU8X5fT8gBh7L636cWzHPPScYts00OyywEq381UB7XwX\r\n' +
+ 'YuWpM5kUW5rkbq1JV3ystTR/4YnLl48YtQIDAQABoycwJTATBgNVHSUEDDAKBggr\r\n' +
+ 'BgEFBQcDATAOBgNVHQ8EBwMFALAAAAAwCQYFKw4DAh0FAAOBgQBuUrU+J2Z5WKcO\r\n' +
+ 'VNjJHFUKo8qpbn8jKQZDl2nvVaXCTXQZblz/qxOm4FaGGzJ/m3GybVZNVfdyHg+U\r\n' +
+ 'lmDpFpOITkvcyNc3xjJCf2GVBo/VvdtVt7Myq0IQtAi/CXRK22BRNhSt9uu2EcRu\r\n' +
+ 'HIXdFWHEzi6eD4PpNw/0X3ID6Gxk4A==\r\n' +
+ '-----END CERTIFICATE-----\r\n';
+ var cert = PKI.certificateFromPem(certPem, true);
+ ASSERT.equal(cert.signatureOid, PKI.oids['sha1WithRSASignature']);
+ ASSERT.equal(cert.md.algorithm, 'sha1');
+ });
+
it('should verify certificate with sha256WithRSAEncryption signature', function() {
var certPem = '-----BEGIN CERTIFICATE-----\r\n' +
'MIIDuzCCAqOgAwIBAgIEO5vZjDANBgkqhkiG9w0BAQsFADBGMQswCQYDVQQGEwJE\r\n' +
diff --git a/tests/websockets/server-webid.js b/tests/websockets/server-webid.js
index 6f7cf37b8..c2d4e2a1b 100644
--- a/tests/websockets/server-webid.js
+++ b/tests/websockets/server-webid.js
@@ -80,7 +80,7 @@ var getPublicKey = function(data, uri, callback) {
var hex = CERT + 'hex';
var decimal = CERT + 'decimal';
- // gets a resource identifer from a node
+ // gets a resource identifier from a node
var getResource = function(node, key) {
var rval = null;
@@ -174,9 +174,10 @@ var fetchUrl = function(url, callback, redirects) {
console.log('Fetching URL: \"' + url + '\"');
// parse URL
- url = forge.util.parseUrl(url);
- var client = http.createClient(
- url.port, url.fullHost, url.scheme === 'https');
+ url = new URL(url);
+ var client = http.createClient({
+ url: url
+ });
var request = client.request('GET', url.path, {
Host: url.host,
Accept: 'application/rdf+xml'
diff --git a/webpack.config.js b/webpack.config.js
index 806401eff..df0db4d34 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -6,7 +6,6 @@
* Copyright 2011-2016 Digital Bazaar, Inc.
*/
const path = require('path');
-const webpack = require('webpack');
// build multiple outputs
module.exports = [];
@@ -79,6 +78,7 @@ outputs.forEach(info => {
// plain unoptimized unminified bundle
const bundle = Object.assign({}, common, {
+ mode: 'development',
output: {
path: path.join(__dirname, 'dist'),
filename: info.filenameBase + '.js',
@@ -95,6 +95,7 @@ outputs.forEach(info => {
// optimized and minified bundle
const minify = Object.assign({}, common, {
+ mode: 'production',
output: {
path: path.join(__dirname, 'dist'),
filename: info.filenameBase + '.min.js',
@@ -103,6 +104,7 @@ outputs.forEach(info => {
},
devtool: 'cheap-module-source-map',
plugins: [
+ /*
new webpack.optimize.UglifyJsPlugin({
sourceMap: true,
compress: {
@@ -113,6 +115,7 @@ outputs.forEach(info => {
}
//beautify: true
})
+ */
]
});
if(info.library === null) {