aboutsummaryrefslogtreecommitdiff
path: root/packages/taler-wallet-core/src/operations/backup/index.ts
diff options
context:
space:
mode:
authorFlorian Dold <florian@dold.me>2023-08-30 18:01:18 +0200
committerFlorian Dold <florian@dold.me>2023-08-30 18:01:18 +0200
commita713d90c3c564408309d92223d383ecc9225924f (patch)
tree18142a4ce3d98df6e8a4945dbca23c47715eee6a /packages/taler-wallet-core/src/operations/backup/index.ts
parent0a4782a0da631aba31dc0ecef7427df2467cc3e6 (diff)
downloadwallet-core-a713d90c3c564408309d92223d383ecc9225924f.tar.xz
wallet-core: remove old sync code, add stored backups skeleton
Diffstat (limited to 'packages/taler-wallet-core/src/operations/backup/index.ts')
-rw-r--r--packages/taler-wallet-core/src/operations/backup/index.ts188
1 files changed, 89 insertions, 99 deletions
diff --git a/packages/taler-wallet-core/src/operations/backup/index.ts b/packages/taler-wallet-core/src/operations/backup/index.ts
index e35765165..a5e8dbd42 100644
--- a/packages/taler-wallet-core/src/operations/backup/index.ts
+++ b/packages/taler-wallet-core/src/operations/backup/index.ts
@@ -43,7 +43,6 @@ import {
TalerErrorDetail,
TalerPreciseTimestamp,
URL,
- WalletBackupContentV1,
buildCodecForObject,
buildCodecForUnion,
bytesToString,
@@ -99,9 +98,8 @@ import {
TaskIdentifiers,
} from "../common.js";
import { checkPaymentByProposalId, preparePayForUri } from "../pay-merchant.js";
-import { exportBackup } from "./export.js";
-import { BackupCryptoPrecomputedData, importBackup } from "./import.js";
-import { getWalletBackupState, provideBackupState } from "./state.js";
+import { WalletStoresV1 } from "../../db.js";
+import { GetReadOnlyAccess } from "../../util/query.js";
const logger = new Logger("operations/backup.ts");
@@ -131,7 +129,7 @@ const magic = "TLRWBK01";
*/
export async function encryptBackup(
config: WalletBackupConfState,
- blob: WalletBackupContentV1,
+ blob: any,
): Promise<Uint8Array> {
const chunks: Uint8Array[] = [];
chunks.push(stringToBytes(magic));
@@ -150,64 +148,6 @@ export async function encryptBackup(
return concatArrays(chunks);
}
-/**
- * Compute cryptographic values for a backup blob.
- *
- * FIXME: Take data that we already know from the DB.
- * FIXME: Move computations into crypto worker.
- */
-async function computeBackupCryptoData(
- cryptoApi: TalerCryptoInterface,
- backupContent: WalletBackupContentV1,
-): Promise<BackupCryptoPrecomputedData> {
- const cryptoData: BackupCryptoPrecomputedData = {
- coinPrivToCompletedCoin: {},
- rsaDenomPubToHash: {},
- proposalIdToContractTermsHash: {},
- proposalNoncePrivToPub: {},
- reservePrivToPub: {},
- };
- for (const backupExchangeDetails of backupContent.exchange_details) {
- for (const backupDenom of backupExchangeDetails.denominations) {
- if (backupDenom.denom_pub.cipher !== DenomKeyType.Rsa) {
- throw Error("unsupported cipher");
- }
- for (const backupCoin of backupDenom.coins) {
- const coinPub = encodeCrock(
- eddsaGetPublic(decodeCrock(backupCoin.coin_priv)),
- );
- const blindedCoin = rsaBlind(
- hash(decodeCrock(backupCoin.coin_priv)),
- decodeCrock(backupCoin.blinding_key),
- decodeCrock(backupDenom.denom_pub.rsa_public_key),
- );
- cryptoData.coinPrivToCompletedCoin[backupCoin.coin_priv] = {
- coinEvHash: encodeCrock(hash(blindedCoin)),
- coinPub,
- };
- }
- cryptoData.rsaDenomPubToHash[backupDenom.denom_pub.rsa_public_key] =
- encodeCrock(hashDenomPub(backupDenom.denom_pub));
- }
- }
- for (const backupWg of backupContent.withdrawal_groups) {
- cryptoData.reservePrivToPub[backupWg.reserve_priv] = encodeCrock(
- eddsaGetPublic(decodeCrock(backupWg.reserve_priv)),
- );
- }
- for (const purch of backupContent.purchases) {
- if (!purch.contract_terms_raw) continue;
- const { h: contractTermsHash } = await cryptoApi.hashString({
- str: canonicalJson(purch.contract_terms_raw),
- });
- const noncePub = encodeCrock(eddsaGetPublic(decodeCrock(purch.nonce_priv)));
- cryptoData.proposalNoncePrivToPub[purch.nonce_priv] = noncePub;
- cryptoData.proposalIdToContractTermsHash[purch.proposal_id] =
- contractTermsHash;
- }
- return cryptoData;
-}
-
function deriveAccountKeyPair(
bc: WalletBackupConfState,
providerUrl: string,
@@ -262,7 +202,9 @@ async function runBackupCycleForProvider(
return TaskRunResult.finished();
}
- const backupJson = await exportBackup(ws);
+ //const backupJson = await exportBackup(ws);
+ // FIXME: re-implement backup
+ const backupJson = {};
const backupConfig = await provideBackupState(ws);
const encBackup = await encryptBackup(backupConfig, backupJson);
const currentBackupHash = hash(encBackup);
@@ -441,9 +383,9 @@ async function runBackupCycleForProvider(
logger.info("conflicting backup found");
const backupEnc = new Uint8Array(await resp.bytes());
const backupConfig = await provideBackupState(ws);
- const blob = await decryptBackup(backupConfig, backupEnc);
- const cryptoData = await computeBackupCryptoData(ws.cryptoApi, blob);
- await importBackup(ws, blob, cryptoData);
+ // const blob = await decryptBackup(backupConfig, backupEnc);
+ // FIXME: Re-implement backup import with merging
+ // await importBackup(ws, blob, cryptoData);
await ws.db
.mktx((x) => [x.backupProviders, x.operationRetries])
.runReadWrite(async (tx) => {
@@ -789,18 +731,6 @@ export interface BackupInfo {
providers: ProviderInfo[];
}
-export async function importBackupPlain(
- ws: InternalWalletState,
- blob: any,
-): Promise<void> {
- // FIXME: parse
- const backup: WalletBackupContentV1 = blob;
-
- const cryptoData = await computeBackupCryptoData(ws.cryptoApi, backup);
-
- await importBackup(ws, blob, cryptoData);
-}
-
export enum ProviderPaymentType {
Unpaid = "unpaid",
Pending = "pending",
@@ -1036,23 +966,10 @@ export async function loadBackupRecovery(
}
}
-export async function exportBackupEncrypted(
- ws: InternalWalletState,
-): Promise<Uint8Array> {
- await provideBackupState(ws);
- const blob = await exportBackup(ws);
- const bs = await ws.db
- .mktx((x) => [x.config])
- .runReadOnly(async (tx) => {
- return await getWalletBackupState(ws, tx);
- });
- return encryptBackup(bs, blob);
-}
-
export async function decryptBackup(
backupConfig: WalletBackupConfState,
data: Uint8Array,
-): Promise<WalletBackupContentV1> {
+): Promise<any> {
const rMagic = bytesToString(data.slice(0, 8));
if (rMagic != magic) {
throw Error("invalid backup file (magic tag mismatch)");
@@ -1068,12 +985,85 @@ export async function decryptBackup(
return JSON.parse(bytesToString(gunzipSync(dataCompressed)));
}
-export async function importBackupEncrypted(
+export async function provideBackupState(
ws: InternalWalletState,
- data: Uint8Array,
+): Promise<WalletBackupConfState> {
+ const bs: ConfigRecord | undefined = await ws.db
+ .mktx((stores) => [stores.config])
+ .runReadOnly(async (tx) => {
+ return await tx.config.get(ConfigRecordKey.WalletBackupState);
+ });
+ if (bs) {
+ checkDbInvariant(bs.key === ConfigRecordKey.WalletBackupState);
+ return bs.value;
+ }
+ // We need to generate the key outside of the transaction
+ // due to how IndexedDB works.
+ const k = await ws.cryptoApi.createEddsaKeypair({});
+ const d = getRandomBytes(5);
+ // FIXME: device ID should be configured when wallet is initialized
+ // and be based on hostname
+ const deviceId = `wallet-core-${encodeCrock(d)}`;
+ return await ws.db
+ .mktx((x) => [x.config])
+ .runReadWrite(async (tx) => {
+ let backupStateEntry: ConfigRecord | undefined = await tx.config.get(
+ ConfigRecordKey.WalletBackupState,
+ );
+ if (!backupStateEntry) {
+ backupStateEntry = {
+ key: ConfigRecordKey.WalletBackupState,
+ value: {
+ deviceId,
+ walletRootPub: k.pub,
+ walletRootPriv: k.priv,
+ lastBackupPlainHash: undefined,
+ },
+ };
+ await tx.config.put(backupStateEntry);
+ }
+ checkDbInvariant(
+ backupStateEntry.key === ConfigRecordKey.WalletBackupState,
+ );
+ return backupStateEntry.value;
+ });
+}
+
+export async function getWalletBackupState(
+ ws: InternalWalletState,
+ tx: GetReadOnlyAccess<{ config: typeof WalletStoresV1.config }>,
+): Promise<WalletBackupConfState> {
+ const bs = await tx.config.get(ConfigRecordKey.WalletBackupState);
+ checkDbInvariant(!!bs, "wallet backup state should be in DB");
+ checkDbInvariant(bs.key === ConfigRecordKey.WalletBackupState);
+ return bs.value;
+}
+
+export async function setWalletDeviceId(
+ ws: InternalWalletState,
+ deviceId: string,
): Promise<void> {
- const backupConfig = await provideBackupState(ws);
- const blob = await decryptBackup(backupConfig, data);
- const cryptoData = await computeBackupCryptoData(ws.cryptoApi, blob);
- await importBackup(ws, blob, cryptoData);
+ await provideBackupState(ws);
+ await ws.db
+ .mktx((x) => [x.config])
+ .runReadWrite(async (tx) => {
+ let backupStateEntry: ConfigRecord | undefined = await tx.config.get(
+ ConfigRecordKey.WalletBackupState,
+ );
+ if (
+ !backupStateEntry ||
+ backupStateEntry.key !== ConfigRecordKey.WalletBackupState
+ ) {
+ return;
+ }
+ backupStateEntry.value.deviceId = deviceId;
+ await tx.config.put(backupStateEntry);
+ });
+}
+
+export async function getWalletDeviceId(
+ ws: InternalWalletState,
+): Promise<string> {
+ const bs = await provideBackupState(ws);
+ return bs.deviceId;
}