Skip to content

Commit

Permalink
refact(der-to-jose) add additional param length checks
Browse files Browse the repository at this point in the history
  • Loading branch information
omsmith committed Jan 18, 2016
1 parent 1791545 commit 7d5588d
Show file tree
Hide file tree
Showing 3 changed files with 200 additions and 146 deletions.
289 changes: 163 additions & 126 deletions spec/der-to-jose.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,139 +7,176 @@ var describe = mocha.describe,
it = mocha.it;

var format = require('..');
var getParamBytesForAlg = require('../src/param-bytes-for-alg');

var CLASS_UNIVERSAL = 0,
PRIMITIVE_BIT = 0x20,
TAG_SEQ = (0x10 | PRIMITIVE_BIT) | (CLASS_UNIVERSAL << 6),
TAG_INT = 0x02 | (CLASS_UNIVERSAL << 6);

describe('#derToJose', function() {
describe('should throw for', function() {
it('no signature', function() {
function fn() {
return format.derToJose();
}

expect(fn).to.throw(TypeError);
});

it('non buffer or base64 signature', function() {
function fn() {
return format.derToJose(123);
}

expect(fn).to.throw(TypeError);
});

it('unknown algorithm', function() {
function fn() {
return format.derToJose('Zm9vLmJhci5iYXo=', 'foozleberries');
}

expect(fn).to.throw(/"foozleberries"/);
});

it('no seq', function() {
var input = new Buffer(10);
input[0] = TAG_SEQ + 1; // not seq

function fn() {
format.derToJose(input, 'ES256');
}

expect(fn).to.throw(Error, /expected "seq"/);
});

it('seq length exceeding input', function() {
var input = new Buffer(10);
input[0] = TAG_SEQ;
input[1] = 10;

function fn() {
format.derToJose(input, 'ES256');
}

expect(fn).to.throw(Error, /length/);
});

it('r is not marked as int', function() {
var input = new Buffer(10);
input[0] = TAG_SEQ;
input[1] = 8;
input[2] = TAG_INT + 1; // not int

function fn() {
format.derToJose(input, 'ES256');
}

expect(fn).to.throw(Error, /expected "int".+"r"/);
});

it('r length exceeds available input', function() {
var input = new Buffer(10);
input[0] = TAG_SEQ;
input[1] = 8;
input[2] = TAG_INT;
input[3] = 5;

function fn() {
format.derToJose(input, 'ES256');
}

expect(fn).to.throw(Error, /"r".+length/);
});

it('s is not marked as int', function() {
var input = new Buffer(10);
input[0] = TAG_SEQ;
input[1] = 8;
input[2] = TAG_INT;
input[3] = 2;
input[4] = 0;
input[5] = 0;
input[6] = TAG_INT + 1; // not int

function fn() {
format.derToJose(input, 'ES256');
}

expect(fn).to.throw(Error, /expected "int".+"s"/);
});

it('s length exceeds available input', function() {
var input = new Buffer(10);
input[0] = TAG_SEQ;
input[1] = 8;
input[2] = TAG_INT;
input[3] = 2;
input[4] = 0;
input[5] = 0;
input[6] = TAG_INT;
input[7] = 3;

function fn() {
format.derToJose(input, 'ES256');
}

expect(fn).to.throw(Error, /"s".+length/);
});

it('s length does not consume available input', function() {
var input = new Buffer(10);
input[0] = TAG_SEQ;
input[1] = 8;
input[2] = TAG_INT;
input[3] = 2;
input[4] = 0;
input[5] = 0;
input[6] = TAG_INT;
input[7] = 1;

function fn() {
format.derToJose(input, 'ES256');
}

expect(fn).to.throw(Error, /"s".+length/);
['ES256', 'ES384', 'ES512'].forEach(function(alg) {
describe(alg, function() {
describe('should throw for', function() {
it('no signature', function() {
function fn() {
return format.derToJose();
}

expect(fn).to.throw(TypeError);
});

it('non buffer or base64 signature', function() {
function fn() {
return format.derToJose(123);
}

expect(fn).to.throw(TypeError);
});

it('unknown algorithm', function() {
function fn() {
return format.derToJose('Zm9vLmJhci5iYXo=', 'foozleberries');
}

expect(fn).to.throw(/"foozleberries"/);
});

it('no seq', function() {
var input = new Buffer(10);
input[0] = TAG_SEQ + 1; // not seq

function fn() {
format.derToJose(input, alg);
}

expect(fn).to.throw(Error, /expected "seq"/);
});

it('seq length exceeding input', function() {
var input = new Buffer(10);
input[0] = TAG_SEQ;
input[1] = 10;

function fn() {
format.derToJose(input, alg);
}

expect(fn).to.throw(Error, /length/);
});

it('r is not marked as int', function() {
var input = new Buffer(10);
input[0] = TAG_SEQ;
input[1] = 8;
input[2] = TAG_INT + 1; // not int

function fn() {
format.derToJose(input, alg);
}

expect(fn).to.throw(Error, /expected "int".+"r"/);
});

it('r length exceeds available input', function() {
var input = new Buffer(10);
input[0] = TAG_SEQ;
input[1] = 8;
input[2] = TAG_INT;
input[3] = 5;

function fn() {
format.derToJose(input, alg);
}

expect(fn).to.throw(Error, /"r".+length/);
});

it('r length exceeds sensical param length', function() {
var input = new Buffer(getParamBytesForAlg(alg) + 2 + 6);
input[0] = TAG_SEQ;
input[1] = getParamBytesForAlg(alg) + 2 + 4;
input[2] = TAG_INT;
input[3] = getParamBytesForAlg(alg) + 2;

function fn() {
format.derToJose(input, alg);
}

expect(fn).to.throw(Error, /"r".+length.+acceptable/);
});

it('s is not marked as int', function() {
var input = new Buffer(10);
input[0] = TAG_SEQ;
input[1] = 8;
input[2] = TAG_INT;
input[3] = 2;
input[4] = 0;
input[5] = 0;
input[6] = TAG_INT + 1; // not int

function fn() {
format.derToJose(input, alg);
}

expect(fn).to.throw(Error, /expected "int".+"s"/);
});

it('s length exceeds available input', function() {
var input = new Buffer(10);
input[0] = TAG_SEQ;
input[1] = 8;
input[2] = TAG_INT;
input[3] = 2;
input[4] = 0;
input[5] = 0;
input[6] = TAG_INT;
input[7] = 3;

function fn() {
format.derToJose(input, alg);
}

expect(fn).to.throw(Error, /"s".+length/);
});

it('s length does not consume available input', function() {
var input = new Buffer(10);
input[0] = TAG_SEQ;
input[1] = 8;
input[2] = TAG_INT;
input[3] = 2;
input[4] = 0;
input[5] = 0;
input[6] = TAG_INT;
input[7] = 1;

function fn() {
format.derToJose(input, alg);
}

expect(fn).to.throw(Error, /"s".+length/);
});

it('s length exceeds sensical param length', function() {
var input = new Buffer(getParamBytesForAlg(alg) + 2 + 8);
input[0] = TAG_SEQ;
input[1] = getParamBytesForAlg(alg) + 2 + 6;
input[2] = TAG_INT;
input[3] = 2;
input[4] = 0;
input[5] = 0;
input[6] = TAG_INT;
input[7] = getParamBytesForAlg(alg) + 2;

function fn() {
format.derToJose(input, alg);
}

expect(fn).to.throw(Error, /"s".+length.+acceptable/);
});
});
});
});
});
34 changes: 14 additions & 20 deletions src/ecdsa-sig-formatter.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

var base64Url = require('base64-url').escape;

var getParamBytesForAlg = require('./param-bytes-for-alg');

var MAX_OCTET = 0x80,
CLASS_UNIVERSAL = 0,
PRIMITIVE_BIT = 0x20,
Expand All @@ -10,26 +12,6 @@ var MAX_OCTET = 0x80,
ENCODED_TAG_SEQ = (TAG_SEQ | PRIMITIVE_BIT) | (CLASS_UNIVERSAL << 6),
ENCODED_TAG_INT = TAG_INT | (CLASS_UNIVERSAL << 6);

function getParamSize(keySize) {
var result = ((keySize / 8) | 0) + (keySize % 8 === 0 ? 0 : 1);
return result;
}

var paramBytesForAlg = {
ES256: getParamSize(256),
ES384: getParamSize(384),
ES512: getParamSize(521)
};

function getParamBytesForAlg(alg) {
var paramBytes = paramBytesForAlg[alg];
if (paramBytes) {
return paramBytes;
}

throw new Error('Unknown algorithm "' + alg + '"');
}

function signatureAsBuffer(signature) {
if (Buffer.isBuffer(signature)) {
return signature;
Expand All @@ -44,6 +26,10 @@ function derToJose(signature, alg) {
signature = signatureAsBuffer(signature);
var paramBytes = getParamBytesForAlg(alg);

// the DER encoded param should at most be the param size, plus a padding
// zero, since due to being a signed integer
var maxEncodedParamLength = paramBytes + 1;

var inputLength = signature.length;

var offset = 0;
Expand All @@ -70,6 +56,10 @@ function derToJose(signature, alg) {
throw new Error('"r" specified length of "' + rLength + '", only "' + (inputLength - offset - 2) + '" available');
}

if (maxEncodedParamLength < rLength) {
throw new Error('"r" specified length of "' + rLength + '", max of "' + maxEncodedParamLength + '" is acceptable');
}

var r = signature.slice(offset, offset + rLength);
offset += r.length;

Expand All @@ -83,6 +73,10 @@ function derToJose(signature, alg) {
throw new Error('"s" specified length of "' + sLength + '", expected "' + (inputLength - offset) + '"');
}

if (maxEncodedParamLength < sLength) {
throw new Error('"s" specified length of "' + sLength + '", max of "' + maxEncodedParamLength + '" is acceptable');
}

var s = signature.slice(offset);
offset += s.length;

Expand Down
23 changes: 23 additions & 0 deletions src/param-bytes-for-alg.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
'use strict';

function getParamSize(keySize) {
var result = ((keySize / 8) | 0) + (keySize % 8 === 0 ? 0 : 1);
return result;
}

var paramBytesForAlg = {
ES256: getParamSize(256),
ES384: getParamSize(384),
ES512: getParamSize(521)
};

function getParamBytesForAlg(alg) {
var paramBytes = paramBytesForAlg[alg];
if (paramBytes) {
return paramBytes;
}

throw new Error('Unknown algorithm "' + alg + '"');
}

module.exports = getParamBytesForAlg;

0 comments on commit 7d5588d

Please sign in to comment.