refactor: Replace key wrapping with manual PKCS8/raw key encryption

Implement utility functions for conversion between Ed25519 raw key data
and PKCS8 format, and update KeyPair encode/decode methods to use direct
encrypt/decrypt operations instead of wrapKey/unwrapKey.
This commit is contained in:
Shibo Lyu 2025-04-12 03:19:18 +08:00
parent 23bd666a6e
commit 249bbf2b54
3 changed files with 42 additions and 8 deletions

View file

@ -3,7 +3,11 @@ import { hexToBuf } from "./mod.ts";
import { pbkdf2Key } from "./pbkdf2.ts";
import { BlahPublicKey } from "./publicKey.ts";
import type { BlahPayloadSignee, BlahSignedPayload } from "./signedPayload.ts";
import { bufToHex } from "./utils.ts";
import {
bufToHex,
ed25519PKCS8ToRawPrivateKey,
ed25519RawPrivateKeyToPKCS8,
} from "./utils.ts";
export type EncodedBlahKeyPair =
& {
@ -72,14 +76,20 @@ export class BlahKeyPair {
}
const derviedKey = await pbkdf2Key(password, encoded.salt);
const privateKey = await crypto.subtle.unwrapKey(
"pkcs8",
hexToBuf(encoded.passwordProtectedPrivateKey),
derviedKey,
const encryptedKeyData = hexToBuf(encoded.passwordProtectedPrivateKey);
const decryptedKeyData = await crypto.subtle.decrypt(
{
name: "AES-GCM",
iv: hexToBuf(encoded.iv),
},
derviedKey,
encryptedKeyData,
);
const pkcs8Bytes = ed25519RawPrivateKeyToPKCS8(decryptedKeyData);
const privateKey = await crypto.subtle.importKey(
"pkcs8",
pkcs8Bytes,
{ name: "Ed25519" },
true,
["sign"],
@ -114,14 +124,20 @@ export class BlahKeyPair {
const iv = bufToHex(ivBuf);
const derviedKey = await pbkdf2Key(password, saltBuf);
const wrappedPrivateKey = await crypto.subtle.wrapKey(
const pkcs8Bytes = await crypto.subtle.exportKey(
"pkcs8",
this.internalPrivateKey,
derviedKey,
);
const rawPrivateKeyBytes = ed25519PKCS8ToRawPrivateKey(pkcs8Bytes);
const wrappedPrivateKey = await crypto.subtle.encrypt(
{
name: "AES-GCM",
iv: ivBuf,
},
derviedKey,
rawPrivateKeyBytes,
);
return {

View file

@ -22,7 +22,7 @@ export async function pbkdf2Key(
passwordKey,
{ name: "AES-GCM", length: 256 },
false,
["wrapKey", "unwrapKey"],
["encrypt", "decrypt"],
);
return key;

View file

@ -9,3 +9,21 @@ export function hexToBuf(hex: string): Uint8Array {
(hex.match(/[\da-f]{2}/gi) ?? []).map((m) => parseInt(m, 16)),
);
}
// https://stackoverflow.com/a/79145876
const ed25519PKCS8Prefix = hexToBuf("302e020100300506032b657004220420");
export function ed25519RawPrivateKeyToPKCS8(
buf: ArrayBufferLike | Uint8Array,
): Uint8Array {
const u8Array = buf instanceof Uint8Array ? buf : new Uint8Array(buf);
const pkcs8 = new Uint8Array(ed25519PKCS8Prefix.length + u8Array.length);
pkcs8.set(ed25519PKCS8Prefix, 0);
pkcs8.set(u8Array, ed25519PKCS8Prefix.length);
return pkcs8;
}
export function ed25519PKCS8ToRawPrivateKey(
buf: ArrayBufferLike | Uint8Array,
): Uint8Array {
const u8Array = buf instanceof Uint8Array ? buf : new Uint8Array(buf);
return u8Array.slice(ed25519PKCS8Prefix.length);
}