summaryrefslogtreecommitdiff
path: root/packages/taler-util/src
diff options
context:
space:
mode:
authorFlorian Dold <florian@dold.me>2022-04-19 17:12:43 +0200
committerFlorian Dold <florian@dold.me>2022-04-27 00:50:17 +0200
commita165afa6824980c409d7c2e22e24171e536800e0 (patch)
tree0e8491f092aba2280655ee4728fef0ca02bb8387 /packages/taler-util/src
parent9b85d139bf7bdc360ea0894e09f6115cd9d472d8 (diff)
downloadwallet-core-a165afa6824980c409d7c2e22e24171e536800e0.tar.gz
wallet-core-a165afa6824980c409d7c2e22e24171e536800e0.tar.bz2
wallet-core-a165afa6824980c409d7c2e22e24171e536800e0.zip
wallet-core: implement age restriction support
Diffstat (limited to 'packages/taler-util/src')
-rw-r--r--packages/taler-util/src/nacl-fast.ts33
-rw-r--r--packages/taler-util/src/talerCrypto.test.ts86
-rw-r--r--packages/taler-util/src/talerCrypto.ts236
-rw-r--r--packages/taler-util/src/talerTypes.ts20
-rw-r--r--packages/taler-util/src/walletTypes.ts15
5 files changed, 357 insertions, 33 deletions
diff --git a/packages/taler-util/src/nacl-fast.ts b/packages/taler-util/src/nacl-fast.ts
index 82bdc7cec..c45674bef 100644
--- a/packages/taler-util/src/nacl-fast.ts
+++ b/packages/taler-util/src/nacl-fast.ts
@@ -1769,7 +1769,7 @@ function crypto_scalarmult_base(q: Uint8Array, n: Uint8Array): number {
return crypto_scalarmult(q, n, _9);
}
-function crypto_scalarmult_noclamp(
+export function crypto_scalarmult_noclamp(
q: Uint8Array,
n: Uint8Array,
p: Uint8Array,
@@ -3033,6 +3033,18 @@ export function crypto_core_ed25519_scalar_add(
return o;
}
+/**
+ * Reduce a scalar "s" to "s mod L". The input can be up to 64 bytes long.
+ */
+export function crypto_core_ed25519_scalar_reduce(x: Uint8Array): Uint8Array {
+ const len = x.length;
+ const z = new Float64Array(64);
+ for (let i = 0; i < len; i++) z[i] = x[i];
+ const o = new Uint8Array(32);
+ modL(o, z);
+ return o;
+}
+
export function crypto_core_ed25519_scalar_sub(
x: Uint8Array,
y: Uint8Array,
@@ -3063,11 +3075,7 @@ export function crypto_edx25519_private_key_create_from_seed(
}
export function crypto_edx25519_get_public(priv: Uint8Array): Uint8Array {
- const pub = new Uint8Array(32);
- if (0 != crypto_scalarmult_base_noclamp(pub.subarray(32), priv)) {
- throw Error();
- }
- return pub;
+ return crypto_scalarmult_ed25519_base_noclamp(priv.subarray(0, 32));
}
export function crypto_edx25519_sign_detached(
@@ -3076,19 +3084,16 @@ export function crypto_edx25519_sign_detached(
pkx: Uint8Array,
): Uint8Array {
const n: number = m.length;
- const d = new Uint8Array(64),
- h = new Uint8Array(64),
- r = new Uint8Array(64);
+ const h = new Uint8Array(64);
+ const r = new Uint8Array(64);
let i, j;
const x = new Float64Array(64);
const p = [gf(), gf(), gf(), gf()];
- for (i = 0; i < 64; i++) d[i] = skx[i];
-
const sm = new Uint8Array(n + 64);
for (i = 0; i < n; i++) sm[64 + i] = m[i];
- for (i = 0; i < 32; i++) sm[32 + i] = d[32 + i];
+ for (i = 0; i < 32; i++) sm[32 + i] = skx[32 + i];
crypto_hash(r, sm.subarray(32), n + 32);
reduce(r);
@@ -3103,12 +3108,12 @@ export function crypto_edx25519_sign_detached(
for (i = 0; i < 32; i++) x[i] = r[i];
for (i = 0; i < 32; i++) {
for (j = 0; j < 32; j++) {
- x[i + j] += h[i] * d[j];
+ x[i + j] += h[i] * skx[j];
}
}
modL(sm.subarray(32), x);
- return sm.subarray(64);
+ return sm.subarray(0, 64);
}
export function crypto_edx25519_sign_detached_verify(
diff --git a/packages/taler-util/src/talerCrypto.test.ts b/packages/taler-util/src/talerCrypto.test.ts
index 70ad8a614..5e8f37d80 100644
--- a/packages/taler-util/src/talerCrypto.test.ts
+++ b/packages/taler-util/src/talerCrypto.test.ts
@@ -34,6 +34,10 @@ import {
scalarMultBase25519,
deriveSecrets,
calcRBlind,
+ Edx25519,
+ getRandomBytes,
+ bigintToNaclArr,
+ bigintFromNaclArr,
} from "./talerCrypto.js";
import { sha512, kdf } from "./kdf.js";
import * as nacl from "./nacl-fast.js";
@@ -44,6 +48,7 @@ import { initNodePrng } from "./prng-node.js";
initNodePrng();
import bigint from "big-integer";
import { AssertionError } from "assert";
+import BigInteger from "big-integer";
test("encoding", (t) => {
const s = "Hello, World";
@@ -343,9 +348,86 @@ test("taler CS blind c", async (t) => {
};
const sig = await csUnblind(bseed, rPub, pub, b, blindsig);
- t.deepEqual(sig.s, decodeCrock("F4ZKMFW3Q7DFN0N94KAMG2JFFHAC362T0QZ6ZCVZ73RS8P91CR70"));
- t.deepEqual(sig.rPub, decodeCrock("CHK7JC4SXZ4Y9RDA3881S82F7BP99H35Q361WR6RBXN5YN2ZM1M0"));
+ t.deepEqual(
+ sig.s,
+ decodeCrock("F4ZKMFW3Q7DFN0N94KAMG2JFFHAC362T0QZ6ZCVZ73RS8P91CR70"),
+ );
+ t.deepEqual(
+ sig.rPub,
+ decodeCrock("CHK7JC4SXZ4Y9RDA3881S82F7BP99H35Q361WR6RBXN5YN2ZM1M0"),
+ );
const res = await csVerify(decodeCrock(msg_hash), sig, pub);
t.deepEqual(res, true);
});
+
+test("bigint/nacl conversion", async (t) => {
+ const b1 = BigInteger(42);
+ const n1 = bigintToNaclArr(b1, 32);
+ t.is(n1[0], 42);
+ t.is(n1.length, 32);
+ const b2 = bigintFromNaclArr(n1);
+ t.true(b1.eq(b2));
+});
+
+test("taler age restriction crypto", async (t) => {
+ const priv1 = await Edx25519.keyCreate();
+ const pub1 = await Edx25519.getPublic(priv1);
+
+ const seed = encodeCrock(getRandomBytes(32));
+
+ const priv2 = await Edx25519.privateKeyDerive(priv1, seed);
+ const pub2 = await Edx25519.publicKeyDerive(pub1, seed);
+
+ const pub2Ref = await Edx25519.getPublic(priv2);
+
+ t.is(pub2, pub2Ref);
+});
+
+test("edx signing", async (t) => {
+ const priv1 = await Edx25519.keyCreate();
+ const pub1 = await Edx25519.getPublic(priv1);
+
+ const msg = stringToBytes("hello world");
+
+ const sig = nacl.crypto_edx25519_sign_detached(
+ msg,
+ decodeCrock(priv1),
+ decodeCrock(pub1),
+ );
+
+ t.true(
+ nacl.crypto_edx25519_sign_detached_verify(msg, sig, decodeCrock(pub1)),
+ );
+
+ sig[0]++;
+
+ t.false(
+ nacl.crypto_edx25519_sign_detached_verify(msg, sig, decodeCrock(pub1)),
+ );
+});
+
+test("edx test vector", async (t) => {
+ // Generated by gnunet-crypto-tvg
+ const tv = {
+ operation: "edx25519_derive",
+ priv1_edx:
+ "216KF1XM46K4JN8TX3Z8HNRX1DX4WRMX1BTCQM3KBS83PYKFY1GV6XRNBYRC5YM02HVDX8BDR20V7A27YX4MZJ8X8K0ADPZ43BD1GXG",
+ pub1_edx: "RKGRRG74SZ8PKF8SYG5SSDY8VRCYYGY5N2AKAJCG0103Z3JK6HTG",
+ seed: "EFK7CYT98YWGPNZNHPP84VJZDMXD5A41PP3E94NSAQZXRCAKVVXHAQNXG9XM2MAND2FJ56ZM238KGDCF3B0KCWNZCYKKHKDB56X6QA0",
+ priv2_edx:
+ "JRV3S06REHQV90E4HJA1FAMCVDBZZAZP9C6N2WF01MSR3CD5KM28QM7HTGGAV6MBJZ73QJ8PSZFA0D6YENJ7YT97344FDVVCGVAFNER",
+ pub2_edx: "ZB546ZC7ZP16DB99AMK67WNZ67WZFPWMRY67Y4PZR9YR1D82GVZ0",
+ };
+
+ {
+ const pub1Prime = await Edx25519.getPublic(tv.priv1_edx);
+ t.is(pub1Prime, tv.pub1_edx);
+ }
+
+ const pub2Prime = await Edx25519.publicKeyDerive(tv.pub1_edx, tv.seed);
+ t.is(pub2Prime, tv.pub2_edx);
+
+ const priv2Prime = await Edx25519.privateKeyDerive(tv.priv1_edx, tv.seed);
+ t.is(priv2Prime, tv.priv2_edx);
+});
diff --git a/packages/taler-util/src/talerCrypto.ts b/packages/taler-util/src/talerCrypto.ts
index 282d22d8b..228dc3269 100644
--- a/packages/taler-util/src/talerCrypto.ts
+++ b/packages/taler-util/src/talerCrypto.ts
@@ -27,6 +27,7 @@ import bigint from "big-integer";
import {
Base32String,
CoinEnvelope,
+ CoinPublicKeyString,
DenominationPubKey,
DenomKeyType,
HashCodeString,
@@ -643,6 +644,17 @@ export function hashCoinEvInner(
}
}
+export function hashCoinPub(
+ coinPub: CoinPublicKeyString,
+ ach?: HashCodeString,
+): Uint8Array {
+ if (!ach) {
+ return hash(decodeCrock(coinPub));
+ }
+
+ return hash(typedArrayConcat([decodeCrock(coinPub), decodeCrock(ach)]));
+}
+
/**
* Hash a denomination public key.
*/
@@ -652,6 +664,7 @@ export function hashDenomPub(pub: DenominationPubKey): Uint8Array {
const hashInputBuf = new ArrayBuffer(pubBuf.length + 4 + 4);
const uint8ArrayBuf = new Uint8Array(hashInputBuf);
const dv = new DataView(hashInputBuf);
+ logger.info("age_mask", pub.age_mask);
dv.setUint32(0, pub.age_mask ?? 0);
dv.setUint32(4, DenomKeyType.toIntTag(pub.cipher));
uint8ArrayBuf.set(pubBuf, 8);
@@ -705,6 +718,14 @@ export function bufferForUint32(n: number): Uint8Array {
return buf;
}
+export function bufferForUint8(n: number): Uint8Array {
+ const arrBuf = new ArrayBuffer(1);
+ const buf = new Uint8Array(arrBuf);
+ const dv = new DataView(arrBuf);
+ dv.setUint8(0, n);
+ return buf;
+}
+
export function setupTipPlanchet(
secretSeed: Uint8Array,
coinNumber: number,
@@ -753,6 +774,7 @@ export enum TalerSignaturePurpose {
WALLET_COIN_RECOUP = 1203,
WALLET_COIN_LINK = 1204,
WALLET_COIN_RECOUP_REFRESH = 1206,
+ WALLET_AGE_ATTESTATION = 1207,
EXCHANGE_CONFIRM_RECOUP = 1039,
EXCHANGE_CONFIRM_RECOUP_REFRESH = 1041,
ANASTASIS_POLICY_UPLOAD = 1400,
@@ -807,6 +829,25 @@ export type Edx25519PublicKey = FlavorP<string, "Edx25519PublicKey", 32>;
export type Edx25519PrivateKey = FlavorP<string, "Edx25519PrivateKey", 64>;
export type Edx25519Signature = FlavorP<string, "Edx25519Signature", 64>;
+/**
+ * Convert a big integer to a fixed-size, little-endian array.
+ */
+export function bigintToNaclArr(
+ x: bigint.BigInteger,
+ size: number,
+): Uint8Array {
+ const byteArr = new Uint8Array(size);
+ const arr = x.toArray(256).value.reverse();
+ byteArr.set(arr, 0);
+ return byteArr;
+}
+
+export function bigintFromNaclArr(arr: Uint8Array): bigint.BigInteger {
+ let rev = new Uint8Array(arr);
+ rev = rev.reverse();
+ return bigint.fromArray(Array.from(rev), 256, false);
+}
+
export namespace Edx25519 {
const revL = [
0xed, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58, 0xd6, 0x9c, 0xf7, 0xa2,
@@ -846,9 +887,9 @@ export namespace Edx25519 {
): Promise<OpaqueData> {
const res = kdfKw({
outputLength: 64,
- salt: stringToBytes("edx2559-derivation"),
+ salt: decodeCrock(seed),
ikm: decodeCrock(pub),
- info: decodeCrock(seed),
+ info: stringToBytes("edx2559-derivation"),
});
return encodeCrock(res);
@@ -860,28 +901,191 @@ export namespace Edx25519 {
): Promise<Edx25519PrivateKey> {
const pub = await getPublic(priv);
const privDec = decodeCrock(priv);
- const privA = privDec.subarray(0, 32).reverse();
- const a = bigint.fromArray(Array.from(privA), 256, false);
+ const a = bigintFromNaclArr(privDec.subarray(0, 32));
+ const factorEnc = await deriveFactor(pub, seed);
+ const factorModL = bigintFromNaclArr(decodeCrock(factorEnc)).mod(L);
+
+ const aPrime = a.divide(8).multiply(factorModL).mod(L).multiply(8).mod(L);
+ const bPrime = nacl
+ .hash(
+ typedArrayConcat([privDec.subarray(32, 64), decodeCrock(factorEnc)]),
+ )
+ .subarray(0, 32);
+
+ const newPriv = encodeCrock(
+ typedArrayConcat([bigintToNaclArr(aPrime, 32), bPrime]),
+ );
+
+ return newPriv;
+ }
- const factorBuf = await deriveFactor(pub, seed);
+ export async function publicKeyDerive(
+ pub: Edx25519PublicKey,
+ seed: OpaqueData,
+ ): Promise<Edx25519PublicKey> {
+ const factorEnc = await deriveFactor(pub, seed);
+ const factorReduced = nacl.crypto_core_ed25519_scalar_reduce(
+ decodeCrock(factorEnc),
+ );
+ const res = nacl.crypto_scalarmult_ed25519_noclamp(
+ factorReduced,
+ decodeCrock(pub),
+ );
+ return encodeCrock(res);
+ }
+}
- const factor = bigint.fromArray(Array.from(factorBuf), 256, false);
+export interface AgeCommitment {
+ mask: number;
- const aPrime = a.divide(8).multiply(factor).multiply(8);
+ /**
+ * Public keys, one for each age group specified in the age mask.
+ */
+ publicKeys: Edx25519PublicKey[];
+}
- const bPrime = nacl.hash(
- typedArrayConcat([privDec.subarray(32, 64), decodeCrock(factorBuf)]),
- );
+export interface AgeProof {
+ /**
+ * Private keys. Typically smaller than the number of public keys,
+ * because we drop private keys from age groups that are restricted.
+ */
+ privateKeys: Edx25519PrivateKey[];
+}
- Uint8Array.from(aPrime.toArray(256).value)
+export interface AgeCommitmentProof {
+ commitment: AgeCommitment;
+ proof: AgeProof;
+}
+function invariant(cond: boolean): asserts cond {
+ if (!cond) {
+ throw Error("invariant failed");
+ }
+}
+
+export namespace AgeRestriction {
+ export function hashCommitment(ac: AgeCommitment): HashCodeString {
+ const hc = new nacl.HashState();
+ for (const pub of ac.publicKeys) {
+ hc.update(decodeCrock(pub));
+ }
+ return encodeCrock(hc.finish().subarray(0, 32));
+ }
+
+ export function countAgeGroups(mask: number): number {
+ let count = 0;
+ let m = mask;
+ while (m > 0) {
+ count += m & 1;
+ m = m >> 1;
+ }
+ return count;
+ }
+
+ export function getAgeGroupIndex(mask: number, age: number): number {
+ invariant((mask & 1) === 1);
+ let i = 0;
+ let m = mask;
+ let a = age;
+ while (m > 0) {
+ if (a <= 0) {
+ break;
+ }
+ m = m >> 1;
+ i += m & 1;
+ a--;
+ }
+ return i;
+ }
+
+ export function ageGroupSpecToMask(ageGroupSpec: string): number {
throw Error("not implemented");
}
- export function publicKeyDerive(
- priv: Edx25519PrivateKey,
- seed: OpaqueData,
- ): Promise<Edx25519PublicKey> {
- throw Error("not implemented")
+ export async function restrictionCommit(
+ ageMask: number,
+ age: number,
+ ): Promise<AgeCommitmentProof> {
+ invariant((ageMask & 1) === 1);
+ const numPubs = countAgeGroups(ageMask) - 1;
+ const numPrivs = getAgeGroupIndex(ageMask, age);
+
+ const pubs: Edx25519PublicKey[] = [];
+ const privs: Edx25519PrivateKey[] = [];
+
+ for (let i = 0; i < numPubs; i++) {
+ const priv = await Edx25519.keyCreate();
+ const pub = await Edx25519.getPublic(priv);
+ pubs.push(pub);
+ if (i < numPrivs) {
+ privs.push(priv);
+ }
+ }
+
+ return {
+ commitment: {
+ mask: ageMask,
+ publicKeys: pubs,
+ },
+ proof: {
+ privateKeys: privs,
+ },
+ };
+ }
+
+ export async function commitmentDerive(
+ commitmentProof: AgeCommitmentProof,
+ salt: OpaqueData,
+ ): Promise<AgeCommitmentProof> {
+ const newPrivs: Edx25519PrivateKey[] = [];
+ const newPubs: Edx25519PublicKey[] = [];
+
+ for (const oldPub of commitmentProof.commitment.publicKeys) {
+ newPubs.push(await Edx25519.publicKeyDerive(oldPub, salt));
+ }
+
+ for (const oldPriv of commitmentProof.proof.privateKeys) {
+ newPrivs.push(await Edx25519.privateKeyDerive(oldPriv, salt));
+ }
+
+ return {
+ commitment: {
+ mask: commitmentProof.commitment.mask,
+ publicKeys: newPubs,
+ },
+ proof: {
+ privateKeys: newPrivs,
+ },
+ };
+ }
+
+ export function commitmentAttest(
+ commitmentProof: AgeCommitmentProof,
+ age: number,
+ ): Edx25519Signature {
+ const d = buildSigPS(TalerSignaturePurpose.WALLET_AGE_ATTESTATION)
+ .put(bufferForUint32(commitmentProof.commitment.mask))
+ .put(bufferForUint32(age))
+ .build();
+ const group = getAgeGroupIndex(commitmentProof.commitment.mask, age);
+ if (group === 0) {
+ // No attestation required.
+ return encodeCrock(new Uint8Array(64));
+ }
+ const priv = commitmentProof.proof.privateKeys[group - 1];
+ const pub = commitmentProof.commitment.publicKeys[group - 1];
+ const sig = nacl.crypto_edx25519_sign_detached(
+ d,
+ decodeCrock(priv),
+ decodeCrock(pub),
+ );
+ return encodeCrock(sig);
+ }
+
+ export function commitmentVerify(
+ commitmentProof: AgeCommitmentProof,
+ age: number,
+ ): Edx25519Signature {
+ throw Error("not implemented");
}
}
diff --git a/packages/taler-util/src/talerTypes.ts b/packages/taler-util/src/talerTypes.ts
index b1bf6ab38..abac1cd12 100644
--- a/packages/taler-util/src/talerTypes.ts
+++ b/packages/taler-util/src/talerTypes.ts
@@ -47,6 +47,7 @@ import {
} from "./time.js";
import { codecForAmountString } from "./amounts.js";
import { strcmp } from "./helpers.js";
+import { Edx25519PublicKey } from "./talerCrypto.js";
/**
* Denomination as found in the /keys response from the exchange.
@@ -283,6 +284,10 @@ export interface CoinDepositPermission {
* URL of the exchange this coin was withdrawn from.
*/
exchange_url: string;
+
+ minimum_age_sig?: EddsaSignatureString;
+
+ age_commitment?: Edx25519PublicKey[];
}
/**
@@ -539,6 +544,8 @@ export interface ContractTerms {
*/
max_wire_fee?: string;
+ minimum_age?: number;
+
/**
* Extra data, interpreted by the mechant only.
*/
@@ -957,6 +964,7 @@ export interface ExchangeMeltRequest {
denom_sig: UnblindedSignature;
rc: string;
value_with_fee: AmountString;
+ age_commitment_hash?: HashCodeString;
}
export interface ExchangeMeltResponse {
@@ -1122,7 +1130,7 @@ export type DenominationPubKey = RsaDenominationPubKey | CsDenominationPubKey;
export interface RsaDenominationPubKey {
readonly cipher: DenomKeyType.Rsa;
readonly rsa_public_key: string;
- readonly age_mask?: number;
+ readonly age_mask: number;
}
export interface CsDenominationPubKey {
@@ -1177,12 +1185,14 @@ export const codecForRsaDenominationPubKey = () =>
buildCodecForObject<RsaDenominationPubKey>()
.property("cipher", codecForConstString(DenomKeyType.Rsa))
.property("rsa_public_key", codecForString())
+ .property("age_mask", codecForNumber())
.build("DenominationPubKey");
export const codecForCsDenominationPubKey = () =>
buildCodecForObject<CsDenominationPubKey>()
.property("cipher", codecForConstString(DenomKeyType.ClauseSchnorr))
.property("cs_public_key", codecForString())
+ .property("age_mask", codecForNumber())
.build("CsDenominationPubKey");
export const codecForBankWithdrawalOperationPostResponse =
@@ -1312,6 +1322,7 @@ export const codecForContractTerms = (): Codec<ContractTerms> =>
.property("exchanges", codecForList(codecForExchangeHandle()))
.property("products", codecOptional(codecForList(codecForProduct())))
.property("extra", codecForAny())
+ .property("minimum_age", codecOptional(codecForNumber()))
.build("ContractTerms");
export const codecForMerchantRefundPermission =
@@ -1717,6 +1728,13 @@ export interface ExchangeRefreshRevealRequest {
transfer_pub: EddsaPublicKeyString;
link_sigs: EddsaSignatureString[];
+
+ /**
+ * Iff the corresponding denomination has support for age restriction,
+ * the client MUST provide the original age commitment, i.e. the vector
+ * of public keys.
+ */
+ old_age_commitment?: Edx25519PublicKey[];
}
export interface DepositSuccess {
diff --git a/packages/taler-util/src/walletTypes.ts b/packages/taler-util/src/walletTypes.ts
index 818ba37fe..e094bc385 100644
--- a/packages/taler-util/src/walletTypes.ts
+++ b/packages/taler-util/src/walletTypes.ts
@@ -47,6 +47,7 @@ import {
codecForConstString,
codecForAny,
buildCodecForUnion,
+ codecForNumber,
} from "./codec.js";
import {
AmountString,
@@ -61,6 +62,7 @@ import { OrderShortInfo, codecForOrderShortInfo } from "./transactionsTypes.js";
import { BackupRecovery } from "./backupTypes.js";
import { PaytoUri } from "./payto.js";
import { TalerErrorCode } from "./taler-error-codes.js";
+import { AgeCommitmentProof } from "./talerCrypto.js";
/**
* Response for the create reserve request to the wallet.
@@ -218,6 +220,8 @@ export interface CreateReserveRequest {
* from this reserve, only used for testing.
*/
forcedDenomSel?: ForcedDenomSel;
+
+ restrictAge?: number;
}
export const codecForCreateReserveRequest = (): Codec<CreateReserveRequest> =>
@@ -489,6 +493,7 @@ export interface WithdrawalPlanchet {
coinEv: CoinEnvelope;
coinValue: AmountJson;
coinEvHash: string;
+ ageCommitmentProof?: AgeCommitmentProof;
}
export interface PlanchetCreationRequest {
@@ -499,6 +504,7 @@ export interface PlanchetCreationRequest {
denomPub: DenominationPubKey;
reservePub: string;
reservePriv: string;
+ restrictAge?: number;
}
/**
@@ -545,6 +551,10 @@ export interface DepositInfo {
denomKeyType: DenomKeyType;
denomPubHash: string;
denomSig: UnblindedSignature;
+
+ requiredMinimumAge?: number;
+
+ ageCommitmentProof?: AgeCommitmentProof;
}
export interface ExchangesListRespose {
@@ -728,12 +738,14 @@ export const codecForAcceptManualWithdrawalRequet =
export interface GetWithdrawalDetailsForAmountRequest {
exchangeBaseUrl: string;
amount: string;
+ restrictAge?: number;
}
export interface AcceptBankIntegratedWithdrawalRequest {
talerWithdrawUri: string;
exchangeBaseUrl: string;
forcedDenomSel?: ForcedDenomSel;
+ restrictAge?: number;
}
export const codecForAcceptBankIntegratedWithdrawalRequest =
@@ -742,6 +754,7 @@ export const codecForAcceptBankIntegratedWithdrawalRequest =
.property("exchangeBaseUrl", codecForString())
.property("talerWithdrawUri", codecForString())
.property("forcedDenomSel", codecForAny())
+ .property("restrictAge", codecOptional(codecForNumber()))
.build("AcceptBankIntegratedWithdrawalRequest");
export const codecForGetWithdrawalDetailsForAmountRequest =
@@ -774,11 +787,13 @@ export const codecForApplyRefundRequest = (): Codec<ApplyRefundRequest> =>
export interface GetWithdrawalDetailsForUriRequest {
talerWithdrawUri: string;
+ restrictAge?: number;
}
export const codecForGetWithdrawalDetailsForUri =
(): Codec<GetWithdrawalDetailsForUriRequest> =>
buildCodecForObject<GetWithdrawalDetailsForUriRequest>()
.property("talerWithdrawUri", codecForString())
+ .property("restrictAge", codecOptional(codecForNumber()))
.build("GetWithdrawalDetailsForUriRequest");
export interface ListKnownBankAccountsRequest {