@pomade/core 0.0.11 → 0.1.0-pre.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client.d.ts +141 -19
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +70 -184
- package/dist/client.js.map +1 -1
- package/dist/message.d.ts +26 -259
- package/dist/message.d.ts.map +1 -1
- package/dist/message.js +1 -127
- package/dist/message.js.map +1 -1
- package/dist/pomade-signer.d.ts +0 -1
- package/dist/pomade-signer.d.ts.map +1 -1
- package/dist/pomade-signer.js +1 -4
- package/dist/pomade-signer.js.map +1 -1
- package/dist/rpc.d.ts +6 -46
- package/dist/rpc.d.ts.map +1 -1
- package/dist/rpc.js +31 -183
- package/dist/rpc.js.map +1 -1
- package/dist/schema.d.ts +78 -126
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +67 -105
- package/dist/schema.js.map +1 -1
- package/dist/signer.d.ts +26 -43
- package/dist/signer.d.ts.map +1 -1
- package/dist/signer.js +226 -365
- package/dist/signer.js.map +1 -1
- package/dist/storage.d.ts +2 -5
- package/dist/storage.d.ts.map +1 -1
- package/dist/storage.js +1 -1
- package/dist/storage.js.map +1 -1
- package/dist/util.d.ts +5 -26
- package/dist/util.d.ts.map +1 -1
- package/dist/util.js +14 -57
- package/dist/util.js.map +1 -1
- package/package.json +1 -1
package/dist/client.d.ts
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import type { SignedEvent, StampedEvent } from "@welshman/util";
|
|
1
|
+
import type { StampedEvent, SignedEvent } from "@welshman/util";
|
|
3
2
|
import type { GroupPackage } from "@frostr/bifrost";
|
|
4
|
-
import { RPC
|
|
5
|
-
import {
|
|
3
|
+
import { RPC } from "./rpc.js";
|
|
4
|
+
import { Message, LoginStartResponse, RecoveryStartResponse } from "./message.js";
|
|
6
5
|
export type ClientOptions = {
|
|
7
6
|
group: GroupPackage;
|
|
8
7
|
secret: string;
|
|
@@ -11,7 +10,7 @@ export type ClientOptions = {
|
|
|
11
10
|
export type ClientOptionsResult<T> = {
|
|
12
11
|
ok: boolean;
|
|
13
12
|
options: [string, string[]][];
|
|
14
|
-
messages:
|
|
13
|
+
messages: Message<T>[];
|
|
15
14
|
clientSecret: string;
|
|
16
15
|
};
|
|
17
16
|
export declare class Client {
|
|
@@ -20,13 +19,15 @@ export declare class Client {
|
|
|
20
19
|
group: GroupPackage;
|
|
21
20
|
userPubkey: string;
|
|
22
21
|
constructor(options: ClientOptions);
|
|
23
|
-
stop(): void;
|
|
24
22
|
getPubkey(): Promise<string>;
|
|
25
|
-
static _buildOptions<T extends
|
|
23
|
+
static _buildOptions<T extends LoginStartResponse | RecoveryStartResponse>(clientSecret: string, messages: Message<T>[], threshold: "total" | "threshold"): ClientOptionsResult<T>;
|
|
26
24
|
static _getKnownPeers(): string[];
|
|
27
25
|
static register(threshold: number, n: number, userSecret: string, recovery?: boolean): Promise<{
|
|
28
26
|
ok: boolean;
|
|
29
|
-
messages:
|
|
27
|
+
messages: (Message<{
|
|
28
|
+
ok: boolean;
|
|
29
|
+
message: string;
|
|
30
|
+
}> | undefined)[];
|
|
30
31
|
clientOptions: {
|
|
31
32
|
peers: string[];
|
|
32
33
|
group: GroupPackage;
|
|
@@ -35,43 +36,164 @@ export declare class Client {
|
|
|
35
36
|
}>;
|
|
36
37
|
setupRecovery(email: string, password: string): Promise<{
|
|
37
38
|
ok: boolean;
|
|
38
|
-
messages:
|
|
39
|
+
messages: Message<{
|
|
40
|
+
ok: boolean;
|
|
41
|
+
message: string;
|
|
42
|
+
}>[];
|
|
39
43
|
}>;
|
|
40
44
|
static requestChallenge(email: string, peers?: string[]): Promise<{
|
|
41
45
|
ok: boolean;
|
|
42
46
|
peersByPrefix: Map<string, string>;
|
|
43
47
|
}>;
|
|
44
|
-
static loginWithPassword(email: string, password: string): Promise<ClientOptionsResult<
|
|
45
|
-
|
|
48
|
+
static loginWithPassword(email: string, password: string): Promise<ClientOptionsResult<{
|
|
49
|
+
ok: boolean;
|
|
50
|
+
message: string;
|
|
51
|
+
items?: {
|
|
52
|
+
pubkey: string;
|
|
53
|
+
client: string;
|
|
54
|
+
created_at: number;
|
|
55
|
+
last_activity: number;
|
|
56
|
+
threshold: number;
|
|
57
|
+
total: number;
|
|
58
|
+
idx: number;
|
|
59
|
+
email?: string | undefined;
|
|
60
|
+
}[] | undefined;
|
|
61
|
+
}>>;
|
|
62
|
+
static loginWithChallenge(email: string, peersByPrefix: Map<string, string>, otps: string[]): Promise<ClientOptionsResult<{
|
|
63
|
+
ok: boolean;
|
|
64
|
+
message: string;
|
|
65
|
+
items?: {
|
|
66
|
+
pubkey: string;
|
|
67
|
+
client: string;
|
|
68
|
+
created_at: number;
|
|
69
|
+
last_activity: number;
|
|
70
|
+
threshold: number;
|
|
71
|
+
total: number;
|
|
72
|
+
idx: number;
|
|
73
|
+
email?: string | undefined;
|
|
74
|
+
}[] | undefined;
|
|
75
|
+
}>>;
|
|
46
76
|
static selectLogin(clientSecret: string, client: string, peers: string[]): Promise<{
|
|
47
77
|
ok: boolean;
|
|
48
|
-
messages:
|
|
78
|
+
messages: Message<{
|
|
79
|
+
ok: boolean;
|
|
80
|
+
message: string;
|
|
81
|
+
group?: {
|
|
82
|
+
commits: {
|
|
83
|
+
idx: number;
|
|
84
|
+
pubkey: string;
|
|
85
|
+
hidden_pn: string;
|
|
86
|
+
binder_pn: string;
|
|
87
|
+
}[];
|
|
88
|
+
group_pk: string;
|
|
89
|
+
threshold: number;
|
|
90
|
+
} | undefined;
|
|
91
|
+
}>[];
|
|
49
92
|
clientOptions: ClientOptions | undefined;
|
|
50
93
|
}>;
|
|
51
|
-
static recoverWithPassword(email: string, password: string): Promise<ClientOptionsResult<
|
|
52
|
-
|
|
94
|
+
static recoverWithPassword(email: string, password: string): Promise<ClientOptionsResult<{
|
|
95
|
+
ok: boolean;
|
|
96
|
+
message: string;
|
|
97
|
+
items?: {
|
|
98
|
+
pubkey: string;
|
|
99
|
+
client: string;
|
|
100
|
+
created_at: number;
|
|
101
|
+
last_activity: number;
|
|
102
|
+
threshold: number;
|
|
103
|
+
total: number;
|
|
104
|
+
idx: number;
|
|
105
|
+
email?: string | undefined;
|
|
106
|
+
}[] | undefined;
|
|
107
|
+
}>>;
|
|
108
|
+
static recoverWithChallenge(email: string, peersByPrefix: Map<string, string>, otps: string[]): Promise<ClientOptionsResult<{
|
|
109
|
+
ok: boolean;
|
|
110
|
+
message: string;
|
|
111
|
+
items?: {
|
|
112
|
+
pubkey: string;
|
|
113
|
+
client: string;
|
|
114
|
+
created_at: number;
|
|
115
|
+
last_activity: number;
|
|
116
|
+
threshold: number;
|
|
117
|
+
total: number;
|
|
118
|
+
idx: number;
|
|
119
|
+
email?: string | undefined;
|
|
120
|
+
}[] | undefined;
|
|
121
|
+
}>>;
|
|
53
122
|
static selectRecovery(clientSecret: string, client: string, peers: string[]): Promise<{
|
|
54
123
|
ok: boolean;
|
|
55
|
-
messages:
|
|
124
|
+
messages: Message<{
|
|
125
|
+
ok: boolean;
|
|
126
|
+
message: string;
|
|
127
|
+
share?: {
|
|
128
|
+
idx: number;
|
|
129
|
+
binder_sn: string;
|
|
130
|
+
hidden_sn: string;
|
|
131
|
+
seckey: string;
|
|
132
|
+
} | undefined;
|
|
133
|
+
group?: {
|
|
134
|
+
commits: {
|
|
135
|
+
idx: number;
|
|
136
|
+
pubkey: string;
|
|
137
|
+
hidden_pn: string;
|
|
138
|
+
binder_pn: string;
|
|
139
|
+
}[];
|
|
140
|
+
group_pk: string;
|
|
141
|
+
threshold: number;
|
|
142
|
+
} | undefined;
|
|
143
|
+
}>[];
|
|
56
144
|
userSecret: string | undefined;
|
|
57
145
|
}>;
|
|
58
146
|
sign(stampedEvent: StampedEvent): Promise<{
|
|
59
147
|
ok: boolean;
|
|
60
|
-
messages:
|
|
148
|
+
messages: Message<{
|
|
149
|
+
ok: boolean;
|
|
150
|
+
message: string;
|
|
151
|
+
result?: {
|
|
152
|
+
idx: number;
|
|
153
|
+
psigs: [string, string][];
|
|
154
|
+
pubkey: string;
|
|
155
|
+
sid: string;
|
|
156
|
+
} | undefined;
|
|
157
|
+
}>[];
|
|
61
158
|
event: SignedEvent;
|
|
62
159
|
} | {
|
|
63
160
|
ok: boolean;
|
|
64
|
-
messages:
|
|
161
|
+
messages: Message<{
|
|
162
|
+
ok: boolean;
|
|
163
|
+
message: string;
|
|
164
|
+
result?: {
|
|
165
|
+
idx: number;
|
|
166
|
+
psigs: [string, string][];
|
|
167
|
+
pubkey: string;
|
|
168
|
+
sid: string;
|
|
169
|
+
} | undefined;
|
|
170
|
+
}>[];
|
|
65
171
|
event?: undefined;
|
|
66
172
|
}>;
|
|
67
173
|
getConversationKey(ecdh_pk: string): Promise<string | undefined>;
|
|
68
174
|
listSessions(): Promise<{
|
|
69
175
|
ok: boolean;
|
|
70
|
-
messages:
|
|
176
|
+
messages: Message<{
|
|
177
|
+
ok: boolean;
|
|
178
|
+
message: string;
|
|
179
|
+
items: {
|
|
180
|
+
pubkey: string;
|
|
181
|
+
client: string;
|
|
182
|
+
created_at: number;
|
|
183
|
+
last_activity: number;
|
|
184
|
+
threshold: number;
|
|
185
|
+
total: number;
|
|
186
|
+
idx: number;
|
|
187
|
+
email?: string | undefined;
|
|
188
|
+
}[];
|
|
189
|
+
}>[];
|
|
71
190
|
}>;
|
|
72
191
|
deleteSession(client: string, peers: string[]): Promise<{
|
|
73
192
|
ok: boolean;
|
|
74
|
-
messages:
|
|
193
|
+
messages: Message<{
|
|
194
|
+
ok: boolean;
|
|
195
|
+
message: string;
|
|
196
|
+
}>[];
|
|
75
197
|
}>;
|
|
76
198
|
}
|
|
77
199
|
//# sourceMappingURL=client.d.ts.map
|
package/dist/client.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EAAC,YAAY,EAAE,WAAW,EAAC,MAAM,gBAAgB,CAAA;AAE7D,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,iBAAiB,CAAA;AAEjD,OAAO,EAAC,GAAG,EAAC,MAAM,UAAU,CAAA;AAE5B,OAAO,EACL,OAAO,EAEP,kBAAkB,EAClB,qBAAqB,EAStB,MAAM,cAAc,CAAA;AAErB,MAAM,MAAM,aAAa,GAAG;IAC1B,KAAK,EAAE,YAAY,CAAA;IACnB,MAAM,EAAE,MAAM,CAAA;IACd,KAAK,EAAE,MAAM,EAAE,CAAA;CAChB,CAAA;AAED,MAAM,MAAM,mBAAmB,CAAC,CAAC,IAAI;IACnC,EAAE,EAAE,OAAO,CAAA;IACX,OAAO,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,EAAE,CAAA;IAC7B,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,CAAA;IACtB,YAAY,EAAE,MAAM,CAAA;CACrB,CAAA;AAED,qBAAa,MAAM;IACjB,GAAG,EAAE,GAAG,CAAA;IACR,KAAK,EAAE,MAAM,EAAE,CAAA;IACf,KAAK,EAAE,YAAY,CAAA;IACnB,UAAU,EAAE,MAAM,CAAA;gBAEN,OAAO,EAAE,aAAa;IAOlC,SAAS;IAIT,MAAM,CAAC,aAAa,CAAC,CAAC,SAAS,kBAAkB,GAAG,qBAAqB,EACvE,YAAY,EAAE,MAAM,EACpB,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,EACtB,SAAS,EAAE,OAAO,GAAG,WAAW,GAC/B,mBAAmB,CAAC,CAAC,CAAC;IA4CzB,MAAM,CAAC,cAAc;WASR,QAAQ,CAAC,SAAS,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,QAAQ,UAAO;;;;;;;;;;;;IAkDjF,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM;;;;;;;WAYtC,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,WAA0B;;;;WAuB/D,iBAAiB,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM;;;;;;;;;;;;;;WAiBjD,kBAAkB,CAC7B,KAAK,EAAE,MAAM,EACb,aAAa,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EAClC,IAAI,EAAE,MAAM,EAAE;;;;;;;;;;;;;;WAuBH,WAAW,CAAC,YAAY,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE;;;;;;;;;;;;;;;;;;WAcjE,mBAAmB,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM;;;;;;;;;;;;;;WAiBnD,oBAAoB,CAC/B,KAAK,EAAE,MAAM,EACb,aAAa,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EAClC,IAAI,EAAE,MAAM,EAAE;;;;;;;;;;;;;;WAuBH,cAAc,CAAC,YAAY,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE;;;;;;;;;;;;;;;;;;;;;;;;IAc3E,IAAI,CAAC,YAAY,EAAE,YAAY;;;;;;;;;;;;eAwBuB,WAAW;;;;;;;;;;;;;;;IAOjE,kBAAkB,CAAC,OAAO,EAAE,MAAM;IAyBlC,YAAY;;;;;;;;;;;;;;;;;IAYZ,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE;;;;;;;CASpD"}
|
package/dist/client.js
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
|
-
import { tryCatch, groupBy, removeUndefined, shuffle, randomId, sortBy, first, last, isDefined,
|
|
1
|
+
import { tryCatch, groupBy, removeUndefined, shuffle, randomId, sortBy, first, last, isDefined, sample, textEncoder, } from "@welshman/lib";
|
|
2
2
|
import { extract } from "@noble/hashes/hkdf.js";
|
|
3
3
|
import { sha256 } from "@noble/hashes/sha2.js";
|
|
4
4
|
import { hexToBytes, bytesToHex } from "@noble/hashes/utils.js";
|
|
5
|
-
import { prep, makeSecret
|
|
5
|
+
import { prep, makeSecret } from "@welshman/util";
|
|
6
6
|
import { Lib } from "@frostr/bifrost";
|
|
7
|
-
import { Method } from "./schema.js";
|
|
8
7
|
import { context, hashEmail, hashPassword } from "./util.js";
|
|
9
8
|
import { RPC } from "./rpc.js";
|
|
10
|
-
import {
|
|
9
|
+
import { PomadeSigner } from "./pomade-signer.js";
|
|
11
10
|
export class Client {
|
|
12
11
|
rpc;
|
|
13
12
|
peers;
|
|
@@ -19,17 +18,14 @@ export class Client {
|
|
|
19
18
|
this.group = options.group;
|
|
20
19
|
this.userPubkey = this.group.group_pk.slice(2);
|
|
21
20
|
}
|
|
22
|
-
stop() {
|
|
23
|
-
this.rpc.stop();
|
|
24
|
-
}
|
|
25
21
|
getPubkey() {
|
|
26
22
|
return this.rpc.signer.getPubkey();
|
|
27
23
|
}
|
|
28
24
|
static _buildOptions(clientSecret, messages, threshold) {
|
|
29
25
|
// Extract all items with their metadata
|
|
30
|
-
const items = messages.flatMap(m => m?.
|
|
26
|
+
const items = messages.flatMap(m => m.res?.items?.map(item => ({
|
|
31
27
|
client: item.client,
|
|
32
|
-
|
|
28
|
+
url: m.url,
|
|
33
29
|
idx: item.idx,
|
|
34
30
|
total: item.total,
|
|
35
31
|
threshold: item.threshold,
|
|
@@ -49,24 +45,23 @@ export class Client {
|
|
|
49
45
|
if (!hasAllIndices)
|
|
50
46
|
continue;
|
|
51
47
|
// Sort by idx and map to peers
|
|
52
|
-
const peers = sortBy(item => item.idx, clientItems).map(item => item.
|
|
48
|
+
const peers = sortBy(item => item.idx, clientItems).map(item => item.url);
|
|
53
49
|
options.push([client, peers]);
|
|
54
50
|
}
|
|
55
|
-
const ok = messages.some(m => m?.
|
|
51
|
+
const ok = messages.some(m => m.res?.ok) && options.length > 0;
|
|
56
52
|
return { ok, options, messages, clientSecret };
|
|
57
53
|
}
|
|
58
54
|
static _getKnownPeers() {
|
|
59
|
-
if (context.
|
|
60
|
-
console.log("[pomade]: You can configure available signer
|
|
61
|
-
throw new Error("No signer
|
|
55
|
+
if (context.signerUrls.length === 0) {
|
|
56
|
+
console.log("[pomade]: You can configure available signer URLs using setSignerUrls");
|
|
57
|
+
throw new Error("No signer URLs available");
|
|
62
58
|
}
|
|
63
|
-
return context.
|
|
59
|
+
return context.signerUrls;
|
|
64
60
|
}
|
|
65
|
-
// Register
|
|
66
61
|
static async register(threshold, n, userSecret, recovery = true) {
|
|
67
|
-
if (context.
|
|
68
|
-
console.log("[pomade]: You can configure available signer
|
|
69
|
-
throw new Error("Not enough signer
|
|
62
|
+
if (context.signerUrls.length < n) {
|
|
63
|
+
console.log("[pomade]: You can configure available signer URLs using setSignerUrls");
|
|
64
|
+
throw new Error("Not enough signer URLs available");
|
|
70
65
|
}
|
|
71
66
|
if (threshold <= 0) {
|
|
72
67
|
throw new Error("Threshold must be greater than 0");
|
|
@@ -74,27 +69,18 @@ export class Client {
|
|
|
74
69
|
const secret = makeSecret();
|
|
75
70
|
const rpc = RPC.fromSecret(secret);
|
|
76
71
|
const { group, shares } = Lib.generate_dealer_pkg(threshold, n, [userSecret]);
|
|
77
|
-
const
|
|
72
|
+
const remainingSignerUrls = shuffle(context.signerUrls);
|
|
78
73
|
const peersByIndex = new Map();
|
|
79
74
|
const messages = await Promise.all(shares.map(async (share, i) => {
|
|
80
|
-
while (
|
|
81
|
-
const
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
.
|
|
85
|
-
if (isRegisterResult(message)) {
|
|
86
|
-
if (message.payload.ok) {
|
|
87
|
-
peersByIndex.set(i, message.event.pubkey);
|
|
88
|
-
}
|
|
89
|
-
resolve(message);
|
|
90
|
-
}
|
|
91
|
-
});
|
|
92
|
-
if (peersByIndex.has(i)) {
|
|
93
|
-
return messages;
|
|
75
|
+
while (remainingSignerUrls.length > 0) {
|
|
76
|
+
const url = remainingSignerUrls.shift();
|
|
77
|
+
const message = await rpc.post(url, "/register", { share, group, recovery }, context.registerPow);
|
|
78
|
+
if (message.res?.ok) {
|
|
79
|
+
peersByIndex.set(i, url);
|
|
94
80
|
}
|
|
81
|
+
return message;
|
|
95
82
|
}
|
|
96
83
|
}));
|
|
97
|
-
rpc.stop();
|
|
98
84
|
const ok = peersByIndex.size === n;
|
|
99
85
|
const peers = sortBy(first, peersByIndex).map(last);
|
|
100
86
|
return {
|
|
@@ -107,158 +93,93 @@ export class Client {
|
|
|
107
93
|
},
|
|
108
94
|
};
|
|
109
95
|
}
|
|
110
|
-
// Recovery setup
|
|
111
96
|
async setupRecovery(email, password) {
|
|
112
|
-
const messages = await Promise.all(this.peers.map(async (
|
|
113
|
-
const password_hash = await hashPassword(email, password,
|
|
114
|
-
return this.rpc
|
|
115
|
-
.channel(peer)
|
|
116
|
-
.send(makeRecoverySetup({ email, password_hash }))
|
|
117
|
-
.receive((message, resolve) => {
|
|
118
|
-
if (isRecoverySetupResult(message)) {
|
|
119
|
-
resolve(message);
|
|
120
|
-
}
|
|
121
|
-
});
|
|
97
|
+
const messages = await Promise.all(this.peers.map(async (url) => {
|
|
98
|
+
const password_hash = await hashPassword(email, password, url);
|
|
99
|
+
return this.rpc.post(url, "/recovery/setup", { email, password_hash });
|
|
122
100
|
}));
|
|
123
|
-
return { ok: messages.every(m => m?.
|
|
101
|
+
return { ok: messages.every(m => m.res?.ok), messages };
|
|
124
102
|
}
|
|
125
|
-
// Challenge
|
|
126
103
|
static async requestChallenge(email, peers = Client._getKnownPeers()) {
|
|
127
104
|
const clientSecret = makeSecret();
|
|
128
105
|
const rpc = RPC.fromSecret(clientSecret);
|
|
129
106
|
const peersByPrefix = new Map();
|
|
130
|
-
const
|
|
107
|
+
const results = await Promise.all(peers.map(async (url) => {
|
|
131
108
|
let prefix = randomId().slice(-2);
|
|
132
109
|
while (peersByPrefix.has(prefix)) {
|
|
133
110
|
prefix = randomId().slice(-2);
|
|
134
111
|
}
|
|
135
|
-
peersByPrefix.set(prefix,
|
|
136
|
-
const email_hash = await hashEmail(email,
|
|
137
|
-
return rpc.
|
|
112
|
+
peersByPrefix.set(prefix, url);
|
|
113
|
+
const email_hash = await hashEmail(email, url);
|
|
114
|
+
return rpc.post(url, "/challenge", { prefix, email_hash });
|
|
138
115
|
}));
|
|
139
|
-
|
|
140
|
-
return { ok: oks.every(identity), peersByPrefix };
|
|
116
|
+
return { ok: results.every(r => r.res?.ok), peersByPrefix };
|
|
141
117
|
}
|
|
142
|
-
// Login
|
|
143
118
|
static async loginWithPassword(email, password) {
|
|
144
119
|
const clientSecret = makeSecret();
|
|
145
120
|
const rpc = RPC.fromSecret(clientSecret);
|
|
146
|
-
const messages = await Promise.all(Client._getKnownPeers().map(async (
|
|
147
|
-
const email_hash = await hashEmail(email,
|
|
148
|
-
const password_hash = await hashPassword(email, password,
|
|
121
|
+
const messages = await Promise.all(Client._getKnownPeers().map(async (url) => {
|
|
122
|
+
const email_hash = await hashEmail(email, url);
|
|
123
|
+
const password_hash = await hashPassword(email, password, url);
|
|
149
124
|
const auth = { email_hash, password_hash };
|
|
150
|
-
return rpc
|
|
151
|
-
.channel(peer)
|
|
152
|
-
.send(makeLoginStart({ auth }))
|
|
153
|
-
.receive((message, resolve) => {
|
|
154
|
-
if (isLoginOptions(message)) {
|
|
155
|
-
resolve(message);
|
|
156
|
-
}
|
|
157
|
-
});
|
|
125
|
+
return rpc.post(url, "/login/start", { auth });
|
|
158
126
|
}));
|
|
159
|
-
rpc.stop();
|
|
160
127
|
return this._buildOptions(clientSecret, messages, "total");
|
|
161
128
|
}
|
|
162
129
|
static async loginWithChallenge(email, peersByPrefix, otps) {
|
|
163
130
|
const clientSecret = makeSecret();
|
|
164
131
|
const rpc = RPC.fromSecret(clientSecret);
|
|
165
|
-
const messages = await Promise.all(otps.map(async (otp) => {
|
|
166
|
-
const
|
|
167
|
-
if (
|
|
168
|
-
const email_hash = await hashEmail(email,
|
|
132
|
+
const messages = removeUndefined(await Promise.all(otps.map(async (otp) => {
|
|
133
|
+
const url = peersByPrefix.get(otp.slice(0, 2));
|
|
134
|
+
if (url) {
|
|
135
|
+
const email_hash = await hashEmail(email, url);
|
|
169
136
|
const auth = { email_hash, otp };
|
|
170
|
-
return rpc
|
|
171
|
-
.channel(peer)
|
|
172
|
-
.send(makeLoginStart({ auth }))
|
|
173
|
-
.receive((message, resolve) => {
|
|
174
|
-
if (isLoginOptions(message)) {
|
|
175
|
-
resolve(message);
|
|
176
|
-
}
|
|
177
|
-
});
|
|
137
|
+
return rpc.post(url, "/login/start", { auth });
|
|
178
138
|
}
|
|
179
|
-
}));
|
|
180
|
-
rpc.stop();
|
|
139
|
+
})));
|
|
181
140
|
return this._buildOptions(clientSecret, messages, "total");
|
|
182
141
|
}
|
|
183
142
|
static async selectLogin(clientSecret, client, peers) {
|
|
184
143
|
const rpc = RPC.fromSecret(clientSecret);
|
|
185
|
-
const messages = await Promise.all(peers.map((
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
.send(makeLoginSelect({ client }))
|
|
189
|
-
.receive((message, resolve) => {
|
|
190
|
-
if (isLoginResult(message)) {
|
|
191
|
-
resolve(message);
|
|
192
|
-
}
|
|
193
|
-
});
|
|
194
|
-
}));
|
|
195
|
-
rpc.stop();
|
|
196
|
-
const group = messages.find(m => m?.payload.group)?.payload.group;
|
|
197
|
-
const ok = Boolean(group && messages.every(m => m?.payload.ok));
|
|
144
|
+
const messages = await Promise.all(peers.map(url => rpc.post(url, "/login/select", { client })));
|
|
145
|
+
const group = messages.find(m => m.res?.group)?.res?.group;
|
|
146
|
+
const ok = Boolean(group && messages.every(m => m.res?.ok));
|
|
198
147
|
const clientOptions = ok ? { group, peers, secret: clientSecret } : undefined;
|
|
199
148
|
return { ok, messages, clientOptions };
|
|
200
149
|
}
|
|
201
|
-
// Recovery
|
|
202
150
|
static async recoverWithPassword(email, password) {
|
|
203
151
|
const clientSecret = makeSecret();
|
|
204
152
|
const rpc = RPC.fromSecret(clientSecret);
|
|
205
|
-
const messages = await Promise.all(Client._getKnownPeers().map(async (
|
|
206
|
-
const email_hash = await hashEmail(email,
|
|
207
|
-
const password_hash = await hashPassword(email, password,
|
|
153
|
+
const messages = await Promise.all(Client._getKnownPeers().map(async (url) => {
|
|
154
|
+
const email_hash = await hashEmail(email, url);
|
|
155
|
+
const password_hash = await hashPassword(email, password, url);
|
|
208
156
|
const auth = { email_hash, password_hash };
|
|
209
|
-
return rpc
|
|
210
|
-
.channel(peer)
|
|
211
|
-
.send(makeRecoveryStart({ auth }))
|
|
212
|
-
.receive((message, resolve) => {
|
|
213
|
-
if (isRecoveryOptions(message)) {
|
|
214
|
-
resolve(message);
|
|
215
|
-
}
|
|
216
|
-
});
|
|
157
|
+
return rpc.post(url, "/recovery/start", { auth });
|
|
217
158
|
}));
|
|
218
|
-
rpc.stop();
|
|
219
159
|
return this._buildOptions(clientSecret, messages, "threshold");
|
|
220
160
|
}
|
|
221
161
|
static async recoverWithChallenge(email, peersByPrefix, otps) {
|
|
222
162
|
const clientSecret = makeSecret();
|
|
223
163
|
const rpc = RPC.fromSecret(clientSecret);
|
|
224
|
-
const messages = await Promise.all(otps.map(async (otp) => {
|
|
225
|
-
const
|
|
226
|
-
if (
|
|
227
|
-
const email_hash = await hashEmail(email,
|
|
164
|
+
const messages = removeUndefined(await Promise.all(otps.map(async (otp) => {
|
|
165
|
+
const url = peersByPrefix.get(otp.slice(0, 2));
|
|
166
|
+
if (url) {
|
|
167
|
+
const email_hash = await hashEmail(email, url);
|
|
228
168
|
const auth = { email_hash, otp };
|
|
229
|
-
return rpc
|
|
230
|
-
.channel(peer)
|
|
231
|
-
.send(makeRecoveryStart({ auth }))
|
|
232
|
-
.receive((message, resolve) => {
|
|
233
|
-
if (isRecoveryOptions(message)) {
|
|
234
|
-
resolve(message);
|
|
235
|
-
}
|
|
236
|
-
});
|
|
169
|
+
return rpc.post(url, "/recovery/start", { auth });
|
|
237
170
|
}
|
|
238
|
-
}));
|
|
239
|
-
rpc.stop();
|
|
171
|
+
})));
|
|
240
172
|
return this._buildOptions(clientSecret, messages, "threshold");
|
|
241
173
|
}
|
|
242
174
|
static async selectRecovery(clientSecret, client, peers) {
|
|
243
175
|
const rpc = RPC.fromSecret(clientSecret);
|
|
244
|
-
const messages = await Promise.all(peers.map(
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
.send(makeRecoverySelect({ client }))
|
|
248
|
-
.receive((message, resolve) => {
|
|
249
|
-
if (isRecoveryResult(message)) {
|
|
250
|
-
resolve(message);
|
|
251
|
-
}
|
|
252
|
-
});
|
|
253
|
-
}));
|
|
254
|
-
rpc.stop();
|
|
255
|
-
const group = messages.find(m => m?.payload.group)?.payload.group;
|
|
256
|
-
const shares = removeUndefined(messages.map(m => m?.payload.share));
|
|
176
|
+
const messages = await Promise.all(peers.map(url => rpc.post(url, "/recovery/select", { client })));
|
|
177
|
+
const group = messages.find(m => m.res?.group)?.res?.group;
|
|
178
|
+
const shares = removeUndefined(messages.map(m => m.res?.share));
|
|
257
179
|
const userSecret = tryCatch(() => Lib.recover_secret_key(group, shares));
|
|
258
180
|
return { ok: Boolean(userSecret), messages, userSecret };
|
|
259
181
|
}
|
|
260
182
|
async sign(stampedEvent) {
|
|
261
|
-
// TODO: optimize this so that all signers are asked, but only the fastest results get used
|
|
262
183
|
const { threshold, commits } = this.group;
|
|
263
184
|
const event = prep(stampedEvent, this.userPubkey);
|
|
264
185
|
const members = sample(threshold, commits).map(c => c.idx);
|
|
@@ -267,19 +188,12 @@ export class Client {
|
|
|
267
188
|
throw new Error("Failed to create signing template");
|
|
268
189
|
const request = Lib.create_session_pkg(this.group, template);
|
|
269
190
|
const messages = await Promise.all(members.map(idx => {
|
|
270
|
-
const
|
|
271
|
-
return this.rpc
|
|
272
|
-
.channel(peer)
|
|
273
|
-
.send(makeSignRequest({ request }))
|
|
274
|
-
.receive((message, resolve) => {
|
|
275
|
-
if (isSignResult(message)) {
|
|
276
|
-
resolve(message);
|
|
277
|
-
}
|
|
278
|
-
});
|
|
191
|
+
const url = this.peers[idx - 1];
|
|
192
|
+
return this.rpc.post(url, "/sign", { request });
|
|
279
193
|
}));
|
|
280
|
-
if (messages.every(m => m?.
|
|
194
|
+
if (messages.every(m => m.res?.ok)) {
|
|
281
195
|
const ctx = Lib.get_session_ctx(this.group, request);
|
|
282
|
-
const pkgs = messages.map(m => m.
|
|
196
|
+
const pkgs = messages.map(m => m.res.result);
|
|
283
197
|
const sig = Lib.combine_signature_pkgs(ctx, pkgs)[0]?.[2];
|
|
284
198
|
if (sig) {
|
|
285
199
|
return { ok: true, messages, event: { ...event, sig } };
|
|
@@ -288,55 +202,27 @@ export class Client {
|
|
|
288
202
|
return { ok: false, messages };
|
|
289
203
|
}
|
|
290
204
|
async getConversationKey(ecdh_pk) {
|
|
291
|
-
// TODO: optimize this so that all signers are asked, but only the fastest results get used
|
|
292
205
|
const { threshold, commits } = this.group;
|
|
293
206
|
const members = sample(threshold, commits).map(c => c.idx);
|
|
294
207
|
const results = await Promise.all(members.map(idx => {
|
|
295
|
-
const
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
.
|
|
299
|
-
.receive((message, resolve) => {
|
|
300
|
-
if (isEcdhResult(message)) {
|
|
301
|
-
resolve(message.payload.result);
|
|
302
|
-
}
|
|
303
|
-
});
|
|
208
|
+
const url = this.peers[idx - 1];
|
|
209
|
+
return this.rpc
|
|
210
|
+
.post(url, "/ecdh", { idx, members, ecdh_pk })
|
|
211
|
+
.then(r => r.res?.result);
|
|
304
212
|
}));
|
|
305
213
|
if (results.every(isDefined)) {
|
|
306
214
|
return bytesToHex(extract(sha256, hexToBytes(Lib.combine_ecdh_pkgs(results).slice(2)), textEncoder.encode("nip44-v2")));
|
|
307
215
|
}
|
|
308
216
|
}
|
|
309
217
|
async listSessions() {
|
|
310
|
-
const
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
return this.rpc
|
|
314
|
-
.channel(peer)
|
|
315
|
-
.send(makeSessionList({ auth }))
|
|
316
|
-
.receive((message, resolve) => {
|
|
317
|
-
if (isSessionListResult(message)) {
|
|
318
|
-
resolve(message);
|
|
319
|
-
}
|
|
320
|
-
});
|
|
321
|
-
}
|
|
322
|
-
}));
|
|
323
|
-
return { ok: messages.every(m => m?.payload.ok), messages };
|
|
218
|
+
const userRpc = new RPC(new PomadeSigner(this));
|
|
219
|
+
const messages = await Promise.all(Client._getKnownPeers().map(url => userRpc.post(url, "/session/list", {})));
|
|
220
|
+
return { ok: messages.every(m => m.res?.ok), messages };
|
|
324
221
|
}
|
|
325
222
|
async deleteSession(client, peers) {
|
|
326
|
-
const
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
return this.rpc
|
|
330
|
-
.channel(peer)
|
|
331
|
-
.send(makeSessionDelete({ client, auth }))
|
|
332
|
-
.receive((message, resolve) => {
|
|
333
|
-
if (isSessionDeleteResult(message)) {
|
|
334
|
-
resolve(message);
|
|
335
|
-
}
|
|
336
|
-
});
|
|
337
|
-
}
|
|
338
|
-
}));
|
|
339
|
-
return { ok: messages.every(m => m?.payload.ok), messages };
|
|
223
|
+
const userRpc = new RPC(new PomadeSigner(this));
|
|
224
|
+
const messages = await Promise.all(peers.map(url => userRpc.post(url, "/session/delete", { client })));
|
|
225
|
+
return { ok: messages.every(m => m.res?.ok), messages };
|
|
340
226
|
}
|
|
341
227
|
}
|
|
342
228
|
//# sourceMappingURL=client.js.map
|