@learncard/lca-api-service 1.5.1 → 1.5.2
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/.env.example +2 -2
- package/CHANGELOG.md +14 -0
- package/config/config.dev.json +1 -1
- package/dist/app.d.ts +220 -1
- package/dist/index.js +2 -2
- package/dist/models/EscrowHold.d.ts +30 -2
- package/dist/models/UserKey.d.ts +89 -5
- package/dist/publish-metafile.json +1 -1
- package/dist/routes/escrow.d.ts +218 -0
- package/dist/routes/index.d.ts +4 -4
- package/dist/routes/keys.d.ts +2 -1
- package/dist/services/escrow-enclave/authorityContract.d.ts +170 -0
- package/dist/services/escrow-enclave/types.d.ts +38 -2
- package/package.json +9 -9
- package/src/config/environment.ts +1 -1
- package/src/config/escrowDeployment.test.ts +8 -0
- package/src/jobs/escrowBlobRewrap.ts +45 -2
- package/src/jobs/escrowHoldReminders.ts +50 -1
- package/src/models/EscrowHold.ts +128 -10
- package/src/models/UserKey.ts +59 -8
- package/src/routes/escrow.ts +371 -51
- package/src/routes/index.ts +1 -1
- package/src/routes/keys.ts +5 -0
- package/src/services/escrow-enclave/README.md +31 -0
- package/src/services/escrow-enclave/authorityContract.ts +126 -0
- package/src/services/escrow-enclave/cancellation.ts +2 -2
- package/src/services/escrow-enclave/remoteEnclave.test.ts +216 -3
- package/src/services/escrow-enclave/remoteEnclave.ts +129 -23
- package/src/services/escrow-enclave/types.ts +51 -3
- package/test/jobs/escrowHoldReminders.spec.ts +195 -3
- package/test/keys-escrow.spec.ts +522 -11
- package/test/models/escrow.spec.ts +13 -0
package/dist/routes/escrow.d.ts
CHANGED
|
@@ -28,6 +28,45 @@ export declare const escrowRouter: import("@trpc/server").TRPCBuiltRouter<{
|
|
|
28
28
|
};
|
|
29
29
|
meta: import("trpc-to-openapi").OpenApiMeta;
|
|
30
30
|
}>;
|
|
31
|
+
getAuthorityState: import("@trpc/server").TRPCMutationProcedure<{
|
|
32
|
+
input: {
|
|
33
|
+
authToken: string;
|
|
34
|
+
providerType: "firebase" | "supertokens" | "keycloak" | "oidc";
|
|
35
|
+
nonce: string;
|
|
36
|
+
};
|
|
37
|
+
output: {
|
|
38
|
+
state: {
|
|
39
|
+
tenant: string;
|
|
40
|
+
authorityGeneration: string;
|
|
41
|
+
did: string;
|
|
42
|
+
enrollmentVersion: number;
|
|
43
|
+
enrollmentEpoch: number;
|
|
44
|
+
status: "empty" | "active" | "disabled";
|
|
45
|
+
envelope: {
|
|
46
|
+
version: 1;
|
|
47
|
+
algorithm: "P-256-HKDF-SHA256-AES-256-GCM";
|
|
48
|
+
keyId: string;
|
|
49
|
+
ephemeralPublicKey: string;
|
|
50
|
+
salt: string;
|
|
51
|
+
iv: string;
|
|
52
|
+
ciphertext: string;
|
|
53
|
+
} | null;
|
|
54
|
+
shareVersion: number | null;
|
|
55
|
+
hasPin: boolean;
|
|
56
|
+
pinGeneration: number;
|
|
57
|
+
attemptsUsed: number;
|
|
58
|
+
};
|
|
59
|
+
proof: {
|
|
60
|
+
version: 1;
|
|
61
|
+
keyId: string;
|
|
62
|
+
measurement: number[];
|
|
63
|
+
payload: string;
|
|
64
|
+
signature: string;
|
|
65
|
+
kind: "snapshot";
|
|
66
|
+
};
|
|
67
|
+
};
|
|
68
|
+
meta: import("trpc-to-openapi").OpenApiMeta;
|
|
69
|
+
}>;
|
|
31
70
|
enroll: import("@trpc/server").TRPCMutationProcedure<{
|
|
32
71
|
input: {
|
|
33
72
|
authToken: string;
|
|
@@ -45,10 +84,56 @@ export declare const escrowRouter: import("@trpc/server").TRPCBuiltRouter<{
|
|
|
45
84
|
enclaveKeyId: string;
|
|
46
85
|
pinSalt?: string | undefined;
|
|
47
86
|
clearPin?: boolean | undefined;
|
|
87
|
+
signedIntent?: {
|
|
88
|
+
intent: {
|
|
89
|
+
version: 2;
|
|
90
|
+
tenant: string;
|
|
91
|
+
did: string;
|
|
92
|
+
authorityGeneration: string;
|
|
93
|
+
expectedEnrollmentVersion: number;
|
|
94
|
+
action: "enroll" | "replace" | "disable" | "carry";
|
|
95
|
+
sourceBlobIdentity: string | null;
|
|
96
|
+
targetBlobIdentity: string | null;
|
|
97
|
+
pinAction: "set" | "change" | "clear" | "keep";
|
|
98
|
+
shareVersion: number;
|
|
99
|
+
enrollmentEpoch: number;
|
|
100
|
+
requestId: string;
|
|
101
|
+
allowRewrap: boolean;
|
|
102
|
+
recoveryEmail: string | null;
|
|
103
|
+
recoveryLanguage: string | null;
|
|
104
|
+
};
|
|
105
|
+
signature: string;
|
|
106
|
+
} | undefined;
|
|
107
|
+
sourceEnvelope?: {
|
|
108
|
+
version: 1;
|
|
109
|
+
algorithm: "P-256-HKDF-SHA256-AES-256-GCM";
|
|
110
|
+
keyId: string;
|
|
111
|
+
ephemeralPublicKey: string;
|
|
112
|
+
salt: string;
|
|
113
|
+
iv: string;
|
|
114
|
+
ciphertext: string;
|
|
115
|
+
} | null | undefined;
|
|
48
116
|
};
|
|
49
117
|
output: {
|
|
50
118
|
success: true;
|
|
51
119
|
shareVersion: number;
|
|
120
|
+
envelope?: {
|
|
121
|
+
version: 1;
|
|
122
|
+
algorithm: "P-256-HKDF-SHA256-AES-256-GCM";
|
|
123
|
+
keyId: string;
|
|
124
|
+
ephemeralPublicKey: string;
|
|
125
|
+
salt: string;
|
|
126
|
+
iv: string;
|
|
127
|
+
ciphertext: string;
|
|
128
|
+
} | undefined;
|
|
129
|
+
receipt?: {
|
|
130
|
+
version: 1;
|
|
131
|
+
keyId: string;
|
|
132
|
+
measurement: number[];
|
|
133
|
+
signature: string;
|
|
134
|
+
kind: "commit";
|
|
135
|
+
payload: string;
|
|
136
|
+
} | undefined;
|
|
52
137
|
};
|
|
53
138
|
meta: import("trpc-to-openapi").OpenApiMeta;
|
|
54
139
|
}>;
|
|
@@ -60,6 +145,62 @@ export declare const escrowRouter: import("@trpc/server").TRPCBuiltRouter<{
|
|
|
60
145
|
};
|
|
61
146
|
output: {
|
|
62
147
|
success: true;
|
|
148
|
+
receipt?: {
|
|
149
|
+
version: 1;
|
|
150
|
+
keyId: string;
|
|
151
|
+
measurement: number[];
|
|
152
|
+
signature: string;
|
|
153
|
+
kind: "commit";
|
|
154
|
+
payload: string;
|
|
155
|
+
} | undefined;
|
|
156
|
+
};
|
|
157
|
+
meta: import("trpc-to-openapi").OpenApiMeta;
|
|
158
|
+
}>;
|
|
159
|
+
disable: import("@trpc/server").TRPCMutationProcedure<{
|
|
160
|
+
input: {
|
|
161
|
+
authToken: string;
|
|
162
|
+
providerType: "firebase" | "supertokens" | "keycloak" | "oidc";
|
|
163
|
+
optOut: boolean;
|
|
164
|
+
signedIntent: {
|
|
165
|
+
intent: {
|
|
166
|
+
version: 2;
|
|
167
|
+
tenant: string;
|
|
168
|
+
did: string;
|
|
169
|
+
authorityGeneration: string;
|
|
170
|
+
expectedEnrollmentVersion: number;
|
|
171
|
+
action: "enroll" | "replace" | "disable" | "carry";
|
|
172
|
+
sourceBlobIdentity: string | null;
|
|
173
|
+
targetBlobIdentity: string | null;
|
|
174
|
+
pinAction: "set" | "change" | "clear" | "keep";
|
|
175
|
+
shareVersion: number;
|
|
176
|
+
enrollmentEpoch: number;
|
|
177
|
+
requestId: string;
|
|
178
|
+
allowRewrap: boolean;
|
|
179
|
+
recoveryEmail: string | null;
|
|
180
|
+
recoveryLanguage: string | null;
|
|
181
|
+
};
|
|
182
|
+
signature: string;
|
|
183
|
+
};
|
|
184
|
+
sourceEnvelope?: {
|
|
185
|
+
version: 1;
|
|
186
|
+
algorithm: "P-256-HKDF-SHA256-AES-256-GCM";
|
|
187
|
+
keyId: string;
|
|
188
|
+
ephemeralPublicKey: string;
|
|
189
|
+
salt: string;
|
|
190
|
+
iv: string;
|
|
191
|
+
ciphertext: string;
|
|
192
|
+
} | undefined;
|
|
193
|
+
};
|
|
194
|
+
output: {
|
|
195
|
+
success: true;
|
|
196
|
+
receipt?: {
|
|
197
|
+
version: 1;
|
|
198
|
+
keyId: string;
|
|
199
|
+
measurement: number[];
|
|
200
|
+
signature: string;
|
|
201
|
+
kind: "commit";
|
|
202
|
+
payload: string;
|
|
203
|
+
} | undefined;
|
|
63
204
|
};
|
|
64
205
|
meta: import("trpc-to-openapi").OpenApiMeta;
|
|
65
206
|
}>;
|
|
@@ -89,10 +230,66 @@ export declare const escrowRouter: import("@trpc/server").TRPCBuiltRouter<{
|
|
|
89
230
|
releaseAfter: string;
|
|
90
231
|
status: "pending";
|
|
91
232
|
resumeToken: string | null;
|
|
233
|
+
holdRecord?: {
|
|
234
|
+
[x: string]: unknown;
|
|
235
|
+
hold: {
|
|
236
|
+
[x: string]: unknown;
|
|
237
|
+
holdId: string;
|
|
238
|
+
did: string;
|
|
239
|
+
shareVersion: number;
|
|
240
|
+
blobHash: string;
|
|
241
|
+
enrollmentEpoch: number;
|
|
242
|
+
releasePolicy: "pin" | "hold";
|
|
243
|
+
clientEphemeralPublicKey: string;
|
|
244
|
+
createdLo: number;
|
|
245
|
+
createdHi: number;
|
|
246
|
+
policyVersion: number;
|
|
247
|
+
signature: string;
|
|
248
|
+
};
|
|
249
|
+
holdDurationMs: number;
|
|
250
|
+
ledgerSeq: number;
|
|
251
|
+
} | undefined;
|
|
252
|
+
requiresConfirmation?: boolean | undefined;
|
|
92
253
|
pinSalt?: string | undefined;
|
|
93
254
|
};
|
|
94
255
|
meta: import("trpc-to-openapi").OpenApiMeta;
|
|
95
256
|
}>;
|
|
257
|
+
confirmRecovery: import("@trpc/server").TRPCMutationProcedure<{
|
|
258
|
+
input: {
|
|
259
|
+
holdId: string;
|
|
260
|
+
resumeToken: string;
|
|
261
|
+
code: string;
|
|
262
|
+
};
|
|
263
|
+
output: {
|
|
264
|
+
success: true;
|
|
265
|
+
receipt?: {
|
|
266
|
+
version: 1;
|
|
267
|
+
keyId: string;
|
|
268
|
+
measurement: number[];
|
|
269
|
+
signature: string;
|
|
270
|
+
kind: "commit";
|
|
271
|
+
payload: string;
|
|
272
|
+
} | {
|
|
273
|
+
version: 1;
|
|
274
|
+
keyId: string;
|
|
275
|
+
measurement: number[];
|
|
276
|
+
payload: string;
|
|
277
|
+
signature: string;
|
|
278
|
+
kind: "snapshot";
|
|
279
|
+
} | undefined;
|
|
280
|
+
};
|
|
281
|
+
meta: import("trpc-to-openapi").OpenApiMeta;
|
|
282
|
+
}>;
|
|
283
|
+
resendRecoveryCode: import("@trpc/server").TRPCMutationProcedure<{
|
|
284
|
+
input: {
|
|
285
|
+
holdId: string;
|
|
286
|
+
resumeToken: string;
|
|
287
|
+
};
|
|
288
|
+
output: {
|
|
289
|
+
success: true;
|
|
290
|
+
};
|
|
291
|
+
meta: import("trpc-to-openapi").OpenApiMeta;
|
|
292
|
+
}>;
|
|
96
293
|
getStatus: import("@trpc/server").TRPCQueryProcedure<{
|
|
97
294
|
input: {
|
|
98
295
|
holdId?: string | undefined;
|
|
@@ -109,6 +306,27 @@ export declare const escrowRouter: import("@trpc/server").TRPCBuiltRouter<{
|
|
|
109
306
|
releasePolicy: "pin" | "hold";
|
|
110
307
|
cancelledAt?: string | undefined;
|
|
111
308
|
completedAt?: string | undefined;
|
|
309
|
+
requiresConfirmation?: boolean | undefined;
|
|
310
|
+
confirmed?: boolean | undefined;
|
|
311
|
+
holdRecord?: {
|
|
312
|
+
[x: string]: unknown;
|
|
313
|
+
hold: {
|
|
314
|
+
[x: string]: unknown;
|
|
315
|
+
holdId: string;
|
|
316
|
+
did: string;
|
|
317
|
+
shareVersion: number;
|
|
318
|
+
blobHash: string;
|
|
319
|
+
enrollmentEpoch: number;
|
|
320
|
+
releasePolicy: "pin" | "hold";
|
|
321
|
+
clientEphemeralPublicKey: string;
|
|
322
|
+
createdLo: number;
|
|
323
|
+
createdHi: number;
|
|
324
|
+
policyVersion: number;
|
|
325
|
+
signature: string;
|
|
326
|
+
};
|
|
327
|
+
holdDurationMs: number;
|
|
328
|
+
ledgerSeq: number;
|
|
329
|
+
} | undefined;
|
|
112
330
|
} | null;
|
|
113
331
|
};
|
|
114
332
|
meta: import("trpc-to-openapi").OpenApiMeta;
|
package/dist/routes/index.d.ts
CHANGED
|
@@ -35,6 +35,7 @@ export declare const t: import("@trpc/server").TRPCRootObject<Context, OpenApiMe
|
|
|
35
35
|
export declare const createContext: (options: CreateAWSLambdaContextOptions<APIGatewayProxyEventV2> | CreateFastifyContextOptions | NodeHTTPCreateContextFnOptions<IncomingMessage, ServerResponse>) => Promise<Context>;
|
|
36
36
|
export declare const redactSecretFields: (value: unknown, depth?: number, seen?: Set<object>) => unknown;
|
|
37
37
|
export declare const openRoute: import("@trpc/server").TRPCProcedureBuilder<Context, OpenApiMeta, {
|
|
38
|
+
tenant: ResolvedTenant;
|
|
38
39
|
debug: boolean | undefined;
|
|
39
40
|
user: {
|
|
40
41
|
did: string;
|
|
@@ -42,11 +43,11 @@ export declare const openRoute: import("@trpc/server").TRPCProcedureBuilder<Cont
|
|
|
42
43
|
authorizedDid?: boolean;
|
|
43
44
|
} | undefined;
|
|
44
45
|
domain: string;
|
|
45
|
-
tenant: ResolvedTenant;
|
|
46
46
|
clientIp: string | undefined;
|
|
47
47
|
providerToken: string | undefined;
|
|
48
48
|
}, import("@trpc/server").TRPCUnsetMarker, import("@trpc/server").TRPCUnsetMarker, import("@trpc/server").TRPCUnsetMarker, import("@trpc/server").TRPCUnsetMarker, false>;
|
|
49
49
|
export declare const didRoute: import("@trpc/server").TRPCProcedureBuilder<Context, OpenApiMeta, {
|
|
50
|
+
tenant: ResolvedTenant;
|
|
50
51
|
debug: boolean | undefined;
|
|
51
52
|
user: {
|
|
52
53
|
did: string;
|
|
@@ -54,11 +55,11 @@ export declare const didRoute: import("@trpc/server").TRPCProcedureBuilder<Conte
|
|
|
54
55
|
authorizedDid?: boolean;
|
|
55
56
|
};
|
|
56
57
|
domain: string;
|
|
57
|
-
tenant: ResolvedTenant;
|
|
58
58
|
clientIp: string | undefined;
|
|
59
59
|
providerToken: string | undefined;
|
|
60
60
|
}, import("@trpc/server").TRPCUnsetMarker, import("@trpc/server").TRPCUnsetMarker, import("@trpc/server").TRPCUnsetMarker, import("@trpc/server").TRPCUnsetMarker, false>;
|
|
61
61
|
export declare const authorizedDidRoute: import("@trpc/server").TRPCProcedureBuilder<Context, OpenApiMeta, {
|
|
62
|
+
tenant: ResolvedTenant;
|
|
62
63
|
debug: boolean | undefined;
|
|
63
64
|
user: {
|
|
64
65
|
did: string;
|
|
@@ -66,11 +67,11 @@ export declare const authorizedDidRoute: import("@trpc/server").TRPCProcedureBui
|
|
|
66
67
|
authorizedDid?: boolean;
|
|
67
68
|
};
|
|
68
69
|
domain: string;
|
|
69
|
-
tenant: ResolvedTenant;
|
|
70
70
|
clientIp: string | undefined;
|
|
71
71
|
providerToken: string | undefined;
|
|
72
72
|
}, import("@trpc/server").TRPCUnsetMarker, import("@trpc/server").TRPCUnsetMarker, import("@trpc/server").TRPCUnsetMarker, import("@trpc/server").TRPCUnsetMarker, false>;
|
|
73
73
|
export declare const didAndChallengeRoute: import("@trpc/server").TRPCProcedureBuilder<Context, OpenApiMeta, {
|
|
74
|
+
tenant: ResolvedTenant;
|
|
74
75
|
debug: boolean | undefined;
|
|
75
76
|
user: {
|
|
76
77
|
did: string;
|
|
@@ -78,7 +79,6 @@ export declare const didAndChallengeRoute: import("@trpc/server").TRPCProcedureB
|
|
|
78
79
|
authorizedDid?: boolean;
|
|
79
80
|
};
|
|
80
81
|
domain: string;
|
|
81
|
-
tenant: ResolvedTenant;
|
|
82
82
|
clientIp: string | undefined;
|
|
83
83
|
providerToken: string | undefined;
|
|
84
84
|
}, import("@trpc/server").TRPCUnsetMarker, import("@trpc/server").TRPCUnsetMarker, import("@trpc/server").TRPCUnsetMarker, import("@trpc/server").TRPCUnsetMarker, false>;
|
package/dist/routes/keys.d.ts
CHANGED
|
@@ -67,6 +67,7 @@ export declare const keysRouter: import("@trpc/server").TRPCBuiltRouter<{
|
|
|
67
67
|
attemptsRemaining: number;
|
|
68
68
|
salt?: string | undefined;
|
|
69
69
|
};
|
|
70
|
+
primaryDid: string | null;
|
|
70
71
|
};
|
|
71
72
|
meta: import("trpc-to-openapi").OpenApiMeta;
|
|
72
73
|
}>;
|
|
@@ -152,7 +153,7 @@ export declare const keysRouter: import("@trpc/server").TRPCBuiltRouter<{
|
|
|
152
153
|
attemptsRemaining: number;
|
|
153
154
|
salt?: string | undefined;
|
|
154
155
|
};
|
|
155
|
-
sssActivationState: "
|
|
156
|
+
sssActivationState: "active" | "provisional";
|
|
156
157
|
escrowStale?: "mode-mismatch" | "key-rotated" | undefined;
|
|
157
158
|
} | null;
|
|
158
159
|
meta: import("trpc-to-openapi").OpenApiMeta;
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export declare const safeInteger: z.ZodNumber;
|
|
3
|
+
export declare const AuthorityEnvelopeValidator: z.ZodObject<{
|
|
4
|
+
version: z.ZodLiteral<1>;
|
|
5
|
+
algorithm: z.ZodLiteral<"P-256-HKDF-SHA256-AES-256-GCM">;
|
|
6
|
+
keyId: z.ZodString;
|
|
7
|
+
ephemeralPublicKey: z.ZodString;
|
|
8
|
+
salt: z.ZodString;
|
|
9
|
+
iv: z.ZodString;
|
|
10
|
+
ciphertext: z.ZodString;
|
|
11
|
+
}, z.core.$strict>;
|
|
12
|
+
export declare const AuthorityRecordValidator: z.ZodObject<{
|
|
13
|
+
version: z.ZodLiteral<1>;
|
|
14
|
+
kind: z.ZodEnum<{
|
|
15
|
+
proposal: "proposal";
|
|
16
|
+
commit: "commit";
|
|
17
|
+
snapshot: "snapshot";
|
|
18
|
+
}>;
|
|
19
|
+
keyId: z.ZodString;
|
|
20
|
+
measurement: z.ZodArray<z.ZodNumber>;
|
|
21
|
+
payload: z.ZodString;
|
|
22
|
+
signature: z.ZodString;
|
|
23
|
+
}, z.core.$strict>;
|
|
24
|
+
export declare const AuthorityCommitValidator: z.ZodObject<{
|
|
25
|
+
version: z.ZodLiteral<1>;
|
|
26
|
+
keyId: z.ZodString;
|
|
27
|
+
measurement: z.ZodArray<z.ZodNumber>;
|
|
28
|
+
signature: z.ZodString;
|
|
29
|
+
kind: z.ZodLiteral<"commit">;
|
|
30
|
+
payload: z.ZodString;
|
|
31
|
+
}, z.core.$strict>;
|
|
32
|
+
export declare const AuthorityHoldSnapshotValidator: z.ZodObject<{
|
|
33
|
+
version: z.ZodLiteral<1>;
|
|
34
|
+
keyId: z.ZodString;
|
|
35
|
+
measurement: z.ZodArray<z.ZodNumber>;
|
|
36
|
+
payload: z.ZodString;
|
|
37
|
+
signature: z.ZodString;
|
|
38
|
+
kind: z.ZodLiteral<"snapshot">;
|
|
39
|
+
}, z.core.$strict>;
|
|
40
|
+
export declare const AuthorityHoldReceiptValidator: z.ZodUnion<readonly [z.ZodObject<{
|
|
41
|
+
version: z.ZodLiteral<1>;
|
|
42
|
+
keyId: z.ZodString;
|
|
43
|
+
measurement: z.ZodArray<z.ZodNumber>;
|
|
44
|
+
signature: z.ZodString;
|
|
45
|
+
kind: z.ZodLiteral<"commit">;
|
|
46
|
+
payload: z.ZodString;
|
|
47
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
48
|
+
version: z.ZodLiteral<1>;
|
|
49
|
+
keyId: z.ZodString;
|
|
50
|
+
measurement: z.ZodArray<z.ZodNumber>;
|
|
51
|
+
payload: z.ZodString;
|
|
52
|
+
signature: z.ZodString;
|
|
53
|
+
kind: z.ZodLiteral<"snapshot">;
|
|
54
|
+
}, z.core.$strict>]>;
|
|
55
|
+
export declare const SignedEnrollmentIntentValidator: z.ZodObject<{
|
|
56
|
+
intent: z.ZodObject<{
|
|
57
|
+
version: z.ZodLiteral<2>;
|
|
58
|
+
tenant: z.ZodString;
|
|
59
|
+
did: z.ZodString;
|
|
60
|
+
authorityGeneration: z.ZodString;
|
|
61
|
+
expectedEnrollmentVersion: z.ZodNumber;
|
|
62
|
+
action: z.ZodEnum<{
|
|
63
|
+
enroll: "enroll";
|
|
64
|
+
replace: "replace";
|
|
65
|
+
disable: "disable";
|
|
66
|
+
carry: "carry";
|
|
67
|
+
}>;
|
|
68
|
+
sourceBlobIdentity: z.ZodNullable<z.ZodString>;
|
|
69
|
+
targetBlobIdentity: z.ZodNullable<z.ZodString>;
|
|
70
|
+
pinAction: z.ZodEnum<{
|
|
71
|
+
set: "set";
|
|
72
|
+
change: "change";
|
|
73
|
+
clear: "clear";
|
|
74
|
+
keep: "keep";
|
|
75
|
+
}>;
|
|
76
|
+
shareVersion: z.ZodNumber;
|
|
77
|
+
enrollmentEpoch: z.ZodNumber;
|
|
78
|
+
requestId: z.ZodString;
|
|
79
|
+
allowRewrap: z.ZodBoolean;
|
|
80
|
+
recoveryEmail: z.ZodNullable<z.ZodString>;
|
|
81
|
+
recoveryLanguage: z.ZodNullable<z.ZodString>;
|
|
82
|
+
}, z.core.$strict>;
|
|
83
|
+
signature: z.ZodString;
|
|
84
|
+
}, z.core.$strict>;
|
|
85
|
+
export declare const AuthorityStateValidator: z.ZodObject<{
|
|
86
|
+
tenant: z.ZodString;
|
|
87
|
+
authorityGeneration: z.ZodString;
|
|
88
|
+
did: z.ZodString;
|
|
89
|
+
enrollmentVersion: z.ZodNumber;
|
|
90
|
+
enrollmentEpoch: z.ZodNumber;
|
|
91
|
+
status: z.ZodEnum<{
|
|
92
|
+
empty: "empty";
|
|
93
|
+
active: "active";
|
|
94
|
+
disabled: "disabled";
|
|
95
|
+
}>;
|
|
96
|
+
envelope: z.ZodNullable<z.ZodObject<{
|
|
97
|
+
version: z.ZodLiteral<1>;
|
|
98
|
+
algorithm: z.ZodLiteral<"P-256-HKDF-SHA256-AES-256-GCM">;
|
|
99
|
+
keyId: z.ZodString;
|
|
100
|
+
ephemeralPublicKey: z.ZodString;
|
|
101
|
+
salt: z.ZodString;
|
|
102
|
+
iv: z.ZodString;
|
|
103
|
+
ciphertext: z.ZodString;
|
|
104
|
+
}, z.core.$strict>>;
|
|
105
|
+
shareVersion: z.ZodNullable<z.ZodNumber>;
|
|
106
|
+
hasPin: z.ZodBoolean;
|
|
107
|
+
pinGeneration: z.ZodNumber;
|
|
108
|
+
attemptsUsed: z.ZodNumber;
|
|
109
|
+
}, z.core.$strict>;
|
|
110
|
+
export declare const AuthorityStateResponseValidator: z.ZodObject<{
|
|
111
|
+
state: z.ZodObject<{
|
|
112
|
+
tenant: z.ZodString;
|
|
113
|
+
authorityGeneration: z.ZodString;
|
|
114
|
+
did: z.ZodString;
|
|
115
|
+
enrollmentVersion: z.ZodNumber;
|
|
116
|
+
enrollmentEpoch: z.ZodNumber;
|
|
117
|
+
status: z.ZodEnum<{
|
|
118
|
+
empty: "empty";
|
|
119
|
+
active: "active";
|
|
120
|
+
disabled: "disabled";
|
|
121
|
+
}>;
|
|
122
|
+
envelope: z.ZodNullable<z.ZodObject<{
|
|
123
|
+
version: z.ZodLiteral<1>;
|
|
124
|
+
algorithm: z.ZodLiteral<"P-256-HKDF-SHA256-AES-256-GCM">;
|
|
125
|
+
keyId: z.ZodString;
|
|
126
|
+
ephemeralPublicKey: z.ZodString;
|
|
127
|
+
salt: z.ZodString;
|
|
128
|
+
iv: z.ZodString;
|
|
129
|
+
ciphertext: z.ZodString;
|
|
130
|
+
}, z.core.$strict>>;
|
|
131
|
+
shareVersion: z.ZodNullable<z.ZodNumber>;
|
|
132
|
+
hasPin: z.ZodBoolean;
|
|
133
|
+
pinGeneration: z.ZodNumber;
|
|
134
|
+
attemptsUsed: z.ZodNumber;
|
|
135
|
+
}, z.core.$strict>;
|
|
136
|
+
proof: z.ZodObject<{
|
|
137
|
+
version: z.ZodLiteral<1>;
|
|
138
|
+
keyId: z.ZodString;
|
|
139
|
+
measurement: z.ZodArray<z.ZodNumber>;
|
|
140
|
+
payload: z.ZodString;
|
|
141
|
+
signature: z.ZodString;
|
|
142
|
+
kind: z.ZodLiteral<"snapshot">;
|
|
143
|
+
}, z.core.$strict>;
|
|
144
|
+
}, z.core.$strict>;
|
|
145
|
+
export declare const AuthorityMutationValidator: z.ZodObject<{
|
|
146
|
+
envelope: z.ZodNullable<z.ZodObject<{
|
|
147
|
+
version: z.ZodLiteral<1>;
|
|
148
|
+
algorithm: z.ZodLiteral<"P-256-HKDF-SHA256-AES-256-GCM">;
|
|
149
|
+
keyId: z.ZodString;
|
|
150
|
+
ephemeralPublicKey: z.ZodString;
|
|
151
|
+
salt: z.ZodString;
|
|
152
|
+
iv: z.ZodString;
|
|
153
|
+
ciphertext: z.ZodString;
|
|
154
|
+
}, z.core.$strict>>;
|
|
155
|
+
receipt: z.ZodObject<{
|
|
156
|
+
version: z.ZodLiteral<1>;
|
|
157
|
+
keyId: z.ZodString;
|
|
158
|
+
measurement: z.ZodArray<z.ZodNumber>;
|
|
159
|
+
signature: z.ZodString;
|
|
160
|
+
kind: z.ZodLiteral<"commit">;
|
|
161
|
+
payload: z.ZodString;
|
|
162
|
+
}, z.core.$strict>;
|
|
163
|
+
authorityGeneration: z.ZodString;
|
|
164
|
+
enrollmentVersion: z.ZodNumber;
|
|
165
|
+
enrollmentEpoch: z.ZodNumber;
|
|
166
|
+
}, z.core.$strict>;
|
|
167
|
+
export type AuthorityRecord = z.infer<typeof AuthorityRecordValidator>;
|
|
168
|
+
export type AuthorityHoldReceipt = z.infer<typeof AuthorityHoldReceiptValidator>;
|
|
169
|
+
export type AuthorityMutationResult = z.infer<typeof AuthorityMutationValidator>;
|
|
170
|
+
export type AuthorityStateResponse = z.infer<typeof AuthorityStateResponseValidator>;
|
|
@@ -1,4 +1,10 @@
|
|
|
1
|
-
import type { EscrowEnvelope } from '@learncard/sss-key-manager';
|
|
1
|
+
import type { EscrowEnvelope, SignedEscrowEnrollmentIntent } from '@learncard/sss-key-manager';
|
|
2
|
+
import type { AuthorityRecord, AuthorityHoldReceipt, AuthorityMutationResult, AuthorityStateResponse } from './authorityContract';
|
|
3
|
+
export interface ApplyEnrollmentInput {
|
|
4
|
+
signedIntent: SignedEscrowEnrollmentIntent;
|
|
5
|
+
sourceEnvelope: EscrowEnvelope | null;
|
|
6
|
+
targetEnvelope: EscrowEnvelope | null;
|
|
7
|
+
}
|
|
2
8
|
export interface EnclaveAttestation {
|
|
3
9
|
mode: 'software' | 'nitro';
|
|
4
10
|
keyId: string;
|
|
@@ -42,11 +48,21 @@ export interface EnclaveCreateHoldInput {
|
|
|
42
48
|
clientEphemeralPublicKey: string;
|
|
43
49
|
}
|
|
44
50
|
export interface CancelHoldRequest {
|
|
51
|
+
requestId?: string;
|
|
45
52
|
envelope: EscrowEnvelope;
|
|
46
53
|
hold: EscrowHoldRecord;
|
|
47
54
|
clientEphemeralPublicKey: string;
|
|
48
55
|
expectedDid: string;
|
|
49
56
|
}
|
|
57
|
+
export interface ConfirmHoldRequest extends CancelHoldRequest {
|
|
58
|
+
code: string;
|
|
59
|
+
}
|
|
60
|
+
export type ResendCodeRequest = CancelHoldRequest;
|
|
61
|
+
export interface SendReleaseWarningRequest {
|
|
62
|
+
requestId?: string;
|
|
63
|
+
holdId: string;
|
|
64
|
+
expectedDid: string;
|
|
65
|
+
}
|
|
50
66
|
export interface VerifyEscrowBlobInput {
|
|
51
67
|
envelope: EscrowEnvelope;
|
|
52
68
|
expectedDid: string;
|
|
@@ -66,6 +82,7 @@ export interface ReleaseRequest extends CancelHoldRequest {
|
|
|
66
82
|
}
|
|
67
83
|
export interface ReleaseResult {
|
|
68
84
|
sealed: EscrowEnvelope;
|
|
85
|
+
receipt?: AuthorityRecord;
|
|
69
86
|
}
|
|
70
87
|
export interface CarryPinVerifierInput {
|
|
71
88
|
sourceEnvelope: EscrowEnvelope;
|
|
@@ -83,6 +100,11 @@ export interface RewrapEscrowBlobInput {
|
|
|
83
100
|
sourceEnrollmentEpoch: number;
|
|
84
101
|
}
|
|
85
102
|
export interface EscrowEnclave {
|
|
103
|
+
getEnrollmentState?(input: {
|
|
104
|
+
expectedDid: string;
|
|
105
|
+
nonce: string;
|
|
106
|
+
}): Promise<AuthorityStateResponse>;
|
|
107
|
+
applyEnrollmentIntent?(input: ApplyEnrollmentInput): Promise<AuthorityMutationResult>;
|
|
86
108
|
getAttestation(nonce?: Uint8Array): Promise<EnclaveAttestation>;
|
|
87
109
|
verifyEscrowBlob(input: VerifyEscrowBlobInput): Promise<VerifyEscrowBlobResult>;
|
|
88
110
|
carryPinVerifier(input: CarryPinVerifierInput): Promise<{
|
|
@@ -90,12 +112,26 @@ export interface EscrowEnclave {
|
|
|
90
112
|
}>;
|
|
91
113
|
rewrapEscrowBlob(input: RewrapEscrowBlobInput): Promise<{
|
|
92
114
|
envelope: EscrowEnvelope;
|
|
115
|
+
authority?: AuthorityMutationResult;
|
|
93
116
|
}>;
|
|
94
117
|
releaseEscrow(input: ReleaseRequest): Promise<ReleaseResult>;
|
|
95
118
|
createHold(input: EnclaveCreateHoldInput): Promise<{
|
|
96
119
|
holdRecord: EscrowHoldRecord;
|
|
97
120
|
}>;
|
|
98
|
-
cancelHold(input: CancelHoldRequest): Promise<void
|
|
121
|
+
cancelHold(input: CancelHoldRequest): Promise<void | {
|
|
122
|
+
receipt: AuthorityRecord;
|
|
123
|
+
}>;
|
|
124
|
+
confirmHold?(input: ConfirmHoldRequest): Promise<{
|
|
125
|
+
receipt: AuthorityHoldReceipt;
|
|
126
|
+
confirmedAtMs: number;
|
|
127
|
+
}>;
|
|
128
|
+
resendCode?(input: ResendCodeRequest): Promise<{
|
|
129
|
+
receipt: AuthorityRecord;
|
|
130
|
+
}>;
|
|
131
|
+
sendReleaseWarning?(input: SendReleaseWarningRequest): Promise<{
|
|
132
|
+
receipt: AuthorityHoldReceipt;
|
|
133
|
+
releaseWarningSentAtMs: number;
|
|
134
|
+
}>;
|
|
99
135
|
}
|
|
100
136
|
export declare class EscrowPolicyError extends Error {
|
|
101
137
|
constructor();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@learncard/lca-api-service",
|
|
3
|
-
"version": "1.5.
|
|
3
|
+
"version": "1.5.2",
|
|
4
4
|
"description": "",
|
|
5
5
|
"engines": {
|
|
6
6
|
"node": ">=24.12 <25"
|
|
@@ -63,15 +63,15 @@
|
|
|
63
63
|
"@fastify/formbody": "^8.0.2",
|
|
64
64
|
"@fastify/rate-limit": "^10.2.0",
|
|
65
65
|
"@fastify/static": "^10.1.1",
|
|
66
|
-
"@learncard/core": "9.4.
|
|
67
|
-
"@learncard/did-web-plugin": "1.1.
|
|
68
|
-
"@learncard/didkit-plugin": "1.10.
|
|
69
|
-
"@learncard/didkit-plugin-node": "0.3.
|
|
66
|
+
"@learncard/core": "9.4.39",
|
|
67
|
+
"@learncard/did-web-plugin": "1.1.39",
|
|
68
|
+
"@learncard/didkit-plugin": "1.10.4",
|
|
69
|
+
"@learncard/didkit-plugin-node": "0.3.4",
|
|
70
70
|
"@learncard/email-templates": "1.2.1",
|
|
71
|
-
"@learncard/helpers": "1.6.
|
|
72
|
-
"@learncard/init": "2.5.
|
|
73
|
-
"@learncard/types": "5.23.
|
|
74
|
-
"@learncard/sss-key-manager": "0.2.
|
|
71
|
+
"@learncard/helpers": "1.6.3",
|
|
72
|
+
"@learncard/init": "2.5.4",
|
|
73
|
+
"@learncard/types": "5.23.1",
|
|
74
|
+
"@learncard/sss-key-manager": "0.2.2",
|
|
75
75
|
"@sentry/serverless": "^7.61.0",
|
|
76
76
|
"@trpc/server": "11.8.0",
|
|
77
77
|
"@types/jsonwebtoken": "^9.0.8",
|
|
@@ -64,7 +64,7 @@ export const lcaApiEnvironmentShape = {
|
|
|
64
64
|
* silently DoS recovery. See services/escrow-ledger-monitor/README.md. */
|
|
65
65
|
ESCROW_RELEASE_KILL_SWITCH: optionalEnvironmentBoolean,
|
|
66
66
|
ESCROW_ENCLAVE_REMOTE_TIMEOUT_MS: optionalEnvironmentString
|
|
67
|
-
.transform(value => (value === undefined ?
|
|
67
|
+
.transform(value => (value === undefined ? 15_000 : Number(value)))
|
|
68
68
|
.pipe(z.number().int().positive().max(30_000)),
|
|
69
69
|
ESCROW_HOLD_DURATION_MS: optionalEnvironmentString
|
|
70
70
|
.transform(value => (value === undefined ? 604_800_000 : Number(value)))
|
|
@@ -101,6 +101,14 @@ describe('escrow deployment wiring', () => {
|
|
|
101
101
|
);
|
|
102
102
|
});
|
|
103
103
|
|
|
104
|
+
it('keeps every checked-in enclave timeout above the host forwarding deadline', () => {
|
|
105
|
+
// enclave 10s < host 12s < lca-api; an equal or shorter value races the response.
|
|
106
|
+
for (const [stage, config] of Object.entries(stages)) {
|
|
107
|
+
const value = (config as Record<string, string>).ESCROW_ENCLAVE_REMOTE_TIMEOUT_MS;
|
|
108
|
+
if (value !== undefined) expect(Number(value), stage).toBeGreaterThan(12_000);
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
|
|
104
112
|
it('never checks in escrow credentials', () => {
|
|
105
113
|
for (const [stage, config] of Object.entries(stages)) {
|
|
106
114
|
for (const name of [...SOURCES.bundle, ...SOURCES.toggle]) {
|