@sebastienrousseau/crypto-server 0.0.7 → 0.0.9

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 (80) hide show
  1. package/README.md +159 -56
  2. package/dist/@types/types.d.ts +1 -2
  3. package/dist/@types/types.d.ts.map +1 -1
  4. package/dist/config/auth-policy.d.ts +18 -0
  5. package/dist/config/auth-policy.d.ts.map +1 -1
  6. package/dist/config/auth-policy.js +109 -0
  7. package/dist/enterprise/metering.d.ts.map +1 -1
  8. package/dist/enterprise/metering.js +2 -4
  9. package/dist/index.d.ts +1 -0
  10. package/dist/index.js +1 -0
  11. package/dist/lib/auth.d.ts +4 -1
  12. package/dist/lib/auth.d.ts.map +1 -1
  13. package/dist/lib/auth.js +87 -40
  14. package/dist/lib/kdf-runner.d.ts +18 -0
  15. package/dist/lib/kdf-runner.d.ts.map +1 -0
  16. package/dist/lib/kdf-runner.js +27 -0
  17. package/dist/lib/key-store.d.ts +30 -0
  18. package/dist/lib/key-store.d.ts.map +1 -0
  19. package/dist/lib/key-store.js +126 -0
  20. package/dist/lib/problem.d.ts +66 -0
  21. package/dist/lib/problem.d.ts.map +1 -0
  22. package/dist/lib/problem.js +152 -0
  23. package/dist/routes/v1/decrypt.d.ts.map +1 -1
  24. package/dist/routes/v1/decrypt.js +20 -15
  25. package/dist/routes/v1/encrypt.d.ts.map +1 -1
  26. package/dist/routes/v1/encrypt.js +12 -13
  27. package/dist/routes/v1/generate.d.ts.map +1 -1
  28. package/dist/routes/v1/generate.js +22 -25
  29. package/dist/routes/v1/revoke.d.ts.map +1 -1
  30. package/dist/routes/v1/revoke.js +6 -11
  31. package/dist/routes/v1/verify.d.ts.map +1 -1
  32. package/dist/routes/v1/verify.js +6 -11
  33. package/dist/routes/v2/algorithms.js +1 -1
  34. package/dist/routes/v2/compliance.js +1 -1
  35. package/dist/routes/v2/encrypt.d.ts.map +1 -1
  36. package/dist/routes/v2/encrypt.js +1 -1
  37. package/dist/routes/v2/hash.d.ts.map +1 -1
  38. package/dist/routes/v2/hash.js +1 -1
  39. package/dist/routes/v2/kdf.d.ts +5 -0
  40. package/dist/routes/v2/kdf.d.ts.map +1 -1
  41. package/dist/routes/v2/kdf.js +27 -12
  42. package/dist/routes/v2/key-wrap.d.ts.map +1 -1
  43. package/dist/routes/v2/key-wrap.js +9 -3
  44. package/dist/routes/v2/keys.d.ts.map +1 -1
  45. package/dist/routes/v2/keys.js +39 -26
  46. package/dist/routes/v2/mac.d.ts.map +1 -1
  47. package/dist/routes/v2/mac.js +9 -3
  48. package/dist/routes/v2/multi-recipient.d.ts.map +1 -1
  49. package/dist/routes/v2/multi-recipient.js +12 -11
  50. package/dist/routes/v2/password-encrypt.d.ts.map +1 -1
  51. package/dist/routes/v2/password-encrypt.js +13 -46
  52. package/dist/routes/v2/password.d.ts +4 -0
  53. package/dist/routes/v2/password.d.ts.map +1 -1
  54. package/dist/routes/v2/password.js +28 -53
  55. package/dist/routes/v2/pq-hash-sign.d.ts.map +1 -1
  56. package/dist/routes/v2/pq-hash-sign.js +30 -24
  57. package/dist/routes/v2/pq-sign.d.ts.map +1 -1
  58. package/dist/routes/v2/pq-sign.js +30 -24
  59. package/dist/routes/v2/pq.d.ts.map +1 -1
  60. package/dist/routes/v2/pq.js +57 -38
  61. package/dist/routes/v2/sealedbox.d.ts.map +1 -1
  62. package/dist/routes/v2/sealedbox.js +32 -14
  63. package/dist/routes/v2/secretbox.d.ts.map +1 -1
  64. package/dist/routes/v2/secretbox.js +10 -3
  65. package/dist/routes/v2/signing.d.ts.map +1 -1
  66. package/dist/routes/v2/signing.js +16 -13
  67. package/dist/routes/v2/stream.d.ts.map +1 -1
  68. package/dist/routes/v2/stream.js +29 -24
  69. package/dist/server.d.ts.map +1 -1
  70. package/dist/server.js +15 -5
  71. package/dist/utils/keys.d.ts +17 -0
  72. package/dist/utils/keys.d.ts.map +1 -0
  73. package/dist/utils/keys.js +36 -0
  74. package/dist/utils/route-helpers.d.ts +1 -5
  75. package/dist/utils/route-helpers.d.ts.map +1 -1
  76. package/dist/utils/route-helpers.js +16 -10
  77. package/dist/utils/validation.d.ts +1 -0
  78. package/dist/utils/validation.d.ts.map +1 -1
  79. package/dist/utils/validation.js +17 -9
  80. package/package.json +23 -20
