@learncard/lca-api-service 1.3.1 → 1.4.0

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.
Files changed (99) hide show
  1. package/.env.example +26 -2
  2. package/AGENTS.md +59 -44
  3. package/CHANGELOG.md +57 -0
  4. package/compose.env +5 -0
  5. package/dist/accesslayer/signing-authority/index.d.ts +8 -2
  6. package/dist/app.d.ts +297 -8
  7. package/dist/cache/challenges.d.ts +2 -0
  8. package/dist/cache/recoverySessions.d.ts +42 -0
  9. package/dist/config/environment.d.ts +152 -2
  10. package/dist/config/environment.test.d.ts +1 -0
  11. package/dist/helpers/learnCard.signingAuthority.test.d.ts +1 -0
  12. package/dist/helpers/rateLimit.helpers.d.ts +4 -0
  13. package/dist/helpers/seedEncryption.helpers.d.ts +23 -0
  14. package/dist/helpers/seedEncryption.helpers.test.d.ts +1 -0
  15. package/dist/index.js +2 -2
  16. package/dist/migrations/signingAuthoritySeeds.d.ts +13 -0
  17. package/dist/migrations/signingAuthoritySeedsCli.test.d.ts +1 -0
  18. package/dist/models/EscrowHold.d.ts +75 -0
  19. package/dist/models/SigningAuthority.d.ts +7 -1
  20. package/dist/models/UserKey.d.ts +316 -3
  21. package/dist/models/UserKey.escrowPin.test.d.ts +1 -0
  22. package/dist/models/index.d.ts +1 -0
  23. package/dist/routes/escrow.d.ts +150 -0
  24. package/dist/routes/firebase.test.d.ts +1 -0
  25. package/dist/routes/index.d.ts +6 -0
  26. package/dist/routes/keys.d.ts +181 -7
  27. package/dist/routes/qr-login.d.ts +1 -1
  28. package/dist/services/escrow-enclave/index.d.ts +9 -0
  29. package/dist/services/escrow-enclave/notifications.d.ts +9 -0
  30. package/dist/services/escrow-enclave/softwareEnclave.d.ts +17 -0
  31. package/dist/services/escrow-enclave/softwareEnclave.test.d.ts +1 -0
  32. package/dist/services/escrow-enclave/types.d.ts +67 -0
  33. package/dist/types/seed-encryption.d.ts +31 -0
  34. package/dist/types/seed-migration.d.ts +49 -0
  35. package/lambda.ts +8 -3
  36. package/package.json +17 -11
  37. package/scripts/check-seed-encryption-infrastructure.mjs +123 -0
  38. package/scripts/check-signing-authority-redis.mjs +93 -0
  39. package/scripts/migrate-signing-authority-seeds-local.sh +51 -0
  40. package/scripts/migrate-signing-authority-seeds.mjs +105 -0
  41. package/seedMigrationLambda.test.ts +145 -0
  42. package/seedMigrationLambda.ts +50 -0
  43. package/serverless-local.yml +3 -0
  44. package/serverless.escrow-env.cjs +20 -0
  45. package/serverless.yml +133 -0
  46. package/src/accesslayer/signing-authority/create.ts +11 -7
  47. package/src/app.ts +2 -0
  48. package/src/cache/challenges.ts +12 -2
  49. package/src/cache/in-memory-lru.ts +1 -0
  50. package/src/cache/recoverySessions.ts +252 -0
  51. package/src/config/environment.test.ts +116 -0
  52. package/src/config/environment.ts +97 -0
  53. package/src/docker-entry.ts +4 -0
  54. package/src/helpers/learnCard.helpers.test.ts +11 -1
  55. package/src/helpers/learnCard.helpers.ts +57 -45
  56. package/src/helpers/learnCard.signingAuthority.test.ts +194 -0
  57. package/src/helpers/rateLimit.helpers.ts +112 -0
  58. package/src/helpers/seedEncryption.helpers.test.ts +427 -0
  59. package/src/helpers/seedEncryption.helpers.ts +372 -0
  60. package/src/helpers/signingAuthority.helpers.ts +4 -1
  61. package/src/migrations/signingAuthoritySeeds.ts +430 -0
  62. package/src/migrations/signingAuthoritySeedsCli.test.ts +70 -0
  63. package/src/models/EscrowHold.ts +225 -0
  64. package/src/models/SigningAuthority.ts +14 -3
  65. package/src/models/UserKey.escrowPin.test.ts +49 -0
  66. package/src/models/UserKey.ts +1024 -40
  67. package/src/models/index.ts +1 -0
  68. package/src/models/pruneOrphanedRecoveryMethods.ts +3 -1
  69. package/src/routes/credentials.ts +8 -0
  70. package/src/routes/escrow.ts +826 -0
  71. package/src/routes/firebase.test.ts +94 -0
  72. package/src/routes/firebase.ts +99 -56
  73. package/src/routes/index.ts +98 -5
  74. package/src/routes/keys.ts +1017 -174
  75. package/src/routes/qr-login.ts +51 -53
  76. package/src/services/delivery/adapters/postmark.adapter.ts +5 -0
  77. package/src/services/escrow-enclave/README.md +51 -0
  78. package/src/services/escrow-enclave/index.ts +36 -0
  79. package/src/services/escrow-enclave/notifications.ts +14 -0
  80. package/src/services/escrow-enclave/softwareEnclave.test.ts +249 -0
  81. package/src/services/escrow-enclave/softwareEnclave.ts +168 -0
  82. package/src/services/escrow-enclave/types.ts +76 -0
  83. package/src/types/seed-encryption.ts +51 -0
  84. package/src/types/seed-migration.ts +61 -0
  85. package/test/credentials.spec.ts +2 -0
  86. package/test/keys-escrow.spec.ts +1389 -0
  87. package/test/keys-identity-rebind.spec.ts +456 -0
  88. package/test/keys-lifecycle-invariants.spec.ts +785 -0
  89. package/test/keys-recovery-confirmation.spec.ts +586 -0
  90. package/test/keys-recovery-rate-limit.spec.ts +123 -0
  91. package/test/keys-secret-leakage.spec.ts +265 -0
  92. package/test/models/escrow.spec.ts +210 -0
  93. package/test/signing-authority-http.spec.ts +161 -0
  94. package/test/signing-authority-seed-migration.spec.ts +414 -0
  95. package/test/signing-authority.spec.ts +40 -0
  96. package/vitest.config.ts +1 -1
  97. package/vitest.integration.config.ts +5 -0
  98. package/dist/cache/signing-authority.d.ts +0 -6
  99. package/src/cache/signing-authority.ts +0 -37
