@enbox/dwn-sdk-js 0.4.3 → 0.4.5

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 (204) hide show
  1. package/dist/browser.mjs +15 -15
  2. package/dist/browser.mjs.map +4 -4
  3. package/dist/esm/generated/precompiled-validators.js +5406 -2200
  4. package/dist/esm/generated/precompiled-validators.js.map +1 -1
  5. package/dist/esm/json-schemas/definitions.json +18 -0
  6. package/dist/esm/src/core/constants.js +3 -3
  7. package/dist/esm/src/core/constants.js.map +1 -1
  8. package/dist/esm/src/core/dwn-error.js +29 -1
  9. package/dist/esm/src/core/dwn-error.js.map +1 -1
  10. package/dist/esm/src/core/messages-grant-authorization.js +20 -0
  11. package/dist/esm/src/core/messages-grant-authorization.js.map +1 -1
  12. package/dist/esm/src/core/protocol-authorization-validation.js +80 -3
  13. package/dist/esm/src/core/protocol-authorization-validation.js.map +1 -1
  14. package/dist/esm/src/core/recording-validation-state-reader.js +5 -0
  15. package/dist/esm/src/core/recording-validation-state-reader.js.map +1 -1
  16. package/dist/esm/src/core/replication-apply.js +163 -7
  17. package/dist/esm/src/core/replication-apply.js.map +1 -1
  18. package/dist/esm/src/core/validation-state-reader.js +20 -0
  19. package/dist/esm/src/core/validation-state-reader.js.map +1 -1
  20. package/dist/esm/src/dwn.js +19 -2
  21. package/dist/esm/src/dwn.js.map +1 -1
  22. package/dist/esm/src/handlers/messages-query.js +3 -5
  23. package/dist/esm/src/handlers/messages-query.js.map +1 -1
  24. package/dist/esm/src/handlers/protocols-configure.js +10 -4
  25. package/dist/esm/src/handlers/protocols-configure.js.map +1 -1
  26. package/dist/esm/src/handlers/records-write.js +1 -1
  27. package/dist/esm/src/handlers/records-write.js.map +1 -1
  28. package/dist/esm/src/index.js +2 -1
  29. package/dist/esm/src/index.js.map +1 -1
  30. package/dist/esm/src/interfaces/protocols-configure.js +23 -10
  31. package/dist/esm/src/interfaces/protocols-configure.js.map +1 -1
  32. package/dist/esm/src/interfaces/records-write-signing.js +2 -8
  33. package/dist/esm/src/interfaces/records-write-signing.js.map +1 -1
  34. package/dist/esm/src/interfaces/records-write.js +43 -65
  35. package/dist/esm/src/interfaces/records-write.js.map +1 -1
  36. package/dist/esm/src/protocols/encryption.js +411 -0
  37. package/dist/esm/src/protocols/encryption.js.map +1 -0
  38. package/dist/esm/src/protocols/permission-grant.js +3 -3
  39. package/dist/esm/src/protocols/permission-grant.js.map +1 -1
  40. package/dist/esm/src/protocols/permissions.js +1 -0
  41. package/dist/esm/src/protocols/permissions.js.map +1 -1
  42. package/dist/esm/src/types/message-types.js.map +1 -1
  43. package/dist/esm/src/types/permission-types.js.map +1 -1
  44. package/dist/esm/src/types/protocols-types.js.map +1 -1
  45. package/dist/esm/src/utils/encryption.js +135 -187
  46. package/dist/esm/src/utils/encryption.js.map +1 -1
  47. package/dist/esm/src/utils/hd-key.js +0 -1
  48. package/dist/esm/src/utils/hd-key.js.map +1 -1
  49. package/dist/esm/src/utils/permission-scope.js +9 -6
  50. package/dist/esm/src/utils/permission-scope.js.map +1 -1
  51. package/dist/esm/src/utils/protocols.js +12 -9
  52. package/dist/esm/src/utils/protocols.js.map +1 -1
  53. package/dist/esm/src/utils/records.js +54 -56
  54. package/dist/esm/src/utils/records.js.map +1 -1
  55. package/dist/esm/src/utils/replication.js +27 -4
  56. package/dist/esm/src/utils/replication.js.map +1 -1
  57. package/dist/esm/tests/core/replication-apply.spec.js +141 -1
  58. package/dist/esm/tests/core/replication-apply.spec.js.map +1 -1
  59. package/dist/esm/tests/core/replication-replay-property.spec.js +1 -0
  60. package/dist/esm/tests/core/replication-replay-property.spec.js.map +1 -1
  61. package/dist/esm/tests/core/validation-read-closure.spec.js +111 -0
  62. package/dist/esm/tests/core/validation-read-closure.spec.js.map +1 -1
  63. package/dist/esm/tests/core/validation-state-reader.spec.js +89 -0
  64. package/dist/esm/tests/core/validation-state-reader.spec.js.map +1 -1
  65. package/dist/esm/tests/features/protocol-composition.spec.js +30 -31
  66. package/dist/esm/tests/features/protocol-composition.spec.js.map +1 -1
  67. package/dist/esm/tests/fuzz/encryption.fuzz.spec.js +29 -121
  68. package/dist/esm/tests/fuzz/encryption.fuzz.spec.js.map +1 -1
  69. package/dist/esm/tests/handlers/messages-query.spec.js +71 -3
  70. package/dist/esm/tests/handlers/messages-query.spec.js.map +1 -1
  71. package/dist/esm/tests/handlers/messages-read.spec.js +2 -2
  72. package/dist/esm/tests/handlers/messages-read.spec.js.map +1 -1
  73. package/dist/esm/tests/handlers/records-read.spec.js +9 -135
  74. package/dist/esm/tests/handlers/records-read.spec.js.map +1 -1
  75. package/dist/esm/tests/handlers/records-write.spec.js +279 -19
  76. package/dist/esm/tests/handlers/records-write.spec.js.map +1 -1
  77. package/dist/esm/tests/interfaces/protocols-configure.spec.js +17 -22
  78. package/dist/esm/tests/interfaces/protocols-configure.spec.js.map +1 -1
  79. package/dist/esm/tests/interfaces/records-write.spec.js +170 -150
  80. package/dist/esm/tests/interfaces/records-write.spec.js.map +1 -1
  81. package/dist/esm/tests/protocols/encryption.spec.js +914 -0
  82. package/dist/esm/tests/protocols/encryption.spec.js.map +1 -0
  83. package/dist/esm/tests/protocols/permission-grant.spec.js +55 -0
  84. package/dist/esm/tests/protocols/permission-grant.spec.js.map +1 -1
  85. package/dist/esm/tests/scenarios/end-to-end-tests.spec.js +100 -100
  86. package/dist/esm/tests/scenarios/end-to-end-tests.spec.js.map +1 -1
  87. package/dist/esm/tests/store/message-store-level.spec.js +20 -2
  88. package/dist/esm/tests/store/message-store-level.spec.js.map +1 -1
  89. package/dist/esm/tests/utils/encryption-callbacks.spec.js +88 -125
  90. package/dist/esm/tests/utils/encryption-callbacks.spec.js.map +1 -1
  91. package/dist/esm/tests/utils/encryption.spec.js +125 -255
  92. package/dist/esm/tests/utils/encryption.spec.js.map +1 -1
  93. package/dist/esm/tests/utils/permission-scope.spec.js +5 -1
  94. package/dist/esm/tests/utils/permission-scope.spec.js.map +1 -1
  95. package/dist/esm/tests/utils/records.spec.js +15 -5
  96. package/dist/esm/tests/utils/records.spec.js.map +1 -1
  97. package/dist/esm/tests/utils/test-data-generator.js +7 -44
  98. package/dist/esm/tests/utils/test-data-generator.js.map +1 -1
  99. package/dist/esm/tests/validation/json-schemas/encryption/grant-key.spec.js +63 -0
  100. package/dist/esm/tests/validation/json-schemas/encryption/grant-key.spec.js.map +1 -0
  101. package/dist/types/generated/precompiled-validators.d.ts +140 -92
  102. package/dist/types/generated/precompiled-validators.d.ts.map +1 -1
  103. package/dist/types/src/core/constants.d.ts +2 -2
  104. package/dist/types/src/core/constants.d.ts.map +1 -1
  105. package/dist/types/src/core/core-protocol.d.ts +1 -1
  106. package/dist/types/src/core/core-protocol.d.ts.map +1 -1
  107. package/dist/types/src/core/dwn-error.d.ts +29 -1
  108. package/dist/types/src/core/dwn-error.d.ts.map +1 -1
  109. package/dist/types/src/core/messages-grant-authorization.d.ts +2 -0
  110. package/dist/types/src/core/messages-grant-authorization.d.ts.map +1 -1
  111. package/dist/types/src/core/protocol-authorization-validation.d.ts +2 -2
  112. package/dist/types/src/core/protocol-authorization-validation.d.ts.map +1 -1
  113. package/dist/types/src/core/recording-validation-state-reader.d.ts +9 -0
  114. package/dist/types/src/core/recording-validation-state-reader.d.ts.map +1 -1
  115. package/dist/types/src/core/replication-apply.d.ts +4 -3
  116. package/dist/types/src/core/replication-apply.d.ts.map +1 -1
  117. package/dist/types/src/core/validation-state-reader.d.ts +9 -0
  118. package/dist/types/src/core/validation-state-reader.d.ts.map +1 -1
  119. package/dist/types/src/dwn.d.ts +1 -0
  120. package/dist/types/src/dwn.d.ts.map +1 -1
  121. package/dist/types/src/handlers/messages-query.d.ts.map +1 -1
  122. package/dist/types/src/handlers/protocols-configure.d.ts.map +1 -1
  123. package/dist/types/src/index.d.ts +6 -5
  124. package/dist/types/src/index.d.ts.map +1 -1
  125. package/dist/types/src/interfaces/protocols-configure.d.ts +1 -1
  126. package/dist/types/src/interfaces/protocols-configure.d.ts.map +1 -1
  127. package/dist/types/src/interfaces/records-write-signing.d.ts +4 -8
  128. package/dist/types/src/interfaces/records-write-signing.d.ts.map +1 -1
  129. package/dist/types/src/interfaces/records-write.d.ts +10 -26
  130. package/dist/types/src/interfaces/records-write.d.ts.map +1 -1
  131. package/dist/types/src/protocols/encryption.d.ts +43 -0
  132. package/dist/types/src/protocols/encryption.d.ts.map +1 -0
  133. package/dist/types/src/protocols/permission-grant.d.ts +3 -6
  134. package/dist/types/src/protocols/permission-grant.d.ts.map +1 -1
  135. package/dist/types/src/protocols/permissions.d.ts +2 -1
  136. package/dist/types/src/protocols/permissions.d.ts.map +1 -1
  137. package/dist/types/src/types/encryption-types.d.ts +5 -38
  138. package/dist/types/src/types/encryption-types.d.ts.map +1 -1
  139. package/dist/types/src/types/message-types.d.ts +0 -13
  140. package/dist/types/src/types/message-types.d.ts.map +1 -1
  141. package/dist/types/src/types/permission-types.d.ts +44 -9
  142. package/dist/types/src/types/permission-types.d.ts.map +1 -1
  143. package/dist/types/src/types/protocols-types.d.ts +9 -14
  144. package/dist/types/src/types/protocols-types.d.ts.map +1 -1
  145. package/dist/types/src/types/records-types.d.ts +3 -3
  146. package/dist/types/src/types/records-types.d.ts.map +1 -1
  147. package/dist/types/src/types/validation-state-reader.d.ts +12 -0
  148. package/dist/types/src/types/validation-state-reader.d.ts.map +1 -1
  149. package/dist/types/src/utils/encryption.d.ts +54 -145
  150. package/dist/types/src/utils/encryption.d.ts.map +1 -1
  151. package/dist/types/src/utils/hd-key.d.ts +1 -1
  152. package/dist/types/src/utils/hd-key.d.ts.map +1 -1
  153. package/dist/types/src/utils/permission-scope.d.ts +4 -3
  154. package/dist/types/src/utils/permission-scope.d.ts.map +1 -1
  155. package/dist/types/src/utils/protocols.d.ts +3 -5
  156. package/dist/types/src/utils/protocols.d.ts.map +1 -1
  157. package/dist/types/src/utils/records.d.ts +5 -13
  158. package/dist/types/src/utils/records.d.ts.map +1 -1
  159. package/dist/types/src/utils/replication.d.ts +3 -0
  160. package/dist/types/src/utils/replication.d.ts.map +1 -1
  161. package/dist/types/tests/features/protocol-composition.spec.d.ts.map +1 -1
  162. package/dist/types/tests/handlers/messages-query.spec.d.ts.map +1 -1
  163. package/dist/types/tests/handlers/records-read.spec.d.ts.map +1 -1
  164. package/dist/types/tests/handlers/records-write.spec.d.ts.map +1 -1
  165. package/dist/types/tests/protocols/encryption.spec.d.ts +2 -0
  166. package/dist/types/tests/protocols/encryption.spec.d.ts.map +1 -0
  167. package/dist/types/tests/scenarios/end-to-end-tests.spec.d.ts.map +1 -1
  168. package/dist/types/tests/utils/test-data-generator.d.ts +0 -5
  169. package/dist/types/tests/utils/test-data-generator.d.ts.map +1 -1
  170. package/dist/types/tests/validation/json-schemas/encryption/grant-key.spec.d.ts +2 -0
  171. package/dist/types/tests/validation/json-schemas/encryption/grant-key.spec.d.ts.map +1 -0
  172. package/package.json +3 -3
  173. package/src/core/constants.ts +3 -3
  174. package/src/core/core-protocol.ts +1 -1
  175. package/src/core/dwn-error.ts +29 -1
  176. package/src/core/messages-grant-authorization.ts +27 -0
  177. package/src/core/protocol-authorization-validation.ts +145 -6
  178. package/src/core/recording-validation-state-reader.ts +13 -0
  179. package/src/core/replication-apply.ts +222 -8
  180. package/src/core/validation-state-reader.ts +30 -0
  181. package/src/dwn.ts +24 -2
  182. package/src/handlers/messages-query.ts +6 -5
  183. package/src/handlers/protocols-configure.ts +19 -3
  184. package/src/handlers/records-write.ts +1 -1
  185. package/src/index.ts +15 -5
  186. package/src/interfaces/protocols-configure.ts +39 -12
  187. package/src/interfaces/records-write-signing.ts +5 -12
  188. package/src/interfaces/records-write.ts +75 -92
  189. package/src/protocols/encryption.ts +649 -0
  190. package/src/protocols/permission-grant.ts +4 -5
  191. package/src/protocols/permissions.ts +8 -6
  192. package/src/types/encryption-types.ts +9 -38
  193. package/src/types/message-types.ts +0 -13
  194. package/src/types/permission-types.ts +57 -9
  195. package/src/types/protocols-types.ts +9 -16
  196. package/src/types/records-types.ts +3 -3
  197. package/src/types/validation-state-reader.ts +13 -0
  198. package/src/utils/encryption.ts +213 -283
  199. package/src/utils/hd-key.ts +1 -1
  200. package/src/utils/permission-scope.ts +10 -6
  201. package/src/utils/protocols.ts +16 -15
  202. package/src/utils/records.ts +86 -71
  203. package/src/utils/replication.ts +30 -4
  204. package/dist/esm/tests/vectors/protocol-definitions/chat.json +0 -70
