@enbox/dwn-sdk-js 0.4.6 → 0.4.8

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 (375) hide show
  1. package/README.md +3 -1
  2. package/dist/browser.mjs +19 -78
  3. package/dist/browser.mjs.map +4 -4
  4. package/dist/esm/generated/precompiled-validators.js +2324 -1525
  5. package/dist/esm/generated/precompiled-validators.js.map +1 -1
  6. package/dist/esm/src/core/constants.js +50 -0
  7. package/dist/esm/src/core/constants.js.map +1 -1
  8. package/dist/esm/src/core/dwn-error.js +29 -11
  9. package/dist/esm/src/core/dwn-error.js.map +1 -1
  10. package/dist/esm/src/core/encryption-control.js +785 -0
  11. package/dist/esm/src/core/encryption-control.js.map +1 -0
  12. package/dist/esm/src/core/message.js +6 -0
  13. package/dist/esm/src/core/message.js.map +1 -1
  14. package/dist/esm/src/core/messages-grant-authorization.js +36 -5
  15. package/dist/esm/src/core/messages-grant-authorization.js.map +1 -1
  16. package/dist/esm/src/core/protocol-authorization-action.js +3 -12
  17. package/dist/esm/src/core/protocol-authorization-action.js.map +1 -1
  18. package/dist/esm/src/core/protocol-authorization-validation.js +31 -30
  19. package/dist/esm/src/core/protocol-authorization-validation.js.map +1 -1
  20. package/dist/esm/src/core/protocol-authorization.js +6 -0
  21. package/dist/esm/src/core/protocol-authorization.js.map +1 -1
  22. package/dist/esm/src/core/recording-validation-state-reader.js +3 -3
  23. package/dist/esm/src/core/recording-validation-state-reader.js.map +1 -1
  24. package/dist/esm/src/core/replication-apply.js +62 -108
  25. package/dist/esm/src/core/replication-apply.js.map +1 -1
  26. package/dist/esm/src/core/validation-state-reader.js +5 -6
  27. package/dist/esm/src/core/validation-state-reader.js.map +1 -1
  28. package/dist/esm/src/dwn.js +1 -2
  29. package/dist/esm/src/dwn.js.map +1 -1
  30. package/dist/esm/src/event-stream/event-emitter-wake-publisher.js +11 -11
  31. package/dist/esm/src/event-stream/event-emitter-wake-publisher.js.map +1 -1
  32. package/dist/esm/src/handlers/messages-query.js +6 -16
  33. package/dist/esm/src/handlers/messages-query.js.map +1 -1
  34. package/dist/esm/src/handlers/messages-read.js +4 -3
  35. package/dist/esm/src/handlers/messages-read.js.map +1 -1
  36. package/dist/esm/src/handlers/messages-subscribe.js +26 -30
  37. package/dist/esm/src/handlers/messages-subscribe.js.map +1 -1
  38. package/dist/esm/src/handlers/protocols-configure.js +6 -4
  39. package/dist/esm/src/handlers/protocols-configure.js.map +1 -1
  40. package/dist/esm/src/handlers/records-count.js +85 -10
  41. package/dist/esm/src/handlers/records-count.js.map +1 -1
  42. package/dist/esm/src/handlers/records-delete.js +11 -0
  43. package/dist/esm/src/handlers/records-delete.js.map +1 -1
  44. package/dist/esm/src/handlers/records-query.js +140 -18
  45. package/dist/esm/src/handlers/records-query.js.map +1 -1
  46. package/dist/esm/src/handlers/records-read.js +20 -4
  47. package/dist/esm/src/handlers/records-read.js.map +1 -1
  48. package/dist/esm/src/handlers/records-subscribe.js +127 -11
  49. package/dist/esm/src/handlers/records-subscribe.js.map +1 -1
  50. package/dist/esm/src/handlers/records-write.js +15 -0
  51. package/dist/esm/src/handlers/records-write.js.map +1 -1
  52. package/dist/esm/src/index.js +8 -11
  53. package/dist/esm/src/index.js.map +1 -1
  54. package/dist/esm/src/interfaces/protocols-configure.js +66 -9
  55. package/dist/esm/src/interfaces/protocols-configure.js.map +1 -1
  56. package/dist/esm/src/interfaces/records-write.js +43 -36
  57. package/dist/esm/src/interfaces/records-write.js.map +1 -1
  58. package/dist/esm/src/jose/algorithms/signing/ed25519.js +5 -5
  59. package/dist/esm/src/jose/algorithms/signing/ed25519.js.map +1 -1
  60. package/dist/esm/src/jose/jws/general/verifier.js +1 -1
  61. package/dist/esm/src/jose/jws/general/verifier.js.map +1 -1
  62. package/dist/esm/src/protocols/encryption.js +60 -270
  63. package/dist/esm/src/protocols/encryption.js.map +1 -1
  64. package/dist/esm/src/store/blockstore-level.js +14 -23
  65. package/dist/esm/src/store/blockstore-level.js.map +1 -1
  66. package/dist/esm/src/store/blockstore-mock.js +10 -27
  67. package/dist/esm/src/store/blockstore-mock.js.map +1 -1
  68. package/dist/esm/src/store/blockstore-utils.js +45 -0
  69. package/dist/esm/src/store/blockstore-utils.js.map +1 -0
  70. package/dist/esm/src/store/blockstore.js +2 -0
  71. package/dist/esm/src/store/blockstore.js.map +1 -0
  72. package/dist/esm/src/store/data-store-level.js +4 -12
  73. package/dist/esm/src/store/data-store-level.js.map +1 -1
  74. package/dist/esm/src/store/level.js +7 -0
  75. package/dist/esm/src/store/level.js.map +1 -0
  76. package/dist/esm/src/types/encryption-types.js +4 -1
  77. package/dist/esm/src/types/encryption-types.js.map +1 -1
  78. package/dist/esm/src/utils/data-stream.js +25 -0
  79. package/dist/esm/src/utils/data-stream.js.map +1 -1
  80. package/dist/esm/src/utils/encryption.js +107 -19
  81. package/dist/esm/src/utils/encryption.js.map +1 -1
  82. package/dist/esm/src/utils/grant-key-coverage.js +180 -0
  83. package/dist/esm/src/utils/grant-key-coverage.js.map +1 -0
  84. package/dist/esm/src/utils/hd-key.js +1 -2
  85. package/dist/esm/src/utils/hd-key.js.map +1 -1
  86. package/dist/esm/src/utils/jws.js +7 -1
  87. package/dist/esm/src/utils/jws.js.map +1 -1
  88. package/dist/esm/src/utils/memory-cache.js +29 -8
  89. package/dist/esm/src/utils/memory-cache.js.map +1 -1
  90. package/dist/esm/src/utils/protocols.js +27 -0
  91. package/dist/esm/src/utils/protocols.js.map +1 -1
  92. package/dist/esm/src/utils/record-limit-occupancy.js +23 -3
  93. package/dist/esm/src/utils/record-limit-occupancy.js.map +1 -1
  94. package/dist/esm/src/utils/records.js +29 -0
  95. package/dist/esm/src/utils/records.js.map +1 -1
  96. package/dist/esm/src/utils/secp256k1.js +12 -11
  97. package/dist/esm/src/utils/secp256k1.js.map +1 -1
  98. package/dist/esm/src/utils/secp256r1.js +12 -20
  99. package/dist/esm/src/utils/secp256r1.js.map +1 -1
  100. package/dist/esm/src/utils/time.js +103 -25
  101. package/dist/esm/src/utils/time.js.map +1 -1
  102. package/dist/esm/tests/core/constants.spec.js +35 -0
  103. package/dist/esm/tests/core/constants.spec.js.map +1 -0
  104. package/dist/esm/tests/core/encryption-control.spec.js +100 -0
  105. package/dist/esm/tests/core/encryption-control.spec.js.map +1 -0
  106. package/dist/esm/tests/core/message.spec.js +35 -0
  107. package/dist/esm/tests/core/message.spec.js.map +1 -1
  108. package/dist/esm/tests/core/protocol-authorization.spec.js +1 -1
  109. package/dist/esm/tests/core/protocol-authorization.spec.js.map +1 -1
  110. package/dist/esm/tests/core/replication-apply.spec.js +157 -74
  111. package/dist/esm/tests/core/replication-apply.spec.js.map +1 -1
  112. package/dist/esm/tests/core/replication-replay-property.spec.js +2 -1
  113. package/dist/esm/tests/core/replication-replay-property.spec.js.map +1 -1
  114. package/dist/esm/tests/core/validation-read-closure.spec.js +14 -28
  115. package/dist/esm/tests/core/validation-read-closure.spec.js.map +1 -1
  116. package/dist/esm/tests/core/validation-state-reader.spec.js +19 -23
  117. package/dist/esm/tests/core/validation-state-reader.spec.js.map +1 -1
  118. package/dist/esm/tests/dwn.spec.js +2 -1
  119. package/dist/esm/tests/dwn.spec.js.map +1 -1
  120. package/dist/esm/tests/event-emitter-wake-publisher.spec.js +51 -0
  121. package/dist/esm/tests/event-emitter-wake-publisher.spec.js.map +1 -0
  122. package/dist/esm/tests/features/protocol-composition.spec.js +1 -1
  123. package/dist/esm/tests/features/protocol-composition.spec.js.map +1 -1
  124. package/dist/esm/tests/features/records-record-limit.spec.js +1 -1
  125. package/dist/esm/tests/features/records-record-limit.spec.js.map +1 -1
  126. package/dist/esm/tests/fuzz/encryption.fuzz.spec.js +9 -1
  127. package/dist/esm/tests/fuzz/encryption.fuzz.spec.js.map +1 -1
  128. package/dist/esm/tests/fuzz/process-message.fuzz.spec.js +1 -1
  129. package/dist/esm/tests/fuzz/process-message.fuzz.spec.js.map +1 -1
  130. package/dist/esm/tests/fuzz/protocols-utils.fuzz.spec.js +29 -1
  131. package/dist/esm/tests/fuzz/protocols-utils.fuzz.spec.js.map +1 -1
  132. package/dist/esm/tests/fuzz/secp256k1.fuzz.spec.js +2 -2
  133. package/dist/esm/tests/fuzz/secp256k1.fuzz.spec.js.map +1 -1
  134. package/dist/esm/tests/fuzz/time.fuzz.spec.js +7 -1
  135. package/dist/esm/tests/fuzz/time.fuzz.spec.js.map +1 -1
  136. package/dist/esm/tests/handlers/messages-query.spec.js +123 -11
  137. package/dist/esm/tests/handlers/messages-query.spec.js.map +1 -1
  138. package/dist/esm/tests/handlers/messages-read.spec.js +95 -0
  139. package/dist/esm/tests/handlers/messages-read.spec.js.map +1 -1
  140. package/dist/esm/tests/handlers/messages-subscribe.spec.js +90 -0
  141. package/dist/esm/tests/handlers/messages-subscribe.spec.js.map +1 -1
  142. package/dist/esm/tests/handlers/protocols-configure.spec.js +66 -0
  143. package/dist/esm/tests/handlers/protocols-configure.spec.js.map +1 -1
  144. package/dist/esm/tests/handlers/records-count.spec.js +370 -0
  145. package/dist/esm/tests/handlers/records-count.spec.js.map +1 -1
  146. package/dist/esm/tests/handlers/records-delete.spec.js +70 -1
  147. package/dist/esm/tests/handlers/records-delete.spec.js.map +1 -1
  148. package/dist/esm/tests/handlers/records-query.spec.js +562 -1
  149. package/dist/esm/tests/handlers/records-query.spec.js.map +1 -1
  150. package/dist/esm/tests/handlers/records-read.spec.js +140 -2
  151. package/dist/esm/tests/handlers/records-read.spec.js.map +1 -1
  152. package/dist/esm/tests/handlers/records-subscribe.spec.js +241 -1
  153. package/dist/esm/tests/handlers/records-subscribe.spec.js.map +1 -1
  154. package/dist/esm/tests/handlers/records-write.spec.js +1187 -29
  155. package/dist/esm/tests/handlers/records-write.spec.js.map +1 -1
  156. package/dist/esm/tests/interfaces/protocols-configure.spec.js +290 -2
  157. package/dist/esm/tests/interfaces/protocols-configure.spec.js.map +1 -1
  158. package/dist/esm/tests/interfaces/records-write.spec.js +47 -5
  159. package/dist/esm/tests/interfaces/records-write.spec.js.map +1 -1
  160. package/dist/esm/tests/protocols/encryption.spec.js +187 -452
  161. package/dist/esm/tests/protocols/encryption.spec.js.map +1 -1
  162. package/dist/esm/tests/scenarios/end-to-end-tests.spec.js +13 -27
  163. package/dist/esm/tests/scenarios/end-to-end-tests.spec.js.map +1 -1
  164. package/dist/esm/tests/store/blockstore-level.spec.js +33 -10
  165. package/dist/esm/tests/store/blockstore-level.spec.js.map +1 -1
  166. package/dist/esm/tests/store/blockstore-mock.spec.js +31 -7
  167. package/dist/esm/tests/store/blockstore-mock.spec.js.map +1 -1
  168. package/dist/esm/tests/store/blockstore-utils.spec.js +115 -0
  169. package/dist/esm/tests/store/blockstore-utils.spec.js.map +1 -0
  170. package/dist/esm/tests/store/index-level.spec.js +3 -3
  171. package/dist/esm/tests/store/index-level.spec.js.map +1 -1
  172. package/dist/esm/tests/test-stores.js +2 -1
  173. package/dist/esm/tests/test-stores.js.map +1 -1
  174. package/dist/esm/tests/utils/cid.spec.js +15 -0
  175. package/dist/esm/tests/utils/cid.spec.js.map +1 -1
  176. package/dist/esm/tests/utils/data-stream.spec.js +30 -0
  177. package/dist/esm/tests/utils/data-stream.spec.js.map +1 -1
  178. package/dist/esm/tests/utils/encryption-control-test-utils.js +111 -0
  179. package/dist/esm/tests/utils/encryption-control-test-utils.js.map +1 -0
  180. package/dist/esm/tests/utils/encryption.spec.js +198 -5
  181. package/dist/esm/tests/utils/encryption.spec.js.map +1 -1
  182. package/dist/esm/tests/utils/grant-key-coverage.spec.js +262 -0
  183. package/dist/esm/tests/utils/grant-key-coverage.spec.js.map +1 -0
  184. package/dist/esm/tests/utils/memory-cache.spec.js +12 -0
  185. package/dist/esm/tests/utils/memory-cache.spec.js.map +1 -1
  186. package/dist/esm/tests/utils/secp256r1.spec.js +2 -2
  187. package/dist/esm/tests/utils/secp256r1.spec.js.map +1 -1
  188. package/dist/esm/tests/utils/test-data-generator.js +2 -1
  189. package/dist/esm/tests/utils/test-data-generator.js.map +1 -1
  190. package/dist/esm/tests/utils/test-validation-state-reader.js +1 -1
  191. package/dist/esm/tests/utils/test-validation-state-reader.js.map +1 -1
  192. package/dist/esm/tests/utils/time.spec.js +26 -0
  193. package/dist/esm/tests/utils/time.spec.js.map +1 -1
  194. package/dist/esm/tests/validation/json-schemas/encryption/audience.spec.js +45 -0
  195. package/dist/esm/tests/validation/json-schemas/encryption/audience.spec.js.map +1 -0
  196. package/dist/esm/tests/validation/json-schemas/encryption/delivery.spec.js +47 -0
  197. package/dist/esm/tests/validation/json-schemas/encryption/delivery.spec.js.map +1 -0
  198. package/dist/esm/tests/validation/json-schemas/encryption/wrapped-grant-key-envelope.spec.js +43 -0
  199. package/dist/esm/tests/validation/json-schemas/encryption/wrapped-grant-key-envelope.spec.js.map +1 -0
  200. package/dist/esm/tests/validation/json-schemas/protocols/protocols-configure.spec.js +152 -0
  201. package/dist/esm/tests/validation/json-schemas/protocols/protocols-configure.spec.js.map +1 -1
  202. package/dist/esm/tests/validation/json-schemas/records/records-write.spec.js +46 -1
  203. package/dist/esm/tests/validation/json-schemas/records/records-write.spec.js.map +1 -1
  204. package/dist/types/generated/precompiled-validators.d.ts +20 -4
  205. package/dist/types/generated/precompiled-validators.d.ts.map +1 -1
  206. package/dist/types/src/core/constants.d.ts +39 -0
  207. package/dist/types/src/core/constants.d.ts.map +1 -1
  208. package/dist/types/src/core/dwn-error.d.ts +29 -11
  209. package/dist/types/src/core/dwn-error.d.ts.map +1 -1
  210. package/dist/types/src/core/encryption-control.d.ts +128 -0
  211. package/dist/types/src/core/encryption-control.d.ts.map +1 -0
  212. package/dist/types/src/core/message.d.ts +4 -0
  213. package/dist/types/src/core/message.d.ts.map +1 -1
  214. package/dist/types/src/core/messages-grant-authorization.d.ts +11 -0
  215. package/dist/types/src/core/messages-grant-authorization.d.ts.map +1 -1
  216. package/dist/types/src/core/protocol-authorization-action.d.ts.map +1 -1
  217. package/dist/types/src/core/protocol-authorization-validation.d.ts.map +1 -1
  218. package/dist/types/src/core/protocol-authorization.d.ts.map +1 -1
  219. package/dist/types/src/core/recording-validation-state-reader.d.ts +2 -3
  220. package/dist/types/src/core/recording-validation-state-reader.d.ts.map +1 -1
  221. package/dist/types/src/core/replication-apply.d.ts +10 -1
  222. package/dist/types/src/core/replication-apply.d.ts.map +1 -1
  223. package/dist/types/src/core/validation-state-reader.d.ts +2 -3
  224. package/dist/types/src/core/validation-state-reader.d.ts.map +1 -1
  225. package/dist/types/src/dwn.d.ts.map +1 -1
  226. package/dist/types/src/event-stream/event-emitter-wake-publisher.d.ts +1 -1
  227. package/dist/types/src/event-stream/event-emitter-wake-publisher.d.ts.map +1 -1
  228. package/dist/types/src/handlers/messages-query.d.ts.map +1 -1
  229. package/dist/types/src/handlers/messages-read.d.ts.map +1 -1
  230. package/dist/types/src/handlers/messages-subscribe.d.ts.map +1 -1
  231. package/dist/types/src/handlers/protocols-configure.d.ts +1 -0
  232. package/dist/types/src/handlers/protocols-configure.d.ts.map +1 -1
  233. package/dist/types/src/handlers/records-count.d.ts +2 -0
  234. package/dist/types/src/handlers/records-count.d.ts.map +1 -1
  235. package/dist/types/src/handlers/records-delete.d.ts.map +1 -1
  236. package/dist/types/src/handlers/records-query.d.ts +3 -0
  237. package/dist/types/src/handlers/records-query.d.ts.map +1 -1
  238. package/dist/types/src/handlers/records-read.d.ts.map +1 -1
  239. package/dist/types/src/handlers/records-subscribe.d.ts +2 -0
  240. package/dist/types/src/handlers/records-subscribe.d.ts.map +1 -1
  241. package/dist/types/src/handlers/records-write.d.ts.map +1 -1
  242. package/dist/types/src/index.d.ts +12 -18
  243. package/dist/types/src/index.d.ts.map +1 -1
  244. package/dist/types/src/interfaces/protocols-configure.d.ts +13 -1
  245. package/dist/types/src/interfaces/protocols-configure.d.ts.map +1 -1
  246. package/dist/types/src/interfaces/records-write.d.ts +5 -1
  247. package/dist/types/src/interfaces/records-write.d.ts.map +1 -1
  248. package/dist/types/src/jose/algorithms/signing/ed25519.d.ts.map +1 -1
  249. package/dist/types/src/protocols/encryption.d.ts +15 -19
  250. package/dist/types/src/protocols/encryption.d.ts.map +1 -1
  251. package/dist/types/src/store/blockstore-level.d.ts +11 -11
  252. package/dist/types/src/store/blockstore-level.d.ts.map +1 -1
  253. package/dist/types/src/store/blockstore-mock.d.ts +12 -12
  254. package/dist/types/src/store/blockstore-mock.d.ts.map +1 -1
  255. package/dist/types/src/store/blockstore-utils.d.ts +23 -0
  256. package/dist/types/src/store/blockstore-utils.d.ts.map +1 -0
  257. package/dist/types/src/store/blockstore.d.ts +3 -0
  258. package/dist/types/src/store/blockstore.d.ts.map +1 -0
  259. package/dist/types/src/store/data-store-level.d.ts.map +1 -1
  260. package/dist/types/src/store/level.d.ts +13 -0
  261. package/dist/types/src/store/level.d.ts.map +1 -0
  262. package/dist/types/src/types/cache.d.ts +2 -2
  263. package/dist/types/src/types/cache.d.ts.map +1 -1
  264. package/dist/types/src/types/encryption-types.d.ts +31 -2
  265. package/dist/types/src/types/encryption-types.d.ts.map +1 -1
  266. package/dist/types/src/types/validation-state-reader.d.ts +3 -5
  267. package/dist/types/src/types/validation-state-reader.d.ts.map +1 -1
  268. package/dist/types/src/utils/data-stream.d.ts +5 -0
  269. package/dist/types/src/utils/data-stream.d.ts.map +1 -1
  270. package/dist/types/src/utils/encryption.d.ts +53 -24
  271. package/dist/types/src/utils/encryption.d.ts.map +1 -1
  272. package/dist/types/src/utils/grant-key-coverage.d.ts +36 -0
  273. package/dist/types/src/utils/grant-key-coverage.d.ts.map +1 -0
  274. package/dist/types/src/utils/hd-key.d.ts.map +1 -1
  275. package/dist/types/src/utils/jws.d.ts.map +1 -1
  276. package/dist/types/src/utils/memory-cache.d.ts +4 -2
  277. package/dist/types/src/utils/memory-cache.d.ts.map +1 -1
  278. package/dist/types/src/utils/protocols.d.ts +11 -0
  279. package/dist/types/src/utils/protocols.d.ts.map +1 -1
  280. package/dist/types/src/utils/record-limit-occupancy.d.ts +7 -0
  281. package/dist/types/src/utils/record-limit-occupancy.d.ts.map +1 -1
  282. package/dist/types/src/utils/records.d.ts +2 -0
  283. package/dist/types/src/utils/records.d.ts.map +1 -1
  284. package/dist/types/src/utils/secp256k1.d.ts.map +1 -1
  285. package/dist/types/src/utils/secp256r1.d.ts.map +1 -1
  286. package/dist/types/src/utils/time.d.ts +21 -13
  287. package/dist/types/src/utils/time.d.ts.map +1 -1
  288. package/dist/types/tests/core/constants.spec.d.ts +2 -0
  289. package/dist/types/tests/core/constants.spec.d.ts.map +1 -0
  290. package/dist/types/tests/core/encryption-control.spec.d.ts +2 -0
  291. package/dist/types/tests/core/encryption-control.spec.d.ts.map +1 -0
  292. package/dist/types/tests/dwn.spec.d.ts.map +1 -1
  293. package/dist/types/tests/event-emitter-wake-publisher.spec.d.ts +2 -0
  294. package/dist/types/tests/event-emitter-wake-publisher.spec.d.ts.map +1 -0
  295. package/dist/types/tests/handlers/messages-query.spec.d.ts.map +1 -1
  296. package/dist/types/tests/handlers/messages-read.spec.d.ts.map +1 -1
  297. package/dist/types/tests/handlers/messages-subscribe.spec.d.ts.map +1 -1
  298. package/dist/types/tests/handlers/protocols-configure.spec.d.ts.map +1 -1
  299. package/dist/types/tests/handlers/records-count.spec.d.ts.map +1 -1
  300. package/dist/types/tests/handlers/records-delete.spec.d.ts.map +1 -1
  301. package/dist/types/tests/handlers/records-query.spec.d.ts.map +1 -1
  302. package/dist/types/tests/handlers/records-read.spec.d.ts.map +1 -1
  303. package/dist/types/tests/handlers/records-subscribe.spec.d.ts.map +1 -1
  304. package/dist/types/tests/handlers/records-write.spec.d.ts.map +1 -1
  305. package/dist/types/tests/scenarios/end-to-end-tests.spec.d.ts.map +1 -1
  306. package/dist/types/tests/store/blockstore-utils.spec.d.ts +2 -0
  307. package/dist/types/tests/store/blockstore-utils.spec.d.ts.map +1 -0
  308. package/dist/types/tests/test-stores.d.ts.map +1 -1
  309. package/dist/types/tests/utils/encryption-control-test-utils.d.ts +46 -0
  310. package/dist/types/tests/utils/encryption-control-test-utils.d.ts.map +1 -0
  311. package/dist/types/tests/utils/grant-key-coverage.spec.d.ts +2 -0
  312. package/dist/types/tests/utils/grant-key-coverage.spec.d.ts.map +1 -0
  313. package/dist/types/tests/utils/test-data-generator.d.ts.map +1 -1
  314. package/dist/types/tests/validation/json-schemas/encryption/audience.spec.d.ts +2 -0
  315. package/dist/types/tests/validation/json-schemas/encryption/audience.spec.d.ts.map +1 -0
  316. package/dist/types/tests/validation/json-schemas/encryption/delivery.spec.d.ts +2 -0
  317. package/dist/types/tests/validation/json-schemas/encryption/delivery.spec.d.ts.map +1 -0
  318. package/dist/types/tests/validation/json-schemas/encryption/wrapped-grant-key-envelope.spec.d.ts +2 -0
  319. package/dist/types/tests/validation/json-schemas/encryption/wrapped-grant-key-envelope.spec.d.ts.map +1 -0
  320. package/package.json +47 -28
  321. package/src/core/constants.ts +61 -0
  322. package/src/core/dwn-error.ts +29 -11
  323. package/src/core/encryption-control.ts +1249 -0
  324. package/src/core/message.ts +7 -0
  325. package/src/core/messages-grant-authorization.ts +60 -9
  326. package/src/core/protocol-authorization-action.ts +3 -13
  327. package/src/core/protocol-authorization-validation.ts +66 -46
  328. package/src/core/protocol-authorization.ts +7 -0
  329. package/src/core/recording-validation-state-reader.ts +4 -5
  330. package/src/core/replication-apply.ts +77 -133
  331. package/src/core/validation-state-reader.ts +6 -8
  332. package/src/dwn.ts +1 -2
  333. package/src/event-stream/event-emitter-wake-publisher.ts +10 -14
  334. package/src/handlers/messages-query.ts +6 -22
  335. package/src/handlers/messages-read.ts +4 -3
  336. package/src/handlers/messages-subscribe.ts +34 -30
  337. package/src/handlers/protocols-configure.ts +7 -4
  338. package/src/handlers/records-count.ts +111 -10
  339. package/src/handlers/records-delete.ts +15 -0
  340. package/src/handlers/records-query.ts +176 -20
  341. package/src/handlers/records-read.ts +19 -6
  342. package/src/handlers/records-subscribe.ts +158 -8
  343. package/src/handlers/records-write.ts +19 -0
  344. package/src/index.ts +50 -22
  345. package/src/interfaces/protocols-configure.ts +102 -11
  346. package/src/interfaces/records-write.ts +49 -43
  347. package/src/jose/algorithms/signing/ed25519.ts +7 -6
  348. package/src/jose/jws/general/verifier.ts +3 -3
  349. package/src/protocols/encryption.ts +94 -411
  350. package/src/store/blockstore-level.ts +33 -37
  351. package/src/store/blockstore-mock.ts +24 -38
  352. package/src/store/blockstore-utils.ts +85 -0
  353. package/src/store/blockstore.ts +8 -0
  354. package/src/store/data-store-level.ts +5 -12
  355. package/src/store/level.ts +19 -0
  356. package/src/types/cache.ts +3 -3
  357. package/src/types/encryption-types.ts +39 -2
  358. package/src/types/validation-state-reader.ts +3 -5
  359. package/src/utils/data-stream.ts +29 -1
  360. package/src/utils/encryption.ts +190 -60
  361. package/src/utils/grant-key-coverage.ts +303 -0
  362. package/src/utils/hd-key.ts +1 -2
  363. package/src/utils/jws.ts +8 -2
  364. package/src/utils/memory-cache.ts +42 -11
  365. package/src/utils/protocols.ts +32 -0
  366. package/src/utils/record-limit-occupancy.ts +34 -5
  367. package/src/utils/records.ts +36 -2
  368. package/src/utils/secp256k1.ts +12 -12
  369. package/src/utils/secp256r1.ts +12 -22
  370. package/src/utils/time.ts +145 -27
  371. package/dist/esm/tests/validation/json-schemas/encryption/audience-key.spec.js +0 -40
  372. package/dist/esm/tests/validation/json-schemas/encryption/audience-key.spec.js.map +0 -1
  373. package/dist/types/tests/validation/json-schemas/encryption/audience-key.spec.d.ts +0 -2
  374. package/dist/types/tests/validation/json-schemas/encryption/audience-key.spec.d.ts.map +0 -1
  375. package/src/types/mitt.d.ts +0 -28
