implement bip70.
This commit is contained in:
parent
2d2f11b5dc
commit
ac601e3675
436
lib/bcoin/bip70/asn1.js
Normal file
436
lib/bcoin/bip70/asn1.js
Normal 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
534
lib/bcoin/bip70/index.js
Normal 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
243
lib/bcoin/bip70/protobuf.js
Normal 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
227
lib/bcoin/bip70/x509.js
Normal 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;
|
||||
Loading…
Reference in New Issue
Block a user