@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
@@ -7,6 +7,7 @@ import type { ValidationStateReader } from '../types/validation-state-reader.js'
7
7
  import type { DataEncodedRecordsWriteMessage, RecordsWriteMessage } from '../types/records-types.js';
8
8
  import type { ProtocolDefinition, ProtocolsConfigureMessage } from '../types/protocols-types.js';
9
9
 
10
+ import { ENCRYPTION_PROTOCOL_URI } from './constants.js';
10
11
  import { FilterUtility } from '../utils/filter.js';
11
12
  import { PermissionGrant } from '../protocols/permission-grant.js';
12
13
  import { PermissionsProtocol } from '../protocols/permissions.js';
@@ -165,6 +166,35 @@ export class StoreValidationStateReader implements ValidationStateReader {
165
166
  return matchingMessages as RecordsWriteMessage[];
166
167
  }
167
168
 
169
+ /** @inheritdoc */
170
+ public async queryAudienceEpochs(input: {
171
+ tenant: string;
172
+ protocol: string;
173
+ contextId: string;
174
+ role: string;
175
+ epoch: number;
176
+ keyId?: string;
177
+ }): Promise<RecordsWriteMessage[]> {
178
+ const filter: Filter = {
179
+ interface : DwnInterfaceName.Records,
180
+ method : DwnMethodName.Write,
181
+ isLatestBaseState : true,
182
+ protocol : ENCRYPTION_PROTOCOL_URI,
183
+ protocolPath : 'audienceEpoch',
184
+ 'tag.protocol' : input.protocol,
185
+ 'tag.contextId' : input.contextId,
186
+ 'tag.role' : input.role,
187
+ 'tag.epoch' : input.epoch,
188
+ };
189
+
190
+ if (input.keyId !== undefined) {
191
+ filter['tag.keyId'] = input.keyId;
192
+ }
193
+
194
+ const { messages } = await this.messageStore.query(input.tenant, [filter]);
195
+ return messages as RecordsWriteMessage[];
196
+ }
197
+
168
198
  /** @inheritdoc */
