Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
tamaina committed Mar 5, 2024
2 parents 7ca917c + 505e15b commit 7f4fb3a
Show file tree
Hide file tree
Showing 13 changed files with 88 additions and 69 deletions.
5 changes: 5 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -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
5 changes: 4 additions & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ jobs:

strategy:
matrix:
node-version: [20.10.0]
node-version: [18.x, 20.x, 21.x]

steps:
- uses: actions/checkout@v4
Expand All @@ -32,3 +32,6 @@ jobs:
- name: Test
run: |
pnpm test
- name: Performance
run: |
pnpm performance
4 changes: 2 additions & 2 deletions dist/draft/sign.d.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/// <reference types="node" />
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?: {
Expand All @@ -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<string>;
export declare function genDraftSignature(privateKey: webcrypto.CryptoKey, signingString: string): Promise<string>;
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;
Expand Down
29 changes: 16 additions & 13 deletions dist/index.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ __export(src_exports, {
getDraftAlgoString: () => getDraftAlgoString,
getNistCurveFromOid: () => getNistCurveFromOid,
getPublicKeyAlgorithmNameFromOid: () => getPublicKeyAlgorithmNameFromOid,
getWebcrypto: () => getWebcrypto,
keyHashAlgosForDraftDecoding: () => keyHashAlgosForDraftDecoding,
keyHashAlgosForDraftEncofing: () => keyHashAlgosForDraftEncofing,
lcObjectGet: () => lcObjectGet,
Expand Down Expand Up @@ -263,6 +264,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__")
Expand Down Expand Up @@ -455,7 +459,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) {
Expand All @@ -465,7 +469,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);
Expand Down Expand Up @@ -722,15 +726,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,
Expand All @@ -746,7 +750,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
Expand All @@ -760,7 +764,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"
},
Expand All @@ -773,7 +777,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"
},
Expand All @@ -787,7 +791,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) => {
Expand All @@ -800,7 +803,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);
}

Expand Down Expand Up @@ -851,7 +854,6 @@ async function verifyRFC3230DigestHeader(request, rawBody, failOnNoDigest = true
}
throw e;
}
;
if (hash !== value) {
if (errorLogger)
errorLogger(`Digest header hash mismatch`);
Expand Down Expand Up @@ -959,8 +961,8 @@ var genSignInfoDraft = parseSignInfo;
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)
Expand Down Expand Up @@ -1007,6 +1009,7 @@ async function verifyDraftSignature(parsed, publicKeyPem, errorLogger) {
getDraftAlgoString,
getNistCurveFromOid,
getPublicKeyAlgorithmNameFromOid,
getWebcrypto,
keyHashAlgosForDraftDecoding,
keyHashAlgosForDraftEncofing,
lcObjectGet,
Expand Down
28 changes: 15 additions & 13 deletions dist/index.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,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__")
Expand Down Expand Up @@ -359,7 +362,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) {
Expand All @@ -369,7 +372,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);
Expand Down Expand Up @@ -626,15 +629,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,
Expand All @@ -650,7 +653,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
Expand All @@ -664,7 +667,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"
},
Expand All @@ -677,7 +680,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"
},
Expand All @@ -691,7 +694,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) => {
Expand All @@ -704,7 +706,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);
}

Expand Down Expand Up @@ -755,7 +757,6 @@ async function verifyRFC3230DigestHeader(request, rawBody, failOnNoDigest = true
}
throw e;
}
;
if (hash !== value) {
if (errorLogger)
errorLogger(`Digest header hash mismatch`);
Expand Down Expand Up @@ -863,8 +864,8 @@ var genSignInfoDraft = parseSignInfo;
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)
Expand Down Expand Up @@ -910,6 +911,7 @@ export {
getDraftAlgoString,
getNistCurveFromOid,
getPublicKeyAlgorithmNameFromOid,
getWebcrypto,
keyHashAlgosForDraftDecoding,
keyHashAlgosForDraftEncofing,
lcObjectGet,
Expand Down
12 changes: 6 additions & 6 deletions dist/keypair.d.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
/// <reference types="node" />
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<string>;
export declare function exportPrivateKeyPem(key: crypto.CryptoKey): Promise<string>;
export declare function genRsaKeyPair(modulusLength?: number, keyUsage?: crypto.KeyUsage[]): Promise<{
export declare function exportPublicKeyPem(key: webcrypto.CryptoKey): Promise<string>;
export declare function exportPrivateKeyPem(key: webcrypto.CryptoKey): Promise<string>;
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;
}>;
Expand Down
2 changes: 2 additions & 0 deletions dist/utils.d.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
/// <reference types="node" />
import type { SignInfo, SignatureHashAlgorithmUpperSnake } from './types.js';
import { ParsedAlgorithmIdentifier } from './pem/spki.js';
export declare function getWebcrypto(): Promise<import("crypto").webcrypto.Crypto>;
/**
* Convert object keys to lowercase
*/
Expand Down
4 changes: 2 additions & 2 deletions src/digest/utils.ts
Original file line number Diff line number Diff line change
@@ -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;

Expand All @@ -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);
}
10 changes: 5 additions & 5 deletions src/draft/sign.ts
Original file line number Diff line number Diff line change
@@ -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';

Expand Down Expand Up @@ -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);
}

Expand All @@ -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 });
Expand Down
6 changes: 3 additions & 3 deletions src/draft/verify.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { ParsedDraftSignature } from "../types";
import { parsePublicKey } from "../pem/spki";
import { parseSignInfo } from "../shared/verify";
import { decodeBase64ToUint8Array, genSignInfo } from "../utils";
import { decodeBase64ToUint8Array, genSignInfo, getWebcrypto } from "../utils";

/**
* @deprecated Use `parseSignInfo`
Expand All @@ -14,9 +14,9 @@ export const genSignInfoDraft = parseSignInfo;
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);
Expand Down
Loading

0 comments on commit 7f4fb3a

Please sign in to comment.