implement bip70.

This commit is contained in:
Christopher Jeffrey 2016-07-22 21:26:59 -07:00
parent 2d2f11b5dc
commit ac601e3675
No known key found for this signature in database
GPG Key ID: 8962AB9DE6666BBD
4 changed files with 1440 additions and 0 deletions

436
lib/bcoin/bip70/asn1.js Normal file
View File

@ -0,0 +1,436 @@
/*!
* 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 BufferWriter = require('../writer');
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, type, value;
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.parseExplicitInt(p, 0, 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)
};
};
asn1.parsePEM = function parsePEM(pem) {
var buf = '';
var chunks = [];
var s, tag, type;
while (pem.length) {
if (s = /^-----BEGIN ([^\-]+)-----/.exec(pem)) {
pem = pem.substring(s[0].length);
tag = s[1];
continue;
}
if (s = /^-----END ([^\-]+)-----/.exec(pem)) {
pem = pem.substring(s[0].length);
assert(tag === s[1], 'Tag mismatch.');
buf = new Buffer(buf, 'base64');
type = tag.split(' ')[0].toLowerCase();
chunks.push({ tag: tag, type: type, data: buf });
buf = '';
tag = null;
continue;
}
if (s = /^[a-zA-Z0-9\+=\/]+/.exec(pem)) {
pem = pem.substring(s[0].length);
buf += s[0];
continue;
}
if (s = /^\s+/.exec(pem)) {
pem = pem.substring(s[0].length);
continue;
}
throw new Error('PEM parse error.');
}
assert(chunks.length !== 0, 'PEM parse error.');
assert(!tag, 'Un-ended tag.');
assert(buf.length === 0, 'Trailing data.');
return chunks;
};
asn1.fromPEM = function fromPEM(pem) {
var chunks = asn1.parsePEM(pem);
var body = chunks[0];
var extra = chunks[1];
var params;
if (extra) {
if (extra.tag.indexOf('PARAMETERS') !== -1)
params = extra.data;
}
return {
type: body.type,
data: body.data,
params: params
};
};
asn1.toPEM = function toPEM(der, type) {
var pem = '';
var i;
type = type.toUpperCase();
der = der.toString('base64');
for (i = 0; i < der.length; i += 64)
pem += der.slice(i, i + 64) + '\r\n';
return ''
+ '-----BEGIN ' + type + '-----\r\n'
+ pem
+ '-----END ' + type + '-----\r\n';
};

534
lib/bcoin/bip70/index.js Normal file
View File

@ -0,0 +1,534 @@
/*!
* bip70.js - bip70 for bcoin
* Copyright (c) 2016, Christopher Jeffrey (MIT License).
* https://github.com/bcoin-org/bcoin
*/
'use strict';
var bcoin = require('../env');
var assert = require('assert');
var utils = bcoin.utils;
var crypto = require('crypto');
var x509 = require('./x509');
var asn1 = require('./asn1');
var protobuf = require('./protobuf');
var ProtoReader = protobuf.ProtoReader;
var ProtoWriter = protobuf.ProtoWriter;
function PaymentRequest(options) {
if (!(this instanceof PaymentRequest))
return new PaymentRequest(options);
this.version = -1;
this.pkiType = null;
this.pkiData = null;
this.paymentDetails = new PaymentDetails();
this.signature = null;
if (options)
this.fromOptions(options);
}
PaymentRequest.prototype.fromOptions = function fromOptions(options) {
if (options.version != null) {
assert(utils.isNumber(options.version));
this.version = options.version;
}
if (options.pkiType != null) {
assert(typeof options.pkiType === 'string');
this.pkiType = options.pkiType;
}
if (options.pkiData) {
assert(Buffer.isBuffer(options.pkiData));
this.pkiData = options.pkiData;
}
if (options.paymentDetails)
this.paymentDetails.fromOptions(options.paymentDetails);
if (options.signature) {
assert(Buffer.isBuffer(options.signature));
this.signature = options.signature;
}
if (options.chain)
this.setChain(this.pkiType, options.chain);
return this;
};
PaymentRequest.fromOptions = function fromOptions(options) {
return new PaymentRequest().fromOptions(options);
};
PaymentRequest.prototype.fromRaw = function fromRaw(data) {
var p = new ProtoReader(data);
this.version = p.readFieldU32(1, true);
this.pkiType = p.readFieldString(2, true);
this.pkiData = p.readFieldBytes(3, true);
this.paymentDetails.fromRaw(p.readFieldBytes(4));
this.signature = p.readFieldBytes(5, true);
return this;
};
PaymentRequest.fromRaw = function fromRaw(data, enc) {
if (typeof data === 'string')
data = new Buffer(data, enc);
return new PaymentRequest().fromRaw(data);
};
PaymentRequest.prototype.toRaw = function toRaw(writer) {
var p = new ProtoWriter(writer);
if (this.version !== -1)
p.writeFieldU32(1, this.version);
if (this.pkiType != null)
p.writeFieldString(2, this.pkiType);
if (this.pkiData)
p.writeFieldBytes(3, this.pkiData);
p.writeFieldBytes(4, this.paymentDetails.toRaw());
if (this.signature)
p.writeFieldBytes(5, this.signature);
if (!writer)
p = p.render();
return p;
};
PaymentRequest.prototype.getAlgorithm = function getAlgorithm() {
var parts;
if (!this.pkiType)
return;
parts = this.pkiType.split('+');
if (parts.length !== 2)
return;
if (parts[0] !== 'x509')
return;
if (parts[1] !== 'sha1' && parts[1] !== 'sha256')
return;
return { key: 'rsa', hash: parts[1] };
};
PaymentRequest.prototype.signatureData = function signatureData() {
var signature = this.signature;
var data;
this.signature = new Buffer(0);
data = this.toRaw();
this.signature = signature;
return data;
};
PaymentRequest.prototype.signatureHash = function signatureHash() {
var alg = this.getAlgorithm();
assert(alg, 'No hash algorithm available.');
return utils.hash(alg.hash, this.signatureData());
};
PaymentRequest.prototype.setChain = function setChain(pkiType, chain) {
var p = new ProtoWriter();
var i, cert;
assert(pkiType === 'x509+sha1' || pkiType === 'x509+sha256');
assert(Array.isArray(chain));
this.pkiType = pkiType;
for (i = 0; i < chain.length; i++) {
cert = chain[i];
if (typeof cert === 'string')
cert = asn1.fromPEM(cert).data;
assert(Buffer.isBuffer(cert), 'Bad cert format.');
p.writeFieldBytes(1, cert);
}
this.pkiData = p.render();
};
PaymentRequest.prototype.getChain = function getChain() {
var chain = [];
var p;
if (!this.pkiData)
return chain;
p = new ProtoReader(this.pkiData);
while (p.nextTag() === 1)
chain.push(p.readFieldBytes(1));
return chain;
};
PaymentRequest.prototype.sign = function sign(key) {
var alg, msg;
if (!this.pkiType || this.pkiType === 'none') {
this.signature = null;
return;
}
alg = this.getAlgorithm();
assert(alg, 'No hash algorithm available.');
msg = this.signatureData();
this.signature = x509.sign(alg.hash, msg, key);
};
PaymentRequest.prototype.verify = function verify() {
var alg, msg, ver, der, pem;
if (!this.pkiType || this.pkiType === 'none')
return true;
if (!this.signature)
return false;
alg = this.getAlgorithm();
if (!alg)
return false;
msg = this.signatureData();
return x509.verifySubject(alg.hash, msg, this.signature, this.getChain());
};
PaymentRequest.prototype.verifyChain = function verifyChain(ignoreTime) {
if (!this.pkiType || this.pkiType === 'none')
return true;
return x509.verifyChain(this.getChain(), ignoreTime);
};
PaymentRequest.prototype.getCA = function getCA() {
var chain;
if (!this.pkiType || this.pkiType === 'none')
return;
chain = this.getChain();
if (chain.length === 0)
return;
return x509.getTrusted(chain[chain.length - 1]);
};
function PaymentDetails(options) {
if (!(this instanceof PaymentDetails))
return new PaymentDetails(options);
this.network = null;
this.outputs = [];
this.time = utils.now();
this.expires = -1;
this.memo = null;
this.paymentUrl = null;
this.merchantData = null;
if (options)
this.fromOptions(options);
}
PaymentDetails.prototype.fromOptions = function fromOptions(options) {
var i, output;
if (options.network != null) {
assert(typeof options.network === 'string');
this.network = options.network;
}
if (options.outputs) {
assert(Array.isArray(options.outputs));
for (i = 0; i < options.outputs.length; i++) {
output = new bcoin.output(options.outputs[i]);
this.outputs.push(output);
}
}
if (options.time != null) {
assert(utils.isNumber(options.time));
this.time = options.time;
}
if (options.expires != null) {
assert(utils.isNumber(options.expires));
this.expires = options.expires;
}
if (options.memo != null) {
assert(typeof options.memo === 'string');
this.memo = options.memo;
}
if (options.paymentUrl != null) {
assert(typeof options.paymentUrl === 'string');
this.paymentUrl = options.paymentUrl;
}
if (options.merchantData) {
assert(Buffer.isBuffer(options.merchantData));
this.merchantData = options.merchantData;
}
return this;
};
PaymentDetails.fromOptions = function fromOptions(options) {
return new PaymentDetails().fromOptions(options);
};
PaymentDetails.prototype.fromRaw = function fromRaw(data) {
var p = new ProtoReader(data);
var network, op, output;
this.network = p.readFieldString(1, true);
while (p.nextTag() === 2) {
op = new ProtoReader(p.readFieldBytes(2));
output = new bcoin.output();
output.value = op.readFieldU64(1, true);
output.script.fromRaw(op.readFieldBytes(2, true));
this.outputs.push(output);
}
this.time = p.readFieldU64(3);
this.expires = p.readFieldU64(4, true);
this.memo = p.readFieldString(5, true);
this.paymentUrl = p.readFieldString(6, true);
this.merchantData = p.readFieldBytes(7, true);
return this;
};
PaymentDetails.fromRaw = function fromRaw(data, enc) {
if (typeof data === 'string')
data = new Buffer(data, enc);
return new PaymentDetails().fromRaw(data);
};
PaymentDetails.prototype.toRaw = function toRaw(writer) {
var p = new ProtoWriter(writer);
var i, op, output;
if (this.network != null)
p.writeFieldString(1, this.network);
for (i = 0; i < this.outputs.length; i++) {
output = this.outputs[i];
op = new ProtoWriter();
op.writeFieldU64(1, output.value);
op.writeFieldBytes(2, output.script.toRaw());
p.writeFieldBytes(2, op.render());
}
p.writeFieldU64(3, this.time);
if (this.expires !== -1)
p.writeFieldU64(4, this.expires);
if (this.memo != null)
p.writeFieldString(5, this.memo);
if (this.paymentUrl != null)
p.writeFieldString(6, this.paymentUrl);
if (this.merchantData)
p.writeFieldString(7, this.merchantData);
if (!writer)
p = p.render();
return p;
};
function Payment(options) {
if (!(this instanceof Payment))
return new Payment(options);
this.merchantData = null;
this.transactions = [];
this.refundTo = [];
this.memo = null;
if (options)
this.fromOptions(options);
}
Payment.prototype.fromOptions = function fromOptions(options) {
var i, tx, output;
if (options.merchantData) {
assert(Buffer.isBuffer(options.merchantData));
this.merchantData = options.merchantData;
}
if (options.transactions) {
assert(Array.isArray(options.transactions));
for (i = 0; i < options.transactions.length; i++) {
tx = new bcoin.tx(options.transactions[i]);
this.transactions.push(tx);
}
}
if (options.refundTo) {
assert(Array.isArray(options.refundTo));
for (i = 0; i < options.refundTo.length; i++) {
output = new bcoin.output(options.refundTo[i]);
this.refundTo.push(output);
}
}
if (options.memo != null) {
assert(typeof options.memo === 'string');
this.memo = options.memo;
}
return this;
};
Payment.fromOptions = function fromOptions(options) {
return new Payment().fromOptions(options);
};
Payment.prototype.fromRaw = function fromRaw(data) {
var p = new ProtoReader(data);
var tx, op, output;
this.merchantData = p.readFieldBytes(1, true);
while (p.nextTag() === 2) {
tx = bcoin.tx.fromRaw(p.readFieldBytes(2));
this.transactions.push(tx);
}
while (p.nextTag() === 3) {
op = new ProtoReader(p.readFieldBytes(3));
output = new bcoin.output();
output.value = op.readFieldU64(1, true);
output.script = bcoin.script.fromRaw(op.readFieldBytes(2, true));
this.refundTo.push(output);
}
this.memo = p.readFieldString(4, true);
return this;
};
Payment.fromRaw = function fromRaw(data, enc) {
if (typeof data === 'string')
data = new Buffer(data, enc);
return new Payment().fromRaw(data);
};
Payment.prototype.toRaw = function toRaw(writer) {
var p = new ProtoWriter(writer);
var i, tx, op, output;
if (this.merchantData)
p.writeFieldBytes(1, this.merchantData);
for (i = 0; i < this.transactions.length; i++) {
tx = this.transactions[i];
this.writeFieldBytes(2, tx.toRaw());
}
for (i = 0; i < this.refundTo.length; i++) {
op = new ProtoWriter();
output = this.refundTo[i];
op.writeFieldU64(1, output.value);
op.writeFieldBytes(2, output.script.toRaw());
p.writeFieldBytes(3, op.render());
}
if (this.memo != null)
p.writeFieldString(4, this.memo);
if (!writer)
p = p.render();
return p;
};
function PaymentACK(options) {
if (!(this instanceof PaymentACK))
return new PaymentACK(options);
this.payment = new Payment();
this.memo = null;
if (options)
this.fromOptions(options);
}
PaymentACK.prototype.fromOptions = function fromOptions(options) {
if (options.payment)
this.payment.fromOptions(options.payment);
if (options.memo != null) {
assert(typeof options.memo === 'string');
this.memo = options.memo;
}
return this;
};
PaymentACK.fromOptions = function fromOptions(options) {
return new PaymentACK().fromOptions(options);
};
PaymentACK.prototype.fromRaw = function fromRaw(data) {
var p = new ProtoReader(data);
this.payment.fromRaw(p.readFieldBytes(1));
this.memo = p.readFieldString(2, true);
return this;
};
PaymentACK.fromRaw = function fromRaw(data, enc) {
if (typeof data === 'string')
data = new Buffer(data, enc);
return new PaymentACK().fromRaw(data);
};
PaymentACK.prototype.toRaw = function toRaw(writer) {
var p = new ProtoWriter(writer);
var i, tx, op, output;
p.writeFieldBytes(1, this.payment.toRaw());
if (this.memo != null)
p.writeFieldString(2, this.memo);
if (!writer)
p = p.render();
return p;
};
exports.PaymentRequest = PaymentRequest;
exports.PaymentDetails = PaymentDetails;
exports.Payment = Payment;
exports.PaymentACK = PaymentACK;

