was-teaching-server 0.9.1

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 (295) hide show
  1. package/CHANGELOG.md +1432 -0
  2. package/LICENSE +661 -0
  3. package/README.md +305 -0
  4. package/common/css/materialize.1.0.0.min.css +13 -0
  5. package/dist/auth-header-hooks.d.ts +84 -0
  6. package/dist/auth-header-hooks.d.ts.map +1 -0
  7. package/dist/auth-header-hooks.js +136 -0
  8. package/dist/auth-header-hooks.js.map +1 -0
  9. package/dist/authorize.d.ts +45 -0
  10. package/dist/authorize.d.ts.map +1 -0
  11. package/dist/authorize.js +76 -0
  12. package/dist/authorize.js.map +1 -0
  13. package/dist/backends/filesystem.d.ts +1179 -0
  14. package/dist/backends/filesystem.d.ts.map +1 -0
  15. package/dist/backends/filesystem.js +2727 -0
  16. package/dist/backends/filesystem.js.map +1 -0
  17. package/dist/backends/postgres.d.ts +833 -0
  18. package/dist/backends/postgres.d.ts.map +1 -0
  19. package/dist/backends/postgres.js +2164 -0
  20. package/dist/backends/postgres.js.map +1 -0
  21. package/dist/backends/postgresSchema.d.ts +36 -0
  22. package/dist/backends/postgresSchema.d.ts.map +1 -0
  23. package/dist/backends/postgresSchema.js +203 -0
  24. package/dist/backends/postgresSchema.js.map +1 -0
  25. package/dist/config.default.d.ts +360 -0
  26. package/dist/config.default.d.ts.map +1 -0
  27. package/dist/config.default.js +493 -0
  28. package/dist/config.default.js.map +1 -0
  29. package/dist/corsProxy.d.ts +45 -0
  30. package/dist/corsProxy.d.ts.map +1 -0
  31. package/dist/corsProxy.js +278 -0
  32. package/dist/corsProxy.js.map +1 -0
  33. package/dist/digest.d.ts +45 -0
  34. package/dist/digest.d.ts.map +1 -0
  35. package/dist/digest.js +174 -0
  36. package/dist/digest.js.map +1 -0
  37. package/dist/errors.d.ts +654 -0
  38. package/dist/errors.d.ts.map +1 -0
  39. package/dist/errors.js +960 -0
  40. package/dist/errors.js.map +1 -0
  41. package/dist/index.d.ts +22 -0
  42. package/dist/index.d.ts.map +1 -0
  43. package/dist/index.js +21 -0
  44. package/dist/index.js.map +1 -0
  45. package/dist/lib/atomicFile.d.ts +61 -0
  46. package/dist/lib/atomicFile.d.ts.map +1 -0
  47. package/dist/lib/atomicFile.js +150 -0
  48. package/dist/lib/atomicFile.js.map +1 -0
  49. package/dist/lib/backendRegistry.d.ts +59 -0
  50. package/dist/lib/backendRegistry.d.ts.map +1 -0
  51. package/dist/lib/backendRegistry.js +92 -0
  52. package/dist/lib/backendRegistry.js.map +1 -0
  53. package/dist/lib/backendUsage.d.ts +28 -0
  54. package/dist/lib/backendUsage.d.ts.map +1 -0
  55. package/dist/lib/backendUsage.js +55 -0
  56. package/dist/lib/backendUsage.js.map +1 -0
  57. package/dist/lib/backends.d.ts +142 -0
  58. package/dist/lib/backends.d.ts.map +1 -0
  59. package/dist/lib/backends.js +272 -0
  60. package/dist/lib/backends.js.map +1 -0
  61. package/dist/lib/blindedIndex.d.ts +156 -0
  62. package/dist/lib/blindedIndex.d.ts.map +1 -0
  63. package/dist/lib/blindedIndex.js +338 -0
  64. package/dist/lib/blindedIndex.js.map +1 -0
  65. package/dist/lib/collectionDescription.d.ts +33 -0
  66. package/dist/lib/collectionDescription.d.ts.map +1 -0
  67. package/dist/lib/collectionDescription.js +21 -0
  68. package/dist/lib/collectionDescription.js.map +1 -0
  69. package/dist/lib/cursor.d.ts +18 -0
  70. package/dist/lib/cursor.d.ts.map +1 -0
  71. package/dist/lib/cursor.js +50 -0
  72. package/dist/lib/cursor.js.map +1 -0
  73. package/dist/lib/edvEnvelope.d.ts +50 -0
  74. package/dist/lib/edvEnvelope.d.ts.map +1 -0
  75. package/dist/lib/edvEnvelope.js +106 -0
  76. package/dist/lib/edvEnvelope.js.map +1 -0
  77. package/dist/lib/encryption.d.ts +192 -0
  78. package/dist/lib/encryption.d.ts.map +1 -0
  79. package/dist/lib/encryption.js +383 -0
  80. package/dist/lib/encryption.js.map +1 -0
  81. package/dist/lib/etag.d.ts +33 -0
  82. package/dist/lib/etag.d.ts.map +1 -0
  83. package/dist/lib/etag.js +36 -0
  84. package/dist/lib/etag.js.map +1 -0
  85. package/dist/lib/exportManifest.d.ts +29 -0
  86. package/dist/lib/exportManifest.d.ts.map +1 -0
  87. package/dist/lib/exportManifest.js +77 -0
  88. package/dist/lib/exportManifest.js.map +1 -0
  89. package/dist/lib/importTar.d.ts +81 -0
  90. package/dist/lib/importTar.d.ts.map +1 -0
  91. package/dist/lib/importTar.js +301 -0
  92. package/dist/lib/importTar.js.map +1 -0
  93. package/dist/lib/isJson.d.ts +16 -0
  94. package/dist/lib/isJson.d.ts.map +1 -0
  95. package/dist/lib/isJson.js +17 -0
  96. package/dist/lib/isJson.js.map +1 -0
  97. package/dist/lib/keyEpoch.d.ts +38 -0
  98. package/dist/lib/keyEpoch.d.ts.map +1 -0
  99. package/dist/lib/keyEpoch.js +75 -0
  100. package/dist/lib/keyEpoch.js.map +1 -0
  101. package/dist/lib/keyedMutex.d.ts +28 -0
  102. package/dist/lib/keyedMutex.d.ts.map +1 -0
  103. package/dist/lib/keyedMutex.js +45 -0
  104. package/dist/lib/keyedMutex.js.map +1 -0
  105. package/dist/lib/kmsModule.d.ts +62 -0
  106. package/dist/lib/kmsModule.d.ts.map +1 -0
  107. package/dist/lib/kmsModule.js +327 -0
  108. package/dist/lib/kmsModule.js.map +1 -0
  109. package/dist/lib/kmsRecordCipher.d.ts +82 -0
  110. package/dist/lib/kmsRecordCipher.d.ts.map +1 -0
  111. package/dist/lib/kmsRecordCipher.js +226 -0
  112. package/dist/lib/kmsRecordCipher.js.map +1 -0
  113. package/dist/lib/pagination.d.ts +19 -0
  114. package/dist/lib/pagination.d.ts.map +1 -0
  115. package/dist/lib/pagination.js +21 -0
  116. package/dist/lib/pagination.js.map +1 -0
  117. package/dist/lib/paths.d.ts +273 -0
  118. package/dist/lib/paths.d.ts.map +1 -0
  119. package/dist/lib/paths.js +270 -0
  120. package/dist/lib/paths.js.map +1 -0
  121. package/dist/lib/preconditions.d.ts +65 -0
  122. package/dist/lib/preconditions.d.ts.map +1 -0
  123. package/dist/lib/preconditions.js +112 -0
  124. package/dist/lib/preconditions.js.map +1 -0
  125. package/dist/lib/resourceFileName.d.ts +41 -0
  126. package/dist/lib/resourceFileName.d.ts.map +1 -0
  127. package/dist/lib/resourceFileName.js +58 -0
  128. package/dist/lib/resourceFileName.js.map +1 -0
  129. package/dist/lib/revocations.d.ts +49 -0
  130. package/dist/lib/revocations.d.ts.map +1 -0
  131. package/dist/lib/revocations.js +86 -0
  132. package/dist/lib/revocations.js.map +1 -0
  133. package/dist/lib/validateDid.d.ts +19 -0
  134. package/dist/lib/validateDid.d.ts.map +1 -0
  135. package/dist/lib/validateDid.js +35 -0
  136. package/dist/lib/validateDid.js.map +1 -0
  137. package/dist/lib/validateId.d.ts +66 -0
  138. package/dist/lib/validateId.d.ts.map +1 -0
  139. package/dist/lib/validateId.js +126 -0
  140. package/dist/lib/validateId.js.map +1 -0
  141. package/dist/plugin.d.ts +115 -0
  142. package/dist/plugin.d.ts.map +1 -0
  143. package/dist/plugin.js +134 -0
  144. package/dist/plugin.js.map +1 -0
  145. package/dist/policy.d.ts +78 -0
  146. package/dist/policy.d.ts.map +1 -0
  147. package/dist/policy.js +115 -0
  148. package/dist/policy.js.map +1 -0
  149. package/dist/provisioning.d.ts +38 -0
  150. package/dist/provisioning.d.ts.map +1 -0
  151. package/dist/provisioning.js +72 -0
  152. package/dist/provisioning.js.map +1 -0
  153. package/dist/requests/BackendRequest.d.ts +61 -0
  154. package/dist/requests/BackendRequest.d.ts.map +1 -0
  155. package/dist/requests/BackendRequest.js +152 -0
  156. package/dist/requests/BackendRequest.js.map +1 -0
  157. package/dist/requests/CollectionRequest.d.ts +234 -0
  158. package/dist/requests/CollectionRequest.d.ts.map +1 -0
  159. package/dist/requests/CollectionRequest.js +705 -0
  160. package/dist/requests/CollectionRequest.js.map +1 -0
  161. package/dist/requests/KeyRequest.d.ts +115 -0
  162. package/dist/requests/KeyRequest.d.ts.map +1 -0
  163. package/dist/requests/KeyRequest.js +468 -0
  164. package/dist/requests/KeyRequest.js.map +1 -0
  165. package/dist/requests/KeystoreRequest.d.ts +99 -0
  166. package/dist/requests/KeystoreRequest.d.ts.map +1 -0
  167. package/dist/requests/KeystoreRequest.js +247 -0
  168. package/dist/requests/KeystoreRequest.js.map +1 -0
  169. package/dist/requests/PolicyRequest.d.ts +53 -0
  170. package/dist/requests/PolicyRequest.d.ts.map +1 -0
  171. package/dist/requests/PolicyRequest.js +124 -0
  172. package/dist/requests/PolicyRequest.js.map +1 -0
  173. package/dist/requests/ResourceRequest.d.ts +134 -0
  174. package/dist/requests/ResourceRequest.d.ts.map +1 -0
  175. package/dist/requests/ResourceRequest.js +548 -0
  176. package/dist/requests/ResourceRequest.js.map +1 -0
  177. package/dist/requests/RevocationRequest.d.ts +67 -0
  178. package/dist/requests/RevocationRequest.d.ts.map +1 -0
  179. package/dist/requests/RevocationRequest.js +187 -0
  180. package/dist/requests/RevocationRequest.js.map +1 -0
  181. package/dist/requests/SpaceRequest.d.ts +204 -0
  182. package/dist/requests/SpaceRequest.d.ts.map +1 -0
  183. package/dist/requests/SpaceRequest.js +520 -0
  184. package/dist/requests/SpaceRequest.js.map +1 -0
  185. package/dist/requests/SpacesRepositoryRequest.d.ts +50 -0
  186. package/dist/requests/SpacesRepositoryRequest.d.ts.map +1 -0
  187. package/dist/requests/SpacesRepositoryRequest.js +159 -0
  188. package/dist/requests/SpacesRepositoryRequest.js.map +1 -0
  189. package/dist/requests/collectionContext.d.ts +21 -0
  190. package/dist/requests/collectionContext.d.ts.map +1 -0
  191. package/dist/requests/collectionContext.js +30 -0
  192. package/dist/requests/collectionContext.js.map +1 -0
  193. package/dist/requests/controllerConsent.d.ts +58 -0
  194. package/dist/requests/controllerConsent.d.ts.map +1 -0
  195. package/dist/requests/controllerConsent.js +73 -0
  196. package/dist/requests/controllerConsent.js.map +1 -0
  197. package/dist/requests/keystoreContext.d.ts +68 -0
  198. package/dist/requests/keystoreContext.d.ts.map +1 -0
  199. package/dist/requests/keystoreContext.js +87 -0
  200. package/dist/requests/keystoreContext.js.map +1 -0
  201. package/dist/requests/resourceInput.d.ts +19 -0
  202. package/dist/requests/resourceInput.d.ts.map +1 -0
  203. package/dist/requests/resourceInput.js +102 -0
  204. package/dist/requests/resourceInput.js.map +1 -0
  205. package/dist/requests/spaceContext.d.ts +127 -0
  206. package/dist/requests/spaceContext.d.ts.map +1 -0
  207. package/dist/requests/spaceContext.js +201 -0
  208. package/dist/requests/spaceContext.js.map +1 -0
  209. package/dist/routes.d.ts +55 -0
  210. package/dist/routes.d.ts.map +1 -0
  211. package/dist/routes.js +306 -0
  212. package/dist/routes.js.map +1 -0
  213. package/dist/server.d.ts +20 -0
  214. package/dist/server.d.ts.map +1 -0
  215. package/dist/server.js +60 -0
  216. package/dist/server.js.map +1 -0
  217. package/dist/start.d.ts +7 -0
  218. package/dist/start.d.ts.map +1 -0
  219. package/dist/start.js +61 -0
  220. package/dist/start.js.map +1 -0
  221. package/dist/storage.d.ts +29 -0
  222. package/dist/storage.d.ts.map +1 -0
  223. package/dist/storage.js +47 -0
  224. package/dist/storage.js.map +1 -0
  225. package/dist/types.d.ts +915 -0
  226. package/dist/types.d.ts.map +1 -0
  227. package/dist/types.js +2 -0
  228. package/dist/types.js.map +1 -0
  229. package/dist/views/home.hbs +9 -0
  230. package/dist/views/templates/main.hbs +12 -0
  231. package/dist/zcap.d.ts +267 -0
  232. package/dist/zcap.d.ts.map +1 -0
  233. package/dist/zcap.js +478 -0
  234. package/dist/zcap.js.map +1 -0
  235. package/package.json +109 -0
  236. package/src/auth-header-hooks.ts +163 -0
  237. package/src/authorize.ts +117 -0
  238. package/src/backends/filesystem.ts +3626 -0
  239. package/src/backends/postgres.ts +3073 -0
  240. package/src/backends/postgresSchema.ts +231 -0
  241. package/src/config.default.ts +657 -0
  242. package/src/corsProxy.ts +326 -0
  243. package/src/declarations.d.ts +28 -0
  244. package/src/digest.ts +223 -0
  245. package/src/errors.ts +1122 -0
  246. package/src/index.ts +21 -0
  247. package/src/lib/atomicFile.ts +176 -0
  248. package/src/lib/backendRegistry.ts +139 -0
  249. package/src/lib/backendUsage.ts +79 -0
  250. package/src/lib/backends.ts +351 -0
  251. package/src/lib/blindedIndex.ts +466 -0
  252. package/src/lib/collectionDescription.ts +43 -0
  253. package/src/lib/cursor.ts +52 -0
  254. package/src/lib/edvEnvelope.ts +120 -0
  255. package/src/lib/encryption.ts +472 -0
  256. package/src/lib/etag.ts +40 -0
  257. package/src/lib/exportManifest.ts +115 -0
  258. package/src/lib/importTar.ts +381 -0
  259. package/src/lib/isJson.ts +18 -0
  260. package/src/lib/keyEpoch.ts +89 -0
  261. package/src/lib/keyedMutex.ts +45 -0
  262. package/src/lib/kmsModule.ts +451 -0
  263. package/src/lib/kmsRecordCipher.ts +292 -0
  264. package/src/lib/pagination.ts +22 -0
  265. package/src/lib/paths.ts +382 -0
  266. package/src/lib/preconditions.ts +145 -0
  267. package/src/lib/resourceFileName.ts +69 -0
  268. package/src/lib/revocations.ts +116 -0
  269. package/src/lib/validateDid.ts +41 -0
  270. package/src/lib/validateId.ts +148 -0
  271. package/src/plugin.ts +288 -0
  272. package/src/policy.ts +176 -0
  273. package/src/provisioning.ts +98 -0
  274. package/src/requests/BackendRequest.ts +219 -0
  275. package/src/requests/CollectionRequest.ts +899 -0
  276. package/src/requests/KeyRequest.ts +617 -0
  277. package/src/requests/KeystoreRequest.ts +325 -0
  278. package/src/requests/PolicyRequest.ts +169 -0
  279. package/src/requests/ResourceRequest.ts +649 -0
  280. package/src/requests/RevocationRequest.ts +267 -0
  281. package/src/requests/SpaceRequest.ts +686 -0
  282. package/src/requests/SpacesRepositoryRequest.ts +194 -0
  283. package/src/requests/collectionContext.ts +41 -0
  284. package/src/requests/controllerConsent.ts +108 -0
  285. package/src/requests/keystoreContext.ts +124 -0
  286. package/src/requests/resourceInput.ts +118 -0
  287. package/src/requests/spaceContext.ts +311 -0
  288. package/src/routes.ts +420 -0
  289. package/src/server.ts +69 -0
  290. package/src/start.ts +71 -0
  291. package/src/storage.ts +60 -0
  292. package/src/types.ts +1016 -0
  293. package/src/views/home.hbs +9 -0
  294. package/src/views/templates/main.hbs +12 -0
  295. package/src/zcap.ts +657 -0
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Helpers for the client-declared key-epoch stamp on encrypted-Collection
3
+ * Resources (the `key-epochs` feature). A writer declares which epoch key
4
+ * encrypted a Resource's content so a reader can pick the right key BEFORE
5
+ * attempting decryption; the value is advisory metadata the server stores
6
+ * opaquely and never computes or verifies (a write may race a rotation).
7
+ *
8
+ * On a **content** write the epoch is declared via the `WAS-Key-Epoch` request
9
+ * header -- one mechanism that works uniformly for JSON, raw-stream binary, and
10
+ * multipart writes. It is deliberately NOT signature-covered (like `If-Match`):
11
+ * it is advisory client-declared metadata, not part of the capability
12
+ * invocation. A content write stores the header's value when present and CLEARS
13
+ * the stamp when absent -- the new ciphertext's epoch is unknown, and a stale
14
+ * stamp is worse than none (a reader falls back to `currentEpoch` and can try
15
+ * other epochs). A `PUT .../meta` may also declare `epoch` as a top-level member
16
+ * of the body (a sibling of `custom`), where omitting it PRESERVES the stored
17
+ * value -- see the `putMeta` handler.
18
+ *
19
+ * The only validation is that a present value is a non-empty string (400
20
+ * otherwise); the server never checks it against the Collection marker's epochs.
21
+ */
22
+ import { InvalidRequestBodyError } from '../errors.js';
23
+ /** The request header carrying the client-declared key-epoch on a content write. */
24
+ export const KEY_EPOCH_HEADER = 'was-key-epoch';
25
+ /**
26
+ * Parses the OPTIONAL `WAS-Key-Epoch` request header into the epoch stamp for a
27
+ * content write. Resolves `{ epoch: string }` for a non-empty single-valued
28
+ * header, `{ epoch: undefined }` when absent (the content write clears any
29
+ * stored stamp), and throws `invalid-request-body` (400) for an empty or
30
+ * array-valued header.
31
+ * @param options {object}
32
+ * @param options.headers {object} the Fastify request headers
33
+ * @param [options.requestName] {string} request name for the 400 error title
34
+ * @returns {{ epoch?: string }}
35
+ */
36
+ export function parseKeyEpochHeader({ headers, requestName }) {
37
+ const value = headers[KEY_EPOCH_HEADER];
38
+ if (value === undefined) {
39
+ return {};
40
+ }
41
+ if (typeof value !== 'string' || value.length === 0) {
42
+ throw new InvalidRequestBodyError({
43
+ requestName,
44
+ detail: 'The "WAS-Key-Epoch" header must be a non-empty string.'
45
+ });
46
+ }
47
+ return { epoch: value };
48
+ }
49
+ /**
50
+ * Validates and extracts the OPTIONAL top-level `epoch` member of an Update
51
+ * Resource Metadata (`PUT .../meta`) body. Unlike a content write, a metadata
52
+ * write PRESERVES the stored epoch when the member is omitted (the stamp
53
+ * describes the content write, not the metadata write); a present value must be
54
+ * a non-empty string (400 otherwise). Returns `{ epoch: string }` when supplied,
55
+ * or `{}` when the member is absent (preserve).
56
+ * @param options {object}
57
+ * @param options.body {object} the parsed request body (already known to be an object)
58
+ * @param [options.requestName] {string} request name for the 400 error title
59
+ * @returns {{ epoch?: string }}
60
+ */
61
+ export function parseMetaEpoch({ body, requestName }) {
62
+ if (!Object.hasOwn(body, 'epoch')) {
63
+ return {};
64
+ }
65
+ const value = body.epoch;
66
+ if (typeof value !== 'string' || value.length === 0) {
67
+ throw new InvalidRequestBodyError({
68
+ requestName,
69
+ detail: 'The "epoch" property must be a non-empty string.',
70
+ pointer: '/epoch'
71
+ });
72
+ }
73
+ return { epoch: value };
74
+ }
75
+ //# sourceMappingURL=keyEpoch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keyEpoch.js","sourceRoot":"","sources":["../../src/lib/keyEpoch.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,OAAO,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAA;AAEtD,oFAAoF;AACpF,MAAM,CAAC,MAAM,gBAAgB,GAAG,eAAe,CAAA;AAE/C;;;;;;;;;;GAUG;AACH,MAAM,UAAU,mBAAmB,CAAC,EAClC,OAAO,EACP,WAAW,EAIZ;IACC,MAAM,KAAK,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAA;IACvC,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QACxB,OAAO,EAAE,CAAA;IACX,CAAC;IACD,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACpD,MAAM,IAAI,uBAAuB,CAAC;YAChC,WAAW;YACX,MAAM,EAAE,wDAAwD;SACjE,CAAC,CAAA;IACJ,CAAC;IACD,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,CAAA;AACzB,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,cAAc,CAAC,EAC7B,IAAI,EACJ,WAAW,EAIZ;IACC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,EAAE,CAAC;QAClC,OAAO,EAAE,CAAA;IACX,CAAC;IACD,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAA;IACxB,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACpD,MAAM,IAAI,uBAAuB,CAAC;YAChC,WAAW;YACX,MAAM,EAAE,kDAAkD;YAC1D,OAAO,EAAE,QAAQ;SAClB,CAAC,CAAA;IACJ,CAAC;IACD,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,CAAA;AACzB,CAAC"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * A keyed in-process mutex that serializes async functions per key, ported from
3
+ * the `@interop/edv-server` storage layer. The conditional-write path uses it to
4
+ * make a Resource's read-check-write atomic: two concurrent writers of the same
5
+ * Resource cannot both observe the same prior version and both succeed.
6
+ *
7
+ * This is a single-instance lock only. It does NOT coordinate writes across
8
+ * multiple server processes or a horizontally-scaled deployment -- that is out
9
+ * of scope for the reference server (see the spec's Conditional Requests note).
10
+ */
11
+ /**
12
+ * Serializes async functions per key. `run` chains the given function onto the
13
+ * tail of the key's promise queue, so all functions for the same key execute
14
+ * strictly one at a time, in call order. Distinct keys run concurrently.
15
+ */
16
+ export declare class KeyedMutex {
17
+ private readonly queues;
18
+ /**
19
+ * Runs `fn` once all previously-queued functions for `key` have settled,
20
+ * resolving (or rejecting) with `fn`'s result. The key's queue entry is
21
+ * cleaned up once it drains so the map does not grow without bound.
22
+ * @param key {string} the serialization key (e.g. a per-Resource path)
23
+ * @param fn {() => Promise<T>} the critical section to run under the lock
24
+ * @returns {Promise<T>}
25
+ */
26
+ run<T>(key: string, fn: () => Promise<T>): Promise<T>;
27
+ }
28
+ //# sourceMappingURL=keyedMutex.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keyedMutex.d.ts","sourceRoot":"","sources":["../../src/lib/keyedMutex.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH;;;;GAIG;AACH,qBAAa,UAAU;IACrB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAsC;IAE7D;;;;;;;OAOG;IACG,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;CAiB5D"}
@@ -0,0 +1,45 @@
1
+ /**
2
+ * A keyed in-process mutex that serializes async functions per key, ported from
3
+ * the `@interop/edv-server` storage layer. The conditional-write path uses it to
4
+ * make a Resource's read-check-write atomic: two concurrent writers of the same
5
+ * Resource cannot both observe the same prior version and both succeed.
6
+ *
7
+ * This is a single-instance lock only. It does NOT coordinate writes across
8
+ * multiple server processes or a horizontally-scaled deployment -- that is out
9
+ * of scope for the reference server (see the spec's Conditional Requests note).
10
+ */
11
+ /**
12
+ * Serializes async functions per key. `run` chains the given function onto the
13
+ * tail of the key's promise queue, so all functions for the same key execute
14
+ * strictly one at a time, in call order. Distinct keys run concurrently.
15
+ */
16
+ export class KeyedMutex {
17
+ queues = new Map();
18
+ /**
19
+ * Runs `fn` once all previously-queued functions for `key` have settled,
20
+ * resolving (or rejecting) with `fn`'s result. The key's queue entry is
21
+ * cleaned up once it drains so the map does not grow without bound.
22
+ * @param key {string} the serialization key (e.g. a per-Resource path)
23
+ * @param fn {() => Promise<T>} the critical section to run under the lock
24
+ * @returns {Promise<T>}
25
+ */
26
+ async run(key, fn) {
27
+ const previous = this.queues.get(key) ?? Promise.resolve();
28
+ const run = previous.then(fn, fn);
29
+ // Keep the chain alive even if `fn` rejects, so a failed critical section
30
+ // does not wedge the key's queue; track this swallowed-rejection tail so the
31
+ // queue entry can be reclaimed once it drains (unless a later call replaced
32
+ // it).
33
+ const tail = run.catch(() => { });
34
+ this.queues.set(key, tail);
35
+ try {
36
+ return await run;
37
+ }
38
+ finally {
39
+ if (this.queues.get(key) === tail) {
40
+ this.queues.delete(key);
41
+ }
42
+ }
43
+ }
44
+ }
45
+ //# sourceMappingURL=keyedMutex.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keyedMutex.js","sourceRoot":"","sources":["../../src/lib/keyedMutex.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH;;;;GAIG;AACH,MAAM,OAAO,UAAU;IACJ,MAAM,GAAG,IAAI,GAAG,EAA4B,CAAA;IAE7D;;;;;;;OAOG;IACH,KAAK,CAAC,GAAG,CAAI,GAAW,EAAE,EAAoB;QAC5C,MAAM,QAAQ,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,OAAO,CAAC,OAAO,EAAE,CAAA;QAC1D,MAAM,GAAG,GAAG,QAAQ,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,CAAC,CAAA;QACjC,0EAA0E;QAC1E,6EAA6E;QAC7E,4EAA4E;QAC5E,OAAO;QACP,MAAM,IAAI,GAAG,GAAG,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAA;QAChC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,CAAA;QAC1B,IAAI,CAAC;YACH,OAAO,MAAM,GAAG,CAAA;QAClB,CAAC;gBAAS,CAAC;YACT,IAAI,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,IAAI,EAAE,CAAC;gBAClC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAA;YACzB,CAAC;QACH,CAAC;IACH,CAAC;CACF"}
@@ -0,0 +1,62 @@
1
+ import type { IDID, KmsKeyDescription, KmsStoredKey } from '../types.js';
2
+ /**
3
+ * Builds the public key-description projection of a stored key: the
4
+ * allowlisted public fields plus the live keystore `controller` (never any
5
+ * secret field), with the `publicAlias` / expanded `publicAliasTemplate`
6
+ * override applied to `id`. Applied identically at generate time and on every
7
+ * subsequent read, so descriptions are stable (the client caches them).
8
+ *
9
+ * @param options {object}
10
+ * @param options.key {KmsStoredKey} the stored (secret-bearing) key
11
+ * @param options.controller {IDID} the keystore's current controller
12
+ * @returns {KmsKeyDescription}
13
+ */
14
+ export declare function describeKmsKey({ key, controller }: {
15
+ key: KmsStoredKey;
16
+ controller: IDID;
17
+ }): KmsKeyDescription;
18
+ /**
19
+ * Generates key material for a supported key type and assembles the stored
20
+ * key (full serialized form, secret material included) plus its public
21
+ * description. Field names are protocol-fixed: the asymmetric pairs carry
22
+ * `publicKeyMultibase` / `privateKeyMultibase`; the symmetric keys carry a
23
+ * base64url 256-bit `secret` (the webkms per-type key generators).
24
+ *
25
+ * @param options {object}
26
+ * @param options.keyId {string} the full key URL (`<keystoreId>/keys/<localId>`)
27
+ * @param options.type {string} the webkms key type
28
+ * @param options.controller {IDID} the keystore's current controller (for
29
+ * the returned description only -- never stored on the key)
30
+ * @param [options.maxCapabilityChainLength] {number}
31
+ * @param [options.publicAlias] {string}
32
+ * @param [options.publicAliasTemplate] {string}
33
+ * @returns {Promise<{ key: KmsStoredKey, keyDescription: KmsKeyDescription }>}
34
+ */
35
+ export declare function generateKmsKey({ keyId, type, controller, maxCapabilityChainLength, publicAlias, publicAliasTemplate }: {
36
+ keyId: string;
37
+ type: string;
38
+ controller: IDID;
39
+ maxCapabilityChainLength?: number;
40
+ publicAlias?: string;
41
+ publicAliasTemplate?: string;
42
+ }): Promise<{
43
+ key: KmsStoredKey;
44
+ keyDescription: KmsKeyDescription;
45
+ }>;
46
+ /**
47
+ * Runs a key operation envelope against a stored key, dispatching on
48
+ * `(key.type, operation.type)`. An operation this module does not serve for
49
+ * the key's type -- or does not recognize at all -- is a clean 400
50
+ * `UnsupportedKeyOperationError`.
51
+ *
52
+ * @param options {object}
53
+ * @param options.key {KmsStoredKey} the stored (secret-bearing) key
54
+ * @param options.operation {Record<string, unknown>} the validated envelope
55
+ * @returns {Promise<object>} the operation's wire result (`signatureValue` /
56
+ * `verified` / `secret` / `wrappedKey` / `unwrappedKey`)
57
+ */
58
+ export declare function runKeyOperation({ key, operation }: {
59
+ key: KmsStoredKey;
60
+ operation: Record<string, unknown>;
61
+ }): Promise<object>;
62
+ //# sourceMappingURL=kmsModule.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"kmsModule.d.ts","sourceRoot":"","sources":["../../src/lib/kmsModule.ts"],"names":[],"mappings":"AA4BA,OAAO,KAAK,EAAE,IAAI,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAsExE;;;;;;;;;;;GAWG;AACH,wBAAgB,cAAc,CAAC,EAC7B,GAAG,EACH,UAAU,EACX,EAAE;IACD,GAAG,EAAE,YAAY,CAAA;IACjB,UAAU,EAAE,IAAI,CAAA;CACjB,GAAG,iBAAiB,CAmBpB;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,cAAc,CAAC,EACnC,KAAK,EACL,IAAI,EACJ,UAAU,EACV,wBAAwB,EACxB,WAAW,EACX,mBAAmB,EACpB,EAAE;IACD,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,MAAM,CAAA;IACZ,UAAU,EAAE,IAAI,CAAA;IAChB,wBAAwB,CAAC,EAAE,MAAM,CAAA;IACjC,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,mBAAmB,CAAC,EAAE,MAAM,CAAA;CAC7B,GAAG,OAAO,CAAC;IAAE,GAAG,EAAE,YAAY,CAAC;IAAC,cAAc,EAAE,iBAAiB,CAAA;CAAE,CAAC,CAoCpE;AAgND;;;;;;;;;;;GAWG;AACH,wBAAsB,eAAe,CAAC,EACpC,GAAG,EACH,SAAS,EACV,EAAE;IACD,GAAG,EAAE,YAAY,CAAA;IACjB,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CACnC,GAAG,OAAO,CAAC,MAAM,CAAC,CAoBlB"}
@@ -0,0 +1,327 @@
1
+ /**
2
+ * The single in-process WebKMS module this server hard-wires (`local-v1`),
3
+ * collecting the module's parts in one file: a pure-crypto core per key type,
4
+ * an operation dispatch table keyed by `(key type, operation type)`, and the
5
+ * generate/describe orchestration (record assembly, `publicAlias` /
6
+ * `publicAliasTemplate` expansion). The module never touches storage -- the
7
+ * request layer loads/inserts `KmsKeyRecord`s through the storage backend and
8
+ * hands this module the (secret-bearing) stored key.
9
+ *
10
+ * Custody draws the line on what is served (see the roadmap's wire contract):
11
+ * every operation here requires the custodial secret -- Ed25519 sign, X25519
12
+ * deriveSecret, HMAC sign/verify, AES-KW wrap/unwrap. Asymmetric verify needs
13
+ * only the public key and is deliberately not served (client-local instead);
14
+ * requesting it is a clean 400.
15
+ */
16
+ import { createCipheriv, createDecipheriv, createHmac, randomBytes, timingSafeEqual } from 'node:crypto';
17
+ import { Ed25519VerificationKey } from '@interop/ed25519-verification-key';
18
+ import { X25519KeyAgreementKey2020 } from '@interop/x25519-key-agreement-key';
19
+ import { InvalidRequestBodyError, UnsupportedKeyOperationError } from '../errors.js';
20
+ /** The JSON-LD context each supported key type's description carries. */
21
+ const KEY_TYPE_CONTEXTS = {
22
+ Ed25519VerificationKey2020: 'https://w3id.org/security/suites/ed25519-2020/v1',
23
+ X25519KeyAgreementKey2020: 'https://w3id.org/security/suites/x25519-2020/v1',
24
+ Sha256HmacKey2019: 'https://w3id.org/security/suites/hmac-2019/v1',
25
+ AesKeyWrappingKey2019: 'https://w3id.org/security/suites/aes-2019/v1'
26
+ };
27
+ /**
28
+ * AES Key Wrap (RFC 3394) parameters: AES-256 in key-wrap mode with the RFC's
29
+ * default initial value. The IV is the integrity check -- `decipher.final()`
30
+ * throws when the unwrapped output would not reproduce it (i.e. the wrong KEK
31
+ * or corrupted ciphertext).
32
+ */
33
+ const AES_KW_ALGORITHM = 'id-aes256-wrap';
34
+ const AES_KW_DEFAULT_IV = Buffer.alloc(8, 0xa6);
35
+ /** Decodes a base64url (no padding) operation field into bytes. */
36
+ function decodeBase64url({ value, field, requestName }) {
37
+ if (typeof value !== 'string' || value.length === 0) {
38
+ throw new InvalidRequestBodyError({
39
+ requestName,
40
+ detail: `Operation "${field}" must be a non-empty base64url string.`,
41
+ pointer: `#/${field}`
42
+ });
43
+ }
44
+ return Buffer.from(value, 'base64url');
45
+ }
46
+ /**
47
+ * Expands a `publicAliasTemplate` against the key description, using the
48
+ * `url-template` package (RFC 6570): `{var}` substitutes the
49
+ * percent-encoded description field, `{+var}` substitutes it raw (a typical
50
+ * template is `{+controller}#{publicKeyMultibase}`). An unknown
51
+ * variable expands to the empty string, as in RFC 6570.
52
+ *
53
+ * @param options {object}
54
+ * @param options.template {string}
55
+ * @param options.description {KmsKeyDescription} expansion variables
56
+ * @returns {string}
57
+ */
58
+ function expandAliasTemplate({ template, description }) {
59
+ const variables = description;
60
+ return template.replace(/\{(\+?)([A-Za-z0-9_]+)\}/g, (_match, rawModifier, name) => {
61
+ const value = variables[name];
62
+ const expanded = typeof value === 'string' ? value : '';
63
+ return rawModifier === '+' ? expanded : encodeURIComponent(expanded);
64
+ });
65
+ }
66
+ /**
67
+ * Builds the public key-description projection of a stored key: the
68
+ * allowlisted public fields plus the live keystore `controller` (never any
69
+ * secret field), with the `publicAlias` / expanded `publicAliasTemplate`
70
+ * override applied to `id`. Applied identically at generate time and on every
71
+ * subsequent read, so descriptions are stable (the client caches them).
72
+ *
73
+ * @param options {object}
74
+ * @param options.key {KmsStoredKey} the stored (secret-bearing) key
75
+ * @param options.controller {IDID} the keystore's current controller
76
+ * @returns {KmsKeyDescription}
77
+ */
78
+ export function describeKmsKey({ key, controller }) {
79
+ const description = {
80
+ '@context': key['@context'],
81
+ id: key.id,
82
+ type: key.type,
83
+ ...(key.publicKeyMultibase !== undefined && {
84
+ publicKeyMultibase: key.publicKeyMultibase
85
+ }),
86
+ controller
87
+ };
88
+ if (key.publicAlias !== undefined) {
89
+ description.id = key.publicAlias;
90
+ }
91
+ else if (key.publicAliasTemplate !== undefined) {
92
+ description.id = expandAliasTemplate({
93
+ template: key.publicAliasTemplate,
94
+ description
95
+ });
96
+ }
97
+ return description;
98
+ }
99
+ /**
100
+ * Generates key material for a supported key type and assembles the stored
101
+ * key (full serialized form, secret material included) plus its public
102
+ * description. Field names are protocol-fixed: the asymmetric pairs carry
103
+ * `publicKeyMultibase` / `privateKeyMultibase`; the symmetric keys carry a
104
+ * base64url 256-bit `secret` (the webkms per-type key generators).
105
+ *
106
+ * @param options {object}
107
+ * @param options.keyId {string} the full key URL (`<keystoreId>/keys/<localId>`)
108
+ * @param options.type {string} the webkms key type
109
+ * @param options.controller {IDID} the keystore's current controller (for
110
+ * the returned description only -- never stored on the key)
111
+ * @param [options.maxCapabilityChainLength] {number}
112
+ * @param [options.publicAlias] {string}
113
+ * @param [options.publicAliasTemplate] {string}
114
+ * @returns {Promise<{ key: KmsStoredKey, keyDescription: KmsKeyDescription }>}
115
+ */
116
+ export async function generateKmsKey({ keyId, type, controller, maxCapabilityChainLength, publicAlias, publicAliasTemplate }) {
117
+ // `Object.hasOwn`, not `KEY_TYPE_CONTEXTS[type] === undefined`: `type` is
118
+ // client-supplied, so a prototype-chain name (`constructor`, `toString`,
119
+ // `hasOwnProperty`, ...) would otherwise resolve to an inherited `Object`
120
+ // member and slip past the fail-closed rejection into a stored key record.
121
+ if (!Object.hasOwn(KEY_TYPE_CONTEXTS, type)) {
122
+ throw new InvalidRequestBodyError({
123
+ requestName: 'Generate Key',
124
+ detail: `Unsupported key type "${type}".`,
125
+ pointer: '#/invocationTarget/type'
126
+ });
127
+ }
128
+ const context = KEY_TYPE_CONTEXTS[type];
129
+ const key = { '@context': context, id: keyId, type };
130
+ if (type === 'Ed25519VerificationKey2020') {
131
+ const keyPair = await Ed25519VerificationKey.generate();
132
+ key.publicKeyMultibase = keyPair.publicKeyMultibase;
133
+ key.privateKeyMultibase = keyPair.privateKeyMultibase;
134
+ }
135
+ else if (type === 'X25519KeyAgreementKey2020') {
136
+ const keyPair = await X25519KeyAgreementKey2020.generate();
137
+ key.publicKeyMultibase = keyPair.publicKeyMultibase;
138
+ key.privateKeyMultibase = keyPair.privateKeyMultibase;
139
+ }
140
+ else {
141
+ // Sha256HmacKey2019 / AesKeyWrappingKey2019: a 256-bit symmetric secret.
142
+ key.secret = randomBytes(32).toString('base64url');
143
+ }
144
+ if (maxCapabilityChainLength !== undefined) {
145
+ key.maxCapabilityChainLength = maxCapabilityChainLength;
146
+ }
147
+ if (publicAlias !== undefined) {
148
+ key.publicAlias = publicAlias;
149
+ }
150
+ if (publicAliasTemplate !== undefined) {
151
+ key.publicAliasTemplate = publicAliasTemplate;
152
+ }
153
+ return { key, keyDescription: describeKmsKey({ key, controller }) };
154
+ }
155
+ /**
156
+ * SignOperation on an Ed25519 key: signs the `verifyData` bytes with the
157
+ * custodial private key. The signature is not a secret, so serving it is the
158
+ * custody model WebKMS is built for.
159
+ */
160
+ async function ed25519Sign({ key, operation }) {
161
+ const data = decodeBase64url({
162
+ value: operation.verifyData,
163
+ field: 'verifyData',
164
+ requestName: 'Sign Operation'
165
+ });
166
+ const keyPair = await Ed25519VerificationKey.from({ ...key });
167
+ const signature = await keyPair.signer().sign({ data });
168
+ return { signatureValue: Buffer.from(signature).toString('base64url') };
169
+ }
170
+ /**
171
+ * DeriveSecretOperation on an X25519 key: raw ECDH against the submitted peer
172
+ * public key -- no KDF is applied; the caller runs the shared secret through
173
+ * one. A peer whose `type` does not match the key's is a clean 400 (the client
174
+ * pre-checks, so only non-client callers hit this).
175
+ */
176
+ async function x25519DeriveSecret({ key, operation }) {
177
+ const requestName = 'Derive Secret Operation';
178
+ const publicKey = operation.publicKey;
179
+ if (typeof publicKey !== 'object' || publicKey === null) {
180
+ throw new InvalidRequestBodyError({
181
+ requestName,
182
+ detail: 'Operation "publicKey" must be a public key object.',
183
+ pointer: '#/publicKey'
184
+ });
185
+ }
186
+ if (publicKey.type !== key.type) {
187
+ throw new InvalidRequestBodyError({
188
+ requestName,
189
+ detail: `The given public key type "${publicKey.type}" does not match the` +
190
+ ` key agreement key's type "${key.type}".`,
191
+ pointer: '#/publicKey/type'
192
+ });
193
+ }
194
+ const keyPair = await X25519KeyAgreementKey2020.from({ ...key });
195
+ let secret;
196
+ try {
197
+ secret = await keyPair.deriveSecret({ publicKey });
198
+ }
199
+ catch {
200
+ // A malformed / wrong-header `publicKeyMultibase` is a client error.
201
+ throw new InvalidRequestBodyError({
202
+ requestName,
203
+ detail: 'Operation "publicKey.publicKeyMultibase" is not a valid key.',
204
+ pointer: '#/publicKey/publicKeyMultibase'
205
+ });
206
+ }
207
+ return { secret: Buffer.from(secret).toString('base64url') };
208
+ }
209
+ /** Computes the HMAC-SHA-256 of `verifyData` under the key's secret. */
210
+ function hmacDigest({ key, operation, requestName }) {
211
+ const data = decodeBase64url({
212
+ value: operation.verifyData,
213
+ field: 'verifyData',
214
+ requestName
215
+ });
216
+ const secret = Buffer.from(key.secret, 'base64url');
217
+ return createHmac('sha256', secret).update(data).digest();
218
+ }
219
+ /** SignOperation on an HMAC key. */
220
+ async function hmacSign({ key, operation }) {
221
+ const digest = hmacDigest({ key, operation, requestName: 'Sign Operation' });
222
+ return { signatureValue: digest.toString('base64url') };
223
+ }
224
+ /**
225
+ * VerifyOperation on an HMAC key -- the one served verify (a client cannot
226
+ * check an HMAC without the symmetric secret). A wrong-*length*
227
+ * `signatureValue` is an ordinary `verified: false`, checked before
228
+ * `timingSafeEqual` (which throws on mismatched lengths).
229
+ */
230
+ async function hmacVerify({ key, operation }) {
231
+ const requestName = 'Verify Operation';
232
+ const signature = decodeBase64url({
233
+ value: operation.signatureValue,
234
+ field: 'signatureValue',
235
+ requestName
236
+ });
237
+ const expected = hmacDigest({ key, operation, requestName });
238
+ const verified = signature.length === expected.length && timingSafeEqual(signature, expected);
239
+ return { verified };
240
+ }
241
+ /** WrapKeyOperation on an AES-KW key encryption key. */
242
+ async function aesWrapKey({ key, operation }) {
243
+ const unwrapped = decodeBase64url({
244
+ value: operation.unwrappedKey,
245
+ field: 'unwrappedKey',
246
+ requestName: 'Wrap Key Operation'
247
+ });
248
+ const secret = Buffer.from(key.secret, 'base64url');
249
+ const cipher = createCipheriv(AES_KW_ALGORITHM, secret, AES_KW_DEFAULT_IV);
250
+ const wrapped = Buffer.concat([cipher.update(unwrapped), cipher.final()]);
251
+ return { wrappedKey: wrapped.toString('base64url') };
252
+ }
253
+ /**
254
+ * UnwrapKeyOperation on an AES-KW key encryption key. A failed unwrap -- the
255
+ * RFC 3394 integrity check rejecting the ciphertext (wrong KEK, corrupted
256
+ * `wrappedKey`) -- resolves `unwrappedKey: null` rather than erroring: that is
257
+ * the client's documented contract (`Kek.unwrapKey` resolves null when the key
258
+ * does not match).
259
+ */
260
+ async function aesUnwrapKey({ key, operation }) {
261
+ const wrapped = decodeBase64url({
262
+ value: operation.wrappedKey,
263
+ field: 'wrappedKey',
264
+ requestName: 'Unwrap Key Operation'
265
+ });
266
+ const secret = Buffer.from(key.secret, 'base64url');
267
+ try {
268
+ const decipher = createDecipheriv(AES_KW_ALGORITHM, secret, AES_KW_DEFAULT_IV);
269
+ const unwrapped = Buffer.concat([
270
+ decipher.update(wrapped),
271
+ decipher.final()
272
+ ]);
273
+ return { unwrappedKey: unwrapped.toString('base64url') };
274
+ }
275
+ catch {
276
+ return { unwrappedKey: null };
277
+ }
278
+ }
279
+ /**
280
+ * The operation dispatch table: which operation envelope `type`s this module
281
+ * serves for each key type, and the pure-crypto implementation for each pair.
282
+ * Deliberate omissions (custody is the criterion): `VerifyOperation` on the
283
+ * asymmetric and key-agreement types (verify client-locally against the
284
+ * description's `publicKeyMultibase`), and every cross-type combination.
285
+ */
286
+ const KEY_OPERATIONS = {
287
+ Ed25519VerificationKey2020: { SignOperation: ed25519Sign },
288
+ X25519KeyAgreementKey2020: { DeriveSecretOperation: x25519DeriveSecret },
289
+ Sha256HmacKey2019: { SignOperation: hmacSign, VerifyOperation: hmacVerify },
290
+ AesKeyWrappingKey2019: {
291
+ WrapKeyOperation: aesWrapKey,
292
+ UnwrapKeyOperation: aesUnwrapKey
293
+ }
294
+ };
295
+ /**
296
+ * Runs a key operation envelope against a stored key, dispatching on
297
+ * `(key.type, operation.type)`. An operation this module does not serve for
298
+ * the key's type -- or does not recognize at all -- is a clean 400
299
+ * `UnsupportedKeyOperationError`.
300
+ *
301
+ * @param options {object}
302
+ * @param options.key {KmsStoredKey} the stored (secret-bearing) key
303
+ * @param options.operation {Record<string, unknown>} the validated envelope
304
+ * @returns {Promise<object>} the operation's wire result (`signatureValue` /
305
+ * `verified` / `secret` / `wrappedKey` / `unwrappedKey`)
306
+ */
307
+ export async function runKeyOperation({ key, operation }) {
308
+ const operationType = operation.type;
309
+ // `Object.hasOwn` at both levels: `key.type` and `operationType` are both
310
+ // client-influenced, so plain-object indexing could otherwise resolve an
311
+ // inherited `Object` member (`constructor`, `toString`, ...) to a truthy
312
+ // function and bypass this fail-closed gate.
313
+ const operations = Object.hasOwn(KEY_OPERATIONS, key.type)
314
+ ? KEY_OPERATIONS[key.type]
315
+ : undefined;
316
+ const runOperation = operations && Object.hasOwn(operations, operationType)
317
+ ? operations[operationType]
318
+ : undefined;
319
+ if (runOperation === undefined) {
320
+ throw new UnsupportedKeyOperationError({
321
+ operationType,
322
+ keyType: key.type
323
+ });
324
+ }
325
+ return await runOperation({ key, operation });
326
+ }
327
+ //# sourceMappingURL=kmsModule.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"kmsModule.js","sourceRoot":"","sources":["../../src/lib/kmsModule.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,82 @@
1
+ import type { KmsKeyRecord, RecordKek, KmsRecordKekRegistry } from '../types.js';
2
+ /**
3
+ * The plaintext allowlist: the `KmsStoredKey` fields that stay in the clear (and
4
+ * queryable) at rest. EVERY other field of `key` -- `privateKeyMultibase`,
5
+ * `secret`, and any secret-bearing field added later -- is swept into the
6
+ * envelope, so new secrets are protected by default unless explicitly added
7
+ * here. (Bedrock additionally lists `controller`; this server never stores
8
+ * `controller` on the key -- it is read from the live keystore config at
9
+ * description time -- so it is simply absent.)
10
+ */
11
+ export declare const PLAINTEXT_KEY_FIELDS: ReadonlySet<string>;
12
+ /**
13
+ * Derives a KEK's non-secret, stable id from its raw key bytes: a SHA-256 digest
14
+ * (one-way, so storing it per record leaks nothing about the key). Self-
15
+ * describing and deterministic, so a rotated-in KEK gets a stable id from its
16
+ * material alone -- the convention bedrock follows.
17
+ * @param key {Buffer} the 32-byte AES-256 KEK
18
+ * @returns {string} the derived `urn:kek:sha256:<hex>` id
19
+ */
20
+ export declare function deriveKekId(key: Buffer): string;
21
+ /**
22
+ * Parses a `secretKeyMultibase` (base58btc Multikey) AES-256 KEK into a
23
+ * `RecordKek` (its raw key plus a derived id). Throws a plain `Error` on a
24
+ * malformed value -- callers are config parsing (fails startup) and tests, not
25
+ * the request path. The error messages name `label` (the offending config
26
+ * variable, e.g. a `KMS_RECORD_KEKS entry N` position) but NEVER echo the secret
27
+ * value itself.
28
+ * @param secretKeyMultibase {string} the multibase (`z...`) KEK value
29
+ * @param [label] {string} the config source named in error messages
30
+ * (defaults to `KMS_RECORD_KEK`)
31
+ * @returns {RecordKek}
32
+ */
33
+ export declare function parseKekMultibase(secretKeyMultibase: string, label?: string): RecordKek;
34
+ /**
35
+ * The KEK that wraps NEW records, or `undefined` when encryption is disabled
36
+ * (no registry, or `currentKekId: null`).
37
+ * @param [registry] {KmsRecordKekRegistry}
38
+ * @returns {RecordKek | undefined}
39
+ */
40
+ export declare function currentRecordKek(registry?: KmsRecordKekRegistry): RecordKek | undefined;
41
+ /**
42
+ * A KEK-lookup function over a registry, resolving a record's stored `kekId` to
43
+ * its `RecordKek` for decryption (`undefined` when the KEK is not registered, or
44
+ * there is no registry at all). The decrypt seam for the pass-through /
45
+ * rotation upgrade paths.
46
+ * @param [registry] {KmsRecordKekRegistry}
47
+ * @returns {(kekId: string) => RecordKek | undefined}
48
+ */
49
+ export declare function recordKekLoader(registry?: KmsRecordKekRegistry): (kekId: string) => RecordKek | undefined;
50
+ /**
51
+ * Encrypts the secret-bearing fields of a key record under a KEK, returning a
52
+ * record whose `key` carries the allowlisted plaintext fields plus an
53
+ * `encrypted` envelope in place of the secrets. Pure: the KEK is injected, no
54
+ * storage or env access. The record's non-`key` members (`keystoreId`,
55
+ * `localId`, `meta`) are unchanged.
56
+ * @param options {object}
57
+ * @param options.record {KmsKeyRecord} the plaintext (secret-bearing) record
58
+ * @param options.kek {RecordKek} the KEK to wrap the fresh CEK under
59
+ * @returns {KmsKeyRecord} the at-rest record (secrets replaced by `encrypted`)
60
+ */
61
+ export declare function encryptKeyRecord({ record, kek }: {
62
+ record: KmsKeyRecord;
63
+ kek: RecordKek;
64
+ }): KmsKeyRecord;
65
+ /**
66
+ * Decrypts a key record: pass-through when `key.encrypted` is absent (a
67
+ * plaintext record -- the default, and the pass-through upgrade path), else
68
+ * looks up the KEK by the record's stored `kekId`, unwraps the CEK, decrypts the
69
+ * secret subset, and splices the secret fields back onto the allowlisted ones
70
+ * (dropping the envelope). Pure: KEK(s) injected via `kekLoader`, no storage or
71
+ * env access. Throws a plain `Error` when the record's KEK is not registered or
72
+ * the ciphertext fails to authenticate; the request seam wraps that as a 500.
73
+ * @param options {object}
74
+ * @param options.record {KmsKeyRecord} the at-rest (or plaintext) record
75
+ * @param options.kekLoader {(kekId: string) => RecordKek | undefined} KEK lookup
76
+ * @returns {KmsKeyRecord} the decrypted record (secrets restored on `key`)
77
+ */
78
+ export declare function decryptKeyRecord({ record, kekLoader }: {
79
+ record: KmsKeyRecord;
80
+ kekLoader: (kekId: string) => RecordKek | undefined;
81
+ }): KmsKeyRecord;
82
+ //# sourceMappingURL=kmsRecordCipher.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"kmsRecordCipher.d.ts","sourceRoot":"","sources":["../../src/lib/kmsRecordCipher.ts"],"names":[],"mappings":"AA2BA,OAAO,KAAK,EAEV,YAAY,EAEZ,SAAS,EACT,oBAAoB,EACrB,MAAM,aAAa,CAAA;AAEpB;;;;;;;;GAQG;AACH,eAAO,MAAM,oBAAoB,EAAE,WAAW,CAAC,MAAM,CAQnD,CAAA;AAsBF;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAE/C;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,iBAAiB,CAC/B,kBAAkB,EAAE,MAAM,EAC1B,KAAK,SAAmB,GACvB,SAAS,CAoBX;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAC9B,QAAQ,CAAC,EAAE,oBAAoB,GAC9B,SAAS,GAAG,SAAS,CAKvB;AAED;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAC7B,QAAQ,CAAC,EAAE,oBAAoB,GAC9B,CAAC,KAAK,EAAE,MAAM,KAAK,SAAS,GAAG,SAAS,CAE1C;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,gBAAgB,CAAC,EAC/B,MAAM,EACN,GAAG,EACJ,EAAE;IACD,MAAM,EAAE,YAAY,CAAA;IACpB,GAAG,EAAE,SAAS,CAAA;CACf,GAAG,YAAY,CAmDf;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,gBAAgB,CAAC,EAC/B,MAAM,EACN,SAAS,EACV,EAAE;IACD,MAAM,EAAE,YAAY,CAAA;IACpB,SAAS,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,SAAS,GAAG,SAAS,CAAA;CACpD,GAAG,YAAY,CAkDf"}