From ee447a186ae59e80055bb45b586c7dc2140d9366 Mon Sep 17 00:00:00 2001 From: Yohann THEPAUT Date: Sun, 28 Apr 2024 00:48:39 +0200 Subject: [PATCH] build: fix build to use WebCryptoAPI and mock for tests --- build/package-lock.json | 1 + package-lock.json | 91 +++++++++++++++++++++++++++++++++++ package.json | 1 + setup.jest.ts | 10 ++++ src/operations/hmac.ts | 5 +- src/operations/kdf.ts | 5 +- src/operations/rsa.ts | 21 +++++--- src/operations/sha2.ts | 15 +++--- tests/operations/sha2.spec.ts | 2 +- 9 files changed, 127 insertions(+), 24 deletions(-) diff --git a/build/package-lock.json b/build/package-lock.json index 978d3f0..d792336 100644 --- a/build/package-lock.json +++ b/build/package-lock.json @@ -24,6 +24,7 @@ "devDependencies": { "@commitlint/cli": "^19.3.0", "@commitlint/config-conventional": "^19.2.2", + "@peculiar/webcrypto": "^1.4.6", "@types/jest": "^29.5.12", "@typescript-eslint/eslint-plugin": "^7.7.1", "@typescript-eslint/parser": "^7.7.1", diff --git a/package-lock.json b/package-lock.json index 15ba60c..18dff63 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,6 +15,7 @@ "devDependencies": { "@commitlint/cli": "^19.3.0", "@commitlint/config-conventional": "^19.2.2", + "@peculiar/webcrypto": "^1.4.6", "@types/jest": "^29.5.12", "@typescript-eslint/eslint-plugin": "^7.7.1", "@typescript-eslint/parser": "^7.7.1", @@ -1746,6 +1747,45 @@ "node": ">= 8" } }, + "node_modules/@peculiar/asn1-schema": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/@peculiar/asn1-schema/-/asn1-schema-2.3.8.tgz", + "integrity": "sha512-ULB1XqHKx1WBU/tTFIA+uARuRoBVZ4pNdOA878RDrRbBfBGcSzi5HBkdScC6ZbHn8z7L8gmKCgPC1LHRrP46tA==", + "dev": true, + "dependencies": { + "asn1js": "^3.0.5", + "pvtsutils": "^1.3.5", + "tslib": "^2.6.2" + } + }, + "node_modules/@peculiar/json-schema": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/@peculiar/json-schema/-/json-schema-1.1.12.tgz", + "integrity": "sha512-coUfuoMeIB7B8/NMekxaDzLhaYmp0HZNPEjYRm9goRou8UZIC3z21s0sL9AWoCw4EG876QyO3kYrc61WNF9B/w==", + "dev": true, + "dependencies": { + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@peculiar/webcrypto": { + "version": "1.4.6", + "resolved": "https://registry.npmjs.org/@peculiar/webcrypto/-/webcrypto-1.4.6.tgz", + "integrity": "sha512-YBcMfqNSwn3SujUJvAaySy5tlYbYm6tVt9SKoXu8BaTdKGROiJDgPR3TXpZdAKUfklzm3lRapJEAltiMQtBgZg==", + "dev": true, + "dependencies": { + "@peculiar/asn1-schema": "^2.3.8", + "@peculiar/json-schema": "^1.1.12", + "pvtsutils": "^1.3.5", + "tslib": "^2.6.2", + "webcrypto-core": "^1.7.9" + }, + "engines": { + "node": ">=10.12.0" + } + }, "node_modules/@sinclair/typebox": { "version": "0.27.8", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", @@ -2284,6 +2324,20 @@ "node": ">=8" } }, + "node_modules/asn1js": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/asn1js/-/asn1js-3.0.5.tgz", + "integrity": "sha512-FVnvrKJwpt9LP2lAMl8qZswRNm3T4q9CON+bxldk2iwk3FFpuwhx2FfinyitizWHsVYyaY+y5JzDR0rCMV5yTQ==", + "dev": true, + "dependencies": { + "pvtsutils": "^1.3.2", + "pvutils": "^1.1.3", + "tslib": "^2.4.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -6747,6 +6801,24 @@ } ] }, + "node_modules/pvtsutils": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/pvtsutils/-/pvtsutils-1.3.5.tgz", + "integrity": "sha512-ARvb14YB9Nm2Xi6nBq1ZX6dAM0FsJnuk+31aUp4TrcZEdKUlSqOqsxJHUPJDNE3qiIp+iUPEIeR6Je/tgV7zsA==", + "dev": true, + "dependencies": { + "tslib": "^2.6.1" + } + }, + "node_modules/pvutils": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/pvutils/-/pvutils-1.1.3.tgz", + "integrity": "sha512-pMpnA0qRdFp32b1sJl1wOJNxZLQ2cbQx+k6tjNtZ8CpvVhNqEPRgivZ2WOUev2YMajecdH7ctUPDvEe87nariQ==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/querystringify": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", @@ -7442,6 +7514,12 @@ } } }, + "node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "dev": true + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -7599,6 +7677,19 @@ "makeerror": "1.0.12" } }, + "node_modules/webcrypto-core": { + "version": "1.7.9", + "resolved": "https://registry.npmjs.org/webcrypto-core/-/webcrypto-core-1.7.9.tgz", + "integrity": "sha512-FE+a4PPkOmBbgNDIyRmcHhgXn+2ClRl3JzJdDu/P4+B8y81LqKe6RAsI9b3lAOHe1T1BMkSjsRHTYRikImZnVA==", + "dev": true, + "dependencies": { + "@peculiar/asn1-schema": "^2.3.8", + "@peculiar/json-schema": "^1.1.12", + "asn1js": "^3.0.1", + "pvtsutils": "^1.3.5", + "tslib": "^2.6.2" + } + }, "node_modules/webidl-conversions": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", diff --git a/package.json b/package.json index bfb7e74..927b3d0 100644 --- a/package.json +++ b/package.json @@ -31,6 +31,7 @@ "devDependencies": { "@commitlint/cli": "^19.3.0", "@commitlint/config-conventional": "^19.2.2", + "@peculiar/webcrypto": "^1.4.6", "@types/jest": "^29.5.12", "@typescript-eslint/eslint-plugin": "^7.7.1", "@typescript-eslint/parser": "^7.7.1", diff --git a/setup.jest.ts b/setup.jest.ts index 4930f24..8393cde 100644 --- a/setup.jest.ts +++ b/setup.jest.ts @@ -1,3 +1,13 @@ +import { Crypto } from "@peculiar/webcrypto"; import { TextEncoder, TextDecoder } from "util"; Object.assign(global, { TextDecoder, TextEncoder }); + +const crypto = new Crypto(); + +Object.defineProperty(global.self, "crypto", { + value: { + subtle: crypto.subtle, + getRandomValues: crypto.getRandomValues + }, +}); diff --git a/src/operations/hmac.ts b/src/operations/hmac.ts index df6c254..ee1d01e 100644 --- a/src/operations/hmac.ts +++ b/src/operations/hmac.ts @@ -1,4 +1,3 @@ -import { subtle } from "crypto"; import { ByteArray } from "../types"; /** @@ -9,7 +8,7 @@ import { ByteArray } from "../types"; * @returns The computed tag. */ async function hmac(key: ByteArray, input: ByteArray): Promise { - const importedKey = await subtle.importKey( + const importedKey = await window.crypto.subtle.importKey( "raw", key, { name: "HMAC", hash: "SHA-256" }, @@ -17,7 +16,7 @@ async function hmac(key: ByteArray, input: ByteArray): Promise { ["sign"] ); - const tag = await subtle.sign("HMAC", importedKey, input); + const tag = await window.crypto.subtle.sign("HMAC", importedKey, input); return new Uint8Array(tag); } diff --git a/src/operations/kdf.ts b/src/operations/kdf.ts index 7a351d1..c1adfca 100644 --- a/src/operations/kdf.ts +++ b/src/operations/kdf.ts @@ -1,4 +1,3 @@ -import { subtle } from "crypto"; import { ByteArray } from "../types"; interface KDFContext { @@ -28,7 +27,7 @@ async function kdf( outputLength: number, salt: ByteArray | null = null ): Promise { - const extractKey = await subtle.importKey("raw", key, { name: "HKDF" }, false, [ + const extractKey = await window.crypto.subtle.importKey("raw", key, { name: "HKDF" }, false, [ "deriveKey", "deriveBits" ]); @@ -36,7 +35,7 @@ async function kdf( const contextBuffer = contextToBuffer(context); const saltBuffer = Buffer.from(salt ?? []); - const derived = await subtle.deriveBits( + const derived = await window.crypto.subtle.deriveBits( { name: "HKDF", info: contextBuffer, diff --git a/src/operations/rsa.ts b/src/operations/rsa.ts index cdafc2f..608b20e 100644 --- a/src/operations/rsa.ts +++ b/src/operations/rsa.ts @@ -1,4 +1,3 @@ -import { subtle } from "crypto"; import { ByteArray, bytesToBase64 } from "../types"; import DecryptionException from "../exceptions/DecryptionException"; @@ -13,7 +12,7 @@ interface KeyPair { * @returns An RSA key pair. */ async function generateRSAKeyPair(): Promise { - const cryptoKeyPair = await subtle.generateKey( + const cryptoKeyPair = await window.crypto.subtle.generateKey( { name: "RSA-OAEP", modulusLength: 2048, @@ -24,9 +23,11 @@ async function generateRSAKeyPair(): Promise { ["encrypt", "decrypt"] ); - const pub: ByteArray = new Uint8Array(await subtle.exportKey("spki", cryptoKeyPair.publicKey)); + const pub: ByteArray = new Uint8Array( + await window.crypto.subtle.exportKey("spki", cryptoKeyPair.publicKey) + ); const secret: ByteArray = new Uint8Array( - await subtle.exportKey("pkcs8", cryptoKeyPair.privateKey) + await window.crypto.subtle.exportKey("pkcs8", cryptoKeyPair.privateKey) ); return { pub, secret }; @@ -52,7 +53,7 @@ function exportAsPem(key: ByteArray, keyType: "PUBLIC" | "SECRET"): string { * @returns The encrypted data. */ async function rsaEncrypt(pub: ByteArray, data: ByteArray): Promise { - const publicKey = await subtle.importKey( + const publicKey = await window.crypto.subtle.importKey( "spki", pub, { name: "RSA-OAEP", hash: "SHA-256" }, @@ -60,7 +61,7 @@ async function rsaEncrypt(pub: ByteArray, data: ByteArray): Promise { ["encrypt"] ); - const encryptedData = await subtle.encrypt({ name: "RSA-OAEP" }, publicKey, data); + const encryptedData = await window.crypto.subtle.encrypt({ name: "RSA-OAEP" }, publicKey, data); return new Uint8Array(encryptedData); } @@ -74,7 +75,7 @@ async function rsaEncrypt(pub: ByteArray, data: ByteArray): Promise { */ async function rsaDecrypt(secret: ByteArray, cipher: ByteArray): Promise { try { - const privateKey = await subtle.importKey( + const privateKey = await window.crypto.subtle.importKey( "pkcs8", secret, { name: "RSA-OAEP", hash: "SHA-256" }, @@ -82,7 +83,11 @@ async function rsaDecrypt(secret: ByteArray, cipher: ByteArray): Promise { + return new Uint8Array(await window.crypto.subtle.digest(algorithm, input)); } -function sha256(input: string): ByteArray { - return sha2(input, "SHA-256"); +async function sha256(input: ByteArray): Promise { + return await sha2(input, "SHA-256"); } -function sha512(input: string): ByteArray { - return sha2(input, "SHA-512"); +async function sha512(input: ByteArray): Promise { + return await sha2(input, "SHA-512"); } export { sha256, sha512 }; diff --git a/tests/operations/sha2.spec.ts b/tests/operations/sha2.spec.ts index 8ccf240..dafba04 100644 --- a/tests/operations/sha2.spec.ts +++ b/tests/operations/sha2.spec.ts @@ -2,7 +2,7 @@ import { ByteArray, bytesToHex } from "../../src/types"; import { sha256, sha512 } from "../../src/operations/sha2"; describe("SHA2", () => { - const input: string = "hash me!"; + const input: ByteArray = new TextEncoder().encode("hash me!"); it("should compute the SHA-256 hash", async () => { const hash: ByteArray = await sha256(input);