set gzip timestamp to 0 in backup blob

This commit is contained in:
Florian Dold 2021-06-23 11:46:10 +02:00
parent 6c14268c1a
commit eaced5ca63
No known key found for this signature in database
GPG Key ID: D2E4F00F29D02A4B
2 changed files with 94 additions and 67 deletions

View File

@ -24,44 +24,54 @@
/** /**
* Imports. * Imports.
*/ */
import { hash } from "../../crypto/primitives/nacl-fast.js";
import { import {
WalletBackupContentV1, Amounts,
BackupExchange,
BackupCoin,
BackupDenomination,
BackupReserve,
BackupPurchase,
BackupProposal,
BackupRefreshGroup,
BackupBackupProvider, BackupBackupProvider,
BackupTip,
BackupRecoupGroup,
BackupWithdrawalGroup,
BackupBackupProviderTerms, BackupBackupProviderTerms,
BackupCoin,
BackupCoinSource, BackupCoinSource,
BackupCoinSourceType, BackupCoinSourceType,
BackupDenomination,
BackupExchange,
BackupExchangeDetails,
BackupExchangeWireFee, BackupExchangeWireFee,
BackupRefundItem, BackupProposal,
BackupRefundState,
BackupProposalStatus, BackupProposalStatus,
BackupPurchase,
BackupRecoupGroup,
BackupRefreshGroup,
BackupRefreshOldCoin, BackupRefreshOldCoin,
BackupRefreshSession, BackupRefreshSession,
BackupExchangeDetails, BackupRefundItem,
BackupRefundState,
BackupReserve,
BackupTip,
BackupWithdrawalGroup,
canonicalizeBaseUrl,
canonicalJson,
getTimestampNow,
Logger,
timestampToIsoString,
WalletBackupContentV1,
} from "@gnu-taler/taler-util"; } from "@gnu-taler/taler-util";
import { InternalWalletState } from "../../common.js"; import { InternalWalletState } from "../../common.js";
import { provideBackupState, getWalletBackupState } from "./state.js"; import { hash } from "../../crypto/primitives/nacl-fast.js";
import { Amounts, getTimestampNow } from "@gnu-taler/taler-util";
import { import {
encodeCrock,
getRandomBytes,
stringToBytes,
} from "../../crypto/talerCrypto.js";
import {
AbortStatus,
CoinSourceType, CoinSourceType,
CoinStatus, CoinStatus,
RefundState,
AbortStatus,
ProposalStatus, ProposalStatus,
RefundState,
WALLET_BACKUP_STATE_KEY, WALLET_BACKUP_STATE_KEY,
} from "../../db.js"; } from "../../db.js";
import { encodeCrock, stringToBytes, getRandomBytes } from "../../crypto/talerCrypto.js"; import { getWalletBackupState, provideBackupState } from "./state.js";
import { canonicalizeBaseUrl, canonicalJson } from "@gnu-taler/taler-util";
const logger = new Logger("backup/export.ts");
export async function exportBackup( export async function exportBackup(
ws: InternalWalletState, ws: InternalWalletState,
@ -444,8 +454,10 @@ export async function exportBackup(
}); });
}); });
const ts = getTimestampNow();
if (!bs.lastBackupTimestamp) { if (!bs.lastBackupTimestamp) {
bs.lastBackupTimestamp = getTimestampNow(); bs.lastBackupTimestamp = ts;
} }
const backupBlob: WalletBackupContentV1 = { const backupBlob: WalletBackupContentV1 = {
@ -469,18 +481,30 @@ export async function exportBackup(
tombstones: [], 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)))); 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( bs.lastBackupPlainHash = encodeCrock(
hash(stringToBytes(canonicalJson(backupBlob))), hash(stringToBytes(canonicalJson(backupBlob))),
); );
bs.lastBackupNonce = encodeCrock(getRandomBytes(32)); bs.lastBackupNonce = encodeCrock(getRandomBytes(32));
logger.trace(
`setting timestamp to ${timestampToIsoString(ts)} and nonce to ${
bs.lastBackupNonce
}`,
);
await tx.config.put({ await tx.config.put({
key: WALLET_BACKUP_STATE_KEY, key: WALLET_BACKUP_STATE_KEY,
value: bs, value: bs,
}); });
} else {
logger.trace("backup hash did not change");
} }
return backupBlob; return backupBlob;

View File