@@ -19,6 +19,9 @@ import { Cid } from '../../src/utils/cid.js';
19
19
  import { DataStream } from '../../src/utils/data-stream.js';
20
20
  import { Dwn } from '../../src/dwn.js';
21
21
  import { Encoder } from '../../src/utils/encoder.js';
22
+ import { ENCRYPTION_CONTROL_AUDIENCE_PATH } from '../../src/core/constants.js';
23
+ import { ENCRYPTION_CONTROL_DELIVERY_PATH } from '../../src/core/constants.js';
24
+ import { EncryptionControlDeliveryRecipientAuthority } from '../../src/types/encryption-types.js';
22
25
  import { GeneralJwsBuilder } from '../../src/jose/jws/general/builder.js';
23
26
  import { Jws } from '../../src/utils/jws.js';
24
27
  import { Message } from '../../src/core/message.js';
@@ -31,8 +34,9 @@ import { TestEventLog } from '../test-event-stream.js';
31
34
  import { TestStores } from '../test-stores.js';
32
35
  import { TestStubGenerator } from '../utils/test-stub-generator.js';
33
36
  import { Time } from '../../src/utils/time.js';
34
- import { ContentEncryptionAlgorithm, Encryption, ROLE_AUDIENCE_DERIVATION_SCHEME } from '../../src/utils/encryption.js';
35
- import { CoreProtocolRegistry, DataStoreLevel, DwnConstant, DwnInterfaceName, DwnMethodName, EncryptionProtocol, KeyDerivationScheme, MessageStoreLevel, PermissionsProtocol, Protocols, RecordsDelete, RecordsQuery } from '../../src/index.js';
37
+ import { ContentEncryptionAlgorithm, Encryption, KeyAgreementAlgorithm, ROLE_AUDIENCE_DERIVATION_SCHEME } from '../../src/utils/encryption.js';
38
+ import { CoreProtocolRegistry, DwnConstant, DwnInterfaceName, DwnMethodName, KeyDerivationScheme, PermissionsProtocol, Protocols, RecordsDelete, RecordsQuery } from '../../src/index.js';
39
+ import { DataStoreLevel, MessageStoreLevel } from '../../src/store/level.js';
36
40
  import { defaultTestProtocolDefinition, TestDataGenerator } from '../utils/test-data-generator.js';
