Skip to content

Commit 5ff495b

Browse files
committed
algorithm names are case-sensitive
The IANA registered names for (supported) JWA algs are all uppercase; however, node-jwa has traditionally treated them as case-insensitive. Should a user of node-jwa try to restrict the algorithms used via a blacklist (e.g. if header.alg === 'none' then throw), this case-insensitivity may cause them to miss other values (e.g. header.alg === 'nOne'). In order to simplify the set of accepted inputs, switch algorithm name matching to be case-sensitive. This patch was provided by a third party. I thank them for their time, help and patience.
1 parent 5b3bcee commit 5ff495b

File tree

7 files changed

+42
-29
lines changed

7 files changed

+42
-29
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ RSA and ECDSA algorithms.
5555

5656
Creates a new `jwa` object with `sign` and `verify` methods for the
5757
algorithm. Valid values for algorithm can be found in the table above
58-
(`'HS256'`, `'HS384'`, etc) and are case-insensitive. Passing an invalid
58+
(`'HS256'`, `'HS384'`, etc) and are case-sensitive. Passing an invalid
5959
algorithm value will throw a `TypeError`.
6060

6161

index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,7 @@ module.exports = function jwa(algorithm) {
239239
es: createECDSAVerifer,
240240
none: createNoneVerifier,
241241
}
242-
var match = algorithm.match(/^(RS|PS|ES|HS)(256|384|512)$|^(none)$/i);
242+
var match = algorithm.match(/^(RS|PS|ES|HS)(256|384|512)$|^(none)$/);
243243
if (!match)
244244
throw typeError(MSG_INVALID_ALGORITHM, algorithm);
245245
var algo = (match[1] || match[3]).toLowerCase();