@@ -31,9 +31,8 @@ import { TestEventLog } from '../test-event-stream.js';
31
31
  import { TestStores } from '../test-stores.js';
32
32
  import { TestStubGenerator } from '../utils/test-stub-generator.js';
33
33
  import { Time } from '../../src/utils/time.js';
34
- import { X25519 } from '@enbox/crypto';
35
- import { ContentEncryptionAlgorithm, Encryption } from '../../src/utils/encryption.js';
36
- import { CoreProtocolRegistry, DataStoreLevel, DwnConstant, DwnInterfaceName, DwnMethodName, KeyDerivationScheme, MessageStoreLevel, PermissionsProtocol, RecordsDelete, RecordsQuery } from '../../src/index.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
36
  import { defaultTestProtocolDefinition, TestDataGenerator } from '../utils/test-data-generator.js';
38
37
  import { DidKey, UniversalResolver } from '@enbox/dids';
39
38
  import { DwnError, DwnErrorCode } from '../../src/core/dwn-error.js';
@@ -2237,10 +2236,11 @@ export function testRecordsWriteHandler() {
2237
2236
  secret: {}
2238
2237
  }
2239
2238
  };
2239
+ const encryptedProtocolDefinition = await Protocols.deriveAndInjectPublicEncryptionKeys(protocolDefinition, alice.keyId, alice.encryptionKeyPair.privateJwk);
2240
2240
  // Alice installs the protocol
