@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.
Files changed (102) 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 +126 -0
  7. package/dist/config/constants.d.ts.map +1 -1
  8. package/dist/config/constants.js +7 -1
  9. package/dist/enterprise/metering.d.ts.map +1 -1
  10. package/dist/enterprise/metering.js +2 -4
  11. package/dist/index.d.ts +1 -0
  12. package/dist/index.js +1 -0
  13. package/dist/lib/auth.d.ts +4 -1
  14. package/dist/lib/auth.d.ts.map +1 -1
  15. package/dist/lib/auth.js +87 -40
  16. package/dist/lib/kdf-runner.d.ts +18 -0
  17. package/dist/lib/kdf-runner.d.ts.map +1 -0
  18. package/dist/lib/kdf-runner.js +27 -0
  19. package/dist/lib/key-store.d.ts +37 -0
  20. package/dist/lib/key-store.d.ts.map +1 -0
  21. package/dist/lib/key-store.js +223 -0
  22. package/dist/lib/opaque-store.d.ts +34 -0
  23. package/dist/lib/opaque-store.d.ts.map +1 -0
  24. package/dist/lib/opaque-store.js +72 -0
  25. package/dist/lib/problem.d.ts +67 -0
  26. package/dist/lib/problem.d.ts.map +1 -0
  27. package/dist/lib/problem.js +162 -0
  28. package/dist/routes/v1/decrypt.d.ts.map +1 -1
  29. package/dist/routes/v1/decrypt.js +24 -16
  30. package/dist/routes/v1/encrypt.d.ts.map +1 -1
  31. package/dist/routes/v1/encrypt.js +12 -13
  32. package/dist/routes/v1/generate.d.ts.map +1 -1
  33. package/dist/routes/v1/generate.js +22 -25
  34. package/dist/routes/v1/revoke.d.ts.map +1 -1
  35. package/dist/routes/v1/revoke.js +6 -11
  36. package/dist/routes/v1/verify.d.ts.map +1 -1
  37. package/dist/routes/v1/verify.js +10 -12
  38. package/dist/routes/v2/algorithms.js +1 -1
  39. package/dist/routes/v2/compliance.js +1 -1
  40. package/dist/routes/v2/encrypt.d.ts.map +1 -1
  41. package/dist/routes/v2/encrypt.js +1 -1
  42. package/dist/routes/v2/hash.d.ts.map +1 -1
  43. package/dist/routes/v2/hash.js +1 -1
  44. package/dist/routes/v2/hpke.d.ts +3 -0
  45. package/dist/routes/v2/hpke.d.ts.map +1 -0
  46. package/dist/routes/v2/hpke.js +168 -0
  47. package/dist/routes/v2/index.d.ts.map +1 -1
  48. package/dist/routes/v2/index.js +6 -0
  49. package/dist/routes/v2/kdf.d.ts +5 -0
  50. package/dist/routes/v2/kdf.d.ts.map +1 -1
  51. package/dist/routes/v2/kdf.js +27 -12
  52. package/dist/routes/v2/key-wrap.d.ts.map +1 -1
  53. package/dist/routes/v2/key-wrap.js +9 -3
  54. package/dist/routes/v2/keys.d.ts.map +1 -1
  55. package/dist/routes/v2/keys.js +39 -26
  56. package/dist/routes/v2/kms.d.ts +7 -0
  57. package/dist/routes/v2/kms.d.ts.map +1 -0
  58. package/dist/routes/v2/kms.js +310 -0
  59. package/dist/routes/v2/mac.d.ts.map +1 -1
  60. package/dist/routes/v2/mac.js +10 -3
  61. package/dist/routes/v2/multi-recipient.d.ts.map +1 -1
  62. package/dist/routes/v2/multi-recipient.js +12 -11
  63. package/dist/routes/v2/opaque.d.ts +4 -0
  64. package/dist/routes/v2/opaque.d.ts.map +1 -0
  65. package/dist/routes/v2/opaque.js +174 -0
  66. package/dist/routes/v2/password-encrypt.d.ts.map +1 -1
  67. package/dist/routes/v2/password-encrypt.js +13 -46
  68. package/dist/routes/v2/password.d.ts +4 -0
  69. package/dist/routes/v2/password.d.ts.map +1 -1
  70. package/dist/routes/v2/password.js +28 -53
  71. package/dist/routes/v2/pq-hash-sign.d.ts.map +1 -1
  72. package/dist/routes/v2/pq-hash-sign.js +33 -24
  73. package/dist/routes/v2/pq-sign.d.ts.map +1 -1
  74. package/dist/routes/v2/pq-sign.js +31 -24
  75. package/dist/routes/v2/pq.d.ts.map +1 -1
  76. package/dist/routes/v2/pq.js +57 -38
  77. package/dist/routes/v2/sealedbox.d.ts.map +1 -1
  78. package/dist/routes/v2/sealedbox.js +32 -14
  79. package/dist/routes/v2/secretbox.d.ts.map +1 -1
  80. package/dist/routes/v2/secretbox.js +10 -3
  81. package/dist/routes/v2/signing.d.ts.map +1 -1
  82. package/dist/routes/v2/signing.js +17 -13
  83. package/dist/routes/v2/stream-schemas.d.ts +296 -0
  84. package/dist/routes/v2/stream-schemas.d.ts.map +1 -0
  85. package/dist/routes/v2/stream-schemas.js +207 -0
  86. package/dist/routes/v2/stream.d.ts.map +1 -1
  87. package/dist/routes/v2/stream.js +144 -151
  88. package/dist/server.d.ts.map +1 -1
  89. package/dist/server.js +19 -5
  90. package/dist/utils/keys.d.ts +17 -0
  91. package/dist/utils/keys.d.ts.map +1 -0
  92. package/dist/utils/keys.js +36 -0
  93. package/dist/utils/route-helpers.d.ts +1 -5
  94. package/dist/utils/route-helpers.d.ts.map +1 -1
  95. package/dist/utils/route-helpers.js +16 -10
  96. package/dist/utils/trace.d.ts +12 -0
  97. package/dist/utils/trace.d.ts.map +1 -0
  98. package/dist/utils/trace.js +62 -0
  99. package/dist/utils/validation.d.ts +1 -0
  100. package/dist/utils/validation.d.ts.map +1 -1
  101. package/dist/utils/validation.js +17 -9
  102. package/package.json +22 -18