@ -24,24 +24,39 @@
/** /**
* Imports. * Imports.
*/ */
import { InternalWalletState } from "../../common.js";
import { import {
AmountString, AmountString,
BackupRecovery, BackupRecovery,
buildCodecForObject,
canonicalizeBaseUrl,
canonicalJson,
Codec,
codecForAmountString, codecForAmountString,
codecForBoolean,
codecForNumber,
codecForString,
codecOptional,
ConfirmPayResultType,
durationFromSpec,
getTimestampNow,
j2s,
Logger,
PreparePayResultType,
RecoveryLoadRequest,
RecoveryMergeStrategy,
TalerErrorDetails,
Timestamp,
timestampAddDuration,
URL,
WalletBackupContentV1, WalletBackupContentV1,
} from "@gnu-taler/taler-util"; } from "@gnu-taler/taler-util";
import { gunzipSync, gzipSync } from "fflate";
import { InternalWalletState } from "../../common.js";
import { kdf } from "../../crypto/primitives/kdf.js";
import { import {
BackupProviderRecord, secretbox,
BackupProviderTerms, secretbox_open,
ConfigRecord, } from "../../crypto/primitives/nacl-fast.js";
WalletBackupConfState,
WALLET_BACKUP_STATE_KEY,
} from "../../db.js";
import {
checkDbInvariant,
checkLogicInvariant,
} from "../../util/invariants.js";
import { import {
bytesToString, bytesToString,
decodeCrock, decodeCrock,
@ -53,43 +68,24 @@ import {
rsaBlind, rsaBlind,
stringToBytes, stringToBytes,
} from "../../crypto/talerCrypto.js"; } from "../../crypto/talerCrypto.js";
import { canonicalizeBaseUrl, canonicalJson, j2s } from "@gnu-taler/taler-util"; import { CryptoApi } from "../../crypto/workers/cryptoApi.js";
import { import {
durationFromSpec, BackupProviderRecord,
getTimestampNow, BackupProviderTerms,
Timestamp, ConfigRecord,
timestampAddDuration, WalletBackupConfState,
URL WALLET_BACKUP_STATE_KEY,
} from "@gnu-taler/taler-util"; } from "../../db.js";
import {
buildCodecForObject,
Codec,
codecForBoolean,
codecForNumber,
codecForString,
codecOptional,
} from "@gnu-taler/taler-util";
import { import {
HttpResponseStatus, HttpResponseStatus,
readSuccessResponseJsonOrThrow, readSuccessResponseJsonOrThrow,
readTalerErrorResponse, readTalerErrorResponse,
} from "../../util/http.js"; } from "../../util/http.js";
import { Logger } from "@gnu-taler/taler-util"; import {
import { gunzipSync, gzipSync } from "fflate"; checkDbInvariant,
import { kdf } from "../../crypto/primitives/kdf.js"; checkLogicInvariant,
} from "../../util/invariants.js";
import { initRetryInfo } from "../../util/retries.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";
import { import {
checkPaymentByProposalId, checkPaymentByProposalId,
confirmPay, confirmPay,
@ -97,7 +93,7 @@ import {
} from "../pay.js"; } from "../pay.js";
import { exportBackup } from "./export.js"; import { exportBackup } from "./export.js";
import { BackupCryptoPrecomputedData, importBackup } from "./import.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"); const logger = new Logger("operations/backup.ts");
@ -137,7 +133,9 @@ export async function encryptBackup(
chunks.push(nonce); chunks.push(nonce);
const backupJsonContent = canonicalJson(blob); const backupJsonContent = canonicalJson(blob);
logger.trace("backup JSON size", backupJsonContent.length); logger.trace("backup JSON size", backupJsonContent.length);
const compressedContent = gzipSync(stringToBytes(backupJsonContent)); const compressedContent = gzipSync(stringToBytes(backupJsonContent), {
mtime: 0,
});
const secret = deriveBlobSecret(config); const secret = deriveBlobSecret(config);
const encrypted = secretbox(compressedContent, nonce.slice(0, 24), secret); const encrypted = secretbox(compressedContent, nonce.slice(0, 24), secret);
chunks.push(encrypted); chunks.push(encrypted);
@ -261,7 +259,12 @@ async function runBackupCycleForProvider(
backupJson, backupJson,
} = args; } = args;
const accountKeyPair = deriveAccountKeyPair(backupConfig, provider.baseUrl); 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(`trying to upload backup to ${provider.baseUrl}`);
logger.trace(`old hash ${oldHash}, new hash ${newHash}`);
const syncSig = await ws.cryptoApi.makeSyncSignature({ const syncSig = await ws.cryptoApi.makeSyncSignature({
newHash: encodeCrock(currentBackupHash), newHash: encodeCrock(currentBackupHash),