import * as asn1js from "asn1js"; import { getParametersValue, clearProps } from "pvutils"; import AlgorithmIdentifier from "./AlgorithmIdentifier.js"; import SignedAndUnsignedAttributes from "./SignedAndUnsignedAttributes.js"; import IssuerAndSerialNumber from "./IssuerAndSerialNumber.js"; //************************************************************************************** /** * Class from RFC5652 */ export default class SignerInfo { //********************************************************************************** /** * Constructor for SignerInfo class * @param {Object} [parameters={}] * @param {Object} [parameters.schema] asn1js parsed value to initialize the class from */ constructor(parameters = {}) { //region Internal properties of the object /** * @type {string} * @desc version */ this.version = getParametersValue(parameters, "version", SignerInfo.defaultValues("version")); /** * @type {Object} * @desc sid */ this.sid = getParametersValue(parameters, "sid", SignerInfo.defaultValues("sid")); /** * @type {AlgorithmIdentifier} * @desc digestAlgorithm */ this.digestAlgorithm = getParametersValue(parameters, "digestAlgorithm", SignerInfo.defaultValues("digestAlgorithm")); if("signedAttrs" in parameters) /** * @type {SignedAndUnsignedAttributes} * @desc signedAttrs */ this.signedAttrs = getParametersValue(parameters, "signedAttrs", SignerInfo.defaultValues("signedAttrs")); /** * @type {AlgorithmIdentifier} * @desc digestAlgorithm */ this.signatureAlgorithm = getParametersValue(parameters, "signatureAlgorithm", SignerInfo.defaultValues("signatureAlgorithm")); /** * @type {OctetString} * @desc signature */ this.signature = getParametersValue(parameters, "signature", SignerInfo.defaultValues("signature")); if("unsignedAttrs" in parameters) /** * @type {SignedAndUnsignedAttributes} * @desc unsignedAttrs */ this.unsignedAttrs = getParametersValue(parameters, "unsignedAttrs", SignerInfo.defaultValues("unsignedAttrs")); //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 */ static defaultValues(memberName) { switch(memberName) { case "version": return 0; case "sid": return new asn1js.Any(); case "digestAlgorithm": return new AlgorithmIdentifier(); case "signedAttrs": return new SignedAndUnsignedAttributes({ type: 0 }); case "signatureAlgorithm": return new AlgorithmIdentifier(); case "signature": return new asn1js.OctetString(); case "unsignedAttrs": return new SignedAndUnsignedAttributes({ type: 1 }); default: throw new Error(`Invalid member name for SignerInfo 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 */ static compareWithDefault(memberName, memberValue) { switch(memberName) { case "version": return (SignerInfo.defaultValues("version") === memberValue); case "sid": return (memberValue instanceof asn1js.Any); case "digestAlgorithm": if((memberValue instanceof AlgorithmIdentifier) === false) return false; return memberValue.isEqual(SignerInfo.defaultValues("digestAlgorithm")); case "signedAttrs": return ((SignedAndUnsignedAttributes.compareWithDefault("type", memberValue.type)) && (SignedAndUnsignedAttributes.compareWithDefault("attributes", memberValue.attributes)) && (SignedAndUnsignedAttributes.compareWithDefault("encodedValue", memberValue.encodedValue))); case "signatureAlgorithm": if((memberValue instanceof AlgorithmIdentifier) === false) return false; return memberValue.isEqual(SignerInfo.defaultValues("signatureAlgorithm")); case "signature": case "unsignedAttrs": return ((SignedAndUnsignedAttributes.compareWithDefault("type", memberValue.type)) && (SignedAndUnsignedAttributes.compareWithDefault("attributes", memberValue.attributes)) && (SignedAndUnsignedAttributes.compareWithDefault("encodedValue", memberValue.encodedValue))); default: throw new Error(`Invalid member name for SignerInfo class: ${memberName}`); } } //********************************************************************************** /** * Return value of pre-defined ASN.1 schema for current class * * ASN.1 schema: * ```asn1 * SignerInfo ::= SEQUENCE { * version CMSVersion, * sid SignerIdentifier, * digestAlgorithm DigestAlgorithmIdentifier, * signedAttrs [0] IMPLICIT SignedAttributes OPTIONAL, * signatureAlgorithm SignatureAlgorithmIdentifier, * signature SignatureValue, * unsignedAttrs [1] IMPLICIT UnsignedAttributes OPTIONAL } * * SignerIdentifier ::= CHOICE { * issuerAndSerialNumber IssuerAndSerialNumber, * subjectKeyIdentifier [0] SubjectKeyIdentifier } * * SubjectKeyIdentifier ::= OCTET STRING * ``` * * @param {Object} parameters Input parameters for the schema * @returns {Object} asn1js schema object */ static schema(parameters = {}) { /** * @type {Object} * @property {string} [blockName] * @property {string} [version] * @property {string} [sid] * @property {string} [digestAlgorithm] * @property {string} [signedAttrs] * @property {string} [signatureAlgorithm] * @property {string} [signature] * @property {string} [unsignedAttrs] */ const names = getParametersValue(parameters, "names", {}); return ( new asn1js.Sequence({ name: "SignerInfo", value: [ new asn1js.Integer({ name: (names.version || "SignerInfo.version") }), new asn1js.Choice({ value: [ IssuerAndSerialNumber.schema(names.sid || { names: { blockName: "SignerInfo.sid" } }), new asn1js.Constructed({ optional: true, name: (names.sid || "SignerInfo.sid"), idBlock: { tagClass: 3, // CONTEXT-SPECIFIC tagNumber: 0 // [0] }, value: [new asn1js.OctetString()] }) ] }), AlgorithmIdentifier.schema(names.digestAlgorithm || { names: { blockName: "SignerInfo.digestAlgorithm" } }), SignedAndUnsignedAttributes.schema(names.signedAttrs || { names: { blockName: "SignerInfo.signedAttrs", tagNumber: 0 } }), AlgorithmIdentifier.schema(names.signatureAlgorithm || { names: { blockName: "SignerInfo.signatureAlgorithm" } }), new asn1js.OctetString({ name: (names.signature || "SignerInfo.signature") }), SignedAndUnsignedAttributes.schema(names.unsignedAttrs || { names: { blockName: "SignerInfo.unsignedAttrs", tagNumber: 1 } }) ] }) ); } //********************************************************************************** /** * Convert parsed asn1js object into current class * @param {!Object} schema */ fromSchema(schema) { //region Clear input data first clearProps(schema, [ "SignerInfo.version", "SignerInfo.sid", "SignerInfo.digestAlgorithm", "SignerInfo.signedAttrs", "SignerInfo.signatureAlgorithm", "SignerInfo.signature", "SignerInfo.unsignedAttrs" ]); //endregion //region Check the schema is valid const asn1 = asn1js.compareSchema(schema, schema, SignerInfo.schema() ); if(asn1.verified === false) throw new Error("Object's schema was not verified against input data for SignerInfo"); //endregion //region Get internal properties from parsed schema this.version = asn1.result["SignerInfo.version"].valueBlock.valueDec; const currentSid = asn1.result["SignerInfo.sid"]; if(currentSid.idBlock.tagClass === 1) this.sid = new IssuerAndSerialNumber({ schema: currentSid }); else this.sid = currentSid; this.digestAlgorithm = new AlgorithmIdentifier({ schema: asn1.result["SignerInfo.digestAlgorithm"] }); if("SignerInfo.signedAttrs" in asn1.result) this.signedAttrs = new SignedAndUnsignedAttributes({ type: 0, schema: asn1.result["SignerInfo.signedAttrs"] }); this.signatureAlgorithm = new AlgorithmIdentifier({ schema: asn1.result["SignerInfo.signatureAlgorithm"] }); this.signature = asn1.result["SignerInfo.signature"]; if("SignerInfo.unsignedAttrs" in asn1.result) this.unsignedAttrs = new SignedAndUnsignedAttributes({ type: 1, schema: asn1.result["SignerInfo.unsignedAttrs"] }); //endregion } //********************************************************************************** /** * Convert current object to asn1js object and set correct values * @returns {Object} asn1js object */ toSchema() { if(SignerInfo.compareWithDefault("sid", this.sid)) throw new Error("Incorrectly initialized \"SignerInfo\" class"); //region Create array for output sequence const outputArray = []; outputArray.push(new asn1js.Integer({ value: this.version })); if(this.sid instanceof IssuerAndSerialNumber) outputArray.push(this.sid.toSchema()); else outputArray.push(this.sid); outputArray.push(this.digestAlgorithm.toSchema()); if("signedAttrs" in this) { if(SignerInfo.compareWithDefault("signedAttrs", this.signedAttrs) === false) outputArray.push(this.signedAttrs.toSchema()); } outputArray.push(this.signatureAlgorithm.toSchema()); outputArray.push(this.signature); if("unsignedAttrs" in this) { if(SignerInfo.compareWithDefault("unsignedAttrs", this.unsignedAttrs) === false) outputArray.push(this.unsignedAttrs.toSchema()); } //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} */ toJSON() { if(SignerInfo.compareWithDefault("sid", this.sid)) throw new Error("Incorrectly initialized \"SignerInfo\" class"); const _object = { version: this.version }; if(!(this.sid instanceof asn1js.Any)) _object.sid = this.sid.toJSON(); _object.digestAlgorithm = this.digestAlgorithm.toJSON(); if(SignerInfo.compareWithDefault("signedAttrs", this.signedAttrs) === false) _object.signedAttrs = this.signedAttrs.toJSON(); _object.signatureAlgorithm = this.signatureAlgorithm.toJSON(); _object.signature = this.signature.toJSON(); if(SignerInfo.compareWithDefault("unsignedAttrs", this.unsignedAttrs) === false) _object.unsignedAttrs = this.unsignedAttrs.toJSON(); return _object; } //********************************************************************************** } //**************************************************************************************