1234567891011121314151617import { blake3 } from "hash-wasm";1819const VAULT_ID_INFO = new TextEncoder().encode("peach-vault-id");20const VAULT_ID_HEX_LENGTH = 64; 2122export interface SyncIdentityKeypair {23 publicKey: Uint8Array; 24 secretKey: Uint8Array; 25}2627282930313233343536373839export async function computeVaultId(masterKey: Uint8Array): Promise<string> {40 if (masterKey.length === 0) throw new Error("masterKey must not be empty");41 const buf = new Uint8Array(masterKey.length + VAULT_ID_INFO.length);42 buf.set(masterKey, 0);43 buf.set(VAULT_ID_INFO, masterKey.length);44 const fullHex = await blake3(buf);45 return fullHex.slice(0, VAULT_ID_HEX_LENGTH);46}47484950515253export async function verifyEd25519(54 publicKeyRaw: Uint8Array,55 message: Uint8Array,56 signature: Uint8Array,57): Promise<boolean> {58 if (publicKeyRaw.length !== 32) return false;59 if (signature.length !== 64) return false;60 try {61 const key = await crypto.subtle.importKey(62 "raw",63 publicKeyRaw as Uint8Array<ArrayBuffer>,64 { name: "Ed25519" },65 false,66 ["verify"],67 );68 return await crypto.subtle.verify(69 "Ed25519",70 key,71 signature as Uint8Array<ArrayBuffer>,72 message as Uint8Array<ArrayBuffer>,73 );74 } catch {75 return false;76 }77}787980export async function signEd25519(81 privateKey: CryptoKey,82 message: Uint8Array,83): Promise<Uint8Array> {84 const sig = await crypto.subtle.sign(85 "Ed25519",86 privateKey,87 message as Uint8Array<ArrayBuffer>,88 );89 return new Uint8Array(sig);90}9192export function pubkeyToHex(pk: Uint8Array): string {93 let s = "";94 for (let i = 0; i < pk.length; i++) {95 s += pk[i].toString(16).padStart(2, "0");96 }97 return s;98}99100export function hexToPubkey(hex: string): Uint8Array {101 if (hex.length !== 64) throw new Error("public key must be exactly 32 bytes");102 const out = new Uint8Array(32);103 for (let i = 0; i < out.length; i++) {104 const byte = parseInt(hex.slice(i * 2, i * 2 + 2), 16);105 if (Number.isNaN(byte)) throw new Error(`invalid hex at offset ${i * 2}`);106 out[i] = byte;107 }108 return out;109}