Source code

Revision control

Copy as Markdown

Other Tools

import * as asn1js from "asn1js";
import * as pvutils from "pvutils";
import { AlgorithmIdentifier, AlgorithmIdentifierJson, AlgorithmIdentifierSchema } from "./AlgorithmIdentifier";
import { EncryptedData, EncryptedDataEncryptParams } from "./EncryptedData";
import { EncryptedContentInfo } from "./EncryptedContentInfo";
import { PrivateKeyInfo } from "./PrivateKeyInfo";
import * as Schema from "./Schema";
import { AsnError } from "./errors";
import { PkiObject, PkiObjectParameters } from "./PkiObject";
import { EMPTY_STRING } from "./constants";
import * as common from "./common";
const ENCRYPTION_ALGORITHM = "encryptionAlgorithm";
const ENCRYPTED_DATA = "encryptedData";
const PARSED_VALUE = "parsedValue";
const CLEAR_PROPS = [
ENCRYPTION_ALGORITHM,
ENCRYPTED_DATA,
];
export interface IPKCS8ShroudedKeyBag {
encryptionAlgorithm: AlgorithmIdentifier;
encryptedData: asn1js.OctetString;
parsedValue?: PrivateKeyInfo;
}
export type PKCS8ShroudedKeyBagParameters = PkiObjectParameters & Partial<IPKCS8ShroudedKeyBag>;
export interface PKCS8ShroudedKeyBagJson {
encryptionAlgorithm: AlgorithmIdentifierJson;
encryptedData: asn1js.OctetStringJson;
}
type PKCS8ShroudedKeyBagMakeInternalValuesParams = Omit<EncryptedDataEncryptParams, "contentToEncrypt">;
/**
* Represents the PKCS8ShroudedKeyBag structure described in [RFC7292](https://datatracker.ietf.org/doc/html/rfc7292)
*/
export class PKCS8ShroudedKeyBag extends PkiObject implements IPKCS8ShroudedKeyBag {
public static override CLASS_NAME = "PKCS8ShroudedKeyBag";
public encryptionAlgorithm!: AlgorithmIdentifier;
public encryptedData!: asn1js.OctetString;
public parsedValue?: PrivateKeyInfo;
/**
* Initializes a new instance of the {@link PKCS8ShroudedKeyBag} class
* @param parameters Initialization parameters
*/
constructor(parameters: PKCS8ShroudedKeyBagParameters = {}) {
super();
this.encryptionAlgorithm = pvutils.getParametersValue(parameters, ENCRYPTION_ALGORITHM, PKCS8ShroudedKeyBag.defaultValues(ENCRYPTION_ALGORITHM));
this.encryptedData = pvutils.getParametersValue(parameters, ENCRYPTED_DATA, PKCS8ShroudedKeyBag.defaultValues(ENCRYPTED_DATA));
if (PARSED_VALUE in parameters) {
this.parsedValue = pvutils.getParametersValue(parameters, PARSED_VALUE, PKCS8ShroudedKeyBag.defaultValues(PARSED_VALUE));
}
if (parameters.schema) {
this.fromSchema(parameters.schema);
}
}
/**
* Returns default values for all class members
* @param memberName String name for a class member
* @returns Default value
*/
public static override defaultValues(memberName: typeof ENCRYPTION_ALGORITHM): AlgorithmIdentifier;
public static override defaultValues(memberName: typeof ENCRYPTED_DATA): asn1js.OctetString;
public static override defaultValues(memberName: typeof PARSED_VALUE): PrivateKeyInfo;
public static override defaultValues(memberName: string): any {
switch (memberName) {
case ENCRYPTION_ALGORITHM:
return (new AlgorithmIdentifier());
case ENCRYPTED_DATA:
return (new asn1js.OctetString());
case PARSED_VALUE:
return {};
default:
return super.defaultValues(memberName);
}
}
/**
* Compare values with default values for all class members
* @param memberName String name for a class member
* @param memberValue Value to compare with default value
*/
public static compareWithDefault(memberName: string, memberValue: any): boolean {
switch (memberName) {
case ENCRYPTION_ALGORITHM:
return ((AlgorithmIdentifier.compareWithDefault("algorithmId", memberValue.algorithmId)) &&
(("algorithmParams" in memberValue) === false));
case ENCRYPTED_DATA:
return (memberValue.isEqual(PKCS8ShroudedKeyBag.defaultValues(memberName)));
case PARSED_VALUE:
return ((memberValue instanceof Object) && (Object.keys(memberValue).length === 0));
default:
return super.defaultValues(memberName);
}
}
/**
* @inheritdoc
* @asn ASN.1 schema
* ```asn
* PKCS8ShroudedKeyBag ::= EncryptedPrivateKeyInfo
*
* EncryptedPrivateKeyInfo ::= SEQUENCE {
* encryptionAlgorithm AlgorithmIdentifier {{KeyEncryptionAlgorithms}},
* encryptedData EncryptedData
* }
*
* EncryptedData ::= OCTET STRING
*```
*/
public static override schema(parameters: Schema.SchemaParameters<{
encryptionAlgorithm?: AlgorithmIdentifierSchema;
encryptedData?: string;
}> = {}): Schema.SchemaType {
/**
* @type {Object}
* @property {string} [blockName]
* @property {string} [encryptionAlgorithm]
* @property {string} [encryptedData]
*/
const names = pvutils.getParametersValue<NonNullable<typeof parameters.names>>(parameters, "names", {});
return (new asn1js.Sequence({
name: (names.blockName || EMPTY_STRING),
value: [
AlgorithmIdentifier.schema(names.encryptionAlgorithm || {
names: {
blockName: ENCRYPTION_ALGORITHM
}
}),
new asn1js.Choice({
value: [
new asn1js.OctetString({ name: (names.encryptedData || ENCRYPTED_DATA) }),
new asn1js.OctetString({
idBlock: {
isConstructed: true
},
name: (names.encryptedData || ENCRYPTED_DATA)
})
]
})
]
}));
}
public fromSchema(schema: Schema.SchemaType): void {
// Clear input data first
pvutils.clearProps(schema, CLEAR_PROPS);
// Check the schema is valid
const asn1 = asn1js.compareSchema(schema,
schema,
PKCS8ShroudedKeyBag.schema({
names: {
encryptionAlgorithm: {
names: {
blockName: ENCRYPTION_ALGORITHM
}
},
encryptedData: ENCRYPTED_DATA
}
})
);
AsnError.assertSchema(asn1, this.className);
// Get internal properties from parsed schema
this.encryptionAlgorithm = new AlgorithmIdentifier({ schema: asn1.result.encryptionAlgorithm });
this.encryptedData = asn1.result.encryptedData;
}
public toSchema(): asn1js.Sequence {
return (new asn1js.Sequence({
value: [
this.encryptionAlgorithm.toSchema(),
this.encryptedData
]
}));
}
public toJSON(): PKCS8ShroudedKeyBagJson {
return {
encryptionAlgorithm: this.encryptionAlgorithm.toJSON(),
encryptedData: this.encryptedData.toJSON(),
};
}
protected async parseInternalValues(parameters: {
password: ArrayBuffer;
}, crypto = common.getCrypto(true)) {
//#region Initial variables
const cmsEncrypted = new EncryptedData({
encryptedContentInfo: new EncryptedContentInfo({
contentEncryptionAlgorithm: this.encryptionAlgorithm,
encryptedContent: this.encryptedData
})
});
//#endregion
//#region Decrypt internal data
const decryptedData = await cmsEncrypted.decrypt(parameters, crypto);
//#endregion
//#region Initialize PARSED_VALUE with decrypted PKCS#8 private key
this.parsedValue = PrivateKeyInfo.fromBER(decryptedData);
//#endregion
}
public async makeInternalValues(parameters: PKCS8ShroudedKeyBagMakeInternalValuesParams): Promise<void> {
//#region Check that we do have PARSED_VALUE
if (!this.parsedValue) {
throw new Error("Please initialize \"parsedValue\" first");
}
//#endregion
//#region Initial variables
const cmsEncrypted = new EncryptedData();
//#endregion
//#region Encrypt internal data
const encryptParams: EncryptedDataEncryptParams = {
...parameters,
contentToEncrypt: this.parsedValue.toSchema().toBER(false),
};
await cmsEncrypted.encrypt(encryptParams);
if (!cmsEncrypted.encryptedContentInfo.encryptedContent) {
throw new Error("The filed `encryptedContent` in EncryptedContentInfo is empty");
}
//#endregion
//#region Initialize internal values
this.encryptionAlgorithm = cmsEncrypted.encryptedContentInfo.contentEncryptionAlgorithm;
this.encryptedData = cmsEncrypted.encryptedContentInfo.encryptedContent;
//#endregion
}
}