2241
2241
  const protocolConfig = await TestDataGenerator.generateProtocolsConfigure({
2242
2242
  author: alice,
2243
- protocolDefinition: protocolDefinition,
2243
+ protocolDefinition: encryptedProtocolDefinition,
2244
2244
  });
2245
2245
  const configReply = await dwn.processMessage(alice.did, protocolConfig.message);
2246
2246
  expect(configReply.status.code).toBe(202);
@@ -2272,25 +2272,24 @@ export function testRecordsWriteHandler() {
2272
2272
  secret: {}
2273
2273
  }
2274
2274
  };
2275
+ const encryptedProtocolDefinition = await Protocols.deriveAndInjectPublicEncryptionKeys(protocolDefinition, alice.keyId, alice.encryptionKeyPair.privateJwk);
2275
2276
  // Alice installs the protocol
2276
2277
  const protocolConfig = await TestDataGenerator.generateProtocolsConfigure({
2277
2278
  author: alice,
2278
- protocolDefinition: protocolDefinition,
2279
+ protocolDefinition: encryptedProtocolDefinition,
2279
2280
  });
2280
2281
  const configReply = await dwn.processMessage(alice.did, protocolConfig.message);
2281
2282
  expect(configReply.status.code).toBe(202);
2282
- // Generate an X25519 key pair for encryption (did:key personas use Ed25519 for signing)
2283
- const encryptionPrivateKey = await X25519.generateKey();
2284
- const encryptionPublicKey = await X25519.getPublicKey({ key: encryptionPrivateKey });
2285
2283
  const data = Encoder.stringToBytes('encrypted secret');