@@ -0,0 +1,207 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.STREAM_MULTI_PQ_DECRYPT_SCHEMA = exports.STREAM_MULTI_PQ_ENCRYPT_SCHEMA = exports.STREAM_PQ_DECRYPT_SCHEMA = exports.STREAM_PQ_ENCRYPT_SCHEMA = exports.ISO20022_SCHEMA = exports.VERIFY_SCHEMA = exports.SIGN_SCHEMA = void 0;
4
+ const keys_1 = require("../../utils/keys");
5
+ exports.SIGN_SCHEMA = {
6
+ tags: ["Streaming"],
7
+ summary: "Batch sign messages with sub-millisecond throughput",
8
+ description: "Process high-throughput batch digital signing operations over multiple payloads.",
9
+ body: {
10
+ type: "object",
11
+ required: ["items"],
12
+ additionalProperties: false,
13
+ properties: {
14
+ items: {
15
+ type: "array",
16
+ minItems: 1,
17
+ maxItems: 1000,
18
+ items: {
19
+ type: "object",
20
+ required: ["id", "message", "keyId"],
21
+ additionalProperties: false,
22
+ properties: {
23
+ id: { type: "string" },
24
+ message: { type: "string", minLength: 1 },
25
+ keyId: keys_1.KEY_ID_SCHEMA,
26
+ },
27
+ },
28
+ },
29
+ },
30
+ },
31
+ };
32
+ exports.VERIFY_SCHEMA = {
33
+ tags: ["Streaming"],
34
+ summary: "Batch verify digital signatures",
35
+ description: "Batch verification pipeline for high-concurrency payment and transaction verification.",
36
+ body: {
37
+ type: "object",
38
+ required: ["items"],
39
+ additionalProperties: false,
40
+ properties: {
41
+ items: {
42
+ type: "array",
43
+ minItems: 1,
44
+ maxItems: 1000,
45
+ items: {
46
+ type: "object",
47
+ required: ["id", "message", "signature", "publicKey"],
48
+ additionalProperties: false,
49
+ properties: {
50
+ id: { type: "string" },
51
+ message: { type: "string", minLength: 1 },
52
+ signature: { type: "string", minLength: 1 },
53
+ publicKey: { type: "string", minLength: 64, maxLength: 64 },
54
+ },
55
+ },
56
+ },
57
+ },
58
+ },
59
+ };
60
+ exports.ISO20022_SCHEMA = {
61
+ tags: ["Wholesale Payments"],
62
+ summary: "Verify ISO 20022 post-quantum dual-signature envelope",
63
+ description: "Validates the payload digest and both the Ed25519 and ML-DSA signatures of a pacs.008, pain.001 or camt.053 envelope. Signatures are checked against trustedKeys, the signer's public keys from the caller's own registry; the public keys inside the envelope are ignored.",
64
+ body: {
65
+ type: "object",
66
+ required: ["envelope", "payload", "trustedKeys"],
67
+ additionalProperties: false,
68
+ properties: {
69
+ trustedKeys: {
70
+ type: "object",
71
+ required: ["classicalPublicKey", "postQuantumPublicKey"],
72
+ additionalProperties: false,
73
+ properties: {
74
+ classicalPublicKey: { type: "string", minLength: 1 },
75
+ postQuantumPublicKey: { type: "string", minLength: 1 },
76
+ },
77
+ },
78
+ envelope: {
79
+ type: "object",
80
+ required: [
81
+ "messageId",
82
+ "messageType",
83
+ "payloadDigest",
84
+ "digestAlgorithm",
85
+ "timestamp",
86
+ "classical",
87
+ "postQuantum",
88
+ ],
89
+ additionalProperties: false,
90
+ properties: {
91
+ messageId: { type: "string" },
92
+ messageType: {
93
+ type: "string",
94
+ enum: ["pacs.008", "pain.001", "camt.053", "generic"],
95
+ },
96
+ payloadDigest: { type: "string" },
97
+ digestAlgorithm: {
98
+ type: "string",
99
+ enum: ["sha256", "sha384", "sha512"],
100
+ },
101
+ timestamp: { type: "string" },
102
+ classical: {
103
+ type: "object",
104
+ required: ["algorithm", "signature", "publicKey"],
105
+ additionalProperties: false,
106
+ properties: {
107
+ algorithm: { type: "string" },
108
+ signature: { type: "string" },
109
+ publicKey: { type: "string" },
110
+ },
111
+ },
112
+ postQuantum: {
113
+ type: "object",
114
+ required: ["algorithm", "signature", "publicKey"],
115
+ additionalProperties: false,
116
+ properties: {
117
+ algorithm: {
118
+ type: "string",
119
+ enum: ["ml-dsa-44", "ml-dsa-65", "ml-dsa-87"],
120
+ },
121
+ signature: { type: "string" },
122
+ publicKey: { type: "string" },
123
+ },
124
+ },
125
+ },
126
+ },
127
+ payload: {},
128
+ },
129
+ },
130
+ };
131
+ exports.STREAM_PQ_ENCRYPT_SCHEMA = {
132
+ tags: ["Streaming"],
133
+ summary: "Encrypt stream with hybrid post-quantum AEAD",
134
+ description: "Encrypts plaintext using chunked hybrid post-quantum STREAM (X25519 + ML-KEM-768 + XChaCha20-Poly1305).",
135
+ body: {
136
+ type: "object",
137
+ required: ["x25519PublicKey", "mlKemPublicKey", "plaintext"],
138
+ additionalProperties: false,
139
+ properties: {
140
+ x25519PublicKey: { type: "string", minLength: 64, maxLength: 64 },
141
+ mlKemPublicKey: { type: "string", minLength: 1 },
142
+ plaintext: { type: "string", minLength: 1 },
143
+ chunkSize: { type: "integer", minimum: 64, maximum: 1048576 },
144
+ },
145
+ },
146
+ };
147
+ exports.STREAM_PQ_DECRYPT_SCHEMA = {
148
+ tags: ["Streaming"],
149
+ summary: "Decrypt stream with server-held hybrid post-quantum key",
150
+ description: "Decrypts hybrid post-quantum STREAM ciphertext using a server-held x25519-ml-kem-768 key.",
151
+ body: {
152
+ type: "object",
153
+ required: ["keyId", "ciphertext"],
154
+ additionalProperties: false,
155
+ properties: {
156
+ keyId: keys_1.KEY_ID_SCHEMA,
157
+ ciphertext: { type: "string", minLength: 1 },
158
+ chunkSize: { type: "integer", minimum: 64, maximum: 1048576 },
159
+ },
160
+ },
161
+ };
162
+ exports.STREAM_MULTI_PQ_ENCRYPT_SCHEMA = {
163
+ tags: ["Streaming"],
164
+ summary: "Encrypt stream for multiple recipients with hybrid post-quantum AEAD",
165
+ description: "Encrypts plaintext for multiple recipients using hybrid post-quantum key encapsulation and chunked AEAD.",
166
+ body: {
167
+ type: "object",
168
+ required: ["recipients", "plaintext"],
169
+ additionalProperties: false,
170
+ properties: {
171
+ recipients: {
172
+ type: "array",
173
+ minItems: 1,
174
+ maxItems: 100,
175
+ items: {
176
+ type: "object",
177
+ required: ["recipientId", "x25519PublicKey", "mlKemPublicKey"],
178
+ additionalProperties: false,
179
+ properties: {
180
+ recipientId: { type: "string", minLength: 1, maxLength: 255 },
181
+ x25519PublicKey: { type: "string", minLength: 64, maxLength: 64 },
182
+ mlKemPublicKey: { type: "string", minLength: 1 },
183
+ },
184
+ },
185
+ },
186
+ plaintext: { type: "string", minLength: 1 },
187
+ chunkSize: { type: "integer", minimum: 1024, maximum: 16777216 },
188
+ },
189
+ },
190
+ };
191
+ exports.STREAM_MULTI_PQ_DECRYPT_SCHEMA = {
192
+ tags: ["Streaming"],
193
+ summary: "Decrypt multi-recipient stream with server-held hybrid post-quantum key",
194
+ description: "Decrypts multi-recipient hybrid post-quantum STREAM ciphertext using a server-held x25519-ml-kem-768 key.",
195
+ body: {
196
+ type: "object",
197
+ required: ["keyId", "ciphertext"],
198
+ additionalProperties: false,
199
+ properties: {
200
+ keyId: keys_1.KEY_ID_SCHEMA,
201
+ recipientId: { type: "string", minLength: 1, maxLength: 255 },
202
+ ciphertext: { type: "string", minLength: 1 },
203
+ chunkSize: { type: "integer", minimum: 1024, maximum: 16777216 },
204
+ },
205
+ },
206
+ };
207
+ //# sourceMappingURL=stream-schemas.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;yBA6U/C,KAAK,eAAe,KAAG,IAAI;AAA3C,wBAQE"}
@@ -1,160 +1,44 @@
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 streaming_1 = require("@sebastienrousseau/crypto-lib/streaming");
5
+ const protocols_1 = require("@sebastienrousseau/crypto-lib/protocols");
5
6
  const route_helpers_1 = require("../../utils/route-helpers");
