@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.
- package/README.md +3 -1
- package/dist/browser.mjs +19 -78
- package/dist/browser.mjs.map +4 -4
- package/dist/esm/generated/precompiled-validators.js +2324 -1525
- package/dist/esm/generated/precompiled-validators.js.map +1 -1
- package/dist/esm/src/core/constants.js +50 -0
- package/dist/esm/src/core/constants.js.map +1 -1
- package/dist/esm/src/core/dwn-error.js +29 -11
- package/dist/esm/src/core/dwn-error.js.map +1 -1
- package/dist/esm/src/core/encryption-control.js +785 -0
- package/dist/esm/src/core/encryption-control.js.map +1 -0
- package/dist/esm/src/core/message.js +6 -0
- package/dist/esm/src/core/message.js.map +1 -1
- package/dist/esm/src/core/messages-grant-authorization.js +36 -5
- package/dist/esm/src/core/messages-grant-authorization.js.map +1 -1
- package/dist/esm/src/core/protocol-authorization-action.js +3 -12
- package/dist/esm/src/core/protocol-authorization-action.js.map +1 -1
- package/dist/esm/src/core/protocol-authorization-validation.js +31 -30
- package/dist/esm/src/core/protocol-authorization-validation.js.map +1 -1
- package/dist/esm/src/core/protocol-authorization.js +6 -0
- package/dist/esm/src/core/protocol-authorization.js.map +1 -1
- package/dist/esm/src/core/recording-validation-state-reader.js +3 -3
- package/dist/esm/src/core/recording-validation-state-reader.js.map +1 -1
- package/dist/esm/src/core/replication-apply.js +62 -108
- package/dist/esm/src/core/replication-apply.js.map +1 -1
- package/dist/esm/src/core/validation-state-reader.js +5 -6
- package/dist/esm/src/core/validation-state-reader.js.map +1 -1
- package/dist/esm/src/dwn.js +1 -2
- package/dist/esm/src/dwn.js.map +1 -1
- package/dist/esm/src/event-stream/event-emitter-wake-publisher.js +11 -11
- package/dist/esm/src/event-stream/event-emitter-wake-publisher.js.map +1 -1
- package/dist/esm/src/handlers/messages-query.js +6 -16
- package/dist/esm/src/handlers/messages-query.js.map +1 -1
- package/dist/esm/src/handlers/messages-read.js +4 -3
- package/dist/esm/src/handlers/messages-read.js.map +1 -1
- package/dist/esm/src/handlers/messages-subscribe.js +26 -30
- package/dist/esm/src/handlers/messages-subscribe.js.map +1 -1
- package/dist/esm/src/handlers/protocols-configure.js +6 -4
- package/dist/esm/src/handlers/protocols-configure.js.map +1 -1
- package/dist/esm/src/handlers/records-count.js +85 -10
- package/dist/esm/src/handlers/records-count.js.map +1 -1
- package/dist/esm/src/handlers/records-delete.js +11 -0
- package/dist/esm/src/handlers/records-delete.js.map +1 -1
- package/dist/esm/src/handlers/records-query.js +140 -18
- package/dist/esm/src/handlers/records-query.js.map +1 -1
- package/dist/esm/src/handlers/records-read.js +20 -4
- package/dist/esm/src/handlers/records-read.js.map +1 -1
- package/dist/esm/src/handlers/records-subscribe.js +127 -11
- package/dist/esm/src/handlers/records-subscribe.js.map +1 -1
- package/dist/esm/src/handlers/records-write.js +15 -0
- package/dist/esm/src/handlers/records-write.js.map +1 -1
- package/dist/esm/src/index.js +8 -11
- package/dist/esm/src/index.js.map +1 -1
- package/dist/esm/src/interfaces/protocols-configure.js +66 -9
- package/dist/esm/src/interfaces/protocols-configure.js.map +1 -1
- package/dist/esm/src/interfaces/records-write.js +43 -36
- package/dist/esm/src/interfaces/records-write.js.map +1 -1
- package/dist/esm/src/jose/algorithms/signing/ed25519.js +5 -5
- package/dist/esm/src/jose/algorithms/signing/ed25519.js.map +1 -1
- package/dist/esm/src/jose/jws/general/verifier.js +1 -1
- package/dist/esm/src/jose/jws/general/verifier.js.map +1 -1
- package/dist/esm/src/protocols/encryption.js +60 -270
- package/dist/esm/src/protocols/encryption.js.map +1 -1
- package/dist/esm/src/store/blockstore-level.js +14 -23
- package/dist/esm/src/store/blockstore-level.js.map +1 -1
- package/dist/esm/src/store/blockstore-mock.js +10 -27
- package/dist/esm/src/store/blockstore-mock.js.map +1 -1
- package/dist/esm/src/store/blockstore-utils.js +45 -0
- package/dist/esm/src/store/blockstore-utils.js.map +1 -0
- package/dist/esm/src/store/blockstore.js +2 -0
- package/dist/esm/src/store/blockstore.js.map +1 -0
- package/dist/esm/src/store/data-store-level.js +4 -12
- package/dist/esm/src/store/data-store-level.js.map +1 -1
- package/dist/esm/src/store/level.js +7 -0
- package/dist/esm/src/store/level.js.map +1 -0
- package/dist/esm/src/types/encryption-types.js +4 -1
- package/dist/esm/src/types/encryption-types.js.map +1 -1
- package/dist/esm/src/utils/data-stream.js +25 -0
- package/dist/esm/src/utils/data-stream.js.map +1 -1
- package/dist/esm/src/utils/encryption.js +107 -19
- package/dist/esm/src/utils/encryption.js.map +1 -1
- package/dist/esm/src/utils/grant-key-coverage.js +180 -0
- package/dist/esm/src/utils/grant-key-coverage.js.map +1 -0
- package/dist/esm/src/utils/hd-key.js +1 -2
- package/dist/esm/src/utils/hd-key.js.map +1 -1
- package/dist/esm/src/utils/jws.js +7 -1
- package/dist/esm/src/utils/jws.js.map +1 -1
- package/dist/esm/src/utils/memory-cache.js +29 -8
- package/dist/esm/src/utils/memory-cache.js.map +1 -1
- package/dist/esm/src/utils/protocols.js +27 -0
- package/dist/esm/src/utils/protocols.js.map +1 -1
- package/dist/esm/src/utils/record-limit-occupancy.js +23 -3
- package/dist/esm/src/utils/record-limit-occupancy.js.map +1 -1
- package/dist/esm/src/utils/records.js +29 -0
- package/dist/esm/src/utils/records.js.map +1 -1
- package/dist/esm/src/utils/secp256k1.js +12 -11
- package/dist/esm/src/utils/secp256k1.js.map +1 -1
- package/dist/esm/src/utils/secp256r1.js +12 -20
- package/dist/esm/src/utils/secp256r1.js.map +1 -1
- package/dist/esm/src/utils/time.js +103 -25
- package/dist/esm/src/utils/time.js.map +1 -1
- package/dist/esm/tests/core/constants.spec.js +35 -0
- package/dist/esm/tests/core/constants.spec.js.map +1 -0
- package/dist/esm/tests/core/encryption-control.spec.js +100 -0
- package/dist/esm/tests/core/encryption-control.spec.js.map +1 -0
- package/dist/esm/tests/core/message.spec.js +35 -0
- package/dist/esm/tests/core/message.spec.js.map +1 -1
- package/dist/esm/tests/core/protocol-authorization.spec.js +1 -1
- package/dist/esm/tests/core/protocol-authorization.spec.js.map +1 -1
- package/dist/esm/tests/core/replication-apply.spec.js +157 -74
- package/dist/esm/tests/core/replication-apply.spec.js.map +1 -1
- package/dist/esm/tests/core/replication-replay-property.spec.js +2 -1
- package/dist/esm/tests/core/replication-replay-property.spec.js.map +1 -1
- package/dist/esm/tests/core/validation-read-closure.spec.js +14 -28
- package/dist/esm/tests/core/validation-read-closure.spec.js.map +1 -1
- package/dist/esm/tests/core/validation-state-reader.spec.js +19 -23
- package/dist/esm/tests/core/validation-state-reader.spec.js.map +1 -1
- package/dist/esm/tests/dwn.spec.js +2 -1
- package/dist/esm/tests/dwn.spec.js.map +1 -1
- package/dist/esm/tests/event-emitter-wake-publisher.spec.js +51 -0
- package/dist/esm/tests/event-emitter-wake-publisher.spec.js.map +1 -0
- package/dist/esm/tests/features/protocol-composition.spec.js +1 -1
- package/dist/esm/tests/features/protocol-composition.spec.js.map +1 -1
- package/dist/esm/tests/features/records-record-limit.spec.js +1 -1
- package/dist/esm/tests/features/records-record-limit.spec.js.map +1 -1
- package/dist/esm/tests/fuzz/encryption.fuzz.spec.js +9 -1
- package/dist/esm/tests/fuzz/encryption.fuzz.spec.js.map +1 -1
- package/dist/esm/tests/fuzz/process-message.fuzz.spec.js +1 -1
- package/dist/esm/tests/fuzz/process-message.fuzz.spec.js.map +1 -1
- package/dist/esm/tests/fuzz/protocols-utils.fuzz.spec.js +29 -1
- package/dist/esm/tests/fuzz/protocols-utils.fuzz.spec.js.map +1 -1
- package/dist/esm/tests/fuzz/secp256k1.fuzz.spec.js +2 -2
- package/dist/esm/tests/fuzz/secp256k1.fuzz.spec.js.map +1 -1
- package/dist/esm/tests/fuzz/time.fuzz.spec.js +7 -1
- package/dist/esm/tests/fuzz/time.fuzz.spec.js.map +1 -1
- package/dist/esm/tests/handlers/messages-query.spec.js +123 -11
- package/dist/esm/tests/handlers/messages-query.spec.js.map +1 -1
- package/dist/esm/tests/handlers/messages-read.spec.js +95 -0
- package/dist/esm/tests/handlers/messages-read.spec.js.map +1 -1
- package/dist/esm/tests/handlers/messages-subscribe.spec.js +90 -0
- package/dist/esm/tests/handlers/messages-subscribe.spec.js.map +1 -1
- package/dist/esm/tests/handlers/protocols-configure.spec.js +66 -0
- package/dist/esm/tests/handlers/protocols-configure.spec.js.map +1 -1
- package/dist/esm/tests/handlers/records-count.spec.js +370 -0
- package/dist/esm/tests/handlers/records-count.spec.js.map +1 -1
- package/dist/esm/tests/handlers/records-delete.spec.js +70 -1
- package/dist/esm/tests/handlers/records-delete.spec.js.map +1 -1
- package/dist/esm/tests/handlers/records-query.spec.js +562 -1
- package/dist/esm/tests/handlers/records-query.spec.js.map +1 -1
- package/dist/esm/tests/handlers/records-read.spec.js +140 -2
- package/dist/esm/tests/handlers/records-read.spec.js.map +1 -1
- package/dist/esm/tests/handlers/records-subscribe.spec.js +241 -1
- package/dist/esm/tests/handlers/records-subscribe.spec.js.map +1 -1
- package/dist/esm/tests/handlers/records-write.spec.js +1187 -29
- package/dist/esm/tests/handlers/records-write.spec.js.map +1 -1
- package/dist/esm/tests/interfaces/protocols-configure.spec.js +290 -2
- package/dist/esm/tests/interfaces/protocols-configure.spec.js.map +1 -1
- package/dist/esm/tests/interfaces/records-write.spec.js +47 -5
- package/dist/esm/tests/interfaces/records-write.spec.js.map +1 -1
- package/dist/esm/tests/protocols/encryption.spec.js +187 -452
- package/dist/esm/tests/protocols/encryption.spec.js.map +1 -1
- package/dist/esm/tests/scenarios/end-to-end-tests.spec.js +13 -27
- package/dist/esm/tests/scenarios/end-to-end-tests.spec.js.map +1 -1
- package/dist/esm/tests/store/blockstore-level.spec.js +33 -10
- package/dist/esm/tests/store/blockstore-level.spec.js.map +1 -1
- package/dist/esm/tests/store/blockstore-mock.spec.js +31 -7
- package/dist/esm/tests/store/blockstore-mock.spec.js.map +1 -1
- package/dist/esm/tests/store/blockstore-utils.spec.js +115 -0
- package/dist/esm/tests/store/blockstore-utils.spec.js.map +1 -0
- package/dist/esm/tests/store/index-level.spec.js +3 -3
- package/dist/esm/tests/store/index-level.spec.js.map +1 -1
- package/dist/esm/tests/test-stores.js +2 -1
- package/dist/esm/tests/test-stores.js.map +1 -1
- package/dist/esm/tests/utils/cid.spec.js +15 -0
- package/dist/esm/tests/utils/cid.spec.js.map +1 -1
- package/dist/esm/tests/utils/data-stream.spec.js +30 -0
- package/dist/esm/tests/utils/data-stream.spec.js.map +1 -1
- package/dist/esm/tests/utils/encryption-control-test-utils.js +111 -0
- package/dist/esm/tests/utils/encryption-control-test-utils.js.map +1 -0
- package/dist/esm/tests/utils/encryption.spec.js +198 -5
- package/dist/esm/tests/utils/encryption.spec.js.map +1 -1
- package/dist/esm/tests/utils/grant-key-coverage.spec.js +262 -0
- package/dist/esm/tests/utils/grant-key-coverage.spec.js.map +1 -0
- package/dist/esm/tests/utils/memory-cache.spec.js +12 -0
- package/dist/esm/tests/utils/memory-cache.spec.js.map +1 -1
- package/dist/esm/tests/utils/secp256r1.spec.js +2 -2
- package/dist/esm/tests/utils/secp256r1.spec.js.map +1 -1
- package/dist/esm/tests/utils/test-data-generator.js +2 -1
- package/dist/esm/tests/utils/test-data-generator.js.map +1 -1
- package/dist/esm/tests/utils/test-validation-state-reader.js +1 -1
- package/dist/esm/tests/utils/test-validation-state-reader.js.map +1 -1
- package/dist/esm/tests/utils/time.spec.js +26 -0
- package/dist/esm/tests/utils/time.spec.js.map +1 -1
- package/dist/esm/tests/validation/json-schemas/encryption/audience.spec.js +45 -0
- package/dist/esm/tests/validation/json-schemas/encryption/audience.spec.js.map +1 -0
- package/dist/esm/tests/validation/json-schemas/encryption/delivery.spec.js +47 -0
- package/dist/esm/tests/validation/json-schemas/encryption/delivery.spec.js.map +1 -0
- package/dist/esm/tests/validation/json-schemas/encryption/wrapped-grant-key-envelope.spec.js +43 -0
- package/dist/esm/tests/validation/json-schemas/encryption/wrapped-grant-key-envelope.spec.js.map +1 -0
- package/dist/esm/tests/validation/json-schemas/protocols/protocols-configure.spec.js +152 -0
- package/dist/esm/tests/validation/json-schemas/protocols/protocols-configure.spec.js.map +1 -1
- package/dist/esm/tests/validation/json-schemas/records/records-write.spec.js +46 -1
- package/dist/esm/tests/validation/json-schemas/records/records-write.spec.js.map +1 -1
- package/dist/types/generated/precompiled-validators.d.ts +20 -4
- package/dist/types/generated/precompiled-validators.d.ts.map +1 -1
- package/dist/types/src/core/constants.d.ts +39 -0
- package/dist/types/src/core/constants.d.ts.map +1 -1
- package/dist/types/src/core/dwn-error.d.ts +29 -11
- package/dist/types/src/core/dwn-error.d.ts.map +1 -1
- package/dist/types/src/core/encryption-control.d.ts +128 -0
- package/dist/types/src/core/encryption-control.d.ts.map +1 -0
- package/dist/types/src/core/message.d.ts +4 -0
- package/dist/types/src/core/message.d.ts.map +1 -1
- package/dist/types/src/core/messages-grant-authorization.d.ts +11 -0
- package/dist/types/src/core/messages-grant-authorization.d.ts.map +1 -1
- package/dist/types/src/core/protocol-authorization-action.d.ts.map +1 -1
- package/dist/types/src/core/protocol-authorization-validation.d.ts.map +1 -1
- package/dist/types/src/core/protocol-authorization.d.ts.map +1 -1
- package/dist/types/src/core/recording-validation-state-reader.d.ts +2 -3
- package/dist/types/src/core/recording-validation-state-reader.d.ts.map +1 -1
- package/dist/types/src/core/replication-apply.d.ts +10 -1
- package/dist/types/src/core/replication-apply.d.ts.map +1 -1
- package/dist/types/src/core/validation-state-reader.d.ts +2 -3
- package/dist/types/src/core/validation-state-reader.d.ts.map +1 -1
- package/dist/types/src/dwn.d.ts.map +1 -1
- package/dist/types/src/event-stream/event-emitter-wake-publisher.d.ts +1 -1
- package/dist/types/src/event-stream/event-emitter-wake-publisher.d.ts.map +1 -1
- package/dist/types/src/handlers/messages-query.d.ts.map +1 -1
- package/dist/types/src/handlers/messages-read.d.ts.map +1 -1
- package/dist/types/src/handlers/messages-subscribe.d.ts.map +1 -1
- package/dist/types/src/handlers/protocols-configure.d.ts +1 -0
- package/dist/types/src/handlers/protocols-configure.d.ts.map +1 -1
- package/dist/types/src/handlers/records-count.d.ts +2 -0
- package/dist/types/src/handlers/records-count.d.ts.map +1 -1
- package/dist/types/src/handlers/records-delete.d.ts.map +1 -1
- package/dist/types/src/handlers/records-query.d.ts +3 -0
- package/dist/types/src/handlers/records-query.d.ts.map +1 -1
- package/dist/types/src/handlers/records-read.d.ts.map +1 -1
- package/dist/types/src/handlers/records-subscribe.d.ts +2 -0
- package/dist/types/src/handlers/records-subscribe.d.ts.map +1 -1
- package/dist/types/src/handlers/records-write.d.ts.map +1 -1
- package/dist/types/src/index.d.ts +12 -18
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/interfaces/protocols-configure.d.ts +13 -1
- package/dist/types/src/interfaces/protocols-configure.d.ts.map +1 -1
- package/dist/types/src/interfaces/records-write.d.ts +5 -1
- package/dist/types/src/interfaces/records-write.d.ts.map +1 -1
- package/dist/types/src/jose/algorithms/signing/ed25519.d.ts.map +1 -1
- package/dist/types/src/protocols/encryption.d.ts +15 -19
- package/dist/types/src/protocols/encryption.d.ts.map +1 -1
- package/dist/types/src/store/blockstore-level.d.ts +11 -11
- package/dist/types/src/store/blockstore-level.d.ts.map +1 -1
- package/dist/types/src/store/blockstore-mock.d.ts +12 -12
- package/dist/types/src/store/blockstore-mock.d.ts.map +1 -1
- package/dist/types/src/store/blockstore-utils.d.ts +23 -0
- package/dist/types/src/store/blockstore-utils.d.ts.map +1 -0
- package/dist/types/src/store/blockstore.d.ts +3 -0
- package/dist/types/src/store/blockstore.d.ts.map +1 -0
- package/dist/types/src/store/data-store-level.d.ts.map +1 -1
- package/dist/types/src/store/level.d.ts +13 -0
- package/dist/types/src/store/level.d.ts.map +1 -0
- package/dist/types/src/types/cache.d.ts +2 -2
- package/dist/types/src/types/cache.d.ts.map +1 -1
- package/dist/types/src/types/encryption-types.d.ts +31 -2
- package/dist/types/src/types/encryption-types.d.ts.map +1 -1
- package/dist/types/src/types/validation-state-reader.d.ts +3 -5
- package/dist/types/src/types/validation-state-reader.d.ts.map +1 -1
- package/dist/types/src/utils/data-stream.d.ts +5 -0
- package/dist/types/src/utils/data-stream.d.ts.map +1 -1
- package/dist/types/src/utils/encryption.d.ts +53 -24
- package/dist/types/src/utils/encryption.d.ts.map +1 -1
- package/dist/types/src/utils/grant-key-coverage.d.ts +36 -0
- package/dist/types/src/utils/grant-key-coverage.d.ts.map +1 -0
- package/dist/types/src/utils/hd-key.d.ts.map +1 -1
- package/dist/types/src/utils/jws.d.ts.map +1 -1
- package/dist/types/src/utils/memory-cache.d.ts +4 -2
- package/dist/types/src/utils/memory-cache.d.ts.map +1 -1
- package/dist/types/src/utils/protocols.d.ts +11 -0
- package/dist/types/src/utils/protocols.d.ts.map +1 -1
- package/dist/types/src/utils/record-limit-occupancy.d.ts +7 -0
- package/dist/types/src/utils/record-limit-occupancy.d.ts.map +1 -1
- package/dist/types/src/utils/records.d.ts +2 -0
- package/dist/types/src/utils/records.d.ts.map +1 -1
- package/dist/types/src/utils/secp256k1.d.ts.map +1 -1
- package/dist/types/src/utils/secp256r1.d.ts.map +1 -1
- package/dist/types/src/utils/time.d.ts +21 -13
- package/dist/types/src/utils/time.d.ts.map +1 -1
- package/dist/types/tests/core/constants.spec.d.ts +2 -0
- package/dist/types/tests/core/constants.spec.d.ts.map +1 -0
- package/dist/types/tests/core/encryption-control.spec.d.ts +2 -0
- package/dist/types/tests/core/encryption-control.spec.d.ts.map +1 -0
- package/dist/types/tests/dwn.spec.d.ts.map +1 -1
- package/dist/types/tests/event-emitter-wake-publisher.spec.d.ts +2 -0
- package/dist/types/tests/event-emitter-wake-publisher.spec.d.ts.map +1 -0
- package/dist/types/tests/handlers/messages-query.spec.d.ts.map +1 -1
- package/dist/types/tests/handlers/messages-read.spec.d.ts.map +1 -1
- package/dist/types/tests/handlers/messages-subscribe.spec.d.ts.map +1 -1
- package/dist/types/tests/handlers/protocols-configure.spec.d.ts.map +1 -1
- package/dist/types/tests/handlers/records-count.spec.d.ts.map +1 -1
- package/dist/types/tests/handlers/records-delete.spec.d.ts.map +1 -1
- package/dist/types/tests/handlers/records-query.spec.d.ts.map +1 -1
- package/dist/types/tests/handlers/records-read.spec.d.ts.map +1 -1
- package/dist/types/tests/handlers/records-subscribe.spec.d.ts.map +1 -1
- package/dist/types/tests/handlers/records-write.spec.d.ts.map +1 -1
- package/dist/types/tests/scenarios/end-to-end-tests.spec.d.ts.map +1 -1
- package/dist/types/tests/store/blockstore-utils.spec.d.ts +2 -0
- package/dist/types/tests/store/blockstore-utils.spec.d.ts.map +1 -0
- package/dist/types/tests/test-stores.d.ts.map +1 -1
- package/dist/types/tests/utils/encryption-control-test-utils.d.ts +46 -0
- package/dist/types/tests/utils/encryption-control-test-utils.d.ts.map +1 -0
- package/dist/types/tests/utils/grant-key-coverage.spec.d.ts +2 -0
- package/dist/types/tests/utils/grant-key-coverage.spec.d.ts.map +1 -0
- package/dist/types/tests/utils/test-data-generator.d.ts.map +1 -1
- package/dist/types/tests/validation/json-schemas/encryption/audience.spec.d.ts +2 -0
- package/dist/types/tests/validation/json-schemas/encryption/audience.spec.d.ts.map +1 -0
- package/dist/types/tests/validation/json-schemas/encryption/delivery.spec.d.ts +2 -0
- package/dist/types/tests/validation/json-schemas/encryption/delivery.spec.d.ts.map +1 -0
- package/dist/types/tests/validation/json-schemas/encryption/wrapped-grant-key-envelope.spec.d.ts +2 -0
- package/dist/types/tests/validation/json-schemas/encryption/wrapped-grant-key-envelope.spec.d.ts.map +1 -0
- package/package.json +47 -28
- package/src/core/constants.ts +61 -0
- package/src/core/dwn-error.ts +29 -11
- package/src/core/encryption-control.ts +1249 -0
- package/src/core/message.ts +7 -0
- package/src/core/messages-grant-authorization.ts +60 -9
- package/src/core/protocol-authorization-action.ts +3 -13
- package/src/core/protocol-authorization-validation.ts +66 -46
- package/src/core/protocol-authorization.ts +7 -0
- package/src/core/recording-validation-state-reader.ts +4 -5
- package/src/core/replication-apply.ts +77 -133
- package/src/core/validation-state-reader.ts +6 -8
- package/src/dwn.ts +1 -2
- package/src/event-stream/event-emitter-wake-publisher.ts +10 -14
- package/src/handlers/messages-query.ts +6 -22
- package/src/handlers/messages-read.ts +4 -3
- package/src/handlers/messages-subscribe.ts +34 -30
- package/src/handlers/protocols-configure.ts +7 -4
- package/src/handlers/records-count.ts +111 -10
- package/src/handlers/records-delete.ts +15 -0
- package/src/handlers/records-query.ts +176 -20
- package/src/handlers/records-read.ts +19 -6
- package/src/handlers/records-subscribe.ts +158 -8
- package/src/handlers/records-write.ts +19 -0
- package/src/index.ts +50 -22
- package/src/interfaces/protocols-configure.ts +102 -11
- package/src/interfaces/records-write.ts +49 -43
- package/src/jose/algorithms/signing/ed25519.ts +7 -6
- package/src/jose/jws/general/verifier.ts +3 -3
- package/src/protocols/encryption.ts +94 -411
- package/src/store/blockstore-level.ts +33 -37
- package/src/store/blockstore-mock.ts +24 -38
- package/src/store/blockstore-utils.ts +85 -0
- package/src/store/blockstore.ts +8 -0
- package/src/store/data-store-level.ts +5 -12
- package/src/store/level.ts +19 -0
- package/src/types/cache.ts +3 -3
- package/src/types/encryption-types.ts +39 -2
- package/src/types/validation-state-reader.ts +3 -5
- package/src/utils/data-stream.ts +29 -1
- package/src/utils/encryption.ts +190 -60
- package/src/utils/grant-key-coverage.ts +303 -0
- package/src/utils/hd-key.ts +1 -2
- package/src/utils/jws.ts +8 -2
- package/src/utils/memory-cache.ts +42 -11
- package/src/utils/protocols.ts +32 -0
- package/src/utils/record-limit-occupancy.ts +34 -5
- package/src/utils/records.ts +36 -2
- package/src/utils/secp256k1.ts +12 -12
- package/src/utils/secp256r1.ts +12 -22
- package/src/utils/time.ts +145 -27
- package/dist/esm/tests/validation/json-schemas/encryption/audience-key.spec.js +0 -40
- package/dist/esm/tests/validation/json-schemas/encryption/audience-key.spec.js.map +0 -1
- package/dist/types/tests/validation/json-schemas/encryption/audience-key.spec.d.ts +0 -2
- package/dist/types/tests/validation/json-schemas/encryption/audience-key.spec.d.ts.map +0 -1
- package/src/types/mitt.d.ts +0 -28
|
@@ -0,0 +1,785 @@
|
|
|
1
|
+
import { checkActor } from './protocol-authorization-action.js';
|
|
2
|
+
import { DwnConstant } from './dwn-constant.js';
|
|
3
|
+
import { Encoder } from '../utils/encoder.js';
|
|
4
|
+
import { Encryption } from '../utils/encryption.js';
|
|
5
|
+
import { EncryptionControlDeliveryRecipientAuthority } from '../types/encryption-types.js';
|
|
6
|
+
import { GrantAuthorization } from './grant-authorization.js';
|
|
7
|
+
import { Jws } from '../utils/jws.js';
|
|
8
|
+
import { lexicographicalCompare } from '../utils/string.js';
|
|
9
|
+
import { Message } from './message.js';
|
|
10
|
+
import { PermissionConditionPublication } from '../types/permission-types.js';
|
|
11
|
+
import { PermissionGrant } from '../protocols/permission-grant.js';
|
|
12
|
+
import { PermissionScopeMatcher } from '../utils/permission-scope.js';
|
|
13
|
+
import { Records } from '../utils/records.js';
|
|
14
|
+
import { selectOccupantRecordIds } from '../utils/record-limit-occupancy.js';
|
|
15
|
+
import { SortDirection } from '../types/query-types.js';
|
|
16
|
+
import { validateJsonSchema } from '../schema-validator.js';
|
|
17
|
+
import { DwnError, DwnErrorCode } from './dwn-error.js';
|
|
18
|
+
import { DwnInterfaceName, DwnMethodName } from '../enums/dwn-interface-method.js';
|
|
19
|
+
import { ENCRYPTION_CONTROL_AUDIENCE_PATH, ENCRYPTION_CONTROL_DELIVERY_PATH, isEncryptionControlPath } from './constants.js';
|
|
20
|
+
import { getRoleContextPrefix, getRuleSetAtPath, isCrossProtocolRef, parseCrossProtocolRef } from '../utils/protocols.js';
|
|
21
|
+
import { grantKeyScopeCoversDeliveredScope, isGrantKeyEligibleRecordsScope } from '../utils/grant-key-coverage.js';
|
|
22
|
+
import { ProtocolAction, ProtocolActor } from '../types/protocols-types.js';
|
|
23
|
+
export class EncryptionControl {
|
|
24
|
+
static isControlMessage(message) {
|
|
25
|
+
return isEncryptionControlPath(message.descriptor.protocolPath);
|
|
26
|
+
}
|
|
27
|
+
static isAudienceControlMessage(message) {
|
|
28
|
+
return message.descriptor.protocolPath === ENCRYPTION_CONTROL_AUDIENCE_PATH;
|
|
29
|
+
}
|
|
30
|
+
static isExactAudienceFilter(filter) {
|
|
31
|
+
return EncryptionControl.getExactAudienceFilterTuple(filter) !== undefined;
|
|
32
|
+
}
|
|
33
|
+
static filterTargetsOnlyControlRecords(filter) {
|
|
34
|
+
return typeof filter.protocolPath === 'string' && isEncryptionControlPath(filter.protocolPath);
|
|
35
|
+
}
|
|
36
|
+
static async authorizeControlReadRequest(input) {
|
|
37
|
+
if (input.requester === undefined || input.requester === input.tenant) {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
await EncryptionControl.getInvokedReadGrants({
|
|
41
|
+
actorDid: input.requester,
|
|
42
|
+
incomingMessage: input.incomingMessage,
|
|
43
|
+
tenant: input.tenant,
|
|
44
|
+
validationStateReader: input.validationStateReader,
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
static buildAudienceRecordFilters(filters) {
|
|
48
|
+
const audienceFilters = [];
|
|
49
|
+
for (const filter of filters) {
|
|
50
|
+
const protocolPath = filter.protocolPath;
|
|
51
|
+
if (protocolPath === undefined) {
|
|
52
|
+
audienceFilters.push({ ...filter, protocolPath: ENCRYPTION_CONTROL_AUDIENCE_PATH });
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
if (typeof protocolPath === 'string') {
|
|
56
|
+
if (protocolPath === ENCRYPTION_CONTROL_AUDIENCE_PATH) {
|
|
57
|
+
audienceFilters.push(filter);
|
|
58
|
+
}
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
if (Array.isArray(protocolPath) && protocolPath.includes(ENCRYPTION_CONTROL_AUDIENCE_PATH)) {
|
|
62
|
+
audienceFilters.push({ ...filter, protocolPath: ENCRYPTION_CONTROL_AUDIENCE_PATH });
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return audienceFilters;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Resolves the current audience record for a role-audience tuple.
|
|
69
|
+
*
|
|
70
|
+
* Current-key projection orders stored audience records by:
|
|
71
|
+
* tenant-authored actual signer first, then oldest `dateCreated`, then `recordId` ascending.
|
|
72
|
+
*/
|
|
73
|
+
static async resolveCurrentAudienceRecord(input) {
|
|
74
|
+
const records = await EncryptionControl.queryStoredAudienceRecordsForTuple(input);
|
|
75
|
+
const currentRecordIds = selectOccupantRecordIds({
|
|
76
|
+
records,
|
|
77
|
+
max: 1,
|
|
78
|
+
getScopeKey: (record) => EncryptionControl.getAudienceTupleKey(EncryptionControl.getRoleAudienceKeyId(record, 'audience')),
|
|
79
|
+
compareRecords: (left, right) => EncryptionControl.compareAudienceProjectionCandidates(input.tenant, left, right),
|
|
80
|
+
});
|
|
81
|
+
const currentRecordId = currentRecordIds.values().next().value;
|
|
82
|
+
return records.find((record) => record.recordId === currentRecordId);
|
|
83
|
+
}
|
|
84
|
+
static async projectCurrentAudienceRecords(input) {
|
|
85
|
+
const currentRecordIds = new Set();
|
|
86
|
+
const tupleKeys = new Set();
|
|
87
|
+
for (const record of input.recordsWriteMessages) {
|
|
88
|
+
if (!EncryptionControl.shouldProjectAudienceRecord(record, input.bypassFilters ?? [])) {
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
const tags = EncryptionControl.getRoleAudienceKeyId(record, 'audience');
|
|
92
|
+
tupleKeys.add(EncryptionControl.getAudienceTupleKey(tags));
|
|
93
|
+
}
|
|
94
|
+
for (const tupleKey of tupleKeys) {
|
|
95
|
+
const tuple = EncryptionControl.parseAudienceTupleKey(tupleKey);
|
|
96
|
+
const currentRecordId = await EncryptionControl.resolveCurrentAudienceRecordId({
|
|
97
|
+
...tuple,
|
|
98
|
+
currentAudienceRecordIdCache: input.currentAudienceRecordIdCache,
|
|
99
|
+
messageStore: input.messageStore,
|
|
100
|
+
tenant: input.tenant,
|
|
101
|
+
});
|
|
102
|
+
if (currentRecordId !== undefined) {
|
|
103
|
+
currentRecordIds.add(currentRecordId);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return input.recordsWriteMessages.filter((record) => !EncryptionControl.shouldProjectAudienceRecord(record, input.bypassFilters ?? []) ||
|
|
107
|
+
currentRecordIds.has(record.recordId));
|
|
108
|
+
}
|
|
109
|
+
static async projectCurrentAudienceRecordPage(input) {
|
|
110
|
+
return {
|
|
111
|
+
messages: await EncryptionControl.projectCurrentAudienceRecords({
|
|
112
|
+
currentAudienceRecordIdCache: input.currentAudienceRecordIdCache,
|
|
113
|
+
messageStore: input.messageStore,
|
|
114
|
+
tenant: input.tenant,
|
|
115
|
+
recordsWriteMessages: input.result.messages,
|
|
116
|
+
bypassFilters: input.filters,
|
|
117
|
+
}),
|
|
118
|
+
cursor: input.result.cursor,
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
static async filterVisibleControlRecords(input) {
|
|
122
|
+
const visibleRecordsWrites = [];
|
|
123
|
+
for (const recordsWrite of input.recordsWriteMessages) {
|
|
124
|
+
const visible = await EncryptionControl.isVisibleControlRecord({
|
|
125
|
+
incomingMessage: input.incomingMessage,
|
|
126
|
+
recordsWriteMessage: recordsWrite,
|
|
127
|
+
requester: input.requester,
|
|
128
|
+
tenant: input.tenant,
|
|
129
|
+
validationStateReader: input.validationStateReader,
|
|
130
|
+
});
|
|
131
|
+
if (visible) {
|
|
132
|
+
visibleRecordsWrites.push(recordsWrite);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
return visibleRecordsWrites;
|
|
136
|
+
}
|
|
137
|
+
static async authorizeRead(input) {
|
|
138
|
+
if (await EncryptionControl.canRead(input)) {
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
throw new DwnError(DwnErrorCode.EncryptionControlReadUnauthorized, 'requester is not authorized to read the encryption control record.');
|
|
142
|
+
}
|
|
143
|
+
static async canRead(input) {
|
|
144
|
+
const { requester, recordsWriteMessage, tenant } = input;
|
|
145
|
+
if (!EncryptionControl.isControlMessage(recordsWriteMessage)) {
|
|
146
|
+
return true;
|
|
147
|
+
}
|
|
148
|
+
if (requester === tenant) {
|
|
149
|
+
return true;
|
|
150
|
+
}
|
|
151
|
+
if (requester === undefined) {
|
|
152
|
+
return false;
|
|
153
|
+
}
|
|
154
|
+
const recordType = EncryptionControl.getRecordType(recordsWriteMessage);
|
|
155
|
+
if (recordType === 'delivery') {
|
|
156
|
+
if (requester === recordsWriteMessage.descriptor.recipient ||
|
|
157
|
+
requester === Message.getAuthor(recordsWriteMessage)) {
|
|
158
|
+
return true;
|
|
159
|
+
}
|
|
160
|
+
return EncryptionControl.canReadDeliveryByDelegatedGrant({
|
|
161
|
+
tenant,
|
|
162
|
+
incomingMessage: input.incomingMessage,
|
|
163
|
+
requester,
|
|
164
|
+
recordsWriteMessage,
|
|
165
|
+
tags: EncryptionControl.getDeliveryTags(recordsWriteMessage),
|
|
166
|
+
validationStateReader: input.validationStateReader,
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
const tags = EncryptionControl.getRoleAudienceKeyId(recordsWriteMessage, 'audience');
|
|
170
|
+
if (EncryptionControl.exactAudienceRequestMatchesRecord(input.incomingMessage, tags, recordsWriteMessage.recordId)) {
|
|
171
|
+
return true;
|
|
172
|
+
}
|
|
173
|
+
return EncryptionControl.canEnumerateAudience({
|
|
174
|
+
tenant,
|
|
175
|
+
actorDid: requester,
|
|
176
|
+
incomingMessage: input.incomingMessage,
|
|
177
|
+
tags,
|
|
178
|
+
validationStateReader: input.validationStateReader,
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
static async authorizeWrite(tenant, recordsWrite, validationStateReader) {
|
|
182
|
+
const recordType = EncryptionControl.getRecordType(recordsWrite.message);
|
|
183
|
+
const tags = EncryptionControl.getRoleAudienceKeyId(recordsWrite.message, recordType);
|
|
184
|
+
const { protocolDefinition, ruleSet } = await EncryptionControl.verifyRoleAudienceDefinition(tenant, tags, recordsWrite.message, validationStateReader, recordType);
|
|
185
|
+
const actor = await EncryptionControl.resolveEffectiveControlActor(tenant, recordsWrite, validationStateReader);
|
|
186
|
+
await EncryptionControl.verifyActorCanCreateRole({
|
|
187
|
+
tenant,
|
|
188
|
+
actor,
|
|
189
|
+
tags,
|
|
190
|
+
protocolDefinition,
|
|
191
|
+
ruleSet,
|
|
192
|
+
validationStateReader,
|
|
193
|
+
message: recordsWrite.message,
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
static async validateReferentialIntegrity(tenant, recordsWrite, validationStateReader) {
|
|
197
|
+
if (!await recordsWrite.isInitialWrite()) {
|
|
198
|
+
throw new DwnError(DwnErrorCode.EncryptionControlValidateUnexpectedRecord, 'encryption control records are immutable.');
|
|
199
|
+
}
|
|
200
|
+
EncryptionControl.verifyInlineControlRecord(recordsWrite.message);
|
|
201
|
+
const recordType = EncryptionControl.getRecordType(recordsWrite.message);
|
|
202
|
+
if (recordType === 'audience') {
|
|
203
|
+
const tags = EncryptionControl.getRoleAudienceKeyId(recordsWrite.message, 'audience');
|
|
204
|
+
await EncryptionControl.verifyRoleAudienceDefinition(tenant, tags, recordsWrite.message, validationStateReader, 'audience');
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
const tags = EncryptionControl.getDeliveryTags(recordsWrite.message);
|
|
208
|
+
await EncryptionControl.verifyRoleAudienceDefinition(tenant, tags, recordsWrite.message, validationStateReader, 'delivery');
|
|
209
|
+
const audiences = await validationStateReader.queryAudienceRecords({
|
|
210
|
+
tenant,
|
|
211
|
+
protocol: tags.protocol,
|
|
212
|
+
rolePath: tags.rolePath,
|
|
213
|
+
contextId: tags.contextId,
|
|
214
|
+
keyId: tags.keyId,
|
|
215
|
+
});
|
|
216
|
+
if (audiences.length === 0) {
|
|
217
|
+
throw new DwnError(DwnErrorCode.EncryptionControlValidateDeliveryAudienceMissing, 'delivery control record references a missing audience.');
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
static async preProcessWrite(tenant, message, validationStateReader) {
|
|
221
|
+
EncryptionControl.verifyInlineControlRecord(message);
|
|
222
|
+
switch (message.descriptor.protocolPath) {
|
|
223
|
+
case ENCRYPTION_CONTROL_AUDIENCE_PATH:
|
|
224
|
+
await EncryptionControl.preProcessAudience(tenant, message, validationStateReader);
|
|
225
|
+
return;
|
|
226
|
+
case ENCRYPTION_CONTROL_DELIVERY_PATH:
|
|
227
|
+
await EncryptionControl.preProcessDelivery(tenant, message, validationStateReader);
|
|
228
|
+
return;
|
|
229
|
+
default:
|
|
230
|
+
throw new DwnError(DwnErrorCode.EncryptionControlValidateUnexpectedRecord, `unexpected encryption control path '${message.descriptor.protocolPath}'.`);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
static async validateRecord(input) {
|
|
234
|
+
const { tenant, message, dataBytes, validationStateReader } = input;
|
|
235
|
+
switch (message.descriptor.protocolPath) {
|
|
236
|
+
case ENCRYPTION_CONTROL_AUDIENCE_PATH:
|
|
237
|
+
await EncryptionControl.validateAudience(tenant, message, dataBytes, validationStateReader);
|
|
238
|
+
return;
|
|
239
|
+
case ENCRYPTION_CONTROL_DELIVERY_PATH:
|
|
240
|
+
EncryptionControl.verifyEncryptedDelivery(message);
|
|
241
|
+
return;
|
|
242
|
+
default:
|
|
243
|
+
throw new DwnError(DwnErrorCode.EncryptionControlValidateUnexpectedRecord, `unexpected encryption control path '${message.descriptor.protocolPath}'.`);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
static mapErrorToStatusCode(errorCode) {
|
|
247
|
+
if (errorCode.startsWith('EncryptionControlValidate')) {
|
|
248
|
+
return 400;
|
|
249
|
+
}
|
|
250
|
+
return undefined;
|
|
251
|
+
}
|
|
252
|
+
static async preProcessAudience(tenant, message, validationStateReader) {
|
|
253
|
+
if (message.encryption !== undefined) {
|
|
254
|
+
throw new DwnError(DwnErrorCode.EncryptionControlValidateUnexpectedRecord, 'audience control records must be plaintext.');
|
|
255
|
+
}
|
|
256
|
+
const tags = EncryptionControl.getRoleAudienceKeyId(message, 'audience');
|
|
257
|
+
await EncryptionControl.verifyRoleAudienceDefinition(tenant, tags, message, validationStateReader, 'audience');
|
|
258
|
+
}
|
|
259
|
+
static async preProcessDelivery(tenant, message, validationStateReader) {
|
|
260
|
+
EncryptionControl.verifyEncryptedDelivery(message);
|
|
261
|
+
if (message.descriptor.recipient === undefined) {
|
|
262
|
+
throw new DwnError(DwnErrorCode.EncryptionControlValidateDeliveryRecipientMissing, 'delivery control records must have a recipient.');
|
|
263
|
+
}
|
|
264
|
+
const tags = EncryptionControl.getDeliveryTags(message);
|
|
265
|
+
await EncryptionControl.verifyRoleAudienceDefinition(tenant, tags, message, validationStateReader, 'delivery');
|
|
266
|
+
const audiences = await validationStateReader.queryAudienceRecords({
|
|
267
|
+
tenant,
|
|
268
|
+
protocol: tags.protocol,
|
|
269
|
+
rolePath: tags.rolePath,
|
|
270
|
+
contextId: tags.contextId,
|
|
271
|
+
keyId: tags.keyId,
|
|
272
|
+
});
|
|
273
|
+
if (audiences.length === 0) {
|
|
274
|
+
throw new DwnError(DwnErrorCode.EncryptionControlValidateDeliveryAudienceMissing, 'delivery control record references a missing audience.');
|
|
275
|
+
}
|
|
276
|
+
await EncryptionControl.verifyDeliveryRecipientAuthority({
|
|
277
|
+
tenant,
|
|
278
|
+
message,
|
|
279
|
+
tags,
|
|
280
|
+
validationStateReader,
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
static async validateAudience(tenant, message, dataBytes, validationStateReader) {
|
|
284
|
+
if (message.encryption !== undefined) {
|
|
285
|
+
throw new DwnError(DwnErrorCode.EncryptionControlValidateUnexpectedRecord, 'audience control records must be plaintext.');
|
|
286
|
+
}
|
|
287
|
+
const dataObject = JSON.parse(Encoder.bytesToString(dataBytes));
|
|
288
|
+
validateJsonSchema('Audience', dataObject);
|
|
289
|
+
const tags = EncryptionControl.getRoleAudienceKeyId(message, 'audience');
|
|
290
|
+
EncryptionControl.verifyAudiencePayloadMatchesTags(dataObject, tags);
|
|
291
|
+
const audienceKeyId = await Encryption.getKeyId(dataObject.publicKeyJwk);
|
|
292
|
+
if (audienceKeyId !== dataObject.keyId) {
|
|
293
|
+
throw new DwnError(DwnErrorCode.EncryptionControlValidateAudienceKeyIdMismatch, 'audience keyId must match publicKeyJwk thumbprint.');
|
|
294
|
+
}
|
|
295
|
+
const { ruleSet } = await EncryptionControl.verifyRoleAudienceDefinition(tenant, tags, message, validationStateReader, 'audience');
|
|
296
|
+
const sealingKeyId = await Encryption.getKeyId(ruleSet.$keyAgreement.publicKeyJwk);
|
|
297
|
+
if (Object.is(sealingKeyId, dataObject.sealedPrivateKey.keyId)) {
|
|
298
|
+
return;
|
|
299
|
+
}
|
|
300
|
+
throw new DwnError(DwnErrorCode.EncryptionControlValidateAudienceSealKeyIdMismatch, 'audience seal keyId must match the role path $keyAgreement public key thumbprint.');
|
|
301
|
+
}
|
|
302
|
+
static verifyEncryptedDelivery(message) {
|
|
303
|
+
if (message.encryption === undefined) {
|
|
304
|
+
throw new DwnError(DwnErrorCode.EncryptionControlValidateUnexpectedRecord, 'delivery control records must be encrypted.');
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
static verifyInlineControlRecord(message) {
|
|
308
|
+
if (message.descriptor.published === true) {
|
|
309
|
+
throw new DwnError(DwnErrorCode.EncryptionControlValidateUnexpectedRecord, 'encryption control records must not be published.');
|
|
310
|
+
}
|
|
311
|
+
if (message.descriptor.dataSize > DwnConstant.maxDataSizeAllowedToBeEncoded) {
|
|
312
|
+
throw new DwnError(DwnErrorCode.EncryptionControlValidateUnexpectedRecord, 'encryption control records must be small enough for inline validation.');
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
static verifyAudiencePayloadMatchesTags(payload, tags) {
|
|
316
|
+
if (payload.protocol !== tags.protocol ||
|
|
317
|
+
payload.rolePath !== tags.rolePath ||
|
|
318
|
+
payload.contextId !== tags.contextId ||
|
|
319
|
+
payload.keyId !== tags.keyId) {
|
|
320
|
+
throw new DwnError(DwnErrorCode.EncryptionControlValidateAudienceTagsMismatch, 'audience tags must match payload fields.');
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
static async verifyRoleAudienceDefinition(tenant, tags, message, validationStateReader, recordType) {
|
|
324
|
+
if (tags.protocol !== message.descriptor.protocol) {
|
|
325
|
+
const errorCode = recordType === 'audience'
|
|
326
|
+
? DwnErrorCode.EncryptionControlValidateAudienceTagsMismatch
|
|
327
|
+
: DwnErrorCode.EncryptionControlValidateDeliveryTagsMismatch;
|
|
328
|
+
throw new DwnError(errorCode, 'control record protocol tag must match descriptor protocol.');
|
|
329
|
+
}
|
|
330
|
+
return EncryptionControl.resolveRoleAudienceDefinition({
|
|
331
|
+
tenant,
|
|
332
|
+
tags,
|
|
333
|
+
messageTimestamp: message.descriptor.messageTimestamp,
|
|
334
|
+
validationStateReader,
|
|
335
|
+
});
|
|
336
|
+
}
|
|
337
|
+
static verifyRoleAudienceContext(rolePath, contextId) {
|
|
338
|
+
const parentDepth = rolePath.split('/').length - 1;
|
|
339
|
+
if ((parentDepth === 0 && contextId !== '') || (parentDepth > 0 && contextId === '')) {
|
|
340
|
+
throw new DwnError(DwnErrorCode.EncryptionControlValidateAudienceContextIdInvalid, 'role audience contextId is inconsistent with role path depth.');
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
static async resolveEffectiveControlActor(tenant, recordsWrite, validationStateReader) {
|
|
344
|
+
if (recordsWrite.isSignedByAuthorDelegate) {
|
|
345
|
+
const grant = PermissionGrant.parse(recordsWrite.message.authorization.authorDelegatedGrant);
|
|
346
|
+
await GrantAuthorization.performBaseValidation({
|
|
347
|
+
incomingMessage: recordsWrite.message,
|
|
348
|
+
expectedGrantor: recordsWrite.author,
|
|
349
|
+
expectedGrantee: recordsWrite.signer,
|
|
350
|
+
permissionGrant: grant,
|
|
351
|
+
validationStateReader,
|
|
352
|
+
});
|
|
353
|
+
EncryptionControl.verifyGrantConditions(recordsWrite.message, grant);
|
|
354
|
+
return { did: recordsWrite.signer, grant };
|
|
355
|
+
}
|
|
356
|
+
if (recordsWrite.isSignedByOwnerDelegate) {
|
|
357
|
+
const grant = PermissionGrant.parse(recordsWrite.message.authorization.ownerDelegatedGrant);
|
|
358
|
+
await GrantAuthorization.performBaseValidation({
|
|
359
|
+
incomingMessage: recordsWrite.message,
|
|
360
|
+
expectedGrantor: recordsWrite.owner,
|
|
361
|
+
expectedGrantee: recordsWrite.ownerSignatureSigner,
|
|
362
|
+
permissionGrant: grant,
|
|
363
|
+
validationStateReader,
|
|
364
|
+
});
|
|
365
|
+
EncryptionControl.verifyGrantConditions(recordsWrite.message, grant);
|
|
366
|
+
return { did: recordsWrite.ownerSignatureSigner, grant };
|
|
367
|
+
}
|
|
368
|
+
if (recordsWrite.owner !== undefined) {
|
|
369
|
+
return { did: recordsWrite.owner };
|
|
370
|
+
}
|
|
371
|
+
const directGrantId = recordsWrite.signaturePayload === undefined
|
|
372
|
+
? undefined
|
|
373
|
+
: Message.getPermissionGrantId(recordsWrite.signaturePayload);
|
|
374
|
+
if (directGrantId === undefined) {
|
|
375
|
+
return { did: recordsWrite.author };
|
|
376
|
+
}
|
|
377
|
+
const grant = await validationStateReader.fetchGrant(tenant, directGrantId);
|
|
378
|
+
await GrantAuthorization.performBaseValidation({
|
|
379
|
+
incomingMessage: recordsWrite.message,
|
|
380
|
+
expectedGrantor: tenant,
|
|
381
|
+
expectedGrantee: recordsWrite.author,
|
|
382
|
+
permissionGrant: grant,
|
|
383
|
+
validationStateReader,
|
|
384
|
+
});
|
|
385
|
+
EncryptionControl.verifyGrantConditions(recordsWrite.message, grant);
|
|
386
|
+
return { did: recordsWrite.author, grant };
|
|
387
|
+
}
|
|
388
|
+
static async verifyActorCanCreateRole(input) {
|
|
389
|
+
const { tenant, actor, tags, protocolDefinition, ruleSet, validationStateReader, message } = input;
|
|
390
|
+
const signaturePayload = Jws.decodePlainObjectPayload(message.authorization.signature);
|
|
391
|
+
if (await EncryptionControl.actorCanCreateRole({
|
|
392
|
+
tenant,
|
|
393
|
+
actor,
|
|
394
|
+
tags,
|
|
395
|
+
protocolDefinition,
|
|
396
|
+
ruleSet,
|
|
397
|
+
validationStateReader,
|
|
398
|
+
invokedRole: signaturePayload.protocolRole,
|
|
399
|
+
})) {
|
|
400
|
+
return;
|
|
401
|
+
}
|
|
402
|
+
throw new DwnError(DwnErrorCode.EncryptionControlValidateAudienceWriterUnauthorized, 'control records must be written by a DID authorized to create the referenced role.');
|
|
403
|
+
}
|
|
404
|
+
static async actorCanCreateRole(input) {
|
|
405
|
+
const { tenant, actor, tags, protocolDefinition, ruleSet, validationStateReader, invokedRole } = input;
|
|
406
|
+
if (actor.did === tenant) {
|
|
407
|
+
return true;
|
|
408
|
+
}
|
|
409
|
+
if (actor.did === undefined) {
|
|
410
|
+
return false;
|
|
411
|
+
}
|
|
412
|
+
if (actor.grant !== undefined && EncryptionControl.grantCoversRoleCreate(actor.grant, tags)) {
|
|
413
|
+
return true;
|
|
414
|
+
}
|
|
415
|
+
const recordChain = await EncryptionControl.constructRoleParentChain(tenant, tags, validationStateReader);
|
|
416
|
+
for (const actionRule of ruleSet.$actions ?? []) {
|
|
417
|
+
if (!actionRule.can.includes(ProtocolAction.Create)) {
|
|
418
|
+
continue;
|
|
419
|
+
}
|
|
420
|
+
if (actionRule.who === ProtocolActor.Anyone) {
|
|
421
|
+
return true;
|
|
422
|
+
}
|
|
423
|
+
if (await EncryptionControl.roleRuleAllowsActor(tenant, actor.did, actionRule, tags, protocolDefinition, validationStateReader, invokedRole)) {
|
|
424
|
+
return true;
|
|
425
|
+
}
|
|
426
|
+
if (await checkActor(actor.did, actionRule, recordChain, protocolDefinition)) {
|
|
427
|
+
return true;
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
return false;
|
|
431
|
+
}
|
|
432
|
+
static async verifyDeliveryRecipientAuthority(input) {
|
|
433
|
+
const { tags } = input;
|
|
434
|
+
if (tags.recipientAuthority !== EncryptionControlDeliveryRecipientAuthority.RoleHolder) {
|
|
435
|
+
throw new DwnError(DwnErrorCode.EncryptionControlValidateDeliveryRecipientAuthorityInvalid, `unsupported delivery recipient authority '${tags.recipientAuthority}'.`);
|
|
436
|
+
}
|
|
437
|
+
if (!await EncryptionControl.deliveryRecipientIsRoleHolder(input)) {
|
|
438
|
+
throw new DwnError(DwnErrorCode.EncryptionControlValidateDeliveryRecipientRoleRecordMissing, 'delivery recipient role record is missing.');
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
static async deliveryRecipientIsRoleHolder(input) {
|
|
442
|
+
return EncryptionControl.hasRoleRecord(input.tenant, input.message.descriptor.recipient, input.tags.protocol, input.tags.rolePath, input.tags.contextId, input.validationStateReader);
|
|
443
|
+
}
|
|
444
|
+
static grantCoversRoleCreate(grant, tags) {
|
|
445
|
+
const scope = grant.scope;
|
|
446
|
+
if (scope.interface !== DwnInterfaceName.Records || scope.method !== DwnMethodName.Write) {
|
|
447
|
+
return false;
|
|
448
|
+
}
|
|
449
|
+
return PermissionScopeMatcher.matches(scope, {
|
|
450
|
+
protocol: tags.protocol,
|
|
451
|
+
protocolPath: tags.rolePath,
|
|
452
|
+
});
|
|
453
|
+
}
|
|
454
|
+
static async canEnumerateAudience(input) {
|
|
455
|
+
const { tenant, actorDid, incomingMessage, tags, validationStateReader } = input;
|
|
456
|
+
const grants = await EncryptionControl.getInvokedReadGrants({
|
|
457
|
+
tenant,
|
|
458
|
+
actorDid,
|
|
459
|
+
incomingMessage,
|
|
460
|
+
validationStateReader,
|
|
461
|
+
});
|
|
462
|
+
if (grants.some(grant => EncryptionControl.grantCoversAudienceEnumeration(grant, tags))) {
|
|
463
|
+
return true;
|
|
464
|
+
}
|
|
465
|
+
const { protocolDefinition, ruleSet } = await EncryptionControl.resolveRoleAudienceDefinition({
|
|
466
|
+
tenant,
|
|
467
|
+
tags,
|
|
468
|
+
messageTimestamp: incomingMessage.descriptor.messageTimestamp,
|
|
469
|
+
validationStateReader,
|
|
470
|
+
});
|
|
471
|
+
const invokedRole = incomingMessage.authorization === undefined
|
|
472
|
+
? undefined
|
|
473
|
+
: Jws.decodePlainObjectPayload(incomingMessage.authorization.signature).protocolRole;
|
|
474
|
+
return EncryptionControl.actorCanCreateRole({
|
|
475
|
+
tenant,
|
|
476
|
+
actor: { did: actorDid },
|
|
477
|
+
tags,
|
|
478
|
+
protocolDefinition,
|
|
479
|
+
ruleSet,
|
|
480
|
+
validationStateReader,
|
|
481
|
+
invokedRole,
|
|
482
|
+
});
|
|
483
|
+
}
|
|
484
|
+
static async canReadDeliveryByDelegatedGrant(input) {
|
|
485
|
+
const { tenant, incomingMessage, requester, recordsWriteMessage, tags, validationStateReader } = input;
|
|
486
|
+
const grants = await EncryptionControl.getInvokedReadGrants({
|
|
487
|
+
tenant,
|
|
488
|
+
actorDid: requester,
|
|
489
|
+
incomingMessage,
|
|
490
|
+
validationStateReader,
|
|
491
|
+
});
|
|
492
|
+
const recipient = recordsWriteMessage.descriptor.recipient;
|
|
493
|
+
let protocolDefinition;
|
|
494
|
+
for (const grant of grants) {
|
|
495
|
+
if (recipient !== grant.grantor && recipient !== grant.grantee) {
|
|
496
|
+
continue;
|
|
497
|
+
}
|
|
498
|
+
if (!isGrantKeyEligibleRecordsScope(grant.scope) || grant.scope.method !== DwnMethodName.Read) {
|
|
499
|
+
continue;
|
|
500
|
+
}
|
|
501
|
+
const deliveredScope = {
|
|
502
|
+
protocol: tags.protocol,
|
|
503
|
+
protocolPath: tags.rolePath,
|
|
504
|
+
};
|
|
505
|
+
if (grantKeyScopeCoversDeliveredScope({
|
|
506
|
+
grantScope: grant.scope,
|
|
507
|
+
deliveredScope,
|
|
508
|
+
})) {
|
|
509
|
+
return true;
|
|
510
|
+
}
|
|
511
|
+
protocolDefinition ??= await validationStateReader.fetchProtocolDefinition(tenant, tags.protocol, incomingMessage.descriptor.messageTimestamp);
|
|
512
|
+
if (grantKeyScopeCoversDeliveredScope({
|
|
513
|
+
grantScope: grant.scope,
|
|
514
|
+
deliveredScope,
|
|
515
|
+
protocolDefinition,
|
|
516
|
+
})) {
|
|
517
|
+
return true;
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
return false;
|
|
521
|
+
}
|
|
522
|
+
static async getInvokedReadGrants(input) {
|
|
523
|
+
if (Message.isSignedByAuthorDelegate(input.incomingMessage)) {
|
|
524
|
+
const grant = PermissionGrant.parse(input.incomingMessage.authorization.authorDelegatedGrant);
|
|
525
|
+
await GrantAuthorization.performBaseValidation({
|
|
526
|
+
incomingMessage: input.incomingMessage,
|
|
527
|
+
expectedGrantor: grant.grantor,
|
|
528
|
+
expectedGrantee: input.actorDid,
|
|
529
|
+
permissionGrant: grant,
|
|
530
|
+
validationStateReader: input.validationStateReader,
|
|
531
|
+
});
|
|
532
|
+
return [grant];
|
|
533
|
+
}
|
|
534
|
+
const { descriptor } = input.incomingMessage;
|
|
535
|
+
if (!('permissionGrantId' in descriptor)) {
|
|
536
|
+
return [];
|
|
537
|
+
}
|
|
538
|
+
const grantId = descriptor.permissionGrantId;
|
|
539
|
+
if (grantId === undefined) {
|
|
540
|
+
return [];
|
|
541
|
+
}
|
|
542
|
+
const grant = await input.validationStateReader.fetchGrant(input.tenant, grantId);
|
|
543
|
+
await GrantAuthorization.performBaseValidation({
|
|
544
|
+
incomingMessage: input.incomingMessage,
|
|
545
|
+
expectedGrantor: input.tenant,
|
|
546
|
+
expectedGrantee: input.actorDid,
|
|
547
|
+
permissionGrant: grant,
|
|
548
|
+
validationStateReader: input.validationStateReader,
|
|
549
|
+
});
|
|
550
|
+
return [grant];
|
|
551
|
+
}
|
|
552
|
+
static grantCoversAudienceEnumeration(grant, tags) {
|
|
553
|
+
const scope = grant.scope;
|
|
554
|
+
if (scope.interface !== DwnInterfaceName.Records || scope.method !== DwnMethodName.Read) {
|
|
555
|
+
return false;
|
|
556
|
+
}
|
|
557
|
+
return PermissionScopeMatcher.matches(scope, {
|
|
558
|
+
protocol: tags.protocol,
|
|
559
|
+
protocolPath: tags.rolePath,
|
|
560
|
+
});
|
|
561
|
+
}
|
|
562
|
+
static async resolveRoleAudienceDefinition(input) {
|
|
563
|
+
const { tenant, tags, messageTimestamp, validationStateReader } = input;
|
|
564
|
+
EncryptionControl.verifyRoleAudienceContext(tags.rolePath, tags.contextId);
|
|
565
|
+
const protocolDefinition = await validationStateReader.fetchProtocolDefinition(tenant, tags.protocol, messageTimestamp);
|
|
566
|
+
const ruleSet = getRuleSetAtPath(tags.rolePath, protocolDefinition.structure);
|
|
567
|
+
if (ruleSet?.$role !== true || ruleSet.$keyAgreement === undefined) {
|
|
568
|
+
throw new DwnError(DwnErrorCode.EncryptionControlValidateAudienceRolePathInvalid, `role audience path '${tags.rolePath}' must be a role path with $keyAgreement.`);
|
|
569
|
+
}
|
|
570
|
+
return { protocolDefinition, ruleSet };
|
|
571
|
+
}
|
|
572
|
+
static verifyGrantConditions(message, grant) {
|
|
573
|
+
if (grant.conditions?.publication === PermissionConditionPublication.Required && message.descriptor.published !== true) {
|
|
574
|
+
throw new DwnError(DwnErrorCode.RecordsGrantAuthorizationConditionPublicationRequired, 'Permission grant requires message to be published');
|
|
575
|
+
}
|
|
576
|
+
if (grant.conditions?.publication === PermissionConditionPublication.Prohibited && message.descriptor.published === true) {
|
|
577
|
+
throw new DwnError(DwnErrorCode.RecordsGrantAuthorizationConditionPublicationProhibited, 'Permission grant prohibits message from being published');
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
static async constructRoleParentChain(tenant, tags, validationStateReader) {
|
|
581
|
+
if (tags.contextId === '') {
|
|
582
|
+
return [];
|
|
583
|
+
}
|
|
584
|
+
const parentRecordId = tags.contextId.split('/').pop();
|
|
585
|
+
if (parentRecordId === undefined || parentRecordId === '') {
|
|
586
|
+
return [];
|
|
587
|
+
}
|
|
588
|
+
return validationStateReader.constructRecordChain(tenant, parentRecordId);
|
|
589
|
+
}
|
|
590
|
+
static async roleRuleAllowsActor(tenant, actor, actionRule, tags, protocolDefinition, validationStateReader, invokedRole) {
|
|
591
|
+
if (actionRule.role === undefined || invokedRole !== actionRule.role) {
|
|
592
|
+
return false;
|
|
593
|
+
}
|
|
594
|
+
const resolved = EncryptionControl.resolveRoleReference(actionRule.role, tags.protocol, protocolDefinition);
|
|
595
|
+
if (resolved === undefined) {
|
|
596
|
+
return false;
|
|
597
|
+
}
|
|
598
|
+
return EncryptionControl.hasRoleRecord(tenant, actor, resolved.protocol, resolved.protocolPath, tags.contextId, validationStateReader);
|
|
599
|
+
}
|
|
600
|
+
static async hasRoleRecord(tenant, actor, protocol, rolePath, contextId, validationStateReader) {
|
|
601
|
+
const contextIdPrefix = getRoleContextPrefix(rolePath, contextId);
|
|
602
|
+
if (contextIdPrefix === undefined && rolePath.includes('/')) {
|
|
603
|
+
return false;
|
|
604
|
+
}
|
|
605
|
+
return validationStateReader.hasMatchingRoleRecord({
|
|
606
|
+
tenant,
|
|
607
|
+
protocol,
|
|
608
|
+
protocolPath: rolePath,
|
|
609
|
+
recipient: actor,
|
|
610
|
+
contextIdPrefix,
|
|
611
|
+
});
|
|
612
|
+
}
|
|
613
|
+
static resolveRoleReference(roleRef, currentProtocol, protocolDefinition) {
|
|
614
|
+
if (!isCrossProtocolRef(roleRef)) {
|
|
615
|
+
return { protocol: currentProtocol, protocolPath: roleRef };
|
|
616
|
+
}
|
|
617
|
+
const parsed = parseCrossProtocolRef(roleRef);
|
|
618
|
+
if (parsed === undefined) {
|
|
619
|
+
return undefined;
|
|
620
|
+
}
|
|
621
|
+
const protocol = protocolDefinition.uses?.[parsed.alias];
|
|
622
|
+
if (protocol === undefined) {
|
|
623
|
+
return undefined;
|
|
624
|
+
}
|
|
625
|
+
return { protocol, protocolPath: parsed.protocolPath };
|
|
626
|
+
}
|
|
627
|
+
static getRoleAudienceKeyId(message, recordType) {
|
|
628
|
+
return {
|
|
629
|
+
protocol: EncryptionControl.getRequiredStringTag(message, 'protocol', recordType),
|
|
630
|
+
rolePath: EncryptionControl.getRequiredStringTag(message, 'rolePath', recordType),
|
|
631
|
+
contextId: EncryptionControl.getRequiredStringTag(message, 'contextId', recordType),
|
|
632
|
+
keyId: EncryptionControl.getRequiredStringTag(message, 'keyId', recordType),
|
|
633
|
+
};
|
|
634
|
+
}
|
|
635
|
+
static getDeliveryTags(message) {
|
|
636
|
+
const tags = EncryptionControl.getRoleAudienceKeyId(message, 'delivery');
|
|
637
|
+
return {
|
|
638
|
+
...tags,
|
|
639
|
+
recipientAuthority: EncryptionControl.getRequiredStringTag(message, 'recipientAuthority', 'delivery'),
|
|
640
|
+
};
|
|
641
|
+
}
|
|
642
|
+
static getRecordType(message) {
|
|
643
|
+
return message.descriptor.protocolPath === ENCRYPTION_CONTROL_DELIVERY_PATH ? 'delivery' : 'audience';
|
|
644
|
+
}
|
|
645
|
+
static async isVisibleControlRecord(input) {
|
|
646
|
+
if (!EncryptionControl.isControlMessage(input.recordsWriteMessage)) {
|
|
647
|
+
return true;
|
|
648
|
+
}
|
|
649
|
+
try {
|
|
650
|
+
return await EncryptionControl.canRead({
|
|
651
|
+
tenant: input.tenant,
|
|
652
|
+
incomingMessage: input.incomingMessage,
|
|
653
|
+
requester: input.requester,
|
|
654
|
+
recordsWriteMessage: input.recordsWriteMessage,
|
|
655
|
+
validationStateReader: input.validationStateReader,
|
|
656
|
+
});
|
|
657
|
+
}
|
|
658
|
+
catch (error) {
|
|
659
|
+
if (error instanceof DwnError) {
|
|
660
|
+
return false;
|
|
661
|
+
}
|
|
662
|
+
throw error;
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
static exactAudienceFilterMatchesRecord(filter, tags, recordId) {
|
|
666
|
+
if (filter.recordId === recordId) {
|
|
667
|
+
return true;
|
|
668
|
+
}
|
|
669
|
+
const tuple = EncryptionControl.getExactAudienceFilterTuple(filter);
|
|
670
|
+
if (tuple === undefined) {
|
|
671
|
+
return false;
|
|
672
|
+
}
|
|
673
|
+
return tuple.protocol === tags.protocol &&
|
|
674
|
+
tuple.rolePath === tags.rolePath &&
|
|
675
|
+
tuple.contextId === tags.contextId &&
|
|
676
|
+
(tuple.keyId === undefined || tuple.keyId === tags.keyId);
|
|
677
|
+
}
|
|
678
|
+
static getExactAudienceFilterTuple(filter) {
|
|
679
|
+
if (filter.protocol === undefined || filter.protocolPath !== ENCRYPTION_CONTROL_AUDIENCE_PATH) {
|
|
680
|
+
return undefined;
|
|
681
|
+
}
|
|
682
|
+
const protocol = EncryptionControl.getExactFilterTag(filter, 'protocol');
|
|
683
|
+
const rolePath = EncryptionControl.getExactFilterTag(filter, 'rolePath');
|
|
684
|
+
const contextId = EncryptionControl.getExactFilterTag(filter, 'contextId');
|
|
685
|
+
if (protocol !== filter.protocol || rolePath === undefined || contextId === undefined) {
|
|
686
|
+
return undefined;
|
|
687
|
+
}
|
|
688
|
+
const keyId = EncryptionControl.getExactFilterTag(filter, 'keyId');
|
|
689
|
+
if (keyId === undefined) {
|
|
690
|
+
return { protocol, rolePath, contextId };
|
|
691
|
+
}
|
|
692
|
+
return { protocol, rolePath, contextId, keyId };
|
|
693
|
+
}
|
|
694
|
+
static exactAudienceRequestMatchesRecord(incomingMessage, tags, recordId) {
|
|
695
|
+
const { descriptor } = incomingMessage;
|
|
696
|
+
if ('filter' in descriptor) {
|
|
697
|
+
return EncryptionControl.exactAudienceFilterMatchesRecord(descriptor.filter, tags, recordId);
|
|
698
|
+
}
|
|
699
|
+
return false;
|
|
700
|
+
}
|
|
701
|
+
static getExactFilterTag(filter, tag) {
|
|
702
|
+
const value = filter.tags?.[tag];
|
|
703
|
+
return typeof value === 'string' ? value : undefined;
|
|
704
|
+
}
|
|
705
|
+
static async queryStoredAudienceRecordsForTuple(input) {
|
|
706
|
+
const filter = {
|
|
707
|
+
interface: DwnInterfaceName.Records,
|
|
708
|
+
method: DwnMethodName.Write,
|
|
709
|
+
isLatestBaseState: true,
|
|
710
|
+
protocol: input.protocol,
|
|
711
|
+
protocolPath: ENCRYPTION_CONTROL_AUDIENCE_PATH,
|
|
712
|
+
'tag.protocol': input.protocol,
|
|
713
|
+
'tag.rolePath': input.rolePath,
|
|
714
|
+
'tag.contextId': input.contextId,
|
|
715
|
+
};
|
|
716
|
+
const { messages } = await input.messageStore.query(input.tenant, [filter], { dateCreated: SortDirection.Ascending });
|
|
717
|
+
return messages.filter(Records.isRecordsWrite);
|
|
718
|
+
}
|
|
719
|
+
static async resolveCurrentAudienceRecordId(input) {
|
|
720
|
+
const tupleKey = EncryptionControl.getAudienceTupleKey(input);
|
|
721
|
+
if (input.currentAudienceRecordIdCache?.has(tupleKey) === true) {
|
|
722
|
+
return input.currentAudienceRecordIdCache.get(tupleKey);
|
|
723
|
+
}
|
|
724
|
+
const current = await EncryptionControl.resolveCurrentAudienceRecord(input);
|
|
725
|
+
const currentRecordId = current?.recordId;
|
|
726
|
+
input.currentAudienceRecordIdCache?.set(tupleKey, currentRecordId);
|
|
727
|
+
return currentRecordId;
|
|
728
|
+
}
|
|
729
|
+
static shouldProjectAudienceRecord(message, bypassFilters) {
|
|
730
|
+
return EncryptionControl.isAudienceControlMessage(message) &&
|
|
731
|
+
!bypassFilters.some((filter) => EncryptionControl.audienceProjectionBypassFilterMatchesRecord(filter, message));
|
|
732
|
+
}
|
|
733
|
+
static audienceProjectionBypassFilterMatchesRecord(filter, message) {
|
|
734
|
+
if (filter.recordId === message.recordId) {
|
|
735
|
+
return true;
|
|
736
|
+
}
|
|
737
|
+
const tags = EncryptionControl.getRoleAudienceKeyId(message, 'audience');
|
|
738
|
+
return filter.protocol === tags.protocol &&
|
|
739
|
+
filter.protocolPath === ENCRYPTION_CONTROL_AUDIENCE_PATH &&
|
|
740
|
+
filter['tag.protocol'] === tags.protocol &&
|
|
741
|
+
filter['tag.rolePath'] === tags.rolePath &&
|
|
742
|
+
filter['tag.contextId'] === tags.contextId &&
|
|
743
|
+
filter['tag.keyId'] === tags.keyId;
|
|
744
|
+
}
|
|
745
|
+
static getAudienceTupleKey(input) {
|
|
746
|
+
return JSON.stringify([input.protocol, input.rolePath, input.contextId]);
|
|
747
|
+
}
|
|
748
|
+
static parseAudienceTupleKey(key) {
|
|
749
|
+
const [protocol, rolePath, contextId] = JSON.parse(key);
|
|
750
|
+
return { protocol, rolePath, contextId };
|
|
751
|
+
}
|
|
752
|
+
static compareAudienceProjectionCandidates(tenant, left, right) {
|
|
753
|
+
const leftIsTenantSigned = Message.getSigner(left) === tenant;
|
|
754
|
+
const rightIsTenantSigned = Message.getSigner(right) === tenant;
|
|
755
|
+
if (leftIsTenantSigned !== rightIsTenantSigned) {
|
|
756
|
+
return leftIsTenantSigned ? -1 : 1;
|
|
757
|
+
}
|
|
758
|
+
const dateComparison = lexicographicalCompare(left.descriptor.dateCreated, right.descriptor.dateCreated);
|
|
759
|
+
if (dateComparison !== 0) {
|
|
760
|
+
return dateComparison;
|
|
761
|
+
}
|
|
762
|
+
return lexicographicalCompare(left.recordId, right.recordId);
|
|
763
|
+
}
|
|
764
|
+
static getRequiredStringTag(message, tag, recordType) {
|
|
765
|
+
const value = message.descriptor.tags?.[tag];
|
|
766
|
+
if (typeof value !== 'string') {
|
|
767
|
+
const errorCode = recordType === 'audience'
|
|
768
|
+
? DwnErrorCode.EncryptionControlValidateAudienceMissingRequiredTag
|
|
769
|
+
: DwnErrorCode.EncryptionControlValidateDeliveryMissingRequiredTag;
|
|
770
|
+
throw new DwnError(errorCode, `${recordType} control records must include string tag '${tag}'.`);
|
|
771
|
+
}
|
|
772
|
+
return value;
|
|
773
|
+
}
|
|
774
|
+
static getOptionalStringTag(tags, tag) {
|
|
775
|
+
const value = tags?.[tag];
|
|
776
|
+
if (value === undefined) {
|
|
777
|
+
return undefined;
|
|
778
|
+
}
|
|
779
|
+
if (typeof value !== 'string') {
|
|
780
|
+
throw new DwnError(DwnErrorCode.EncryptionControlValidateDeliveryMissingRequiredTag, `delivery control tag '${tag}' must be a string.`);
|
|
781
|
+
}
|
|
782
|
+
return value;
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
//# sourceMappingURL=encryption-control.js.map
|