37
41
  import { DidKey, UniversalResolver } from '@enbox/dids';
38
42
  import { DwnError, DwnErrorCode } from '../../src/core/dwn-error.js';
@@ -70,6 +74,99 @@ export function testRecordsWriteHandler() {
70
74
  afterAll(async () => {
71
75
  await dwn.close();
72
76
  });
77
+ async function installEncryptedProtocol(author, protocolDefinition) {
78
+ const encryptedProtocolDefinition = await Protocols.deriveAndInjectPublicEncryptionKeys(protocolDefinition, author.keyId, author.encryptionKeyPair.privateJwk);
79
+ const protocolConfig = await TestDataGenerator.generateProtocolsConfigure({
80
+ author,
81
+ protocolDefinition: encryptedProtocolDefinition,
82
+ });
83
+ expect((await dwn.processMessage(author.did, protocolConfig.message)).status.code).toBe(202);
84
+ return encryptedProtocolDefinition;
85
+ }
86
+ async function createAudienceControlWrite(input) {
87
+ const audiencePublicKeyJwk = input.author.encryptionKeyPair.publicJwk;
88
+ const audienceKeyId = await Encryption.getKeyId(audiencePublicKeyJwk);
89
+ const sealKeyId = input.sealKeyId ?? await Encryption.getKeyId(input.roleRuleSet.$keyAgreement.publicKeyJwk);
90
+ const payload = {
91
+ protocol: input.protocol,
92
+ rolePath: input.rolePath,
93
+ contextId: input.contextId ?? '',
94
+ keyId: audienceKeyId,
95
+ publicKeyJwk: audiencePublicKeyJwk,
96
+ sealedPrivateKey: {
97
+ algorithm: KeyAgreementAlgorithm.X25519HkdfSha256A256Kw,
98
+ derivationScheme: 'seal',
99
+ keyId: sealKeyId,
100
+ ephemeralPublicKey: input.author.encryptionKeyPair.publicJwk,
101
+ encryptedKey: Encoder.bytesToBase64Url(TestDataGenerator.randomBytes(32)),
102
+ },
103
+ ...input.payloadOverrides,
104
+ };
105
+ const dataBytes = Encoder.objectToBytes(payload);
106
+ const recordsWrite = await RecordsWrite.create({
107
+ data: dataBytes,
108
+ dataFormat: 'application/json',
109
+ dateCreated: input.dateCreated,
110
+ messageTimestamp: input.dateCreated,
111
+ delegatedGrant: input.delegatedGrant,
112
+ permissionGrantId: input.permissionGrantId,
113
+ protocol: input.protocol,
114
+ protocolPath: ENCRYPTION_CONTROL_AUDIENCE_PATH,
115
+ protocolRole: input.protocolRole,
116
+ published: input.published,
117
+ schema: 'https://identity.foundation/dwn/json-schemas/encryption/audience.json',
118
+ signer: Jws.createSigner(input.signer ?? input.author),
119
+ tags: {
120
+ protocol: input.protocol,
121
+ rolePath: input.rolePath,
122
+ contextId: input.contextId ?? '',
123
+ keyId: audienceKeyId,
124
+ ...input.tags,
125
+ },
126
+ });
127
+ return {
128
+ dataBytes,
129
+ keyId: audienceKeyId,
130
+ payload,
131
+ recordsWrite,
132
+ };
133
+ }
134
+ async function createDeliveryControlWrite(input) {
135
+ const tags = {
136
+ protocol: input.protocol,
137
+ rolePath: input.rolePath,
138
+ contextId: input.contextId ?? '',
139
+ keyId: input.keyId,
140
+ recipientAuthority: input.recipientAuthority,
141
+ };
142
+ const dataBytes = Encoder.objectToBytes({
143
+ protocol: input.protocol,
144
+ rolePath: input.rolePath,
145
+ contextId: input.contextId ?? '',
146
+ keyId: input.keyId,
147
+ });
148
+ const recordsWrite = await RecordsWrite.create({
149
+ data: dataBytes,
150
+ dataFormat: 'application/json',
151
+ encryptionInput: {
152
+ initializationVector: TestDataGenerator.randomBytes(16),
153
+ key: TestDataGenerator.randomBytes(32),
154
+ keyEncryptionInputs: [{
155
+ algorithm: KeyAgreementAlgorithm.X25519HkdfSha256A256Kw,
156
+ keyId: await Encryption.getKeyId(input.roleRuleSet.$keyAgreement.publicKeyJwk),
157
+ publicKey: input.roleRuleSet.$keyAgreement.publicKeyJwk,
158
+ derivationScheme: KeyDerivationScheme.ProtocolPath,
159
+ }],
160
+ },
161
+ protocol: input.protocol,
162
+ protocolPath: ENCRYPTION_CONTROL_DELIVERY_PATH,
163
+ recipient: input.recipient,
164
+ schema: 'https://identity.foundation/dwn/json-schemas/encryption/delivery.json',
165
+ signer: Jws.createSigner(input.author),
166
+ tags,
167
+ });
168
+ return { dataBytes, recordsWrite };
169
+ }
73
170
  it('should dispatch preProcessWrite hook via CoreProtocolRegistry and abort before storage on failure', async () => {
74
171
  // Register a mock core protocol whose preProcessWrite hook throws.
75
172
  // Verify that authorization completes but processMessageWithDataStream is never reached.
@@ -98,6 +195,716 @@ export function testRecordsWriteHandler() {
98
195
  // Cleanup: unregister the mock so it doesn't affect other tests
99
196
  coreProtocols._protocols.delete(protocolUri);
100
197
  });
198
+ it('should accept tenant-authored audience control records at the reserved source-protocol path', async () => {
199
+ const alice = await TestDataGenerator.generateDidKeyPersona();
200
+ const protocolDefinition = {
201
+ protocol: 'http://encryption-control-audience-accepted.xyz',
202
+ published: false,
203
+ types: {
204
+ member: { schema: 'http://member-schema', dataFormats: ['application/json'] },
205
+ message: { schema: 'http://message-schema', dataFormats: ['text/plain'] },
206
+ },
207
+ structure: {
208
+ member: { $role: true },
209
+ message: {},
210
+ },
211
+ };
212
+ const encryptedProtocolDefinition = await installEncryptedProtocol(alice, protocolDefinition);
213
+ const roleRuleSet = encryptedProtocolDefinition.structure.member;
214
+ const audience = await createAudienceControlWrite({
215
+ author: alice,
216
+ protocol: protocolDefinition.protocol,
217
+ rolePath: 'member',
218
+ roleRuleSet,
219
+ });
220
+ const reply = await dwn.processMessage(alice.did, audience.recordsWrite.message, { dataStream: DataStream.fromBytes(audience.dataBytes) });
221
+ expect(reply.status.code).toBe(202);
222
+ });
223
+ it('should reject published encryption control records', async () => {
224
+ const alice = await TestDataGenerator.generateDidKeyPersona();
225
+ const protocolDefinition = {
226
+ protocol: 'http://encryption-control-published-rejected.xyz',
227
+ published: false,
228
+ types: {
229
+ member: { schema: 'http://member-schema', dataFormats: ['application/json'] },
230
+ },
231
+ structure: {
232
+ member: { $role: true },
233
+ },
234
+ };
235
+ const encryptedProtocolDefinition = await installEncryptedProtocol(alice, protocolDefinition);
236
+ const audience = await createAudienceControlWrite({
237
+ author: alice,
238
+ protocol: protocolDefinition.protocol,
239
+ published: true,
240
+ rolePath: 'member',
241
+ roleRuleSet: encryptedProtocolDefinition.structure.member,
242
+ });
243
+ const reply = await dwn.processMessage(alice.did, audience.recordsWrite.message, { dataStream: DataStream.fromBytes(audience.dataBytes) });
244
+ expect(reply.status.code).toBe(400);
245
+ expect(reply.status.detail).toContain(DwnErrorCode.EncryptionControlValidateUnexpectedRecord);
246
+ });
247
+ it('should reject dataless and update attempts for immutable audience control records', async () => {
248
+ const alice = await TestDataGenerator.generateDidKeyPersona();
249
+ const protocolDefinition = {
250
+ protocol: 'http://encryption-control-audience-immutable.xyz',
251
+ published: false,
252
+ types: {
253
+ member: { schema: 'http://member-schema', dataFormats: ['application/json'] },
254
+ },
255
+ structure: {
256
+ member: { $role: true },
257
+ },
258
+ };
259
+ const encryptedProtocolDefinition = await installEncryptedProtocol(alice, protocolDefinition);
260
+ const roleRuleSet = encryptedProtocolDefinition.structure.member;
261
+ const audience = await createAudienceControlWrite({
262
+ author: alice,
263
+ protocol: protocolDefinition.protocol,
264
+ rolePath: 'member',
265
+ roleRuleSet,
266
+ });
267
+ expect((await dwn.processMessage(alice.did, audience.recordsWrite.message, { dataStream: DataStream.fromBytes(audience.dataBytes) })).status.code).toBe(202);
268
+ const updateData = Encoder.objectToBytes({ ...audience.payload, publicKeyJwk: roleRuleSet.$keyAgreement.publicKeyJwk });
269
+ const update = await RecordsWrite.createFrom({
270
+ data: updateData,
271
+ recordsWriteMessage: audience.recordsWrite.message,
272
+ signer: Jws.createSigner(alice),
273
+ });
274
+ const updateReply = await dwn.processMessage(alice.did, update.message, { dataStream: DataStream.fromBytes(updateData) });
275
+ expect(updateReply.status.code).toBe(400);
276
+ expect(updateReply.status.detail).toContain(DwnErrorCode.EncryptionControlValidateUnexpectedRecord);
277
+ const datalessAudience = await createAudienceControlWrite({
278
+ author: alice,
279
+ protocol: protocolDefinition.protocol,
280
+ rolePath: 'member',
281
+ roleRuleSet,
282
+ });
283
+ const datalessReply = await dwn.processMessage(alice.did, datalessAudience.recordsWrite.message);
284
+ expect(datalessReply.status.code).toBe(400);
285
+ expect(datalessReply.status.detail).toContain(DwnErrorCode.EncryptionControlValidateUnexpectedRecord);
286
+ });
287
+ it('should reject audience control records whose seal keyId does not match the role path key', async () => {
288
+ const alice = await TestDataGenerator.generateDidKeyPersona();
289
+ const bob = await TestDataGenerator.generateDidKeyPersona();
290
+ const protocolDefinition = {
291
+ protocol: 'http://encryption-control-audience-seal-mismatch.xyz',
292
+ published: false,
293
+ types: {
294
+ member: { schema: 'http://member-schema', dataFormats: ['application/json'] },
295
+ },
296
+ structure: {
297
+ member: { $role: true },
298
+ },
299
+ };
300
+ const encryptedProtocolDefinition = await installEncryptedProtocol(alice, protocolDefinition);
301
+ const roleRuleSet = encryptedProtocolDefinition.structure.member;
302
+ const audience = await createAudienceControlWrite({
303
+ author: alice,
304
+ protocol: protocolDefinition.protocol,
305
+ rolePath: 'member',
306
+ roleRuleSet,
307
+ sealKeyId: await Encryption.getKeyId(bob.encryptionKeyPair.publicJwk),
308
+ });
309
+ const reply = await dwn.processMessage(alice.did, audience.recordsWrite.message, { dataStream: DataStream.fromBytes(audience.dataBytes) });
310
+ expect(reply.status.code).toBe(400);
311
+ expect(reply.status.detail).toContain(DwnErrorCode.EncryptionControlValidateAudienceSealKeyIdMismatch);
312
+ });
313
+ it('should reject malformed audience control tags as bad input before authorization', async () => {
314
+ const alice = await TestDataGenerator.generateDidKeyPersona();
315
+ const dataBytes = Encoder.objectToBytes({
316
+ protocol: 'http://encryption-control-audience-malformed-tags.xyz',
317
+ rolePath: 'member',
318
+ contextId: '',
319
+ keyId: await Encryption.getKeyId(alice.encryptionKeyPair.publicJwk),
320
+ publicKeyJwk: alice.encryptionKeyPair.publicJwk,
321
+ sealedPrivateKey: {
322
+ algorithm: KeyAgreementAlgorithm.X25519HkdfSha256A256Kw,
323
+ derivationScheme: 'seal',
324
+ keyId: await Encryption.getKeyId(alice.encryptionKeyPair.publicJwk),
325
+ ephemeralPublicKey: alice.encryptionKeyPair.publicJwk,
326
+ encryptedKey: Encoder.bytesToBase64Url(TestDataGenerator.randomBytes(32)),
327
+ },
328
+ });
329
+ const audience = await RecordsWrite.create({
330
+ data: dataBytes,
331
+ dataFormat: 'application/json',
332
+ protocol: 'http://encryption-control-audience-malformed-tags.xyz',
333
+ protocolPath: ENCRYPTION_CONTROL_AUDIENCE_PATH,
334
+ schema: 'https://identity.foundation/dwn/json-schemas/encryption/audience.json',
335
+ signer: Jws.createSigner(alice),
336
+ tags: {
337
+ protocol: 'http://encryption-control-audience-malformed-tags.xyz',
338
+ rolePath: 'member',
339
+ contextId: '',
340
+ },
341
+ });
342
+ const reply = await dwn.processMessage(alice.did, audience.message, { dataStream: DataStream.fromBytes(dataBytes) });
343
+ expect(reply.status.code).toBe(400);
344
+ expect(reply.status.detail).toContain(DwnErrorCode.EncryptionControlValidateAudienceMissingRequiredTag);
345
+ });
346
+ it('should reject delivery control records that reference a missing audience', async () => {
347
+ const alice = await TestDataGenerator.generateDidKeyPersona();
348
+ const protocolDefinition = {
349
+ protocol: 'http://encryption-control-delivery-missing-audience.xyz',
350
+ published: false,
351
+ types: {
352
+ member: { schema: 'http://member-schema', dataFormats: ['application/json'] },
353
+ },
354
+ structure: {
355
+ member: { $role: true },
356
+ },
357
+ };
358
+ const encryptedProtocolDefinition = await installEncryptedProtocol(alice, protocolDefinition);
359
+ const roleRuleSet = encryptedProtocolDefinition.structure.member;
360
+ const rolePublicKey = roleRuleSet.$keyAgreement.publicKeyJwk;
361
+ const missingAudienceKeyId = await Encryption.getKeyId(alice.encryptionKeyPair.publicJwk);
362
+ const dataBytes = Encoder.objectToBytes({ encrypted: true });
363
+ const delivery = await RecordsWrite.create({
364
+ data: dataBytes,
365
+ dataFormat: 'application/json',
366
+ encryptionInput: {
367
+ initializationVector: TestDataGenerator.randomBytes(16),
368
+ key: TestDataGenerator.randomBytes(32),
369
+ keyEncryptionInputs: [{
370
+ algorithm: KeyAgreementAlgorithm.X25519HkdfSha256A256Kw,
371
+ keyId: await Encryption.getKeyId(rolePublicKey),
372
+ publicKey: rolePublicKey,
373
+ derivationScheme: KeyDerivationScheme.ProtocolPath,
374
+ }],
375
+ },
376
+ protocol: protocolDefinition.protocol,
377
+ protocolPath: ENCRYPTION_CONTROL_DELIVERY_PATH,
378
+ recipient: alice.did,
379
+ schema: 'https://identity.foundation/dwn/json-schemas/encryption/delivery.json',
380
+ signer: Jws.createSigner(alice),
381
+ tags: {
382
+ protocol: protocolDefinition.protocol,
383
+ rolePath: 'member',
384
+ contextId: '',
385
+ keyId: missingAudienceKeyId,
386
+ recipientAuthority: 'roleHolder',
387
+ },
388
+ });
389
+ const reply = await dwn.processMessage(alice.did, delivery.message, { dataStream: DataStream.fromBytes(dataBytes) });
390
+ expect(reply.status.code).toBe(400);
391
+ expect(reply.status.detail).toContain(DwnErrorCode.EncryptionControlValidateDeliveryAudienceMissing);
392
+ });
393
+ it('should reject author-delegated audience control writes when the grant does not cover the role path', async () => {
394
+ const alice = await TestDataGenerator.generateDidKeyPersona();
395
+ const bob = await TestDataGenerator.generateDidKeyPersona();
396
+ const protocolDefinition = {
397
+ protocol: 'http://encryption-control-author-delegate-rejected.xyz',
398
+ published: false,
399
+ types: {
400
+ member: { schema: 'http://member-schema', dataFormats: ['application/json'] },
401
+ message: { schema: 'http://message-schema', dataFormats: ['text/plain'] },
402
+ },
403
+ structure: {
404
+ member: { $role: true },
405
+ message: {},
406
+ },
407
+ };
408
+ const encryptedProtocolDefinition = await Protocols.deriveAndInjectPublicEncryptionKeys(protocolDefinition, alice.keyId, alice.encryptionKeyPair.privateJwk);
409
+ const protocolConfig = await TestDataGenerator.generateProtocolsConfigure({
410
+ author: alice,
411
+ protocolDefinition: encryptedProtocolDefinition,
412
+ });
413
+ expect((await dwn.processMessage(alice.did, protocolConfig.message)).status.code).toBe(202);
414
+ const grant = await TestDataGenerator.generateGrantCreate({
415
+ author: alice,
416
+ grantedTo: bob,
417
+ delegated: true,
418
+ scope: {
419
+ interface: DwnInterfaceName.Records,
420
+ method: DwnMethodName.Write,
421
+ protocol: protocolDefinition.protocol,
422
+ protocolPath: 'message',
423
+ },
424
+ });
425
+ expect((await dwn.processMessage(alice.did, grant.message, { dataStream: grant.dataStream })).status.code).toBe(202);
426
+ const roleRuleSet = encryptedProtocolDefinition.structure.member;
427
+ const audiencePublicKeyJwk = alice.encryptionKeyPair.publicJwk;
428
+ const audienceKeyId = await Encryption.getKeyId(audiencePublicKeyJwk);
429
+ const sealKeyId = await Encryption.getKeyId(roleRuleSet.$keyAgreement.publicKeyJwk);
430
+ const dataBytes = Encoder.objectToBytes({
431
+ protocol: protocolDefinition.protocol,
432
+ rolePath: 'member',
433
+ contextId: '',
434
+ keyId: audienceKeyId,
435
+ publicKeyJwk: audiencePublicKeyJwk,
436
+ sealedPrivateKey: {
437
+ algorithm: KeyAgreementAlgorithm.X25519HkdfSha256A256Kw,
438
+ derivationScheme: 'seal',
439
+ keyId: sealKeyId,
440
+ ephemeralPublicKey: alice.encryptionKeyPair.publicJwk,
441
+ encryptedKey: Encoder.bytesToBase64Url(TestDataGenerator.randomBytes(32)),
442
+ },
443
+ });
444
+ const audience = await RecordsWrite.create({
445
+ data: dataBytes,
446
+ dataFormat: 'application/json',
447
+ delegatedGrant: grant.dataEncodedMessage,
448
+ protocol: protocolDefinition.protocol,
449
+ protocolPath: ENCRYPTION_CONTROL_AUDIENCE_PATH,
450
+ schema: 'https://identity.foundation/dwn/json-schemas/encryption/audience.json',
451
+ signer: Jws.createSigner(bob),
452
+ tags: {
453
+ protocol: protocolDefinition.protocol,
454
+ rolePath: 'member',
455
+ contextId: '',
456
+ keyId: audienceKeyId,
457
+ },
458
+ });
459
+ const reply = await dwn.processMessage(alice.did, audience.message, { dataStream: DataStream.fromBytes(dataBytes) });
460
+ expect(reply.status.code).toBe(401);
461
+ expect(reply.status.detail).toContain(DwnErrorCode.EncryptionControlValidateAudienceWriterUnauthorized);
462
+ });
463
+ it('should accept audience control records from authorized non-tenant role creators', async () => {
464
+ const alice = await TestDataGenerator.generateDidKeyPersona();
465
+ const bob = await TestDataGenerator.generateDidKeyPersona();
466
+ const carol = await TestDataGenerator.generateDidKeyPersona();
467
+ const anyoneProtocol = {
468
+ protocol: 'http://encryption-control-audience-anyone-writer.xyz',
469
+ published: false,
470
+ types: {
471
+ member: { schema: 'http://member-schema', dataFormats: ['application/json'] },
472
+ },
473
+ structure: {
474
+ member: {
475
+ $role: true,
476
+ $actions: [{ who: 'anyone', can: ['create'] }],
477
+ },
478
+ },
479
+ };
480
+ const encryptedAnyoneProtocol = await installEncryptedProtocol(alice, anyoneProtocol);
481
+ const anyoneRoleRuleSet = encryptedAnyoneProtocol.structure.member;
482
+ const anyoneAudience = await createAudienceControlWrite({
483
+ author: bob,
484
+ protocol: anyoneProtocol.protocol,
485
+ rolePath: 'member',
486
+ roleRuleSet: anyoneRoleRuleSet,
487
+ });
488
+ expect((await dwn.processMessage(alice.did, anyoneAudience.recordsWrite.message, {
489
+ dataStream: DataStream.fromBytes(anyoneAudience.dataBytes),
490
+ })).status.code).toBe(202);
491
+ const roleProtocol = {
492
+ protocol: 'http://encryption-control-audience-role-writer.xyz',
493
+ published: false,
494
+ types: {
495
+ admin: { schema: 'http://admin-schema', dataFormats: ['application/json'] },
496
+ member: { schema: 'http://member-schema', dataFormats: ['application/json'] },
497
+ },
498
+ structure: {
499
+ admin: { $role: true },
500
+ member: {
501
+ $role: true,
502
+ $actions: [{ role: 'admin', can: ['create'] }],
503
+ },
504
+ },
505
+ };
506
+ const encryptedRoleProtocol = await installEncryptedProtocol(alice, roleProtocol);
507
+ const memberRoleRuleSet = encryptedRoleProtocol.structure.member;
508
+ const adminRole = await TestDataGenerator.generateRecordsWrite({
509
+ author: alice,
510
+ data: Encoder.stringToBytes('bob is an admin'),
511
+ dataFormat: 'application/json',
512
+ protocol: roleProtocol.protocol,
513
+ protocolPath: 'admin',
514
+ recipient: bob.did,
515
+ schema: 'http://admin-schema',
516
+ });
517
+ expect((await dwn.processMessage(alice.did, adminRole.message, { dataStream: adminRole.dataStream })).status.code).toBe(202);
518
+ const roleAudience = await createAudienceControlWrite({
519
+ author: bob,
520
+ protocol: roleProtocol.protocol,
521
+ protocolRole: 'admin',
522
+ rolePath: 'member',
523
+ roleRuleSet: memberRoleRuleSet,
524
+ });
525
+ expect((await dwn.processMessage(alice.did, roleAudience.recordsWrite.message, {
526
+ dataStream: DataStream.fromBytes(roleAudience.dataBytes),
527
+ })).status.code).toBe(202);
528
+ const grantProtocol = {
529
+ protocol: 'http://encryption-control-audience-delegated-writer.xyz',
530
+ published: false,
531
+ types: {
532
+ member: { schema: 'http://member-schema', dataFormats: ['application/json'] },
533
+ },
534
+ structure: {
535
+ member: { $role: true },
536
+ },
537
+ };
538
+ const encryptedGrantProtocol = await installEncryptedProtocol(alice, grantProtocol);
539
+ const grantRoleRuleSet = encryptedGrantProtocol.structure.member;
540
+ const grant = await TestDataGenerator.generateGrantCreate({
541
+ author: alice,
542
+ grantedTo: carol,
543
+ delegated: true,
544
+ scope: {
545
+ interface: DwnInterfaceName.Records,
546
+ method: DwnMethodName.Write,
547
+ protocol: grantProtocol.protocol,
548
+ protocolPath: 'member',
549
+ },
550
+ });
551
+ expect((await dwn.processMessage(alice.did, grant.message, { dataStream: grant.dataStream })).status.code).toBe(202);
552
+ const delegatedAudience = await createAudienceControlWrite({
553
+ author: alice,
554
+ delegatedGrant: grant.dataEncodedMessage,
555
+ protocol: grantProtocol.protocol,
556
+ rolePath: 'member',
557
+ roleRuleSet: grantRoleRuleSet,
558
+ signer: carol,
559
+ });
560
+ expect((await dwn.processMessage(alice.did, delegatedAudience.recordsWrite.message, {
561
+ dataStream: DataStream.fromBytes(delegatedAudience.dataBytes),
562
+ })).status.code).toBe(202);
563
+ const directGrant = await TestDataGenerator.generateGrantCreate({
564
+ author: alice,
565
+ grantedTo: bob,
566
+ scope: {
567
+ interface: DwnInterfaceName.Records,
568
+ method: DwnMethodName.Write,
569
+ protocol: grantProtocol.protocol,
570
+ protocolPath: 'member',
571
+ },
572
+ });
573
+ expect((await dwn.processMessage(alice.did, directGrant.message, { dataStream: directGrant.dataStream })).status.code).toBe(202);
574
+ const directGrantAudience = await createAudienceControlWrite({
575
+ author: bob,
576
+ permissionGrantId: directGrant.message.recordId,
577
+ protocol: grantProtocol.protocol,
578
+ rolePath: 'member',
579
+ roleRuleSet: grantRoleRuleSet,
580
+ });
581
+ expect((await dwn.processMessage(alice.did, directGrantAudience.recordsWrite.message, {
582
+ dataStream: DataStream.fromBytes(directGrantAudience.dataBytes),
583
+ })).status.code).toBe(202);
584
+ });
585
+ it('should enforce publication conditions on delegated audience control writes', async () => {
586
+ const alice = await TestDataGenerator.generateDidKeyPersona();
587
+ const bob = await TestDataGenerator.generateDidKeyPersona();
588
+ const protocolDefinition = {
589
+ protocol: 'http://encryption-control-delegated-publication-condition.xyz',
590
+ published: false,
591
+ types: {
592
+ member: { schema: 'http://member-schema', dataFormats: ['application/json'] },
593
+ },
594
+ structure: {
595
+ member: { $role: true },
596
+ },
597
+ };
598
+ const encryptedProtocolDefinition = await installEncryptedProtocol(alice, protocolDefinition);
599
+ const roleRuleSet = encryptedProtocolDefinition.structure.member;
600
+ const grant = await TestDataGenerator.generateGrantCreate({
601
+ author: alice,
602
+ grantedTo: bob,
603
+ delegated: true,
604
+ scope: {
605
+ interface: DwnInterfaceName.Records,
606
+ method: DwnMethodName.Write,
607
+ protocol: protocolDefinition.protocol,
608
+ protocolPath: 'member',
609
+ },
610
+ conditions: {
611
+ publication: PermissionConditionPublication.Required,
612
+ },
613
+ });
614
+ expect((await dwn.processMessage(alice.did, grant.message, { dataStream: grant.dataStream })).status.code).toBe(202);
615
+ const audience = await createAudienceControlWrite({
616
+ author: alice,
617
+ delegatedGrant: grant.dataEncodedMessage,
618
+ protocol: protocolDefinition.protocol,
619
+ published: false,
620
+ rolePath: 'member',
621
+ roleRuleSet,
622
+ signer: bob,
623
+ });
624
+ const reply = await dwn.processMessage(alice.did, audience.recordsWrite.message, { dataStream: DataStream.fromBytes(audience.dataBytes) });
625
+ expect(reply.status.code).toBe(401);
626
+ expect(reply.status.detail).toContain(DwnErrorCode.RecordsGrantAuthorizationConditionPublicationRequired);
627
+ });
628
+ it('should reject malformed audience control records with typed validation errors', async () => {
629
+ const alice = await TestDataGenerator.generateDidKeyPersona();
630
+ const bob = await TestDataGenerator.generateDidKeyPersona();
631
+ const protocolDefinition = {
632
+ protocol: 'http://encryption-control-audience-malformed-payload.xyz',
633
+ published: false,
634
+ types: {
635
+ chat: { schema: 'http://chat-schema', dataFormats: ['application/json'] },
636
+ member: { schema: 'http://member-schema', dataFormats: ['application/json'] },
637
+ },
638
+ structure: {
639
+ chat: {
640
+ member: { $role: true },
641
+ },
642
+ member: { $role: true },
643
+ },
644
+ };
645
+ const encryptedProtocolDefinition = await installEncryptedProtocol(alice, protocolDefinition);
646
+ const rootRoleRuleSet = encryptedProtocolDefinition.structure.member;
647
+ const nestedRoleRuleSet = encryptedProtocolDefinition.structure.chat.member;
648
+ const payloadMismatch = await createAudienceControlWrite({
649
+ author: alice,
650
+ payloadOverrides: { rolePath: 'other' },
651
+ protocol: protocolDefinition.protocol,
652
+ rolePath: 'member',
653
+ roleRuleSet: rootRoleRuleSet,
654
+ });
655
+ const payloadMismatchReply = await dwn.processMessage(alice.did, payloadMismatch.recordsWrite.message, { dataStream: DataStream.fromBytes(payloadMismatch.dataBytes) });
656
+ expect(payloadMismatchReply.status.code).toBe(400);
657
+ expect(payloadMismatchReply.status.detail).toContain(DwnErrorCode.EncryptionControlValidateAudienceTagsMismatch);
658
+ const keyMismatch = await createAudienceControlWrite({
659
+ author: alice,
660
+ payloadOverrides: { publicKeyJwk: bob.encryptionKeyPair.publicJwk },
661
+ protocol: protocolDefinition.protocol,
662
+ rolePath: 'member',
663
+ roleRuleSet: rootRoleRuleSet,
664
+ });
665
+ const keyMismatchReply = await dwn.processMessage(alice.did, keyMismatch.recordsWrite.message, { dataStream: DataStream.fromBytes(keyMismatch.dataBytes) });
666
+ expect(keyMismatchReply.status.code).toBe(400);
667
+ expect(keyMismatchReply.status.detail).toContain(DwnErrorCode.EncryptionControlValidateAudienceKeyIdMismatch);
668
+ const rootWithContext = await createAudienceControlWrite({
669
+ author: alice,
670
+ contextId: 'root-context',
671
+ protocol: protocolDefinition.protocol,
672
+ rolePath: 'member',
673
+ roleRuleSet: rootRoleRuleSet,
674
+ });
675
+ const rootWithContextReply = await dwn.processMessage(alice.did, rootWithContext.recordsWrite.message, { dataStream: DataStream.fromBytes(rootWithContext.dataBytes) });
676
+ expect(rootWithContextReply.status.code).toBe(400);
677
+ expect(rootWithContextReply.status.detail).toContain(DwnErrorCode.EncryptionControlValidateAudienceContextIdInvalid);
678
+ const nestedWithoutContext = await createAudienceControlWrite({
679
+ author: alice,
680
+ protocol: protocolDefinition.protocol,
681
+ rolePath: 'chat/member',
682
+ roleRuleSet: nestedRoleRuleSet,
683
+ });
684
+ const nestedWithoutContextReply = await dwn.processMessage(alice.did, nestedWithoutContext.recordsWrite.message, { dataStream: DataStream.fromBytes(nestedWithoutContext.dataBytes) });
685
+ expect(nestedWithoutContextReply.status.code).toBe(400);
686
+ expect(nestedWithoutContextReply.status.detail).toContain(DwnErrorCode.EncryptionControlValidateAudienceContextIdInvalid);
687
+ const protocolTagMismatch = await createAudienceControlWrite({
688
+ author: alice,
689
+ protocol: protocolDefinition.protocol,
690
+ rolePath: 'member',
691
+ roleRuleSet: rootRoleRuleSet,
692
+ tags: { protocol: 'http://mismatched-control-tag.xyz' },
693
+ });
694
+ const protocolTagMismatchReply = await dwn.processMessage(alice.did, protocolTagMismatch.recordsWrite.message, { dataStream: DataStream.fromBytes(protocolTagMismatch.dataBytes) });
695
+ expect(protocolTagMismatchReply.status.code).toBe(400);
696
+ expect(protocolTagMismatchReply.status.detail).toContain(DwnErrorCode.EncryptionControlValidateAudienceTagsMismatch);
697
+ });
698
+ it('should reject audience control records for role paths without key agreement material', async () => {
699
+ const alice = await TestDataGenerator.generateDidKeyPersona();
700
+ const protocolDefinition = {
701
+ protocol: 'http://encryption-control-role-without-keyagreement.xyz',
702
+ published: false,
703
+ types: {
704
+ member: { schema: 'http://member-schema', dataFormats: ['application/json'] },
705
+ },
706
+ structure: {
707
+ member: { $role: true },
708
+ },
709
+ };
710
+ const protocolConfig = await TestDataGenerator.generateProtocolsConfigure({
711
+ author: alice,
712
+ protocolDefinition,
713
+ });
714
+ expect((await dwn.processMessage(alice.did, protocolConfig.message)).status.code).toBe(202);
715
+ const audience = await createAudienceControlWrite({
716
+ author: alice,
717
+ protocol: protocolDefinition.protocol,
718
+ rolePath: 'member',
719
+ roleRuleSet: protocolDefinition.structure.member,
720
+ sealKeyId: await Encryption.getKeyId(alice.encryptionKeyPair.publicJwk),
721
+ });
722
+ const reply = await dwn.processMessage(alice.did, audience.recordsWrite.message, { dataStream: DataStream.fromBytes(audience.dataBytes) });
723
+ expect(reply.status.code).toBe(400);
724
+ expect(reply.status.detail).toContain(DwnErrorCode.EncryptionControlValidateAudienceRolePathInvalid);
725
+ });
726
+ it('should reject oversized inline audience control records', async () => {
727
+ const alice = await TestDataGenerator.generateDidKeyPersona();
728
+ const protocolDefinition = {
729
+ protocol: 'http://encryption-control-oversized-audience.xyz',
730
+ published: false,
731
+ types: {
732
+ member: { schema: 'http://member-schema', dataFormats: ['application/json'] },
733
+ },
734
+ structure: {
735
+ member: { $role: true },
736
+ },
737
+ };
738
+ const encryptedProtocolDefinition = await installEncryptedProtocol(alice, protocolDefinition);
739
+ const roleRuleSet = encryptedProtocolDefinition.structure.member;
740
+ const audience = await createAudienceControlWrite({
741
+ author: alice,
742
+ payloadOverrides: { filler: 'x'.repeat(DwnConstant.maxDataSizeAllowedToBeEncoded) },
743
+ protocol: protocolDefinition.protocol,
744
+ rolePath: 'member',
745
+ roleRuleSet,
746
+ });
747
+ const reply = await dwn.processMessage(alice.did, audience.recordsWrite.message, { dataStream: DataStream.fromBytes(audience.dataBytes) });
748
+ expect(reply.status.code).toBe(400);
749
+ expect(reply.status.detail).toContain(DwnErrorCode.EncryptionControlValidateUnexpectedRecord);
750
+ });
751
+ it('should accept delivery control records for role holders', async () => {
752
+ const alice = await TestDataGenerator.generateDidKeyPersona();
753
+ const bob = await TestDataGenerator.generateDidKeyPersona();
754
+ const protocolDefinition = {
755
+ protocol: 'http://encryption-control-delivery-authorities.xyz',
756
+ published: false,
757
+ types: {
758
+ member: { schema: 'http://member-schema', dataFormats: ['application/json'] },
759
+ },
760
+ structure: {
761
+ member: {
762
+ $role: true,
763
+ $actions: [{ who: 'anyone', can: ['create'] }],
764
+ },
765
+ },
766
+ };
767
+ const encryptedProtocolDefinition = await installEncryptedProtocol(alice, protocolDefinition);
768
+ const memberRuleSet = encryptedProtocolDefinition.structure.member;
769
+ const audience = await createAudienceControlWrite({
770
+ author: alice,
771
+ protocol: protocolDefinition.protocol,
772
+ rolePath: 'member',
773
+ roleRuleSet: memberRuleSet,
774
+ });
775
+ expect((await dwn.processMessage(alice.did, audience.recordsWrite.message, { dataStream: DataStream.fromBytes(audience.dataBytes) })).status.code).toBe(202);
776
+ const memberRole = await TestDataGenerator.generateRecordsWrite({
777
+ author: alice,
778
+ data: Encoder.stringToBytes('bob is a member'),
779
+ dataFormat: 'application/json',
780
+ protocol: protocolDefinition.protocol,
781
+ protocolPath: 'member',
782
+ recipient: bob.did,
783
+ schema: 'http://member-schema',
784
+ });
785
+ expect((await dwn.processMessage(alice.did, memberRole.message, { dataStream: memberRole.dataStream })).status.code).toBe(202);
786
+ const roleHolderDelivery = await createDeliveryControlWrite({
787
+ author: alice,
788
+ keyId: audience.keyId,
789
+ protocol: protocolDefinition.protocol,
790
+ recipient: bob.did,
791
+ recipientAuthority: EncryptionControlDeliveryRecipientAuthority.RoleHolder,
792
+ rolePath: 'member',
793
+ roleRuleSet: memberRuleSet,
794
+ });
795
+ expect((await dwn.processMessage(alice.did, roleHolderDelivery.recordsWrite.message, {
796
+ dataStream: DataStream.fromBytes(roleHolderDelivery.dataBytes),
797
+ })).status.code).toBe(202);
798
+ });
799
+ it('should reject malformed delivery control records with typed validation errors', async () => {
800
+ const alice = await TestDataGenerator.generateDidKeyPersona();
801
+ const bob = await TestDataGenerator.generateDidKeyPersona();
802
+ const protocolDefinition = {
803
+ protocol: 'http://encryption-control-delivery-malformed.xyz',
804
+ published: false,
805
+ types: {
806
+ member: { schema: 'http://member-schema', dataFormats: ['application/json'] },
807
+ },
808
+ structure: {
809
+ member: { $role: true },
810
+ },
811
+ };
812
+ const encryptedProtocolDefinition = await installEncryptedProtocol(alice, protocolDefinition);
813
+ const memberRuleSet = encryptedProtocolDefinition.structure.member;
814
+ const audience = await createAudienceControlWrite({
815
+ author: alice,
816
+ protocol: protocolDefinition.protocol,
817
+ rolePath: 'member',
818
+ roleRuleSet: memberRuleSet,
819
+ });
820
+ expect((await dwn.processMessage(alice.did, audience.recordsWrite.message, { dataStream: DataStream.fromBytes(audience.dataBytes) })).status.code).toBe(202);
821
+ const unencryptedData = Encoder.objectToBytes({ protocol: protocolDefinition.protocol });
822
+ const unencryptedDelivery = await RecordsWrite.create({
823
+ data: unencryptedData,
824
+ dataFormat: 'application/json',
825
+ protocol: protocolDefinition.protocol,
826
+ protocolPath: ENCRYPTION_CONTROL_DELIVERY_PATH,
827
+ recipient: bob.did,
828
+ schema: 'https://identity.foundation/dwn/json-schemas/encryption/delivery.json',
829
+ signer: Jws.createSigner(alice),
830
+ tags: {
831
+ protocol: protocolDefinition.protocol,
832
+ rolePath: 'member',
833
+ contextId: '',
834
+ keyId: audience.keyId,
835
+ recipientAuthority: EncryptionControlDeliveryRecipientAuthority.RoleHolder,
836
+ },
837
+ });
838
+ const unencryptedReply = await dwn.processMessage(alice.did, unencryptedDelivery.message, { dataStream: DataStream.fromBytes(unencryptedData) });
839
+ expect(unencryptedReply.status.code).toBe(400);
840
+ expect(unencryptedReply.status.detail).toContain(DwnErrorCode.EncryptionControlValidateUnexpectedRecord);
841
+ const noRecipientData = Encoder.objectToBytes({ protocol: protocolDefinition.protocol });
842
+ const noRecipientDelivery = await RecordsWrite.create({
843
+ data: noRecipientData,
844
+ dataFormat: 'application/json',
845
+ encryptionInput: {
846
+ initializationVector: TestDataGenerator.randomBytes(16),
847
+ key: TestDataGenerator.randomBytes(32),
848
+ keyEncryptionInputs: [{
849
+ algorithm: KeyAgreementAlgorithm.X25519HkdfSha256A256Kw,
850
+ keyId: await Encryption.getKeyId(memberRuleSet.$keyAgreement.publicKeyJwk),
851
+ publicKey: memberRuleSet.$keyAgreement.publicKeyJwk,
852
+ derivationScheme: KeyDerivationScheme.ProtocolPath,
853
+ }],
854
+ },
855
+ protocol: protocolDefinition.protocol,
856
+ protocolPath: ENCRYPTION_CONTROL_DELIVERY_PATH,
857
+ schema: 'https://identity.foundation/dwn/json-schemas/encryption/delivery.json',
858
+ signer: Jws.createSigner(alice),
859
+ tags: {
860
+ protocol: protocolDefinition.protocol,
861
+ rolePath: 'member',
862
+ contextId: '',
863
+ keyId: audience.keyId,
864
+ recipientAuthority: EncryptionControlDeliveryRecipientAuthority.RoleHolder,
865
+ },
866
+ });
867
+ const noRecipientReply = await dwn.processMessage(alice.did, noRecipientDelivery.message, { dataStream: DataStream.fromBytes(noRecipientData) });
868
+ expect(noRecipientReply.status.code).toBe(400);
869
+ expect(noRecipientReply.status.detail).toContain(DwnErrorCode.EncryptionControlValidateDeliveryRecipientMissing);
870
+ const invalidAuthorityDelivery = await createDeliveryControlWrite({
871
+ author: alice,
872
+ keyId: audience.keyId,
873
+ protocol: protocolDefinition.protocol,
874
+ recipient: bob.did,
875
+ recipientAuthority: 'unknown',
876
+ rolePath: 'member',
877
+ roleRuleSet: memberRuleSet,
878
+ });
879
+ const invalidAuthorityReply = await dwn.processMessage(alice.did, invalidAuthorityDelivery.recordsWrite.message, { dataStream: DataStream.fromBytes(invalidAuthorityDelivery.dataBytes) });
880
+ expect(invalidAuthorityReply.status.code).toBe(400);
881
+ expect(invalidAuthorityReply.status.detail).toContain(DwnErrorCode.EncryptionControlValidateDeliveryRecipientAuthorityInvalid);
882
+ const unauthorizedRoleHolderDelivery = await createDeliveryControlWrite({
883
+ author: alice,
884
+ keyId: audience.keyId,
885
+ protocol: protocolDefinition.protocol,
886
+ recipient: bob.did,
887
+ recipientAuthority: EncryptionControlDeliveryRecipientAuthority.RoleHolder,
888
+ rolePath: 'member',
889
+ roleRuleSet: memberRuleSet,
890
+ });
891
+ const unauthorizedRoleHolderReply = await dwn.processMessage(alice.did, unauthorizedRoleHolderDelivery.recordsWrite.message, { dataStream: DataStream.fromBytes(unauthorizedRoleHolderDelivery.dataBytes) });
892
+ expect(unauthorizedRoleHolderReply.status.code).toBe(400);
893
+ expect(unauthorizedRoleHolderReply.status.detail).toContain(DwnErrorCode.EncryptionControlValidateDeliveryRecipientRoleRecordMissing);
894
+ });
895
+ it('should not treat lookalike encryption control paths as reserved paths', async () => {
896
+ const alice = await TestDataGenerator.generateDidKeyPersona();
897
+ await TestDataGenerator.installDefaultTestProtocol(dwn, alice);
898
+ const { message, dataStream } = await TestDataGenerator.generateRecordsWrite({
899
+ author: alice,
900
+ protocol: defaultTestProtocolDefinition.protocol,
901
+ protocolPath: '$encryptionx/audience',
902
+ });
903
+ const reply = await dwn.processMessage(alice.did, message, { dataStream });
904
+ expect(reply.status.code).toBe(400);
905
+ expect(reply.status.detail).toContain('SchemaValidatorFailure');
906
+ expect(reply.status.detail).toContain('protocolPath: must match pattern');
907
+ });
101
908
  it('should only be able to overwrite existing record if new record has a later `messageTimestamp` value', async () => {
102
909
  // write a message into DB
103
910
  const author = await TestDataGenerator.generateDidKeyPersona();
@@ -2289,6 +3096,7 @@ export function testRecordsWriteHandler() {
2289
3096
  initializationVector: dataEncryptionInitializationVector,
2290
3097
  key: dataEncryptionKey,
2291
3098
  keyEncryptionInputs: [{
3099
+ algorithm: KeyAgreementAlgorithm.X25519HkdfSha256A256Kw,
2292
3100
  keyId: await Encryption.getKeyId(encryptionPublicKey),
2293
3101
  publicKey: encryptionPublicKey,
2294
3102
  derivationScheme: KeyDerivationScheme.ProtocolPath
@@ -2352,6 +3160,7 @@ export function testRecordsWriteHandler() {
2352
3160
  initializationVector: dataEncryptionInitializationVector,
2353
3161
  key: dataEncryptionKey,
2354
3162
  keyEncryptionInputs: [{
3163
+ algorithm: KeyAgreementAlgorithm.X25519HkdfSha256A256Kw,
2355
3164
  keyId: await Encryption.getKeyId(protocolPathPublicKey),
2356
3165
  publicKey: protocolPathPublicKey,
2357
3166
  derivationScheme: KeyDerivationScheme.ProtocolPath,
@@ -2371,10 +3180,10 @@ export function testRecordsWriteHandler() {
2371
3180
  expect(writeReply.status.code).toBe(400);
2372
3181
  expect(writeReply.status.detail).toContain(DwnErrorCode.ProtocolAuthorizationEncryptionRoleAudienceEntryMissing);
2373
3182
  });
2374
- it('should accept an encrypted role-readable RecordsWrite with a matching audienceEpoch', async () => {
3183
+ it('should reject an encrypted role-readable RecordsWrite whose roleAudience entry references a missing audience record', async () => {
2375
3184
  const alice = await TestDataGenerator.generateDidKeyPersona();
2376
3185
  const protocolDefinition = {
2377
- protocol: 'http://role-audience-entry-accepted.xyz',
3186
+ protocol: 'http://role-audience-record-required.xyz',
2378
3187
  published: false,
2379
3188
  types: {
2380
3189
  thread: { schema: 'http://thread-schema', dataFormats: ['application/json'] },
@@ -2407,35 +3216,380 @@ export function testRecordsWriteHandler() {
2407
3216
  data: Encoder.stringToBytes('{"title":"secret"}'),
2408
3217
  });
2409
3218
  expect((await dwn.processMessage(alice.did, thread.message, { dataStream: thread.dataStream })).status.code).toBe(202);
2410
- const role = 'thread/participant';
3219
+ const dataEncryptionKey = TestDataGenerator.randomBytes(32);
3220
+ const dataEncryptionInitializationVector = TestDataGenerator.randomBytes(16);
3221
+ const encryptedData = await Encryption.encrypt(ContentEncryptionAlgorithm.A256CTR, dataEncryptionKey, dataEncryptionInitializationVector, Encoder.stringToBytes('encrypted chat'));
2411
3222
  const threadRuleSet = encryptedProtocolDefinition.structure.thread;
2412
- const participantRuleSet = threadRuleSet.participant;
2413
- const rolePublicKey = participantRuleSet.$keyAgreement.publicKeyJwk;
2414
- const roleKeyId = await Encryption.getKeyId(rolePublicKey);
2415
- const audienceEpochData = Encoder.objectToBytes({
3223
+ const chatRuleSet = threadRuleSet.chat;
3224
+ const protocolPathPublicKey = chatRuleSet.$keyAgreement.publicKeyJwk;
3225
+ const audiencePublicKey = alice.encryptionKeyPair.publicJwk;
3226
+ const encryptionInput = {
3227
+ initializationVector: dataEncryptionInitializationVector,
3228
+ key: dataEncryptionKey,
3229
+ keyEncryptionInputs: [
3230
+ {
3231
+ algorithm: KeyAgreementAlgorithm.X25519HkdfSha256A256Kw,
3232
+ keyId: await Encryption.getKeyId(protocolPathPublicKey),
3233
+ publicKey: protocolPathPublicKey,
3234
+ derivationScheme: KeyDerivationScheme.ProtocolPath,
3235
+ },
3236
+ {
3237
+ algorithm: KeyAgreementAlgorithm.X25519HkdfSha256A256Kw,
3238
+ keyId: await Encryption.getKeyId(audiencePublicKey),
3239
+ publicKey: audiencePublicKey,
3240
+ derivationScheme: ROLE_AUDIENCE_DERIVATION_SCHEME,
3241
+ protocol: protocolDefinition.protocol,
3242
+ rolePath: 'thread/participant',
3243
+ },
3244
+ ],
3245
+ };
3246
+ const recordsWrite = await TestDataGenerator.generateRecordsWrite({
3247
+ author: alice,
2416
3248
  protocol: protocolDefinition.protocol,
3249
+ protocolPath: 'thread/chat',
3250
+ parentContextId: thread.message.contextId,
3251
+ schema: 'http://chat-schema',
3252
+ dataFormat: 'text/plain',
3253
+ data: encryptedData,
3254
+ encryptionInput,
3255
+ });
3256
+ const writeReply = await dwn.processMessage(alice.did, recordsWrite.message, { dataStream: recordsWrite.dataStream });
3257
+ expect(writeReply.status.code).toBe(400);
3258
+ expect(writeReply.status.detail).toContain(DwnErrorCode.ProtocolAuthorizationEncryptionRoleAudienceMissing);
3259
+ });
3260
+ it('should accept an encrypted role-readable RecordsWrite when any source roleAudience entry matches an accepted audience record', async () => {
3261
+ const alice = await TestDataGenerator.generateDidKeyPersona();
3262
+ const bob = await TestDataGenerator.generateDidKeyPersona();
3263
+ const protocolDefinition = {
3264
+ protocol: 'http://role-audience-second-entry-accepted.xyz',
3265
+ published: false,
3266
+ types: {
3267
+ thread: { schema: 'http://thread-schema', dataFormats: ['application/json'] },
3268
+ participant: { schema: 'http://participant-schema', dataFormats: ['application/json'] },
3269
+ chat: { schema: 'http://chat-schema', dataFormats: ['text/plain'], encryptionRequired: true },
3270
+ },
3271
+ structure: {
3272
+ thread: {
3273
+ participant: { $role: true },
3274
+ chat: {
3275
+ $actions: [
3276
+ { role: 'thread/participant', can: ['read'] },
3277
+ ],
3278
+ },
3279
+ },
3280
+ },
3281
+ };
3282
+ const encryptedProtocolDefinition = await Protocols.deriveAndInjectPublicEncryptionKeys(protocolDefinition, alice.keyId, alice.encryptionKeyPair.privateJwk);
3283
+ const protocolConfig = await TestDataGenerator.generateProtocolsConfigure({
3284
+ author: alice,
3285
+ protocolDefinition: encryptedProtocolDefinition,
3286
+ });
3287
+ expect((await dwn.processMessage(alice.did, protocolConfig.message)).status.code).toBe(202);
3288
+ const thread = await TestDataGenerator.generateRecordsWrite({
3289
+ author: alice,
3290
+ protocol: protocolDefinition.protocol,
3291
+ protocolPath: 'thread',
3292
+ schema: 'http://thread-schema',
3293
+ dataFormat: 'application/json',
3294
+ data: Encoder.stringToBytes('{"title":"secret"}'),
3295
+ });
3296
+ expect((await dwn.processMessage(alice.did, thread.message, { dataStream: thread.dataStream })).status.code).toBe(202);
3297
+ const rolePath = 'thread/participant';
3298
+ const threadRuleSet = encryptedProtocolDefinition.structure.thread;
3299
+ const participantRuleSet = threadRuleSet.participant;
3300
+ const audience = await createAudienceControlWrite({
3301
+ author: alice,
2417
3302
  contextId: thread.message.contextId,
2418
- role,
2419
- epoch: 1,
2420
- keyId: roleKeyId,
2421
- publicKeyJwk: rolePublicKey,
3303
+ protocol: protocolDefinition.protocol,
3304
+ rolePath,
3305
+ roleRuleSet: participantRuleSet,
2422
3306
  });
2423
- const audienceEpoch = await RecordsWrite.create({
2424
- data: audienceEpochData,
3307
+ expect((await dwn.processMessage(alice.did, audience.recordsWrite.message, { dataStream: DataStream.fromBytes(audience.dataBytes) })).status.code).toBe(202);
3308
+ const dataEncryptionKey = TestDataGenerator.randomBytes(32);
3309
+ const dataEncryptionInitializationVector = TestDataGenerator.randomBytes(16);
3310
+ const encryptedData = await Encryption.encrypt(ContentEncryptionAlgorithm.A256CTR, dataEncryptionKey, dataEncryptionInitializationVector, Encoder.stringToBytes('encrypted chat'));
3311
+ const chatRuleSet = threadRuleSet.chat;
3312
+ const protocolPathPublicKey = chatRuleSet.$keyAgreement.publicKeyJwk;
3313
+ const encryptionInput = {
3314
+ initializationVector: dataEncryptionInitializationVector,
3315
+ key: dataEncryptionKey,
3316
+ keyEncryptionInputs: [
3317
+ {
3318
+ algorithm: KeyAgreementAlgorithm.X25519HkdfSha256A256Kw,
3319
+ keyId: await Encryption.getKeyId(protocolPathPublicKey),
3320
+ publicKey: protocolPathPublicKey,
3321
+ derivationScheme: KeyDerivationScheme.ProtocolPath,
3322
+ },
3323
+ {
3324
+ algorithm: KeyAgreementAlgorithm.X25519HkdfSha256A256Kw,
3325
+ keyId: await Encryption.getKeyId(bob.encryptionKeyPair.publicJwk),
3326
+ publicKey: bob.encryptionKeyPair.publicJwk,
3327
+ derivationScheme: ROLE_AUDIENCE_DERIVATION_SCHEME,
3328
+ protocol: protocolDefinition.protocol,
3329
+ rolePath,
3330
+ },
3331
+ {
3332
+ algorithm: KeyAgreementAlgorithm.X25519HkdfSha256A256Kw,
3333
+ keyId: audience.keyId,
3334
+ publicKey: alice.encryptionKeyPair.publicJwk,
3335
+ derivationScheme: ROLE_AUDIENCE_DERIVATION_SCHEME,
3336
+ protocol: protocolDefinition.protocol,
3337
+ rolePath,
3338
+ },
3339
+ ],
3340
+ };
3341
+ const recordsWrite = await TestDataGenerator.generateRecordsWrite({
3342
+ author: alice,
3343
+ protocol: protocolDefinition.protocol,
3344
+ protocolPath: 'thread/chat',
3345
+ parentContextId: thread.message.contextId,
3346
+ schema: 'http://chat-schema',
3347
+ dataFormat: 'text/plain',
3348
+ data: encryptedData,
3349
+ encryptionInput,
3350
+ });
3351
+ const writeReply = await dwn.processMessage(alice.did, recordsWrite.message, { dataStream: recordsWrite.dataStream });
3352
+ expect(writeReply.status.code).toBe(202);
3353
+ });
3354
+ it('should admit role-audience records and deliveries that reference a stored non-current audience key', async () => {
3355
+ const alice = await TestDataGenerator.generateDidKeyPersona();
3356
+ const bob = await TestDataGenerator.generateDidKeyPersona();
3357
+ const protocolDefinition = {
3358
+ protocol: 'http://role-audience-loser-key-admission.xyz',
3359
+ published: false,
3360
+ types: {
3361
+ member: { schema: 'http://member-schema', dataFormats: ['application/json'] },
3362
+ message: { schema: 'http://message-schema', dataFormats: ['text/plain'], encryptionRequired: true },
3363
+ },
3364
+ structure: {
3365
+ member: {
3366
+ $role: true,
3367
+ $actions: [{ who: 'anyone', can: ['create'] }],
3368
+ },
3369
+ message: {
3370
+ $actions: [
3371
+ { role: 'member', can: ['read'] },
3372
+ ],
3373
+ },
3374
+ },
3375
+ };
3376
+ const encryptedProtocolDefinition = await installEncryptedProtocol(alice, protocolDefinition);
3377
+ const memberRuleSet = encryptedProtocolDefinition.structure.member;
3378
+ const messageRuleSet = encryptedProtocolDefinition.structure.message;
3379
+ const rolePath = 'member';
3380
+ const loserAudience = await createAudienceControlWrite({
3381
+ author: bob,
3382
+ dateCreated: '2025-01-01T00:00:00.000000Z',
3383
+ protocol: protocolDefinition.protocol,
3384
+ rolePath,
3385
+ roleRuleSet: memberRuleSet,
3386
+ });
3387
+ expect((await dwn.processMessage(alice.did, loserAudience.recordsWrite.message, { dataStream: DataStream.fromBytes(loserAudience.dataBytes) })).status.code).toBe(202);
3388
+ const currentAudience = await createAudienceControlWrite({
3389
+ author: alice,
3390
+ dateCreated: '2025-01-02T00:00:00.000000Z',
3391
+ protocol: protocolDefinition.protocol,
3392
+ rolePath,
3393
+ roleRuleSet: memberRuleSet,
3394
+ });
3395
+ expect((await dwn.processMessage(alice.did, currentAudience.recordsWrite.message, { dataStream: DataStream.fromBytes(currentAudience.dataBytes) })).status.code).toBe(202);
3396
+ const bobMemberRole = await TestDataGenerator.generateRecordsWrite({
3397
+ author: bob,
3398
+ data: Encoder.stringToBytes('bob is a member'),
2425
3399
  dataFormat: 'application/json',
2426
- protocol: EncryptionProtocol.uri,
2427
- protocolPath: EncryptionProtocol.audienceEpochPath,
2428
- schema: EncryptionProtocol.definition.types.audienceEpoch.schema,
2429
- signer: Jws.createSigner(alice),
2430
- tags: {
3400
+ protocol: protocolDefinition.protocol,
3401
+ protocolPath: rolePath,
3402
+ recipient: bob.did,
3403
+ schema: 'http://member-schema',
3404
+ });
3405
+ expect((await dwn.processMessage(alice.did, bobMemberRole.message, { dataStream: bobMemberRole.dataStream })).status.code).toBe(202);
3406
+ const buildEncryptedMessage = async (plaintext) => {
3407
+ const dataEncryptionKey = TestDataGenerator.randomBytes(32);
3408
+ const dataEncryptionInitializationVector = TestDataGenerator.randomBytes(16);
3409
+ const encryptedData = await Encryption.encrypt(ContentEncryptionAlgorithm.A256CTR, dataEncryptionKey, dataEncryptionInitializationVector, Encoder.stringToBytes(plaintext));
3410
+ const protocolPathPublicKey = messageRuleSet.$keyAgreement.publicKeyJwk;
3411
+ const encryptionInput = {
3412
+ initializationVector: dataEncryptionInitializationVector,
3413
+ key: dataEncryptionKey,
3414
+ keyEncryptionInputs: [
3415
+ {
3416
+ algorithm: KeyAgreementAlgorithm.X25519HkdfSha256A256Kw,
3417
+ keyId: await Encryption.getKeyId(protocolPathPublicKey),
3418
+ publicKey: protocolPathPublicKey,
3419
+ derivationScheme: KeyDerivationScheme.ProtocolPath,
3420
+ },
3421
+ {
3422
+ algorithm: KeyAgreementAlgorithm.X25519HkdfSha256A256Kw,
3423
+ keyId: loserAudience.keyId,
3424
+ publicKey: bob.encryptionKeyPair.publicJwk,
3425
+ derivationScheme: ROLE_AUDIENCE_DERIVATION_SCHEME,
3426
+ protocol: protocolDefinition.protocol,
3427
+ rolePath,
3428
+ },
3429
+ ],
3430
+ };
3431
+ return TestDataGenerator.generateRecordsWrite({
3432
+ author: alice,
2431
3433
  protocol: protocolDefinition.protocol,
2432
- contextId: thread.message.contextId,
2433
- role,
2434
- epoch: 1,
2435
- keyId: roleKeyId,
3434
+ protocolPath: 'message',
3435
+ schema: 'http://message-schema',
3436
+ dataFormat: 'text/plain',
3437
+ data: encryptedData,
3438
+ encryptionInput,
3439
+ });
3440
+ };
3441
+ const directWrite = await buildEncryptedMessage('direct write to loser key');
3442
+ const directReply = await dwn.processMessage(alice.did, directWrite.message, { dataStream: directWrite.dataStream });
3443
+ expect(directReply.status.code).toBe(202);
3444
+ const replicatedWrite = await buildEncryptedMessage('replicated write to loser key');
3445
+ const replicatedResult = await dwn.applyReplicatedMessage(alice.did, replicatedWrite.message, { dataStream: replicatedWrite.dataStream });
3446
+ expect(replicatedResult).toEqual(expect.objectContaining({ kind: 'Applied' }));
3447
+ const delivery = await createDeliveryControlWrite({
3448
+ author: alice,
3449
+ keyId: loserAudience.keyId,
3450
+ protocol: protocolDefinition.protocol,
3451
+ recipient: bob.did,
3452
+ recipientAuthority: EncryptionControlDeliveryRecipientAuthority.RoleHolder,
3453
+ rolePath,
3454
+ roleRuleSet: memberRuleSet,
3455
+ });
3456
+ const deliveryReply = await dwn.processMessage(alice.did, delivery.recordsWrite.message, { dataStream: DataStream.fromBytes(delivery.dataBytes) });
3457
+ expect(deliveryReply.status.code).toBe(202);
3458
+ });
3459
+ it('should reject an encrypted role-readable RecordsWrite when the roleAudience keyId does not match the audience record', async () => {
3460
+ const alice = await TestDataGenerator.generateDidKeyPersona();
3461
+ const bob = await TestDataGenerator.generateDidKeyPersona();
3462
+ const protocolDefinition = {
3463
+ protocol: 'http://role-audience-key-id-bound.xyz',
3464
+ published: false,
3465
+ types: {
3466
+ thread: { schema: 'http://thread-schema', dataFormats: ['application/json'] },
3467
+ participant: { schema: 'http://participant-schema', dataFormats: ['application/json'] },
3468
+ chat: { schema: 'http://chat-schema', dataFormats: ['text/plain'], encryptionRequired: true },
3469
+ },
3470
+ structure: {
3471
+ thread: {
3472
+ participant: { $role: true },
3473
+ chat: {
3474
+ $actions: [
3475
+ { role: 'thread/participant', can: ['read'] },
3476
+ ],
3477
+ },
3478
+ },
2436
3479
  },
3480
+ };
3481
+ const encryptedProtocolDefinition = await Protocols.deriveAndInjectPublicEncryptionKeys(protocolDefinition, alice.keyId, alice.encryptionKeyPair.privateJwk);
3482
+ const protocolConfig = await TestDataGenerator.generateProtocolsConfigure({
3483
+ author: alice,
3484
+ protocolDefinition: encryptedProtocolDefinition,
3485
+ });
3486
+ expect((await dwn.processMessage(alice.did, protocolConfig.message)).status.code).toBe(202);
3487
+ const thread = await TestDataGenerator.generateRecordsWrite({
3488
+ author: alice,
3489
+ protocol: protocolDefinition.protocol,
3490
+ protocolPath: 'thread',
3491
+ schema: 'http://thread-schema',
3492
+ dataFormat: 'application/json',
3493
+ data: Encoder.stringToBytes('{"title":"secret"}'),
2437
3494
  });
2438
- expect((await dwn.processMessage(alice.did, audienceEpoch.message, { dataStream: DataStream.fromBytes(audienceEpochData) })).status.code).toBe(202);
3495
+ expect((await dwn.processMessage(alice.did, thread.message, { dataStream: thread.dataStream })).status.code).toBe(202);
3496
+ const rolePath = 'thread/participant';
3497
+ const threadRuleSet = encryptedProtocolDefinition.structure.thread;
3498
+ const participantRuleSet = threadRuleSet.participant;
3499
+ const audience = await createAudienceControlWrite({
3500
+ author: alice,
3501
+ contextId: thread.message.contextId,
3502
+ protocol: protocolDefinition.protocol,
3503
+ rolePath,
3504
+ roleRuleSet: participantRuleSet,
3505
+ });
3506
+ expect((await dwn.processMessage(alice.did, audience.recordsWrite.message, { dataStream: DataStream.fromBytes(audience.dataBytes) })).status.code).toBe(202);
3507
+ const dataEncryptionKey = TestDataGenerator.randomBytes(32);
3508
+ const dataEncryptionInitializationVector = TestDataGenerator.randomBytes(16);
3509
+ const encryptedData = await Encryption.encrypt(ContentEncryptionAlgorithm.A256CTR, dataEncryptionKey, dataEncryptionInitializationVector, Encoder.stringToBytes('encrypted chat'));
3510
+ const chatRuleSet = threadRuleSet.chat;
3511
+ const protocolPathPublicKey = chatRuleSet.$keyAgreement.publicKeyJwk;
3512
+ const encryptionInput = {
3513
+ initializationVector: dataEncryptionInitializationVector,
3514
+ key: dataEncryptionKey,
3515
+ keyEncryptionInputs: [
3516
+ {
3517
+ algorithm: KeyAgreementAlgorithm.X25519HkdfSha256A256Kw,
3518
+ keyId: await Encryption.getKeyId(protocolPathPublicKey),
3519
+ publicKey: protocolPathPublicKey,
3520
+ derivationScheme: KeyDerivationScheme.ProtocolPath,
3521
+ },
3522
+ {
3523
+ algorithm: KeyAgreementAlgorithm.X25519HkdfSha256A256Kw,
3524
+ keyId: await Encryption.getKeyId(bob.encryptionKeyPair.publicJwk),
3525
+ publicKey: bob.encryptionKeyPair.publicJwk,
3526
+ derivationScheme: ROLE_AUDIENCE_DERIVATION_SCHEME,
3527
+ protocol: protocolDefinition.protocol,
3528
+ rolePath,
3529
+ },
3530
+ ],
3531
+ };
3532
+ const recordsWrite = await TestDataGenerator.generateRecordsWrite({
3533
+ author: alice,
3534
+ protocol: protocolDefinition.protocol,
3535
+ protocolPath: 'thread/chat',
3536
+ parentContextId: thread.message.contextId,
3537
+ schema: 'http://chat-schema',
3538
+ dataFormat: 'text/plain',
3539
+ data: encryptedData,
3540
+ encryptionInput,
3541
+ });
3542
+ const writeReply = await dwn.processMessage(alice.did, recordsWrite.message, { dataStream: recordsWrite.dataStream });
3543
+ expect(writeReply.status.code).toBe(400);
3544
+ expect(writeReply.status.detail).toContain(DwnErrorCode.ProtocolAuthorizationEncryptionRoleAudienceMissing);
3545
+ });
3546
+ it('should accept an encrypted role-readable RecordsWrite with a matching audience record', async () => {
3547
+ const alice = await TestDataGenerator.generateDidKeyPersona();
3548
+ const protocolDefinition = {
3549
+ protocol: 'http://role-audience-entry-accepted.xyz',
3550
+ published: false,
3551
+ types: {
3552
+ thread: { schema: 'http://thread-schema', dataFormats: ['application/json'] },
3553
+ participant: { schema: 'http://participant-schema', dataFormats: ['application/json'] },
3554
+ chat: { schema: 'http://chat-schema', dataFormats: ['text/plain'], encryptionRequired: true },
3555
+ },
3556
+ structure: {
3557
+ thread: {
3558
+ participant: { $role: true },
3559
+ chat: {
3560
+ $actions: [
3561
+ { role: 'thread/participant', can: ['read'] },
3562
+ ],
3563
+ },
3564
+ },
3565
+ },
3566
+ };
3567
+ const encryptedProtocolDefinition = await Protocols.deriveAndInjectPublicEncryptionKeys(protocolDefinition, alice.keyId, alice.encryptionKeyPair.privateJwk);
3568
+ const protocolConfig = await TestDataGenerator.generateProtocolsConfigure({
3569
+ author: alice,
3570
+ protocolDefinition: encryptedProtocolDefinition,
3571
+ });
3572
+ expect((await dwn.processMessage(alice.did, protocolConfig.message)).status.code).toBe(202);
3573
+ const thread = await TestDataGenerator.generateRecordsWrite({
3574
+ author: alice,
3575
+ protocol: protocolDefinition.protocol,
3576
+ protocolPath: 'thread',
3577
+ schema: 'http://thread-schema',
3578
+ dataFormat: 'application/json',
3579
+ data: Encoder.stringToBytes('{"title":"secret"}'),
3580
+ });
3581
+ expect((await dwn.processMessage(alice.did, thread.message, { dataStream: thread.dataStream })).status.code).toBe(202);
3582
+ const rolePath = 'thread/participant';
3583
+ const threadRuleSet = encryptedProtocolDefinition.structure.thread;
3584
+ const participantRuleSet = threadRuleSet.participant;
3585
+ const audience = await createAudienceControlWrite({
3586
+ author: alice,
3587
+ contextId: thread.message.contextId,
3588
+ protocol: protocolDefinition.protocol,
3589
+ rolePath,
3590
+ roleRuleSet: participantRuleSet,
3591
+ });
3592
+ expect((await dwn.processMessage(alice.did, audience.recordsWrite.message, { dataStream: DataStream.fromBytes(audience.dataBytes) })).status.code).toBe(202);
2439
3593
  const data = Encoder.stringToBytes('encrypted chat');
2440
3594
  const dataEncryptionKey = TestDataGenerator.randomBytes(32);
2441
3595
  const dataEncryptionInitializationVector = TestDataGenerator.randomBytes(16);
@@ -2447,17 +3601,18 @@ export function testRecordsWriteHandler() {
2447
3601
  key: dataEncryptionKey,
2448
3602
  keyEncryptionInputs: [
2449
3603
  {
3604
+ algorithm: KeyAgreementAlgorithm.X25519HkdfSha256A256Kw,
2450
3605
  keyId: await Encryption.getKeyId(protocolPathPublicKey),
2451
3606
  publicKey: protocolPathPublicKey,
2452
3607
  derivationScheme: KeyDerivationScheme.ProtocolPath,
2453
3608
  },
2454
3609
  {
2455
- keyId: roleKeyId,
2456
- publicKey: rolePublicKey,
3610
+ algorithm: KeyAgreementAlgorithm.X25519HkdfSha256A256Kw,
3611
+ keyId: audience.keyId,
3612
+ publicKey: alice.encryptionKeyPair.publicJwk,
2457
3613
  derivationScheme: ROLE_AUDIENCE_DERIVATION_SCHEME,
2458
3614
  protocol: protocolDefinition.protocol,
2459
- role,
2460
- epoch: 1,
3615
+ rolePath,
2461
3616
  },
2462
3617
  ],
2463
3618
  };
@@ -2746,6 +3901,7 @@ export function testRecordsWriteHandler() {
2746
3901
  initializationVector: dataEncryptionInitializationVector,
2747
3902
  key: dataEncryptionKey,
2748
3903
  keyEncryptionInputs: [{
3904
+ algorithm: KeyAgreementAlgorithm.X25519HkdfSha256A256Kw,
2749
3905
  keyId: await Encryption.getKeyId(alice.encryptionKeyPair.publicJwk),
2750
3906
  publicKey: alice.encryptionKeyPair.publicJwk,
2751
3907
  derivationScheme: KeyDerivationScheme.ProtocolPath
@@ -2788,6 +3944,7 @@ export function testRecordsWriteHandler() {
2788
3944
  initializationVector: dataEncryptionInitializationVector,
2789
3945
  key: dataEncryptionKey,
2790
3946
  keyEncryptionInputs: [{
3947
+ algorithm: KeyAgreementAlgorithm.X25519HkdfSha256A256Kw,
2791
3948
  derivationScheme: KeyDerivationScheme.ProtocolPath,
2792
3949
  keyId: await Encryption.getKeyId(alice.encryptionKeyPair.publicJwk),
2793
3950
  publicKey: alice.encryptionKeyPair.publicJwk,
@@ -2835,6 +3992,7 @@ export function testRecordsWriteHandler() {
2835
3992
  initializationVector: dataEncryptionInitializationVector,
2836
3993
  key: dataEncryptionKey,
2837
3994
  keyEncryptionInputs: [{
3995
+ algorithm: KeyAgreementAlgorithm.X25519HkdfSha256A256Kw,
2838
3996
  derivationScheme: KeyDerivationScheme.ProtocolPath,
2839
3997
  keyId: await Encryption.getKeyId(alice.encryptionKeyPair.publicJwk),
2840
3998
  publicKey: alice.encryptionKeyPair.publicJwk,