6
- const SIGN_SCHEMA = {
7
- tags: ["Streaming"],
8
- summary: "Batch sign messages with sub-millisecond throughput",
9
- description: "Process high-throughput batch digital signing operations over multiple payloads.",
10
- body: {
11
- type: "object",
12
- required: ["items"],
13
- additionalProperties: false,
14
- properties: {
15
- items: {
16
- type: "array",
17
- minItems: 1,
18
- maxItems: 1000,
19
- items: {
20
- type: "object",
21
- required: ["id", "message", "privateKey"],
22
- additionalProperties: false,
23
- properties: {
24
- id: { type: "string" },
25
- message: { type: "string", minLength: 1 },
26
- privateKey: { type: "string", minLength: 64, maxLength: 64 },
27
- },
28
- },
29
- },
30
- },
31
- },
32
- };
33
- const VERIFY_SCHEMA = {
34
- tags: ["Streaming"],
35
- summary: "Batch verify digital signatures",
36
- description: "Batch verification pipeline for high-concurrency payment and transaction verification.",
37
- body: {
38
- type: "object",
39
- required: ["items"],
40
- additionalProperties: false,
41
- properties: {
42
- items: {
43
- type: "array",
44
- minItems: 1,
45
- maxItems: 1000,
46
- items: {
47
- type: "object",
48
- required: ["id", "message", "signature", "publicKey"],
49
- additionalProperties: false,
50
- properties: {
51
- id: { type: "string" },
52
- message: { type: "string", minLength: 1 },
53
- signature: { type: "string", minLength: 1 },
54
- publicKey: { type: "string", minLength: 64, maxLength: 64 },
55
- },
56
- },
57
- },
58
- },
59
- },
60
- };
61
- const ISO20022_SCHEMA = {
62
- tags: ["Wholesale Payments"],
63
- summary: "Verify ISO 20022 post-quantum dual-signature envelope",
64
- description: "Validates the payload digest and both the Ed25519 and ML-DSA signatures of a pacs.008, pain.001 or camt.053 envelope. Signatures are checked against trustedKeys, the signer's public keys from the caller's own registry; the public keys inside the envelope are ignored.",
65
- body: {
66
- type: "object",
67
- required: ["envelope", "payload", "trustedKeys"],
68
- additionalProperties: false,
69
- properties: {
70
- trustedKeys: {
71
- type: "object",
72
- required: ["classicalPublicKey", "postQuantumPublicKey"],
73
- additionalProperties: false,
74
- properties: {
75
- classicalPublicKey: { type: "string", minLength: 1 },
76
- postQuantumPublicKey: { type: "string", minLength: 1 },
77
- },
78
- },
79
- envelope: {
80
- type: "object",
81
- required: [
82
- "messageId",
83
- "messageType",
84
- "payloadDigest",
85
- "digestAlgorithm",
86
- "timestamp",
87
- "classical",
88
- "postQuantum",
89
- ],
90
- additionalProperties: false,
91
- properties: {
92
- messageId: { type: "string" },
93
- messageType: {
94
- type: "string",
95
- enum: ["pacs.008", "pain.001", "camt.053", "generic"],
96
- },
97
- payloadDigest: { type: "string" },
98
- digestAlgorithm: {
99
- type: "string",
100
- enum: ["sha256", "sha384", "sha512"],
101
- },
102
- timestamp: { type: "string" },
103
- classical: {
104
- type: "object",
105
- required: ["algorithm", "signature", "publicKey"],
106
- additionalProperties: false,
107
- properties: {
108
- algorithm: { type: "string" },
109
- signature: { type: "string" },
110
- publicKey: { type: "string" },
111
- },
112
- },
113
- postQuantum: {
114
- type: "object",
115
- required: ["algorithm", "signature", "publicKey"],
116
- additionalProperties: false,
117
- properties: {
118
- algorithm: {
119
- type: "string",
120
- enum: ["ml-dsa-44", "ml-dsa-65", "ml-dsa-87"],
121
- },
122
- signature: { type: "string" },
123
- publicKey: { type: "string" },
124
- },
125
- },
126
- },
127
- },
128
- payload: {},
129
- },
130
- },
131
- };
7
+ const keys_1 = require("../../utils/keys");
8
+ const stream_schemas_1 = require("./stream-schemas");
9
+ async function resolveBatchKeys(request, items) {
10
+ const keys = new Map();
11
+ for (const keyId of new Set(items.map((i) => i.keyId))) {
12
+ keys.set(keyId, await (0, keys_1.resolveKey)(request, keyId, ["ed25519"]));
13
+ }
14
+ return keys;
15
+ }
132
16
  function registerBatchSign(app) {
133
- 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
- }
17
+ app.post("/v2/stream/sign", { schema: stream_schemas_1.SIGN_SCHEMA }, async (request, reply) => {
18
+ const { items } = request.body;
19
+ const keys = await resolveBatchKeys(request, items);
20
+ const signatures = items.map((item) => {
21
+ const key = keys.get(item.keyId);
22
+ const res = (0, crypto_lib_1.ed25519Sign)(key.privateParts["privateKey"], item.message);
23
+ return {
24
+ id: item.id,
25
+ signature: res.signature,
26
+ algorithm: "ed25519",
27
+ };
28
+ });
29
+ return reply.send({
30
+ data: {
31
+ count: signatures.length,
32
+ signatures,
33
+ },
34
+ });
154
35
  });
