@shockpkg/ria-packager
Version:
Package for creating Adobe AIR packages
454 lines (345 loc) • 12.3 kB
JavaScript
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.Signature = void 0;
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
var _timestamper = require("./security/timestamper");
var _sha = require("./hasher/sha1");
var _sha2 = require("./hasher/sha256");
const templates = [['certificate', '' + '<X509Certificate>{0}</X509Certificate>'], ['crl', '' + '<X509CRL>{0}</X509CRL>'], ['fileReference', '' + '<Reference URI="{0}">' + '<DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"></DigestMethod>' + '<DigestValue>{1}</DigestValue>' + '</Reference>'], ['packageManifest', '' + '<Manifest xmlns="http://www.w3.org/2000/09/xmldsig#" Id="PackageContents">{0}</Manifest>'], ['PackageSignature', '' + '<signatures>\n' + ' <Signature xmlns="http://www.w3.org/2000/09/xmldsig#" Id="PackageSignature">\n' + ' <SignedInfo>\n' + ' <CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>\n' + ' <SignatureMethod Algorithm="http://www.w3.org/TR/xmldsig-core#rsa-sha1"/>\n' + ' <Reference URI="#PackageContents">\n' + ' <Transforms>\n' + ' <Transform Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>\n' + ' </Transforms>\n' + ' <DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>\n' + ' <DigestValue>{0}</DigestValue>\n' + ' </Reference>\n' + ' </SignedInfo>\n' + ' <SignatureValue Id="PackageSignatureValue">{1}</SignatureValue>\n' + ' <KeyInfo>\n' + ' <X509Data>\n' + ' {2}\n' + ' </X509Data>\n' + ' </KeyInfo>\n' + ' <Object>\n' + ' <Manifest Id="PackageContents">\n' + ' {3}\n' + ' </Manifest>\n' + ' </Object>\n' + ' {4}\n' + ' </Signature>\n' + '</signatures>\n'], ['SignedInfo', '' + '<SignedInfo xmlns="http://www.w3.org/2000/09/xmldsig#">' + '<CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"></CanonicalizationMethod>' + '<SignatureMethod Algorithm="http://www.w3.org/TR/xmldsig-core#rsa-sha1"></SignatureMethod>' + '<Reference URI="#PackageContents">' + '<Transforms>' + '<Transform Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"></Transform>' + '</Transforms>' + '<DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"></DigestMethod>' + '<DigestValue>{0}</DigestValue>' + '</Reference>' + '</SignedInfo>'], ['timestamp', '' + '<Object xmlns:xades="http://uri.etsi.org/01903/v1.1.1#" > \n' + ' <xades:QualifyingProperties>\n' + ' <xades:UnsignedProperties > \n' + ' <xades:UnsignedSignatureProperties>\n' + ' <xades:SignatureTimeStamp>\n' + ' \t <xades:HashDataInfo uri="{0}">\n' + ' \t <Transforms>\n' + ' \t <Transform Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>\n' + ' </Transforms>\n' + ' <xades:EncapsulatedTimeStamp>\n' + ' {1}\n' + ' </xades:EncapsulatedTimeStamp> \t\n' + ' \t </xades:HashDataInfo> \t\n' + ' </xades:SignatureTimeStamp>\n' + ' </xades:UnsignedSignatureProperties> \n' + ' </xades:UnsignedProperties>\n' + ' </xades:QualifyingProperties>\n' + '</Object>'], ['SignatureValue', '' + '<SignatureValue xmlns="http://www.w3.org/2000/09/xmldsig#" Id="PackageSignatureValue">{0}</SignatureValue>']];
/**
* Signature constructor.
*/
class Signature extends Object {
/**
* Certificate.
*/
/**
* Private key.
*/
/**
* Timestamp URL.
*/
/**
* Timestamp URI for SignatureValue.
*/
/**
* Timestamp URI for #PackageSignatureValue.
*/
/**
* Template strings for signatures.
*/
/**
* File references.
*/
/**
* Manifest digest.
*/
/**
* Signed data.
*/
/**
* Signature digest.
*/
/**
* Key info.
*/
/**
* Timestamp info.
*/
constructor() {
super();
(0, _defineProperty2.default)(this, "certificate", null);
(0, _defineProperty2.default)(this, "keyPrivate", null);
(0, _defineProperty2.default)(this, "timestampUrl", null);
(0, _defineProperty2.default)(this, "timestampUriSignature", false);
(0, _defineProperty2.default)(this, "timestampUriPackage", true);
(0, _defineProperty2.default)(this, "_templates", new Map(templates));
(0, _defineProperty2.default)(this, "_packageManifest", []);
(0, _defineProperty2.default)(this, "_manifestDiest", null);
(0, _defineProperty2.default)(this, "_signedInfo", null);
(0, _defineProperty2.default)(this, "_signature", null);
(0, _defineProperty2.default)(this, "_keyInfo", null);
(0, _defineProperty2.default)(this, "_timestamp", null);
}
/**
* Reset options to defaults.
*/
defaults() {
this.certificate = null;
this.keyPrivate = null;
this.timestampUrl = null;
this.timestampUriSignature = false;
this.timestampUriPackage = true;
}
/**
* Reset the internal state.
*/
reset() {
this._packageManifest = [];
this._manifestDiest = null;
this._signedInfo = null;
this._signature = null;
this._keyInfo = null;
this._timestamp = null;
}
/**
* Add file to signature.
*
* @param uri File URI.
* @param data File data.
*/
addFile(uri, data) {
if (this._signedInfo || this._manifestDiest) {
throw new Error('Cannot call after: digest');
}
const digestB64 = this._base64Encode(this._hashSha256(data)); // Not perfect, but matches official packager.
const uriEncoded = uri.replace(/&/g, '&');
this._packageManifest.push(this._templated('fileReference', [uriEncoded, digestB64]));
}
/**
* Digest contents.
*/
digest() {
if (this._signedInfo || this._manifestDiest) {
throw new Error('Already called');
}
const manifest = this._templated('packageManifest', [this._packageManifest.join('')]);
const digest = this._hashSha256(Buffer.from(manifest, 'utf8'));
const signed = this._templated('SignedInfo', [this._base64Encode(digest)]);
this._manifestDiest = digest;
this._signedInfo = signed;
}
/**
* Sign signature.
*/
sign() {
if (this._signature || this._keyInfo !== null) {
throw new Error('Already called');
}
const signedInfo = this._signedInfo;
if (!signedInfo) {
throw new Error('Must call after: digest');
}
const {
keyPrivate
} = this;
if (!keyPrivate) {
throw new Error('Private key not set');
}
const keyInfo = this._buildKeyInfo();
const signature = keyPrivate.sign(Buffer.from(signedInfo, 'utf8'), 'sha1');
this._signature = signature;
this._keyInfo = keyInfo;
}
/**
* Add timestamp to signature.
*/
async timestamp() {
if (this._timestamp) {
throw new Error('Already called');
}
const signature = this._signature;
if (!signature) {
throw new Error('Must call after: sign');
}
const {
timestampUrl
} = this;
if (!timestampUrl) {
throw new Error('Timestamp URL not set');
}
const message = this._templated('SignatureValue', [this._base64Encode(signature)]);
const timestamper = this._createSecurityTimestamper(timestampUrl);
const timestamp = await timestamper.timestamp(this._hashSha1(Buffer.from(message, 'utf8')), 'sha1');
this._timestamp = timestamp;
}
/**
* Encode signature.
*
* @returns Encoded signature.
*/
encode() {
const signature = this._signature;
if (!signature) {
throw new Error('Must call after: sign');
}
const manifestDiest = this._manifestDiest;
const keyInfo = this._keyInfo;
if (!manifestDiest || keyInfo === null) {
throw new Error('Internal error');
}
const timestamp = this._timestamp ? this._createTimestampXml() : '';
return Buffer.from(this._templated('PackageSignature', [this._base64Encode(manifestDiest), this._base64Encode(signature), keyInfo, this._packageManifest.join(''), timestamp]), 'utf8');
}
/**
* Get list of timestamp data references for URI attribute.
*
* @returns List of references.
*/
_getTimestampDataReferenceUris() {
const r = [];
if (this.timestampUriSignature) {
r.push('SignatureValue');
}
if (this.timestampUriPackage) {
r.push('#PackageSignatureValue');
}
return r;
}
/**
* Create string from a template string.
*
* @param name Template name.
* @param values Indexed values.
* @returns Complete string.
*/
_templated(name, values) {
const template = this._templates.get(name);
if (!template) {
throw new Error(`Unknown template name: ${name}`);
}
return template.replace(/\{(\d+)\}/g, (str, index) => {
const i = +index;
if (i >= values.length) {
throw new Error(`Index out of range: ${i} > ${values.length}`);
}
return values[i];
});
}
/**
* Create timestamper.
*
* @param url Server URL.
* @returns Timestamper instance.
*/
_createSecurityTimestamper(url) {
return new _timestamper.SecurityTimestamper(url);
}
/**
* Create SHA1 hasher instance.
*
* @returns Hasher instance.
*/
_createHasherSha1() {
return new _sha.HasherSha1();
}
/**
* Create SHA256 hasher instance.
*
* @returns Hasher instance.
*/
_createHasherSha256() {
return new _sha2.HasherSha256();
}
/**
* Hash data using SHA1.
*
* @param data Data to be hashed.
* @returns Hash digest.
*/
_hashSha1(data) {
const hasher = this._createHasherSha1();
hasher.update(data);
return hasher.digest();
}
/**
* Hash data using SHA256.
*
* @param data Data to be hashed.
* @returns Hash digest.
*/
_hashSha256(data) {
const hasher = this._createHasherSha256();
hasher.update(data);
return hasher.digest();
}
/**
* Base64 encode with some defaults to match official pacakger.
*
* @param data Data to be encoded.
* @param chunk Chunk size.
* @param delimit Chunk delimiter.
* @returns Encoded data.
*/
_base64Encode(data, chunk = 76, delimit = '\n') {
let b64 = data.toString('base64');
const chunks = [];
while (b64.length > chunk) {
chunks.push(b64.substr(0, chunk));
b64 = b64.substr(chunk);
}
if (b64.length) {
chunks.push(b64);
}
return chunks.join(delimit);
}
/**
* Create the timestamp XML.
*
* @returns Timestamp XML.
*/
_createTimestampXml() {
const timestamp = this._timestamp;
if (!timestamp) {
throw new Error('Internal error');
}
const timestampBase64 = this._base64Encode(timestamp);
const result = [];
for (const uri of this._getTimestampDataReferenceUris()) {
result.push(this._templated('timestamp', [uri, timestampBase64]));
}
return result.join('\n');
}
/**
* Build the key info.
*
* @returns Key info.
*/
_buildKeyInfo() {
const {
certchain,
crlValidationCerts,
crls
} = this._buildAndVerifyCertChain();
const out = [];
for (const data of certchain) {
out.push(this._templated('certificate', [this._base64Encode(data)]));
}
if (crls.length) {
for (const data of crlValidationCerts) {
out.push(this._templated('certificate', [this._base64Encode(data)]));
}
}
for (const data of crls) {
out.push(this._templated('crl', [this._base64Encode(data)]));
}
return out.join('');
}
/**
* Build the certchain data.
*
* @returns Certchain data.
*/
_buildAndVerifyCertChain() {
const {
certificate
} = this;
if (!certificate) {
throw new Error('Certificate not set');
} // Not exactly complete, but enough for self-signed anyway.
const certchain = [];
const crlValidationCerts = [];
const crls = []; // Add the certificate data.
certchain.push(certificate.encodeCertchain());
return {
certchain,
crlValidationCerts,
crls
};
}
}
exports.Signature = Signature;
//# sourceMappingURL=signature.js.map