summaryrefslogtreecommitdiffstats
path: root/third_party/js/PKI.js/src/EncryptedData.ts
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/js/PKI.js/src/EncryptedData.ts')
-rw-r--r--third_party/js/PKI.js/src/EncryptedData.ts303
1 files changed, 303 insertions, 0 deletions
diff --git a/third_party/js/PKI.js/src/EncryptedData.ts b/third_party/js/PKI.js/src/EncryptedData.ts
new file mode 100644
index 0000000000..72076b8db3
--- /dev/null
+++ b/third_party/js/PKI.js/src/EncryptedData.ts
@@ -0,0 +1,303 @@
+import * as asn1js from "asn1js";
+import * as pvutils from "pvutils";
+import * as common from "./common";
+import { EncryptedContentInfo, EncryptedContentInfoJson, EncryptedContentInfoSchema } from "./EncryptedContentInfo";
+import { Attribute, AttributeJson } from "./Attribute";
+import * as Schema from "./Schema";
+import { ArgumentError, AsnError } from "./errors";
+import { CryptoEngineEncryptParams } from "./CryptoEngine/CryptoEngineInterface";
+import { PkiObject, PkiObjectParameters } from "./PkiObject";
+import { EMPTY_STRING } from "./constants";
+
+const VERSION = "version";
+const ENCRYPTED_CONTENT_INFO = "encryptedContentInfo";
+const UNPROTECTED_ATTRS = "unprotectedAttrs";
+const CLEAR_PROPS = [
+ VERSION,
+ ENCRYPTED_CONTENT_INFO,
+ UNPROTECTED_ATTRS,
+];
+
+export interface IEncryptedData {
+ /**
+ * Version number.
+ *
+ * If `unprotectedAttrs` is present, then the version MUST be 2. If `unprotectedAttrs` is absent, then version MUST be 0.
+ */
+ version: number;
+ /**
+ * Encrypted content information
+ */
+ encryptedContentInfo: EncryptedContentInfo;
+ /**
+ * Collection of attributes that are not encrypted
+ */
+ unprotectedAttrs?: Attribute[];
+}
+
+export interface EncryptedDataJson {
+ version: number;
+ encryptedContentInfo: EncryptedContentInfoJson;
+ unprotectedAttrs?: AttributeJson[];
+}
+
+export type EncryptedDataParameters = PkiObjectParameters & Partial<IEncryptedData>;
+
+export type EncryptedDataEncryptParams = Omit<CryptoEngineEncryptParams, "contentType">;
+
+/**
+ * Represents the EncryptedData structure described in [RFC5652](https://datatracker.ietf.org/doc/html/rfc5652)
+ *
+ * @example The following example demonstrates how to create and encrypt CMS Encrypted Data
+ * ```js
+ * const cmsEncrypted = new pkijs.EncryptedData();
+ *
+ * await cmsEncrypted.encrypt({
+ * contentEncryptionAlgorithm: {
+ * name: "AES-GCM",
+ * length: 256,
+ * },
+ * hmacHashAlgorithm: "SHA-256",
+ * iterationCount: 1000,
+ * password: password,
+ * contentToEncrypt: dataToEncrypt,
+ * });
+ *
+ * // Add Encrypted Data into CMS Content Info
+ * const cmsContent = new pkijs.ContentInfo();
+ * cmsContent.contentType = pkijs.ContentInfo.ENCRYPTED_DATA;
+ * cmsContent.content = cmsEncrypted.toSchema();
+ *
+ * const cmsContentRaw = cmsContent.toSchema().toBER();
+ * ```
+ *
+ * @example The following example demonstrates how to decrypt CMS Encrypted Data
+ * ```js
+ * // Parse CMS Content Info
+ * const cmsContent = pkijs.ContentInfo.fromBER(cmsContentRaw);
+ * if (cmsContent.contentType !== pkijs.ContentInfo.ENCRYPTED_DATA) {
+ * throw new Error("CMS is not Encrypted Data");
+ * }
+ * // Parse CMS Encrypted Data
+ * const cmsEncrypted = new pkijs.EncryptedData({ schema: cmsContent.content });
+ *
+ * // Decrypt data
+ * const decryptedData = await cmsEncrypted.decrypt({
+ * password: password,
+ * });
+ * ```
+ */
+export class EncryptedData extends PkiObject implements IEncryptedData {
+
+ public static override CLASS_NAME = "EncryptedData";
+
+ public version!: number;
+ public encryptedContentInfo!: EncryptedContentInfo;
+ public unprotectedAttrs?: Attribute[];
+
+ /**
+ * Initializes a new instance of the {@link EncryptedData} class
+ * @param parameters Initialization parameters
+ */
+ constructor(parameters: EncryptedDataParameters = {}) {
+ super();
+
+ this.version = pvutils.getParametersValue(parameters, VERSION, EncryptedData.defaultValues(VERSION));
+ this.encryptedContentInfo = pvutils.getParametersValue(parameters, ENCRYPTED_CONTENT_INFO, EncryptedData.defaultValues(ENCRYPTED_CONTENT_INFO));
+ if (UNPROTECTED_ATTRS in parameters) {
+ this.unprotectedAttrs = pvutils.getParametersValue(parameters, UNPROTECTED_ATTRS, EncryptedData.defaultValues(UNPROTECTED_ATTRS));
+ }
+
+ 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 VERSION): number;
+ public static override defaultValues(memberName: typeof ENCRYPTED_CONTENT_INFO): EncryptedContentInfo;
+ public static override defaultValues(memberName: typeof UNPROTECTED_ATTRS): Attribute[];
+ public static override defaultValues(memberName: string): any {
+ switch (memberName) {
+ case VERSION:
+ return 0;
+ case ENCRYPTED_CONTENT_INFO:
+ return new EncryptedContentInfo();
+ case UNPROTECTED_ATTRS:
+ 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 VERSION:
+ return (memberValue === 0);
+ case ENCRYPTED_CONTENT_INFO:
+ // TODO move to isEmpty method
+ return ((EncryptedContentInfo.compareWithDefault("contentType", memberValue.contentType)) &&
+ (EncryptedContentInfo.compareWithDefault("contentEncryptionAlgorithm", memberValue.contentEncryptionAlgorithm)) &&
+ (EncryptedContentInfo.compareWithDefault("encryptedContent", memberValue.encryptedContent)));
+ case UNPROTECTED_ATTRS:
+ return (memberValue.length === 0);
+ default:
+ return super.defaultValues(memberName);
+ }
+ }
+
+ /**
+ * @inheritdoc
+ * @asn ASN.1 schema
+ * ```asn
+ * EncryptedData ::= SEQUENCE {
+ * version CMSVersion,
+ * encryptedContentInfo EncryptedContentInfo,
+ * unprotectedAttrs [1] IMPLICIT UnprotectedAttributes OPTIONAL }
+ *```
+ */
+ public static override schema(parameters: Schema.SchemaParameters<{
+ version?: string;
+ encryptedContentInfo?: EncryptedContentInfoSchema;
+ unprotectedAttrs?: string;
+ }> = {}): Schema.SchemaType {
+ const names = pvutils.getParametersValue<NonNullable<typeof parameters.names>>(parameters, "names", {});
+
+ return (new asn1js.Sequence({
+ name: (names.blockName || EMPTY_STRING),
+ value: [
+ new asn1js.Integer({ name: (names.version || EMPTY_STRING) }),
+ EncryptedContentInfo.schema(names.encryptedContentInfo || {}),
+ new asn1js.Constructed({
+ optional: true,
+ idBlock: {
+ tagClass: 3, // CONTEXT-SPECIFIC
+ tagNumber: 1 // [1]
+ },
+ value: [
+ new asn1js.Repeated({
+ name: (names.unprotectedAttrs || EMPTY_STRING),
+ value: Attribute.schema()
+ })
+ ]
+ })
+ ]
+ }));
+ }
+
+ 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,
+ EncryptedData.schema({
+ names: {
+ version: VERSION,
+ encryptedContentInfo: {
+ names: {
+ blockName: ENCRYPTED_CONTENT_INFO
+ }
+ },
+ unprotectedAttrs: UNPROTECTED_ATTRS
+ }
+ })
+ );
+ AsnError.assertSchema(asn1, this.className);
+
+ // Get internal properties from parsed schema
+ this.version = asn1.result.version.valueBlock.valueDec;
+ this.encryptedContentInfo = new EncryptedContentInfo({ schema: asn1.result.encryptedContentInfo });
+ if (UNPROTECTED_ATTRS in asn1.result)
+ this.unprotectedAttrs = Array.from(asn1.result.unprotectedAttrs, element => new Attribute({ schema: element }));
+ }
+
+ public toSchema(): asn1js.Sequence {
+ //#region Create array for output sequence
+ const outputArray = [];
+
+ outputArray.push(new asn1js.Integer({ value: this.version }));
+ outputArray.push(this.encryptedContentInfo.toSchema());
+
+ if (this.unprotectedAttrs) {
+ outputArray.push(new asn1js.Constructed({
+ optional: true,
+ idBlock: {
+ tagClass: 3, // CONTEXT-SPECIFIC
+ tagNumber: 1 // [1]
+ },
+ value: Array.from(this.unprotectedAttrs, o => o.toSchema())
+ }));
+ }
+ //#endregion
+
+ //#region Construct and return new ASN.1 schema for this object
+ return (new asn1js.Sequence({
+ value: outputArray
+ }));
+ //#endregion
+ }
+
+ public toJSON(): EncryptedDataJson {
+ const res: EncryptedDataJson = {
+ version: this.version,
+ encryptedContentInfo: this.encryptedContentInfo.toJSON()
+ };
+
+ if (this.unprotectedAttrs)
+ res.unprotectedAttrs = Array.from(this.unprotectedAttrs, o => o.toJSON());
+
+ return res;
+ }
+
+ /**
+ * Creates a new CMS Encrypted Data content
+ * @param parameters Parameters necessary for encryption
+ */
+ public async encrypt(parameters: EncryptedDataEncryptParams): Promise<void> {
+ //#region Check for input parameters
+ ArgumentError.assert(parameters, "parameters", "object");
+ //#endregion
+
+ //#region Set "contentType" parameter
+ const encryptParams: CryptoEngineEncryptParams = {
+ ...parameters,
+ contentType: "1.2.840.113549.1.7.1",
+ };
+ //#endregion
+
+ this.encryptedContentInfo = await common.getCrypto(true).encryptEncryptedContentInfo(encryptParams);
+ }
+
+ /**
+ * Creates a new CMS Encrypted Data content
+ * @param parameters Parameters necessary for encryption
+ * @param crypto Crypto engine
+ * @returns Returns decrypted raw data
+ */
+ async decrypt(parameters: {
+ password: ArrayBuffer;
+ }, crypto = common.getCrypto(true)): Promise<ArrayBuffer> {
+ // Check for input parameters
+ ArgumentError.assert(parameters, "parameters", "object");
+
+ // Set ENCRYPTED_CONTENT_INFO value
+ const decryptParams = {
+ ...parameters,
+ encryptedContentInfo: this.encryptedContentInfo,
+ };
+
+ return crypto.decryptEncryptedContentInfo(decryptParams);
+ }
+
+}