taler-typescript-core

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

commit 57bae624d26245042ea12b5acadc3638319c5c7f
parent 77552c979f06ad5a169aa7f74c6a34d269c21222
Author: Florian Dold <florian@dold.me>
Date:   Sun, 23 Feb 2025 11:34:25 +0100

fix signing of never timestamps, implement support for attributes in AML decision signature

Diffstat:
Mpackages/taler-util/src/taler-crypto.ts | 11++++++++---
Mpackages/taler-util/src/taler-signatures.ts | 12+++++++++++-
Mpackages/taler-util/src/types-taler-exchange.ts | 22++++++++++++++++++----
3 files changed, 37 insertions(+), 8 deletions(-)

diff --git a/packages/taler-util/src/taler-crypto.ts b/packages/taler-util/src/taler-crypto.ts @@ -1624,18 +1624,23 @@ export function amountToBuffer(amount: AmountLike): Uint8Array { return u8buf; } +const foreverNum = 2n ** 64n - 1n; + export function timestampRoundedToBuffer( ts: TalerProtocolTimestamp, ): Uint8Array { const b = new ArrayBuffer(8); const v = new DataView(b); + const numVal = + ts.t_s === "never" ? foreverNum : BigInt(ts.t_s) * 1000n * 1000n; // The buffer we sign over represents the timestamp in microseconds. if (typeof v.setBigUint64 !== "undefined") { - const s = BigInt(ts.t_s) * BigInt(1000 * 1000); - v.setBigUint64(0, s); + v.setBigUint64(0, numVal); } else { const s = - ts.t_s === "never" ? bigint.zero : bigint(ts.t_s).multiply(1000 * 1000); + ts.t_s === "never" + ? bigint(foreverNum) + : bigint(ts.t_s).multiply(1000 * 1000); const arr = s.toArray(2 ** 8).value; let offset = 8 - arr.length; for (let i = 0; i < arr.length; i++) { diff --git a/packages/taler-util/src/taler-signatures.ts b/packages/taler-util/src/taler-signatures.ts @@ -14,7 +14,7 @@ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> */ -import { canonicalJson } from "./index.js"; +import { canonicalJson, TalerProtocolTimestamp } from "./index.js"; import { bufferForUint64, buildSigPS, @@ -46,6 +46,11 @@ export function signAmlDecision( const flags: number = decision.keep_investigating ? 1 : 0; builder.put(timestampRoundedToBuffer(decision.decision_time)); + builder.put( + timestampRoundedToBuffer( + decision.attributes_expiration ?? TalerProtocolTimestamp.fromSeconds(0), + ), + ); builder.put(decodeCrock(decision.h_payto)); builder.put(hash(stringToBytes(decision.justification))); builder.put(hash(stringToBytes(canonicalJson(decision.properties) + "\0"))); @@ -55,6 +60,11 @@ export function signAmlDecision( } else { builder.put(new Uint8Array(64)); } + if (decision.attributes != null) { + builder.put(hash(stringToBytes(canonicalJson(decision.attributes) + "\0"))); + } else { + builder.put(new Uint8Array(64)); + } builder.put(bufferForUint64(flags)); const sigBlob = builder.build(); diff --git a/packages/taler-util/src/types-taler-exchange.ts b/packages/taler-util/src/types-taler-exchange.ts @@ -1985,12 +1985,26 @@ export interface AmlDecisionRequest { // New since protocol **v20**. keep_investigating: boolean; - // Signature by the AML officer over a TALER_AmlDecisionPS. - // Must have purpose TALER_SIGNATURE_MASTER_AML_KEY. + /** + * Signature by the AML officer over a TALER_AmlDecisionPS. + * Must have purpose TALER_SIGNATURE_MASTER_AML_KEY. + */ officer_sig: EddsaSignatureString; - // When was the decision made? + /** + * When was the decision made? + */ decision_time: Timestamp; + + /** + * When do the attributes expire? + */ + attributes_expiration?: Timestamp; + + /** + * Attributes set by the AML officer. + */ + attributes?: Object; } export interface KycRule { @@ -2054,7 +2068,7 @@ export interface KycAttributeCollectionEvent { // The collected KYC data. NULL if the attribute data could not // be decrypted (internal error of the exchange, likely the // attribute key was changed). - attributes?: Object; + attributes?: { [x: string]: unknown }; // Time when the KYC data was collected collection_time: Timestamp;