1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
|
/*
This file is part of GNU Taler
(C) 2020 Taler Systems SA
GNU 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.
GNU 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
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
import { encodeCrock, getRandomBytes } from "@gnu-taler/taler-util";
import {
ConfigRecord,
WalletBackupConfState,
WalletStoresV1,
WALLET_BACKUP_STATE_KEY,
} from "../../db.js";
import { checkDbInvariant } from "../../util/invariants.js";
import { GetReadOnlyAccess } from "../../util/query.js";
import { InternalWalletState } from "../../common.js";
export async function provideBackupState(
ws: InternalWalletState,
): Promise<WalletBackupConfState> {
const bs: ConfigRecord | undefined = await ws.db
.mktx((x) => ({
config: x.config,
}))
.runReadOnly(async (tx) => {
return await tx.config.get(WALLET_BACKUP_STATE_KEY);
});
if (bs) {
checkDbInvariant(bs.key === WALLET_BACKUP_STATE_KEY);
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) => ({
config: x.config,
}))
.runReadWrite(async (tx) => {
let backupStateEntry: ConfigRecord | undefined = await tx.config.get(
WALLET_BACKUP_STATE_KEY,
);
if (!backupStateEntry) {
backupStateEntry = {
key: WALLET_BACKUP_STATE_KEY,
value: {
deviceId,
walletRootPub: k.pub,
walletRootPriv: k.priv,
lastBackupPlainHash: undefined,
},
};
await tx.config.put(backupStateEntry);
}
checkDbInvariant(backupStateEntry.key === WALLET_BACKUP_STATE_KEY);
return backupStateEntry.value;
});
}
export async function getWalletBackupState(
ws: InternalWalletState,
tx: GetReadOnlyAccess<{ config: typeof WalletStoresV1.config }>,
): Promise<WalletBackupConfState> {
const bs = await tx.config.get(WALLET_BACKUP_STATE_KEY);
checkDbInvariant(!!bs, "wallet backup state should be in DB");
checkDbInvariant(bs.key === WALLET_BACKUP_STATE_KEY);
return bs.value;
}
export async function setWalletDeviceId(
ws: InternalWalletState,
deviceId: string,
): Promise<void> {
await provideBackupState(ws);
await ws.db
.mktx((x) => ({
config: x.config,
}))
.runReadWrite(async (tx) => {
let backupStateEntry: ConfigRecord | undefined = await tx.config.get(
WALLET_BACKUP_STATE_KEY,
);
if (
!backupStateEntry ||
backupStateEntry.key !== WALLET_BACKUP_STATE_KEY
) {
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;
}
|