diff options
author | Florian Dold <florian@dold.me> | 2020-12-17 12:21:03 +0100 |
---|---|---|
committer | Florian Dold <florian@dold.me> | 2020-12-17 12:21:03 +0100 |
commit | 84d5b5e5ef34f7289256c6fd301206cda19be694 (patch) | |
tree | 5db646c7fb1b782b42150a38c6d4f1e6bdfea55f /packages/taler-wallet-core/src | |
parent | bafb52edff4d56bcb9e3c3d0a260f507c517b08c (diff) | |
download | wallet-core-84d5b5e5ef34f7289256c6fd301206cda19be694.tar.xz |
export complete backup, derive planchets in withdrawal
Diffstat (limited to 'packages/taler-wallet-core/src')
10 files changed, 91 insertions, 35 deletions
diff --git a/packages/taler-wallet-core/src/crypto/talerCrypto.ts b/packages/taler-wallet-core/src/crypto/talerCrypto.ts index 095957982..d28f11174 100644 --- a/packages/taler-wallet-core/src/crypto/talerCrypto.ts +++ b/packages/taler-wallet-core/src/crypto/talerCrypto.ts @@ -390,6 +390,25 @@ export function setupRefreshPlanchet( }; } +export function setupWithdrawPlanchet( + secretSeed: Uint8Array, + coinNumber: number, +): FreshCoin { + const info = stringToBytes("taler-withdrawal-coin-derivation"); + const saltArrBuf = new ArrayBuffer(4); + const salt = new Uint8Array(saltArrBuf); + const saltDataView = new DataView(saltArrBuf); + saltDataView.setUint32(0, coinNumber); + const out = kdf(64, secretSeed, salt, info); + const coinPriv = out.slice(0, 32); + const bks = out.slice(32, 64); + return { + bks, + coinPriv, + coinPub: eddsaGetPublic(coinPriv), + }; +} + export function setupTipPlanchet( secretSeed: Uint8Array, coinNumber: number, diff --git a/packages/taler-wallet-core/src/crypto/workers/cryptoImplementation.ts b/packages/taler-wallet-core/src/crypto/workers/cryptoImplementation.ts index 4f553c502..fc8b53eb7 100644 --- a/packages/taler-wallet-core/src/crypto/workers/cryptoImplementation.ts +++ b/packages/taler-wallet-core/src/crypto/workers/cryptoImplementation.ts @@ -61,13 +61,11 @@ import { rsaVerify, setupRefreshTransferPub, setupTipPlanchet, + setupWithdrawPlanchet, } from "../talerCrypto"; import { randomBytes } from "../primitives/nacl-fast"; import { kdf } from "../primitives/kdf"; -import { - Timestamp, - timestampTruncateToSecond, -} from "../../util/time"; +import { Timestamp, timestampTruncateToSecond } from "../../util/time"; import { Logger } from "../../util/logging"; import { @@ -161,10 +159,12 @@ export class CryptoImplementation { const reservePub = decodeCrock(req.reservePub); const reservePriv = decodeCrock(req.reservePriv); const denomPub = decodeCrock(req.denomPub); - const coinKeyPair = createEddsaKeyPair(); - const blindingFactor = createBlindingKeySecret(); - const coinPubHash = hash(coinKeyPair.eddsaPub); - const ev = rsaBlind(coinPubHash, blindingFactor, denomPub); + const derivedPlanchet = setupWithdrawPlanchet( + decodeCrock(req.secretSeed), + req.coinIndex, + ); + const coinPubHash = hash(derivedPlanchet.coinPub); + const ev = rsaBlind(coinPubHash, derivedPlanchet.bks, denomPub); const amountWithFee = Amounts.add(req.value, req.feeWithdraw).amount; const denomPubHash = hash(denomPub); const evHash = hash(ev); @@ -179,10 +179,10 @@ export class CryptoImplementation { const sig = eddsaSign(withdrawRequest, reservePriv); const planchet: PlanchetCreationResult = { - blindingKey: encodeCrock(blindingFactor), + blindingKey: encodeCrock(derivedPlanchet.bks), coinEv: encodeCrock(ev), - coinPriv: encodeCrock(coinKeyPair.eddsaPriv), - coinPub: encodeCrock(coinKeyPair.eddsaPub), + coinPriv: encodeCrock(derivedPlanchet.coinPriv), + coinPub: encodeCrock(derivedPlanchet.coinPub), coinValue: req.value, denomPub: encodeCrock(denomPub), denomPubHash: encodeCrock(denomPubHash), diff --git a/packages/taler-wallet-core/src/operations/backup.ts b/packages/taler-wallet-core/src/operations/backup.ts index 1e5aa542d..f071b6d08 100644 --- a/packages/taler-wallet-core/src/operations/backup.ts +++ b/packages/taler-wallet-core/src/operations/backup.ts @@ -198,17 +198,17 @@ export async function exportBackup( const withdrawalGroups = (withdrawalGroupsByReserve[ wg.reservePub ] ??= []); - // FIXME: finish! - // withdrawalGroups.push({ - // raw_withdrawal_amount: Amounts.stringify(wg.rawWithdrawalAmount), - // selected_denoms: wg.denomsSel.selectedDenoms.map((x) => ({ - // count: x.count, - // denom_pub_hash: x.denomPubHash, - // })), - // timestamp_start: wg.timestampStart, - // timestamp_finish: wg.timestampFinish, - // withdrawal_group_id: wg.withdrawalGroupId, - // }); + withdrawalGroups.push({ + raw_withdrawal_amount: Amounts.stringify(wg.rawWithdrawalAmount), + selected_denoms: wg.denomsSel.selectedDenoms.map((x) => ({ + count: x.count, + denom_pub_hash: x.denomPubHash, + })), + timestamp_start: wg.timestampStart, + timestamp_finish: wg.timestampFinish, + withdrawal_group_id: wg.withdrawalGroupId, + secret_seed: wg.secretSeed + }); }); await tx.iter(Stores.reserves).forEach((reserve) => { @@ -572,11 +572,29 @@ export async function encryptBackup( throw Error("not implemented"); } -export function importBackup( +export async function importBackup( ws: InternalWalletState, backupRequest: BackupRequest, ): Promise<void> { - throw Error("not implemented"); + await provideBackupState(ws); + return ws.db.runWithWriteTransaction( + [ + Stores.config, + Stores.exchanges, + Stores.coins, + Stores.denominations, + Stores.purchases, + Stores.proposals, + Stores.refreshGroups, + Stores.backupProviders, + Stores.tips, + Stores.recoupGroups, + Stores.reserves, + Stores.withdrawalGroups, + ], + async (tx) => { + + }); } function deriveAccountKeyPair( diff --git a/packages/taler-wallet-core/src/operations/reserves.ts b/packages/taler-wallet-core/src/operations/reserves.ts index 95c38120c..4e4db1fc9 100644 --- a/packages/taler-wallet-core/src/operations/reserves.ts +++ b/packages/taler-wallet-core/src/operations/reserves.ts @@ -623,6 +623,7 @@ async function updateReserve( retryInfo: initRetryInfo(), lastError: undefined, denomsSel: denomSelectionInfoToState(denomSelInfo), + secretSeed: encodeCrock(getRandomBytes(64)), }; newReserve.lastError = undefined; diff --git a/packages/taler-wallet-core/src/operations/tip.ts b/packages/taler-wallet-core/src/operations/tip.ts index f47f76623..f683999bc 100644 --- a/packages/taler-wallet-core/src/operations/tip.ts +++ b/packages/taler-wallet-core/src/operations/tip.ts @@ -48,7 +48,7 @@ import { } from "../util/http"; import { URL } from "../util/url"; import { Logger } from "../util/logging"; -import { checkDbInvariant } from "../util/invariants"; +import { checkDbInvariant, checkLogicInvariant } from "../util/invariants"; import { TalerErrorCode } from "../TalerErrorCode"; import { initRetryInfo, updateRetryInfoTimeout } from "../util/retries"; import { j2s } from "../util/helpers"; @@ -221,13 +221,13 @@ async function processTipImpl( dh.denomPubHash, ]); checkDbInvariant(!!denom, "denomination should be in database"); - denomForPlanchet[planchets.length] = denom; for (let i = 0; i < dh.count; i++) { const p = await ws.cryptoApi.createTipPlanchet({ - denomPub: dh.denomPubHash, + denomPub: denom.denomPub, planchetIndex: planchets.length, secretSeed: tipRecord.secretSeed, }); + denomForPlanchet[planchets.length] = denom; planchets.push(p); planchetsDetail.push({ coin_ev: p.coinEv, @@ -275,7 +275,9 @@ async function processTipImpl( const blindedSig = response.blind_sigs[i].blind_sig; const denom = denomForPlanchet[i]; + checkLogicInvariant(!!denom); const planchet = planchets[i]; + checkLogicInvariant(!!planchet); const denomSig = await ws.cryptoApi.rsaUnblind( blindedSig, diff --git a/packages/taler-wallet-core/src/operations/withdraw.ts b/packages/taler-wallet-core/src/operations/withdraw.ts index 758b80787..c2dfcd1c4 100644 --- a/packages/taler-wallet-core/src/operations/withdraw.ts +++ b/packages/taler-wallet-core/src/operations/withdraw.ts @@ -284,6 +284,8 @@ async function processPlanchetGenerate( reservePriv: reserve.reservePriv, reservePub: reserve.reservePub, value: denom.value, + coinIndex: coinIdx, + secretSeed: withdrawalGroup.secretSeed, }); const newPlanchet: PlanchetRecord = { blindingKey: r.blindingKey, diff --git a/packages/taler-wallet-core/src/types/backupTypes.ts b/packages/taler-wallet-core/src/types/backupTypes.ts index a3261ae35..d40d4fa6c 100644 --- a/packages/taler-wallet-core/src/types/backupTypes.ts +++ b/packages/taler-wallet-core/src/types/backupTypes.ts @@ -626,6 +626,11 @@ export interface BackupWithdrawalGroup { withdrawal_group_id: string; /** + * Secret seed to derive the planchets. + */ + secret_seed: string; + + /** * When was the withdrawal operation started started? * Timestamp in milliseconds. */ @@ -653,14 +658,6 @@ export interface BackupWithdrawalGroup { denom_pub_hash: string; count: number; }[]; - - /** - * One planchet/coin for each selected denomination. - */ - planchets: { - blinding_key: string; - coin_priv: string; - }[]; } export enum BackupRefundState { diff --git a/packages/taler-wallet-core/src/types/dbTypes.ts b/packages/taler-wallet-core/src/types/dbTypes.ts index 71a591310..7ba3b8604 100644 --- a/packages/taler-wallet-core/src/types/dbTypes.ts +++ b/packages/taler-wallet-core/src/types/dbTypes.ts @@ -1322,6 +1322,11 @@ export interface DenomSelectionState { export interface WithdrawalGroupRecord { withdrawalGroupId: string; + /** + * Secret seed used to derive planchets. + */ + secretSeed: string; + reservePub: string; exchangeBaseUrl: string; diff --git a/packages/taler-wallet-core/src/types/walletTypes.ts b/packages/taler-wallet-core/src/types/walletTypes.ts index 7dc675b38..1b962e1c4 100644 --- a/packages/taler-wallet-core/src/types/walletTypes.ts +++ b/packages/taler-wallet-core/src/types/walletTypes.ts @@ -557,6 +557,8 @@ export interface PlanchetCreationResult { } export interface PlanchetCreationRequest { + secretSeed: string; + coinIndex: number; value: AmountJson; feeWithdraw: AmountJson; denomPub: string; diff --git a/packages/taler-wallet-core/src/util/invariants.ts b/packages/taler-wallet-core/src/util/invariants.ts index fa381d333..b788d044e 100644 --- a/packages/taler-wallet-core/src/util/invariants.ts +++ b/packages/taler-wallet-core/src/util/invariants.ts @@ -27,3 +27,13 @@ export function checkDbInvariant(b: boolean, m?: string): asserts b { } } } + +export function checkLogicInvariant(b: boolean, m?: string): asserts b { + if (!b) { + if (m) { + throw Error(`BUG: logic invariant failed (${m})`); + } else { + throw Error("BUG: logic invariant failed"); + } + } +} |