Skip to content

Commit dae9b3f

Browse files
committed
feat: Add support for AES-GCM family
1 parent e711f7b commit dae9b3f

File tree

4 files changed

+255
-213
lines changed

4 files changed

+255
-213
lines changed

lib/xmlenc.js

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,12 @@ function encrypt(content, options, callback) {
7878
case 'http://www.w3.org/2001/04/xmlenc#aes256-cbc':
7979
crypto.randomBytes(32, cb); // generate a symmetric random key 32 bytes length
8080
break;
81+
case 'http://www.w3.org/2009/xmlenc11#aes128-gcm':
82+
crypto.randomBytes(16, cb); // generate a symmetric random key 16 bytes length
83+
break;
84+
case 'http://www.w3.org/2009/xmlenc11#aes256-gcm':
85+
crypto.randomBytes(32, cb); // generate a symmetric random key 32 bytes length
86+
break;
8187
case 'http://www.w3.org/2001/04/xmlenc#tripledes-cbc':
8288
crypto.randomBytes(24, cb); // generate a symmetric random key 24 bytes (192 bits) length
8389
break;
@@ -100,6 +106,18 @@ function encrypt(content, options, callback) {
100106
cb(null, encryptedContent);
101107
});
102108
break;
109+
case 'http://www.w3.org/2009/xmlenc11#aes128-gcm':
110+
encryptWithAlgorithm('aes-128-gcm', symmetricKey, 12, content, options.input_encoding, function (err, encryptedContent) {
111+
if (err) return cb(err);
112+
cb(null, encryptedContent);
113+
});
114+
break;
115+
case 'http://www.w3.org/2009/xmlenc11#aes256-gcm':
116+
encryptWithAlgorithm('aes-256-gcm', symmetricKey, 12, content, options.input_encoding, function (err, encryptedContent) {
117+
if (err) return cb(err);
118+
cb(null, encryptedContent);
119+
});
120+
break;
103121
case 'http://www.w3.org/2001/04/xmlenc#tripledes-cbc':
104122
encryptWithAlgorithm('des-ede3-cbc', symmetricKey, 8, content, options.input_encoding, function (err, encryptedContent) {
105123
if (err) return cb(err);
@@ -114,7 +132,6 @@ function encrypt(content, options, callback) {
114132
function encrypt_key(symmetricKey, encryptedContent, cb) {
115133
encryptKeyInfo(symmetricKey, options, function(err, keyInfo) {
116134
if (err) return cb(err);
117-
118135
var result = utils.renderTemplate('encrypted-key', {
119136
encryptedContent: encryptedContent.toString('base64'),
120137
keyInfo: keyInfo,
@@ -170,14 +187,17 @@ function decrypt(xml, options, callback) {
170187
var encryptedContent = xpath.select("//*[local-name(.)='EncryptedData']/*[local-name(.)='CipherData']/*[local-name(.)='CipherValue']", doc)[0];
171188

172189
var encrypted = Buffer.from(encryptedContent.textContent, 'base64');
173-
174190
switch (encryptionAlgorithm) {
175191
case 'http://www.w3.org/2001/04/xmlenc#aes128-cbc':
176192
return callback(null, decryptWithAlgorithm('aes-128-cbc', symmetricKey, 16, encrypted));
177193
case 'http://www.w3.org/2001/04/xmlenc#aes256-cbc':
178194
return callback(null, decryptWithAlgorithm('aes-256-cbc', symmetricKey, 16, encrypted));
179195
case 'http://www.w3.org/2001/04/xmlenc#tripledes-cbc':
180196
return callback(null, decryptWithAlgorithm('des-ede3-cbc', symmetricKey, 8, encrypted));
197+
case 'http://www.w3.org/2009/xmlenc11#aes128-gcm':
198+
return callback(null, decryptWithAlgorithm('aes-128-gcm', symmetricKey, 12, encrypted));
199+
case 'http://www.w3.org/2009/xmlenc11#aes256-gcm':
200+
return callback(null, decryptWithAlgorithm('aes-256-gcm', symmetricKey, 12, encrypted));
181201
default:
182202
return callback(new Error('encryption algorithm ' + encryptionAlgorithm + ' not supported'));
183203
}
@@ -237,24 +257,34 @@ function encryptWithAlgorithm(algorithm, symmetricKey, ivLength, content, encodi
237257
var cipher = crypto.createCipheriv(algorithm, symmetricKey, iv);
238258
// encrypted content
239259
var encrypted = cipher.update(content, encoding, 'binary') + cipher.final('binary');
240-
return callback(null, Buffer.concat([iv, Buffer.from(encrypted, 'binary')]));
260+
var authTag = algorithm.slice(-3) === "gcm" ? cipher.getAuthTag() : Buffer.from("");
261+
//Format mentioned: https://www.w3.org/TR/xmlenc-core1/#sec-AES-GCM
262+
var r = Buffer.concat([iv, Buffer.from(encrypted, 'binary'), authTag]);
263+
return callback(null, r);
241264
});
242265
}
243266

244267
function decryptWithAlgorithm(algorithm, symmetricKey, ivLength, content) {
245268
var decipher = crypto.createDecipheriv(algorithm, symmetricKey, content.slice(0,ivLength));
246269
decipher.setAutoPadding(false);
247270

271+
if (algorithm.slice(-3) === "gcm") {
272+
decipher.setAuthTag(content.slice(-16));
273+
content = content.slice(0,-16);
274+
}
248275
var decrypted = decipher.update(content.slice(ivLength), null, 'binary') + decipher.final('binary');
249276

277+
if (algorithm.slice(-3) !== "gcm") {
250278
// Remove padding bytes equal to the value of the last byte of the returned data.
279+
//Padding for GCM not required per: https://www.w3.org/TR/xmlenc-core1/#sec-AES-GCM
251280
var padding = decrypted.charCodeAt(decrypted.length - 1);
252281
if (1 <= padding && padding <= ivLength) {
253282
decrypted = decrypted.substr(0, decrypted.length - padding);
254283
} else {
255284
callback(new Error('padding length invalid'));
256285
return;
257286
}
287+
}
258288

259289
return Buffer.from(decrypted, 'binary').toString('utf8');
260290
}

0 commit comments

Comments
 (0)