From dfc6ff365ad6ce6030b8904c44aed1ed3d80a6fa Mon Sep 17 00:00:00 2001 From: Leonardo Zanivan Date: Fri, 19 Apr 2024 17:23:33 +0100 Subject: [PATCH] Add support for oaepHash=sha256 --- README.md | 7 +++--- lib/templates/keyinfo.tpl.xml.js | 4 ++-- lib/xmlenc.js | 37 ++++++++++++++++++++++---------- package-lock.json | 7 ++---- package.json | 5 +---- test/xmlenc.encryptedkey.js | 22 +++++++++++++++---- 6 files changed, 52 insertions(+), 30 deletions(-) diff --git a/README.md b/README.md index 9b77964..a8595ab 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,8 @@ [![Build Status](https://travis-ci.org/auth0/node-xml-encryption.png)](https://travis-ci.org/auth0/node-xml-encryption) -W3C XML Encryption implementation for node.js (http://www.w3.org/TR/xmlenc-core/) +W3C XML Encryption implementation for Node.js (http://www.w3.org/TR/xmlenc-core/) -Supports node >= 12 < 18 - -node 18 not supported due to https://github.com/nodejs/node/issues/52017 for Triple DES algorithms. +Node 18+ does not support Triple DES algorithms due to https://github.com/nodejs/node/issues/52017 ## Usage @@ -20,6 +18,7 @@ var options = { pem: fs.readFileSync(__dirname + '/your_public_cert.pem'), encryptionAlgorithm: 'http://www.w3.org/2001/04/xmlenc#aes256-cbc', keyEncryptionAlgorithm: 'http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p', + keyEncryptionDigest: 'sha1', disallowEncryptionWithInsecureAlgorithm: true, warnInsecureAlgorithm: true }; diff --git a/lib/templates/keyinfo.tpl.xml.js b/lib/templates/keyinfo.tpl.xml.js index 9f7316c..b629b37 100644 --- a/lib/templates/keyinfo.tpl.xml.js +++ b/lib/templates/keyinfo.tpl.xml.js @@ -1,10 +1,10 @@ var escapehtml = require('escape-html'); -module.exports = ({ encryptionPublicCert, encryptedKey, keyEncryptionMethod }) => ` +module.exports = ({ encryptionPublicCert, encryptedKey, keyEncryptionMethod, keyEncryptionDigest }) => ` - + ${encryptionPublicCert} diff --git a/lib/xmlenc.js b/lib/xmlenc.js index 7491744..b5b204f 100644 --- a/lib/xmlenc.js +++ b/lib/xmlenc.js @@ -9,13 +9,13 @@ const insecureAlgorithms = [ //https://csrc.nist.gov/News/2017/Update-to-Current-Use-and-Deprecation-of-TDEA 'http://www.w3.org/2001/04/xmlenc#tripledes-cbc']; -function encryptKeyInfoWithScheme(symmetricKey, options, scheme, callback) { - const padding = scheme === 'RSA-OAEP' ? crypto.constants.RSA_PKCS1_OAEP_PADDING : crypto.constants.RSA_PKCS1_PADDING; +function encryptKeyInfoWithScheme(symmetricKey, options, padding, callback) { const symmetricKeyBuffer = Buffer.isBuffer(symmetricKey) ? symmetricKey : Buffer.from(symmetricKey, 'utf-8'); try { var encrypted = crypto.publicEncrypt({ key: options.rsa_pub, + oaepHash: padding == crypto.constants.RSA_PKCS1_OAEP_PADDING ? options.keyEncryptionDigest : undefined, padding: padding }, symmetricKeyBuffer); var base64EncodedEncryptedKey = encrypted.toString('base64'); @@ -23,7 +23,8 @@ function encryptKeyInfoWithScheme(symmetricKey, options, scheme, callback) { var params = { encryptedKey: base64EncodedEncryptedKey, encryptionPublicCert: '' + utils.pemToCert(options.pem.toString()) + '', - keyEncryptionMethod: options.keyEncryptionAlgorithm + keyEncryptionMethod: options.keyEncryptionAlgorithm, + keyEncryptionDigest: options.keyEncryptionDigest, }; var result = utils.renderTemplate('keyinfo', params); @@ -47,13 +48,14 @@ function encryptKeyInfo(symmetricKey, options, callback) { && insecureAlgorithms.indexOf(options.keyEncryptionAlgorithm) >= 0) { return callback(new Error('encryption algorithm ' + options.keyEncryptionAlgorithm + 'is not secure')); } + options.keyEncryptionDigest = options.keyEncryptionDigest || 'sha1'; switch (options.keyEncryptionAlgorithm) { case 'http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p': - return encryptKeyInfoWithScheme(symmetricKey, options, 'RSA-OAEP', callback); + return encryptKeyInfoWithScheme(symmetricKey, options, crypto.constants.RSA_PKCS1_OAEP_PADDING, callback); case 'http://www.w3.org/2001/04/xmlenc#rsa-1_5': utils.warnInsecureAlgorithm(options.keyEncryptionAlgorithm, options.warnInsecureAlgorithm); - return encryptKeyInfoWithScheme(symmetricKey, options, 'RSAES-PKCS1-V1_5', callback); + return encryptKeyInfoWithScheme(symmetricKey, options, crypto.constants.RSA_PKCS1_PADDING, callback); default: return callback(new Error('encryption key algorithm not supported')); @@ -235,6 +237,20 @@ function decryptKeyInfo(doc, options) { throw new Error('cant find encryption algorithm'); } + let oaepHash = 'sha1'; + const keyDigestMethod = xpath.select("//*[local-name(.)='KeyInfo']/*[local-name(.)='EncryptedKey']/*[local-name(.)='EncryptionMethod']/*[local-name(.)='DigestMethod']", doc)[0]; + if (keyDigestMethod) { + const keyDigestMethodAlgorithm = keyDigestMethod.getAttribute('Algorithm'); + switch (keyDigestMethodAlgorithm) { + case 'http://www.w3.org/2000/09/xmldsig#sha256': + oaepHash = 'sha256'; + break; + case 'http://www.w3.org/2000/09/xmldsig#sha512': + oaepHash = 'sha512'; + break; + } + } + var keyEncryptionAlgorithm = keyEncryptionMethod.getAttribute('Algorithm'); if (options.disallowDecryptionWithInsecureAlgorithm && insecureAlgorithms.indexOf(keyEncryptionAlgorithm) >= 0) { @@ -246,19 +262,18 @@ function decryptKeyInfo(doc, options) { switch (keyEncryptionAlgorithm) { case 'http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p': - return decryptKeyInfoWithScheme(encryptedKey, options, 'RSA-OAEP'); + return decryptKeyInfoWithScheme(encryptedKey, options, crypto.constants.RSA_PKCS1_OAEP_PADDING, oaepHash); case 'http://www.w3.org/2001/04/xmlenc#rsa-1_5': utils.warnInsecureAlgorithm(keyEncryptionAlgorithm, options.warnInsecureAlgorithm); - return decryptKeyInfoWithScheme(encryptedKey, options, 'RSAES-PKCS1-V1_5'); + return decryptKeyInfoWithScheme(encryptedKey, options, crypto.constants.RSA_PKCS1_PADDING); default: throw new Error('key encryption algorithm ' + keyEncryptionAlgorithm + ' not supported'); } } -function decryptKeyInfoWithScheme(encryptedKey, options, scheme) { - var padding = scheme === 'RSA-OAEP' ? crypto.constants.RSA_PKCS1_OAEP_PADDING : crypto.constants.RSA_PKCS1_PADDING; - var key = Buffer.from(encryptedKey.textContent, 'base64'); - var decrypted = crypto.privateDecrypt({ key: options.key, padding: padding}, key); +function decryptKeyInfoWithScheme(encryptedKey, options, padding, oaepHash) { + const key = Buffer.from(encryptedKey.textContent, 'base64'); + const decrypted = crypto.privateDecrypt({ key: options.key, padding, oaepHash}, key); return Buffer.from(decrypted, 'binary'); } diff --git a/package-lock.json b/package-lock.json index ed9d877..7abe921 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "xml-encryption", - "version": "3.0.2", + "version": "3.1.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "xml-encryption", - "version": "3.0.1", + "version": "3.1.0", "license": "MIT", "dependencies": { "@xmldom/xmldom": "^0.8.5", @@ -17,9 +17,6 @@ "mocha": "^7.1.2", "should": "^11.2.1", "sinon": "^9.0.2" - }, - "engines": { - "node": ">=12" } }, "node_modules/@sinonjs/commons": { diff --git a/package.json b/package.json index e388f21..3a33621 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "xml-encryption", - "version": "3.0.2", + "version": "3.1.0", "devDependencies": { "mocha": "^7.1.2", "should": "^11.2.1", @@ -30,8 +30,5 @@ ], "scripts": { "test": "mocha" - }, - "engines": { - "node": ">=12 < 18" } } diff --git a/test/xmlenc.encryptedkey.js b/test/xmlenc.encryptedkey.js index 8835dda..bc21160 100644 --- a/test/xmlenc.encryptedkey.js +++ b/test/xmlenc.encryptedkey.js @@ -1,9 +1,7 @@ var assert = require('assert'); var fs = require('fs'); -var should = require('should'); var sinon = require('sinon'); var xmlenc = require('../lib'); -var xpath = require('xpath'); describe('encrypt', function() { let consoleSpy = null; @@ -39,6 +37,21 @@ describe('encrypt', function() { encryptionAlgorithm: 'http://www.w3.org/2009/xmlenc11#aes128-gcm', keyEncryptionAlgorithm: 'http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p' } + }, + { + name: 'aes-128-gcm with sha256', + encryptionOptions: { + encryptionAlgorithm: 'http://www.w3.org/2009/xmlenc11#aes128-gcm', + keyEncryptionAlgorithm: 'http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p', + keyEncryptionDigest: 'sha256' + } + }, { + name: 'aes-128-gcm with sha512', + encryptionOptions: { + encryptionAlgorithm: 'http://www.w3.org/2009/xmlenc11#aes128-gcm', + keyEncryptionAlgorithm: 'http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p', + keyEncryptionDigest: 'sha512' + } }, { name: 'des-ede3-cbc', encryptionOptions: { @@ -72,6 +85,7 @@ describe('encrypt', function() { xmlenc.encrypt(content, options, function(err, result) { xmlenc.decrypt(result, { key: fs.readFileSync(__dirname + '/test-auth0.key'), warnInsecureAlgorithm: false}, function (err, decrypted) { + if (err) return done(err); assert.equal(decrypted, content); done(); }); @@ -92,7 +106,7 @@ describe('encrypt', function() { assert(err); assert(!result); //should not pop up warns due to options.warnInsecureAlgorithm = false; - consoleSpy.called.should.equal(false); + assert.equal(consoleSpy.called, false); done(); }); }); @@ -222,7 +236,7 @@ describe('encrypt', function() { xmlenc.encryptKeyInfo(plaintext, options, function(err, encryptedKeyInfo) { if (err) return done(err); - consoleSpy.called.should.equal(true); + assert.equal(consoleSpy.called, true); assert.throws( function(){xmlenc.decryptKeyInfo( encryptedKeyInfo,