From f4c959c9c14c1e5615c6b01c8cad954efdfc1eff Mon Sep 17 00:00:00 2001 From: "David I. Lehn" Date: Fri, 30 Mar 2018 20:16:26 -0400 Subject: [PATCH 001/197] Start 0.7.6. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 6369621a9..96b229dc3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "node-forge", - "version": "0.7.5", + "version": "0.7.6-dev", "description": "JavaScript implementations of network transports, cryptography, ciphers, PKI, message digests, and various utilities.", "homepage": "https://github.com/digitalbazaar/forge", "author": { From dfdde475677a8a25c851e33e8f81dca60d90cfb9 Mon Sep 17 00:00:00 2001 From: "David I. Lehn" Date: Wed, 25 Apr 2018 17:21:51 -0400 Subject: [PATCH 002/197] Test with Node.js 10. --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index fc33ed895..0726fee33 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,6 +3,7 @@ node_js: - "4" - "6" - "8" + - "10" - "node" sudo: false install: npm install From 8467c04329753297c5e36f9efef6feea761baca8 Mon Sep 17 00:00:00 2001 From: Michael Siebert Date: Wed, 20 Jun 2018 08:53:51 +0200 Subject: [PATCH 003/197] Correctly detect webpack when trying to polyfill utils.setImmediate. Fixes regression in #385 When using a stock webpack config (which injects `process`), calling `forge.utils.setImmedate` fails with `invalid calling object` on IE11 and Edge. A fix for this was implemented in #390 but the detection didn't work properly. By checking also for `process.browser`, we can now properly detect a webpack / browser environment and use the proper workaround for the error (wrapping `setImmediate`) --- lib/util.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/util.js b/lib/util.js index eff883fa7..3e31690b9 100644 --- a/lib/util.js +++ b/lib/util.js @@ -13,8 +13,10 @@ var util = module.exports = forge.util = forge.util || {}; // define setImmediate and nextTick (function() { - // use native nextTick - if(typeof process !== 'undefined' && process.nextTick) { + // use native nextTick (unless we're in webpack) + // webpack (or better node-libs-browser polyfill) sets process.browser. + // this way we can detect webpack properly + if(typeof process !== 'undefined' && process.nextTick && !process.browser) { util.nextTick = process.nextTick; if(typeof setImmediate === 'function') { util.setImmediate = setImmediate; From 6a97e1934c720329582faff5abe88cba9bbe46a8 Mon Sep 17 00:00:00 2001 From: Valery Buchinsky Date: Wed, 8 Aug 2018 13:52:24 +0300 Subject: [PATCH 004/197] Add signDetached() to PKCS#7 --- lib/pkcs7.js | 52 ++++++++++++++++++++++++++++--------- tests/unit/pkcs7.js | 62 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 102 insertions(+), 12 deletions(-) diff --git a/lib/pkcs7.js b/lib/pkcs7.js index 66f3277f2..cb99d5b23 100644 --- a/lib/pkcs7.js +++ b/lib/pkcs7.js @@ -328,8 +328,26 @@ p7.createSignedData = function() { /** * Signs the content. + * + * Includes the signed data with digital signature. */ sign: function() { + msg.baseSign(false); + }, + + /** + * Signs the content in detached mode + * + * Includes the signature and certificate without the signed data. + */ + signDetached: function() { + msg.baseSign(true); + }, + + /** + * Signs the content. + */ + baseSign: function(detached) { // auto-generate content info if(typeof msg.content !== 'object' || msg.contentInfo === null) { // use Data ContentInfo @@ -349,12 +367,16 @@ p7.createSignedData = function() { content = forge.util.encodeUtf8(msg.content); } - msg.contentInfo.value.push( - // [0] EXPLICIT content - asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, true, [ - asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false, - content) - ])); + if (detached) { + msg.detachedContent = asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false, content); + } else { + msg.contentInfo.value.push( + // [0] EXPLICIT content + asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, true, [ + asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false, + content) + ])); + } } } @@ -437,10 +459,21 @@ p7.createSignedData = function() { } function addSignerInfos(mds) { + var content; + + if (msg.detachedContent) { + content = msg.detachedContent; + } else { + // get ContentInfo content + content = msg.contentInfo.value[1]; + // skip [0] EXPLICIT content wrapper + content = content.value[0]; + } + // Note: ContentInfo is a SEQUENCE with 2 values, second value is // the content field and is optional for a ContentInfo but required here // since signers are present - if(msg.contentInfo.value.length < 2) { + if(!content) { throw new Error( 'Could not sign PKCS#7 message; there is no content to sign.'); } @@ -448,11 +481,6 @@ p7.createSignedData = function() { // get ContentInfo content type var contentType = asn1.derToOid(msg.contentInfo.value[0].value); - // get ContentInfo content - var content = msg.contentInfo.value[1]; - // skip [0] EXPLICIT content wrapper - content = content.value[0]; - // serialize content var bytes = asn1.toDer(content); diff --git a/tests/unit/pkcs7.js b/tests/unit/pkcs7.js index c759d8720..69125fa9b 100644 --- a/tests/unit/pkcs7.js +++ b/tests/unit/pkcs7.js @@ -338,6 +338,43 @@ var support = require('./support'); 'BwEwHAYJKoZIhvcNAQkFMQ8XDTE4MDEyMjEyMzAwMlowIwYJKoZIhvcNAQkEMRYEFK1hHB7W5la2\r\n' + 'AWAHCWVgYPYyJzAxMAkGByqGSM44BAMELzAtAhUAsQXD04cP48o7HVHWJtVRHZEUkBICFHcuPVAu\r\n' + '7KVSbiWnFnDL0v87RSxhAAAAAAAA\r\n' + + '-----END PKCS7-----\r\n', + detachedSignature: + '-----BEGIN PKCS7-----\r\n' + + 'MIIGNAYJKoZIhvcNAQcCoIIGJTCCBiECAQExDzANBglghkgBZQMEAgEFADALBgkq\r\n' + + 'hkiG9w0BBwGgggO4MIIDtDCCApwCCQDUVBxA2DXi8zANBgkqhkiG9w0BAQUFADCB\r\n' + + 'mzELMAkGA1UEBhMCREUxEjAQBgNVBAgMCUZyYW5jb25pYTEQMA4GA1UEBwwHQW5z\r\n' + + 'YmFjaDEVMBMGA1UECgwMU3RlZmFuIFNpZWdsMRIwEAYDVQQLDAlHZWllcmxlaW4x\r\n' + + 'FjAUBgNVBAMMDUdlaWVybGVpbiBERVYxIzAhBgkqhkiG9w0BCQEWFHN0ZXNpZUBi\r\n' + + 'cm9rZW5waXBlLmRlMB4XDTEyMDMxODIyNTc0M1oXDTEzMDMxODIyNTc0M1owgZsx\r\n' + + 'CzAJBgNVBAYTAkRFMRIwEAYDVQQIDAlGcmFuY29uaWExEDAOBgNVBAcMB0Fuc2Jh\r\n' + + 'Y2gxFTATBgNVBAoMDFN0ZWZhbiBTaWVnbDESMBAGA1UECwwJR2VpZXJsZWluMRYw\r\n' + + 'FAYDVQQDDA1HZWllcmxlaW4gREVWMSMwIQYJKoZIhvcNAQkBFhRzdGVzaWVAYnJv\r\n' + + 'a2VucGlwZS5kZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMsAbQ4f\r\n' + + 'WevHqP1K1y/ewpMS3vYovBto7IsKBq0v3NmC2kPf3NhyaSKfjOOS5uAPONLffLck\r\n' + + '+iGdOLLFia6OSpM60tyQIV9lHoRh7fOEYORab0Z+aBUZcEGT9yotBOraX1YbKc5f\r\n' + + '9XO+80eG4XYvb5ua1NHrxWqe4w2p3zGJCKO+wHpvGkbKz0nfu36jwWz5aihfHi9h\r\n' + + 'p/xs8mfH86mIKiD7f2X2KeZ1PK9RvppA0X3lLb2VLOqMt+FHWicyZ/wjhQZ4oW55\r\n' + + 'ln2yYJUQ+adlgaYnPrtnsxmbTxM+99oF0F2/HmGrNs8nLZSva1Vy+hmjmWz6/O8Z\r\n' + + 'xhiIj7oBRqYcAocCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEAvfvtu31GFBO5+mFj\r\n' + + 'PAoR4BlzKq/H3EPOqS8cm/TjHgDRALwSnwKYCFs/bXqE4iOTD6otV4TusX3EPbqL\r\n' + + '2vzZQEcZn6paU/oZZVXwQqMqY5tf2teQiNxqxNmSIEPRHOr2QVBVIx2YF4Po89KG\r\n' + + 'UqJ9u/3/10lDqRwpsReijr5UKv5aygEcnwcW8+Ne4rTx934UDsutKG20dr5trZfW\r\n' + + 'QRVS9fS9CFwJehEXHAMUc/0++80NhfQthmWZWlWM1R3dr4TrIPtWdn5z0MtGeDvq\r\n' + + 'Bk7HjGrhcVS6kAsyZ9y/lfLPjBuxlQAHztEJCWgI4TW3/RLhgfg2gI1noM2n84Cd\r\n' + + 'misfkjGCAkAwggI8AgEBMIGpMIGbMQswCQYDVQQGEwJERTESMBAGA1UECAwJRnJh\r\n' + + 'bmNvbmlhMRAwDgYDVQQHDAdBbnNiYWNoMRUwEwYDVQQKDAxTdGVmYW4gU2llZ2wx\r\n' + + 'EjAQBgNVBAsMCUdlaWVybGVpbjEWMBQGA1UEAwwNR2VpZXJsZWluIERFVjEjMCEG\r\n' + + 'CSqGSIb3DQEJARYUc3Rlc2llQGJyb2tlbnBpcGUuZGUCCQDUVBxA2DXi8zANBglg\r\n' + + 'hkgBZQMEAgEFAKBpMBgGCSqGSIb3DQEJAzELBgkqhkiG9w0BBwEwLwYJKoZIhvcN\r\n' + + 'AQkEMSIEIC/0wwUPtS5pCJOLtE2nG0I+hr17VTeUqyxb02vkq/NjMBwGCSqGSIb3\r\n' + + 'DQEJBTEPFw01MDAxMDEwMDAwMDBaMA0GCSqGSIb3DQEBAQUABIIBAAF4/ivsVoaJ\r\n' + + 'aAR1vrq/DGfi9+SMwT6Zfk8Lssbh0NHsfXrko8oQ+01grokJqvDbVgdZOpeWt4dU\r\n' + + 'ZgWaqQnZC6kV5YbZNxMDSOP26svyiQ9UAN0q8V7oO9AHDEj2as/Zo9SIFNjgf6d4\r\n' + + 'Z4yuCgtGDtHVVqKMr9vL61v9yTsti4G9/8srhwoTS33/BNjD4icfPdNaGBSfgP+Z\r\n' + + '6m/90OGUus11leuRlpgs1hR1TU/ScfPIAAfemPigk18hox9vMMAdRs7JBGdKDDQr\r\n' + + 'c6mfUV75ZWEKFZM7y5bgX0IrolPexuMrOgeJzkzYtoMGwXA5fudHT3Nk53D3tLj3\r\n' + + 'x4KOfz69nJA=\r\n' + '-----END PKCS7-----\r\n' }; @@ -640,6 +677,31 @@ var support = require('./support'); ASSERT.equal(pem, _pem.signedDataWithAttrs1950UTCTime); }); + it('should create PKCS#7 detached signature', function() { + var p7 = PKCS7.createSignedData(); + p7.content = UTIL.createBuffer('To be signed.', 'utf8'); + p7.addCertificate(_pem.certificate); + p7.addSigner({ + key: PKI.privateKeyFromPem(_pem.privateKey), + certificate: _pem.certificate, + digestAlgorithm: PKI.oids.sha256, + authenticatedAttributes: [{ + type: forge.pki.oids.contentType, + value: forge.pki.oids.data + }, { + type: forge.pki.oids.messageDigest + // value will be auto-populated at signing time + }, { + type: forge.pki.oids.signingTime, + // will be encoded as UTC time because it's >= 1950 + value: new Date('1950-01-01T00:00:00Z') + }] + }); + p7.signDetached(); + var pem = PKCS7.messageToPem(p7); + ASSERT.equal(pem, _pem.detachedSignature); + }); + // FIXME: remove skipping PhantomJS tests when possible // skip 2049/2050 tests in PhantomJS // likely due to https://bugs.webkit.org/show_bug.cgi?id=130123 From 368a9cda0c3e4051d785ee0d505278a86cab0a50 Mon Sep 17 00:00:00 2001 From: Valery Buchinsky Date: Thu, 9 Aug 2018 17:17:26 +0300 Subject: [PATCH 005/197] Apply code review changes * baseSign() should not be public. sign() and signDetached() are the public ones; * Fix comments in addSignerInfos(); * Added jsDoc @param to _baseSign(); --- lib/pkcs7.js | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/lib/pkcs7.js b/lib/pkcs7.js index cb99d5b23..8244f88ed 100644 --- a/lib/pkcs7.js +++ b/lib/pkcs7.js @@ -332,7 +332,7 @@ p7.createSignedData = function() { * Includes the signed data with digital signature. */ sign: function() { - msg.baseSign(false); + msg._baseSign(false); }, /** @@ -341,13 +341,14 @@ p7.createSignedData = function() { * Includes the signature and certificate without the signed data. */ signDetached: function() { - msg.baseSign(true); + msg._baseSign(true); }, /** * Signs the content. + * @param detached If signing should be done in detached mode */ - baseSign: function(detached) { + _baseSign: function(detached) { // auto-generate content info if(typeof msg.content !== 'object' || msg.contentInfo === null) { // use Data ContentInfo @@ -462,17 +463,18 @@ p7.createSignedData = function() { var content; if (msg.detachedContent) { + // Signature has been made in detached mode. content = msg.detachedContent; } else { + // Note: ContentInfo is a SEQUENCE with 2 values, second value is + // the content field and is optional for a ContentInfo but required here + // since signers are present // get ContentInfo content content = msg.contentInfo.value[1]; // skip [0] EXPLICIT content wrapper content = content.value[0]; } - // Note: ContentInfo is a SEQUENCE with 2 values, second value is - // the content field and is optional for a ContentInfo but required here - // since signers are present if(!content) { throw new Error( 'Could not sign PKCS#7 message; there is no content to sign.'); From 00e9d15d781c4ee854ecee805511d8b3c8a674d8 Mon Sep 17 00:00:00 2001 From: Valery Buchinsky Date: Fri, 10 Aug 2018 10:31:55 +0300 Subject: [PATCH 006/197] _baseSign accepts options instead of a single boolean --- lib/pkcs7.js | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/lib/pkcs7.js b/lib/pkcs7.js index 8244f88ed..78eb69426 100644 --- a/lib/pkcs7.js +++ b/lib/pkcs7.js @@ -332,7 +332,7 @@ p7.createSignedData = function() { * Includes the signed data with digital signature. */ sign: function() { - msg._baseSign(false); + msg._baseSign(); }, /** @@ -341,14 +341,16 @@ p7.createSignedData = function() { * Includes the signature and certificate without the signed data. */ signDetached: function() { - msg._baseSign(true); + msg._baseSign({detached: true}); }, /** * Signs the content. - * @param detached If signing should be done in detached mode + * @param options Options to apply when signing: + * [detached] boolean. If signing should be done in detached mode. Defaults to false. */ - _baseSign: function(detached) { + _baseSign: function(options) { + options = options || {}; // auto-generate content info if(typeof msg.content !== 'object' || msg.contentInfo === null) { // use Data ContentInfo @@ -368,7 +370,7 @@ p7.createSignedData = function() { content = forge.util.encodeUtf8(msg.content); } - if (detached) { + if (options.detached) { msg.detachedContent = asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false, content); } else { msg.contentInfo.value.push( From e996ae2e2584a367ea320dd814ff0b14482fa7bd Mon Sep 17 00:00:00 2001 From: Valery Buchinsky Date: Mon, 13 Aug 2018 10:25:20 +0300 Subject: [PATCH 007/197] Remove signDetached. Use sign({detached: true}) instead --- lib/pkcs7.js | 20 +------------------- tests/unit/pkcs7.js | 2 +- 2 files changed, 2 insertions(+), 20 deletions(-) diff --git a/lib/pkcs7.js b/lib/pkcs7.js index 78eb69426..bb87de363 100644 --- a/lib/pkcs7.js +++ b/lib/pkcs7.js @@ -326,30 +326,12 @@ p7.createSignedData = function() { }); }, - /** - * Signs the content. - * - * Includes the signed data with digital signature. - */ - sign: function() { - msg._baseSign(); - }, - - /** - * Signs the content in detached mode - * - * Includes the signature and certificate without the signed data. - */ - signDetached: function() { - msg._baseSign({detached: true}); - }, - /** * Signs the content. * @param options Options to apply when signing: * [detached] boolean. If signing should be done in detached mode. Defaults to false. */ - _baseSign: function(options) { + sign: function(options) { options = options || {}; // auto-generate content info if(typeof msg.content !== 'object' || msg.contentInfo === null) { diff --git a/tests/unit/pkcs7.js b/tests/unit/pkcs7.js index 69125fa9b..0a4beafc3 100644 --- a/tests/unit/pkcs7.js +++ b/tests/unit/pkcs7.js @@ -697,7 +697,7 @@ var support = require('./support'); value: new Date('1950-01-01T00:00:00Z') }] }); - p7.signDetached(); + p7.sign({detached: true}); var pem = PKCS7.messageToPem(p7); ASSERT.equal(pem, _pem.detachedSignature); }); From d2940f468f251f42a5447ff22042ad7668f5b3ea Mon Sep 17 00:00:00 2001 From: Valery Buchinsky Date: Mon, 13 Aug 2018 10:49:24 +0300 Subject: [PATCH 008/197] Add a note on PKCS#7 detached signing --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 4a92660de..2efbfaa69 100644 --- a/README.md +++ b/README.md @@ -1378,6 +1378,10 @@ p7.addSigner({ p7.sign(); var pem = forge.pkcs7.messageToPem(p7); +// PKCS#7 Sign in detached mode. +// Includes the signature and certificate without the signed data. +p7.sign({detached: true}); + ``` From fb69220891a5ee58e017b48a1813208d968e6bac Mon Sep 17 00:00:00 2001 From: "David I. Lehn" Date: Tue, 14 Aug 2018 17:15:05 -0400 Subject: [PATCH 009/197] Release 0.7.6. --- CHANGELOG.md | 9 +++++++++ package.json | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f45b768fd..e7b95fa11 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,15 @@ Forge ChangeLog =============== +## 0.7.6 - 2018-08-14 + +### Added +- Test on Node.js 10.x. +- Support for PKCS#7 detached signatures. + +### Changed +- Improve webpack/browser detection. + ## 0.7.5 - 2018-03-30 ### Fixed diff --git a/package.json b/package.json index 96b229dc3..0d5b10ad3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "node-forge", - "version": "0.7.6-dev", + "version": "0.7.6", "description": "JavaScript implementations of network transports, cryptography, ciphers, PKI, message digests, and various utilities.", "homepage": "https://github.com/digitalbazaar/forge", "author": { From 8e54eca094b47a1b7ab62de45f8c3748a8bf9b15 Mon Sep 17 00:00:00 2001 From: "David I. Lehn" Date: Tue, 14 Aug 2018 17:15:38 -0400 Subject: [PATCH 010/197] Start 0.7.7. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 0d5b10ad3..7df75b2eb 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "node-forge", - "version": "0.7.6", + "version": "0.7.7-dev", "description": "JavaScript implementations of network transports, cryptography, ciphers, PKI, message digests, and various utilities.", "homepage": "https://github.com/digitalbazaar/forge", "author": { From b017ab82bbf611b3970d60c020abf4d3f6b7a883 Mon Sep 17 00:00:00 2001 From: Herpiko Dwi Aguno Date: Wed, 26 Sep 2018 12:10:50 +0800 Subject: [PATCH 011/197] Add description (2.5.4.13) to OID list. --- lib/oids.js | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/oids.js b/lib/oids.js index c908c7c86..381b7be3a 100644 --- a/lib/oids.js +++ b/lib/oids.js @@ -108,6 +108,7 @@ _IN('2.5.4.7', 'localityName'); _IN('2.5.4.8', 'stateOrProvinceName'); _IN('2.5.4.10', 'organizationName'); _IN('2.5.4.11', 'organizationalUnitName'); +_IN('2.5.4.13', 'description'); // X.509 extension OIDs _IN('2.16.840.1.113730.1.1', 'nsCertType'); From 2d6be74bd4b97f5ef07ab8c6b2136032c4c84feb Mon Sep 17 00:00:00 2001 From: sruetzler Date: Wed, 2 Jan 2019 14:50:41 +0100 Subject: [PATCH 012/197] rumo-3280: add test for certifcate after 2050 --- tests/unit/x509.js | 115 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 114 insertions(+), 1 deletion(-) diff --git a/tests/unit/x509.js b/tests/unit/x509.js index 2a4c032be..aeccce474 100644 --- a/tests/unit/x509.js +++ b/tests/unit/x509.js @@ -200,6 +200,60 @@ var UTIL = require('../../lib/util'); '-----END CERTIFICATE-----\r\n' ]; + var _pem_past_2050 = { + privateKey: '-----BEGIN PRIVATE KEY-----\r\n' + + 'MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCtQM1R5VoB8Hrr\r\n' + + 'hdL514H+oQN/FSIiHXsdoZlQqUEQ7yUBgC2c7fbq4Wm9AUHBkc/KfUGb9+ZeX6xo\r\n' + + '6L06M57QBD8nkSTStNHozlOwjnKu7DnlyW4j3ej9rBZzshLzAvWGZkx5sR1Eyz/2\r\n' + + '2Ns9PL+S4h3eiRGpTw3g3geI1wxBv3CCb/LUF5Wa4NWwIU1GP9S6d8MrOb3WGhR3\r\n' + + '64ZxajxSeX8R9WLsAo1Qx0dhsJ/UMwIibZ3hA9y1VBma2QyqQMzdY9OYC4oyE646\r\n' + + 'WfyJzHscpc6KNiVsWnYDP9+NStQlvaXMwhaPFpyVkLLB0vHho4oMXgEDTCH1iIe4\r\n' + + 'YGw01wQ/AgMBAAECggEAdx1wjHfFNEQkHr25WbDDXU9SWhMrjoz6UlsCT6SuaXgh\r\n' + + '1zBLK/OnqcEks5+jl/QqCqunahY8OnJI1S/+uX84Fwh0az2tNXjAQPFqNJ8bVgxv\r\n' + + 'mf6tTNeLEq04Gn856/4C1E6NEbWly+B5r7tUsHuNsuznYFKY4/DIN+wu/fPsJ17X\r\n' + + 'vbZiev6+OID/XCKPYWNRYiszBcmktGM5L+JLcYYPpxFYGFtw+khhUlazx0OmIuDB\r\n' + + 'AaePTtyXb1qQNKHv6wy7l+BTBKAlxoQQHMCw5qsnCr9RHed7LJr0kssUbVaseUOg\r\n' + + 'KMWtRG0Ms3ovOPWsVFC509gbGal8q+NSwyKd07SlQQKBgQDZX6bwE1H+V7r5GJwz\r\n' + + 'EGRLrMUDbbCb2qqZcSn+TCk9hsJ6jeSwz9KjZ2qONcmMl3WY6XAOdTjmJZl20wF9\r\n' + + 'GV58pZAoPhLh6tByX9RlUffXz+1KKMP0/5cxmwV8N0RBWrHcmRc6yMwJGZE7qBUh\r\n' + + 'JX/77xj9yPoDW+uOSGp+f0nZMQKBgQDMChqQF1cw7iIljzmtOhSnSEiCH7WYCJgp\r\n' + + 'um7KEGfFXxbNX07g6pojdjVFFf9uA6tPaiTnwXLc8wI8yiuwt8yPh60QTrcW/E/0\r\n' + + '+iQQGkwrFvWybbEuI++K6rOXdNMq7FEU467qN26lzHpagSEn6bXdss8L7AOcGRif\r\n' + + '0E/rSXlYbwKBgQDHFAA6vScJzmUxvyVG6ws/90IT6sClbHVzxB1WhX/7llDElvFM\r\n' + + 'MXlTJ+KBzacB+LC904VJ6Hes5+CN35/sZ3COrb7B7F+0wi4XocZO6OwYnZhPo9gb\r\n' + + 'qH1a9APpCGCdjid4xkhtEPs0llLZlQ2M5uA45ng37Xlz3Bp2m8HUilUi8QKBgQCa\r\n' + + 'RpV5F7zciWIWRipVGZJePeBdSz6SOwVan9V/QVJFQTXLiWHp3Fk5sPpsR0rAU1Pn\r\n' + + 'kxlehr2j5LZvYmoQj5jDedHYf7weTB7k23IDHu8ysYSLKjeK7K8FuZqbTUERtmdE\r\n' + + 'RTePbuRhxq9I2VRJioPxom680/KSx8L/q5GSFRcETwKBgDZhnBZ7+dIKlPAgfwPC\r\n' + + 'OCA2kKE+5UxyeAUeojNrSfHGdzF891PX+90D7sPfJi7SJU4E/UOMG/u8wUv5i2/+\r\n' + + '/nKOI6NwKB/m0FqpvxamuNJapn9RQ9bC7oz0Rj2Tho90mohwQG4DHF2uO8364Wb+\r\n' + + '9Bu1FCWmILPGcJM5iw6TJPA0\r\n' + + '-----END PRIVATE KEY-----\r\n', + certificate: '-----BEGIN CERTIFICATE-----\r\n' + + 'MIIDqjCCApKgAwIBAgIJAJvrXrdlRWTTMA0GCSqGSIb3DQEBCwUAMGgxCzAJBgNV\r\n' + + 'BAYTAlVTMRAwDgYDVQQIDAdWaXJnaW5hMRMwEQYDVQQHDApCbGFja3NidXJnMQ0w\r\n' + + 'CwYDVQQKDARUZXN0MQ0wCwYDVQQLDARUZXN0MRQwEgYDVQQDDAtleGFtcGxlLm9y\r\n' + + 'ZzAiGA8yMDUwMDIwMTIzMDAyOVoYDzIwNTEwMjAxMjMwMDI5WjBoMQswCQYDVQQG\r\n' + + 'EwJVUzEQMA4GA1UECAwHVmlyZ2luYTETMBEGA1UEBwwKQmxhY2tzYnVyZzENMAsG\r\n' + + 'A1UECgwEVGVzdDENMAsGA1UECwwEVGVzdDEUMBIGA1UEAwwLZXhhbXBsZS5vcmcw\r\n' + + 'ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCtQM1R5VoB8HrrhdL514H+\r\n' + + 'oQN/FSIiHXsdoZlQqUEQ7yUBgC2c7fbq4Wm9AUHBkc/KfUGb9+ZeX6xo6L06M57Q\r\n' + + 'BD8nkSTStNHozlOwjnKu7DnlyW4j3ej9rBZzshLzAvWGZkx5sR1Eyz/22Ns9PL+S\r\n' + + '4h3eiRGpTw3g3geI1wxBv3CCb/LUF5Wa4NWwIU1GP9S6d8MrOb3WGhR364ZxajxS\r\n' + + 'eX8R9WLsAo1Qx0dhsJ/UMwIibZ3hA9y1VBma2QyqQMzdY9OYC4oyE646WfyJzHsc\r\n' + + 'pc6KNiVsWnYDP9+NStQlvaXMwhaPFpyVkLLB0vHho4oMXgEDTCH1iIe4YGw01wQ/\r\n' + + 'AgMBAAGjUzBRMB0GA1UdDgQWBBRxifZwjEsDjYgbajBq+e1r4krdwjAfBgNVHSME\r\n' + + 'GDAWgBRxifZwjEsDjYgbajBq+e1r4krdwjAPBgNVHRMBAf8EBTADAQH/MA0GCSqG\r\n' + + 'SIb3DQEBCwUAA4IBAQB0V8zdJ1WebOvZNwl6WcbzNJRQePPnGp9pAbGuqpLZHvs6\r\n' + + 'geAocgmEqleGOsU9GT30MV1vtkR1IY6CWkVPeSiXS43HT8enoYCJX3AZd6ItUrQH\r\n' + + '8UonY8UqAmzsGLO+ttO5o6kEY6K0e1QUdmFkOh9Z6M9U3s3DASwrKQ/xFlHQ2mNi\r\n' + + 'h7pKaH2+XlDTrCjhO1ip0n4AwG5lgFJpJlVOZ9+Axzc146q/YZqrhXHYU152Wqo/\r\n' + + 'mFlygydsKNwWdpK5fwGBZkBR8AsZvNZaQ9Rr3Rr3y5Xz7+aPfLfWF5hW+d11ghuy\r\n' + + 'FDeZMUBehXXEJLXrirfmO2KFmy3iKrniJDDa35Lg\r\n' + + '-----END CERTIFICATE-----\r\n' + }; + + describe('x509', function() { it('should convert SHA-1 based certificate to/from PEM', function() { var certificate = PKI.certificateFromPem(_pem.certificate); @@ -211,6 +265,11 @@ var UTIL = require('../../lib/util'); ASSERT.equal(PKI.certificateToPem(certificate), _pem_sha256.certificate); }); + it('should convert certificate not before < 2050 < not after to/from PEM', function() { + var certificate = PKI.certificateFromPem(_pem_past_2050.certificate); + ASSERT.equal(PKI.certificateToPem(certificate), _pem_past_2050.certificate); + }); + it('should convert SHA-512 based certificate to/from PEM', function() { var certificate = PKI.certificateFromPem(_pem_sha512.certificate); ASSERT.equal(PKI.certificateToPem(certificate), _pem_sha512.certificate); @@ -231,6 +290,12 @@ var UTIL = require('../../lib/util'); ASSERT.ok(certificate.verify(certificate)); }); + it('should verify not before < 2050 < not after self-signed certificate', function() { + var certificate = PKI.certificateFromPem(_pem_past_2050.certificate); + ASSERT.ok(certificate.verify(certificate)); + }); + + it('should generate a certificate with authorityKeyIdentifier extension', function() { var keys = { privateKey: PKI.privateKeyFromPem(_pem.privateKey), @@ -396,6 +461,53 @@ var UTIL = require('../../lib/util'); var pem = PKI.certificateToPem(cert); cert = PKI.certificateFromPem(pem); + // verify certificate chain + var caStore = PKI.createCaStore(); + caStore.addCertificate(cert); + PKI.verifyCertificateChain(caStore, [cert], function(vfd, depth, chain) { + ASSERT.equal(vfd, true); + ASSERT.ok(cert.verifySubjectKeyIdentifier()); + return true; + }); + }); + + it('should generate a self-signed certificate after 2050', function() { + var keys = { + privateKey: PKI.privateKeyFromPem(_pem.privateKey), + publicKey: PKI.publicKeyFromPem(_pem.publicKey) + }; + var attrs = [{ + name: 'commonName', + value: 'example.org' + }, { + name: 'countryName', + value: 'US' + }, { + shortName: 'ST', + value: 'Virginia' + }, { + name: 'localityName', + value: 'Blacksburg' + }, { + name: 'organizationName', + value: 'Test' + }, { + shortName: 'OU', + value: 'Test' + }]; + var cert = createCertificate({ + publicKey: keys.publicKey, + signingKey: keys.privateKey, + serialNumber: '01', + subject: attrs, + issuer: attrs, + isCA: true, + notBefore : new Date("2050-02-02") + }); + + var pem = PKI.certificateToPem(cert); + cert = PKI.certificateFromPem(pem); + // verify certificate chain var caStore = PKI.createCaStore(); caStore.addCertificate(cert); @@ -1203,11 +1315,12 @@ var UTIL = require('../../lib/util'); var issuer = options.issuer; var isCA = options.isCA; var serialNumber = options.serialNumber || '01'; + var notBefore = options.notBefore || new Date(); var cert = PKI.createCertificate(); cert.publicKey = publicKey; cert.serialNumber = serialNumber; - cert.validity.notBefore = new Date(); + cert.validity.notBefore = notBefore; cert.validity.notAfter = new Date(); cert.validity.notAfter.setFullYear( cert.validity.notBefore.getFullYear() + 1); From fcda4a9162ef808dfa79e242eb1ecd2b80a94d08 Mon Sep 17 00:00:00 2001 From: sruetzler Date: Wed, 2 Jan 2019 15:52:01 +0100 Subject: [PATCH 013/197] rumo-3280: validity data after 2050 --- lib/x509.js | 25 +++++++++++++++++++------ tests/unit/x509.js | 20 ++++++++------------ 2 files changed, 27 insertions(+), 18 deletions(-) diff --git a/lib/x509.js b/lib/x509.js index 0f9ff6684..36c323471 100644 --- a/lib/x509.js +++ b/lib/x509.js @@ -2474,6 +2474,8 @@ function _CRIAttributesToAsn1(csr) { */ pki.getTBSCertificate = function(cert) { // TBSCertificate + var notBefore = dateToPem(cert.validity.notBefore); + var notAfter = dateToPem(cert.validity.notAfter); var tbs = asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [ // version asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, true, [ @@ -2497,12 +2499,8 @@ pki.getTBSCertificate = function(cert) { _dnToAsn1(cert.issuer), // validity asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [ - // notBefore - asn1.create(asn1.Class.UNIVERSAL, asn1.Type.UTCTIME, false, - asn1.dateToUtcTime(cert.validity.notBefore)), - // notAfter - asn1.create(asn1.Class.UNIVERSAL, asn1.Type.UTCTIME, false, - asn1.dateToUtcTime(cert.validity.notAfter)) + notBefore, + notAfter ]), // subject _dnToAsn1(cert.subject), @@ -2543,6 +2541,21 @@ pki.getTBSCertificate = function(cert) { return tbs; }; +const jan_1_1950 = new Date('1950-01-01T00:00:00Z'); +const jan_1_2050 = new Date('2050-01-01T00:00:00Z'); + +function dateToPem(date){ + if(date >= jan_1_1950 && date < jan_1_2050) { + return asn1.create( + asn1.Class.UNIVERSAL, asn1.Type.UTCTIME, false, + asn1.dateToUtcTime(date)); + } else { + return asn1.create( + asn1.Class.UNIVERSAL, asn1.Type.GENERALIZEDTIME, false, + asn1.dateToGeneralizedTime(date)); + } +} + /** * Gets the ASN.1 CertificationRequestInfo part of a * PKCS#10 CertificationRequest. diff --git a/tests/unit/x509.js b/tests/unit/x509.js index aeccce474..416c92680 100644 --- a/tests/unit/x509.js +++ b/tests/unit/x509.js @@ -471,7 +471,7 @@ var UTIL = require('../../lib/util'); }); }); - it('should generate a self-signed certificate after 2050', function() { + it('should generate a self-signed certificate after 2050', function() { var keys = { privateKey: PKI.privateKeyFromPem(_pem.privateKey), publicKey: PKI.publicKeyFromPem(_pem.publicKey) @@ -495,6 +495,7 @@ var UTIL = require('../../lib/util'); shortName: 'OU', value: 'Test' }]; + let notBefore = new Date("2050-02-02"); var cert = createCertificate({ publicKey: keys.publicKey, signingKey: keys.privateKey, @@ -502,20 +503,15 @@ var UTIL = require('../../lib/util'); subject: attrs, issuer: attrs, isCA: true, - notBefore : new Date("2050-02-02") + notBefore : notBefore }); var pem = PKI.certificateToPem(cert); cert = PKI.certificateFromPem(pem); - // verify certificate chain - var caStore = PKI.createCaStore(); - caStore.addCertificate(cert); - PKI.verifyCertificateChain(caStore, [cert], function(vfd, depth, chain) { - ASSERT.equal(vfd, true); - ASSERT.ok(cert.verifySubjectKeyIdentifier()); - return true; - }); + let notAfter = new Date("2051-02-02"); + ASSERT.equal(cert.validity.notBefore.toString(), notBefore.toString()); + ASSERT.equal(cert.validity.notAfter.toString(), notAfter.toString()); }); it('should generate and fail to verify a self-signed certificate that is not in the CA store', function() { @@ -1315,13 +1311,13 @@ var UTIL = require('../../lib/util'); var issuer = options.issuer; var isCA = options.isCA; var serialNumber = options.serialNumber || '01'; - var notBefore = options.notBefore || new Date(); + var notBefore = options.notBefore || new Date(); var cert = PKI.createCertificate(); cert.publicKey = publicKey; cert.serialNumber = serialNumber; cert.validity.notBefore = notBefore; - cert.validity.notAfter = new Date(); + cert.validity.notAfter = new Date(cert.validity.notBefore); cert.validity.notAfter.setFullYear( cert.validity.notBefore.getFullYear() + 1); cert.setSubject(subject); From 4db3187a8f656b2ea92fd5d93f7c5bc2f43852b7 Mon Sep 17 00:00:00 2001 From: sruetzler Date: Wed, 2 Jan 2019 16:05:59 +0100 Subject: [PATCH 014/197] rumo-3280: change version number --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 7df75b2eb..d31e1766f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "node-forge", - "version": "0.7.7-dev", + "version": "0.7.6-1", "description": "JavaScript implementations of network transports, cryptography, ciphers, PKI, message digests, and various utilities.", "homepage": "https://github.com/digitalbazaar/forge", "author": { From 7e0705ad3839dd8caf8e676d3787ef579a168881 Mon Sep 17 00:00:00 2001 From: sruetzler Date: Tue, 8 Jan 2019 11:40:59 +0100 Subject: [PATCH 015/197] use var instead of let --- tests/unit/x509.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/unit/x509.js b/tests/unit/x509.js index 416c92680..5acd84fce 100644 --- a/tests/unit/x509.js +++ b/tests/unit/x509.js @@ -495,7 +495,7 @@ var UTIL = require('../../lib/util'); shortName: 'OU', value: 'Test' }]; - let notBefore = new Date("2050-02-02"); + var notBefore = new Date("2050-02-02"); var cert = createCertificate({ publicKey: keys.publicKey, signingKey: keys.privateKey, @@ -509,7 +509,7 @@ var UTIL = require('../../lib/util'); var pem = PKI.certificateToPem(cert); cert = PKI.certificateFromPem(pem); - let notAfter = new Date("2051-02-02"); + var notAfter = new Date("2051-02-02"); ASSERT.equal(cert.validity.notBefore.toString(), notBefore.toString()); ASSERT.equal(cert.validity.notAfter.toString(), notAfter.toString()); }); From d87bd003bd4980e70874aac1907c14a427403aa5 Mon Sep 17 00:00:00 2001 From: sruetzler Date: Tue, 8 Jan 2019 11:41:14 +0100 Subject: [PATCH 016/197] fix version string --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d31e1766f..7df75b2eb 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "node-forge", - "version": "0.7.6-1", + "version": "0.7.7-dev", "description": "JavaScript implementations of network transports, cryptography, ciphers, PKI, message digests, and various utilities.", "homepage": "https://github.com/digitalbazaar/forge", "author": { From f2d907be2968aadc90d295e6f4d5e7909bcba01d Mon Sep 17 00:00:00 2001 From: sruetzler Date: Tue, 8 Jan 2019 11:41:45 +0100 Subject: [PATCH 017/197] rename dateToPem to _dateToAsn1 --- lib/x509.js | 42 +++++++++++++++++++++++++----------------- 1 file changed, 25 insertions(+), 17 deletions(-) diff --git a/lib/x509.js b/lib/x509.js index 36c323471..64faf457a 100644 --- a/lib/x509.js +++ b/lib/x509.js @@ -2465,6 +2465,29 @@ function _CRIAttributesToAsn1(csr) { return rval; } +const jan_1_1950 = new Date('1950-01-01T00:00:00Z'); +const jan_1_2050 = new Date('2050-01-01T00:00:00Z'); + +/** + * Converts a Date object to ASN.1 + * Handles the different format before and after 1st January 2050 + * + * @param date date object. + * + * @return the ASN.1 object representing the date. + */ + function _dateToAsn1(date){ + if(date >= jan_1_1950 && date < jan_1_2050) { + return asn1.create( + asn1.Class.UNIVERSAL, asn1.Type.UTCTIME, false, + asn1.dateToUtcTime(date)); + } else { + return asn1.create( + asn1.Class.UNIVERSAL, asn1.Type.GENERALIZEDTIME, false, + asn1.dateToGeneralizedTime(date)); + } +} + /** * Gets the ASN.1 TBSCertificate part of an X.509v3 certificate. * @@ -2474,8 +2497,8 @@ function _CRIAttributesToAsn1(csr) { */ pki.getTBSCertificate = function(cert) { // TBSCertificate - var notBefore = dateToPem(cert.validity.notBefore); - var notAfter = dateToPem(cert.validity.notAfter); + var notBefore = _dateToAsn1(cert.validity.notBefore); + var notAfter = _dateToAsn1(cert.validity.notAfter); var tbs = asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [ // version asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, true, [ @@ -2541,21 +2564,6 @@ pki.getTBSCertificate = function(cert) { return tbs; }; -const jan_1_1950 = new Date('1950-01-01T00:00:00Z'); -const jan_1_2050 = new Date('2050-01-01T00:00:00Z'); - -function dateToPem(date){ - if(date >= jan_1_1950 && date < jan_1_2050) { - return asn1.create( - asn1.Class.UNIVERSAL, asn1.Type.UTCTIME, false, - asn1.dateToUtcTime(date)); - } else { - return asn1.create( - asn1.Class.UNIVERSAL, asn1.Type.GENERALIZEDTIME, false, - asn1.dateToGeneralizedTime(date)); - } -} - /** * Gets the ASN.1 CertificationRequestInfo part of a * PKCS#10 CertificationRequest. From 6968b5c8c4c5171d8c406f7060292288c3db97d7 Mon Sep 17 00:00:00 2001 From: "David I. Lehn" Date: Tue, 8 Jan 2019 13:36:04 -0500 Subject: [PATCH 018/197] Update changelog. --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e7b95fa11..f6665bfc4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,13 @@ Forge ChangeLog =============== +### Fixed +- Handle creation of certificates with notBefore and notAfter dates less than + Jan 1, 1950 or greater than or equal to Jan 1, 2050. + +### Added +- Add OID 2.5.4.13 "description". + ## 0.7.6 - 2018-08-14 ### Added From 26bf82aed3b9814ab032cb88244b8176d6653bb1 Mon Sep 17 00:00:00 2001 From: "David I. Lehn" Date: Thu, 10 Jan 2019 19:03:25 -0500 Subject: [PATCH 019/197] Fix typo. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2efbfaa69..7ed09caa7 100644 --- a/README.md +++ b/README.md @@ -2096,4 +2096,4 @@ Financial support is welcome and helps contribute to futher development: [freenode]: https://freenode.net/ [unpkg]: https://unpkg.com/ [webpack]: https://webpack.github.io/ -[TweetNaCl]: https://github.com/dchest/tweetnacl-js +[TweetNaCl.js]: https://github.com/dchest/tweetnacl-js From d5bb56dc33b9d36010787c92540ed992a078f562 Mon Sep 17 00:00:00 2001 From: Jeff Garzik Date: Tue, 15 Jan 2019 03:07:05 +0000 Subject: [PATCH 020/197] README: Remove circular documentation reference Main README references testing README, which is a one-line file pointing the reader back to main. --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index 7ed09caa7..cee82b566 100644 --- a/README.md +++ b/README.md @@ -209,8 +209,6 @@ forge you need. Testing ------- -See the [testing README](./tests/README.md) for full details. - ### Prepare to run tests npm install From e3cb4c53cb3affed1b56fdb4b62a584d396f4d4f Mon Sep 17 00:00:00 2001 From: Jeff Garzik Date: Sat, 12 Jan 2019 23:03:21 +0000 Subject: [PATCH 021/197] x509: add support for Netscape comment extension This extension is in theory deprecated, but remains widely used, and supported by openssl and other tools. It is a useful method to associate a comment or memo with a certificate. OID 2.16.840.1.113730.1.13 requires IA5STRING value for the comment. We filter for this requirement, and also to avoid creating pathologically long strings by default. --- lib/oids.js | 1 + lib/x509.js | 8 +++++++ tests/unit/x509.js | 58 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 67 insertions(+) diff --git a/lib/oids.js b/lib/oids.js index 381b7be3a..a8f9a94e9 100644 --- a/lib/oids.js +++ b/lib/oids.js @@ -112,6 +112,7 @@ _IN('2.5.4.13', 'description'); // X.509 extension OIDs _IN('2.16.840.1.113730.1.1', 'nsCertType'); +_IN('2.16.840.1.113730.1.13', 'nsComment'); // depreciated in theory; still widely used _I_('2.5.29.1', 'authorityKeyIdentifier'); // deprecated, use .35 _I_('2.5.29.2', 'keyAttributes'); // obsolete use .37 or .15 _I_('2.5.29.3', 'certificatePolicies'); // deprecated, use .32 diff --git a/lib/x509.js b/lib/x509.js index 64faf457a..659b902ac 100644 --- a/lib/x509.js +++ b/lib/x509.js @@ -2272,6 +2272,14 @@ function _fillMissingExtensionFields(e, options) { asn1.Class.CONTEXT_SPECIFIC, altName.type, false, value)); } + } else if(e.name === 'nsComment' && options.cert) { + // sanity check value is ASCII (req'd) and not too big + if (!(/^[\x00-\x7F]*$/.test(e.comment)) || + (e.comment.length < 1) || (e.comment.length > 128)) + throw new Error("nsComment invalid content"); + // IA5STRING opaque comment + e.value = asn1.create( + asn1.Class.UNIVERSAL, asn1.Type.IA5STRING, false, e.comment); } else if(e.name === 'subjectKeyIdentifier' && options.cert) { var ski = options.cert.generateSubjectKeyIdentifier(); e.subjectKeyIdentifier = ski.toHex(); diff --git a/tests/unit/x509.js b/tests/unit/x509.js index 5acd84fce..858b3f2e2 100644 --- a/tests/unit/x509.js +++ b/tests/unit/x509.js @@ -425,6 +425,64 @@ var UTIL = require('../../lib/util'); }); }); + it('should generate a certificate with nsComment extension', function() { + var keys = { + privateKey: PKI.privateKeyFromPem(_pem.privateKey), + publicKey: PKI.publicKeyFromPem(_pem.publicKey) + }; + var attrs = [{ + name: 'commonName', + value: 'example.org' + }, { + name: 'countryName', + value: 'US' + }, { + shortName: 'ST', + value: 'Virginia' + }, { + name: 'localityName', + value: 'Blacksburg' + }, { + name: 'organizationName', + value: 'Test' + }, { + shortName: 'OU', + value: 'Test' + }]; + var dummyTestStr = 'node-forge is awesome'; + var cert = createCertificate({ + publicKey: keys.publicKey, + signingKey: keys.privateKey, + extensions: [{ + name: 'nsComment', + comment: dummyTestStr + }], + serialNumber: '01', + subject: attrs, + issuer: attrs, + isCA: true + }); + + // verify certificate encoding/parsing + var pem = PKI.certificateToPem(cert); + cert = PKI.certificateFromPem(pem); + + // verify cRLDistributionPoints extension + var index = findIndex(cert.extensions, {id: '2.16.840.1.113730.1.13'}); + ASSERT.ok(index !== -1); + var ext = cert.extensions[index]; + ASSERT.equal(ASN1.fromDer(ext.value).value, dummyTestStr); + + // verify certificate chain + var caStore = PKI.createCaStore(); + caStore.addCertificate(cert); + PKI.verifyCertificateChain(caStore, [cert], function(vfd, depth, chain) { + ASSERT.equal(vfd, true); + ASSERT.ok(cert.verifySubjectKeyIdentifier()); + return true; + }); + }); + it('should generate and verify a self-signed certificate', function() { var keys = { privateKey: PKI.privateKeyFromPem(_pem.privateKey), From fcbc3c56411c55c15c59cf106824808fd53653dd Mon Sep 17 00:00:00 2001 From: "David I. Lehn" Date: Mon, 14 Jan 2019 21:53:36 -0500 Subject: [PATCH 022/197] Update tests/unit/x509.js: fix Comment to match nsComment test code Credit: @davidlehn Co-Authored-By: jgarzik --- tests/unit/x509.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/x509.js b/tests/unit/x509.js index 858b3f2e2..613ca24fc 100644 --- a/tests/unit/x509.js +++ b/tests/unit/x509.js @@ -467,7 +467,7 @@ var UTIL = require('../../lib/util'); var pem = PKI.certificateToPem(cert); cert = PKI.certificateFromPem(pem); - // verify cRLDistributionPoints extension + // verify nsComment extension var index = findIndex(cert.extensions, {id: '2.16.840.1.113730.1.13'}); ASSERT.ok(index !== -1); var ext = cert.extensions[index]; From f79d85255732f716591eba48c64a3f572d6edda9 Mon Sep 17 00:00:00 2001 From: Dave Longley Date: Tue, 15 Jan 2019 00:27:04 -0500 Subject: [PATCH 023/197] Update lib/oids.js: fix nsComment comment typo Credit: @dlongley Co-Authored-By: jgarzik --- lib/oids.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/oids.js b/lib/oids.js index a8f9a94e9..328ebd36b 100644 --- a/lib/oids.js +++ b/lib/oids.js @@ -112,7 +112,7 @@ _IN('2.5.4.13', 'description'); // X.509 extension OIDs _IN('2.16.840.1.113730.1.1', 'nsCertType'); -_IN('2.16.840.1.113730.1.13', 'nsComment'); // depreciated in theory; still widely used +_IN('2.16.840.1.113730.1.13', 'nsComment'); // deprecated in theory; still widely used _I_('2.5.29.1', 'authorityKeyIdentifier'); // deprecated, use .35 _I_('2.5.29.2', 'keyAttributes'); // obsolete use .37 or .15 _I_('2.5.29.3', 'certificatePolicies'); // deprecated, use .32 From fba9c8a0ba2f2c6d84d0d712b3fa1e21b063ab63 Mon Sep 17 00:00:00 2001 From: Dave Longley Date: Thu, 17 Jan 2019 15:09:56 +0000 Subject: [PATCH 024/197] lib/x509.js: cosmetic style fix Signed-off-by: Jeff Garzik --- lib/x509.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/x509.js b/lib/x509.js index 659b902ac..8102d6303 100644 --- a/lib/x509.js +++ b/lib/x509.js @@ -2274,9 +2274,10 @@ function _fillMissingExtensionFields(e, options) { } } else if(e.name === 'nsComment' && options.cert) { // sanity check value is ASCII (req'd) and not too big - if (!(/^[\x00-\x7F]*$/.test(e.comment)) || - (e.comment.length < 1) || (e.comment.length > 128)) - throw new Error("nsComment invalid content"); + if(!(/^[\x00-\x7F]*$/.test(e.comment)) || + (e.comment.length < 1) || (e.comment.length > 128)) { + throw new Error('Invalid "nsComment" content.'); + } // IA5STRING opaque comment e.value = asn1.create( asn1.Class.UNIVERSAL, asn1.Type.IA5STRING, false, e.comment); From 4be5f0546faccf90c724f12bdf28859ce4e75112 Mon Sep 17 00:00:00 2001 From: "David I. Lehn" Date: Thu, 17 Jan 2019 13:45:10 -0500 Subject: [PATCH 025/197] Update changelog. --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f6665bfc4..b10d06ed5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ Forge ChangeLog ### Added - Add OID 2.5.4.13 "description". +- Add OID 2.16.840.1.113730.1.13 "nsComment". + - Also handled extension when creating a certificate. ## 0.7.6 - 2018-08-14 From 6853a9d632f0906a004d06a87569a113032009c0 Mon Sep 17 00:00:00 2001 From: Leo Selig Date: Thu, 31 May 2018 16:50:54 +0200 Subject: [PATCH 026/197] Detect WebCrypto API in web workers via `self` --- lib/prng.js | 13 ++++++------- lib/random.js | 14 +++++++------- lib/rsa.js | 28 ++++++++++++++++------------ lib/util.js | 12 ++++++++++++ 4 files changed, 41 insertions(+), 26 deletions(-) diff --git a/lib/prng.js b/lib/prng.js index b2eb1d430..442361d30 100644 --- a/lib/prng.js +++ b/lib/prng.js @@ -267,13 +267,12 @@ prng.create = function(plugin) { function defaultSeedFile(needed) { // use window.crypto.getRandomValues strong source of entropy if available var getRandomValues = null; - if(typeof window !== 'undefined') { - var _crypto = window.crypto || window.msCrypto; - if(_crypto && _crypto.getRandomValues) { - getRandomValues = function(arr) { - return _crypto.getRandomValues(arr); - }; - } + var globalScope = forge.util.getGlobalScope(); + var _crypto = globalScope.crypto || globalScope.msCrypto; + if(_crypto && _crypto.getRandomValues) { + getRandomValues = function(arr) { + return _crypto.getRandomValues(arr); + }; } var b = forge.util.createBuffer(); diff --git a/lib/random.js b/lib/random.js index d4f1928ca..74d851f82 100644 --- a/lib/random.js +++ b/lib/random.js @@ -115,14 +115,14 @@ var _ctx = spawnPrng(); // add other sources of entropy only if window.crypto.getRandomValues is not // available -- otherwise this source will be automatically used by the prng var getRandomValues = null; -if(typeof window !== 'undefined') { - var _crypto = window.crypto || window.msCrypto; - if(_crypto && _crypto.getRandomValues) { - getRandomValues = function(arr) { - return _crypto.getRandomValues(arr); - }; - } +var globalScope = forge.util.getGlobalScope(); +var _crypto = globalScope.crypto || globalScope.msCrypto; +if(_crypto && _crypto.getRandomValues) { + getRandomValues = function(arr) { + return _crypto.getRandomValues(arr); + }; } + if(forge.options.usePureJavaScript || (!forge.util.isNodejs && !getRandomValues)) { // if this is a web worker, do not use weak entropy, instead register to diff --git a/lib/rsa.js b/lib/rsa.js index fd4d988d5..7e3e99be1 100644 --- a/lib/rsa.js +++ b/lib/rsa.js @@ -77,6 +77,9 @@ if(typeof BigInteger === 'undefined') { // shortcut for asn.1 API var asn1 = forge.asn1; +// shortcut for util API +var util = forge.util; + /* * RSA encryption and decryption, see RFC 2313. */ @@ -84,6 +87,7 @@ forge.pki = forge.pki || {}; module.exports = forge.pki.rsa = forge.rsa = forge.rsa || {}; var pki = forge.pki; + // for finding primes, which are 30k+i for i = 1, 7, 11, 13, 17, 19, 23, 29 var GCD_30_DELTA = [6, 4, 2, 4, 2, 4, 6, 2]; @@ -889,14 +893,14 @@ pki.rsa.generateKeyPair = function(bits, e, options, callback) { bits >= 256 && bits <= 16384 && (e === 0x10001 || e === 3)) { if(_detectSubtleCrypto('generateKey') && _detectSubtleCrypto('exportKey')) { // use standard native generateKey - return window.crypto.subtle.generateKey({ + return util.getGlobalScope().crypto.subtle.generateKey({ name: 'RSASSA-PKCS1-v1_5', modulusLength: bits, publicExponent: _intToUint8Array(e), hash: {name: 'SHA-256'} }, true /* key can be exported*/, ['sign', 'verify']) .then(function(pair) { - return window.crypto.subtle.exportKey('pkcs8', pair.privateKey); + return util.getGlobalScope().crypto.subtle.exportKey('pkcs8', pair.privateKey); // avoiding catch(function(err) {...}) to support IE <= 8 }).then(undefined, function(err) { callback(err); @@ -913,7 +917,7 @@ pki.rsa.generateKeyPair = function(bits, e, options, callback) { } if(_detectSubtleMsCrypto('generateKey') && _detectSubtleMsCrypto('exportKey')) { - var genOp = window.msCrypto.subtle.generateKey({ + var genOp = util.getGlobalScope().msCrypto.subtle.generateKey({ name: 'RSASSA-PKCS1-v1_5', modulusLength: bits, publicExponent: _intToUint8Array(e), @@ -921,7 +925,7 @@ pki.rsa.generateKeyPair = function(bits, e, options, callback) { }, true /* key can be exported*/, ['sign', 'verify']); genOp.oncomplete = function(e) { var pair = e.target.result; - var exportOp = window.msCrypto.subtle.exportKey( + var exportOp = util.getGlobalScope().msCrypto.subtle.exportKey( 'pkcs8', pair.privateKey); exportOp.oncomplete = function(e) { var pkcs8 = e.target.result; @@ -1735,10 +1739,10 @@ function _getMillerRabinTests(bits) { * @return true if detected, false if not. */ function _detectSubtleCrypto(fn) { - return (typeof window !== 'undefined' && - typeof window.crypto === 'object' && - typeof window.crypto.subtle === 'object' && - typeof window.crypto.subtle[fn] === 'function'); + return (typeof util.getGlobalScope() !== 'undefined' && + typeof util.getGlobalScope().crypto === 'object' && + typeof util.getGlobalScope().crypto.subtle === 'object' && + typeof util.getGlobalScope().crypto.subtle[fn] === 'function'); } /** @@ -1751,10 +1755,10 @@ function _detectSubtleCrypto(fn) { * @return true if detected, false if not. */ function _detectSubtleMsCrypto(fn) { - return (typeof window !== 'undefined' && - typeof window.msCrypto === 'object' && - typeof window.msCrypto.subtle === 'object' && - typeof window.msCrypto.subtle[fn] === 'function'); + return (typeof util.getGlobalScope() !== 'undefined' && + typeof util.getGlobalScope().msCrypto === 'object' && + typeof util.getGlobalScope().msCrypto.subtle === 'object' && + typeof util.getGlobalScope().msCrypto.subtle[fn] === 'function'); } function _intToUint8Array(x) { diff --git a/lib/util.js b/lib/util.js index 3e31690b9..7b7b78a71 100644 --- a/lib/util.js +++ b/lib/util.js @@ -106,6 +106,18 @@ var util = module.exports = forge.util = forge.util || {}; util.nextTick = util.setImmediate; })(); + +// 'self' will also work in Web Workers (instance of WorkerGlobalScope) while it will point to +// `window` in the main thread +// To remain compatible with older browsers, we fall back to 'window' if 'self' is not available +util.getGlobalScope = function() { + if (util.isNodejs) { + return global; + } + + return typeof self === "undefined" ? window : self; +}; + // check if running under Node.js util.isNodejs = typeof process !== 'undefined' && process.versions && process.versions.node; From eff68d67d3042e84b658f240bced2fb7c01fe196 Mon Sep 17 00:00:00 2001 From: Leo Selig Date: Mon, 4 Jun 2018 11:44:25 +0200 Subject: [PATCH 027/197] Introduce different bunlde entry point for karma tests This will allow for separate, karma-only test suites in the future. --- karma.conf.js | 4 ++-- tests/karma/index.js | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) create mode 100644 tests/karma/index.js diff --git a/karma.conf.js b/karma.conf.js index 8422ea762..442b5e37b 100644 --- a/karma.conf.js +++ b/karma.conf.js @@ -30,7 +30,7 @@ module.exports = function(config) { // list of files / patterns to load in the browser files: [ - 'tests/unit/index.js', + 'tests/karma/index.js', // for webworkers { pattern: 'lib/prime.worker.js', @@ -45,7 +45,7 @@ module.exports = function(config) { // preprocess matching files before serving them to the browser // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor preprocessors: { - 'tests/unit/index.js': preprocessors, + 'tests/karma/index.js': preprocessors, 'lib/prime.worker.js': workerPreprocessors }, diff --git a/tests/karma/index.js b/tests/karma/index.js new file mode 100644 index 000000000..26ea4195f --- /dev/null +++ b/tests/karma/index.js @@ -0,0 +1,2 @@ +// The karma tests include all standard unit tests... +require('../unit'); From 91e516a545b0bc9318180c57f632da9306a946e4 Mon Sep 17 00:00:00 2001 From: Leo Selig Date: Mon, 4 Jun 2018 11:44:48 +0200 Subject: [PATCH 028/197] Add karma test for web worker compatibility of key pair generation --- package.json | 3 ++- tests/karma/index.js | 3 +++ tests/karma/rsa.js | 28 ++++++++++++++++++++++++++++ tests/karma/testWorker.js | 35 +++++++++++++++++++++++++++++++++++ 4 files changed, 68 insertions(+), 1 deletion(-) create mode 100644 tests/karma/rsa.js create mode 100644 tests/karma/testWorker.js diff --git a/package.json b/package.json index 7df75b2eb..557ec4a35 100644 --- a/package.json +++ b/package.json @@ -40,7 +40,8 @@ "nodejs-websocket": "^1.7.1", "nyc": "^11.5.0", "opts": "^1.2.2", - "webpack": "^3.11.0" + "webpack": "^3.11.0", + "worker-loader": "^2.0.0" }, "repository": { "type": "git", diff --git a/tests/karma/index.js b/tests/karma/index.js index 26ea4195f..05d8220b3 100644 --- a/tests/karma/index.js +++ b/tests/karma/index.js @@ -1,2 +1,5 @@ // The karma tests include all standard unit tests... require('../unit'); + +// ...plus some tests that can only run in the browser (e.g. web worker compatibility) +require('./rsa'); \ No newline at end of file diff --git a/tests/karma/rsa.js b/tests/karma/rsa.js new file mode 100644 index 000000000..6282adc7a --- /dev/null +++ b/tests/karma/rsa.js @@ -0,0 +1,28 @@ +var ASSERT = require('assert'); +var RSA = require('../../lib/rsa'); + +// The worker-loader build the ./testWorker.js as a separate bundle and returns a constructor +// that saves us from having to know the public script path +// I.e.: new Worker('path/to/public/script.js') becomes new TestWorker() +var TestWorker = require('worker-loader!./testWorker'); +var testWorker = new TestWorker(); + + +describe('rsa', function() { + it('should generate key pairs when running forge in a web worker', function(done) { + // Make test worker call rsa.generateKeyPair() on its own side + testWorker.postMessage({method:'rsa.generateKeyPair'}); + + // Wait for a result (see testWorker.js for what event data to expect) + testWorker.addEventListener('message', function (event) { + ASSERT.equal(event.data.type, 'success'); + // This is primarily to ensure that the node-forge code runs successfully in the worker + // (e.g. no usages of `window` or similar). So, comparing for structural sanity of the result + // only should be fine + ASSERT.equal(typeof event.data.result.publicKey, 'string'); + ASSERT.equal(typeof event.data.result.privateKey, 'string'); + + done(); + }); + }); +}); diff --git a/tests/karma/testWorker.js b/tests/karma/testWorker.js new file mode 100644 index 000000000..69d07ad71 --- /dev/null +++ b/tests/karma/testWorker.js @@ -0,0 +1,35 @@ +var RSA = require('../../lib/rsa'); +var PKI = require('../../lib/pki'); + +// The following code allows main thread scripts to make the worker call certain forge APIs within +// the worker (primarily to ensure compatibility) +// We define a light-weight protocol to simplify testing +self.addEventListener('message', function (event) { + + // Test scripts call worker.postMessage(data)` – we can access the payload via event.data + + // data.method defines what the worker should call (we skip sophisticated reflection-magic, + // argument passing, etc. for now) + switch (event.data.method) { + case 'rsa.generateKeyPair': + RSA.generateKeyPair({ bits: 512, workers: -1 }, function(error, keyPair) { + // We signal the outcome of the call via event.data with... + if (error) { + // ...event.data.type === 'error' if the call failed + self.postMessage({ + type: 'error', + result: error.toString() + }); + } else { + // ...event.data.type === 'success' if the call succeeded + self.postMessage({ + type: 'success', + result: { + publicKey: PKI.publicKeyToPem(keyPair.publicKey), + privateKey: PKI.privateKeyToPem(keyPair.privateKey), + } + }); + } + }); + } +}); \ No newline at end of file From 091ed541e0b09558549fa9263b30d4f6db8d1163 Mon Sep 17 00:00:00 2001 From: Leo Selig Date: Fri, 24 Aug 2018 10:00:15 +0200 Subject: [PATCH 029/197] Optimize `getGlobalScope()`-function to `globalScope`-constant The evaluation function stays the same but is wrapped as an IEFE and executed once on module initialization. This should avoid unnecessary recomputations in the relying code. --- lib/prng.js | 2 +- lib/random.js | 2 +- lib/rsa.js | 24 ++++++++++++------------ lib/util.js | 14 +++++++------- 4 files changed, 21 insertions(+), 21 deletions(-) diff --git a/lib/prng.js b/lib/prng.js index 442361d30..c2f5f0518 100644 --- a/lib/prng.js +++ b/lib/prng.js @@ -267,7 +267,7 @@ prng.create = function(plugin) { function defaultSeedFile(needed) { // use window.crypto.getRandomValues strong source of entropy if available var getRandomValues = null; - var globalScope = forge.util.getGlobalScope(); + var globalScope = forge.util.globalScope; var _crypto = globalScope.crypto || globalScope.msCrypto; if(_crypto && _crypto.getRandomValues) { getRandomValues = function(arr) { diff --git a/lib/random.js b/lib/random.js index 74d851f82..d4e4bea9e 100644 --- a/lib/random.js +++ b/lib/random.js @@ -115,7 +115,7 @@ var _ctx = spawnPrng(); // add other sources of entropy only if window.crypto.getRandomValues is not // available -- otherwise this source will be automatically used by the prng var getRandomValues = null; -var globalScope = forge.util.getGlobalScope(); +var globalScope = forge.util.globalScope; var _crypto = globalScope.crypto || globalScope.msCrypto; if(_crypto && _crypto.getRandomValues) { getRandomValues = function(arr) { diff --git a/lib/rsa.js b/lib/rsa.js index 7e3e99be1..2338be2bc 100644 --- a/lib/rsa.js +++ b/lib/rsa.js @@ -893,14 +893,14 @@ pki.rsa.generateKeyPair = function(bits, e, options, callback) { bits >= 256 && bits <= 16384 && (e === 0x10001 || e === 3)) { if(_detectSubtleCrypto('generateKey') && _detectSubtleCrypto('exportKey')) { // use standard native generateKey - return util.getGlobalScope().crypto.subtle.generateKey({ + return util.globalScope.crypto.subtle.generateKey({ name: 'RSASSA-PKCS1-v1_5', modulusLength: bits, publicExponent: _intToUint8Array(e), hash: {name: 'SHA-256'} }, true /* key can be exported*/, ['sign', 'verify']) .then(function(pair) { - return util.getGlobalScope().crypto.subtle.exportKey('pkcs8', pair.privateKey); + return util.globalScope.crypto.subtle.exportKey('pkcs8', pair.privateKey); // avoiding catch(function(err) {...}) to support IE <= 8 }).then(undefined, function(err) { callback(err); @@ -917,7 +917,7 @@ pki.rsa.generateKeyPair = function(bits, e, options, callback) { } if(_detectSubtleMsCrypto('generateKey') && _detectSubtleMsCrypto('exportKey')) { - var genOp = util.getGlobalScope().msCrypto.subtle.generateKey({ + var genOp = util.globalScope.msCrypto.subtle.generateKey({ name: 'RSASSA-PKCS1-v1_5', modulusLength: bits, publicExponent: _intToUint8Array(e), @@ -925,7 +925,7 @@ pki.rsa.generateKeyPair = function(bits, e, options, callback) { }, true /* key can be exported*/, ['sign', 'verify']); genOp.oncomplete = function(e) { var pair = e.target.result; - var exportOp = util.getGlobalScope().msCrypto.subtle.exportKey( + var exportOp = util.globalScope.msCrypto.subtle.exportKey( 'pkcs8', pair.privateKey); exportOp.oncomplete = function(e) { var pkcs8 = e.target.result; @@ -1739,10 +1739,10 @@ function _getMillerRabinTests(bits) { * @return true if detected, false if not. */ function _detectSubtleCrypto(fn) { - return (typeof util.getGlobalScope() !== 'undefined' && - typeof util.getGlobalScope().crypto === 'object' && - typeof util.getGlobalScope().crypto.subtle === 'object' && - typeof util.getGlobalScope().crypto.subtle[fn] === 'function'); + return (typeof util.globalScope !== 'undefined' && + typeof util.globalScope.crypto === 'object' && + typeof util.globalScope.crypto.subtle === 'object' && + typeof util.globalScope.crypto.subtle[fn] === 'function'); } /** @@ -1755,10 +1755,10 @@ function _detectSubtleCrypto(fn) { * @return true if detected, false if not. */ function _detectSubtleMsCrypto(fn) { - return (typeof util.getGlobalScope() !== 'undefined' && - typeof util.getGlobalScope().msCrypto === 'object' && - typeof util.getGlobalScope().msCrypto.subtle === 'object' && - typeof util.getGlobalScope().msCrypto.subtle[fn] === 'function'); + return (typeof util.globalScope !== 'undefined' && + typeof util.globalScope.msCrypto === 'object' && + typeof util.globalScope.msCrypto.subtle === 'object' && + typeof util.globalScope.msCrypto.subtle[fn] === 'function'); } function _intToUint8Array(x) { diff --git a/lib/util.js b/lib/util.js index 7b7b78a71..ddb87a08c 100644 --- a/lib/util.js +++ b/lib/util.js @@ -106,21 +106,21 @@ var util = module.exports = forge.util = forge.util || {}; util.nextTick = util.setImmediate; })(); +// check if running under Node.js +util.isNodejs = + typeof process !== 'undefined' && process.versions && process.versions.node; + // 'self' will also work in Web Workers (instance of WorkerGlobalScope) while it will point to // `window` in the main thread // To remain compatible with older browsers, we fall back to 'window' if 'self' is not available -util.getGlobalScope = function() { +util.globalScope = (function() { if (util.isNodejs) { return global; } - return typeof self === "undefined" ? window : self; -}; - -// check if running under Node.js -util.isNodejs = - typeof process !== 'undefined' && process.versions && process.versions.node; + return typeof self === 'undefined' ? window : self; +})(); // define isArray util.isArray = Array.isArray || function(x) { From 54be0eb3c6d2a9efafb4bb39b870bd988ffd4c8e Mon Sep 17 00:00:00 2001 From: Leo Selig Date: Fri, 24 Aug 2018 10:01:53 +0200 Subject: [PATCH 030/197] Fix code formatting issues These were mentioned in the PR review here: https://github.com/digitalbazaar/forge/pull/586#issuecomment-413004807 --- tests/karma/index.js | 2 +- tests/karma/rsa.js | 2 +- tests/karma/testWorker.js | 50 +++++++++++++++++++-------------------- 3 files changed, 27 insertions(+), 27 deletions(-) diff --git a/tests/karma/index.js b/tests/karma/index.js index 05d8220b3..95a16dec5 100644 --- a/tests/karma/index.js +++ b/tests/karma/index.js @@ -2,4 +2,4 @@ require('../unit'); // ...plus some tests that can only run in the browser (e.g. web worker compatibility) -require('./rsa'); \ No newline at end of file +require('./rsa'); diff --git a/tests/karma/rsa.js b/tests/karma/rsa.js index 6282adc7a..5522a90e3 100644 --- a/tests/karma/rsa.js +++ b/tests/karma/rsa.js @@ -11,7 +11,7 @@ var testWorker = new TestWorker(); describe('rsa', function() { it('should generate key pairs when running forge in a web worker', function(done) { // Make test worker call rsa.generateKeyPair() on its own side - testWorker.postMessage({method:'rsa.generateKeyPair'}); + testWorker.postMessage({ method: 'rsa.generateKeyPair' }); // Wait for a result (see testWorker.js for what event data to expect) testWorker.addEventListener('message', function (event) { diff --git a/tests/karma/testWorker.js b/tests/karma/testWorker.js index 69d07ad71..ce54f92d1 100644 --- a/tests/karma/testWorker.js +++ b/tests/karma/testWorker.js @@ -6,30 +6,30 @@ var PKI = require('../../lib/pki'); // We define a light-weight protocol to simplify testing self.addEventListener('message', function (event) { - // Test scripts call worker.postMessage(data)` – we can access the payload via event.data + // Test scripts call worker.postMessage(data)` – we can access the payload via event.data - // data.method defines what the worker should call (we skip sophisticated reflection-magic, - // argument passing, etc. for now) - switch (event.data.method) { - case 'rsa.generateKeyPair': - RSA.generateKeyPair({ bits: 512, workers: -1 }, function(error, keyPair) { - // We signal the outcome of the call via event.data with... - if (error) { - // ...event.data.type === 'error' if the call failed - self.postMessage({ - type: 'error', - result: error.toString() - }); - } else { - // ...event.data.type === 'success' if the call succeeded - self.postMessage({ - type: 'success', - result: { - publicKey: PKI.publicKeyToPem(keyPair.publicKey), - privateKey: PKI.privateKeyToPem(keyPair.privateKey), - } - }); - } - }); - } + // data.method defines what the worker should call (we skip sophisticated reflection-magic, + // argument passing, etc. for now) + switch (event.data.method) { + case 'rsa.generateKeyPair': + RSA.generateKeyPair({ bits: 512, workers: -1 }, function(error, keyPair) { + // We signal the outcome of the call via event.data with... + if (error) { + // ...event.data.type === 'error' if the call failed + self.postMessage({ + type: 'error', + result: error.toString() + }); + } else { + // ...event.data.type === 'success' if the call succeeded + self.postMessage({ + type: 'success', + result: { + publicKey: PKI.publicKeyToPem(keyPair.publicKey), + privateKey: PKI.privateKeyToPem(keyPair.privateKey), + } + }); + } + }); + } }); \ No newline at end of file From 27217a58e2fe74054becc7d2df7da2d92e727cfc Mon Sep 17 00:00:00 2001 From: "David I. Lehn" Date: Thu, 31 Jan 2019 01:08:36 -0500 Subject: [PATCH 031/197] Remove unused require. --- tests/karma/rsa.js | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/karma/rsa.js b/tests/karma/rsa.js index 5522a90e3..244e79575 100644 --- a/tests/karma/rsa.js +++ b/tests/karma/rsa.js @@ -1,5 +1,4 @@ var ASSERT = require('assert'); -var RSA = require('../../lib/rsa'); // The worker-loader build the ./testWorker.js as a separate bundle and returns a constructor // that saves us from having to know the public script path From 97777e73d5004302edceea35077319ed4ba74280 Mon Sep 17 00:00:00 2001 From: "David I. Lehn" Date: Thu, 31 Jan 2019 01:09:01 -0500 Subject: [PATCH 032/197] Use unique suite name. --- tests/karma/rsa.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/karma/rsa.js b/tests/karma/rsa.js index 244e79575..b21d6fac2 100644 --- a/tests/karma/rsa.js +++ b/tests/karma/rsa.js @@ -7,9 +7,9 @@ var TestWorker = require('worker-loader!./testWorker'); var testWorker = new TestWorker(); -describe('rsa', function() { +describe('web worker rsa', function() { it('should generate key pairs when running forge in a web worker', function(done) { - // Make test worker call rsa.generateKeyPair() on its own side + // Make test worker call rsa.generateKeyPair() on its own side testWorker.postMessage({ method: 'rsa.generateKeyPair' }); // Wait for a result (see testWorker.js for what event data to expect) From 3ade5afb41a9b1cd85a14a25c4745b6625af90f0 Mon Sep 17 00:00:00 2001 From: "David I. Lehn" Date: Thu, 31 Jan 2019 01:37:02 -0500 Subject: [PATCH 033/197] Formatting. --- tests/karma/rsa.js | 11 +++++------ tests/karma/testWorker.js | 17 +++++++++-------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/tests/karma/rsa.js b/tests/karma/rsa.js index b21d6fac2..29ea1d200 100644 --- a/tests/karma/rsa.js +++ b/tests/karma/rsa.js @@ -1,12 +1,11 @@ var ASSERT = require('assert'); -// The worker-loader build the ./testWorker.js as a separate bundle and returns a constructor -// that saves us from having to know the public script path +// The worker-loader build the ./testWorker.js as a separate bundle and returns +// a constructor that saves us from having to know the public script path // I.e.: new Worker('path/to/public/script.js') becomes new TestWorker() var TestWorker = require('worker-loader!./testWorker'); var testWorker = new TestWorker(); - describe('web worker rsa', function() { it('should generate key pairs when running forge in a web worker', function(done) { // Make test worker call rsa.generateKeyPair() on its own side @@ -15,9 +14,9 @@ describe('web worker rsa', function() { // Wait for a result (see testWorker.js for what event data to expect) testWorker.addEventListener('message', function (event) { ASSERT.equal(event.data.type, 'success'); - // This is primarily to ensure that the node-forge code runs successfully in the worker - // (e.g. no usages of `window` or similar). So, comparing for structural sanity of the result - // only should be fine + // This is primarily to ensure that the node-forge code runs successfully + // in the worker (e.g. no usages of `window` or similar). So, comparing + // for structural sanity of the result only should be fine ASSERT.equal(typeof event.data.result.publicKey, 'string'); ASSERT.equal(typeof event.data.result.privateKey, 'string'); diff --git a/tests/karma/testWorker.js b/tests/karma/testWorker.js index ce54f92d1..3f682e1b7 100644 --- a/tests/karma/testWorker.js +++ b/tests/karma/testWorker.js @@ -1,15 +1,16 @@ var RSA = require('../../lib/rsa'); var PKI = require('../../lib/pki'); -// The following code allows main thread scripts to make the worker call certain forge APIs within -// the worker (primarily to ensure compatibility) -// We define a light-weight protocol to simplify testing +// The following code allows main thread scripts to make the worker call +// certain forge APIs within the worker (primarily to ensure compatibility) +// We define a light-weight protocol to simplify testing self.addEventListener('message', function (event) { - - // Test scripts call worker.postMessage(data)` – we can access the payload via event.data - // data.method defines what the worker should call (we skip sophisticated reflection-magic, - // argument passing, etc. for now) + // Test scripts call worker.postMessage(data) + // we can access the payload via event.data + + // data.method defines what the worker should call (we skip sophisticated + // reflection-magic, argument passing, etc. for now) switch (event.data.method) { case 'rsa.generateKeyPair': RSA.generateKeyPair({ bits: 512, workers: -1 }, function(error, keyPair) { @@ -32,4 +33,4 @@ self.addEventListener('message', function (event) { } }); } -}); \ No newline at end of file +}); From 8f82ad1bf5c8b0a981ea84891eb6ec2d6f7a7e97 Mon Sep 17 00:00:00 2001 From: "David I. Lehn" Date: Thu, 31 Jan 2019 02:16:12 -0500 Subject: [PATCH 034/197] Improve browserify support. - Rename web worker rsa test for clarity. - Only run web worker test when testing webpack. --- karma.conf.js | 22 +++++++++++++--------- tests/karma/index.js | 7 +++++-- tests/karma/{rsa.js => web-worker-rsa.js} | 0 3 files changed, 18 insertions(+), 11 deletions(-) rename tests/karma/{rsa.js => web-worker-rsa.js} (100%) diff --git a/karma.conf.js b/karma.conf.js index 442b5e37b..175b50736 100644 --- a/karma.conf.js +++ b/karma.conf.js @@ -9,6 +9,15 @@ module.exports = function(config) { var preprocessors = []; // webworker bundle preprocessors (always use webpack) var workerPreprocessors = ['webpack', 'sourcemap']; + // files/patterns to load + var files = [ + 'tests/karma/index.js', + // for webworkers + { + pattern: 'lib/prime.worker.js', + watched: false, included: false, served: true, nocache: false + } + ]; if(bundler === 'browserify') { frameworks.push(bundler); @@ -16,6 +25,7 @@ module.exports = function(config) { } else if(bundler === 'webpack') { preprocessors.push(bundler); preprocessors.push('sourcemap'); + files.push('tests/karma/web-worker-rsa.js'); } else { throw Error('Unknown bundler'); } @@ -29,14 +39,7 @@ module.exports = function(config) { frameworks: frameworks, // list of files / patterns to load in the browser - files: [ - 'tests/karma/index.js', - // for webworkers - { - pattern: 'lib/prime.worker.js', - watched: false, included: false, served: true, nocache: false - } - ], + files: files, // list of files to exclude exclude: [ @@ -45,7 +48,8 @@ module.exports = function(config) { // preprocess matching files before serving them to the browser // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor preprocessors: { - 'tests/karma/index.js': preprocessors, + 'tests/unit/**.js': preprocessors, + 'tests/karma/**.js': preprocessors, 'lib/prime.worker.js': workerPreprocessors }, diff --git a/tests/karma/index.js b/tests/karma/index.js index 95a16dec5..8d95c9422 100644 --- a/tests/karma/index.js +++ b/tests/karma/index.js @@ -1,5 +1,8 @@ // The karma tests include all standard unit tests... require('../unit'); -// ...plus some tests that can only run in the browser (e.g. web worker compatibility) -require('./rsa'); +// ...plus some tests that can only run in the browser (e.g. web worker +// compatibility) +// FIXME: if browserify testing is dropped, enable this and remove browserify +// specific code from karma.config.js +//require('./web-worker-rsa'); diff --git a/tests/karma/rsa.js b/tests/karma/web-worker-rsa.js similarity index 100% rename from tests/karma/rsa.js rename to tests/karma/web-worker-rsa.js From e53d956dc72def7040e98c28c0170e9706c51c43 Mon Sep 17 00:00:00 2001 From: "David I. Lehn" Date: Thu, 31 Jan 2019 02:22:25 -0500 Subject: [PATCH 035/197] Update changelog. --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b10d06ed5..1d5c21082 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,9 @@ Forge ChangeLog - Add OID 2.16.840.1.113730.1.13 "nsComment". - Also handled extension when creating a certificate. +### Changed +- Support WebCrypto API in web workers. + ## 0.7.6 - 2018-08-14 ### Added From 2bb68397473a69e402e5bbf5f1e08a24a1119474 Mon Sep 17 00:00:00 2001 From: Dave Longley Date: Thu, 31 Jan 2019 13:52:53 -0500 Subject: [PATCH 036/197] Fix style. Co-Authored-By: davidlehn --- lib/util.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/util.js b/lib/util.js index ddb87a08c..c49a95d9c 100644 --- a/lib/util.js +++ b/lib/util.js @@ -115,7 +115,7 @@ util.isNodejs = // `window` in the main thread // To remain compatible with older browsers, we fall back to 'window' if 'self' is not available util.globalScope = (function() { - if (util.isNodejs) { + if(util.isNodejs) { return global; } From c02c4bbb7292fdb61f14ae2a627725afa5279871 Mon Sep 17 00:00:00 2001 From: "David I. Lehn" Date: Thu, 31 Jan 2019 13:53:54 -0500 Subject: [PATCH 037/197] Fix wrapping. --- lib/util.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/util.js b/lib/util.js index c49a95d9c..a67ee5182 100644 --- a/lib/util.js +++ b/lib/util.js @@ -111,14 +111,15 @@ util.isNodejs = typeof process !== 'undefined' && process.versions && process.versions.node; -// 'self' will also work in Web Workers (instance of WorkerGlobalScope) while it will point to -// `window` in the main thread -// To remain compatible with older browsers, we fall back to 'window' if 'self' is not available +// 'self' will also work in Web Workers (instance of WorkerGlobalScope) while +// it will point to `window` in the main thread. +// To remain compatible with older browsers, we fall back to 'window' if 'self' +// is not available. util.globalScope = (function() { if(util.isNodejs) { return global; } - + return typeof self === 'undefined' ? window : self; })(); From 2807076ad132fe053b9d4e48a977f2efeedc5718 Mon Sep 17 00:00:00 2001 From: Kevin Ennis Date: Sun, 13 Jan 2019 09:08:58 -0500 Subject: [PATCH 038/197] Use node.crypto.generateKeyPair when available --- lib/rsa.js | 42 ++++++++++++++++++++++++++++++++++++++++++ lib/util.js | 5 +++++ tests/unit/rsa.js | 6 +++--- 3 files changed, 50 insertions(+), 3 deletions(-) diff --git a/lib/rsa.js b/lib/rsa.js index 2338be2bc..527bf80bd 100644 --- a/lib/rsa.js +++ b/lib/rsa.js @@ -74,6 +74,8 @@ if(typeof BigInteger === 'undefined') { var BigInteger = forge.jsbn.BigInteger; } +var _crypto = forge.util.isNodejs ? require('crypto') : null; + // shortcut for asn.1 API var asn1 = forge.asn1; @@ -891,6 +893,30 @@ pki.rsa.generateKeyPair = function(bits, e, options, callback) { // key generation code if available and if parameters are acceptable if(!forge.options.usePureJavaScript && callback && bits >= 256 && bits <= 16384 && (e === 0x10001 || e === 3)) { + if(forge.util.isNodejs && _detectNodeCrypto('generateKeyPair')) { + return _crypto.generateKeyPair('rsa', { + modulusLength: bits, + publicExponent: e, + publicKeyEncoding: { + type: 'pkcs1', + format: 'der' + }, + privateKeyEncoding: { + type: 'pkcs8', + format: 'der' + } + }, function(err, pub, pkcs8) { + if (err) { + return callback(err); + } + var privateKey = pki.privateKeyFromAsn1( + asn1.fromDer(forge.util.createBuffer(pkcs8))); + callback(null, { + privateKey: privateKey, + publicKey: pki.setRsaPublicKey(privateKey.n, privateKey.e) + }); + }); + } if(_detectSubtleCrypto('generateKey') && _detectSubtleCrypto('exportKey')) { // use standard native generateKey return util.globalScope.crypto.subtle.generateKey({ @@ -1731,6 +1757,22 @@ function _getMillerRabinTests(bits) { return 2; } +/** + * Performs feature detection on the Node crypto interface. + * + * @param fn the feature (function) to detect. + * + * @return true if detected, false if not. + */ +function _detectNodeCrypto(fn) { + try { + var crypto = require('crypto'); + return typeof crypto[fn] === 'function'; + } catch (err) { + return false; + } +} + /** * Performs feature detection on the SubtleCrypto interface. * diff --git a/lib/util.js b/lib/util.js index a67ee5182..82961c68e 100644 --- a/lib/util.js +++ b/lib/util.js @@ -138,6 +138,11 @@ util.isArrayBufferView = function(x) { return x && util.isArrayBuffer(x.buffer) && x.byteLength !== undefined; }; +// Node versions >= 10.2 support native keyPair generation, +// which is non-deterministic +util.isDeterministic = util.isNodejs && + typeof require('crypto').generateKeyPair !== 'function'; + /** * Ensure a bits param is 8, 16, 24, or 32. Used to validate input for * algorithms where bit manipulation, JavaScript limitations, and/or algorithm diff --git a/tests/unit/rsa.js b/tests/unit/rsa.js index 763f2ab60..10f88306d 100644 --- a/tests/unit/rsa.js +++ b/tests/unit/rsa.js @@ -189,7 +189,7 @@ var UTIL = require('../../lib/util'); var pair1 = _genSync({samePrng: true}); _genAsync({samePrng: true}, function(pair2) { // check if the same on supported deterministic platforms - if(UTIL.isNodejs) { + if(UTIL.isDeterministic) { _pairCmp(pair1, pair2); } done(); @@ -200,7 +200,7 @@ var UTIL = require('../../lib/util'); _genAsync({samePrng: true}, function(pair1) { var pair2 = _genSync({samePrng: true}); // check if the same on supported deterministic platforms - if(UTIL.isNodejs) { + if(UTIL.isDeterministic) { _pairCmp(pair1, pair2); } done(); @@ -214,7 +214,7 @@ var UTIL = require('../../lib/util'); function _done() { if(pair1 && pair2) { // check if the same on supported deterministic platforms - if(UTIL.isNodejs) { + if(UTIL.isDeterministic) { _pairCmp(pair1, pair2); } done(); From c999f7856b5c474c2b3568e02444a6fe298174e3 Mon Sep 17 00:00:00 2001 From: Kevin Ennis Date: Sun, 13 Jan 2019 09:53:14 -0500 Subject: [PATCH 039/197] Address PR feedback --- lib/rsa.js | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/lib/rsa.js b/lib/rsa.js index 527bf80bd..2b7822718 100644 --- a/lib/rsa.js +++ b/lib/rsa.js @@ -893,24 +893,23 @@ pki.rsa.generateKeyPair = function(bits, e, options, callback) { // key generation code if available and if parameters are acceptable if(!forge.options.usePureJavaScript && callback && bits >= 256 && bits <= 16384 && (e === 0x10001 || e === 3)) { - if(forge.util.isNodejs && _detectNodeCrypto('generateKeyPair')) { + if(_detectNodeCrypto('generateKeyPair')) { return _crypto.generateKeyPair('rsa', { modulusLength: bits, publicExponent: e, publicKeyEncoding: { type: 'pkcs1', - format: 'der' + format: 'pem' }, privateKeyEncoding: { type: 'pkcs8', - format: 'der' + format: 'pem' } - }, function(err, pub, pkcs8) { + }, function(err, pub, priv) { if (err) { return callback(err); } - var privateKey = pki.privateKeyFromAsn1( - asn1.fromDer(forge.util.createBuffer(pkcs8))); + var privateKey = pki.privateKeyFromPem(priv); callback(null, { privateKey: privateKey, publicKey: pki.setRsaPublicKey(privateKey.n, privateKey.e) @@ -1765,12 +1764,7 @@ function _getMillerRabinTests(bits) { * @return true if detected, false if not. */ function _detectNodeCrypto(fn) { - try { - var crypto = require('crypto'); - return typeof crypto[fn] === 'function'; - } catch (err) { - return false; - } + return forge.util.isNodejs && typeof _crypto[fn] === 'function'; } /** From 0c17d9a4b33241ca53c914a278fd2b10e6bc735f Mon Sep 17 00:00:00 2001 From: Kevin Ennis Date: Sun, 13 Jan 2019 10:50:58 -0500 Subject: [PATCH 040/197] Simplify public key creation --- lib/rsa.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/rsa.js b/lib/rsa.js index 2b7822718..e83d47e6f 100644 --- a/lib/rsa.js +++ b/lib/rsa.js @@ -909,10 +909,9 @@ pki.rsa.generateKeyPair = function(bits, e, options, callback) { if (err) { return callback(err); } - var privateKey = pki.privateKeyFromPem(priv); callback(null, { - privateKey: privateKey, - publicKey: pki.setRsaPublicKey(privateKey.n, privateKey.e) + privateKey: pki.privateKeyFromPem(priv), + publicKey: pki.publicKeyFromPem(pub) }); }); } From 1a0c12a64a0f3983b5c9b57bfff34b33c6554cc7 Mon Sep 17 00:00:00 2001 From: Kevin Ennis Date: Mon, 14 Jan 2019 15:12:06 -0500 Subject: [PATCH 041/197] Fix isDeterministic comment --- lib/util.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/util.js b/lib/util.js index 82961c68e..ce86812ac 100644 --- a/lib/util.js +++ b/lib/util.js @@ -138,7 +138,7 @@ util.isArrayBufferView = function(x) { return x && util.isArrayBuffer(x.buffer) && x.byteLength !== undefined; }; -// Node versions >= 10.2 support native keyPair generation, +// Node versions >= 10.12.0 support native keyPair generation, // which is non-deterministic util.isDeterministic = util.isNodejs && typeof require('crypto').generateKeyPair !== 'function'; From 3e7393640d1a8cc12430b9ced39a3371af2eb040 Mon Sep 17 00:00:00 2001 From: Kevin Ennis Date: Fri, 25 Jan 2019 19:43:49 -0500 Subject: [PATCH 042/197] Switch Node generateKeyPair public key from pkcs1 to spki --- lib/rsa.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rsa.js b/lib/rsa.js index e83d47e6f..21e76b485 100644 --- a/lib/rsa.js +++ b/lib/rsa.js @@ -898,7 +898,7 @@ pki.rsa.generateKeyPair = function(bits, e, options, callback) { modulusLength: bits, publicExponent: e, publicKeyEncoding: { - type: 'pkcs1', + type: 'spki', format: 'pem' }, privateKeyEncoding: { From 90c4f5c4f5d4bf2b33ff6851a98f5be94cbe7665 Mon Sep 17 00:00:00 2001 From: Kevin Ennis Date: Fri, 25 Jan 2019 19:44:16 -0500 Subject: [PATCH 043/197] Move isDeterministic to rsa test file --- lib/util.js | 5 ----- tests/unit/rsa.js | 11 ++++++++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/util.js b/lib/util.js index ce86812ac..a67ee5182 100644 --- a/lib/util.js +++ b/lib/util.js @@ -138,11 +138,6 @@ util.isArrayBufferView = function(x) { return x && util.isArrayBuffer(x.buffer) && x.byteLength !== undefined; }; -// Node versions >= 10.12.0 support native keyPair generation, -// which is non-deterministic -util.isDeterministic = util.isNodejs && - typeof require('crypto').generateKeyPair !== 'function'; - /** * Ensure a bits param is 8, 16, 24, or 32. Used to validate input for * algorithms where bit manipulation, JavaScript limitations, and/or algorithm diff --git a/tests/unit/rsa.js b/tests/unit/rsa.js index 10f88306d..44f1bcb10 100644 --- a/tests/unit/rsa.js +++ b/tests/unit/rsa.js @@ -127,6 +127,11 @@ var UTIL = require('../../lib/util'); }); } + // Node versions >= 10.12.0 support native keyPair generation, + // which is non-deterministic + var isDeterministic = UTIL.isNodejs && + typeof require('crypto').generateKeyPair !== 'function'; + it('should generate 512 bit key pair (sync)', function() { _genSync(); }); @@ -189,7 +194,7 @@ var UTIL = require('../../lib/util'); var pair1 = _genSync({samePrng: true}); _genAsync({samePrng: true}, function(pair2) { // check if the same on supported deterministic platforms - if(UTIL.isDeterministic) { + if(isDeterministic) { _pairCmp(pair1, pair2); } done(); @@ -200,7 +205,7 @@ var UTIL = require('../../lib/util'); _genAsync({samePrng: true}, function(pair1) { var pair2 = _genSync({samePrng: true}); // check if the same on supported deterministic platforms - if(UTIL.isDeterministic) { + if(isDeterministic) { _pairCmp(pair1, pair2); } done(); @@ -214,7 +219,7 @@ var UTIL = require('../../lib/util'); function _done() { if(pair1 && pair2) { // check if the same on supported deterministic platforms - if(UTIL.isDeterministic) { + if(isDeterministic) { _pairCmp(pair1, pair2); } done(); From 980d094410142da7367b81453e903a7eea3ac27e Mon Sep 17 00:00:00 2001 From: "David I. Lehn" Date: Thu, 31 Jan 2019 00:04:23 -0500 Subject: [PATCH 044/197] Use native rsa.generateKeyPair APIs if possible. - Use native Node.js 10.12.0+ key generation APIs if possible. Support both async and sync. - Always use JS fallback if prng is specified. - Only run key generation comparison tests if keys will be deterministic. - Improve docs. --- CHANGELOG.md | 6 ++ README.md | 8 +-- lib/rsa.js | 172 ++++++++++++++++++++++++++-------------------- tests/unit/rsa.js | 94 +++++++++++++++++++------ 4 files changed, 180 insertions(+), 100 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1d5c21082..5067590eb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,12 @@ Forge ChangeLog ### Changed - Support WebCrypto API in web workers. +- `rsa.generateKeyPair`: + - Use `crypto.generateKeyPair`/`crypto.generateKeyPairSync` on Node.js if + available (10.12.0+) and not in pure JS mode. + - Use JS fallback in `rsa.generateKeyPair` if `prng` option specified since + this isn't supported by current native APIs. + - Only run key generation comparison tests if keys will be deterministic. ## 0.7.6 - 2018-08-14 diff --git a/README.md b/README.md index cee82b566..0606722e3 100644 --- a/README.md +++ b/README.md @@ -959,14 +959,14 @@ __Examples__ var rsa = forge.pki.rsa; // generate an RSA key pair synchronously -// *NOT RECOMMENDED* -- can be significantly slower than async and will not -// use native APIs if available. +// *NOT RECOMMENDED*: Can be significantly slower than async and may block +// JavaScript execution. Will use native Node.js 10.12.0+ API if possible. var keypair = rsa.generateKeyPair({bits: 2048, e: 0x10001}); // generate an RSA key pair asynchronously (uses web workers if available) // use workers: -1 to run a fast core estimator to optimize # of workers -// *RECOMMENDED* - can be significantly faster than sync -- and will use -// native APIs if available. +// *RECOMMENDED*: Can be significantly faster than sync. Will use native +// Node.js 10.12.0+ or WebCrypto API if possible. rsa.generateKeyPair({bits: 2048, workers: 2}, function(err, keypair) { // keypair.privateKey, keypair.publicKey }); diff --git a/lib/rsa.js b/lib/rsa.js index 21e76b485..03343f4ba 100644 --- a/lib/rsa.js +++ b/lib/rsa.js @@ -826,7 +826,7 @@ pki.rsa.stepKeyPairGenerationState = function(state, n) { * @param [bits] the size for the private key in bits, defaults to 2048. * @param [e] the public exponent to use, defaults to 65537. * @param [options] options for key-pair generation, if given then 'bits' - * and 'e' must *not* be given: + * and 'e' must *not* be given: * bits the size for the private key in bits, (default: 2048). * e the public exponent to use, (default: 65537 (0x10001)). * workerScript the worker script URL. @@ -836,7 +836,7 @@ pki.rsa.stepKeyPairGenerationState = function(state, n) { * numbers for each web worker to check per work assignment, * (default: 100). * prng a custom crypto-secure pseudo-random number generator to use, - * that must define "getBytesSync". + * that must define "getBytesSync". Disables use of native APIs. * algorithm the algorithm to use (default: 'PRIMEINC'). * @param [callback(err, keypair)] called once the operation completes. * @@ -889,85 +889,107 @@ pki.rsa.generateKeyPair = function(bits, e, options, callback) { e = options.e || 0x10001; } - // if native code is permitted and a callback is given, use native - // key generation code if available and if parameters are acceptable - if(!forge.options.usePureJavaScript && callback && + // use native code if permitted, available, and parameters are acceptable + if(!forge.options.usePureJavaScript && !options.prng && bits >= 256 && bits <= 16384 && (e === 0x10001 || e === 3)) { - if(_detectNodeCrypto('generateKeyPair')) { - return _crypto.generateKeyPair('rsa', { - modulusLength: bits, - publicExponent: e, - publicKeyEncoding: { - type: 'spki', - format: 'pem' - }, - privateKeyEncoding: { - type: 'pkcs8', - format: 'pem' - } - }, function(err, pub, priv) { - if (err) { - return callback(err); - } - callback(null, { - privateKey: pki.privateKeyFromPem(priv), - publicKey: pki.publicKeyFromPem(pub) - }); - }); - } - if(_detectSubtleCrypto('generateKey') && _detectSubtleCrypto('exportKey')) { - // use standard native generateKey - return util.globalScope.crypto.subtle.generateKey({ - name: 'RSASSA-PKCS1-v1_5', - modulusLength: bits, - publicExponent: _intToUint8Array(e), - hash: {name: 'SHA-256'} - }, true /* key can be exported*/, ['sign', 'verify']) - .then(function(pair) { - return util.globalScope.crypto.subtle.exportKey('pkcs8', pair.privateKey); - // avoiding catch(function(err) {...}) to support IE <= 8 - }).then(undefined, function(err) { - callback(err); - }).then(function(pkcs8) { - if(pkcs8) { - var privateKey = pki.privateKeyFromAsn1( - asn1.fromDer(forge.util.createBuffer(pkcs8))); + if(callback) { + // try native async + if(_detectNodeCrypto('generateKeyPair')) { + return _crypto.generateKeyPair('rsa', { + modulusLength: bits, + publicExponent: e, + publicKeyEncoding: { + type: 'spki', + format: 'pem' + }, + privateKeyEncoding: { + type: 'pkcs8', + format: 'pem' + } + }, function(err, pub, priv) { + if (err) { + return callback(err); + } callback(null, { - privateKey: privateKey, - publicKey: pki.setRsaPublicKey(privateKey.n, privateKey.e) - }); - } - }); - } - if(_detectSubtleMsCrypto('generateKey') && - _detectSubtleMsCrypto('exportKey')) { - var genOp = util.globalScope.msCrypto.subtle.generateKey({ - name: 'RSASSA-PKCS1-v1_5', - modulusLength: bits, - publicExponent: _intToUint8Array(e), - hash: {name: 'SHA-256'} - }, true /* key can be exported*/, ['sign', 'verify']); - genOp.oncomplete = function(e) { - var pair = e.target.result; - var exportOp = util.globalScope.msCrypto.subtle.exportKey( - 'pkcs8', pair.privateKey); - exportOp.oncomplete = function(e) { - var pkcs8 = e.target.result; - var privateKey = pki.privateKeyFromAsn1( - asn1.fromDer(forge.util.createBuffer(pkcs8))); - callback(null, { - privateKey: privateKey, - publicKey: pki.setRsaPublicKey(privateKey.n, privateKey.e) + privateKey: pki.privateKeyFromPem(priv), + publicKey: pki.publicKeyFromPem(pub) }); + }); + } + if(_detectSubtleCrypto('generateKey') && _detectSubtleCrypto('exportKey')) { + // use standard native generateKey + return window.crypto.subtle.generateKey({ + name: 'RSASSA-PKCS1-v1_5', + modulusLength: bits, + publicExponent: _intToUint8Array(e), + hash: {name: 'SHA-256'} + }, true /* key can be exported*/, ['sign', 'verify']) + .then(function(pair) { + return window.crypto.subtle.exportKey('pkcs8', pair.privateKey); + // avoiding catch(function(err) {...}) to support IE <= 8 + }).then(undefined, function(err) { + callback(err); + }).then(function(pkcs8) { + if(pkcs8) { + var privateKey = pki.privateKeyFromAsn1( + asn1.fromDer(forge.util.createBuffer(pkcs8))); + callback(null, { + privateKey: privateKey, + publicKey: pki.setRsaPublicKey(privateKey.n, privateKey.e) + }); + } + }); + } + if(_detectSubtleMsCrypto('generateKey') && + _detectSubtleMsCrypto('exportKey')) { + var genOp = window.msCrypto.subtle.generateKey({ + name: 'RSASSA-PKCS1-v1_5', + modulusLength: bits, + publicExponent: _intToUint8Array(e), + hash: {name: 'SHA-256'} + }, true /* key can be exported*/, ['sign', 'verify']); + genOp.oncomplete = function(e) { + var pair = e.target.result; + var exportOp = window.msCrypto.subtle.exportKey( + 'pkcs8', pair.privateKey); + exportOp.oncomplete = function(e) { + var pkcs8 = e.target.result; + var privateKey = pki.privateKeyFromAsn1( + asn1.fromDer(forge.util.createBuffer(pkcs8))); + callback(null, { + privateKey: privateKey, + publicKey: pki.setRsaPublicKey(privateKey.n, privateKey.e) + }); + }; + exportOp.onerror = function(err) { + callback(err); + }; }; - exportOp.onerror = function(err) { + genOp.onerror = function(err) { callback(err); }; - }; - genOp.onerror = function(err) { - callback(err); - }; - return; + return; + } + } else { + // try native sync + if(_detectNodeCrypto('generateKeyPairSync')) { + var keypair = _crypto.generateKeyPairSync('rsa', { + modulusLength: bits, + publicExponent: e, + publicKeyEncoding: { + type: 'spki', + format: 'pem' + }, + privateKeyEncoding: { + type: 'pkcs8', + format: 'pem' + } + }); + return { + privateKey: pki.privateKeyFromPem(keypair.privateKey), + publicKey: pki.publicKeyFromPem(keypair.publicKey) + }; + } } } diff --git a/tests/unit/rsa.js b/tests/unit/rsa.js index 44f1bcb10..39e9b3e54 100644 --- a/tests/unit/rsa.js +++ b/tests/unit/rsa.js @@ -127,10 +127,33 @@ var UTIL = require('../../lib/util'); }); } - // Node versions >= 10.12.0 support native keyPair generation, - // which is non-deterministic - var isDeterministic = UTIL.isNodejs && - typeof require('crypto').generateKeyPair !== 'function'; + // check if keygen params use deterministic algorithm + // NOTE: needs to match implementation details + function isDeterministic(isPrng, isAsync, isPurejs) { + // always needs to have a prng + if(!isPrng) { + return false; + } + if(UTIL.isNodejs) { + // Node versions >= 10.12.0 support native keyPair generation, + // which is non-deterministic + if(isAsync && !isPurejs && + typeof require('crypto').generateKeyPair === 'function') { + return false; + }; + if(!isAsync && !isPurejs && + typeof require('crypto').generateKeyPairSync === 'function') { + return false; + } + } else { + // async browser code has race conditions with multiple workers + if(isAsync) { + return false; + } + } + // will run deterministic algorithm + return true; + } it('should generate 512 bit key pair (sync)', function() { _genSync(); @@ -172,13 +195,19 @@ var UTIL = require('../../lib/util'); }); }); - it('should generate the same 512 bit key pair (sync+sync)', function() { + it('should generate same 512 bit key pair (prng+sync,prng+sync)', + function() { var pair1 = _genSync({samePrng: true}); var pair2 = _genSync({samePrng: true}); _pairCmp(pair1, pair2); }); - it('should generate the same 512 bit key pair (sync+purejs)', function() { + it('should generate same 512 bit key pair (prng+sync,prng+sync+purejs)', + function() { + if(!isDeterministic(true,false,false) || + !isDeterministic(true,false,true)) { + this.skip(); + } var pair1 = _genSync({samePrng: true}); // save var purejs = FORGE.options.usePureJavaScript; @@ -190,38 +219,61 @@ var UTIL = require('../../lib/util'); _pairCmp(pair1, pair2); }); - it('should generate 512 bit key pairs (sync+async)', function(done) { + it('should generate same 512 bit key pair ' + + '(prng+sync+purejs,prng+sync+purejs)', function() { + if(!isDeterministic(true,false,true) || + !isDeterministic(true,false,true)) { + this.skip(); + } + // save + var purejs = FORGE.options.usePureJavaScript; + // test pure mode + FORGE.options.usePureJavaScript = true; + var pair1 = _genSync({samePrng: true}); + var pair2 = _genSync({samePrng: true}); + // restore + FORGE.options.usePureJavaScript = purejs; + _pairCmp(pair1, pair2); + }); + + it('should generate same 512 bit key pair (prng+sync,prng+async)', + function(done) { + if(!isDeterministic(true,false,false) || + !isDeterministic(true,true,false)) { + this.skip(); + } var pair1 = _genSync({samePrng: true}); _genAsync({samePrng: true}, function(pair2) { - // check if the same on supported deterministic platforms - if(isDeterministic) { - _pairCmp(pair1, pair2); - } + _pairCmp(pair1, pair2); done(); }); }); - it('should generate 512 bit key pairs (async+sync)', function(done) { + it('should generate same 512 bit key pair (prng+async,prng+sync)', + function(done) { + if(!isDeterministic(true,true,false) || + !isDeterministic(true,false,false)) { + this.skip(); + } _genAsync({samePrng: true}, function(pair1) { var pair2 = _genSync({samePrng: true}); - // check if the same on supported deterministic platforms - if(isDeterministic) { - _pairCmp(pair1, pair2); - } + _pairCmp(pair1, pair2); done(); }); }); - it('should generate 512 bit key pairs (async+async)', function(done) { + it('should generate same 512 bit key pair (prng+async,prng+async)', + function(done) { + if(!isDeterministic(true,true,false) || + !isDeterministic(true,true,false)) { + this.skip(); + } var pair1; var pair2; // finish when both complete function _done() { if(pair1 && pair2) { - // check if the same on supported deterministic platforms - if(isDeterministic) { - _pairCmp(pair1, pair2); - } + _pairCmp(pair1, pair2); done(); } } From 8343452307c8483aa1cc1955e533c3e599c0dff5 Mon Sep 17 00:00:00 2001 From: "David I. Lehn" Date: Thu, 31 Jan 2019 00:41:26 -0500 Subject: [PATCH 045/197] Switch tests from PhantomJS to headless Chrome. - PhantomJS is deprecated. Now using headless Chrome. --- CHANGELOG.md | 4 ++++ README.md | 6 +++--- karma.conf.js | 4 ++-- package.json | 1 - tests/unit/asn1.js | 11 ++--------- tests/unit/pkcs7.js | 11 ++--------- tests/unit/support.js | 7 ------- 7 files changed, 13 insertions(+), 31 deletions(-) delete mode 100644 tests/unit/support.js diff --git a/CHANGELOG.md b/CHANGELOG.md index 5067590eb..424178417 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,10 @@ Forge ChangeLog - Use JS fallback in `rsa.generateKeyPair` if `prng` option specified since this isn't supported by current native APIs. - Only run key generation comparison tests if keys will be deterministic. +- PhantomJS is deprecated, now using headless Chrome with Karma. +- **Note**: Using headless Chrome vs PhantomJS may cause newer JS features to + slip into releases without proper support for older runtimes and browsers. + Please report such issues and they will be addressed. ## 0.7.6 - 2018-08-14 diff --git a/README.md b/README.md index 0606722e3..187d06f80 100644 --- a/README.md +++ b/README.md @@ -219,10 +219,10 @@ Forge natively runs in a [Node.js][] environment: npm test -### Running automated tests with PhantomJS +### Running automated tests with headless Chrome Automated testing is done via [Karma][]. By default it will run the tests in a -headless manner with PhantomJS. +headless manner with Chrome. npm run test-karma @@ -239,7 +239,7 @@ By default [webpack][] is used. [Browserify][] can also be used. You can also specify one or more browsers to use. - npm run test-karma -- --browsers Chrome,Firefox,Safari,PhantomJS + npm run test-karma -- --browsers Chrome,Firefox,Safari,ChromeHeadless The reporter option and `BUNDLER` environment variable can also be used. diff --git a/karma.conf.js b/karma.conf.js index 175b50736..9912213b1 100644 --- a/karma.conf.js +++ b/karma.conf.js @@ -89,8 +89,8 @@ module.exports = function(config) { // start these browsers // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher - //browsers: ['PhantomJS', 'Chrome', 'Firefox', 'Safari'], - browsers: ['PhantomJS'], + //browsers: ['ChromeHeadless', 'Chrome', 'Firefox', 'Safari'], + browsers: ['ChromeHeadless'], customLaunchers: { IE9: { diff --git a/package.json b/package.json index 557ec4a35..628ecfbf9 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,6 @@ "karma-ie-launcher": "^1.0.0", "karma-mocha": "^1.3.0", "karma-mocha-reporter": "^2.2.5", - "karma-phantomjs-launcher": "^1.0.2", "karma-safari-launcher": "^1.0.0", "karma-sauce-launcher": "^1.2.0", "karma-sourcemap-loader": "^0.3.7", diff --git a/tests/unit/asn1.js b/tests/unit/asn1.js index a8be892bf..0d7349211 100644 --- a/tests/unit/asn1.js +++ b/tests/unit/asn1.js @@ -1,7 +1,6 @@ var ASSERT = require('assert'); var ASN1 = require('../../lib/asn1'); var UTIL = require('../../lib/util'); -var support = require('./support'); (function() { describe('asn1', function() { @@ -209,10 +208,6 @@ var support = require('./support'); })(); (function() { - // TODO: remove skipping PhantomJS tests when possible - // skip 2050 tests in PhantomJS - // likely due to https://bugs.webkit.org/show_bug.cgi?id=130123 - // can't parse dates between 2034-03-01 and 2100-02-28 var tests = [{ in: 'Jan 1 1949 00:00:00 GMT', out: '19490101000000Z' @@ -221,15 +216,13 @@ var support = require('./support'); out: '20000101000000Z' }, { in: 'Jan 1 2050 00:00:00 GMT', - out: '20500101000000Z', - phantomjsskip: true + out: '20500101000000Z' }, { in: 'Mar 1 2100 00:00:00 GMT', out: '21000301000000Z' }]; tests.forEach(function(test) { - var _it = (test.phantomjsskip && support.isPhantomJS) ? it.skip : it; - _it('should convert date "' + test.in + '" to generalized time', function() { + it('should convert date "' + test.in + '" to generalized time', function() { var d = ASN1.dateToGeneralizedTime(new Date(test.in)); ASSERT.equal(d, test.out); }); diff --git a/tests/unit/pkcs7.js b/tests/unit/pkcs7.js index 0a4beafc3..01efc3470 100644 --- a/tests/unit/pkcs7.js +++ b/tests/unit/pkcs7.js @@ -5,7 +5,6 @@ var PKI = require('../../lib/pki'); var AES = require('../../lib/aes'); var DES = require('../../lib/des'); var UTIL = require('../../lib/util'); -var support = require('./support'); (function() { var _pem = { @@ -702,13 +701,7 @@ var support = require('./support'); ASSERT.equal(pem, _pem.detachedSignature); }); - // FIXME: remove skipping PhantomJS tests when possible - // skip 2049/2050 tests in PhantomJS - // likely due to https://bugs.webkit.org/show_bug.cgi?id=130123 - // can't parse dates between 2034-03-01 and 2100-02-28 - var _it = support.isPhantomJS ? it.skip : it; - - _it('should create PKCS#7 SignedData with content-type, message-digest, ' + + it('should create PKCS#7 SignedData with content-type, message-digest, ' + 'and signing-time attributes using UTCTime (2049)', function() { // verify with: // openssl smime -verify -in p7.pem -signer certificate.pem \ @@ -737,7 +730,7 @@ var support = require('./support'); ASSERT.equal(pem, _pem.signedDataWithAttrs2049UTCTime); }); - _it('should create PKCS#7 SignedData with content-type, message-digest, ' + + it('should create PKCS#7 SignedData with content-type, message-digest, ' + 'and signing-time attributes using GeneralizedTime (2050)', function() { // verify with: // openssl smime -verify -in p7.pem -signer certificate.pem \ diff --git a/tests/unit/support.js b/tests/unit/support.js deleted file mode 100644 index 5b0a7eb8a..000000000 --- a/tests/unit/support.js +++ /dev/null @@ -1,7 +0,0 @@ -// test support - -// true if running in PhantomJS -exports.isPhantomJS = - (typeof navigator !== 'undefined' && navigator.userAgent) ? - navigator.userAgent.indexOf('PhantomJS') !== -1 : - false; From 05a15f6c90ec194a8151c06d157647e97786ada3 Mon Sep 17 00:00:00 2001 From: "David I. Lehn" Date: Thu, 31 Jan 2019 14:49:16 -0500 Subject: [PATCH 046/197] Fix Headless Chrome name references. --- CHANGELOG.md | 4 ++-- README.md | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 424178417..ee29fd75c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,8 +18,8 @@ Forge ChangeLog - Use JS fallback in `rsa.generateKeyPair` if `prng` option specified since this isn't supported by current native APIs. - Only run key generation comparison tests if keys will be deterministic. -- PhantomJS is deprecated, now using headless Chrome with Karma. -- **Note**: Using headless Chrome vs PhantomJS may cause newer JS features to +- PhantomJS is deprecated, now using Headless Chrome with Karma. +- **Note**: Using Headless Chrome vs PhantomJS may cause newer JS features to slip into releases without proper support for older runtimes and browsers. Please report such issues and they will be addressed. diff --git a/README.md b/README.md index 187d06f80..82e9dae39 100644 --- a/README.md +++ b/README.md @@ -219,10 +219,10 @@ Forge natively runs in a [Node.js][] environment: npm test -### Running automated tests with headless Chrome +### Running automated tests with Headless Chrome -Automated testing is done via [Karma][]. By default it will run the tests in a -headless manner with Chrome. +Automated testing is done via [Karma][]. By default it will run the tests with +Headless Chrome. npm run test-karma From bddd5572391e79b5809415df6157041e717f34e2 Mon Sep 17 00:00:00 2001 From: "David I. Lehn" Date: Thu, 31 Jan 2019 15:05:19 -0500 Subject: [PATCH 047/197] Use Node.js 10 for browser tests. --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 0726fee33..24b8a8454 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,9 +13,9 @@ script: # only run karma tests for one node version matrix: include: - - node_js: "6" + - node_js: "10" env: BUNDLER=webpack - - node_js: "6" + - node_js: "10" env: BUNDLER=browserify notifications: email: From 2c3c7fc923273b351c478d65e808cce7776e5fce Mon Sep 17 00:00:00 2001 From: "David I. Lehn" Date: Thu, 31 Jan 2019 15:17:33 -0500 Subject: [PATCH 048/197] Update dependency. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 628ecfbf9..1a0c5d719 100644 --- a/package.json +++ b/package.json @@ -37,7 +37,7 @@ "mocha": "^5.0.1", "mocha-lcov-reporter": "^1.2.0", "nodejs-websocket": "^1.7.1", - "nyc": "^11.5.0", + "nyc": "^13.1.0", "opts": "^1.2.2", "webpack": "^3.11.0", "worker-loader": "^2.0.0" From a79ef7c87d08fe94b1afe9b2a4ced2b837b88a9d Mon Sep 17 00:00:00 2001 From: "David I. Lehn" Date: Thu, 31 Jan 2019 16:05:46 -0500 Subject: [PATCH 049/197] Update karma dependencies. --- package.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 1a0c5d719..31f758ccc 100644 --- a/package.json +++ b/package.json @@ -21,8 +21,8 @@ "express": "^4.16.2", "jscs": "^3.0.7", "jshint": "^2.9.5", - "karma": "^2.0.0", - "karma-browserify": "^5.2.0", + "karma": "^3.1.4", + "karma-browserify": "^6.0.0", "karma-chrome-launcher": "^2.2.0", "karma-edge-launcher": "^0.4.2", "karma-firefox-launcher": "^1.1.0", @@ -33,7 +33,7 @@ "karma-sauce-launcher": "^1.2.0", "karma-sourcemap-loader": "^0.3.7", "karma-tap-reporter": "0.0.6", - "karma-webpack": "^2.0.13", + "karma-webpack": "^3.0.5", "mocha": "^5.0.1", "mocha-lcov-reporter": "^1.2.0", "nodejs-websocket": "^1.7.1", From 17cf7e83c19ced0cdcaca707a39cddfe622bb072 Mon Sep 17 00:00:00 2001 From: "David I. Lehn" Date: Thu, 31 Jan 2019 18:36:30 -0500 Subject: [PATCH 050/197] Fix globalScope usage. - Changes missing due to bad merge. --- lib/rsa.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/rsa.js b/lib/rsa.js index 03343f4ba..402e09f84 100644 --- a/lib/rsa.js +++ b/lib/rsa.js @@ -918,14 +918,15 @@ pki.rsa.generateKeyPair = function(bits, e, options, callback) { } if(_detectSubtleCrypto('generateKey') && _detectSubtleCrypto('exportKey')) { // use standard native generateKey - return window.crypto.subtle.generateKey({ + return util.globalScope.crypto.subtle.generateKey({ name: 'RSASSA-PKCS1-v1_5', modulusLength: bits, publicExponent: _intToUint8Array(e), hash: {name: 'SHA-256'} }, true /* key can be exported*/, ['sign', 'verify']) .then(function(pair) { - return window.crypto.subtle.exportKey('pkcs8', pair.privateKey); + return util.globalScope.crypto.subtle.exportKey( + 'pkcs8', pair.privateKey); // avoiding catch(function(err) {...}) to support IE <= 8 }).then(undefined, function(err) { callback(err); @@ -942,7 +943,7 @@ pki.rsa.generateKeyPair = function(bits, e, options, callback) { } if(_detectSubtleMsCrypto('generateKey') && _detectSubtleMsCrypto('exportKey')) { - var genOp = window.msCrypto.subtle.generateKey({ + var genOp = util.globalScope.msCrypto.subtle.generateKey({ name: 'RSASSA-PKCS1-v1_5', modulusLength: bits, publicExponent: _intToUint8Array(e), @@ -950,7 +951,7 @@ pki.rsa.generateKeyPair = function(bits, e, options, callback) { }, true /* key can be exported*/, ['sign', 'verify']); genOp.oncomplete = function(e) { var pair = e.target.result; - var exportOp = window.msCrypto.subtle.exportKey( + var exportOp = util.globalScope.msCrypto.subtle.exportKey( 'pkcs8', pair.privateKey); exportOp.oncomplete = function(e) { var pkcs8 = e.target.result; From b241077218f7ef7f62e61bd95195e2fd9cc20f43 Mon Sep 17 00:00:00 2001 From: "David I. Lehn" Date: Thu, 31 Jan 2019 18:41:58 -0500 Subject: [PATCH 051/197] Improve web worker debug features. --- tests/karma/testWorker.js | 49 ++++++++++++++++++++++++++++------- tests/karma/web-worker-rsa.js | 49 ++++++++++++++++++++++++++++------- 2 files changed, 78 insertions(+), 20 deletions(-) diff --git a/tests/karma/testWorker.js b/tests/karma/testWorker.js index 3f682e1b7..b7eb62f21 100644 --- a/tests/karma/testWorker.js +++ b/tests/karma/testWorker.js @@ -1,36 +1,65 @@ var RSA = require('../../lib/rsa'); var PKI = require('../../lib/pki'); +// log on main thread +function _log(message) { + self.postMessage({ + type: 'log', + message: message + }); +} + // The following code allows main thread scripts to make the worker call // certain forge APIs within the worker (primarily to ensure compatibility) // We define a light-weight protocol to simplify testing -self.addEventListener('message', function (event) { - +// @param event +// event.data {Object} message content +// event.data.type {String} message type +// event.data.* {Any} type specific data +self.addEventListener('message', function(event) { // Test scripts call worker.postMessage(data) // we can access the payload via event.data - // data.method defines what the worker should call (we skip sophisticated - // reflection-magic, argument passing, etc. for now) - switch (event.data.method) { + //_log('message type: ' + event.data.type); + + // data.type defines what the worker should call + switch(event.data.type) { + case 'ping': + self.postMessage({ + type: 'pong' + }); + break; case 'rsa.generateKeyPair': - RSA.generateKeyPair({ bits: 512, workers: -1 }, function(error, keyPair) { + //_log('keygen start'); + //RSA.generateKeyPair({bits: 512, workers: -1}, function(error, keyPair) { + RSA.generateKeyPair({bits: 512, workers: 1}, function(error, keyPair) { + //_log('keygen done'); // We signal the outcome of the call via event.data with... - if (error) { + if(error) { // ...event.data.type === 'error' if the call failed self.postMessage({ type: 'error', - result: error.toString() + error: error.toString() }); } else { // ...event.data.type === 'success' if the call succeeded self.postMessage({ type: 'success', - result: { + keypair: { publicKey: PKI.publicKeyToPem(keyPair.publicKey), - privateKey: PKI.privateKeyToPem(keyPair.privateKey), + privateKey: PKI.privateKeyToPem(keyPair.privateKey) } }); } }); + break; + case 'stop': + self.close(); + break; + default: + self.postMessage({ + type: 'error', + error: 'Unknown message type: ' + event.data.type + }); } }); diff --git a/tests/karma/web-worker-rsa.js b/tests/karma/web-worker-rsa.js index 29ea1d200..492ee4dde 100644 --- a/tests/karma/web-worker-rsa.js +++ b/tests/karma/web-worker-rsa.js @@ -6,21 +6,50 @@ var ASSERT = require('assert'); var TestWorker = require('worker-loader!./testWorker'); var testWorker = new TestWorker(); -describe('web worker rsa', function() { +function _log(message) { + console.log('[main] ' + message); +} + +describe.only('web worker rsa', function() { it('should generate key pairs when running forge in a web worker', function(done) { // Make test worker call rsa.generateKeyPair() on its own side - testWorker.postMessage({ method: 'rsa.generateKeyPair' }); + //testWorker.postMessage({type: 'ping'}); + testWorker.postMessage({type: 'rsa.generateKeyPair'}); // Wait for a result (see testWorker.js for what event data to expect) - testWorker.addEventListener('message', function (event) { - ASSERT.equal(event.data.type, 'success'); - // This is primarily to ensure that the node-forge code runs successfully - // in the worker (e.g. no usages of `window` or similar). So, comparing - // for structural sanity of the result only should be fine - ASSERT.equal(typeof event.data.result.publicKey, 'string'); - ASSERT.equal(typeof event.data.result.privateKey, 'string'); + testWorker.addEventListener('message', function(event) { + //_log('message type: ' + event.data.type); + switch(event.data.type) { + case 'success': + // This is primarily to ensure that the node-forge code runs + // successfully in the worker (e.g. no usages of `window` or + // similar). So, comparing for structural sanity of the result only + // should be fine. + ASSERT.equal(typeof event.data.keypair, 'object'); + ASSERT.equal(typeof event.data.keypair.publicKey, 'string'); + ASSERT.equal(typeof event.data.keypair.privateKey, 'string'); + + // done with worker tests + testWorker.terminate(); - done(); + done(); + break; + case 'error': + ASSERT.fail('web worker error: ' + event.data.result); + break; + case 'log': + console.log('[worker] ' + event.data.message); + break; + case 'pong': + // for debugging worker is alive + //_log('pong'); + break; + case 'stop': + testWorker.terminate(); + break; + default: + console.log('UNKNOWN MESSAGE TYPE: ' + event.data.type); + } }); }); }); From 97ca29d4bc510348039ff56952a1ef4f78c09844 Mon Sep 17 00:00:00 2001 From: Alex Hoppen Date: Mon, 7 Jan 2019 17:07:58 +0100 Subject: [PATCH 052/197] Allow the specification of a custom date against which the validity period of a certificate chain should be verified --- lib/x509.js | 54 ++++++++++++----- tests/unit/x509.js | 147 ++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 183 insertions(+), 18 deletions(-) diff --git a/lib/x509.js b/lib/x509.js index 8102d6303..8cb1bc1c1 100644 --- a/lib/x509.js +++ b/lib/x509.js @@ -2940,7 +2940,13 @@ pki.certificateError = { * @param caStore a certificate store to verify against. * @param chain the certificate chain to verify, with the root or highest * authority at the end (an array of certificates). - * @param verify called for every certificate in the chain. + * @param options a callback to be called for every certificate in the chain or + * an object with: + * verify a callback to be called for every certificate in the + * chain + * validityCheckDate the date against which the certificate + * validity period should be checked. Pass null to not check + * the validity period. By default, the current date is used. * * The verify callback has the following signature: * @@ -2956,7 +2962,7 @@ pki.certificateError = { * * @return true if successful, error thrown if not. */ -pki.verifyCertificateChain = function(caStore, chain, verify) { +pki.verifyCertificateChain = function(caStore, chain, options) { /* From: RFC3280 - Internet X.509 Public Key Infrastructure Certificate Section 6: Certification Path Validation See inline parentheticals related to this particular implementation. @@ -3086,14 +3092,27 @@ pki.verifyCertificateChain = function(caStore, chain, verify) { CAs that may appear below a CA before only end-entity certificates may be issued. */ + // if a verify callback is passed as the third parameter, package it within + // the options object. This is to support a legacy function signature that + // expected the verify callback as the third parameter. + if(typeof options === 'function') { + options = {verify: options}; + } + options = options || {}; + // copy cert chain references to another array to protect against changes // in verify callback chain = chain.slice(0); var certs = chain.slice(0); - // get current date - var now = new Date(); - + var validityCheckDate = options.validityCheckDate; + // if no validityCheckDate is specified, default to the current date. Make + // sure to maintain the value null because it indicates that the validity + // period should not be checked. + if(typeof validityCheckDate === 'undefined') { + validityCheckDate = new Date(); + } + // verify each cert in the chain using its parent, where the parent // is either the next in the chain or from the CA store var first = true; @@ -3104,15 +3123,20 @@ pki.verifyCertificateChain = function(caStore, chain, verify) { var parent = null; var selfSigned = false; - // 1. check valid time - if(now < cert.validity.notBefore || now > cert.validity.notAfter) { - error = { - message: 'Certificate is not valid yet or has expired.', - error: pki.certificateError.certificate_expired, - notBefore: cert.validity.notBefore, - notAfter: cert.validity.notAfter, - now: now - }; + if(validityCheckDate) { + // 1. check valid time + if(validityCheckDate < cert.validity.notBefore || + validityCheckDate > cert.validity.notAfter) { + error = { + message: 'Certificate is not valid yet or has expired.', + error: pki.certificateError.certificate_expired, + notBefore: cert.validity.notBefore, + notAfter: cert.validity.notAfter, + // TODO: we might want to reconsider renaming 'now' to + // 'validityCheckDate' should this API be changed in the future. + now: validityCheckDate + }; + } } // 2. verify with parent from chain or CA store @@ -3259,7 +3283,7 @@ pki.verifyCertificateChain = function(caStore, chain, verify) { // call application callback var vfd = (error === null) ? true : error.error; - var ret = verify ? verify(vfd, depth, certs) : vfd; + var ret = options.verify ? options.verify(vfd, depth, certs) : vfd; if(ret === true) { // clear any set error error = null; diff --git a/tests/unit/x509.js b/tests/unit/x509.js index 613ca24fc..3e2585c07 100644 --- a/tests/unit/x509.js +++ b/tests/unit/x509.js @@ -802,6 +802,143 @@ var UTIL = require('../../lib/util'); }); }); + it('should verify based on a custom specified validity date', function() { + var keys = { + privateKey: PKI.privateKeyFromPem(_pem.privateKey), + publicKey: PKI.publicKeyFromPem(_pem.publicKey) + }; + + var attrs = [{ + name: 'commonName', + value: 'example.org' + }, { + name: 'countryName', + value: 'US' + }, { + shortName: 'ST', + value: 'Virginia' + }, { + name: 'localityName', + value: 'Blacksburg' + }, { + name: 'organizationName', + value: 'Test' + }, { + shortName: 'OU', + value: 'Test' + }]; + + var cert = createCertificate({ + publicKey: keys.publicKey, + signingKey: keys.privateKey, + extensions: [{ + name: 'authorityKeyIdentifier', + keyIdentifier: true, + authorityCertIssuer: true, + serialNumber: true + }], + serialNumber: '01', + subject: attrs, + issuer: attrs, + isCA: true + }); + var caStore = PKI.createCaStore(); + caStore.addCertificate(cert); + + var verifyDate = new Date(); + PKI.verifyCertificateChain(caStore, [cert], { + validityCheckDate: verifyDate, + verify: function(vfd, depth, chain) { + ASSERT.equal(vfd, true); + return true; + } + }); + + verifyDate = new Date(); + verifyDate.setFullYear(verifyDate.getFullYear() + 2); + PKI.verifyCertificateChain(caStore, [cert], { + validityCheckDate: verifyDate, + verify: function(vfd, depth, chain) { + ASSERT.equal(vfd, "forge.pki.CertificateExpired"); + return true; + } + }); + + verifyDate = new Date(); + verifyDate.setFullYear(verifyDate.getFullYear() - 1); + PKI.verifyCertificateChain(caStore, [cert], { + validityCheckDate: verifyDate, + verify: function(vfd, depth, chain) { + ASSERT.equal(vfd, "forge.pki.CertificateExpired"); + return true; + } + }); + }); + + it('should not verify the validity period if null is passed as validityCheckDate', function() { + var keys = { + privateKey: PKI.privateKeyFromPem(_pem.privateKey), + publicKey: PKI.publicKeyFromPem(_pem.publicKey) + }; + + var attrs = [{ + name: 'commonName', + value: 'example.org' + }, { + name: 'countryName', + value: 'US' + }, { + shortName: 'ST', + value: 'Virginia' + }, { + name: 'localityName', + value: 'Blacksburg' + }, { + name: 'organizationName', + value: 'Test' + }, { + shortName: 'OU', + value: 'Test' + }]; + + var pastDate = new Date(); + pastDate.setFullYear(pastDate.getFullYear() - 1); + + var cert = createCertificate({ + publicKey: keys.publicKey, + signingKey: keys.privateKey, + extensions: [{ + name: 'authorityKeyIdentifier', + keyIdentifier: true, + authorityCertIssuer: true, + serialNumber: true + }], + serialNumber: '01', + subject: attrs, + issuer: attrs, + isCA: true, + notBefore: pastDate, + notAfter: pastDate + }); + var caStore = PKI.createCaStore(); + caStore.addCertificate(cert); + + PKI.verifyCertificateChain(caStore, [cert], { + verify: function(vfd, depth, chain) { + ASSERT.equal(vfd, "forge.pki.CertificateExpired"); + return true; + } + }); + + PKI.verifyCertificateChain(caStore, [cert], { + validityCheckDate: null, + verify: function(vfd, depth, chain) { + ASSERT.equal(vfd, true); + return true; + } + }); + }); + it('should verify certificate with sha1WithRSAEncryption signature', function() { var certPem = '-----BEGIN CERTIFICATE-----\r\n' + 'MIIDZDCCAs2gAwIBAgIKQ8fjjgAAAABh3jANBgkqhkiG9w0BAQUFADBGMQswCQYD\r\n' + @@ -1370,14 +1507,18 @@ var UTIL = require('../../lib/util'); var isCA = options.isCA; var serialNumber = options.serialNumber || '01'; var notBefore = options.notBefore || new Date(); + if(options.notAfter) { + var notAfter = options.notAfter; + } else { + var notAfter = new Date(notBefore); + notAfter.setFullYear(notAfter.getFullYear() + 1); + } var cert = PKI.createCertificate(); cert.publicKey = publicKey; cert.serialNumber = serialNumber; cert.validity.notBefore = notBefore; - cert.validity.notAfter = new Date(cert.validity.notBefore); - cert.validity.notAfter.setFullYear( - cert.validity.notBefore.getFullYear() + 1); + cert.validity.notAfter = notAfter; cert.setSubject(subject); cert.setIssuer(issuer); var extensions = options.extensions || []; From 9a6ae3d50a33c353e1ada11c2c6cb51bf1db9573 Mon Sep 17 00:00:00 2001 From: Alex Hoppen Date: Mon, 28 Jan 2019 11:11:27 +0100 Subject: [PATCH 053/197] Allow specification of certificate chain validation options on tls --- lib/tls.js | 73 ++++++++++++++++++++++++++++++++---------------------- 1 file changed, 43 insertions(+), 30 deletions(-) diff --git a/lib/tls.js b/lib/tls.js index e953fb89b..0b3b687cf 100644 --- a/lib/tls.js +++ b/lib/tls.js @@ -3528,40 +3528,48 @@ var _alertDescToCertError = function(desc) { */ tls.verifyCertificateChain = function(c, chain) { try { - // verify chain - forge.pki.verifyCertificateChain(c.caStore, chain, - function verify(vfd, depth, chain) { - // convert pki.certificateError to tls alert description - var desc = _certErrorToAlertDesc(vfd); - - // call application callback - var ret = c.verify(c, vfd, depth, chain); - if(ret !== true) { - if(typeof ret === 'object' && !forge.util.isArray(ret)) { - // throw custom error - var error = new Error('The application rejected the certificate.'); - error.send = true; - error.alert = { - level: tls.Alert.Level.fatal, - description: tls.Alert.Description.bad_certificate - }; - if(ret.message) { - error.message = ret.message; - } - if(ret.alert) { - error.alert.description = ret.alert; - } - throw error; - } + // Make a copy of c.verifyOptions so that we can modify options.verify + // without modifying c.verifyOptions. + var options = {}; + for (var key in c.verifyOptions) { + options[key] = c.verifyOptions[key]; + } + + options.verify = function(vfd, depth, chain) { + // convert pki.certificateError to tls alert description + var desc = _certErrorToAlertDesc(vfd); - // convert tls alert description to pki.certificateError - if(ret !== vfd) { - ret = _alertDescToCertError(ret); + // call application callback + var ret = c.verify(c, vfd, depth, chain); + if(ret !== true) { + if(typeof ret === 'object' && !forge.util.isArray(ret)) { + // throw custom error + var error = new Error('The application rejected the certificate.'); + error.send = true; + error.alert = { + level: tls.Alert.Level.fatal, + description: tls.Alert.Description.bad_certificate + }; + if(ret.message) { + error.message = ret.message; } + if(ret.alert) { + error.alert.description = ret.alert; + } + throw error; } - return ret; - }); + // convert tls alert description to pki.certificateError + if(ret !== vfd) { + ret = _alertDescToCertError(ret); + } + } + + return ret; + }; + + // verify chain + forge.pki.verifyCertificateChain(c.caStore, chain, options); } catch(ex) { // build tls error if not already customized var err = ex; @@ -3718,6 +3726,7 @@ tls.createConnection = function(options) { virtualHost: options.virtualHost || null, verifyClient: options.verifyClient || false, verify: options.verify || function(cn, vfd, dpth, cts) {return vfd;}, + verifyOptions: options.verifyOptions || {}, getCertificate: options.getCertificate || null, getPrivateKey: options.getPrivateKey || null, getSignature: options.getSignature || null, @@ -4247,6 +4256,10 @@ forge.tls.createSessionCache = tls.createSessionCache; * verifyClient: true to require a client certificate in server mode, * 'optional' to request one, false not to (default: false). * verify: a handler used to custom verify certificates in the chain. + * verifyOptions: an object with options for the certificate chain validation. + * See documentation of pki.verifyCertificateChain for possible options. + * verifyOptions.verify is ignored. If you wish to specify a verify handler + * use the verify key. * getCertificate: an optional callback used to get a certificate or * a chain of certificates (as an array). * getPrivateKey: an optional callback used to get a private key. From 3c05f0e15694cfc283c3dde4fa34d650543a6a0d Mon Sep 17 00:00:00 2001 From: "David I. Lehn" Date: Thu, 31 Jan 2019 19:51:07 -0500 Subject: [PATCH 054/197] Update changelog. --- CHANGELOG.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ee29fd75c..e1aca68ac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,14 @@ Forge ChangeLog - Add OID 2.5.4.13 "description". - Add OID 2.16.840.1.113730.1.13 "nsComment". - Also handled extension when creating a certificate. +- `pki.verifyCertificateChain`: + - Add `validityCheckDate` option to allow checking the certificate validity + period against an arbitrary `Date` or `null` for no check at all. The + current date is used by default. +- `tls.createConnection`: + - Add `verifyOptions` option that passes through to + `pki.verifyCertificateChain`. Can be used for the above `validityCheckDate` + option. ### Changed - Support WebCrypto API in web workers. @@ -22,6 +30,10 @@ Forge ChangeLog - **Note**: Using Headless Chrome vs PhantomJS may cause newer JS features to slip into releases without proper support for older runtimes and browsers. Please report such issues and they will be addressed. +- `pki.verifyCertificateChain`: + - Signature changed to `(caStore, chain, options)`. Older `(caStore, chain, + verify)` signature is still supported. New style is to to pass in a + `verify` option. ## 0.7.6 - 2018-08-14 From 9c76197841f4929569f76adb6d5246dee5cbff88 Mon Sep 17 00:00:00 2001 From: "David I. Lehn" Date: Thu, 31 Jan 2019 19:53:51 -0500 Subject: [PATCH 055/197] Update changelog. --- CHANGELOG.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e1aca68ac..e7fd833c3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,13 +2,13 @@ Forge ChangeLog =============== ### Fixed -- Handle creation of certificates with notBefore and notAfter dates less than - Jan 1, 1950 or greater than or equal to Jan 1, 2050. +- Handle creation of certificates with `notBefore` and `notAfter` dates less + than Jan 1, 1950 or greater than or equal to Jan 1, 2050. ### Added - Add OID 2.5.4.13 "description". - Add OID 2.16.840.1.113730.1.13 "nsComment". - - Also handled extension when creating a certificate. + - Also handle extension when creating a certificate. - `pki.verifyCertificateChain`: - Add `validityCheckDate` option to allow checking the certificate validity period against an arbitrary `Date` or `null` for no check at all. The From 23f86f309623a2005116383c83a9f1a86d05007b Mon Sep 17 00:00:00 2001 From: "David I. Lehn" Date: Thu, 31 Jan 2019 19:56:58 -0500 Subject: [PATCH 056/197] Use https URLs. --- README.md | 56 +++++++++++++++++++++++++++---------------------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/README.md b/README.md index 82e9dae39..1408b4864 100644 --- a/README.md +++ b/README.md @@ -2037,8 +2037,8 @@ When using this code please keep the following in mind: Library Background ------------------ -* http://digitalbazaar.com/2010/07/20/javascript-tls-1/ -* http://digitalbazaar.com/2010/07/20/javascript-tls-2/ +* https://digitalbazaar.com/2010/07/20/javascript-tls-1/ +* https://digitalbazaar.com/2010/07/20/javascript-tls-2/ Contact ------- @@ -2058,39 +2058,39 @@ Financial support is welcome and helps contribute to futher development: [#forgejs]: https://webchat.freenode.net/?channels=#forgejs [0.6.x]: https://github.com/digitalbazaar/forge/tree/0.6.x -[3DES]: http://en.wikipedia.org/wiki/Triple_DES -[AES]: http://en.wikipedia.org/wiki/Advanced_Encryption_Standard -[ASN.1]: http://en.wikipedia.org/wiki/ASN.1 +[3DES]: https://en.wikipedia.org/wiki/Triple_DES +[AES]: https://en.wikipedia.org/wiki/Advanced_Encryption_Standard +[ASN.1]: https://en.wikipedia.org/wiki/ASN.1 [Bower]: https://bower.io/ [Browserify]: http://browserify.org/ -[CBC]: http://en.wikipedia.org/wiki/Block_cipher_mode_of_operation -[CFB]: http://en.wikipedia.org/wiki/Block_cipher_mode_of_operation -[CTR]: http://en.wikipedia.org/wiki/Block_cipher_mode_of_operation +[CBC]: https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation +[CFB]: https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation +[CTR]: https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation [CommonJS]: https://en.wikipedia.org/wiki/CommonJS -[DES]: http://en.wikipedia.org/wiki/Data_Encryption_Standard -[ECB]: http://en.wikipedia.org/wiki/Block_cipher_mode_of_operation -[Fortuna]: http://en.wikipedia.org/wiki/Fortuna_(PRNG) -[GCM]: http://en.wikipedia.org/wiki/GCM_mode -[HMAC]: http://en.wikipedia.org/wiki/HMAC -[JavaScript]: http://en.wikipedia.org/wiki/JavaScript +[DES]: https://en.wikipedia.org/wiki/Data_Encryption_Standard +[ECB]: https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation +[Fortuna]: https://en.wikipedia.org/wiki/Fortuna_(PRNG) +[GCM]: https://en.wikipedia.org/wiki/GCM_mode +[HMAC]: https://en.wikipedia.org/wiki/HMAC +[JavaScript]: https://en.wikipedia.org/wiki/JavaScript [Karma]: https://karma-runner.github.io/ -[MD5]: http://en.wikipedia.org/wiki/MD5 -[Node.js]: http://nodejs.org/ -[OFB]: http://en.wikipedia.org/wiki/Block_cipher_mode_of_operation -[PKCS#10]: http://en.wikipedia.org/wiki/Certificate_signing_request -[PKCS#12]: http://en.wikipedia.org/wiki/PKCS_%E2%99%AF12 -[PKCS#5]: http://en.wikipedia.org/wiki/PKCS -[PKCS#7]: http://en.wikipedia.org/wiki/Cryptographic_Message_Syntax +[MD5]: https://en.wikipedia.org/wiki/MD5 +[Node.js]: https://nodejs.org/ +[OFB]: https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation +[PKCS#10]: https://en.wikipedia.org/wiki/Certificate_signing_request +[PKCS#12]: https://en.wikipedia.org/wiki/PKCS_%E2%99%AF12 +[PKCS#5]: https://en.wikipedia.org/wiki/PKCS +[PKCS#7]: https://en.wikipedia.org/wiki/Cryptographic_Message_Syntax [PayPal]: https://www.paypal.com/ -[RC2]: http://en.wikipedia.org/wiki/RC2 -[SHA-1]: http://en.wikipedia.org/wiki/SHA-1 -[SHA-256]: http://en.wikipedia.org/wiki/SHA-256 -[SHA-384]: http://en.wikipedia.org/wiki/SHA-384 -[SHA-512]: http://en.wikipedia.org/wiki/SHA-512 +[RC2]: https://en.wikipedia.org/wiki/RC2 +[SHA-1]: https://en.wikipedia.org/wiki/SHA-1 +[SHA-256]: https://en.wikipedia.org/wiki/SHA-256 +[SHA-384]: https://en.wikipedia.org/wiki/SHA-384 +[SHA-512]: https://en.wikipedia.org/wiki/SHA-512 [Subresource Integrity]: https://www.w3.org/TR/SRI/ -[TLS]: http://en.wikipedia.org/wiki/Transport_Layer_Security +[TLS]: https://en.wikipedia.org/wiki/Transport_Layer_Security [UMD]: https://github.com/umdjs/umd -[X.509]: http://en.wikipedia.org/wiki/X.509 +[X.509]: https://en.wikipedia.org/wiki/X.509 [freenode]: https://freenode.net/ [unpkg]: https://unpkg.com/ [webpack]: https://webpack.github.io/ From 06de7f43c9befb3ae8d43a34bcf4e049f1deb23c Mon Sep 17 00:00:00 2001 From: "David I. Lehn" Date: Thu, 31 Jan 2019 22:45:17 -0500 Subject: [PATCH 057/197] Release 0.8.0. --- CHANGELOG.md | 2 ++ package.json | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e7fd833c3..1fcc96636 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,8 @@ Forge ChangeLog =============== +## 0.8.0 - 2019-01-31 + ### Fixed - Handle creation of certificates with `notBefore` and `notAfter` dates less than Jan 1, 1950 or greater than or equal to Jan 1, 2050. diff --git a/package.json b/package.json index 31f758ccc..5a923fe0a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "node-forge", - "version": "0.7.7-dev", + "version": "0.8.0", "description": "JavaScript implementations of network transports, cryptography, ciphers, PKI, message digests, and various utilities.", "homepage": "https://github.com/digitalbazaar/forge", "author": { From c3e7f9812002bc5bc4e61fecf4afd10f0f1de24e Mon Sep 17 00:00:00 2001 From: "David I. Lehn" Date: Thu, 31 Jan 2019 22:46:05 -0500 Subject: [PATCH 058/197] Start 0.8.1. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 5a923fe0a..bd0821de0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "node-forge", - "version": "0.8.0", + "version": "0.8.1-dev", "description": "JavaScript implementations of network transports, cryptography, ciphers, PKI, message digests, and various utilities.", "homepage": "https://github.com/digitalbazaar/forge", "author": { From cee40c9e02f515e6ae9425f0b83724a6b823a99c Mon Sep 17 00:00:00 2001 From: Dave Longley Date: Fri, 22 Feb 2019 22:31:24 -0500 Subject: [PATCH 059/197] Fix kem off-by-1 bug w/random generation. --- CHANGELOG.md | 5 +++++ lib/kem.js | 4 ++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1fcc96636..5b820eb62 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,11 @@ Forge ChangeLog =============== +## 0.8.1 - 2019-02-xx + +### Fixed +- Fix off-by-1 bug with kem random generation. + ## 0.8.0 - 2019-01-31 ### Fixed diff --git a/lib/kem.js b/lib/kem.js index f2d8d7805..1967016df 100644 --- a/lib/kem.js +++ b/lib/kem.js @@ -53,14 +53,14 @@ forge.kem.rsa.create = function(kdf, options) { * key: the secret key to use for encrypting a message. */ kem.encrypt = function(publicKey, keyLength) { - // generate a random r where 1 > r > n + // generate a random r where 1 < r < n var byteLength = Math.ceil(publicKey.n.bitLength() / 8); var r; do { r = new BigInteger( forge.util.bytesToHex(prng.getBytesSync(byteLength)), 16).mod(publicKey.n); - } while(r.equals(BigInteger.ZERO)); + } while(r.compareTo(BigInteger.ONE) <= 0); // prepend r with zeros r = forge.util.hexToBytes(r.toString(16)); From 1160fc4cc78e43630d42c8a68868cb518872089a Mon Sep 17 00:00:00 2001 From: "David I. Lehn" Date: Sat, 23 Feb 2019 16:41:13 -0500 Subject: [PATCH 060/197] Release 0.8.1. --- CHANGELOG.md | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5b820eb62..cebea2319 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,7 @@ Forge ChangeLog =============== -## 0.8.1 - 2019-02-xx +## 0.8.1 - 2019-02-23 ### Fixed - Fix off-by-1 bug with kem random generation. diff --git a/package.json b/package.json index bd0821de0..c1fdc4dbd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "node-forge", - "version": "0.8.1-dev", + "version": "0.8.1", "description": "JavaScript implementations of network transports, cryptography, ciphers, PKI, message digests, and various utilities.", "homepage": "https://github.com/digitalbazaar/forge", "author": { From 0fdccc2fef8b204ebe97c8c38c7f3ca41ae80dd5 Mon Sep 17 00:00:00 2001 From: "David I. Lehn" Date: Sat, 23 Feb 2019 16:41:41 -0500 Subject: [PATCH 061/197] Start 0.8.2. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index c1fdc4dbd..4640bb791 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "node-forge", - "version": "0.8.1", + "version": "0.8.2-dev", "description": "JavaScript implementations of network transports, cryptography, ciphers, PKI, message digests, and various utilities.", "homepage": "https://github.com/digitalbazaar/forge", "author": { From 72346b94cb4b8cddca7e0154fb41cccdbfecf07d Mon Sep 17 00:00:00 2001 From: "David I. Lehn" Date: Fri, 1 Feb 2019 19:38:20 -0500 Subject: [PATCH 062/197] Switch to eslint. - Remove jscs and jshint. - Use eslint-config-digitalbazaar. - Add overrides to handle current ~ES5 code. --- .eslintrc.js | 22 ++++++++++++++++++++++ CHANGELOG.md | 3 +++ package.json | 9 +++------ 3 files changed, 28 insertions(+), 6 deletions(-) create mode 100644 .eslintrc.js diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 000000000..a716eedf0 --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,22 @@ +module.exports = { + "env": { + "browser": true, + "commonjs": true, + "node": true + }, + "extends": ["eslint-config-digitalbazaar"], + "parserOptions": { + "ecmaVersion": 5 + }, + "rules": { + "quotes": [ + "error", + "single" + ], + // overrides to support ES5, remove when updated to ES20xx + "no-unused-vars": "warn", + "no-var": "off", + "object-shorthand": "off", + "prefer-const": "off", + } +}; diff --git a/CHANGELOG.md b/CHANGELOG.md index cebea2319..85dd4bde2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,9 @@ Forge ChangeLog =============== +### Changed +- Switch to eslint. + ## 0.8.1 - 2019-02-23 ### Fixed diff --git a/package.json b/package.json index 4640bb791..e27d57820 100644 --- a/package.json +++ b/package.json @@ -18,9 +18,9 @@ "browserify": "^16.1.0", "commander": "^2.14.1", "cross-env": "^5.1.3", + "eslint": "^5.12.1", + "eslint-config-digitalbazaar": "^1.1.0", "express": "^4.16.2", - "jscs": "^3.0.7", - "jshint": "^2.9.5", "karma": "^3.1.4", "karma-browserify": "^6.0.0", "karma-chrome-launcher": "^2.2.0", @@ -102,10 +102,7 @@ "test-server-webid": "node tests/websockets/server-webid.js", "coverage": "rm -rf coverage && nyc --reporter=lcov --reporter=text-summary npm test", "coverage-report": "nyc report", - "jscs": "jscs *.js lib/*.js tests/*.js tests/unit/*.js tests/legacy/*.js tests/issues/*.js tests/websockets/*.js", - "jshint": "jshint *.js lib/*.js tests/unit/*.js tests/legacy/*.js tests/issues/*.js tests/websockets/*.js", - "_jscs": "jscs *.js lib/*.js tests/*.js tests/unit/*.js tests/legacy/*.js tests/issues/*.js tests/websockets/*.js", - "_jshint": "jshint *.js lib/*.js tests/*.js tests/unit/*.js tests/legacy/*.js tests/issues/*.js tests/websockets/*.js" + "lint": "eslint *.js lib/*.js tests/*.js tests/unit/*.js tests/legacy/*.js tests/issues/*.js tests/websockets/*.js" }, "nyc": { "exclude": [ From 0627c5bc7f97dbba7293deed595ff9521bd0aa42 Mon Sep 17 00:00:00 2001 From: "David I. Lehn" Date: Fri, 1 Feb 2019 19:53:45 -0500 Subject: [PATCH 063/197] Fix a few linting issues. --- karma.conf.js | 13 +- lib/aesCipherSuites.js | 6 +- lib/des.js | 3 +- lib/pkcs1.js | 2 +- lib/rsa.js | 84 +++---- lib/sha1.js | 12 +- lib/sha512.js | 2 +- lib/x509.js | 496 +++++++++++++++++++++-------------------- webpack.config.js | 2 +- 9 files changed, 316 insertions(+), 304 deletions(-) diff --git a/karma.conf.js b/karma.conf.js index 9912213b1..996a5adf4 100644 --- a/karma.conf.js +++ b/karma.conf.js @@ -46,7 +46,8 @@ module.exports = function(config) { ], // preprocess matching files before serving them to the browser - // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor + // available preprocessors: + // https://npmjs.org/browse/keyword/karma-preprocessor preprocessors: { 'tests/unit/**.js': preprocessors, 'tests/karma/**.js': preprocessors, @@ -81,14 +82,18 @@ module.exports = function(config) { colors: true, // level of logging - // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG + // possible values: + // config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || + // config.LOG_INFO || config.LOG_DEBUG logLevel: config.LOG_INFO, - // enable / disable watching file and executing tests whenever any file changes + // enable / disable watching file and executing tests whenever any file + // changes autoWatch: false, // start these browsers - // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher + // available browser launchers: + // https://npmjs.org/browse/keyword/karma-launcher //browsers: ['ChromeHeadless', 'Chrome', 'Firefox', 'Safari'], browsers: ['ChromeHeadless'], diff --git a/lib/aesCipherSuites.js b/lib/aesCipherSuites.js index aeb91c1c3..fed60f369 100644 --- a/lib/aesCipherSuites.js +++ b/lib/aesCipherSuites.js @@ -16,7 +16,7 @@ var tls = module.exports = forge.tls; * Supported cipher suites. */ tls.CipherSuites['TLS_RSA_WITH_AES_128_CBC_SHA'] = { - id: [0x00,0x2f], + id: [0x00, 0x2f], name: 'TLS_RSA_WITH_AES_128_CBC_SHA', initSecurityParameters: function(sp) { sp.bulk_cipher_algorithm = tls.BulkCipherAlgorithm.aes; @@ -32,7 +32,7 @@ tls.CipherSuites['TLS_RSA_WITH_AES_128_CBC_SHA'] = { initConnectionState: initConnectionState }; tls.CipherSuites['TLS_RSA_WITH_AES_256_CBC_SHA'] = { - id: [0x00,0x35], + id: [0x00, 0x35], name: 'TLS_RSA_WITH_AES_256_CBC_SHA', initSecurityParameters: function(sp) { sp.bulk_cipher_algorithm = tls.BulkCipherAlgorithm.aes; @@ -199,10 +199,8 @@ function decrypt_aes_cbc_sha1_padding(blockSize, output, decrypt) { * * @return true on success, false on failure. */ -var count = 0; function decrypt_aes_cbc_sha1(record, s) { var rval = false; - ++count; var iv; if(record.version.minor === tls.Versions.TLS_1_0.minor) { diff --git a/lib/des.js b/lib/des.js index 6d2f5174f..ed8239aa9 100644 --- a/lib/des.js +++ b/lib/des.js @@ -7,7 +7,8 @@ * Paul Tero, July 2001 * http://www.tero.co.uk/des/ * - * Optimised for performance with large blocks by Michael Hayworth, November 2001 + * Optimised for performance with large blocks by + * Michael Hayworth, November 2001 * http://www.netdealing.com * * THIS SOFTWARE IS PROVIDED "AS IS" AND diff --git a/lib/pkcs1.js b/lib/pkcs1.js index 5fae4a07e..a3af9242e 100644 --- a/lib/pkcs1.js +++ b/lib/pkcs1.js @@ -119,7 +119,7 @@ pkcs1.encode_rsa_oaep = function(key, message, options) { var PS = ''; var PS_length = maxLength - message.length; - for (var i = 0; i < PS_length; i++) { + for(var i = 0; i < PS_length; i++) { PS += '\x00'; } diff --git a/lib/rsa.js b/lib/rsa.js index 402e09f84..7c67917ce 100644 --- a/lib/rsa.js +++ b/lib/rsa.js @@ -89,7 +89,6 @@ forge.pki = forge.pki || {}; module.exports = forge.pki.rsa = forge.rsa = forge.rsa || {}; var pki = forge.pki; - // for finding primes, which are 30k+i for i = 1, 7, 11, 13, 17, 19, 23, 29 var GCD_30_DELTA = [6, 4, 2, 4, 2, 4, 6, 2]; @@ -688,7 +687,7 @@ pki.rsa.stepKeyPairGenerationState = function(state, n) { var THIRTY = new BigInteger(null); THIRTY.fromInt(30); var deltaIdx = 0; - var op_or = function(x, y) { return x|y; }; + var op_or = function(x, y) {return x | y;}; // keep stepping until time limit is reached or done var t1 = +new Date(); @@ -737,7 +736,7 @@ pki.rsa.stepKeyPairGenerationState = function(state, n) { // ensure number is coprime with e state.pqState = (state.num.subtract(BigInteger.ONE).gcd(state.e) - .compareTo(BigInteger.ONE) === 0) ? 3 : 0; + .compareTo(BigInteger.ONE) === 0) ? 3 : 0; } else if(state.pqState === 3) { // store p or q state.pqState = 0; @@ -907,7 +906,7 @@ pki.rsa.generateKeyPair = function(bits, e, options, callback) { format: 'pem' } }, function(err, pub, priv) { - if (err) { + if(err) { return callback(err); } callback(null, { @@ -916,7 +915,8 @@ pki.rsa.generateKeyPair = function(bits, e, options, callback) { }); }); } - if(_detectSubtleCrypto('generateKey') && _detectSubtleCrypto('exportKey')) { + if(_detectSubtleCrypto('generateKey') && + _detectSubtleCrypto('exportKey')) { // use standard native generateKey return util.globalScope.crypto.subtle.generateKey({ name: 'RSASSA-PKCS1-v1_5', @@ -1054,7 +1054,7 @@ pki.setRsaPublicKey = pki.rsa.setPublicKey = function(n, e) { } }; } else if(['RAW', 'NONE', 'NULL', null].indexOf(scheme) !== -1) { - scheme = { encode: function(e) { return e; } }; + scheme = {encode: function(e) {return e;}}; } else if(typeof scheme === 'string') { throw new Error('Unsupported encryption scheme: "' + scheme + '".'); } @@ -1095,37 +1095,37 @@ pki.setRsaPublicKey = pki.rsa.setPublicKey = function(n, e) { * * @return true if the signature was verified, false if not. */ - key.verify = function(digest, signature, scheme) { - if(typeof scheme === 'string') { - scheme = scheme.toUpperCase(); - } else if(scheme === undefined) { - scheme = 'RSASSA-PKCS1-V1_5'; - } - - if(scheme === 'RSASSA-PKCS1-V1_5') { - scheme = { - verify: function(digest, d) { - // remove padding - d = _decodePkcs1_v1_5(d, key, true); - // d is ASN.1 BER-encoded DigestInfo - var obj = asn1.fromDer(d); - // compare the given digest to the decrypted one - return digest === obj.value[1].value; - } - }; - } else if(scheme === 'NONE' || scheme === 'NULL' || scheme === null) { - scheme = { - verify: function(digest, d) { - // remove padding - d = _decodePkcs1_v1_5(d, key, true); - return digest === d; - } - }; - } - - // do rsa decryption w/o any decoding, then verify -- which does decoding - var d = pki.rsa.decrypt(signature, key, true, false); - return scheme.verify(digest, d, key.n.bitLength()); + key.verify = function(digest, signature, scheme) { + if(typeof scheme === 'string') { + scheme = scheme.toUpperCase(); + } else if(scheme === undefined) { + scheme = 'RSASSA-PKCS1-V1_5'; + } + + if(scheme === 'RSASSA-PKCS1-V1_5') { + scheme = { + verify: function(digest, d) { + // remove padding + d = _decodePkcs1_v1_5(d, key, true); + // d is ASN.1 BER-encoded DigestInfo + var obj = asn1.fromDer(d); + // compare the given digest to the decrypted one + return digest === obj.value[1].value; + } + }; + } else if(scheme === 'NONE' || scheme === 'NULL' || scheme === null) { + scheme = { + verify: function(digest, d) { + // remove padding + d = _decodePkcs1_v1_5(d, key, true); + return digest === d; + } + }; + } + + // do rsa decryption w/o any decoding, then verify -- which does decoding + var d = pki.rsa.decrypt(signature, key, true, false); + return scheme.verify(digest, d, key.n.bitLength()); }; return key; @@ -1183,7 +1183,7 @@ pki.setRsaPrivateKey = pki.rsa.setPrivateKey = function( var d = pki.rsa.decrypt(data, key, false, false); if(scheme === 'RSAES-PKCS1-V1_5') { - scheme = { decode: _decodePkcs1_v1_5 }; + scheme = {decode: _decodePkcs1_v1_5}; } else if(scheme === 'RSA-OAEP' || scheme === 'RSAES-OAEP') { scheme = { decode: function(d, key) { @@ -1191,7 +1191,7 @@ pki.setRsaPrivateKey = pki.rsa.setPrivateKey = function( } }; } else if(['RAW', 'NONE', 'NULL', null].indexOf(scheme) !== -1) { - scheme = { decode: function(d) { return d; } }; + scheme = {decode: function(d) {return d;}}; } else { throw new Error('Unsupported encryption scheme: "' + scheme + '".'); } @@ -1233,10 +1233,10 @@ pki.setRsaPrivateKey = pki.rsa.setPrivateKey = function( } if(scheme === undefined || scheme === 'RSASSA-PKCS1-V1_5') { - scheme = { encode: emsaPkcs1v15encode }; + scheme = {encode: emsaPkcs1v15encode}; bt = 0x01; } else if(scheme === 'NONE' || scheme === 'NULL' || scheme === null) { - scheme = { encode: function() { return md; } }; + scheme = {encode: function() {return md;}}; bt = 0x01; } @@ -1271,7 +1271,7 @@ pki.wrapRsaPrivateKey = function(rsaKey) { // PrivateKey asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false, asn1.toDer(rsaKey).getBytes()) - ]); + ]); }; /** diff --git a/lib/sha1.js b/lib/sha1.js index 28776ee2f..5f84eb667 100644 --- a/lib/sha1.js +++ b/lib/sha1.js @@ -113,12 +113,12 @@ sha1.create = function() { return md; }; - /** - * Produces the digest. - * - * @return a byte buffer containing the digest value. - */ - md.digest = function() { + /** + * Produces the digest. + * + * @return a byte buffer containing the digest value. + */ + md.digest = function() { /* Note: Here we copy the remaining bytes in the input buffer and add the appropriate SHA-1 padding. Then we do the final update on a copy of the state so that if the user wants to get diff --git a/lib/sha512.js b/lib/sha512.js index 763a9c573..e09b442af 100644 --- a/lib/sha512.js +++ b/lib/sha512.js @@ -81,7 +81,7 @@ sha512.create = function(algorithm) { // determine digest length by algorithm name (default) var digestLength = 64; - switch (algorithm) { + switch(algorithm) { case 'SHA-384': digestLength = 48; break; diff --git a/lib/x509.js b/lib/x509.js index 8cb1bc1c1..3bfbc188c 100644 --- a/lib/x509.js +++ b/lib/x509.js @@ -251,8 +251,8 @@ var x509CertificateValidator = { constructed: true, captureAsn1: 'certSubject' }, - // SubjectPublicKeyInfo - publicKeyValidator, + // SubjectPublicKeyInfo + publicKeyValidator, { // issuerUniqueID (optional) name: 'Certificate.TBSCertificate.issuerUniqueID', @@ -464,32 +464,33 @@ var certificationRequestValidator = { captureAsn1: 'csr', value: [ certificationRequestInfoValidator, { - // AlgorithmIdentifier (signature algorithm) - name: 'CertificationRequest.signatureAlgorithm', - tagClass: asn1.Class.UNIVERSAL, - type: asn1.Type.SEQUENCE, - constructed: true, - value: [{ - // algorithm - name: 'CertificationRequest.signatureAlgorithm.algorithm', + // AlgorithmIdentifier (signature algorithm) + name: 'CertificationRequest.signatureAlgorithm', tagClass: asn1.Class.UNIVERSAL, - type: asn1.Type.OID, - constructed: false, - capture: 'csrSignatureOid' + type: asn1.Type.SEQUENCE, + constructed: true, + value: [{ + // algorithm + name: 'CertificationRequest.signatureAlgorithm.algorithm', + tagClass: asn1.Class.UNIVERSAL, + type: asn1.Type.OID, + constructed: false, + capture: 'csrSignatureOid' + }, { + name: 'CertificationRequest.signatureAlgorithm.parameters', + tagClass: asn1.Class.UNIVERSAL, + optional: true, + captureAsn1: 'csrSignatureParams' + }] }, { - name: 'CertificationRequest.signatureAlgorithm.parameters', + // signature + name: 'CertificationRequest.signature', tagClass: asn1.Class.UNIVERSAL, - optional: true, - captureAsn1: 'csrSignatureParams' - }] - }, { - // signature - name: 'CertificationRequest.signature', - tagClass: asn1.Class.UNIVERSAL, - type: asn1.Type.BITSTRING, - constructed: false, - captureBitStringValue: 'csrSignature' - }] + type: asn1.Type.BITSTRING, + constructed: false, + captureBitStringValue: 'csrSignature' + } + ] }; /** @@ -709,13 +710,15 @@ pki.certificateFromPem = function(pem, computeHash, strict) { if(msg.type !== 'CERTIFICATE' && msg.type !== 'X509 CERTIFICATE' && msg.type !== 'TRUSTED CERTIFICATE') { - var error = new Error('Could not convert certificate from PEM; PEM header type ' + + var error = new Error( + 'Could not convert certificate from PEM; PEM header type ' + 'is not "CERTIFICATE", "X509 CERTIFICATE", or "TRUSTED CERTIFICATE".'); error.headerType = msg.type; throw error; } if(msg.procType && msg.procType.type === 'ENCRYPTED') { - throw new Error('Could not convert certificate from PEM; PEM is encrypted.'); + throw new Error( + 'Could not convert certificate from PEM; PEM is encrypted.'); } // convert DER to ASN.1 object @@ -822,14 +825,14 @@ pki.getPublicKeyFingerprint = function(key, options) { var bytes; switch(type) { - case 'RSAPublicKey': - bytes = asn1.toDer(pki.publicKeyToRSAPublicKey(key)).getBytes(); - break; - case 'SubjectPublicKeyInfo': - bytes = asn1.toDer(pki.publicKeyToAsn1(key)).getBytes(); - break; - default: - throw new Error('Unknown fingerprint type "' + options.type + '".'); + case 'RSAPublicKey': + bytes = asn1.toDer(pki.publicKeyToRSAPublicKey(key)).getBytes(); + break; + case 'SubjectPublicKeyInfo': + bytes = asn1.toDer(pki.publicKeyToAsn1(key)).getBytes(); + break; + default: + throw new Error('Unknown fingerprint type "' + options.type + '".'); } // hash public key bytes @@ -1062,7 +1065,8 @@ pki.createCertificate = function() { if(!cert.issued(child)) { var issuer = child.issuer; var subject = cert.subject; - var error = new Error('The parent certificate did not issue the given child ' + + var error = new Error( + 'The parent certificate did not issue the given child ' + 'certificate; the child certificate\'s issuer does not match the ' + 'parent\'s subject.'); error.expectedIssuer = issuer.attributes; @@ -1076,24 +1080,24 @@ pki.createCertificate = function() { if(child.signatureOid in oids) { var oid = oids[child.signatureOid]; switch(oid) { - case 'sha1WithRSAEncryption': - md = forge.md.sha1.create(); - break; - case 'md5WithRSAEncryption': - md = forge.md.md5.create(); - break; - case 'sha256WithRSAEncryption': - md = forge.md.sha256.create(); - break; - case 'sha384WithRSAEncryption': - md = forge.md.sha384.create(); - break; - case 'sha512WithRSAEncryption': - md = forge.md.sha512.create(); - break; - case 'RSASSA-PSS': - md = forge.md.sha256.create(); - break; + case 'sha1WithRSAEncryption': + md = forge.md.sha1.create(); + break; + case 'md5WithRSAEncryption': + md = forge.md.md5.create(); + break; + case 'sha256WithRSAEncryption': + md = forge.md.sha256.create(); + break; + case 'sha384WithRSAEncryption': + md = forge.md.sha384.create(); + break; + case 'sha512WithRSAEncryption': + md = forge.md.sha512.create(); + break; + case 'RSASSA-PSS': + md = forge.md.sha256.create(); + break; } } if(md === null) { @@ -1113,44 +1117,44 @@ pki.createCertificate = function() { var scheme; switch(child.signatureOid) { - case oids.sha1WithRSAEncryption: - scheme = undefined; /* use PKCS#1 v1.5 padding scheme */ - break; - case oids['RSASSA-PSS']: - var hash, mgf; - - /* initialize mgf */ - hash = oids[child.signatureParameters.mgf.hash.algorithmOid]; - if(hash === undefined || forge.md[hash] === undefined) { - var error = new Error('Unsupported MGF hash function.'); - error.oid = child.signatureParameters.mgf.hash.algorithmOid; - error.name = hash; - throw error; - } + case oids.sha1WithRSAEncryption: + scheme = undefined; /* use PKCS#1 v1.5 padding scheme */ + break; + case oids['RSASSA-PSS']: + var hash, mgf; + + /* initialize mgf */ + hash = oids[child.signatureParameters.mgf.hash.algorithmOid]; + if(hash === undefined || forge.md[hash] === undefined) { + var error = new Error('Unsupported MGF hash function.'); + error.oid = child.signatureParameters.mgf.hash.algorithmOid; + error.name = hash; + throw error; + } - mgf = oids[child.signatureParameters.mgf.algorithmOid]; - if(mgf === undefined || forge.mgf[mgf] === undefined) { - var error = new Error('Unsupported MGF function.'); - error.oid = child.signatureParameters.mgf.algorithmOid; - error.name = mgf; - throw error; - } + mgf = oids[child.signatureParameters.mgf.algorithmOid]; + if(mgf === undefined || forge.mgf[mgf] === undefined) { + var error = new Error('Unsupported MGF function.'); + error.oid = child.signatureParameters.mgf.algorithmOid; + error.name = mgf; + throw error; + } - mgf = forge.mgf[mgf].create(forge.md[hash].create()); + mgf = forge.mgf[mgf].create(forge.md[hash].create()); - /* initialize hash function */ - hash = oids[child.signatureParameters.hash.algorithmOid]; - if(hash === undefined || forge.md[hash] === undefined) { - throw { - message: 'Unsupported RSASSA-PSS hash function.', - oid: child.signatureParameters.hash.algorithmOid, - name: hash - }; - } + /* initialize hash function */ + hash = oids[child.signatureParameters.hash.algorithmOid]; + if(hash === undefined || forge.md[hash] === undefined) { + throw { + message: 'Unsupported RSASSA-PSS hash function.', + oid: child.signatureParameters.hash.algorithmOid, + name: hash + }; + } - scheme = forge.pss.create(forge.md[hash].create(), mgf, - child.signatureParameters.saltLength); - break; + scheme = forge.pss.create(forge.md[hash].create(), mgf, + child.signatureParameters.saltLength); + break; } // verify signature on cert using public key @@ -1334,24 +1338,24 @@ pki.certificateFromAsn1 = function(obj, computeHash) { if(cert.signatureOid in oids) { var oid = oids[cert.signatureOid]; switch(oid) { - case 'sha1WithRSAEncryption': - cert.md = forge.md.sha1.create(); - break; - case 'md5WithRSAEncryption': - cert.md = forge.md.md5.create(); - break; - case 'sha256WithRSAEncryption': - cert.md = forge.md.sha256.create(); - break; - case 'sha384WithRSAEncryption': - cert.md = forge.md.sha384.create(); - break; - case 'sha512WithRSAEncryption': - cert.md = forge.md.sha512.create(); - break; - case 'RSASSA-PSS': - cert.md = forge.md.sha256.create(); - break; + case 'sha1WithRSAEncryption': + cert.md = forge.md.sha1.create(); + break; + case 'md5WithRSAEncryption': + cert.md = forge.md.md5.create(); + break; + case 'sha256WithRSAEncryption': + cert.md = forge.md.sha256.create(); + break; + case 'sha384WithRSAEncryption': + cert.md = forge.md.sha384.create(); + break; + case 'sha512WithRSAEncryption': + cert.md = forge.md.sha512.create(); + break; + case 'RSASSA-PSS': + cert.md = forge.md.sha256.create(); + break; } } if(cert.md === null) { @@ -1598,24 +1602,24 @@ pki.certificateExtensionFromAsn1 = function(ext) { // Note: Support for types 1,2,6,7,8 switch(gn.type) { - // rfc822Name - case 1: - // dNSName - case 2: - // uniformResourceIdentifier (URI) - case 6: - break; - // IPAddress - case 7: - // convert to IPv4/IPv6 string representation - altName.ip = forge.util.bytesToIP(gn.value); - break; - // registeredID - case 8: - altName.oid = asn1.derToOid(gn.value); - break; - default: - // unsupported + // rfc822Name + case 1: + // dNSName + case 2: + // uniformResourceIdentifier (URI) + case 6: + break; + // IPAddress + case 7: + // convert to IPv4/IPv6 string representation + altName.ip = forge.util.bytesToIP(gn.value); + break; + // registeredID + case 8: + altName.oid = asn1.derToOid(gn.value); + break; + default: + // unsupported } } } else if(e.name === 'subjectKeyIdentifier') { @@ -1678,24 +1682,24 @@ pki.certificationRequestFromAsn1 = function(obj, computeHash) { if(csr.signatureOid in oids) { var oid = oids[csr.signatureOid]; switch(oid) { - case 'sha1WithRSAEncryption': - csr.md = forge.md.sha1.create(); - break; - case 'md5WithRSAEncryption': - csr.md = forge.md.md5.create(); - break; - case 'sha256WithRSAEncryption': - csr.md = forge.md.sha256.create(); - break; - case 'sha384WithRSAEncryption': - csr.md = forge.md.sha384.create(); - break; - case 'sha512WithRSAEncryption': - csr.md = forge.md.sha512.create(); - break; - case 'RSASSA-PSS': - csr.md = forge.md.sha256.create(); - break; + case 'sha1WithRSAEncryption': + csr.md = forge.md.sha1.create(); + break; + case 'md5WithRSAEncryption': + csr.md = forge.md.md5.create(); + break; + case 'sha256WithRSAEncryption': + csr.md = forge.md.sha256.create(); + break; + case 'sha384WithRSAEncryption': + csr.md = forge.md.sha384.create(); + break; + case 'sha512WithRSAEncryption': + csr.md = forge.md.sha512.create(); + break; + case 'RSASSA-PSS': + csr.md = forge.md.sha256.create(); + break; } } if(csr.md === null) { @@ -1848,28 +1852,29 @@ pki.createCertificationRequest = function() { // TODO: create DRY `OID to md` function var oid = oids[csr.signatureOid]; switch(oid) { - case 'sha1WithRSAEncryption': - md = forge.md.sha1.create(); - break; - case 'md5WithRSAEncryption': - md = forge.md.md5.create(); - break; - case 'sha256WithRSAEncryption': - md = forge.md.sha256.create(); - break; - case 'sha384WithRSAEncryption': - md = forge.md.sha384.create(); - break; - case 'sha512WithRSAEncryption': - md = forge.md.sha512.create(); - break; - case 'RSASSA-PSS': - md = forge.md.sha256.create(); - break; + case 'sha1WithRSAEncryption': + md = forge.md.sha1.create(); + break; + case 'md5WithRSAEncryption': + md = forge.md.md5.create(); + break; + case 'sha256WithRSAEncryption': + md = forge.md.sha256.create(); + break; + case 'sha384WithRSAEncryption': + md = forge.md.sha384.create(); + break; + case 'sha512WithRSAEncryption': + md = forge.md.sha512.create(); + break; + case 'RSASSA-PSS': + md = forge.md.sha256.create(); + break; } } if(md === null) { - var error = new Error('Could not compute certification request digest. ' + + var error = new Error( + 'Could not compute certification request digest. ' + 'Unknown signature OID.'); error.signatureOid = csr.signatureOid; throw error; @@ -1886,43 +1891,43 @@ pki.createCertificationRequest = function() { var scheme; switch(csr.signatureOid) { - case oids.sha1WithRSAEncryption: - /* use PKCS#1 v1.5 padding scheme */ - break; - case oids['RSASSA-PSS']: - var hash, mgf; - - /* initialize mgf */ - hash = oids[csr.signatureParameters.mgf.hash.algorithmOid]; - if(hash === undefined || forge.md[hash] === undefined) { - var error = new Error('Unsupported MGF hash function.'); - error.oid = csr.signatureParameters.mgf.hash.algorithmOid; - error.name = hash; - throw error; - } + case oids.sha1WithRSAEncryption: + /* use PKCS#1 v1.5 padding scheme */ + break; + case oids['RSASSA-PSS']: + var hash, mgf; + + /* initialize mgf */ + hash = oids[csr.signatureParameters.mgf.hash.algorithmOid]; + if(hash === undefined || forge.md[hash] === undefined) { + var error = new Error('Unsupported MGF hash function.'); + error.oid = csr.signatureParameters.mgf.hash.algorithmOid; + error.name = hash; + throw error; + } - mgf = oids[csr.signatureParameters.mgf.algorithmOid]; - if(mgf === undefined || forge.mgf[mgf] === undefined) { - var error = new Error('Unsupported MGF function.'); - error.oid = csr.signatureParameters.mgf.algorithmOid; - error.name = mgf; - throw error; - } + mgf = oids[csr.signatureParameters.mgf.algorithmOid]; + if(mgf === undefined || forge.mgf[mgf] === undefined) { + var error = new Error('Unsupported MGF function.'); + error.oid = csr.signatureParameters.mgf.algorithmOid; + error.name = mgf; + throw error; + } - mgf = forge.mgf[mgf].create(forge.md[hash].create()); + mgf = forge.mgf[mgf].create(forge.md[hash].create()); - /* initialize hash function */ - hash = oids[csr.signatureParameters.hash.algorithmOid]; - if(hash === undefined || forge.md[hash] === undefined) { - var error = new Error('Unsupported RSASSA-PSS hash function.'); - error.oid = csr.signatureParameters.hash.algorithmOid; - error.name = hash; - throw error; - } + /* initialize hash function */ + hash = oids[csr.signatureParameters.hash.algorithmOid]; + if(hash === undefined || forge.md[hash] === undefined) { + var error = new Error('Unsupported RSASSA-PSS hash function.'); + error.oid = csr.signatureParameters.hash.algorithmOid; + error.name = hash; + throw error; + } - scheme = forge.pss.create(forge.md[hash].create(), mgf, - csr.signatureParameters.saltLength); - break; + scheme = forge.pss.create(forge.md[hash].create(), mgf, + csr.signatureParameters.saltLength); + break; } // verify signature on csr using its public key @@ -2317,15 +2322,17 @@ function _fillMissingExtensionFields(e, options) { seq.push( asn1.create(asn1.Class.CONTEXT_SPECIFIC, 2, false, serialNumber)); } - } else if (e.name === 'cRLDistributionPoints') { + } else if(e.name === 'cRLDistributionPoints') { e.value = asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, []); var seq = e.value.value; // Create sub SEQUENCE of DistributionPointName - var subSeq = asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, []); + var subSeq = asn1.create( + asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, []); // Create fullName CHOICE - var fullNameGeneralNames = asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, true, []); + var fullNameGeneralNames = asn1.create( + asn1.Class.CONTEXT_SPECIFIC, 0, true, []); var altName; for(var n = 0; n < e.altNames.length; ++n) { altName = e.altNames[n]; @@ -2354,7 +2361,8 @@ function _fillMissingExtensionFields(e, options) { } // Add to the parent SEQUENCE - subSeq.value.push(asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, true, [fullNameGeneralNames])); + subSeq.value.push(asn1.create( + asn1.Class.CONTEXT_SPECIFIC, 0, true, [fullNameGeneralNames])); seq.push(subSeq); } @@ -2377,44 +2385,44 @@ function _fillMissingExtensionFields(e, options) { */ function _signatureParametersToAsn1(oid, params) { switch(oid) { - case oids['RSASSA-PSS']: - var parts = []; - - if(params.hash.algorithmOid !== undefined) { - parts.push(asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, true, [ - asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [ - asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false, - asn1.oidToDer(params.hash.algorithmOid).getBytes()), - asn1.create(asn1.Class.UNIVERSAL, asn1.Type.NULL, false, '') - ]) - ])); - } + case oids['RSASSA-PSS']: + var parts = []; - if(params.mgf.algorithmOid !== undefined) { - parts.push(asn1.create(asn1.Class.CONTEXT_SPECIFIC, 1, true, [ - asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [ - asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false, - asn1.oidToDer(params.mgf.algorithmOid).getBytes()), + if(params.hash.algorithmOid !== undefined) { + parts.push(asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, true, [ asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [ asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false, - asn1.oidToDer(params.mgf.hash.algorithmOid).getBytes()), + asn1.oidToDer(params.hash.algorithmOid).getBytes()), asn1.create(asn1.Class.UNIVERSAL, asn1.Type.NULL, false, '') ]) - ]) - ])); - } + ])); + } - if(params.saltLength !== undefined) { - parts.push(asn1.create(asn1.Class.CONTEXT_SPECIFIC, 2, true, [ - asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false, - asn1.integerToDer(params.saltLength).getBytes()) - ])); - } + if(params.mgf.algorithmOid !== undefined) { + parts.push(asn1.create(asn1.Class.CONTEXT_SPECIFIC, 1, true, [ + asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [ + asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false, + asn1.oidToDer(params.mgf.algorithmOid).getBytes()), + asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [ + asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false, + asn1.oidToDer(params.mgf.hash.algorithmOid).getBytes()), + asn1.create(asn1.Class.UNIVERSAL, asn1.Type.NULL, false, '') + ]) + ]) + ])); + } - return asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, parts); + if(params.saltLength !== undefined) { + parts.push(asn1.create(asn1.Class.CONTEXT_SPECIFIC, 2, true, [ + asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false, + asn1.integerToDer(params.saltLength).getBytes()) + ])); + } + + return asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, parts); - default: - return asn1.create(asn1.Class.UNIVERSAL, asn1.Type.NULL, false, ''); + default: + return asn1.create(asn1.Class.UNIVERSAL, asn1.Type.NULL, false, ''); } } @@ -2485,7 +2493,7 @@ const jan_1_2050 = new Date('2050-01-01T00:00:00Z'); * * @return the ASN.1 object representing the date. */ - function _dateToAsn1(date){ +function _dateToAsn1(date) { if(date >= jan_1_1950 && date < jan_1_2050) { return asn1.create( asn1.Class.UNIVERSAL, asn1.Type.UTCTIME, false, @@ -2779,7 +2787,7 @@ pki.createCaStore = function(certs) { ensureSubjectHasHash(cert.subject); - if(!caStore.hasCertificate(cert)) { // avoid duplicate certificates in store + if(!caStore.hasCertificate(cert)) { // avoid duplicate certificates in store if(cert.subject.hash in caStore.certs) { // subject hash already exists, append to array var tmp = caStore.certs[cert.subject.hash]; @@ -2904,7 +2912,7 @@ pki.createCaStore = function(certs) { // produce subject hash if it doesn't exist if(!subject.hash) { var md = forge.md.sha1.create(); - subject.attributes = pki.RDNAttributesAsArray(_dnToAsn1(subject), md); + subject.attributes = pki.RDNAttributesAsArray(_dnToAsn1(subject), md); subject.hash = md.digest().toHex(); } } @@ -2940,13 +2948,13 @@ pki.certificateError = { * @param caStore a certificate store to verify against. * @param chain the certificate chain to verify, with the root or highest * authority at the end (an array of certificates). - * @param options a callback to be called for every certificate in the chain or + * @param options a callback to be called for every certificate in the chain or * an object with: * verify a callback to be called for every certificate in the * chain - * validityCheckDate the date against which the certificate - * validity period should be checked. Pass null to not check - * the validity period. By default, the current date is used. + * validityCheckDate the date against which the certificate + * validity period should be checked. Pass null to not check + * the validity period. By default, the current date is used. * * The verify callback has the following signature: * @@ -3092,8 +3100,8 @@ pki.verifyCertificateChain = function(caStore, chain, options) { CAs that may appear below a CA before only end-entity certificates may be issued. */ - // if a verify callback is passed as the third parameter, package it within - // the options object. This is to support a legacy function signature that + // if a verify callback is passed as the third parameter, package it within + // the options object. This is to support a legacy function signature that // expected the verify callback as the third parameter. if(typeof options === 'function') { options = {verify: options}; @@ -3106,13 +3114,13 @@ pki.verifyCertificateChain = function(caStore, chain, options) { var certs = chain.slice(0); var validityCheckDate = options.validityCheckDate; - // if no validityCheckDate is specified, default to the current date. Make - // sure to maintain the value null because it indicates that the validity + // if no validityCheckDate is specified, default to the current date. Make + // sure to maintain the value null because it indicates that the validity // period should not be checked. if(typeof validityCheckDate === 'undefined') { - validityCheckDate = new Date(); + validityCheckDate = new Date(); } - + // verify each cert in the chain using its parent, where the parent // is either the next in the chain or from the CA store var first = true; @@ -3125,14 +3133,14 @@ pki.verifyCertificateChain = function(caStore, chain, options) { if(validityCheckDate) { // 1. check valid time - if(validityCheckDate < cert.validity.notBefore || + if(validityCheckDate < cert.validity.notBefore || validityCheckDate > cert.validity.notAfter) { error = { message: 'Certificate is not valid yet or has expired.', error: pki.certificateError.certificate_expired, notBefore: cert.validity.notBefore, notAfter: cert.validity.notAfter, - // TODO: we might want to reconsider renaming 'now' to + // TODO: we might want to reconsider renaming 'now' to // 'validityCheckDate' should this API be changed in the future. now: validityCheckDate }; @@ -3301,7 +3309,7 @@ pki.verifyCertificateChain = function(caStore, chain, options) { // set custom message and error if(typeof ret === 'object' && !forge.util.isArray(ret)) { if(ret.message) { - error.message = ret.message; + error.message = ret.message; } if(ret.error) { error.error = ret.error; diff --git a/webpack.config.js b/webpack.config.js index 64e8578d6..806401eff 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -61,7 +61,7 @@ const outputs = [ //} ]; -outputs.forEach((info) => { +outputs.forEach(info => { // common to bundle and minified const common = { // each output uses the "forge" name but with different contents From 8acd1509766c2cdb1a9b19fc186ae004ab14890b Mon Sep 17 00:00:00 2001 From: "David I. Lehn" Date: Sat, 23 Feb 2019 16:50:31 -0500 Subject: [PATCH 064/197] Update dependencies. --- package.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index e27d57820..464743d56 100644 --- a/package.json +++ b/package.json @@ -18,8 +18,8 @@ "browserify": "^16.1.0", "commander": "^2.14.1", "cross-env": "^5.1.3", - "eslint": "^5.12.1", - "eslint-config-digitalbazaar": "^1.1.0", + "eslint": "^5.14.1", + "eslint-config-digitalbazaar": "^1.7.0", "express": "^4.16.2", "karma": "^3.1.4", "karma-browserify": "^6.0.0", @@ -37,7 +37,7 @@ "mocha": "^5.0.1", "mocha-lcov-reporter": "^1.2.0", "nodejs-websocket": "^1.7.1", - "nyc": "^13.1.0", + "nyc": "^13.3.0", "opts": "^1.2.2", "webpack": "^3.11.0", "worker-loader": "^2.0.0" From 5478021a7bdbbbc3b2ea981f28a0487b204cea10 Mon Sep 17 00:00:00 2001 From: "David I. Lehn" Date: Sat, 23 Feb 2019 17:27:59 -0500 Subject: [PATCH 065/197] Fix more linting issues. --- .eslintrc.js | 28 +++---- examples/create-cert.js | 2 +- examples/sign-p7.js | 2 +- flash/policyserver.js | 8 +- package.json | 2 +- tests/benchmarks/so-44303784.js | 32 ++++---- tests/unit/aes.js | 128 +++++++++++++++---------------- tests/unit/asn1.js | 2 +- tests/unit/ed25519.js | 2 - tests/unit/pkcs1.js | 4 +- tests/unit/pkcs7.js | 2 +- tests/unit/rsa.js | 32 ++++---- tests/unit/tls.js | 2 +- tests/unit/util.js | 4 +- tests/unit/x509.js | 21 +++-- tests/websockets/server-webid.js | 26 +++---- 16 files changed, 144 insertions(+), 153 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index a716eedf0..5477843af 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,22 +1,18 @@ module.exports = { - "env": { - "browser": true, - "commonjs": true, - "node": true + env: { + browser: true, + commonjs: true, + node: true }, - "extends": ["eslint-config-digitalbazaar"], - "parserOptions": { - "ecmaVersion": 5 + extends: ['eslint-config-digitalbazaar'], + parserOptions: { + ecmaVersion: 5 }, - "rules": { - "quotes": [ - "error", - "single" - ], + rules: { // overrides to support ES5, remove when updated to ES20xx - "no-unused-vars": "warn", - "no-var": "off", - "object-shorthand": "off", - "prefer-const": "off", + 'no-unused-vars': 'warn', + 'no-var': 'off', + 'object-shorthand': 'off', + 'prefer-const': 'off' } }; diff --git a/examples/create-cert.js b/examples/create-cert.js index 584de5392..365f5a782 100644 --- a/examples/create-cert.js +++ b/examples/create-cert.js @@ -103,7 +103,7 @@ try { console.log('Certificate verified.'); } return true; - }); + }); } catch(ex) { console.log('Certificate verification failure: ' + JSON.stringify(ex, null, 2)); diff --git a/examples/sign-p7.js b/examples/sign-p7.js index a2e249c9b..406ce787e 100644 --- a/examples/sign-p7.js +++ b/examples/sign-p7.js @@ -19,7 +19,7 @@ try { type: forge.pki.oids.messageDigest // value will be auto-populated at signing time }, { - type: forge.pki.oids.signingTime, + type: forge.pki.oids.signingTime // value will be auto-populated at signing time //value: new Date('2050-01-01T00:00:00Z') }] diff --git a/flash/policyserver.js b/flash/policyserver.js index bf1b04b6a..3f46bfc04 100644 --- a/flash/policyserver.js +++ b/flash/policyserver.js @@ -23,14 +23,14 @@ let policyFile = // Looks for a request string and returns the policy file. exports.policyServer = function(port) { let prefix = '[policy-server] '; - let server = net.createServer((socket) => { + let server = net.createServer(socket => { let remoteAddress = socket.remoteAddress + ':' + socket.remotePort; console.log(prefix + 'new client connection from %s', remoteAddress); // deal with strings socket.setEncoding('utf8'); - socket.on('data', (d) => { + socket.on('data', d => { if(d.indexOf('') === 0) { console.log(prefix + 'policy file request from: %s', remoteAddress); socket.write(policyFile); @@ -41,11 +41,11 @@ exports.policyServer = function(port) { socket.once('close', () => { console.log(prefix + 'connection from %s closed', remoteAddress); }); - socket.on('error', (err) => { + socket.on('error', err => { console.error( prefix + 'connection %s error: %s', remoteAddress, err.message); }); - }).on('error', (err) => { + }).on('error', err => { throw err; }); server.listen(port, () => { diff --git a/package.json b/package.json index 464743d56..5c0b15c72 100644 --- a/package.json +++ b/package.json @@ -102,7 +102,7 @@ "test-server-webid": "node tests/websockets/server-webid.js", "coverage": "rm -rf coverage && nyc --reporter=lcov --reporter=text-summary npm test", "coverage-report": "nyc report", - "lint": "eslint *.js lib/*.js tests/*.js tests/unit/*.js tests/legacy/*.js tests/issues/*.js tests/websockets/*.js" + "lint": "eslint *.js lib/*.js tests/*.js tests/**/*.js examples/*.js flash/*.js" }, "nyc": { "exclude": [ diff --git a/tests/benchmarks/so-44303784.js b/tests/benchmarks/so-44303784.js index 924e68c6c..a7c96581f 100644 --- a/tests/benchmarks/so-44303784.js +++ b/tests/benchmarks/so-44303784.js @@ -7,7 +7,7 @@ const forge = require('../..'); const assert = require('assert'); const crypto = require('crypto'); -const pwd = "aStringPassword"; +const pwd = 'aStringPassword'; const iv = forge.random.getBytesSync(16); const salt = forge.random.getBytesSync(16); const key = forge.pkcs5.pbkdf2(pwd, salt, 100, 16); @@ -86,7 +86,7 @@ function test_node(bytes) { function data(megs) { // slower single chunk const start = new Date(); - var x = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; + var x = '0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef'; var plain = ''; const minlen = megs * 1024 * 1024; while(plain.length < minlen) { @@ -117,7 +117,7 @@ function data_chunk(megs, chunkSize) { // faster with chunksize const start = new Date(); // make some large plain text bigger than some size - var x = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; + var x = '0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef'; var plain = ''; const minlen = megs * 1024 * 1024; while(plain.length < minlen) { @@ -185,8 +185,10 @@ function compareImpl() { tns.forEach(res => assert(input.plain == res.plain)); const tn = tns.reduce((prev, cur) => prev.time < cur.time ? prev : cur); - csv += `${i}\t${tf.time}\t${i/tf.time}\t${tfc.time}\t${i/tfc.time}\t${tn.time}\t${i/tn.time}\t${tf.time/tn.time}\t${tfc.time/tn.time}\n`; - console.log(`m:${i} tf:${tf.time} tf/s:${i/tf.time} tfc:${tfc.time} tfc/s:${i/tfc.time} tn:${tn.time} tn/s:${i/tn.time} sf:${tf.time/tn.time} sfc:${tfc.time/tn.time}`); + /* eslint-disable max-len */ + csv += `${i}\t${tf.time}\t${i / tf.time}\t${tfc.time}\t${i / tfc.time}\t${tn.time}\t${i / tn.time}\t${tf.time / tn.time}\t${tfc.time / tn.time}\n`; + console.log(`m:${i} tf:${tf.time} tf/s:${i / tf.time} tfc:${tfc.time} tfc/s:${i / tfc.time} tn:${tn.time} tn/s:${i / tn.time} sf:${tf.time / tn.time} sfc:${tfc.time / tn.time}`); + /* eslint-enable max-len */ } console.log(csv); } @@ -196,7 +198,7 @@ function compareDecChunkSize() { let csv = ''; const input = data_chunk(megs, 1024 * 64); function _test(k) { - chunkSize = 1024 * k; + const chunkSize = 1024 * k; const tfcs = [ test_forge_chunk(input.encrypted, chunkSize), test_forge_chunk(input.encrypted, chunkSize), @@ -204,13 +206,13 @@ function compareDecChunkSize() { ]; tfcs.forEach(res => assert(input.plain == res.plain)); const tfc = tfcs.reduce((prev, cur) => prev.time < cur.time ? prev : cur); - csv += `${k}\t${tfc.time}\t${megs/tfc.time}\n`; - console.log(`k:${k} tfc:${tfc.time} tfc/s:${megs/tfc.time}`); + csv += `${k}\t${tfc.time}\t${megs / tfc.time}\n`; + console.log(`k:${k} tfc:${tfc.time} tfc/s:${megs / tfc.time}`); } // sweep KB chunkSize const sweep = [ - 1,2,4,8,16,32,64,96,128,160,192,256, - 320,384,448,512,576,640,704,768,832,896,960,1024 + 1, 2, 4, 8, 16, 32, 64, 96, 128, 160, 192, 256, + 320, 384, 448, 512, 576, 640, 704, 768, 832, 896, 960, 1024 ]; sweep.forEach(k => _test(k)); console.log(csv); @@ -220,20 +222,20 @@ function compareEncChunkSize() { const megs = 10; let csv = ''; function _test(k) { - chunkSize = 1024 * k; + const chunkSize = 1024 * k; const dcs = [ data_chunk(megs, chunkSize), data_chunk(megs, chunkSize), data_chunk(megs, chunkSize) ]; const dc = dcs.reduce((prev, cur) => prev.time < cur.time ? prev : cur); - csv += `${k}\t${dc.time}\t${megs/dc.time}\n`; - console.log(`k:${k} dc:${dc.time} dc/s:${megs/dc.time}`); + csv += `${k}\t${dc.time}\t${megs / dc.time}\n`; + console.log(`k:${k} dc:${dc.time} dc/s:${megs / dc.time}`); } // sweep KB chunkSize const sweep = [ - 1,2,4,8,16,32,64,96,128,160,192,256, - 320,384,448,512,576,640,704,768,832,896,960,1024 + 1, 2, 4, 8, 16, 32, 64, 96, 128, 160, 192, 256, + 320, 384, 448, 512, 576, 640, 704, 768, 832, 896, 960, 1024 ]; sweep.forEach(k => _test(k)); console.log(csv); diff --git a/tests/unit/aes.js b/tests/unit/aes.js index ef7c4693a..d139cc0c1 100644 --- a/tests/unit/aes.js +++ b/tests/unit/aes.js @@ -40,79 +40,79 @@ var UTIL = require('../../lib/util'); }); it('should encrypt a single block with a 192-bit key', function() { - var key = [ - 0x00010203, 0x04050607, 0x08090a0b, 0x0c0d0e0f, - 0x10111213, 0x14151617]; - var block = [0x00112233, 0x44556677, 0x8899aabb, 0xccddeeff]; - - var output = []; - var w = AES._expandKey(key, false); - AES._updateBlock(w, block, output, false); - - var out = UTIL.createBuffer(); - out.putInt32(output[0]); - out.putInt32(output[1]); - out.putInt32(output[2]); - out.putInt32(output[3]); - - ASSERT.equal(out.toHex(), 'dda97ca4864cdfe06eaf70a0ec0d7191'); + var key = [ + 0x00010203, 0x04050607, 0x08090a0b, 0x0c0d0e0f, + 0x10111213, 0x14151617]; + var block = [0x00112233, 0x44556677, 0x8899aabb, 0xccddeeff]; + + var output = []; + var w = AES._expandKey(key, false); + AES._updateBlock(w, block, output, false); + + var out = UTIL.createBuffer(); + out.putInt32(output[0]); + out.putInt32(output[1]); + out.putInt32(output[2]); + out.putInt32(output[3]); + + ASSERT.equal(out.toHex(), 'dda97ca4864cdfe06eaf70a0ec0d7191'); }); it('should decrypt a single block with a 192-bit key', function() { - var key = [ - 0x00010203, 0x04050607, 0x08090a0b, 0x0c0d0e0f, - 0x10111213, 0x14151617]; - var block = [0xdda97ca4, 0x864cdfe0, 0x6eaf70a0, 0xec0d7191]; - - var output = []; - var w = AES._expandKey(key, true); - AES._updateBlock(w, block, output, true); - - var out = UTIL.createBuffer(); - out.putInt32(output[0]); - out.putInt32(output[1]); - out.putInt32(output[2]); - out.putInt32(output[3]); - - ASSERT.equal(out.toHex(), '00112233445566778899aabbccddeeff'); + var key = [ + 0x00010203, 0x04050607, 0x08090a0b, 0x0c0d0e0f, + 0x10111213, 0x14151617]; + var block = [0xdda97ca4, 0x864cdfe0, 0x6eaf70a0, 0xec0d7191]; + + var output = []; + var w = AES._expandKey(key, true); + AES._updateBlock(w, block, output, true); + + var out = UTIL.createBuffer(); + out.putInt32(output[0]); + out.putInt32(output[1]); + out.putInt32(output[2]); + out.putInt32(output[3]); + + ASSERT.equal(out.toHex(), '00112233445566778899aabbccddeeff'); }); it('should encrypt a single block with a 256-bit key', function() { - var key = [ - 0x00010203, 0x04050607, 0x08090a0b, 0x0c0d0e0f, - 0x10111213, 0x14151617, 0x18191a1b, 0x1c1d1e1f]; - var block = [0x00112233, 0x44556677, 0x8899aabb, 0xccddeeff]; - - var output = []; - var w = AES._expandKey(key, false); - AES._updateBlock(w, block, output, false); - - var out = UTIL.createBuffer(); - out.putInt32(output[0]); - out.putInt32(output[1]); - out.putInt32(output[2]); - out.putInt32(output[3]); - - ASSERT.equal(out.toHex(), '8ea2b7ca516745bfeafc49904b496089'); + var key = [ + 0x00010203, 0x04050607, 0x08090a0b, 0x0c0d0e0f, + 0x10111213, 0x14151617, 0x18191a1b, 0x1c1d1e1f]; + var block = [0x00112233, 0x44556677, 0x8899aabb, 0xccddeeff]; + + var output = []; + var w = AES._expandKey(key, false); + AES._updateBlock(w, block, output, false); + + var out = UTIL.createBuffer(); + out.putInt32(output[0]); + out.putInt32(output[1]); + out.putInt32(output[2]); + out.putInt32(output[3]); + + ASSERT.equal(out.toHex(), '8ea2b7ca516745bfeafc49904b496089'); }); it('should decrypt a single block with a 256-bit key', function() { - var key = [ - 0x00010203, 0x04050607, 0x08090a0b, 0x0c0d0e0f, - 0x10111213, 0x14151617, 0x18191a1b, 0x1c1d1e1f]; - var block = [0x8ea2b7ca, 0x516745bf, 0xeafc4990, 0x4b496089]; - - var output = []; - var w = AES._expandKey(key, true); - AES._updateBlock(w, block, output, true); - - var out = UTIL.createBuffer(); - out.putInt32(output[0]); - out.putInt32(output[1]); - out.putInt32(output[2]); - out.putInt32(output[3]); - - ASSERT.equal(out.toHex(), '00112233445566778899aabbccddeeff'); + var key = [ + 0x00010203, 0x04050607, 0x08090a0b, 0x0c0d0e0f, + 0x10111213, 0x14151617, 0x18191a1b, 0x1c1d1e1f]; + var block = [0x8ea2b7ca, 0x516745bf, 0xeafc4990, 0x4b496089]; + + var output = []; + var w = AES._expandKey(key, true); + AES._updateBlock(w, block, output, true); + + var out = UTIL.createBuffer(); + out.putInt32(output[0]); + out.putInt32(output[1]); + out.putInt32(output[2]); + out.putInt32(output[3]); + + ASSERT.equal(out.toHex(), '00112233445566778899aabbccddeeff'); }); // AES-128-ECB diff --git a/tests/unit/asn1.js b/tests/unit/asn1.js index 0d7349211..65a1db510 100644 --- a/tests/unit/asn1.js +++ b/tests/unit/asn1.js @@ -452,7 +452,7 @@ var UTIL = require('../../lib/util'); // validator check if(!throws && options.v) { var capture = {}; - var errors = [] + var errors = []; var asn1ok = ASN1.validate(asn1, options.v, capture, errors); ASSERT.deepEqual(errors, []); if(options.captured) { diff --git a/tests/unit/ed25519.js b/tests/unit/ed25519.js index fa204d8d9..6fc9166ed 100644 --- a/tests/unit/ed25519.js +++ b/tests/unit/ed25519.js @@ -1,7 +1,5 @@ var ASSERT = require('assert'); -var FORGE = require('../../lib/forge'); var ED25519 = require('../../lib/ed25519'); -var RANDOM = require('../../lib/random'); var SHA256 = require('../../lib/sha256'); var UTIL = require('../../lib/util'); diff --git a/tests/unit/pkcs1.js b/tests/unit/pkcs1.js index c26da03a5..6681802b3 100644 --- a/tests/unit/pkcs1.js +++ b/tests/unit/pkcs1.js @@ -686,13 +686,13 @@ var UTIL = require('../../lib/util'); message: 'SoZglTTuQ0psvKP36WLnbUVeMmTBn2Bfbl/2E3xlxW1/s0TNUryTN089FmyfDG+cUGutGTMJctI=', seed: 'HKwZzpk971X5ggP2hSiWyVzMofMcrBnOmT3vVfmCA/Y=', encrypted: 'AooWJVOiXRikAgxb8XW7nkDMKIcrCgZNTV0sY352+QatjTq4go6/DtieHvIgUgb/QYBYlOPOZkdiMWXtOFdapIMRFraGeq4mKhEVmSM8G5mpVgc62nVR0jX49AXeuw7kMGxnKTV4whJanPYYQRoOb0L4Mf+8uJ5QdqBE03Ohupsp' - /* FIXME: could not convert 4.2', to SHA-256, message too long + /* FIXME: could not convert 4.2', to SHA-256, message too long }, { title: 'RSAES-OAEP Encryption Example 4.2', message: 'sK3E8/4R2lnOmSdz2QWZQ8AwRkl+6dn5oG3xFm20bZj1jSfsB0wC7ubL4kSci5/FCAxcP0QzCSUS7EaqeTdDyA==', seed: '9UXViXWF49txqgy42nbFHQMq6WM=', encrypted: 'AJe2mMYWVkWzA0hvv1oqRHnA7oWIm1QabwuFjWtll7E7hU60+DmvAzmagNeb2mV4yEH5DWRXFbKA03FDmS3RhsgLlJt3XK6XNw5OyXRDE2xtpITpcP/bEyOiCEeCHTsYOB3hO7SarqZlMMSkuCcfPq4XLNNm4H5mNvEBnSoortFe' - */ + */ }, { title: 'RSAES-OAEP Encryption Example 4.3', message: 'v21C5wFwex0CBrDItFoccmQf8SiJIZqCveqWW155qWsNAWPtnVeOya2iDy+88eo8QInYNBm6gbDGDzYG2pk=', diff --git a/tests/unit/pkcs7.js b/tests/unit/pkcs7.js index 01efc3470..e99c13867 100644 --- a/tests/unit/pkcs7.js +++ b/tests/unit/pkcs7.js @@ -409,7 +409,7 @@ var UTIL = require('../../lib/util'); ASSERT.equal(p7.recipients[0].encryptedContent.content.length, 256); ASSERT.equal(p7.encryptedContent.algorithm, PKI.oids['aes256-CBC']); - ASSERT.equal(p7.encryptedContent.parameter.data.length, 16); // IV + ASSERT.equal(p7.encryptedContent.parameter.data.length, 16); // IV }); it('should import indefinite length message from PEM', function() { diff --git a/tests/unit/rsa.js b/tests/unit/rsa.js index 39e9b3e54..0cdd28e01 100644 --- a/tests/unit/rsa.js +++ b/tests/unit/rsa.js @@ -140,7 +140,7 @@ var UTIL = require('../../lib/util'); if(isAsync && !isPurejs && typeof require('crypto').generateKeyPair === 'function') { return false; - }; + } if(!isAsync && !isPurejs && typeof require('crypto').generateKeyPairSync === 'function') { return false; @@ -204,8 +204,8 @@ var UTIL = require('../../lib/util'); it('should generate same 512 bit key pair (prng+sync,prng+sync+purejs)', function() { - if(!isDeterministic(true,false,false) || - !isDeterministic(true,false,true)) { + if(!isDeterministic(true, false, false) || + !isDeterministic(true, false, true)) { this.skip(); } var pair1 = _genSync({samePrng: true}); @@ -221,8 +221,8 @@ var UTIL = require('../../lib/util'); it('should generate same 512 bit key pair ' + '(prng+sync+purejs,prng+sync+purejs)', function() { - if(!isDeterministic(true,false,true) || - !isDeterministic(true,false,true)) { + if(!isDeterministic(true, false, true) || + !isDeterministic(true, false, true)) { this.skip(); } // save @@ -238,8 +238,8 @@ var UTIL = require('../../lib/util'); it('should generate same 512 bit key pair (prng+sync,prng+async)', function(done) { - if(!isDeterministic(true,false,false) || - !isDeterministic(true,true,false)) { + if(!isDeterministic(true, false, false) || + !isDeterministic(true, true, false)) { this.skip(); } var pair1 = _genSync({samePrng: true}); @@ -251,8 +251,8 @@ var UTIL = require('../../lib/util'); it('should generate same 512 bit key pair (prng+async,prng+sync)', function(done) { - if(!isDeterministic(true,true,false) || - !isDeterministic(true,false,false)) { + if(!isDeterministic(true, true, false) || + !isDeterministic(true, false, false)) { this.skip(); } _genAsync({samePrng: true}, function(pair1) { @@ -264,8 +264,8 @@ var UTIL = require('../../lib/util'); it('should generate same 512 bit key pair (prng+async,prng+async)', function(done) { - if(!isDeterministic(true,true,false) || - !isDeterministic(true,true,false)) { + if(!isDeterministic(true, true, false) || + !isDeterministic(true, true, false)) { this.skip(); } var pair1; @@ -310,7 +310,7 @@ var UTIL = require('../../lib/util'); it('should PKCS#8 encrypt and decrypt private key with ' + algorithm, function() { var privateKey = PKI.privateKeyFromPem(_pem.privateKey); var encryptedPem = PKI.encryptRsaPrivateKey( - privateKey, 'password', {algorithm: algorithm}); + privateKey, 'password', {algorithm: algorithm}); privateKey = PKI.decryptRsaPrivateKey(encryptedPem, 'password'); ASSERT.equal(PKI.privateKeyToPem(privateKey), _pem.privateKey); }); @@ -326,10 +326,10 @@ var UTIL = require('../../lib/util'); ' encryption and ' + prfAlgorithm + ' PRF', function() { var privateKey = PKI.privateKeyFromPem(_pem.privateKey); var encryptedPem = PKI.encryptRsaPrivateKey( - privateKey, 'password', { - algorithm: algorithm, - prfAlgorithm: prfAlgorithm - }); + privateKey, 'password', { + algorithm: algorithm, + prfAlgorithm: prfAlgorithm + }); privateKey = PKI.decryptRsaPrivateKey(encryptedPem, 'password'); ASSERT.equal(PKI.privateKeyToPem(privateKey), _pem.privateKey); }); diff --git a/tests/unit/tls.js b/tests/unit/tls.js index 34460a12a..45b0712a4 100644 --- a/tests/unit/tls.js +++ b/tests/unit/tls.js @@ -12,7 +12,7 @@ require('../../lib/util'); // But that link is now dead. var secret = forge.util.createBuffer().fillWithByte(0xAB, 48).getBytes(); var seed = forge.util.createBuffer().fillWithByte(0xCD, 64).getBytes(); - var bytes = forge.tls.prf_tls1(secret, 'PRF Testvector', seed, 104); + var bytes = forge.tls.prf_tls1(secret, 'PRF Testvector', seed, 104); var expect = 'd3d4d1e349b5d515044666d51de32bab258cb521' + 'b6b053463e354832fd976754443bcf9a296519bc' + diff --git a/tests/unit/util.js b/tests/unit/util.js index 323dd78dc..7b4344397 100644 --- a/tests/unit/util.js +++ b/tests/unit/util.js @@ -5,7 +5,7 @@ var UTIL = require('../../lib/util'); // custom assertion to test array-like objects function assertArrayEqual(actual, expected) { ASSERT.equal(actual.length, expected.length); - for (var idx = 0; idx < expected.length; idx++) { + for(var idx = 0; idx < expected.length; idx++) { ASSERT.equal(actual[idx], expected[idx]); } } @@ -202,7 +202,7 @@ var UTIL = require('../../lib/util'); var b = UTIL.createBuffer(UTIL.hexToBytes('1234567887654321')); ASSERT.equal(b.getInt(32), 0x12345678); // FIXME: getInt bit shifts create signed int - ASSERT.equal(b.getInt(32), 0x87654321<<0); + ASSERT.equal(b.getInt(32), 0x87654321 << 0); ASSERT.equal(b.length(), 0); }); diff --git a/tests/unit/x509.js b/tests/unit/x509.js index 3e2585c07..eb060dcbc 100644 --- a/tests/unit/x509.js +++ b/tests/unit/x509.js @@ -253,7 +253,6 @@ var UTIL = require('../../lib/util'); '-----END CERTIFICATE-----\r\n' }; - describe('x509', function() { it('should convert SHA-1 based certificate to/from PEM', function() { var certificate = PKI.certificateFromPem(_pem.certificate); @@ -295,7 +294,6 @@ var UTIL = require('../../lib/util'); ASSERT.ok(certificate.verify(certificate)); }); - it('should generate a certificate with authorityKeyIdentifier extension', function() { var keys = { privateKey: PKI.privateKeyFromPem(_pem.privateKey), @@ -534,7 +532,7 @@ var UTIL = require('../../lib/util'); privateKey: PKI.privateKeyFromPem(_pem.privateKey), publicKey: PKI.publicKeyFromPem(_pem.publicKey) }; - var attrs = [{ + var attrs = [{ name: 'commonName', value: 'example.org' }, { @@ -553,7 +551,7 @@ var UTIL = require('../../lib/util'); shortName: 'OU', value: 'Test' }]; - var notBefore = new Date("2050-02-02"); + var notBefore = new Date('2050-02-02'); var cert = createCertificate({ publicKey: keys.publicKey, signingKey: keys.privateKey, @@ -561,13 +559,13 @@ var UTIL = require('../../lib/util'); subject: attrs, issuer: attrs, isCA: true, - notBefore : notBefore + notBefore: notBefore }); var pem = PKI.certificateToPem(cert); cert = PKI.certificateFromPem(pem); - var notAfter = new Date("2051-02-02"); + var notAfter = new Date('2051-02-02'); ASSERT.equal(cert.validity.notBefore.toString(), notBefore.toString()); ASSERT.equal(cert.validity.notAfter.toString(), notAfter.toString()); }); @@ -859,7 +857,7 @@ var UTIL = require('../../lib/util'); PKI.verifyCertificateChain(caStore, [cert], { validityCheckDate: verifyDate, verify: function(vfd, depth, chain) { - ASSERT.equal(vfd, "forge.pki.CertificateExpired"); + ASSERT.equal(vfd, 'forge.pki.CertificateExpired'); return true; } }); @@ -869,7 +867,7 @@ var UTIL = require('../../lib/util'); PKI.verifyCertificateChain(caStore, [cert], { validityCheckDate: verifyDate, verify: function(vfd, depth, chain) { - ASSERT.equal(vfd, "forge.pki.CertificateExpired"); + ASSERT.equal(vfd, 'forge.pki.CertificateExpired'); return true; } }); @@ -925,7 +923,7 @@ var UTIL = require('../../lib/util'); PKI.verifyCertificateChain(caStore, [cert], { verify: function(vfd, depth, chain) { - ASSERT.equal(vfd, "forge.pki.CertificateExpired"); + ASSERT.equal(vfd, 'forge.pki.CertificateExpired'); return true; } }); @@ -1507,10 +1505,11 @@ var UTIL = require('../../lib/util'); var isCA = options.isCA; var serialNumber = options.serialNumber || '01'; var notBefore = options.notBefore || new Date(); + var notAfter; if(options.notAfter) { - var notAfter = options.notAfter; + notAfter = options.notAfter; } else { - var notAfter = new Date(notBefore); + notAfter = new Date(notBefore); notAfter.setFullYear(notAfter.getFullYear() + 1); } diff --git a/tests/websockets/server-webid.js b/tests/websockets/server-webid.js index 70b46cac0..6f7cf37b8 100644 --- a/tests/websockets/server-webid.js +++ b/tests/websockets/server-webid.js @@ -68,7 +68,7 @@ var getPublicKey = function(data, uri, callback) { //var RSA = rdf.Namespace('http://www.w3.org/ns/auth/rsa#'); var RDF = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#'; var CERT = 'http://www.w3.org/ns/auth/cert#'; - var RSA = 'http://www.w3.org/ns/auth/rsa#'; + var RSA = 'http://www.w3.org/ns/auth/rsa#'; var desc = RDF + 'Description'; var about = RDF + 'about'; var type = RDF + 'type'; @@ -92,9 +92,9 @@ var getPublicKey = function(data, uri, callback) { } // any other resource else if( - key in node && - typeof node[key] === 'object' && !forge.util.isArray(node[key]) && - '@' in node[key] && resource in node[key]['@']) { + key in node && + typeof node[key] === 'object' && !forge.util.isArray(node[key]) && + '@' in node[key] && resource in node[key]['@']) { rval = node[key]['@'][resource]; } @@ -115,8 +115,7 @@ var getPublicKey = function(data, uri, callback) { // normalize RDF descriptions to array if(!forge.util.isArray(result[desc])) { desc = [result[desc]]; - } - else { + } else { desc = result[desc]; } @@ -179,23 +178,22 @@ var fetchUrl = function(url, callback, redirects) { var client = http.createClient( url.port, url.fullHost, url.scheme === 'https'); var request = client.request('GET', url.path, { - 'Host': url.host, - 'Accept': 'application/rdf+xml' + Host: url.host, + Accept: 'application/rdf+xml' }); request.addListener('response', function(response) { var body = ''; // error, return empty body if(response.statusCode >= 400) { - callback(body); + callback(body); } // follow redirect else if(response.statusCode === 302) { if(redirects > 0) { // follow redirect fetchUrl(response.headers.location, callback, --redirects); - } - else { + } else { // return empty body callback(body); } @@ -277,8 +275,7 @@ var authenticateWebId = function(c, state) { webID: url, rdf: forge.util.encode64(body) })); - } - else { + } else { // try next alt name authNext(); } @@ -426,8 +423,7 @@ var createTls = function(websocket) { // do WebID authentication try { authenticateWebId(c, state); - } - catch(ex) { + } catch(ex) { c.close(); } }, From 91c74a10e5ed4427d0646e7be04402e2a87aa41a Mon Sep 17 00:00:00 2001 From: Dave Longley Date: Sun, 17 Mar 2019 12:48:50 -0400 Subject: [PATCH 066/197] Fix tag calculation when continuing an AES-GCM block. - Addresses #666. --- lib/cipherModes.js | 2 +- tests/unit/aes.js | 38 +++++++++++++++++++++++++++++++------- 2 files changed, 32 insertions(+), 8 deletions(-) diff --git a/lib/cipherModes.js b/lib/cipherModes.js index b6831e4bd..1f2d41ddc 100644 --- a/lib/cipherModes.js +++ b/lib/cipherModes.js @@ -652,7 +652,7 @@ modes.gcm.prototype.encrypt = function(input, output, finish) { this._partialOutput.putInt32(input.getInt32() ^ this._outBlock[i]); } - if(partialBytes === 0 || finish) { + if(partialBytes <= 0 || finish) { // handle overflow prior to hashing if(finish) { // get block overflow diff --git a/tests/unit/aes.js b/tests/unit/aes.js index d139cc0c1..c1b779317 100644 --- a/tests/unit/aes.js +++ b/tests/unit/aes.js @@ -1080,7 +1080,8 @@ var UTIL = require('../../lib/util'); 'feffe9928665731c6d6a8f9467308308', 'feffe9928665731c6d6a8f9467308308', 'feffe9928665731c6d6a8f9467308308', - '00000000000000000000000000000000' + '00000000000000000000000000000000', + '31313131323232323333333334343434' ]; var ivs = [ @@ -1093,7 +1094,8 @@ var UTIL = require('../../lib/util'); '6a7a9538534f7da1e4c303d2a318a728' + 'c3c0c95156809539fcf0e2429a6b5254' + '16aedbf5a0de6a57a637b39b', - '000000000000000000000000' + '000000000000000000000000', + '313131323232333333343434' ]; var adatas = [ @@ -1106,6 +1108,7 @@ var UTIL = require('../../lib/util'); 'abaddad2', 'feedfacedeadbeeffeedfacedeadbeef' + 'abaddad2', + '', '' ]; @@ -1128,7 +1131,9 @@ var UTIL = require('../../lib/util'); '86a7a9531534f7da2e4c303d8a318a72' + '1c3c0c95956809532fcf0e2449a6b525' + 'b16aedf5aa0de657ba637b39', - '0000' + '0000', + '3131313131323232323231313131313232' + + '3232323131313131323232323231313131313232323232' ]; var outputs = [ @@ -1150,7 +1155,9 @@ var UTIL = require('../../lib/util'); 'be9112a5c3a211a8ba262a3cca7e2ca7' + '01e4a9a4fba43c90ccdcb281d48c7c6f' + 'd62875d2aca417034c34aee5', - '0388' + '0388', + '0d75de6b0ddea90e4846e5fafeccf82d91' + + '927f1b5e5074e29911be7d7fd2b317aea570a359354f2d' ]; var tags = [ @@ -1160,7 +1167,8 @@ var UTIL = require('../../lib/util'); '5bc94fbc3221a5db94fae95ae7121a47', '3612d2e79e3b0785561be14aaca2fccb', '619cc5aefffe0bfa462af43c1699d050', - '93dcdd26f79ec1dd9bff57204d9b33f5' + '93dcdd26f79ec1dd9bff57204d9b33f5', + '766028a0b2fa2fff04c564f3b960988f' ]; for(var i = 0; i < keys.length; ++i) { @@ -1211,6 +1219,22 @@ var UTIL = require('../../lib/util'); ASSERT.equal(out.toHex(), outputs[i]); ASSERT.equal(cipher.mode.tag.toHex(), tags[i]); }); + + it('should aes-128-gcm encrypt (blockSize/2+1 bytes at a time): ' + inputs[i], function() { + // encrypt + var cipher = CIPHER.createCipher('AES-GCM', key); + var size = cipher.blockSize / 2 + 1; + cipher.start({iv: iv, additionalData: adata}); + var input_ = UTIL.createBuffer(input); + var out = UTIL.createBuffer(); + while(input_.length() > 0) { + cipher.update(UTIL.createBuffer(input_.getBytes(size))); + out.putBytes(cipher.output.getBytes(size)); + } + cipher.finish(); + ASSERT.equal(out.toHex(), outputs[i]); + ASSERT.equal(cipher.mode.tag.toHex(), tags[i]); + }); })(i); } })(); @@ -1443,7 +1467,7 @@ var UTIL = require('../../lib/util'); var input = UTIL.hexToBytes(inputs[i]); var output = UTIL.hexToBytes(outputs[i]); - it('should aes-128-gcm encrypt: ' + inputs[i], function() { + it('should aes-256-gcm encrypt: ' + inputs[i], function() { // encrypt var cipher = CIPHER.createCipher('AES-GCM', key); cipher.start({iv: iv, additionalData: adata}); @@ -1453,7 +1477,7 @@ var UTIL = require('../../lib/util'); ASSERT.equal(cipher.mode.tag.toHex(), tags[i]); }); - it('should aes-128-gcm decrypt: ' + outputs[i], function() { + it('should aes-256-gcm decrypt: ' + outputs[i], function() { // decrypt var cipher = CIPHER.createDecipher('AES-GCM', key); cipher.start({ From edd23b9e7e0b93847733c2aca5fe26554381954f Mon Sep 17 00:00:00 2001 From: "David I. Lehn" Date: Mon, 18 Mar 2019 17:09:45 -0400 Subject: [PATCH 067/197] Fix indents. --- tests/unit/aes.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/unit/aes.js b/tests/unit/aes.js index c1b779317..2cd5bd23e 100644 --- a/tests/unit/aes.js +++ b/tests/unit/aes.js @@ -1133,7 +1133,7 @@ var UTIL = require('../../lib/util'); 'b16aedf5aa0de657ba637b39', '0000', '3131313131323232323231313131313232' + - '3232323131313131323232323231313131313232323232' + '3232323131313131323232323231313131313232323232' ]; var outputs = [ @@ -1157,7 +1157,7 @@ var UTIL = require('../../lib/util'); 'd62875d2aca417034c34aee5', '0388', '0d75de6b0ddea90e4846e5fafeccf82d91' + - '927f1b5e5074e29911be7d7fd2b317aea570a359354f2d' + '927f1b5e5074e29911be7d7fd2b317aea570a359354f2d' ]; var tags = [ From 334202af042a02a1cd81cbd9fb6248558a2267fa Mon Sep 17 00:00:00 2001 From: "David I. Lehn" Date: Mon, 18 Mar 2019 17:10:50 -0400 Subject: [PATCH 068/197] Update changelog. --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 85dd4bde2..50356703c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,9 @@ Forge ChangeLog =============== +### Fixed +- Fix tag calculation when continuing an AES-GCM block. + ### Changed - Switch to eslint. From 6139558173a632068a2a1494c85ec053f5115f80 Mon Sep 17 00:00:00 2001 From: "David I. Lehn" Date: Mon, 18 Mar 2019 17:33:34 -0400 Subject: [PATCH 069/197] Release 0.8.2. --- CHANGELOG.md | 2 ++ package.json | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 50356703c..3c4138e5f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,8 @@ Forge ChangeLog =============== +## 0.8.2 - 2019-03-18 + ### Fixed - Fix tag calculation when continuing an AES-GCM block. diff --git a/package.json b/package.json index 5c0b15c72..668a29d85 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "node-forge", - "version": "0.8.2-dev", + "version": "0.8.2", "description": "JavaScript implementations of network transports, cryptography, ciphers, PKI, message digests, and various utilities.", "homepage": "https://github.com/digitalbazaar/forge", "author": { From d0a4bda7bb51c3ca17efe84ad9dab46bf8c4728f Mon Sep 17 00:00:00 2001 From: "David I. Lehn" Date: Mon, 18 Mar 2019 17:33:53 -0400 Subject: [PATCH 070/197] Start 0.8.3. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 668a29d85..2dcf30c09 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "node-forge", - "version": "0.8.2", + "version": "0.8.3-dev", "description": "JavaScript implementations of network transports, cryptography, ciphers, PKI, message digests, and various utilities.", "homepage": "https://github.com/digitalbazaar/forge", "author": { From 4da76e2957e49796c07370a0bb03a6786ce46234 Mon Sep 17 00:00:00 2001 From: Ian Gartley Date: Tue, 14 May 2019 15:32:46 -0400 Subject: [PATCH 071/197] Remove non breaking space from tls.js -- this causes issues on some browsers --- lib/tls.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/tls.js b/lib/tls.js index 0b3b687cf..fadfd646f 100644 --- a/lib/tls.js +++ b/lib/tls.js @@ -3528,7 +3528,7 @@ var _alertDescToCertError = function(desc) { */ tls.verifyCertificateChain = function(c, chain) { try { - // Make a copy of c.verifyOptions so that we can modify options.verify + // Make a copy of c.verifyOptions so that we can modify options.verify // without modifying c.verifyOptions. var options = {}; for (var key in c.verifyOptions) { @@ -3726,7 +3726,7 @@ tls.createConnection = function(options) { virtualHost: options.virtualHost || null, verifyClient: options.verifyClient || false, verify: options.verify || function(cn, vfd, dpth, cts) {return vfd;}, - verifyOptions: options.verifyOptions || {}, + verifyOptions: options.verifyOptions || {}, getCertificate: options.getCertificate || null, getPrivateKey: options.getPrivateKey || null, getSignature: options.getSignature || null, From ebc78824f6057d361eeef7f19e8fb12614342ba7 Mon Sep 17 00:00:00 2001 From: "David I. Lehn" Date: Wed, 15 May 2019 14:20:17 -0400 Subject: [PATCH 072/197] Remove unused jscs and jshint config files. --- .jscsrc | 83 --------------------------------------------------- .jshintignore | 0 .jshintrc | 4 --- 3 files changed, 87 deletions(-) delete mode 100644 .jscsrc delete mode 100644 .jshintignore delete mode 100644 .jshintrc diff --git a/.jscsrc b/.jscsrc deleted file mode 100644 index cc52fb3ae..000000000 --- a/.jscsrc +++ /dev/null @@ -1,83 +0,0 @@ -{ - "excludeFiles": [ - "./lib/jsbn.js" - ], - "disallowKeywords": ["with"], - "disallowKeywordsOnNewLine": ["else", "catch"], - // FIXME: enable this? - //"disallowImplicitTypeConversion": ["string"], - "disallowMixedSpacesAndTabs": true, - "disallowMultipleLineBreaks": true, - // FIXME: enable this or do we prefer to - // use w/angular directive templates? - //"disallowMultipleLineStrings": true, - "disallowNewlineBeforeBlockStatements": true, - //"disallowSpaceAfterKeywords": [ - // "if", - // "for", - // "while", - // "do", - // "switch", - // "catch" - //], - "disallowSpaceAfterObjectKeys": true, - "disallowSpaceAfterPrefixUnaryOperators": ["++", "--", "+", "-", "~", "!"], - "disallowSpaceBeforeBinaryOperators": [","], - "disallowSpaceBeforePostfixUnaryOperators": ["++", "--"], - "disallowSpacesInAnonymousFunctionExpression": { - "beforeOpeningRoundBrace": true - }, - //"disallowSpacesInCallExpression": true, - "disallowSpacesInFunctionDeclaration": { - "beforeOpeningRoundBrace": true - }, - "disallowSpacesInNamedFunctionExpression": { - "beforeOpeningRoundBrace": true - }, - //"disallowSpacesInsideArrayBrackets": true, - "disallowSpacesInsideParentheses": true, - "disallowTrailingComma": true, - "disallowTrailingWhitespace": true, - //"maximumLineLength": { - // "value": 80, - // "allExcept": ["comments", "regex"] - //}, - "requireCommaBeforeLineBreak": true, - "requireCurlyBraces": ["if", "else", "for", "while", "do", "try", "catch"], - "requireLineFeedAtFileEnd": true, - "requireSemicolons": true, - "requireSpaceAfterBinaryOperators": ["?", ":", "+", "-", "/", "*", "%", "==", "===", "!=", "!==", ">", ">=", "<", "<=", "&&", "||"], - "requireSpaceBeforeBinaryOperators": ["?", ":", "+", "-", "/", "*", "%", "==", "===", "!=", "!==", ">", ">=", "<", "<=", "&&", "||"], - //"requireSpaceBeforeObjectValues": true, - "requireSpaceAfterKeywords": [ - "else", - "do", - "return", - "try" - ], - //"requireSpaceBeforeKeywords": [ - // "else", - // "while", - // "catch" - //], - "requireSpaceBeforeBlockStatements": true, - "requireSpacesInConditionalExpression": { - "afterTest": true, - "beforeConsequent": true, - "afterConsequent": true, - "beforeAlternate": true - }, - //"requireSpacesInForStatement": true, - "requireSpacesInFunction": { - "beforeOpeningCurlyBrace": true - }, - "safeContextKeyword": "self", - //"validateIndentation": 2, - "validateLineBreaks": "LF", - // FIXME: enable doc checks (update to use newer jscs jsdoc module) - //"validateJSDoc": { - // "checkParamNames": true, - // "requireParamTypes": true - //}, - "validateParameterSeparator": ", " -} diff --git a/.jshintignore b/.jshintignore deleted file mode 100644 index e69de29bb..000000000 diff --git a/.jshintrc b/.jshintrc deleted file mode 100644 index ef5cdec92..000000000 --- a/.jshintrc +++ /dev/null @@ -1,4 +0,0 @@ -{ - "sub": true, - "esversion": 6 -} From 1d19238a4cf671b34d90df68a4ed9f7650255d49 Mon Sep 17 00:00:00 2001 From: "David I. Lehn" Date: Wed, 15 May 2019 14:32:17 -0400 Subject: [PATCH 073/197] Use basic character set. --- lib/aes.js | 4 ++-- lib/pkcs12.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/aes.js b/lib/aes.js index d64062085..1ac3e8205 100644 --- a/lib/aes.js +++ b/lib/aes.js @@ -648,7 +648,7 @@ function initialize() { * of Nb*(Nr + 1) words: the algorithm requires an initial set of Nb words, * and each of the Nr rounds requires Nb words of key data. The resulting * key schedule consists of a linear array of 4-byte words, denoted [wi ], - * with i in the range 0 ≤ i < Nb(Nr + 1). + * with i in the range 0 <= i < Nb(Nr + 1). * * KeyExpansion(byte key[4*Nk], word w[Nb*(Nr+1)], Nk) * AES-128 (Nb=4, Nk=4, Nr=10) @@ -805,7 +805,7 @@ function _updateBlock(w, input, output, decrypt) { byte state[4,Nb] state = in AddRoundKey(state, w[0, Nb-1]) - for round = 1 step 1 to Nr–1 + for round = 1 step 1 to Nr-1 SubBytes(state) ShiftRows(state) MixColumns(state) diff --git a/lib/pkcs12.js b/lib/pkcs12.js index 19ff2a9ed..cd06c494a 100644 --- a/lib/pkcs12.js +++ b/lib/pkcs12.js @@ -68,7 +68,7 @@ * PKCS12Attribute ::= SEQUENCE { * attrId ATTRIBUTE.&id ({PKCS12AttrSet}), * attrValues SET OF ATTRIBUTE.&Type ({PKCS12AttrSet}{@attrId}) - * } -- This type is compatible with the X.500 type ’Attribute’ + * } -- This type is compatible with the X.500 type 'Attribute' * * PKCS12AttrSet ATTRIBUTE ::= { * friendlyName | -- from PKCS #9 From 1887cfce43a8f5ca9cb5c256168cf12ce1715ecf Mon Sep 17 00:00:00 2001 From: "David I. Lehn" Date: Wed, 15 May 2019 14:32:29 -0400 Subject: [PATCH 074/197] Fix lint issues. --- lib/aes.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/aes.js b/lib/aes.js index 1ac3e8205..3c1ddb29b 100644 --- a/lib/aes.js +++ b/lib/aes.js @@ -704,7 +704,7 @@ function _expandKey(key, decrypt) { w[i] = w[i - Nk] ^ temp; } - /* When we are updating a cipher block we always use the code path for + /* When we are updating a cipher block we always use the code path for encryption whether we are decrypting or not (to shorten code and simplify the generation of look up tables). However, because there are differences in the decryption algorithm, other than just swapping @@ -1017,7 +1017,7 @@ function _updateBlock(w, input, output, decrypt) { InvSubBytes(state) AddRoundKey(state, w[0, Nb-1]) */ - // Note: rows are shifted inline + // Note: rows are shifted inline output[0] = (sub[a >>> 24] << 24) ^ (sub[b >>> 16 & 255] << 16) ^ From 9b17a28853d58e50a13073f3f38e1929c3fafecc Mon Sep 17 00:00:00 2001 From: "David I. Lehn" Date: Wed, 15 May 2019 15:14:03 -0400 Subject: [PATCH 075/197] Fix karma tests. --- tests/karma/web-worker-rsa.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/karma/web-worker-rsa.js b/tests/karma/web-worker-rsa.js index 492ee4dde..38d156254 100644 --- a/tests/karma/web-worker-rsa.js +++ b/tests/karma/web-worker-rsa.js @@ -10,7 +10,7 @@ function _log(message) { console.log('[main] ' + message); } -describe.only('web worker rsa', function() { +describe('web worker rsa', function() { it('should generate key pairs when running forge in a web worker', function(done) { // Make test worker call rsa.generateKeyPair() on its own side //testWorker.postMessage({type: 'ping'}); From 297f258a36f35ad724a5db6b46a73e690d0ee38e Mon Sep 17 00:00:00 2001 From: "David I. Lehn" Date: Wed, 15 May 2019 15:16:35 -0400 Subject: [PATCH 076/197] Update changelog. --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3c4138e5f..dc5529390 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,9 @@ Forge ChangeLog =============== +### Fixed +- Use basic character set for code. + ## 0.8.2 - 2019-03-18 ### Fixed From 70685e996c9e81fb5722a5ce397084a86ed99048 Mon Sep 17 00:00:00 2001 From: "David I. Lehn" Date: Wed, 15 May 2019 15:16:47 -0400 Subject: [PATCH 077/197] Update dependencies. --- package.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 2dcf30c09..3a0e29485 100644 --- a/package.json +++ b/package.json @@ -16,10 +16,10 @@ ], "devDependencies": { "browserify": "^16.1.0", - "commander": "^2.14.1", + "commander": "^2.20.0", "cross-env": "^5.1.3", - "eslint": "^5.14.1", - "eslint-config-digitalbazaar": "^1.7.0", + "eslint": "^5.16.0", + "eslint-config-digitalbazaar": "^2.0.0", "express": "^4.16.2", "karma": "^3.1.4", "karma-browserify": "^6.0.0", @@ -34,10 +34,10 @@ "karma-sourcemap-loader": "^0.3.7", "karma-tap-reporter": "0.0.6", "karma-webpack": "^3.0.5", - "mocha": "^5.0.1", + "mocha": "^6.1.4", "mocha-lcov-reporter": "^1.2.0", "nodejs-websocket": "^1.7.1", - "nyc": "^13.3.0", + "nyc": "^14.1.1", "opts": "^1.2.2", "webpack": "^3.11.0", "worker-loader": "^2.0.0" From 1463d1813e7814ed5e5ef1eeb048267b690f83f4 Mon Sep 17 00:00:00 2001 From: "David I. Lehn" Date: Wed, 15 May 2019 15:32:44 -0400 Subject: [PATCH 078/197] Revert to mocha@5 for Node.js 4.x support. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 3a0e29485..c8d244672 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,7 @@ "karma-sourcemap-loader": "^0.3.7", "karma-tap-reporter": "0.0.6", "karma-webpack": "^3.0.5", - "mocha": "^6.1.4", + "mocha": "^5.2.0", "mocha-lcov-reporter": "^1.2.0", "nodejs-websocket": "^1.7.1", "nyc": "^14.1.1", From 86dff53db42829473df18cdd3059f065d4eae5cc Mon Sep 17 00:00:00 2001 From: "David I. Lehn" Date: Wed, 15 May 2019 15:38:03 -0400 Subject: [PATCH 079/197] Release 0.8.3. --- CHANGELOG.md | 2 ++ package.json | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dc5529390..914129709 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,8 @@ Forge ChangeLog =============== +## 0.8.3 - 2019-05-15 + ### Fixed - Use basic character set for code. diff --git a/package.json b/package.json index c8d244672..742c0b165 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "node-forge", - "version": "0.8.3-dev", + "version": "0.8.3", "description": "JavaScript implementations of network transports, cryptography, ciphers, PKI, message digests, and various utilities.", "homepage": "https://github.com/digitalbazaar/forge", "author": { From 82356826ff7515fde7cb1d357ad14639feb27889 Mon Sep 17 00:00:00 2001 From: "David I. Lehn" Date: Wed, 15 May 2019 15:38:32 -0400 Subject: [PATCH 080/197] Start 0.8.4. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 742c0b165..a1a87eddd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "node-forge", - "version": "0.8.3", + "version": "0.8.4-dev", "description": "JavaScript implementations of network transports, cryptography, ciphers, PKI, message digests, and various utilities.", "homepage": "https://github.com/digitalbazaar/forge", "author": { From 8de3c0fbbd9dca97518ca0e5003d65111e94ef43 Mon Sep 17 00:00:00 2001 From: andrewjones Date: Mon, 20 May 2019 11:44:02 -0400 Subject: [PATCH 081/197] Deprecate node v6 new Buffer calls in favor of Buffer.from. --- README.md | 8 ++++---- lib/asn1.js | 2 +- lib/ed25519.js | 4 ++-- lib/pbkdf2.js | 4 ++-- tests/benchmarks/so-44303784.js | 6 +++--- tests/unit/ed25519.js | 4 ++-- 6 files changed, 14 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 1408b4864..a0ad924ab 100644 --- a/README.md +++ b/README.md @@ -904,7 +904,7 @@ var signature = ED25519.sign({ // sign a message passed as a buffer var signature = ED25519.sign({ // also accepts a forge ByteBuffer or Uint8Array - message: new Buffer('test', 'utf8'), + message: Buffer.from('test', 'utf8'), privateKey: privateKey }); @@ -930,7 +930,7 @@ var verified = ED25519.verify({ // sign a message passed as a buffer var verified = ED25519.verify({ // also accepts a forge ByteBuffer or Uint8Array - message: new Buffer('test', 'utf8'), + message: Buffer.from('test', 'utf8'), // node.js Buffer, Uint8Array, forge ByteBuffer, or binary string signature: signature, // node.js Buffer, Uint8Array, forge ByteBuffer, or binary string @@ -1961,11 +1961,11 @@ bytes.getBytes(/* count */); // convert a forge buffer into a Node.js Buffer // make sure you specify the encoding as 'binary' var forgeBuffer = forge.util.createBuffer(); -var nodeBuffer = new Buffer(forgeBuffer.getBytes(), 'binary'); +var nodeBuffer = Buffer.from(forgeBuffer.getBytes(), 'binary'); // convert a Node.js Buffer into a forge buffer // make sure you specify the encoding as 'binary' -var nodeBuffer = new Buffer(); +var nodeBuffer = Buffer.alloc(10); var forgeBuffer = forge.util.createBuffer(nodeBuffer.toString('binary')); // parse a URL diff --git a/lib/asn1.js b/lib/asn1.js index af9fe524d..e0fea0e08 100644 --- a/lib/asn1.js +++ b/lib/asn1.js @@ -619,7 +619,7 @@ function _fromDer(bytes, remaining, depth, options) { } // add BIT STRING contents if available - var asn1Options = bitStringContents === undefined ? null : { + var asn1Options = bitStringContents === undefined ? null : { bitStringContents: bitStringContents }; diff --git a/lib/ed25519.js b/lib/ed25519.js index 4054bbff8..c7c992988 100644 --- a/lib/ed25519.js +++ b/lib/ed25519.js @@ -168,7 +168,7 @@ function messageToNativeBuffer(options) { if(typeof message === 'string') { if(typeof Buffer !== 'undefined') { - return new Buffer(message, encoding); + return Buffer.from(message, encoding); } message = new ByteBuffer(message, encoding); } else if(!(message instanceof ByteBuffer)) { @@ -217,7 +217,7 @@ function sha512(msg, msgLen) { md.update(buffer.getBytes(msgLen), 'binary'); var hash = md.digest().getBytes(); if(typeof Buffer !== 'undefined') { - return new Buffer(hash, 'binary'); + return Buffer.from(hash, 'binary'); } var out = new NativeBuffer(ed25519.constants.HASH_BYTE_LENGTH); for(var i = 0; i < 64; ++i) { diff --git a/lib/pbkdf2.js b/lib/pbkdf2.js index ece98850b..714560e38 100644 --- a/lib/pbkdf2.js +++ b/lib/pbkdf2.js @@ -51,8 +51,8 @@ module.exports = forge.pbkdf2 = pkcs5.pbkdf2 = function( // default prf to SHA-1 md = 'sha1'; } - p = new Buffer(p, 'binary'); - s = new Buffer(s, 'binary'); + p = Buffer.from(p, 'binary'); + s = Buffer.from(s, 'binary'); if(!callback) { if(crypto.pbkdf2Sync.length === 4) { return crypto.pbkdf2Sync(p, s, c, dkLen).toString('binary'); diff --git a/tests/benchmarks/so-44303784.js b/tests/benchmarks/so-44303784.js index a7c96581f..05ec9977a 100644 --- a/tests/benchmarks/so-44303784.js +++ b/tests/benchmarks/so-44303784.js @@ -63,9 +63,9 @@ function test_forge_chunk(bytes, chunkSize) { } function test_node(bytes) { - const bufb = new Buffer(bytes, 'binary'); - const ivb = new Buffer(iv, 'binary'); - const keyb = new Buffer(key, 'binary'); + const bufb = Buffer.from(bytes, 'binary'); + const ivb = Buffer.from(iv, 'binary'); + const keyb = Buffer.from(key, 'binary'); const start = new Date(); diff --git a/tests/unit/ed25519.js b/tests/unit/ed25519.js index 6fc9166ed..d6dd0b026 100644 --- a/tests/unit/ed25519.js +++ b/tests/unit/ed25519.js @@ -127,7 +127,7 @@ var UTIL = require('../../lib/util'); var seed = md.digest().getBytes(); var kp = ED25519.generateKeyPair({seed: seed}); var signature = ED25519.sign({ - message: new Buffer('test', 'utf8'), + message: Buffer.from('test', 'utf8'), privateKey: kp.privateKey }); ASSERT.equal(eb64(signature), b64Signature); @@ -179,7 +179,7 @@ var UTIL = require('../../lib/util'); var seed = md.digest().getBytes(); var kp = ED25519.generateKeyPair({seed: seed}); - var signature = new Buffer(db64(b64Signature).getBytes(), 'binary'); + var signature = Buffer.from(db64(b64Signature).getBytes(), 'binary'); var verified = ED25519.verify({ message: 'test', From 737de7de3a570674eeca30e138951af705f0bb34 Mon Sep 17 00:00:00 2001 From: andrewjones Date: Mon, 20 May 2019 11:45:12 -0400 Subject: [PATCH 082/197] Update changelog. --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 914129709..bfeda54ab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,12 @@ Forge ChangeLog =============== + +## 0.8.4 - + +### Removed +- All instances of node new Buffer in favor of Buffer.from and Buffer.alloc + ## 0.8.3 - 2019-05-15 ### Fixed From 7c172e0c221c28595dc25ea41f6b6c430c7ba435 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Mon, 20 May 2019 15:07:28 -0400 Subject: [PATCH 083/197] Make README example more explicit. Co-Authored-By: Max Fichtelmann --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a0ad924ab..5f6dff594 100644 --- a/README.md +++ b/README.md @@ -1965,7 +1965,7 @@ var nodeBuffer = Buffer.from(forgeBuffer.getBytes(), 'binary'); // convert a Node.js Buffer into a forge buffer // make sure you specify the encoding as 'binary' -var nodeBuffer = Buffer.alloc(10); +var nodeBuffer = Buffer.from('CAFE', 'hex'); var forgeBuffer = forge.util.createBuffer(nodeBuffer.toString('binary')); // parse a URL From 2a9ee9acbf0fe7934e1f1613605d3766e02c1fc8 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Mon, 20 May 2019 15:08:07 -0400 Subject: [PATCH 084/197] Update Changelog with `highlights` for remove section. Co-Authored-By: David I. Lehn --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bfeda54ab..a5e49c69d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ Forge ChangeLog ## 0.8.4 - ### Removed -- All instances of node new Buffer in favor of Buffer.from and Buffer.alloc +- All instances of Node.js `new Buffer` in favor of `Buffer.from` and `Buffer.alloc`. ## 0.8.3 - 2019-05-15 From 063bc493649a6c9117068b894cbdbd4dd5c572ed Mon Sep 17 00:00:00 2001 From: andrewjones Date: Mon, 20 May 2019 16:27:55 -0400 Subject: [PATCH 085/197] Set engine node to >= 4.5.0. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a1a87eddd..2549cddbb 100644 --- a/package.json +++ b/package.json @@ -59,7 +59,7 @@ "dist/*.min.js.map" ], "engines": { - "node": "*" + "node": ">= 4.5.0" }, "keywords": [ "aes", From e64a575fecbb6b99af19861ad611b94733cee778 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Wed, 22 May 2019 14:57:09 -0400 Subject: [PATCH 086/197] Update CHANGELOG.md Co-Authored-By: Dave Longley --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a5e49c69d..1403f1326 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ Forge ChangeLog ## 0.8.4 - ### Removed -- All instances of Node.js `new Buffer` in favor of `Buffer.from` and `Buffer.alloc`. +- Replace all instances of Node.js `new Buffer` with `Buffer.from` and `Buffer.alloc`. ## 0.8.3 - 2019-05-15 From 2fa16b0ebe38173ccdadf1a32edc389d006df50b Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Wed, 22 May 2019 14:57:34 -0400 Subject: [PATCH 087/197] Update Removed to Changed in CHANGELOG.md. Co-Authored-By: Dave Longley --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1403f1326..adc4a1d77 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ Forge ChangeLog ## 0.8.4 - -### Removed +### Changed - Replace all instances of Node.js `new Buffer` with `Buffer.from` and `Buffer.alloc`. ## 0.8.3 - 2019-05-15 From 1aaf497a7c2866fd5f01cf43678073f619681fbd Mon Sep 17 00:00:00 2001 From: "David I. Lehn" Date: Wed, 22 May 2019 16:25:39 -0400 Subject: [PATCH 088/197] Release 0.8.4. --- CHANGELOG.md | 3 +-- package.json | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index adc4a1d77..6c1a735f6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,7 @@ Forge ChangeLog =============== - -## 0.8.4 - +## 0.8.4 - 2019-05-22 ### Changed - Replace all instances of Node.js `new Buffer` with `Buffer.from` and `Buffer.alloc`. diff --git a/package.json b/package.json index 2549cddbb..7057afce9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "node-forge", - "version": "0.8.4-dev", + "version": "0.8.4", "description": "JavaScript implementations of network transports, cryptography, ciphers, PKI, message digests, and various utilities.", "homepage": "https://github.com/digitalbazaar/forge", "author": { From 392c222d161038f623632a863f763441b56c8511 Mon Sep 17 00:00:00 2001 From: "David I. Lehn" Date: Wed, 22 May 2019 16:26:04 -0400 Subject: [PATCH 089/197] Start 0.8.5. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 7057afce9..15c605d3c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "node-forge", - "version": "0.8.4", + "version": "0.8.5-dev", "description": "JavaScript implementations of network transports, cryptography, ciphers, PKI, message digests, and various utilities.", "homepage": "https://github.com/digitalbazaar/forge", "author": { From 8753559e3881e4fe7db8bcc1d19003b2f5be7baa Mon Sep 17 00:00:00 2001 From: Chanbong Park Date: Tue, 18 Jun 2019 13:34:53 +0900 Subject: [PATCH 090/197] change 'const' to 'var' --- lib/x509.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/x509.js b/lib/x509.js index 3bfbc188c..95dbc2946 100644 --- a/lib/x509.js +++ b/lib/x509.js @@ -2482,8 +2482,8 @@ function _CRIAttributesToAsn1(csr) { return rval; } -const jan_1_1950 = new Date('1950-01-01T00:00:00Z'); -const jan_1_2050 = new Date('2050-01-01T00:00:00Z'); +var jan_1_1950 = new Date('1950-01-01T00:00:00Z'); +var jan_1_2050 = new Date('2050-01-01T00:00:00Z'); /** * Converts a Date object to ASN.1 From 3916f83fc81a8002af1ca93d6f83e1e44552bb06 Mon Sep 17 00:00:00 2001 From: "David I. Lehn" Date: Tue, 18 Jun 2019 21:02:40 -0400 Subject: [PATCH 091/197] Update changelog. --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6c1a735f6..a30fcfcc9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,9 @@ Forge ChangeLog =============== +### Fixed +- Remove use of `const`. + ## 0.8.4 - 2019-05-22 ### Changed From 7192006e1916ce87498f6ef9bd86c080a5c48614 Mon Sep 17 00:00:00 2001 From: "David I. Lehn" Date: Tue, 18 Jun 2019 21:05:22 -0400 Subject: [PATCH 092/197] Release 0.8.5. --- CHANGELOG.md | 2 ++ package.json | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a30fcfcc9..632905f6a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,8 @@ Forge ChangeLog =============== +## 0.8.5 - 2019-06-18 + ### Fixed - Remove use of `const`. diff --git a/package.json b/package.json index 15c605d3c..5db4693e6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "node-forge", - "version": "0.8.5-dev", + "version": "0.8.5", "description": "JavaScript implementations of network transports, cryptography, ciphers, PKI, message digests, and various utilities.", "homepage": "https://github.com/digitalbazaar/forge", "author": { From 292172949edc891d1e6e0c8f40444751798baee9 Mon Sep 17 00:00:00 2001 From: "David I. Lehn" Date: Tue, 18 Jun 2019 21:05:41 -0400 Subject: [PATCH 093/197] Start 0.8.6. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 5db4693e6..e03f4bbd5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "node-forge", - "version": "0.8.5", + "version": "0.8.6-dev", "description": "JavaScript implementations of network transports, cryptography, ciphers, PKI, message digests, and various utilities.", "homepage": "https://github.com/digitalbazaar/forge", "author": { From 248549ab4a6ca81fd78e0572123c82c284ee4bcb Mon Sep 17 00:00:00 2001 From: Dave Longley Date: Thu, 27 Jun 2019 10:10:22 -0400 Subject: [PATCH 094/197] Clarify custom password example in README. --- README.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 5f6dff594..40bf29561 100644 --- a/README.md +++ b/README.md @@ -1409,15 +1409,17 @@ var privateKeyInfo = pki.wrapRsaPrivateKey(rsaPrivateKey); // convert a PKCS#8 ASN.1 PrivateKeyInfo to PEM var pem = pki.privateKeyInfoToPem(privateKeyInfo); -// encrypts a PrivateKeyInfo and outputs an EncryptedPrivateKeyInfo +// encrypts a PrivateKeyInfo using a custom password and +// outputs an EncryptedPrivateKeyInfo var encryptedPrivateKeyInfo = pki.encryptPrivateKeyInfo( - privateKeyInfo, 'password', { + privateKeyInfo, 'myCustomPasswordHere', { algorithm: 'aes256', // 'aes128', 'aes192', 'aes256', '3des' }); -// decrypts an ASN.1 EncryptedPrivateKeyInfo +// decrypts an ASN.1 EncryptedPrivateKeyInfo that was encrypted +// with a custom password var privateKeyInfo = pki.decryptPrivateKeyInfo( - encryptedPrivateKeyInfo, 'password'); + encryptedPrivateKeyInfo, 'myCustomPasswordHere'); // converts an EncryptedPrivateKeyInfo to PEM var pem = pki.encryptedPrivateKeyToPem(encryptedPrivateKeyInfo); From dd5d972b1cc6faf225afda3fc04093bd93cad16a Mon Sep 17 00:00:00 2001 From: Dave Longley Date: Sat, 13 Jul 2019 16:39:30 -0400 Subject: [PATCH 095/197] Update some very old and incorrect encoding docs. --- lib/util.js | 55 ++++++++++++++++++++++++++++++----------------------- 1 file changed, 31 insertions(+), 24 deletions(-) diff --git a/lib/util.js b/lib/util.js index a67ee5182..a86609284 100644 --- a/lib/util.js +++ b/lib/util.js @@ -279,7 +279,7 @@ util.ByteStringBuffer.prototype.fillWithByte = function(b, n) { /** * Puts bytes in this buffer. * - * @param bytes the bytes (as a UTF-8 encoded string) to put. + * @param bytes the bytes (as a binary encoded string) to put. * * @return this buffer. */ @@ -567,11 +567,13 @@ util.ByteStringBuffer.prototype.getSignedInt = function(n) { }; /** - * Reads bytes out into a UTF-8 string and clears them from the buffer. + * Reads bytes out as a binary encoded string and clears them from the + * buffer. Note that the resulting string is binary encoded (in node.js this + * encoding is referred to as `binary`, it is *not* `utf8`). * * @param count the number of bytes to read, undefined or null for all. * - * @return a UTF-8 string of bytes. + * @return a binary encoded string of bytes. */ util.ByteStringBuffer.prototype.getBytes = function(count) { var rval; @@ -591,12 +593,12 @@ util.ByteStringBuffer.prototype.getBytes = function(count) { }; /** - * Gets a UTF-8 encoded string of the bytes from this buffer without modifying - * the read pointer. + * Gets a binary encoded string of the bytes from this buffer without + * modifying the read pointer. * * @param count the number of bytes to get, omit to get all. * - * @return a string full of UTF-8 encoded characters. + * @return a string full of binary encoded characters. */ util.ByteStringBuffer.prototype.bytes = function(count) { return (typeof(count) === 'undefined' ? @@ -1228,11 +1230,12 @@ util.DataBuffer.prototype.getSignedInt = function(n) { }; /** - * Reads bytes out into a UTF-8 string and clears them from the buffer. + * Reads bytes out as a binary encoded string and clears them from the + * buffer. * * @param count the number of bytes to read, undefined or null for all. * - * @return a UTF-8 string of bytes. + * @return a binary encoded string of bytes. */ util.DataBuffer.prototype.getBytes = function(count) { // TODO: deprecate this method, it is poorly named and @@ -1255,12 +1258,12 @@ util.DataBuffer.prototype.getBytes = function(count) { }; /** - * Gets a UTF-8 encoded string of the bytes from this buffer without modifying - * the read pointer. + * Gets a binary encoded string of the bytes from this buffer without + * modifying the read pointer. * * @param count the number of bytes to get, omit to get all. * - * @return a string full of UTF-8 encoded characters. + * @return a string full of binary encoded characters. */ util.DataBuffer.prototype.bytes = function(count) { // TODO: deprecate this method, it is poorly named, add "getString()" @@ -1407,12 +1410,13 @@ util.DataBuffer.prototype.toString = function(encoding) { /** End Buffer w/UInt8Array backing */ /** - * Creates a buffer that stores bytes. A value may be given to put into the - * buffer that is either a string of bytes or a UTF-16 string that will - * be encoded using UTF-8 (to do the latter, specify 'utf8' as the encoding). + * Creates a buffer that stores bytes. A value may be given to populate the + * buffer with data. This value can either be string of encoded bytes or a + * regular string of characters. When passing a string of binary encoded + * bytes, the encoding `raw` should be given. This is also the default. When + * passing a string of characters, the encoding `utf8` should be given. * - * @param [input] the bytes to wrap (as a string) or a UTF-16 string to encode - * as UTF-8. + * @param [input] a string with encoded bytes to store in the buffer. * @param [encoding] (default: 'raw', other: 'utf8'). */ util.createBuffer = function(input, encoding) { @@ -1641,24 +1645,27 @@ util.decode64 = function(input) { }; /** - * UTF-8 encodes the given UTF-16 encoded string (a standard JavaScript - * string). Non-ASCII characters will be encoded as multiple bytes according - * to UTF-8. + * Encodes the given string of characters (a standard JavaScript + * string) as a binary encoded string where the bytes represent + * a UTF-8 encoded string of characters. Non-ASCII characters will be + * encoded as multiple bytes according to UTF-8. * - * @param str the string to encode. + * @param str a standard string of characters to encode. * - * @return the UTF-8 encoded string. + * @return the binary encoded string. */ util.encodeUtf8 = function(str) { return unescape(encodeURIComponent(str)); }; /** - * Decodes a UTF-8 encoded string into a UTF-16 string. + * Decodes a binary encoded string that contains bytes that + * represent a UTF-8 encoded string of characters -- into a + * string of characters (a standard JavaScript string). * - * @param str the string to decode. + * @param str the binary encoded string to decode. * - * @return the UTF-16 encoded string (standard JavaScript string). + * @return the resulting standard string of characters. */ util.decodeUtf8 = function(str) { return decodeURIComponent(escape(str)); From 7e833b918c10c84494285c082a22e011fde86932 Mon Sep 17 00:00:00 2001 From: Matthew Collier Date: Tue, 16 Jul 2019 12:09:45 -0400 Subject: [PATCH 096/197] Add oid for EdDSA25519. --- lib/oids.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/oids.js b/lib/oids.js index 328ebd36b..553c41efc 100644 --- a/lib/oids.js +++ b/lib/oids.js @@ -34,6 +34,8 @@ _IN('1.2.840.113549.1.1.10', 'RSASSA-PSS'); _IN('1.2.840.113549.1.1.11', 'sha256WithRSAEncryption'); _IN('1.2.840.113549.1.1.12', 'sha384WithRSAEncryption'); _IN('1.2.840.113549.1.1.13', 'sha512WithRSAEncryption'); +// Edwards-curve Digital Signature Algorithm (EdDSA) Ed25519 +_IN('1.3.101.112', 'EdDSA25519'); _IN('1.2.840.10040.4.3', 'dsa-with-sha1'); From 183a4cba57e53b837e7d8db0e4829e155f4d8c49 Mon Sep 17 00:00:00 2001 From: Matthew Collier Date: Mon, 22 Jul 2019 16:02:29 -0400 Subject: [PATCH 097/197] Implement ed25519.publicKeyFromAsn1. --- lib/asn1-validator.js | 91 +++++++++++++++++++++++++++++++++++++++++++ lib/ed25519.js | 41 +++++++++++++++++++ 2 files changed, 132 insertions(+) create mode 100644 lib/asn1-validator.js diff --git a/lib/asn1-validator.js b/lib/asn1-validator.js new file mode 100644 index 000000000..2be328501 --- /dev/null +++ b/lib/asn1-validator.js @@ -0,0 +1,91 @@ +/** + * Copyright (c) 2019 Digital Bazaar, Inc. + */ + +var forge = require('./forge'); +require('./asn1'); +var asn1 = forge.asn1; + +exports.privateKeyValidator = { + // PrivateKeyInfo + name: 'PrivateKeyInfo', + tagClass: asn1.Class.UNIVERSAL, + type: asn1.Type.SEQUENCE, + constructed: true, + value: [{ + // Version (INTEGER) + name: 'PrivateKeyInfo.version', + tagClass: asn1.Class.UNIVERSAL, + type: asn1.Type.INTEGER, + constructed: false, + capture: 'privateKeyVersion' + }, { + // privateKeyAlgorithm + name: 'PrivateKeyInfo.privateKeyAlgorithm', + tagClass: asn1.Class.UNIVERSAL, + type: asn1.Type.SEQUENCE, + constructed: true, + value: [{ + name: 'AlgorithmIdentifier.algorithm', + tagClass: asn1.Class.UNIVERSAL, + type: asn1.Type.OID, + constructed: false, + capture: 'privateKeyOid' + }] + }, { + // PrivateKey + name: 'PrivateKeyInfo', + tagClass: asn1.Class.UNIVERSAL, + type: asn1.Type.OCTETSTRING, + constructed: false, + capture: 'privateKey' + }] +}; + +exports.publicKeyValidator = { + name: 'SubjectPublicKeyInfo', + tagClass: asn1.Class.UNIVERSAL, + type: asn1.Type.SEQUENCE, + constructed: true, + captureAsn1: 'subjectPublicKeyInfo', + value: [{ + name: 'SubjectPublicKeyInfo.AlgorithmIdentifier', + tagClass: asn1.Class.UNIVERSAL, + type: asn1.Type.SEQUENCE, + constructed: true, + value: [{ + name: 'AlgorithmIdentifier.algorithm', + tagClass: asn1.Class.UNIVERSAL, + type: asn1.Type.OID, + constructed: false, + capture: 'publicKeyOid' + }] + }, + // capture group for ed25519PublicKey + { + tagClass: asn1.Class.UNIVERSAL, + type: asn1.Type.BITSTRING, + constructed: false, + composed: true, + captureBitStringValue: 'ed25519PublicKey' + } + // FIXME: this is capture group for rsaPublicKey, use it in this API or + // discard? + /* { + // subjectPublicKey + name: 'SubjectPublicKeyInfo.subjectPublicKey', + tagClass: asn1.Class.UNIVERSAL, + type: asn1.Type.BITSTRING, + constructed: false, + value: [{ + // RSAPublicKey + name: 'SubjectPublicKeyInfo.subjectPublicKey.RSAPublicKey', + tagClass: asn1.Class.UNIVERSAL, + type: asn1.Type.SEQUENCE, + constructed: true, + optional: true, + captureAsn1: 'rsaPublicKey' + }] + } */ + ] +}; diff --git a/lib/ed25519.js b/lib/ed25519.js index c7c992988..5decd46da 100644 --- a/lib/ed25519.js +++ b/lib/ed25519.js @@ -13,6 +13,9 @@ require('./jsbn'); require('./random'); require('./sha512'); require('./util'); +var asn1Validator = require('./asn1-validator'); +var publicKeyValidator = asn1Validator.publicKeyValidator; +var privateKeyValidator = asn1Validator.privateKeyValidator; if(typeof BigInteger === 'undefined') { var BigInteger = forge.jsbn.BigInteger; @@ -64,6 +67,44 @@ ed25519.generateKeyPair = function(options) { return {publicKey: pk, privateKey: sk}; }; +ed25519.privateKeyFromAsn1 = function() { + // TODO: implement +}; + +/** + * Converts a public key from an ASN.1 SubjectPublicKeyInfo. + * + * @param obj the asn1 representation of a SubjectPublicKeyInfo. + * + * @return the public key. + */ +ed25519.publicKeyFromAsn1 = function(obj) { + // get SubjectPublicKeyInfo + var capture = {}; + var errors = []; + var valid = forge.asn1.validate(obj, publicKeyValidator, capture, errors); + if(!valid) { + var error = new Error('Invalid Key.'); + error.errors = errors; + throw error; + } + var oid = forge.asn1.derToOid(capture.publicKeyOid); + var ed25519Oid = forge.oids.EdDSA25519; + if(oid !== ed25519Oid) { + throw new Error('Invalid OID "' + oid + '"; OID must be "' + + ed25519Oid + '".'); + } + var publicKeyBytes = capture.ed25519PublicKey; + if(publicKeyBytes.length !== ed25519.constants.PUBLIC_KEY_BYTE_LENGTH) { + throw new Error('Key length is invalid.'); + } + // FIXME: what should be returned from this API? + return messageToNativeBuffer({ + message: publicKeyBytes, + encoding: 'binary' + }); +}; + ed25519.publicKeyFromPrivateKey = function(options) { options = options || {}; var privateKey = messageToNativeBuffer({ From e98643536614626030c8ad67f1ef6a3c205bb8b2 Mon Sep 17 00:00:00 2001 From: Matthew Collier Date: Mon, 22 Jul 2019 17:25:00 -0400 Subject: [PATCH 098/197] Implement ed25519.privateKeyFromAsn1. --- lib/ed25519.js | 43 +++++++++++++++++++++++++++++++++++++------ 1 file changed, 37 insertions(+), 6 deletions(-) diff --git a/lib/ed25519.js b/lib/ed25519.js index 5decd46da..8dbe21c89 100644 --- a/lib/ed25519.js +++ b/lib/ed25519.js @@ -67,16 +67,48 @@ ed25519.generateKeyPair = function(options) { return {publicKey: pk, privateKey: sk}; }; -ed25519.privateKeyFromAsn1 = function() { - // TODO: implement +/** + * Converts a private key from a RFC8410 ASN.1 encoding. + * + * @param obj - The asn1 representation of a private key. + * + * @returns {Object} keyInfo - The key information. + * @returns {Buffer|Uint8Array} keyInfo.privateKeyBytes - 32 private key bytes. + */ +ed25519.privateKeyFromAsn1 = function(obj) { + var capture = {}; + var errors = []; + var valid = forge.asn1.validate(obj, privateKeyValidator, capture, errors); + if(!valid) { + var error = new Error('Invalid Key.'); + error.errors = errors; + throw error; + } + var oid = forge.asn1.derToOid(capture.privateKeyOid); + var ed25519Oid = forge.oids.EdDSA25519; + if(oid !== ed25519Oid) { + throw new Error('Invalid OID "' + oid + '"; OID must be "' + + ed25519Oid + '".'); + } + var privateKey = capture.privateKey; + // manually extract the private key bytes from nested octet string, see FIXME: + // https://github.com/digitalbazaar/forge/blob/master/lib/asn1.js#L542 + var privateKeyBytes = messageToNativeBuffer({ + message: forge.asn1.fromDer(privateKey).value, + encoding: 'binary' + }); + // TODO: RFC8410 specifies a format for encoding the public key bytes along + // with the private key bytes. `publicKeyBytes` can be returned in the + // future. https://tools.ietf.org/html/rfc8410#section-10.3 + return {privateKeyBytes: privateKeyBytes}; }; /** - * Converts a public key from an ASN.1 SubjectPublicKeyInfo. + * Converts a public key from a RFC8410 ASN.1 encoding. * - * @param obj the asn1 representation of a SubjectPublicKeyInfo. + * @param obj - The asn1 representation of a public key. * - * @return the public key. + * @return {Buffer|Uint8Array} - 32 public key bytes. */ ed25519.publicKeyFromAsn1 = function(obj) { // get SubjectPublicKeyInfo @@ -98,7 +130,6 @@ ed25519.publicKeyFromAsn1 = function(obj) { if(publicKeyBytes.length !== ed25519.constants.PUBLIC_KEY_BYTE_LENGTH) { throw new Error('Key length is invalid.'); } - // FIXME: what should be returned from this API? return messageToNativeBuffer({ message: publicKeyBytes, encoding: 'binary' From ddd1f061e45d08a92c34362c8ee781827c1dce72 Mon Sep 17 00:00:00 2001 From: Matthew Collier Date: Wed, 4 Sep 2019 16:25:24 -0400 Subject: [PATCH 099/197] Accept 32 byte private key in sign function. --- lib/ed25519.js | 6 +++++- tests/unit/ed25519.js | 16 ++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/lib/ed25519.js b/lib/ed25519.js index 8dbe21c89..9f4a5df68 100644 --- a/lib/ed25519.js +++ b/lib/ed25519.js @@ -161,9 +161,13 @@ ed25519.sign = function(options) { message: options.privateKey, encoding: 'binary' }); - if(privateKey.length !== ed25519.constants.PRIVATE_KEY_BYTE_LENGTH) { + if(privateKey.length === ed25519.constants.SEED_BYTE_LENGTH) { + var keyPair = ed25519.generateKeyPair({seed: privateKey}); + privateKey = keyPair.privateKey; + } else if(privateKey.length !== ed25519.constants.PRIVATE_KEY_BYTE_LENGTH) { throw new TypeError( '"options.privateKey" must have a byte length of ' + + ed25519.constants.SEED_BYTE_LENGTH + ' or ' + ed25519.constants.PRIVATE_KEY_BYTE_LENGTH); } diff --git a/tests/unit/ed25519.js b/tests/unit/ed25519.js index d6dd0b026..a7af3ddac 100644 --- a/tests/unit/ed25519.js +++ b/tests/unit/ed25519.js @@ -60,6 +60,22 @@ var UTIL = require('../../lib/util'); ASSERT.equal(eb64(signature), b64Sha256Signature); }); + it('should sign a digest given 32 private key bytes', function() { + var pwd = 'password'; + var md = SHA256.create(); + md.update(pwd, 'utf8'); + var seed = md.digest().getBytes(); + var kp = ED25519.generateKeyPair({seed: seed}); + md = SHA256.create(); + md.update('test', 'utf8'); + var privateKey = kp.privateKey.slice(0, 32); + var signature = ED25519.sign({ + md: md, + privateKey: privateKey + }); + ASSERT.equal(eb64(signature), b64Sha256Signature); + }); + it('should sign a UTF-8 message', function() { var pwd = 'password'; var md = SHA256.create(); From 7f9bcfbac2ce786fe4cce20cfa1f9f98fe1608eb Mon Sep 17 00:00:00 2001 From: Matthew Collier Date: Wed, 4 Sep 2019 17:32:48 -0400 Subject: [PATCH 100/197] Update copyright notice. --- lib/ed25519.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ed25519.js b/lib/ed25519.js index 9f4a5df68..05d2b9439 100644 --- a/lib/ed25519.js +++ b/lib/ed25519.js @@ -1,7 +1,7 @@ /** * JavaScript implementation of Ed25519. * - * Copyright (c) 2017-2018 Digital Bazaar, Inc. + * Copyright (c) 2017-2019 Digital Bazaar, Inc. * * This implementation is based on the most excellent TweetNaCl which is * in the public domain. Many thanks to its contributors: From 7e879e75ebf0e3b455f21a0ebaf12e13bbd0f5f2 Mon Sep 17 00:00:00 2001 From: Matthew Collier Date: Wed, 4 Sep 2019 18:15:05 -0400 Subject: [PATCH 101/197] Update changelog. --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 632905f6a..352ef3594 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,11 @@ Forge ChangeLog =============== +## 0.9.0 - TBD + +### Added +- Add ed25519.publicKeyFromAsn1 and ed25519.privateKeyFromAsn1 APIs. + ## 0.8.5 - 2019-06-18 ### Fixed From 19906347b415d1deaf513334e01a54da82dd0675 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20D=C3=BCsterhus?= Date: Tue, 23 Jul 2019 14:11:16 +0200 Subject: [PATCH 102/197] Check for NativeBuffer in ed25519#messageToNativeBuffer While this technically is not necessary, because node.js' Buffer inherits from Uint8Array this contract is broken by jest. See facebook/jest#4422. Add a `message instanceof NativeBuffer` to the function to fix the issue, it does no harm and *technically* would be the more correct check, because for node.js passing a raw Uint8Array does not return a Buffer. --- lib/ed25519.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ed25519.js b/lib/ed25519.js index 05d2b9439..f3e6faaaa 100644 --- a/lib/ed25519.js +++ b/lib/ed25519.js @@ -223,7 +223,7 @@ ed25519.verify = function(options) { function messageToNativeBuffer(options) { var message = options.message; - if(message instanceof Uint8Array) { + if(message instanceof Uint8Array || message instanceof NativeBuffer) { return message; } From c3d6c9a175b52949e5edbb0da221f6bfccddbc47 Mon Sep 17 00:00:00 2001 From: Anton Ku Date: Fri, 9 Aug 2019 23:09:48 +0300 Subject: [PATCH 103/197] Add OIDs for streetAddress, businessCategory, postalCode, jurisdictionST and jurisdictionC --- lib/oids.js | 5 + tests/unit/x509.js | 225 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 230 insertions(+) diff --git a/lib/oids.js b/lib/oids.js index 553c41efc..6a937f571 100644 --- a/lib/oids.js +++ b/lib/oids.js @@ -108,9 +108,14 @@ _IN('2.5.4.5', 'serialName'); _IN('2.5.4.6', 'countryName'); _IN('2.5.4.7', 'localityName'); _IN('2.5.4.8', 'stateOrProvinceName'); +_IN('2.5.4.9', 'streetAddress'); _IN('2.5.4.10', 'organizationName'); _IN('2.5.4.11', 'organizationalUnitName'); _IN('2.5.4.13', 'description'); +_IN('2.5.4.15', 'businessCategory'); +_IN('2.5.4.17', 'postalCode'); +_IN('1.3.6.1.4.1.311.60.2.1.2', 'jurisdictionOfIncorporationStateOrProvinceName'); +_IN('1.3.6.1.4.1.311.60.2.1.3', 'jurisdictionOfIncorporationCountryName'); // X.509 extension OIDs _IN('2.16.840.1.113730.1.1', 'nsCertType'); diff --git a/tests/unit/x509.js b/tests/unit/x509.js index eb060dcbc..e382c4520 100644 --- a/tests/unit/x509.js +++ b/tests/unit/x509.js @@ -481,6 +481,231 @@ var UTIL = require('../../lib/util'); }); }); + it('should generate a certificate with postalCode attribute', function() { + var keys = { + privateKey: PKI.privateKeyFromPem(_pem.privateKey), + publicKey: PKI.publicKeyFromPem(_pem.publicKey) + }; + var attrs = [{ + name: 'commonName', + value: 'example.org' + }, { + name: 'countryName', + value: 'US' + }, { + shortName: 'ST', + value: 'Virginia' + }, { + name: 'localityName', + value: 'Blacksburg' + }, { + name: 'organizationName', + value: 'Test' + }, { + shortName: 'OU', + value: 'Test' + }, { + name: 'postalCode', + value: '1234' + }]; + var cert = createCertificate({ + publicKey: keys.publicKey, + signingKey: keys.privateKey, + serialNumber: '01', + subject: attrs, + issuer: attrs, + isCA: true + }); + + var pem = PKI.certificateToPem(cert); + cert = PKI.certificateFromPem(pem); + var index = findIndex(cert.subject.attributes, {type: '2.5.4.17'}); + ASSERT.ok(index !== -1); + var attribute = cert.subject.attributes[index]; + ASSERT.equal(attribute.name, 'postalCode'); + ASSERT.equal(attribute.value, '1234'); + }); + + it('should generate a certificate with businessCategory attribute', function() { + var keys = { + privateKey: PKI.privateKeyFromPem(_pem.privateKey), + publicKey: PKI.publicKeyFromPem(_pem.publicKey) + }; + var attrs = [{ + name: 'commonName', + value: 'example.org' + }, { + name: 'countryName', + value: 'US' + }, { + shortName: 'ST', + value: 'Virginia' + }, { + name: 'localityName', + value: 'Blacksburg' + }, { + name: 'organizationName', + value: 'Test' + }, { + shortName: 'OU', + value: 'Test' + }, { + name: 'businessCategory', + value: 'Test Organization' + }]; + var cert = createCertificate({ + publicKey: keys.publicKey, + signingKey: keys.privateKey, + serialNumber: '01', + subject: attrs, + issuer: attrs, + isCA: true + }); + + var pem = PKI.certificateToPem(cert); + cert = PKI.certificateFromPem(pem); + var index = findIndex(cert.subject.attributes, {type: '2.5.4.15'}); + ASSERT.ok(index !== -1); + var attribute = cert.subject.attributes[index]; + ASSERT.equal(attribute.name, 'businessCategory'); + ASSERT.equal(attribute.value, 'Test Organization'); + }); + + it('should generate a certificate with streetAddress attribute', function() { + var keys = { + privateKey: PKI.privateKeyFromPem(_pem.privateKey), + publicKey: PKI.publicKeyFromPem(_pem.publicKey) + }; + var attrs = [{ + name: 'commonName', + value: 'example.org' + }, { + name: 'countryName', + value: 'US' + }, { + shortName: 'ST', + value: 'Virginia' + }, { + name: 'localityName', + value: 'Blacksburg' + }, { + name: 'organizationName', + value: 'Test' + }, { + shortName: 'OU', + value: 'Test' + }, { + name: 'streetAddress', + value: 'Test Avenue' + }]; + var cert = createCertificate({ + publicKey: keys.publicKey, + signingKey: keys.privateKey, + serialNumber: '01', + subject: attrs, + issuer: attrs, + isCA: true + }); + + var pem = PKI.certificateToPem(cert); + cert = PKI.certificateFromPem(pem); + var index = findIndex(cert.subject.attributes, {type: '2.5.4.9'}); + ASSERT.ok(index !== -1); + var attribute = cert.subject.attributes[index]; + ASSERT.equal(attribute.name, 'streetAddress'); + ASSERT.equal(attribute.value, 'Test Avenue'); + }); + + it('should generate a certificate with jurisdictionOfIncorporationStateOrProvinceName attribute', function() { + var keys = { + privateKey: PKI.privateKeyFromPem(_pem.privateKey), + publicKey: PKI.publicKeyFromPem(_pem.publicKey) + }; + var attrs = [{ + name: 'commonName', + value: 'example.org' + }, { + name: 'countryName', + value: 'US' + }, { + shortName: 'ST', + value: 'Virginia' + }, { + name: 'localityName', + value: 'Blacksburg' + }, { + name: 'organizationName', + value: 'Test' + }, { + shortName: 'OU', + value: 'Test' + }, { + name: 'jurisdictionOfIncorporationStateOrProvinceName', + value: 'Delaware' + }]; + var cert = createCertificate({ + publicKey: keys.publicKey, + signingKey: keys.privateKey, + serialNumber: '01', + subject: attrs, + issuer: attrs, + isCA: true + }); + + var pem = PKI.certificateToPem(cert); + cert = PKI.certificateFromPem(pem); + var index = findIndex(cert.subject.attributes, {type: '1.3.6.1.4.1.311.60.2.1.2'}); + ASSERT.ok(index !== -1); + var attribute = cert.subject.attributes[index]; + ASSERT.equal(attribute.name, 'jurisdictionOfIncorporationStateOrProvinceName'); + ASSERT.equal(attribute.value, 'Delaware'); + }); + + it('should generate a certificate with jurisdictionOfIncorporationCountryName attribute', function() { + var keys = { + privateKey: PKI.privateKeyFromPem(_pem.privateKey), + publicKey: PKI.publicKeyFromPem(_pem.publicKey) + }; + var attrs = [{ + name: 'commonName', + value: 'example.org' + }, { + name: 'countryName', + value: 'US' + }, { + shortName: 'ST', + value: 'Virginia' + }, { + name: 'localityName', + value: 'Blacksburg' + }, { + name: 'organizationName', + value: 'Test' + }, { + shortName: 'OU', + value: 'Test' + }, { + name: 'jurisdictionOfIncorporationCountryName', + value: 'US' + }]; + var cert = createCertificate({ + publicKey: keys.publicKey, + signingKey: keys.privateKey, + serialNumber: '01', + subject: attrs, + issuer: attrs, + isCA: true + }); + + var pem = PKI.certificateToPem(cert); + cert = PKI.certificateFromPem(pem); + var index = findIndex(cert.subject.attributes, {type: '1.3.6.1.4.1.311.60.2.1.3'}); + ASSERT.ok(index !== -1); + var attribute = cert.subject.attributes[index]; + ASSERT.equal(attribute.name, 'jurisdictionOfIncorporationCountryName'); + ASSERT.equal(attribute.value, 'US'); + }); + it('should generate and verify a self-signed certificate', function() { var keys = { privateKey: PKI.privateKeyFromPem(_pem.privateKey), From 2a9ad37f869fd8566a689eb51054508168749b4b Mon Sep 17 00:00:00 2001 From: "David I. Lehn" Date: Wed, 4 Sep 2019 22:20:13 -0400 Subject: [PATCH 104/197] Fix test postal code. --- tests/unit/x509.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/unit/x509.js b/tests/unit/x509.js index e382c4520..43a9ea61b 100644 --- a/tests/unit/x509.js +++ b/tests/unit/x509.js @@ -506,7 +506,7 @@ var UTIL = require('../../lib/util'); value: 'Test' }, { name: 'postalCode', - value: '1234' + value: '24060' }]; var cert = createCertificate({ publicKey: keys.publicKey, @@ -523,7 +523,7 @@ var UTIL = require('../../lib/util'); ASSERT.ok(index !== -1); var attribute = cert.subject.attributes[index]; ASSERT.equal(attribute.name, 'postalCode'); - ASSERT.equal(attribute.value, '1234'); + ASSERT.equal(attribute.value, '24060'); }); it('should generate a certificate with businessCategory attribute', function() { From 958372b3b56845bc66048c86050dd780ce5a59a0 Mon Sep 17 00:00:00 2001 From: "David I. Lehn" Date: Wed, 4 Sep 2019 22:33:40 -0400 Subject: [PATCH 105/197] Update changelog. --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 352ef3594..f2a688449 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,10 @@ Forge ChangeLog ### Added - Add ed25519.publicKeyFromAsn1 and ed25519.privateKeyFromAsn1 APIs. +- A few OIDs used in EV certs. + +### Fixed +- Improve ed25519 NativeBuffer check. ## 0.8.5 - 2019-06-18 From 5513f7788764fd9c8e21523fa6ab40a3928f2ed6 Mon Sep 17 00:00:00 2001 From: "David I. Lehn" Date: Wed, 4 Sep 2019 22:35:01 -0400 Subject: [PATCH 106/197] Release 0.9.0. --- CHANGELOG.md | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f2a688449..370fe3480 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,7 @@ Forge ChangeLog =============== -## 0.9.0 - TBD +## 0.9.0 - 2019-09-04 ### Added - Add ed25519.publicKeyFromAsn1 and ed25519.privateKeyFromAsn1 APIs. diff --git a/package.json b/package.json index e03f4bbd5..ab3f2cef3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "node-forge", - "version": "0.8.6-dev", + "version": "0.9.0", "description": "JavaScript implementations of network transports, cryptography, ciphers, PKI, message digests, and various utilities.", "homepage": "https://github.com/digitalbazaar/forge", "author": { From 1af95997c4c555cf96e954a395c4295759f00fd7 Mon Sep 17 00:00:00 2001 From: "David I. Lehn" Date: Wed, 4 Sep 2019 22:35:29 -0400 Subject: [PATCH 107/197] Start 0.9.1. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index ab3f2cef3..d91010e39 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "node-forge", - "version": "0.9.0", + "version": "0.9.1-dev", "description": "JavaScript implementations of network transports, cryptography, ciphers, PKI, message digests, and various utilities.", "homepage": "https://github.com/digitalbazaar/forge", "author": { From 99791690247f27972c314ffd2273d258c17da0ed Mon Sep 17 00:00:00 2001 From: Dave Longley Date: Thu, 26 Sep 2019 10:28:50 -0400 Subject: [PATCH 108/197] Ensure given IV matches block size. --- lib/cipherModes.js | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/lib/cipherModes.js b/lib/cipherModes.js index 1f2d41ddc..339915cc1 100644 --- a/lib/cipherModes.js +++ b/lib/cipherModes.js @@ -119,7 +119,7 @@ modes.cbc.prototype.start = function(options) { throw new Error('Invalid IV parameter.'); } else { // save IV as "previous" block - this._iv = transformIV(options.iv); + this._iv = transformIV(options.iv, this.blockSize); this._prev = this._iv.slice(0); } }; @@ -215,7 +215,7 @@ modes.cfb.prototype.start = function(options) { throw new Error('Invalid IV parameter.'); } // use IV as first input - this._iv = transformIV(options.iv); + this._iv = transformIV(options.iv, this.blockSize); this._inBlock = this._iv.slice(0); this._partialBytes = 0; }; @@ -359,7 +359,7 @@ modes.ofb.prototype.start = function(options) { throw new Error('Invalid IV parameter.'); } // use IV as first input - this._iv = transformIV(options.iv); + this._iv = transformIV(options.iv, this.blockSize); this._inBlock = this._iv.slice(0); this._partialBytes = 0; }; @@ -444,7 +444,7 @@ modes.ctr.prototype.start = function(options) { throw new Error('Invalid IV parameter.'); } // use IV as first input - this._iv = transformIV(options.iv); + this._iv = transformIV(options.iv, this.blockSize); this._inBlock = this._iv.slice(0); this._partialBytes = 0; }; @@ -954,7 +954,7 @@ modes.gcm.prototype.generateSubHashTable = function(mid, bits) { /** Utility functions */ -function transformIV(iv) { +function transformIV(iv, blockSize) { if(typeof iv === 'string') { // convert iv string into byte buffer iv = forge.util.createBuffer(iv); @@ -968,9 +968,21 @@ function transformIV(iv) { iv.putByte(tmp[i]); } } + + if(iv.length() < blockSize) { + throw new Error( + 'Invalid IV length; got ' + iv.length() + + ' bytes and expected ' + blockSize + ' bytes.'); + } + if(!forge.util.isArray(iv)) { // convert iv byte buffer into 32-bit integer array - iv = [iv.getInt32(), iv.getInt32(), iv.getInt32(), iv.getInt32()]; + var ints = []; + var blocks = blockSize / 4; + for(var i = 0; i < blocks; ++i) { + ints.push(iv.getInt32()); + } + iv = ints; } return iv; From 22647b56475404169f8085e33b78564ff0dd2a7d Mon Sep 17 00:00:00 2001 From: Andy Wang Date: Thu, 26 Sep 2019 15:07:56 +0100 Subject: [PATCH 109/197] Add DES-CTR tests --- tests/unit/des.js | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/tests/unit/des.js b/tests/unit/des.js index 77ac035e4..086391e5f 100644 --- a/tests/unit/des.js +++ b/tests/unit/des.js @@ -61,6 +61,48 @@ 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/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/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'); + }); + // OpenSSL equivalent: // openssl enc -des-ede3 -K a1c06b381adf36517e84575552777779da5e3d9f994b05b5 -nosalt it('should 3des-ecb encrypt: foobar', function() { From e7b9a796e67ced6971a9bae62636df2a6499ece7 Mon Sep 17 00:00:00 2001 From: Andy Wang Date: Thu, 26 Sep 2019 15:34:07 +0100 Subject: [PATCH 110/197] Add des-ctr decryption tests --- tests/unit/des.js | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/tests/unit/des.js b/tests/unit/des.js index 086391e5f..37794de5d 100644 --- a/tests/unit/des.js +++ b/tests/unit/des.js @@ -75,6 +75,20 @@ var UTIL = require('../../lib/util'); 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( @@ -89,6 +103,20 @@ var UTIL = require('../../lib/util'); 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( @@ -103,6 +131,20 @@ var UTIL = require('../../lib/util'); 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() { From f178523168b006f9ae094f13c61a61a36aa6cb4b Mon Sep 17 00:00:00 2001 From: "David I. Lehn" Date: Thu, 26 Sep 2019 14:24:01 -0400 Subject: [PATCH 111/197] Check DES-CBC short IV. --- tests/unit/des.js | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/tests/unit/des.js b/tests/unit/des.js index 37794de5d..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() { From 7b6c7e5762b9ad803be4ab671cb3ffadb521b832 Mon Sep 17 00:00:00 2001 From: "David I. Lehn" Date: Thu, 26 Sep 2019 14:27:20 -0400 Subject: [PATCH 112/197] Update changelog. --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 370fe3480..4c9a963d6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,9 @@ Forge ChangeLog =============== +### Fixed +- Ensure DES-CBC given IV is long enough for block size. + ## 0.9.0 - 2019-09-04 ### Added From 197218e159cbf82c611da91994c1126e2cfa35c3 Mon Sep 17 00:00:00 2001 From: "David I. Lehn" Date: Thu, 26 Sep 2019 15:01:35 -0400 Subject: [PATCH 113/197] Release 0.9.1. --- CHANGELOG.md | 2 ++ package.json | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4c9a963d6..a2965cd38 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,8 @@ Forge ChangeLog =============== +## 0.9.1 - 2019-09-26 + ### Fixed - Ensure DES-CBC given IV is long enough for block size. diff --git a/package.json b/package.json index d91010e39..ce9b1c3b8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "node-forge", - "version": "0.9.1-dev", + "version": "0.9.1", "description": "JavaScript implementations of network transports, cryptography, ciphers, PKI, message digests, and various utilities.", "homepage": "https://github.com/digitalbazaar/forge", "author": { From 628b17fb5bc0bf22074b632432ffa535805a1b2b Mon Sep 17 00:00:00 2001 From: "David I. Lehn" Date: Thu, 26 Sep 2019 15:02:01 -0400 Subject: [PATCH 114/197] Start 0.9.1. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index ce9b1c3b8..5d5b2856e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "node-forge", - "version": "0.9.1", + "version": "0.9.2-dev", "description": "JavaScript implementations of network transports, cryptography, ciphers, PKI, message digests, and various utilities.", "homepage": "https://github.com/digitalbazaar/forge", "author": { From c5ab1e503dfd3dedf2de3dd5ce39b8387a0b0f48 Mon Sep 17 00:00:00 2001 From: Matthew Collier Date: Wed, 29 Jul 2020 12:21:13 -0400 Subject: [PATCH 115/197] Ignore .vscode. --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 3c9cc9129..134b7dedd 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,7 @@ .nyc_output .project .settings +.vscode TAGS coverage dist From e955dcb34f7d5653c3020b81aee09e992e1eab0a Mon Sep 17 00:00:00 2001 From: "David I. Lehn" Date: Tue, 1 Sep 2020 21:05:56 -0400 Subject: [PATCH 116/197] Add util.setPath security notes. --- CHANGELOG.md | 20 ++++++++++++++++++++ README.md | 2 ++ lib/util.js | 3 +++ 3 files changed, 25 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a2965cd38..0c0879244 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,26 @@ Forge ChangeLog =============== +### Changed +- Added `util.setPath` security note to function docs and to README. + +### Notes +- **SECURITY**: The `util.setPath` function has the potential to cause + prototype pollution if used with unsafe input. + - This function is **not** used internally by `forge`. + - The rest of the library is unaffected by this issue. + - **Do not** use unsafe input with this function. + - Usage with known input should function as expected. (Including input + intentionally using potentially problematic keys.) + - No code changes will be made to address this issue in 0.9.x. The current + behavior *could* be considered a feature rather than a security issue. + 0.10.0 will be released that removes `util.getPath` and `util.setPath`. + Consider `get` and `set` from [lodash](https://lodash.com/) if you need + replacements. But also consider the potential similar security issues with + those APIs. + - https://snyk.io/vuln/SNYK-JS-NODEFORGE-598677 + - https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-7720 + ## 0.9.1 - 2019-09-26 ### Fixed diff --git a/README.md b/README.md index 40bf29561..ae7fc3cbe 100644 --- a/README.md +++ b/README.md @@ -2035,6 +2035,8 @@ When using this code please keep the following in mind: - Certain features in this library are less susceptible to attacks depending on usage. This primarily includes features that deal with data format manipulation or those that are not involved in communication. +- Do not pass unsafe inputs to `util.setPath`. Doing so could expose a + prototype pollution security issue. Library Background ------------------ diff --git a/lib/util.js b/lib/util.js index a86609284..a6ad158f3 100644 --- a/lib/util.js +++ b/lib/util.js @@ -2520,6 +2520,9 @@ util.makeLink = function(path, query, fragment) { * leaf nodes are given as non-final path keys. * Used to avoid exceptions from missing parts of the path. * + * SECURITY NOTE: Do not use unsafe inputs. Doing so could expose a prototype + * pollution security issue. + * * @param object the starting object. * @param keys an array of string keys. * @param value the value to set. From bf049a46d94d22da095f3c294ad5fbdcaa3f3f16 Mon Sep 17 00:00:00 2001 From: "David I. Lehn" Date: Tue, 1 Sep 2020 21:20:08 -0400 Subject: [PATCH 117/197] Release 0.9.2. --- CHANGELOG.md | 2 ++ package.json | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0c0879244..cf06499a5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,8 @@ Forge ChangeLog =============== +## 0.9.2 - 2019-09-01 + ### Changed - Added `util.setPath` security note to function docs and to README. diff --git a/package.json b/package.json index 5d5b2856e..6c78da7b7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "node-forge", - "version": "0.9.2-dev", + "version": "0.9.2", "description": "JavaScript implementations of network transports, cryptography, ciphers, PKI, message digests, and various utilities.", "homepage": "https://github.com/digitalbazaar/forge", "author": { From c470b83ef8433d28d393790cd1a3a38408b663b4 Mon Sep 17 00:00:00 2001 From: "David I. Lehn" Date: Tue, 1 Sep 2020 21:20:37 -0400 Subject: [PATCH 118/197] Start 0.9.3. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 6c78da7b7..3488ad4fb 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "node-forge", - "version": "0.9.2", + "version": "0.9.3-dev", "description": "JavaScript implementations of network transports, cryptography, ciphers, PKI, message digests, and various utilities.", "homepage": "https://github.com/digitalbazaar/forge", "author": { From dfa516cc1bc577616d17e3301dcbcc5dd8a7a864 Mon Sep 17 00:00:00 2001 From: "David I. Lehn" Date: Tue, 11 Feb 2020 18:27:08 -0500 Subject: [PATCH 119/197] Update eslint config. --- .eslintrc.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.eslintrc.js b/.eslintrc.js index 5477843af..11e4c6eb4 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,10 +1,13 @@ module.exports = { + root: true, env: { browser: true, commonjs: true, node: true }, - extends: ['eslint-config-digitalbazaar'], + extends: [ + 'digitalbazaar' + ], parserOptions: { ecmaVersion: 5 }, From ba0207fa359f79a5af0e71d12f488a846e9265f6 Mon Sep 17 00:00:00 2001 From: "David I. Lehn" Date: Tue, 11 Feb 2020 18:27:27 -0500 Subject: [PATCH 120/197] Test on Node.js 12. --- .travis.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 24b8a8454..acf170044 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,6 +4,7 @@ node_js: - "6" - "8" - "10" + - "12" - "node" sudo: false install: npm install @@ -13,9 +14,9 @@ script: # only run karma tests for one node version matrix: include: - - node_js: "10" + - node_js: "12" env: BUNDLER=webpack - - node_js: "10" + - node_js: "12" env: BUNDLER=browserify notifications: email: From afc5a728f5c6bf5cb80eddb5b85c7340b28bfa51 Mon Sep 17 00:00:00 2001 From: "David I. Lehn" Date: Tue, 11 Feb 2020 18:33:54 -0500 Subject: [PATCH 121/197] Update dependencies. --- package.json | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/package.json b/package.json index 3488ad4fb..20834a86e 100644 --- a/package.json +++ b/package.json @@ -17,28 +17,28 @@ "devDependencies": { "browserify": "^16.1.0", "commander": "^2.20.0", - "cross-env": "^5.1.3", + "cross-env": "^5.2.1", "eslint": "^5.16.0", - "eslint-config-digitalbazaar": "^2.0.0", + "eslint-config-digitalbazaar": "^2.2.0", "express": "^4.16.2", - "karma": "^3.1.4", - "karma-browserify": "^6.0.0", - "karma-chrome-launcher": "^2.2.0", + "karma": "^4.4.1", + "karma-browserify": "^7.0.0", + "karma-chrome-launcher": "^3.1.0", "karma-edge-launcher": "^0.4.2", - "karma-firefox-launcher": "^1.1.0", + "karma-firefox-launcher": "^1.3.0", "karma-ie-launcher": "^1.0.0", "karma-mocha": "^1.3.0", "karma-mocha-reporter": "^2.2.5", "karma-safari-launcher": "^1.0.0", - "karma-sauce-launcher": "^1.2.0", + "karma-sauce-launcher": "^2.0.2", "karma-sourcemap-loader": "^0.3.7", "karma-tap-reporter": "0.0.6", - "karma-webpack": "^3.0.5", + "karma-webpack": "^4.0.2", "mocha": "^5.2.0", "mocha-lcov-reporter": "^1.2.0", "nodejs-websocket": "^1.7.1", - "nyc": "^14.1.1", - "opts": "^1.2.2", + "nyc": "^15.0.0", + "opts": "^1.2.7", "webpack": "^3.11.0", "worker-loader": "^2.0.0" }, From c8d5395e078f838604eb9df32ef9d298288057fd Mon Sep 17 00:00:00 2001 From: "David I. Lehn" Date: Tue, 11 Feb 2020 18:57:09 -0500 Subject: [PATCH 122/197] Add travis browser test names. --- .travis.yml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index acf170044..31c1915b5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,16 +7,19 @@ node_js: - "12" - "node" sudo: false -install: npm install +install: + - npm install script: - if [ "x$BUNDLER" = "x" ]; then npm test; fi - if [ "x$BUNDLER" != "x" ]; then npm run test-karma; fi # only run karma tests for one node version matrix: include: - - node_js: "12" + - name: "Browser Unit Tests (webpack)" + node_js: "12" env: BUNDLER=webpack - - node_js: "12" + - name: "Browser Unit Tests (browserify)" + node_js: "12" env: BUNDLER=browserify notifications: email: From ba13a1c8bfdb1d6abb286fa7fcb82a57b40ae345 Mon Sep 17 00:00:00 2001 From: "David I. Lehn" Date: Tue, 11 Feb 2020 19:04:41 -0500 Subject: [PATCH 123/197] Update webpack. --- karma.conf.js | 1 + package.json | 3 ++- webpack.config.js | 5 ++++- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/karma.conf.js b/karma.conf.js index 996a5adf4..a61407dc1 100644 --- a/karma.conf.js +++ b/karma.conf.js @@ -55,6 +55,7 @@ module.exports = function(config) { }, webpack: { + mode: 'development', devtool: 'inline-source-map', node: { Buffer: false, diff --git a/package.json b/package.json index 20834a86e..d40f0bba1 100644 --- a/package.json +++ b/package.json @@ -39,7 +39,8 @@ "nodejs-websocket": "^1.7.1", "nyc": "^15.0.0", "opts": "^1.2.7", - "webpack": "^3.11.0", + "webpack": "^4.41.6", + "webpack-cli": "^3.3.11", "worker-loader": "^2.0.0" }, "repository": { 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) { From 7b59028142bb5f5e299df0d0931a8306d7d7046b Mon Sep 17 00:00:00 2001 From: "David I. Lehn" Date: Tue, 1 Sep 2020 16:48:33 -0400 Subject: [PATCH 124/197] Test on Node.js 14. --- .travis.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 31c1915b5..54aa4b543 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,6 +5,7 @@ node_js: - "8" - "10" - "12" + - "14" - "node" sudo: false install: @@ -16,10 +17,10 @@ script: matrix: include: - name: "Browser Unit Tests (webpack)" - node_js: "12" + node_js: "14" env: BUNDLER=webpack - name: "Browser Unit Tests (browserify)" - node_js: "12" + node_js: "14" env: BUNDLER=browserify notifications: email: From 81abd87a9a8ef705af17d1e712a086ef4b7869a1 Mon Sep 17 00:00:00 2001 From: "David I. Lehn" Date: Tue, 1 Sep 2020 19:46:05 -0400 Subject: [PATCH 125/197] Improve linting. - Code is very out-of-date with proper style. - Fix so it at least shows errors with modern eslint. - Disable some rules with too many hits for current style. --- .eslintrc.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index 11e4c6eb4..7fa4468b3 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -9,13 +9,16 @@ module.exports = { 'digitalbazaar' ], parserOptions: { - ecmaVersion: 5 + ecmaVersion: 5, + sourceType: 'script' }, rules: { // overrides to support ES5, remove when updated to ES20xx 'no-unused-vars': 'warn', 'no-var': 'off', 'object-shorthand': 'off', - 'prefer-const': 'off' + 'prefer-const': 'off', + // fix when code is globally reformatted + 'max-len': 'off' } }; From 1ba83ecca436c2e1d0d37ea154467e9934019504 Mon Sep 17 00:00:00 2001 From: "David I. Lehn" Date: Tue, 1 Sep 2020 19:47:31 -0400 Subject: [PATCH 126/197] Update dependencies. --- package.json | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/package.json b/package.json index d40f0bba1..ac2f4c375 100644 --- a/package.json +++ b/package.json @@ -15,11 +15,11 @@ "Christoph Dorn " ], "devDependencies": { - "browserify": "^16.1.0", + "browserify": "^16.5.2", "commander": "^2.20.0", "cross-env": "^5.2.1", - "eslint": "^5.16.0", - "eslint-config-digitalbazaar": "^2.2.0", + "eslint": "^7.8.1", + "eslint-config-digitalbazaar": "^2.5.0", "express": "^4.16.2", "karma": "^4.4.1", "karma-browserify": "^7.0.0", @@ -31,16 +31,16 @@ "karma-mocha-reporter": "^2.2.5", "karma-safari-launcher": "^1.0.0", "karma-sauce-launcher": "^2.0.2", - "karma-sourcemap-loader": "^0.3.7", + "karma-sourcemap-loader": "^0.3.8", "karma-tap-reporter": "0.0.6", "karma-webpack": "^4.0.2", "mocha": "^5.2.0", "mocha-lcov-reporter": "^1.2.0", "nodejs-websocket": "^1.7.1", - "nyc": "^15.0.0", + "nyc": "^15.1.0", "opts": "^1.2.7", - "webpack": "^4.41.6", - "webpack-cli": "^3.3.11", + "webpack": "^4.44.1", + "webpack-cli": "^3.3.12", "worker-loader": "^2.0.0" }, "repository": { From 30d560c6b522b9a97a67d9101ecf8f860a4ba63a Mon Sep 17 00:00:00 2001 From: "David I. Lehn" Date: Tue, 1 Sep 2020 19:58:39 -0400 Subject: [PATCH 127/197] Remove Node.js 4 support. --- .travis.yml | 1 - CHANGELOG.md | 5 +++++ package.json | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 54aa4b543..6ea8d7085 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,5 @@ language: node_js node_js: - - "4" - "6" - "8" - "10" diff --git a/CHANGELOG.md b/CHANGELOG.md index cf06499a5..10cc588a0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,11 @@ Forge ChangeLog =============== +- **BREAKING**: Node.js 4 no longer supported. The code *may* still work, and + non-invasive patches to keep it working will be considered. However, more + modern tools no longer support very old Node.js versions making testing + difficult. + ## 0.9.2 - 2019-09-01 ### Changed diff --git a/package.json b/package.json index ac2f4c375..1f4afd2e8 100644 --- a/package.json +++ b/package.json @@ -60,7 +60,7 @@ "dist/*.min.js.map" ], "engines": { - "node": ">= 4.5.0" + "node": ">= 6.0.0" }, "keywords": [ "aes", From 6a1e3ef74f6eb345bcff1b82184201d1e28b6756 Mon Sep 17 00:00:00 2001 From: "David I. Lehn" Date: Tue, 1 Sep 2020 21:37:39 -0400 Subject: [PATCH 128/197] Remove object path functions. - Remove `util.getPath`, `util.setPath`, and `util.deletePath`. - These are unused in `forge` itself. - Path processing has potential security issues. (For `setPath` in particular). - `lodash` has better replacements: `get`, `set`, and `unset`. - See also: - CHANGELOG.md 0.9.2 entry. - https://snyk.io/vuln/SNYK-JS-NODEFORGE-598677 - https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-7720 --- CHANGELOG.md | 15 ++++++-- README.md | 2 -- lib/util.js | 96 ---------------------------------------------------- 3 files changed, 13 insertions(+), 100 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 10cc588a0..e3f1bfc5d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,21 @@ Forge ChangeLog =============== +### Changed - **BREAKING**: Node.js 4 no longer supported. The code *may* still work, and non-invasive patches to keep it working will be considered. However, more - modern tools no longer support very old Node.js versions making testing - difficult. + modern tools no longer support old Node.js versions making testing difficult. + +### Removed +- **BREAKING**: Remove `util.getPath`, `util.setPath`, and `util.deletePath`. + `util.setPath` had a potential prototype pollution security issue when used + with unsafe inputs. These functions are not used by `forge` itself. They date + from an early time when `forge` was targeted at providing general helper + functions. The library direction changed to be more focused on cryptography. + Many other excellent libraries are more suitable for general utilities. If + you need a replacement for these functions, consier `get`, `set`, and `unset` + from [lodash](https://lodash.com/). But also consider the potential similar + security issues with those APIs. ## 0.9.2 - 2019-09-01 diff --git a/README.md b/README.md index ae7fc3cbe..40bf29561 100644 --- a/README.md +++ b/README.md @@ -2035,8 +2035,6 @@ When using this code please keep the following in mind: - Certain features in this library are less susceptible to attacks depending on usage. This primarily includes features that deal with data format manipulation or those that are not involved in communication. -- Do not pass unsafe inputs to `util.setPath`. Doing so could expose a - prototype pollution security issue. Library Background ------------------ diff --git a/lib/util.js b/lib/util.js index a6ad158f3..98dfd3427 100644 --- a/lib/util.js +++ b/lib/util.js @@ -2513,102 +2513,6 @@ util.makeLink = function(path, query, fragment) { ((fragment.length > 0) ? ('#' + fragment) : ''); }; -/** - * Follows a path of keys deep into an object hierarchy and set a value. - * If a key does not exist or it's value is not an object, create an - * object in it's place. This can be destructive to a object tree if - * leaf nodes are given as non-final path keys. - * Used to avoid exceptions from missing parts of the path. - * - * SECURITY NOTE: Do not use unsafe inputs. Doing so could expose a prototype - * pollution security issue. - * - * @param object the starting object. - * @param keys an array of string keys. - * @param value the value to set. - */ -util.setPath = function(object, keys, value) { - // need to start at an object - if(typeof(object) === 'object' && object !== null) { - var i = 0; - var len = keys.length; - while(i < len) { - var next = keys[i++]; - if(i == len) { - // last - object[next] = value; - } else { - // more - var hasNext = (next in object); - if(!hasNext || - (hasNext && typeof(object[next]) !== 'object') || - (hasNext && object[next] === null)) { - object[next] = {}; - } - object = object[next]; - } - } - } -}; - -/** - * Follows a path of keys deep into an object hierarchy and return a value. - * If a key does not exist, create an object in it's place. - * Used to avoid exceptions from missing parts of the path. - * - * @param object the starting object. - * @param keys an array of string keys. - * @param _default value to return if path not found. - * - * @return the value at the path if found, else default if given, else - * undefined. - */ -util.getPath = function(object, keys, _default) { - var i = 0; - var len = keys.length; - var hasNext = true; - while(hasNext && i < len && - typeof(object) === 'object' && object !== null) { - var next = keys[i++]; - hasNext = next in object; - if(hasNext) { - object = object[next]; - } - } - return (hasNext ? object : _default); -}; - -/** - * Follow a path of keys deep into an object hierarchy and delete the - * last one. If a key does not exist, do nothing. - * Used to avoid exceptions from missing parts of the path. - * - * @param object the starting object. - * @param keys an array of string keys. - */ -util.deletePath = function(object, keys) { - // need to start at an object - if(typeof(object) === 'object' && object !== null) { - var i = 0; - var len = keys.length; - while(i < len) { - var next = keys[i++]; - if(i == len) { - // last - delete object[next]; - } else { - // more - if(!(next in object) || - (typeof(object[next]) !== 'object') || - (object[next] === null)) { - break; - } - object = object[next]; - } - } - } -}; - /** * Check if an object is empty. * From 8018c3ea6f8e75d2df1b9ce4ec9c536db3db37e6 Mon Sep 17 00:00:00 2001 From: "David I. Lehn" Date: Tue, 1 Sep 2020 22:01:59 -0400 Subject: [PATCH 129/197] Release 0.10.0. --- CHANGELOG.md | 2 ++ package.json | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e3f1bfc5d..81176fa81 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,8 @@ Forge ChangeLog =============== +## 0.10.0 - 2019-09-01 + ### Changed - **BREAKING**: Node.js 4 no longer supported. The code *may* still work, and non-invasive patches to keep it working will be considered. However, more diff --git a/package.json b/package.json index 1f4afd2e8..f5cc879f9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "node-forge", - "version": "0.9.3-dev", + "version": "0.10.0", "description": "JavaScript implementations of network transports, cryptography, ciphers, PKI, message digests, and various utilities.", "homepage": "https://github.com/digitalbazaar/forge", "author": { From 9d286917dba012b6e2c991f694763c590671a967 Mon Sep 17 00:00:00 2001 From: "David I. Lehn" Date: Tue, 1 Sep 2020 22:02:28 -0400 Subject: [PATCH 130/197] Start 0.10.1. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f5cc879f9..997982278 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "node-forge", - "version": "0.10.0", + "version": "0.10.1-dev", "description": "JavaScript implementations of network transports, cryptography, ciphers, PKI, message digests, and various utilities.", "homepage": "https://github.com/digitalbazaar/forge", "author": { From 588c41062d9a13f8dc91be3723b159c6cc434b15 Mon Sep 17 00:00:00 2001 From: "David I. Lehn" Date: Wed, 2 Sep 2020 10:28:14 -0400 Subject: [PATCH 131/197] Fix release dates. --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 81176fa81..91d13bdbb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,7 @@ Forge ChangeLog =============== -## 0.10.0 - 2019-09-01 +## 0.10.0 - 2020-09-01 ### Changed - **BREAKING**: Node.js 4 no longer supported. The code *may* still work, and @@ -19,7 +19,7 @@ Forge ChangeLog from [lodash](https://lodash.com/). But also consider the potential similar security issues with those APIs. -## 0.9.2 - 2019-09-01 +## 0.9.2 - 2020-09-01 ### Changed - Added `util.setPath` security note to function docs and to README. From e06afc4faa100f4363b013ea6f70002308072c6c Mon Sep 17 00:00:00 2001 From: troyfactor4 <5209556+troyfactor4@users.noreply.github.com> Date: Mon, 16 Nov 2020 01:45:48 -0400 Subject: [PATCH 132/197] fix: make PKCS#7 parameter optional as per RFC 5280 --- lib/pkcs7.js | 2 +- lib/pkcs7asn1.js | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/pkcs7.js b/lib/pkcs7.js index bb87de363..95c54e361 100644 --- a/lib/pkcs7.js +++ b/lib/pkcs7.js @@ -837,7 +837,7 @@ function _recipientFromAsn1(obj) { serialNumber: forge.util.createBuffer(capture.serial).toHex(), encryptedContent: { algorithm: asn1.derToOid(capture.encAlgorithm), - parameter: capture.encParameter.value, + parameter: capture.encParameter ? capture.encParameter.value : null, content: capture.encKey } }; diff --git a/lib/pkcs7asn1.js b/lib/pkcs7asn1.js index a2ac01f85..0e13c8915 100644 --- a/lib/pkcs7asn1.js +++ b/lib/pkcs7asn1.js @@ -397,7 +397,8 @@ p7v.recipientInfoValidator = { name: 'RecipientInfo.keyEncryptionAlgorithm.parameter', tagClass: asn1.Class.UNIVERSAL, constructed: false, - captureAsn1: 'encParameter' + captureAsn1: 'encParameter', + optional: true }] }, { name: 'RecipientInfo.encryptedKey', From 8d7595b95e0c0c39ef82d8fdb5b3d890debd2bb7 Mon Sep 17 00:00:00 2001 From: troyfactor4 <5209556+troyfactor4@users.noreply.github.com> Date: Tue, 17 Nov 2020 13:11:56 -0400 Subject: [PATCH 133/197] fix: null check ec.parameter --- lib/pkcs7.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/pkcs7.js b/lib/pkcs7.js index 95c54e361..3e6cfe931 100644 --- a/lib/pkcs7.js +++ b/lib/pkcs7.js @@ -1124,8 +1124,10 @@ function _encryptedContentToAsn1(ec) { asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false, asn1.oidToDer(ec.algorithm).getBytes()), // Parameters (IV) - asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false, - ec.parameter.getBytes()) + ( ec.parameter ? + asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false, + ec.parameter.getBytes()) : undefined + ) ]), // [0] EncryptedContent asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, true, [ From 4292496097d8756bdaeabd85b9cb7520cd80e5f4 Mon Sep 17 00:00:00 2001 From: Dave Longley Date: Wed, 18 Nov 2020 15:14:56 -0500 Subject: [PATCH 134/197] Apply suggestions from code review. --- lib/pkcs7.js | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/lib/pkcs7.js b/lib/pkcs7.js index 3e6cfe931..3a5d845c5 100644 --- a/lib/pkcs7.js +++ b/lib/pkcs7.js @@ -837,7 +837,7 @@ function _recipientFromAsn1(obj) { serialNumber: forge.util.createBuffer(capture.serial).toHex(), encryptedContent: { algorithm: asn1.derToOid(capture.encAlgorithm), - parameter: capture.encParameter ? capture.encParameter.value : null, + parameter: capture.encParameter ? capture.encParameter.value : undefined, content: capture.encKey } }; @@ -1124,10 +1124,11 @@ function _encryptedContentToAsn1(ec) { asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false, asn1.oidToDer(ec.algorithm).getBytes()), // Parameters (IV) - ( ec.parameter ? - asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false, - ec.parameter.getBytes()) : undefined - ) + !ec.parameter ? + undefined : + asn1.create( + asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false, + ec.parameter.getBytes()) ]), // [0] EncryptedContent asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, true, [ From c666282c812d6dc18e97b419b152dd6ad98c802c Mon Sep 17 00:00:00 2001 From: Daniel Hensby Date: Thu, 25 Mar 2021 10:21:35 +0000 Subject: [PATCH 135/197] Remove link to nodeguide.com It appears nodeguide.com domain has been taken over by domain squatters and the style.html page appears to push a questionable PDF download. --- CONTRIBUTING.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c5f08e8b1..299d2711c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -6,7 +6,7 @@ Want to contribute to forge? Great! Here are a few notes: Code ---- -* In general, follow the current code style or the [Node.js Style Guide][]. +* In general, follow the current code style. * Read the [contributing](./README.md#contributing) notes. * Ensure [tests pass](./README.md#testing). @@ -15,5 +15,4 @@ Release Process Maintainers should refer to the [release instructions](./RELEASE.md). -[Node.js Style Guide]: http://nodeguide.com/style.html [Semantic Versioning]: http://semver.org/ From 724158f264be5ad691d1546347a94a6214bd25cf Mon Sep 17 00:00:00 2001 From: "David I. Lehn" Date: Wed, 7 Apr 2021 03:21:46 -0400 Subject: [PATCH 136/197] Remove `forge.task` API. Task API still used in tests so moved to `tests/support/` and files updated appropriately. --- CHANGELOG.md | 9 +++++++++ lib/index.js | 1 - tests/issues/issue-428.html | 1 + tests/issues/issue-428.js | 2 +- tests/legacy/common.html | 1 + tests/legacy/common.js | 2 +- tests/legacy/tasks.html | 1 + tests/legacy/tasks.js | 4 ++-- tests/legacy/xhr.html | 1 + tests/legacy/xhr.js | 2 +- tests/server.js | 2 ++ {lib => tests/support}/task.js | 21 +++++++++------------ 12 files changed, 29 insertions(+), 18 deletions(-) rename {lib => tests/support}/task.js (97%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 91d13bdbb..cfe650070 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,15 @@ Forge ChangeLog =============== +## 0.11.0 - 2021-xx-xx + +### Removed +- **BREAKING**: Remove `forge.task` API. This API was never used, documented, + or advertised by the maintainers. If anyone was using this API and wishes to + continue development it in other project, please let the maintainers know. + Due to use in the test suite, a modified version is located in + `tests/support/`. + ## 0.10.0 - 2020-09-01 ### Changed diff --git a/lib/index.js b/lib/index.js index ea8c14cf9..ffb931286 100644 --- a/lib/index.js +++ b/lib/index.js @@ -30,6 +30,5 @@ require('./pss'); require('./random'); require('./rc2'); require('./ssh'); -require('./task'); require('./tls'); require('./util'); diff --git a/tests/issues/issue-428.html b/tests/issues/issue-428.html index fc9af30c6..1685c3e1f 100644 --- a/tests/issues/issue-428.html +++ b/tests/issues/issue-428.html @@ -4,6 +4,7 @@ Forge Issue 428 Test + diff --git a/tests/issues/issue-428.js b/tests/issues/issue-428.js index 7b9427e4a..179477d42 100644 --- a/tests/issues/issue-428.js +++ b/tests/issues/issue-428.js @@ -52,7 +52,7 @@ jQuery(function($) { $('#start').attr('disabled', 'true'); $('#stop').attr('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/legacy/common.html b/tests/legacy/common.html index 9ee073117..d835af37f 100644 --- a/tests/legacy/common.html +++ b/tests/legacy/common.html @@ -4,6 +4,7 @@ Forge Common Tests + diff --git a/tests/legacy/common.js b/tests/legacy/common.js index 57dfbc4ba..4c08e1319 100644 --- a/tests/legacy/common.js +++ b/tests/legacy/common.js @@ -39,7 +39,7 @@ jQuery(function($) { $('#start').attr('disabled', 'true'); // 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/legacy/tasks.html b/tests/legacy/tasks.html index dc7e48d60..5456faded 100644 --- a/tests/legacy/tasks.html +++ b/tests/legacy/tasks.html @@ -4,6 +4,7 @@ Forge Tasks Tests + diff --git a/tests/legacy/tasks.js b/tests/legacy/tasks.js index 2c99a9d36..eef594a19 100644 --- a/tests/legacy/tasks.js +++ b/tests/legacy/tasks.js @@ -33,7 +33,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) { @@ -365,7 +365,7 @@ jQuery(function($) for(var i = 0; i < count; ++i) { - forge.task.start(tasks[i]); + forge_task.start(tasks[i]); } }); diff --git a/tests/legacy/xhr.html b/tests/legacy/xhr.html index 5aa868f54..fbb086f2a 100644 --- a/tests/legacy/xhr.html +++ b/tests/legacy/xhr.html @@ -5,6 +5,7 @@ + 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/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..5bf8e465a 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 @@ -277,7 +274,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 +342,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 +378,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 +615,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 +639,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 +670,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 +685,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: {} From 07942ef36461e6fff9bb50dcdabb0dac78643fb5 Mon Sep 17 00:00:00 2001 From: "David I. Lehn" Date: Thu, 27 May 2021 21:55:10 -0400 Subject: [PATCH 137/197] Update eslint dependencies. --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 997982278..0636212bb 100644 --- a/package.json +++ b/package.json @@ -18,8 +18,8 @@ "browserify": "^16.5.2", "commander": "^2.20.0", "cross-env": "^5.2.1", - "eslint": "^7.8.1", - "eslint-config-digitalbazaar": "^2.5.0", + "eslint": "^7.27.0", + "eslint-config-digitalbazaar": "^2.8.0", "express": "^4.16.2", "karma": "^4.4.1", "karma-browserify": "^7.0.0", From 5d09946b713ff54bd963c47dfee56d9d3d54c680 Mon Sep 17 00:00:00 2001 From: "David I. Lehn" Date: Thu, 27 May 2021 21:55:25 -0400 Subject: [PATCH 138/197] Add eslint config for tests. --- tests/.eslintrc.js | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 tests/.eslintrc.js diff --git a/tests/.eslintrc.js b/tests/.eslintrc.js new file mode 100644 index 000000000..bf3479fb6 --- /dev/null +++ b/tests/.eslintrc.js @@ -0,0 +1,5 @@ +module.exports = { + env: { + mocha: true + } +}; From 51228083550dde97701ac8e06c629a5184117562 Mon Sep 17 00:00:00 2001 From: "David I. Lehn" Date: Thu, 27 May 2021 21:58:45 -0400 Subject: [PATCH 139/197] Remove `forge.debug` API. The API has the potential for prototype pollution. This API was only briefly used by the maintainers for internal project debug purposes and was never inteneded to be used with untrusted user intputs. This API was not documented or advertised and is being removed rather than fixed. --- CHANGELOG.md | 5 +++ README.md | 14 -------- lib/debug.js | 78 ------------------------------------------- lib/http.js | 11 ------ lib/index.js | 1 - tests/support/task.js | 4 --- 6 files changed, 5 insertions(+), 108 deletions(-) delete mode 100644 lib/debug.js diff --git a/CHANGELOG.md b/CHANGELOG.md index cfe650070..86241ba04 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,11 @@ Forge ChangeLog ## 0.11.0 - 2021-xx-xx ### Removed +- **SECURITY**, **BREAKING**: Remove `forge.debug` API. The API has the + potential for prototype pollution. This API was only briefly used by the + maintainers for internal project debug purposes and was never inteneded to be + used with untrusted user intputs. This API was not documented or advertised + and is being removed rather than fixed. - **BREAKING**: Remove `forge.task` API. This API was never used, documented, or advertised by the maintainers. If anyone was using this API and wishes to continue development it in other project, please let the maintainers know. diff --git a/README.md b/README.md index 40bf29561..bfc640fee 100644 --- a/README.md +++ b/README.md @@ -80,7 +80,6 @@ Documentation * [Tasks](#task) * [Utilities](#util) * [Logging](#log) -* [Debugging](#debug) * [Flash Networking Support](#flash) ### Other @@ -1988,19 +1987,6 @@ __Examples__ // TODO ``` - - -### Debugging - -Provides storage of debugging information normally inaccessible in -closures for viewing/investigation. - -__Examples__ - -```js -// TODO -``` - ### Flash Networking Support diff --git a/lib/debug.js b/lib/debug.js deleted file mode 100644 index 26756350e..000000000 --- a/lib/debug.js +++ /dev/null @@ -1,78 +0,0 @@ -/** - * Debugging support for web applications. - * - * @author David I. Lehn - * - * Copyright 2008-2013 Digital Bazaar, Inc. - */ -var forge = require('./forge'); - -/* DEBUG API */ -module.exports = forge.debug = forge.debug || {}; - -// Private storage for debugging. -// Useful to expose data that is otherwise unviewable behind closures. -// NOTE: remember that this can hold references to data and cause leaks! -// format is "forge._debug.. = data" -// Example: -// (function() { -// var cat = 'forge.test.Test'; // debugging category -// var sState = {...}; // local state -// forge.debug.set(cat, 'sState', sState); -// })(); -forge.debug.storage = {}; - -/** - * Gets debug data. Omit name for all cat data Omit name and cat for - * all data. - * - * @param cat name of debugging category. - * @param name name of data to get (optional). - * @return object with requested debug data or undefined. - */ -forge.debug.get = function(cat, name) { - var rval; - if(typeof(cat) === 'undefined') { - rval = forge.debug.storage; - } else if(cat in forge.debug.storage) { - if(typeof(name) === 'undefined') { - rval = forge.debug.storage[cat]; - } else { - rval = forge.debug.storage[cat][name]; - } - } - return rval; -}; - -/** - * Sets debug data. - * - * @param cat name of debugging category. - * @param name name of data to set. - * @param data data to set. - */ -forge.debug.set = function(cat, name, data) { - if(!(cat in forge.debug.storage)) { - forge.debug.storage[cat] = {}; - } - forge.debug.storage[cat][name] = data; -}; - -/** - * Clears debug data. Omit name for all cat data. Omit name and cat for - * all data. - * - * @param cat name of debugging category. - * @param name name of data to clear or omit to clear entire category. - */ -forge.debug.clear = function(cat, name) { - if(typeof(cat) === 'undefined') { - forge.debug.storage = {}; - } else if(cat in forge.debug.storage) { - if(typeof(name) === 'undefined') { - delete forge.debug.storage[cat]; - } else { - delete forge.debug.storage[cat][name]; - } - } -}; diff --git a/lib/http.js b/lib/http.js index 1dcb0a65e..0ae863050 100644 --- a/lib/http.js +++ b/lib/http.js @@ -6,7 +6,6 @@ * Copyright (c) 2010-2014 Digital Bazaar, Inc. All rights reserved. */ var forge = require('./forge'); -require('./debug'); require('./tls'); require('./util'); @@ -16,11 +15,6 @@ var http = module.exports = forge.http = forge.http || {}; // logging category var cat = 'forge.http'; -// add array of clients to debug storage -if(forge.debug) { - forge.debug.set('forge.http', 'clients', []); -} - // normalizes an http header field name var _normalize = function(name) { return name.toLowerCase().replace(/(^.)|(-.)/g, @@ -484,11 +478,6 @@ http.createClient = function(options) { true : options.persistCookies }; - // add client to debug storage - if(forge.debug) { - forge.debug.get('forge.http', 'clients').push(client); - } - // load cookies from disk _loadCookies(client); diff --git a/lib/index.js b/lib/index.js index ffb931286..6cdd5a9cc 100644 --- a/lib/index.js +++ b/lib/index.js @@ -10,7 +10,6 @@ require('./aes'); require('./aesCipherSuites'); require('./asn1'); require('./cipher'); -require('./debug'); require('./des'); require('./ed25519'); require('./hmac'); diff --git a/tests/support/task.js b/tests/support/task.js index 5bf8e465a..4607ecb12 100644 --- a/tests/support/task.js +++ b/tests/support/task.js @@ -24,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 = '?'; From 7aa796efd838422cfd216f6472e7444c1b57bf0d Mon Sep 17 00:00:00 2001 From: "David I. Lehn" Date: Thu, 9 Sep 2021 22:33:10 -0400 Subject: [PATCH 140/197] Switch from travis to github actions. --- .github/workflows/main.yml | 72 ++++++++++++++++++++++++++++++++++++++ .travis.yml | 27 -------------- package.json | 1 + 3 files changed, 73 insertions(+), 27 deletions(-) create mode 100644 .github/workflows/main.yml delete mode 100644 .travis.yml diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 000000000..927425990 --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,72 @@ +name: Node.js CI + +on: [push] + +jobs: + test-node: + runs-on: ubuntu-latest + timeout-minutes: 10 + strategy: + matrix: + node-version: [6.x, 8.x, 10.x, 12.x, 14.x] + steps: + - uses: actions/checkout@v2 + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v1 + with: + node-version: ${{ matrix.node-version }} + - run: npm install + - name: Run test with Node.js ${{ matrix.node-version }} + run: npm run test-node + test-karma: + runs-on: ubuntu-latest + timeout-minutes: 10 + strategy: + matrix: + node-version: [14.x] + env: + BUNDLER: [webpack, browserify] + steps: + - uses: actions/checkout@v2 + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v1 + with: + node-version: ${{ matrix.node-version }} + - run: npm install + - name: Run karma tests + run: npm run test-karma + lint: + runs-on: ubuntu-latest + timeout-minutes: 10 + strategy: + matrix: + node-version: [14.x] + steps: + - uses: actions/checkout@v2 + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v1 + with: + node-version: ${{ matrix.node-version }} + - run: npm install + - name: Run eslint + run: npm run lint + coverage: + runs-on: ubuntu-latest + timeout-minutes: 10 + strategy: + matrix: + node-version: [14.x] + steps: + - uses: actions/checkout@v2 + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v1 + with: + node-version: ${{ matrix.node-version }} + - run: npm install + - name: Generate coverage report + run: npm run coverage-ci + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v1 + with: + file: ./coverage/lcov.info + fail_ci_if_error: true diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 6ea8d7085..000000000 --- a/.travis.yml +++ /dev/null @@ -1,27 +0,0 @@ -language: node_js -node_js: - - "6" - - "8" - - "10" - - "12" - - "14" - - "node" -sudo: false -install: - - npm install -script: - - if [ "x$BUNDLER" = "x" ]; then npm test; fi - - if [ "x$BUNDLER" != "x" ]; then npm run test-karma; fi -# only run karma tests for one node version -matrix: - include: - - name: "Browser Unit Tests (webpack)" - node_js: "14" - env: BUNDLER=webpack - - name: "Browser Unit Tests (browserify)" - node_js: "14" - env: BUNDLER=browserify -notifications: - email: - on_success: change - on_failure: change diff --git a/package.json b/package.json index 0636212bb..60fb1f83c 100644 --- a/package.json +++ b/package.json @@ -102,6 +102,7 @@ "test-server-ws": "node tests/websockets/server-ws.js", "test-server-webid": "node tests/websockets/server-webid.js", "coverage": "rm -rf coverage && nyc --reporter=lcov --reporter=text-summary npm test", + "coverage-ci": "rm -rf coverage && nyc --reporter=lcovonly npm test", "coverage-report": "nyc report", "lint": "eslint *.js lib/*.js tests/*.js tests/**/*.js examples/*.js flash/*.js" }, From cbebc13ffdf4ed97cab5d0b4a2cefaff4e4c6fc8 Mon Sep 17 00:00:00 2001 From: "David I. Lehn" Date: Thu, 9 Sep 2021 22:39:41 -0400 Subject: [PATCH 141/197] Fix workflow. --- .github/workflows/main.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 927425990..2223c7a94 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -24,8 +24,7 @@ jobs: strategy: matrix: node-version: [14.x] - env: - BUNDLER: [webpack, browserify] + bundler: [webpack, browserify] steps: - uses: actions/checkout@v2 - name: Use Node.js ${{ matrix.node-version }} @@ -35,6 +34,8 @@ jobs: - run: npm install - name: Run karma tests run: npm run test-karma + env: + BUNDLER: ${{ matrix.bundler }} lint: runs-on: ubuntu-latest timeout-minutes: 10 From bff212370e595f77faa9e4e4063e3b2c636026d6 Mon Sep 17 00:00:00 2001 From: "David I. Lehn" Date: Thu, 9 Sep 2021 22:43:25 -0400 Subject: [PATCH 142/197] Add 'test-node' script target. --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 60fb1f83c..f54ff72d3 100644 --- a/package.json +++ b/package.json @@ -95,7 +95,8 @@ "prepublish": "npm run build", "build": "webpack", "test-build": "webpack --config webpack-tests.config.js", - "test": "cross-env NODE_ENV=test mocha -t 30000 -R ${REPORTER:-spec} tests/unit/index.js", + "test": "npm run test-node", + "test-node": "cross-env NODE_ENV=test mocha -t 30000 -R ${REPORTER:-spec} tests/unit/index.js", "test-karma": "karma start", "test-karma-sauce": "karma start karma-sauce.conf", "test-server": "node tests/server.js", From 423b2f32b2b81153acbf4699ca6da234dd45368e Mon Sep 17 00:00:00 2001 From: "David I. Lehn" Date: Thu, 9 Sep 2021 22:45:16 -0400 Subject: [PATCH 143/197] Disable lint check. Code is not even close to ready for the modern digitalbazaar linting style. --- .github/workflows/main.yml | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 2223c7a94..3549c9294 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -36,21 +36,21 @@ jobs: run: npm run test-karma env: BUNDLER: ${{ matrix.bundler }} - lint: - runs-on: ubuntu-latest - timeout-minutes: 10 - strategy: - matrix: - node-version: [14.x] - steps: - - uses: actions/checkout@v2 - - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v1 - with: - node-version: ${{ matrix.node-version }} - - run: npm install - - name: Run eslint - run: npm run lint +# lint: +# runs-on: ubuntu-latest +# timeout-minutes: 10 +# strategy: +# matrix: +# node-version: [14.x] +# steps: +# - uses: actions/checkout@v2 +# - name: Use Node.js ${{ matrix.node-version }} +# uses: actions/setup-node@v1 +# with: +# node-version: ${{ matrix.node-version }} +# - run: npm install +# - name: Run eslint +# run: npm run lint coverage: runs-on: ubuntu-latest timeout-minutes: 10 From dc9aa5e270b3bb7c200d8cac1f161eab2867b802 Mon Sep 17 00:00:00 2001 From: "David I. Lehn" Date: Thu, 9 Sep 2021 22:54:51 -0400 Subject: [PATCH 144/197] Rename main workflow. --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 3549c9294..a63a29963 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -1,4 +1,4 @@ -name: Node.js CI +name: Main Checks on: [push] From 99676ae88403178285f25f9948e55510d39c4734 Mon Sep 17 00:00:00 2001 From: "David I. Lehn" Date: Thu, 9 Sep 2021 22:55:11 -0400 Subject: [PATCH 145/197] Update main checks workflow badge. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index bfc640fee..2e7ec3e64 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [![npm package](https://nodei.co/npm/node-forge.png?downloads=true&downloadRank=true&stars=true)](https://nodei.co/npm/node-forge/) -[![Build status](https://img.shields.io/travis/digitalbazaar/forge.svg?branch=master)](https://travis-ci.org/digitalbazaar/forge) +[![Build Status](https://github.com/digitalbazaar/forge/workflows/Main%20Checks/badge.svg)](https://github.com/digitalbazaar/forge/actions?query=workflow%3A%22Main+Checks%22) A native implementation of [TLS][] (and various other cryptographic tools) in [JavaScript][]. From f981667d2d3c0f7437090a8e2bff520252df78da Mon Sep 17 00:00:00 2001 From: Renze Nicolai Date: Sun, 14 Mar 2021 13:13:18 +0100 Subject: [PATCH 146/197] Add OIDs for surname, title and givenName --- lib/oids.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/oids.js b/lib/oids.js index 6a937f571..1c8d65a1f 100644 --- a/lib/oids.js +++ b/lib/oids.js @@ -104,6 +104,7 @@ _IN('2.16.840.1.101.3.4.1.42', 'aes256-CBC'); // certificate issuer/subject OIDs _IN('2.5.4.3', 'commonName'); +_IN('2.5.4.4', 'surname'); _IN('2.5.4.5', 'serialName'); _IN('2.5.4.6', 'countryName'); _IN('2.5.4.7', 'localityName'); @@ -111,9 +112,11 @@ _IN('2.5.4.8', 'stateOrProvinceName'); _IN('2.5.4.9', 'streetAddress'); _IN('2.5.4.10', 'organizationName'); _IN('2.5.4.11', 'organizationalUnitName'); +_IN('2.5.4.12', 'title'); _IN('2.5.4.13', 'description'); _IN('2.5.4.15', 'businessCategory'); _IN('2.5.4.17', 'postalCode'); +_IN('2.5.4.42', 'givenName'); _IN('1.3.6.1.4.1.311.60.2.1.2', 'jurisdictionOfIncorporationStateOrProvinceName'); _IN('1.3.6.1.4.1.311.60.2.1.3', 'jurisdictionOfIncorporationCountryName'); From 4d9a7939314815623885bd601e1cc64a934aa175 Mon Sep 17 00:00:00 2001 From: Renze Nicolai Date: Fri, 26 Mar 2021 14:12:31 +0100 Subject: [PATCH 147/197] Fix spacing Co-authored-by: Daniel Hensby --- lib/oids.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/oids.js b/lib/oids.js index 1c8d65a1f..2148297c4 100644 --- a/lib/oids.js +++ b/lib/oids.js @@ -104,7 +104,7 @@ _IN('2.16.840.1.101.3.4.1.42', 'aes256-CBC'); // certificate issuer/subject OIDs _IN('2.5.4.3', 'commonName'); -_IN('2.5.4.4', 'surname'); +_IN('2.5.4.4', 'surname'); _IN('2.5.4.5', 'serialName'); _IN('2.5.4.6', 'countryName'); _IN('2.5.4.7', 'localityName'); From 66145112894b8cefa94a58f1f4656407d243e9ee Mon Sep 17 00:00:00 2001 From: "David I. Lehn" Date: Thu, 9 Sep 2021 23:26:12 -0400 Subject: [PATCH 148/197] Update changelog. --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 86241ba04..8060d8eb6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,9 @@ Forge ChangeLog Due to use in the test suite, a modified version is located in `tests/support/`. +### Added +- OIDs for `surname`, `title`, and `givenName`. + ## 0.10.0 - 2020-09-01 ### Changed From e01b2ee72cf1901258ebfcb2e9852a917eb40bfe Mon Sep 17 00:00:00 2001 From: "David I. Lehn" Date: Thu, 9 Sep 2021 23:33:31 -0400 Subject: [PATCH 149/197] Fix typos. --- CHANGELOG.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8060d8eb6..5e06ef755 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,8 +6,8 @@ Forge ChangeLog ### Removed - **SECURITY**, **BREAKING**: Remove `forge.debug` API. The API has the potential for prototype pollution. This API was only briefly used by the - maintainers for internal project debug purposes and was never inteneded to be - used with untrusted user intputs. This API was not documented or advertised + maintainers for internal project debug purposes and was never intended to be + used with untrusted user inputs. This API was not documented or advertised and is being removed rather than fixed. - **BREAKING**: Remove `forge.task` API. This API was never used, documented, or advertised by the maintainers. If anyone was using this API and wishes to @@ -32,7 +32,7 @@ Forge ChangeLog from an early time when `forge` was targeted at providing general helper functions. The library direction changed to be more focused on cryptography. Many other excellent libraries are more suitable for general utilities. If - you need a replacement for these functions, consier `get`, `set`, and `unset` + you need a replacement for these functions, consider `get`, `set`, and `unset` from [lodash](https://lodash.com/). But also consider the potential similar security issues with those APIs. From 219bbb2a566d6f8169739d4887a4ab55d6a220b6 Mon Sep 17 00:00:00 2001 From: Ziding Zhang Date: Fri, 10 Sep 2021 12:57:38 +0100 Subject: [PATCH 150/197] Create SECURITY.md A simple instruction for security researchers. Closes #907 --- SECURITY.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 SECURITY.md diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 000000000..dc070c111 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,5 @@ +# Security Policy + +## Reporting a Vulnerability + +Please report security issues to `security@digitalbazaar.com` From c90cd85104e9167703e7a25f6b88e7febc9aa35a Mon Sep 17 00:00:00 2001 From: "David I. Lehn" Date: Fri, 10 Sep 2021 12:43:30 -0400 Subject: [PATCH 151/197] Use plain email. Plain email will let markdown render as a mailto link. --- SECURITY.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SECURITY.md b/SECURITY.md index dc070c111..090cbbc12 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -2,4 +2,4 @@ ## Reporting a Vulnerability -Please report security issues to `security@digitalbazaar.com` +Please report security issues to security@digitalbazaar.com. From c0bb359afca73bb0f3ba6feb3f93bbcb9166af2e Mon Sep 17 00:00:00 2001 From: Kevin Backhouse Date: Mon, 11 Oct 2021 18:07:48 +0100 Subject: [PATCH 152/197] Fix double call of String.fromCharCode. --- lib/prng.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/prng.js b/lib/prng.js index c2f5f0518..d3bd22e05 100644 --- a/lib/prng.js +++ b/lib/prng.js @@ -317,7 +317,7 @@ prng.create = function(plugin) { // throw in more pseudo random next = seed >>> (i << 3); next ^= Math.floor(Math.random() * 0x0100); - b.putByte(String.fromCharCode(next & 0xFF)); + b.putByte(next & 0xFF); } } } From 6a10f7c5bad32286fd2a02eac350109f2333a272 Mon Sep 17 00:00:00 2001 From: "David I. Lehn" Date: Thu, 9 Sep 2021 23:35:17 -0400 Subject: [PATCH 153/197] Fix OID `serialName` to `serialNumber`. - OID 2.5.4.5 should be `serialNumber`. --- CHANGELOG.md | 5 +++++ lib/oids.js | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5e06ef755..3071b09ad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,11 @@ Forge ChangeLog ### Added - OIDs for `surname`, `title`, and `givenName`. +### Fixed +- **BREAKING**: OID 2.5.4.5 name fixed from `serialName` to `serialNumber`. + Depending on how applications used this id to name association it could cause + compatibility issues. + ## 0.10.0 - 2020-09-01 ### Changed diff --git a/lib/oids.js b/lib/oids.js index 2148297c4..1c86c2189 100644 --- a/lib/oids.js +++ b/lib/oids.js @@ -105,7 +105,7 @@ _IN('2.16.840.1.101.3.4.1.42', 'aes256-CBC'); // certificate issuer/subject OIDs _IN('2.5.4.3', 'commonName'); _IN('2.5.4.4', 'surname'); -_IN('2.5.4.5', 'serialName'); +_IN('2.5.4.5', 'serialNumber'); _IN('2.5.4.6', 'countryName'); _IN('2.5.4.7', 'localityName'); _IN('2.5.4.8', 'stateOrProvinceName'); From e1a740d0be6c773af1840e0f0620994b8beeb020 Mon Sep 17 00:00:00 2001 From: ctcpip Date: Fri, 20 Aug 2021 14:10:25 -0500 Subject: [PATCH 154/197] =?UTF-8?q?=F0=9F=94=92=20change=20CSR=20examples?= =?UTF-8?q?=20to=20use=202048=20bits?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- examples/create-cert.js | 4 ++-- examples/create-csr.js | 4 ++-- examples/create-pkcs12.js | 4 ++-- examples/sign-p7.js | 4 ++-- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 2e7ec3e64..c308ebe06 100644 --- a/README.md +++ b/README.md @@ -1451,7 +1451,7 @@ __Examples__ ```js // generate a key pair -var keys = forge.pki.rsa.generateKeyPair(1024); +var keys = forge.pki.rsa.generateKeyPair(2048); // create a certification request (CSR) var csr = forge.pki.createCertificationRequest(); diff --git a/examples/create-cert.js b/examples/create-cert.js index 365f5a782..03df72c95 100644 --- a/examples/create-cert.js +++ b/examples/create-cert.js @@ -1,7 +1,7 @@ var forge = require('..'); -console.log('Generating 1024-bit key-pair...'); -var keys = forge.pki.rsa.generateKeyPair(1024); +console.log('Generating 2048-bit key-pair...'); +var keys = forge.pki.rsa.generateKeyPair(2048); console.log('Key-pair created.'); console.log('Creating self-signed certificate...'); diff --git a/examples/create-csr.js b/examples/create-csr.js index e5be773a2..8961a31fd 100644 --- a/examples/create-csr.js +++ b/examples/create-csr.js @@ -1,7 +1,7 @@ var forge = require('..'); -console.log('Generating 1024-bit key-pair...'); -var keys = forge.pki.rsa.generateKeyPair(1024); +console.log('Generating 2048-bit key-pair...'); +var keys = forge.pki.rsa.generateKeyPair(2048); console.log('Key-pair created.'); console.log('Creating certification request (CSR) ...'); diff --git a/examples/create-pkcs12.js b/examples/create-pkcs12.js index 1125fc0f1..d41965fb1 100644 --- a/examples/create-pkcs12.js +++ b/examples/create-pkcs12.js @@ -2,8 +2,8 @@ var forge = require('..'); try { // generate a keypair - console.log('Generating 1024-bit key-pair...'); - var keys = forge.pki.rsa.generateKeyPair(1024); + console.log('Generating 2048-bit key-pair...'); + var keys = forge.pki.rsa.generateKeyPair(2048); console.log('Key-pair created.'); // create a certificate diff --git a/examples/sign-p7.js b/examples/sign-p7.js index 406ce787e..73d07e566 100644 --- a/examples/sign-p7.js +++ b/examples/sign-p7.js @@ -42,8 +42,8 @@ function createSigner(name) { console.log('Creating signer "' + name + '"...'); // generate a keypair - console.log('Generating 1024-bit key-pair...'); - var keys = forge.pki.rsa.generateKeyPair(1024); + console.log('Generating 2048-bit key-pair...'); + var keys = forge.pki.rsa.generateKeyPair(2048); console.log('Key-pair created:'); console.log(forge.pki.privateKeyToPem(keys.privateKey)); console.log(forge.pki.publicKeyToPem(keys.publicKey)); From db8016c805371e72b06d8e2edfe0ace0df934a5e Mon Sep 17 00:00:00 2001 From: "David I. Lehn" Date: Thu, 21 Oct 2021 20:15:32 -0400 Subject: [PATCH 155/197] Remove forge.util.parseUrl. - Switch URL parsing to the WHATWG URL Standard `URL` API. - Older browser or Node.js usage of related code might now require a URL polyfill. --- CHANGELOG.md | 16 +++++++++++++ README.md | 4 ---- lib/http.js | 39 +++++++++++++------------------- lib/util.js | 37 ------------------------------ lib/xhr.js | 14 +++++++----- package.json | 2 +- tests/websockets/server-webid.js | 7 +++--- 7 files changed, 45 insertions(+), 74 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3071b09ad..0bd63e496 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,12 +9,22 @@ Forge ChangeLog maintainers for internal project debug purposes and was never intended to be used with untrusted user inputs. This API was not documented or advertised and is being removed rather than fixed. +- **SECURITY**, **BREAKING**: Remove `forge.util.parseUrl()` (and + `forge.http.parseUrl` alias) and use the [WHATWG URL + Standard](https://url.spec.whatwg.org/). `URL` is supported by modern browers + and modern Node.js. This change is needed to address URL parsing security + issues. If `forge.util.parseUrl()` is used directly or through `forge.xhr` or + `forge.http` APIs, and support is needed for environments without `URL` + support, then a polyfill must be used. - **BREAKING**: Remove `forge.task` API. This API was never used, documented, or advertised by the maintainers. If anyone was using this API and wishes to continue development it in other project, please let the maintainers know. Due to use in the test suite, a modified version is located in `tests/support/`. +### Changed +- **BREAKING**: Increase supported Node.js version to 6.13.0 for URL support. + ### Added - OIDs for `surname`, `title`, and `givenName`. @@ -23,6 +33,12 @@ Forge ChangeLog Depending on how applications used this id to name association it could cause compatibility issues. +### Notes +- The URL related changes may expose bugs in some of the networking related + code (unrelated to the much wider used cryptography code). The automated and + manual test coverage for this code is weak at best. Issues or patches to + update the code or tests would be appriciated. + ## 0.10.0 - 2020-09-01 ### Changed diff --git a/README.md b/README.md index c308ebe06..bddcffe6e 100644 --- a/README.md +++ b/README.md @@ -1968,10 +1968,6 @@ var nodeBuffer = Buffer.from(forgeBuffer.getBytes(), 'binary'); // make sure you specify the encoding as 'binary' var nodeBuffer = Buffer.from('CAFE', 'hex'); var forgeBuffer = forge.util.createBuffer(nodeBuffer.toString('binary')); - -// parse a URL -var parsed = forge.util.parseUrl('http://example.com/foo?bar=baz'); -// parsed.scheme, parsed.host, parsed.port, parsed.path, parsed.fullHost ``` diff --git a/lib/http.js b/lib/http.js index 0ae863050..fe52986b1 100644 --- a/lib/http.js +++ b/lib/http.js @@ -33,8 +33,8 @@ var _getStorageId = function(client) { // browsers (if this is undesirable) // navigator.userAgent return 'forge.http.' + - client.url.scheme + '.' + - client.url.host + '.' + + client.url.protocol.slice(0, -1) + '.' + + client.url.hostname + '.' + client.url.port; }; @@ -121,7 +121,7 @@ var _doRequest = function(client, socket) { // connect socket.options.request.connectTime = +new Date(); socket.connect({ - host: client.url.host, + host: client.url.hostname, port: client.url.port, policyPort: client.policyPort, policyUrl: client.policyUrl @@ -310,7 +310,7 @@ var _initSocket = function(client, socket, tlsOptions) { // prime socket by connecting and caching TLS session, will do // next request from there socket.connect({ - host: client.url.host, + host: client.url.hostname, port: client.url.port, policyPort: client.policyPort, policyUrl: client.policyUrl @@ -405,7 +405,7 @@ var _readCookies = function(client, response) { * * @param options: * url: the url to connect to (scheme://host:port). - * socketPool: the flash socket pool to use. + * socketPool: the flash socket pool to use. * policyPort: the flash policy port to use (if other than the * socket pool default), use 0 for flash default. * policyUrl: the flash policy file URL to use (if provided will @@ -441,8 +441,10 @@ http.createClient = function(options) { // get scheme, host, and port from url options.url = (options.url || window.location.protocol + '//' + window.location.host); - var url = http.parseUrl(options.url); - if(!url) { + var url; + try { + url = new URL(options.url); + } catch(e) { var error = new Error('Invalid url.'); error.details = {url: options.url}; throw error; @@ -469,7 +471,7 @@ http.createClient = function(options) { // idle sockets idle: [], // whether or not the connections are secure - secure: (url.scheme === 'https'), + secure: (url.protocol === 'https:'), // cookie jar (key'd off of name and then path, there is only 1 domain // and one setting for secure per client so name+path is unique) cookies: {}, @@ -497,7 +499,7 @@ http.createClient = function(options) { if(depth === 0 && verified === true) { // compare common name to url host var cn = certs[depth].subject.getField('CN'); - if(cn === null || client.url.host !== cn.value) { + if(cn === null || client.url.hostname !== cn.value) { verified = { message: 'Certificate common name does not match url host.' }; @@ -512,7 +514,7 @@ http.createClient = function(options) { tlsOptions = { caStore: caStore, cipherSuites: options.cipherSuites || null, - virtualHost: options.virtualHost || url.host, + virtualHost: options.virtualHost || url.hostname, verify: options.verify || _defaultCertificateVerify, getCertificate: options.getCertificate || null, getPrivateKey: options.getPrivateKey || null, @@ -552,7 +554,7 @@ http.createClient = function(options) { client.send = function(options) { // add host header if not set if(options.request.getField('Host') === null) { - options.request.setField('Host', client.url.fullHost); + options.request.setField('Host', client.url.origin); } // set default dummy handlers @@ -1307,15 +1309,6 @@ http.createResponse = function() { return response; }; -/** - * Parses the scheme, host, and port from an http(s) url. - * - * @param str the url string. - * - * @return the parsed url object or null if the url is invalid. - */ -http.parseUrl = forge.util.parseUrl; - /** * Returns true if the given url is within the given cookie's domain. * @@ -1336,11 +1329,11 @@ http.withinCookieDomain = function(url, cookie) { // ensure domain starts with a '.' // parse URL as necessary if(typeof url === 'string') { - url = http.parseUrl(url); + url = new URL(url); } - // add '.' to front of URL host to match against domain - var host = '.' + url.host; + // add '.' to front of URL hostname to match against domain + var host = '.' + url.hostname; // if the host ends with domain then it falls within it var idx = host.lastIndexOf(domain); diff --git a/lib/util.js b/lib/util.js index 98dfd3427..5100eab6e 100644 --- a/lib/util.js +++ b/lib/util.js @@ -2258,43 +2258,6 @@ util.clearItems = function(api, id, location) { _callStorageFunction(_clearItems, arguments, location); }; -/** - * Parses the scheme, host, and port from an http(s) url. - * - * @param str the url string. - * - * @return the parsed url object or null if the url is invalid. - */ -util.parseUrl = function(str) { - // FIXME: this regex looks a bit broken - var regex = /^(https?):\/\/([^:&^\/]*):?(\d*)(.*)$/g; - regex.lastIndex = 0; - var m = regex.exec(str); - var url = (m === null) ? null : { - full: str, - scheme: m[1], - host: m[2], - port: m[3], - path: m[4] - }; - if(url) { - url.fullHost = url.host; - if(url.port) { - if(url.port !== 80 && url.scheme === 'http') { - url.fullHost += ':' + url.port; - } else if(url.port !== 443 && url.scheme === 'https') { - url.fullHost += ':' + url.port; - } - } else if(url.scheme === 'http') { - url.port = 80; - } else if(url.scheme === 'https') { - url.port = 443; - } - url.full = url.scheme + '://' + url.fullHost; - } - return url; -}; - /* Storage for query variables */ var _queryVariables = null; diff --git a/lib/xhr.js b/lib/xhr.js index e493c3b60..fa928352b 100644 --- a/lib/xhr.js +++ b/lib/xhr.js @@ -151,7 +151,7 @@ xhrApi.init = function(options) { getPrivateKey: options.getPrivateKey, getSignature: options.getSignature }); - _clients[_client.url.full] = _client; + _clients[_client.url.origin] = _client; forge.log.debug(cat, 'ready'); }; @@ -380,8 +380,10 @@ xhrApi.create = function(options) { // use default _state.client = _client; } else { - var url = http.parseUrl(options.url); - if(!url) { + var url; + try { + url = new URL(options.url); + } catch(e) { var error = new Error('Invalid url.'); error.details = { url: options.url @@ -389,9 +391,9 @@ xhrApi.create = function(options) { } // find client - if(url.full in _clients) { + if(url.origin in _clients) { // client found - _state.client = _clients[url.full]; + _state.client = _clients[url.origin]; } else { // create client _state.client = http.createClient({ @@ -409,7 +411,7 @@ xhrApi.create = function(options) { getPrivateKey: options.getPrivateKey, getSignature: options.getSignature }); - _clients[url.full] = _state.client; + _clients[url.origin] = _state.client; } } diff --git a/package.json b/package.json index f54ff72d3..22ff40e46 100644 --- a/package.json +++ b/package.json @@ -60,7 +60,7 @@ "dist/*.min.js.map" ], "engines": { - "node": ">= 6.0.0" + "node": ">= 6.13.0" }, "keywords": [ "aes", diff --git a/tests/websockets/server-webid.js b/tests/websockets/server-webid.js index 6f7cf37b8..5319372bb 100644 --- a/tests/websockets/server-webid.js +++ b/tests/websockets/server-webid.js @@ -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' From aea85c5cb9e7a1a180298cb4fd84e39cea254e03 Mon Sep 17 00:00:00 2001 From: "David I. Lehn" Date: Tue, 28 Dec 2021 00:11:31 -0500 Subject: [PATCH 156/197] Remove URL related APIs. **BREAKING**: Remove `forge.util.makeLink`, `forge.util.makeRequest`, `forge.util.parseFragment`, `forge.util.getQueryVariables`. Replace with `URL`, `URLSearchParams`, and custom code as needed. --- CHANGELOG.md | 3 + lib/log.js | 15 ++- lib/util.js | 218 -------------------------------------- tests/legacy/loginDemo.js | 10 +- 4 files changed, 18 insertions(+), 228 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0bd63e496..1e4bd343e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,9 @@ Forge ChangeLog continue development it in other project, please let the maintainers know. Due to use in the test suite, a modified version is located in `tests/support/`. +- **BREAKING**: Remove `forge.util.makeLink`, `forge.util.makeRequest`, + `forge.util.parseFragment`, `forge.util.getQueryVariables`. Replace with + `URL`, `URLSearchParams`, and custom code as needed. ### Changed - **BREAKING**: Increase supported Node.js version to 6.13.0 for URL support. diff --git a/lib/log.js b/lib/log.js index 8d36f4a89..b8a265c20 100644 --- a/lib/log.js +++ b/lib/log.js @@ -298,15 +298,20 @@ if(typeof(console) !== 'undefined' && 'log' in console) { * that could otherwise be limited by a user config. */ if(sConsoleLogger !== null) { - var query = forge.util.getQueryVariables(); - if('console.level' in query) { + var query; + if(typeof(window) !== 'undefined' && window.location) { + query = new URL(window.location.href).searchParams; + } else { + query = new URLSearchParams(); + } + if(query.has('console.level')) { // set with last value forge.log.setLevel( - sConsoleLogger, query['console.level'].slice(-1)[0]); + sConsoleLogger, query.get('console.level').slice(-1)[0]); } - if('console.lock' in query) { + if(query.has('console.lock')) { // set with last value - var lock = query['console.lock'].slice(-1)[0]; + var lock = query.get('console.lock').slice(-1)[0]; if(lock == 'true') { forge.log.lock(sConsoleLogger); } diff --git a/lib/util.js b/lib/util.js index 5100eab6e..aaede5ad2 100644 --- a/lib/util.js +++ b/lib/util.js @@ -2258,224 +2258,6 @@ util.clearItems = function(api, id, location) { _callStorageFunction(_clearItems, arguments, location); }; -/* Storage for query variables */ -var _queryVariables = null; - -/** - * Returns the window location query variables. Query is parsed on the first - * call and the same object is returned on subsequent calls. The mapping - * is from keys to an array of values. Parameters without values will have - * an object key set but no value added to the value array. Values are - * unescaped. - * - * ...?k1=v1&k2=v2: - * { - * "k1": ["v1"], - * "k2": ["v2"] - * } - * - * ...?k1=v1&k1=v2: - * { - * "k1": ["v1", "v2"] - * } - * - * ...?k1=v1&k2: - * { - * "k1": ["v1"], - * "k2": [] - * } - * - * ...?k1=v1&k1: - * { - * "k1": ["v1"] - * } - * - * ...?k1&k1: - * { - * "k1": [] - * } - * - * @param query the query string to parse (optional, default to cached - * results from parsing window location search query). - * - * @return object mapping keys to variables. - */ -util.getQueryVariables = function(query) { - var parse = function(q) { - var rval = {}; - var kvpairs = q.split('&'); - for(var i = 0; i < kvpairs.length; i++) { - var pos = kvpairs[i].indexOf('='); - var key; - var val; - if(pos > 0) { - key = kvpairs[i].substring(0, pos); - val = kvpairs[i].substring(pos + 1); - } else { - key = kvpairs[i]; - val = null; - } - if(!(key in rval)) { - rval[key] = []; - } - // disallow overriding object prototype keys - if(!(key in Object.prototype) && val !== null) { - rval[key].push(unescape(val)); - } - } - return rval; - }; - - var rval; - if(typeof(query) === 'undefined') { - // set cached variables if needed - if(_queryVariables === null) { - if(typeof(window) !== 'undefined' && window.location && window.location.search) { - // parse window search query - _queryVariables = parse(window.location.search.substring(1)); - } else { - // no query variables available - _queryVariables = {}; - } - } - rval = _queryVariables; - } else { - // parse given query - rval = parse(query); - } - return rval; -}; - -/** - * Parses a fragment into a path and query. This method will take a URI - * fragment and break it up as if it were the main URI. For example: - * /bar/baz?a=1&b=2 - * results in: - * { - * path: ["bar", "baz"], - * query: {"k1": ["v1"], "k2": ["v2"]} - * } - * - * @return object with a path array and query object. - */ -util.parseFragment = function(fragment) { - // default to whole fragment - var fp = fragment; - var fq = ''; - // split into path and query if possible at the first '?' - var pos = fragment.indexOf('?'); - if(pos > 0) { - fp = fragment.substring(0, pos); - fq = fragment.substring(pos + 1); - } - // split path based on '/' and ignore first element if empty - var path = fp.split('/'); - if(path.length > 0 && path[0] === '') { - path.shift(); - } - // convert query into object - var query = (fq === '') ? {} : util.getQueryVariables(fq); - - return { - pathString: fp, - queryString: fq, - path: path, - query: query - }; -}; - -/** - * Makes a request out of a URI-like request string. This is intended to - * be used where a fragment id (after a URI '#') is parsed as a URI with - * path and query parts. The string should have a path beginning and - * delimited by '/' and optional query parameters following a '?'. The - * query should be a standard URL set of key value pairs delimited by - * '&'. For backwards compatibility the initial '/' on the path is not - * required. The request object has the following API, (fully described - * in the method code): - * { - * path: . - * query: , - * getPath(i): get part or all of the split path array, - * getQuery(k, i): get part or all of a query key array, - * getQueryLast(k, _default): get last element of a query key array. - * } - * - * @return object with request parameters. - */ -util.makeRequest = function(reqString) { - var frag = util.parseFragment(reqString); - var req = { - // full path string - path: frag.pathString, - // full query string - query: frag.queryString, - /** - * Get path or element in path. - * - * @param i optional path index. - * - * @return path or part of path if i provided. - */ - getPath: function(i) { - return (typeof(i) === 'undefined') ? frag.path : frag.path[i]; - }, - /** - * Get query, values for a key, or value for a key index. - * - * @param k optional query key. - * @param i optional query key index. - * - * @return query, values for a key, or value for a key index. - */ - getQuery: function(k, i) { - var rval; - if(typeof(k) === 'undefined') { - rval = frag.query; - } else { - rval = frag.query[k]; - if(rval && typeof(i) !== 'undefined') { - rval = rval[i]; - } - } - return rval; - }, - getQueryLast: function(k, _default) { - var rval; - var vals = req.getQuery(k); - if(vals) { - rval = vals[vals.length - 1]; - } else { - rval = _default; - } - return rval; - } - }; - return req; -}; - -/** - * Makes a URI out of a path, an object with query parameters, and a - * fragment. Uses jQuery.param() internally for query string creation. - * If the path is an array, it will be joined with '/'. - * - * @param path string path or array of strings. - * @param query object with query parameters. (optional) - * @param fragment fragment string. (optional) - * - * @return string object with request parameters. - */ -util.makeLink = function(path, query, fragment) { - // join path parts if needed - path = jQuery.isArray(path) ? path.join('/') : path; - - var qstr = jQuery.param(query || {}); - fragment = fragment || ''; - return path + - ((qstr.length > 0) ? ('?' + qstr) : '') + - ((fragment.length > 0) ? ('#' + fragment) : ''); -}; - /** * Check if an object is empty. * diff --git a/tests/legacy/loginDemo.js b/tests/legacy/loginDemo.js index 35dab6b18..dc0d301db 100644 --- a/tests/legacy/loginDemo.js +++ b/tests/legacy/loginDemo.js @@ -29,11 +29,11 @@ var init = function($) try { // get query variables - var query = forge.util.getQueryVariables(); - var domain = query.domain || ''; - var auth = query.auth || ''; - var redirect = query.redirect || ''; - var pport = query.pport || 843; + var query = new URL(window.location.href).searchParams; + var domain = query.get('domain') || ''; + var auth = query.get('auth') || ''; + var redirect = query.get('redirect') || ''; + var pport = parseInt(query.get('pport')) || 843; redirect = 'https://' + domain + '/' + redirect; if(domain) { From a3f48e4078211ec0176b6e387d83bbc3f8470b0a Mon Sep 17 00:00:00 2001 From: "David I. Lehn" Date: Tue, 4 Jan 2022 18:31:54 -0500 Subject: [PATCH 157/197] Fix spelling. Co-authored-by: Dave Longley --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1e4bd343e..c572f3817 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -40,7 +40,7 @@ Forge ChangeLog - The URL related changes may expose bugs in some of the networking related code (unrelated to the much wider used cryptography code). The automated and manual test coverage for this code is weak at best. Issues or patches to - update the code or tests would be appriciated. + update the code or tests would be appreciated. ## 0.10.0 - 2020-09-01 From 27286feec0f9ac0094a6b7a3041e5c1a412ad7a5 Mon Sep 17 00:00:00 2001 From: "David I. Lehn" Date: Tue, 4 Jan 2022 18:32:19 -0500 Subject: [PATCH 158/197] Fix style. Co-authored-by: Dave Longley --- lib/log.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/log.js b/lib/log.js index b8a265c20..5228047f6 100644 --- a/lib/log.js +++ b/lib/log.js @@ -299,7 +299,7 @@ if(typeof(console) !== 'undefined' && 'log' in console) { */ if(sConsoleLogger !== null) { var query; - if(typeof(window) !== 'undefined' && window.location) { + if(typeof window !== 'undefined' && window.location) { query = new URL(window.location.href).searchParams; } else { query = new URLSearchParams(); From 5f8d5c215f157faf8d2e1d8061c4d6086363f871 Mon Sep 17 00:00:00 2001 From: "David I. Lehn" Date: Tue, 4 Jan 2022 20:54:48 -0500 Subject: [PATCH 159/197] Update docs. - Update to v1.0.0. - Update changelog. - Update release details. - Remove mentions of bower and forge-dist. - Rename master to main. - Add Libera.Chat IRC channel. - Minor other fixes. --- .gitignore | 1 - CHANGELOG.md | 22 ++++++++++----- README.md | 28 ++++++------------- RELEASE.md | 78 +++++++--------------------------------------------- 4 files changed, 34 insertions(+), 95 deletions(-) diff --git a/.gitignore b/.gitignore index 134b7dedd..01519a399 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,6 @@ *.py[co] *.sw[nop] *~ -.bower.json .cdtproject .classpath .cproject diff --git a/CHANGELOG.md b/CHANGELOG.md index c572f3817..39c7139e4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,15 @@ Forge ChangeLog =============== -## 0.11.0 - 2021-xx-xx +## 1.0.0 - 2022-xx-xx + +### Notes +- **1.0.0**! +- This project is over a decade old! Time for a 1.0.0 release. +- The URL related changes may expose bugs in some of the networking related + code (unrelated to the much wider used cryptography code). The automated and + manual test coverage for this code is weak at best. Issues or patches to + update the code or tests would be appreciated. ### Removed - **SECURITY**, **BREAKING**: Remove `forge.debug` API. The API has the @@ -27,6 +35,12 @@ Forge ChangeLog ### Changed - **BREAKING**: Increase supported Node.js version to 6.13.0 for URL support. +- **BREAKING**: Renamed `master` branch to `main`. +- **BREAKING**: Release process updated to use tooling that prefixes versions + with `v`. Other tools, scripts, or scanners may need to adapt. +- **BREAKING**: Remove docs related to Bower and + [forge-dist](https://github.com/digitalbazaar/forge-dist). Use [NPM][] or + another CDN. (Also be sure to read "Security Considerations" in the README.) ### Added - OIDs for `surname`, `title`, and `givenName`. @@ -36,12 +50,6 @@ Forge ChangeLog Depending on how applications used this id to name association it could cause compatibility issues. -### Notes -- The URL related changes may expose bugs in some of the networking related - code (unrelated to the much wider used cryptography code). The automated and - manual test coverage for this code is weak at best. Issues or patches to - update the code or tests would be appreciated. - ## 0.10.0 - 2020-09-01 ### Changed diff --git a/README.md b/README.md index bddcffe6e..6f3279efb 100644 --- a/README.md +++ b/README.md @@ -105,7 +105,7 @@ not be regularly updated. If you want to use forge with [Node.js][], it is available through `npm`: -https://npmjs.org/package/node-forge +https://www.npmjs.com/package/node-forge Installation: @@ -120,24 +120,12 @@ var forge = require('node-forge'); The npm package includes pre-built `forge.min.js`, `forge.all.min.js`, and `prime.worker.min.js` using the [UMD][] format. -### Bundle / Bower - -Each release is published in a separate repository as pre-built and minimized -basic forge bundles using the [UMD][] format. - -https://github.com/digitalbazaar/forge-dist - -This bundle can be used in many environments. In particular it can be installed -with [Bower][]: - - bower install forge - ### jsDelivr CDN To use it via [jsDelivr](https://www.jsdelivr.com/package/npm/node-forge) include this in your html: ```html - + ``` ### unpkg CDN @@ -145,7 +133,7 @@ To use it via [jsDelivr](https://www.jsdelivr.com/package/npm/node-forge) includ To use it via [unpkg](https://unpkg.com/#/) include this in your html: ```html - + ``` ### Development Requirements @@ -2003,8 +1991,8 @@ When using this code please keep the following in mind: runtime characteristics, runtime optimization, code optimization, code minimization, code obfuscation, bundling tools, possible bugs, the Forge code itself, and so on. -- If using pre-built bundles from [Bower][] or similar be aware someone else - ran the tools to create those files. +- If using pre-built bundles from [NPM][], another CDN, or similar, be aware + someone else ran the tools to create those files. - Use a secure transport channel such as [TLS][] to load scripts and consider using additional security mechanisms such as [Subresource Integrity][] script attributes. @@ -2030,7 +2018,8 @@ Contact * Code: https://github.com/digitalbazaar/forge * Bugs: https://github.com/digitalbazaar/forge/issues * Email: support@digitalbazaar.com -* IRC: [#forgejs][] on [freenode][] +* IRC: [#forgejs][] on [Libera.Chat][] (people may also be on [freenode][] for + historical reasons). Donations --------- @@ -2045,7 +2034,6 @@ Financial support is welcome and helps contribute to futher development: [3DES]: https://en.wikipedia.org/wiki/Triple_DES [AES]: https://en.wikipedia.org/wiki/Advanced_Encryption_Standard [ASN.1]: https://en.wikipedia.org/wiki/ASN.1 -[Bower]: https://bower.io/ [Browserify]: http://browserify.org/ [CBC]: https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation [CFB]: https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation @@ -2058,7 +2046,9 @@ Financial support is welcome and helps contribute to futher development: [HMAC]: https://en.wikipedia.org/wiki/HMAC [JavaScript]: https://en.wikipedia.org/wiki/JavaScript [Karma]: https://karma-runner.github.io/ +[Libera.Chat]: https://libera.chat/ [MD5]: https://en.wikipedia.org/wiki/MD5 +[NPM]: https://www.npmjs.com/ [Node.js]: https://nodejs.org/ [OFB]: https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation [PKCS#10]: https://en.wikipedia.org/wiki/Certificate_signing_request diff --git a/RELEASE.md b/RELEASE.md index c90a249f4..92c01d248 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -1,77 +1,19 @@ Forge Release Process ===================== -Versioning ----------- +Prepare a Release +----------------- * Follow the [Semantic Versioning][] guidelines. -* Use version X.Y.Z-dev in dev mode. -* Use version X.Y.Z for releases. - -Master Branch Release Process ------------------------------ - * Ensure [tests pass](./README.md#testing). +* Ensure [CHANGELOG.md](./CHANGELOG.md) is up-to-date using [Keep a + CHANGELOG][] style. -## Update the main repository: - -* Commit changes. -* Update the [CHANGELOG](./CHANGELOG.md) as needed using rougly - [Keep a CHANGELOG][] style. -* `$EDITOR package.json`: update to release version and remove `-dev` suffix. -* `git commit package.json -m "Release {version}."` -* `git tag {version}` -* `$EDITOR package.json`: update to next version and add `-dev` suffix. -* `git commit package.json -m "Start {next-version}."` -* `git push` -* `git push --tags` - -## Publish to NPM: - -To ensure a clean upload, use a clean updated checkout, and run the following: - -* `git checkout {version}` -* `npm install` -* `npm publish` - -## Update bundled distribution - -This is kept in a different repository to avoid the accumulated size when -adding per-release bundles. - -* Checkout [forge-dist][]. -* Build a clean Forge version you want to distribute: - * `git checkout {version}` - * `npm install` - * `npm run build` -* Copy files to `forge-dist`: - * `cp dist/forge.min.js{,.map} dist/prime.worker.min.js{,.map} FORGEDIST/dist/` -* Release `forge-dist`: - * `git commit -a -m "Release {version}."` - * `git tag {version}` - * `git push` - * `git push origin {version}` - -Older Branch Release Process ----------------------------- - -In order to provide support for Bower (and similar) for current built bundle -releases and historical releases the [forge-dist][] repository needs to be -updated with code changes and tags from the main repository. Once a historical -branch, like 0.6.x, on the main repository is updated and tagged, do the -following: +Publish to NPM +-------------- -* Checkout [forge-dist][]. -* Setup an upstream branch: - * `git remote add upstream git@github.com:digitalbazaar/forge.git` - * `git fetch upstream` -* Merge changes: - * `git checkout 0.6.x` - * `git merge upstream/0.6.x` -* Push code and tag(s): - * `git push` - * `git push origin {version}` +As of Forge 1.0.0 publishing is performed using the `pubnpm` script from +https://github.com/digitalbazaar/publish-script. -[Keep a CHANGELOG]: http://keepachangelog.com/ -[Semantic Versioning]: http://semver.org/ -[forge-dist]: https://github.com/digitalbazaar/forge-dist +[Keep a CHANGELOG]: https://keepachangelog.com/ +[Semantic Versioning]: https://semver.org/ From 69395d0684eb56ee0cdd9a0ea0e541a4013dafd2 Mon Sep 17 00:00:00 2001 From: "David I. Lehn" Date: Tue, 4 Jan 2022 20:59:12 -0500 Subject: [PATCH 160/197] Fix install note. --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 39c7139e4..8a63b7089 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,8 +39,8 @@ Forge ChangeLog - **BREAKING**: Release process updated to use tooling that prefixes versions with `v`. Other tools, scripts, or scanners may need to adapt. - **BREAKING**: Remove docs related to Bower and - [forge-dist](https://github.com/digitalbazaar/forge-dist). Use [NPM][] or - another CDN. (Also be sure to read "Security Considerations" in the README.) + [forge-dist](https://github.com/digitalbazaar/forge-dist). Install using + [another method](./README.md#installation). ### Added - OIDs for `surname`, `title`, and `givenName`. From 9055d6f6099e5199d7e62027e8eb0f5860d33938 Mon Sep 17 00:00:00 2001 From: "David I. Lehn" Date: Tue, 4 Jan 2022 22:00:24 -0500 Subject: [PATCH 161/197] Update changelog. --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8a63b7089..981521ce3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,7 @@ Forge ChangeLog =============== -## 1.0.0 - 2022-xx-xx +## 1.0.0 - 2022-01-04 ### Notes - **1.0.0**! From bc1a8d8837ef29672dbd320c5d03f06068ae4116 Mon Sep 17 00:00:00 2001 From: "David I. Lehn" Date: Tue, 4 Jan 2022 22:00:24 -0500 Subject: [PATCH 162/197] Release 1.0.0. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 22ff40e46..1d010d195 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "node-forge", - "version": "0.10.1-dev", + "version": "1.0.0", "description": "JavaScript implementations of network transports, cryptography, ciphers, PKI, message digests, and various utilities.", "homepage": "https://github.com/digitalbazaar/forge", "author": { From ea4a83c9bb8cdc667623ff7ffc750a628f8d9181 Mon Sep 17 00:00:00 2001 From: "David I. Lehn" Date: Tue, 4 Jan 2022 22:00:57 -0500 Subject: [PATCH 163/197] Start 1.0.1-0. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 1d010d195..ff9979cfe 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "node-forge", - "version": "1.0.0", + "version": "1.0.1-0", "description": "JavaScript implementations of network transports, cryptography, ciphers, PKI, message digests, and various utilities.", "homepage": "https://github.com/digitalbazaar/forge", "author": { From cebb5ff18773610b4078193c864f028b4ab417e6 Mon Sep 17 00:00:00 2001 From: Daniel Hensby Date: Fri, 23 Apr 2021 12:19:59 +0100 Subject: [PATCH 164/197] FIX [x509] Certificate issuer and subject hash correctly computed --- lib/x509.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/x509.js b/lib/x509.js index 95dbc2946..5dce82da1 100644 --- a/lib/x509.js +++ b/lib/x509.js @@ -1372,6 +1372,8 @@ pki.certificateFromAsn1 = function(obj, computeHash) { // handle issuer, build issuer message digest var imd = forge.md.sha1.create(); + var ibytes = asn1.toDer(capture.certIssuer); + imd.update(ibytes.getBytes()); cert.issuer.getField = function(sn) { return _getAttribute(cert.issuer, sn); }; @@ -1379,7 +1381,7 @@ pki.certificateFromAsn1 = function(obj, computeHash) { _fillMissingFields([attr]); cert.issuer.attributes.push(attr); }; - cert.issuer.attributes = pki.RDNAttributesAsArray(capture.certIssuer, imd); + cert.issuer.attributes = pki.RDNAttributesAsArray(capture.certIssuer); if(capture.certIssuerUniqueId) { cert.issuer.uniqueId = capture.certIssuerUniqueId; } @@ -1387,6 +1389,8 @@ pki.certificateFromAsn1 = function(obj, computeHash) { // handle subject, build subject message digest var smd = forge.md.sha1.create(); + var sbytes = asn1.toDer(capture.certSubject); + smd.update(sbytes.getBytes()); cert.subject.getField = function(sn) { return _getAttribute(cert.subject, sn); }; @@ -1394,7 +1398,7 @@ pki.certificateFromAsn1 = function(obj, computeHash) { _fillMissingFields([attr]); cert.subject.attributes.push(attr); }; - cert.subject.attributes = pki.RDNAttributesAsArray(capture.certSubject, smd); + cert.subject.attributes = pki.RDNAttributesAsArray(capture.certSubject); if(capture.certSubjectUniqueId) { cert.subject.uniqueId = capture.certSubjectUniqueId; } From eca152796ab05a639e7b03b23584953f93b7af74 Mon Sep 17 00:00:00 2001 From: Daniel Hensby Date: Fri, 23 Apr 2021 12:32:12 +0100 Subject: [PATCH 165/197] Add test for issuer and subject hashes --- tests/unit/x509.js | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/tests/unit/x509.js b/tests/unit/x509.js index 43a9ea61b..8ae5c4698 100644 --- a/tests/unit/x509.js +++ b/tests/unit/x509.js @@ -1206,6 +1206,52 @@ 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 sha256WithRSAEncryption signature', function() { var certPem = '-----BEGIN CERTIFICATE-----\r\n' + 'MIIDuzCCAqOgAwIBAgIEO5vZjDANBgkqhkiG9w0BAQsFADBGMQswCQYDVQQGEwJE\r\n' + From fb9d7f1e4e2b89f8abf01f0044906b0cc07eac92 Mon Sep 17 00:00:00 2001 From: "David I. Lehn" Date: Thu, 6 Jan 2022 18:35:27 -0500 Subject: [PATCH 166/197] Update changelog. --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 981521ce3..062af27f3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ Forge ChangeLog =============== +### Fixed +- [x509]: Correctly compute certificate issuer and subject hashes to match + behavior of openssl. + ## 1.0.0 - 2022-01-04 ### Notes From 9c2aba10fd4af7799177c21f56363af696c629a5 Mon Sep 17 00:00:00 2001 From: mundry Date: Mon, 14 Oct 2019 19:09:28 +0200 Subject: [PATCH 167/197] Accept CSRs with NEW in the label for decoding. --- lib/pem.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pem.js b/lib/pem.js index aed8bdf92..7dddf1135 100644 --- a/lib/pem.js +++ b/lib/pem.js @@ -96,7 +96,7 @@ pem.decode = function(str) { var rval = []; // split string into PEM messages (be lenient w/EOF on BEGIN line) - var rMessage = /\s*-----BEGIN ([A-Z0-9- ]+)-----\r?\n?([\x21-\x7e\s]+?(?:\r?\n\r?\n))?([:A-Za-z0-9+\/=\s]+?)-----END \1-----/g; + var rMessage = /\s*-----BEGIN(?: NEW)? ([A-Z0-9- ]+)-----\r?\n?([\x21-\x7e\s]+?(?:\r?\n\r?\n))?([:A-Za-z0-9+\/=\s]+?)-----END(?: NEW)? \1-----/g; var rHeader = /([\x21-\x7e]+):\s*([\x21-\x7e\s^:]+)/; var rCRLF = /\r?\n/; var match; From e1e8762f085d0f6cd7ba216cf260cf31050d19f5 Mon Sep 17 00:00:00 2001 From: mundry Date: Tue, 15 Oct 2019 20:30:15 +0200 Subject: [PATCH 168/197] Add pem unit tests for NEW in CSR labels. --- tests/unit/pem.js | 74 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) 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'); + } + }); }); })(); From bbd80a40df7bc2d573b1e95ea7bde0fa194417c1 Mon Sep 17 00:00:00 2001 From: "David I. Lehn" Date: Thu, 6 Jan 2022 18:59:13 -0500 Subject: [PATCH 169/197] Adjust how PEM CSR with "NEW" are handled. - Remove "NEW" from regex to avoid it being accepted for all types. - Use a special case for "NEW CERTIFICATE REQUEST". - Update changelog. --- CHANGELOG.md | 2 ++ lib/pem.js | 11 +++++++++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 062af27f3..7d5621c77 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ Forge ChangeLog ### Fixed - [x509]: Correctly compute certificate issuer and subject hashes to match behavior of openssl. +- [pem]: Accept certificate requests with "NEW" in the label. "BEGIN NEW + CERTIFICATE REQUEST" handled as "BEGIN CERTIFICATE REQUEST". ## 1.0.0 - 2022-01-04 diff --git a/lib/pem.js b/lib/pem.js index 7dddf1135..1992bc77b 100644 --- a/lib/pem.js +++ b/lib/pem.js @@ -96,7 +96,7 @@ pem.decode = function(str) { var rval = []; // split string into PEM messages (be lenient w/EOF on BEGIN line) - var rMessage = /\s*-----BEGIN(?: NEW)? ([A-Z0-9- ]+)-----\r?\n?([\x21-\x7e\s]+?(?:\r?\n\r?\n))?([:A-Za-z0-9+\/=\s]+?)-----END(?: NEW)? \1-----/g; + var rMessage = /\s*-----BEGIN ([A-Z0-9- ]+)-----\r?\n?([\x21-\x7e\s]+?(?:\r?\n\r?\n))?([:A-Za-z0-9+\/=\s]+?)-----END \1-----/g; var rHeader = /([\x21-\x7e]+):\s*([\x21-\x7e\s^:]+)/; var rCRLF = /\r?\n/; var match; @@ -106,8 +106,15 @@ pem.decode = function(str) { break; } + // accept "NEW CERTIFICATE REQUEST" as "CERTIFICATE REQUEST" + // https://datatracker.ietf.org/doc/html/rfc7468#section-7 + var type = match[1]; + if(type === 'NEW CERTIFICATE REQUEST') { + type = 'CERTIFICATE REQUEST'; + } + var msg = { - type: match[1], + type: type, procType: null, contentDomain: null, dekInfo: null, From c9d3cb72cc97c5ac41a53348a0179f190135ae8e Mon Sep 17 00:00:00 2001 From: "David I. Lehn" Date: Thu, 6 Jan 2022 23:00:49 -0500 Subject: [PATCH 170/197] Update changelog. --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7d5621c77..d1bf64cc5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,8 @@ Forge ChangeLog =============== +## 1.1.0 - 2022-01-06 + ### Fixed - [x509]: Correctly compute certificate issuer and subject hashes to match behavior of openssl. From cb93fe5ce2d6f9a4fd9aaca5067c2cfe887a24ca Mon Sep 17 00:00:00 2001 From: "David I. Lehn" Date: Thu, 6 Jan 2022 23:00:49 -0500 Subject: [PATCH 171/197] Release 1.1.0. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index ff9979cfe..e6e3eb5e7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "node-forge", - "version": "1.0.1-0", + "version": "1.1.0", "description": "JavaScript implementations of network transports, cryptography, ciphers, PKI, message digests, and various utilities.", "homepage": "https://github.com/digitalbazaar/forge", "author": { From c61b204874d9341fa8bf8bb626aa532520c3e205 Mon Sep 17 00:00:00 2001 From: "David I. Lehn" Date: Thu, 6 Jan 2022 23:01:23 -0500 Subject: [PATCH 172/197] Start 1.1.1-0. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e6e3eb5e7..5855ee67c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "node-forge", - "version": "1.1.0", + "version": "1.1.1-0", "description": "JavaScript implementations of network transports, cryptography, ciphers, PKI, message digests, and various utilities.", "homepage": "https://github.com/digitalbazaar/forge", "author": { From cca7eea9d6a885913522d56f43f20d8ed6bd7f14 Mon Sep 17 00:00:00 2001 From: Jeremy Barber <38042377+JeremyBarber@users.noreply.github.com> Date: Wed, 22 Jul 2020 16:25:16 +0100 Subject: [PATCH 173/197] 'Expected' and 'Actual' issuers were backwards in verification failure message --- lib/x509.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/x509.js b/lib/x509.js index 5dce82da1..65dd854ec 100644 --- a/lib/x509.js +++ b/lib/x509.js @@ -1069,8 +1069,8 @@ pki.createCertificate = function() { 'The parent certificate did not issue the given child ' + 'certificate; the child certificate\'s issuer does not match the ' + 'parent\'s subject.'); - error.expectedIssuer = issuer.attributes; - error.actualIssuer = subject.attributes; + error.expectedIssuer = subject.attributes; + error.actualIssuer = issuer.attributes; throw error; } From 874cef8c9a2e7d756603a08a740c24dbca70df58 Mon Sep 17 00:00:00 2001 From: "David I. Lehn" Date: Fri, 7 Jan 2022 00:03:14 -0500 Subject: [PATCH 174/197] Update changelog. --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d1bf64cc5..bb5ff0362 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ Forge ChangeLog =============== +### Fixed +- [x509] 'Expected' and 'Actual' issuers were backwards in verification failure + message. + ## 1.1.0 - 2022-01-06 ### Fixed From 03d3ed73711cf7b391edeb9a50fdbaca2986a893 Mon Sep 17 00:00:00 2001 From: David Watrous Date: Wed, 21 Oct 2020 15:16:01 -0400 Subject: [PATCH 175/197] Added alternate OID 1.3.14.3.2.29 for sha1 with RSA --- lib/oids.js | 2 ++ lib/x509.js | 18 ++++++++++++------ tests/unit/x509.js | 18 ++++++++++++++++++ 3 files changed, 32 insertions(+), 6 deletions(-) diff --git a/lib/oids.js b/lib/oids.js index 1c86c2189..0ca96e989 100644 --- a/lib/oids.js +++ b/lib/oids.js @@ -42,6 +42,8 @@ _IN('1.2.840.10040.4.3', 'dsa-with-sha1'); _IN('1.3.14.3.2.7', 'desCBC'); _IN('1.3.14.3.2.26', 'sha1'); +// Deprecated equivalent of sha1WithRSAEncryption +_IN('1.3.14.3.2.29', 'sha1WithRSASignature'); _IN('2.16.840.1.101.3.4.2.1', 'sha256'); _IN('2.16.840.1.101.3.4.2.2', 'sha384'); _IN('2.16.840.1.101.3.4.2.3', 'sha512'); diff --git a/lib/x509.js b/lib/x509.js index 65dd854ec..d46e0948c 100644 --- a/lib/x509.js +++ b/lib/x509.js @@ -1081,8 +1081,9 @@ pki.createCertificate = function() { var oid = oids[child.signatureOid]; switch(oid) { case 'sha1WithRSAEncryption': - md = forge.md.sha1.create(); - break; + case 'sha1WithRSASignature': + md = forge.md.sha1.create(); + break; case 'md5WithRSAEncryption': md = forge.md.md5.create(); break; @@ -1118,8 +1119,9 @@ pki.createCertificate = function() { switch(child.signatureOid) { case oids.sha1WithRSAEncryption: - scheme = undefined; /* use PKCS#1 v1.5 padding scheme */ - break; + case oids.sha1WithRSASignature: + scheme = undefined; /* use PKCS#1 v1.5 padding scheme */ + break; case oids['RSASSA-PSS']: var hash, mgf; @@ -1339,8 +1341,9 @@ pki.certificateFromAsn1 = function(obj, computeHash) { var oid = oids[cert.signatureOid]; switch(oid) { case 'sha1WithRSAEncryption': - cert.md = forge.md.sha1.create(); - break; + case 'sha1WithRSASignature': + cert.md = forge.md.sha1.create(); + break; case 'md5WithRSAEncryption': cert.md = forge.md.md5.create(); break; @@ -1687,6 +1690,7 @@ pki.certificationRequestFromAsn1 = function(obj, computeHash) { var oid = oids[csr.signatureOid]; switch(oid) { case 'sha1WithRSAEncryption': + case 'sha1WithRSASignature': csr.md = forge.md.sha1.create(); break; case 'md5WithRSAEncryption': @@ -1857,6 +1861,7 @@ pki.createCertificationRequest = function() { var oid = oids[csr.signatureOid]; switch(oid) { case 'sha1WithRSAEncryption': + case 'sha1WithRSASignature': md = forge.md.sha1.create(); break; case 'md5WithRSAEncryption': @@ -1896,6 +1901,7 @@ pki.createCertificationRequest = function() { switch(csr.signatureOid) { case oids.sha1WithRSAEncryption: + case oids.sha1WithRSASignature: /* use PKCS#1 v1.5 padding scheme */ break; case oids['RSASSA-PSS']: diff --git a/tests/unit/x509.js b/tests/unit/x509.js index 8ae5c4698..474e97a89 100644 --- a/tests/unit/x509.js +++ b/tests/unit/x509.js @@ -1252,6 +1252,24 @@ var UTIL = require('../../lib/util'); 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' + From 2fb9995d783626aec7519641b06223c9d58f67c8 Mon Sep 17 00:00:00 2001 From: "David I. Lehn" Date: Thu, 6 Jan 2022 22:11:48 -0500 Subject: [PATCH 176/197] Add helper to create signature digest. - Reduce duplicate code. - Fix style nit. - Update changelog. --- CHANGELOG.md | 10 +++ lib/x509.js | 189 ++++++++++++++++----------------------------------- 2 files changed, 69 insertions(+), 130 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bb5ff0362..8f6c22f5d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,16 @@ Forge ChangeLog - [x509] 'Expected' and 'Actual' issuers were backwards in verification failure message. +### Added +- [oid,x509]: Added OID `1.3.14.3.2.29 / sha1WithRSASignature` for sha1 with + RSA. Considered a deprecated equivalent to `1.2.840.113549.1.1.5 / + sha1WithRSAEncryption`. See [discussion and + links](https://github.com/digitalbazaar/forge/issues/825). + +### Changed +- [x509]: Reduce duplicate code with a helper function to create a signature + digest given an signature algorithm OID. + ## 1.1.0 - 2022-01-06 ### Fixed diff --git a/lib/x509.js b/lib/x509.js index d46e0948c..d7d2f892f 100644 --- a/lib/x509.js +++ b/lib/x509.js @@ -689,6 +689,44 @@ var _readSignatureParameters = function(oid, obj, fillDefaults) { return params; }; +/** + * Create signature digest for OID. + * + * @param options + * signatureOid: the OID specifying the signature algorithm. + * type: a human readable type for error messages + * @return a created md instance. throws if unknown oid. + */ +var _createSignatureDigest = function(options) { + switch(oids[options.signatureOid]) { + case 'sha1WithRSAEncryption': + case 'sha1WithRSASignature': + return forge.md.sha1.create(); + break; + case 'md5WithRSAEncryption': + return forge.md.md5.create(); + break; + case 'sha256WithRSAEncryption': + return forge.md.sha256.create(); + break; + case 'sha384WithRSAEncryption': + return forge.md.sha384.create(); + break; + case 'sha512WithRSAEncryption': + return forge.md.sha512.create(); + break; + case 'RSASSA-PSS': + return forge.md.sha256.create(); + break; + default: + var error = new Error( + 'Could not compute ' + options.type + ' digest. ' + + 'Unknown signature OID.'); + error.signatureOid = options.signatureOid; + throw error; + } +}; + /** * Converts an X.509 certificate from PEM format. * @@ -1076,37 +1114,11 @@ pki.createCertificate = function() { var md = child.md; if(md === null) { - // check signature OID for supported signature types - if(child.signatureOid in oids) { - var oid = oids[child.signatureOid]; - switch(oid) { - case 'sha1WithRSAEncryption': - case 'sha1WithRSASignature': - md = forge.md.sha1.create(); - break; - case 'md5WithRSAEncryption': - md = forge.md.md5.create(); - break; - case 'sha256WithRSAEncryption': - md = forge.md.sha256.create(); - break; - case 'sha384WithRSAEncryption': - md = forge.md.sha384.create(); - break; - case 'sha512WithRSAEncryption': - md = forge.md.sha512.create(); - break; - case 'RSASSA-PSS': - md = forge.md.sha256.create(); - break; - } - } - if(md === null) { - var error = new Error('Could not compute certificate digest. ' + - 'Unknown signature OID.'); - error.signatureOid = child.signatureOid; - throw error; - } + // create digest for OID signature types + md = _createSignatureDigest({ + signatureOid: child.signatureOid, + type: 'certificate' + }); // produce DER formatted TBSCertificate and digest it var tbsCertificate = child.tbsCertificate || pki.getTBSCertificate(child); @@ -1120,8 +1132,8 @@ pki.createCertificate = function() { switch(child.signatureOid) { case oids.sha1WithRSAEncryption: case oids.sha1WithRSASignature: - scheme = undefined; /* use PKCS#1 v1.5 padding scheme */ - break; + scheme = undefined; /* use PKCS#1 v1.5 padding scheme */ + break; case oids['RSASSA-PSS']: var hash, mgf; @@ -1335,38 +1347,11 @@ pki.certificateFromAsn1 = function(obj, computeHash) { cert.tbsCertificate = capture.tbsCertificate; if(computeHash) { - // check signature OID for supported signature types - cert.md = null; - if(cert.signatureOid in oids) { - var oid = oids[cert.signatureOid]; - switch(oid) { - case 'sha1WithRSAEncryption': - case 'sha1WithRSASignature': - cert.md = forge.md.sha1.create(); - break; - case 'md5WithRSAEncryption': - cert.md = forge.md.md5.create(); - break; - case 'sha256WithRSAEncryption': - cert.md = forge.md.sha256.create(); - break; - case 'sha384WithRSAEncryption': - cert.md = forge.md.sha384.create(); - break; - case 'sha512WithRSAEncryption': - cert.md = forge.md.sha512.create(); - break; - case 'RSASSA-PSS': - cert.md = forge.md.sha256.create(); - break; - } - } - if(cert.md === null) { - var error = new Error('Could not compute certificate digest. ' + - 'Unknown signature OID.'); - error.signatureOid = cert.signatureOid; - throw error; - } + // create digest for OID signature type + cert.md = _createSignatureDigest({ + signatureOid: cert.signatureOid, + type: 'certificate' + }); // produce DER formatted TBSCertificate and digest it var bytes = asn1.toDer(cert.tbsCertificate); @@ -1684,38 +1669,11 @@ pki.certificationRequestFromAsn1 = function(obj, computeHash) { csr.certificationRequestInfo = capture.certificationRequestInfo; if(computeHash) { - // check signature OID for supported signature types - csr.md = null; - if(csr.signatureOid in oids) { - var oid = oids[csr.signatureOid]; - switch(oid) { - case 'sha1WithRSAEncryption': - case 'sha1WithRSASignature': - csr.md = forge.md.sha1.create(); - break; - case 'md5WithRSAEncryption': - csr.md = forge.md.md5.create(); - break; - case 'sha256WithRSAEncryption': - csr.md = forge.md.sha256.create(); - break; - case 'sha384WithRSAEncryption': - csr.md = forge.md.sha384.create(); - break; - case 'sha512WithRSAEncryption': - csr.md = forge.md.sha512.create(); - break; - case 'RSASSA-PSS': - csr.md = forge.md.sha256.create(); - break; - } - } - if(csr.md === null) { - var error = new Error('Could not compute certification request digest. ' + - 'Unknown signature OID.'); - error.signatureOid = csr.signatureOid; - throw error; - } + // create digest for OID signature type + csr.md = _createSignatureDigest({ + signatureOid: csr.signatureOid, + type: 'certification request' + }); // produce DER formatted CertificationRequestInfo and digest it var bytes = asn1.toDer(csr.certificationRequestInfo); @@ -1855,39 +1813,10 @@ pki.createCertificationRequest = function() { var md = csr.md; if(md === null) { - // check signature OID for supported signature types - if(csr.signatureOid in oids) { - // TODO: create DRY `OID to md` function - var oid = oids[csr.signatureOid]; - switch(oid) { - case 'sha1WithRSAEncryption': - case 'sha1WithRSASignature': - md = forge.md.sha1.create(); - break; - case 'md5WithRSAEncryption': - md = forge.md.md5.create(); - break; - case 'sha256WithRSAEncryption': - md = forge.md.sha256.create(); - break; - case 'sha384WithRSAEncryption': - md = forge.md.sha384.create(); - break; - case 'sha512WithRSAEncryption': - md = forge.md.sha512.create(); - break; - case 'RSASSA-PSS': - md = forge.md.sha256.create(); - break; - } - } - if(md === null) { - var error = new Error( - 'Could not compute certification request digest. ' + - 'Unknown signature OID.'); - error.signatureOid = csr.signatureOid; - throw error; - } + md = _createSignatureDigest({ + signatureOid: csr.signatureOid, + type: 'certification request' + }); // produce DER formatted CertificationRequestInfo and digest it var cri = csr.certificationRequestInfo || From 9d8b0eea8196d49d73e5e2f4c971d53c81d6e233 Mon Sep 17 00:00:00 2001 From: "David I. Lehn" Date: Thu, 6 Jan 2022 22:48:44 -0500 Subject: [PATCH 177/197] Add verification helper. - Reduce duplicate code. - Small cleanups. - Add note about deprecated OID alias. --- CHANGELOG.md | 5 +- lib/x509.js | 168 +++++++++++++++++++++------------------------------ 2 files changed, 72 insertions(+), 101 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8f6c22f5d..32efeb70f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,8 +12,9 @@ Forge ChangeLog links](https://github.com/digitalbazaar/forge/issues/825). ### Changed -- [x509]: Reduce duplicate code with a helper function to create a signature - digest given an signature algorithm OID. +- [x509]: Reduce duplicate code. Add helper function to create a signature + digest given an signature algorithm OID. Add helper function to verify + signatures. ## 1.1.0 - 2022-01-06 diff --git a/lib/x509.js b/lib/x509.js index d7d2f892f..25096c17d 100644 --- a/lib/x509.js +++ b/lib/x509.js @@ -700,24 +700,19 @@ var _readSignatureParameters = function(oid, obj, fillDefaults) { var _createSignatureDigest = function(options) { switch(oids[options.signatureOid]) { case 'sha1WithRSAEncryption': + // depreacted alias case 'sha1WithRSASignature': return forge.md.sha1.create(); - break; case 'md5WithRSAEncryption': return forge.md.md5.create(); - break; case 'sha256WithRSAEncryption': return forge.md.sha256.create(); - break; case 'sha384WithRSAEncryption': return forge.md.sha384.create(); - break; case 'sha512WithRSAEncryption': return forge.md.sha512.create(); - break; case 'RSASSA-PSS': return forge.md.sha256.create(); - break; default: var error = new Error( 'Could not compute ' + options.type + ' digest. ' + @@ -727,6 +722,68 @@ var _createSignatureDigest = function(options) { } }; +/** + * Verify signature on certificate or CSR. + * + * @param options: + * certificate the certificate or CSR to verify. + * md the signature digest. + * signature the signature + * @return a created md instance. throws if unknown oid. + */ +var _verifySignature = function(options) { + var cert = options.certificate; + var scheme; + + switch(cert.signatureOid) { + case oids.sha1WithRSAEncryption: + // depreacted alias + case oids.sha1WithRSASignature: + /* use PKCS#1 v1.5 padding scheme */ + break; + case oids['RSASSA-PSS']: + var hash, mgf; + + /* initialize mgf */ + hash = oids[cert.signatureParameters.mgf.hash.algorithmOid]; + if(hash === undefined || forge.md[hash] === undefined) { + var error = new Error('Unsupported MGF hash function.'); + error.oid = cert.signatureParameters.mgf.hash.algorithmOid; + error.name = hash; + throw error; + } + + mgf = oids[cert.signatureParameters.mgf.algorithmOid]; + if(mgf === undefined || forge.mgf[mgf] === undefined) { + var error = new Error('Unsupported MGF function.'); + error.oid = cert.signatureParameters.mgf.algorithmOid; + error.name = mgf; + throw error; + } + + mgf = forge.mgf[mgf].create(forge.md[hash].create()); + + /* initialize hash function */ + hash = oids[cert.signatureParameters.hash.algorithmOid]; + if(hash === undefined || forge.md[hash] === undefined) { + var error = new Error('Unsupported RSASSA-PSS hash function.'); + error.oid = cert.signatureParameters.hash.algorithmOid; + error.name = hash; + throw error; + } + + scheme = forge.pss.create( + forge.md[hash].create(), mgf, cert.signatureParameters.saltLength + ); + break; + } + + // verify signature on cert using public key + return cert.publicKey.verify( + options.md.digest().getBytes(), options.signature, scheme + ); +}; + /** * Converts an X.509 certificate from PEM format. * @@ -1127,53 +1184,9 @@ pki.createCertificate = function() { } if(md !== null) { - var scheme; - - switch(child.signatureOid) { - case oids.sha1WithRSAEncryption: - case oids.sha1WithRSASignature: - scheme = undefined; /* use PKCS#1 v1.5 padding scheme */ - break; - case oids['RSASSA-PSS']: - var hash, mgf; - - /* initialize mgf */ - hash = oids[child.signatureParameters.mgf.hash.algorithmOid]; - if(hash === undefined || forge.md[hash] === undefined) { - var error = new Error('Unsupported MGF hash function.'); - error.oid = child.signatureParameters.mgf.hash.algorithmOid; - error.name = hash; - throw error; - } - - mgf = oids[child.signatureParameters.mgf.algorithmOid]; - if(mgf === undefined || forge.mgf[mgf] === undefined) { - var error = new Error('Unsupported MGF function.'); - error.oid = child.signatureParameters.mgf.algorithmOid; - error.name = mgf; - throw error; - } - - mgf = forge.mgf[mgf].create(forge.md[hash].create()); - - /* initialize hash function */ - hash = oids[child.signatureParameters.hash.algorithmOid]; - if(hash === undefined || forge.md[hash] === undefined) { - throw { - message: 'Unsupported RSASSA-PSS hash function.', - oid: child.signatureParameters.hash.algorithmOid, - name: hash - }; - } - - scheme = forge.pss.create(forge.md[hash].create(), mgf, - child.signatureParameters.saltLength); - break; - } - - // verify signature on cert using public key - rval = cert.publicKey.verify( - md.digest().getBytes(), child.signature, scheme); + rval = _verifySignature({ + certificate: cert, md: md, signature: child.signature + }); } return rval; @@ -1826,52 +1839,9 @@ pki.createCertificationRequest = function() { } if(md !== null) { - var scheme; - - switch(csr.signatureOid) { - case oids.sha1WithRSAEncryption: - case oids.sha1WithRSASignature: - /* use PKCS#1 v1.5 padding scheme */ - break; - case oids['RSASSA-PSS']: - var hash, mgf; - - /* initialize mgf */ - hash = oids[csr.signatureParameters.mgf.hash.algorithmOid]; - if(hash === undefined || forge.md[hash] === undefined) { - var error = new Error('Unsupported MGF hash function.'); - error.oid = csr.signatureParameters.mgf.hash.algorithmOid; - error.name = hash; - throw error; - } - - mgf = oids[csr.signatureParameters.mgf.algorithmOid]; - if(mgf === undefined || forge.mgf[mgf] === undefined) { - var error = new Error('Unsupported MGF function.'); - error.oid = csr.signatureParameters.mgf.algorithmOid; - error.name = mgf; - throw error; - } - - mgf = forge.mgf[mgf].create(forge.md[hash].create()); - - /* initialize hash function */ - hash = oids[csr.signatureParameters.hash.algorithmOid]; - if(hash === undefined || forge.md[hash] === undefined) { - var error = new Error('Unsupported RSASSA-PSS hash function.'); - error.oid = csr.signatureParameters.hash.algorithmOid; - error.name = hash; - throw error; - } - - scheme = forge.pss.create(forge.md[hash].create(), mgf, - csr.signatureParameters.saltLength); - break; - } - - // verify signature on csr using its public key - rval = csr.publicKey.verify( - md.digest().getBytes(), csr.signature, scheme); + rval = _verifySignature({ + certificate: csr, md: md, signature: csr.signature + }); } return rval; From f8e498a6682dc32233eb361400ee7198cf95c855 Mon Sep 17 00:00:00 2001 From: "David I. Lehn" Date: Thu, 6 Jan 2022 22:49:48 -0500 Subject: [PATCH 178/197] Fix typos. --- lib/x509.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/x509.js b/lib/x509.js index 25096c17d..2877810c1 100644 --- a/lib/x509.js +++ b/lib/x509.js @@ -700,7 +700,7 @@ var _readSignatureParameters = function(oid, obj, fillDefaults) { var _createSignatureDigest = function(options) { switch(oids[options.signatureOid]) { case 'sha1WithRSAEncryption': - // depreacted alias + // deprecated alias case 'sha1WithRSASignature': return forge.md.sha1.create(); case 'md5WithRSAEncryption': @@ -737,7 +737,7 @@ var _verifySignature = function(options) { switch(cert.signatureOid) { case oids.sha1WithRSAEncryption: - // depreacted alias + // deprecated alias case oids.sha1WithRSASignature: /* use PKCS#1 v1.5 padding scheme */ break; From a9f013ab985cdb87536826e86d2adb0b26c7652d Mon Sep 17 00:00:00 2001 From: "David I. Lehn" Date: Fri, 7 Jan 2022 21:19:48 -0500 Subject: [PATCH 179/197] Update changelog. --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 32efeb70f..6d7849f94 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,8 @@ Forge ChangeLog =============== +## 1.2.0 - 2022-01-07 + ### Fixed - [x509] 'Expected' and 'Actual' issuers were backwards in verification failure message. From 866ed40ae64264d48ffcc8cf663a6d13b9446e78 Mon Sep 17 00:00:00 2001 From: "David I. Lehn" Date: Fri, 7 Jan 2022 21:19:48 -0500 Subject: [PATCH 180/197] Release 1.2.0. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 5855ee67c..a75316c16 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "node-forge", - "version": "1.1.1-0", + "version": "1.2.0", "description": "JavaScript implementations of network transports, cryptography, ciphers, PKI, message digests, and various utilities.", "homepage": "https://github.com/digitalbazaar/forge", "author": { From 154531600a7c928774e402148215664945961d53 Mon Sep 17 00:00:00 2001 From: "David I. Lehn" Date: Fri, 7 Jan 2022 21:20:23 -0500 Subject: [PATCH 181/197] Start 1.2.1-0. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a75316c16..736e6c76f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "node-forge", - "version": "1.2.0", + "version": "1.2.1-0", "description": "JavaScript implementations of network transports, cryptography, ciphers, PKI, message digests, and various utilities.", "homepage": "https://github.com/digitalbazaar/forge", "author": { From 50a20ec77ee6b0a1b5e8124b3c6c4aba6a37bebe Mon Sep 17 00:00:00 2001 From: "David I. Lehn" Date: Tue, 11 Jan 2022 13:00:20 -0500 Subject: [PATCH 182/197] Load entire module while testing. - Improves top-level testing. - Improves coverage reporting. --- CHANGELOG.md | 4 ++++ tests/unit/forge.js | 2 ++ tests/unit/index.js | 1 + 3 files changed, 7 insertions(+) create mode 100644 tests/unit/forge.js diff --git a/CHANGELOG.md b/CHANGELOG.md index 6d7849f94..5fe424fe5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ Forge ChangeLog =============== +### Fixed +- [tests] Load entire module to improve top-level testing and coverage + reporting. + ## 1.2.0 - 2022-01-07 ### Fixed 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..c881a4366 100644 --- a/tests/unit/index.js +++ b/tests/unit/index.js @@ -1,3 +1,4 @@ +require('./forge'); require('./util'); require('./md5'); require('./sha1'); From 2f3820a138413860a64aeecbfc47d89e8fa91310 Mon Sep 17 00:00:00 2001 From: "David I. Lehn" Date: Tue, 11 Jan 2022 14:51:44 -0500 Subject: [PATCH 183/197] Refactor logging to avoid use of URLSearchParams. The logic path was hitting URLSearchParams initialization in older Node.js versions that don't have that global. Removing since it's not needed. --- CHANGELOG.md | 3 ++- lib/log.js | 13 +++++-------- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5fe424fe5..0a6862653 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,8 +2,9 @@ Forge ChangeLog =============== ### Fixed -- [tests] Load entire module to improve top-level testing and coverage +- [tests]: Load entire module to improve top-level testing and coverage reporting. +- [log]: Refactor logging setup to avoid use of `URLSearchParams`. ## 1.2.0 - 2022-01-07 diff --git a/lib/log.js b/lib/log.js index 5228047f6..4ef700591 100644 --- a/lib/log.js +++ b/lib/log.js @@ -286,7 +286,7 @@ if(typeof(console) !== 'undefined' && 'log' in console) { } /* - * Check for logging control query vars. + * Check for logging control query vars in current URL. * * console.level= * Set's the console log level by name. Useful to override defaults and @@ -297,13 +297,10 @@ if(typeof(console) !== 'undefined' && 'log' in console) { * after console.level is processed. Useful to force a level of verbosity * that could otherwise be limited by a user config. */ -if(sConsoleLogger !== null) { - var query; - if(typeof window !== 'undefined' && window.location) { - query = new URL(window.location.href).searchParams; - } else { - query = new URLSearchParams(); - } +if(sConsoleLogger !== null && + typeof window !== 'undefined' && window.location +) { + var query = new URL(window.location.href).searchParams; if(query.has('console.level')) { // set with last value forge.log.setLevel( From 43a456e4d5d707563609becf8ea5dbbfaa5bf3ff Mon Sep 17 00:00:00 2001 From: "David I. Lehn" Date: Tue, 11 Jan 2022 14:56:26 -0500 Subject: [PATCH 184/197] Update changelog. --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0a6862653..730767d0b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,8 @@ Forge ChangeLog =============== +## 1.2.1 - 2022-01-11 + ### Fixed - [tests]: Load entire module to improve top-level testing and coverage reporting. From 2162bfca12ef16de04a99d8bfa208eabcdf177be Mon Sep 17 00:00:00 2001 From: "David I. Lehn" Date: Tue, 11 Jan 2022 14:56:26 -0500 Subject: [PATCH 185/197] Release 1.2.1. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 736e6c76f..cfe91332b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "node-forge", - "version": "1.2.1-0", + "version": "1.2.1", "description": "JavaScript implementations of network transports, cryptography, ciphers, PKI, message digests, and various utilities.", "homepage": "https://github.com/digitalbazaar/forge", "author": { From 7928551717b60e5def1785cfa7728c1107716c91 Mon Sep 17 00:00:00 2001 From: "David I. Lehn" Date: Tue, 11 Jan 2022 14:56:57 -0500 Subject: [PATCH 186/197] Start 1.2.2-0. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index cfe91332b..ab7771410 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "node-forge", - "version": "1.2.1", + "version": "1.2.2-0", "description": "JavaScript implementations of network transports, cryptography, ciphers, PKI, message digests, and various utilities.", "homepage": "https://github.com/digitalbazaar/forge", "author": { From 2b1f368c93861ef751e32574b08ee4caa5e80c7f Mon Sep 17 00:00:00 2001 From: "David I. Lehn" Date: Mon, 7 Mar 2022 19:41:19 -0500 Subject: [PATCH 187/197] Add fallback to pretty print invalid UTF8 data. Malformed UTF8 data can cause the escaping code to fail. Capture the failures and print out a hex version with error note. --- CHANGELOG.md | 5 +++++ lib/asn1.js | 11 ++++++++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 730767d0b..075ea30a7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,11 @@ Forge ChangeLog =============== +## 1.3.0 - 2022-XXX + +### Fixed +- [asn1] Add fallback to pretty print invalid UTF8 data. + ## 1.2.1 - 2022-01-11 ### Fixed diff --git a/lib/asn1.js b/lib/asn1.js index e0fea0e08..c142f6358 100644 --- a/lib/asn1.js +++ b/lib/asn1.js @@ -1391,7 +1391,16 @@ asn1.prettyPrint = function(obj, level, indentation) { } rval += '0x' + forge.util.bytesToHex(obj.value); } else if(obj.type === asn1.Type.UTF8) { - rval += forge.util.decodeUtf8(obj.value); + try { + rval += forge.util.decodeUtf8(obj.value); + } catch(e) { + if(e.message === 'URI malformed') { + rval += + '0x' + forge.util.bytesToHex(obj.value) + ' (malformed UTF8)'; + } else { + throw e; + } + } } else if(obj.type === asn1.Type.PRINTABLESTRING || obj.type === asn1.Type.IA5String) { rval += obj.value; From e27f61230f19fb9f085a163f31d0573305271b84 Mon Sep 17 00:00:00 2001 From: "David I. Lehn" Date: Mon, 7 Mar 2022 19:42:23 -0500 Subject: [PATCH 188/197] Remove unused option. --- lib/asn1.js | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/asn1.js b/lib/asn1.js index c142f6358..44ee5d439 100644 --- a/lib/asn1.js +++ b/lib/asn1.js @@ -566,7 +566,6 @@ function _fromDer(bytes, remaining, depth, options) { start = bytes.length(); var subOptions = { // enforce strict mode to avoid parsing ASN.1 from plain data - verbose: options.verbose, strict: true, decodeBitStrings: true }; From c20f309311d83445e11abe7c313cc4b467c18914 Mon Sep 17 00:00:00 2001 From: "David I. Lehn" Date: Mon, 7 Mar 2022 19:45:11 -0500 Subject: [PATCH 189/197] Adjust remaining length. This is not strictly needed since the value isn't used after this point. However, when temporary debugging is added to the code it is helpful for this valud to be accurate. Adding it in to avoid future confusion. --- lib/asn1.js | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/asn1.js b/lib/asn1.js index 44ee5d439..b6465351c 100644 --- a/lib/asn1.js +++ b/lib/asn1.js @@ -614,6 +614,7 @@ function _fromDer(bytes, remaining, depth, options) { } } else { value = bytes.getBytes(length); + remaining -= length; } } From 3f0b49a0573ef1bb7af7f5673c0cfebf00424df1 Mon Sep 17 00:00:00 2001 From: "David I. Lehn" Date: Mon, 7 Mar 2022 19:53:18 -0500 Subject: [PATCH 190/197] Fix signature verification issues. **SECURITY**: Three RSA PKCS#1 v1.5 signature verification issues were reported by Moosa Yahyazadeh (moosa-yahyazadeh@uiowa.edu): - Leniency in checking `digestAlgorithm` structure can lead to signature forgery. - The code is lenient in checking the digest algorithm structure. This can allow a crafted structure that steals padding bytes and uses unchecked portion of the PKCS#1 encoded message to forge a signature when a low public exponent is being used. - Failing to check tailing garbage bytes can lead to signature forgery. - The code does not check for tailing garbage bytes after decoding a `DigestInfo` ASN.1 structure. This can allow padding bytes to be removed and garbage data added to forge a signature when a low public exponent is being used. - Leniency in checking type octet. - `DigestInfo` is not properly checked for proper ASN.1 structure. This can lead to successful verification with signatures that contain invalid structures but a valid digest. For more information, please see "Bleichenbacher's RSA signature forgery based on implementation error" by Hal Finney: https://mailarchive.ietf.org/arch/msg/openpgp/5rnE9ZRN1AokBVj3VqblGlP63QE/ Fixed with the following: - [asn1] `fromDer` is now more strict and will default to ensuring all input bytes are parsed or throw an error. A new option `parseAllBytes` can disable this behavior. - **NOTE**: The previous behavior is being changed since it can lead to security issues with crafted inputs. It is possible that code doing custom DER parsing may need to adapt to this new behavior and optional flag. - [rsa] Add and use a validator to check for proper structure of parsed ASN.1 `RSASSA-PKCS-v1_5` `DigestInfo` data. Additionally check that the hash algorithm identifier is a known value. An invalid `DigestInfo` or algorithm identifier will now cause an error to be thrown. - [oid] Added `1.2.840.113549.2.2` / `md2` for hash algorithm checking. - [tests] Tests were added for all of the reported issues. A private verify option was added to assist in checking multiple possible failures in the test data. --- CHANGELOG.md | 38 ++++++++++++ lib/asn1.js | 19 +++++- lib/oids.js | 1 + lib/rsa.js | 80 ++++++++++++++++++++++++- tests/unit/rsa.js | 150 ++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 284 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 075ea30a7..29b968d37 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,8 +3,46 @@ Forge ChangeLog ## 1.3.0 - 2022-XXX +### Security +- **SECURITY**: Three RSA PKCS#1 v1.5 signature verification issues were + reported by Moosa Yahyazadeh (moosa-yahyazadeh@uiowa.edu). + - Leniency in checking `digestAlgorithm` structure can lead to signature + forgery. + - The code is lenient in checking the digest algorithm structure. This can + allow a crafted structure that steals padding bytes and uses unchecked + portion of the PKCS#1 encoded message to forge a signature when a low + public exponent is being used. For more information, please see + ["Bleichenbacher's RSA signature forgery based on implementation + error"](https://mailarchive.ietf.org/arch/msg/openpgp/5rnE9ZRN1AokBVj3VqblGlP63QE/) + by Hal Finney. + - Failing to check tailing garbage bytes can lead to signature forgery. + - The code does not check for tailing garbage bytes after decoding a + `DigestInfo` ASN.1 structure. This can allow padding bytes to be removed + and garbage data added to forge a signature when a low public exponent is + being used. For more information, please see ["Bleichenbacher's RSA + signature forgery based on implementation + error"](https://mailarchive.ietf.org/arch/msg/openpgp/5rnE9ZRN1AokBVj3VqblGlP63QE/) + by Hal Finney. + - Leniency in checking type octet. + - `DigestInfo` is not properly checked for proper ASN.1 structure. This can + lead to successful verification with signatures that contain invalid + structures but a valid digest. + ### Fixed - [asn1] Add fallback to pretty print invalid UTF8 data. +- [asn1] `fromDer` is now more strict and will default to ensuring all input + bytes are parsed or throw an error. A new option `parseAllBytes` can disable + this behavior. + - **NOTE**: The previous behavior is being changed since it can lead to + security issues with crafted inputs. It is possible that code doing custom + DER parsing may need to adapt to this new behavior and optional flag. +- [rsa] Add and use a validator to check for proper structure of parsed ASN.1 + `RSASSA-PKCS-v1_5` `DigestInfo` data. Additionally check that the hash + algorithm identifier is a known value. An invalid `DigestInfo` or algorithm + identifier will now cause an error to be thrown. + +### Added +- [oid] Added `1.2.840.113549.2.2` / `md2` for hash algorithm checking. ## 1.2.1 - 2022-01-11 diff --git a/lib/asn1.js b/lib/asn1.js index b6465351c..4025f8a9e 100644 --- a/lib/asn1.js +++ b/lib/asn1.js @@ -411,6 +411,8 @@ var _getValueLength = function(bytes, remaining) { * @param [options] object with options or boolean strict flag * [strict] true to be strict when checking value lengths, false to * allow truncated values (default: true). + * [parseAllBytes] true to ensure all bytes are parsed + * (default: true) * [decodeBitStrings] true to attempt to decode the content of * BIT STRINGs (not OCTET STRINGs) using strict mode. Note that * without schema support to understand the data context this can @@ -418,24 +420,31 @@ var _getValueLength = function(bytes, remaining) { * flag will be deprecated or removed as soon as schema support is * available. (default: true) * + * @throws Will throw an error for various malformed input conditions. + * * @return the parsed asn1 object. */ asn1.fromDer = function(bytes, options) { if(options === undefined) { options = { strict: true, + parseAllBytes: true, decodeBitStrings: true }; } if(typeof options === 'boolean') { options = { strict: options, + parseAllBytes: true, decodeBitStrings: true }; } if(!('strict' in options)) { options.strict = true; } + if(!('parseAllBytes' in options)) { + options.parseAllBytes = true; + } if(!('decodeBitStrings' in options)) { options.decodeBitStrings = true; } @@ -445,7 +454,15 @@ asn1.fromDer = function(bytes, options) { bytes = forge.util.createBuffer(bytes); } - return _fromDer(bytes, bytes.length(), 0, options); + var byteCount = bytes.length(); + var value = _fromDer(bytes, bytes.length(), 0, options); + if(options.parseAllBytes && bytes.length() !== 0) { + var error = new Error('Unparsed DER bytes remain after ASN.1 parsing.'); + error.byteCount = byteCount; + error.remaining = bytes.length(); + throw error; + } + return value; }; /** diff --git a/lib/oids.js b/lib/oids.js index 0ca96e989..5483d72cd 100644 --- a/lib/oids.js +++ b/lib/oids.js @@ -47,6 +47,7 @@ _IN('1.3.14.3.2.29', 'sha1WithRSASignature'); _IN('2.16.840.1.101.3.4.2.1', 'sha256'); _IN('2.16.840.1.101.3.4.2.2', 'sha384'); _IN('2.16.840.1.101.3.4.2.3', 'sha512'); +_IN('1.2.840.113549.2.2', 'md2'); _IN('1.2.840.113549.2.5', 'md5'); // pkcs#7 content types diff --git a/lib/rsa.js b/lib/rsa.js index 7c67917ce..48a4bd261 100644 --- a/lib/rsa.js +++ b/lib/rsa.js @@ -264,6 +264,40 @@ var publicKeyValidator = forge.pki.rsa.publicKeyValidator = { }] }; +// validator for a DigestInfo structure +var digestInfoValidator = { + name: 'DigestInfo', + tagClass: asn1.Class.UNIVERSAL, + type: asn1.Type.SEQUENCE, + constructed: true, + value: [{ + name: 'DigestInfo.DigestAlgorithm', + tagClass: asn1.Class.UNIVERSAL, + type: asn1.Type.SEQUENCE, + constructed: true, + value: [{ + name: 'DigestInfo.DigestAlgorithm.algorithmIdentifier', + tagClass: asn1.Class.UNIVERSAL, + type: asn1.Type.OID, + constructed: false, + capture: 'algorithmIdentifier' + }, { + // NULL paramters + name: 'DigestInfo.DigestAlgorithm.parameters', + tagClass: asn1.Class.UNIVERSAL, + type: asn1.Type.NULL, + constructed: false + }] + }, { + // digest + name: 'DigestInfo.digest', + tagClass: asn1.Class.UNIVERSAL, + type: asn1.Type.OCTETSTRING, + constructed: false, + capture: 'digest' + }] +}; + /** * Wrap digest in DigestInfo object. * @@ -1092,15 +1126,27 @@ pki.setRsaPublicKey = pki.rsa.setPublicKey = function(n, e) { * a Forge PSS object for RSASSA-PSS, * 'NONE' or null for none, DigestInfo will not be expected, but * PKCS#1 v1.5 padding will still be used. + * @param options optional verify options + * _parseAllDigestBytes testing flag to control parsing of all + * digest bytes. Unsupported and not for general usage. + * (default: true) * * @return true if the signature was verified, false if not. */ - key.verify = function(digest, signature, scheme) { + key.verify = function(digest, signature, scheme, options) { if(typeof scheme === 'string') { scheme = scheme.toUpperCase(); } else if(scheme === undefined) { scheme = 'RSASSA-PKCS1-V1_5'; } + if(options === undefined) { + options = { + _parseAllDigestBytes: true + }; + } + if(!('_parseAllDigestBytes' in options)) { + options._parseAllDigestBytes = true; + } if(scheme === 'RSASSA-PKCS1-V1_5') { scheme = { @@ -1108,9 +1154,37 @@ pki.setRsaPublicKey = pki.rsa.setPublicKey = function(n, e) { // remove padding d = _decodePkcs1_v1_5(d, key, true); // d is ASN.1 BER-encoded DigestInfo - var obj = asn1.fromDer(d); + var obj = asn1.fromDer(d, { + parseAllBytes: options._parseAllDigestBytes + }); + + // validate DigestInfo + var capture = {}; + var errors = []; + if(!asn1.validate(obj, digestInfoValidator, capture, errors)) { + var error = new Error( + 'ASN.1 object does not contain a valid RSASSA-PKCS1-v1_5 ' + + 'DigestInfo value.'); + error.errors = errors; + throw error; + } + // check hash algorithm identifier + // FIXME: add support to vaidator for strict value choices + var oid = asn1.derToOid(capture.algorithmIdentifier); + if(!(oid === forge.oids.md2 || + oid === forge.oids.md5 || + oid === forge.oids.sha1 || + oid === forge.oids.sha256 || + oid === forge.oids.sha384 || + oid === forge.oids.sha512)) { + var error = new Error( + 'Unknown RSASSA-PKCS1-v1_5 DigestAlgorithm identifier.'); + error.oid = oid; + throw error; + } + // compare the given digest to the decrypted one - return digest === obj.value[1].value; + return digest === capture.digest; } }; } else if(scheme === 'NONE' || scheme === 'NULL' || scheme === null) { diff --git a/tests/unit/rsa.js b/tests/unit/rsa.js index 0cdd28e01..def5c2d78 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'); @@ -773,5 +774,154 @@ var UTIL = require('../../lib/util'); }); } })(); + + describe('bad data', function() { + // 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); + }, { + message: '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 + }); + }, { + message: 'ASN.1 object does not contain a valid RSASSA-PKCS1-v1_5 DigestInfo value.' + }); + } + + it('should check DigestInfo structure', function() { + var publicKey = RSA.setPublicKey(N, e); + var S = UTIL.binary.hex.decode( + 'e7410e05bdc38d1c72fab784be41df3d3de2ae83894d9ec86cb5fe343d5dc7d45df2a36fc60363faf32f0d37ab457648af40a48a6c53ae7af0575e92cb1ffc236d55e1325af8c71b3ac313f2630fb498b8e1546093aca1ed56026a96cb525d991159a2d6ccbfd5ef63ae718f8ace2469e357ccf3f6a048bbf9760f5fb36b9dd38fb330eab504f05078b83f5d8bd95dce8fccc6b46babd56f678300f2b39083e53e04e79f503358a6222f8dd66b561fea3a51ecf3be16c9e2ea6ba8aaed9fbe6ba510ff752e4529385f759d4d6120b15f65534248ed5bbb1307a7d0a9838329697f5fbae91f48e478dcbb77190f0d173b6cb8b1299cf4202570d25d11a7862b47'); + + _checkBadDigestInfo(publicKey, S); + }); + + it('should check tailing garbage and DigestInfo [1]', function() { + var publicKey = RSA.setPublicKey(N, e); + var S = UTIL.binary.hex.decode( + 'c2ad2fa23c246ee98c453d69023e7ec05956b48bd0e287341ba9d342ad49b0fff2bcbb9adc50f1ccbfc54106305cc74a88db89ff94901a08359893a08426373e7949a8794798233445af6c48bc6ccbe278bdeb62c31e40c3bf0014af2faadcc9ed7885756789a5b95c2a355fbb3f04412f42e0f9ed335ab51af8f091a62aaaaf6577422220917daaece3ca2f4e66dc4e0574356762592052b406768c31c25cf4c1754e6da9dc3440e238c4f9b25cccc174dd1b17b027e0f9ce2763b86f0e6871690ddd018d2e774bc968c9c6e907a000daf5044ba31a0b9eefbd7b4b1ec466d20bc1dd3f020cb1091af6b476416da3024ea046b09fbbbc4d2355da9a2bc6ddb9'); + + _checkBadTailingGarbage(publicKey, S); + _checkBadDigestInfo(publicKey, S, true); + }); + + it('should check tailing garbage and DigestIfno [2]', function() { + var publicKey = RSA.setPublicKey(N, e); + var S = UTIL.binary.hex.decode( + 'a7c5812d7fc0eef766a481aac18c8c48483daf9b5ffb6614bd98ebe4ecb746dd493cf5dd2cbe16ecaa0b52109b744930eda49316605fc823fd57a68b5b2c62e8c1b158b26e1547a2e33cdd79427d7c513f07d02261ffe43db197d8cddca2b5b43c1df85aaed6e91aadd44a46bff7f5c70f1acc1a193917e3908444632f30e69cfe95d8036d3b6ad318eefd3952804f16613c969e6d13604bb4e723dfad24c42c8d9b5b16a9f5a4b40dcf17b167d319017740f9cc0836436c14d51c3d8a697f1fa2b65196deb5c21b1559c7dea7f598007fa7320909825009f8bf376491c298d8155a382e967042db952e995d14b2f961e1b22f911d1b77895def1c7ef229c87e'); + + _checkBadTailingGarbage(publicKey, S); + _checkBadDigestInfo(publicKey, S, true); + }); + + it('should check tailing garbage and DigestInfo [e=3]', function() { + var N = new JSBN.BigInteger( + '29438513389594867490232201282478838726734464161887801289068585100507839535636256317277708295678804401391394313946142335874609638666081950936114152574870224034382561784743283763961349980806819078028975594777103388280272392844112380900374508170221075553517641170327441791034393719271744724924194371070527213991317221667249077972700842199037403799480569910844701030644322616045408039715278394572328099192023924503077673178227614549351191204851805076359472439160130994385433568113626206477097769842080459156024112389406200687233341779381667082591421496870666931268548504674362230725756397511775557878046572472650613407143'); + var e = new JSBN.BigInteger('3'); + var publicKey = RSA.setPublicKey(N, e); + + var S = UTIL.binary.hex.decode( + '0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002853ccc2cd32a8d430dd3bde37e70782ac82cdb7bce3c044219b50aefd689c20d3b840299f28e2fde6c67c8a7f9e528ac222fae947a6dee0d812e3c3b3452171717396e8bedc3132d92d8317e3593642640d1431ef'); + + _checkBadTailingGarbage(publicKey, S); + _checkBadDigestInfo(publicKey, S, true); + }); + + it('should check tailing garbage and DigestInfo [e=5]', function() { + var N = new JSBN.BigInteger( + '29438513389594867490232201282478838726734464161887801289068585100507839535636256317277708295678804401391394313946142335874609638666081950936114152574870224034382561784743283763961349980806819078028975594777103388280272392844112380900374508170221075553517641170327441791034393719271744724924194371070527213991317221667249077972700842199037403799480569910844701030644322616045408039715278394572328099192023924503077673178227614549351191204851805076359472439160130994385433568113626206477097769842080459156024112389406200687233341779381667082591421496870666931268548504674362230725756397511775557878046572472650613407143'); + var e = new JSBN.BigInteger('5'); + var publicKey = RSA.setPublicKey(N, e); + + var S = UTIL.binary.hex.decode( + '000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005475fe2681d7125972bd2c2f2c7ab7b8003b03d4a487d6dee07c14eb5212a9fe0071b93f84ba5bb4b0cfaf20c976b11d902013'); + + _checkBadTailingGarbage(publicKey, S); + _checkBadDigestInfo(publicKey, S, true); + }); + + it('should check tailing garbage and DigestInfo [e=17]', function() { + var N = new JSBN.BigInteger( + '928365641661298526294114382771769657905695995680009680444002258089796055192245321020911051590379097587133341820043795407471021630328875171430160513961779154294247563032373839871165519961382202811828883364651574763124699947662060849683176689286181021501400261976653416725246403933613615758181648971537689642956474563961490989544033629566558036444831495046301215543198107208071526376318961481739278769122885031686763776874806317352741548232110892401401727195758835975800106904020775937891505819798776295294696516670437057465296389148672556848624501468669295285428387365416747516180652630054765393335211528084329716917821726670549155619986875030049107668205064454104328601041931972319966348825621299693193542460060799067674344247887198933507132592770898312271636011037138984729256515515185153334743685479709085410902269777563691615719884708908509618352792737826421059819474305949001978916949447029010362775778664826653636547333219983468955600305523140183269580452792812503399042201081785972707218144968460623663922470814889738564730816412201128810370324070680245854669130551872958017494277468722193869883705529583737211815974801292292728082721785855274147991979220001018156560009927148374995236030383474031418802554714043680969417015155298092390680188406177667101020936206754551985229636814788735090951246816765035721775759652424641736739668936540450232814857289312589998505627375553038062765493408460941597629291231866042662108291164359496334978563287523685872262509560463225096226739991402761266388226652661345282274508037924611589455395655512013078629375186805951823181371561289129616028768733583565439798508002546685505512478002960132511531323264596144585611962969372672455541953777622436993987703564293487820434112162562492086865147598436647725445230861246093950020099084994990632102506848190196407855705745530407617253129971665939853842224965079537303198339986953399517682750248394628026225887174258267456078564070387327653989505416943226163989004419377363130466566387761757272563996086708621913140580687414698126490572618509858141748692837570235128900627675422927964369356691123905362222855545719945605604307263252851081309622569225811979426856464673233875589085773616373798857001344093594417138323005260179781153950803127773817702016534081581157881295739782000814998795398671806283018844936919299070562538763900037469485135699677248580365379125702903186174995651938469412191388327852955727869345476087173047665259892129895247785416834855450881318585909376917039'); + var e = new JSBN.BigInteger('17'); + var publicKey = RSA.setPublicKey(N, e); + + var S = UTIL.binary.hex.decode( + '00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001eb90acbec1bf590ba1e50960db8381fb5bdc363d46379d09956560a616b88616ce7fa4309dc45f47f5fa47d61bf66baa3d11732ce71768ded295f962'); + + _checkBadTailingGarbage(publicKey, S); + _checkBadDigestInfo(publicKey, S, true); + }); + + it('should check DigestInfo type octet [1]', function() { + var publicKey = RSA.setPublicKey(N, e); + var S = UTIL.binary.hex.decode( + 'd8298a199e1b6ac18f3c0067a004bd9ff7af87be6ad857d73cc3d24ef06195b82aaddb0194f8e61fc31453b9163062255e8baf9c480200d0991a5f764f63d5f6afd283b9cd6afe54f0b7f738707b4eb6b8807539bb627e74db87a50413ab18e504e37975aad1edc612bc8ecad53b81ea249deb5a2acc27e6419c61ab9acec6608f5ae6a2985ba0b6f42d831bc6cce4b044864154b935cf179967d129e0ad8eda9bfbb638121c3ff13c64d439632e62250d4be928a3deb112ef76a025c5d918051e601878eac0049fc9d82be9ae3475deb7ca515c830c20b91b7bedf2184fef66aea0bde62ccd1659afbfd1342322b095309451b1a87e007e640e368fb68a13c9'); + + _checkBadDigestInfo(publicKey, S); + }); + + it('should check DigestInfo type octet [2]', function() { + var publicKey = RSA.setPublicKey(N, e); + var S = UTIL.binary.hex.decode( + 'c1acdd3aef5f0439c254980295fc0d81b628df00726310a1041d79b5dd94c11d3bcaf0236763c77c25d9ab49522ed2a7d6ea3a4e483a29838acd48f2d60a790275f4cd46e4b1d09c527a426ec373e8a21746ad3ea541d3b85ba4c303ff793ea8a0a3458e93a7ec42ed66f675d7c299b0817ac95f7f45b2f48c09b3c070171f31a33ac789da9943da5dabcda1c95b42531d45484ac1efde0fe0519077debb93183e63de8f80d7f3cbfecb03cbb44ac4a2d56699e33fca0663b79ca627755fc4fc684b4ab358a0b4ac5b7e9d0cc18b6ab6300b40781502a1c03d34f31dd19d81195f8a44bc03a2595a706f06f0cb39b8e3f4afe06675fe7439b057f1200a06f4fd'); + + _checkBadDigestInfo(publicKey, S); + }); + }); }); })(); From aa9372d6dd78eb1479392b9274457036c2404b66 Mon Sep 17 00:00:00 2001 From: "David I. Lehn" Date: Tue, 8 Mar 2022 18:28:05 -0500 Subject: [PATCH 191/197] Add missing RFC 8017 algorithm identifiers. --- CHANGELOG.md | 12 +++++++++--- lib/oids.js | 3 +++ lib/rsa.js | 6 +++++- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 29b968d37..ed4288966 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -38,11 +38,17 @@ Forge ChangeLog DER parsing may need to adapt to this new behavior and optional flag. - [rsa] Add and use a validator to check for proper structure of parsed ASN.1 `RSASSA-PKCS-v1_5` `DigestInfo` data. Additionally check that the hash - algorithm identifier is a known value. An invalid `DigestInfo` or algorithm - identifier will now cause an error to be thrown. + algorithm identifier is a known value from RFC 8017 + `PKCS1-v1-5DigestAlgorithms`. An invalid `DigestInfo` or algorithm identifier + will now cause an error to be thrown. ### Added -- [oid] Added `1.2.840.113549.2.2` / `md2` for hash algorithm checking. +- [oid] Added missing RFC 8017 PKCS1-v1-5DigestAlgorithms algorithm + identifiers: + - `1.2.840.113549.2.2` / `md2` + - `2.16.840.1.101.3.4.2.4` / `sha224` + - `2.16.840.1.101.3.4.2.5` / `sha512-224` + - `2.16.840.1.101.3.4.2.6` / `sha512-256` ## 1.2.1 - 2022-01-11 diff --git a/lib/oids.js b/lib/oids.js index 5483d72cd..d1504eb16 100644 --- a/lib/oids.js +++ b/lib/oids.js @@ -47,6 +47,9 @@ _IN('1.3.14.3.2.29', 'sha1WithRSASignature'); _IN('2.16.840.1.101.3.4.2.1', 'sha256'); _IN('2.16.840.1.101.3.4.2.2', 'sha384'); _IN('2.16.840.1.101.3.4.2.3', 'sha512'); +_IN('2.16.840.1.101.3.4.2.4', 'sha224'); +_IN('2.16.840.1.101.3.4.2.5', 'sha512-224'); +_IN('2.16.840.1.101.3.4.2.6', 'sha512-256'); _IN('1.2.840.113549.2.2', 'md2'); _IN('1.2.840.113549.2.5', 'md5'); diff --git a/lib/rsa.js b/lib/rsa.js index 48a4bd261..f3b320212 100644 --- a/lib/rsa.js +++ b/lib/rsa.js @@ -1169,14 +1169,18 @@ pki.setRsaPublicKey = pki.rsa.setPublicKey = function(n, e) { throw error; } // check hash algorithm identifier + // see PKCS1-v1-5DigestAlgorithms in RFC 8017 // FIXME: add support to vaidator for strict value choices var oid = asn1.derToOid(capture.algorithmIdentifier); if(!(oid === forge.oids.md2 || oid === forge.oids.md5 || oid === forge.oids.sha1 || + oid === forge.oids.sha224 || oid === forge.oids.sha256 || oid === forge.oids.sha384 || - oid === forge.oids.sha512)) { + oid === forge.oids.sha512 || + oid === forge.oids['sha512-224'] || + oid === forge.oids['sha512-256'])) { var error = new Error( 'Unknown RSASSA-PKCS1-v1_5 DigestAlgorithm identifier.'); error.oid = oid; From a4405bb9d6b638084df478fa4ac60a410332c2d8 Mon Sep 17 00:00:00 2001 From: "David I. Lehn" Date: Tue, 15 Mar 2022 22:51:59 -0400 Subject: [PATCH 192/197] Improve signature verification tests. - Attribute tests to Moosa Yahyazadeh (moosa-yahyazadeh@uiowa.edu) - Add some details from initial report until it can be included in full. - Line wrap test data. --- tests/unit/rsa.js | 247 +++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 234 insertions(+), 13 deletions(-) diff --git a/tests/unit/rsa.js b/tests/unit/rsa.js index def5c2d78..f1493545b 100644 --- a/tests/unit/rsa.js +++ b/tests/unit/rsa.js @@ -775,7 +775,12 @@ var UTIL = require('../../lib/util'); } })(); - describe('bad data', function() { + 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 @@ -844,64 +849,244 @@ var UTIL = require('../../lib/util'); 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( - 'e7410e05bdc38d1c72fab784be41df3d3de2ae83894d9ec86cb5fe343d5dc7d45df2a36fc60363faf32f0d37ab457648af40a48a6c53ae7af0575e92cb1ffc236d55e1325af8c71b3ac313f2630fb498b8e1546093aca1ed56026a96cb525d991159a2d6ccbfd5ef63ae718f8ace2469e357ccf3f6a048bbf9760f5fb36b9dd38fb330eab504f05078b83f5d8bd95dce8fccc6b46babd56f678300f2b39083e53e04e79f503358a6222f8dd66b561fea3a51ecf3be16c9e2ea6ba8aaed9fbe6ba510ff752e4529385f759d4d6120b15f65534248ed5bbb1307a7d0a9838329697f5fbae91f48e478dcbb77190f0d173b6cb8b1299cf4202570d25d11a7862b47'); + '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( - 'c2ad2fa23c246ee98c453d69023e7ec05956b48bd0e287341ba9d342ad49b0fff2bcbb9adc50f1ccbfc54106305cc74a88db89ff94901a08359893a08426373e7949a8794798233445af6c48bc6ccbe278bdeb62c31e40c3bf0014af2faadcc9ed7885756789a5b95c2a355fbb3f04412f42e0f9ed335ab51af8f091a62aaaaf6577422220917daaece3ca2f4e66dc4e0574356762592052b406768c31c25cf4c1754e6da9dc3440e238c4f9b25cccc174dd1b17b027e0f9ce2763b86f0e6871690ddd018d2e774bc968c9c6e907a000daf5044ba31a0b9eefbd7b4b1ec466d20bc1dd3f020cb1091af6b476416da3024ea046b09fbbbc4d2355da9a2bc6ddb9'); + 'c2ad2fa23c246ee98c453d69023e7ec05956b48bd0e287341ba9d342ad49b0ff' + + 'f2bcbb9adc50f1ccbfc54106305cc74a88db89ff94901a08359893a08426373e' + + '7949a8794798233445af6c48bc6ccbe278bdeb62c31e40c3bf0014af2faadcc9' + + 'ed7885756789a5b95c2a355fbb3f04412f42e0f9ed335ab51af8f091a62aaaaf' + + '6577422220917daaece3ca2f4e66dc4e0574356762592052b406768c31c25cf4' + + 'c1754e6da9dc3440e238c4f9b25cccc174dd1b17b027e0f9ce2763b86f0e6871' + + '690ddd018d2e774bc968c9c6e907a000daf5044ba31a0b9eefbd7b4b1ec466d2' + + '0bc1dd3f020cb1091af6b476416da3024ea046b09fbbbc4d2355da9a2bc6ddb9'); _checkBadTailingGarbage(publicKey, S); _checkBadDigestInfo(publicKey, S, true); }); - it('should check tailing garbage and DigestIfno [2]', function() { + 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( - 'a7c5812d7fc0eef766a481aac18c8c48483daf9b5ffb6614bd98ebe4ecb746dd493cf5dd2cbe16ecaa0b52109b744930eda49316605fc823fd57a68b5b2c62e8c1b158b26e1547a2e33cdd79427d7c513f07d02261ffe43db197d8cddca2b5b43c1df85aaed6e91aadd44a46bff7f5c70f1acc1a193917e3908444632f30e69cfe95d8036d3b6ad318eefd3952804f16613c969e6d13604bb4e723dfad24c42c8d9b5b16a9f5a4b40dcf17b167d319017740f9cc0836436c14d51c3d8a697f1fa2b65196deb5c21b1559c7dea7f598007fa7320909825009f8bf376491c298d8155a382e967042db952e995d14b2f961e1b22f911d1b77895def1c7ef229c87e'); + '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( - '29438513389594867490232201282478838726734464161887801289068585100507839535636256317277708295678804401391394313946142335874609638666081950936114152574870224034382561784743283763961349980806819078028975594777103388280272392844112380900374508170221075553517641170327441791034393719271744724924194371070527213991317221667249077972700842199037403799480569910844701030644322616045408039715278394572328099192023924503077673178227614549351191204851805076359472439160130994385433568113626206477097769842080459156024112389406200687233341779381667082591421496870666931268548504674362230725756397511775557878046572472650613407143'); + '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( - '0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002853ccc2cd32a8d430dd3bde37e70782ac82cdb7bce3c044219b50aefd689c20d3b840299f28e2fde6c67c8a7f9e528ac222fae947a6dee0d812e3c3b3452171717396e8bedc3132d92d8317e3593642640d1431ef'); + '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( - '29438513389594867490232201282478838726734464161887801289068585100507839535636256317277708295678804401391394313946142335874609638666081950936114152574870224034382561784743283763961349980806819078028975594777103388280272392844112380900374508170221075553517641170327441791034393719271744724924194371070527213991317221667249077972700842199037403799480569910844701030644322616045408039715278394572328099192023924503077673178227614549351191204851805076359472439160130994385433568113626206477097769842080459156024112389406200687233341779381667082591421496870666931268548504674362230725756397511775557878046572472650613407143'); + '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( - '000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005475fe2681d7125972bd2c2f2c7ab7b8003b03d4a487d6dee07c14eb5212a9fe0071b93f84ba5bb4b0cfaf20c976b11d902013'); + '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( - '928365641661298526294114382771769657905695995680009680444002258089796055192245321020911051590379097587133341820043795407471021630328875171430160513961779154294247563032373839871165519961382202811828883364651574763124699947662060849683176689286181021501400261976653416725246403933613615758181648971537689642956474563961490989544033629566558036444831495046301215543198107208071526376318961481739278769122885031686763776874806317352741548232110892401401727195758835975800106904020775937891505819798776295294696516670437057465296389148672556848624501468669295285428387365416747516180652630054765393335211528084329716917821726670549155619986875030049107668205064454104328601041931972319966348825621299693193542460060799067674344247887198933507132592770898312271636011037138984729256515515185153334743685479709085410902269777563691615719884708908509618352792737826421059819474305949001978916949447029010362775778664826653636547333219983468955600305523140183269580452792812503399042201081785972707218144968460623663922470814889738564730816412201128810370324070680245854669130551872958017494277468722193869883705529583737211815974801292292728082721785855274147991979220001018156560009927148374995236030383474031418802554714043680969417015155298092390680188406177667101020936206754551985229636814788735090951246816765035721775759652424641736739668936540450232814857289312589998505627375553038062765493408460941597629291231866042662108291164359496334978563287523685872262509560463225096226739991402761266388226652661345282274508037924611589455395655512013078629375186805951823181371561289129616028768733583565439798508002546685505512478002960132511531323264596144585611962969372672455541953777622436993987703564293487820434112162562492086865147598436647725445230861246093950020099084994990632102506848190196407855705745530407617253129971665939853842224965079537303198339986953399517682750248394628026225887174258267456078564070387327653989505416943226163989004419377363130466566387761757272563996086708621913140580687414698126490572618509858141748692837570235128900627675422927964369356691123905362222855545719945605604307263252851081309622569225811979426856464673233875589085773616373798857001344093594417138323005260179781153950803127773817702016534081581157881295739782000814998795398671806283018844936919299070562538763900037469485135699677248580365379125702903186174995651938469412191388327852955727869345476087173047665259892129895247785416834855450881318585909376917039'); + '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( - '00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001eb90acbec1bf590ba1e50960db8381fb5bdc363d46379d09956560a616b88616ce7fa4309dc45f47f5fa47d61bf66baa3d11732ce71768ded295f962'); + '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); @@ -909,16 +1094,52 @@ var UTIL = require('../../lib/util'); 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( - 'd8298a199e1b6ac18f3c0067a004bd9ff7af87be6ad857d73cc3d24ef06195b82aaddb0194f8e61fc31453b9163062255e8baf9c480200d0991a5f764f63d5f6afd283b9cd6afe54f0b7f738707b4eb6b8807539bb627e74db87a50413ab18e504e37975aad1edc612bc8ecad53b81ea249deb5a2acc27e6419c61ab9acec6608f5ae6a2985ba0b6f42d831bc6cce4b044864154b935cf179967d129e0ad8eda9bfbb638121c3ff13c64d439632e62250d4be928a3deb112ef76a025c5d918051e601878eac0049fc9d82be9ae3475deb7ca515c830c20b91b7bedf2184fef66aea0bde62ccd1659afbfd1342322b095309451b1a87e007e640e368fb68a13c9'); + '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( - 'c1acdd3aef5f0439c254980295fc0d81b628df00726310a1041d79b5dd94c11d3bcaf0236763c77c25d9ab49522ed2a7d6ea3a4e483a29838acd48f2d60a790275f4cd46e4b1d09c527a426ec373e8a21746ad3ea541d3b85ba4c303ff793ea8a0a3458e93a7ec42ed66f675d7c299b0817ac95f7f45b2f48c09b3c070171f31a33ac789da9943da5dabcda1c95b42531d45484ac1efde0fe0519077debb93183e63de8f80d7f3cbfecb03cbb44ac4a2d56699e33fca0663b79ca627755fc4fc684b4ab358a0b4ac5b7e9d0cc18b6ab6300b40781502a1c03d34f31dd19d81195f8a44bc03a2595a706f06f0cb39b8e3f4afe06675fe7439b057f1200a06f4fd'); + 'c1acdd3aef5f0439c254980295fc0d81b628df00726310a1041d79b5dd94c11d' + + '3bcaf0236763c77c25d9ab49522ed2a7d6ea3a4e483a29838acd48f2d60a7902' + + '75f4cd46e4b1d09c527a426ec373e8a21746ad3ea541d3b85ba4c303ff793ea8' + + 'a0a3458e93a7ec42ed66f675d7c299b0817ac95f7f45b2f48c09b3c070171f31' + + 'a33ac789da9943da5dabcda1c95b42531d45484ac1efde0fe0519077debb9318' + + '3e63de8f80d7f3cbfecb03cbb44ac4a2d56699e33fca0663b79ca627755fc4fc' + + '684b4ab358a0b4ac5b7e9d0cc18b6ab6300b40781502a1c03d34f31dd19d8119' + + '5f8a44bc03a2595a706f06f0cb39b8e3f4afe06675fe7439b057f1200a06f4fd'); _checkBadDigestInfo(publicKey, S); }); From d4395fec831622837ecfec9e428d4620e208f9a8 Mon Sep 17 00:00:00 2001 From: "David I. Lehn" Date: Tue, 15 Mar 2022 23:09:41 -0400 Subject: [PATCH 193/197] Update changelog. --- CHANGELOG.md | 52 ++++++++++++++++++++++++++++------------------------ 1 file changed, 28 insertions(+), 24 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ed4288966..231efebae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,29 +4,30 @@ Forge ChangeLog ## 1.3.0 - 2022-XXX ### Security -- **SECURITY**: Three RSA PKCS#1 v1.5 signature verification issues were - reported by Moosa Yahyazadeh (moosa-yahyazadeh@uiowa.edu). - - Leniency in checking `digestAlgorithm` structure can lead to signature - forgery. - - The code is lenient in checking the digest algorithm structure. This can - allow a crafted structure that steals padding bytes and uses unchecked - portion of the PKCS#1 encoded message to forge a signature when a low - public exponent is being used. For more information, please see - ["Bleichenbacher's RSA signature forgery based on implementation - error"](https://mailarchive.ietf.org/arch/msg/openpgp/5rnE9ZRN1AokBVj3VqblGlP63QE/) - by Hal Finney. - - Failing to check tailing garbage bytes can lead to signature forgery. - - The code does not check for tailing garbage bytes after decoding a - `DigestInfo` ASN.1 structure. This can allow padding bytes to be removed - and garbage data added to forge a signature when a low public exponent is - being used. For more information, please see ["Bleichenbacher's RSA - signature forgery based on implementation - error"](https://mailarchive.ietf.org/arch/msg/openpgp/5rnE9ZRN1AokBVj3VqblGlP63QE/) - by Hal Finney. - - Leniency in checking type octet. - - `DigestInfo` is not properly checked for proper ASN.1 structure. This can - lead to successful verification with signatures that contain invalid - structures but a valid digest. +- Three RSA PKCS#1 v1.5 signature verification issues were reported by Moosa + Yahyazadeh (moosa-yahyazadeh@uiowa.edu). +- **HIGH**: Leniency in checking `digestAlgorithm` structure can lead to + signature forgery. + - The code is lenient in checking the digest algorithm structure. This can + allow a crafted structure that steals padding bytes and uses unchecked + portion of the PKCS#1 encoded message to forge a signature when a low + public exponent is being used. For more information, please see + ["Bleichenbacher's RSA signature forgery based on implementation + error"](https://mailarchive.ietf.org/arch/msg/openpgp/5rnE9ZRN1AokBVj3VqblGlP63QE/) + by Hal Finney. +- **HIGH**: Failing to check tailing garbage bytes can lead to signature + forgery. + - The code does not check for tailing garbage bytes after decoding a + `DigestInfo` ASN.1 structure. This can allow padding bytes to be removed + and garbage data added to forge a signature when a low public exponent is + being used. For more information, please see ["Bleichenbacher's RSA + signature forgery based on implementation + error"](https://mailarchive.ietf.org/arch/msg/openpgp/5rnE9ZRN1AokBVj3VqblGlP63QE/) + by Hal Finney. +- **MEDIUM**: Leniency in checking type octet. + - `DigestInfo` is not properly checked for proper ASN.1 structure. This can + lead to successful verification with signatures that contain invalid + structures but a valid digest. ### Fixed - [asn1] Add fallback to pretty print invalid UTF8 data. @@ -40,7 +41,10 @@ Forge ChangeLog `RSASSA-PKCS-v1_5` `DigestInfo` data. Additionally check that the hash algorithm identifier is a known value from RFC 8017 `PKCS1-v1-5DigestAlgorithms`. An invalid `DigestInfo` or algorithm identifier - will now cause an error to be thrown. + will now throw an error. + - **NOTE**: The previous lenient behavior is being changed to be more strict + since it could lead to security issues with crafted inputs. It is possible + that code may have to handle the errors from these stricter checks. ### Added - [oid] Added missing RFC 8017 PKCS1-v1-5DigestAlgorithms algorithm From bb822c02df0b61211836472e29b9790cc541cdb2 Mon Sep 17 00:00:00 2001 From: "David I. Lehn" Date: Thu, 17 Mar 2022 18:54:51 -0400 Subject: [PATCH 194/197] Add advisory links. --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 231efebae..a7384fb85 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,8 @@ Forge ChangeLog ["Bleichenbacher's RSA signature forgery based on implementation error"](https://mailarchive.ietf.org/arch/msg/openpgp/5rnE9ZRN1AokBVj3VqblGlP63QE/) by Hal Finney. + - CVE ID: [CVE-2022-24771](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2022-24771) + - GHSA ID: [GHSA-cfm4-qjh2-4765](https://github.com/digitalbazaar/forge/security/advisories/GHSA-cfm4-qjh2-4765) - **HIGH**: Failing to check tailing garbage bytes can lead to signature forgery. - The code does not check for tailing garbage bytes after decoding a @@ -24,10 +26,14 @@ Forge ChangeLog signature forgery based on implementation error"](https://mailarchive.ietf.org/arch/msg/openpgp/5rnE9ZRN1AokBVj3VqblGlP63QE/) by Hal Finney. + - CVE ID: [CVE-2022-24772](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2022-24772) + - GHSA ID: [GHSA-x4jg-mjrx-434g](https://github.com/digitalbazaar/forge/security/advisories/GHSA-x4jg-mjrx-434g) - **MEDIUM**: Leniency in checking type octet. - `DigestInfo` is not properly checked for proper ASN.1 structure. This can lead to successful verification with signatures that contain invalid structures but a valid digest. + - CVE ID: [CVE-2022-24773](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2022-24773) + - GHSA ID: [GHSA-2r2c-g63r-vccr](https://github.com/digitalbazaar/forge/security/advisories/GHSA-2r2c-g63r-vccr) ### Fixed - [asn1] Add fallback to pretty print invalid UTF8 data. From dc77b39dd347e7f8b60a0f25a311fe5f06130579 Mon Sep 17 00:00:00 2001 From: "David I. Lehn" Date: Thu, 17 Mar 2022 19:12:01 -0400 Subject: [PATCH 195/197] Fix error checking. - Newer object style not available in older platforms. - Using message regex style. --- tests/unit/rsa.js | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/tests/unit/rsa.js b/tests/unit/rsa.js index f1493545b..1cd9e072e 100644 --- a/tests/unit/rsa.js +++ b/tests/unit/rsa.js @@ -829,9 +829,8 @@ var UTIL = require('../../lib/util'); ASSERT.throws(function() { publicKey.verify(md.digest().getBytes(), S); - }, { - message: 'Unparsed DER bytes remain after ASN.1 parsing.' - }); + }, + /^Error: Unparsed DER bytes remain after ASN.1 parsing.$/); } function _checkBadDigestInfo(publicKey, S, skipTailingGarbage) { @@ -842,9 +841,8 @@ var UTIL = require('../../lib/util'); publicKey.verify(md.digest().getBytes(), S, undefined, { _parseAllDigestBytes: !skipTailingGarbage }); - }, { - message: 'ASN.1 object does not contain a valid RSASSA-PKCS1-v1_5 DigestInfo value.' - }); + }, + /^Error: ASN.1 object does not contain a valid RSASSA-PKCS1-v1_5 DigestInfo value.$/); } it('should check DigestInfo structure', function() { From 0f3972ad5883a9869703c6f54a0627bc454bca47 Mon Sep 17 00:00:00 2001 From: "David I. Lehn" Date: Thu, 17 Mar 2022 19:20:23 -0400 Subject: [PATCH 196/197] Update changelog. --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a7384fb85..2987c54b2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,7 @@ Forge ChangeLog =============== -## 1.3.0 - 2022-XXX +## 1.3.0 - 2022-03-17 ### Security - Three RSA PKCS#1 v1.5 signature verification issues were reported by Moosa From 6c5b90133d46af63d139b98bf65371732c8c7dad Mon Sep 17 00:00:00 2001 From: "David I. Lehn" Date: Thu, 17 Mar 2022 19:20:24 -0400 Subject: [PATCH 197/197] Release 1.3.0. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index ab7771410..68231c0ee 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "node-forge", - "version": "1.2.2-0", + "version": "1.3.0", "description": "JavaScript implementations of network transports, cryptography, ciphers, PKI, message digests, and various utilities.", "homepage": "https://github.com/digitalbazaar/forge", "author": {