129 lines
3.2 KiB
TypeScript
129 lines
3.2 KiB
TypeScript
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<Account> {
|
|
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<Account & { safe: LockedAccount }> {
|
|
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;
|
|
}
|
|
}
|