243
lib/bcoin/bip70/protobuf.js Normal file
View File

@ -0,0 +1,243 @@
/*!
* protobuf.js - protobufs for bcoin
* Copyright (c) 2016, Christopher Jeffrey (MIT License).
* https://github.com/bcoin-org/bcoin
*/
'use strict';
var utils = require('../utils');
var assert = utils.assert;
var BufferReader = require('../reader');
var BufferWriter = require('../writer');
var wireType = {
VARINT: 0,
FIXED64: 1,
DELIMITED: 2,
START_GROUP: 3,
END_GROUP: 4,
FIXED32: 5
};
function ProtoReader(data, zeroCopy) {
if (data instanceof ProtoReader)
return data;
if (!(this instanceof ProtoReader))
return new ProtoReader(data, zeroCopy);
BufferReader.call(this, data, zeroCopy);
}
utils.inherits(ProtoReader, BufferReader);
ProtoReader.prototype.readVarint = function readVarint() {
var result = exports.readVarint(this.data, this.offset);
this.offset += result.size;
return result.value;
};
ProtoReader.prototype.readFieldValue = function readFieldValue(tag, opt) {
var field = this.readField(tag, opt);
if (!field)
return -1;
assert(field.value != null);
return field.value;
};
ProtoReader.prototype.readFieldU64 = function readFieldU64(tag, opt) {
var field = this.readField(tag, opt);
if (!field)
return -1;
assert(field.type === wireType.VARINT || field.type === wireType.FIXED64);
return field.value;
};
ProtoReader.prototype.readFieldU32 = function readFieldU32(tag, opt) {
var field = this.readField(tag, opt);
if (!field)
return -1;
assert(field.type === wireType.VARINT || field.type === wireType.FIXED32);
return field.value;
};
ProtoReader.prototype.readFieldBytes = function readFieldBytes(tag, opt) {
var field = this.readField(tag, opt);
if (!field)
return null;
assert(field.data);
return field.data;
};
ProtoReader.prototype.readFieldString = function readFieldString(tag, opt, enc) {
var field = this.readField(tag, opt);
if (!field)
return null;
assert(field.data);
return field.data.toString(enc || 'utf8');
};
ProtoReader.prototype.nextTag = function nextTag() {
var field;
if (this.left() === 0)
return -1;
field = this.readField();
this.seek(-field.size);
return field.tag;
};
ProtoReader.prototype.readField = function readField(tag, opt) {
var offset = this.offset;
var header = this.readVarint();
var value, data, group, field;
if (tag != null && (header >>> 3) !== tag) {
assert(opt, 'Non-optional field not present.');
this.offset = offset;
return;
}
switch (header & 7) {
case wireType.VARINT:
value = this.readVarint();
break;
case wireType.FIXED64:
value = this.readU64N();
break;
case wireType.DELIMITED:
data = this.readVarBytes();
break;
case wireType.START_GROUP:
group = [];
for (;;) {
field = this.readField();
if (field.type === wireType.END_GROUP)
break;
group.push(field);
}
break;
case wireType.END_GROUP:
assert(false, 'Unexpected end group.');
break;
case wireType.FIXED32:
value = this.readU32();
break;
default:
assert(false, 'Bad wire type.');
break;
}
return {
size: this.offset - offset,
header: header,
tag: header >>> 3,
type: header & 7,
value: value,
data: data,
group: group
};
};
function ProtoWriter(options) {
if (options instanceof ProtoWriter)
return options;
if (!(this instanceof ProtoWriter))
return new ProtoWriter(options);
BufferWriter.call(this, options);
}
utils.inherits(ProtoWriter, BufferWriter);
ProtoWriter.prototype.writeVarint = function writeVarint(num) {
var size = exports.sizeVarint(num);
var buf = new Buffer(size);
exports.writeVarint(buf, num, 0);
this.writeBytes(buf);
};
ProtoWriter.prototype.writeFieldValue = function writeFieldValue(tag, value) {
var header = (tag << 3) | wireType.VARINT;
this.writeVarint(header);
this.writeVarint(value);
};
ProtoWriter.prototype.writeFieldU64 = function writeFieldU64(tag, value) {
this.writeFieldValue(tag, value);
};
ProtoWriter.prototype.writeFieldU32 = function writeFieldU32(tag, value) {
this.writeFieldValue(tag, value);
};
ProtoWriter.prototype.writeFieldBytes = function writeFieldBytes(tag, data) {
var header = (tag << 3) | wireType.DELIMITED;
this.writeVarint(header);
this.writeVarint(data.length);
this.writeBytes(data);
};
ProtoWriter.prototype.writeFieldString = function writeFieldString(tag, data, enc) {
if (typeof data === 'string')
data = new Buffer(data, enc || 'utf8');
this.writeFieldBytes(tag, data);
};
exports.readVarint = function readVarint(data, off) {
var num = 0;
var ch = 0x80;
var size = 0;
while (ch & 0x80) {
if (off >= data.length) {
num = 0;
break;
}
ch = data[off++];
assert(size + 1 < 6, 'Number exceeds 2^53-1.');
num += (ch & 0x7f) * Math.pow(2, 7 * size);
size++;
}
return { size: size, value: num };
};
exports.writeVarint = function writeVarint(data, num, off) {
var ch;
assert(utils.isSafeInteger(num), 'Number exceeds 2^53-1.');
do {
assert(off < data.length);
ch = num & 0x7f;
num -= num % 0x80;
num /= 0x80;
if (num !== 0)
ch |= 0x80;
data[off] = ch;
off++;
} while (num > 0);
return off;
};
exports.sizeVarint = function sizeVarint(num) {
var size = 0;
assert(utils.isSafeInteger(num), 'Number exceeds 2^53-1.');
do {
num -= num % 0x80;
num /= 0x80;
size++;
} while (num > 0);
return size;
};
exports.ProtoReader = ProtoReader;
exports.ProtoWriter = ProtoWriter;

