gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[taler-wallet-core] branch master updated (c27a7abf -> 12234083)


From: gnunet
Subject: [taler-wallet-core] branch master updated (c27a7abf -> 12234083)
Date: Mon, 14 Dec 2020 16:44:52 +0100

This is an automated email from the git hooks/post-receive script.

dold pushed a change to branch master
in repository wallet-core.

    from c27a7abf backup WIP (getting it back to build)
     new 80a0fab1 backup schema
     new 12234083 derive refresh info from secret seed

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../taler-wallet-core/src/crypto/talerCrypto.ts    |  16 ++
 .../src/crypto/workers/cryptoApi.ts                |  24 +--
 .../src/crypto/workers/cryptoImplementation.ts     |  95 ++++-----
 .../taler-wallet-core/src/operations/backup.ts     |  21 +-
 .../taler-wallet-core/src/operations/refresh.ts    | 236 ++++++++++++++-------
 .../taler-wallet-core/src/types/backupTypes.ts     | 183 +++++++++++-----
 .../taler-wallet-core/src/types/cryptoTypes.ts     | 111 ++++++++++
 packages/taler-wallet-core/src/types/dbTypes.ts    |  77 ++-----
 8 files changed, 497 insertions(+), 266 deletions(-)
 create mode 100644 packages/taler-wallet-core/src/types/cryptoTypes.ts

diff --git a/packages/taler-wallet-core/src/crypto/talerCrypto.ts 
b/packages/taler-wallet-core/src/crypto/talerCrypto.ts
index 8713fc96..4faa523a 100644
--- a/packages/taler-wallet-core/src/crypto/talerCrypto.ts
+++ b/packages/taler-wallet-core/src/crypto/talerCrypto.ts
@@ -389,3 +389,19 @@ export function setupRefreshPlanchet(
     coinPub: eddsaGetPublic(coinPriv),
   };
 }
