forked from mirrors/gecko-dev
248 lines
8 KiB
TypeScript
248 lines
8 KiB
TypeScript
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
|
|
}
|
|
|
|
}
|