@shockpkg/ria-packager
Version:
Package for creating Adobe AIR packages
220 lines (207 loc) • 7.09 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.SecurityTimestamper = void 0;
var _nodeForge = require("node-forge");
var _meta = require("../meta.js");
/**
* SecurityTimestamper object.
*/
class SecurityTimestamper {
/**
* The timestamp server URL.
*/
/**
* The default headers for HTTP requests.
*/
headers = {
// eslint-disable-next-line @typescript-eslint/naming-convention
'User-Agent': `${_meta.NAME}/${_meta.VERSION}`
};
/**
* A fetch-like interface requiring only a sebset of features.
*/
fetch = typeof fetch === 'undefined' ? null : fetch;
/**
* SecurityTimestamper constructor.
*
* @param url The timestamp server URL.
*/
constructor(url) {
this.url = url;
}
/**
* Timestamp data digested with specified algorithm.
*
* @param digested The data to timestamp.
* @param digest Digest algorithm.
* @returns Timestamp data.
*/
async timestamp(digested, digest) {
const encodedRequest = this._encodeRequest(digested, digest);
const response = await this._sendRequest(encodedRequest);
return this._decodeResponse(response);
}
/**
* Send message request and return response or error on failure.
*
* @param message Encoded message.
* @returns Encoded response.
*/
async _sendRequest(message) {
const {
url,
headers
} = this;
const fetch = this._ensureFetch();
const response = await fetch(url, {
method: 'POST',
headers,
body: message
});
if (response.status !== 200) {
throw new Error(`Status code: ${response.status}: ${url}`);
}
return new Uint8Array(await response.arrayBuffer());
}
/**
* Encode request.
*
* @param digested Digested message.
* @param digest Digest algorithm.
* @returns Encoded request.
*/
_encodeRequest(digested, digest) {
digest = digest.toLowerCase();
let iod = '';
switch (digest) {
case 'sha1':
{
iod = _nodeForge.pki.oids.sha1;
break;
}
case 'sha256':
{
iod = _nodeForge.pki.oids.sha256;
break;
}
default:
{
throw new Error(`Unsupported digest algorithm: ${digest}`);
}
}
const certReq = true;
const hashAlgoDef = _nodeForge.asn1.create(_nodeForge.asn1.Class.UNIVERSAL, _nodeForge.asn1.Type.SEQUENCE, true, [_nodeForge.asn1.create(_nodeForge.asn1.Class.UNIVERSAL, _nodeForge.asn1.Type.OID, false, _nodeForge.asn1.oidToDer(iod).getBytes()), _nodeForge.asn1.create(_nodeForge.asn1.Class.UNIVERSAL, _nodeForge.asn1.Type.NULL, false, '')]);
const messageImprintDef = _nodeForge.asn1.create(_nodeForge.asn1.Class.UNIVERSAL, _nodeForge.asn1.Type.SEQUENCE, true, [hashAlgoDef, _nodeForge.asn1.create(_nodeForge.asn1.Class.UNIVERSAL, _nodeForge.asn1.Type.OCTETSTRING, false,
// eslint-disable-next-line unicorn/prefer-code-point
String.fromCharCode(...digested))]);
// Could be set to some bytes.
// ie: reqPolicy = 'some bytes';
const reqPolicy = null;
const asn1ReqPolicy = reqPolicy ? _nodeForge.asn1.create(_nodeForge.asn1.Class.UNIVERSAL, _nodeForge.asn1.Type.OID, false, reqPolicy) : null;
// Always null.
const nonceDER = null;
// This could be a DER encodable, if extensions is set to be?
// Just null for now.
// const extensions = null;
// const asn1Extn = extensions ? extensions : null;
const asn1Extn = null;
const tsaReqDef = _nodeForge.asn1.create(_nodeForge.asn1.Class.UNIVERSAL, _nodeForge.asn1.Type.SEQUENCE, true, [_nodeForge.asn1.create(_nodeForge.asn1.Class.UNIVERSAL, _nodeForge.asn1.Type.INTEGER, false,
// eslint-disable-next-line unicorn/prefer-code-point
String.fromCharCode(1)), messageImprintDef, asn1ReqPolicy, nonceDER, _nodeForge.asn1.create(_nodeForge.asn1.Class.UNIVERSAL, _nodeForge.asn1.Type.BOOLEAN, false,
// eslint-disable-next-line unicorn/prefer-code-point
String.fromCharCode(certReq ? 0xff : 0)), asn1Extn].filter(Boolean));
return _nodeForge.util.binary.raw.decode(_nodeForge.asn1.toDer(tsaReqDef).bytes());
}
/**
* Decode response.
*
* @param response Encoded response.
* @returns Decoded response.
*/
_decodeResponse(response) {
const object = _nodeForge.asn1.fromDer(new _nodeForge.util.ByteStringBuffer(response));
const validator = {
name: 'root',
tagClass: _nodeForge.asn1.Class.UNIVERSAL,
type: _nodeForge.asn1.Type.SEQUENCE,
constructed: true,
value: [{
name: 'root.statusInfo',
tagClass: _nodeForge.asn1.Class.UNIVERSAL,
type: _nodeForge.asn1.Type.SEQUENCE,
constructed: true,
value: [{
name: 'root.statusInfo.pkiStatus',
tagClass: _nodeForge.asn1.Class.UNIVERSAL,
type: _nodeForge.asn1.Type.INTEGER,
constructed: false,
captureAsn1: 'root.statusInfo.pkiStatus',
optional: true
}, {
name: 'root.statusInfo.pkiFreeText',
tagClass: _nodeForge.asn1.Class.UNIVERSAL,
type: _nodeForge.asn1.Type.UTF8,
constructed: false,
captureAsn1: 'root.statusInfo.pkiFreeText',
optional: true
}, {
name: 'root.statusInfo.pkiFailureInfo',
tagClass: _nodeForge.asn1.Class.UNIVERSAL,
type: _nodeForge.asn1.Type.BITSTRING,
constructed: false,
captureAsn1: 'root.statusInfo.pkiFailureInfo',
optional: true
}]
}, {
name: 'root.tst',
tagClass: _nodeForge.asn1.Class.UNIVERSAL,
type: _nodeForge.asn1.Type.SEQUENCE,
constructed: true,
captureAsn1: 'root.tst',
optional: true
}]
};
const capture = {};
const errors = [];
const success = _nodeForge.asn1.validate(object, validator, capture, errors);
if (!success || errors.length) {
const error = errors[0] || 'Unknown error';
throw new Error(`Decode error: ${error}`);
}
const pkiStatus = capture['root.statusInfo.pkiStatus'];
if (!pkiStatus) {
throw new Error('Missing PKI status');
}
if (pkiStatus.value.length !== 1) {
throw new Error(`Unexpected PKI status length: ${pkiStatus.value.length}`);
}
// eslint-disable-next-line unicorn/prefer-code-point
const pkiStatusCode = pkiStatus.value.charCodeAt(0);
if (pkiStatusCode !== 0 && pkiStatusCode !== 1) {
throw new Error(`Unexpected PKI status code: ${pkiStatusCode}`);
}
const tst = capture['root.tst'];
if (!tst) {
throw new Error('Missing PKI TSTInfo');
}
return _nodeForge.util.binary.raw.decode(_nodeForge.asn1.toDer(tst).bytes());
}
/**
* Ensure fetch-like function is set.
*
* @returns The fetch-like function.
*/
_ensureFetch() {
const {
fetch
} = this;
if (!fetch) {
throw new Error('Default fetch not available');
}
return fetch;
}
}
exports.SecurityTimestamper = SecurityTimestamper;
//# sourceMappingURL=timestamper.js.map