From 961b00e8ea30023ec87817c7d209441ecd87ea42 Mon Sep 17 00:00:00 2001 From: mei23 Date: Mon, 4 Mar 2024 13:17:09 +0900 Subject: [PATCH 1/6] Node v18 CI --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 6ac5451..9b2a2ca 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -8,7 +8,7 @@ jobs: strategy: matrix: - node-version: [20.10.0] + node-version: [18.x, 20.x] steps: - uses: actions/checkout@v4 From ac84ba2499fd2a8cd74eb1bc4d3662838159b6a5 Mon Sep 17 00:00:00 2001 From: mei23 Date: Mon, 4 Mar 2024 14:01:06 +0900 Subject: [PATCH 2/6] getWebcrypto --- .node-version | 1 + dist/draft/sign.d.ts | 4 ++-- dist/index.cjs | 28 ++++++++++++++++------------ dist/index.mjs | 27 +++++++++++++++------------ dist/keypair.d.ts | 12 ++++++------ dist/pem/pkcs8.d.ts | 2 -- dist/pem/spki.d.ts | 11 ++++++++++- dist/utils.d.ts | 2 ++ src/digest/utils.ts | 4 ++-- src/draft/sign.ts | 10 +++++----- src/draft/verify.ts | 6 +++--- src/keypair.ts | 31 ++++++++++++++++--------------- src/pem/pem.test.ts | 18 +++++++++--------- src/utils.ts | 4 ++++ 14 files changed, 91 insertions(+), 69 deletions(-) create mode 100644 .node-version diff --git a/.node-version b/.node-version new file mode 100644 index 0000000..3c5535c --- /dev/null +++ b/.node-version @@ -0,0 +1 @@ +18.19.1 diff --git a/dist/draft/sign.d.ts b/dist/draft/sign.d.ts index 78766f5..a541d79 100644 --- a/dist/draft/sign.d.ts +++ b/dist/draft/sign.d.ts @@ -1,5 +1,5 @@ /// -import type { webcrypto as crypto } from 'node:crypto'; +import type { webcrypto } from 'node:crypto'; import type { PrivateKey, RequestLike, SignInfo, SignatureHashAlgorithmUpperSnake } from '../types.js'; export declare function getDraftAlgoString(algorithm: SignInfo): string; export declare function genDraftSigningString(request: RequestLike, includeHeaders: string[], additional?: { @@ -9,7 +9,7 @@ export declare function genDraftSigningString(request: RequestLike, includeHeade expires?: string; opaque?: string; }): string; -export declare function genDraftSignature(privateKey: crypto.CryptoKey, signingString: string): Promise; +export declare function genDraftSignature(privateKey: webcrypto.CryptoKey, signingString: string): Promise; export declare function genDraftSignatureHeader(includeHeaders: string[], keyId: string, signature: string, algorithm: string): string; export declare function signAsDraftToRequest(request: RequestLike, key: PrivateKey, includeHeaders: string[], opts?: { hashAlgorithm?: SignatureHashAlgorithmUpperSnake; diff --git a/dist/index.cjs b/dist/index.cjs index bbe98ad..659706f 100644 --- a/dist/index.cjs +++ b/dist/index.cjs @@ -68,6 +68,7 @@ __export(src_exports, { getDraftAlgoString: () => getDraftAlgoString, getNistCurveFromOid: () => getNistCurveFromOid, getPublicKeyAlgorithmNameFromOid: () => getPublicKeyAlgorithmNameFromOid, + getWebcrypto: () => getWebcrypto, keyHashAlgosForDraftDecoding: () => keyHashAlgosForDraftDecoding, keyHashAlgosForDraftEncofing: () => keyHashAlgosForDraftEncofing, lcObjectGet: () => lcObjectGet, @@ -262,6 +263,9 @@ function parsePublicKey(input) { } // src/utils.ts +async function getWebcrypto() { + return globalThis.crypto ?? (await import("node:crypto")).webcrypto; +} function lcObjectKey(src) { return Object.entries(src).reduce((dst, [key, value]) => { if (key === "__proto__") @@ -454,7 +458,7 @@ function genDraftSigningString(request, includeHeaders, additional) { return results.join("\n"); } async function genDraftSignature(privateKey, signingString) { - const signatureAB = await globalThis.crypto.subtle.sign(privateKey.algorithm, privateKey, new TextEncoder().encode(signingString)); + const signatureAB = await (await getWebcrypto()).subtle.sign(privateKey.algorithm, privateKey, new TextEncoder().encode(signingString)); return encodeArrayBufferToBase64(signatureAB); } function genDraftSignatureHeader(includeHeaders, keyId, signature, algorithm) { @@ -464,7 +468,7 @@ async function signAsDraftToRequest(request, key, includeHeaders, opts = {}) { const hash = opts?.hashAlgorithm || "SHA-256"; const parsedPrivateKey = parsePkcs8(key.privateKeyPem); const importParams = genSignInfo(parsedPrivateKey, { hash, ec: "DSA" }); - const privateKey = await globalThis.crypto.subtle.importKey("pkcs8", parsedPrivateKey.der, importParams, false, ["sign"]); + const privateKey = await (await getWebcrypto()).subtle.importKey("pkcs8", parsedPrivateKey.der, importParams, false, ["sign"]); const algoString = getDraftAlgoString(importParams); const signingString = genDraftSigningString(request, includeHeaders, { keyId: key.keyId, algorithm: algoString }); const signature = await genDraftSignature(privateKey, signingString); @@ -720,15 +724,15 @@ function parseRequestSignature(request, options) { // src/keypair.ts async function exportPublicKeyPem(key) { - const ab = await globalThis.crypto.subtle.exportKey("spki", key); + const ab = await (await getWebcrypto()).subtle.exportKey("spki", key); return "-----BEGIN PUBLIC KEY-----\n" + splitPer64Chars(encodeArrayBufferToBase64(ab)).join("\n") + "\n-----END PUBLIC KEY-----\n"; } async function exportPrivateKeyPem(key) { - const ab = await globalThis.crypto.subtle.exportKey("pkcs8", key); + const ab = await (await getWebcrypto()).subtle.exportKey("pkcs8", key); return "-----BEGIN PRIVATE KEY-----\n" + splitPer64Chars(encodeArrayBufferToBase64(ab)).join("\n") + "\n-----END PRIVATE KEY-----\n"; } async function genRsaKeyPair(modulusLength = 4096, keyUsage = ["sign", "verify"]) { - const keyPair = await globalThis.crypto.subtle.generateKey( + const keyPair = await (await getWebcrypto()).subtle.generateKey( { name: "RSASSA-PKCS1-v1_5", modulusLength, @@ -744,7 +748,7 @@ async function genRsaKeyPair(modulusLength = 4096, keyUsage = ["sign", "verify"] }; } async function genEcKeyPair(namedCurve = "P-256", keyUsage = ["sign", "verify"]) { - const keyPair = await globalThis.crypto.subtle.generateKey( + const keyPair = await (await getWebcrypto()).subtle.generateKey( { name: "ECDSA", namedCurve @@ -758,7 +762,7 @@ async function genEcKeyPair(namedCurve = "P-256", keyUsage = ["sign", "verify"]) }; } async function genEd25519KeyPair(keyUsage = ["sign", "verify"]) { - const keyPair = await globalThis.crypto.subtle.generateKey( + const keyPair = await (await getWebcrypto()).subtle.generateKey( { name: "Ed25519" }, @@ -771,7 +775,7 @@ async function genEd25519KeyPair(keyUsage = ["sign", "verify"]) { }; } async function genEd448KeyPair(keyUsage) { - const keyPair = await globalThis.crypto.subtle.generateKey( + const keyPair = await (await getWebcrypto()).subtle.generateKey( { name: "Ed448" }, @@ -785,7 +789,6 @@ async function genEd448KeyPair(keyUsage) { } // src/digest/utils.ts -var import_node_crypto = require("node:crypto"); async function createBase64Digest(body, hash = "SHA-256") { if (Array.isArray(hash)) { return new Map(await Promise.all(hash.map((h) => { @@ -798,7 +801,7 @@ async function createBase64Digest(body, hash = "SHA-256") { if (typeof body === "string") { body = new TextEncoder().encode(body); } - const hashAb = await import_node_crypto.webcrypto.subtle.digest(hash, body); + const hashAb = await (await getWebcrypto()).subtle.digest(hash, body); return encodeArrayBufferToBase64(hashAb); } @@ -935,8 +938,8 @@ function genSignInfoDraft(algorithm, parsed, errorLogger) { async function verifyDraftSignature(parsed, publicKeyPem, errorLogger) { try { const parsedSpki = parsePublicKey(publicKeyPem); - const publicKey = await globalThis.crypto.subtle.importKey("spki", parsedSpki.der, genSignInfo(parsedSpki), false, ["verify"]); - const verify = await globalThis.crypto.subtle.verify(publicKey.algorithm, publicKey, decodeBase64ToUint8Array(parsed.params.signature), new TextEncoder().encode(parsed.signingString)); + const publicKey = await (await getWebcrypto()).subtle.importKey("spki", parsedSpki.der, genSignInfo(parsedSpki), false, ["verify"]); + const verify = await (await getWebcrypto()).subtle.verify(publicKey.algorithm, publicKey, decodeBase64ToUint8Array(parsed.params.signature), new TextEncoder().encode(parsed.signingString)); return verify; } catch (e) { if (errorLogger) @@ -984,6 +987,7 @@ async function verifyDraftSignature(parsed, publicKeyPem, errorLogger) { getDraftAlgoString, getNistCurveFromOid, getPublicKeyAlgorithmNameFromOid, + getWebcrypto, keyHashAlgosForDraftDecoding, keyHashAlgosForDraftEncofing, lcObjectGet, diff --git a/dist/index.mjs b/dist/index.mjs index 681cefe..e629dfb 100644 --- a/dist/index.mjs +++ b/dist/index.mjs @@ -165,6 +165,9 @@ function parsePublicKey(input) { } // src/utils.ts +async function getWebcrypto() { + return globalThis.crypto ?? (await import("node:crypto")).webcrypto; +} function lcObjectKey(src) { return Object.entries(src).reduce((dst, [key, value]) => { if (key === "__proto__") @@ -357,7 +360,7 @@ function genDraftSigningString(request, includeHeaders, additional) { return results.join("\n"); } async function genDraftSignature(privateKey, signingString) { - const signatureAB = await globalThis.crypto.subtle.sign(privateKey.algorithm, privateKey, new TextEncoder().encode(signingString)); + const signatureAB = await (await getWebcrypto()).subtle.sign(privateKey.algorithm, privateKey, new TextEncoder().encode(signingString)); return encodeArrayBufferToBase64(signatureAB); } function genDraftSignatureHeader(includeHeaders, keyId, signature, algorithm) { @@ -367,7 +370,7 @@ async function signAsDraftToRequest(request, key, includeHeaders, opts = {}) { const hash = opts?.hashAlgorithm || "SHA-256"; const parsedPrivateKey = parsePkcs8(key.privateKeyPem); const importParams = genSignInfo(parsedPrivateKey, { hash, ec: "DSA" }); - const privateKey = await globalThis.crypto.subtle.importKey("pkcs8", parsedPrivateKey.der, importParams, false, ["sign"]); + const privateKey = await (await getWebcrypto()).subtle.importKey("pkcs8", parsedPrivateKey.der, importParams, false, ["sign"]); const algoString = getDraftAlgoString(importParams); const signingString = genDraftSigningString(request, includeHeaders, { keyId: key.keyId, algorithm: algoString }); const signature = await genDraftSignature(privateKey, signingString); @@ -623,15 +626,15 @@ function parseRequestSignature(request, options) { // src/keypair.ts async function exportPublicKeyPem(key) { - const ab = await globalThis.crypto.subtle.exportKey("spki", key); + const ab = await (await getWebcrypto()).subtle.exportKey("spki", key); return "-----BEGIN PUBLIC KEY-----\n" + splitPer64Chars(encodeArrayBufferToBase64(ab)).join("\n") + "\n-----END PUBLIC KEY-----\n"; } async function exportPrivateKeyPem(key) { - const ab = await globalThis.crypto.subtle.exportKey("pkcs8", key); + const ab = await (await getWebcrypto()).subtle.exportKey("pkcs8", key); return "-----BEGIN PRIVATE KEY-----\n" + splitPer64Chars(encodeArrayBufferToBase64(ab)).join("\n") + "\n-----END PRIVATE KEY-----\n"; } async function genRsaKeyPair(modulusLength = 4096, keyUsage = ["sign", "verify"]) { - const keyPair = await globalThis.crypto.subtle.generateKey( + const keyPair = await (await getWebcrypto()).subtle.generateKey( { name: "RSASSA-PKCS1-v1_5", modulusLength, @@ -647,7 +650,7 @@ async function genRsaKeyPair(modulusLength = 4096, keyUsage = ["sign", "verify"] }; } async function genEcKeyPair(namedCurve = "P-256", keyUsage = ["sign", "verify"]) { - const keyPair = await globalThis.crypto.subtle.generateKey( + const keyPair = await (await getWebcrypto()).subtle.generateKey( { name: "ECDSA", namedCurve @@ -661,7 +664,7 @@ async function genEcKeyPair(namedCurve = "P-256", keyUsage = ["sign", "verify"]) }; } async function genEd25519KeyPair(keyUsage = ["sign", "verify"]) { - const keyPair = await globalThis.crypto.subtle.generateKey( + const keyPair = await (await getWebcrypto()).subtle.generateKey( { name: "Ed25519" }, @@ -674,7 +677,7 @@ async function genEd25519KeyPair(keyUsage = ["sign", "verify"]) { }; } async function genEd448KeyPair(keyUsage) { - const keyPair = await globalThis.crypto.subtle.generateKey( + const keyPair = await (await getWebcrypto()).subtle.generateKey( { name: "Ed448" }, @@ -688,7 +691,6 @@ async function genEd448KeyPair(keyUsage) { } // src/digest/utils.ts -import { webcrypto as crypto } from "node:crypto"; async function createBase64Digest(body, hash = "SHA-256") { if (Array.isArray(hash)) { return new Map(await Promise.all(hash.map((h) => { @@ -701,7 +703,7 @@ async function createBase64Digest(body, hash = "SHA-256") { if (typeof body === "string") { body = new TextEncoder().encode(body); } - const hashAb = await crypto.subtle.digest(hash, body); + const hashAb = await (await getWebcrypto()).subtle.digest(hash, body); return encodeArrayBufferToBase64(hashAb); } @@ -838,8 +840,8 @@ function genSignInfoDraft(algorithm, parsed, errorLogger) { async function verifyDraftSignature(parsed, publicKeyPem, errorLogger) { try { const parsedSpki = parsePublicKey(publicKeyPem); - const publicKey = await globalThis.crypto.subtle.importKey("spki", parsedSpki.der, genSignInfo(parsedSpki), false, ["verify"]); - const verify = await globalThis.crypto.subtle.verify(publicKey.algorithm, publicKey, decodeBase64ToUint8Array(parsed.params.signature), new TextEncoder().encode(parsed.signingString)); + const publicKey = await (await getWebcrypto()).subtle.importKey("spki", parsedSpki.der, genSignInfo(parsedSpki), false, ["verify"]); + const verify = await (await getWebcrypto()).subtle.verify(publicKey.algorithm, publicKey, decodeBase64ToUint8Array(parsed.params.signature), new TextEncoder().encode(parsed.signingString)); return verify; } catch (e) { if (errorLogger) @@ -886,6 +888,7 @@ export { getDraftAlgoString, getNistCurveFromOid, getPublicKeyAlgorithmNameFromOid, + getWebcrypto, keyHashAlgosForDraftDecoding, keyHashAlgosForDraftEncofing, lcObjectGet, diff --git a/dist/keypair.d.ts b/dist/keypair.d.ts index 893e14b..4e78922 100644 --- a/dist/keypair.d.ts +++ b/dist/keypair.d.ts @@ -1,17 +1,17 @@ /// -import type { webcrypto as crypto } from 'node:crypto'; +import { webcrypto } from 'node:crypto'; import { ECNamedCurve } from './types'; -export declare function exportPublicKeyPem(key: crypto.CryptoKey): Promise; -export declare function exportPrivateKeyPem(key: crypto.CryptoKey): Promise; -export declare function genRsaKeyPair(modulusLength?: number, keyUsage?: crypto.KeyUsage[]): Promise<{ +export declare function exportPublicKeyPem(key: webcrypto.CryptoKey): Promise; +export declare function exportPrivateKeyPem(key: webcrypto.CryptoKey): Promise; +export declare function genRsaKeyPair(modulusLength?: number, keyUsage?: webcrypto.KeyUsage[]): Promise<{ publicKey: string; privateKey: string; }>; -export declare function genEcKeyPair(namedCurve?: ECNamedCurve, keyUsage?: crypto.KeyUsage[]): Promise<{ +export declare function genEcKeyPair(namedCurve?: ECNamedCurve, keyUsage?: webcrypto.KeyUsage[]): Promise<{ publicKey: string; privateKey: string; }>; -export declare function genEd25519KeyPair(keyUsage?: crypto.KeyUsage[]): Promise<{ +export declare function genEd25519KeyPair(keyUsage?: webcrypto.KeyUsage[]): Promise<{ publicKey: string; privateKey: string; }>; diff --git a/dist/pem/pkcs8.d.ts b/dist/pem/pkcs8.d.ts index f018b48..a6fa922 100644 --- a/dist/pem/pkcs8.d.ts +++ b/dist/pem/pkcs8.d.ts @@ -6,8 +6,6 @@ export declare class Pkcs8ParseError extends Error { export type ParsedPkcs8 = ParsedAlgorithmIdentifierBase & { /** * DER - * - * (Somehow crypto.createPublicKey will cause `error:1E08010C:DECODER routines::unsupported`) */ der: ArrayBuffer; attributesRaw: ArrayBuffer | null; diff --git a/dist/pem/spki.d.ts b/dist/pem/spki.d.ts index 90c0464..ac975cd 100644 --- a/dist/pem/spki.d.ts +++ b/dist/pem/spki.d.ts @@ -7,7 +7,7 @@ export declare class SpkiParseError extends Error { * Get algorithm name from OID * https://datatracker.ietf.org/doc/html/rfc3279#section-2.3 * https://datatracker.ietf.org/doc/html/rfc8420#appendix-A - * @param oidStr e.g. '1.2.840.113549.1.1.1' or SpkiParsedAlgorithmIdentifier.algorithm + * @param oidStr e.g. '1.2.840.113549.1.1.1' or ParsedAlgorithmIdentifier.algorithm * @returns e.g. 'RSASSA-PKCS1-v1_5' */ export declare function getPublicKeyAlgorithmNameFromOid(oidStr: string): KeyAlgorithmName; @@ -78,6 +78,15 @@ export declare function parseAlgorithmIdentifier(input: ASN1): ParsedAlgorithmId export declare function parseSpki(input: ASN1.StreamOrBinary): SpkiParsedAlgorithmIdentifier; /** * Parse X.509 SubjectPublicKeyInfo (SPKI) public key + * + * In Node.js, `createPublicKey(publicKey)` does not need any information, + * but `crypto.subtle.importKey` needs to be provided by us for the key type. + * + * So, this function parses the SPKI and parses the type of key stored. + * + * If the key is PKCS#1, the function wraps it in SPKI. In that case, + * it assumes that the algorithm is `RSASSA-PKCS1-v1_5`. + * * @param input SPKI public key PEM or DER * @returns parsed object */ diff --git a/dist/utils.d.ts b/dist/utils.d.ts index 88c0e85..39b194d 100644 --- a/dist/utils.d.ts +++ b/dist/utils.d.ts @@ -1,5 +1,7 @@ +/// import type { SignInfo, SignatureHashAlgorithmUpperSnake } from './types.js'; import { ParsedAlgorithmIdentifier } from './pem/spki.js'; +export declare function getWebcrypto(): Promise; /** * Convert object keys to lowercase */ diff --git a/src/digest/utils.ts b/src/digest/utils.ts index 36d393f..e85cbfb 100644 --- a/src/digest/utils.ts +++ b/src/digest/utils.ts @@ -1,6 +1,6 @@ import { webcrypto as crypto } from 'node:crypto'; import { DigestHashAlgorithm } from '../types'; -import { encodeArrayBufferToBase64 } from '../utils'; +import { encodeArrayBufferToBase64, getWebcrypto } from '../utils'; export type DigestSource = crypto.BufferSource | string; @@ -23,6 +23,6 @@ export async function createBase64Digest( if (typeof body === 'string') { body = (new TextEncoder()).encode(body); } - const hashAb = await crypto.subtle.digest(hash, body); + const hashAb = await (await getWebcrypto()).subtle.digest(hash, body); return encodeArrayBufferToBase64(hashAb); } diff --git a/src/draft/sign.ts b/src/draft/sign.ts index 37e1921..cf44fb3 100644 --- a/src/draft/sign.ts +++ b/src/draft/sign.ts @@ -1,6 +1,6 @@ -import type { webcrypto as crypto } from 'node:crypto'; +import type { webcrypto } from 'node:crypto'; import type { PrivateKey, RequestLike, SignInfo, SignatureHashAlgorithmUpperSnake } from '../types.js'; -import { encodeArrayBufferToBase64, genSignInfo, lcObjectKey } from '../utils.js'; +import { encodeArrayBufferToBase64, genSignInfo, getWebcrypto, lcObjectKey } from '../utils.js'; import { parsePkcs8 } from '../pem/pkcs8.js'; import { keyHashAlgosForDraftEncofing } from './const.js'; @@ -72,8 +72,8 @@ export function genDraftSigningString( return results.join('\n'); } -export async function genDraftSignature(privateKey: crypto.CryptoKey, signingString: string) { - const signatureAB = await globalThis.crypto.subtle.sign(privateKey.algorithm, privateKey, new TextEncoder().encode(signingString)); +export async function genDraftSignature(privateKey: webcrypto.CryptoKey, signingString: string) { + const signatureAB = await (await getWebcrypto()).subtle.sign(privateKey.algorithm, privateKey, new TextEncoder().encode(signingString)); return encodeArrayBufferToBase64(signatureAB); } @@ -86,7 +86,7 @@ export async function signAsDraftToRequest(request: RequestLike, key: PrivateKey const parsedPrivateKey = parsePkcs8(key.privateKeyPem); const importParams = genSignInfo(parsedPrivateKey, { hash, ec: 'DSA' }); - const privateKey = await globalThis.crypto.subtle.importKey('pkcs8', parsedPrivateKey.der, importParams, false, ['sign']); + const privateKey = await (await getWebcrypto()).subtle.importKey('pkcs8', parsedPrivateKey.der, importParams, false, ['sign']); const algoString = getDraftAlgoString(importParams); const signingString = genDraftSigningString(request, includeHeaders, { keyId: key.keyId, algorithm: algoString }); diff --git a/src/draft/verify.ts b/src/draft/verify.ts index 702f46b..c9b8b0e 100644 --- a/src/draft/verify.ts +++ b/src/draft/verify.ts @@ -4,7 +4,7 @@ import type { ParsedDraftSignature, SignInfo } from '../types.js'; import { ParsedAlgorithmIdentifier, getNistCurveFromOid, getPublicKeyAlgorithmNameFromOid, parsePublicKey } from '../pem/spki.js'; -import { decodeBase64ToUint8Array, genSignInfo } from '../utils.js'; +import { decodeBase64ToUint8Array, genSignInfo, getWebcrypto } from '../utils.js'; import type { SignatureHashAlgorithmUpperSnake } from '../types.js'; import { keyHashAlgosForDraftDecoding } from './const.js'; @@ -114,9 +114,9 @@ export function genSignInfoDraft(algorithm: string | undefined, parsed: ParsedAl export async function verifyDraftSignature(parsed: ParsedDraftSignature['value'], publicKeyPem: string, errorLogger?: ((message: any) => any)) { try { const parsedSpki = parsePublicKey(publicKeyPem); - const publicKey = await globalThis.crypto.subtle.importKey('spki', parsedSpki.der, genSignInfo(parsedSpki), false, ['verify']); + const publicKey = await (await getWebcrypto()).subtle.importKey('spki', parsedSpki.der, genSignInfo(parsedSpki), false, ['verify']); - const verify = await globalThis.crypto.subtle.verify(publicKey.algorithm, publicKey, decodeBase64ToUint8Array(parsed.params.signature), (new TextEncoder()).encode(parsed.signingString)); + const verify = await (await getWebcrypto()).subtle.verify(publicKey.algorithm, publicKey, decodeBase64ToUint8Array(parsed.params.signature), (new TextEncoder()).encode(parsed.signingString)); return verify; } catch (e) { if (errorLogger) errorLogger(e); diff --git a/src/keypair.ts b/src/keypair.ts index 4c55b0d..d94289f 100644 --- a/src/keypair.ts +++ b/src/keypair.ts @@ -3,26 +3,26 @@ * SPDX-License-Identifier: MIT */ -import type { webcrypto as crypto } from 'node:crypto'; -import { encodeArrayBufferToBase64, splitPer64Chars } from './utils'; +import { webcrypto } from 'node:crypto'; +import { encodeArrayBufferToBase64, getWebcrypto, splitPer64Chars } from './utils'; import { ECNamedCurve } from './types'; -export async function exportPublicKeyPem(key: crypto.CryptoKey) { - const ab = await globalThis.crypto.subtle.exportKey('spki', key); +export async function exportPublicKeyPem(key: webcrypto.CryptoKey) { + const ab = await (await getWebcrypto()).subtle.exportKey('spki', key); return '-----BEGIN PUBLIC KEY-----\n' + splitPer64Chars(encodeArrayBufferToBase64(ab)).join('\n') + '\n-----END PUBLIC KEY-----\n'; } -export async function exportPrivateKeyPem(key: crypto.CryptoKey) { - const ab = await globalThis.crypto.subtle.exportKey('pkcs8', key); +export async function exportPrivateKeyPem(key: webcrypto.CryptoKey) { + const ab = await (await getWebcrypto()).subtle.exportKey('pkcs8', key); return '-----BEGIN PRIVATE KEY-----\n' + splitPer64Chars(encodeArrayBufferToBase64(ab)).join('\n') + '\n-----END PRIVATE KEY-----\n'; } -export async function genRsaKeyPair(modulusLength = 4096, keyUsage: crypto.KeyUsage[] = ['sign', 'verify']) { - const keyPair = await globalThis.crypto.subtle.generateKey( +export async function genRsaKeyPair(modulusLength = 4096, keyUsage: webcrypto.KeyUsage[] = ['sign', 'verify']) { + const keyPair = await (await getWebcrypto()).subtle.generateKey( { name: 'RSASSA-PKCS1-v1_5', modulusLength, @@ -38,8 +38,8 @@ export async function genRsaKeyPair(modulusLength = 4096, keyUsage: crypto.KeyUs }; } -export async function genEcKeyPair(namedCurve: ECNamedCurve = 'P-256', keyUsage: crypto.KeyUsage[] = ['sign', 'verify']) { - const keyPair = await globalThis.crypto.subtle.generateKey( +export async function genEcKeyPair(namedCurve: ECNamedCurve = 'P-256', keyUsage: webcrypto.KeyUsage[] = ['sign', 'verify']) { + const keyPair = await (await getWebcrypto()).subtle.generateKey( { name: 'ECDSA', namedCurve @@ -53,14 +53,14 @@ export async function genEcKeyPair(namedCurve: ECNamedCurve = 'P-256', keyUsage: }; } -export async function genEd25519KeyPair(keyUsage: crypto.KeyUsage[] = ['sign', 'verify']) { - const keyPair = await globalThis.crypto.subtle.generateKey( +export async function genEd25519KeyPair(keyUsage: webcrypto.KeyUsage[] = ['sign', 'verify']) { + const keyPair = await (await getWebcrypto()).subtle.generateKey( { name: 'Ed25519', }, true, keyUsage, - ) as crypto.CryptoKeyPair; + ) as webcrypto.CryptoKeyPair; return { publicKey: await exportPublicKeyPem(keyPair.publicKey), privateKey: await exportPrivateKeyPem(keyPair.privateKey) @@ -68,13 +68,14 @@ export async function genEd25519KeyPair(keyUsage: crypto.KeyUsage[] = ['sign', ' } export async function genEd448KeyPair(keyUsage) { - const keyPair = await globalThis.crypto.subtle.generateKey( + + const keyPair = await (await getWebcrypto()).subtle.generateKey( { name: 'Ed448', }, true, keyUsage, - ) as crypto.CryptoKeyPair; + ) as webcrypto.CryptoKeyPair; return { publicKey: await exportPublicKeyPem(keyPair.publicKey), privateKey: await exportPrivateKeyPem(keyPair.privateKey) diff --git a/src/pem/pem.test.ts b/src/pem/pem.test.ts index 5b02156..06a620e 100644 --- a/src/pem/pem.test.ts +++ b/src/pem/pem.test.ts @@ -3,7 +3,7 @@ import { genSpkiFromPkcs1, parsePkcs1 } from './pkcs1'; import { parsePkcs8 } from './pkcs8'; import { rsa4096, ed25519 } from '../../test/keys'; import { genEcKeyPair } from '../keypair'; -import { genSignInfo } from '../utils'; +import { genSignInfo, getWebcrypto } from '../utils'; import { sign, generateKeyPair } from 'node:crypto'; import * as util from 'node:util'; @@ -19,13 +19,13 @@ describe('spki', () => { const importParams = genSignInfo(parsed); expect(importParams).toEqual({ name: 'RSASSA-PKCS1-v1_5', hash: 'SHA-256' }); - const publicKey = await crypto.subtle.importKey('spki', parsed.der, importParams, true, ['verify']); + const publicKey = await (await getWebcrypto()).subtle.importKey('spki', parsed.der, importParams, true, ['verify']); expect((publicKey?.algorithm as any).modulusLength).toBe(4096); const signed = sign('sha256', test_buffer, rsa4096.privateKey); const verifyAlgorithm = genSignInfo(parsed); - const verify = await crypto.subtle.verify(verifyAlgorithm, publicKey, signed, test_buffer); + const verify = await (await getWebcrypto()).subtle.verify(verifyAlgorithm, publicKey, signed, test_buffer); expect(verify).toBe(true); }); test('ed25519', async () => { @@ -36,13 +36,13 @@ describe('spki', () => { const importParams = genSignInfo(parsed); expect(importParams).toEqual({ name: 'Ed25519' }); - const publicKey = await crypto.subtle.importKey('spki', parsed.der, importParams, true, ['verify']); + const publicKey = await (await getWebcrypto()).subtle.importKey('spki', parsed.der, importParams, true, ['verify']); expect((publicKey?.algorithm as any).name).toBe('Ed25519'); const signed = sign(null, test_buffer, ed25519.privateKey); const verifyAlgorithm = genSignInfo(parsed); - const verify = await crypto.subtle.verify(verifyAlgorithm, publicKey, signed, test_buffer); + const verify = await (await getWebcrypto()).subtle.verify(verifyAlgorithm, publicKey, signed, test_buffer); expect(verify).toBe(true); }); test('ec', async () => { @@ -54,13 +54,13 @@ describe('spki', () => { const importParams = genSignInfo(parsed); expect(importParams).toEqual({ name: 'ECDSA', namedCurve: 'P-256', hash: 'SHA-256' }); - const publicKey = await crypto.subtle.importKey('spki', parsed.der, importParams, true, ['verify']); + const publicKey = await (await getWebcrypto()).subtle.importKey('spki', parsed.der, importParams, true, ['verify']); expect((publicKey?.algorithm as any).name).toBe('ECDSA'); const signed = sign('sha256', test_buffer, { key: keyPair.privateKey, dsaEncoding: 'ieee-p1363' }); const verifyAlgorithm = genSignInfo(parsed, { hash: 'SHA-256', ec: 'DSA' }); - const verify = await crypto.subtle.verify(verifyAlgorithm, publicKey, signed, test_buffer); + const verify = await (await getWebcrypto()).subtle.verify(verifyAlgorithm, publicKey, signed, test_buffer); expect(verify).toBe(true); }); test('invalid input', () => { @@ -95,10 +95,10 @@ describe('pkcs1', () => { const signed = sign('sha256', test_buffer, kp.privateKey); - const publicKey = await crypto.subtle.importKey('spki', spki, genSignInfo(parsed), true, ['verify']); + const publicKey = await (await getWebcrypto()).subtle.importKey('spki', spki, genSignInfo(parsed), true, ['verify']); expect((publicKey?.algorithm as any).modulusLength).toBe(4096); - const verify = await crypto.subtle.verify(genSignInfo(parsed), publicKey, signed, test_buffer); + const verify = await (await getWebcrypto()).subtle.verify(genSignInfo(parsed), publicKey, signed, test_buffer); expect(verify).toBe(true); }); }); diff --git a/src/utils.ts b/src/utils.ts index d23295c..2fb8c7c 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,6 +1,10 @@ import type { SignInfo, SignatureHashAlgorithmUpperSnake } from './types.js'; import { ParsedAlgorithmIdentifier, getNistCurveFromOid, getPublicKeyAlgorithmNameFromOid } from './pem/spki.js'; +export async function getWebcrypto() { + return globalThis.crypto ?? (await import('node:crypto')).webcrypto; +} + /** * Convert object keys to lowercase */ From 33cafb39b57236728c0b1fd56243c44e7c840114 Mon Sep 17 00:00:00 2001 From: mei23 Date: Mon, 4 Mar 2024 14:03:15 +0900 Subject: [PATCH 3/6] lint --- src/keypair.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/keypair.ts b/src/keypair.ts index d94289f..6b4224c 100644 --- a/src/keypair.ts +++ b/src/keypair.ts @@ -68,7 +68,6 @@ export async function genEd25519KeyPair(keyUsage: webcrypto.KeyUsage[] = ['sign' } export async function genEd448KeyPair(keyUsage) { - const keyPair = await (await getWebcrypto()).subtle.generateKey( { name: 'Ed448', From e24f050d739914b330b23ea8232fcaa5bb5488b3 Mon Sep 17 00:00:00 2001 From: mei23 Date: Mon, 4 Mar 2024 14:05:30 +0900 Subject: [PATCH 4/6] 21.x --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 9b2a2ca..c5da1da 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -8,7 +8,7 @@ jobs: strategy: matrix: - node-version: [18.x, 20.x] + node-version: [18.x, 20.x, 21.x] steps: - uses: actions/checkout@v4 From 8a3f4ad01ad2d8c6f39c7dbecec298503b0b2f37 Mon Sep 17 00:00:00 2001 From: mei23 Date: Mon, 4 Mar 2024 14:57:47 +0900 Subject: [PATCH 5/6] remove --- .node-version | 1 - 1 file changed, 1 deletion(-) delete mode 100644 .node-version diff --git a/.node-version b/.node-version deleted file mode 100644 index 3c5535c..0000000 --- a/.node-version +++ /dev/null @@ -1 +0,0 @@ -18.19.1 From 3ffbb3554d9c3031edf87783b79f511f0efb3e69 Mon Sep 17 00:00:00 2001 From: tamaina Date: Tue, 5 Mar 2024 06:28:41 +0000 Subject: [PATCH 6/6] run perf test in test ci --- .editorconfig | 5 +++++ .github/workflows/test.yml | 3 +++ dist/index.cjs | 1 - dist/index.mjs | 1 - 4 files changed, 8 insertions(+), 2 deletions(-) diff --git a/.editorconfig b/.editorconfig index 450e73e..7970f0c 100644 --- a/.editorconfig +++ b/.editorconfig @@ -7,3 +7,8 @@ charset = utf-8 insert_final_newline = true end_of_line = lf trim_trailing_whitespace = true + +[*.{yml,yaml}] +indent_style = space +indent_size = 2 +trim_trailing_whitespace = false diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index c5da1da..672bd2b 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -32,3 +32,6 @@ jobs: - name: Test run: | pnpm test + - name: Performance + run: | + pnpm performance diff --git a/dist/index.cjs b/dist/index.cjs index 8a52334..3479e4b 100644 --- a/dist/index.cjs +++ b/dist/index.cjs @@ -854,7 +854,6 @@ async function verifyRFC3230DigestHeader(request, rawBody, failOnNoDigest = true } throw e; } - ; if (hash !== value) { if (errorLogger) errorLogger(`Digest header hash mismatch`); diff --git a/dist/index.mjs b/dist/index.mjs index ac9834e..0638914 100644 --- a/dist/index.mjs +++ b/dist/index.mjs @@ -757,7 +757,6 @@ async function verifyRFC3230DigestHeader(request, rawBody, failOnNoDigest = true } throw e; } - ; if (hash !== value) { if (errorLogger) errorLogger(`Digest header hash mismatch`);