+
+export function setupRefreshTransferPub(
+  secretSeed: Uint8Array,
+  transferPubIndex: number,
+): EcdheKeyPair {
+  const info = stringToBytes("taler-transfer-pub-derivation");
+  const saltArrBuf = new ArrayBuffer(4);
+  const salt = new Uint8Array(saltArrBuf);
+  const saltDataView = new DataView(saltArrBuf);
+  saltDataView.setUint32(0, transferPubIndex);
+  const out = kdf(32, secretSeed, salt, info);
+  return {
+    ecdhePriv: out,
+    ecdhePub: ecdheGetPublic(out),
+  };
+}
diff --git a/packages/taler-wallet-core/src/crypto/workers/cryptoApi.ts 
b/packages/taler-wallet-core/src/crypto/workers/cryptoApi.ts
index 29f3b02b..6a4264d2 100644
--- a/packages/taler-wallet-core/src/crypto/workers/cryptoApi.ts
+++ b/packages/taler-wallet-core/src/crypto/workers/cryptoApi.ts
@@ -47,6 +47,10 @@ import {
 
 import * as timer from "../../util/timer";
 import { Logger } from "../../util/logging";
+import {
+  DerivedRefreshSession,
+  DeriveRefreshSessionRequest,
+} from "../../types/cryptoTypes";
 
 const logger = new Logger("cryptoApi.ts");
 
@@ -417,22 +421,10 @@ export class CryptoApi {
     return this.doRpc<RecoupRequest>("createRecoupRequest", 1, coin);
   }
 
-  createRefreshSession(
-    exchangeBaseUrl: string,
-    kappa: number,
-    meltCoin: CoinRecord,
-    newCoinDenoms: DenominationSelectionInfo,
-    meltFee: AmountJson,
-  ): Promise<RefreshSessionRecord> {
-    return this.doRpc<RefreshSessionRecord>(
-      "createRefreshSession",
-      4,
-      exchangeBaseUrl,
-      kappa,
-      meltCoin,
-      newCoinDenoms,
-      meltFee,
-    );
+  deriveRefreshSession(
+    req: DeriveRefreshSessionRequest,
+  ): Promise<DerivedRefreshSession> {
+    return this.doRpc<DerivedRefreshSession>("deriveRefreshSession", 4, req);
   }
 
   signCoinLink(
diff --git 
a/packages/taler-wallet-core/src/crypto/workers/cryptoImplementation.ts 
b/packages/taler-wallet-core/src/crypto/workers/cryptoImplementation.ts
index e55fa3d7..d14f663e 100644
--- a/packages/taler-wallet-core/src/crypto/workers/cryptoImplementation.ts
+++ b/packages/taler-wallet-core/src/crypto/workers/cryptoImplementation.ts
@@ -63,6 +63,8 @@ import {
   keyExchangeEcdheEddsa,
   setupRefreshPlanchet,
   rsaVerify,
+  getRandomBytes,
+  setupRefreshTransferPub,
 } from "../talerCrypto";
 import { randomBytes } from "../primitives/nacl-fast";
 import { kdf } from "../primitives/kdf";
@@ -73,6 +75,10 @@ import {
 } from "../../util/time";
 
 import { Logger } from "../../util/logging";
+import {
+  DerivedRefreshSession,
+  DeriveRefreshSessionRequest,
+} from "../../types/cryptoTypes";
 
 const logger = new Logger("cryptoImplementation.ts");
 
@@ -375,21 +381,24 @@ export class CryptoImplementation {
     return s;
   }
 
-  /**
-   * Create a new refresh session.
-   */
-  createRefreshSession(
-    exchangeBaseUrl: string,
-    kappa: number,
-    meltCoin: CoinRecord,
-    newCoinDenoms: DenominationSelectionInfo,
-    meltFee: AmountJson,
-  ): RefreshSessionRecord {
-    const currency = newCoinDenoms.selectedDenoms[0].denom.value.currency;
+  deriveRefreshSession(
+    req: DeriveRefreshSessionRequest,
+  ): DerivedRefreshSession {
+    const {
+      newCoinDenoms,
+      feeRefresh: meltFee,
+      kappa,
+      meltCoinDenomPubHash,
+      meltCoinPriv,
+      meltCoinPub,
+      sessionSecretSeed: refreshSessionSecretSeed,
+    } = req;
+
+    const currency = newCoinDenoms[0].value.currency;
     let valueWithFee = Amounts.getZero(currency);
 
-    for (const ncd of newCoinDenoms.selectedDenoms) {
-      const t = Amounts.add(ncd.denom.value, ncd.denom.feeWithdraw).amount;
+    for (const ncd of newCoinDenoms) {
+      const t = Amounts.add(ncd.value, ncd.feeWithdraw).amount;
       valueWithFee = Amounts.add(
         valueWithFee,
         Amounts.mult(t, ncd.count).amount,
@@ -409,7 +418,10 @@ export class CryptoImplementation {
     logger.trace("starting RC computation");
 
     for (let i = 0; i < kappa; i++) {
-      const transferKeyPair = createEcdheKeyPair();
+      const transferKeyPair = setupRefreshTransferPub(
+        decodeCrock(refreshSessionSecretSeed),
+        i,
+      );
       sessionHc.update(transferKeyPair.ecdhePub);
       logger.trace(
         `HASH transfer_pub ${encodeCrock(transferKeyPair.ecdhePub)}`,
@@ -418,16 +430,16 @@ export class CryptoImplementation {
       transferPubs.push(encodeCrock(transferKeyPair.ecdhePub));
     }
 
-    for (const denomSel of newCoinDenoms.selectedDenoms) {
+    for (const denomSel of newCoinDenoms) {
       for (let i = 0; i < denomSel.count; i++) {
-        const r = decodeCrock(denomSel.denom.denomPub);
+        const r = decodeCrock(denomSel.denomPub);
         sessionHc.update(r);
         logger.trace(`HASH new_coins ${encodeCrock(r)}`);
       }
     }
 
-    sessionHc.update(decodeCrock(meltCoin.coinPub));
-    logger.trace(`HASH coin_pub ${meltCoin.coinPub}`);
+    sessionHc.update(decodeCrock(meltCoinPub));
+    logger.trace(`HASH coin_pub ${meltCoinPub}`);
     sessionHc.update(amountToBuffer(valueWithFee));
     logger.trace(
       `HASH melt_amount ${encodeCrock(amountToBuffer(valueWithFee))}`,
@@ -435,12 +447,12 @@ export class CryptoImplementation {
 
     for (let i = 0; i < kappa; i++) {
       const planchets: RefreshPlanchet[] = [];
-      for (let j = 0; j < newCoinDenoms.selectedDenoms.length; j++) {
-        const denomSel = newCoinDenoms.selectedDenoms[j];
+      for (let j = 0; j < newCoinDenoms.length; j++) {
+        const denomSel = newCoinDenoms[j];
         for (let k = 0; k < denomSel.count; k++) {
           const coinNumber = planchets.length;
           const transferPriv = decodeCrock(transferPrivs[i]);
-          const oldCoinPub = decodeCrock(meltCoin.coinPub);
+          const oldCoinPub = decodeCrock(meltCoinPub);
           const transferSecret = keyExchangeEcdheEddsa(
             transferPriv,
             oldCoinPub,
@@ -450,7 +462,7 @@ export class CryptoImplementation {
           const coinPub = fresh.coinPub;
           const blindingFactor = fresh.bks;
           const pubHash = hash(coinPub);
-          const denomPub = decodeCrock(denomSel.denom.denomPub);
+          const denomPub = decodeCrock(denomSel.denomPub);
           const ev = rsaBlind(pubHash, blindingFactor, denomPub);
           const planchet: RefreshPlanchet = {
             blindingKey: encodeCrock(blindingFactor),
@@ -481,49 +493,22 @@ export class CryptoImplementation {
 
     const confirmData = buildSigPS(SignaturePurpose.WALLET_COIN_MELT)
       .put(sessionHash)
-      .put(decodeCrock(meltCoin.denomPubHash))
+      .put(decodeCrock(meltCoinDenomPubHash))
       .put(amountToBuffer(valueWithFee))
       .put(amountToBuffer(meltFee))
-      .put(decodeCrock(meltCoin.coinPub))
+      .put(decodeCrock(meltCoinPub))
       .build();
 
-    const confirmSig = eddsaSign(confirmData, decodeCrock(meltCoin.coinPriv));
-
-    let valueOutput = Amounts.getZero(currency);
-    for (const denomSel of newCoinDenoms.selectedDenoms) {
-      const denom = denomSel.denom;
-      for (let i = 0; i < denomSel.count; i++) {
-        valueOutput = Amounts.add(valueOutput, denom.value).amount;
-      }
-    }
-
-    const newDenoms: string[] = [];
-    const newDenomHashes: string[] = [];
-
-    for (const denomSel of newCoinDenoms.selectedDenoms) {
-      const denom = denomSel.denom;
-      for (let i = 0; i < denomSel.count; i++) {
-        newDenoms.push(denom.denomPub);
-        newDenomHashes.push(denom.denomPubHash);
-      }
-    }
+    const confirmSig = eddsaSign(confirmData, decodeCrock(meltCoinPriv));
 
-    const refreshSession: RefreshSessionRecord = {
+    const refreshSession: DerivedRefreshSession = {
       confirmSig: encodeCrock(confirmSig),
-      exchangeBaseUrl,
       hash: encodeCrock(sessionHash),
-      meltCoinPub: meltCoin.coinPub,
-      newDenomHashes,
-      newDenoms,
-      norevealIndex: undefined,
+      meltCoinPub: meltCoinPub,
       planchetsForGammas: planchetsForGammas,
       transferPrivs,
       transferPubs,
-      amountRefreshOutput: valueOutput,
-      amountRefreshInput: valueWithFee,
-      timestampCreated: getTimestampNow(),
-      finishedTimestamp: undefined,
-      lastError: undefined,
+      meltValueWithFee: valueWithFee,
     };
 
     return refreshSession;
diff --git a/packages/taler-wallet-core/src/operations/backup.ts 
b/packages/taler-wallet-core/src/operations/backup.ts
index cf73de62..6c497b30 100644
--- a/packages/taler-wallet-core/src/operations/backup.ts
+++ b/packages/taler-wallet-core/src/operations/backup.ts
@@ -54,7 +54,7 @@ import {
   stringToBytes,
 } from "../crypto/talerCrypto";
 import { canonicalizeBaseUrl, canonicalJson, j2s } from "../util/helpers";
-import { Timestamp } from "../util/time";
+import { getTimestampNow, Timestamp } from "../util/time";
 import { URL } from "../util/url";
 import { AmountString } from "../types/talerTypes";
 import {
@@ -77,6 +77,16 @@ interface WalletBackupConfState {
   walletRootPriv: string;
   clocks: { [device_id: string]: number };
   lastBackupHash?: string;
+
+  /**
+   * Timestamp stored in the last backup.
+   */
+  lastBackupTimestamp?: Timestamp;
+
+  /**
+   * Last time we tried to do a backup.
+   */
+  lastBackupCheckTimestamp?: Timestamp;
   lastBackupNonce?: string;
 }
 
@@ -237,6 +247,7 @@ export async function exportBackup(
           base_url: ex.baseUrl,
           accounts: ex.wireInfo.accounts.map((x) => ({
             payto_uri: x.payto_uri,
+            master_sig: x.master_sig,
           })),
           auditors: ex.details.auditors.map((x) => ({
             auditor_pub: x.auditor_pub,
@@ -261,6 +272,10 @@ export async function exportBackup(
         });
       });
 
+      if (!bs.lastBackupTimestamp) {
+        bs.lastBackupTimestamp = getTimestampNow();
+      }
+
       const backupBlob: WalletBackupContentV1 = {
         schema_id: "gnu-taler-wallet-backup-content",
         schema_version: 1,
@@ -275,6 +290,10 @@ export async function exportBackup(
         recoup_groups: [],
         refresh_groups: [],
         tips: [],
+        timestamp: bs.lastBackupTimestamp,
+        trusted_auditors: {},
+        trusted_exchanges: {},
+        intern_table: {},
       };
 
       // If the backup changed, we increment our clock.
diff --git a/packages/taler-wallet-core/src/operations/refresh.ts 
b/packages/taler-wallet-core/src/operations/refresh.ts
index 21c884d4..16b691ec 100644
--- a/packages/taler-wallet-core/src/operations/refresh.ts
+++ b/packages/taler-wallet-core/src/operations/refresh.ts
@@ -60,6 +60,8 @@ import {
 import { URL } from "../util/url";
 import { checkDbInvariant } from "../util/invariants";
 import { initRetryInfo, updateRetryInfoTimeout } from "../util/retries";
+import { WALLET_EXCHANGE_PROTOCOL_VERSION } from "./versions";
+import { RefreshNewDenomInfo } from "../types/cryptoTypes";
 
 const logger = new Logger("refresh.ts");
 
@@ -182,13 +184,7 @@ async function refreshCreateSession(
     return;
   }
 
-  const refreshSession: RefreshSessionRecord = await 
ws.cryptoApi.createRefreshSession(
-    exchange.baseUrl,
-    3,
-    coin,
-    newCoinDenoms,
-    oldDenom.feeRefresh,
-  );
+  const sessionSecretSeed = encodeCrock(getRandomBytes(64));
 
   // Store refresh session for this coin in the database.
   await ws.db.runWithWriteTransaction(
@@ -201,7 +197,15 @@ async function refreshCreateSession(
       if (rg.refreshSessionPerCoin[coinIndex]) {
         return;
       }
-      rg.refreshSessionPerCoin[coinIndex] = refreshSession;
+      rg.refreshSessionPerCoin[coinIndex] = {
+        norevealIndex: undefined,
+        sessionSecretSeed: sessionSecretSeed,
+        newDenoms: newCoinDenoms.selectedDenoms.map((x) => ({
+          count: x.count,
+          denomPubHash: x.denom.denomPubHash,
+        })),
+        amountRefreshOutput: newCoinDenoms.totalCoinValue,
+      };
       await tx.put(Stores.refreshGroups, rg);
     },
   );
@@ -232,24 +236,57 @@ async function refreshMelt(
     return;
   }
 
-  const coin = await ws.db.get(Stores.coins, refreshSession.meltCoinPub);
+  const oldCoin = await ws.db.get(
+    Stores.coins,
+    refreshGroup.oldCoinPubs[coinIndex],
+  );
+  checkDbInvariant(!!oldCoin, "melt coin doesn't exist");
+  const oldDenom = await ws.db.get(Stores.denominations, [
+    oldCoin.exchangeBaseUrl,
+    oldCoin.denomPubHash,
+  ]);
+  checkDbInvariant(!!oldDenom, "denomination for melted coin doesn't exist");
 
-  if (!coin) {
-    console.error("can't melt coin, it does not exist");
-    return;
+  const newCoinDenoms: RefreshNewDenomInfo[] = [];
+
+  for (const dh of refreshSession.newDenoms) {
+    const newDenom = await ws.db.get(Stores.denominations, [
+      oldCoin.exchangeBaseUrl,
+      dh.denomPubHash,
+    ]);
+    checkDbInvariant(
+      !!newDenom,
+      "new denomination for refresh not in database",
+    );
+    newCoinDenoms.push({
+      count: dh.count,
+      denomPub: newDenom.denomPub,
+      feeWithdraw: newDenom.feeWithdraw,
+      value: newDenom.value,
+    });
   }
 
+  const derived = await ws.cryptoApi.deriveRefreshSession({
+    kappa: 3,
+    meltCoinDenomPubHash: oldCoin.denomPubHash,
+    meltCoinPriv: oldCoin.coinPriv,
+    meltCoinPub: oldCoin.coinPub,
+    feeRefresh: oldDenom.feeRefresh,
+    newCoinDenoms,
+    sessionSecretSeed: refreshSession.sessionSecretSeed,
+  });
+
   const reqUrl = new URL(
-    `coins/${coin.coinPub}/melt`,
-    refreshSession.exchangeBaseUrl,
+    `coins/${oldCoin.coinPub}/melt`,
+    oldCoin.exchangeBaseUrl,
   );
   const meltReq = {
-    coin_pub: coin.coinPub,
-    confirm_sig: refreshSession.confirmSig,
-    denom_pub_hash: coin.denomPubHash,
-    denom_sig: coin.denomSig,
-    rc: refreshSession.hash,
-    value_with_fee: Amounts.stringify(refreshSession.amountRefreshInput),
+    coin_pub: oldCoin.coinPub,
+    confirm_sig: derived.confirmSig,
+    denom_pub_hash: oldCoin.denomPubHash,
+    denom_sig: oldCoin.denomSig,
+    rc: derived.hash,
+    value_with_fee: Amounts.stringify(derived.meltValueWithFee),
   };
   logger.trace(`melt request for coin:`, meltReq);
 
@@ -270,13 +307,13 @@ async function refreshMelt(
 
   await ws.db.mutate(Stores.refreshGroups, refreshGroupId, (rg) => {
     const rs = rg.refreshSessionPerCoin[coinIndex];
-    if (!rs) {
+    if (rg.timestampFinished) {
       return;
     }
-    if (rs.norevealIndex !== undefined) {
+    if (!rs) {
       return;
     }
-    if (rs.finishedTimestamp) {
+    if (rs.norevealIndex !== undefined) {
       return;
     }
     rs.norevealIndex = norevealIndex;
@@ -305,48 +342,95 @@ async function refreshReveal(
   if (norevealIndex === undefined) {
     throw Error("can't reveal without melting first");
   }
-  const privs = Array.from(refreshSession.transferPrivs);
+
+  const oldCoin = await ws.db.get(
+    Stores.coins,
+    refreshGroup.oldCoinPubs[coinIndex],
+  );
+  checkDbInvariant(!!oldCoin, "melt coin doesn't exist");
+  const oldDenom = await ws.db.get(Stores.denominations, [
+    oldCoin.exchangeBaseUrl,
+    oldCoin.denomPubHash,
+  ]);
+  checkDbInvariant(!!oldDenom, "denomination for melted coin doesn't exist");
+
+  const newCoinDenoms: RefreshNewDenomInfo[] = [];
+
+  for (const dh of refreshSession.newDenoms) {
+    const newDenom = await ws.db.get(Stores.denominations, [
+      oldCoin.exchangeBaseUrl,
+      dh.denomPubHash,
+    ]);
+    checkDbInvariant(
+      !!newDenom,
+      "new denomination for refresh not in database",
+    );
+    newCoinDenoms.push({
+      count: dh.count,
+      denomPub: newDenom.denomPub,
+      feeWithdraw: newDenom.feeWithdraw,
+      value: newDenom.value,
+    });
+  }
+
+  const derived = await ws.cryptoApi.deriveRefreshSession({
+    kappa: 3,
+    meltCoinDenomPubHash: oldCoin.denomPubHash,
+    meltCoinPriv: oldCoin.coinPriv,
+    meltCoinPub: oldCoin.coinPub,
+    feeRefresh: oldDenom.feeRefresh,
+    newCoinDenoms,
+    sessionSecretSeed: refreshSession.sessionSecretSeed,
+  });
+
+  const privs = Array.from(derived.transferPrivs);
   privs.splice(norevealIndex, 1);
 
-  const planchets = refreshSession.planchetsForGammas[norevealIndex];
+  const planchets = derived.planchetsForGammas[norevealIndex];
   if (!planchets) {
     throw Error("refresh index error");
   }
 
   const meltCoinRecord = await ws.db.get(
     Stores.coins,
-    refreshSession.meltCoinPub,
+    refreshGroup.oldCoinPubs[coinIndex],
   );
   if (!meltCoinRecord) {
     throw Error("inconsistent database");
   }
 
   const evs = planchets.map((x: RefreshPlanchet) => x.coinEv);
-
+  const newDenomsFlat: string[] = [];
   const linkSigs: string[] = [];
+
   for (let i = 0; i < refreshSession.newDenoms.length; i++) {
-    const linkSig = await ws.cryptoApi.signCoinLink(
-      meltCoinRecord.coinPriv,
-      refreshSession.newDenomHashes[i],
-      refreshSession.meltCoinPub,
-      refreshSession.transferPubs[norevealIndex],
-      planchets[i].coinEv,
-    );
-    linkSigs.push(linkSig);
+    const dsel = refreshSession.newDenoms[i];
+    for (let j = 0; j < dsel.count; j++) {
+      const newCoinIndex = linkSigs.length;
+      const linkSig = await ws.cryptoApi.signCoinLink(
+        meltCoinRecord.coinPriv,
+        dsel.denomPubHash,
+        meltCoinRecord.coinPub,
+        derived.transferPubs[norevealIndex],
+        planchets[newCoinIndex].coinEv,
+      );
+      linkSigs.push(linkSig);
+      newDenomsFlat.push(dsel.denomPubHash);
+    }
   }
 
   const req = {
     coin_evs: evs,
-    new_denoms_h: refreshSession.newDenomHashes,
-    rc: refreshSession.hash,
+    new_denoms_h: newDenomsFlat,
+    rc: derived.hash,
     transfer_privs: privs,
-    transfer_pub: refreshSession.transferPubs[norevealIndex],
+    transfer_pub: derived.transferPubs[norevealIndex],
     link_sigs: linkSigs,
   };
 
   const reqUrl = new URL(
-    `refreshes/${refreshSession.hash}/reveal`,
-    refreshSession.exchangeBaseUrl,
+    `refreshes/${derived.hash}/reveal`,
+    oldCoin.exchangeBaseUrl,
   );
 
   const resp = await ws.runSequentialized([EXCHANGE_COINS_LOCK], async () => {
@@ -362,39 +446,42 @@ async function refreshReveal(
 
   const coins: CoinRecord[] = [];
 
-  for (let i = 0; i < reveal.ev_sigs.length; i++) {
-    const denom = await ws.db.get(Stores.denominations, [
-      refreshSession.exchangeBaseUrl,
-      refreshSession.newDenomHashes[i],
-    ]);
-    if (!denom) {
-      console.error("denom not found");
-      continue;
+  for (let i = 0; i < refreshSession.newDenoms.length; i++) {
+    for (let j = 0; j < refreshSession.newDenoms[i].count; j++) {
+      const newCoinIndex = coins.length;
+      const denom = await ws.db.get(Stores.denominations, [
+        oldCoin.exchangeBaseUrl,
+        refreshSession.newDenoms[i].denomPubHash,
+      ]);
+      if (!denom) {
+        console.error("denom not found");
+        continue;
+      }
+      const pc = derived.planchetsForGammas[norevealIndex][newCoinIndex];
+      const denomSig = await ws.cryptoApi.rsaUnblind(
+        reveal.ev_sigs[newCoinIndex].ev_sig,
+        pc.blindingKey,
+        denom.denomPub,
+      );
+      const coin: CoinRecord = {
+        blindingKey: pc.blindingKey,
+        coinPriv: pc.privateKey,
+        coinPub: pc.publicKey,
+        currentAmount: denom.value,
+        denomPub: denom.denomPub,
+        denomPubHash: denom.denomPubHash,
+        denomSig,
+        exchangeBaseUrl: oldCoin.exchangeBaseUrl,
+        status: CoinStatus.Fresh,
+        coinSource: {
+          type: CoinSourceType.Refresh,
+          oldCoinPub: refreshGroup.oldCoinPubs[coinIndex],
+        },
+        suspended: false,
+      };
+
+      coins.push(coin);
     }
-    const pc = refreshSession.planchetsForGammas[norevealIndex][i];
-    const denomSig = await ws.cryptoApi.rsaUnblind(
-      reveal.ev_sigs[i].ev_sig,
-      pc.blindingKey,
-      denom.denomPub,
-    );
-    const coin: CoinRecord = {
-      blindingKey: pc.blindingKey,
-      coinPriv: pc.privateKey,
-      coinPub: pc.publicKey,
-      currentAmount: denom.value,
-      denomPub: denom.denomPub,
-      denomPubHash: denom.denomPubHash,
-      denomSig,
-      exchangeBaseUrl: refreshSession.exchangeBaseUrl,
-      status: CoinStatus.Fresh,
-      coinSource: {
-        type: CoinSourceType.Refresh,
-        oldCoinPub: refreshSession.meltCoinPub,
-      },
-      suspended: false,
-    };
-
-    coins.push(coin);
   }
 
   await ws.db.runWithWriteTransaction(
@@ -409,11 +496,6 @@ async function refreshReveal(
       if (!rs) {
         return;
       }
-      if (rs.finishedTimestamp) {
-        logger.warn("refresh session already finished");
-        return;
-      }
-      rs.finishedTimestamp = getTimestampNow();
       rg.finishedPerCoin[coinIndex] = true;
       let allDone = true;
       for (const f of rg.finishedPerCoin) {
diff --git a/packages/taler-wallet-core/src/types/backupTypes.ts 
b/packages/taler-wallet-core/src/types/backupTypes.ts
index 2b0e7496..247a4d39 100644
--- a/packages/taler-wallet-core/src/types/backupTypes.ts
+++ b/packages/taler-wallet-core/src/types/backupTypes.ts
@@ -50,6 +50,21 @@ import {
  */
 type BackupAmountString = string;
 
+/**
+ * A human-recognizable identifier here that is
+ * reasonable unique and assigned the first time the wallet is
+ * started/installed, such as:
+ *
+ * `${wallet-implementation} ${os} ${hostname} (${short-uid})`
+ * => e.g. "GNU Taler Android iceking ABC123"
+ */
+type DeviceIdString = string;
+
+/**
+ * Integer-valued clock.
+ */
+type ClockValue = number;
+
 /**
  * Content of the backup.
  *
@@ -76,21 +91,29 @@ export interface WalletBackupContentV1 {
 
   /**
    * Current device identifier that "owns" the backup.
-   * 
+   *
    * This identifier allows one wallet to notice when another
    * wallet is "alive" and connected to the same sync provider.
    */
-  current_device_id: string;
+  current_device_id: DeviceIdString;
 
   /**
    * Monotonically increasing clock of the wallet,
    * used to determine causality when merging backups.
-   * 
+   *
    * Information about other clocks, used to delete
    * tombstones in the hopefully rare case that multiple wallets
    * are connected to the same sync server.
    */
-  clocks: { [device_id: string]: number };
+  clocks: { [device_id: string]: ClockValue };
+
+  /**
+   * Timestamp of the backup.
+   *
+   * This timestamp should only be advanced if the content
+   * of the backup changes.
+   */
+  timestamp: Timestamp;
 
   /**
    * Per-exchange data sorted by exchange master public key.
@@ -155,6 +178,94 @@ export interface WalletBackupContentV1 {
      */
     proposal_id: string;
   }[];
+
+  /**
+   * Trusted auditors, either for official (3 letter) or local (4-12 letter)
+   * currencies.
+   *
+   * Auditors are sorted by their canonicalized base URL.
+   */
+  trusted_auditors: { [currency: string]: BackupTrustAuditor[] };
+
+  /**
+   * Trusted exchange.  Only applicable for local currencies (4-12 letter 
currency code).
+   *
+   * Exchanges are sorted by their canonicalized base URL.
+   */
+  trusted_exchanges: { [currency: string]: BackupTrustExchange[] };
+
+  /**
+   * Interning table for forgettable values of contract terms.
+   * 
+   * Used to reduce storage space, as many forgettable items (product image,
+   * addresses, etc.) might be shared among many contract terms.
+   */
+  intern_table: { [hash: string]: any };
+}
+
+/**
+ * Trust declaration for an auditor.
+ * 
+ * The trust applies based on the public key of
+ * the auditor, irrespective of what base URL the exchange
+ * is referencing.
+ */
+export interface BackupTrustAuditor {
+  /**
+   * Base URL of the auditor.
+   */
+  auditor_base_url: string;
+
+  /**
+   * Public key of the auditor.
+   */
+  auditor_pub: string;
+
+  /**
+   * Clock when the auditor trust has been added.
+   * 
+   * Can be undefined if this entry represents a removal delta
+   * from the wallet's defaults.
+   */
+  clock_added?: ClockValue;
+
+  /**
+   * Clock for when the auditor trust has been removed.
+   */
+  clock_removed?: ClockValue;
+}
+
+/**
+ * Trust declaration for an exchange.
+ * 
+ * The trust only applies for the combination of base URL
+ * and public key.  If the master public key changes while the base
+ * URL stays the same, the exchange has to be re-added by a wallet update
+ * or by the user.
+ */
+export interface BackupTrustExchange {
+  /**
+   * Canonicalized exchange base URL.
+   */
+  exchange_base_url: string;
+
+  /**
+   * Master public key of the exchange.
+   */
+  exchange_master_pub: string;
+
+  /**
+   * Clock when the exchange trust has been added.
+   * 
+   * Can be undefined if this entry represents a removal delta
+   * from the wallet's defaults.
+   */
+  clock_added?: ClockValue;
+  
+  /**
+   * Clock for when the exchange trust has been removed.
+   */
+  clock_removed?: ClockValue;
 }
 
 /**
@@ -182,9 +293,9 @@ export class BackupBackupProvider {
   storage_limit_in_megabytes: number;
 
   /**
-   * Last proposal ID to pay for the backup provider.
+   * Proposal IDs for payments to this provider.
    */
-  pay_proposal_id?: string;
+  pay_proposal_ids: string[];
 }
 
 /**
@@ -421,21 +532,6 @@ export enum BackupRefreshReason {
   Scheduled = "scheduled",
 }
 
-/**
- * Planchet for a coin during refresh.
- */
-export interface BackupRefreshPlanchet {
-  /**
-   * Private key for the coin.
-   */
-  private_key: string;
-
-  /**
-   * Blinding key used.
-   */
-  blinding_key: string;
-}
-
 /**
  * Information about one refresh session, always part
  * of a refresh group.
@@ -443,45 +539,21 @@ export interface BackupRefreshPlanchet {
  * (Public key of the old coin is stored in the refresh group.)
  */
 export interface BackupRefreshSession {
-  /**
-   * Signature made by the old coin to confirm the melting.
-   */
-  confirm_sig: string;
-
   /**
    * Hased denominations of the newly requested coins.
    */
   new_denom_hashes: string[];
 
   /**
-   * Planchets for each cut-and-choose instance.
-   */
-  planchets_for_gammas: BackupRefreshPlanchet[][];
-
-  /**
-   * Private keys for the transfer public keys.
+   * Seed used to derive the planchets and
+   * transfer private keys for this refresh session.
    */
-  transfer_privs: string[];
+  session_secret_seed: string;
 
   /**
    * The no-reveal-index after we've done the melting.
    */
   noreveal_index?: number;
-
-  /**
-   * Hash of the session.
-   */
-  hash: string;
-
-  /**
-   * Timestamp when the refresh session finished.
-   */
-  timestamp_finished: Timestamp | undefined;
-
-  /**
-   * When has this refresh session been created?
-   */
-  timestamp_created: Timestamp;
 }
 
 /**
@@ -516,10 +588,10 @@ export interface BackupRefreshGroup {
     estimated_output_amount: BackupAmountString;
 
     /**
-     * Coin is skipped (finished without a refresh session) because
-     * there is not enough value left on it.
+     * Did the refresh session finish (or was it unnecessary/impossible to 
create
+     * one)
      */
-    skipped: boolean;
+    finished: boolean;
 
     /**
      * Refresh session (if created) or undefined it not created yet.
@@ -620,7 +692,7 @@ export interface BackupRefundItemCommon {
    *
    * Might be lower in practice when two refunds on the same
    * coin are refreshed in the same refresh operation.
-   * 
+   *
    * Used to display fees, and stored since it's expensive to recompute
    * accurately.
    */
@@ -891,7 +963,7 @@ export interface BackupReserve {
 }
 
 /**
- * Wire fee for one wire method as stored in the
+ * Wire fee for one wire payment target type as stored in the
  * wallet's database.
  *
  * (Flattened to a list to make the declaration simpler).
@@ -1023,10 +1095,7 @@ export interface BackupExchange {
    */
   accounts: {
     payto_uri: string;
-    /**
-     * Optional, since older wallets don't store this.
-     */
-    master_sig?: string;
+    master_sig: string;
   }[];
 
   /**
diff --git a/packages/taler-wallet-core/src/types/cryptoTypes.ts 
b/packages/taler-wallet-core/src/types/cryptoTypes.ts
new file mode 100644
index 00000000..a7f51ab5
--- /dev/null
+++ b/packages/taler-wallet-core/src/types/cryptoTypes.ts
@@ -0,0 +1,111 @@
+/*
+ This file is part of GNU Taler
+ (C) 2020 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
+ */
+
+/**
+ * Types used by the wallet crypto worker.
+ *
+ * These types are defined in a separate file make tree shaking easier, since
+ * some components use these types (via RPC) but do not depend on the wallet
+ * code directly.
+ *
+ * @author Florian Dold <dold@taler.net>
+ */
+
+/**
+ * Imports.
+ */
+import { AmountJson } from "../util/amounts";
+
+export interface RefreshNewDenomInfo {
+  count: number;
+  value: AmountJson;
+  feeWithdraw: AmountJson;
+  denomPub: string;
+}
+
+/**
+ * Request to derive a refresh session from the refresh session
+ * secret seed.
+ */
+export interface DeriveRefreshSessionRequest {
+  sessionSecretSeed: string;
+  kappa: number;
+  meltCoinPub: string;
+  meltCoinPriv: string;
+  meltCoinDenomPubHash: string;
+  newCoinDenoms: RefreshNewDenomInfo[];
+  feeRefresh: AmountJson;
+}
+
+/**
+ *
+ */
+export interface DerivedRefreshSession {
+  /**
+   * Public key that's being melted in this session.
+   */
+  meltCoinPub: string;
+
+  /**
+   * Signature to confirm the melting.
+   */
+  confirmSig: string;
+
+  /**
+   * Planchets for each cut-and-choose instance.
+   */
+  planchetsForGammas: {
+    /**
+     * Public key for the coin.
+     */
+    publicKey: string;
+
+    /**
+     * Private key for the coin.
+     */
+    privateKey: string;
+
+    /**
+     * Blinded public key.
+     */
+    coinEv: string;
+
+    /**
+     * Blinding key used.
+     */
+    blindingKey: string;
+  }[][];
+
+  /**
+   * The transfer keys, kappa of them.
+   */
+  transferPubs: string[];
+
+  /**
+   * Private keys for the transfer public keys.
+   */
+  transferPrivs: string[];
+
+  /**
+   * Hash of the session.
+   */
+  hash: string;
+
+  /**
+   * Exact value that is being melted.
+   */
+  meltValueWithFee: AmountJson;
+}
diff --git a/packages/taler-wallet-core/src/types/dbTypes.ts 
b/packages/taler-wallet-core/src/types/dbTypes.ts
index dc1ee104..18a1102b 100644
--- a/packages/taler-wallet-core/src/types/dbTypes.ts
+++ b/packages/taler-wallet-core/src/types/dbTypes.ts
@@ -507,6 +507,7 @@ export enum ExchangeUpdateStatus {
 
 export interface ExchangeBankAccount {
   payto_uri: string;
+  master_sig: string;
 }
 
 export interface ExchangeWireInfo {
@@ -556,7 +557,7 @@ export interface ExchangeRecord {
 
   /**
    * Terms of service text or undefined if not downloaded yet.
-   * 
+   *
    * This is just used as a cache of the last downloaded ToS.
    */
   termsOfServiceText: string | undefined;
@@ -663,14 +664,17 @@ export interface RefreshPlanchet {
    * Public key for the coin.
    */
   publicKey: string;
+
   /**
    * Private key for the coin.
    */
   privateKey: string;
+
   /**
    * Blinded public key.
    */
   coinEv: string;
+
   /**
    * Blinding key used.
    */
@@ -990,18 +994,14 @@ export interface RefreshGroupRecord {
  * Ongoing refresh
  */
 export interface RefreshSessionRecord {
-  lastError: TalerErrorDetails | undefined;
-
   /**
-   * Public key that's being melted in this session.
-   */
-  meltCoinPub: string;
-
-  /**
-   * How much of the coin's value is melted away
-   * with this refresh session?
+   * 512-bit secret that can be used to derive
+   * the other cryptographic material for the refresh session.
+   *
+   * FIXME:  We currently store the derived material, but
+   * should always derive it.
    */
-  amountRefreshInput: AmountJson;
+  sessionSecretSeed: string;
 
   /**
    * Sum of the value of denominations we want
@@ -1010,59 +1010,17 @@ export interface RefreshSessionRecord {
   amountRefreshOutput: AmountJson;
 
   /**
-   * Signature to confirm the melting.
+   * Hashed denominations of the newly requested coins.
    */
-  confirmSig: string;
-
-  /**
-   * Hased denominations of the newly requested coins.
-   */
-  newDenomHashes: string[];
-
-  /**
-   * Denominations of the newly requested coins.
-   */
-  newDenoms: string[];
-
-  /**
-   * Planchets for each cut-and-choose instance.
-   */
-  planchetsForGammas: RefreshPlanchet[][];
-
-  /**
-   * The transfer keys, kappa of them.
-   */
-  transferPubs: string[];
-
-  /**
-   * Private keys for the transfer public keys.
-   */
-  transferPrivs: string[];
+  newDenoms: {
+    denomPubHash: string;
+    count: number;
+  }[];
 
   /**
    * The no-reveal-index after we've done the melting.
    */
   norevealIndex?: number;
-
-  /**
-   * Hash of the session.
-   */
-  hash: string;
-
-  /**
-   * Timestamp when the refresh session finished.
-   */
-  finishedTimestamp: Timestamp | undefined;
-
-  /**
-   * When has this refresh session been created?
-   */
-  timestampCreated: Timestamp;
-
-  /**
-   * Base URL for the exchange we're doing the refresh with.
-   */
-  exchangeBaseUrl: string;
 }
 
 /**
@@ -1601,7 +1559,7 @@ class PurchasesStore extends Store<"purchases", 
PurchaseRecord> {
     string,
     PurchaseRecord
   >(this, "fulfillmentUrlIndex", "contractData.fulfillmentUrl");
-  
+
   orderIdIndex = new Index<"purchases", "orderIdIndex", string, 
PurchaseRecord>(
     this,
     "orderIdIndex",
@@ -1711,7 +1669,6 @@ class BankWithdrawUrisStore extends Store<
   }
 }
 
-
 /**
  */
 class BackupProvidersStore extends Store<

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]