155
36
  }
156
37
  function registerBatchVerify(app) {
157
- app.post("/v2/stream/verify", { schema: VERIFY_SCHEMA }, async (request, reply) => {
38
+ app.post("/v2/stream/verify", {
39
+ schema: stream_schemas_1.VERIFY_SCHEMA,
40
+ config: { rateLimit: { max: 100, timeWindow: "1 minute" } },
41
+ }, async (request, reply) => {
158
42
  try {
159
43
  const { items } = request.body;
160
44
  let validCount = 0;
@@ -184,10 +68,13 @@ function registerBatchVerify(app) {
184
68
  });
185
69
  }
186
70
  function registerIso20022Verify(app) {
187
- app.post("/v2/stream/iso20022", { schema: ISO20022_SCHEMA }, async (request, reply) => {
71
+ app.post("/v2/stream/iso20022", {
72
+ schema: stream_schemas_1.ISO20022_SCHEMA,
73
+ config: { rateLimit: { max: 100, timeWindow: "1 minute" } },
74
+ }, async (request, reply) => {
188
75
  try {
189
76
  const { envelope, payload, trustedKeys } = request.body;
190
- const result = (0, iso20022_1.verifyIso20022Payment)(envelope, payload, {
77
+ const result = (0, protocols_1.verifyIso20022Payment)(envelope, payload, {
191
78
  classicalPublicKeyHex: trustedKeys.classicalPublicKey,
192
79
  postQuantumPublicKeyHex: trustedKeys.postQuantumPublicKey,
193
80
  });
@@ -198,9 +85,115 @@ function registerIso20022Verify(app) {
198
85
  }
199
86
  });
200
87
  }
88
+ function registerStreamPqEncrypt(app) {
89
+ app.post("/v2/stream/pq-encrypt", { schema: stream_schemas_1.STREAM_PQ_ENCRYPT_SCHEMA }, async (request, reply) => {
90
+ try {
91
+ const { x25519PublicKey, mlKemPublicKey, plaintext, chunkSize } = request.body;
92
+ const ptBytes = Buffer.from(plaintext, "utf8");
93
+ const res = (0, streaming_1.streamPqEncrypt)({
94
+ recipientX25519Public: x25519PublicKey,
95
+ recipientMlKemPublic: mlKemPublicKey,
96
+ plaintext: ptBytes,
97
+ ...(chunkSize !== undefined ? { chunkSize } : {}),
98
+ });
99
+ return reply.send({
100
+ data: {
101
+ ciphertext: Buffer.from(res.ciphertext).toString("base64"),
102
+ algorithm: res.algorithm,
103
+ },
104
+ });
105
+ }
106
+ catch (error) {
107
+ return (0, route_helpers_1.classifyCryptoError)(error, request, reply, "PQ stream encryption");
108
+ }
109
+ });
110
+ }
111
+ function registerStreamPqDecrypt(app) {
112
+ app.post("/v2/stream/pq-decrypt", { schema: stream_schemas_1.STREAM_PQ_DECRYPT_SCHEMA }, async (request, reply) => {
113
+ const { keyId, ciphertext, chunkSize } = request.body;
114
+ const key = await (0, keys_1.resolveKey)(request, keyId, ["x25519-ml-kem-768"]);
115
+ try {
116
+ const ctBytes = Buffer.from(ciphertext, "base64");
117
+ const decrypted = (0, streaming_1.streamPqDecrypt)({
118
+ recipientX25519Secret: key.privateParts["x25519PrivateKey"],
119
+ recipientMlKemSecret: key.privateParts["mlKemSecretKey"],
120
+ ciphertext: ctBytes,
121
+ ...(chunkSize !== undefined ? { chunkSize } : {}),
122
+ });
123
+ const plaintext = Buffer.from(decrypted).toString("utf8");
124
+ (0, crypto_lib_1.wipeMemory)(decrypted);
125
+ return reply.send({ data: { plaintext } });
126
+ }
127
+ catch (error) {
128
+ return (0, route_helpers_1.classifyCryptoError)(error, request, reply, "PQ stream decryption");
129
+ }
130
+ });
131
+ }
132
+ function registerStreamMultiPqEncrypt(app) {
133
+ app.post("/v2/stream/multi-pq-encrypt", { schema: stream_schemas_1.STREAM_MULTI_PQ_ENCRYPT_SCHEMA }, async (request, reply) => {
134
+ try {
135
+ const { recipients, plaintext, chunkSize } = request.body;
136
+ const ptBytes = Buffer.from(plaintext, "utf8");
137
+ const res = (0, streaming_1.streamMultiPqEncrypt)({
138
+ recipients: recipients.map((r) => ({
139
+ recipientId: r.recipientId,
140
+ recipientX25519Public: r.x25519PublicKey,
141
+ recipientMlKemPublic: r.mlKemPublicKey,
142
+ })),
143
+ plaintext: ptBytes,
144
+ ...(chunkSize !== undefined ? { chunkSize } : {}),
145
+ });
146
+ (0, crypto_lib_1.wipeMemory)(ptBytes);
147
+ return reply.send({
148
+ data: {
149
+ ciphertext: Buffer.from(res.ciphertext).toString("base64"),
150
+ algorithm: res.algorithm,
151
+ recipientCount: res.recipientCount,
152
+ },
153
+ });
154
+ }
155
+ catch (error) {
156
+ return (0, route_helpers_1.classifyCryptoError)(error, request, reply, "Multi-PQ stream encryption");
157
+ }
158
+ });
159
+ }
160
+ function registerStreamMultiPqDecrypt(app) {
161
+ app.post("/v2/stream/multi-pq-decrypt", {
162
+ schema: stream_schemas_1.STREAM_MULTI_PQ_DECRYPT_SCHEMA,
163
+ config: { rateLimit: { max: 100, timeWindow: "1 minute" } },
164
+ }, async (request, reply) => {
165
+ const { keyId, recipientId, ciphertext, chunkSize } = request.body;
166
+ const key = await (0, keys_1.resolveKey)(request, keyId, ["x25519-ml-kem-768"]);
167
+ try {
168
+ const ctBytes = Buffer.from(ciphertext, "base64");
169
+ const decrypted = (0, streaming_1.streamMultiPqDecrypt)({
170
+ ...(recipientId !== undefined ? { recipientId } : {}),
171
+ recipientX25519Secret: key.privateParts["x25519PrivateKey"],
172
+ recipientMlKemSecret: key.privateParts["mlKemSecretKey"],
173
+ ciphertext: ctBytes,
174
+ ...(chunkSize !== undefined ? { chunkSize } : {}),
175
+ });
176
+ const plaintext = Buffer.from(decrypted.plaintext).toString("utf8");
177
+ (0, crypto_lib_1.wipeMemory)(decrypted.plaintext);
178
+ return reply.send({
179
+ data: {
180
+ plaintext,
181
+ recipientId: decrypted.recipientId,
182
+ },
183
+ });
184
+ }
185
+ catch (error) {
186
+ return (0, route_helpers_1.classifyCryptoError)(error, request, reply, "Multi-PQ stream decryption");
187
+ }
188
+ });
189
+ }
201
190
  exports.default = (app) => {
202
191
  registerBatchSign(app);
203
192
  registerBatchVerify(app);
204
193
  registerIso20022Verify(app);
194
+ registerStreamPqEncrypt(app);
195
+ registerStreamPqDecrypt(app);
196
+ registerStreamMultiPqEncrypt(app);
197
+ registerStreamMultiPqDecrypt(app);
205
198
  };
206
199
  //# sourceMappingURL=stream.js.map
@@ -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":"AA6CA,OAAO,KAAK,OAAO,MAAM,SAAS,CAAC;AAuCnC,iBAAe,IAAI,IAAI,OAAO,CAAC,OAAO,CAAC,eAAe,CAAC,CAgFtD;AAED,OAAO,EAAE,IAAI,EAAE,CAAC"}
package/dist/server.js CHANGED
@@ -50,12 +50,15 @@ 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");
58
+ const trace_1 = require("./utils/trace");
59
+ const opaque_store_1 = require("./lib/opaque-store");
54
60
  const routes_1 = __importDefault(require("./routes"));
55
61
  const fastify = __importStar(require("fastify"));
56
- function isPublicPath(url) {
57
- return (0, constants_1.isProbePath)(url) || url.startsWith("/docs");
58
- }
59
62
  async function registerDocs(app) {
60
63
  await app.register(swagger_1.default, {
61
64
  openapi: {
@@ -82,10 +85,16 @@ async function registerDocs(app) {
82
85
  }
83
86
  async function init() {
84
87
  const app = fastify.fastify(constants_1.fastifyOptions);
88
+ const registered = [];
89
+ app.addHook("onRoute", (route) => {
90
+ registered.push({ method: route.method, url: route.url });
91
+ });
85
92
  logger_1.default.info("\n\nEnvironment details: " + constants_1.consoleOutput);
93
+ (0, problem_1.registerProblemHandlers)(app);
86
94
  app.addHook("onRequest", async (request, reply) => {
87
95
  const reqId = request.headers["x-request-id"] ?? (0, crypto_1.randomUUID)();
88
96
  reply.header("x-request-id", reqId);
97
+ (0, trace_1.propagateTraceContext)(request, reply);
89
98
  });
90
99
  app.addHook("onSend", async (request, reply) => {
91
100
  if (request.url.startsWith("/v2/")) {
@@ -107,18 +116,23 @@ async function init() {
107
116
  });
108
117
  await (0, auth_1.registerAuth)(app);
109
118
  app.addHook("onRequest", async (request, reply) => {
110
- if (isPublicPath(request.url))
119
+ if ((0, auth_policy_1.isPublicRoute)(request.url))
111
120
  return;
112
121
  const auth = await (0, auth_1.authenticate)(request, reply);
113
- if (!auth)
122
+ if (!auth || !(0, auth_1.authorizeRoute)(request, reply, auth))
114
123
  return reply;
115
124
  request.auth = auth;
116
125
  });
126
+ app.decorate("keyStore", (0, key_store_1.keyStoreFromEnv)());
127
+ app.decorate("kdf", new kdf_runner_1.KdfRunner());
128
+ app.decorate("opaqueStore", new opaque_store_1.OpaqueStore());
129
+ app.addHook("onClose", () => app.kdf.close());
117
130
  (0, metering_1.registerMetering)(app);
118
131
  await app.register(async (scope) => {
119
132
  (0, routes_1.default)(scope);
120
133
  });
121
134
  await app.ready();
135
+ (0, auth_policy_1.assertRoutesCovered)(registered);
122
136
  return app;
123
137
  }
124
138
  //# 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