test/A.1/test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ const key = Buffer.from(JSON.parse(fs.readFileSync(path.join(__dirname, 'key.jso
1818
const signature = fs.readFileSync(path.join(__dirname, 'signature.txt'), 'ascii');
1919
const signatureFromBytes = Buffer.from(JSON.parse(fs.readFileSync(path.join(__dirname, 'signature.bytes.json'), 'utf8')));
2020

21-
const algo = jwa('hs256');
21+
const algo = jwa('HS256');
2222

2323
test('A.1', function (t) {
2424
t.plan(6);

test/A.2/test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ const pubKey = jwkToPem(jwk);
2121
const signature = fs.readFileSync(path.join(__dirname, 'signature.txt'), 'ascii');
2222
const signatureFromBytes = Buffer.from(JSON.parse(fs.readFileSync(path.join(__dirname, 'signature.bytes.json'), 'utf8')));
2323

24-
const algo = jwa('rs256');
24+
const algo = jwa('RS256');
2525

2626
test('A.2', function (t) {
2727
t.plan(6);

test/A.3/test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ const pubKey = jwkToPem(jwk);
1919

2020
const signature = fs.readFileSync(path.join(__dirname, 'signature.txt'), 'ascii');
2121

22-
const algo = jwa('es256');
22+
const algo = jwa('ES256');
2323

2424
test('A.3', function (t) {
2525
t.plan(3);

test/A.4/test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ const pubKey = jwkToPem(jwk);
1919

2020
const signature = fs.readFileSync(path.join(__dirname, 'signature.txt'), 'ascii');
2121

22-
const algo = jwa('es512');
22+
const algo = jwa('ES512');
2323

2424
test('A.4', function (t) {
2525
t.plan(3);

test/jwa.test.js

Lines changed: 36 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ test('HMAC signing, verifying', function (t) {
4040
const input = 'eugene mirman';
4141
const secret = 'shhhhhhhhhh';
4242
BIT_DEPTHS.forEach(function (bits) {
43-
const algo = jwa('hs'+bits);
43+
const algo = jwa('HS'+bits);
4444
const sig = algo.sign(input, secret);
4545
t.ok(algo.verify(input, sig, secret), 'should verify');
4646
t.notOk(algo.verify(input, 'other sig', secret), 'should verify');
@@ -57,7 +57,7 @@ if (SUPPORTS_KEY_OBJECTS) {
5757
const secretObj = crypto.createSecretKey(secretBuf);
5858

5959
test('HS' + bits + 'signing, verifying (w/ KeyObject)', function (t) {
60-
const algo = jwa('hs' + bits);
60+
const algo = jwa('HS' + bits);
6161

6262
const sigs = [
6363
algo.sign(input, secret),
@@ -79,7 +79,7 @@ if (SUPPORTS_KEY_OBJECTS) {
7979
test('RSA signing, verifying', function (t) {
8080
const input = 'h. jon benjamin';
8181
BIT_DEPTHS.forEach(function (bits) {
82-
const algo = jwa('rs'+bits);
82+
const algo = jwa('RS'+bits);
8383
const sig = algo.sign(input, rsaPrivateKey);
8484
t.ok(algo.verify(input, sig, rsaPublicKey), 'should verify');
8585
t.notOk(algo.verify(input, sig, rsaWrongPublicKey), 'shoud not verify');
@@ -92,7 +92,7 @@ if (semver.gte(nodeVersion, '0.11.8')) {
9292
test('RSA with passphrase signing, verifying', function (t) {
9393
const input = 'test input';
9494
BIT_DEPTHS.forEach(function (bits) {
95-
const algo = jwa('rs'+bits);
95+
const algo = jwa('RS'+bits);
9696
const secret = 'test_pass';
9797
const sig = algo.sign(input, {key: rsaPrivateKeyWithPassphrase, passphrase: secret});
9898
t.ok(algo.verify(input, sig, rsaPublicKeyWithPassphrase), 'should verify');
@@ -105,7 +105,7 @@ if (SUPPORTS_KEY_OBJECTS) {
105105
BIT_DEPTHS.forEach(function (bits) {
106106
test('RS'+bits+': signing, verifying (KeyObject)', function (t) {
107107
const input = 'h. jon benjamin';
108-
const algo = jwa('rs'+bits);
108+
const algo = jwa('RS'+bits);
109109
const sig = algo.sign(input, crypto.createPrivateKey(rsaPrivateKey));
110110
t.ok(algo.verify(input, sig, crypto.createPublicKey(rsaPublicKey)), 'should verify');
111111
t.notOk(algo.verify(input, sig, crypto.createPublicKey(rsaWrongPublicKey)), 'shoud not verify');
@@ -119,7 +119,7 @@ if (semver.satisfies(nodeVersion, '^6.12.0 || >=8.0.0')) {
119119
test('RSA-PSS signing, verifying', function (t) {
120120
const input = 'h. jon benjamin';
121121
BIT_DEPTHS.forEach(function (bits) {
122-
const algo = jwa('ps'+bits);
122+
const algo = jwa('PS'+bits);
123123
const sig = algo.sign(input, rsaPrivateKey);
124124
t.ok(algo.verify(input, sig, rsaPublicKey), 'should verify');
125125
t.notOk(algo.verify(input, sig, rsaWrongPublicKey), 'shoud not verify');
@@ -131,7 +131,7 @@ if (semver.satisfies(nodeVersion, '^6.12.0 || >=8.0.0')) {
131131
BIT_DEPTHS.forEach(function (bits) {
132132
test('PS'+bits+': signing, verifying (KeyObject)', function (t) {
133133
const input = 'h. jon benjamin';
134-
const algo = jwa('ps'+bits);
134+
const algo = jwa('PS'+bits);
135135
const sig = algo.sign(input, crypto.createPrivateKey(rsaPrivateKey));
136136
t.ok(algo.verify(input, sig, crypto.createPublicKey(rsaPublicKey)), 'should verify');
137137
t.notOk(algo.verify(input, sig, crypto.createPublicKey(rsaWrongPublicKey)), 'should not verify');
@@ -145,7 +145,7 @@ if (semver.satisfies(nodeVersion, '^6.12.0 || >=8.0.0')) {
145145
BIT_DEPTHS.forEach(function (bits) {
146146
test('RS'+bits+': openssl sign -> js verify', function (t) {
147147
const input = 'iodine';
148-
const algo = jwa('rs'+bits);
148+
const algo = jwa('RS'+bits);
149149
const dgst = spawn('openssl', ['dgst', '-sha'+bits, '-sign', __dirname + '/rsa-private.pem']);
150150
var buffer = Buffer.alloc(0);
151151

@@ -173,7 +173,7 @@ if (semver.satisfies(nodeVersion, '^6.12.0 || >=8.0.0')) {
173173
BIT_DEPTHS.forEach(function (bits) {
174174
test('PS'+bits+': openssl sign -> js verify', function (t) {
175175
const input = 'iodine';
176-
const algo = jwa('ps'+bits);
176+
const algo = jwa('PS'+bits);
177177
const dgst = spawn('openssl', ['dgst', '-sha'+bits, '-sigopt', 'rsa_padding_mode:pss', '-sigopt', 'rsa_pss_saltlen:-1', '-sign', __dirname + '/rsa-private.pem']);
178178
var buffer = Buffer.alloc(0);
179179

@@ -201,7 +201,7 @@ if (semver.satisfies(nodeVersion, '^6.12.0 || >=8.0.0')) {
201201
BIT_DEPTHS.forEach(function (bits) {
202202
test('ES'+bits+': signing, verifying', function (t) {
203203
const input = 'kristen schaal';
204-
const algo = jwa('es'+bits);
204+
const algo = jwa('ES'+bits);
205205
const sig = algo.sign(input, ecdsaPrivateKey[bits]);
206206
t.ok(algo.verify(input, sig, ecdsaPublicKey[bits]), 'should verify');
207207
t.notOk(algo.verify(input, sig, ecdsaWrongPublicKey[bits]), 'should not verify');
@@ -213,7 +213,7 @@ if (SUPPORTS_KEY_OBJECTS) {
213213
BIT_DEPTHS.forEach(function (bits) {
214214
test('ES'+bits+': signing, verifying (KeyObject)', function (t) {
215215
const input = 'kristen schaal';
216-
const algo = jwa('es'+bits);
216+
const algo = jwa('ES'+bits);
217217
const sig = algo.sign(input, crypto.createPrivateKey(ecdsaPrivateKey[bits]));
218218
t.ok(algo.verify(input, sig, crypto.createPublicKey(ecdsaPublicKey[bits])), 'should verify');
219219
t.notOk(algo.verify(input, sig, crypto.createPublicKey(ecdsaWrongPublicKey[bits])), 'should not verify');
@@ -225,7 +225,7 @@ if (SUPPORTS_KEY_OBJECTS) {
225225
BIT_DEPTHS.forEach(function (bits) {
226226
test('ES'+bits+': openssl sign -> js verify', function (t) {
227227
const input = 'strawberry';
228-
const algo = jwa('es'+bits);
228+
const algo = jwa('ES'+bits);
229229
const dgst = spawn('openssl', ['dgst', '-sha'+bits, '-sign', __dirname + '/ec'+bits+'-private.pem']);
230230
var buffer = Buffer.alloc(0);
231231
dgst.stdin.end(input);
@@ -264,7 +264,7 @@ BIT_DEPTHS.forEach(function (bits) {
264264
const privateKey = ecdsaPrivateKey[bits];
265265
const signature =
266266
formatEcdsa.joseToDer(
267-
jwa('es'+bits).sign(input, privateKey),
267+
jwa('ES'+bits).sign(input, privateKey),
268268
'ES' + bits
269269
);
270270
fs.writeFileSync(inputFile, input);
@@ -301,7 +301,7 @@ BIT_DEPTHS.forEach(function (bits) {
301301
const privateKey = rsaPrivateKey;
302302
const signature =
303303
base64url.toBuffer(
304-
jwa('rs'+bits).sign(input, privateKey)
304+
jwa('RS'+bits).sign(input, privateKey)
305305
);
306306
fs.writeFileSync(signatureFile, signature);
307307
fs.writeFileSync(inputFile, input);
@@ -339,7 +339,7 @@ if (semver.satisfies(nodeVersion, '^6.12.0 || >=8.0.0')) {
339339
const privateKey = rsaPrivateKey;
340340
const signature =
341341
base64url.toBuffer(
342-
jwa('ps'+bits).sign(input, privateKey)
342+
jwa('PS'+bits).sign(input, privateKey)
343343
);
344344
fs.writeFileSync(signatureFile, signature);
345345
fs.writeFileSync(inputFile, input);
@@ -376,6 +376,19 @@ test('jwa: some garbage algorithm', function (t) {
376376
t.end();
377377
});
378378

379+
['hs256', 'nonE', 'ps256', 'es256', 'rs256'].forEach(function (alg) {
380+
test('jwa: non-IANA names', function (t) {
381+
try {
382+
jwa(alg);
383+
t.fail('should throw');
384+
} catch(ex) {
385+
t.same(ex.name, 'TypeError');
386+
t.ok(ex.message.match(/valid algorithm/), 'should say something about algorithms');
387+
}
388+
t.end();
389+
});
390+
});
391+
379392
['ahs256b', 'anoneb', 'none256', 'rsnone'].forEach(function (superstringAlg) {
380393
test('jwa: superstrings of other algorithms', function (t) {
381394
try {
@@ -403,7 +416,7 @@ test('jwa: some garbage algorithm', function (t) {
403416
});
404417

405418
test('jwa: hs512, missing secret', function (t) {
406-
const algo = jwa('hs512');
419+
const algo = jwa('HS512');
407420
try {
408421
algo.sign('some stuff');
409422
t.fail('should throw');
@@ -415,7 +428,7 @@ test('jwa: hs512, missing secret', function (t) {
415428
});
416429

417430
test('jwa: hs512, weird input type', function (t) {
418-
const algo = jwa('hs512');
431+
const algo = jwa('HS512');
419432
const input = {a: ['whatever', 'this', 'is']};
420433
const secret = 'bones';
421434
const sig = algo.sign(input, secret);
@@ -425,7 +438,7 @@ test('jwa: hs512, weird input type', function (t) {
425438
});
426439

427440
test('jwa: rs512, weird input type', function (t) {
428-
const algo = jwa('rs512');
441+
const algo = jwa('RS512');
429442
const input = {a: ['whatever', 'this', 'is']};
430443
const sig = algo.sign(input, rsaPrivateKey);
431444
t.ok(algo.verify(input, sig, rsaPublicKey), 'should verify');
@@ -434,7 +447,7 @@ test('jwa: rs512, weird input type', function (t) {
434447
});
435448

436449
test('jwa: rs512, missing signing key', function (t) {
437-
const algo = jwa('rs512');
450+
const algo = jwa('RS512');
438451
try {
439452
algo.sign('some stuff');
440453
t.fail('should throw');
@@ -446,7 +459,7 @@ test('jwa: rs512, missing signing key', function (t) {
446459
});
447460

448461
test('jwa: rs512, missing verifying key', function (t) {
449-
const algo = jwa('rs512');
462+
const algo = jwa('RS512');
450463
const input = {a: ['whatever', 'this', 'is']};
451464
const sig = algo.sign(input, rsaPrivateKey);
452465
try {
@@ -461,7 +474,7 @@ test('jwa: rs512, missing verifying key', function (t) {
461474

462475
if (semver.satisfies(nodeVersion, '^6.12.0 || >=8.0.0')) {
463476
test('jwa: ps512, weird input type', function (t) {
464-
const algo = jwa('ps512');
477+
const algo = jwa('PS512');
465478
const input = {a: ['whatever', 'this', 'is']};
466479
const sig = algo.sign(input, rsaPrivateKey);
467480
t.ok(algo.verify(input, sig, rsaPublicKey), 'should verify');
@@ -470,7 +483,7 @@ if (semver.satisfies(nodeVersion, '^6.12.0 || >=8.0.0')) {
470483
});
471484

472485
test('jwa: ps512, missing signing key', function (t) {
473-
const algo = jwa('ps512');
486+
const algo = jwa('PS512');
474487
try {
475488
algo.sign('some stuff');
476489
t.fail('should throw');
@@ -482,7 +495,7 @@ if (semver.satisfies(nodeVersion, '^6.12.0 || >=8.0.0')) {
482495
});
483496

484497
test('jwa: ps512, missing verifying key', function (t) {
485-
const algo = jwa('ps512');
498+
const algo = jwa('PS512');
486499
const input = {a: ['whatever', 'this', 'is']};
487500
const sig = algo.sign(input, rsaPrivateKey);
488501
try {

0 commit comments

Comments
 (0)