diff options
-rw-r--r-- | packages/taler-wallet-core/src/operations/backup/export.ts | 72 | ||||
-rw-r--r-- | packages/taler-wallet-core/src/operations/backup/index.ts | 89 |
2 files changed, 94 insertions, 67 deletions
diff --git a/packages/taler-wallet-core/src/operations/backup/export.ts b/packages/taler-wallet-core/src/operations/backup/export.ts index ab2262653..eae7995ca 100644 --- a/packages/taler-wallet-core/src/operations/backup/export.ts +++ b/packages/taler-wallet-core/src/operations/backup/export.ts @@ -24,44 +24,54 @@ /** * Imports. */ -import { hash } from "../../crypto/primitives/nacl-fast.js"; import { - WalletBackupContentV1, - BackupExchange, - BackupCoin, - BackupDenomination, - BackupReserve, - BackupPurchase, - BackupProposal, - BackupRefreshGroup, + Amounts, BackupBackupProvider, - BackupTip, - BackupRecoupGroup, - BackupWithdrawalGroup, BackupBackupProviderTerms, + BackupCoin, BackupCoinSource, BackupCoinSourceType, + BackupDenomination, + BackupExchange, + BackupExchangeDetails, BackupExchangeWireFee, - BackupRefundItem, - BackupRefundState, + BackupProposal, BackupProposalStatus, + BackupPurchase, + BackupRecoupGroup, + BackupRefreshGroup, BackupRefreshOldCoin, BackupRefreshSession, - BackupExchangeDetails, + BackupRefundItem, + BackupRefundState, + BackupReserve, + BackupTip, + BackupWithdrawalGroup, + canonicalizeBaseUrl, + canonicalJson, + getTimestampNow, + Logger, + timestampToIsoString, + WalletBackupContentV1, } from "@gnu-taler/taler-util"; import { InternalWalletState } from "../../common.js"; -import { provideBackupState, getWalletBackupState } from "./state.js"; -import { Amounts, getTimestampNow } from "@gnu-taler/taler-util"; +import { hash } from "../../crypto/primitives/nacl-fast.js"; import { + encodeCrock, + getRandomBytes, + stringToBytes, +} from "../../crypto/talerCrypto.js"; +import { + AbortStatus, CoinSourceType, CoinStatus, - RefundState, - AbortStatus, ProposalStatus, + RefundState, WALLET_BACKUP_STATE_KEY, } from "../../db.js"; -import { encodeCrock, stringToBytes, getRandomBytes } from "../../crypto/talerCrypto.js"; -import { canonicalizeBaseUrl, canonicalJson } from "@gnu-taler/taler-util"; +import { getWalletBackupState, provideBackupState } from "./state.js"; + +const logger = new Logger("backup/export.ts"); export async function exportBackup( ws: InternalWalletState, @@ -444,8 +454,10 @@ export async function exportBackup( }); }); + const ts = getTimestampNow(); + if (!bs.lastBackupTimestamp) { - bs.lastBackupTimestamp = getTimestampNow(); + bs.lastBackupTimestamp = ts; } const backupBlob: WalletBackupContentV1 = { @@ -469,18 +481,30 @@ export async function exportBackup( tombstones: [], }; - // If the backup changed, we increment our clock. + // If the backup changed, we change our nonce and timestamp. let h = encodeCrock(hash(stringToBytes(canonicalJson(backupBlob)))); - if (h != bs.lastBackupPlainHash) { + if (h !== bs.lastBackupPlainHash) { + logger.trace( + `plain backup hash changed (from ${bs.lastBackupPlainHash}to ${h})`, + ); + bs.lastBackupTimestamp = ts; + backupBlob.timestamp = ts; bs.lastBackupPlainHash = encodeCrock( hash(stringToBytes(canonicalJson(backupBlob))), ); bs.lastBackupNonce = encodeCrock(getRandomBytes(32)); + logger.trace( + `setting timestamp to ${timestampToIsoString(ts)} and nonce to ${ + bs.lastBackupNonce + }`, + ); await tx.config.put({ key: WALLET_BACKUP_STATE_KEY, value: bs, }); + } else { + logger.trace("backup hash did not change"); } return backupBlob; diff --git a/packages/taler-wallet-core/src/operations/backup/index.ts b/packages/taler-wallet-core/src/operations/backup/index.ts index 041c927a5..0d3cf5786 100644 --- a/packages/taler-wallet-core/src/operations/backup/index.ts +++ b/packages/taler-wallet-core/src/operations/backup/index.ts @@ -24,24 +24,39 @@ /** * Imports. */ -import { InternalWalletState } from "../../common.js"; import { AmountString, BackupRecovery, + buildCodecForObject, + canonicalizeBaseUrl, + canonicalJson, + Codec, codecForAmountString, + codecForBoolean, + codecForNumber, + codecForString, + codecOptional, + ConfirmPayResultType, + durationFromSpec, + getTimestampNow, + j2s, + Logger, + PreparePayResultType, + RecoveryLoadRequest, + RecoveryMergeStrategy, + TalerErrorDetails, + Timestamp, + timestampAddDuration, + URL, WalletBackupContentV1, } from "@gnu-taler/taler-util"; +import { gunzipSync, gzipSync } from "fflate"; +import { InternalWalletState } from "../../common.js"; +import { kdf } from "../../crypto/primitives/kdf.js"; import { - BackupProviderRecord, - BackupProviderTerms, - ConfigRecord, - WalletBackupConfState, - WALLET_BACKUP_STATE_KEY, -} from "../../db.js"; -import { - checkDbInvariant, - checkLogicInvariant, -} from "../../util/invariants.js"; + secretbox, + secretbox_open, +} from "../../crypto/primitives/nacl-fast.js"; import { bytesToString, decodeCrock, @@ -53,43 +68,24 @@ import { rsaBlind, stringToBytes, } from "../../crypto/talerCrypto.js"; -import { canonicalizeBaseUrl, canonicalJson, j2s } from "@gnu-taler/taler-util"; -import { - durationFromSpec, - getTimestampNow, - Timestamp, - timestampAddDuration, - URL -} from "@gnu-taler/taler-util"; +import { CryptoApi } from "../../crypto/workers/cryptoApi.js"; import { - buildCodecForObject, - Codec, - codecForBoolean, - codecForNumber, - codecForString, - codecOptional, -} from "@gnu-taler/taler-util"; + BackupProviderRecord, + BackupProviderTerms, + ConfigRecord, + WalletBackupConfState, + WALLET_BACKUP_STATE_KEY, +} from "../../db.js"; import { HttpResponseStatus, readSuccessResponseJsonOrThrow, readTalerErrorResponse, } from "../../util/http.js"; -import { Logger } from "@gnu-taler/taler-util"; -import { gunzipSync, gzipSync } from "fflate"; -import { kdf } from "../../crypto/primitives/kdf.js"; -import { initRetryInfo } from "../../util/retries.js"; import { - ConfirmPayResultType, - PreparePayResultType, - RecoveryLoadRequest, - RecoveryMergeStrategy, - TalerErrorDetails, -} from "@gnu-taler/taler-util"; -import { CryptoApi } from "../../crypto/workers/cryptoApi.js"; -import { - secretbox, - secretbox_open, -} from "../../crypto/primitives/nacl-fast.js"; + checkDbInvariant, + checkLogicInvariant, +} from "../../util/invariants.js"; +import { initRetryInfo } from "../../util/retries.js"; import { checkPaymentByProposalId, confirmPay, @@ -97,7 +93,7 @@ import { } from "../pay.js"; import { exportBackup } from "./export.js"; import { BackupCryptoPrecomputedData, importBackup } from "./import.js"; -import { provideBackupState, getWalletBackupState } from "./state.js"; +import { getWalletBackupState, provideBackupState } from "./state.js"; const logger = new Logger("operations/backup.ts"); @@ -137,7 +133,9 @@ export async function encryptBackup( chunks.push(nonce); const backupJsonContent = canonicalJson(blob); logger.trace("backup JSON size", backupJsonContent.length); - const compressedContent = gzipSync(stringToBytes(backupJsonContent)); + const compressedContent = gzipSync(stringToBytes(backupJsonContent), { + mtime: 0, + }); const secret = deriveBlobSecret(config); const encrypted = secretbox(compressedContent, nonce.slice(0, 24), secret); chunks.push(encrypted); @@ -261,7 +259,12 @@ async function runBackupCycleForProvider( backupJson, } = args; const accountKeyPair = deriveAccountKeyPair(backupConfig, provider.baseUrl); + + const newHash = encodeCrock(currentBackupHash); + const oldHash = provider.lastBackupHash; + logger.trace(`trying to upload backup to ${provider.baseUrl}`); + logger.trace(`old hash ${oldHash}, new hash ${newHash}`); const syncSig = await ws.cryptoApi.makeSyncSignature({ newHash: encodeCrock(currentBackupHash), |