@@ -0,0 +1,150 @@
1
+ export declare const escrowRouter: import("@trpc/server").TRPCBuiltRouter<{
2
+ ctx: import("@routes").Context;
3
+ meta: import("trpc-to-openapi").OpenApiMeta;
4
+ errorShape: import("@trpc/server").TRPCDefaultErrorShape;
5
+ transformer: false;
6
+ }, import("@trpc/server").TRPCDecorateCreateRouterOptions<{
7
+ getAttestation: import("@trpc/server").TRPCQueryProcedure<{
8
+ input: Record<string, never>;
9
+ output: {
10
+ attestation: {
11
+ mode: "software" | "nitro";
12
+ keyId: string;
13
+ publicKey: string;
14
+ measurements: {
15
+ imageSha384?: string | undefined;
16
+ pcr0?: string | undefined;
17
+ pcr1?: string | undefined;
18
+ pcr2?: string | undefined;
19
+ };
20
+ document: string;
21
+ issuedAt: string;
22
+ };
23
+ holdDurationMs: number;
24
+ };
25
+ meta: import("trpc-to-openapi").OpenApiMeta;
26
+ }>;
27
+ enroll: import("@trpc/server").TRPCMutationProcedure<{
28
+ input: {
29
+ authToken: string;
30
+ providerType: "firebase" | "supertokens" | "keycloak" | "oidc";
31
+ envelope: {
32
+ version: 1;
33
+ algorithm: "P-256-HKDF-SHA256-AES-256-GCM";
34
+ keyId: string;
35
+ ephemeralPublicKey: string;
36
+ salt: string;
37
+ iv: string;
38
+ ciphertext: string;
39
+ };
40
+ shareVersion: number;
41
+ enclaveKeyId: string;
42
+ pinSalt?: string | undefined;
43
+ clearPin?: boolean | undefined;
44
+ };
45
+ output: {
46
+ success: true;
47
+ shareVersion: number;
48
+ };
49
+ meta: import("trpc-to-openapi").OpenApiMeta;
50
+ }>;
51
+ remove: import("@trpc/server").TRPCMutationProcedure<{
52
+ input: {
53
+ authToken: string;
54
+ providerType: "firebase" | "supertokens" | "keycloak" | "oidc";
55
+ optOut: boolean;
56
+ };
57
+ output: {
58
+ success: true;
59
+ };
60
+ meta: import("trpc-to-openapi").OpenApiMeta;
61
+ }>;
62
+ optIn: import("@trpc/server").TRPCMutationProcedure<{
63
+ input: {
64
+ authToken: string;
65
+ providerType: "firebase" | "supertokens" | "keycloak" | "oidc";
66
+ };
67
+ output: {
68
+ success: true;
69
+ };
70
+ meta: import("trpc-to-openapi").OpenApiMeta;
71
+ }>;
72
+ startRecovery: import("@trpc/server").TRPCMutationProcedure<{
73
+ input: {
74
+ clientEphemeralPublicKey: string;
75
+ releasePolicy?: "pin" | "hold" | undefined;
76
+ restart?: boolean | undefined;
77
+ authToken?: string | undefined;
78
+ providerType?: "firebase" | "supertokens" | "keycloak" | "oidc" | undefined;
79
+ recoverySessionToken?: string | undefined;
80
+ };
81
+ output: {
82
+ requestedAt: string;
83
+ releaseAfter: string;
84
+ releasePolicy: "pin" | "hold";
85
+ holdId: string;
86
+ status: "pending";
87
+ resumeToken: string | null;
88
+ pinSalt?: string | undefined;
89
+ };
90
+ meta: import("trpc-to-openapi").OpenApiMeta;
91
+ }>;
92
+ getStatus: import("@trpc/server").TRPCQueryProcedure<{
93
+ input: {
94
+ holdId?: string | undefined;
95
+ resumeToken?: string | undefined;
96
+ authToken?: string | undefined;
97
+ providerType?: "firebase" | "supertokens" | "keycloak" | "oidc" | undefined;
98
+ };
99
+ output: {
100
+ hold: {
101
+ holdId: string;
102
+ status: "pending" | "cancelled" | "completed" | "expired";
103
+ requestedAt: string;
104
+ releaseAfter: string;
105
+ releasePolicy: "pin" | "hold";
106
+ cancelledAt?: string | undefined;
107
+ completedAt?: string | undefined;
108
+ } | null;
109
+ };
110
+ meta: import("trpc-to-openapi").OpenApiMeta;
111
+ }>;
112
+ cancelRecovery: import("@trpc/server").TRPCMutationProcedure<{
113
+ input: {
114
+ authToken: string;
115
+ providerType: "firebase" | "supertokens" | "keycloak" | "oidc";
116
+ };
117
+ output: {
118
+ success: true;
119
+ cancelled: boolean;
120
+ };
121
+ meta: import("trpc-to-openapi").OpenApiMeta;
122
+ }>;
123
+ completeRecovery: import("@trpc/server").TRPCMutationProcedure<{
124
+ input: {
125
+ holdId: string;
126
+ resumeToken: string;
127
+ pinProof?: string | undefined;
128
+ };
129
+ output: {
130
+ sealedShare: {
131
+ version: 1;
132
+ algorithm: "P-256-HKDF-SHA256-AES-256-GCM";
133
+ keyId: string;
134
+ ephemeralPublicKey: string;
135
+ salt: string;
136
+ iv: string;
137
+ ciphertext: string;
138
+ };
139
+ authShare: {
140
+ encryptedData: string;
141
+ encryptedDek: string;
142
+ iv: string;
143
+ };
144
+ primaryDid: string;
145
+ shareVersion: number;
146
+ rebindSessionToken: string;
147
+ };
148
+ meta: import("trpc-to-openapi").OpenApiMeta;
149
+ }>;
150
+ }>>;
@@ -0,0 +1 @@
1
+ export {};
@@ -24,6 +24,7 @@ export type Context = {
24
24
  tenant: ResolvedTenant;
25
25
  debug?: boolean;
26
26
  clientIp?: string;
27
+ providerToken?: string;
27
28
  };