227
lib/bcoin/bip70/x509.js Normal file
View File

@ -0,0 +1,227 @@
/*!
* x509.js - x509 handling for bcoin
* Copyright (c) 2016, Christopher Jeffrey (MIT License).
* https://github.com/bcoin-org/bcoin
*/
'use strict';
var assert = require('assert');
var crypto = require('crypto');
var asn1 = require('./asn1');
var utils = require('../utils');
var x509 = exports;
x509.certs = [];
x509.trusted = {};
x509.getTrusted = function getTrusted(cert) {
var hash;
if (!Buffer.isBuffer(cert))
cert = cert.raw;
hash = utils.hash256(cert).toString('hex');
return x509.trusted[hash];
};
x509.setTrust = function setTrust(certs) {
var keys = Object.keys(certs);
var i, key, cert, hash, pem;
for (i = 0; i < keys.length; i++) {
key = keys[i];
cert = certs[key];
if (typeof cert === 'string') {
pem = asn1.fromPEM(cert);
assert(pem.type === 'certificate', 'Must add certificates to trust.');
cert = pem.data;
}
assert(Buffer.isBuffer(cert), 'Certificates must be PEM or DER.');
hash = utils.hash256(cert).toString('hex');
cert = {
name: key,
fingerprint: hash,
cert: asn1.parseCert(cert)
};
x509.certs.push(cert);
x509.trusted[hash] = cert;
}
};
/*
* https://www.ietf.org/rfc/rfc2459.txt
* https://tools.ietf.org/html/rfc3279
* http://oid-info.com/get/1.2.840.10040.4
* http://oid-info.com/get/1.2.840.113549.1.1
* http://oid-info.com/get/1.2.840.10045.4.3
*/
x509.oid = {
'1.2.840.10040.4.1' : { key: 'dsa', hash: null },
'1.2.840.10040.4.2' : { key: 'dsa', hash: null },
'1.2.840.10040.4.3' : { key: 'dsa', hash: 'sha1' },
'1.2.840.113549.1.1.1' : { key: 'rsa', hash: null },
'1.2.840.113549.1.1.2' : { key: 'rsa', hash: 'md2' },
'1.2.840.113549.1.1.3' : { key: 'rsa', hash: 'md4' },
'1.2.840.113549.1.1.4' : { key: 'rsa', hash: 'md5' },
'1.2.840.113549.1.1.5' : { key: 'rsa', hash: 'sha1' },
'1.2.840.113549.1.1.11': { key: 'rsa', hash: 'sha256' },
'1.2.840.113549.1.1.12': { key: 'rsa', hash: 'sha384' },
'1.2.840.113549.1.1.13': { key: 'rsa', hash: 'sha512' },
'1.2.840.113549.1.1.14': { key: 'rsa', hash: 'sha224' },
'1.2.840.10045.2.1' : { key: 'ecdsa', hash: null },
'1.2.840.10045.4.1' : { key: 'ecdsa', hash: 'sha1' },
'1.2.840.10045.4.3.1' : { key: 'ecdsa', hash: 'sha224' },
'1.2.840.10045.4.3.2' : { key: 'ecdsa', hash: 'sha256' },
'1.2.840.10045.4.3.3' : { key: 'ecdsa', hash: 'sha384' },
'1.2.840.10045.4.3.4' : { key: 'ecdsa', hash: 'sha512' }
};
x509.getKeyAlgorithm = function getKeyAlgorithm(cert) {
var alg = cert.tbs.pubkey.alg.alg;
return x509.oid[alg];
};
x509.getSigAlgorithm = function getSigAlgorithm(cert) {
var alg = cert.sigAlg.alg;
return x509.oid[alg];
};
x509.parse = function parse(der) {
try {
return asn1.parseCert(der);
} catch (e) {
;
}
};
x509.getPublicKey = function getPublicKey(cert) {
var alg = x509.getKeyAlgorithm(cert);
var key, params, pem;
if (!alg)
return;
key = cert.tbs.pubkey.pubkey;
params = cert.tbs.pubkey.alg.params;
pem = asn1.toPEM(key, alg.key + ' PUBLIC KEY');
if (params)
pem += asn1.toPEM(params, alg.key + ' PARAMETERS');
return pem;
};
x509.verifyTime = function verifyTime(cert) {
var time = child.tbs.validity;
var now = Math.floor(Date.now() / 1000);
return now > time.notBefore && now < time.notAfter;
};
x509.verifySubject = function verifySubject(hash, msg, sig, chain) {
var cert, key, alg;
if (chain.length === 0)
return false;
cert = x509.parse(chain[0]);
if (!cert)
return false;
key = x509.getPublicKey(cert);
if (!key)
return false;
alg = x509.getKeyAlgorithm(cert);
if (!alg)
return false;
return x509.verify(alg.key, hash, msg, sig, key);
};
x509.verifyChain = function verifyChain(chain, ignoreTime) {
var i, child, parent, alg, key, sig, msg;
if (chain.length < 2)
return false;
for (i = 1; i < chain.length; i++) {
child = chain[i - 1];
parent = chain[i];
child = x509.parse(child);
if (!child)
return false;
parent = x509.parse(parent);
if (!parent)
return false;
if (!ignoreTime) {
if (!x509.verifyTime(child))
return false;
if (!x509.verifyTime(parent))
return false;
}
alg = x509.getSigAlgorithm(child);
if (!alg || !alg.hash)
return false;
key = x509.getPublicKey(parent);
if (!key)
return false;
sig = child.sig;
msg = child.tbs.raw;
if (!x509.verify(alg.key, alg.hash, msg, sig, key))
return false;
if (x509.getTrusted(parent))
return true;
}
if (exports.certs.length === 0)
return true;
return false;
};
x509.verify = function verify(alg, hash, msg, sig, key) {
var algo = alg.toUpperCase() + '-' + hash.toUpperCase();
var verify;
try {
verify = crypto.createVerify(algo);
verify.update(msg);
return verify.verify(key, sig);
} catch (e) {
return false;
}
};
x509.sign = function sign(alg, hash, msg, key) {
var algo = alg.toUpperCase() + '-' + hash.toUpperCase();
var sig = crypto.createSign(algo);
sig.update(msg);
return sig.sign(key);
};
x509.asn1 = asn1;