@sebastienrousseau/crypto-server 0.0.8 → 0.0.24
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/README.md +159 -56
- package/dist/@types/types.d.ts +1 -2
- package/dist/@types/types.d.ts.map +1 -1
- package/dist/config/auth-policy.d.ts +18 -0
- package/dist/config/auth-policy.d.ts.map +1 -1
- package/dist/config/auth-policy.js +126 -0
- package/dist/config/constants.d.ts.map +1 -1
- package/dist/config/constants.js +7 -1
- package/dist/enterprise/metering.d.ts.map +1 -1
- package/dist/enterprise/metering.js +2 -4
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/lib/auth.d.ts +4 -1
- package/dist/lib/auth.d.ts.map +1 -1
- package/dist/lib/auth.js +87 -40
- package/dist/lib/kdf-runner.d.ts +18 -0
- package/dist/lib/kdf-runner.d.ts.map +1 -0
- package/dist/lib/kdf-runner.js +27 -0
- package/dist/lib/key-store.d.ts +37 -0
- package/dist/lib/key-store.d.ts.map +1 -0
- package/dist/lib/key-store.js +223 -0
- package/dist/lib/opaque-store.d.ts +34 -0
- package/dist/lib/opaque-store.d.ts.map +1 -0
- package/dist/lib/opaque-store.js +72 -0
- package/dist/lib/problem.d.ts +67 -0
- package/dist/lib/problem.d.ts.map +1 -0
- package/dist/lib/problem.js +162 -0
- package/dist/routes/v1/decrypt.d.ts.map +1 -1
- package/dist/routes/v1/decrypt.js +24 -16
- package/dist/routes/v1/encrypt.d.ts.map +1 -1
- package/dist/routes/v1/encrypt.js +12 -13
- package/dist/routes/v1/generate.d.ts.map +1 -1
- package/dist/routes/v1/generate.js +22 -25
- package/dist/routes/v1/revoke.d.ts.map +1 -1
- package/dist/routes/v1/revoke.js +6 -11
- package/dist/routes/v1/verify.d.ts.map +1 -1
- package/dist/routes/v1/verify.js +10 -12
- package/dist/routes/v2/algorithms.js +1 -1
- package/dist/routes/v2/compliance.js +1 -1
- package/dist/routes/v2/encrypt.d.ts.map +1 -1
- package/dist/routes/v2/encrypt.js +1 -1
- package/dist/routes/v2/hash.d.ts.map +1 -1
- package/dist/routes/v2/hash.js +1 -1
- package/dist/routes/v2/hpke.d.ts +3 -0
- package/dist/routes/v2/hpke.d.ts.map +1 -0
- package/dist/routes/v2/hpke.js +168 -0
- package/dist/routes/v2/index.d.ts.map +1 -1
- package/dist/routes/v2/index.js +6 -0
- package/dist/routes/v2/kdf.d.ts +5 -0
- package/dist/routes/v2/kdf.d.ts.map +1 -1
- package/dist/routes/v2/kdf.js +27 -12
- package/dist/routes/v2/key-wrap.d.ts.map +1 -1
- package/dist/routes/v2/key-wrap.js +9 -3
- package/dist/routes/v2/keys.d.ts.map +1 -1
- package/dist/routes/v2/keys.js +39 -26
- package/dist/routes/v2/kms.d.ts +7 -0
- package/dist/routes/v2/kms.d.ts.map +1 -0
- package/dist/routes/v2/kms.js +310 -0
- package/dist/routes/v2/mac.d.ts.map +1 -1
- package/dist/routes/v2/mac.js +10 -3
- package/dist/routes/v2/multi-recipient.d.ts.map +1 -1
- package/dist/routes/v2/multi-recipient.js +12 -11
- package/dist/routes/v2/opaque.d.ts +4 -0
- package/dist/routes/v2/opaque.d.ts.map +1 -0
- package/dist/routes/v2/opaque.js +174 -0
- package/dist/routes/v2/password-encrypt.d.ts.map +1 -1
- package/dist/routes/v2/password-encrypt.js +13 -46
- package/dist/routes/v2/password.d.ts +4 -0
- package/dist/routes/v2/password.d.ts.map +1 -1
- package/dist/routes/v2/password.js +28 -53
- package/dist/routes/v2/pq-hash-sign.d.ts.map +1 -1
- package/dist/routes/v2/pq-hash-sign.js +33 -24
- package/dist/routes/v2/pq-sign.d.ts.map +1 -1
- package/dist/routes/v2/pq-sign.js +31 -24
- package/dist/routes/v2/pq.d.ts.map +1 -1
- package/dist/routes/v2/pq.js +57 -38
- package/dist/routes/v2/sealedbox.d.ts.map +1 -1
- package/dist/routes/v2/sealedbox.js +32 -14
- package/dist/routes/v2/secretbox.d.ts.map +1 -1
- package/dist/routes/v2/secretbox.js +10 -3
- package/dist/routes/v2/signing.d.ts.map +1 -1
- package/dist/routes/v2/signing.js +17 -13
- package/dist/routes/v2/stream-schemas.d.ts +296 -0
- package/dist/routes/v2/stream-schemas.d.ts.map +1 -0
- package/dist/routes/v2/stream-schemas.js +207 -0
- package/dist/routes/v2/stream.d.ts.map +1 -1
- package/dist/routes/v2/stream.js +144 -151
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +19 -5
- package/dist/utils/keys.d.ts +17 -0
- package/dist/utils/keys.d.ts.map +1 -0
- package/dist/utils/keys.js +36 -0
- package/dist/utils/route-helpers.d.ts +1 -5
- package/dist/utils/route-helpers.d.ts.map +1 -1
- package/dist/utils/route-helpers.js +16 -10
- package/dist/utils/trace.d.ts +12 -0
- package/dist/utils/trace.d.ts.map +1 -0
- package/dist/utils/trace.js +62 -0
- package/dist/utils/validation.d.ts +1 -0
- package/dist/utils/validation.d.ts.map +1 -1
- package/dist/utils/validation.js +17 -9
- package/package.json +22 -18
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.registerKmsProvider = registerKmsProvider;
|
|
4
|
+
exports.resetKmsProviders = resetKmsProviders;
|
|
5
|
+
exports.resolveKmsProvider = resolveKmsProvider;
|
|
6
|
+
exports.default = kmsRoute;
|
|
7
|
+
const crypto_kms_1 = require("@sebastienrousseau/crypto-kms");
|
|
8
|
+
const route_helpers_1 = require("../../utils/route-helpers");
|
|
9
|
+
const providerRegistry = new Map();
|
|
10
|
+
providerRegistry.set("local", new crypto_kms_1.LocalKmsProvider());
|
|
11
|
+
function registerKmsProvider(name, provider) {
|
|
12
|
+
providerRegistry.set(name.toLowerCase(), provider);
|
|
13
|
+
}
|
|
14
|
+
function resetKmsProviders() {
|
|
15
|
+
providerRegistry.clear();
|
|
16
|
+
providerRegistry.set("local", new crypto_kms_1.LocalKmsProvider());
|
|
17
|
+
}
|
|
18
|
+
function createAwsProvider() {
|
|
19
|
+
return new crypto_kms_1.AwsKmsProvider({
|
|
20
|
+
region: process.env["AWS_REGION"] ?? "us-east-1",
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
function createGcpProvider() {
|
|
24
|
+
return new crypto_kms_1.GcpKmsProvider({
|
|
25
|
+
projectId: process.env["GCP_PROJECT_ID"] ?? "test-project",
|
|
26
|
+
locationId: process.env["GCP_LOCATION_ID"] ?? "global",
|
|
27
|
+
keyRingId: process.env["GCP_KEY_RING_ID"] ?? "test-ring",
|
|
28
|
+
token: process.env["GCP_AUTH_TOKEN"] ?? "test-token",
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
function createVaultProvider() {
|
|
32
|
+
return new crypto_kms_1.VaultKmsProvider({
|
|
33
|
+
address: process.env["VAULT_ADDR"] ?? "http://localhost:8200",
|
|
34
|
+
token: process.env["VAULT_TOKEN"] ?? "test-token",
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
function createAzureProvider() {
|
|
38
|
+
return new crypto_kms_1.AzureKmsProvider({
|
|
39
|
+
vaultUrl: process.env["AZURE_VAULT_URL"] ?? "https://my-vault.vault.azure.net",
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
function createDefaultProvider(key, name) {
|
|
43
|
+
switch (key) {
|
|
44
|
+
case "aws":
|
|
45
|
+
return createAwsProvider();
|
|
46
|
+
case "gcp":
|
|
47
|
+
return createGcpProvider();
|
|
48
|
+
case "vault":
|
|
49
|
+
return createVaultProvider();
|
|
50
|
+
case "azure":
|
|
51
|
+
return createAzureProvider();
|
|
52
|
+
default:
|
|
53
|
+
throw new Error(`Unsupported KMS provider: ${name}`);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
function resolveKmsProvider(name) {
|
|
57
|
+
const key = (name ?? "local").toLowerCase();
|
|
58
|
+
const existing = providerRegistry.get(key);
|
|
59
|
+
if (existing)
|
|
60
|
+
return existing;
|
|
61
|
+
const provider = createDefaultProvider(key, name);
|
|
62
|
+
providerRegistry.set(key, provider);
|
|
63
|
+
return provider;
|
|
64
|
+
}
|
|
65
|
+
function parseBytes(text) {
|
|
66
|
+
const isHex = /^[0-9a-fA-F]*$/.test(text) && text.length % 2 === 0;
|
|
67
|
+
return isHex
|
|
68
|
+
? new Uint8Array(Buffer.from(text, "hex"))
|
|
69
|
+
: new Uint8Array(Buffer.from(text, "utf8"));
|
|
70
|
+
}
|
|
71
|
+
function sanitizeKeyId(id) {
|
|
72
|
+
if (typeof id !== "string" ||
|
|
73
|
+
!/^[a-zA-Z0-9_\-/:.]+$/.test(id) ||
|
|
74
|
+
id.includes("..")) {
|
|
75
|
+
throw new Error("Invalid keyId format");
|
|
76
|
+
}
|
|
77
|
+
return encodeURIComponent(id).replace(/%2F/g, "/").replace(/%3A/g, ":");
|
|
78
|
+
}
|
|
79
|
+
const CREATE_KEY_SCHEMA = {
|
|
80
|
+
tags: ["KMS"],
|
|
81
|
+
summary: "Create a managed key in the designated KMS provider",
|
|
82
|
+
body: {
|
|
83
|
+
type: "object",
|
|
84
|
+
additionalProperties: false,
|
|
85
|
+
properties: {
|
|
86
|
+
provider: { type: "string" },
|
|
87
|
+
algorithm: { type: "string" },
|
|
88
|
+
usage: { type: "string", enum: ["encrypt", "sign", "wrap"] },
|
|
89
|
+
metadata: { type: "object", additionalProperties: { type: "string" } },
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
};
|
|
93
|
+
const WRAP_SCHEMA = {
|
|
94
|
+
tags: ["KMS"],
|
|
95
|
+
summary: "Wrap an existing key using a managed KMS key",
|
|
96
|
+
body: {
|
|
97
|
+
type: "object",
|
|
98
|
+
required: ["keyId", "unwrappedKey"],
|
|
99
|
+
additionalProperties: false,
|
|
100
|
+
properties: {
|
|
101
|
+
keyId: { type: "string" },
|
|
102
|
+
unwrappedKey: { type: "string" },
|
|
103
|
+
provider: { type: "string" },
|
|
104
|
+
context: { type: "object", additionalProperties: { type: "string" } },
|
|
105
|
+
},
|
|
106
|
+
},
|
|
107
|
+
};
|
|
108
|
+
const UNWRAP_SCHEMA = {
|
|
109
|
+
tags: ["KMS"],
|
|
110
|
+
summary: "Unwrap a wrapped key using a managed KMS key",
|
|
111
|
+
body: {
|
|
112
|
+
type: "object",
|
|
113
|
+
required: ["keyId", "wrappedKey"],
|
|
114
|
+
additionalProperties: false,
|
|
115
|
+
properties: {
|
|
116
|
+
keyId: { type: "string" },
|
|
117
|
+
wrappedKey: { type: "string" },
|
|
118
|
+
provider: { type: "string" },
|
|
119
|
+
context: { type: "object", additionalProperties: { type: "string" } },
|
|
120
|
+
},
|
|
121
|
+
},
|
|
122
|
+
};
|
|
123
|
+
const DATA_KEY_SCHEMA = {
|
|
124
|
+
tags: ["KMS"],
|
|
125
|
+
summary: "Generate a Data Encryption Key (DEK) via KMS provider",
|
|
126
|
+
body: {
|
|
127
|
+
type: "object",
|
|
128
|
+
required: ["keyId"],
|
|
129
|
+
additionalProperties: false,
|
|
130
|
+
properties: {
|
|
131
|
+
keyId: { type: "string" },
|
|
132
|
+
provider: { type: "string" },
|
|
133
|
+
keySpec: { type: "string" },
|
|
134
|
+
},
|
|
135
|
+
},
|
|
136
|
+
};
|
|
137
|
+
const ENCRYPT_SCHEMA = {
|
|
138
|
+
tags: ["KMS"],
|
|
139
|
+
summary: "Encrypt plaintext with a managed KMS key",
|
|
140
|
+
body: {
|
|
141
|
+
type: "object",
|
|
142
|
+
required: ["keyId", "plaintext"],
|
|
143
|
+
additionalProperties: false,
|
|
144
|
+
properties: {
|
|
145
|
+
keyId: { type: "string" },
|
|
146
|
+
plaintext: { type: "string" },
|
|
147
|
+
provider: { type: "string" },
|
|
148
|
+
context: { type: "object", additionalProperties: { type: "string" } },
|
|
149
|
+
},
|
|
150
|
+
},
|
|
151
|
+
};
|
|
152
|
+
const DECRYPT_SCHEMA = {
|
|
153
|
+
tags: ["KMS"],
|
|
154
|
+
summary: "Decrypt ciphertext with a managed KMS key",
|
|
155
|
+
body: {
|
|
156
|
+
type: "object",
|
|
157
|
+
required: ["keyId", "ciphertext"],
|
|
158
|
+
additionalProperties: false,
|
|
159
|
+
properties: {
|
|
160
|
+
keyId: { type: "string" },
|
|
161
|
+
ciphertext: { type: "string" },
|
|
162
|
+
provider: { type: "string" },
|
|
163
|
+
context: { type: "object", additionalProperties: { type: "string" } },
|
|
164
|
+
},
|
|
165
|
+
},
|
|
166
|
+
};
|
|
167
|
+
function registerCreateKey(app) {
|
|
168
|
+
app.post("/v2/kms/create-key", {
|
|
169
|
+
config: { rateLimit: { max: 100, timeWindow: "1 minute" } },
|
|
170
|
+
schema: CREATE_KEY_SCHEMA,
|
|
171
|
+
}, async (request, reply) => {
|
|
172
|
+
const b = request.body;
|
|
173
|
+
try {
|
|
174
|
+
const provider = resolveKmsProvider(b["provider"]);
|
|
175
|
+
const meta = await provider.createKey(b["algorithm"] ?? "aes-256-gcm", b["usage"] ?? "encrypt", b["metadata"]);
|
|
176
|
+
return reply.send({ data: meta });
|
|
177
|
+
}
|
|
178
|
+
catch (err) {
|
|
179
|
+
return (0, route_helpers_1.classifyCryptoError)(err, request, reply, "KMS CreateKey");
|
|
180
|
+
}
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
function registerWrap(app) {
|
|
184
|
+
app.post("/v2/kms/wrap", {
|
|
185
|
+
config: { rateLimit: { max: 100, timeWindow: "1 minute" } },
|
|
186
|
+
schema: WRAP_SCHEMA,
|
|
187
|
+
}, async (request, reply) => {
|
|
188
|
+
const b = request.body;
|
|
189
|
+
try {
|
|
190
|
+
const provider = resolveKmsProvider(b["provider"]);
|
|
191
|
+
if (!provider.wrapKey) {
|
|
192
|
+
throw new Error(`Provider ${provider.name} does not support wrapKey`);
|
|
193
|
+
}
|
|
194
|
+
const keyId = sanitizeKeyId(b["keyId"]);
|
|
195
|
+
const bytes = parseBytes(b["unwrappedKey"]);
|
|
196
|
+
const res = await provider.wrapKey(keyId, bytes, b["context"]);
|
|
197
|
+
return reply.send({
|
|
198
|
+
data: { ...res, provider: provider.name },
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
catch (err) {
|
|
202
|
+
return (0, route_helpers_1.classifyCryptoError)(err, request, reply, "KMS Wrap");
|
|
203
|
+
}
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
function registerUnwrap(app) {
|
|
207
|
+
app.post("/v2/kms/unwrap", {
|
|
208
|
+
config: { rateLimit: { max: 100, timeWindow: "1 minute" } },
|
|
209
|
+
schema: UNWRAP_SCHEMA,
|
|
210
|
+
}, async (request, reply) => {
|
|
211
|
+
const b = request.body;
|
|
212
|
+
try {
|
|
213
|
+
const provider = resolveKmsProvider(b["provider"]);
|
|
214
|
+
if (!provider.unwrapKey) {
|
|
215
|
+
throw new Error(`Provider ${provider.name} does not support unwrapKey`);
|
|
216
|
+
}
|
|
217
|
+
const keyId = sanitizeKeyId(b["keyId"]);
|
|
218
|
+
const res = await provider.unwrapKey(keyId, b["wrappedKey"], b["context"]);
|
|
219
|
+
return reply.send({
|
|
220
|
+
data: {
|
|
221
|
+
unwrappedKey: Buffer.from(res.unwrappedKey).toString("hex"),
|
|
222
|
+
keyId: res.keyId,
|
|
223
|
+
provider: provider.name,
|
|
224
|
+
},
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
catch (err) {
|
|
228
|
+
return (0, route_helpers_1.classifyCryptoError)(err, request, reply, "KMS Unwrap");
|
|
229
|
+
}
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
function registerDataKey(app) {
|
|
233
|
+
app.post("/v2/kms/generate-data-key", {
|
|
234
|
+
config: { rateLimit: { max: 100, timeWindow: "1 minute" } },
|
|
235
|
+
schema: DATA_KEY_SCHEMA,
|
|
236
|
+
}, async (request, reply) => {
|
|
237
|
+
const b = request.body;
|
|
238
|
+
try {
|
|
239
|
+
const provider = resolveKmsProvider(b["provider"]);
|
|
240
|
+
const keyId = sanitizeKeyId(b["keyId"]);
|
|
241
|
+
const res = await provider.generateDataKey(keyId, b["keySpec"]);
|
|
242
|
+
return reply.send({
|
|
243
|
+
data: {
|
|
244
|
+
plaintext: Buffer.from(res.plaintext).toString("hex"),
|
|
245
|
+
ciphertext: res.ciphertext,
|
|
246
|
+
keyId,
|
|
247
|
+
provider: provider.name,
|
|
248
|
+
},
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
catch (err) {
|
|
252
|
+
return (0, route_helpers_1.classifyCryptoError)(err, request, reply, "KMS GenerateDataKey");
|
|
253
|
+
}
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
function registerEncrypt(app) {
|
|
257
|
+
app.post("/v2/kms/encrypt", {
|
|
258
|
+
config: { rateLimit: { max: 100, timeWindow: "1 minute" } },
|
|
259
|
+
schema: ENCRYPT_SCHEMA,
|
|
260
|
+
}, async (request, reply) => {
|
|
261
|
+
const b = request.body;
|
|
262
|
+
try {
|
|
263
|
+
const provider = resolveKmsProvider(b["provider"]);
|
|
264
|
+
const keyId = sanitizeKeyId(b["keyId"]);
|
|
265
|
+
const bytes = parseBytes(b["plaintext"]);
|
|
266
|
+
const res = await provider.encrypt(keyId, bytes, b["context"]);
|
|
267
|
+
return reply.send({
|
|
268
|
+
data: { ...res, provider: provider.name },
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
catch (err) {
|
|
272
|
+
return (0, route_helpers_1.classifyCryptoError)(err, request, reply, "KMS Encrypt");
|
|
273
|
+
}
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
function registerDecrypt(app) {
|
|
277
|
+
app.post("/v2/kms/decrypt", {
|
|
278
|
+
config: { rateLimit: { max: 100, timeWindow: "1 minute" } },
|
|
279
|
+
schema: DECRYPT_SCHEMA,
|
|
280
|
+
}, async (request, reply) => {
|
|
281
|
+
const b = request.body;
|
|
282
|
+
try {
|
|
283
|
+
const provider = resolveKmsProvider(b["provider"]);
|
|
284
|
+
const keyId = sanitizeKeyId(b["keyId"]);
|
|
285
|
+
const res = await provider.decrypt(keyId, b["ciphertext"], b["context"]);
|
|
286
|
+
const utf8 = Buffer.from(res.plaintext).toString("utf8");
|
|
287
|
+
const hex = Buffer.from(res.plaintext).toString("hex");
|
|
288
|
+
return reply.send({
|
|
289
|
+
data: {
|
|
290
|
+
plaintext: utf8,
|
|
291
|
+
hex,
|
|
292
|
+
keyId: res.keyId,
|
|
293
|
+
provider: provider.name,
|
|
294
|
+
},
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
catch (err) {
|
|
298
|
+
return (0, route_helpers_1.classifyCryptoError)(err, request, reply, "KMS Decrypt");
|
|
299
|
+
}
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
function kmsRoute(app) {
|
|
303
|
+
registerCreateKey(app);
|
|
304
|
+
registerWrap(app);
|
|
305
|
+
registerUnwrap(app);
|
|
306
|
+
registerDataKey(app);
|
|
307
|
+
registerEncrypt(app);
|
|
308
|
+
registerDecrypt(app);
|
|
309
|
+
}
|
|
310
|
+
//# sourceMappingURL=kms.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mac.d.ts","sourceRoot":"","sources":["../../../src/routes/v2/mac.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"mac.d.ts","sourceRoot":"","sources":["../../../src/routes/v2/mac.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;yBAgG/B,KAAK,eAAe,KAAG,IAAI;AAA3C,wBAGE"}
|
package/dist/routes/v2/mac.js
CHANGED
|
@@ -35,7 +35,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
const route_helpers_1 = require("../../utils/route-helpers");
|
|
37
37
|
const HMAC_ALGORITHMS = ["sha256", "sha384", "sha512", "sha3-256", "sha3-512"];
|
|
38
|
-
|
|
38
|
+
function registerHmac(app) {
|
|
39
39
|
app.post("/v2/hmac", {
|
|
40
40
|
schema: {
|
|
41
41
|
tags: ["MAC"],
|
|
@@ -53,7 +53,7 @@ exports.default = (app) => {
|
|
|
53
53
|
},
|
|
54
54
|
}, async (request, reply) => {
|
|
55
55
|
try {
|
|
56
|
-
const { computeHmac } = await Promise.resolve().then(() => __importStar(require("@sebastienrousseau/crypto-lib/
|
|
56
|
+
const { computeHmac } = await Promise.resolve().then(() => __importStar(require("@sebastienrousseau/crypto-lib/modern")));
|
|
57
57
|
const body = request.body;
|
|
58
58
|
const result = computeHmac({
|
|
59
59
|
algorithm: body.algorithm,
|
|
@@ -66,6 +66,8 @@ exports.default = (app) => {
|
|
|
66
66
|
return (0, route_helpers_1.classifyCryptoError)(error, request, reply, "HMAC computation");
|
|
67
67
|
}
|
|
68
68
|
});
|
|
69
|
+
}
|
|
70
|
+
function registerHmacVerify(app) {
|
|
69
71
|
app.post("/v2/hmac/verify", {
|
|
70
72
|
schema: {
|
|
71
73
|
tags: ["MAC"],
|
|
@@ -82,9 +84,10 @@ exports.default = (app) => {
|
|
|
82
84
|
},
|
|
83
85
|
},
|
|
84
86
|
},
|
|
87
|
+
config: { rateLimit: { max: 100, timeWindow: "1 minute" } },
|
|
85
88
|
}, async (request, reply) => {
|
|
86
89
|
try {
|
|
87
|
-
const { verifyHmac } = await Promise.resolve().then(() => __importStar(require("@sebastienrousseau/crypto-lib/
|
|
90
|
+
const { verifyHmac } = await Promise.resolve().then(() => __importStar(require("@sebastienrousseau/crypto-lib/modern")));
|
|
88
91
|
const body = request.body;
|
|
89
92
|
const result = verifyHmac({
|
|
90
93
|
algorithm: body.algorithm,
|
|
@@ -98,5 +101,9 @@ exports.default = (app) => {
|
|
|
98
101
|
return (0, route_helpers_1.classifyCryptoError)(error, request, reply, "HMAC verification");
|
|
99
102
|
}
|
|
100
103
|
});
|
|
104
|
+
}
|
|
105
|
+
exports.default = (app) => {
|
|
106
|
+
registerHmac(app);
|
|
107
|
+
registerHmacVerify(app);
|
|
101
108
|
};
|
|
102
109
|
//# sourceMappingURL=mac.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"multi-recipient.d.ts","sourceRoot":"","sources":["../../../src/routes/v2/multi-recipient.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"multi-recipient.d.ts","sourceRoot":"","sources":["../../../src/routes/v2/multi-recipient.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;yBAuB/B,KAAK,eAAe,KAAG,IAAI;AAA3C,wBAiDE"}
|
|
@@ -34,6 +34,16 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
const route_helpers_1 = require("../../utils/route-helpers");
|
|
37
|
+
function toLibRecipient(r) {
|
|
38
|
+
if (r.type === "classical") {
|
|
39
|
+
return { type: "x25519", publicKey: r.publicKey };
|
|
40
|
+
}
|
|
41
|
+
return {
|
|
42
|
+
type: "x25519-ml-kem-768",
|
|
43
|
+
x25519PublicKey: r.publicKey,
|
|
44
|
+
mlKemPublicKey: r.mlKemPublicKey,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
37
47
|
exports.default = (app) => {
|
|
38
48
|
app.post("/v2/multi-recipient/encrypt", {
|
|
39
49
|
schema: {
|
|
@@ -66,18 +76,9 @@ exports.default = (app) => {
|
|
|
66
76
|
},
|
|
67
77
|
}, async (request, reply) => {
|
|
68
78
|
try {
|
|
69
|
-
const { multiEncrypt } = await Promise.resolve().then(() => __importStar(require("@sebastienrousseau/crypto-lib/
|
|
79
|
+
const { multiEncrypt } = await Promise.resolve().then(() => __importStar(require("@sebastienrousseau/crypto-lib/high-level")));
|
|
70
80
|
const { plaintext, recipients } = request.body;
|
|
71
|
-
const libRecipients = recipients.map(
|
|
72
|
-
if (r.type === "classical") {
|
|
73
|
-
return { type: "x25519", publicKey: r.publicKey };
|
|
74
|
-
}
|
|
75
|
-
return {
|
|
76
|
-
type: "x25519-ml-kem-768",
|
|
77
|
-
x25519PublicKey: r.publicKey,
|
|
78
|
-
mlKemPublicKey: r.mlKemPublicKey,
|
|
79
|
-
};
|
|
80
|
-
});
|
|
81
|
+
const libRecipients = recipients.map(toLibRecipient);
|
|
81
82
|
return reply.send({ data: multiEncrypt(libRecipients, plaintext) });
|
|
82
83
|
}
|
|
83
84
|
catch (error) {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"opaque.d.ts","sourceRoot":"","sources":["../../../src/routes/v2/opaque.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;yBAoQ/B,KAAK,eAAe,KAAG,IAAI;AAA3C,wBAKE"}
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const crypto_lib_1 = require("@sebastienrousseau/crypto-lib");
|
|
4
|
+
const problem_1 = require("../../lib/problem");
|
|
5
|
+
const route_helpers_1 = require("../../utils/route-helpers");
|
|
6
|
+
const { pake } = crypto_lib_1.protocols;
|
|
7
|
+
function parseHex(hex) {
|
|
8
|
+
if (hex.length % 2 !== 0 || !/^[0-9a-fA-F]+$/.test(hex)) {
|
|
9
|
+
throw new crypto_lib_1.CryptoError("Invalid hex string", "INVALID_HEX");
|
|
10
|
+
}
|
|
11
|
+
return new Uint8Array(Buffer.from(hex, "hex"));
|
|
12
|
+
}
|
|
13
|
+
function toHex(bytes) {
|
|
14
|
+
return Buffer.from(bytes).toString("hex");
|
|
15
|
+
}
|
|
16
|
+
const OPAQUE_RATE_LIMIT = Object.freeze({
|
|
17
|
+
config: { rateLimit: { max: 100, timeWindow: "1 minute" } },
|
|
18
|
+
});
|
|
19
|
+
const SUPPORTED_SUITES = ["P256-SHA256", "ristretto255-SHA512"];
|
|
20
|
+
const REGISTER_INIT_SCHEMA = {
|
|
21
|
+
tags: ["OPAQUE"],
|
|
22
|
+
summary: "Initiate OPAQUE credential registration",
|
|
23
|
+
description: "Evaluates the client's blinded registration request using the server's OPRF seed.",
|
|
24
|
+
body: {
|
|
25
|
+
type: "object",
|
|
26
|
+
required: ["credentialIdentifier", "request"],
|
|
27
|
+
additionalProperties: false,
|
|
28
|
+
properties: {
|
|
29
|
+
credentialIdentifier: { type: "string", minLength: 1, maxLength: 256 },
|
|
30
|
+
request: { type: "string", minLength: 2, pattern: "^[0-9a-fA-F]+$" },
|
|
31
|
+
suite: { type: "string", enum: SUPPORTED_SUITES },
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
const REGISTER_FINISH_SCHEMA = {
|
|
36
|
+
tags: ["OPAQUE"],
|
|
37
|
+
summary: "Finish OPAQUE credential registration",
|
|
38
|
+
description: "Stores the client's registration record under their credential identifier.",
|
|
39
|
+
body: {
|
|
40
|
+
type: "object",
|
|
41
|
+
required: ["credentialIdentifier", "record"],
|
|
42
|
+
additionalProperties: false,
|
|
43
|
+
properties: {
|
|
44
|
+
credentialIdentifier: { type: "string", minLength: 1, maxLength: 256 },
|
|
45
|
+
record: { type: "string", minLength: 2, pattern: "^[0-9a-fA-F]+$" },
|
|
46
|
+
suite: { type: "string", enum: SUPPORTED_SUITES },
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
const LOGIN_INIT_SCHEMA = {
|
|
51
|
+
tags: ["OPAQUE"],
|
|
52
|
+
summary: "Initiate OPAQUE login handshake",
|
|
53
|
+
description: "Evaluates client's KE1 and returns KE2 along with a transient login session identifier.",
|
|
54
|
+
body: {
|
|
55
|
+
type: "object",
|
|
56
|
+
required: ["credentialIdentifier", "ke1"],
|
|
57
|
+
additionalProperties: false,
|
|
58
|
+
properties: {
|
|
59
|
+
credentialIdentifier: { type: "string", minLength: 1, maxLength: 256 },
|
|
60
|
+
ke1: { type: "string", minLength: 2, pattern: "^[0-9a-fA-F]+$" },
|
|
61
|
+
suite: { type: "string", enum: SUPPORTED_SUITES },
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
const LOGIN_FINISH_SCHEMA = {
|
|
66
|
+
tags: ["OPAQUE"],
|
|
67
|
+
summary: "Finish OPAQUE login handshake",
|
|
68
|
+
description: "Verifies client's KE3 auth response in constant time and returns the shared session key.",
|
|
69
|
+
body: {
|
|
70
|
+
type: "object",
|
|
71
|
+
required: ["sessionId", "ke3"],
|
|
72
|
+
additionalProperties: false,
|
|
73
|
+
properties: {
|
|
74
|
+
sessionId: { type: "string", minLength: 1, maxLength: 128 },
|
|
75
|
+
ke3: { type: "string", minLength: 2, pattern: "^[0-9a-fA-F]+$" },
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
};
|
|
79
|
+
function registerRegisterInit(app) {
|
|
80
|
+
app.post("/v2/opaque/register/init", { ...OPAQUE_RATE_LIMIT, schema: REGISTER_INIT_SCHEMA }, async (request, reply) => {
|
|
81
|
+
try {
|
|
82
|
+
const body = request.body;
|
|
83
|
+
const suite = body.suite ?? "P256-SHA256";
|
|
84
|
+
const store = request.server.opaqueStore;
|
|
85
|
+
const setup = store.getSetup(suite);
|
|
86
|
+
const reqBytes = parseHex(body.request);
|
|
87
|
+
const regReq = pake.deserializeRegistrationRequest(reqBytes, suite);
|
|
88
|
+
const regRes = pake.createRegistrationResponse(regReq, setup.serverPublicKey, body.credentialIdentifier, setup.oprfSeed, { suite });
|
|
89
|
+
const responseBytes = pake.serializeRegistrationResponse(regRes);
|
|
90
|
+
return reply.send({ data: { response: toHex(responseBytes) } });
|
|
91
|
+
}
|
|
92
|
+
catch (error) {
|
|
93
|
+
return (0, route_helpers_1.classifyCryptoError)(error, request, reply, "OPAQUE registration init");
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
function registerRegisterFinish(app) {
|
|
98
|
+
app.post("/v2/opaque/register/finish", { ...OPAQUE_RATE_LIMIT, schema: REGISTER_FINISH_SCHEMA }, async (request, reply) => {
|
|
99
|
+
try {
|
|
100
|
+
const body = request.body;
|
|
101
|
+
const suite = body.suite ?? "P256-SHA256";
|
|
102
|
+
const store = request.server.opaqueStore;
|
|
103
|
+
if (store.hasRecord(body.credentialIdentifier)) {
|
|
104
|
+
return (0, problem_1.sendProblem)(reply, 409, "conflict", "Credential identifier is already registered", { code: "CREDENTIAL_EXISTS" });
|
|
105
|
+
}
|
|
106
|
+
const recBytes = parseHex(body.record);
|
|
107
|
+
const record = pake.deserializeRegistrationRecord(recBytes, suite);
|
|
108
|
+
store.setRecord(body.credentialIdentifier, record);
|
|
109
|
+
return reply.send({
|
|
110
|
+
data: {
|
|
111
|
+
status: "registered",
|
|
112
|
+
credentialIdentifier: body.credentialIdentifier,
|
|
113
|
+
},
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
catch (error) {
|
|
117
|
+
return (0, route_helpers_1.classifyCryptoError)(error, request, reply, "OPAQUE registration finish");
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
function registerLoginInit(app) {
|
|
122
|
+
app.post("/v2/opaque/login/init", { ...OPAQUE_RATE_LIMIT, schema: LOGIN_INIT_SCHEMA }, async (request, reply) => {
|
|
123
|
+
try {
|
|
124
|
+
const body = request.body;
|
|
125
|
+
const suite = body.suite ?? "P256-SHA256";
|
|
126
|
+
const store = request.server.opaqueStore;
|
|
127
|
+
const setup = store.getSetup(suite);
|
|
128
|
+
const record = store.getRecord(body.credentialIdentifier) ??
|
|
129
|
+
store.getFakeRecord(suite);
|
|
130
|
+
const ke1Bytes = parseHex(body.ke1);
|
|
131
|
+
const ke1 = pake.deserializeKE1(ke1Bytes, suite);
|
|
132
|
+
const { ke2, state } = pake.generateKE2({
|
|
133
|
+
serverPrivateKey: setup.serverPrivateKey,
|
|
134
|
+
serverPublicKey: setup.serverPublicKey,
|
|
135
|
+
oprfSeed: setup.oprfSeed,
|
|
136
|
+
record,
|
|
137
|
+
credentialIdentifier: body.credentialIdentifier,
|
|
138
|
+
ke1,
|
|
139
|
+
}, { suite });
|
|
140
|
+
const sessionId = store.createSession(body.credentialIdentifier, state, suite);
|
|
141
|
+
const ke2Bytes = pake.serializeKE2(ke2);
|
|
142
|
+
return reply.send({ data: { sessionId, ke2: toHex(ke2Bytes) } });
|
|
143
|
+
}
|
|
144
|
+
catch (error) {
|
|
145
|
+
return (0, route_helpers_1.classifyCryptoError)(error, request, reply, "OPAQUE login init");
|
|
146
|
+
}
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
function registerLoginFinish(app) {
|
|
150
|
+
app.post("/v2/opaque/login/finish", { ...OPAQUE_RATE_LIMIT, schema: LOGIN_FINISH_SCHEMA }, async (request, reply) => {
|
|
151
|
+
try {
|
|
152
|
+
const body = request.body;
|
|
153
|
+
const store = request.server.opaqueStore;
|
|
154
|
+
const session = store.consumeSession(body.sessionId);
|
|
155
|
+
if (!session) {
|
|
156
|
+
return (0, problem_1.sendProblem)(reply, 400, "invalid-input", "Invalid or expired login session", { code: "INVALID_SESSION" });
|
|
157
|
+
}
|
|
158
|
+
const ke3Bytes = parseHex(body.ke3);
|
|
159
|
+
const ke3 = pake.deserializeKE3(ke3Bytes, session.suite);
|
|
160
|
+
const sessionKey = pake.serverFinish(session.serverState, ke3);
|
|
161
|
+
return reply.send({ data: { sessionKey: toHex(sessionKey) } });
|
|
162
|
+
}
|
|
163
|
+
catch (error) {
|
|
164
|
+
return (0, route_helpers_1.classifyCryptoError)(error, request, reply, "OPAQUE login finish");
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
exports.default = (app) => {
|
|
169
|
+
registerRegisterInit(app);
|
|
170
|
+
registerRegisterFinish(app);
|
|
171
|
+
registerLoginInit(app);
|
|
172
|
+
registerLoginFinish(app);
|
|
173
|
+
};
|
|
174
|
+
//# sourceMappingURL=opaque.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"password-encrypt.d.ts","sourceRoot":"","sources":["../../../src/routes/v2/password-encrypt.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"password-encrypt.d.ts","sourceRoot":"","sources":["../../../src/routes/v2/password-encrypt.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;yBAoF/B,KAAK,eAAe,KAAG,IAAI;AAA3C,wBAGE"}
|