diff --git a/lib/asn1.js b/lib/asn1.js index e3cb5007b..7a2d2d4ab 100644 --- a/lib/asn1.js +++ b/lib/asn1.js @@ -778,17 +778,18 @@ asn1.oidToDer = function(oid) { var values = oid.split('.'); var bytes = forge.util.createBuffer(); - // first byte is 40 * value1 + value2 - bytes.putByte(40 * parseInt(values[0], 10) + parseInt(values[1], 10)); - // other bytes are each value in base 128 with 8th bit set except for - // the last byte for each value + // the first subidentifier is 40 * value1 + value2; the remaining + // subidentifiers are each a single value. every subidentifier is encoded in + // base 128 with the 8th bit set on every byte except its last. var last, valueBytes, value, b; - for(var i = 2; i < values.length; ++i) { + for(var i = 1; i < values.length; ++i) { // produce value bytes in reverse because we don't know how many // bytes it will take to store the value last = true; valueBytes = []; - value = parseInt(values[i], 10); + value = (i === 1) ? + 40 * parseInt(values[0], 10) + parseInt(values[1], 10) : + parseInt(values[i], 10); // TODO: Change bitwise logic to allow larger values. if(value > 0xffffffff) { throw new Error('OID value too large; max is 32-bits.'); @@ -830,13 +831,12 @@ asn1.derToOid = function(bytes) { bytes = forge.util.createBuffer(bytes); } - // first byte is 40 * value1 + value2 - var b = bytes.getByte(); - oid = Math.floor(b / 40) + '.' + (b % 40); - - // other bytes are each value in base 128 with 8th bit set except for - // the last byte for each value + // each subidentifier is encoded in base 128 with the 8th bit set on every + // byte except its last; the first subidentifier encodes the first two arcs + // as 40 * value1 + value2. + var b; var value = 0; + var first = true; while(bytes.length() > 0) { // error if 7b shift would exceed Number.MAX_SAFE_INTEGER // (Number.MAX_SAFE_INTEGER / 128) @@ -850,7 +850,16 @@ asn1.derToOid = function(bytes) { value += b & 0x7F; } else { // last byte - oid += '.' + (value + b); + value += b; + if(first) { + // split the first subidentifier into the first two arcs + oid = (value < 80) ? + Math.floor(value / 40) + '.' + (value % 40) : + '2.' + (value - 80); + first = false; + } else { + oid += '.' + value; + } value = 0; } } diff --git a/tests/unit/asn1.js b/tests/unit/asn1.js index a99783cb9..4e8a6906e 100644 --- a/tests/unit/asn1.js +++ b/tests/unit/asn1.js @@ -54,6 +54,18 @@ var UTIL = require('../../lib/util'); ); }); + it('should convert an OID with a multi-byte first subidentifier to DER', function() { + // X.690 8.19: the first subidentifier (40 * arc1 + arc2) can exceed one + // byte when arc1 is 2, so it must be encoded in base 128 + ASSERT.equal(ASN1.oidToDer('2.999.3').toHex(), '883703'); + ASSERT.equal(ASN1.oidToDer('2.100').toHex(), '8134'); + }); + + it('should convert an OID with a multi-byte first subidentifier from DER', function() { + ASSERT.equal(ASN1.derToOid(UTIL.hexToBytes('883703')), '2.999.3'); + ASSERT.equal(ASN1.derToOid(UTIL.hexToBytes('8134')), '2.100'); + }); + it('should convert INTEGER 0 to DER', function() { ASSERT.equal(ASN1.integerToDer(0).toHex(), '00'); });