28
29
  export declare const t: import("@trpc/server").TRPCRootObject<Context, OpenApiMeta, import("@trpc/server").TRPCRuntimeConfigOptions<Context, OpenApiMeta>, {
29
30
  ctx: Context;
@@ -32,6 +33,7 @@ export declare const t: import("@trpc/server").TRPCRootObject<Context, OpenApiMe
32
33
  transformer: false;
33
34
  }>;
34
35
  export declare const createContext: (options: CreateAWSLambdaContextOptions<APIGatewayProxyEventV2> | CreateFastifyContextOptions | NodeHTTPCreateContextFnOptions<IncomingMessage, ServerResponse>) => Promise<Context>;
36
+ export declare const redactSecretFields: (value: unknown, depth?: number, seen?: Set<object>) => unknown;
35
37
  export declare const openRoute: import("@trpc/server").TRPCProcedureBuilder<Context, OpenApiMeta, {
36
38
  debug: boolean | undefined;
37
39
  user: {
@@ -42,6 +44,7 @@ export declare const openRoute: import("@trpc/server").TRPCProcedureBuilder<Cont
42
44
  domain: string;
43
45
  tenant: ResolvedTenant;
44
46
  clientIp: string | undefined;
47
+ providerToken: string | undefined;
45
48
  }, import("@trpc/server").TRPCUnsetMarker, import("@trpc/server").TRPCUnsetMarker, import("@trpc/server").TRPCUnsetMarker, import("@trpc/server").TRPCUnsetMarker, false>;
46
49
  export declare const didRoute: import("@trpc/server").TRPCProcedureBuilder<Context, OpenApiMeta, {
47
50
  debug: boolean | undefined;
@@ -53,6 +56,7 @@ export declare const didRoute: import("@trpc/server").TRPCProcedureBuilder<Conte
53
56
  domain: string;
54
57
  tenant: ResolvedTenant;
55
58
  clientIp: string | undefined;
59
+ providerToken: string | undefined;
56
60
  }, import("@trpc/server").TRPCUnsetMarker, import("@trpc/server").TRPCUnsetMarker, import("@trpc/server").TRPCUnsetMarker, import("@trpc/server").TRPCUnsetMarker, false>;
57
61
  export declare const authorizedDidRoute: import("@trpc/server").TRPCProcedureBuilder<Context, OpenApiMeta, {
58
62
  debug: boolean | undefined;
@@ -64,6 +68,7 @@ export declare const authorizedDidRoute: import("@trpc/server").TRPCProcedureBui
64
68
  domain: string;
65
69
  tenant: ResolvedTenant;
66
70
  clientIp: string | undefined;
71
+ providerToken: string | undefined;
67
72
  }, import("@trpc/server").TRPCUnsetMarker, import("@trpc/server").TRPCUnsetMarker, import("@trpc/server").TRPCUnsetMarker, import("@trpc/server").TRPCUnsetMarker, false>;
68
73
  export declare const didAndChallengeRoute: import("@trpc/server").TRPCProcedureBuilder<Context, OpenApiMeta, {
69
74
  debug: boolean | undefined;
@@ -75,4 +80,5 @@ export declare const didAndChallengeRoute: import("@trpc/server").TRPCProcedureB
75
80
  domain: string;
76
81
  tenant: ResolvedTenant;
77
82
  clientIp: string | undefined;
83
+ providerToken: string | undefined;
78
84
  }, import("@trpc/server").TRPCUnsetMarker, import("@trpc/server").TRPCUnsetMarker, import("@trpc/server").TRPCUnsetMarker, import("@trpc/server").TRPCUnsetMarker, false>;
@@ -1,9 +1,126 @@
1
+ import { z } from 'zod';
2
+ import { AuthProviderType } from '@helpers/auth.helpers';
3
+ import { type ContactMethod, type AuthProviderMapping, type MongoUserKeyType } from '@models';
4
+ export declare const AuthProviderTypeValidator: z.ZodEnum<{
5
+ firebase: "firebase";
6
+ supertokens: "supertokens";
7
+ keycloak: "keycloak";
8
+ oidc: "oidc";
9
+ }>;
10
+ export declare const AuthInputValidator: z.ZodObject<{
11
+ authToken: z.ZodString;
12
+ providerType: z.ZodEnum<{
13
+ firebase: "firebase";
14
+ supertokens: "supertokens";
15
+ keycloak: "keycloak";
16
+ oidc: "oidc";
17
+ }>;
18
+ }, z.core.$strip>;
19
+ export declare const RecoverySessionTokenValidator: z.ZodString;
20
+ export declare function verifyAndGetContactMethod(input: {
21
+ authToken: string;
22
+ providerType: AuthProviderType;
23
+ }): Promise<{
24
+ user: {
25
+ id: string;
26
+ email?: string;
27
+ phone?: string;
28
+ providerType: string;
29
+ };
30
+ contactMethod: ContactMethod;
31
+ authProvider: AuthProviderMapping;
32
+ }>;
33
+ export declare const requireUserKey: (authProvider: AuthProviderMapping, message?: string) => Promise<MongoUserKeyType>;
34
+ export declare const assertDidOwner: (userKey: MongoUserKeyType, authenticatedDid: string) => void;
35
+ export declare const requireRecoverySession: (token: string, scope: "recover" | "rebind") => Promise<AuthProviderMapping>;
1
36
  export declare const keysRouter: import("@trpc/server").TRPCBuiltRouter<{
2
37
  ctx: import("@routes").Context;
3
38
  meta: import("trpc-to-openapi").OpenApiMeta;
4
39
  errorShape: import("@trpc/server").TRPCDefaultErrorShape;
5
40
  transformer: false;
6
41
  }, import("@trpc/server").TRPCDecorateCreateRouterOptions<{
42
+ startRecoverySession: import("@trpc/server").TRPCMutationProcedure<{
43
+ input: {
44
+ email: string;
45
+ };
46
+ output: {
47
+ success: true;
48
+ };
49
+ meta: import("trpc-to-openapi").OpenApiMeta;
50
+ }>;
51
+ verifyRecoverySession: import("@trpc/server").TRPCMutationProcedure<{
52
+ input: {
53
+ email: string;
54
+ code: string;
55
+ };
56
+ output: {
57
+ recoverySessionToken: string;
58
+ recoveryMethods: {
59
+ type: "email" | "passkey" | "backup" | "phrase" | "escrow";
60
+ createdAt: string;
61
+ credentialId?: string | undefined;
62
+ shareVersion?: number | undefined;
63
+ }[];
64
+ escrowPin: {
65
+ state: "enabled" | "none" | "locked" | "stale";
66
+ enabled: boolean;
67
+ attemptsRemaining: number;
68
+ salt?: string | undefined;
69
+ };
70
+ };
71
+ meta: import("trpc-to-openapi").OpenApiMeta;
72
+ }>;
73
+ useRecoverySession: import("@trpc/server").TRPCMutationProcedure<{
74
+ input: {
75
+ recoverySessionToken: string;
76
+ type: "email" | "passkey" | "backup" | "phrase" | "escrow";
77
+ credentialId?: string | undefined;
78
+ };
79
+ output: {
80
+ authShare: {
81
+ encryptedData: string;
82
+ encryptedDek: string;
83
+ iv: string;
84
+ };
85
+ primaryDid: string;
86
+ shareVersion: number;
87
+ rebindSessionToken: string;
88
+ encryptedShare?: {
89
+ encryptedData: string;
90
+ iv: string;
91
+ salt?: string | undefined;
92
+ } | undefined;
93
+ };
94
+ meta: import("trpc-to-openapi").OpenApiMeta;
95
+ }>;
96
+ completeRecoveryRebind: import("@trpc/server").TRPCMutationProcedure<{
97
+ input: {
98
+ recoverySessionToken: string;
99
+ providerType: "firebase" | "supertokens" | "keycloak" | "oidc";
100
+ primaryDid: string;
101
+ authShare: {
102
+ encryptedData: string;
103
+ encryptedDek: string;
104
+ iv: string;
105
+ };
106
+ newAuthToken?: string | undefined;
107
+ };
108
+ output: {
109
+ success: true;
110
+ shareVersion: number;
111
+ recoveryMethodsRequireConfirmation: ("email" | "passkey" | "backup" | "phrase" | "escrow")[];
112
+ };
113
+ meta: import("trpc-to-openapi").OpenApiMeta;
114
+ }>;
115
+ issueDidChallenge: import("@trpc/server").TRPCMutationProcedure<{
116
+ input: {
117
+ did: string;
118
+ };
119
+ output: {
120
+ challenge: string;
121
+ };
122
+ meta: import("trpc-to-openapi").OpenApiMeta;
123
+ }>;
7
124
  getAuthShare: import("@trpc/server").TRPCMutationProcedure<{
8
125
  input: {
9
126
  authToken: string;
@@ -19,14 +136,23 @@ export declare const keysRouter: import("@trpc/server").TRPCBuiltRouter<{
19
136
  primaryDid: string | null;
20
137
  securityLevel: "basic" | "enhanced" | "advanced";
21
138
  recoveryMethods: {
22
- type: "email" | "passkey" | "backup" | "phrase";
139
+ type: "email" | "passkey" | "backup" | "phrase" | "escrow";
23
140
  createdAt: string;
24
141
  credentialId?: string | undefined;
25
142
  shareVersion?: number | undefined;
143
+ confirmedAt?: string | undefined;
26
144
  }[];
27
145
  keyProvider: "web3auth" | "sss";
28
146
  shareVersion: number;
29
147
  maskedRecoveryEmail: string | null;
148
+ escrowOptedOut: boolean;
149
+ escrowPin: {
150
+ state: "enabled" | "none" | "locked" | "stale";
151
+ enabled: boolean;
152
+ attemptsRemaining: number;
153
+ salt?: string | undefined;
154
+ };
155
+ sssActivationState: "provisional" | "active";
30
156
  } | null;
31
157
  meta: import("trpc-to-openapi").OpenApiMeta;
32
158
  }>;
@@ -42,10 +168,13 @@ export declare const keysRouter: import("@trpc/server").TRPCBuiltRouter<{
42
168
  primaryDid: string;
43
169
  securityLevel?: "basic" | "enhanced" | "advanced" | undefined;
44
170
  keyProvider?: "web3auth" | "sss" | undefined;
171
+ sssActivationState?: "provisional" | undefined;
172
+ expectedShareVersion?: number | undefined;
45
173
  };
46
174
  output: {
47
175
  success: boolean;
48
176
  shareVersion: number;
177
+ expectedShareVersionChecked: boolean;
49
178
  };
50
179
  meta: import("trpc-to-openapi").OpenApiMeta;
51
180
  }>;
@@ -53,7 +182,7 @@ export declare const keysRouter: import("@trpc/server").TRPCBuiltRouter<{
53
182
  input: {
54
183
  authToken: string;
55
184
  providerType: "firebase" | "supertokens" | "keycloak" | "oidc";
56
- type: "email" | "passkey" | "backup" | "phrase";
185
+ type: "email" | "passkey" | "backup" | "phrase" | "escrow";
57
186
  encryptedShare?: {
58
187
  encryptedData: string;
59
188
  iv: string;
@@ -67,11 +196,36 @@ export declare const keysRouter: import("@trpc/server").TRPCBuiltRouter<{
67
196
  };
68
197
  meta: import("trpc-to-openapi").OpenApiMeta;
69
198
  }>;
70
- getRecoveryShare: import("@trpc/server").TRPCQueryProcedure<{
199
+ confirmRecoveryMethod: import("@trpc/server").TRPCMutationProcedure<{
71
200
  input: {
72
201
  authToken: string;
73
202
  providerType: "firebase" | "supertokens" | "keycloak" | "oidc";
74
- type: "email" | "passkey" | "backup" | "phrase";
203
+ type: "email" | "passkey" | "backup" | "phrase" | "escrow";
204
+ credentialId?: string | undefined;
205
+ code?: string | undefined;
206
+ };
207
+ output: {
208
+ success: boolean;
209
+ };
210
+ meta: import("trpc-to-openapi").OpenApiMeta;
211
+ }>;
212
+ deleteRecoveryMethod: import("@trpc/server").TRPCMutationProcedure<{
213
+ input: {
214
+ authToken: string;
215
+ providerType: "firebase" | "supertokens" | "keycloak" | "oidc";
216
+ type: "email" | "passkey" | "backup" | "phrase" | "escrow";
217
+ credentialId?: string | undefined;
218
+ };
219
+ output: {
220
+ success: boolean;
221
+ };
222
+ meta: import("trpc-to-openapi").OpenApiMeta;
223
+ }>;
224
+ getRecoveryShare: import("@trpc/server").TRPCQueryProcedure<{
225
+ input: {
226
+ providerType: "firebase" | "supertokens" | "keycloak" | "oidc";
227
+ type: "email" | "passkey" | "backup" | "phrase" | "escrow";
228
+ authToken?: string | undefined;
75
229
  credentialId?: string | undefined;
76
230
  };
77
231
  output: {
@@ -94,6 +248,16 @@ export declare const keysRouter: import("@trpc/server").TRPCBuiltRouter<{
94
248
  };
95
249
  meta: import("trpc-to-openapi").OpenApiMeta;
96
250
  }>;
251
+ activate: import("@trpc/server").TRPCMutationProcedure<{
252
+ input: {
253
+ authToken: string;
254
+ providerType: "firebase" | "supertokens" | "keycloak" | "oidc";
255
+ };
256
+ output: {
257
+ success: boolean;
258
+ };
259
+ meta: import("trpc-to-openapi").OpenApiMeta;
260
+ }>;
97
261
  addRecoveryEmail: import("@trpc/server").TRPCMutationProcedure<{
98
262
  input: {
99
263
  authToken: string;
@@ -131,9 +295,18 @@ export declare const keysRouter: import("@trpc/server").TRPCBuiltRouter<{
131
295
  input: {
132
296
  authToken: string;
133
297
  providerType: "firebase" | "supertokens" | "keycloak" | "oidc";
134
- emailShare: string;
135
- email?: string | undefined;
136
- useRecoveryEmail?: boolean | undefined;
298
+ relayPayload: {
299
+ version: 1;
300
+ algorithm: "P-256-HKDF-SHA256-AES-256-GCM";
301
+ keyId: string;
302
+ ephemeralPublicKey: string;
303
+ salt: string;
304
+ iv: string;
305
+ ciphertext: string;
306
+ };
307
+ confirmationCode: string;
308
+ email: string;
309
+ shareVersion?: number | undefined;
137
310
  };
138
311
  output: {
139
312
  success: boolean;
@@ -159,6 +332,7 @@ export declare const keysRouter: import("@trpc/server").TRPCBuiltRouter<{
159
332
  input: {
160
333
  authToken: string;
161
334
  providerType: "firebase" | "supertokens" | "keycloak" | "oidc";
335
+ challenge: string;
162
336
  };
163
337
  output: {
164
338
  success: boolean;
@@ -22,7 +22,7 @@ export declare const qrLoginRouter: import("@trpc/server").TRPCBuiltRouter<{
22
22
  output: {
23
23
  sessionId: string;
24
24
  publicKey: string;
25
- status: "approved" | "pending";
25
+ status: "pending" | "approved";
26
26
  encryptedPayload?: string | undefined;
27
27
  approverDid?: string | undefined;
28
28
  };
@@ -0,0 +1,9 @@
1
+ import { type EscrowEnclave } from './types';
2
+ export * from './types';
3
+ export * from './softwareEnclave';
4
+ export * from './notifications';
5
+ export declare const isEscrowEnabled: () => boolean;
6
+ export declare const getEscrowHoldDurationMs: () => number;
7
+ export declare const getEscrowHoldRestartMinAgeMs: () => number;
8
+ export declare const getEscrowEnclave: () => EscrowEnclave;
9
+ export declare const __setEscrowEnclaveForTests: (replacement: EscrowEnclave | undefined) => void;
@@ -0,0 +1,9 @@
1
+ import type { EscrowHold } from '../../models/EscrowHold';
2
+ import type { MongoUserKeyType } from '../../models/UserKey';
3
+ export interface EscrowHoldEvent {
4
+ kind: 'started' | 'cancelled' | 'completed';
5
+ hold: EscrowHold;
6
+ userKey: MongoUserKeyType;
7
+ reason?: 'superseded';
8
+ }
9
+ export declare const notifyEscrowHoldEvent: (event: EscrowHoldEvent) => Promise<void>;
@@ -0,0 +1,17 @@
1
+ import { type EscrowEnclave, type EnclaveAttestation, type VerifyEscrowBlobInput, type VerifyEscrowBlobResult, type ReleaseRequest, type ReleaseResult, type CarryPinVerifierInput } from './types';
2
+ export interface SoftwareEnclaveConfig {
3
+ privateKeys: Record<string, string>;
4
+ activeKeyId: string;
5
+ }
6
+ export declare class SoftwareEnclave implements EscrowEnclave {
7
+ private readonly privateKeys;
8
+ private readonly activeKeyId;
9
+ constructor(config: SoftwareEnclaveConfig);
10
+ getAttestation(): Promise<EnclaveAttestation>;
11
+ private decrypt;
12
+ verifyEscrowBlob(input: VerifyEscrowBlobInput): Promise<VerifyEscrowBlobResult>;
13
+ carryPinVerifier(input: CarryPinVerifierInput): Promise<{
14
+ envelope: import("@learncard/sss-key-manager").EscrowEnvelope;
15
+ }>;
16
+ releaseEscrow(input: ReleaseRequest): Promise<ReleaseResult>;
17
+ }
@@ -0,0 +1,67 @@
1
+ import type { EscrowEnvelope } from '@learncard/sss-key-manager';
2
+ import type { EscrowHold } from '../../models/EscrowHold';
3
+ export interface EnclaveAttestation {
4
+ mode: 'software' | 'nitro';
5
+ keyId: string;
6
+ publicKey: string;
7
+ measurements: {
8
+ imageSha384?: string;
9
+ pcr0?: string;
10
+ pcr1?: string;
11
+ pcr2?: string;
12
+ };
13
+ document: string;
14
+ issuedAt: string;
15
+ }
16
+ export type EscrowHoldForEnclave = Pick<EscrowHold, '_id' | 'status' | 'releaseAfter' | 'releasePolicy' | 'primaryDid' | 'shareVersion' | 'clientEphemeralPublicKey'>;
17
+ export interface VerifyEscrowBlobInput {
18
+ envelope: EscrowEnvelope;
19
+ expectedDid: string;
20
+ expectedShareVersion: number;
21
+ }
22
+ export type VerifyEscrowBlobResult = {
23
+ ok: true;
24
+ hasPin: boolean;
25
+ } | {
26
+ ok: false;
27
+ hasPin: boolean;
28
+ reason: string;
29
+ };
30
+ export interface ReleaseRequest {
31
+ envelope: EscrowEnvelope;
32
+ hold: EscrowHoldForEnclave;
33
+ clientEphemeralPublicKey: string;
34
+ expectedDid: string;
35
+ now?: Date;
36
+ pinProof?: string;
37
+ }
38
+ export interface ReleaseResult {
39
+ sealed: EscrowEnvelope;
40
+ }
41
+ export interface CarryPinVerifierInput {
42
+ sourceEnvelope: EscrowEnvelope;
43
+ targetEnvelope: EscrowEnvelope;
44
+ expectedDid: string;
45
+ sourceShareVersion: number;
46
+ targetShareVersion: number;
47
+ }
48
+ export interface EscrowEnclave {
49
+ getAttestation(): Promise<EnclaveAttestation>;
50
+ verifyEscrowBlob(input: VerifyEscrowBlobInput): Promise<VerifyEscrowBlobResult>;
51
+ carryPinVerifier(input: CarryPinVerifierInput): Promise<{
52
+ envelope: EscrowEnvelope;
53
+ }>;
54
+ releaseEscrow(input: ReleaseRequest): Promise<ReleaseResult>;
55
+ }
56
+ export declare class EscrowPolicyError extends Error {
57
+ constructor();
58
+ }
59
+ export declare class EscrowPinMismatchError extends Error {
60
+ constructor();
61
+ }
62
+ export declare class EscrowBlobError extends Error {
63
+ constructor();
64
+ }
65
+ export declare class EscrowUnavailableError extends Error {
66
+ constructor();
67
+ }
@@ -0,0 +1,31 @@
1
+ export interface SigningAuthoritySeedIdentity {
2
+ _id: string;
3
+ ownerDid: string;
4
+ name: string;
5
+ did?: string;
6
+ }
7
+ export interface EncryptedSigningAuthoritySeed {
8
+ encryptedSeed: string;
9
+ encryptedDek: string;
10
+ keyVersion: 'kms-v1' | 'local-v1';
11
+ }
12
+ export interface SigningAuthoritySeedRecord {
13
+ _id?: unknown;
14
+ ownerDid: string;
15
+ name: string;
16
+ did?: string;
17
+ seed?: string;
18
+ encryptedSeed?: unknown;
19
+ encryptedDek?: unknown;
20
+ keyVersion?: unknown;
21
+ }
22
+ export interface SeedEncryptionConfig {
23
+ kmsKeyArn?: string;
24
+ localKek?: string;
25
+ allowLocal: boolean;
26
+ }
27
+ export interface SeedEncryption {
28
+ encrypt: (seed: string, identity: SigningAuthoritySeedIdentity) => Promise<EncryptedSigningAuthoritySeed>;
29
+ decrypt: (envelope: EncryptedSigningAuthoritySeed, identity: SigningAuthoritySeedIdentity) => Promise<string>;
30
+ }
31
+ export type SeedEncryptionFailure = 'invalid_record' | 'invalid_envelope' | 'configuration' | 'legacy_disabled' | 'authentication_failed' | 'kms_access_denied' | 'kms_unavailable' | 'kms_failed';
@@ -0,0 +1,49 @@
1
+ import type { Document, ObjectId } from 'mongodb';
2
+ import type { EncryptedSigningAuthoritySeed } from './seed-encryption';
3
+ export type SeedMigrationPhase = 'dry-run' | 'prepare' | 'verify' | 'purge';
4
+ export interface SeedMigrationRequest {
5
+ phase: SeedMigrationPhase;
6
+ batchSize?: number;
7
+ }
8
+ export interface SeedMigrationCounts {
9
+ total: number;
10
+ encrypted: number;
11
+ legacyOnly: number;
12
+ malformed: number;
13
+ plaintextRemaining: number;
14
+ }
15
+ export interface SeedMigrationResult {
16
+ phase: SeedMigrationPhase;
17
+ done: boolean;
18
+ processed: number;
19
+ counts: SeedMigrationCounts;
20
+ countsReconciled: boolean;
21
+ rescanRequired?: boolean;
22
+ }
23
+ export interface MigrationSigningAuthority extends Document {
24
+ _id: string | ObjectId;
25
+ ownerDid: string;
26
+ name: string;
27
+ did?: string;
28
+ seed?: string;
29
+ }
30
+ export interface SeedMigrationState {
31
+ _id: string;
32
+ leaseOwner?: string;
33
+ leaseExpiresAt?: Date;
34
+ phase?: SeedMigrationPhase;
35
+ epoch?: string;
36
+ verifiedEpoch?: string;
37
+ status?: 'running' | 'complete' | 'failed';
38
+ processed?: number;
39
+ counts?: SeedMigrationCounts;
40
+ updatedAt?: Date;
41
+ cursor?: string | ObjectId;
42
+ }
43
+ export interface SeedMigrationReceipt extends EncryptedSigningAuthoritySeed {
44
+ _id: string | ObjectId;
45
+ epoch: string;
46
+ ownerDid: string;
47
+ name: string;
48
+ did: string | null;
49
+ }