pkijs
Version:
Public Key Infrastructure (PKI) is the basis of how identity and key management is performed on the web today. PKIjs is a pure JavaScript library implementing the formats that are used in PKI applications. It is built on WebCrypto and aspires to make it p
1,204 lines (1,005 loc) • 40.9 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }();
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
var _asn1js = require("asn1js");
var asn1js = _interopRequireWildcard(_asn1js);
var _pvutils = require("pvutils");
var _common = require("./common.js");
var _AlgorithmIdentifier = require("./AlgorithmIdentifier.js");
var _AlgorithmIdentifier2 = _interopRequireDefault(_AlgorithmIdentifier);
var _EncapsulatedContentInfo = require("./EncapsulatedContentInfo.js");
var _EncapsulatedContentInfo2 = _interopRequireDefault(_EncapsulatedContentInfo);
var _Certificate = require("./Certificate.js");
var _Certificate2 = _interopRequireDefault(_Certificate);
var _CertificateRevocationList = require("./CertificateRevocationList.js");
var _CertificateRevocationList2 = _interopRequireDefault(_CertificateRevocationList);
var _OtherRevocationInfoFormat = require("./OtherRevocationInfoFormat.js");
var _OtherRevocationInfoFormat2 = _interopRequireDefault(_OtherRevocationInfoFormat);
var _SignerInfo = require("./SignerInfo.js");
var _SignerInfo2 = _interopRequireDefault(_SignerInfo);
var _CertificateSet = require("./CertificateSet.js");
var _CertificateSet2 = _interopRequireDefault(_CertificateSet);
var _RevocationInfoChoices = require("./RevocationInfoChoices.js");
var _RevocationInfoChoices2 = _interopRequireDefault(_RevocationInfoChoices);
var _IssuerAndSerialNumber = require("./IssuerAndSerialNumber.js");
var _IssuerAndSerialNumber2 = _interopRequireDefault(_IssuerAndSerialNumber);
var _TSTInfo = require("./TSTInfo.js");
var _TSTInfo2 = _interopRequireDefault(_TSTInfo);
var _CertificateChainValidationEngine = require("./CertificateChainValidationEngine.js");
var _CertificateChainValidationEngine2 = _interopRequireDefault(_CertificateChainValidationEngine);
var _BasicOCSPResponse = require("./BasicOCSPResponse.js");
var _BasicOCSPResponse2 = _interopRequireDefault(_BasicOCSPResponse);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }
function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
//**************************************************************************************
/**
* Class from RFC5652
*/
var SignedData = function () {
//**********************************************************************************
/**
* Constructor for SignedData class
* @param {Object} [parameters={}]
* @property {Object} [schema] asn1js parsed value
*/
function SignedData() {
var parameters = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
_classCallCheck(this, SignedData);
//region Internal properties of the object
/**
* @type {number}
* @description version
*/
this.version = (0, _pvutils.getParametersValue)(parameters, "version", SignedData.defaultValues("version"));
/**
* @type {Array.<AlgorithmIdentifier>}
* @description digestAlgorithms
*/
this.digestAlgorithms = (0, _pvutils.getParametersValue)(parameters, "digestAlgorithms", SignedData.defaultValues("digestAlgorithms"));
/**
* @type {EncapsulatedContentInfo}
* @description encapContentInfo
*/
this.encapContentInfo = (0, _pvutils.getParametersValue)(parameters, "encapContentInfo", SignedData.defaultValues("encapContentInfo"));
if ("certificates" in parameters)
/**
* @type {Array.<Certificate|OtherCertificateFormat>}
* @description certificates
*/
this.certificates = (0, _pvutils.getParametersValue)(parameters, "certificates", SignedData.defaultValues("certificates"));
if ("crls" in parameters)
/**
* @type {Array.<CertificateRevocationList|OtherRevocationInfoFormat>}
* @description crls
*/
this.crls = (0, _pvutils.getParametersValue)(parameters, "crls", SignedData.defaultValues("crls"));
if ("ocsps" in parameters)
/**
* @type {Array.<BasicOCSPResponse>}
* @description crls
*/
this.ocsps = (0, _pvutils.getParametersValue)(parameters, "ocsps", SignedData.defaultValues("ocsps"));
/**
* @type {Array.<SignerInfo>}
* @description signerInfos
*/
this.signerInfos = (0, _pvutils.getParametersValue)(parameters, "signerInfos", SignedData.defaultValues("signerInfos"));
//endregion
//region If input argument array contains "schema" for this object
if ("schema" in parameters) this.fromSchema(parameters.schema);
//endregion
}
//**********************************************************************************
/**
* Return default values for all class members
* @param {string} memberName String name for a class member
*/
_createClass(SignedData, [{
key: "fromSchema",
//**********************************************************************************
/**
* Convert parsed asn1js object into current class
* @param {!Object} schema
*/
value: function fromSchema(schema) {
//region Check the schema is valid
var asn1 = asn1js.compareSchema(schema, schema, SignedData.schema());
if (asn1.verified === false) throw new Error("Object's schema was not verified against input data for SignedData");
//endregion
//region Get internal properties from parsed schema
this.version = asn1.result["SignedData.version"].valueBlock.valueDec;
if ("SignedData.digestAlgorithms" in asn1.result) // Could be empty SET of digest algorithms
this.digestAlgorithms = Array.from(asn1.result["SignedData.digestAlgorithms"], function (algorithm) {
return new _AlgorithmIdentifier2.default({ schema: algorithm });
});
this.encapContentInfo = new _EncapsulatedContentInfo2.default({ schema: asn1.result["SignedData.encapContentInfo"] });
if ("SignedData.certificates" in asn1.result) {
asn1.result["SignedData.certificates"].idBlock.tagClass = 1; // UNIVERSAL
asn1.result["SignedData.certificates"].idBlock.tagNumber = 17; // SET
var certificateSet = new _CertificateSet2.default({ schema: asn1.result["SignedData.certificates"] });
this.certificates = certificateSet.certificates.slice(0); // Copy all just for making comfortable access
}
if ("SignedData.crls" in asn1.result) {
this.crls = Array.from(asn1.result["SignedData.crls"], function (crl) {
if (crl.idBlock.tagClass === 1) return new _CertificateRevocationList2.default({ schema: crl });
//region Create SEQUENCE from [1]
crl.idBlock.tagClass = 1; // UNIVERSAL
crl.idBlock.tagNumber = 16; // SEQUENCE
//endregion
return new _OtherRevocationInfoFormat2.default({ schema: crl });
});
}
if ("SignedData.signerInfos" in asn1.result) // Could be empty SET SignerInfos
this.signerInfos = Array.from(asn1.result["SignedData.signerInfos"], function (signerInfoSchema) {
return new _SignerInfo2.default({ schema: signerInfoSchema });
});
//endregion
}
//**********************************************************************************
/**
* Convert current object to asn1js object and set correct values
* @returns {Object} asn1js object
*/
}, {
key: "toSchema",
value: function toSchema() {
var encodeFlag = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
//region Create array for output sequence
var outputArray = [];
outputArray.push(new asn1js.Integer({ value: this.version }));
//region Create array of digest algorithms
outputArray.push(new asn1js.Set({
value: Array.from(this.digestAlgorithms, function (algorithm) {
return algorithm.toSchema(encodeFlag);
})
}));
//endregion
outputArray.push(this.encapContentInfo.toSchema());
if ("certificates" in this) {
var certificateSet = new _CertificateSet2.default({ certificates: this.certificates });
var certificateSetSchema = certificateSet.toSchema();
certificateSetSchema.idBlock.tagClass = 3;
certificateSetSchema.idBlock.tagNumber = 0;
outputArray.push(certificateSetSchema);
}
if ("crls" in this) {
outputArray.push(new asn1js.Constructed({
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 1 // [1]
},
value: Array.from(this.crls, function (crl) {
if (crl instanceof _OtherRevocationInfoFormat2.default) {
var crlSchema = crl.toSchema(encodeFlag);
crlSchema.idBlock.tagClass = 3;
crlSchema.idBlock.tagNumber = 1;
return crlSchema;
}
return crl.toSchema(encodeFlag);
})
}));
}
//region Create array of signer infos
outputArray.push(new asn1js.Set({
value: Array.from(this.signerInfos, function (signerInfo) {
return signerInfo.toSchema(encodeFlag);
})
}));
//endregion
//endregion
//region Construct and return new ASN.1 schema for this object
return new asn1js.Sequence({
value: outputArray
});
//endregion
}
//**********************************************************************************
/**
* Convertion for the class to JSON object
* @returns {Object}
*/
}, {
key: "toJSON",
value: function toJSON() {
var _object = {
version: this.version,
digestAlgorithms: Array.from(this.digestAlgorithms, function (algorithm) {
return algorithm.toJSON();
}),
encapContentInfo: this.encapContentInfo.toJSON()
};
if ("certificates" in this) _object.certificates = Array.from(this.certificates, function (certificate) {
return certificate.toJSON();
});
if ("crls" in this) _object.crls = Array.from(this.crls, function (crl) {
return crl.toJSON();
});
_object.signerInfos = Array.from(this.signerInfos, function (signerInfo) {
return signerInfo.toJSON();
});
return _object;
}
//**********************************************************************************
/**
* Verify current SignedData value
* @param signer
* @param data
* @param trustedCerts
* @param checkDate
* @param checkChain
* @param includeSignerCertificate
* @param extendedMode
* @param findOrigin
* @param findIssuer
* @returns {*}
*/
}, {
key: "verify",
value: function verify() {
var _this = this;
var _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
_ref$signer = _ref.signer,
signer = _ref$signer === undefined ? -1 : _ref$signer,
_ref$data = _ref.data,
data = _ref$data === undefined ? new ArrayBuffer(0) : _ref$data,
_ref$trustedCerts = _ref.trustedCerts,
trustedCerts = _ref$trustedCerts === undefined ? [] : _ref$trustedCerts,
_ref$checkDate = _ref.checkDate,
checkDate = _ref$checkDate === undefined ? new Date() : _ref$checkDate,
_ref$checkChain = _ref.checkChain,
checkChain = _ref$checkChain === undefined ? false : _ref$checkChain,
_ref$extendedMode = _ref.extendedMode,
extendedMode = _ref$extendedMode === undefined ? false : _ref$extendedMode,
_ref$findOrigin = _ref.findOrigin,
findOrigin = _ref$findOrigin === undefined ? null : _ref$findOrigin,
_ref$findIssuer = _ref.findIssuer,
findIssuer = _ref$findIssuer === undefined ? null : _ref$findIssuer;
//region Global variables
var sequence = Promise.resolve();
var messageDigestValue = new ArrayBuffer(0);
var shaAlgorithm = "";
var signerCertificate = {};
var timestampSerial = null;
var certificatePath = [];
var engine = (0, _common.getEngine)();
//endregion
//region Get a "crypto" extension
var crypto = (0, _common.getCrypto)();
if (typeof crypto === "undefined") return Promise.reject("Unable to create WebCrypto object");
//endregion
//region Get a signer number
if (signer === -1) {
if (extendedMode) {
return Promise.reject({
date: checkDate,
code: 1,
message: "Unable to get signer index from input parameters",
signatureVerified: null,
signerCertificate: null,
signerCertificateVerified: null
});
}
return Promise.reject("Unable to get signer index from input parameters");
}
//endregion
//region Check that certificates field was included in signed data
if ("certificates" in this === false) {
if (extendedMode) {
return Promise.reject({
date: checkDate,
code: 2,
message: "No certificates attached to this signed data",
signatureVerified: null,
signerCertificate: null,
signerCertificateVerified: null
});
}
return Promise.reject("No certificates attached to this signed data");
}
//endregion
//region Find a certificate for specified signer
if (this.signerInfos[signer].sid instanceof _IssuerAndSerialNumber2.default) {
sequence = sequence.then(function () {
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
try {
for (var _iterator = _this.certificates[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var certificate = _step.value;
if (certificate instanceof _Certificate2.default === false) continue;
if (certificate.issuer.isEqual(_this.signerInfos[signer].sid.issuer) && certificate.serialNumber.isEqual(_this.signerInfos[signer].sid.serialNumber)) {
signerCertificate = certificate;
return Promise.resolve();
}
}
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator.return) {
_iterator.return();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
}
if (extendedMode) {
return Promise.reject({
date: checkDate,
code: 3,
message: "Unable to find signer certificate",
signatureVerified: null,
signerCertificate: null,
signerCertificateVerified: null
});
}
return Promise.reject("Unable to find signer certificate");
});
} else // Find by SubjectKeyIdentifier
{
sequence = sequence.then(function () {
return Promise.all(Array.from(_this.certificates.filter(function (certificate) {
return certificate instanceof _Certificate2.default;
}), function (certificate) {
return crypto.digest({ name: "sha-1" }, new Uint8Array(certificate.subjectPublicKeyInfo.subjectPublicKey.valueBlock.valueHex));
})).then(function (results) {
var _iteratorNormalCompletion2 = true;
var _didIteratorError2 = false;
var _iteratorError2 = undefined;
try {
for (var _iterator2 = _this.certificates.entries()[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
var _ref2 = _step2.value;
var _ref3 = _slicedToArray(_ref2, 2);
var index = _ref3[0];
var certificate = _ref3[1];
if (certificate instanceof _Certificate2.default === false) continue;
if ((0, _pvutils.isEqualBuffer)(results[index], _this.signerInfos[signer].sid.valueBlock.valueHex)) {
signerCertificate = certificate;
return Promise.resolve();
}
}
} catch (err) {
_didIteratorError2 = true;
_iteratorError2 = err;
} finally {
try {
if (!_iteratorNormalCompletion2 && _iterator2.return) {
_iterator2.return();
}
} finally {
if (_didIteratorError2) {
throw _iteratorError2;
}
}
}
if (extendedMode) {
return Promise.reject({
date: checkDate,
code: 3,
message: "Unable to find signer certificate",
signatureVerified: null,
signerCertificate: null,
signerCertificateVerified: null
});
}
return Promise.reject("Unable to find signer certificate");
}, function () {
if (extendedMode) {
return Promise.reject({
date: checkDate,
code: 3,
message: "Unable to find signer certificate",
signatureVerified: null,
signerCertificate: null,
signerCertificateVerified: null
});
}
return Promise.reject("Unable to find signer certificate");
});
});
}
//endregion
//region Verify internal digest in case of "tSTInfo" content type
sequence = sequence.then(function () {
if (_this.encapContentInfo.eContentType === "1.2.840.113549.1.9.16.1.4") {
//region Check "eContent" precense
if ("eContent" in _this.encapContentInfo === false) return false;
//endregion
//region Initialize TST_INFO value
var asn1 = asn1js.fromBER(_this.encapContentInfo.eContent.valueBlock.valueHex);
var tstInfo = void 0;
try {
tstInfo = new _TSTInfo2.default({ schema: asn1.result });
} catch (ex) {
return false;
}
//endregion
//region Change "checkDate" and append "timestampSerial"
checkDate = tstInfo.genTime;
timestampSerial = tstInfo.serialNumber.valueBlock.valueHex;
//endregion
//region Check that we do have detached data content
if (data.byteLength === 0) {
if (extendedMode) {
return Promise.reject({
date: checkDate,
code: 4,
message: "Missed detached data input array",
signatureVerified: null,
signerCertificate: signerCertificate,
signerCertificateVerified: null
});
}
return Promise.reject("Missed detached data input array");
}
//endregion
return tstInfo.verify({ data: data });
}
return true;
});
//endregion
//region Make additional verification for signer's certificate
function checkCA(cert) {
/// <param name="cert" type="in_window.org.pkijs.simpl.CERT">Certificate to find CA flag for</param>
//region Do not include signer's certificate
if (cert.issuer.isEqual(signerCertificate.issuer) === true && cert.serialNumber.isEqual(signerCertificate.serialNumber) === true) return null;
//endregion
var isCA = false;
if ("extensions" in cert) {
var _iteratorNormalCompletion3 = true;
var _didIteratorError3 = false;
var _iteratorError3 = undefined;
try {
for (var _iterator3 = cert.extensions[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) {
var extension = _step3.value;
if (extension.extnID === "2.5.29.19") // BasicConstraints
{
if ("cA" in extension.parsedValue) {
if (extension.parsedValue.cA === true) isCA = true;
}
}
}
} catch (err) {
_didIteratorError3 = true;
_iteratorError3 = err;
} finally {
try {
if (!_iteratorNormalCompletion3 && _iterator3.return) {
_iterator3.return();
}
} finally {
if (_didIteratorError3) {
throw _iteratorError3;
}
}
}
}
if (isCA) return cert;
return null;
}
if (checkChain) {
sequence = sequence.then(function (result) {
var _certificateChainEngi;
//region Verify result of previous operation
if (result === false) return false;
//endregion
var promiseResults = Array.from(_this.certificates.filter(function (certificate) {
return certificate instanceof _Certificate2.default;
}), function (certificate) {
return checkCA(certificate);
});
var certificateChainValidationEngineParameters = {
checkDate: checkDate,
certs: Array.from(promiseResults.filter(function (_result) {
return _result !== null;
})),
trustedCerts: trustedCerts
};
if (findIssuer !== null) certificateChainValidationEngineParameters.findIssuer = findIssuer;
if (findOrigin !== null) certificateChainValidationEngineParameters.findOrigin = findOrigin;
var certificateChainEngine = new _CertificateChainValidationEngine2.default(certificateChainValidationEngineParameters);
certificateChainEngine.certs.push(signerCertificate);
if ("crls" in _this) {
var _iteratorNormalCompletion4 = true;
var _didIteratorError4 = false;
var _iteratorError4 = undefined;
try {
for (var _iterator4 = _this.crls[Symbol.iterator](), _step4; !(_iteratorNormalCompletion4 = (_step4 = _iterator4.next()).done); _iteratorNormalCompletion4 = true) {
var crl = _step4.value;
if (crl instanceof _CertificateRevocationList2.default) certificateChainEngine.crls.push(crl);else // Assumed "revocation value" has "OtherRevocationInfoFormat"
{
if (crl.otherRevInfoFormat === "1.3.6.1.5.5.7.48.1.1") // Basic OCSP response
certificateChainEngine.ocsps.push(new _BasicOCSPResponse2.default({ schema: crl.otherRevInfo }));
}
}
} catch (err) {
_didIteratorError4 = true;
_iteratorError4 = err;
} finally {
try {
if (!_iteratorNormalCompletion4 && _iterator4.return) {
_iterator4.return();
}
} finally {
if (_didIteratorError4) {
throw _iteratorError4;
}
}
}
}
if ("ocsps" in _this) (_certificateChainEngi = certificateChainEngine.ocsps).push.apply(_certificateChainEngi, _toConsumableArray(_this.ocsps));
return certificateChainEngine.verify().then(function (verificationResult) {
if ("certificatePath" in verificationResult) certificatePath = verificationResult.certificatePath;
if (verificationResult.result === true) return Promise.resolve(true);
if (extendedMode) {
return Promise.reject({
date: checkDate,
code: 5,
message: "Validation of signer's certificate failed: " + verificationResult.resultMessage,
signatureVerified: null,
signerCertificate: signerCertificate,
signerCertificateVerified: false
});
}
return Promise.reject("Validation of signer's certificate failed");
}, function (error) {
if (extendedMode) {
return Promise.reject({
date: checkDate,
code: 5,
message: "Validation of signer's certificate failed with error: " + (error instanceof Object ? error.resultMessage : error),
signatureVerified: null,
signerCertificate: signerCertificate,
signerCertificateVerified: false
});
}
return Promise.reject("Validation of signer's certificate failed with error: " + (error instanceof Object ? error.resultMessage : error));
});
});
}
//endregion
//region Find signer's hashing algorithm
sequence = sequence.then(function (result) {
//region Verify result of previous operation
if (result === false) return false;
//endregion
var signerInfoHashAlgorithm = (0, _common.getAlgorithmByOID)(_this.signerInfos[signer].digestAlgorithm.algorithmId);
if ("name" in signerInfoHashAlgorithm === false) {
if (extendedMode) {
return Promise.reject({
date: checkDate,
code: 7,
message: "Unsupported signature algorithm: " + _this.signerInfos[signer].digestAlgorithm.algorithmId,
signatureVerified: null,
signerCertificate: signerCertificate,
signerCertificateVerified: true
});
}
return Promise.reject("Unsupported signature algorithm: " + _this.signerInfos[signer].digestAlgorithm.algorithmId);
}
shaAlgorithm = signerInfoHashAlgorithm.name;
return true;
});
//endregion
//region Create correct data block for verification
sequence = sequence.then(function (result) {
//region Verify result of previous operation
if (result === false) return false;
//endregion
if ("eContent" in _this.encapContentInfo) // Attached data
{
if (_this.encapContentInfo.eContent.idBlock.tagClass === 1 && _this.encapContentInfo.eContent.idBlock.tagNumber === 4) {
if (_this.encapContentInfo.eContent.idBlock.isConstructed === false) data = _this.encapContentInfo.eContent.valueBlock.valueHex;else {
var _iteratorNormalCompletion5 = true;
var _didIteratorError5 = false;
var _iteratorError5 = undefined;
try {
for (var _iterator5 = _this.encapContentInfo.eContent.valueBlock.value[Symbol.iterator](), _step5; !(_iteratorNormalCompletion5 = (_step5 = _iterator5.next()).done); _iteratorNormalCompletion5 = true) {
var contentValue = _step5.value;
data = (0, _pvutils.utilConcatBuf)(data, contentValue.valueBlock.valueHex);
}
} catch (err) {
_didIteratorError5 = true;
_iteratorError5 = err;
} finally {
try {
if (!_iteratorNormalCompletion5 && _iterator5.return) {
_iterator5.return();
}
} finally {
if (_didIteratorError5) {
throw _iteratorError5;
}
}
}
}
} else data = _this.encapContentInfo.eContent.valueBlock.valueBeforeDecode;
} else // Detached data
{
if (data.byteLength === 0) // Check that "data" already provided by function parameter
{
if (extendedMode) {
return Promise.reject({
date: checkDate,
code: 8,
message: "Missed detached data input array",
signatureVerified: null,
signerCertificate: signerCertificate,
signerCertificateVerified: true
});
}
return Promise.reject("Missed detached data input array");
}
}
if ("signedAttrs" in _this.signerInfos[signer]) {
//region Check mandatory attributes
var foundContentType = false;
var foundMessageDigest = false;
var _iteratorNormalCompletion6 = true;
var _didIteratorError6 = false;
var _iteratorError6 = undefined;
try {
for (var _iterator6 = _this.signerInfos[signer].signedAttrs.attributes[Symbol.iterator](), _step6; !(_iteratorNormalCompletion6 = (_step6 = _iterator6.next()).done); _iteratorNormalCompletion6 = true) {
var attribute = _step6.value;
//region Check that "content-type" attribute exists
if (attribute.type === "1.2.840.113549.1.9.3") foundContentType = true;
//endregion
//region Check that "message-digest" attribute exists
if (attribute.type === "1.2.840.113549.1.9.4") {
foundMessageDigest = true;
messageDigestValue = attribute.values[0].valueBlock.valueHex;
}
//endregion
//region Speed-up searching
if (foundContentType && foundMessageDigest) break;
//endregion
}
} catch (err) {
_didIteratorError6 = true;
_iteratorError6 = err;
} finally {
try {
if (!_iteratorNormalCompletion6 && _iterator6.return) {
_iterator6.return();
}
} finally {
if (_didIteratorError6) {
throw _iteratorError6;
}
}
}
if (foundContentType === false) {
if (extendedMode) {
return Promise.reject({
date: checkDate,
code: 9,
message: "Attribute \"content-type\" is a mandatory attribute for \"signed attributes\"",
signatureVerified: null,
signerCertificate: signerCertificate,
signerCertificateVerified: true
});
}
return Promise.reject("Attribute \"content-type\" is a mandatory attribute for \"signed attributes\"");
}
if (foundMessageDigest === false) {
if (extendedMode) {
return Promise.reject({
date: checkDate,
code: 10,
message: "Attribute \"message-digest\" is a mandatory attribute for \"signed attributes\"",
signatureVerified: null,
signerCertificate: signerCertificate,
signerCertificateVerified: true
});
}
return Promise.reject("Attribute \"message-digest\" is a mandatory attribute for \"signed attributes\"");
}
//endregion
}
return true;
});
//endregion
//region Verify "message-digest" attribute in case of "signedAttrs"
sequence = sequence.then(function (result) {
//region Verify result of previous operation
if (result === false) return false;
//endregion
if ("signedAttrs" in _this.signerInfos[signer]) return crypto.digest(shaAlgorithm, new Uint8Array(data));
return true;
}).then(
/**
* @param {ArrayBuffer} result
*/
function (result) {
//region Verify result of previous operation
if (result === false) return false;
//endregion
if ("signedAttrs" in _this.signerInfos[signer]) {
if ((0, _pvutils.isEqualBuffer)(result, messageDigestValue)) {
data = _this.signerInfos[signer].signedAttrs.encodedValue;
return true;
}
return false;
}
return true;
});
//endregion
sequence = sequence.then(function (result) {
//region Verify result of previous operation
if (result === false) return false;
//endregion
return engine.subtle.verifyWithPublicKey(data, _this.signerInfos[signer].signature, signerCertificate.subjectPublicKeyInfo, signerCertificate.signatureAlgorithm, shaAlgorithm);
});
//region Make a final result
sequence = sequence.then(function (result) {
if (extendedMode) {
return {
date: checkDate,
code: 14,
message: "",
signatureVerified: result,
signerCertificate: signerCertificate,
timestampSerial: timestampSerial,
signerCertificateVerified: true,
certificatePath: certificatePath
};
}
return result;
}, function (error) {
if (extendedMode) {
if ("code" in error) return Promise.reject(error);
return Promise.reject({
date: checkDate,
code: 15,
message: "Error during verification: " + error.message,
signatureVerified: null,
signerCertificate: signerCertificate,
timestampSerial: timestampSerial,
signerCertificateVerified: true
});
}
return Promise.reject(error);
});
//endregion
return sequence;
}
//**********************************************************************************
/**
* Signing current SignedData
* @param {key} privateKey Private key for "subjectPublicKeyInfo" structure
* @param {number} signerIndex Index number (starting from 0) of signer index to make signature for
* @param {string} [hashAlgorithm="SHA-1"] Hashing algorithm. Default SHA-1
* @param {ArrayBuffer} [data] Detached data
* @returns {*}
*/
}, {
key: "sign",
value: function sign(privateKey, signerIndex) {
var _this2 = this;
var hashAlgorithm = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : "SHA-1";
var data = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : new ArrayBuffer(0);
//region Initial checking
if (typeof privateKey === "undefined") return Promise.reject("Need to provide a private key for signing");
//endregion
//region Initial variables
var sequence = Promise.resolve();
var parameters = void 0;
var engine = (0, _common.getEngine)();
//endregion
//region Simple check for supported algorithm
var hashAlgorithmOID = (0, _common.getOIDByAlgorithm)({ name: hashAlgorithm });
if (hashAlgorithmOID === "") return Promise.reject("Unsupported hash algorithm: " + hashAlgorithm);
//endregion
//region Append information about hash algorithm
if (this.digestAlgorithms.filter(function (algorithm) {
return algorithm.algorithmId === hashAlgorithmOID;
}).length === 0) {
this.digestAlgorithms.push(new _AlgorithmIdentifier2.default({
algorithmId: hashAlgorithmOID,
algorithmParams: new asn1js.Null()
}));
}
this.signerInfos[signerIndex].digestAlgorithm = new _AlgorithmIdentifier2.default({
algorithmId: hashAlgorithmOID,
algorithmParams: new asn1js.Null()
});
//endregion
//region Get a "default parameters" for current algorithm and set correct signature algorithm
sequence = sequence.then(function () {
return engine.subtle.getSignatureParameters(privateKey, hashAlgorithm);
});
sequence = sequence.then(function (result) {
parameters = result.parameters;
_this2.signerInfos[signerIndex].signatureAlgorithm = result.signatureAlgorithm;
});
//endregion
//region Create TBS data for signing
sequence = sequence.then(function () {
if ("signedAttrs" in _this2.signerInfos[signerIndex]) {
if (_this2.signerInfos[signerIndex].signedAttrs.encodedValue.byteLength !== 0) data = _this2.signerInfos[signerIndex].signedAttrs.encodedValue;else {
data = _this2.signerInfos[signerIndex].signedAttrs.toSchema(true).toBER(false);
//region Change type from "[0]" to "SET" acordingly to standard
var view = new Uint8Array(data);
view[0] = 0x31;
//endregion
}
} else {
if ("eContent" in _this2.encapContentInfo) // Attached data
{
if (_this2.encapContentInfo.eContent.idBlock.tagClass === 1 && _this2.encapContentInfo.eContent.idBlock.tagNumber === 4) {
if (_this2.encapContentInfo.eContent.idBlock.isConstructed === false) data = _this2.encapContentInfo.eContent.valueBlock.valueHex;else {
var _iteratorNormalCompletion7 = true;
var _didIteratorError7 = false;
var _iteratorError7 = undefined;
try {
for (var _iterator7 = _this2.encapContentInfo.eContent.valueBlock.value[Symbol.iterator](), _step7; !(_iteratorNormalCompletion7 = (_step7 = _iterator7.next()).done); _iteratorNormalCompletion7 = true) {
var content = _step7.value;
data = (0, _pvutils.utilConcatBuf)(data, content.valueBlock.valueHex);
}
} catch (err) {
_didIteratorError7 = true;
_iteratorError7 = err;
} finally {
try {
if (!_iteratorNormalCompletion7 && _iterator7.return) {
_iterator7.return();
}
} finally {
if (_didIteratorError7) {
throw _iteratorError7;
}
}
}
}
} else data = _this2.encapContentInfo.eContent.valueBlock.valueBeforeDecode;
} else // Detached data
{
if (data.byteLength === 0) // Check that "data" already provided by function parameter
return Promise.reject("Missed detached data input array");
}
}
return Promise.resolve();
});
//endregion
//region Signing TBS data on provided private key
sequence = sequence.then(function () {
return engine.subtle.signWithPrivateKey(data, privateKey, parameters);
});
sequence = sequence.then(function (result) {
_this2.signerInfos[signerIndex].signature = new asn1js.OctetString({ valueHex: result });
return result;
});
//endregion
return sequence;
}
//**********************************************************************************
}], [{
key: "defaultValues",
value: function defaultValues(memberName) {
switch (memberName) {
case "version":
return 0;
case "digestAlgorithms":
return [];
case "encapContentInfo":
return new _EncapsulatedContentInfo2.default();
case "certificates":
return [];
case "crls":
return [];
case "ocsps":
return [];
case "signerInfos":
return [];
default:
throw new Error("Invalid member name for SignedData class: " + memberName);
}
}
//**********************************************************************************
/**
* Compare values with default values for all class members
* @param {string} memberName String name for a class member
* @param {*} memberValue Value to compare with default value
*/
}, {
key: "compareWithDefault",
value: function compareWithDefault(memberName, memberValue) {
switch (memberName) {
case "version":
return memberValue === SignedData.defaultValues("version");
case "encapContentInfo":
return new _EncapsulatedContentInfo2.default();
case "digestAlgorithms":
case "certificates":
case "crls":
case "ocsps":
case "signerInfos":
return memberValue.length === 0;
default:
throw new Error("Invalid member name for SignedData class: " + memberName);
}
}
//**********************************************************************************
/**
* Return value of asn1js schema for current class
* @param {Object} parameters Input parameters for the schema
* @returns {Object} asn1js schema object
*/
}, {
key: "schema",
value: function schema() {
var parameters = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
//SignedData ::= SEQUENCE {
// version CMSVersion,
// digestAlgorithms DigestAlgorithmIdentifiers,
// encapContentInfo EncapsulatedContentInfo,
// certificates [0] IMPLICIT CertificateSet OPTIONAL,
// crls [1] IMPLICIT RevocationInfoChoices OPTIONAL,
// signerInfos SignerInfos }
/**
* @type {Object}
* @property {string} [blockName]
* @property {string} [optional]
* @property {string} [digestAlgorithms]
* @property {string} [encapContentInfo]
* @property {string} [certificates]
* @property {string} [crls]
* @property {string} [signerInfos]
*/
var names = (0, _pvutils.getParametersValue)(parameters, "names", {});
if ("optional" in names === false) names.optional = false;
return new asn1js.Sequence({
name: names.blockName || "SignedData",
optional: names.optional,
value: [new asn1js.Integer({ name: names.version || "SignedData.version" }), new asn1js.Set({
value: [new asn1js.Repeated({
name: names.digestAlgorithms || "SignedData.digestAlgorithms",
value: _AlgorithmIdentifier2.default.schema()
})]
}), _EncapsulatedContentInfo2.default.schema(names.encapContentInfo || {
names: {
blockName: "SignedData.encapContentInfo"
}
}), new asn1js.Constructed({
name: names.certificates || "SignedData.certificates",
optional: true,
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 0 // [0]
},
value: _CertificateSet2.default.schema().valueBlock.value
}), // IMPLICIT CertificateSet
new asn1js.Constructed({
optional: true,
idBlock: {
tagClass: 3, // CONTEXT-SPECIFIC
tagNumber: 1 // [1]
},
value: _RevocationInfoChoices2.default.schema(names.crls || {
names: {
crls: "SignedData.crls"
}
}).valueBlock.value
}), // IMPLICIT RevocationInfoChoices
new asn1js.Set({
value: [new asn1js.Repeated({
name: names.signerInfos || "SignedData.signerInfos",
value: _SignerInfo2.default.schema()
})]
})]
});
}
}]);
return SignedData;
}();
//**************************************************************************************
exports.default = SignedData;
//# sourceMappingURL=SignedData.js.map