fcoin/lib/utils/asn1.js
2016-11-19 02:47:44 -08:00

362 lines
8.2 KiB
JavaScript

/*!
* asn1.js - asn1 parsing for bcoin
* Copyright (c) 2016, Christopher Jeffrey (MIT License).
* https://github.com/bcoin-org/bcoin
*
* Parts of this software are based on asn1.js.
* https://github.com/indutny/asn1.js
*
* Copyright Fedor Indutny, 2013.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to
* deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
'use strict';
var assert = require('assert');
var BufferReader = require('./reader');
var ASN1 = exports;
ASN1.parseTag = function parseTag(p) {
var tag = p.readU8();
var primitive = (tag & 0x20) === 0;
var oct;
if ((tag & 0x1f) === 0x1f) {
oct = tag;
tag = 0;
while ((oct & 0x80) === 0x80) {
oct = p.readU8();
tag <<= 7;
tag |= oct & 0x7f;
}
} else {
tag &= 0x1f;
}
return {
primitive: primitive,
tag: tag,
len: ASN1.parseLen(p, primitive)
};
};
ASN1.parseLen = function parseLen(p, primitive) {
var len = p.readU8();
var num, i, j;
// Indefinite form
if (!primitive && len === 0x80)
return null;
// Definite form
if ((len & 0x80) === 0) {
// Short form
return len;
}
// Long form
num = len & 0x7f;
assert(num < 4, 'length octect is too long');
len = 0;
for (i = 0; i < num; i++) {
len <<= 8;
j = p.readU8();
len |= j;
}
return len;
};
ASN1.parseCert = function parseCert(data) {
var d = BufferReader(data);
var p;
d.start();
p = BufferReader(ASN1.parseSeq(d));
return {
tbs: ASN1.parseTBS(p),
sigAlg: ASN1.parseAlgIdent(p),
sig: ASN1.parseBitstr(p),
raw: d.endData(true)
};
};
ASN1.parseTBS = function parseTBS(data) {
var d = BufferReader(data);
var p;
d.start();
p = BufferReader(ASN1.parseSeq(d));
return {
version: ASN1.parseExplicitInt(p, 0, true),
serial: ASN1.parseInt(p),
sig: ASN1.parseAlgIdent(p),
issuer: ASN1.parseName(p),
validity: ASN1.parseValidity(p),
subject: ASN1.parseName(p),
pubkey: ASN1.parsePubkey(p),
raw: d.endData(true)
};
};
ASN1.parseSeq = function parseSeq(data) {
var p = BufferReader(data);
var tag = ASN1.parseTag(p);
assert.equal(tag.tag, 0x10); // seq
return p.readBytes(tag.len, true);
};
ASN1.parseInt = function parseInt(data, readNum) {
var p = BufferReader(data);
var tag = ASN1.parseTag(p);
var num;
assert.equal(tag.tag, 0x02); // int
num = p.readBytes(tag.len, true);
if (readNum)
return num.readUIntBE(0, num.length);
return num;
};
ASN1.parseExplicitInt = function parseExplicitInt(data, i, readNum) {
var p = BufferReader(data);
var off = p.offset;
var tag = ASN1.parseTag(p);
if (tag.tag !== i) {
p.seek(-(p.offset - off));
return -1;
}
return ASN1.parseInt(p, readNum);
};
ASN1.parseBitstr = function parseBitstr(data) {
var p = BufferReader(data);
var tag = ASN1.parseTag(p);
assert.equal(tag.tag, 0x03); // bitstr
return ASN1.alignBitstr(p.readBytes(tag.len, true));
};
ASN1.parseString = function parseString(data) {
var p = BufferReader(data);
var tag = ASN1.parseTag(p);
switch (tag.tag) {
case 0x03: // bitstr
return ASN1.alignBitstr(p.readBytes(tag.len, true));
case 0x04: // octstr
case 0x12: // numstr
case 0x13: // prinstr
case 0x14: // t61str
case 0x15: // videostr
case 0x16: // ia5str
case 0x19: // graphstr
case 0x0c: // utf8str
case 0x1a: // iso646str
case 0x1b: // genstr
case 0x1c: // unistr
case 0x1d: // charstr
case 0x1e: // bmpstr
return p.readString('utf8', tag.len);
default:
assert(false, 'Bad string.');
}
};
ASN1.alignBitstr = function(data) {
var padding = data[0];
var bits = (data.length - 1) * 8 - padding;
var buf = data.slice(1);
var shift = 8 - (bits % 8);
var i, out;
if (shift === 8 || buf.length === 0)
return buf;
out = new Buffer(buf.length);
out[0] = buf[0] >>> shift;
for (i = 1; i < buf.length; i++) {
out[i] = buf[i - 1] << (8 - shift);
out[i] |= buf[i] >>> shift;
}
return out;
};
ASN1.parsePubkey = function parsePubkey(data) {
var p = BufferReader(data);
p = BufferReader(ASN1.parseSeq(p));
return {
alg: ASN1.parseAlgIdent(p),
pubkey: ASN1.parseBitstr(p)
};
};
ASN1.parseName = function parseName(data) {
var p = BufferReader(data);
var values = [];
var tag;
p = BufferReader(ASN1.parseSeq(p));
while (p.left()) {
tag = ASN1.parseTag(p);
assert.equal(tag.tag, 0x11); // set
tag = ASN1.parseTag(p);
assert.equal(tag.tag, 0x10); // seq
values.push({
type: ASN1.parseOID(p),
value: ASN1.parseString(p)
});
}
return values;
};
ASN1.parseValidity = function parseValidity(data) {
var p = BufferReader(data);
p = BufferReader(ASN1.parseSeq(p));
return {
notBefore: ASN1.parseTime(p),
notAfter: ASN1.parseTime(p)
};
};
ASN1.parseTime = function parseTime(data) {
var p = BufferReader(data);
var tag = ASN1.parseTag(p);
var str = p.readString('ascii', tag.len);
var year, mon, day, hour, min, sec;
switch (tag.tag) {
case 0x17: // utctime
year = str.slice(0, 2) | 0;
mon = str.slice(2, 4) | 0;
day = str.slice(4, 6) | 0;
hour = str.slice(6, 8) | 0;
min = str.slice(8, 10) | 0;
sec = str.slice(10, 12) | 0;
if (year < 70)
year = 2000 + year;
else
year = 1900 + year;
break;
case 0x18: // gentime
year = str.slice(0, 4) | 0;
mon = str.slice(4, 6) | 0;
day = str.slice(6, 8) | 0;
hour = str.slice(8, 10) | 0;
min = str.slice(10, 12) | 0;
sec = str.slice(12, 14) | 0;
break;
default:
assert(false);
break;
}
return Date.UTC(year, mon - 1, day, hour, min, sec, 0) / 1000;
};
ASN1.parseOID = function parseOID(data) {
var p = BufferReader(data);
var tag = ASN1.parseTag(p);
var ids = [];
var ident = 0;
var subident = 0;
var objid, result, first, second;
assert.equal(tag.tag, 0x06); // objid
objid = p.readBytes(tag.len, true);
p = BufferReader(objid);
while (p.left()) {
subident = p.readU8();
ident <<= 7;
ident |= subident & 0x7f;
if ((subident & 0x80) === 0) {
ids.push(ident);
ident = 0;
}
}
if (subident & 0x80)
ids.push(ident);
first = (ids[0] / 40) | 0;
second = ids[0] % 40;
result = [first, second].concat(ids.slice(1));
return result.join('.');
};
ASN1.parseAlgIdent = function parseAlgIdent(data) {
var p = BufferReader(data);
var params = null;
var alg;
p = BufferReader(ASN1.parseSeq(p));
alg = ASN1.parseOID(p);
if (p.left() > 0) {
params = p.readBytes(ASN1.parseTag(p).len, true);
if (params.length === 0)
params = null;
}
return {
alg: alg,
params: params
};
};
ASN1.parseRSAPublic = function parseRSAPublic(data) {
var p = BufferReader(data);
p = BufferReader(ASN1.parseSeq(p));
return {
modulus: ASN1.parseInt(p),
publicExponent: ASN1.parseInt(p)
};
};
ASN1.parseRSAPrivate = function parseRSAPrivate(data) {
var p = BufferReader(data);
p = BufferReader(ASN1.parseSeq(p));
return {
version: ASN1.parseInt(p, true),
modulus: ASN1.parseInt(p),
publicExponent: ASN1.parseInt(p),
privateExponent: ASN1.parseInt(p),
prime1: ASN1.parseInt(p),
prime2: ASN1.parseInt(p),
exponent1: ASN1.parseInt(p),
exponent2: ASN1.parseInt(p),
coefficient: ASN1.parseInt(p)
};
};