2286
2284
  const dataEncryptionKey = TestDataGenerator.randomBytes(32);
2287
- const dataEncryptionInitializationVector = TestDataGenerator.randomBytes(12);
2285
+ const dataEncryptionInitializationVector = TestDataGenerator.randomBytes(16);
2286
+ const encryptedData = await Encryption.encrypt(ContentEncryptionAlgorithm.A256CTR, dataEncryptionKey, dataEncryptionInitializationVector, data);
2287
+ const encryptionPublicKey = encryptedProtocolDefinition.structure.secret.$keyAgreement.publicKeyJwk;
2288
2288
  const encryptionInput = {
2289
2289
  initializationVector: dataEncryptionInitializationVector,
2290
2290
  key: dataEncryptionKey,
2291
- authenticationTag: TestDataGenerator.randomBytes(16),
2292
2291
  keyEncryptionInputs: [{
2293
- publicKeyId: alice.keyId,
2292
+ keyId: await Encryption.getKeyId(encryptionPublicKey),
2294
2293
  publicKey: encryptionPublicKey,
2295
2294
  derivationScheme: KeyDerivationScheme.ProtocolPath
2296
2295
  }]
@@ -2300,7 +2299,176 @@ export function testRecordsWriteHandler() {
2300
2299
  protocol: protocolDefinition.protocol,
2301
2300
  protocolPath: 'secret',
2302
2301
  schema: 'http://secret-schema',
2303
- data,
2302
+ data: encryptedData,
2303
+ encryptionInput,
2304
+ });
2305
+ const writeReply = await dwn.processMessage(alice.did, recordsWrite.message, { dataStream: recordsWrite.dataStream });
2306
+ expect(writeReply.status.code).toBe(202);
2307
+ });
2308
+ it('should reject an encrypted role-readable RecordsWrite without a roleAudience keyEncryption entry', async () => {
2309
+ const alice = await TestDataGenerator.generateDidKeyPersona();
2310
+ const protocolDefinition = {
2311
+ protocol: 'http://role-audience-entry-required.xyz',
2312
+ published: false,
2313
+ types: {
2314
+ thread: { schema: 'http://thread-schema', dataFormats: ['application/json'] },
2315
+ participant: { schema: 'http://participant-schema', dataFormats: ['application/json'] },
2316
+ chat: { schema: 'http://chat-schema', dataFormats: ['text/plain'], encryptionRequired: true },
2317
+ },
2318
+ structure: {
2319
+ thread: {
2320
+ participant: { $role: true },
2321
+ chat: {
2322
+ $actions: [
2323
+ { role: 'thread/participant', can: ['read'] },
2324
+ ],
2325
+ },
2326
+ },
2327
+ },
2328
+ };
2329
+ const encryptedProtocolDefinition = await Protocols.deriveAndInjectPublicEncryptionKeys(protocolDefinition, alice.keyId, alice.encryptionKeyPair.privateJwk);
2330
+ const protocolConfig = await TestDataGenerator.generateProtocolsConfigure({
2331
+ author: alice,
2332
+ protocolDefinition: encryptedProtocolDefinition,
2333
+ });
2334
+ expect((await dwn.processMessage(alice.did, protocolConfig.message)).status.code).toBe(202);
2335
+ const thread = await TestDataGenerator.generateRecordsWrite({
2336
+ author: alice,
2337
+ protocol: protocolDefinition.protocol,
2338
+ protocolPath: 'thread',
2339
+ schema: 'http://thread-schema',
2340
+ dataFormat: 'application/json',
2341
+ data: Encoder.stringToBytes('{"title":"secret"}'),
2342
+ });
2343
+ expect((await dwn.processMessage(alice.did, thread.message, { dataStream: thread.dataStream })).status.code).toBe(202);
2344
+ const data = Encoder.stringToBytes('encrypted chat');
2345
+ const dataEncryptionKey = TestDataGenerator.randomBytes(32);
2346
+ const dataEncryptionInitializationVector = TestDataGenerator.randomBytes(16);
2347
+ const encryptedData = await Encryption.encrypt(ContentEncryptionAlgorithm.A256CTR, dataEncryptionKey, dataEncryptionInitializationVector, data);
2348
+ const threadRuleSet = encryptedProtocolDefinition.structure.thread;
2349
+ const chatRuleSet = threadRuleSet.chat;
2350
+ const protocolPathPublicKey = chatRuleSet.$keyAgreement.publicKeyJwk;
2351
+ const encryptionInput = {
2352
+ initializationVector: dataEncryptionInitializationVector,
2353
+ key: dataEncryptionKey,
2354
+ keyEncryptionInputs: [{
2355
+ keyId: await Encryption.getKeyId(protocolPathPublicKey),
2356
+ publicKey: protocolPathPublicKey,
2357
+ derivationScheme: KeyDerivationScheme.ProtocolPath,
2358
+ }],
2359
+ };
2360
+ const recordsWrite = await TestDataGenerator.generateRecordsWrite({
2361
+ author: alice,
2362
+ protocol: protocolDefinition.protocol,
2363
+ protocolPath: 'thread/chat',
2364
+ parentContextId: thread.message.contextId,
2365
+ schema: 'http://chat-schema',
2366
+ dataFormat: 'text/plain',
2367
+ data: encryptedData,
2368
+ encryptionInput,
2369
+ });
2370
+ const writeReply = await dwn.processMessage(alice.did, recordsWrite.message, { dataStream: recordsWrite.dataStream });
2371
+ expect(writeReply.status.code).toBe(400);
2372
+ expect(writeReply.status.detail).toContain(DwnErrorCode.ProtocolAuthorizationEncryptionRoleAudienceEntryMissing);
2373
+ });
2374
+ it('should accept an encrypted role-readable RecordsWrite with a matching audienceEpoch', async () => {
2375
+ const alice = await TestDataGenerator.generateDidKeyPersona();
2376
+ const protocolDefinition = {
2377
+ protocol: 'http://role-audience-entry-accepted.xyz',
2378
+ published: false,
2379
+ types: {
2380
+ thread: { schema: 'http://thread-schema', dataFormats: ['application/json'] },
2381
+ participant: { schema: 'http://participant-schema', dataFormats: ['application/json'] },
2382
+ chat: { schema: 'http://chat-schema', dataFormats: ['text/plain'], encryptionRequired: true },
2383
+ },
2384
+ structure: {
2385
+ thread: {
2386
+ participant: { $role: true },
2387
+ chat: {
2388
+ $actions: [
2389
+ { role: 'thread/participant', can: ['read'] },
2390
+ ],
2391
+ },
2392
+ },
2393
+ },
2394
+ };
2395
+ const encryptedProtocolDefinition = await Protocols.deriveAndInjectPublicEncryptionKeys(protocolDefinition, alice.keyId, alice.encryptionKeyPair.privateJwk);
2396
+ const protocolConfig = await TestDataGenerator.generateProtocolsConfigure({
2397
+ author: alice,
2398
+ protocolDefinition: encryptedProtocolDefinition,
2399
+ });
2400
+ expect((await dwn.processMessage(alice.did, protocolConfig.message)).status.code).toBe(202);
2401
+ const thread = await TestDataGenerator.generateRecordsWrite({
2402
+ author: alice,
2403
+ protocol: protocolDefinition.protocol,
2404
+ protocolPath: 'thread',
2405
+ schema: 'http://thread-schema',
2406
+ dataFormat: 'application/json',
2407
+ data: Encoder.stringToBytes('{"title":"secret"}'),
2408
+ });
2409
+ expect((await dwn.processMessage(alice.did, thread.message, { dataStream: thread.dataStream })).status.code).toBe(202);
2410
+ const role = 'thread/participant';
2411
+ 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({
2416
+ protocol: protocolDefinition.protocol,
2417
+ contextId: thread.message.contextId,
2418
+ role,
2419
+ epoch: 1,
2420
+ keyId: roleKeyId,
2421
+ publicKeyJwk: rolePublicKey,
2422
+ });
2423
+ const audienceEpoch = await RecordsWrite.create({
2424
+ data: audienceEpochData,
2425
+ 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: {
2431
+ protocol: protocolDefinition.protocol,
2432
+ contextId: thread.message.contextId,
2433
+ role,
2434
+ epoch: 1,
2435
+ keyId: roleKeyId,
2436
+ },
2437
+ });
2438
+ expect((await dwn.processMessage(alice.did, audienceEpoch.message, { dataStream: DataStream.fromBytes(audienceEpochData) })).status.code).toBe(202);
2439
+ const data = Encoder.stringToBytes('encrypted chat');
2440
+ const dataEncryptionKey = TestDataGenerator.randomBytes(32);
2441
+ const dataEncryptionInitializationVector = TestDataGenerator.randomBytes(16);
2442
+ const encryptedData = await Encryption.encrypt(ContentEncryptionAlgorithm.A256CTR, dataEncryptionKey, dataEncryptionInitializationVector, data);
2443
+ const chatRuleSet = threadRuleSet.chat;
2444
+ const protocolPathPublicKey = chatRuleSet.$keyAgreement.publicKeyJwk;
2445
+ const encryptionInput = {
2446
+ initializationVector: dataEncryptionInitializationVector,
2447
+ key: dataEncryptionKey,
2448
+ keyEncryptionInputs: [
2449
+ {
2450
+ keyId: await Encryption.getKeyId(protocolPathPublicKey),
2451
+ publicKey: protocolPathPublicKey,
2452
+ derivationScheme: KeyDerivationScheme.ProtocolPath,
2453
+ },
2454
+ {
2455
+ keyId: roleKeyId,
2456
+ publicKey: rolePublicKey,
2457
+ derivationScheme: ROLE_AUDIENCE_DERIVATION_SCHEME,
2458
+ protocol: protocolDefinition.protocol,
2459
+ role,
2460
+ epoch: 1,
2461
+ },
2462
+ ],
2463
+ };
2464
+ const recordsWrite = await TestDataGenerator.generateRecordsWrite({
2465
+ author: alice,
2466
+ protocol: protocolDefinition.protocol,
2467
+ protocolPath: 'thread/chat',
2468
+ parentContextId: thread.message.contextId,
2469
+ schema: 'http://chat-schema',
2470
+ dataFormat: 'text/plain',
2471
+ data: encryptedData,
2304
2472
  encryptionInput,
2305
2473
  });
2306
2474
  const writeReply = await dwn.processMessage(alice.did, recordsWrite.message, { dataStream: recordsWrite.dataStream });
@@ -2320,10 +2488,11 @@ export function testRecordsWriteHandler() {
2320
2488
  secret: {},
2321
2489
  }
2322
2490
  };
2491
+ const encryptedProtocolDefinition = await Protocols.deriveAndInjectPublicEncryptionKeys(protocolDefinition, alice.keyId, alice.encryptionKeyPair.privateJwk);
2323
2492
  // Alice installs the protocol
2324
2493
  const protocolConfig = await TestDataGenerator.generateProtocolsConfigure({
2325
2494
  author: alice,
2326
- protocolDefinition: protocolDefinition,
2495
+ protocolDefinition: encryptedProtocolDefinition,
2327
2496
  });
2328
2497
  const configReply = await dwn.processMessage(alice.did, protocolConfig.message);
2329
2498
  expect(configReply.status.code).toBe(202);
@@ -2570,15 +2739,14 @@ export function testRecordsWriteHandler() {
2570
2739
  const protocolsConfigureReply = await dwn.processMessage(alice.did, protocolsConfig.message);
2571
2740
  expect(protocolsConfigureReply.status.code).toBe(202);
2572
2741
  const bobMessageBytes = Encoder.stringToBytes('message from bob');
2573
- const dataEncryptionInitializationVector = TestDataGenerator.randomBytes(12);
2742
+ const dataEncryptionInitializationVector = TestDataGenerator.randomBytes(16);
2574
2743
  const dataEncryptionKey = TestDataGenerator.randomBytes(32);
2575
- const { ciphertext: bobMessageEncryptedBytes, tag: authenticationTag } = await Encryption.aeadEncrypt(ContentEncryptionAlgorithm.A256GCM, dataEncryptionKey, dataEncryptionInitializationVector, bobMessageBytes);
2744
+ const bobMessageEncryptedBytes = await Encryption.encrypt(ContentEncryptionAlgorithm.A256CTR, dataEncryptionKey, dataEncryptionInitializationVector, bobMessageBytes);
2576
2745
  const encryptionInput = {
2577
2746
  initializationVector: dataEncryptionInitializationVector,
2578
2747
  key: dataEncryptionKey,
2579
- authenticationTag,
2580
2748
  keyEncryptionInputs: [{
2581
- publicKeyId: alice.keyId, // reusing signing key for encryption purely as a convenience
2749
+ keyId: await Encryption.getKeyId(alice.encryptionKeyPair.publicJwk),
2582
2750
  publicKey: alice.encryptionKeyPair.publicJwk,
2583
2751
  derivationScheme: KeyDerivationScheme.ProtocolPath
2584
2752
  }]
@@ -2591,8 +2759,8 @@ export function testRecordsWriteHandler() {
2591
2759
  data: bobMessageEncryptedBytes,
2592
2760
  encryptionInput
2593
2761
  });
2594
- // replace valid `encryption` property with a mismatching one mutate the iv to cause CID mismatch
2595
- message.encryption.iv = Encoder.stringToBase64Url('any value which will result in a different CID');
2762
+ // Replace valid `encryption` property with a mismatching one while keeping the envelope schema-valid.
2763
+ message.encryption.initializationVector = Encoder.bytesToBase64Url(TestDataGenerator.randomBytes(16));
2596
2764
  const recordsWriteHandler = new RecordsWriteHandler({
2597
2765
  didResolver, messageStore, dataStore, coreProtocols: new CoreProtocolRegistry(), eventLog,
2598
2766
  validationStateReader: createTestValidationStateReader({ messageStore, dataStore }),
@@ -2601,6 +2769,98 @@ export function testRecordsWriteHandler() {
2601
2769
  expect(writeReply.status.code).toBe(400);
2602
2770
  expect(writeReply.status.detail).toContain(DwnErrorCode.RecordsWriteValidateIntegrityEncryptionCidMismatch);
2603
2771
  });
2772
+ it('should 400 if `encryption` is present without a signed encryptionCid', async () => {
2773
+ const alice = await TestDataGenerator.generatePersona();
2774
+ TestStubGenerator.stubDidResolver(didResolver, [alice]);
2775
+ const protocolDefinition = emailProtocolDefinition;
2776
+ const protocol = protocolDefinition.protocol;
2777
+ const protocolsConfig = await TestDataGenerator.generateProtocolsConfigure({
2778
+ author: alice,
2779
+ protocolDefinition
2780
+ });
2781
+ const protocolsConfigureReply = await dwn.processMessage(alice.did, protocolsConfig.message);
2782
+ expect(protocolsConfigureReply.status.code).toBe(202);
2783
+ const messageBytes = Encoder.stringToBytes('encrypted message');
2784
+ const dataEncryptionInitializationVector = TestDataGenerator.randomBytes(16);
2785
+ const dataEncryptionKey = TestDataGenerator.randomBytes(32);
2786
+ const encryptedBytes = await Encryption.encrypt(ContentEncryptionAlgorithm.A256CTR, dataEncryptionKey, dataEncryptionInitializationVector, messageBytes);
2787
+ const encryptionInput = {
2788
+ initializationVector: dataEncryptionInitializationVector,
2789
+ key: dataEncryptionKey,
2790
+ keyEncryptionInputs: [{
2791
+ derivationScheme: KeyDerivationScheme.ProtocolPath,
2792
+ keyId: await Encryption.getKeyId(alice.encryptionKeyPair.publicJwk),
2793
+ publicKey: alice.encryptionKeyPair.publicJwk,
2794
+ }],
2795
+ };
2796
+ const { message, dataStream } = await TestDataGenerator.generateRecordsWrite({
2797
+ author: alice,
2798
+ data: encryptedBytes,
2799
+ encryptionInput,
2800
+ protocol,
2801
+ protocolPath: 'email',
2802
+ schema: 'email',
2803
+ });
2804
+ const signaturePayload = Jws.decodePlainObjectPayload(message.authorization.signature);
2805
+ delete signaturePayload.encryptionCid;
2806
+ message.authorization.signature.payload = Encoder.stringToBase64Url(JSON.stringify(signaturePayload));
2807
+ const recordsWriteHandler = new RecordsWriteHandler({
2808
+ coreProtocols: new CoreProtocolRegistry(),
2809
+ dataStore,
2810
+ didResolver,
2811
+ eventLog,
2812
+ messageStore,
2813
+ validationStateReader: createTestValidationStateReader({ messageStore, dataStore }),
2814
+ });
2815
+ const writeReply = await recordsWriteHandler.handle({ tenant: alice.did, message, dataStream: dataStream });
2816
+ expect(writeReply.status.code).toBe(400);
2817
+ expect(writeReply.status.detail).toContain(DwnErrorCode.RecordsWriteValidateIntegrityEncryptionCidMissing);
2818
+ });
2819
+ it('should 400 if a signed encryptionCid is present without `encryption`', async () => {
2820
+ const alice = await TestDataGenerator.generatePersona();
2821
+ TestStubGenerator.stubDidResolver(didResolver, [alice]);
2822
+ const protocolDefinition = emailProtocolDefinition;
2823
+ const protocol = protocolDefinition.protocol;
2824
+ const protocolsConfig = await TestDataGenerator.generateProtocolsConfigure({
2825
+ author: alice,
2826
+ protocolDefinition
2827
+ });
2828
+ const protocolsConfigureReply = await dwn.processMessage(alice.did, protocolsConfig.message);
2829
+ expect(protocolsConfigureReply.status.code).toBe(202);
2830
+ const messageBytes = Encoder.stringToBytes('encrypted message');
2831
+ const dataEncryptionInitializationVector = TestDataGenerator.randomBytes(16);
2832
+ const dataEncryptionKey = TestDataGenerator.randomBytes(32);
2833
+ const encryptedBytes = await Encryption.encrypt(ContentEncryptionAlgorithm.A256CTR, dataEncryptionKey, dataEncryptionInitializationVector, messageBytes);
2834
+ const encryptionInput = {
2835
+ initializationVector: dataEncryptionInitializationVector,
2836
+ key: dataEncryptionKey,
2837
+ keyEncryptionInputs: [{
2838
+ derivationScheme: KeyDerivationScheme.ProtocolPath,
2839
+ keyId: await Encryption.getKeyId(alice.encryptionKeyPair.publicJwk),
2840
+ publicKey: alice.encryptionKeyPair.publicJwk,
2841
+ }],
2842
+ };
2843
+ const { message, dataStream } = await TestDataGenerator.generateRecordsWrite({
2844
+ author: alice,
2845
+ data: encryptedBytes,
2846
+ encryptionInput,
2847
+ protocol,
2848
+ protocolPath: 'email',
2849
+ schema: 'email',
2850
+ });
2851
+ delete message.encryption;
2852
+ const recordsWriteHandler = new RecordsWriteHandler({
2853
+ coreProtocols: new CoreProtocolRegistry(),
2854
+ dataStore,
2855
+ didResolver,
2856
+ eventLog,
2857
+ messageStore,
2858
+ validationStateReader: createTestValidationStateReader({ messageStore, dataStore }),
2859
+ });
2860
+ const writeReply = await recordsWriteHandler.handle({ tenant: alice.did, message, dataStream: dataStream });
2861
+ expect(writeReply.status.code).toBe(400);
2862
+ expect(writeReply.status.detail).toContain(DwnErrorCode.RecordsWriteValidateIntegrityEncryptionCidMissing);
2863
+ });
2604
2864
  it('should return 400 if protocol is not normalized', async () => {
2605
2865
  const alice = await TestDataGenerator.generateDidKeyPersona();
2606
2866
  const protocolDefinition = emailProtocolDefinition;