From 2a9dddb84e77ed0a92b3e7fcbc4fbdca5e1af5d1 Mon Sep 17 00:00:00 2001 From: microshine Date: Tue, 4 Apr 2023 22:25:05 +0200 Subject: [PATCH] fix: support CKM_RSA_X_509 for RSA-OAEP --- src/mechs/rsa/rsa-oaep.ts | 238 +++++++++++++++++++++++++++++++++++++- src/mechs/sha/crypto.ts | 23 ++++ test/vectors.ts | 170 +++++++++++---------------- 3 files changed, 327 insertions(+), 104 deletions(-) diff --git a/src/mechs/rsa/rsa-oaep.ts b/src/mechs/rsa/rsa-oaep.ts index dbd3ace..d4d9d93 100644 --- a/src/mechs/rsa/rsa-oaep.ts +++ b/src/mechs/rsa/rsa-oaep.ts @@ -1,9 +1,11 @@ +import * as crypto from "crypto"; import * as graphene from "graphene-pk11"; import * as core from "webcrypto-core"; import { CryptoKey } from "../../key"; import * as types from "../../types"; import { alwaysAuthenticate } from "../../utils"; +import { ShaCrypto } from "../sha/crypto"; import { RsaCrypto } from "./crypto"; import { RsaCryptoKey } from "./key"; @@ -31,23 +33,137 @@ export class RsaOaepProvider extends core.RsaOaepProvider implements types.ICont return key; } + /** + * Checks if the token supports the RSA_PKCS mechanism. + * @returns True if the RSA_PKCS mechanism is supported, false otherwise. + */ + protected hasRsaPkcsMechanism(): boolean { + const mechanisms = this.container.session.slot.getMechanisms(); + + for (let i = 0; i < mechanisms.length; i++) { + const mechanism = mechanisms.tryGetItem(i); + if (mechanism && (mechanism.type === graphene.MechanismEnum.RSA_X_509)) { + return true; + } + } + + return false; + } + public async onEncrypt(algorithm: RsaOaepParams, key: RsaCryptoKey, data: ArrayBuffer): Promise { + if (this.hasRsaPkcsMechanism()) { + return this.onEncryptRsaX509(algorithm, key, data); + } + + return this.onEncryptRsaOAEP(algorithm, key, data); + } + + /** + * Performs RSA-OAEP encryption with the specified algorithm, key, and data. + * @param algorithm The algorithm to use for encryption. + * @param key The key to use for encryption. + * @param data The data to encrypt. + * @returns A Promise that resolves to the encrypted data as an ArrayBuffer. + */ + protected async onEncryptRsaOAEP(algorithm: RsaOaepParams, key: RsaCryptoKey, data: ArrayBuffer): Promise { return new Promise((resolve, reject) => { + // Convert data to a Buffer const buf = Buffer.from(data); + + // Get the RSA-OAEP mechanism for the specified algorithm and key algorithm const mechanism = this.wc2pk11(algorithm, key.algorithm); + + // Create a context buffer for the cipher const context = Buffer.alloc((key.algorithm).modulusLength >> 3); - this.container.session.createCipher(mechanism, key.key) + + // Create a cipher using the RSA-OAEP mechanism and the key + this.container.session.createCipher(mechanism, key.key).once(buf, context, (err, data2) => { + if (err) { + reject(err); + } else { + // Convert the encrypted data to an ArrayBuffer and resolve the Promise + resolve(new Uint8Array(data2).buffer); + } + }); + }); + } + + + /** + * Performs RSA-OAEP encryption with the specified algorithm, key, and data. + * @param algorithm The algorithm to use for encryption. + * @param key The key to use for encryption. + * @param data The data to encrypt. + * @returns A Promise that resolves to the encrypted data as an ArrayBuffer. + * @throws An error if the data is too large to encrypt with the given key. + */ + protected async onEncryptRsaX509(algorithm: RsaOaepParams, key: RsaCryptoKey, data: ArrayBuffer): Promise { + // Convert data to a Uint8Array + const dataView = new Uint8Array(data); + + // Calculate key, hash, and data sizes + const keySize = Math.ceil(key.algorithm.modulusLength >> 3); + const hashSize = ShaCrypto.size(key.algorithm.hash) >> 3; + const dataLength = dataView.byteLength; + const psLength = keySize - dataLength - 2 * hashSize - 2; + + // Check if data is too large for the key + if (dataLength > keySize - 2 * hashSize - 2) { + throw new Error("Data too large"); + } + + // Create message array + const message = new Uint8Array(keySize); + + // Generate random seed + const seed = message.subarray(1, hashSize + 1); + crypto.randomFillSync(seed); + + // Create data block + const dataBlock = message.subarray(hashSize + 1); + const labelHash = crypto.createHash(key.algorithm.hash.name.replace("-", "")) + .update(core.BufferSourceConverter.toUint8Array(algorithm.label || new Uint8Array(0))) + .digest(); + dataBlock.set(labelHash, 0); + dataBlock[hashSize + psLength] = 1; + dataBlock.set(dataView, hashSize + psLength + 1); + + // Apply data block mask + const dataBlockMask = this.mgf1(key.algorithm.hash, seed, dataBlock.length); + for (let i = 0; i < dataBlock.length; i++) { + dataBlock[i] ^= dataBlockMask[i]; + } + + // Apply seed mask + const seedMask = this.mgf1(key.algorithm.hash, dataBlock, seed.length); + for (let i = 0; i < seed.length; i++) { + seed[i] ^= seedMask[i]; + } + + // Encrypt the data using the key and RSA_PKCS cipher + return new Promise((resolve, reject) => { + const buf = Buffer.from(message); + const context = Buffer.alloc((key.algorithm).modulusLength >> 3); + this.container.session.createCipher(graphene.MechanismEnum.RSA_X_509, key.key) .once(buf, context, (err, data2) => { if (err) { reject(err); } else { - resolve(new Uint8Array(data2).buffer); + resolve(core.BufferSourceConverter.toArrayBuffer(data2)); } }); }); } public async onDecrypt(algorithm: RsaOaepParams, key: RsaCryptoKey, data: ArrayBuffer): Promise { + if (this.hasRsaPkcsMechanism()) { + return this.onDecryptRsaX509(algorithm, key, data); + } + + return this.onDecryptRsaOAEP(algorithm, key, data); + } + + protected async onDecryptRsaOAEP(algorithm: RsaOaepParams, key: RsaCryptoKey, data: ArrayBuffer): Promise { const buf = Buffer.from(data); const mechanism = this.wc2pk11(algorithm, key.algorithm); const context = Buffer.alloc((key.algorithm).modulusLength >> 3); @@ -64,6 +180,7 @@ export class RsaOaepProvider extends core.RsaOaepProvider implements types.ICont } throw e; } + return new Promise((resolve, reject) => { decipher.once(buf, context, (err, data2) => { if (err) { @@ -75,6 +192,90 @@ export class RsaOaepProvider extends core.RsaOaepProvider implements types.ICont }); } + /** + * Performs RSA-OAEP decryption with the specified algorithm, key, and data. + * @param algorithm The algorithm to use for decryption. + * @param key The key to use for decryption. + * @param data The data to decrypt. + * @returns A Promise that resolves to the decrypted data as an ArrayBuffer. + * @throws An error if the data is too large to decrypt with the given key. + */ + protected async onDecryptRsaX509(algorithm: RsaOaepParams, key: RsaCryptoKey, data: ArrayBuffer): Promise { + // Calculate key, hash, and data sizes + const keySize = Math.ceil(key.algorithm.modulusLength >> 3); + const hashSize = ShaCrypto.size(key.algorithm.hash) >> 3; + + // Check if data is too large for the key + if (data.byteLength > keySize || keySize < 2 * hashSize + 2) { + throw new Error("Data too large"); + } + + + // Decrypt the data using the key and RSA_PKCS cipher + const buf = Buffer.from(data); + const context = Buffer.alloc((key.algorithm).modulusLength >> 3); + const decipher = this.container.session.createDecipher(graphene.MechanismEnum.RSA_X_509, key.key); + + try { + await alwaysAuthenticate(key, this.container); + } catch (e) { + try { + // call C_SignFinal to close the active state + decipher.once(buf, context); + } catch { + // nothing + } + throw e; + } + + const pkcs0 = await new Promise((resolve, reject) => { + decipher.once(buf, context, (err, data2) => { + if (err) { + reject(err); + } else { + resolve(data2); + } + }); + }); + + const z = pkcs0[0]; + const seed = pkcs0.subarray(1, hashSize + 1); + const dataBlock = pkcs0.subarray(hashSize + 1); + + if (z !== 0) { + throw new Error("Decryption failed"); + } + + const seedMask = this.mgf1(key.algorithm.hash, dataBlock, seed.length); + for (let i = 0; i < seed.length; i++) { + seed[i] ^= seedMask[i]; + } + + const dataBlockMask = this.mgf1(key.algorithm.hash, seed, dataBlock.length); + for (let i = 0; i < dataBlock.length; i++) { + dataBlock[i] ^= dataBlockMask[i]; + } + + const labelHash = crypto.createHash(key.algorithm.hash.name.replace("-", "")) + .update(core.BufferSourceConverter.toUint8Array(algorithm.label || new Uint8Array(0))) + .digest(); + const expectedLabelHash = dataBlock.subarray(0, hashSize); + if (!core.BufferSourceConverter.isEqual(expectedLabelHash, labelHash)) { + throw new Error("Label hash mismatch"); + } + + // Remove padding and return the decrypted data + let index = hashSize + 1; + while (dataBlock[index] === 0) { + index++; + } + if (dataBlock[index++] !== 1) { + throw new Error("Invalid padding"); + } + + return core.BufferSourceConverter.toArrayBuffer(dataBlock.subarray(index)); + } + public async onExportKey(format: KeyFormat, key: RsaCryptoKey): Promise { return this.crypto.exportKey(format, key); } @@ -117,4 +318,37 @@ export class RsaOaepProvider extends core.RsaOaepProvider implements types.ICont return res; } + /** + * RSA MGF1 + * @param algorithm Hash algorithm + * @param seed Seed + * @param length Length of mask + */ + protected mgf1(algorithm: Algorithm, seed: Uint8Array, length = 0): Uint8Array { + const hashSize = ShaCrypto.size(algorithm) >> 3; + const mask = new Uint8Array(length); + const counter = new Uint8Array(4); + const chunks = Math.ceil(length / hashSize); + for (let i = 0; i < chunks; i++) { + counter[0] = i >>> 24; + counter[1] = (i >>> 16) & 255; + counter[2] = (i >>> 8) & 255; + counter[3] = i & 255; + + const subMask = mask.subarray(i * hashSize); + + let chunk = crypto.createHash(algorithm.name.replace("-", "")) + .update(seed) + .update(counter) + .digest() as Uint8Array; + if (chunk.length > subMask.length) { + chunk = chunk.subarray(0, subMask.length); + } + + subMask.set(chunk); + } + + return mask; + } + } diff --git a/src/mechs/sha/crypto.ts b/src/mechs/sha/crypto.ts index f116756..5187d8e 100644 --- a/src/mechs/sha/crypto.ts +++ b/src/mechs/sha/crypto.ts @@ -4,6 +4,29 @@ import * as types from "../../types"; export class ShaCrypto implements types.IContainer { + /** + * Returns size of the hash algorithm in bits + * @param algorithm Hash algorithm + * @throws Throws Error if an unrecognized name + */ + public static size(algorithm: Algorithm): number { + switch (algorithm.name.toUpperCase()) { + case "SHA-1": + return 160; + case "SHA-256": + case "SHA3-256": + return 256; + case "SHA-384": + case "SHA3-384": + return 384; + case "SHA-512": + case "SHA3-512": + return 512; + default: + throw new Error("Unrecognized name"); + } + } + public constructor(public container: types.ISessionContainer) { } public async digest(algorithm: Algorithm, data: ArrayBuffer): Promise { diff --git a/test/vectors.ts b/test/vectors.ts index f95c320..f7ba8d1 100644 --- a/test/vectors.ts +++ b/test/vectors.ts @@ -1,8 +1,12 @@ +import * as assert from "node:assert"; import { WebcryptoTest, vectors } from "@peculiar/webcrypto-test"; import { ITestImportAction } from "@peculiar/webcrypto-test/build/types/types"; +import * as graphene from "graphene-pk11"; +import { Convert } from "pvtsutils"; + import { Crypto } from "../src"; import * as config from "./config"; -import { isNSS, isSoftHSM } from "./helper"; +import { isNSS } from "./helper"; function fixEcImport(item: ITestImportAction): void { if (item.name?.startsWith("JWK private key")) { @@ -39,7 +43,6 @@ WebcryptoTest.check(config.crypto as Crypto, { AES128KW: true, AES192KW: true, AES256KW: true, - RSAOAEP: true, PBKDF2: true, HKDF: true, DESCBC: true, @@ -53,106 +56,69 @@ WebcryptoTest.check(config.crypto as Crypto, { AES256CTR: true, }); -WebcryptoTest.add(config.crypto as Crypto, { - name: "RSA-OAEP-SHA1", - actions: { - encrypt: [ - { - skip: isNSS("RSA-OAEP-SHA1 throws CKR_DEVICE_ERROR"), - name: "without label", - algorithm: { - name: "RSA-OAEP", - } as Algorithm, - data: new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8]), - encData: Buffer.from("MAKiRseL08AlR8Fmn1uVz/lDDdrDiRyI6KUW3mcE/0kxwW7/VizQJP+jiTSWyHexhQ+Sp0ugm6Doa/jahajuVf0aFkqJCcEKlSeMGvu4QdDc9tJzeNJVqSbPovFy60Criyjei4ganw2RQM2Umav//HfQEyqGTcyftMxXzkDDBQU=", "base64"), - key: { - publicKey: { - format: "jwk", - algorithm: { name: "RSA-OAEP", hash: "SHA-1" } as RsaHashedImportParams, - data: { - alg: "RSA-OAEP", - e: "AQAB", - ext: true, - key_ops: ["encrypt"], - kty: "RSA", - n: "vqpvdxuyZ6rKYnWTj_ZzDBFZAAAlpe5hpoiYHqa2j5kK7v8U5EaPY2bLib9m4B40j-n3FV9xUCGiplWdqMJJKT-4PjGO5E3S4N9kjFhu57noYT7z7302J0sJXeoFbXxlgE-4G55Oxlm52ID2_RJesP5nzcGTriQwoRbrJP5OEt0", - }, - extractable: true, - keyUsages: ["encrypt"], - }, - privateKey: { - format: "jwk", - algorithm: { name: "RSA-OAEP", hash: "SHA-1" } as RsaHashedImportParams, - data: { - alg: "RSA-OAEP", - d: "AkeIWJywp9OfYsj0ECsKmhDVBw55ZL_yU-rbIrashQ_31P6gsc_0I-SVN1rd8Hz79OJ_rTY8ZRBZ4PIyFdPoyvuo5apHdAHH6riJKxDHWPxhE-ReNVEPSTiF1ry8DSe5zC7w9BLnH_QM8bkN4cOnvgqrg7EbrGWomAGJVvoRwOM", - dp: "pOolqL7HwnmWLn7GDX8zGkm0Q1IAj-ouBL7ZZbaTm3wETLtwu-dGsQheEdzP_mfL_CTiCAwGuQBcSItimD0DdQ", - dq: "FTSY59AnkgmB7TsErWNBE3xlVB_pMpE2xWyCBCz96gyDOUOFDz8vlSV-clhjawJeRd1n30nZOPSBtOHozhwZmQ", - e: "AQAB", - ext: true, - key_ops: ["decrypt"], - kty: "RSA", - n: "vqpvdxuyZ6rKYnWTj_ZzDBFZAAAlpe5hpoiYHqa2j5kK7v8U5EaPY2bLib9m4B40j-n3FV9xUCGiplWdqMJJKT-4PjGO5E3S4N9kjFhu57noYT7z7302J0sJXeoFbXxlgE-4G55Oxlm52ID2_RJesP5nzcGTriQwoRbrJP5OEt0", - p: "6jFtmBJJQFIlQUXXZYIgvH70Y9a03oWKjNuF2veb5Zf09EtLNE86NpnIm463OnoHJPW0m8wHFXZZfcYVTIPR_w", - q: "0GttDMl1kIzSV2rNzGXpOS8tUqr5Lz0EtVZwIb9GJPMmJ0P3gZ801zEgZZ4-esU7cLUf-BSZEAmfnKA80G2jIw", - qi: "FByTxX4G2eXkk1xe0IuiEv7I5NS-CnFyp8iB4XLG0rabnfcIZFKpf__X0sNyVOAVo5-jJMuUYjCRTdaXNAWhkg", - }, - extractable: true, - keyUsages: ["decrypt"], - }, - }, - }, - { - skip: isSoftHSM("RSA-OAEP-SHA1 supports encryption without label only") - || isNSS("RSA-OAEP-SHA1 throws CKR_DEVICE_ERROR"), - name: "with label", - algorithm: { - name: "RSA-OAEP", - label: new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8]), - } as RsaOaepParams, - data: new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8]), - encData: Buffer.from("YLtmJDT8Y4Z2Y/VoGHUvhgs5kptNShFRUCcsKpUgI9A+YCYXL3K8fnEkbzO/Nkd4/0RsvfnmXkUJg3JdzPslwO1bOdlNsd2hRi0qi4cpxVmHDjuI3EHMb7FI3Pb9cF/kMFeEQzttpIDqh/UQJnoyh4d/RyZS1w37Vk0sNer7xw0=", "base64"), - key: { - publicKey: { - format: "jwk" as KeyFormat, - algorithm: { name: "RSA-OAEP", hash: "SHA-1" } as RsaHashedImportParams, - data: { - alg: "RSA-OAEP", - e: "AQAB", - ext: true, - key_ops: ["encrypt"], - kty: "RSA", - n: "vqpvdxuyZ6rKYnWTj_ZzDBFZAAAlpe5hpoiYHqa2j5kK7v8U5EaPY2bLib9m4B40j-n3FV9xUCGiplWdqMJJKT-4PjGO5E3S4N9kjFhu57noYT7z7302J0sJXeoFbXxlgE-4G55Oxlm52ID2_RJesP5nzcGTriQwoRbrJP5OEt0", - }, - extractable: true, - keyUsages: ["encrypt"], - }, - privateKey: { - format: "jwk", - algorithm: { name: "RSA-OAEP", hash: "SHA-1" } as RsaHashedImportParams, - data: { - alg: "RSA-OAEP", - d: "AkeIWJywp9OfYsj0ECsKmhDVBw55ZL_yU-rbIrashQ_31P6gsc_0I-SVN1rd8Hz79OJ_rTY8ZRBZ4PIyFdPoyvuo5apHdAHH6riJKxDHWPxhE-ReNVEPSTiF1ry8DSe5zC7w9BLnH_QM8bkN4cOnvgqrg7EbrGWomAGJVvoRwOM", - dp: "pOolqL7HwnmWLn7GDX8zGkm0Q1IAj-ouBL7ZZbaTm3wETLtwu-dGsQheEdzP_mfL_CTiCAwGuQBcSItimD0DdQ", - dq: "FTSY59AnkgmB7TsErWNBE3xlVB_pMpE2xWyCBCz96gyDOUOFDz8vlSV-clhjawJeRd1n30nZOPSBtOHozhwZmQ", - e: "AQAB", - ext: true, - key_ops: ["decrypt"], - kty: "RSA", - n: "vqpvdxuyZ6rKYnWTj_ZzDBFZAAAlpe5hpoiYHqa2j5kK7v8U5EaPY2bLib9m4B40j-n3FV9xUCGiplWdqMJJKT-4PjGO5E3S4N9kjFhu57noYT7z7302J0sJXeoFbXxlgE-4G55Oxlm52ID2_RJesP5nzcGTriQwoRbrJP5OEt0", - p: "6jFtmBJJQFIlQUXXZYIgvH70Y9a03oWKjNuF2veb5Zf09EtLNE86NpnIm463OnoHJPW0m8wHFXZZfcYVTIPR_w", - q: "0GttDMl1kIzSV2rNzGXpOS8tUqr5Lz0EtVZwIb9GJPMmJ0P3gZ801zEgZZ4-esU7cLUf-BSZEAmfnKA80G2jIw", - qi: "FByTxX4G2eXkk1xe0IuiEv7I5NS-CnFyp8iB4XLG0rabnfcIZFKpf__X0sNyVOAVo5-jJMuUYjCRTdaXNAWhkg", - }, - extractable: true, - keyUsages: ["decrypt"], - }, - }, - }, - ], - }, -}); +context("RSA-OAEP", () => { + + const CKM_RSA_OAEP = graphene.MechanismEnum.RSA_X_509; + + before(() => { + // @ts-ignore Change mechanism to skip CKM_RSA_X_509 usage + graphene.MechanismEnum.RSA_X_509 = graphene.MechanismEnum.VENDOR_DEFINED | graphene.MechanismEnum.RSA_X_509; + }); + + after(() => { + // @ts-ignore Restore mechanism + graphene.MechanismEnum.RSA_X_509 = CKM_RSA_OAEP; + }); + + const test = isNSS("RSA-OAEP-SHA1 throws CKR_DEVICE_ERROR") ? it.skip : it; + test("Use standard CKM_RSA_OAEP instead of CKM_RSA_X_509", async () => { + const data = new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8]); + const encData = Buffer.from("MAKiRseL08AlR8Fmn1uVz/lDDdrDiRyI6KUW3mcE/0kxwW7/VizQJP+jiTSWyHexhQ+Sp0ugm6Doa/jahajuVf0aFkqJCcEKlSeMGvu4QdDc9tJzeNJVqSbPovFy60Criyjei4ganw2RQM2Umav//HfQEyqGTcyftMxXzkDDBQU=", "base64"); + + // import keys + const jwkPublicKey = { + alg: "RSA-OAEP", + e: "AQAB", + ext: true, + key_ops: ["encrypt"], + kty: "RSA", + n: "vqpvdxuyZ6rKYnWTj_ZzDBFZAAAlpe5hpoiYHqa2j5kK7v8U5EaPY2bLib9m4B40j-n3FV9xUCGiplWdqMJJKT-4PjGO5E3S4N9kjFhu57noYT7z7302J0sJXeoFbXxlgE-4G55Oxlm52ID2_RJesP5nzcGTriQwoRbrJP5OEt0", + }; + const jwkPrivateKey = { + alg: "RSA-OAEP", + d: "AkeIWJywp9OfYsj0ECsKmhDVBw55ZL_yU-rbIrashQ_31P6gsc_0I-SVN1rd8Hz79OJ_rTY8ZRBZ4PIyFdPoyvuo5apHdAHH6riJKxDHWPxhE-ReNVEPSTiF1ry8DSe5zC7w9BLnH_QM8bkN4cOnvgqrg7EbrGWomAGJVvoRwOM", + dp: "pOolqL7HwnmWLn7GDX8zGkm0Q1IAj-ouBL7ZZbaTm3wETLtwu-dGsQheEdzP_mfL_CTiCAwGuQBcSItimD0DdQ", + dq: "FTSY59AnkgmB7TsErWNBE3xlVB_pMpE2xWyCBCz96gyDOUOFDz8vlSV-clhjawJeRd1n30nZOPSBtOHozhwZmQ", + e: "AQAB", + ext: true, + key_ops: ["decrypt"], + kty: "RSA", + n: "vqpvdxuyZ6rKYnWTj_ZzDBFZAAAlpe5hpoiYHqa2j5kK7v8U5EaPY2bLib9m4B40j-n3FV9xUCGiplWdqMJJKT-4PjGO5E3S4N9kjFhu57noYT7z7302J0sJXeoFbXxlgE-4G55Oxlm52ID2_RJesP5nzcGTriQwoRbrJP5OEt0", + p: "6jFtmBJJQFIlQUXXZYIgvH70Y9a03oWKjNuF2veb5Zf09EtLNE86NpnIm463OnoHJPW0m8wHFXZZfcYVTIPR_w", + q: "0GttDMl1kIzSV2rNzGXpOS8tUqr5Lz0EtVZwIb9GJPMmJ0P3gZ801zEgZZ4-esU7cLUf-BSZEAmfnKA80G2jIw", + qi: "FByTxX4G2eXkk1xe0IuiEv7I5NS-CnFyp8iB4XLG0rabnfcIZFKpf__X0sNyVOAVo5-jJMuUYjCRTdaXNAWhkg", + }; + const alg = { name: "RSA-OAEP", hash: "SHA-1" }; + const keys = { + publicKey: await config.crypto.subtle.importKey("jwk", jwkPublicKey, alg, true, ["encrypt"]), + privateKey: await config.crypto.subtle.importKey("jwk", jwkPrivateKey, alg, true, ["decrypt"]), + }; + const encKey = keys.publicKey; + const decKey = keys.privateKey; + + // encrypt + const enc = await config.crypto.subtle.encrypt(alg, encKey, data); + + // decrypt + let dec = await config.crypto.subtle.decrypt(alg, decKey, enc); + assert.equal(Convert.ToHex(dec), Convert.ToHex(data)); + + dec = await config.crypto.subtle.decrypt(alg, decKey, encData); + assert.equal(Convert.ToHex(dec), Convert.ToHex(data)); + }); + +}); it("custom", async () => { const pem = [ @@ -160,7 +126,7 @@ it("custom", async () => { `-----BEGIN CERTIFICATE-----\nMIIFnDCCBISgAwIBAgIQXiLl2Y8He0o6EDaiiiW53DANBgkqhkiG9w0BAQsFADBs\nMQswCQYDVQQGEwJJVDEYMBYGA1UECgwPQXJ1YmFQRUMgUy5wLkEuMSEwHwYDVQQL\nDBhDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eUMxIDAeBgNVBAMMF0FydWJhUEVDIFMu\ncC5BLiBORyBDQSAzMB4XDTIwMDYwOTAwMDAwMFoXDTIzMDYwOTIzNTk1OVowdzEL\nMAkGA1UEBhMCSVQxFDASBgNVBAMMC0JFUlRJIE1JUktPMR8wHQYDVQQFExZUSU5J\nVC1CUlRNUks3NFAyOEIwMDZUMQ4wDAYDVQQqDAVNSVJLTzEOMAwGA1UEBAwFQkVS\nVEkxETAPBgNVBC4TCDIxNDAzODkxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB\nCgKCAQEAuhcbGseJ6TGP1HW+ys8zuztEEfG/LjGgeg+C5hhE7CmIr2fSKXn5NTfV\nIqR9sRkAoaLt0asZHZPfqlYXqCfpnC35zn5oP9g1gDq0Cp7FDFuW16tdFRFjyaW5\nJMT4O82Cf861OswyYSHos9nAeqhWg/NeARI4aaGTtDH5Jd/ebhMr+n9YXNQsW0uH\nm2Q+YcV5QdiOAA+dHff2Edgcz+cRaryB9ma3EZmKnKQKCmuck1lkxRX64fV4Dc3e\nSHTm6Fcmfbe7OHl2fuvxk5H15cZdxSF9VM1mcaKxarTpR5zLkKxqQJKQ/YAkgeA1\n1pMlejJ45YM/xmAasZUV/STSf6XBbwIDAQABo4ICLTCCAikwDgYDVR0PAQH/BAQD\nAgZAMB0GA1UdDgQWBBTc/tOHIIBJZ+XYUm8PVfBZSu/S6jBPBgNVHSAESDBGMDwG\nCysGAQQBgegtAQEBMC0wKwYIKwYBBQUHAgEWH2h0dHBzOi8vY2EuYXJ1YmFwZWMu\naXQvY3BzLmh0bWwwBgYEK0wQBjBYBgNVHR8EUTBPME2gS6BJhkdodHRwOi8vY3Js\nLmFydWJhcGVjLml0L0FydWJhUEVDU3BBQ2VydGlmaWNhdGlvbkF1dGhvcml0eUMv\nTGF0ZXN0Q1JMLmNybDCBvwYIKwYBBQUHAQMEgbIwga8wCAYGBACORgEBMAsGBgQA\njkYBAwIBFDAIBgYEAI5GAQQwgYsGBgQAjkYBBTCBgDA+FjhodHRwczovL3d3dy5w\nZWMuaXQvcmVwb3NpdG9yeS9hcnViYXBlYy1xdWFsaWYtcGRzLWVuLnBkZhMCZW4w\nPhY4aHR0cHM6Ly93d3cucGVjLml0L3JlcG9zaXRvcnkvYXJ1YmFwZWMtcXVhbGlm\nLXBkcy1pdC5wZGYTAml0MB8GA1UdIwQYMBaAFPDARbG2NbTqXyn6gwNK3C/1s33o\nMGoGCCsGAQUFBwEBBF4wXDA1BggrBgEFBQcwAoYpaHR0cDovL2NhY2VydC5wZWMu\naXQvY2VydHMvQVBfTkdfQ0FfMy5jZXIwIwYIKwYBBQUHMAGGF2h0dHA6Ly9vY3Nw\nLmFydWJhcGVjLml0MA0GCSqGSIb3DQEBCwUAA4IBAQBa1xYtYVvHqIS076oNItYW\n8f/cUZmrtSuzz4DR9vgnh8Nnl4UZOjtNLa6EEWg8N2Qe6CQKRDkNre/Imm+BsLkT\nxzGcVTTUX04ERL9NLe/jave3s+SXrOz8Dcr9CSeswa6Ky+/8WBZeqDi9pJWTL0bD\nwLofLlWNP6iTvezMq/5WBAtOCnVMLvKcMzrFacoDrbBiOummNoJ2u0+/DXgtmmcx\nQNgW1j6lDRWanthAmyXtmqn49T6EWn11q27UbLSDfuQkRTHBgUpRmYtn8vw7PYMo\nC+2O5/OOiDrW2MGwLYZ7zwJA7r4TdLD0AltBl6h6HnDPMu23xngj2wi3pda2bC7I\n-----END CERTIFICATE-----`, ]; for (const item of pem) { - const cert = await config.crypto.certStorage.importCert("pem", item, {name: "RSASSA-PKCS1-v1_5", hash: "SHA-256"}, ["verify"]); + const cert = await config.crypto.certStorage.importCert("pem", item, { name: "RSASSA-PKCS1-v1_5", hash: "SHA-256" }, ["verify"]); await config.crypto.certStorage.setItem(cert); } });