From 9f0429cb2f8ad9cb2e98a787139602d913c1aefa Mon Sep 17 00:00:00 2001 From: Florian Dold Date: Wed, 17 Nov 2021 10:23:22 +0100 Subject: wallet: implement exchange protocol v9 --- packages/taler-util/src/backupTypes.ts | 5 ++- packages/taler-util/src/helpers.ts | 11 +++-- packages/taler-util/src/talerCrypto.ts | 15 +++++++ packages/taler-util/src/talerTypes.ts | 77 ++++++++++++++++++++++++++++++---- packages/taler-util/src/walletTypes.ts | 8 ++-- 5 files changed, 96 insertions(+), 20 deletions(-) (limited to 'packages/taler-util/src') diff --git a/packages/taler-util/src/backupTypes.ts b/packages/taler-util/src/backupTypes.ts index 70e52e63b..ecdd6fdf8 100644 --- a/packages/taler-util/src/backupTypes.ts +++ b/packages/taler-util/src/backupTypes.ts @@ -53,6 +53,7 @@ /** * Imports. */ +import { DenominationPubKey, UnblindedSignature } from "./talerTypes.js"; import { Duration, Timestamp } from "./time.js"; /** @@ -440,7 +441,7 @@ export interface BackupCoin { /** * Unblinded signature by the exchange. */ - denom_sig: string; + denom_sig: UnblindedSignature; /** * Amount that's left on the coin. @@ -831,7 +832,7 @@ export interface BackupDenomination { /** * The denomination public key. */ - denom_pub: string; + denom_pub: DenominationPubKey; /** * Fee for withdrawing. diff --git a/packages/taler-util/src/helpers.ts b/packages/taler-util/src/helpers.ts index 089602c9d..6c836c482 100644 --- a/packages/taler-util/src/helpers.ts +++ b/packages/taler-util/src/helpers.ts @@ -94,7 +94,7 @@ export function canonicalJson(obj: any): string { /** * Lexically compare two strings. */ -export function strcmp(s1: string, s2: string): number { +export function strcmp(s1: string, s2: string): -1 | 0 | 1 { if (s1 < s2) { return -1; } @@ -113,15 +113,14 @@ export function j2s(x: any): string { /** * Use this to filter null or undefined from an array in a type-safe fashion - * + * * example: * const array: Array = [undefined, null] * const filtered: Array = array.filter(notEmpty) - * - * @param value - * @returns + * + * @param value + * @returns */ export function notEmpty(value: T | null | undefined): value is T { return value !== null && value !== undefined; } - diff --git a/packages/taler-util/src/talerCrypto.ts b/packages/taler-util/src/talerCrypto.ts index d8ac75dc0..b107786cd 100644 --- a/packages/taler-util/src/talerCrypto.ts +++ b/packages/taler-util/src/talerCrypto.ts @@ -24,6 +24,7 @@ import * as nacl from "./nacl-fast.js"; import { kdf } from "./kdf.js"; import bigint from "big-integer"; +import { DenominationPubKey } from "./talerTypes.js"; export function getRandomBytes(n: number): Uint8Array { return nacl.randomBytes(n); @@ -348,6 +349,20 @@ export function hash(d: Uint8Array): Uint8Array { return nacl.hash(d); } +export function hashDenomPub(pub: DenominationPubKey): Uint8Array { + if (pub.cipher !== 1) { + throw Error("unsupported cipher"); + } + const pubBuf = decodeCrock(pub.rsa_public_key); + const hashInputBuf = new ArrayBuffer(pubBuf.length + 4 + 4); + const uint8ArrayBuf = new Uint8Array(hashInputBuf); + const dv = new DataView(hashInputBuf); + dv.setUint32(0, pub.age_mask ?? 0); + dv.setUint32(4, pub.cipher); + uint8ArrayBuf.set(pubBuf, 8); + return nacl.hash(uint8ArrayBuf); +} + export function eddsaSign(msg: Uint8Array, eddsaPriv: Uint8Array): Uint8Array { const pair = nacl.crypto_sign_keyPair_fromSeed(eddsaPriv); return nacl.sign_detached(msg, pair.secretKey); diff --git a/packages/taler-util/src/talerTypes.ts b/packages/taler-util/src/talerTypes.ts index 56110ec1e..04d700483 100644 --- a/packages/taler-util/src/talerTypes.ts +++ b/packages/taler-util/src/talerTypes.ts @@ -59,7 +59,7 @@ export class Denomination { /** * Public signing key of the denomination. */ - denom_pub: string; + denom_pub: DenominationPubKey; /** * Fee for withdrawing. @@ -158,7 +158,7 @@ export interface RecoupRequest { /** * Signature over the coin public key by the denomination. */ - denom_sig: string; + denom_sig: UnblindedSignature; /** * Coin public key of the coin we want to refund. @@ -198,6 +198,11 @@ export interface RecoupConfirmation { old_coin_pub?: string; } +export interface UnblindedSignature { + cipher: DenomKeyType.Rsa; + rsa_signature: string; +} + /** * Deposit permission for a single coin. */ @@ -213,7 +218,7 @@ export interface CoinDepositPermission { /** * Signature made by the denomination public key. */ - ub_sig: string; + ub_sig: UnblindedSignature; /** * The denomination public key associated with this coin. */ @@ -779,8 +784,38 @@ export class TipPickupGetResponse { expiration: Timestamp; } +export enum DenomKeyType { + Rsa = 1, + ClauseSchnorr = 2, +} + +export interface RsaBlindedDenominationSignature { + cipher: DenomKeyType.Rsa; + blinded_rsa_signature: string; +} + +export interface CSBlindedDenominationSignature { + cipher: DenomKeyType.ClauseSchnorr; +} + +export type BlindedDenominationSignature = + | RsaBlindedDenominationSignature + | CSBlindedDenominationSignature; + +export const codecForBlindedDenominationSignature = () => + buildCodecForUnion() + .discriminateOn("cipher") + .alternative(1, codecForRsaBlindedDenominationSignature()) + .build("BlindedDenominationSignature"); + +export const codecForRsaBlindedDenominationSignature = () => + buildCodecForObject() + .property("cipher", codecForConstNumber(1)) + .property("blinded_rsa_signature", codecForString()) + .build("RsaBlindedDenominationSignature"); + export class WithdrawResponse { - ev_sig: string; + ev_sig: BlindedDenominationSignature; } /** @@ -792,7 +827,7 @@ export interface CoinDumpJson { /** * The coin's denomination's public key. */ - denom_pub: string; + denom_pub: DenominationPubKey; /** * Hash of denom_pub. */ @@ -875,7 +910,7 @@ export interface ExchangeMeltResponse { } export interface ExchangeRevealItem { - ev_sig: string; + ev_sig: BlindedDenominationSignature; } export interface ExchangeRevealResponse { @@ -994,6 +1029,30 @@ export interface BankWithdrawalOperationPostResponse { transfer_done: boolean; } +export type DenominationPubKey = RsaDenominationPubKey | CsDenominationPubKey; + +export interface RsaDenominationPubKey { + cipher: 1; + rsa_public_key: string; + age_mask?: number; +} + +export interface CsDenominationPubKey { + cipher: 2; +} + +export const codecForDenominationPubKey = () => + buildCodecForUnion() + .discriminateOn("cipher") + .alternative(1, codecForRsaDenominationPubKey()) + .build("DenominationPubKey"); + +export const codecForRsaDenominationPubKey = () => + buildCodecForObject() + .property("cipher", codecForConstNumber(1)) + .property("rsa_public_key", codecForString()) + .build("DenominationPubKey"); + export const codecForBankWithdrawalOperationPostResponse = (): Codec => buildCodecForObject() .property("transfer_done", codecForBoolean()) @@ -1008,7 +1067,7 @@ export type CoinPublicKeyString = string; export const codecForDenomination = (): Codec => buildCodecForObject() .property("value", codecForString()) - .property("denom_pub", codecForString()) + .property("denom_pub", codecForDenominationPubKey()) .property("fee_withdraw", codecForString()) .property("fee_deposit", codecForString()) .property("fee_refresh", codecForString()) @@ -1242,7 +1301,7 @@ export const codecForRecoupConfirmation = (): Codec => export const codecForWithdrawResponse = (): Codec => buildCodecForObject() - .property("ev_sig", codecForString()) + .property("ev_sig", codecForBlindedDenominationSignature()) .build("WithdrawResponse"); export const codecForMerchantPayResponse = (): Codec => @@ -1260,7 +1319,7 @@ export const codecForExchangeMeltResponse = (): Codec => export const codecForExchangeRevealItem = (): Codec => buildCodecForObject() - .property("ev_sig", codecForString()) + .property("ev_sig", codecForBlindedDenominationSignature()) .build("ExchangeRevealItem"); export const codecForExchangeRevealResponse = (): Codec => diff --git a/packages/taler-util/src/walletTypes.ts b/packages/taler-util/src/walletTypes.ts index 6e68ee080..879640e82 100644 --- a/packages/taler-util/src/walletTypes.ts +++ b/packages/taler-util/src/walletTypes.ts @@ -48,6 +48,8 @@ import { AmountString, codecForContractTerms, ContractTerms, + DenominationPubKey, + UnblindedSignature, } from "./talerTypes.js"; import { OrderShortInfo, codecForOrderShortInfo } from "./transactionsTypes.js"; import { BackupRecovery } from "./backupTypes.js"; @@ -454,7 +456,7 @@ export interface PlanchetCreationResult { coinPriv: string; reservePub: string; denomPubHash: string; - denomPub: string; + denomPub: DenominationPubKey; blindingKey: string; withdrawSig: string; coinEv: string; @@ -467,7 +469,7 @@ export interface PlanchetCreationRequest { coinIndex: number; value: AmountJson; feeWithdraw: AmountJson; - denomPub: string; + denomPub: DenominationPubKey; reservePub: string; reservePriv: string; } @@ -514,7 +516,7 @@ export interface DepositInfo { feeDeposit: AmountJson; wireInfoHash: string; denomPubHash: string; - denomSig: string; + denomSig: UnblindedSignature; } export interface ExchangesListRespose { -- cgit v1.2.3