import { Amounts, TalerSignaturePurpose, amountToBuffer, bufferForUint32, buildSigPS, createEddsaKeyPair, decodeCrock, decryptWithDerivedKey, eddsaGetPublic, eddsaSign, encodeCrock, encryptWithDerivedKey, getRandomBytesF, hash, hashTruncate32, stringToBytes, timestampRoundedToBuffer } 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, decision: AmlExchangeBackend.AmlDecision, ): string { const zero = new Uint8Array(new ArrayBuffer(64)) const sigBlob = buildSigPS(TalerSignaturePurpose.TALER_SIGNATURE_AML_DECISION) //TODO: new need the null terminator, also in the exchange .put(hash(stringToBytes(decision.justification)))//check null .put(timestampRoundedToBuffer(decision.decision_time)) .put(amountToBuffer(decision.new_threshold)) .put(decodeCrock(decision.h_payto)) .put(zero) //kyc_requirement .put(bufferForUint32(decision.new_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; } }