UNPKG

@shockpkg/ria-packager

Version:

Package for creating Adobe AIR packages

257 lines (213 loc) 7.17 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports.SecurityTimestamper = void 0; var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty")); var _nodeFetch = _interopRequireDefault(require("node-fetch")); var _nodeForge = _interopRequireDefault(require("node-forge")); var _meta = require("../meta"); const { asn1, pki } = _nodeForge.default; const userAgent = `${_meta.NAME}/${_meta.VERSION}`; /** * SecurityTimestamper constructor. * * @param url The timestamp server URL. */ class SecurityTimestamper extends Object { /** * The timestamp server URL. */ constructor(url) { super(); (0, _defineProperty2.default)(this, "url", void 0); 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); } /** * Create a request object. * * @returns Request object. */ _createRequest() { return (options, cb) => { let response = { statusCode: 0 }; const { encoding } = options; (async () => { const res = await (0, _nodeFetch.default)(options.url, { method: options.method || 'GET', headers: { 'User-Agent': userAgent, ...(options.headers || {}) }, body: options.body || null }); response = { statusCode: res.status }; return res.buffer(); })().then(data => { cb(null, response, encoding === null ? data : data.toString(encoding)); }, err => { cb(err, response, null); }); }; } /** * Send message request and return response or error on failure. * * @param message Encoded message. * @returns Encoded response. */ async _sendRequest(message) { const { url } = this; const req = this._createRequest(); const [response, body] = await new Promise((resolve, reject) => { req({ method: 'POST', url, headers: { 'Content-Type': 'application/timestamp-query' }, body: message, encoding: null }, (error, response, body) => { if (error) { reject(error); return; } resolve([response, body]); }); }); const { statusCode } = response; if (statusCode !== 200) { throw new Error(`Unexpected status code: ${statusCode}: ${body}`); } return body; } /** * Encode request. * * @param digested Digested message. * @param digest Digest algorithm. * @returns Encoded request. */ _encodeRequest(digested, digest) { digest = digest.toLowerCase(); if (digest !== 'sha1') { throw new Error(`Unsupported digest algorithm: ${digest}`); } const hashedMessage = _nodeForge.default.util.decode64(digested.toString('base64')); const certReq = true; const hashAlgoDef = _nodeForge.default.asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false, asn1.oidToDer(pki.oids.sha1).getBytes()), asn1.create(asn1.Class.UNIVERSAL, asn1.Type.NULL, false, '')]); const messageImprintDef = asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [hashAlgoDef, asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OCTETSTRING, false, hashedMessage)]); // Could be set to some bytes. // ie: reqPolicy = new forge.util.DataBuffer(Buffer.from('test')); const reqPolicy = null; const asn1ReqPolicy = reqPolicy ? asn1.create(asn1.Class.UNIVERSAL, 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 = asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [asn1.create(asn1.Class.UNIVERSAL, asn1.Type.INTEGER, false, String.fromCharCode(1)), messageImprintDef, asn1ReqPolicy, nonceDER, asn1.create(asn1.Class.UNIVERSAL, asn1.Type.BOOLEAN, false, String.fromCharCode(certReq ? 0xFF : 0)), asn1Extn].filter(Boolean)); return Buffer.from(asn1.toDer(tsaReqDef).toHex(), 'hex'); } /** * Decode response. * * @param response Encoded response. * @returns Decoded response. */ _decodeResponse(response) { const object = asn1.fromDer(_nodeForge.default.util.decode64(response.toString('base64'))); const validator = { name: 'root', tagClass: asn1.Class.UNIVERSAL, type: asn1.Type.SEQUENCE, constructed: true, value: [{ name: 'root.statusInfo', tagClass: asn1.Class.UNIVERSAL, type: asn1.Type.SEQUENCE, constructed: true, value: [{ name: 'root.statusInfo.pkiStatus', tagClass: asn1.Class.UNIVERSAL, type: asn1.Type.INTEGER, constructed: false, captureAsn1: 'root.statusInfo.pkiStatus', optional: true }, { name: 'root.statusInfo.pkiFreeText', tagClass: asn1.Class.UNIVERSAL, type: asn1.Type.UTF8, constructed: false, captureAsn1: 'root.statusInfo.pkiFreeText', optional: true }, { name: 'root.statusInfo.pkiFailureInfo', tagClass: asn1.Class.UNIVERSAL, type: asn1.Type.BITSTRING, constructed: false, captureAsn1: 'root.statusInfo.pkiFailureInfo', optional: true }] }, { name: 'root.tst', tagClass: asn1.Class.UNIVERSAL, type: asn1.Type.SEQUENCE, constructed: true, captureAsn1: 'root.tst', optional: true }] }; const capture = {}; const errors = []; const success = 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}`); } 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 Buffer.from(asn1.toDer(tst).toHex(), 'hex'); } } exports.SecurityTimestamper = SecurityTimestamper; //# sourceMappingURL=timestamper.js.map