taler-typescript-core

Wallet core logic and WebUIs for various components
Log | Files | Refs | Submodules | README | LICENSE

commit 35047ee77b6c9dbc82f3815e716ea5e4688a6ad0
parent 0997ade82af82e625fb0b4da40f2543bac38992d
Author: Antoine A <>
Date:   Tue, 30 Jun 2026 12:51:07 +0200

prepared-transfer-api: support new signatures

Diffstat:
Mpackages/taler-harness/src/integrationtests/test-prepared-transfer.ts | 41+++++++++++++++++++----------------------
Mpackages/taler-util/src/http-client/bank-prepared.ts | 4++--
Mpackages/taler-util/src/taler-crypto.ts | 9+++++++++
Mpackages/taler-util/src/taler_signatures.ts | 5+++++
Mpackages/taler-util/src/types-taler-prepared-transfer.ts | 8++++----
Mpackages/taler-wallet-core/src/crypto/cryptoImplementation.ts | 68+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
Mpackages/taler-wallet-core/src/crypto/cryptoTypes.ts | 23+++++++++++++++++++++++
7 files changed, 129 insertions(+), 29 deletions(-)

diff --git a/packages/taler-harness/src/integrationtests/test-prepared-transfer.ts b/packages/taler-harness/src/integrationtests/test-prepared-transfer.ts @@ -19,11 +19,11 @@ */ import { alternativeOrThrow, - encodeCrock, succeedOrThrow, TalerCoreBankHttpClient, TalerErrorCode, TalerPreparedTransferHttpClient, + TalerProtocolTimestamp, TalerWireGatewayHttpClient, } from "@gnu-taler/taler-util"; import { createSyncCryptoApi } from "@gnu-taler/taler-wallet-core"; @@ -86,6 +86,15 @@ export async function runPreparedTransferTest(t: GlobalTestState) { // Check registration const auth_keypair = await cryptoApi.createEddsaKeypair({}); const reserve_keypair = await cryptoApi.createEddsaKeypair({}); + const sigRes = await cryptoApi.signPreparedTransferRegister({ + credit_account: exchangePaytoUri, + credit_amount: "TESTKUDOS:10", + type: "reserve", + alg: "EdDSA", + account_pub: reserve_keypair.pub, + authorization_priv: auth_keypair.priv, + recurrent: false, + }); succeedOrThrow( await preparedClient.register({ credit_account: exchangePaytoUri, @@ -94,29 +103,22 @@ export async function runPreparedTransferTest(t: GlobalTestState) { alg: "EdDSA", account_pub: reserve_keypair.pub, authorization_pub: auth_keypair.pub, - authorization_sig: ( - await cryptoApi.eddsaSign({ - msg: reserve_keypair.pub, - priv: auth_keypair.priv, - }) - ).sig, + authorization_sig: sigRes.sig, recurrent: false, }), ); // Check unregistration - const timestamp = new Date().toISOString(); - const uint8 = new TextEncoder().encode(timestamp); + const timestamp = TalerProtocolTimestamp.now(); + const sigUnres = await cryptoApi.signPreparedTransferUnregister({ + timestamp, + authorization_priv: auth_keypair.priv, + }); succeedOrThrow( await preparedClient.unregister({ - timestamp: timestamp, + timestamp, authorization_pub: auth_keypair.pub, - authorization_sig: ( - await cryptoApi.eddsaSign({ - msg: encodeCrock(uint8), - priv: auth_keypair.priv, - }) - ).sig, + authorization_sig: sigUnres.sig, }), ); @@ -140,12 +142,7 @@ export async function runPreparedTransferTest(t: GlobalTestState) { alg: "EdDSA", account_pub: reserve_keypair.pub, authorization_pub: auth_keypair.pub, - authorization_sig: ( - await cryptoApi.eddsaSign({ - msg: reserve_keypair.pub, - priv: auth_keypair.priv, - }) - ).sig, + authorization_sig: sigRes.sig, recurrent: false, }), ); diff --git a/packages/taler-util/src/http-client/bank-prepared.ts b/packages/taler-util/src/http-client/bank-prepared.ts @@ -103,10 +103,10 @@ export class TalerPreparedTransferHttpClient { method: "POST", body, }); - console.log(body); switch (resp.status) { case HttpStatusCode.Ok: return opSuccessFromHttp(resp, codecForRegistrationResponse()); + case HttpStatusCode.Forbidden: case HttpStatusCode.BadRequest: case HttpStatusCode.Unauthorized: case HttpStatusCode.NotFound: @@ -133,13 +133,13 @@ export class TalerPreparedTransferHttpClient { case HttpStatusCode.BadRequest: case HttpStatusCode.Unauthorized: case HttpStatusCode.NotFound: + case HttpStatusCode.Forbidden: return opKnownHttpFailure(resp.status, resp); case HttpStatusCode.Conflict: { const body = await readTalerErrorResponse(resp); const details = codecForTalerErrorDetail().decode(body); switch (details.code) { case TalerErrorCode.BANK_OLD_TIMESTAMP: - case TalerErrorCode.BANK_BAD_SIGNATURE: return opKnownTalerFailure(resp, details.code, details); default: return opKnownHttpFailure(resp.status, resp, details); diff --git a/packages/taler-util/src/taler-crypto.ts b/packages/taler-util/src/taler-crypto.ts @@ -1025,6 +1025,14 @@ export interface FreshCoin { ageCommitmentProof: AgeCommitmentProof | undefined; } +export function bufferForUint16(n: number): Uint8Array { + const arrBuf = new ArrayBuffer(2); + const buf = new Uint8Array(arrBuf); + const dv = new DataView(arrBuf); + dv.setUint16(0, n); + return buf; +} + export function bufferForUint32(n: number): Uint8Array { const arrBuf = new ArrayBuffer(4); const buf = new Uint8Array(arrBuf); @@ -1142,6 +1150,7 @@ export class SignaturePurposeBuilder { const dvbuf = new DataView(buf); dvbuf.setUint32(0, payloadLen + 4 + 4); dvbuf.setUint32(4, this.purposeNum); + console.log((u8buf)) return u8buf; } } diff --git a/packages/taler-util/src/taler_signatures.ts b/packages/taler-util/src/taler_signatures.ts @@ -401,6 +401,11 @@ export enum TalerSignaturePurpose { */ WALLET_TOKEN_USE = 1222, + + WALLET_PREPARED_TRANSFER_REGISTER = 1224, + + WALLET_PREPARED_TRANSFER_UNREGISTER = 1225, + /** * Signature on a denomination key announcement. diff --git a/packages/taler-util/src/types-taler-prepared-transfer.ts b/packages/taler-util/src/types-taler-prepared-transfer.ts @@ -30,7 +30,7 @@ import { codecOptional, } from "./codec.js"; import { TalerPreparedTransferApi } from "./index.js"; -import { codecForTimestamp } from "./time.js"; +import { codecForTimestamp, TalerProtocolTimestamp } from "./time.js"; import { AmountString, EddsaPublicKey, @@ -134,8 +134,8 @@ export interface RegistrationResponse { } export interface Unregistration { - // Current timestamp in the ISO 8601 - timestamp: string; + // Current timestamp + timestamp: TalerProtocolTimestamp; // Public key used for registration authorization_pub: EddsaPublicKey; @@ -225,7 +225,7 @@ export const codecForRegistrationResponse = export const codecForUnregistration = (): Codec<TalerPreparedTransferApi.Unregistration> => buildCodecForObject<TalerPreparedTransferApi.Unregistration>() - .property("timestamp", codecForString()) + .property("timestamp", codecForTimestamp) .property("authorization_pub", codecForEddsaPublicKey()) .property("authorization_sig", codecForEddsaSignature()) .build("TalerPreparedTransferApi.Unregistration"); diff --git a/packages/taler-wallet-core/src/crypto/cryptoImplementation.ts b/packages/taler-wallet-core/src/crypto/cryptoImplementation.ts @@ -31,6 +31,7 @@ import { Amounts, AmountString, BlindedDenominationSignature, + bufferForUint16, bufferForUint32, bufferForUint64, bufferFromAmount, @@ -119,6 +120,10 @@ import { SignContractTermsHashResponse, SignDeletePurseRequest, SignDeletePurseResponse, + SignPreparedTransferRegisterRequest, + SignPreparedTransferRegisterResponse, + SignPreparedTransferUnregisterRequest, + SignPreparedTransferUnregisterResponse, SignPurseMergeRequest, SignPurseMergeResponse, SignRefundRequest, @@ -310,6 +315,14 @@ export interface TalerCryptoInterface { signContractTermsHash( req: SignContractTermsHashRequest, ): Promise<SignContractTermsHashResponse>; + + signPreparedTransferRegister( + req: SignPreparedTransferRegisterRequest, + ): Promise<SignPreparedTransferRegisterResponse>; + + signPreparedTransferUnregister( + req: SignPreparedTransferUnregisterRequest, + ): Promise<SignPreparedTransferUnregisterResponse>; } /** @@ -549,6 +562,16 @@ export const nullCrypto: TalerCryptoInterface = { ): Promise<ValidationResult> { throw new Error("Function not implemented."); }, + signPreparedTransferRegister: function ( + req: SignPreparedTransferRegisterRequest, + ): Promise<SignPreparedTransferRegisterResponse> { + throw new Error("Function not implemented."); + }, + signPreparedTransferUnregister: function ( + req: SignPreparedTransferUnregisterRequest, + ): Promise<SignPreparedTransferUnregisterResponse> { + throw new Error("Function not implemented."); + }, }; export type WithArg<X> = X extends (req: infer T) => infer R @@ -2330,7 +2353,50 @@ export const nativeCryptoR: TalerCryptoInterfaceR = { sig: encodeCrock(sig), }; }, -}; + async signPreparedTransferRegister( + tci: TalerCryptoInterfaceR, + req: SignPreparedTransferRegisterRequest, + ): Promise<SignPreparedTransferRegisterResponse> { + + let b = buildSigPS( + TalerSignaturePurpose.WALLET_PREPARED_TRANSFER_REGISTER + ) + .put(hashTruncate32(stringToBytes(req.credit_account + "\0"))) + .put(bufferFromAmount(req.credit_amount)); + if (req.type == "reserve") { + b = b.put(bufferForUint32(1)); + } else { + b = b.put(bufferForUint32(2)); + } + + if (req.recurrent) { + b = b.put(bufferForUint16(2)); + } else { + b = b.put(bufferForUint16(1)); + } + + b = b.put(bufferForUint16(1)).put(decodeCrock(req.account_pub)); + + return await tci.eddsaSign(tci, { + msg: encodeCrock(b.build()), + priv: req.authorization_priv, + }) + }, + async signPreparedTransferUnregister( + tci: TalerCryptoInterfaceR, + req: SignPreparedTransferUnregisterRequest, + ): Promise<SignPreparedTransferUnregisterResponse> { + + const b = buildSigPS( + TalerSignaturePurpose.WALLET_PREPARED_TRANSFER_UNREGISTER + ) + .put(timestampRoundedToBuffer(req.timestamp)).build(); + return await tci.eddsaSign(tci, { + msg: encodeCrock(b), + priv: req.authorization_priv, + }) + } +} export interface EddsaSignRequest { msg: string; diff --git a/packages/taler-wallet-core/src/crypto/cryptoTypes.ts b/packages/taler-wallet-core/src/crypto/cryptoTypes.ts @@ -387,3 +387,26 @@ export interface SignReservePurseCreateResponse { purseSig: string; } + +export interface SignPreparedTransferRegisterRequest { + credit_account: string; + credit_amount: AmountString, + type: 'reserve' | 'kyc', + recurrent: boolean, + alg: 'EdDSA' + account_pub: string + authorization_priv: string; +} + +export interface SignPreparedTransferRegisterResponse { + sig: string; +} + +export interface SignPreparedTransferUnregisterRequest { + timestamp: TalerProtocolTimestamp; + authorization_priv: string; +} + +export interface SignPreparedTransferUnregisterResponse { + sig: string; +}