169
199
  public async fetchGrant(tenant: string, permissionGrantId: string): Promise<PermissionGrant> {
170
200
  const grantQuery = {
package/src/dwn.ts CHANGED
@@ -41,6 +41,7 @@ import { Cid } from './utils/cid.js';
41
41
  import { CoreProtocolRegistry } from './core/core-protocol.js';
42
42
  import { DataStream } from './utils/data-stream.js';
43
43
  import { DwnConstant } from './core/dwn-constant.js';
44
+ import { EncryptionProtocol } from './protocols/encryption.js';
44
45
  import { Message } from './core/message.js';
45
46
  import { messageReplyFromError } from './core/message-reply.js';
46
47
  import { MessagesQueryHandler } from './handlers/messages-query.js';
@@ -115,6 +116,7 @@ export class Dwn {
115
116
 
116
117
  // Initialize the core protocol registry with built-in system protocols.
117
118
  this._coreProtocols = new CoreProtocolRegistry();
119
+ this._coreProtocols.register(new EncryptionProtocol());
118
120
  this._coreProtocols.register(new PermissionsProtocol());
119
121
 
120
122
  // The single narrow surface through which validation logic reads state (replay-basis closure).
@@ -426,7 +428,7 @@ export class Dwn {
426
428
  RecordsWrite.validateDataIntegrity(message.descriptor.dataCid, message.descriptor.dataSize, dataCid, dataBytes.length);
427
429
 
428
430
  if (coreProtocol?.validateRecord !== undefined) {
429
- coreProtocol.validateRecord(message, dataBytes);
431
+ await coreProtocol.validateRecord(message, dataBytes);
430
432
  }
431
433
 
432
434
  return;
@@ -492,7 +494,10 @@ export class Dwn {
492
494
  reply: { status: { detail?: string } },
493
495
  ): Promise<ProtocolDefinition | undefined> {
494
496
  const detail = reply.status.detail ?? '';
495
- if (!detail.startsWith(`${DwnErrorCode.ProtocolAuthorizationMatchingRoleRecordNotFound}:`)) {
497
+ if (
498
+ !detail.startsWith(`${DwnErrorCode.ProtocolAuthorizationMatchingRoleRecordNotFound}:`) &&
499
+ !detail.startsWith(`${DwnErrorCode.EncryptionProtocolValidateAudienceWriterUnauthorized}:`)
500
+ ) {
496
501
  return undefined;
497
502
  }
498
503
 
@@ -516,6 +521,14 @@ export class Dwn {
516
521
  message: GenericMessage,
517
522
  ): ReplicationApplyProtocolDefinitionLookup | undefined {
518
523
  if (Dwn.isRecordsWriteMessage(message)) {
524
+ const taggedProtocol = Dwn.getEncryptionRecordTaggedProtocol(message);
525
+ if (taggedProtocol !== undefined) {
526
+ return {
527
+ protocol : taggedProtocol,
528
+ messageTimestamp : message.descriptor.messageTimestamp,
529
+ };
530
+ }
531
+
519
532
  return {
520
533
  protocol : message.descriptor.protocol,
521
534
  messageTimestamp : message.descriptor.messageTimestamp,
@@ -526,6 +539,15 @@ export class Dwn {
526
539
  return protocol === undefined ? undefined : { protocol };
527
540
  }
528
541
 
542
+ private static getEncryptionRecordTaggedProtocol(message: RecordsWriteMessage): string | undefined {
543
+ if (message.descriptor.protocol !== EncryptionProtocol.uri) {
544
+ return undefined;
545
+ }
546
+
547
+ const protocol = message.descriptor.tags?.protocol;
548
+ return typeof protocol === 'string' ? protocol : undefined;
549
+ }
550
+
529
551
  private static getMessageProtocolForReplicationApply(message: GenericMessage): string | undefined {
530
552
  const descriptor = message.descriptor as { protocol?: unknown; filter?: { protocol?: unknown } };
531
553
  if (typeof descriptor.protocol === 'string') {
@@ -9,7 +9,6 @@ import { messageReplyFromError } from '../core/message-reply.js';
9
9
  import { Messages } from '../utils/messages.js';
10
10
  import { MessagesGrantAuthorization } from '../core/messages-grant-authorization.js';
11
11
  import { MessagesQuery } from '../interfaces/messages-query.js';
12
- import { PermissionsProtocol } from '../protocols/permissions.js';
13
12
  import { Replication } from '../utils/replication.js';
14
13
  import { DwnError, DwnErrorCode } from '../core/dwn-error.js';
15
14
 
@@ -187,10 +186,12 @@ export class MessagesQueryHandler implements MethodHandler {
187
186
  protocols.add(filter.protocol);
188
187
  }
189
188
 
190
- const protocolList = [...protocols];
191
- const scopes = protocolList.map(protocol => Replication.protocolDomain(protocol));
192
- if (!protocols.has(PermissionsProtocol.uri)) {
193
- scopes.push(...protocolList.map(protocol => Replication.permissionDomain(protocol)));
189
+ const scopes: string[] = [];
190
+ for (const protocol of protocols) {
191
+ scopes.push(
192
+ Replication.protocolDomain(protocol),
193
+ ...Replication.taggedCoreProtocolDomains(protocol, protocols),
194
+ );
194
195
  }
195
196
 
196
197
  return scopes;
@@ -15,7 +15,7 @@ import { RecordsWrite } from '../interfaces/records-write.js';
15
15
  import { StorageController } from '../store/storage-controller.js';
16
16
  import { DwnError, DwnErrorCode } from '../core/dwn-error.js';
17
17
  import { DwnInterfaceName, DwnMethodName } from '../enums/dwn-interface-method.js';
18
- import { getRuleSetAtPath, parseCrossProtocolRef } from '../utils/protocols.js';
18
+ import { getRuleSetAtPath, getTypeName, parseCrossProtocolRef } from '../utils/protocols.js';
19
19
 
20
20
  type StoredInitialWriteConfigValidity = 'valid' | 'invalid' | 'unknown';
21
21
 
@@ -291,7 +291,12 @@ export class ProtocolsConfigureHandler implements MethodHandler {
291
291
  }
292
292
 
293
293
  // Walk the structure and validate all $ref paths and cross-protocol role references
294
- ProtocolsConfigureHandler.validateRefsAndRolesRecursively(definition.structure as ProtocolRuleSet, '', referencedDefinitions);
294
+ ProtocolsConfigureHandler.validateRefsAndRolesRecursively(
295
+ definition,
296
+ definition.structure as ProtocolRuleSet,
297
+ '',
298
+ referencedDefinitions,
299
+ );
295
300
  }
296
301
 
297
302
  /**
@@ -300,6 +305,7 @@ export class ProtocolsConfigureHandler implements MethodHandler {
300
305
  * - Cross-protocol `role` references point to valid `$role: true` paths in the referenced protocol
301
306
  */
302
307
  private static validateRefsAndRolesRecursively(
308
+ definition: ProtocolDefinition,
303
309
  ruleSet: ProtocolRuleSet,
304
310
  protocolPath: string,
305
311
  referencedDefinitions: Map<string, ProtocolDefinition>
@@ -362,6 +368,8 @@ export class ProtocolsConfigureHandler implements MethodHandler {
362
368
 
363
369
  // Validate cross-protocol references in $actions (roles and `of` paths)
364
370
  const actionRules = childRuleSet.$actions ?? [];
371
+ const typeName = getTypeName(childProtocolPath);
372
+ const isEncryptedPath = definition.types[typeName]?.encryptionRequired === true;
365
373
  for (const actionRule of actionRules) {
366
374
  // Validate cross-protocol role references
367
375
  if (actionRule.role !== undefined) {
@@ -385,6 +393,14 @@ export class ProtocolsConfigureHandler implements MethodHandler {
385
393
  `does not point to a valid role ($role: true) in protocol '${refDefinition.protocol}'.`
386
394
  );
387
395
  }
396
+
397
+ if (isEncryptedPath && actionRule.can.includes('read') && roleRuleSet.$keyAgreement === undefined) {
398
+ throw new DwnError(
399
+ DwnErrorCode.ProtocolsConfigureInvalidEncryptedRoleMissingKeyAgreement,
400
+ `encrypted protocol path '${childProtocolPath}' references cross-protocol role '${actionRule.role}' ` +
401
+ `with no $keyAgreement.`
402
+ );
403
+ }
388
404
  }
389
405
  }
390
406
 
@@ -414,7 +430,7 @@ export class ProtocolsConfigureHandler implements MethodHandler {
414
430
  }
415
431
 
416
432
  // Recurse into children
417
- ProtocolsConfigureHandler.validateRefsAndRolesRecursively(childRuleSet, childProtocolPath, referencedDefinitions);
433
+ ProtocolsConfigureHandler.validateRefsAndRolesRecursively(definition, childRuleSet, childProtocolPath, referencedDefinitions);
418
434
  }
419
435
  }
420
436
  }
@@ -244,7 +244,7 @@ export class RecordsWriteHandler implements MethodHandler {
244
244
  ? undefined
245
245
  : this.deps.coreProtocols?.get(message.descriptor.protocol);
246
246
  if (coreProtocol?.validateRecord !== undefined) {
247
- coreProtocol.validateRecord(message, dataBytes);
247
+ await coreProtocol.validateRecord(message, dataBytes);
248
248
  }
249
249
 
250
250
  messageWithOptionalEncodedData = await this.cloneAndAddEncodedData(message, dataBytes);
package/src/index.ts CHANGED
@@ -10,7 +10,7 @@ export type { RecordedValidationRead } from './core/recording-validation-state-r
10
10
  export { RecordingValidationStateReader } from './core/recording-validation-state-reader.js';
11
11
  export type { MessagesFilter, MessagesQueryDescriptor, MessagesQueryMessage, MessagesQueryReply, MessagesQueryReplyEntry, MessagesReadMessage, MessagesReadReply, MessagesReadReplyEntry, MessagesReadDescriptor, MessagesSubscribeDescriptor, MessagesSubscribeMessage, MessagesSubscribeReply, MessagesSubscribeMessageOptions } from './types/messages-types.js';
12
12
  export type { GT, LT, Filter, FilterValue, KeyValues, EqualFilter, OneOfFilter, RangeFilter, RangeCriterion, PaginationCursor, QueryOptions, RangeValue, StartsWithFilter } from './types/query-types.js';
13
- export type { ProtocolsConfigureDescriptor, ProtocolDefinition, ProtocolTypes, ProtocolRuleSet, ProtocolsQueryFilter, ProtocolsConfigureMessage, ProtocolsQueryMessage, ProtocolsQueryReply, ProtocolActionRule, ProtocolDeliveryStrategy, ProtocolPathEncryption, ProtocolsQueryDescriptor, ProtocolRecordLimitDefinition, ProtocolSizeDefinition, ProtocolTagsDefinition, ProtocolTagSchema, ProtocolType, ProtocolUses } from './types/protocols-types.js';
13
+ export type { ProtocolsConfigureDescriptor, ProtocolDefinition, ProtocolTypes, ProtocolRuleSet, ProtocolsQueryFilter, ProtocolsConfigureMessage, ProtocolsQueryMessage, ProtocolsQueryReply, ProtocolActionRule, ProtocolDeliveryStrategy, ProtocolKeyAgreement, ProtocolsQueryDescriptor, ProtocolRecordLimitDefinition, ProtocolSizeDefinition, ProtocolTagsDefinition, ProtocolTagSchema, ProtocolType, ProtocolUses } from './types/protocols-types.js';
14
14
  export { ProtocolRecordLimitStrategy } from './types/protocols-types.js';
15
15
  export type { DataEncodedRecordsWriteMessage, RecordsCountDescriptor, RecordsCountMessage, RecordsCountReply, RecordsDeleteMessage, RecordsFilter, RecordsQueryMessage, RecordsQueryReply, RecordsQueryReplyEntry, RecordsReadMessage, RecordsReadReply, RecordsSubscribeDescriptor, RecordsSubscribeMessage, RecordsSubscribeReply, RecordsWriteDescriptor, RecordsWriteTags, RecordsWriteTagValue, RecordsWriteMessage, RecordsWriteSignaturePayload, RecordsDeleteDescriptor, RecordsQueryDescriptor, RecordsReadDescriptor, RecordsSubscribeMessageOptions, RecordsWriteMessageOptions, InternalRecordsWriteMessage, RecordEvent, RecordsWriteTagsFilter } from './types/records-types.js';
16
16
  export type { GeneralJws, SignatureEntry } from './types/jws-types.js';
@@ -38,10 +38,19 @@ export { DwnInterfaceName, DwnMethodName } from './enums/dwn-interface-method.js
38
38
  export { Encoder } from './utils/encoder.js';
39
39
  export { MessagesSubscribe } from './interfaces/messages-subscribe.js';
40
40
  export type { MessagesSubscribeOptions } from './interfaces/messages-subscribe.js';
41
- export { Encryption, ContentEncryptionAlgorithm, KeyAgreementAlgorithm } from './utils/encryption.js';
42
- export type { JweEncryption, JweRecipient, JweRecipientHeader, JweProtectedHeader, JweKeyUnwrapPayload } from './utils/encryption.js';
41
+ export { Encryption, ContentEncryptionAlgorithm, KeyAgreementAlgorithm, ROLE_AUDIENCE_DERIVATION_SCHEME } from './utils/encryption.js';
42
+ export type {
43
+ DwnEncryption,
44
+ KeyEncryption,
45
+ KeyEncryptionInput,
46
+ KeyUnwrapPayload,
47
+ ProtocolPathKeyEncryption,
48
+ ProtocolPathKeyEncryptionInput,
49
+ RoleAudienceKeyEncryption,
50
+ RoleAudienceKeyEncryptionInput,
51
+ } from './utils/encryption.js';
43
52
  export { RecordsWrite } from './interfaces/records-write.js';
44
- export type { EncryptionInput, KeyEncryptionInput, RecordsWriteOptions, CreateFromOptions } from './interfaces/records-write.js';
53
+ export type { EncryptionInput, RecordsWriteOptions, CreateFromOptions } from './interfaces/records-write.js';
45
54
  export { executeUnlessAborted } from './utils/abort.js';
46
55
  export { Jws } from './utils/jws.js';
47
56
  export type { KeyMaterial, PrivateKeyJwk, PublicKeyJwk, Jwk } from './types/jose-types.js';
@@ -57,6 +66,7 @@ export type { MessageInterface } from './types/message-interface.js';
57
66
  export { PermissionGrant } from './protocols/permission-grant.js';
58
67
  export { PermissionRequest } from './protocols/permission-request.js';
59
68
  export { PermissionsProtocol } from './protocols/permissions.js';
69
+ export { EncryptionProtocol } from './protocols/encryption.js';
60
70
  export type { PermissionGrantCreateOptions, PermissionRequestCreateOptions, PermissionRevocationCreateOptions } from './protocols/permissions.js';
61
71
  export { PermissionScopeMatcher } from './utils/permission-scope.js';
62
72
  export type { ProtocolScope } from './utils/permission-scope.js';
@@ -80,7 +90,7 @@ export { Secp256r1 } from './utils/secp256r1.js';
80
90
  export type { SupportedCurve } from './jose/algorithms/signing/signature-algorithms.js';
81
91
  export type { MessageSigner } from './types/signer.js';
82
92
  export { SortDirection } from './types/query-types.js';
83
- export type { EncryptionKeyDeriver, KeyDecrypter } from './types/encryption-types.js';
93
+ export type { EncryptionKeyDeriver, KeyDecrypter, KeyDecrypterDerivationScheme } from './types/encryption-types.js';
84
94
  export { Time } from './utils/time.js';
85
95
  export * from './types/permission-types.js';
86
96
  export * from './types/records-types.js';
@@ -15,7 +15,7 @@ import { Time } from '../utils/time.js';
15
15
  import { validateProtocolTagSchemaDefinition } from '../utils/protocol-tags.js';
16
16
  import { DwnError, DwnErrorCode } from '../core/dwn-error.js';
17
17
  import { DwnInterfaceName, DwnMethodName } from '../enums/dwn-interface-method.js';
18
- import { isCrossProtocolRef, parseCrossProtocolRef } from '../utils/protocols.js';
18
+ import { getRuleSetAtPath, isCrossProtocolRef, parseCrossProtocolRef } from '../utils/protocols.js';
19
19
  import { normalizeProtocolUrl, normalizeSchemaUrl, validateProtocolUrlNormalized, validateSchemaUrlNormalized } from '../utils/url.js';
20
20
  import { ProtocolAction, ProtocolActor, ProtocolRecordLimitStrategy } from '../types/protocols-types.js';
21
21
 
@@ -150,6 +150,14 @@ export class ProtocolsConfigure extends AbstractMessage<ProtocolsConfigureMessag
150
150
  // gather all declared record types
151
151
  const recordTypes = Object.keys(definition.types);
152
152
 
153
+ const hasEncryptedTypes = Object.values(definition.types).some((type): boolean => type.encryptionRequired === true);
154
+ if (hasEncryptedTypes && definition.$keyAgreement === undefined) {
155
+ throw new DwnError(
156
+ DwnErrorCode.ProtocolsConfigureMissingTopLevelKeyAgreement,
157
+ `Protocol '${definition.protocol}' declares encrypted types but has no top-level $keyAgreement.`
158
+ );
159
+ }
160
+
153
161
  // gather all roles (local roles only — cross-protocol roles are validated by alias existence)
154
162
  const roles = ProtocolsConfigure.fetchAllRolePathsRecursively('', definition.structure as ProtocolRuleSet, []);
155
163
 
@@ -161,6 +169,7 @@ export class ProtocolsConfigure extends AbstractMessage<ProtocolsConfigureMessag
161
169
  roles,
162
170
  uses,
163
171
  types : definition.types,
172
+ rootStructure : definition.structure,
164
173
  });
165
174
  }
166
175
 
@@ -207,10 +216,10 @@ export class ProtocolsConfigure extends AbstractMessage<ProtocolsConfigureMessag
207
216
  private static validateRuleSetRecursively(
208
217
  input: {
209
218
  ruleSet: ProtocolRuleSet, ruleSetProtocolPath: string, recordTypes: string[],
210
- roles: string[], uses?: ProtocolUses, types: ProtocolTypes
219
+ roles: string[], uses?: ProtocolUses, types: ProtocolTypes, rootStructure: { [key: string]: ProtocolRuleSet }
211
220
  }
212
221
  ): void {
213
- const { ruleSet, ruleSetProtocolPath, recordTypes, roles, uses, types } = input;
222
+ const { ruleSet, ruleSetProtocolPath, recordTypes, roles, uses, types, rootStructure } = input;
214
223
 
215
224
  // Validate $ref constraints: $ref is only supported at root level (no `/` in protocol path),
216
225
  // and a $ref node is a pure attachment point with no other directives.
@@ -412,23 +421,40 @@ export class ProtocolsConfigure extends AbstractMessage<ProtocolsConfigureMessag
412
421
  }
413
422
  }
414
423
 
415
- // Warn when `encryptionRequired: true` is combined with `{ who: 'anyone', can: ['read'] }`.
416
- // Authorization allows anyone to read the record, but encryption prevents them from
417
- // decrypting the data — almost certainly unintentional. (issue #115)
418
424
  if (ruleSetProtocolPath !== '') {
419
425
  const typeName = ruleSetProtocolPath.split('/').pop()!;
420
426
  const protocolType = types[typeName];
421
427
  if (protocolType?.encryptionRequired === true) {
428
+ if (ruleSet.$keyAgreement === undefined) {
429
+ throw new DwnError(
430
+ DwnErrorCode.ProtocolsConfigureMissingEncryptedPathKeyAgreement,
431
+ `Encrypted protocol path '${ruleSetProtocolPath}' has no $keyAgreement.`
432
+ );
433
+ }
434
+
422
435
  const anyoneCanRead = actionRules.some(
423
436
  (rule: ProtocolActionRule): boolean => rule.who === ProtocolActor.Anyone && rule.can.includes(ProtocolAction.Read)
424
437
  );
425
438
  if (anyoneCanRead) {
426
- console.warn(
427
- `ProtocolsConfigure: type '${typeName}' at path '${ruleSetProtocolPath}' has ` +
428
- `encryptionRequired: true but allows { who: 'anyone', can: ['read'] }. ` +
429
- `Anyone can read the record but no one outside the key holders can decrypt it.`
439
+ throw new DwnError(
440
+ DwnErrorCode.ProtocolsConfigureInvalidEncryptedAnyoneRead,
441
+ `Encrypted protocol path '${ruleSetProtocolPath}' allows { who: 'anyone', can: ['read'] }.`
430
442
  );
431
443
  }
444
+
445
+ for (const actionRule of actionRules) {
446
+ if (actionRule.role === undefined || !actionRule.can.includes(ProtocolAction.Read) || isCrossProtocolRef(actionRule.role)) {
447
+ continue;
448
+ }
449
+
450
+ const roleRuleSet = getRuleSetAtPath(actionRule.role, rootStructure);
451
+ if (roleRuleSet?.$keyAgreement === undefined) {
452
+ throw new DwnError(
453
+ DwnErrorCode.ProtocolsConfigureInvalidEncryptedRoleMissingKeyAgreement,
454
+ `Encrypted protocol path '${ruleSetProtocolPath}' references role '${actionRule.role}' with no $keyAgreement.`
455
+ );
456
+ }
457
+ }
432
458
  }
433
459
  }
434
460
 
@@ -490,13 +516,14 @@ export class ProtocolsConfigure extends AbstractMessage<ProtocolsConfigureMessag
490
516
  roles,
491
517
  uses,
492
518
  types,
519
+ rootStructure,
493
520
  });
494
521
  }
495
522
  }
496
523
 
497
524
  /**
498
525
  * Validates that a `$ref` node is a pure attachment point: it must NOT have
499
- * `$actions`, `$role`, `$size`, `$tags`, or `$encryption`.
526
+ * `$actions`, `$role`, `$size`, `$tags`, or `$keyAgreement`.
500
527
  * Also validates that the `$ref` alias exists in the `uses` map.
501
528
  */
502
529
  private static validateRefNode(ruleSet: ProtocolRuleSet, ruleSetProtocolPath: string, uses: ProtocolUses | undefined): void {
@@ -519,7 +546,7 @@ export class ProtocolsConfigure extends AbstractMessage<ProtocolsConfigureMessag
519
546
  }
520
547
 
521
548
  // validate that `$ref` nodes do not have other directives
522
- const forbiddenDirectives = ['$actions', '$role', '$size', '$tags', '$encryption', '$recordLimit', '$immutable', '$delivery', '$squash'] as const;
549
+ const forbiddenDirectives = ['$actions', '$role', '$size', '$tags', '$keyAgreement', '$encryption', '$recordLimit', '$immutable', '$delivery', '$squash'] as const;
523
550
  for (const directive of forbiddenDirectives) {
524
551
  if (ruleSet[directive] !== undefined) {
525
552
  throw new DwnError(
@@ -1,6 +1,6 @@
1
1
  import type { GeneralJws } from '../types/jws-types.js';
2
2
  import type { MessageSigner } from '../types/signer.js';
3
- import type { EncryptionInput, JweEncryption } from '../utils/encryption.js';
3
+ import type { DwnEncryption, EncryptionInput } from '../utils/encryption.js';
4
4
  import type { RecordsWriteAttestationPayload, RecordsWriteMessage, RecordsWriteSignaturePayload } from '../types/records-types.js';
5
5
 
6
6
  import { Cid } from '../utils/cid.js';
@@ -11,22 +11,15 @@ import { Jws } from '../utils/jws.js';
11
11
  import { removeUndefinedProperties } from '@enbox/common';
12
12
  import { DwnError, DwnErrorCode } from '../core/dwn-error.js';
13
13
 
14
- /**
15
- * Creates the JWE `encryption` property if encryption input is given. Else `undefined` is returned.
16
- * Uses ECDH-ES+A256KW key agreement with X25519 and AEAD content encryption (A256GCM or XC20P).
17
- * @param encryptionInput The encryption input containing CEK, IV, authentication tag, and recipient key encryption inputs.
18
- */
14
+ /** Creates the RecordsWrite `encryption` property if encryption input is given. */
19
15
  export async function createEncryptionProperty(
20
16
  encryptionInput: EncryptionInput | undefined,
21
- ): Promise<JweEncryption | undefined> {
17
+ ): Promise<DwnEncryption | undefined> {
22
18
  if (encryptionInput === undefined) {
23
19
  return undefined;
24
20
  }
25
21
 
26
- // Build the JWE structure. The authentication tag comes from the AEAD encryption of record data.
27
- const jwe = await Encryption.buildJwe(encryptionInput, encryptionInput.authenticationTag);
28
-
29
- return jwe;
22
+ return Encryption.buildEncryptionProperty(encryptionInput);
30
23
  }
31
24
 
32
25
  /**
@@ -52,7 +45,7 @@ export async function createSignerSignature(input: {
52
45
  contextId: string,
53
46
  descriptorCid: string,
54
47
  attestation: GeneralJws | undefined,
55
- encryption: JweEncryption | undefined,
48
+ encryption: DwnEncryption | undefined,
56
49
  signer: MessageSigner,
57
50
  delegatedGrantId?: string,
58
51
  permissionGrantId?: string,