@@ -1,24 +1,28 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- const modern_1 = require("@sebastienrousseau/crypto-lib/dist/modern");
3
+ const modern_1 = require("@sebastienrousseau/crypto-lib/modern");
4
4
  const route_helpers_1 = require("../../utils/route-helpers");
5
- exports.default = (app) => {
5
+ const keys_1 = require("../../utils/keys");
6
+ const EMPTY_BODY = {
7
+ type: "object",
8
+ additionalProperties: false,
9
+ properties: {},
10
+ };
11
+ function registerMlKemKeygen(app) {
6
12
  app.post("/v2/pq/keygen", {
7
13
  schema: {
8
14
  tags: ["Post-Quantum"],
9
- summary: "Generate ML-KEM-768 key pair",
10
- description: "Generate a NIST FIPS 203 ML-KEM-768 key pair for quantum-resistant key encapsulation.",
11
- body: { type: "object", additionalProperties: false, properties: {} },
15
+ summary: "Generate a server-held ML-KEM-768 key pair",
16
+ description: "Generates a NIST FIPS 203 ML-KEM-768 key pair, keeps the secret key on the server and returns its keyId with the public key.",
17
+ body: EMPTY_BODY,
12
18
  },
13
19
  }, async (request, reply) => {
14
- try {
15
- const keyPair = (0, modern_1.mlKemGenerateKeyPair)();
16
- return reply.send({ data: keyPair });
17
- }
18
- catch (error) {
19
- return (0, route_helpers_1.classifyCryptoError)(error, request, reply, "Key generation");
20
- }
20
+ const { publicKey, secretKey, algorithm } = (0, modern_1.mlKemGenerateKeyPair)();
21
+ const stored = await (0, keys_1.storeKey)(request, algorithm, { publicKey }, { secretKey });
22
+ return reply.send({ data: (0, keys_1.publicView)(stored) });
21
23
  });
24
+ }
25
+ function registerMlKemEncapsulate(app) {
22
26
  app.post("/v2/pq/encapsulate", {
23
27
  schema: {
24
28
  tags: ["Post-Quantum"],
@@ -43,47 +47,56 @@ exports.default = (app) => {
43
47
  return (0, route_helpers_1.classifyCryptoError)(error, request, reply, "Encapsulation");
44
48
  }
45
49
  });
50
+ }
51
+ function registerMlKemDecapsulate(app) {
46
52
  app.post("/v2/pq/decapsulate", {
47
53
  schema: {
48
54
  tags: ["Post-Quantum"],
49
55
  summary: "ML-KEM decapsulate",
50
- description: "Decapsulate and recover the shared secret using the ML-KEM secret key.",
56
+ description: "Recover the shared secret with a server-held ML-KEM-768 key (keyId from POST /v2/pq/keygen or POST /v2/keys/generate).",
51
57
  body: {
52
58
  type: "object",
53
- required: ["secretKey", "ciphertext"],
59
+ required: ["keyId", "ciphertext"],
54
60
  additionalProperties: false,
55
61
  properties: {
56
- secretKey: { type: "string", minLength: 1 },
62
+ keyId: keys_1.KEY_ID_SCHEMA,
57
63
  ciphertext: { type: "string", minLength: 1 },
58
64
  },
59
65
  },
60
66
  },
61
67
  }, async (request, reply) => {
68
+ const { keyId, ciphertext } = request.body;
69
+ const key = await (0, keys_1.resolveKey)(request, keyId, ["ml-kem-768"]);
62
70
  try {
63
- const { secretKey, ciphertext } = request.body;
64
- const result = (0, modern_1.mlKemDecapsulate)(secretKey, ciphertext);
71
+ const result = (0, modern_1.mlKemDecapsulate)(key.privateParts["secretKey"], ciphertext);
65
72
  return reply.send({ data: result });
66
73
  }
67
74
  catch (error) {
68
75
  return (0, route_helpers_1.classifyCryptoError)(error, request, reply, "Decapsulation");
69
76
  }
70
77
  });
78
+ }
79
+ function registerHybridKeygen(app) {
71
80
  app.post("/v2/pq/hybrid/keygen", {
72
81
  schema: {
73
82
  tags: ["Post-Quantum"],
74
- summary: "Generate hybrid X25519 + ML-KEM-768 key pair",
75
- description: "Generates both classical (X25519) and post-quantum (ML-KEM-768) key pairs for hybrid key exchange.",
76
- body: { type: "object", additionalProperties: false, properties: {} },
83
+ summary: "Generate a server-held hybrid X25519 + ML-KEM-768 key pair",
84
+ description: "Generates classical (X25519) and post-quantum (ML-KEM-768) key pairs for hybrid key exchange, keeps the private keys on the server and returns a keyId with the public keys.",
85
+ body: EMPTY_BODY,
77
86
  },
78
87
  }, async (request, reply) => {
79
- try {
80
- const keyPair = (0, modern_1.hybridGenerateKeyPair)();
81
- return reply.send({ data: keyPair });
82
- }
83
- catch (error) {
84
- return (0, route_helpers_1.classifyCryptoError)(error, request, reply, "Key generation");
85
- }
88
+ const kp = (0, modern_1.hybridGenerateKeyPair)();
89
+ const stored = await (0, keys_1.storeKey)(request, kp.algorithm, {
90
+ x25519PublicKey: kp.x25519PublicKey,
91
+ mlKemPublicKey: kp.mlKemPublicKey,
92
+ }, {
93
+ x25519PrivateKey: kp.x25519PrivateKey,
94
+ mlKemSecretKey: kp.mlKemSecretKey,
95
+ });
96
+ return reply.send({ data: (0, keys_1.publicView)(stored) });
86
97
  });
98
+ }
99
+ function registerHybridEncapsulate(app) {
87
100
  app.post("/v2/pq/hybrid/encapsulate", {
88
101
  schema: {
89
102
  tags: ["Post-Quantum"],
@@ -109,23 +122,19 @@ exports.default = (app) => {
109
122
  return (0, route_helpers_1.classifyCryptoError)(error, request, reply, "Hybrid encapsulation");
110
123
  }
111
124
  });
125
+ }
126
+ function registerHybridDecapsulate(app) {
112
127
  app.post("/v2/pq/hybrid/decapsulate", {
113
128
  schema: {
114
129
  tags: ["Post-Quantum"],
115
130
  summary: "Hybrid decapsulate (X25519 + ML-KEM-768)",
116
- description: "Recovers the combined shared secret using private keys and sender's ephemeral data.",
131
+ description: "Recovers the combined shared secret with a server-held x25519-ml-kem-768 key (keyId from POST /v2/pq/hybrid/keygen) and the sender's ephemeral data.",
117
132
  body: {
118
133
  type: "object",
119
- required: [
120
- "x25519PrivateKey",
121
- "mlKemSecretKey",
122
- "x25519EphemeralPublic",
123
- "mlKemCiphertext",
124
- ],
134
+ required: ["keyId", "x25519EphemeralPublic", "mlKemCiphertext"],
125
135
  additionalProperties: false,
126
136
  properties: {
127
- x25519PrivateKey: { type: "string", minLength: 64, maxLength: 64 },
128
- mlKemSecretKey: { type: "string", minLength: 1 },
137
+ keyId: keys_1.KEY_ID_SCHEMA,
129
138
  x25519EphemeralPublic: {
130
139
  type: "string",
131
140
  minLength: 64,
@@ -136,14 +145,24 @@ exports.default = (app) => {
136
145
  },
137
146
  },
138
147
  }, async (request, reply) => {
148
+ const body = request.body;
149
+ const key = await (0, keys_1.resolveKey)(request, body.keyId, ["x25519-ml-kem-768"]);
139
150
  try {
140
- const body = request.body;
141
- const result = (0, modern_1.hybridDecapsulate)(body.x25519PrivateKey, body.mlKemSecretKey, body.x25519EphemeralPublic, body.mlKemCiphertext);
151
+ const { x25519PrivateKey, mlKemSecretKey } = key.privateParts;
152
+ const result = (0, modern_1.hybridDecapsulate)(x25519PrivateKey, mlKemSecretKey, body.x25519EphemeralPublic, body.mlKemCiphertext);
142
153
  return reply.send({ data: result });
143
154
  }
144
155
  catch (error) {
145
156
  return (0, route_helpers_1.classifyCryptoError)(error, request, reply, "Hybrid decapsulation");
146
157
  }
147
158
  });
159
+ }
160
+ exports.default = (app) => {
161
+ registerMlKemKeygen(app);
162
+ registerMlKemEncapsulate(app);
163
+ registerMlKemDecapsulate(app);
164
+ registerHybridKeygen(app);
165
+ registerHybridEncapsulate(app);
166
+ registerHybridDecapsulate(app);
148
167
  };
149
168
  //# sourceMappingURL=pq.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"sealedbox.d.ts","sourceRoot":"","sources":["../../../src/routes/v2/sealedbox.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;yBAI/B,KAAK,eAAe,KAAG,IAAI;AAA3C,wBA6IE"}
1
+ {"version":3,"file":"sealedbox.d.ts","sourceRoot":"","sources":["../../../src/routes/v2/sealedbox.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;yBAmK/B,KAAK,eAAe,KAAG,IAAI;AAA3C,wBAKE"}
@@ -34,7 +34,9 @@ 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
- exports.default = (app) => {
37
+ const keys_1 = require("../../utils/keys");
38
+ const loadSealedbox = async () => (await Promise.resolve().then(() => __importStar(require("@sebastienrousseau/crypto-lib/high-level")))).sealedbox;
39
+ function registerSeal(app) {
38
40
  app.post("/v2/sealedbox/seal", {
39
41
  schema: {
40
42
  tags: ["Sealed Box"],
@@ -51,7 +53,7 @@ exports.default = (app) => {
51
53
  },
52
54
  }, async (request, reply) => {
53
55
  try {
54
- const { seal } = await Promise.resolve().then(() => __importStar(require("@sebastienrousseau/crypto-lib/dist/high-level/sealedbox")));
56
+ const { seal } = await loadSealedbox();
55
57
  const { recipientPublicKey, plaintext } = request.body;
56
58
  return reply.send({ data: seal(recipientPublicKey, plaintext) });
57
59
  }
@@ -59,25 +61,29 @@ exports.default = (app) => {
59
61
  return (0, route_helpers_1.classifyCryptoError)(error, request, reply, "Encryption");
60
62
  }
61
63
  });
64
+ }
65
+ function registerOpen(app) {
62
66
  app.post("/v2/sealedbox/open", {
63
67
  schema: {
64
68
  tags: ["Sealed Box"],
65
69
  summary: "Decrypt an anonymous sealed box (X25519)",
70
+ description: "Opens a sealed box with a server-held x25519 key (keyId from POST /v2/keys/generate).",
66
71
  body: {
67
72
  type: "object",
68
- required: ["recipientSecretKey", "sealed"],
73
+ required: ["keyId", "sealed"],
69
74
  additionalProperties: false,
70
75
  properties: {
71
- recipientSecretKey: { type: "string", minLength: 1 },
76
+ keyId: keys_1.KEY_ID_SCHEMA,
72
77
  sealed: { type: "string", minLength: 1 },
73
78
  },
74
79
  },
75
80
  },
76
81
  }, async (request, reply) => {
82
+ const { keyId, sealed } = request.body;
83
+ const key = await (0, keys_1.resolveKey)(request, keyId, ["x25519"]);
77
84
  try {
78
- const { open } = await Promise.resolve().then(() => __importStar(require("@sebastienrousseau/crypto-lib/dist/high-level/sealedbox")));
79
- const { recipientSecretKey, sealed } = request.body;
80
- const plaintext = open(recipientSecretKey, sealed);
85
+ const { open } = await loadSealedbox();
86
+ const plaintext = open(key.privateParts["privateKey"], sealed);
81
87
  return reply.send({
82
88
  data: Buffer.from(plaintext).toString("utf8"),
83
89
  });
@@ -86,6 +92,8 @@ exports.default = (app) => {
86
92
  return (0, route_helpers_1.classifyCryptoError)(error, request, reply, "Decryption");
87
93
  }
88
94
  });
95
+ }
96
+ function registerSealPq(app) {
89
97
  app.post("/v2/sealedbox/seal-pq", {
90
98
  schema: {
91
99
  tags: ["Sealed Box"],
@@ -103,7 +111,7 @@ exports.default = (app) => {
103
111
  },
104
112
  }, async (request, reply) => {
105
113
  try {
106
- const { sealPQ } = await Promise.resolve().then(() => __importStar(require("@sebastienrousseau/crypto-lib/dist/high-level/sealedbox")));
114
+ const { sealPQ } = await loadSealedbox();
107
115
  const { x25519PublicKey, mlKemPublicKey, plaintext } = request.body;
108
116
  return reply.send({
109
117
  data: sealPQ(x25519PublicKey, mlKemPublicKey, plaintext),
@@ -113,26 +121,30 @@ exports.default = (app) => {
113
121
  return (0, route_helpers_1.classifyCryptoError)(error, request, reply, "Encryption");
114
122
  }
115
123
  });
124
+ }
125
+ function registerOpenPq(app) {
116
126
  app.post("/v2/sealedbox/open-pq", {
117
127
  schema: {
118
128
  tags: ["Sealed Box"],
119
129
  summary: "Decrypt a post-quantum sealed box (X25519 + ML-KEM-768)",
130
+ description: "Opens a hybrid sealed box with a server-held x25519-ml-kem-768 key (keyId from POST /v2/pq/hybrid/keygen).",
120
131
  body: {
121
132
  type: "object",
122
- required: ["x25519SecretKey", "mlKemSecretKey", "sealed"],
133
+ required: ["keyId", "sealed"],
123
134
  additionalProperties: false,
124
135
  properties: {
125
- x25519SecretKey: { type: "string", minLength: 1 },
126
- mlKemSecretKey: { type: "string", minLength: 1 },
136
+ keyId: keys_1.KEY_ID_SCHEMA,
127
137
  sealed: { type: "string", minLength: 1 },
128
138
  },
129
139
  },
130
140
  },
131
141
  }, async (request, reply) => {
142
+ const { keyId, sealed } = request.body;
143
+ const key = await (0, keys_1.resolveKey)(request, keyId, ["x25519-ml-kem-768"]);
132
144
  try {
133
- const { openPQ } = await Promise.resolve().then(() => __importStar(require("@sebastienrousseau/crypto-lib/dist/high-level/sealedbox")));
134
- const { x25519SecretKey, mlKemSecretKey, sealed } = request.body;
135
- const plaintext = openPQ(x25519SecretKey, mlKemSecretKey, sealed);
145
+ const { openPQ } = await loadSealedbox();
146
+ const { x25519PrivateKey, mlKemSecretKey } = key.privateParts;
147
+ const plaintext = openPQ(x25519PrivateKey, mlKemSecretKey, sealed);
136
148
  return reply.send({
137
149
  data: Buffer.from(plaintext).toString("utf8"),
138
150
  });
@@ -141,5 +153,11 @@ exports.default = (app) => {
141
153
  return (0, route_helpers_1.classifyCryptoError)(error, request, reply, "Decryption");
142
154
  }
143
155
  });
156
+ }
157
+ exports.default = (app) => {
158
+ registerSeal(app);
159
+ registerOpen(app);
160
+ registerSealPq(app);
161
+ registerOpenPq(app);
144
162
  };
145
163
  //# sourceMappingURL=sealedbox.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"secretbox.d.ts","sourceRoot":"","sources":["../../../src/routes/v2/secretbox.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;yBAI/B,KAAK,eAAe,KAAG,IAAI;AAA3C,wBAyEE"}
1
+ {"version":3,"file":"secretbox.d.ts","sourceRoot":"","sources":["../../../src/routes/v2/secretbox.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;yBAqF/B,KAAK,eAAe,KAAG,IAAI;AAA3C,wBAGE"}
@@ -34,7 +34,8 @@ 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
- exports.default = (app) => {
37
+ const loadSecretbox = async () => (await Promise.resolve().then(() => __importStar(require("@sebastienrousseau/crypto-lib/high-level")))).secretbox;
38
+ function registerSeal(app) {
38
39
  app.post("/v2/secretbox/seal", {
39
40
  schema: {
40
41
  tags: ["Secretbox"],
@@ -52,7 +53,7 @@ exports.default = (app) => {
52
53
  },
53
54
  }, async (request, reply) => {
54
55
  try {
55
- const { seal } = await Promise.resolve().then(() => __importStar(require("@sebastienrousseau/crypto-lib/dist/high-level/secretbox")));
56
+ const { seal } = await loadSecretbox();
56
57
  const { key, plaintext, aad } = request.body;
57
58
  const aadBytes = aad ? Buffer.from(aad, "utf8") : undefined;
58
59
  return reply.send({ data: seal(key, plaintext, aadBytes) });
@@ -61,6 +62,8 @@ exports.default = (app) => {
61
62
  return (0, route_helpers_1.classifyCryptoError)(error, request, reply, "Encryption");
62
63
  }
63
64
  });
65
+ }
66
+ function registerOpen(app) {
64
67
  app.post("/v2/secretbox/open", {
65
68
  schema: {
66
69
  tags: ["Secretbox"],
@@ -78,7 +81,7 @@ exports.default = (app) => {
78
81
  },
79
82
  }, async (request, reply) => {
80
83
  try {
81
- const { open } = await Promise.resolve().then(() => __importStar(require("@sebastienrousseau/crypto-lib/dist/high-level/secretbox")));
84
+ const { open } = await loadSecretbox();
82
85
  const { key, ciphertext, aad } = request.body;
83
86
  const aadBytes = aad ? Buffer.from(aad, "utf8") : undefined;
84
87
  const plaintext = open(key, ciphertext, aadBytes);
@@ -90,5 +93,9 @@ exports.default = (app) => {
90
93
  return (0, route_helpers_1.classifyCryptoError)(error, request, reply, "Decryption");
91
94
  }
92
95
  });
96
+ }
97
+ exports.default = (app) => {
98
+ registerSeal(app);
99
+ registerOpen(app);
93
100
  };
94
101
  //# sourceMappingURL=secretbox.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"signing.d.ts","sourceRoot":"","sources":["../../../src/routes/v2/signing.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;yBAQ/B,KAAK,eAAe,KAAG,IAAI;AAA3C,wBA6EE"}
1
+ {"version":3,"file":"signing.d.ts","sourceRoot":"","sources":["../../../src/routes/v2/signing.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;yBAyF/B,KAAK,eAAe,KAAG,IAAI;AAA3C,wBAIE"}
@@ -1,19 +1,20 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- const modern_1 = require("@sebastienrousseau/crypto-lib/dist/modern");
3
+ const modern_1 = require("@sebastienrousseau/crypto-lib/modern");
4
4
  const route_helpers_1 = require("../../utils/route-helpers");
5
- exports.default = (app) => {
5
+ const keys_1 = require("../../utils/keys");
6
+ function registerSign(app) {
6
7
  app.post("/v2/sign", {
7
8
  schema: {
8
9
  tags: ["Signing"],
9
10
  summary: "Sign with Ed25519",
10
- description: "Create an Ed25519 digital signature over a message.",
11
+ description: "Create an Ed25519 digital signature over a message with a server-held ed25519 key (keyId from POST /v2/keys/generate).",
11
12
  body: {
12
13
  type: "object",
13
- required: ["privateKey", "message"],
14
+ required: ["keyId", "message"],
14
15
  additionalProperties: false,
15
16
  properties: {
16
- privateKey: { type: "string", minLength: 64, maxLength: 64 },
17
+ keyId: keys_1.KEY_ID_SCHEMA,
17
18
  message: {
18
19
  type: "string",
19
20
  minLength: 1,
@@ -23,15 +24,13 @@ exports.default = (app) => {
23
24
  },
24
25
  },
25
26
  }, async (request, reply) => {
26
- try {
27
- const { privateKey, message } = request.body;
28
- const result = (0, modern_1.ed25519Sign)(privateKey, message);
29
- return reply.send({ data: result });
30
- }
31
- catch (error) {
32
- return (0, route_helpers_1.classifyCryptoError)(error, request, reply, "Signing");
33
- }
27
+ const { keyId, message } = request.body;
28
+ const key = await (0, keys_1.resolveKey)(request, keyId, ["ed25519"]);
29
+ const result = (0, modern_1.ed25519Sign)(key.privateParts["privateKey"], message);
30
+ return reply.send({ data: result });
34
31
  });
32
+ }
33
+ function registerVerify(app) {
35
34
  app.post("/v2/verify", {
36
35
  schema: {
37
36
  tags: ["Signing"],
@@ -62,5 +61,9 @@ exports.default = (app) => {
62
61
  return (0, route_helpers_1.classifyCryptoError)(error, request, reply, "Verification");
63
62
  }
64
63
  });
64
+ }
65
+ exports.default = (app) => {
66
+ registerSign(app);
67
+ registerVerify(app);
65
68
  };
66
69
  //# sourceMappingURL=signing.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"stream.d.ts","sourceRoot":"","sources":["../../../src/routes/v2/stream.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;yBAuQ/B,KAAK,eAAe,KAAG,IAAI;AAA3C,wBAIE"}
1
+ {"version":3,"file":"stream.d.ts","sourceRoot":"","sources":["../../../src/routes/v2/stream.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,eAAe,EAAkB,MAAM,SAAS,CAAC;yBAsR/C,KAAK,eAAe,KAAG,IAAI;AAA3C,wBAIE"}
@@ -1,8 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const crypto_lib_1 = require("@sebastienrousseau/crypto-lib");
4
- const iso20022_1 = require("@sebastienrousseau/crypto-lib/dist/protocols/iso20022");
4
+ const protocols_1 = require("@sebastienrousseau/crypto-lib/protocols");
5
5
  const route_helpers_1 = require("../../utils/route-helpers");
6
+ const keys_1 = require("../../utils/keys");
6
7
  const SIGN_SCHEMA = {
7
8
  tags: ["Streaming"],
8
9
  summary: "Batch sign messages with sub-millisecond throughput",
@@ -18,12 +19,12 @@ const SIGN_SCHEMA = {
18
19
  maxItems: 1000,
19
20
  items: {
20
21
  type: "object",
21
- required: ["id", "message", "privateKey"],
22
+ required: ["id", "message", "keyId"],
22
23
  additionalProperties: false,
23
24
  properties: {
24
25
  id: { type: "string" },
25
26
  message: { type: "string", minLength: 1 },
26
- privateKey: { type: "string", minLength: 64, maxLength: 64 },
27
+ keyId: keys_1.KEY_ID_SCHEMA,
27
28
  },
28
29
  },
29
30
  },
@@ -129,28 +130,32 @@ const ISO20022_SCHEMA = {
129
130
  },
130
131
  },
131
132
  };
133
+ async function resolveBatchKeys(request, items) {
134
+ const keys = new Map();
135
+ for (const keyId of new Set(items.map((i) => i.keyId))) {
136
+ keys.set(keyId, await (0, keys_1.resolveKey)(request, keyId, ["ed25519"]));
137
+ }
138
+ return keys;
139
+ }
132
140
  function registerBatchSign(app) {
133
141
  app.post("/v2/stream/sign", { schema: SIGN_SCHEMA }, async (request, reply) => {
134
- try {
135
- const { items } = request.body;
136
- const signatures = items.map((item) => {
137
- const res = (0, crypto_lib_1.ed25519Sign)(item.privateKey, item.message);
138
- return {
139
- id: item.id,
140
- signature: res.signature,
141
- algorithm: "ed25519",
142
- };
143
- });
144
- return reply.send({
145
- data: {
146
- count: signatures.length,
147
- signatures,
148
- },
149
- });
150
- }
151
- catch (error) {
152
- return (0, route_helpers_1.classifyCryptoError)(error, request, reply, "Batch signing");
153
- }
142
+ const { items } = request.body;
143
+ const keys = await resolveBatchKeys(request, items);
144
+ const signatures = items.map((item) => {
145
+ const key = keys.get(item.keyId);
146
+ const res = (0, crypto_lib_1.ed25519Sign)(key.privateParts["privateKey"], item.message);
147
+ return {
148
+ id: item.id,
149
+ signature: res.signature,
150
+ algorithm: "ed25519",
151
+ };
152
+ });
153
+ return reply.send({
154
+ data: {
155
+ count: signatures.length,
156
+ signatures,
157
+ },
158
+ });
154
159
  });
155
160
  }
156
161
  function registerBatchVerify(app) {
@@ -187,7 +192,7 @@ function registerIso20022Verify(app) {
187
192
  app.post("/v2/stream/iso20022", { schema: ISO20022_SCHEMA }, async (request, reply) => {
188
193
  try {
189
194
  const { envelope, payload, trustedKeys } = request.body;
190
- const result = (0, iso20022_1.verifyIso20022Payment)(envelope, payload, {
195
+ const result = (0, protocols_1.verifyIso20022Payment)(envelope, payload, {
191
196
  classicalPublicKeyHex: trustedKeys.classicalPublicKey,
192
197
  postQuantumPublicKeyHex: trustedKeys.postQuantumPublicKey,
193
198
  });
@@ -1 +1 @@
1
- {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAoCA,OAAO,KAAK,OAAO,MAAM,SAAS,CAAC;AA4CnC,iBAAe,IAAI,IAAI,OAAO,CAAC,OAAO,CAAC,eAAe,CAAC,CA0DtD;AAED,OAAO,EAAE,IAAI,EAAE,CAAC"}
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AA2CA,OAAO,KAAK,OAAO,MAAM,SAAS,CAAC;AAuCnC,iBAAe,IAAI,IAAI,OAAO,CAAC,OAAO,CAAC,eAAe,CAAC,CA6EtD;AAED,OAAO,EAAE,IAAI,EAAE,CAAC"}
package/dist/server.js CHANGED
@@ -50,12 +50,13 @@ const swagger_ui_1 = __importDefault(require("@fastify/swagger-ui"));
50
50
  const crypto_1 = require("crypto");
51
51
  const logger_1 = __importDefault(require("./lib/logger"));
52
52
  const auth_1 = require("./lib/auth");
53
+ const auth_policy_1 = require("./config/auth-policy");
53
54
  const metering_1 = require("./enterprise/metering");
55
+ const key_store_1 = require("./lib/key-store");
56
+ const kdf_runner_1 = require("./lib/kdf-runner");
57
+ const problem_1 = require("./lib/problem");
54
58
  const routes_1 = __importDefault(require("./routes"));
55
59
  const fastify = __importStar(require("fastify"));
56
- function isPublicPath(url) {
57
- return (0, constants_1.isProbePath)(url) || url.startsWith("/docs");
58
- }
59
60
  async function registerDocs(app) {
60
61
  await app.register(swagger_1.default, {
61
62
  openapi: {
@@ -82,7 +83,12 @@ async function registerDocs(app) {
82
83
  }
83
84
  async function init() {
84
85
  const app = fastify.fastify(constants_1.fastifyOptions);
86
+ const registered = [];
87
+ app.addHook("onRoute", (route) => {
88
+ registered.push({ method: route.method, url: route.url });
89
+ });
85
90
  logger_1.default.info("\n\nEnvironment details: " + constants_1.consoleOutput);
91
+ (0, problem_1.registerProblemHandlers)(app);
86
92
  app.addHook("onRequest", async (request, reply) => {
87
93
  const reqId = request.headers["x-request-id"] ?? (0, crypto_1.randomUUID)();
88
94
  reply.header("x-request-id", reqId);
@@ -107,18 +113,22 @@ async function init() {
107
113
  });
108
114
  await (0, auth_1.registerAuth)(app);
109
115
  app.addHook("onRequest", async (request, reply) => {
110
- if (isPublicPath(request.url))
116
+ if ((0, auth_policy_1.isPublicRoute)(request.url))
111
117
  return;
112
118
  const auth = await (0, auth_1.authenticate)(request, reply);
113
- if (!auth)
119
+ if (!auth || !(0, auth_1.authorizeRoute)(request, reply, auth))
114
120
  return reply;
115
121
  request.auth = auth;
116
122
  });
123
+ app.decorate("keyStore", (0, key_store_1.keyStoreFromEnv)());
124
+ app.decorate("kdf", new kdf_runner_1.KdfRunner());
125
+ app.addHook("onClose", () => app.kdf.close());
117
126
  (0, metering_1.registerMetering)(app);
118
127
  await app.register(async (scope) => {
119
128
  (0, routes_1.default)(scope);
120
129
  });
121
130
  await app.ready();
131
+ (0, auth_policy_1.assertRoutesCovered)(registered);
122
132
  return app;
123
133
  }
124
134
  //# sourceMappingURL=server.js.map
@@ -0,0 +1,17 @@
1
+ import type { FastifyRequest } from "fastify";
2
+ import { type KeyParts, type KeyStore, type StoredKey } from "../lib/key-store";
3
+ declare module "fastify" {
4
+ interface FastifyInstance {
5
+ keyStore: KeyStore;
6
+ }
7
+ }
8
+ export declare const KEY_ID_SCHEMA: {
9
+ readonly type: "string";
10
+ readonly pattern: "^k_[A-Za-z0-9_-]{22}$";
11
+ readonly description: "Identifier of a server-held key, returned by a key-generation route.";
12
+ };
13
+ export declare function resolveKey(request: FastifyRequest, keyId: string, algorithms?: readonly string[]): Promise<StoredKey>;
14
+ export declare function storeKey(request: FastifyRequest, algorithm: string, publicParts: KeyParts, privateParts: KeyParts): Promise<StoredKey>;
15
+ export declare function serverPgpPrivateKey(): Promise<string>;
16
+ export declare function publicView(key: StoredKey): Record<string, string>;
17
+ //# sourceMappingURL=keys.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keys.d.ts","sourceRoot":"","sources":["../../src/utils/keys.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAE9C,OAAO,EAEL,KAAK,QAAQ,EACb,KAAK,QAAQ,EACb,KAAK,SAAS,EACf,MAAM,kBAAkB,CAAC;AAE1B,OAAO,QAAQ,SAAS,CAAC;IACvB,UAAU,eAAe;QAEvB,QAAQ,EAAE,QAAQ,CAAC;KACpB;CACF;AAGD,eAAO,MAAM,aAAa;;;;CAKhB,CAAC;AAeX,wBAAgB,UAAU,CACxB,OAAO,EAAE,cAAc,EACvB,KAAK,EAAE,MAAM,EACb,UAAU,CAAC,EAAE,SAAS,MAAM,EAAE,GAC7B,OAAO,CAAC,SAAS,CAAC,CAEpB;AAGD,wBAAgB,QAAQ,CACtB,OAAO,EAAE,cAAc,EACvB,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,QAAQ,EACrB,YAAY,EAAE,QAAQ,GACrB,OAAO,CAAC,SAAS,CAAC,CAOpB;AAOD,wBAAsB,mBAAmB,IAAI,OAAO,CAAC,MAAM,CAAC,CAG3D;AAGD,wBAAgB,UAAU,CAAC,GAAG,EAAE,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAEjE"}
@@ -0,0 +1,36 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.KEY_ID_SCHEMA = void 0;
4
+ exports.resolveKey = resolveKey;
5
+ exports.storeKey = storeKey;
6
+ exports.serverPgpPrivateKey = serverPgpPrivateKey;
7
+ exports.publicView = publicView;
8
+ const pgp_1 = require("@sebastienrousseau/crypto-lib/pgp");
9
+ const key_store_1 = require("../lib/key-store");
10
+ exports.KEY_ID_SCHEMA = {
11
+ type: "string",
12
+ pattern: key_store_1.KEY_ID_PATTERN,
13
+ description: "Identifier of a server-held key, returned by a key-generation route.",
14
+ };
15
+ function principalOf(request) {
16
+ return request.auth.sub;
17
+ }
18
+ function resolveKey(request, keyId, algorithms) {
19
+ return request.server.keyStore.get(keyId, principalOf(request), algorithms);
20
+ }
21
+ function storeKey(request, algorithm, publicParts, privateParts) {
22
+ return request.server.keyStore.put({
23
+ algorithm,
24
+ owner: principalOf(request),
25
+ publicParts,
26
+ privateParts,
27
+ });
28
+ }
29
+ async function serverPgpPrivateKey() {
30
+ const { privateKeyArmored } = await (0, pgp_1.loadKeystore)();
31
+ return Buffer.from(privateKeyArmored, "latin1").toString("base64");
32
+ }
33
+ function publicView(key) {
34
+ return { keyId: key.keyId, algorithm: key.algorithm, ...key.publicParts };
35
+ }
36
+ //# sourceMappingURL=keys.js.map
@@ -7,9 +7,5 @@ export declare function classifyCryptoError(error: unknown, request: {
7
7
  log: {
8
8
  error: (err: unknown, msg: string) => void;
9
9
  };
10
- }, reply: {
11
- status: (code: number) => {
12
- send: (body: unknown) => unknown;
13
- };
14
- }, operation: string): unknown;
10
+ }, reply: FastifyReply, operation: string): FastifyReply;
15
11
  //# sourceMappingURL=route-helpers.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"route-helpers.d.ts","sourceRoot":"","sources":["../../src/utils/route-helpers.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAC5C,OAAO,EAGL,gBAAgB,EACjB,MAAM,cAAc,CAAC;AAOtB,wBAAgB,iBAAiB,CAC/B,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,OAAO,CAAC,CAAC,EAEnD,OAAO,EAAE,CAAC,EACV,KAAK,EAAE,YAAY,GAEjB;KAAG,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,gBAAgB,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,GAAG,KAAK;CAAE,GACtE,IAAI,CAmBP;AAUD,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,OAAO,EACd,OAAO,EAAE;IAAE,GAAG,EAAE;QAAE,KAAK,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,KAAK,IAAI,CAAA;KAAE,CAAA;CAAE,EAChE,KAAK,EAAE;IAAE,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK;QAAE,IAAI,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,OAAO,CAAA;KAAE,CAAA;CAAE,EACzE,SAAS,EAAE,MAAM,GAChB,OAAO,CAkBT"}
1
+ {"version":3,"file":"route-helpers.d.ts","sourceRoot":"","sources":["../../src/utils/route-helpers.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAE5C,OAAO,EAGL,gBAAgB,EACjB,MAAM,cAAc,CAAC;AAOtB,wBAAgB,iBAAiB,CAC/B,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,OAAO,CAAC,CAAC,EAEnD,OAAO,EAAE,CAAC,EACV,KAAK,EAAE,YAAY,GAEjB;KAAG,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,gBAAgB,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,GAAG,KAAK;CAAE,GACtE,IAAI,CAmBP;AAsBD,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,OAAO,EACd,OAAO,EAAE;IAAE,GAAG,EAAE;QAAE,KAAK,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,KAAK,IAAI,CAAA;KAAE,CAAA;CAAE,EAChE,KAAK,EAAE,YAAY,EACnB,SAAS,EAAE,MAAM,GAChB,YAAY,CA6Bd"}