import { PaytoUri, TalerSignaturePurpose, bufferForUint32, buildSigPS, createEddsaKeyPair, decodeCrock, decryptWithDerivedKey, eddsaGetPublic, eddsaSign, encodeCrock, encryptWithDerivedKey, getRandomBytesF, stringToBytes, stringifyPaytoUri, } from "@gnu-taler/taler-util"; import { AmlExchangeBackend } from "./types.js"; export interface Account { accountId: AccountId; signingKey: SigningKey; } /** * Restore previous session and unlock account with password * * @param salt string from which crypto params will be derived * @param key secured private key * @param password password for the private key * @returns */ export async function unlockAccount( account: LockedAccount, password: string, ): Promise { const rawKey = decodeCrock(account); const rawPassword = stringToBytes(password); const signingKey = (await decryptWithDerivedKey( rawKey, rawPassword, password, ).catch((e: Error) => { throw new UnwrapKeyError(e.message); })) as SigningKey; const publicKey = eddsaGetPublic(signingKey); const accountId = encodeCrock(publicKey) as AccountId; return { accountId, signingKey }; } export function buildQuerySignature(key: SigningKey): string { const sigBlob = buildSigPS( TalerSignaturePurpose.TALER_SIGNATURE_AML_QUERY, ).build(); return encodeCrock(eddsaSign(sigBlob, key)); } export function buildDecisionSignature( key: SigningKey, payto: PaytoUri, state: AmlExchangeBackend.AmlState, ): string { const sigBlob = buildSigPS(TalerSignaturePurpose.TALER_SIGNATURE_AML_DECISION) .put(decodeCrock(stringifyPaytoUri(payto))) .put(bufferForUint32(state)) .build(); return encodeCrock(eddsaSign(sigBlob, key)); } declare const opaque_Account: unique symbol; export type LockedAccount = string & { [opaque_Account]: true }; declare const opaque_AccountId: unique symbol; export type AccountId = string & { [opaque_AccountId]: true }; declare const opaque_SigningKey: unique symbol; export type SigningKey = Uint8Array & { [opaque_SigningKey]: true }; /** * Create new account (secured private key) * secured with the given password * * @param sessionId * @param password * @returns */ export async function createNewAccount( password: string, ): Promise { const { eddsaPriv, eddsaPub } = createEddsaKeyPair(); const key = stringToBytes(password); const protectedPrivKey = await encryptWithDerivedKey( getRandomBytesF(24), key, eddsaPriv, password, ); const signingKey = eddsaPriv as SigningKey; const accountId = encodeCrock(eddsaPub) as AccountId; const safe = encodeCrock(protectedPrivKey) as LockedAccount; return { accountId, signingKey, safe }; } export class UnwrapKeyError extends Error { public cause: string; constructor(cause: string) { super(`Recovering private key failed on: ${cause}`); this.cause = cause; } }