From 355f621383d1f9483abcb099091a2ce073f811b6 Mon Sep 17 00:00:00 2001 From: Dan Draper Date: Sun, 1 Nov 2026 19:31:08 +1100 Subject: [PATCH 1/4] fix(stack-encrypt): align context composition Append repeated extensions to match Go and require shared context conformance. Old records written with replacement semantics still open with the final extension alone. Closes #1475 Fixes #1120 --- conformance/stack-encrypt/README.md | 11 +- conformance/stack-encrypt/context-contract.md | 110 ++++ conformance/stack-encrypt/coverage.md | 14 + conformance/stack-encrypt/fixture-contract.md | 17 +- .../stack-encrypt/v1/context-boundaries.json | 78 +++ .../stack-encrypt/v1/contexts-native.json | 146 +++++ conformance/stack-encrypt/v1/contexts.json | 216 +++++++ .../stack-encrypt/v1/fixture-profiles.json | 23 + .../2026-10-31-stack-encrypt-conformance.md | 16 +- .../encrypt/context_conformance_test.go | 350 ++++++++++++ .../golang/encrypt/context_field_test.go | 20 +- .../encrypt/fixture_conformance_test.go | 2 + packages/stack-encrypt/CHANGELOG.md | 10 + packages/stack-encrypt/src/plan/chain.rs | 5 +- packages/stack-encrypt/src/target/context.rs | 5 + .../tests/context_conformance.rs | 530 ++++++++++++++++++ packages/stack-encrypt/tests/plan.rs | 18 +- .../stack-encrypt/tests/record_lowering.rs | 5 + .../__tests__/stack-conformance-ci.test.mjs | 11 +- scripts/stack-conformance-selection.mjs | 2 + scripts/stack-conformance.sh | 2 +- 21 files changed, 1570 insertions(+), 21 deletions(-) create mode 100644 conformance/stack-encrypt/context-contract.md create mode 100644 conformance/stack-encrypt/v1/context-boundaries.json create mode 100644 conformance/stack-encrypt/v1/contexts-native.json create mode 100644 conformance/stack-encrypt/v1/contexts.json create mode 100644 languages/golang/encrypt/context_conformance_test.go create mode 100644 packages/stack-encrypt/tests/context_conformance.rs diff --git a/conformance/stack-encrypt/README.md b/conformance/stack-encrypt/README.md index 11d4cd96b..4889deebc 100644 --- a/conformance/stack-encrypt/README.md +++ b/conformance/stack-encrypt/README.md @@ -21,7 +21,7 @@ native Rust against the ciphertexts Go has just produced. It never regenerates expectations. CI also runs the Go cases on its other supported host platforms. The same task runs the shared declaration acceptance/rejection corpus in both languages, with native observations of normalization. It also runs the shared -runtime and read cases, and enrolls the existing value-kind and query-operator +runtime, read and context cases, and enrolls the existing value-kind, context-field and query-operator fixtures described below. Set no fixture-update variables when checking conformance. The Rust runner is `packages/stack-encrypt/tests/conformance.rs`; the Go runner @@ -29,6 +29,15 @@ is `languages/golang/encrypt/conformance_test.go`. Go calls the record interface used by generated code. Testing public SDK declarations is a separate layer. Python and TypeScript runners are pending; their absence is not a passing result. +## Context composition and expectations + +The [context contract](context-contract.md) covers ordered extensions, canonical +context encodings, typed/generated API calls, stored-context expectations, +tampering and preflight boundaries. The required command runs the new +`context_conformance` Rust binary and `TestContextConformance` / +`TestContextBoundaries` Go runners, together with native `plan_grammar` and +existing context-field fixture tests. + ## Existing value-kind and query-operator fixtures The [fixture contract](fixture-contract.md) defines `record-value-kinds-v1` and diff --git a/conformance/stack-encrypt/context-contract.md b/conformance/stack-encrypt/context-contract.md new file mode 100644 index 000000000..ef7d5fcb3 --- /dev/null +++ b/conformance/stack-encrypt/context-contract.md @@ -0,0 +1,110 @@ +# Context composition + +`record-contexts-v1` (`v1/contexts.json`) specifies composition for the current +record API. `context-boundaries-v1` (`v1/context-boundaries.json`) specifies +stored-context expectations and refusal boundaries. Both are required in Rust +and Go. Python and TypeScript must implement these profiles when their runners +are added; their absence is not conformance evidence. + +## Current semantics + +A field's base is a plain label: the record's declared label followed by the +field's identity. A context-field record obtains the record label from each +row's stored field. A Rust plan built without a context can instead take that +label from the call. Go generated types declare either a literal context or a +context field; they do not expose Rust's contextless saved-plan builder. + +Caller input has two distinct purposes: + +- A **base label** supplies a missing base for a query or a contextless Rust plan. + For a record storing its own context, an expected label checks that stored + value. It does not replace or extend it. A caller label on a plan with a + literal base is refused. +- An **extension** appends to the resolved field context for encryption, + decryption and search. Each call appends in order. Rust + `.extend(7u64).extend(nonempty!("eu"))` and Go + `.Extend(uint64(7)).Extend("eu")` produce `((label)/7u64)/eu`. + Rust `.extend(())` and Go `.Extend()` append nothing. A Rust + `DeclaredContext` containing several parts appends all of those parts. + +Grouping is significant. Rust `.extend(NonEmpty::from(7u64).with("eu"))` adds +one composite part, producing `(label)/(7u64/eu)`. This has different AAD and +terms from separate extensions. Nested `ContextPiece` values are supported by +Rust's lower-level context APIs; the dynamic record grammar and Go `Extend` +accept scalar extension parts only. Their refusal of a grouped extension is +part of the supported API boundary, not permission to flatten it. + +These tests fix #1120: repeated Rust chain extensions previously replaced the +first extension. Previously written records from such chains still require the +**last extension alone** when opening them; callers must explicitly retain that +old context when reading old records. This change does not rewrite ciphertext. + +The native base and each appended scalar retain their type and structure. +`7i32`, `7i64`, `7u64`, text `eu`, and bytes `6575` do not collapse to one AAD +or PRF context. Descriptor rendering is deliberately coarser in some cases: +text and bytes can render alike, as can positive signed and unsigned integers. +A matching descriptor alone therefore does not prove matching AAD. Empty text +is accepted by dynamic record/Go extension APIs when the base remains nonempty; +Rust's `NonEmpty` constructor rejects an empty standalone extension. The +composition runner records this API difference instead of bypassing it. + +## Composition evidence + +Each case has a unique `id`, `base` (`literal` or `field`), nonempty `label`, and +ordered `parts`. Each part is `{ "type": ..., "text": ... }`: string, int32, +int64, uint64, or bytes (hex). Integers are decimal strings, never JSON numbers. +Every case encrypts and queries the string `hello` in the `text` field, using +the public all-zero deterministic-provider test seed. + +`v1/contexts-native.json` is keyed by exactly those case IDs. It pins the +context frame, descriptor, AAD, PRF bytes, one Rust ciphertext and equality +term. Rust checks actual descriptors sent to the key provider, fresh typed and +dynamic writes, queries, opens, and frozen ciphertexts. The typed chain calls +`extend` repeatedly, including a final no-op, so #1120 cannot regress unnoticed. + +Go compares its exact context tree encoding against the frozen frame and opens +the Rust ciphertext. It compares fresh stored and query terms, both variadic +and repeated extensions, and generated `Note` methods for context-field cases. +Go does not expose a separate descriptor/AAD encoder: equality of the input +frame and successful authenticated cross-opening are its evidence for those +observations. Ciphertext bytes are randomized and are not compared for equality. + +Regenerate deliberately with `STACK_ENCRYPT_UPDATE_FIXTURES=1` and the native +`context_conformance` binary. Required conformance rejects fixture updates. +Changes to frozen context encodings require compatibility review. + +## Expectations and boundaries + +Boundary cases specify rows' `tenants`, the caller's `expected` label (the empty +string explicitly means no expectation in this corpus), a mutation of the last +stored row, an `outcome`, and native `retrievals`. All properties are required; +null is not an alternative spelling for omission or the empty string. + +Cases cover unrestricted reads, matching expectations, late mismatches, empty +batches, changed stored labels with and without matching expectations, and +missing/empty stored contexts late in a batch. Key-independent refusals must +make no retrieval. An empty valid batch also makes none. Changing the stored +label does not confer permission to open the ciphertext even if the caller's +expectation matches the replacement label. + +Native `CountingSource` checks exact data-key call counts. Its fake provider +does not authenticate descriptors, so a tampered context fails AEAD after one +retrieval. Go's deterministic provider authenticates the descriptor and refuses +the same tampering with `ErrForbidden`. An uninitialised Go checker separately +proves the key-free preflight boundary. This is not a claim that Go exposes +provider call counters. + +The earlier read profile continues to cover context storage shape and exact +structured diagnostics. The enrolled `record-context-fields-v1` fixture +retains generated Go expectation, query, tampering and single-field update +checks and Rust typed/dynamic cross-opening. The required native `plan_grammar` +binary additionally covers missing caller bases, conflicting sources, invalid +labels, caller-derived queries and empty-batch validation before keyset loading. + +## Future roles + +This profile describes the current shared context: it feeds the data-key +descriptor, ciphertext AAD and index PRF context. It does not introduce separate +ciphertext-binding and search-scope contexts. That API design remains #1487; +any future split needs explicit compatibility rules and new profile coverage. +It also does not assert plaintext erasure in Go, Python or TypeScript heaps. diff --git a/conformance/stack-encrypt/coverage.md b/conformance/stack-encrypt/coverage.md index bad3e2a5d..05f6a9d05 100644 --- a/conformance/stack-encrypt/coverage.md +++ b/conformance/stack-encrypt/coverage.md @@ -91,3 +91,17 @@ For each removal, link the old assertion to stable case IDs, compare coverage and failure behavior, and run both runners in CI. Preserve unrelated assertions from mixed tests. Enroll Python and TypeScript against the existing corpus so adding a language does not create another copy of the expected behavior. + +## Context checkpoint + +The [context contract](context-contract.md) adds 18 shared composition cases and +nine shared boundary cases, plus enrollment of the existing record-lowering +context-field fixture. Rust typed write/query/open chains and Go generated +methods execute the composition cases. Required native plan-grammar tests cover +caller-provided bases and empty-batch source validation. Existing context tests +remain: no mixed-purpose Rust or Go test is removed. + +Go proves canonical AAD indirectly through its exact context frame and native +ciphertext opening; provider-call instrumentation remains a separate gap. +Nested composite extensions remain a native API capability rather than an +unsupported feature silently advertised by the Go/dynamic record profile. diff --git a/conformance/stack-encrypt/fixture-contract.md b/conformance/stack-encrypt/fixture-contract.md index efb589960..587c6762d 100644 --- a/conformance/stack-encrypt/fixture-contract.md +++ b/conformance/stack-encrypt/fixture-contract.md @@ -1,6 +1,7 @@ -# Value-kind and query-operator profiles +# Enrolled fixture profiles -`record-value-kinds-v1` and `record-query-operators-v1` enroll the existing +`record-value-kinds-v1`, `record-query-operators-v1` and +`record-context-fields-v1` enroll the existing Rust/Go fixtures in the conformance contract. They are experimental profiles of the cases those fixtures contain, not exhaustive type/operator certification. They use the plain record resolver (`NoTargets`), with public deterministic @@ -108,3 +109,15 @@ the listed observations through their own host values and public APIs. Their integer/decimal/time mappings and actual backends must be specified by those implementations. EQL domains, JSON operations, all-type boundary matrices, shared query refusals and supported-release matrices remain separate work. + +## Context-field requirements + +`record-context-fields-v1` enrolls the context-field section of the existing +`record_lowering.json` fixture. Its format and seed remain owned by that fixture. +The [context contract](context-contract.md) adds composition and boundary cases +without copying these existing vectors. + +| Requirement ID | Required observation | Fixture evidence | +|---|---|---| +| `contexts.frozen-open` | Typed and lowered Rust records open through the opposite path and generated Go methods, under their stored context. | `context_field.records` | +| `contexts.expectations` | An expectation checks the stored context; a mismatch refuses it. Generated Go queries name the base context, and tampering does not open. | Fresh records and `context_field` plan | diff --git a/conformance/stack-encrypt/v1/context-boundaries.json b/conformance/stack-encrypt/v1/context-boundaries.json new file mode 100644 index 000000000..7a3470595 --- /dev/null +++ b/conformance/stack-encrypt/v1/context-boundaries.json @@ -0,0 +1,78 @@ +{ + "version": 1, + "profile": "context-boundaries-v1", + "cases": [ + { + "id": "unrestricted", + "tenants": ["tenants/acme", "tenants/globex"], + "expected": "", + "mutation": "none", + "outcome": "success", + "retrievals": 1 + }, + { + "id": "expected", + "tenants": ["tenants/acme"], + "expected": "tenants/acme", + "mutation": "none", + "outcome": "success", + "retrievals": 1 + }, + { + "id": "late-mismatch", + "tenants": ["tenants/acme", "tenants/globex"], + "expected": "tenants/acme", + "mutation": "none", + "outcome": "mismatch", + "retrievals": 0 + }, + { + "id": "empty", + "tenants": [], + "expected": "tenants/acme", + "mutation": "none", + "outcome": "success", + "retrievals": 0 + }, + { + "id": "tampered", + "tenants": ["tenants/acme"], + "expected": "", + "mutation": "tamper", + "outcome": "authentication", + "retrievals": 1 + }, + { + "id": "tampered-matching-expectation", + "tenants": ["tenants/acme"], + "expected": "tenants/globex", + "mutation": "tamper", + "outcome": "authentication", + "retrievals": 1 + }, + { + "id": "tampered-mismatching-expectation", + "tenants": ["tenants/acme"], + "expected": "tenants/acme", + "mutation": "tamper", + "outcome": "mismatch", + "retrievals": 0 + }, + { + "id": "late-missing", + "tenants": ["tenants/acme", "tenants/globex"], + "expected": "", + "mutation": "missing", + "outcome": "shape", + "retrievals": 0 + }, + { + "id": "late-empty", + "tenants": ["tenants/acme", "tenants/globex"], + "expected": "", + "mutation": "empty", + "outcome": "shape", + "retrievals": 0 + } + ] +} diff --git a/conformance/stack-encrypt/v1/contexts-native.json b/conformance/stack-encrypt/v1/contexts-native.json new file mode 100644 index 000000000..e73501abe --- /dev/null +++ b/conformance/stack-encrypt/v1/contexts-native.json @@ -0,0 +1,146 @@ +{ + "field-base": { + "context_frame": "f0030000000a0700000074656e616e74730a0400000061636d650a0400000074657874", + "descriptor": "tenants/acme/text", + "aad": "0300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000074657874", + "prf": "0300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000074657874", + "ciphertext": "0100000000000000000000000000000000e42a8b64607989ab5b6197de58ff5f322000175d4e4987626d968634ab7dda022fdbc41dce31bf9183e729c1f562185b175202698f817c8ac5153febe0a12c6d017bedc729dcd3c0e6a34704bda1de65ba86edabb3", + "equality": "d07ccccb55205dbb5c69c80bd88c907496a18bbed9317f39d96a83f6fd308a85" + }, + "field-bytes": { + "context_frame": "f002000000f0030000000a0700000074656e616e74730a0400000061636d650a04000000746578740b020000006575", + "descriptor": "(tenants/acme/text)/eu", + "aad": "020000000000000022010000000000000300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000074657874540000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311900000000000000766974616d696e632f636f6e746578742f62797465732f763102000000000000006575", + "prf": "020000000000000022010000000000000300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000074657874540000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311900000000000000766974616d696e632f636f6e746578742f62797465732f763102000000000000006575", + "ciphertext": "0100000000000000000000000000000000ab137bf75fe4fa4f42a20f099ff3a77f20001a726f50cba00da93aab760c6e038be772c928d0cf8a5877dcff2e9ecbafadfe02a07f63015a3bb45c950f1a6669327d39ea2dd813655ea0f990f66f83bca2a4d98c29", + "equality": "df58c27184a7707a4735ac1afd925fa3be93689eb1fa26e615cc96e78107524e" + }, + "field-empty-part": { + "context_frame": "f002000000f0030000000a0700000074656e616e74730a0400000061636d650a04000000746578740a00000000", + "descriptor": "(tenants/acme/text)/b64:", + "aad": "020000000000000022010000000000000300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000074657874510000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f76310000000000000000", + "prf": "020000000000000022010000000000000300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000074657874510000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f76310000000000000000", + "ciphertext": "01000000000000000000000000000000001c03b0987bb51494b2f64841124a60c12000deede8eb83519fef962190d1e9c7104f75ed87d19c0f221d7c85136429b3f4b702523faf4b8207654708e4699416aaa800f0917353fb5ad19c9ba9c9cdfe63697c11a9", + "equality": "6562228f385c28b4762ff6bcb92057ecf739847f85fc18c4c9b917e96b240ad6" + }, + "field-narrow": { + "context_frame": "f002000000f0030000000a0700000074656e616e74730a0400000061636d650a04000000746578740407000000", + "descriptor": "(tenants/acme/text)/7u32", + "aad": "020000000000000022010000000000000300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000074657874570000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311a00000000000000766974616d696e632f636f6e746578742f6933322d6c652f7631040000000000000007000000", + "prf": "020000000000000022010000000000000300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000074657874570000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311a00000000000000766974616d696e632f636f6e746578742f6933322d6c652f7631040000000000000007000000", + "ciphertext": "0100000000000000000000000000000000d8767b62020fa04a135b492d4bed93e62000023c54cc52c80ba067235a64a801d702f3f558640fa9f80508c999afe98f7cbf0201d9c0bdd57345ca59e8758b8cbd823f2087c300740fc1f878fa15c2686f415164cf", + "equality": "a600febf5d31afa2e8fa9dfd0d47bbed76e2a6b69b3a26f3d9ba490d953c5c43" + }, + "field-one": { + "context_frame": "f002000000f0030000000a0700000074656e616e74730a0400000061636d650a0400000074657874070700000000000000", + "descriptor": "(tenants/acme/text)/7u64", + "aad": "020000000000000022010000000000000300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f76310400000000000000746578745b0000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311a00000000000000766974616d696e632f636f6e746578742f7536342d6c652f763108000000000000000700000000000000", + "prf": "020000000000000022010000000000000300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f76310400000000000000746578745b0000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311a00000000000000766974616d696e632f636f6e746578742f7536342d6c652f763108000000000000000700000000000000", + "ciphertext": "01000000000000000000000000000000004216f7c1c404ef65c06bf83fab7d67b720009c0baa84274beb7ccb55fb28f343ab4ad12e6d00803b3955cb5bcf9e6ed2b51202dc72629ac375399fc444afa1bb11918c9a62f1341d353ac50009aef27fdd5a5bcfaf", + "equality": "ef4fd47818a5ec5b472f768bed2e28e9f3e0ef1bf86b95fb85b7d23208fb215a" + }, + "field-ordered": { + "context_frame": "f002000000f002000000f0030000000a0700000074656e616e74730a0400000061636d650a04000000746578740707000000000000000a020000006575", + "descriptor": "((tenants/acme/text)/7u64)/eu", + "aad": "02000000000000009501000000000000020000000000000022010000000000000300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f76310400000000000000746578745b0000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311a00000000000000766974616d696e632f636f6e746578742f7536342d6c652f763108000000000000000700000000000000530000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f763102000000000000006575", + "prf": "02000000000000009501000000000000020000000000000022010000000000000300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f76310400000000000000746578745b0000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311a00000000000000766974616d696e632f636f6e746578742f7536342d6c652f763108000000000000000700000000000000530000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f763102000000000000006575", + "ciphertext": "0100000000000000000000000000000000d25279cf7ce2862c0e1ead193d5a7698200054b497feda700c887e38cd921614aac2016a88eb2c367e4c0b72b5c6483f5b0c02df5aa44610fba1a90888e44cd3c8cb61d60ec171d4e6f1c026925ff97b0e8d090eb3", + "equality": "ad34715d40cf4bb40e35830f19c01180c08ef0492d40febc9d3267cab1464562" + }, + "field-reversed": { + "context_frame": "f002000000f002000000f0030000000a0700000074656e616e74730a0400000061636d650a04000000746578740a020000006575070700000000000000", + "descriptor": "((tenants/acme/text)/eu)/7u64", + "aad": "02000000000000008d01000000000000020000000000000022010000000000000300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000074657874530000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631020000000000000065755b0000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311a00000000000000766974616d696e632f636f6e746578742f7536342d6c652f763108000000000000000700000000000000", + "prf": "02000000000000008d01000000000000020000000000000022010000000000000300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000074657874530000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631020000000000000065755b0000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311a00000000000000766974616d696e632f636f6e746578742f7536342d6c652f763108000000000000000700000000000000", + "ciphertext": "01000000000000000000000000000000000682e6a8c346478d0bed4f633f9f591720001d0ee4f5786d90124b1bb4a56452173e72b1dbdb1837bb1761a3dc3ba04574600237a1b985afbdc095b044282f1c8828dc9dd8ba5caa9defa6a3548ff6321e52608629", + "equality": "ac1ae266a8574b0a164b771046e0028bc290e4018c2e48ba351515dcf95a7801" + }, + "field-signed": { + "context_frame": "f002000000f0030000000a0700000074656e616e74730a0400000061636d650a0400000074657874050700000000000000", + "descriptor": "(tenants/acme/text)/7u64", + "aad": "020000000000000022010000000000000300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f76310400000000000000746578745b0000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311a00000000000000766974616d696e632f636f6e746578742f6936342d6c652f763108000000000000000700000000000000", + "prf": "020000000000000022010000000000000300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f76310400000000000000746578745b0000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311a00000000000000766974616d696e632f636f6e746578742f6936342d6c652f763108000000000000000700000000000000", + "ciphertext": "0100000000000000000000000000000000e0fa052c95f14a1b6e95adbfc952cf6e2000ee4082d049338a812d8b10673453127da1fe6dcb665dea4c96da426f52df55bf0260e25b0d83fd0287132f2071ca0aa61de3dee9f05a344fda170211c9c763d4c3240f", + "equality": "1bcd9b5f12c4218bbd584dce0bad0003138408cc312c1cc8bb69bc8861a8227e" + }, + "field-text": { + "context_frame": "f002000000f0030000000a0700000074656e616e74730a0400000061636d650a04000000746578740a020000006575", + "descriptor": "(tenants/acme/text)/eu", + "aad": "020000000000000022010000000000000300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000074657874530000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f763102000000000000006575", + "prf": "020000000000000022010000000000000300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000074657874530000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f763102000000000000006575", + "ciphertext": "0100000000000000000000000000000000d21a6d4e940e4329994b30afc2a81cb12000203fb57f381e7ef64dc8ebe63bd33f546a772bedc94a624e4d47b6a6c5eb45f3029ae4e5e1d82d1a315fffbe7865cfdae1c2666e69d534b66a652dd42fa4396199f73c", + "equality": "76f509a979f486b783f2d4f872a1913abff9f3ef66d7007b01e3cab3a734e270" + }, + "literal-base": { + "context_frame": "f0030000000a0700000074656e616e74730a0400000061636d650a0400000074657874", + "descriptor": "tenants/acme/text", + "aad": "0300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000074657874", + "prf": "0300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000074657874", + "ciphertext": "0100000000000000000000000000000000233bbec00515bcc147012024a94dda1d2000421022b5ace076acb43d72b4aa3de668968552535247283db06bed86af0def73029d811dd365ef1445b2a7cf7dff745881871bdcd379986f1b42e5f0b83176e321d0c9", + "equality": "d07ccccb55205dbb5c69c80bd88c907496a18bbed9317f39d96a83f6fd308a85" + }, + "literal-bytes": { + "context_frame": "f002000000f0030000000a0700000074656e616e74730a0400000061636d650a04000000746578740b020000006575", + "descriptor": "(tenants/acme/text)/eu", + "aad": "020000000000000022010000000000000300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000074657874540000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311900000000000000766974616d696e632f636f6e746578742f62797465732f763102000000000000006575", + "prf": "020000000000000022010000000000000300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000074657874540000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311900000000000000766974616d696e632f636f6e746578742f62797465732f763102000000000000006575", + "ciphertext": "0100000000000000000000000000000000f992ac91510557386b3bcfcc26e9d45d2000f9ba6397d1be9552b633ffeef89249a1a3b28a1b2a58d4c2808de639001020ae02301ac6e31ef94bd6f03880588e4aa15a0c70cf5b3d498e74eab105e29c688a0d2406", + "equality": "df58c27184a7707a4735ac1afd925fa3be93689eb1fa26e615cc96e78107524e" + }, + "literal-empty-part": { + "context_frame": "f002000000f0030000000a0700000074656e616e74730a0400000061636d650a04000000746578740a00000000", + "descriptor": "(tenants/acme/text)/b64:", + "aad": "020000000000000022010000000000000300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000074657874510000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f76310000000000000000", + "prf": "020000000000000022010000000000000300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000074657874510000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f76310000000000000000", + "ciphertext": "010000000000000000000000000000000014850b0d9658afa82000b1c214375fd42000ea552a62dd1fdd018c2279f78de9f25c5283b33cd3d51fee029fb98da7e0c35a02c46ea737a9175b1f64ce252c907814ec151ab8265f860e2192a18a455f1fcbe488af", + "equality": "6562228f385c28b4762ff6bcb92057ecf739847f85fc18c4c9b917e96b240ad6" + }, + "literal-narrow": { + "context_frame": "f002000000f0030000000a0700000074656e616e74730a0400000061636d650a04000000746578740407000000", + "descriptor": "(tenants/acme/text)/7u32", + "aad": "020000000000000022010000000000000300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000074657874570000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311a00000000000000766974616d696e632f636f6e746578742f6933322d6c652f7631040000000000000007000000", + "prf": "020000000000000022010000000000000300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000074657874570000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311a00000000000000766974616d696e632f636f6e746578742f6933322d6c652f7631040000000000000007000000", + "ciphertext": "010000000000000000000000000000000006c936cf0e7c4804593649d96a530f3e20008295873720d38723e2a83007f0cc80867629e97736b65d8da267e09028679ba102304eb13d354282baadc0f719255db1b9dff7bf443aa167959fde52a210c540def110", + "equality": "a600febf5d31afa2e8fa9dfd0d47bbed76e2a6b69b3a26f3d9ba490d953c5c43" + }, + "literal-one": { + "context_frame": "f002000000f0030000000a0700000074656e616e74730a0400000061636d650a0400000074657874070700000000000000", + "descriptor": "(tenants/acme/text)/7u64", + "aad": "020000000000000022010000000000000300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f76310400000000000000746578745b0000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311a00000000000000766974616d696e632f636f6e746578742f7536342d6c652f763108000000000000000700000000000000", + "prf": "020000000000000022010000000000000300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f76310400000000000000746578745b0000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311a00000000000000766974616d696e632f636f6e746578742f7536342d6c652f763108000000000000000700000000000000", + "ciphertext": "0100000000000000000000000000000000d4e2b614715887c3b0b8b7ec13c8854820008e13eaedd78736f2531e16737e2346916e314064927acfb970d613fb11c81ba10271d40c1b9b7a0df25f48606585fc1e7337e33ab6f0816b959654cf48a22370352e03", + "equality": "ef4fd47818a5ec5b472f768bed2e28e9f3e0ef1bf86b95fb85b7d23208fb215a" + }, + "literal-ordered": { + "context_frame": "f002000000f002000000f0030000000a0700000074656e616e74730a0400000061636d650a04000000746578740707000000000000000a020000006575", + "descriptor": "((tenants/acme/text)/7u64)/eu", + "aad": "02000000000000009501000000000000020000000000000022010000000000000300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f76310400000000000000746578745b0000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311a00000000000000766974616d696e632f636f6e746578742f7536342d6c652f763108000000000000000700000000000000530000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f763102000000000000006575", + "prf": "02000000000000009501000000000000020000000000000022010000000000000300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f76310400000000000000746578745b0000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311a00000000000000766974616d696e632f636f6e746578742f7536342d6c652f763108000000000000000700000000000000530000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f763102000000000000006575", + "ciphertext": "0100000000000000000000000000000000127e42644c439f781b72f98a90a1d0282000d1df0e854edc5616954c06efdfc883277cb48bc41b10f03e96a58d6e43e9660d022ca2c0feab076d352efd3acbf89362ce17e2433f7efc4765f2e898a074883a9e1558", + "equality": "ad34715d40cf4bb40e35830f19c01180c08ef0492d40febc9d3267cab1464562" + }, + "literal-reversed": { + "context_frame": "f002000000f002000000f0030000000a0700000074656e616e74730a0400000061636d650a04000000746578740a020000006575070700000000000000", + "descriptor": "((tenants/acme/text)/eu)/7u64", + "aad": "02000000000000008d01000000000000020000000000000022010000000000000300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000074657874530000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631020000000000000065755b0000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311a00000000000000766974616d696e632f636f6e746578742f7536342d6c652f763108000000000000000700000000000000", + "prf": "02000000000000008d01000000000000020000000000000022010000000000000300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000074657874530000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631020000000000000065755b0000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311a00000000000000766974616d696e632f636f6e746578742f7536342d6c652f763108000000000000000700000000000000", + "ciphertext": "01000000000000000000000000000000008b7f54275b6c8f7b0acf5eed3c74894420006442ff82abfe7e68dffd900ec2826f1bc9ed4ae95e55118ccb6914165f4bd0760219cdf75189337f6170a4841f4cf1047c99b38e519538cb731a2c06dd706aece87288", + "equality": "ac1ae266a8574b0a164b771046e0028bc290e4018c2e48ba351515dcf95a7801" + }, + "literal-signed": { + "context_frame": "f002000000f0030000000a0700000074656e616e74730a0400000061636d650a0400000074657874050700000000000000", + "descriptor": "(tenants/acme/text)/7u64", + "aad": "020000000000000022010000000000000300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f76310400000000000000746578745b0000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311a00000000000000766974616d696e632f636f6e746578742f6936342d6c652f763108000000000000000700000000000000", + "prf": "020000000000000022010000000000000300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f76310400000000000000746578745b0000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311a00000000000000766974616d696e632f636f6e746578742f6936342d6c652f763108000000000000000700000000000000", + "ciphertext": "010000000000000000000000000000000096958fa4c5718b558f2a196c4c02b901200011ff26eaa183171680c1943ed6124393bd39aa8b86e3eaadeb381dce51cb4f3902d5dad6d829cd36e38316f37972ac0daa8686959c51ab8ea85306708edbc6f6bf84e8", + "equality": "1bcd9b5f12c4218bbd584dce0bad0003138408cc312c1cc8bb69bc8861a8227e" + }, + "literal-text": { + "context_frame": "f002000000f0030000000a0700000074656e616e74730a0400000061636d650a04000000746578740a020000006575", + "descriptor": "(tenants/acme/text)/eu", + "aad": "020000000000000022010000000000000300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000074657874530000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f763102000000000000006575", + "prf": "020000000000000022010000000000000300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000074657874530000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f763102000000000000006575", + "ciphertext": "01000000000000000000000000000000006379fc33f72fe6d1aa66fcdd2f394c342000e66e4d1f85d1e763c0244e39e813e1efc0af0620b0c18d2224e5183f49fc161402173f5892942f89e3a41297f4a9813e17da78930df22bb1f973085848b8fbaa74704a", + "equality": "76f509a979f486b783f2d4f872a1913abff9f3ef66d7007b01e3cab3a734e270" + } +} diff --git a/conformance/stack-encrypt/v1/contexts.json b/conformance/stack-encrypt/v1/contexts.json new file mode 100644 index 000000000..b7935d7a1 --- /dev/null +++ b/conformance/stack-encrypt/v1/contexts.json @@ -0,0 +1,216 @@ +{ + "version": 1, + "profile": "record-contexts-v1", + "required_capabilities": [ + "composition", + "typed-extensions", + "context-fields", + "frozen-open" + ], + "cases": [ + { + "id": "literal-base", + "base": "literal", + "label": "tenants/acme", + "parts": [] + }, + { + "id": "literal-one", + "base": "literal", + "label": "tenants/acme", + "parts": [ + { + "type": "uint64", + "text": "7" + } + ] + }, + { + "id": "literal-ordered", + "base": "literal", + "label": "tenants/acme", + "parts": [ + { + "type": "uint64", + "text": "7" + }, + { + "type": "string", + "text": "eu" + } + ] + }, + { + "id": "literal-reversed", + "base": "literal", + "label": "tenants/acme", + "parts": [ + { + "type": "string", + "text": "eu" + }, + { + "type": "uint64", + "text": "7" + } + ] + }, + { + "id": "literal-signed", + "base": "literal", + "label": "tenants/acme", + "parts": [ + { + "type": "int64", + "text": "7" + } + ] + }, + { + "id": "literal-narrow", + "base": "literal", + "label": "tenants/acme", + "parts": [ + { + "type": "int32", + "text": "7" + } + ] + }, + { + "id": "literal-text", + "base": "literal", + "label": "tenants/acme", + "parts": [ + { + "type": "string", + "text": "eu" + } + ] + }, + { + "id": "literal-bytes", + "base": "literal", + "label": "tenants/acme", + "parts": [ + { + "type": "bytes", + "text": "6575" + } + ] + }, + { + "id": "literal-empty-part", + "base": "literal", + "label": "tenants/acme", + "parts": [ + { + "type": "string", + "text": "" + } + ] + }, + { + "id": "field-base", + "base": "field", + "label": "tenants/acme", + "parts": [] + }, + { + "id": "field-one", + "base": "field", + "label": "tenants/acme", + "parts": [ + { + "type": "uint64", + "text": "7" + } + ] + }, + { + "id": "field-ordered", + "base": "field", + "label": "tenants/acme", + "parts": [ + { + "type": "uint64", + "text": "7" + }, + { + "type": "string", + "text": "eu" + } + ] + }, + { + "id": "field-reversed", + "base": "field", + "label": "tenants/acme", + "parts": [ + { + "type": "string", + "text": "eu" + }, + { + "type": "uint64", + "text": "7" + } + ] + }, + { + "id": "field-signed", + "base": "field", + "label": "tenants/acme", + "parts": [ + { + "type": "int64", + "text": "7" + } + ] + }, + { + "id": "field-narrow", + "base": "field", + "label": "tenants/acme", + "parts": [ + { + "type": "int32", + "text": "7" + } + ] + }, + { + "id": "field-text", + "base": "field", + "label": "tenants/acme", + "parts": [ + { + "type": "string", + "text": "eu" + } + ] + }, + { + "id": "field-bytes", + "base": "field", + "label": "tenants/acme", + "parts": [ + { + "type": "bytes", + "text": "6575" + } + ] + }, + { + "id": "field-empty-part", + "base": "field", + "label": "tenants/acme", + "parts": [ + { + "type": "string", + "text": "" + } + ] + } + ] +} diff --git a/conformance/stack-encrypt/v1/fixture-profiles.json b/conformance/stack-encrypt/v1/fixture-profiles.json index 050c76be8..6b50bd450 100644 --- a/conformance/stack-encrypt/v1/fixture-profiles.json +++ b/conformance/stack-encrypt/v1/fixture-profiles.json @@ -78,6 +78,29 @@ ] } ] + }, + { + "id": "record-context-fields-v1", + "fixture": "packages/stack-encrypt/tests/fixtures/record_lowering.json", + "rust_binary": "record_lowering", + "go_source": "languages/golang/encrypt/context_field_test.go", + "requirements": [ + { + "id": "contexts.frozen-open", + "rust_tests": ["the_committed_context_field_case_opens_both_ways"], + "go_tests": ["TestGeneratedCodeOpensTheRustContextFieldFixture"] + }, + { + "id": "contexts.expectations", + "rust_tests": [ + "the_chain_and_the_lowering_agree_on_a_context_field_today" + ], + "go_tests": [ + "TestContextFieldSealsEachRowUnderItsOwnContext", + "TestContextFieldQueriesNameTheContext" + ] + } + ] } ] } diff --git a/docs/plans/2026-10-31-stack-encrypt-conformance.md b/docs/plans/2026-10-31-stack-encrypt-conformance.md index 5cde8cea2..359a93de7 100644 --- a/docs/plans/2026-10-31-stack-encrypt-conformance.md +++ b/docs/plans/2026-10-31-stack-encrypt-conformance.md @@ -1,6 +1,6 @@ # Stack Encrypt cross-language conformance -Date: 2026-10-31. Status: scalar, declaration, initial runtime and read slices implemented; existing value-kind and query-operator fixtures enrolled. The complete contract is not frozen. +Date: 2026-10-31. Status: scalar, declaration, initial runtime, read and context-composition slices implemented; existing value-kind, query-operator and context-field fixtures enrolled. The complete contract is not frozen. This work is stacked on [PR #1404](https://github.com/cipherstash/stack/pull/1404), at `b684a217e2da629c762efa70fac958ec7e21646a`. It follows the accepted @@ -209,3 +209,17 @@ open in native Rust, fixed native values open in Go, and malformed query inputs produce the specified diagnostics. Python and TypeScript remain visibly pending until their runners execute. The complete specification, SQL profile, and all language SDK mappings remain follow-up work. + + +## Context composition checkpoint (#1475) + +Implemented shared composition and boundary cases in +`conformance/stack-encrypt/context-contract.md`: 18 typed extension cases, +nine read/preflight cases, generated Go calls and native typed chains. Existing +context-field fixture tests and the native plan-grammar suite are required by +`test:conformance`. Repeated Rust `.extend()` now appends, resolving #1120; +the changelog documents compatibility for records written under the old behavior. +The current shared binding/search context remains distinct from future #1487. + +Next: #1476, the EQL/JSON operation profile. Python and TypeScript runner delivery +and the wider fresh artifact exchange remain separate planned work. diff --git a/languages/golang/encrypt/context_conformance_test.go b/languages/golang/encrypt/context_conformance_test.go new file mode 100644 index 000000000..8b0722275 --- /dev/null +++ b/languages/golang/encrypt/context_conformance_test.go @@ -0,0 +1,350 @@ +package encrypt_test + +import ( + "bytes" + "context" + "encoding/hex" + "encoding/json" + "errors" + "fmt" + "path/filepath" + "reflect" + "testing" + + "github.com/cipherstash/stack/languages/golang/encrypt" + "github.com/cipherstash/stack/languages/golang/encrypt/internal/testusers" + "github.com/cipherstash/stack/languages/golang/internal/record" + "github.com/cipherstash/vitaminc/bindings/go/vcffi" + "github.com/cipherstash/vitaminc/bindings/go/vcvalue" +) + +type contextCase struct { + ID string `json:"id"` + Base string `json:"base"` + Label string `json:"label"` + Parts []contextPart `json:"parts"` +} +type contextVector struct { + ContextFrame string `json:"context_frame"` + Descriptor string `json:"descriptor"` + AAD string `json:"aad"` + PRF string `json:"prf"` + Ciphertext string `json:"ciphertext"` + Equality string `json:"equality"` +} + +func TestContextConformance(t *testing.T) { + ctx := context.Background() + client, err := encrypt.NewDeterministicClient(ctx, [32]byte{}) + skipMissingConformanceGuest(t, err) + if err != nil { + t.Fatal(err) + } + t.Cleanup(func() { _ = client.Close() }) + root := filepath.Join("..", "..", "..", "conformance", "stack-encrypt", "v1") + var corpus struct { + Version int `json:"version"` + Profile string `json:"profile"` + RequiredCapabilities []string `json:"required_capabilities"` + Cases []contextCase `json:"cases"` + } + readConformanceJSON(t, filepath.Join(root, "contexts.json"), &corpus) + if corpus.Version != 1 || corpus.Profile != "record-contexts-v1" || len(corpus.Cases) == 0 || !reflect.DeepEqual(corpus.RequiredCapabilities, []string{"composition", "typed-extensions", "context-fields", "frozen-open"}) { + t.Fatal("unsupported context profile") + } + var vectors map[string]contextVector + readConformanceJSON(t, filepath.Join(root, "contexts-native.json"), &vectors) + if len(vectors) != len(corpus.Cases) { + t.Fatal("vector case set differs") + } + t.Run("grouped-extension-is-not-a-go-record-part", func(t *testing.T) { + cipher := client.DefaultKeyset().Extend([]any{uint64(7), "eu"}) + _, err := testusers.NoteFields.Text.Eq(ctx, cipher.Context("tenants/acme"), "hello") + if !errors.Is(err, encrypt.ErrEncoding) { + t.Fatalf("grouped extension: %v", err) + } + }) + seen := map[string]bool{} + for _, c := range corpus.Cases { + if c.ID == "" || seen[c.ID] { + t.Fatal("empty or repeated case ID") + } + seen[c.ID] = true + t.Run(c.ID, func(t *testing.T) { + want, ok := vectors[c.ID] + if !ok { + t.Fatal("missing vector") + } + decode := func(s string) []byte { + t.Helper() + b, e := hex.DecodeString(s) + if e != nil { + t.Fatal(e) + } + return b + } + label, e := record.ParseContext(c.Label) + if e != nil { + t.Fatal(e) + } + p := &record.Plan{Context: label, Fields: []record.Field{{Name: "text", Kind: record.String, Outputs: []record.Output{record.Ciphertext, record.Equality}}}} + source := []record.Source{{"text": "hello"}} + switch c.Base { + case "literal": + case "field": + p.Context = nil + p.ContextField = "tenant" + source[0]["tenant"] = c.Label + default: + t.Fatal("unknown context base") + } + cipher := client.DefaultKeyset() + parts := make([]any, 0, len(c.Parts)) + for _, part := range c.Parts { + var v any + switch part.Type { + case "bytes": + v = decode(part.Text) + case "string", "int32", "int64", "uint64": + v = conformanceValue(t, conformanceInput(part)) + default: + t.Fatal("unsupported context part") + } + parts = append(parts, v) + cipher = cipher.Extend(v) + } + // Compare the precise context tree that native Rust encoded to AAD/PRF. + // Go has no independent public AAD or descriptor encoder. + resolved := *p + resolved.Context = label + resolved.ContextField = "" + resolved.Extension = parts + framed, e := vcffi.Marshal(resolved.FieldContext(p.Fields[0])) + if e != nil { + t.Fatal(e) + } + if hex.EncodeToString(framed) != want.ContextFrame { + t.Fatal("context tree differs from native") + } + if want.Descriptor == "" || len(decode(want.AAD)) == 0 || len(decode(want.PRF)) == 0 { + t.Fatal("missing canonical context observations") + } + fresh, e := cipher.Seal(ctx, p, source) + if e != nil { + t.Fatal(e) + } + if !bytes.Equal(fresh[0]["text"].Terms[record.Equality], decode(want.Equality)) { + t.Fatal("stored term differs") + } + frozen := []record.Sealed{{"text": {Ciphertext: decode(want.Ciphertext), Terms: map[record.Output][]byte{record.Equality: decode(want.Equality)}}}} + if c.Base == "field" { + frozen[0]["tenant"] = record.Outputs{Context: c.Label} + } + for _, stored := range [][]record.Sealed{fresh, frozen} { + opened, e := cipher.Open(ctx, p, stored) + if e != nil || !reflect.DeepEqual(opened, source) { + t.Fatalf("open: %v; values differ: %v", e, !reflect.DeepEqual(opened, source)) + } + } + queryCipher := cipher + if c.Base == "field" { + queryCipher = cipher.Context(c.Label) + } + query, e := queryCipher.QueryTerm(ctx, p, "text", "eq", "hello") + if e != nil { + t.Fatal(e) + } + if len(query.Terms) != 1 || !bytes.Equal(query.Terms[0], decode(want.Equality)) { + t.Fatal("query differs") + } + // Variadic and repeated calls must agree, including a no-op Extend(). + combined := client.DefaultKeyset().Extend(parts...).Extend() + if c.Base == "field" { + combined = combined.Context(c.Label) + } + q, e := combined.QueryTerm(ctx, p, "text", "eq", "hello") + if e != nil || !reflect.DeepEqual(q.Terms, query.Terms) { + t.Fatalf("combined extensions: %v", e) + } + if c.Base == "field" { + notes := []testusers.Note{{Tenant: c.Label, Text: "hello", ID: 0}} + sealed, e := testusers.EncryptNote(ctx, cipher, notes) + if e != nil { + t.Fatal(e) + } + if !bytes.Equal(sealed[0].Text.Equality, decode(want.Equality)) { + t.Fatal("generated write differs") + } + sealed[0].Text.Ciphertext = decode(want.Ciphertext) + opened, e := testusers.DecryptNote(ctx, cipher, sealed) + if e != nil || !reflect.DeepEqual(opened, notes) { + t.Fatalf("generated cross-open: %v", e) + } + q, e := testusers.NoteFields.Text.Eq(ctx, queryCipher, "hello") + if e != nil || !q.Equal(decode(want.Equality)) { + t.Fatalf("generated query: %v", e) + } + } + }) + } +} + +type contextBoundary struct { + ID string `json:"id"` + Tenants []string `json:"tenants"` + Expected string `json:"expected"` + Mutation string `json:"mutation"` + Outcome string `json:"outcome"` + Retrievals int `json:"retrievals"` +} + +func TestContextBoundaries(t *testing.T) { + ctx := context.Background() + client, err := encrypt.NewDeterministicClient(ctx, [32]byte{}) + skipMissingConformanceGuest(t, err) + if err != nil { + t.Fatal(err) + } + t.Cleanup(func() { _ = client.Close() }) + wasm, err := encrypt.DeterministicGuest() + if err != nil { + t.Fatal(err) + } + checker, err := encrypt.NewCheckerOver(ctx, wasm) + if err != nil { + t.Fatal(err) + } + t.Cleanup(func() { _ = checker.Close() }) + var corpus struct { + Version int `json:"version"` + Profile string `json:"profile"` + Cases []contextBoundary `json:"cases"` + } + readConformanceJSON(t, filepath.Join("..", "..", "..", "conformance", "stack-encrypt", "v1", "context-boundaries.json"), &corpus) + if corpus.Version != 1 || corpus.Profile != "context-boundaries-v1" || len(corpus.Cases) == 0 { + t.Fatal("unsupported boundary profile") + } + p := &record.Plan{ContextField: "tenant", Fields: []record.Field{{Name: "text", Kind: record.String, Outputs: []record.Output{record.Ciphertext, record.Equality}}}} + seen := map[string]bool{} + for _, c := range corpus.Cases { + if c.ID == "" || seen[c.ID] { + t.Fatal("empty or repeated ID") + } + seen[c.ID] = true + t.Run(c.ID, func(t *testing.T) { + rows := make([]record.Source, len(c.Tenants)) + for i, tenant := range c.Tenants { + rows[i] = record.Source{"tenant": tenant, "text": "hello"} + } + cipher := client.DefaultKeyset() + sealed, e := cipher.Seal(ctx, p, rows) + if e != nil { + t.Fatal(e) + } + tree := make([]any, len(sealed)) + for i, row := range sealed { + tree[i] = map[string]any{"tenant": map[string]any{"passthrough": vcvalue.Plain{V: row["tenant"].Context}}, "text": map[string]any{"c": encrypt.Ciphertext(row["text"].Ciphertext), "eq": vcvalue.Plain{V: row["text"].Terms[record.Equality]}}} + } + switch c.Mutation { + case "none": + case "tamper", "empty", "missing": + if len(tree) == 0 { + t.Fatal("mutation requires a row") + } + row := tree[len(tree)-1].(map[string]any) + if c.Mutation == "missing" { + delete(row, "tenant") + } else { + tenant := "" + if c.Mutation == "tamper" { + tenant = "tenants/globex" + } + row["tenant"] = map[string]any{"passthrough": vcvalue.Plain{V: tenant}} + } + default: + t.Fatal("unknown mutation") + } + _, preflight := checker.OpenTreeForTest(ctx, tree, p, c.Expected) + // An uninitialised guest cannot retrieve keys: valid shapes reach ErrState. + switch c.Outcome { + case "success", "authentication": + if !errors.Is(preflight, encrypt.ErrState) { + t.Fatalf("preflight: %v", preflight) + } + case "mismatch", "shape": + if preflight == nil || errors.Is(preflight, encrypt.ErrState) { + t.Fatalf("expected key-free refusal: %v", preflight) + } + default: + t.Fatal("unknown outcome") + } + reader := cipher + if c.Expected != "" { + reader = reader.Context(c.Expected) + } + opened, e := reader.OpenTreeForTest(ctx, tree, p) + switch c.Outcome { + case "success": + if e != nil || !reflect.DeepEqual(opened, rows) { + t.Fatalf("open: %v; rows differ: %v", e, !reflect.DeepEqual(opened, rows)) + } + want := 1 + if len(rows) == 0 { + want = 0 + } + if c.Retrievals != want { + t.Fatal("invalid retrieval expectation") + } + case "mismatch": + if !errors.Is(e, encrypt.ErrContextMismatch) || c.Retrievals != 0 { + t.Fatalf("mismatch: %v", e) + } + case "shape": + if !errors.Is(e, encrypt.ErrEncoding) || c.Retrievals != 0 { + t.Fatalf("shape: %v", e) + } + case "authentication": + if !errors.Is(e, encrypt.ErrForbidden) || c.Retrievals != 1 { + t.Fatalf("tampering: %v", e) + } + } + }) + } +} + +// Go's zero values must not make a missing or null field valid when Rust's +// required Deserialize fields reject it. The corpus is a strict shared format. +func decodeRequiredContextJSON(data []byte, out any, required ...string) error { + var fields map[string]json.RawMessage + if err := json.Unmarshal(data, &fields); err != nil { + return err + } + for _, name := range required { + value, ok := fields[name] + if !ok || bytes.Equal(bytes.TrimSpace(value), []byte("null")) { + return fmt.Errorf("missing or null %s", name) + } + } + decoder := json.NewDecoder(bytes.NewReader(data)) + decoder.DisallowUnknownFields() + return decoder.Decode(out) +} + +type contextPart conformanceInput + +func (p *contextPart) UnmarshalJSON(data []byte) error { + type plain contextPart + return decodeRequiredContextJSON(data, (*plain)(p), "type", "text") +} +func (c *contextCase) UnmarshalJSON(data []byte) error { + type plain contextCase + return decodeRequiredContextJSON(data, (*plain)(c), "id", "base", "label", "parts") +} +func (v *contextVector) UnmarshalJSON(data []byte) error { + type plain contextVector + return decodeRequiredContextJSON(data, (*plain)(v), "context_frame", "descriptor", "aad", "prf", "ciphertext", "equality") +} +func (c *contextBoundary) UnmarshalJSON(data []byte) error { + type plain contextBoundary + return decodeRequiredContextJSON(data, (*plain)(c), "id", "tenants", "expected", "mutation", "outcome", "retrievals") +} diff --git a/languages/golang/encrypt/context_field_test.go b/languages/golang/encrypt/context_field_test.go index 303d71ae4..12b450b93 100644 --- a/languages/golang/encrypt/context_field_test.go +++ b/languages/golang/encrypt/context_field_test.go @@ -27,7 +27,7 @@ var notes = []testusers.Note{ } func TestContextFieldSealsEachRowUnderItsOwnContext(t *testing.T) { - c := deterministicClient(t) + c := contextFixtureClient(t) ctx := context.Background() cipher := c.DefaultKeyset() @@ -101,7 +101,7 @@ func TestContextFieldSealsEachRowUnderItsOwnContext(t *testing.T) { } func TestContextFieldQueriesNameTheContext(t *testing.T) { - c := deterministicClient(t) + c := contextFixtureClient(t) ctx := context.Background() cipher := c.DefaultKeyset() encrypted, err := testusers.EncryptNote(ctx, cipher, notes) @@ -185,9 +185,7 @@ func TestGeneratedCodeOpensTheRustContextFieldFixture(t *testing.T) { t.Fatalf("seed: %v (%d bytes)", err, len(seedBytes)) } c, err := encrypt.NewDeterministicClient(context.Background(), [32]byte(seedBytes)) - if errors.Is(err, encrypt.ErrDeterministicGuestNotBuilt) || errors.Is(err, encrypt.ErrGuestNotBuilt) { - t.Skip(err) - } + skipMissingConformanceGuest(t, err) if err != nil { t.Fatal(err) } @@ -234,3 +232,15 @@ func TestGeneratedCodeOpensTheRustContextFieldFixture(t *testing.T) { }) } } + +// Required conformance must fail if its guest is absent. +func contextFixtureClient(t *testing.T) *encrypt.Client { + t.Helper() + c, err := encrypt.NewDeterministicClient(context.Background(), testSeed) + skipMissingConformanceGuest(t, err) + if err != nil { + t.Fatal(err) + } + t.Cleanup(func() { _ = c.Close() }) + return c +} diff --git a/languages/golang/encrypt/fixture_conformance_test.go b/languages/golang/encrypt/fixture_conformance_test.go index de62ed107..e5b7720e4 100644 --- a/languages/golang/encrypt/fixture_conformance_test.go +++ b/languages/golang/encrypt/fixture_conformance_test.go @@ -74,6 +74,8 @@ func TestFixtureConformanceRequiresGuest(t *testing.T) { } // These full-suite tests also share queryFixtureClient's required-mode policy; // they remain outside the shared fixture requirements and aggregate selection. + names["TestContextConformance"] = true + names["TestContextBoundaries"] = true names["TestAQueryTheFieldDoesNotAnswerIsRefused"] = true names["TestAMatchesQueryNeverPrintsItsText"] = true tests := make([]string, 0, len(names)) diff --git a/packages/stack-encrypt/CHANGELOG.md b/packages/stack-encrypt/CHANGELOG.md index fe6766795..a786176e2 100644 --- a/packages/stack-encrypt/CHANGELOG.md +++ b/packages/stack-encrypt/CHANGELOG.md @@ -5,6 +5,16 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [Unreleased] + +### Fixed + +- Repeated plan-chain `.extend(..)` calls now append parts in order, matching + Go's `Cipher.Extend`, instead of replacing earlier extensions (#1120). + `.extend(())` adds nothing, and grouped `NonEmpty` parts retain their grouping. + Records written using the previous replacement behavior must still be opened + with the final extension alone; this does not migrate stored ciphertext. + ## [0.3.0] - 2026-10-27 ### Breaking diff --git a/packages/stack-encrypt/src/plan/chain.rs b/packages/stack-encrypt/src/plan/chain.rs index 2b8137a72..847c71327 100644 --- a/packages/stack-encrypt/src/plan/chain.rs +++ b/packages/stack-encrypt/src/plan/chain.rs @@ -232,9 +232,10 @@ macro_rules! chain_options { /// each field's `/` becomes /// `(/)/`, as a derived record's caller /// context does. The same parts must be given to read it back. - /// A second call replaces the first. + /// Further calls append in order, nesting to the left. A grouped + /// `NonEmpty` remains one part; `.extend(())` adds nothing. pub fn extend(mut self, parts: impl Into) -> Self { - self.common.extend = parts.into(); + self.common.extend.append(parts.into()); self } } diff --git a/packages/stack-encrypt/src/target/context.rs b/packages/stack-encrypt/src/target/context.rs index aaddc1ae7..3e9ca028b 100644 --- a/packages/stack-encrypt/src/target/context.rs +++ b/packages/stack-encrypt/src/target/context.rs @@ -184,6 +184,11 @@ impl DeclaredContext { self } + /// Append the caller's parts, preserving each part's grouping. + pub(crate) fn append(&mut self, other: Self) { + self.0.extend(other.0); + } + /// The context one field is derived under: its own `own`, extended by /// the caller's context if one was given โ€” `("users", "age")` as it is /// under `()`, `(("users", "age"), id)` under a caller's `id`, and diff --git a/packages/stack-encrypt/tests/context_conformance.rs b/packages/stack-encrypt/tests/context_conformance.rs new file mode 100644 index 000000000..09cc459fe --- /dev/null +++ b/packages/stack-encrypt/tests/context_conformance.rs @@ -0,0 +1,530 @@ +//! Shared context composition, typed chains and dynamic lowering. +#![cfg(feature = "dynamic")] +mod common; + +use common::conformance::{value, Input}; +use common::frames::{frame, object}; +use common::{hex, unhex}; +use miette::Diagnostic; +use serde::{Deserialize, Serialize}; +use stack_encrypt::dynamic::{self, record, Scope, ValueFrame}; +use stack_encrypt::plan::pick; +use stack_encrypt::sem::EqualityTerm; +use stack_encrypt::target::{DeclaredContext, Encrypted}; +use stack_encrypt::{ + CipherText, Descriptor, Equality, IntoAad, IntoPrfContext, Label, NonEmpty, Plan, SealedValue, + StackCipherText, +}; +use std::collections::{BTreeMap, BTreeSet}; + +#[derive(Deserialize)] +#[serde(deny_unknown_fields)] +struct Corpus { + version: u32, + profile: String, + required_capabilities: Vec, + cases: Vec, +} +#[derive(Deserialize)] +#[serde(deny_unknown_fields)] +struct Case { + id: String, + base: String, + label: String, + parts: Vec, +} +#[derive(Deserialize, Serialize)] +#[serde(deny_unknown_fields)] +struct Vector { + context_frame: String, + descriptor: String, + aad: String, + prf: String, + ciphertext: String, + equality: String, +} + +fn part(input: &Input) -> ValueFrame { + if input.kind == "bytes" { + frame(unhex(&input.text)) + } else { + assert!(["string", "int32", "int64", "uint64"].contains(&input.kind.as_str())); + value(input) + } +} +fn extension(input: &Input) -> DeclaredContext { + // An empty scalar is a valid extension of an already nonempty label. + // CallerContext's constructors enforce nonemptiness on each part, so + // the empty-part case is exercised through lowering only. + match input.kind.as_str() { + "string" => DeclaredContext::from(NonEmpty::new(input.text.clone()).unwrap()), + "bytes" => DeclaredContext::from(NonEmpty::new(unhex(&input.text)).unwrap()), + "int32" => input.text.parse::().unwrap().into(), + "int64" => input.text.parse::().unwrap().into(), + "uint64" => input.text.parse::().unwrap().into(), + _ => panic!("unsupported context part"), + } +} +fn context(case: &Case, field_only: bool) -> ValueFrame { + let mut label = if field_only { + vec![] + } else { + case.label.split('/').map(str::to_owned).collect::>() + }; + label.push("text".into()); + case.parts + .iter() + .fold(frame(label), |head, p| frame(vec![head, part(p)])) +} +fn plan(case: &Case) -> record::Plan { + let mut fields = vec![]; + if case.base == "field" { + fields.push(("context_field", frame("tenant"))); + fields.push(( + "tenant", + object(vec![ + ( + "context", + case.parts + .iter() + .fold(frame(vec!["tenant"]), |head, p| frame(vec![head, part(p)])), + ), + ("outputs", frame(vec!["passthrough"])), + ("type", frame("string")), + ]), + )); + } + fields.push(( + "text", + object(vec![ + ("context", context(case, case.base == "field")), + ("outputs", frame(vec!["c", "eq"])), + ("type", frame("string")), + ]), + )); + record::plan(object(fields).risky_ref()).unwrap() +} +fn source(case: &Case) -> ValueFrame { + let mut fields = vec![]; + if case.base == "field" { + fields.push(("tenant", frame(case.label.clone()))); + } + fields.push(("text", frame("hello"))); + object(fields) +} +fn ciphertext(tree: StackCipherText) -> Vec { + let CipherText::Map(fields) = tree else { + panic!("record") + }; + let (_, CipherText::Map(outputs)) = fields.into_iter().find(|(k, _)| k == "text").unwrap() + else { + panic!("outputs") + }; + let (_, CipherText::Single(leaf)) = outputs.into_iter().find(|(k, _)| k == "c").unwrap() else { + panic!("leaf") + }; + leaf.to_bytes() +} +fn stored(case: &Case, vector: &Vector) -> StackCipherText { + let mut fields = vec![]; + if case.base == "field" { + fields.push(( + "tenant".into(), + CipherText::Map(vec![( + "passthrough".into(), + CipherText::Passthrough(common::passthrough(case.label.clone())), + )]), + )); + } + fields.push(( + "text".into(), + CipherText::Map(vec![ + ( + "c".into(), + CipherText::Single(SealedValue::from_bytes(&unhex(&vector.ciphertext)).unwrap()), + ), + ( + "eq".into(), + CipherText::Passthrough(common::passthrough(unhex(&vector.equality))), + ), + ]), + )); + CipherText::Map(fields) +} +struct Note { + tenant: String, + text: String, +} + +#[tokio::test] +async fn shared_context_composition() { + let update = common::fixture_updates_requested_or_panic(); + let corpus: Corpus = serde_json::from_str(include_str!( + "../../../conformance/stack-encrypt/v1/contexts.json" + )) + .unwrap(); + assert_eq!(corpus.version, 1); + assert_eq!(corpus.profile, "record-contexts-v1"); + assert_eq!( + corpus.required_capabilities, + [ + "composition", + "typed-extensions", + "context-fields", + "frozen-open" + ] + ); + assert!(!corpus.cases.is_empty()); + let path = std::path::Path::new(env!("CARGO_MANIFEST_DIR")) + .join("../../conformance/stack-encrypt/v1/contexts-native.json"); + let baseline: BTreeMap = if update { + BTreeMap::new() + } else { + serde_json::from_slice(&std::fs::read(&path).unwrap()).unwrap() + }; + let mut vectors = BTreeMap::new(); + let mut ids = BTreeSet::new(); + let cipher = common::deterministic_cipher([0; 32]).await; + let keyset = cipher.default_keyset(); + for case in corpus.cases { + assert!(!case.id.is_empty() && ids.insert(case.id.clone())); + assert!(["literal", "field"].contains(&case.base.as_str())); + Label::parse(&case.label).unwrap(); + eprintln!("context conformance: {}", case.id); + let context_frame = context(&case, false); + let ctx = dynamic::context(context_frame.risky_ref()).unwrap(); + let descriptor = Descriptor::of(ctx.clone()).as_str().to_owned(); + let aad = hex(ctx.clone().into_aad().as_bytes()); + let prf = hex(ctx.clone().into_prf_context().as_bytes()); + let term = keyset.equality_term("hello", ctx.clone()).await.unwrap(); + let plan = plan(&case); + let fresh = record::encrypt(&keyset, source(&case), &plan) + .unwrap() + .await + .unwrap(); + let vector = Vector { + context_frame: hex(context_frame.risky_ref()), + descriptor, + aad, + prf, + ciphertext: hex(&ciphertext(fresh)), + equality: hex(&term.to_bytes()), + }; + let (recording, sent) = common::recording_cipher().await; + record::encrypt(&recording.default_keyset(), source(&case), &plan) + .unwrap() + .await + .unwrap(); + assert_eq!( + sent.lock().unwrap().generated(), + std::slice::from_ref(&vector.descriptor) + ); + let query_plan = record::plan( + object(vec![( + "text", + object(vec![ + ("context", context_frame.clone()), + ("outputs", frame(vec!["c", "eq"])), + ("type", frame("string")), + ]), + )]) + .risky_ref(), + ) + .unwrap(); + let query = dynamic::query_term( + &keyset, + &query_plan, + "text", + dynamic::OpSpec::Eq, + "hello", + &dynamic::NoTargets, + ) + .unwrap() + .await + .unwrap(); + let dynamic::Operand::One(bytes) = query.operand() else { + panic!("one term") + }; + assert_eq!(hex(bytes), vector.equality); + if case.base == "field" { + assert!( + dynamic::query_term( + &keyset, + &plan, + "text", + dynamic::OpSpec::Eq, + "hello", + &dynamic::NoTargets + ) + .is_err(), + "field query requires a context" + ); + } + let expected = if update { &vector } else { &baseline[&case.id] }; + assert_eq!(vector.context_frame, expected.context_frame); + assert_eq!(vector.descriptor, expected.descriptor); + assert_eq!(vector.aad, expected.aad); + assert_eq!(vector.prf, expected.prf); + assert_eq!(vector.equality, expected.equality); + let opened = record::decrypt( + Scope::Keyset(keyset.clone()), + stored(&case, expected), + &plan, + None, + ) + .unwrap() + .await + .unwrap(); + assert_eq!(opened.risky_ref(), source(&case).risky_ref()); + let plain: String = cipher + .decrypt( + CipherText::Single(SealedValue::from_bytes(&unhex(&expected.ciphertext)).unwrap()), + ctx, + ) + .await + .unwrap(); + assert_eq!(plain, "hello"); + + // Native public chains: repeated calls, not one preassembled extension. + if !case.parts.iter().any(|p| p.text.is_empty()) { + let note = Note { + tenant: case.label.clone(), + text: "hello".into(), + }; + let fields = if case.base == "field" { + Plan::fields().context_field(pick("tenant", |n: &Note| &n.tenant)) + } else { + Plan::context(case.label.as_str()).fields() + }; + let typed = fields + .encrypt_index(pick("text", |n: &Note| &n.text), Equality) + .build() + .unwrap(); + let mut chain = cipher.encrypt(¬e).using(&typed); + for p in &case.parts { + chain = chain.extend(extension(p)); + } + // A no-op extension must not clear earlier calls (#1120). + let mut row = chain.extend(()).await.unwrap(); + let field_plan = typed.field("text").unwrap(); + let mut query = cipher.query(stack_encrypt::op::Eq, "hello"); + if case.base == "field" { + query = query.context(case.label.as_str()); + } + let mut query = query.on(&field_plan); + for p in &case.parts { + query = query.extend(extension(p)); + } + let query: EqualityTerm = query.extend(()).await.unwrap(); + assert_eq!(hex(&query.to_bytes()), expected.equality); + let field: Encrypted = row.take("text").unwrap(); + assert_eq!(hex(&field.terms.to_bytes()), expected.equality); + row.insert("text", field); + let mut open = cipher.open(row).using(&typed); + for p in &case.parts { + open = open.extend(extension(p)); + } + let mut opened = open.extend(()).await.unwrap(); + assert_eq!(opened.take::("text").unwrap(), "hello"); + } + vectors.insert(case.id, vector); + } + if update { + std::fs::write(path, serde_json::to_string_pretty(&vectors).unwrap() + "\n").unwrap(); + } else { + assert_eq!( + baseline.keys().collect::>(), + vectors.keys().collect::>() + ); + } +} + +#[test] +fn grouped_extensions_preserve_their_shape() { + let own = || NonEmpty::from(Label::parse("users/age").unwrap()); + let separate = DeclaredContext::default() + .with(7u64) + .with(NonEmpty::new("eu").unwrap()) + .under(own()); + let grouped = DeclaredContext::from(NonEmpty::from(7u64).with("eu")).under(own()); + assert_eq!( + Descriptor::of(separate.clone()).as_str(), + "((users/age)/7u64)/eu" + ); + assert_eq!( + Descriptor::of(grouped.clone()).as_str(), + "(users/age)/(7u64/eu)" + ); + assert_ne!( + separate.into_aad().as_bytes(), + grouped.into_aad().as_bytes() + ); +} + +#[derive(Deserialize)] +#[serde(deny_unknown_fields)] +struct Boundaries { + version: u32, + profile: String, + cases: Vec, +} +#[derive(Deserialize)] +#[serde(deny_unknown_fields)] +struct Boundary { + id: String, + tenants: Vec, + expected: String, + mutation: String, + outcome: String, + retrievals: usize, +} + +#[tokio::test] +async fn shared_context_boundaries() { + use std::sync::atomic::Ordering; + let corpus: Boundaries = serde_json::from_str(include_str!( + "../../../conformance/stack-encrypt/v1/context-boundaries.json" + )) + .unwrap(); + assert_eq!(corpus.version, 1); + assert_eq!(corpus.profile, "context-boundaries-v1"); + assert!(!corpus.cases.is_empty()); + let mut ids = BTreeSet::new(); + let case = Case { + id: "boundary".into(), + base: "field".into(), + label: "tenants/acme".into(), + parts: vec![], + }; + let plan = plan(&case); + for case in corpus.cases { + assert!(!case.id.is_empty() && ids.insert(case.id.clone())); + eprintln!("context boundary: {}", case.id); + let (cipher, generates, retrieves) = common::counting_cipher().await; + let keyset = cipher.default_keyset(); + let rows = frame( + case.tenants + .iter() + .map(|tenant| { + object(vec![ + ("tenant", frame(tenant.clone())), + ("text", frame("hello")), + ]) + }) + .collect::>(), + ); + let mut stored = record::encrypt(&keyset, rows.clone(), &plan) + .unwrap() + .await + .unwrap(); + let CipherText::Sequence(batch) = &mut stored else { + panic!("batch") + }; + match case.mutation.as_str() { + "none" => {} + "tamper" | "empty" | "missing" => { + let CipherText::Map(fields) = batch.last_mut().expect("mutation requires a row") + else { + panic!("row") + }; + if case.mutation == "missing" { + fields.retain(|(k, _)| k != "tenant"); + } else { + let tenant = &mut fields.iter_mut().find(|(k, _)| k == "tenant").unwrap().1; + let text = if case.mutation == "tamper" { + "tenants/globex" + } else { + "" + }; + *tenant = CipherText::Map(vec![( + "passthrough".into(), + CipherText::Passthrough(common::passthrough(text)), + )]); + } + } + _ => panic!("unknown mutation"), + } + generates.store(0, Ordering::SeqCst); + retrieves.store(0, Ordering::SeqCst); + let expected = if case.expected.is_empty() { + None + } else { + Some(Label::parse(&case.expected).unwrap()) + }; + let result = match record::decrypt(Scope::Keyset(keyset.clone()), stored, &plan, expected) { + Ok(pending) => pending.await.map_err(|e| e.code().unwrap().to_string()), + Err(error) => Err(error.code().unwrap().to_string()), + }; + match case.outcome.as_str() { + "success" => assert_eq!(result.unwrap().risky_ref(), rows.risky_ref()), + "mismatch" => assert_eq!(result.unwrap_err(), "stack_encrypt::context_mismatch"), + "authentication" => assert_eq!(result.unwrap_err(), "stack_encrypt::aead"), + "shape" => { + let code = if case.mutation == "missing" { + "stack_encrypt::dynamic_record" + } else { + "stack_encrypt::plan_context_label" + }; + assert_eq!(result.unwrap_err(), code); + } + _ => panic!("unknown outcome"), + } + assert_eq!( + retrieves.load(Ordering::SeqCst), + case.retrievals, + "{}", + case.id + ); + assert_eq!(generates.load(Ordering::SeqCst), 0); + } +} + +#[tokio::test] +async fn a_grouped_extension_remains_one_part_when_appended() { + let (cipher, sent) = common::recording_cipher().await; + let plan = Plan::context("users/age") + .with::(Equality) + .build() + .unwrap(); + let grouped = || NonEmpty::from(7u64).with("eu"); + let out = cipher + .encrypt(&34u32) + .using(&plan) + .extend(grouped()) + .extend(9u64) + .await + .unwrap(); + assert_eq!( + sent.lock().unwrap().generated(), + ["((users/age)/(7u64/eu))/9u64"] + ); + let query = cipher + .query(stack_encrypt::op::Eq, &34u32) + .on(&plan) + .extend(grouped()) + .extend(9u64) + .await + .unwrap(); + assert_eq!(out.terms, query); + let back: u32 = cipher + .open(out) + .using(&plan) + .extend(grouped()) + .extend(9u64) + .await + .unwrap(); + assert_eq!(back, 34); + // The record lowering accepts scalar extension parts, not this native-only group. + let grouped_context = frame(vec![ + frame(vec!["users", "age"]), + frame(vec![frame(7u64), frame("eu")]), + ]); + let declaration = object(vec![( + "age", + object(vec![ + ("context", grouped_context), + ("outputs", frame(vec!["c"])), + ]), + )]); + assert!(record::plan(declaration.risky_ref()).is_err()); +} diff --git a/packages/stack-encrypt/tests/plan.rs b/packages/stack-encrypt/tests/plan.rs index c578619c8..ef5734fc7 100644 --- a/packages/stack-encrypt/tests/plan.rs +++ b/packages/stack-encrypt/tests/plan.rs @@ -557,10 +557,9 @@ async fn an_index_only_field_matches_its_query_and_mints_no_data_key() { assert_eq!(matching.to_bytes(), sealed.terms.1.to_bytes()); } -/// A second `.extend(..)` replaces the first: every data key is asked for -/// under the last extension only, and the record opens with it alone. +/// Repeated extensions append in order, and opening needs both parts. #[tokio::test] -async fn a_second_extend_replaces_the_first() { +async fn a_second_extend_appends_to_the_first() { let (cipher, sent) = recording_cipher().await; let users_plan: Plan = users_plan(); let row = cipher @@ -573,12 +572,17 @@ async fn a_second_extend_replaces_the_first() { assert_eq!( sent.lock().unwrap().generated(), [ - "(users/email)/7u64", - "(users/age)/7u64", - "(users/notes)/7u64" + "((users/email)/1u64)/7u64", + "((users/age)/1u64)/7u64", + "((users/notes)/1u64)/7u64" ] ); - let back = cipher.open(row).using(&users_plan).extend(7u64).await; + let back = cipher + .open(row) + .using(&users_plan) + .extend(1u64) + .extend(7u64) + .await; assert!(back.is_ok(), "{back:?}"); } diff --git a/packages/stack-encrypt/tests/record_lowering.rs b/packages/stack-encrypt/tests/record_lowering.rs index ce5e7f22a..6ec783dd8 100644 --- a/packages/stack-encrypt/tests/record_lowering.rs +++ b/packages/stack-encrypt/tests/record_lowering.rs @@ -840,3 +840,8 @@ async fn the_fixture_records_are_bound_to_their_labels() { Ok(_) => panic!("a leaf under another field's label opened"), } } + +#[test] +fn enrolled_context_tests_are_registered() { + common::assert_fixture_tests_registered("record_lowering"); +} diff --git a/scripts/__tests__/stack-conformance-ci.test.mjs b/scripts/__tests__/stack-conformance-ci.test.mjs index 25501e67d..2948c1944 100644 --- a/scripts/__tests__/stack-conformance-ci.test.mjs +++ b/scripts/__tests__/stack-conformance-ci.test.mjs @@ -44,6 +44,8 @@ describe('shared Stack Encrypt conformance is required in CI', () => { expect(script).toMatch(/cargo nextest run[^\n]*--test declarations/) expect(script).toMatch(/cargo nextest run[^\n]*--test runtime_conformance/) expect(script).toMatch(/cargo nextest run[^\n]*--test read_conformance/) + expect(script).toMatch(/cargo nextest run[^\n]*--test context_conformance/) + expect(script).toMatch(/cargo nextest run[^\n]*--test plan_grammar/) expect(script).toMatch(/cargo nextest run[^\n]*--test fixture_safety/) const selected = goSelection(script) for (const test of [ @@ -51,6 +53,8 @@ describe('shared Stack Encrypt conformance is required in CI', () => { 'TestDeclarationConformance', 'TestRuntimeConformance', 'TestReadConformance', + 'TestContextConformance', + 'TestContextBoundaries', 'TestFixtureConformanceRequiresGuest', ]) { expect(selected.test(test), test).toBe(true) @@ -67,6 +71,8 @@ describe('shared Stack Encrypt conformance is required in CI', () => { 'TestDeclarationConformance', 'TestRuntimeConformance', 'TestReadConformance', + 'TestContextConformance', + 'TestContextBoundaries', 'TestFixtureConformanceRequiresGuest', ...index.profiles.flatMap((p) => p.requirements.flatMap((r) => r.go_tests), @@ -97,6 +103,7 @@ describe('shared Stack Encrypt conformance is required in CI', () => { expect(Object.keys(index).sort()).toEqual(['profiles', 'version']) expect(index.version).toBe(1) expect(index.profiles.map((profile) => profile.id).sort()).toEqual([ + 'record-context-fields-v1', 'record-query-operators-v1', 'record-value-kinds-v1', ]) @@ -140,7 +147,7 @@ describe('shared Stack Encrypt conformance is required in CI', () => { 'id', 'rust_tests', ]) - expect(requirement.id).toMatch(/^(values|queries)\.[a-z-]+$/) + expect(requirement.id).toMatch(/^(values|queries|contexts)\.[a-z-]+$/) expect(requirements.has(requirement.id), requirement.id).toBe(false) requirements.add(requirement.id) expect(contract).toContain(`\`${requirement.id}\``) @@ -160,7 +167,7 @@ describe('shared Stack Encrypt conformance is required in CI', () => { } } const documented = [ - ...contract.matchAll(/\| `((?:values|queries)\.[a-z-]+)` \|/g), + ...contract.matchAll(/\| `((?:values|queries|contexts)\.[a-z-]+)` \|/g), ].map((match) => match[1]) expect([...requirements].sort()).toEqual(documented.sort()) }) diff --git a/scripts/stack-conformance-selection.mjs b/scripts/stack-conformance-selection.mjs index d3ca1e787..905e7b957 100644 --- a/scripts/stack-conformance-selection.mjs +++ b/scripts/stack-conformance-selection.mjs @@ -9,6 +9,8 @@ export function goSelection(index) { 'TestDeclarationConformance', 'TestRuntimeConformance', 'TestReadConformance', + 'TestContextConformance', + 'TestContextBoundaries', 'TestFixtureConformanceRequiresGuest', ]) for (const profile of index.profiles) { diff --git a/scripts/stack-conformance.sh b/scripts/stack-conformance.sh index 025c834a4..53cf2780a 100644 --- a/scripts/stack-conformance.sh +++ b/scripts/stack-conformance.sh @@ -19,7 +19,7 @@ go_tests=$(node scripts/stack-conformance-selection.mjs) artifacts=$(mktemp -d) trap 'rm -rf "$artifacts"' EXIT -mise x --env test -- cargo nextest run --locked -p stack-encrypt --all-features --test conformance --test declarations --test runtime_conformance --test read_conformance --test value_kinds --test query_operators --test fixture_safety +mise x --env test -- cargo nextest run --locked -p stack-encrypt --all-features --test conformance --test declarations --test runtime_conformance --test read_conformance --test context_conformance --test record_lowering --test plan_grammar --test value_kinds --test query_operators --test fixture_safety ( cd languages/golang STACK_CONFORMANCE_OUTPUT="$artifacts/go.json" CGO_ENABLED=0 \ From d2a0ef3ee32792dd21cf649f3b7a406a9c79ed71 Mon Sep 17 00:00:00 2001 From: Dan Draper Date: Sun, 1 Nov 2026 21:11:02 +1100 Subject: [PATCH 2/4] fix(stack-encrypt)!: address context review Expand query, batch, generated-API and guest-wire conformance. Pin compatibility with records sealed under replacement semantics. BREAKING CHANGE: Repeated extensions append. Old records and query terms require the final extension alone until re-encrypted and re-indexed. Refs #1475, #1120 --- conformance/stack-encrypt/context-contract.md | 50 +++-- conformance/stack-encrypt/coverage.md | 5 +- .../stack-encrypt/v1/context-boundaries.json | 60 +++++- .../stack-encrypt/v1/contexts-native.json | 18 ++ docs/plans/2026-10-04-plan-builder.md | 2 +- .../2026-10-31-stack-encrypt-conformance.md | 13 +- .../encrypt/context_conformance_test.go | 193 ++++++++++++++--- .../golang/encrypt/context_field_test.go | 2 +- languages/golang/encrypt/export_test.go | 28 +++ .../internal/testusers/context_note.go | 9 + .../internal/testusers/literalnote_stash.go | 119 +++++++++++ packages/stack-encrypt/CHANGELOG.md | 9 +- packages/stack-encrypt/src/dynamic/record.rs | 15 ++ .../tests/context_conformance.rs | 198 ++++++++++++++++-- .../stack-encrypt/tests/record_lowering.rs | 2 +- .../__tests__/stack-conformance-ci.test.mjs | 23 +- scripts/stack-conformance-selection.mjs | 20 +- 17 files changed, 652 insertions(+), 114 deletions(-) create mode 100644 languages/golang/encrypt/internal/testusers/context_note.go create mode 100644 languages/golang/encrypt/internal/testusers/literalnote_stash.go diff --git a/conformance/stack-encrypt/context-contract.md b/conformance/stack-encrypt/context-contract.md index ef7d5fcb3..77793ac66 100644 --- a/conformance/stack-encrypt/context-contract.md +++ b/conformance/stack-encrypt/context-contract.md @@ -34,10 +34,13 @@ Rust's lower-level context APIs; the dynamic record grammar and Go `Extend` accept scalar extension parts only. Their refusal of a grouped extension is part of the supported API boundary, not permission to flatten it. -These tests fix #1120: repeated Rust chain extensions previously replaced the -first extension. Previously written records from such chains still require the +Previously, repeated Rust chain extensions replaced earlier parts. Records +written by those chains still require the **last extension alone** when opening them; callers must explicitly retain that -old context when reading old records. This change does not rewrite ciphertext. +old context when reading and querying old records. A query using repeated +extensions now succeeds with different terms, silently missing old rows. +Re-encrypt and re-index old rows to adopt the appended context. This change +does not rewrite ciphertext or migrate stored terms. The native base and each appended scalar retain their type and structure. `7i32`, `7i64`, `7u64`, text `eu`, and bytes `6575` do not collapse to one AAD @@ -57,17 +60,21 @@ Every case encrypts and queries the string `hello` in the `text` field, using the public all-zero deterministic-provider test seed. `v1/contexts-native.json` is keyed by exactly those case IDs. It pins the -context frame, descriptor, AAD, PRF bytes, one Rust ciphertext and equality -term. Rust checks actual descriptors sent to the key provider, fresh typed and +resolved context frame, the field context frame sent in a plan, descriptor, +AAD, PRF bytes, one Rust ciphertext and equality term. Rust checks actual descriptors sent to the key provider, fresh typed and dynamic writes, queries, opens, and frozen ciphertexts. The typed chain calls -`extend` repeatedly, including a final no-op, so #1120 cannot regress unnoticed. - -Go compares its exact context tree encoding against the frozen frame and opens -the Rust ciphertext. It compares fresh stored and query terms, both variadic -and repeated extensions, and generated `Note` methods for context-field cases. -Go does not expose a separate descriptor/AAD encoder: equality of the input -frame and successful authenticated cross-opening are its evidence for those -observations. Ciphertext bytes are randomized and are not compared for equality. +`extend` repeatedly, including a final no-op, so replacement behavior cannot +regress unnoticed. + +Go observes the actual plan buffer at the `se_encrypt_record` WASM boundary +through a test-only function wrapper. It compares the field context in those +bytes with the frozen native plan-context frame, then opens the Rust ciphertext. +It compares fresh stored and query terms, both variadic and repeated extensions, +and generated `Note` (field base) and `LiteralNote` (literal base) methods. +Go does not expose a separate descriptor/AAD encoder: matching the actual plan +and successful authenticated cross-opening provide indirect evidence for those +observations. Native Rust checks the canonical descriptor/AAD/PRF bytes directly. +Ciphertext bytes are randomized and are not compared for equality. Regenerate deliberately with `STACK_ENCRYPT_UPDATE_FIXTURES=1` and the native `context_conformance` binary. Required conformance rejects fixture updates. @@ -77,13 +84,22 @@ Changes to frozen context encodings require compatibility review. Boundary cases specify rows' `tenants`, the caller's `expected` label (the empty string explicitly means no expectation in this corpus), a mutation of the last -stored row, an `outcome`, and native `retrievals`. All properties are required; +stored row, an `outcome`, and `native_retrievals`. All properties are required; null is not an alternative spelling for omission or the empty string. Cases cover unrestricted reads, matching expectations, late mismatches, empty batches, changed stored labels with and without matching expectations, and -missing/empty stored contexts late in a batch. Key-independent refusals must -make no retrieval. An empty valid batch also makes none. Changing the stored +missing/empty stored contexts late in a batch. A two-row ciphertext-tampering +case reaches AEAD after retrieval in both languages and returns no partial +plaintext. Typed Rust opening and generated Go opening exercise the boundary +cases too, except that an absent member cannot be represented by a generated Go +struct and is tested at the raw-tree boundary. + +The separate `queries` array specifies a caller `context`, an `outcome` and +whether the query `matches` the stored term for `tenants/acme`. Missing and +invalid contexts refuse; another tenant produces a valid nonmatching query. +Rust typed query chains and generated Go query methods execute these cases. +Key-independent refusals must make no retrieval. An empty valid batch also makes none. Changing the stored label does not confer permission to open the ciphertext even if the caller's expectation matches the replacement label. @@ -105,6 +121,6 @@ labels, caller-derived queries and empty-batch validation before keyset loading. This profile describes the current shared context: it feeds the data-key descriptor, ciphertext AAD and index PRF context. It does not introduce separate -ciphertext-binding and search-scope contexts. That API design remains #1487; +ciphertext-binding and search-scope contexts. That API design remains separate work; any future split needs explicit compatibility rules and new profile coverage. It also does not assert plaintext erasure in Go, Python or TypeScript heaps. diff --git a/conformance/stack-encrypt/coverage.md b/conformance/stack-encrypt/coverage.md index 05f6a9d05..47f204db3 100644 --- a/conformance/stack-encrypt/coverage.md +++ b/conformance/stack-encrypt/coverage.md @@ -95,13 +95,14 @@ adding a language does not create another copy of the expected behavior. ## Context checkpoint The [context contract](context-contract.md) adds 18 shared composition cases and -nine shared boundary cases, plus enrollment of the existing record-lowering +eleven shared read boundary cases and four query cases, plus enrollment of the +existing record-lowering context-field fixture. Rust typed write/query/open chains and Go generated methods execute the composition cases. Required native plan-grammar tests cover caller-provided bases and empty-batch source validation. Existing context tests remain: no mixed-purpose Rust or Go test is removed. -Go proves canonical AAD indirectly through its exact context frame and native +Go proves canonical AAD indirectly through its actual guest plan frame and native ciphertext opening; provider-call instrumentation remains a separate gap. Nested composite extensions remain a native API capability rather than an unsupported feature silently advertised by the Go/dynamic record profile. diff --git a/conformance/stack-encrypt/v1/context-boundaries.json b/conformance/stack-encrypt/v1/context-boundaries.json index 7a3470595..bda82bbf3 100644 --- a/conformance/stack-encrypt/v1/context-boundaries.json +++ b/conformance/stack-encrypt/v1/context-boundaries.json @@ -8,7 +8,7 @@ "expected": "", "mutation": "none", "outcome": "success", - "retrievals": 1 + "native_retrievals": 1 }, { "id": "expected", @@ -16,7 +16,7 @@ "expected": "tenants/acme", "mutation": "none", "outcome": "success", - "retrievals": 1 + "native_retrievals": 1 }, { "id": "late-mismatch", @@ -24,7 +24,7 @@ "expected": "tenants/acme", "mutation": "none", "outcome": "mismatch", - "retrievals": 0 + "native_retrievals": 0 }, { "id": "empty", @@ -32,7 +32,7 @@ "expected": "tenants/acme", "mutation": "none", "outcome": "success", - "retrievals": 0 + "native_retrievals": 0 }, { "id": "tampered", @@ -40,7 +40,7 @@ "expected": "", "mutation": "tamper", "outcome": "authentication", - "retrievals": 1 + "native_retrievals": 1 }, { "id": "tampered-matching-expectation", @@ -48,7 +48,7 @@ "expected": "tenants/globex", "mutation": "tamper", "outcome": "authentication", - "retrievals": 1 + "native_retrievals": 1 }, { "id": "tampered-mismatching-expectation", @@ -56,7 +56,7 @@ "expected": "tenants/acme", "mutation": "tamper", "outcome": "mismatch", - "retrievals": 0 + "native_retrievals": 0 }, { "id": "late-missing", @@ -64,7 +64,7 @@ "expected": "", "mutation": "missing", "outcome": "shape", - "retrievals": 0 + "native_retrievals": 0 }, { "id": "late-empty", @@ -72,7 +72,49 @@ "expected": "", "mutation": "empty", "outcome": "shape", - "retrievals": 0 + "native_retrievals": 0 + }, + { + "id": "late-tampered-context", + "tenants": ["tenants/acme", "tenants/acme"], + "expected": "", + "mutation": "tamper", + "outcome": "authentication", + "native_retrievals": 1 + }, + { + "id": "late-tampered-ciphertext", + "tenants": ["tenants/acme", "tenants/acme"], + "expected": "", + "mutation": "ciphertext", + "outcome": "aead", + "native_retrievals": 1 + } + ], + "queries": [ + { + "id": "query-missing", + "context": "", + "outcome": "refused", + "matches": false + }, + { + "id": "query-invalid", + "context": "tenants/(acme)", + "outcome": "refused", + "matches": false + }, + { + "id": "query-matching", + "context": "tenants/acme", + "outcome": "success", + "matches": true + }, + { + "id": "query-other-tenant", + "context": "tenants/globex", + "outcome": "success", + "matches": false } ] } diff --git a/conformance/stack-encrypt/v1/contexts-native.json b/conformance/stack-encrypt/v1/contexts-native.json index e73501abe..44089fc94 100644 --- a/conformance/stack-encrypt/v1/contexts-native.json +++ b/conformance/stack-encrypt/v1/contexts-native.json @@ -1,6 +1,7 @@ { "field-base": { "context_frame": "f0030000000a0700000074656e616e74730a0400000061636d650a0400000074657874", + "plan_context_frame": "f0010000000a0400000074657874", "descriptor": "tenants/acme/text", "aad": "0300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000074657874", "prf": "0300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000074657874", @@ -9,6 +10,7 @@ }, "field-bytes": { "context_frame": "f002000000f0030000000a0700000074656e616e74730a0400000061636d650a04000000746578740b020000006575", + "plan_context_frame": "f002000000f0010000000a04000000746578740b020000006575", "descriptor": "(tenants/acme/text)/eu", "aad": "020000000000000022010000000000000300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000074657874540000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311900000000000000766974616d696e632f636f6e746578742f62797465732f763102000000000000006575", "prf": "020000000000000022010000000000000300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000074657874540000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311900000000000000766974616d696e632f636f6e746578742f62797465732f763102000000000000006575", @@ -17,6 +19,7 @@ }, "field-empty-part": { "context_frame": "f002000000f0030000000a0700000074656e616e74730a0400000061636d650a04000000746578740a00000000", + "plan_context_frame": "f002000000f0010000000a04000000746578740a00000000", "descriptor": "(tenants/acme/text)/b64:", "aad": "020000000000000022010000000000000300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000074657874510000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f76310000000000000000", "prf": "020000000000000022010000000000000300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000074657874510000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f76310000000000000000", @@ -25,6 +28,7 @@ }, "field-narrow": { "context_frame": "f002000000f0030000000a0700000074656e616e74730a0400000061636d650a04000000746578740407000000", + "plan_context_frame": "f002000000f0010000000a04000000746578740407000000", "descriptor": "(tenants/acme/text)/7u32", "aad": "020000000000000022010000000000000300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000074657874570000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311a00000000000000766974616d696e632f636f6e746578742f6933322d6c652f7631040000000000000007000000", "prf": "020000000000000022010000000000000300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000074657874570000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311a00000000000000766974616d696e632f636f6e746578742f6933322d6c652f7631040000000000000007000000", @@ -33,6 +37,7 @@ }, "field-one": { "context_frame": "f002000000f0030000000a0700000074656e616e74730a0400000061636d650a0400000074657874070700000000000000", + "plan_context_frame": "f002000000f0010000000a0400000074657874070700000000000000", "descriptor": "(tenants/acme/text)/7u64", "aad": "020000000000000022010000000000000300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f76310400000000000000746578745b0000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311a00000000000000766974616d696e632f636f6e746578742f7536342d6c652f763108000000000000000700000000000000", "prf": "020000000000000022010000000000000300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f76310400000000000000746578745b0000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311a00000000000000766974616d696e632f636f6e746578742f7536342d6c652f763108000000000000000700000000000000", @@ -41,6 +46,7 @@ }, "field-ordered": { "context_frame": "f002000000f002000000f0030000000a0700000074656e616e74730a0400000061636d650a04000000746578740707000000000000000a020000006575", + "plan_context_frame": "f002000000f002000000f0010000000a04000000746578740707000000000000000a020000006575", "descriptor": "((tenants/acme/text)/7u64)/eu", "aad": "02000000000000009501000000000000020000000000000022010000000000000300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f76310400000000000000746578745b0000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311a00000000000000766974616d696e632f636f6e746578742f7536342d6c652f763108000000000000000700000000000000530000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f763102000000000000006575", "prf": "02000000000000009501000000000000020000000000000022010000000000000300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f76310400000000000000746578745b0000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311a00000000000000766974616d696e632f636f6e746578742f7536342d6c652f763108000000000000000700000000000000530000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f763102000000000000006575", @@ -49,6 +55,7 @@ }, "field-reversed": { "context_frame": "f002000000f002000000f0030000000a0700000074656e616e74730a0400000061636d650a04000000746578740a020000006575070700000000000000", + "plan_context_frame": "f002000000f002000000f0010000000a04000000746578740a020000006575070700000000000000", "descriptor": "((tenants/acme/text)/eu)/7u64", "aad": "02000000000000008d01000000000000020000000000000022010000000000000300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000074657874530000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631020000000000000065755b0000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311a00000000000000766974616d696e632f636f6e746578742f7536342d6c652f763108000000000000000700000000000000", "prf": "02000000000000008d01000000000000020000000000000022010000000000000300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000074657874530000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631020000000000000065755b0000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311a00000000000000766974616d696e632f636f6e746578742f7536342d6c652f763108000000000000000700000000000000", @@ -57,6 +64,7 @@ }, "field-signed": { "context_frame": "f002000000f0030000000a0700000074656e616e74730a0400000061636d650a0400000074657874050700000000000000", + "plan_context_frame": "f002000000f0010000000a0400000074657874050700000000000000", "descriptor": "(tenants/acme/text)/7u64", "aad": "020000000000000022010000000000000300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f76310400000000000000746578745b0000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311a00000000000000766974616d696e632f636f6e746578742f6936342d6c652f763108000000000000000700000000000000", "prf": "020000000000000022010000000000000300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f76310400000000000000746578745b0000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311a00000000000000766974616d696e632f636f6e746578742f6936342d6c652f763108000000000000000700000000000000", @@ -65,6 +73,7 @@ }, "field-text": { "context_frame": "f002000000f0030000000a0700000074656e616e74730a0400000061636d650a04000000746578740a020000006575", + "plan_context_frame": "f002000000f0010000000a04000000746578740a020000006575", "descriptor": "(tenants/acme/text)/eu", "aad": "020000000000000022010000000000000300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000074657874530000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f763102000000000000006575", "prf": "020000000000000022010000000000000300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000074657874530000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f763102000000000000006575", @@ -73,6 +82,7 @@ }, "literal-base": { "context_frame": "f0030000000a0700000074656e616e74730a0400000061636d650a0400000074657874", + "plan_context_frame": "f0030000000a0700000074656e616e74730a0400000061636d650a0400000074657874", "descriptor": "tenants/acme/text", "aad": "0300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000074657874", "prf": "0300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000074657874", @@ -81,6 +91,7 @@ }, "literal-bytes": { "context_frame": "f002000000f0030000000a0700000074656e616e74730a0400000061636d650a04000000746578740b020000006575", + "plan_context_frame": "f002000000f0030000000a0700000074656e616e74730a0400000061636d650a04000000746578740b020000006575", "descriptor": "(tenants/acme/text)/eu", "aad": "020000000000000022010000000000000300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000074657874540000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311900000000000000766974616d696e632f636f6e746578742f62797465732f763102000000000000006575", "prf": "020000000000000022010000000000000300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000074657874540000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311900000000000000766974616d696e632f636f6e746578742f62797465732f763102000000000000006575", @@ -89,6 +100,7 @@ }, "literal-empty-part": { "context_frame": "f002000000f0030000000a0700000074656e616e74730a0400000061636d650a04000000746578740a00000000", + "plan_context_frame": "f002000000f0030000000a0700000074656e616e74730a0400000061636d650a04000000746578740a00000000", "descriptor": "(tenants/acme/text)/b64:", "aad": "020000000000000022010000000000000300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000074657874510000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f76310000000000000000", "prf": "020000000000000022010000000000000300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000074657874510000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f76310000000000000000", @@ -97,6 +109,7 @@ }, "literal-narrow": { "context_frame": "f002000000f0030000000a0700000074656e616e74730a0400000061636d650a04000000746578740407000000", + "plan_context_frame": "f002000000f0030000000a0700000074656e616e74730a0400000061636d650a04000000746578740407000000", "descriptor": "(tenants/acme/text)/7u32", "aad": "020000000000000022010000000000000300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000074657874570000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311a00000000000000766974616d696e632f636f6e746578742f6933322d6c652f7631040000000000000007000000", "prf": "020000000000000022010000000000000300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000074657874570000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311a00000000000000766974616d696e632f636f6e746578742f6933322d6c652f7631040000000000000007000000", @@ -105,6 +118,7 @@ }, "literal-one": { "context_frame": "f002000000f0030000000a0700000074656e616e74730a0400000061636d650a0400000074657874070700000000000000", + "plan_context_frame": "f002000000f0030000000a0700000074656e616e74730a0400000061636d650a0400000074657874070700000000000000", "descriptor": "(tenants/acme/text)/7u64", "aad": "020000000000000022010000000000000300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f76310400000000000000746578745b0000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311a00000000000000766974616d696e632f636f6e746578742f7536342d6c652f763108000000000000000700000000000000", "prf": "020000000000000022010000000000000300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f76310400000000000000746578745b0000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311a00000000000000766974616d696e632f636f6e746578742f7536342d6c652f763108000000000000000700000000000000", @@ -113,6 +127,7 @@ }, "literal-ordered": { "context_frame": "f002000000f002000000f0030000000a0700000074656e616e74730a0400000061636d650a04000000746578740707000000000000000a020000006575", + "plan_context_frame": "f002000000f002000000f0030000000a0700000074656e616e74730a0400000061636d650a04000000746578740707000000000000000a020000006575", "descriptor": "((tenants/acme/text)/7u64)/eu", "aad": "02000000000000009501000000000000020000000000000022010000000000000300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f76310400000000000000746578745b0000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311a00000000000000766974616d696e632f636f6e746578742f7536342d6c652f763108000000000000000700000000000000530000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f763102000000000000006575", "prf": "02000000000000009501000000000000020000000000000022010000000000000300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f76310400000000000000746578745b0000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311a00000000000000766974616d696e632f636f6e746578742f7536342d6c652f763108000000000000000700000000000000530000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f763102000000000000006575", @@ -121,6 +136,7 @@ }, "literal-reversed": { "context_frame": "f002000000f002000000f0030000000a0700000074656e616e74730a0400000061636d650a04000000746578740a020000006575070700000000000000", + "plan_context_frame": "f002000000f002000000f0030000000a0700000074656e616e74730a0400000061636d650a04000000746578740a020000006575070700000000000000", "descriptor": "((tenants/acme/text)/eu)/7u64", "aad": "02000000000000008d01000000000000020000000000000022010000000000000300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000074657874530000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631020000000000000065755b0000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311a00000000000000766974616d696e632f636f6e746578742f7536342d6c652f763108000000000000000700000000000000", "prf": "02000000000000008d01000000000000020000000000000022010000000000000300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000074657874530000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631020000000000000065755b0000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311a00000000000000766974616d696e632f636f6e746578742f7536342d6c652f763108000000000000000700000000000000", @@ -129,6 +145,7 @@ }, "literal-signed": { "context_frame": "f002000000f0030000000a0700000074656e616e74730a0400000061636d650a0400000074657874050700000000000000", + "plan_context_frame": "f002000000f0030000000a0700000074656e616e74730a0400000061636d650a0400000074657874050700000000000000", "descriptor": "(tenants/acme/text)/7u64", "aad": "020000000000000022010000000000000300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f76310400000000000000746578745b0000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311a00000000000000766974616d696e632f636f6e746578742f6936342d6c652f763108000000000000000700000000000000", "prf": "020000000000000022010000000000000300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f76310400000000000000746578745b0000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311a00000000000000766974616d696e632f636f6e746578742f6936342d6c652f763108000000000000000700000000000000", @@ -137,6 +154,7 @@ }, "literal-text": { "context_frame": "f002000000f0030000000a0700000074656e616e74730a0400000061636d650a04000000746578740a020000006575", + "plan_context_frame": "f002000000f0030000000a0700000074656e616e74730a0400000061636d650a04000000746578740a020000006575", "descriptor": "(tenants/acme/text)/eu", "aad": "020000000000000022010000000000000300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000074657874530000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f763102000000000000006575", "prf": "020000000000000022010000000000000300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000074657874530000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f763102000000000000006575", diff --git a/docs/plans/2026-10-04-plan-builder.md b/docs/plans/2026-10-04-plan-builder.md index 5037c67ad..8476eca52 100644 --- a/docs/plans/2026-10-04-plan-builder.md +++ b/docs/plans/2026-10-04-plan-builder.md @@ -1039,7 +1039,7 @@ The existing Go package has never been released, so these are removed, not depre - `EncryptRecord`, `EncryptRecords`, `DecryptRecord` and `DecryptRecords`, on `Cipher` and on `Client`. - `EncryptedRecord` and `EncryptedField`: a generated type replaces them. - `Cipher.Term`: the query methods of a field entry replace it. -- `RecordOption`, `WithPlan` and `ExtendContext`: `Cipher.Extend` replaces the last. +- `RecordOption`, `WithPlan` and `ExtendContext`: `Cipher.Extend` replaces the `ExtendContext` function. - `WithGuest`: the import of `encrypt/eql` chooses the guest build. - `TermKind`: `Index` replaces it, for generated code. - `Plan`, `FieldPlan`, `NewPlan` and `Plan.Validate`: the generator replaces them. diff --git a/docs/plans/2026-10-31-stack-encrypt-conformance.md b/docs/plans/2026-10-31-stack-encrypt-conformance.md index 359a93de7..4f55a7031 100644 --- a/docs/plans/2026-10-31-stack-encrypt-conformance.md +++ b/docs/plans/2026-10-31-stack-encrypt-conformance.md @@ -210,16 +210,17 @@ produce the specified diagnostics. Python and TypeScript remain visibly pending until their runners execute. The complete specification, SQL profile, and all language SDK mappings remain follow-up work. - -## Context composition checkpoint (#1475) +## Context composition checkpoint Implemented shared composition and boundary cases in `conformance/stack-encrypt/context-contract.md`: 18 typed extension cases, -nine read/preflight cases, generated Go calls and native typed chains. Existing +eleven read/preflight cases and four query cases, generated Go calls and native +typed chains. Existing context-field fixture tests and the native plan-grammar suite are required by -`test:conformance`. Repeated Rust `.extend()` now appends, resolving #1120; +`test:conformance`. Repeated Rust `.extend()` now appends, aligning Rust with Go; the changelog documents compatibility for records written under the old behavior. -The current shared binding/search context remains distinct from future #1487. +The current shared binding/search context remains distinct from the future +binding/search-scope split. -Next: #1476, the EQL/JSON operation profile. Python and TypeScript runner delivery +Next: the EQL/JSON operation profile. Python and TypeScript runner delivery and the wider fresh artifact exchange remain separate planned work. diff --git a/languages/golang/encrypt/context_conformance_test.go b/languages/golang/encrypt/context_conformance_test.go index 8b0722275..7d921a76f 100644 --- a/languages/golang/encrypt/context_conformance_test.go +++ b/languages/golang/encrypt/context_conformance_test.go @@ -25,12 +25,13 @@ type contextCase struct { Parts []contextPart `json:"parts"` } type contextVector struct { - ContextFrame string `json:"context_frame"` - Descriptor string `json:"descriptor"` - AAD string `json:"aad"` - PRF string `json:"prf"` - Ciphertext string `json:"ciphertext"` - Equality string `json:"equality"` + ContextFrame string `json:"context_frame"` + PlanContextFrame string `json:"plan_context_frame"` + Descriptor string `json:"descriptor"` + AAD string `json:"aad"` + PRF string `json:"prf"` + Ciphertext string `json:"ciphertext"` + Equality string `json:"equality"` } func TestContextConformance(t *testing.T) { @@ -113,26 +114,30 @@ func TestContextConformance(t *testing.T) { parts = append(parts, v) cipher = cipher.Extend(v) } - // Compare the precise context tree that native Rust encoded to AAD/PRF. - // Go has no independent public AAD or descriptor encoder. - resolved := *p - resolved.Context = label - resolved.ContextField = "" - resolved.Extension = parts - framed, e := vcffi.Marshal(resolved.FieldContext(p.Fields[0])) + // Inspect the bytes actually passed to se_encrypt_record, not a + // separately reconstructed copy of the host's plan. + var sentPlans [][]byte + restore := encrypt.ObserveEncryptPlansForTest(client, func(plan []byte) { sentPlans = append(sentPlans, plan) }) + fresh, e := cipher.Seal(ctx, p, source) + restore() if e != nil { t.Fatal(e) } - if hex.EncodeToString(framed) != want.ContextFrame { - t.Fatal("context tree differs from native") + if len(sentPlans) != 1 { + t.Fatalf("expected one encrypt call, got %d", len(sentPlans)) } - if want.Descriptor == "" || len(decode(want.AAD)) == 0 || len(decode(want.PRF)) == 0 { - t.Fatal("missing canonical context observations") + decoded, e := vcffi.Unmarshal(sentPlans[0]) + if e != nil { + t.Fatal(e) } - fresh, e := cipher.Seal(ctx, p, source) + field := contextObjectField(t, contextObjectField(t, decoded, "text"), "context") + framed, e := vcffi.Marshal(field) if e != nil { t.Fatal(e) } + if hex.EncodeToString(framed) != want.PlanContextFrame { + t.Fatal("actual guest context differs from native plan") + } if !bytes.Equal(fresh[0]["text"].Terms[record.Equality], decode(want.Equality)) { t.Fatal("stored term differs") } @@ -166,6 +171,28 @@ func TestContextConformance(t *testing.T) { if e != nil || !reflect.DeepEqual(q.Terms, query.Terms) { t.Fatalf("combined extensions: %v", e) } + if c.Base == "literal" { + if c.Label != "tenants/acme" { + t.Fatal("literal generated declaration has a fixed base") + } + notes := []testusers.LiteralNote{{Text: "hello"}} + sealed, err := testusers.EncryptLiteralNote(ctx, cipher, notes) + if err != nil { + t.Fatal(err) + } + if !sealed[0].Text.Equality.Equal(decode(want.Equality)) { + t.Fatal("literal generated write differs") + } + sealed[0].Text.Ciphertext = decode(want.Ciphertext) + opened, err := testusers.DecryptLiteralNote(ctx, cipher, sealed) + if err != nil || !reflect.DeepEqual(opened, notes) { + t.Fatalf("literal generated open: %v", err) + } + q, err := testusers.LiteralNoteFields.Text.Eq(ctx, cipher, "hello") + if err != nil || !q.Equal(decode(want.Equality)) { + t.Fatalf("literal generated query: %v", err) + } + } if c.Base == "field" { notes := []testusers.Note{{Tenant: c.Label, Text: "hello", ID: 0}} sealed, e := testusers.EncryptNote(ctx, cipher, notes) @@ -189,13 +216,20 @@ func TestContextConformance(t *testing.T) { } } +type contextQueryBoundary struct { + ID string `json:"id"` + Context string `json:"context"` + Outcome string `json:"outcome"` + Matches bool `json:"matches"` +} + type contextBoundary struct { - ID string `json:"id"` - Tenants []string `json:"tenants"` - Expected string `json:"expected"` - Mutation string `json:"mutation"` - Outcome string `json:"outcome"` - Retrievals int `json:"retrievals"` + ID string `json:"id"` + Tenants []string `json:"tenants"` + Expected string `json:"expected"` + Mutation string `json:"mutation"` + Outcome string `json:"outcome"` + NativeRetrievals int `json:"native_retrievals"` } func TestContextBoundaries(t *testing.T) { @@ -216,9 +250,10 @@ func TestContextBoundaries(t *testing.T) { } t.Cleanup(func() { _ = checker.Close() }) var corpus struct { - Version int `json:"version"` - Profile string `json:"profile"` - Cases []contextBoundary `json:"cases"` + Version int `json:"version"` + Profile string `json:"profile"` + Cases []contextBoundary `json:"cases"` + Queries []contextQueryBoundary `json:"queries"` } readConformanceJSON(t, filepath.Join("..", "..", "..", "conformance", "stack-encrypt", "v1", "context-boundaries.json"), &corpus) if corpus.Version != 1 || corpus.Profile != "context-boundaries-v1" || len(corpus.Cases) == 0 { @@ -247,6 +282,12 @@ func TestContextBoundaries(t *testing.T) { } switch c.Mutation { case "none": + case "ciphertext": + row := tree[len(tree)-1].(map[string]any) + field := row["text"].(map[string]any) + ciphertext := append(encrypt.Ciphertext(nil), field["c"].(encrypt.Ciphertext)...) + ciphertext[len(ciphertext)-1] ^= 1 + field["c"] = ciphertext case "tamper", "empty", "missing": if len(tree) == 0 { t.Fatal("mutation requires a row") @@ -267,7 +308,7 @@ func TestContextBoundaries(t *testing.T) { _, preflight := checker.OpenTreeForTest(ctx, tree, p, c.Expected) // An uninitialised guest cannot retrieve keys: valid shapes reach ErrState. switch c.Outcome { - case "success", "authentication": + case "success", "authentication", "aead": if !errors.Is(preflight, encrypt.ErrState) { t.Fatalf("preflight: %v", preflight) } @@ -282,7 +323,34 @@ func TestContextBoundaries(t *testing.T) { if c.Expected != "" { reader = reader.Context(c.Expected) } + // A generated Go struct cannot represent an absent tenant member; + // the raw-tree path above owns that shape refusal. + if c.Mutation != "missing" { + generated := make([]testusers.EncryptedNote, len(tree)) + for i, row := range tree { + fields := row.(map[string]any) + tenant := fields["tenant"].(map[string]any)["passthrough"].(vcvalue.Plain).V.(string) + ciphertext := fields["text"].(map[string]any)["c"].(encrypt.Ciphertext) + generated[i] = testusers.EncryptedNote{Tenant: tenant, Text: testusers.EncryptedNoteText{Ciphertext: ciphertext}} + } + opened, err := testusers.DecryptNote(ctx, reader, generated) + if c.Outcome == "success" { + if err != nil || len(opened) != len(c.Tenants) { + t.Fatalf("generated boundary: %v", err) + } + for i, row := range opened { + if row.Tenant != c.Tenants[i] || row.Text != "hello" { + t.Fatal("generated values differ") + } + } + } else if err == nil || len(opened) != 0 { + t.Fatalf("generated failure must return no rows: %v", err) + } + } opened, e := reader.OpenTreeForTest(ctx, tree, p) + if e != nil && len(opened) != 0 { + t.Fatal("batch failure returned partial plaintext") + } switch c.Outcome { case "success": if e != nil || !reflect.DeepEqual(opened, rows) { @@ -292,24 +360,61 @@ func TestContextBoundaries(t *testing.T) { if len(rows) == 0 { want = 0 } - if c.Retrievals != want { + if c.NativeRetrievals != want { t.Fatal("invalid retrieval expectation") } case "mismatch": - if !errors.Is(e, encrypt.ErrContextMismatch) || c.Retrievals != 0 { + if !errors.Is(e, encrypt.ErrContextMismatch) || c.NativeRetrievals != 0 { t.Fatalf("mismatch: %v", e) } case "shape": - if !errors.Is(e, encrypt.ErrEncoding) || c.Retrievals != 0 { + if !errors.Is(e, encrypt.ErrEncoding) || c.NativeRetrievals != 0 { t.Fatalf("shape: %v", e) } + case "aead": + if !errors.Is(e, encrypt.ErrAuthentication) || c.NativeRetrievals != 1 { + t.Fatalf("AEAD failure: %v", e) + } case "authentication": - if !errors.Is(e, encrypt.ErrForbidden) || c.Retrievals != 1 { + if !errors.Is(e, encrypt.ErrForbidden) || c.NativeRetrievals != 1 { t.Fatalf("tampering: %v", e) } } }) } + if len(corpus.Queries) == 0 { + t.Fatal("missing query boundaries") + } + for _, q := range corpus.Queries { + if q.ID == "" || seen[q.ID] { + t.Fatal("empty or repeated query ID") + } + seen[q.ID] = true + t.Run(q.ID, func(t *testing.T) { + cipher := client.DefaultKeyset() + stored, err := testusers.EncryptNote(ctx, cipher, []testusers.Note{{Tenant: "tenants/acme", Text: "hello"}}) + if err != nil { + t.Fatal(err) + } + if q.Context != "" { + cipher = cipher.Context(q.Context) + } + term, err := testusers.NoteFields.Text.Eq(ctx, cipher, "hello") + switch q.Outcome { + case "refused": + if !errors.Is(err, encrypt.ErrEncoding) || q.Matches { + t.Fatalf("query refusal: %v", err) + } + case "success": + if err != nil || term.Equal(stored[0].Text.Equality) != q.Matches { + t.Fatalf("query match: %v", err) + } + default: + t.Fatal("unknown query outcome") + } + }) + } + } // Go's zero values must not make a missing or null field valid when Rust's @@ -342,9 +447,29 @@ func (c *contextCase) UnmarshalJSON(data []byte) error { } func (v *contextVector) UnmarshalJSON(data []byte) error { type plain contextVector - return decodeRequiredContextJSON(data, (*plain)(v), "context_frame", "descriptor", "aad", "prf", "ciphertext", "equality") + return decodeRequiredContextJSON(data, (*plain)(v), "context_frame", "plan_context_frame", "descriptor", "aad", "prf", "ciphertext", "equality") } func (c *contextBoundary) UnmarshalJSON(data []byte) error { type plain contextBoundary - return decodeRequiredContextJSON(data, (*plain)(c), "id", "tenants", "expected", "mutation", "outcome", "retrievals") + return decodeRequiredContextJSON(data, (*plain)(c), "id", "tenants", "expected", "mutation", "outcome", "native_retrievals") +} + +func contextObjectField(t *testing.T, value any, name string) any { + t.Helper() + object, ok := value.(vcvalue.Object) + if !ok { + t.Fatalf("expected object, got %T", value) + } + for _, field := range object { + if field.Key == name { + return field.Value + } + } + t.Fatalf("missing field %s", name) + return nil +} + +func (q *contextQueryBoundary) UnmarshalJSON(data []byte) error { + type plain contextQueryBoundary + return decodeRequiredContextJSON(data, (*plain)(q), "id", "context", "outcome", "matches") } diff --git a/languages/golang/encrypt/context_field_test.go b/languages/golang/encrypt/context_field_test.go index 12b450b93..e9d2ee34a 100644 --- a/languages/golang/encrypt/context_field_test.go +++ b/languages/golang/encrypt/context_field_test.go @@ -233,7 +233,7 @@ func TestGeneratedCodeOpensTheRustContextFieldFixture(t *testing.T) { } } -// Required conformance must fail if its guest is absent. +// A missing guest skips optional runs and fails required conformance. func contextFixtureClient(t *testing.T) *encrypt.Client { t.Helper() c, err := encrypt.NewDeterministicClient(context.Background(), testSeed) diff --git a/languages/golang/encrypt/export_test.go b/languages/golang/encrypt/export_test.go index 2b500104e..56f8b7d13 100644 --- a/languages/golang/encrypt/export_test.go +++ b/languages/golang/encrypt/export_test.go @@ -11,6 +11,7 @@ import ( "github.com/cipherstash/stack/languages/golang/internal/guest" "github.com/cipherstash/stack/languages/golang/internal/record" "github.com/cipherstash/vitaminc/bindings/go/vcffi" + "github.com/tetratelabs/wazero/api" ) // The test-only ways to give a client a token. The public API takes tokens @@ -219,3 +220,30 @@ func (cph *Cipher) OpenTreeForTest(ctx context.Context, tree []any, plan *record func (k *Checker) OpenTreeForTest(ctx context.Context, tree []any, plan *record.Plan, expected string) ([]record.Source, error) { return k.c.openTree(ctx, k.c.DefaultKeyset().keyset, expected, plan, tree) } + +// ObserveEncryptPlansForTest copies the actual plan buffer at the WASM call +// boundary. Install and restore only while this test's client is idle. +func ObserveEncryptPlansForTest(c *Client, observe func([]byte)) func() { + original := c.inst.encryptRecord + c.inst.encryptRecord = &observedEncryptCall{Function: original, module: c.inst.module, observe: observe} + return func() { c.inst.encryptRecord = original } +} + +type observedEncryptCall struct { + api.Function + module api.Module + observe func([]byte) +} + +func (f *observedEncryptCall) Call(ctx context.Context, params ...uint64) ([]uint64, error) { + // se_encrypt_record(source_ptr, source_len, plan_ptr, plan_len, opts_ptr, opts_len). + if len(params) != 6 { + return nil, fmt.Errorf("unexpected encrypt ABI: %d arguments", len(params)) + } + plan, ok := f.module.Memory().Read(uint32(params[2]), uint32(params[3])) + if !ok { + return nil, errors.New("plan outside guest memory") + } + f.observe(append([]byte(nil), plan...)) + return f.Function.Call(ctx, params...) +} diff --git a/languages/golang/encrypt/internal/testusers/context_note.go b/languages/golang/encrypt/internal/testusers/context_note.go new file mode 100644 index 000000000..c80094740 --- /dev/null +++ b/languages/golang/encrypt/internal/testusers/context_note.go @@ -0,0 +1,9 @@ +package testusers + +//go:generate go run github.com/cipherstash/stack/languages/golang/cmd/stashgen -type LiteralNote -name LiteralNote -redact + +// LiteralNote exercises the declared base used by context conformance. +type LiteralNote struct { + _ struct{} `stash:"context=tenants/acme"` + Text string `stash:"text,encrypt,index=equality"` +} diff --git a/languages/golang/encrypt/internal/testusers/literalnote_stash.go b/languages/golang/encrypt/internal/testusers/literalnote_stash.go new file mode 100644 index 000000000..a3eb49f29 --- /dev/null +++ b/languages/golang/encrypt/internal/testusers/literalnote_stash.go @@ -0,0 +1,119 @@ +// Code generated by stashgen. DO NOT EDIT. + +package testusers + +import ( + "context" + "log/slog" + + "github.com/cipherstash/stack/languages/golang/encrypt" + "github.com/cipherstash/stack/languages/golang/encrypt/gensupport" +) + +// Stops compiling when the library does not accept this version of generated file. +const _ = gensupport.GeneratedVersion2 + +type EncryptedLiteralNote struct { + Text EncryptedLiteralNoteText +} + +type EncryptedLiteralNoteText struct { + Ciphertext encrypt.Ciphertext + Equality encrypt.EqualityTerm +} + +func (e EncryptedLiteralNote) String() string { + return gensupport.Redacted("EncryptedLiteralNote", nil, "Text") +} + +func (e EncryptedLiteralNote) LogValue() slog.Value { + return gensupport.RedactedLog(nil, "Text") +} + +// Written because of -redact: LiteralNote no longer prints its sealed fields. +func (l LiteralNote) String() string { + return gensupport.Redacted("LiteralNote", nil, "Text") +} + +func (l LiteralNote) LogValue() slog.Value { + return gensupport.RedactedLog(nil, "Text") +} + +// GoString is what %#v prints; without it %#v prints every field. +func (l LiteralNote) GoString() string { + return gensupport.Redacted("LiteralNote", nil, "Text") +} + +// Stops compiling when LiteralNote gains, loses, reorders or retypes a field. +var _ = literalNoteShape(LiteralNote{}) + +type literalNoteShape struct { + _ struct{} + Text string +} + +var literalNoteDeclaration = gensupport.Declare("tenants/acme"). + EncryptIndex("text", gensupport.String, encrypt.Equality) + +var literalNoteCodec = gensupport.New(gensupport.Generated[LiteralNote, EncryptedLiteralNote]{ + TypeName: "LiteralNote", + Declaration: literalNoteDeclaration, + Source: func(v LiteralNote) gensupport.Values { + return gensupport.Values{"text": v.Text} + }, + Seal: func(rec gensupport.Record) (EncryptedLiteralNote, error) { + var e EncryptedLiteralNote + e.Text = EncryptedLiteralNoteText{ + Ciphertext: rec["text"].Ciphertext, + Equality: rec["text"].Equality, + } + return e, nil + }, + Open: func(e EncryptedLiteralNote) gensupport.Record { + return gensupport.Record{"text": {Ciphertext: e.Text.Ciphertext, Equality: e.Text.Equality}} + }, + Value: func(e EncryptedLiteralNote, vals gensupport.Values) (LiteralNote, error) { + var v LiteralNote + var err error + if v.Text, err = gensupport.Get[string](vals, "text"); err != nil { + return LiteralNote{}, err + } + return v, nil + }, +}) + +// EncryptLiteralNote seals each LiteralNote, with one ZeroKMS request for each +// 500 sealed values, plus one the first time a keyset is used. The result has +// one element for each input, in the same order. +func EncryptLiteralNote(ctx context.Context, cipher *encrypt.Cipher, values []LiteralNote) ([]EncryptedLiteralNote, error) { + return literalNoteCodec.Encrypt(ctx, cipher, values) +} + +// DecryptLiteralNote opens each EncryptedLiteralNote, with one ZeroKMS request +// for each 500 sealed values, plus one the first time a keyset is used. +func DecryptLiteralNote(ctx context.Context, d encrypt.Decrypter, encrypted []EncryptedLiteralNote) ([]LiteralNote, error) { + return literalNoteCodec.Decrypt(ctx, d, encrypted) +} + +var LiteralNoteFields = struct { + Text LiteralNoteTextField +}{ + Text: LiteralNoteTextField{gensupport.NewField[string](literalNoteDeclaration, "text")}, +} + +type LiteralNoteTextField struct { + field gensupport.Field[string] +} + +func (f LiteralNoteTextField) Encrypt(ctx context.Context, c *encrypt.Cipher, v string) (EncryptedLiteralNoteText, error) { + out, err := f.field.Encrypt(ctx, c, v) + if err != nil { + return EncryptedLiteralNoteText{}, err + } + return EncryptedLiteralNoteText{Ciphertext: out.Ciphertext, Equality: out.Equality}, nil +} + +func (f LiteralNoteTextField) Eq(ctx context.Context, c *encrypt.Cipher, v string) (encrypt.EqualityTerm, error) { + term, err := f.field.Term(ctx, c, "eq", v) + return encrypt.EqualityTerm(term), err +} diff --git a/packages/stack-encrypt/CHANGELOG.md b/packages/stack-encrypt/CHANGELOG.md index a786176e2..74b5d3df6 100644 --- a/packages/stack-encrypt/CHANGELOG.md +++ b/packages/stack-encrypt/CHANGELOG.md @@ -7,13 +7,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] -### Fixed +### Breaking - Repeated plan-chain `.extend(..)` calls now append parts in order, matching - Go's `Cipher.Extend`, instead of replacing earlier extensions (#1120). + Go's `Cipher.Extend`, instead of replacing earlier extensions. `.extend(())` adds nothing, and grouped `NonEmpty` parts retain their grouping. Records written using the previous replacement behavior must still be opened - with the final extension alone; this does not migrate stored ciphertext. + with the final extension alone. Queries using repeated extensions now derive + different terms and silently miss those old rows: they return no error. Keep + the final extension alone for both reads and queries of old data, or re-encrypt + and re-index it under the appended context. This does not migrate stored data. ## [0.3.0] - 2026-10-27 diff --git a/packages/stack-encrypt/src/dynamic/record.rs b/packages/stack-encrypt/src/dynamic/record.rs index 0cea0891a..3baeebf32 100644 --- a/packages/stack-encrypt/src/dynamic/record.rs +++ b/packages/stack-encrypt/src/dynamic/record.rs @@ -4368,6 +4368,21 @@ mod tests { let mut age = map(node(&mut fields, "age")); let stored = term_bytes(&node(&mut age, "eq")); + struct Age { + age: u32, + } + let mut typed = cipher + .encrypt(&Age { age: 34 }) + .context("users") + .fields() + .encrypt_index(pick("age", |a: &Age| &a.age), Equality) + .extend(7u64) + .extend(nonempty!("eu")) + .await + .expect("typed chain"); + let typed_age: Encrypted = typed.take("age").expect("age"); + assert_eq!(stored, typed_age.terms.to_bytes()); + let nested = nonempty!("users").with("age").with(7u64).with("eu"); let probe = keyset.equality_term(34u32, nested).await.expect("probe"); assert_eq!(stored, probe.to_bytes(), "left-nested, part by part"); diff --git a/packages/stack-encrypt/tests/context_conformance.rs b/packages/stack-encrypt/tests/context_conformance.rs index 09cc459fe..1607cc9f2 100644 --- a/packages/stack-encrypt/tests/context_conformance.rs +++ b/packages/stack-encrypt/tests/context_conformance.rs @@ -37,6 +37,7 @@ struct Case { #[serde(deny_unknown_fields)] struct Vector { context_frame: String, + plan_context_frame: String, descriptor: String, aad: String, prf: String, @@ -65,6 +66,12 @@ fn extension(input: &Input) -> DeclaredContext { _ => panic!("unsupported context part"), } } +fn nest(head: ValueFrame, parts: &[Input]) -> ValueFrame { + parts + .iter() + .fold(head, |head, p| frame(vec![head, part(p)])) +} + fn context(case: &Case, field_only: bool) -> ValueFrame { let mut label = if field_only { vec![] @@ -72,9 +79,7 @@ fn context(case: &Case, field_only: bool) -> ValueFrame { case.label.split('/').map(str::to_owned).collect::>() }; label.push("text".into()); - case.parts - .iter() - .fold(frame(label), |head, p| frame(vec![head, part(p)])) + nest(frame(label), &case.parts) } fn plan(case: &Case) -> record::Plan { let mut fields = vec![]; @@ -83,12 +88,7 @@ fn plan(case: &Case) -> record::Plan { fields.push(( "tenant", object(vec![ - ( - "context", - case.parts - .iter() - .fold(frame(vec!["tenant"]), |head, p| frame(vec![head, part(p)])), - ), + ("context", nest(frame(vec!["tenant"]), &case.parts)), ("outputs", frame(vec!["passthrough"])), ("type", frame("string")), ]), @@ -204,6 +204,7 @@ async fn shared_context_composition() { .unwrap(); let vector = Vector { context_frame: hex(context_frame.risky_ref()), + plan_context_frame: hex(context(&case, case.base == "field").risky_ref()), descriptor, aad, prf, @@ -262,6 +263,7 @@ async fn shared_context_composition() { } let expected = if update { &vector } else { &baseline[&case.id] }; assert_eq!(vector.context_frame, expected.context_frame); + assert_eq!(vector.plan_context_frame, expected.plan_context_frame); assert_eq!(vector.descriptor, expected.descriptor); assert_eq!(vector.aad, expected.aad); assert_eq!(vector.prf, expected.prf); @@ -286,7 +288,12 @@ async fn shared_context_composition() { assert_eq!(plain, "hello"); // Native public chains: repeated calls, not one preassembled extension. - if !case.parts.iter().any(|p| p.text.is_empty()) { + if case.parts.iter().any(|p| p.text.is_empty()) { + assert!( + NonEmpty::new(String::new()).is_err(), + "typed empty extension must refuse" + ); + } else { let note = Note { tenant: case.label.clone(), text: "hello".into(), @@ -304,7 +311,7 @@ async fn shared_context_composition() { for p in &case.parts { chain = chain.extend(extension(p)); } - // A no-op extension must not clear earlier calls (#1120). + // A no-op extension must not clear earlier calls. let mut row = chain.extend(()).await.unwrap(); let field_plan = typed.field("text").unwrap(); let mut query = cipher.query(stack_encrypt::op::Eq, "hello"); @@ -367,6 +374,7 @@ struct Boundaries { version: u32, profile: String, cases: Vec, + queries: Vec, } #[derive(Deserialize)] #[serde(deny_unknown_fields)] @@ -376,7 +384,16 @@ struct Boundary { expected: String, mutation: String, outcome: String, - retrievals: usize, + native_retrievals: usize, +} + +#[derive(Deserialize)] +#[serde(deny_unknown_fields)] +struct QueryBoundary { + id: String, + context: String, + outcome: String, + matches: bool, } #[tokio::test] @@ -422,6 +439,23 @@ async fn shared_context_boundaries() { }; match case.mutation.as_str() { "none" => {} + "ciphertext" => { + let CipherText::Map(fields) = batch.last_mut().unwrap() else { + panic!("row") + }; + let CipherText::Map(outputs) = + &mut fields.iter_mut().find(|(k, _)| k == "text").unwrap().1 + else { + panic!("outputs") + }; + let node = &mut outputs.iter_mut().find(|(k, _)| k == "c").unwrap().1; + let CipherText::Single(leaf) = node else { + panic!("leaf") + }; + let mut bytes = leaf.to_bytes(); + *bytes.last_mut().unwrap() ^= 1; + *node = CipherText::Single(SealedValue::from_bytes(&bytes).unwrap()); + } "tamper" | "empty" | "missing" => { let CipherText::Map(fields) = batch.last_mut().expect("mutation requires a row") else { @@ -444,6 +478,71 @@ async fn shared_context_boundaries() { } _ => panic!("unknown mutation"), } + let typed_rows = batch + .iter() + .map(|row| { + let CipherText::Map(fields) = row else { + panic!("row") + }; + let mut values = stack_encrypt::plan::FieldValues::new(); + for (name, node) in fields { + let CipherText::Map(outputs) = node else { + panic!("outputs") + }; + for (output, node) in outputs { + match (output.as_str(), node) { + ("passthrough", CipherText::Passthrough(payload)) => { + values.insert( + name, + vitaminc_aead_frame::serde::from_slice::(&payload.0) + .unwrap(), + ); + } + ("c", CipherText::Single(leaf)) => { + values.insert( + name, + StackCipherText::Single( + SealedValue::from_bytes(&leaf.to_bytes()).unwrap(), + ), + ); + } + _ => {} + } + } + } + values + }) + .collect::>(); + let typed = Plan::fields() + .context_field(pick("tenant", |n: &Note| &n.tenant)) + .encrypt_index(pick("text", |n: &Note| &n.text), Equality) + .build() + .unwrap(); + retrieves.store(0, Ordering::SeqCst); + let mut open = cipher.open(typed_rows); + if !case.expected.is_empty() { + open = open.context(case.expected.as_str()); + } + let typed_result = open.using(&typed).await; + assert_eq!( + typed_result.is_ok(), + case.outcome == "success", + "typed {}", + case.id + ); + if let Ok(opened) = typed_result { + assert_eq!(opened.len(), case.tenants.len()); + for (row, tenant) in opened.iter().zip(&case.tenants) { + assert_eq!(row.get::("text").unwrap(), "hello"); + assert_eq!(row.get::("tenant").unwrap(), tenant); + } + } + assert_eq!( + retrieves.load(Ordering::SeqCst), + case.native_retrievals, + "typed {}", + case.id + ); generates.store(0, Ordering::SeqCst); retrieves.store(0, Ordering::SeqCst); let expected = if case.expected.is_empty() { @@ -458,7 +557,7 @@ async fn shared_context_boundaries() { match case.outcome.as_str() { "success" => assert_eq!(result.unwrap().risky_ref(), rows.risky_ref()), "mismatch" => assert_eq!(result.unwrap_err(), "stack_encrypt::context_mismatch"), - "authentication" => assert_eq!(result.unwrap_err(), "stack_encrypt::aead"), + "authentication" | "aead" => assert_eq!(result.unwrap_err(), "stack_encrypt::aead"), "shape" => { let code = if case.mutation == "missing" { "stack_encrypt::dynamic_record" @@ -471,12 +570,45 @@ async fn shared_context_boundaries() { } assert_eq!( retrieves.load(Ordering::SeqCst), - case.retrievals, + case.native_retrievals, "{}", case.id ); assert_eq!(generates.load(Ordering::SeqCst), 0); } + assert!(!corpus.queries.is_empty()); + for query in corpus.queries { + assert!(!query.id.is_empty() && ids.insert(query.id.clone())); + let (cipher, generates, retrieves) = common::counting_cipher().await; + let typed = Plan::fields() + .context_field(pick("tenant", |n: &Note| &n.tenant)) + .encrypt_index(pick("text", |n: &Note| &n.text), Equality) + .build() + .unwrap(); + let note = Note { + tenant: "tenants/acme".into(), + text: "hello".into(), + }; + let mut stored = cipher.encrypt(¬e).using(&typed).await.unwrap(); + let field: Encrypted = stored.take("text").unwrap(); + generates.store(0, Ordering::SeqCst); + let field_plan = typed.field("text").unwrap(); + let mut call = cipher.query(stack_encrypt::op::Eq, "hello"); + if !query.context.is_empty() { + call = call.context(query.context.as_str()); + } + let result: Result = call.on(&field_plan).await; + match query.outcome.as_str() { + "refused" => { + assert!(result.is_err()); + assert!(!query.matches); + } + "success" => assert_eq!(result.unwrap() == field.terms, query.matches), + _ => panic!("unknown query outcome"), + } + assert_eq!(generates.load(Ordering::SeqCst), 0); + assert_eq!(retrieves.load(Ordering::SeqCst), 0); + } } #[tokio::test] @@ -528,3 +660,41 @@ async fn a_grouped_extension_remains_one_part_when_appended() { )]); assert!(record::plan(declaration.risky_ref()).is_err()); } + +#[tokio::test] +async fn replacement_context_records_keep_their_original_read_and_query_context() { + let cipher = common::deterministic_cipher([0; 32]).await; + let plan = Plan::context("users/age") + .with::(Equality) + .build() + .unwrap(); + // The old chain discarded the first extension before sealing. Writing with + // its surviving part exactly reproduces that context without an old API. + let old = cipher + .encrypt(&34u32) + .using(&plan) + .extend(7u64) + .await + .unwrap(); + let old_term = old.terms.clone(); + let current = cipher + .query(stack_encrypt::op::Eq, &34u32) + .on(&plan) + .extend(1u64) + .extend(7u64) + .await + .unwrap(); + assert_ne!( + current, old_term, + "the changed query succeeds but silently misses old rows" + ); + let compatible = cipher + .query(stack_encrypt::op::Eq, &34u32) + .on(&plan) + .extend(7u64) + .await + .unwrap(); + assert_eq!(compatible, old_term); + let value: u32 = cipher.open(old).using(&plan).extend(7u64).await.unwrap(); + assert_eq!(value, 34); +} diff --git a/packages/stack-encrypt/tests/record_lowering.rs b/packages/stack-encrypt/tests/record_lowering.rs index 6ec783dd8..3fa04934a 100644 --- a/packages/stack-encrypt/tests/record_lowering.rs +++ b/packages/stack-encrypt/tests/record_lowering.rs @@ -842,6 +842,6 @@ async fn the_fixture_records_are_bound_to_their_labels() { } #[test] -fn enrolled_context_tests_are_registered() { +fn enrolled_fixture_tests_are_registered() { common::assert_fixture_tests_registered("record_lowering"); } diff --git a/scripts/__tests__/stack-conformance-ci.test.mjs b/scripts/__tests__/stack-conformance-ci.test.mjs index 2948c1944..1bf80c4ec 100644 --- a/scripts/__tests__/stack-conformance-ci.test.mjs +++ b/scripts/__tests__/stack-conformance-ci.test.mjs @@ -2,7 +2,10 @@ import { spawnSync } from 'node:child_process' import { readFileSync } from 'node:fs' import { join } from 'node:path' import { describe, expect, it } from 'vitest' -import { goSelection as selectGoTests } from '../stack-conformance-selection.mjs' +import { + coreGoTests, + goSelection as selectGoTests, +} from '../stack-conformance-selection.mjs' import { REPO_ROOT } from './lib/repo-root.mjs' import { readWorkflow } from './lib/workflows.mjs' @@ -48,15 +51,7 @@ describe('shared Stack Encrypt conformance is required in CI', () => { expect(script).toMatch(/cargo nextest run[^\n]*--test plan_grammar/) expect(script).toMatch(/cargo nextest run[^\n]*--test fixture_safety/) const selected = goSelection(script) - for (const test of [ - 'TestStackConformance', - 'TestDeclarationConformance', - 'TestRuntimeConformance', - 'TestReadConformance', - 'TestContextConformance', - 'TestContextBoundaries', - 'TestFixtureConformanceRequiresGuest', - ]) { + for (const test of coreGoTests) { expect(selected.test(test), test).toBe(true) } }) @@ -67,13 +62,7 @@ describe('shared Stack Encrypt conformance is required in CI', () => { ) const expected = [ ...new Set([ - 'TestStackConformance', - 'TestDeclarationConformance', - 'TestRuntimeConformance', - 'TestReadConformance', - 'TestContextConformance', - 'TestContextBoundaries', - 'TestFixtureConformanceRequiresGuest', + ...coreGoTests, ...index.profiles.flatMap((p) => p.requirements.flatMap((r) => r.go_tests), ), diff --git a/scripts/stack-conformance-selection.mjs b/scripts/stack-conformance-selection.mjs index 905e7b957..8725f360d 100644 --- a/scripts/stack-conformance-selection.mjs +++ b/scripts/stack-conformance-selection.mjs @@ -3,16 +3,18 @@ import { readFileSync } from 'node:fs' import { fileURLToPath } from 'node:url' +export const coreGoTests = [ + 'TestStackConformance', + 'TestDeclarationConformance', + 'TestRuntimeConformance', + 'TestReadConformance', + 'TestContextConformance', + 'TestContextBoundaries', + 'TestFixtureConformanceRequiresGuest', +] + export function goSelection(index) { - const tests = new Set([ - 'TestStackConformance', - 'TestDeclarationConformance', - 'TestRuntimeConformance', - 'TestReadConformance', - 'TestContextConformance', - 'TestContextBoundaries', - 'TestFixtureConformanceRequiresGuest', - ]) + const tests = new Set(coreGoTests) for (const profile of index.profiles) { for (const requirement of profile.requirements) { for (const name of requirement.go_tests) { From 07d4ef23f306418d89a734ed3f0c31ea0d6abb27 Mon Sep 17 00:00:00 2001 From: Dan Draper Date: Tue, 3 Nov 2026 22:31:57 +1100 Subject: [PATCH 3/4] test(conformance): name each file after its profile and role The conformance files were named inconsistently: cases.json held record-scalars-v1, native.json held its outputs while the context outputs were contexts-native.json, go-declarations.json put the runner first, and nothing in a name said whether a file held inputs, frozen outputs or both. Each file is now ..json, where the profile is the exact `profile` in its header and the role is `cases`, `expected.` or `inputs.`. The enrollment index becomes fixture-enrollment.json. The README states the rule and lists every file with its readers. The boundary profile, new in this PR, becomes record-context-boundaries-v1 to match the other record profiles. The files are renamed only; apart from that profile ID their contents are unchanged. --- conformance/stack-encrypt/README.md | 53 ++++++++++++++++--- conformance/stack-encrypt/context-contract.md | 6 +-- conformance/stack-encrypt/coverage.md | 8 +-- .../stack-encrypt/declaration-grammar.md | 4 +- conformance/stack-encrypt/fixture-contract.md | 2 +- conformance/stack-encrypt/read-contract.md | 2 +- conformance/stack-encrypt/runtime-contract.md | 2 +- ...-profiles.json => fixture-enrollment.json} | 0 ...> record-context-boundaries-v1.cases.json} | 2 +- ...xts.json => record-contexts-v1.cases.json} | 0 ...ord-contexts-v1.expected.rust-native.json} | 0 ...json => record-declarations-v1.cases.json} | 0 ... => record-declarations-v1.inputs.go.json} | 0 ...{reads.json => record-reads-v1.cases.json} | 0 ...time.json => record-runtime-v1.cases.json} | 0 ...ases.json => record-scalars-v1.cases.json} | 0 ...cord-scalars-v1.expected.rust-native.json} | 0 .../2026-10-31-stack-encrypt-conformance.md | 2 +- languages/golang/encrypt/conformance_test.go | 4 +- .../encrypt/context_conformance_test.go | 8 +-- .../encrypt/declaration_conformance_test.go | 4 +- .../encrypt/fixture_conformance_test.go | 2 +- .../golang/encrypt/read_conformance_test.go | 2 +- .../encrypt/runtime_conformance_test.go | 2 +- packages/stack-encrypt/tests/common/mod.rs | 2 +- packages/stack-encrypt/tests/conformance.rs | 10 ++-- .../tests/context_conformance.rs | 8 +-- packages/stack-encrypt/tests/declarations.rs | 2 +- .../stack-encrypt/tests/read_conformance.rs | 4 +- .../tests/runtime_conformance.rs | 2 +- .../__tests__/stack-conformance-ci.test.mjs | 4 +- scripts/stack-conformance-selection.mjs | 2 +- 32 files changed, 89 insertions(+), 48 deletions(-) rename conformance/stack-encrypt/v1/{fixture-profiles.json => fixture-enrollment.json} (100%) rename conformance/stack-encrypt/v1/{context-boundaries.json => record-context-boundaries-v1.cases.json} (98%) rename conformance/stack-encrypt/v1/{contexts.json => record-contexts-v1.cases.json} (100%) rename conformance/stack-encrypt/v1/{contexts-native.json => record-contexts-v1.expected.rust-native.json} (100%) rename conformance/stack-encrypt/v1/{declarations.json => record-declarations-v1.cases.json} (100%) rename conformance/stack-encrypt/v1/{go-declarations.json => record-declarations-v1.inputs.go.json} (100%) rename conformance/stack-encrypt/v1/{reads.json => record-reads-v1.cases.json} (100%) rename conformance/stack-encrypt/v1/{runtime.json => record-runtime-v1.cases.json} (100%) rename conformance/stack-encrypt/v1/{cases.json => record-scalars-v1.cases.json} (100%) rename conformance/stack-encrypt/v1/{native.json => record-scalars-v1.expected.rust-native.json} (100%) diff --git a/conformance/stack-encrypt/README.md b/conformance/stack-encrypt/README.md index 4889deebc..3ef35fea5 100644 --- a/conformance/stack-encrypt/README.md +++ b/conformance/stack-encrypt/README.md @@ -5,6 +5,45 @@ This directory holds a shared test contract for Stack Encrypt bindings. The sets out the wider work; [coverage.md](coverage.md) maps existing evidence and gaps. These profiles are **experimental**, not certification of an entire SDK. +## Files + +Each file in `v1/` is named `..json`. `` is the exact +`profile` value in the file's header, so searching for a profile ID finds every +file that belongs to it. `v1/` is the corpus format version, which is separate +from each profile's own version. `` is one of: + +- `cases`: the shared, language-neutral cases that every runner executes. When + an expectation is a category rather than bytes, such as an error class or a + retrieval count, it is written in the case itself. +- `expected.`: frozen outputs keyed by the case IDs in `cases`, + produced by ``. `rust-native` is the native Rust engine. Only the + producer regenerates this file, deliberately; runners compare against it. +- `inputs.`: extra inputs keyed by case ID that only `` needs, + such as Go host values that have no language-neutral form. + +Every file has a `version` and `profile` header. An `expected` file also names +its `producer`, and an `inputs` file names its `runner`. + +| File | Role | Rust reader | Go reader | +|---|---|---|---| +| `record-scalars-v1.cases.json` | cases | `tests/conformance.rs` | `conformance_test.go` | +| `record-scalars-v1.expected.rust-native.json` | expected | `tests/conformance.rs` (writes it) | `conformance_test.go` | +| `record-declarations-v1.cases.json` | cases | `tests/declarations.rs` | `declaration_conformance_test.go` | +| `record-declarations-v1.inputs.go.json` | Go inputs | โ€” | `declaration_conformance_test.go` | +| `record-reads-v1.cases.json` | cases | `tests/read_conformance.rs` | `read_conformance_test.go` | +| `record-runtime-v1.cases.json` | cases | `tests/runtime_conformance.rs` | `runtime_conformance_test.go` | +| `record-contexts-v1.cases.json` | cases | `tests/context_conformance.rs` | `context_conformance_test.go` | +| `record-contexts-v1.expected.rust-native.json` | expected | `tests/context_conformance.rs` (writes it) | `context_conformance_test.go` | +| `record-context-boundaries-v1.cases.json` | cases | `tests/context_conformance.rs` | `context_conformance_test.go` | +| `fixture-enrollment.json` | index | `tests/common/mod.rs` | `fixture_conformance_test.go` | + +Rust readers are under `packages/stack-encrypt/`; Go readers are under +`languages/golang/encrypt/`. `fixture-enrollment.json` is not a profile: it maps +requirement IDs onto existing fixtures and tests (see +[the fixture contract](fixture-contract.md)), and +`scripts/stack-conformance-selection.mjs` reads it to choose the Go tests that +`mise run test:conformance` runs. + ## Run From the repository root, with the pinned mise tools installed: @@ -41,7 +80,7 @@ existing context-field fixture tests. ## Existing value-kind and query-operator fixtures The [fixture contract](fixture-contract.md) defines `record-value-kinds-v1` and -`record-query-operators-v1`. Their [enrollment index](v1/fixture-profiles.json) +`record-query-operators-v1`. Their [enrollment index](v1/fixture-enrollment.json) maps stable requirement IDs to the existing Rust and Go tests. The fixtures remain under `packages/stack-encrypt/tests/fixtures`; no vectors are copied. @@ -57,7 +96,7 @@ operations remain separate work. ## `record-reads-v1` contract -The [read contract](read-contract.md) defines cases in `v1/reads.json` for +The [read contract](read-contract.md) defines cases in `v1/record-reads-v1.cases.json` for malformed stored records, authenticated read-type failures and context preflight. The native runner observes retrieval stages. Go exercises the same cases through an uninitialised guest and a configured cipher, plus the normal record adapter @@ -67,7 +106,7 @@ crate and `encrypt/read_conformance_test.go` in the Go module. ## `record-runtime-v1` contract -The [runtime contract](runtime-contract.md) defines cases in `v1/runtime.json`: +The [runtime contract](runtime-contract.md) defines cases in `v1/record-runtime-v1.cases.json`: missing and explicit null values, SQL-null omission, sealed-null storage and equality, typed batch round trips, write validation and lossless query conversions. The runners are `packages/stack-encrypt/tests/runtime_conformance.rs` and @@ -78,7 +117,7 @@ data-key call counts; Go host source validation preserves shared structured diag The [declaration grammar](declaration-grammar.md) defines the engine's current record-plan syntax, defaults, context rules, kind/index table and null policies. -`v1/declarations.json` contains cases, including malformed frames and all +`v1/record-declarations-v1.cases.json` contains cases, including malformed frames and all 21 kinds against five scalar indexes. The resolver is explicitly `none`; EQL targets need their own profile. @@ -89,11 +128,11 @@ frames document the different native-parser and WASI-boundary classifications. Native Rust also checks normalization observations. Go's checker does not expose the parsed plan, so it asserts acceptance and diagnostics against both release and deterministic guests. Representable cases also exercise `Checker.Check` -through explicit host plan fixtures in `v1/go-declarations.json`. +through explicit host plan fixtures in `v1/record-declarations-v1.inputs.go.json`. ## `record-scalars-v1` contract -`v1/cases.json` has a corpus format `version`, a semantic `profile`, a public +`v1/record-scalars-v1.cases.json` has a corpus format `version`, a semantic `profile`, a public deterministic-provider `seed`, a record `context`, a `field`, an exact list of `required_capabilities`, and uniquely identified `cases`. Unknown fields, versions, profiles, capabilities and input types fail. Every case must run. @@ -137,7 +176,7 @@ profiles or cases. The full SQL behavior is not part of this initial profile. ## Artifacts and evolution -`v1/native.json` is a frozen artifact set produced by the Rust owned scalar API. +`v1/record-scalars-v1.expected.rust-native.json` is a frozen artifact set produced by the Rust owned scalar API. It records `version`, `profile`, `producer`, and a case-ID map containing hex `frame`, `ciphertext` and `equality` bytes. The map must contain exactly every positive case; missing or extra cases fail. Every key and plaintext here is diff --git a/conformance/stack-encrypt/context-contract.md b/conformance/stack-encrypt/context-contract.md index 77793ac66..f9c1841bf 100644 --- a/conformance/stack-encrypt/context-contract.md +++ b/conformance/stack-encrypt/context-contract.md @@ -1,7 +1,7 @@ # Context composition -`record-contexts-v1` (`v1/contexts.json`) specifies composition for the current -record API. `context-boundaries-v1` (`v1/context-boundaries.json`) specifies +`record-contexts-v1` (`v1/record-contexts-v1.cases.json`) specifies composition for the current +record API. `record-context-boundaries-v1` (`v1/record-context-boundaries-v1.cases.json`) specifies stored-context expectations and refusal boundaries. Both are required in Rust and Go. Python and TypeScript must implement these profiles when their runners are added; their absence is not conformance evidence. @@ -59,7 +59,7 @@ int64, uint64, or bytes (hex). Integers are decimal strings, never JSON numbers. Every case encrypts and queries the string `hello` in the `text` field, using the public all-zero deterministic-provider test seed. -`v1/contexts-native.json` is keyed by exactly those case IDs. It pins the +`v1/record-contexts-v1.expected.rust-native.json` is keyed by exactly those case IDs. It pins the resolved context frame, the field context frame sent in a plan, descriptor, AAD, PRF bytes, one Rust ciphertext and equality term. Rust checks actual descriptors sent to the key provider, fresh typed and dynamic writes, queries, opens, and frozen ciphertexts. The typed chain calls diff --git a/conformance/stack-encrypt/coverage.md b/conformance/stack-encrypt/coverage.md index 47f204db3..ca8c1bc04 100644 --- a/conformance/stack-encrypt/coverage.md +++ b/conformance/stack-encrypt/coverage.md @@ -8,7 +8,7 @@ Paths are relative to the repository root. New profile cases run from `v1/`. |---|---|---| | Scalar value widths and lossless transport | Initial scalar cases and [value-kind requirements](fixture-contract.md#value-kind-requirements), executed by the existing Rust and generated Go runners | Complete per-kind boundary matrices, bytes and broader floats; Python/TS mappings | | Owned opaque ciphertext and storage envelope | `packages/stack-encrypt/tests/scalar.rs`, `frozen_bytes.rs`; initial cross-opening cases | Blob profile; encoded targets; supported version matrix | -| Record declaration and lowering | [Declaration grammar](declaration-grammar.md); `v1/declarations.json` shared by native Rust and Go; native normalization observations; existing record lowering suites | EQL-resolver profile; SDK-to-declaration mappings; runtime source/read shapes | +| Record declaration and lowering | [Declaration grammar](declaration-grammar.md); `v1/record-declarations-v1.cases.json` shared by native Rust and Go; native normalization observations; existing record lowering suites | EQL-resolver profile; SDK-to-declaration mappings; runtime source/read shapes | | Context binding and composition | `packages/stack-encrypt/tests/target.rs`, `descriptor.rs`, `record_lowering.rs`; `languages/golang/encrypt/context_field_test.go`; initial wrong-context cases | Shared composition vectors; expected-context and required-input matrix | | Query terms and diagnostics | [Query-operator requirements](fixture-contract.md#query-operator-requirements), existing Rust/Go runners and initial equality/query-error cases | Common rejection corpus for every operator, capability and input shape; wider kind/index combinations | | Missing, SQL NULL and sealed null | `packages/stack-encrypt/src/dynamic/record.rs` null tests; `languages/golang/encrypt/null_test.go` | Explicit language mappings and common missing/null fixtures | @@ -25,7 +25,7 @@ absence is a delivery gap, not a test skip that counts toward certification. ## Runtime checkpoint -`v1/runtime.json` adds shared cases for typed writes and reads, null storage, +`v1/record-runtime-v1.cases.json` adds shared cases for typed writes and reads, null storage, batches, equality queries and lossless numeric conversion. See the [runtime contract](runtime-contract.md) for exact observations and limits. Native data-key counters cover zero-call preflight failures and single-call @@ -41,7 +41,7 @@ batches; Go provider-call instrumentation remains open. ## Read checkpoint -`v1/reads.json` adds cases for stored-shape validation, authenticated types +`v1/record-reads-v1.cases.json` adds cases for stored-shape validation, authenticated types and context preflight. The [read contract](read-contract.md) distinguishes native shape/pending stages and the guest's earlier `check_record` diagnostics. Native data-key counters and Go's uninitialised guest verify preflight boundaries. @@ -60,7 +60,7 @@ raw-byte profile must cover that boundary explicitly. ## Consolidation policy The value-kind/query-operator enrollment adds named requirements for the -existing fixtures through [the enrollment index](v1/fixture-profiles.json). +existing fixtures through [the enrollment index](v1/fixture-enrollment.json). The conformance command selects their original runners; CI guards the mapping and selection. Typed Rust comparisons, generated Go methods, verification and fetch-limit checks stay in those runners. No fixture is copied and no existing diff --git a/conformance/stack-encrypt/declaration-grammar.md b/conformance/stack-encrypt/declaration-grammar.md index 6fed321e3..6693d7303 100644 --- a/conformance/stack-encrypt/declaration-grammar.md +++ b/conformance/stack-encrypt/declaration-grammar.md @@ -239,7 +239,7 @@ independent observation of normalization or add a production export for testing. ## Diagnostics and corpus format -The corpus is `v1/declarations.json`. Its version, profile and resolver are +The corpus is `v1/record-declarations-v1.cases.json`. Its version, profile and resolver are required. Case IDs are unique and stable. Every case contains exactly one input: - `{"json": value}` converts JSON objects in lexical key order. Nonnegative @@ -279,7 +279,7 @@ TypeScript runners can adopt these inputs and expectations without copying the engine's validation rules. The Go runner checks both the release and deterministic guests. Go-specific -`v1/go-declarations.json` supplies explicit `record.Plan` inputs for the subset +`v1/record-declarations-v1.inputs.go.json` supplies explicit `record.Plan` inputs for the subset representable by that host model, keyed by the shared case ID. Those cases also run through production `Checker.Check` (Validate, Wire and the engine), with the same acceptance outcome. Raw malformed frames and shapes the Go plan cannot diff --git a/conformance/stack-encrypt/fixture-contract.md b/conformance/stack-encrypt/fixture-contract.md index 587c6762d..79e109b67 100644 --- a/conformance/stack-encrypt/fixture-contract.md +++ b/conformance/stack-encrypt/fixture-contract.md @@ -16,7 +16,7 @@ Their [existing schema documentation](../../packages/stack-encrypt/tests/fixture continues to own the formats. No expected frames, ciphertexts, terms, verified pages or fetch limits are copied into this directory. -[`v1/fixture-profiles.json`](v1/fixture-profiles.json) maps stable requirement IDs +[`v1/fixture-enrollment.json`](v1/fixture-enrollment.json) maps stable requirement IDs to the existing Rust test functions and Go tests that provide their evidence. It is an enrollment index, not another input corpus or a runtime capability negotiation format. The CI wiring test checks fixture paths, source declarations, diff --git a/conformance/stack-encrypt/read-contract.md b/conformance/stack-encrypt/read-contract.md index 80fae01f0..4123b1a0b 100644 --- a/conformance/stack-encrypt/read-contract.md +++ b/conformance/stack-encrypt/read-contract.md @@ -1,6 +1,6 @@ # Record read profile -`record-reads-v1` adds shared cases in `v1/reads.json` for stored record +`record-reads-v1` adds shared cases in `v1/record-reads-v1.cases.json` for stored record shapes, authenticated type checks and context preflight. It fixes the resolver to `NoTargets`. It is experimental and covers native Rust and Go WASI. diff --git a/conformance/stack-encrypt/runtime-contract.md b/conformance/stack-encrypt/runtime-contract.md index 3c3d67ff3..b442751cb 100644 --- a/conformance/stack-encrypt/runtime-contract.md +++ b/conformance/stack-encrypt/runtime-contract.md @@ -1,6 +1,6 @@ # Record runtime profile -`record-runtime-v1` describes the behavior exercised by `v1/runtime.json`. +`record-runtime-v1` describes the behavior exercised by `v1/record-runtime-v1.cases.json`. It is an experimental test contract for native Rust and Go WASI, with no EQL resolver. Python and TypeScript runners remain pending. diff --git a/conformance/stack-encrypt/v1/fixture-profiles.json b/conformance/stack-encrypt/v1/fixture-enrollment.json similarity index 100% rename from conformance/stack-encrypt/v1/fixture-profiles.json rename to conformance/stack-encrypt/v1/fixture-enrollment.json diff --git a/conformance/stack-encrypt/v1/context-boundaries.json b/conformance/stack-encrypt/v1/record-context-boundaries-v1.cases.json similarity index 98% rename from conformance/stack-encrypt/v1/context-boundaries.json rename to conformance/stack-encrypt/v1/record-context-boundaries-v1.cases.json index bda82bbf3..e8e50457a 100644 --- a/conformance/stack-encrypt/v1/context-boundaries.json +++ b/conformance/stack-encrypt/v1/record-context-boundaries-v1.cases.json @@ -1,6 +1,6 @@ { "version": 1, - "profile": "context-boundaries-v1", + "profile": "record-context-boundaries-v1", "cases": [ { "id": "unrestricted", diff --git a/conformance/stack-encrypt/v1/contexts.json b/conformance/stack-encrypt/v1/record-contexts-v1.cases.json similarity index 100% rename from conformance/stack-encrypt/v1/contexts.json rename to conformance/stack-encrypt/v1/record-contexts-v1.cases.json diff --git a/conformance/stack-encrypt/v1/contexts-native.json b/conformance/stack-encrypt/v1/record-contexts-v1.expected.rust-native.json similarity index 100% rename from conformance/stack-encrypt/v1/contexts-native.json rename to conformance/stack-encrypt/v1/record-contexts-v1.expected.rust-native.json diff --git a/conformance/stack-encrypt/v1/declarations.json b/conformance/stack-encrypt/v1/record-declarations-v1.cases.json similarity index 100% rename from conformance/stack-encrypt/v1/declarations.json rename to conformance/stack-encrypt/v1/record-declarations-v1.cases.json diff --git a/conformance/stack-encrypt/v1/go-declarations.json b/conformance/stack-encrypt/v1/record-declarations-v1.inputs.go.json similarity index 100% rename from conformance/stack-encrypt/v1/go-declarations.json rename to conformance/stack-encrypt/v1/record-declarations-v1.inputs.go.json diff --git a/conformance/stack-encrypt/v1/reads.json b/conformance/stack-encrypt/v1/record-reads-v1.cases.json similarity index 100% rename from conformance/stack-encrypt/v1/reads.json rename to conformance/stack-encrypt/v1/record-reads-v1.cases.json diff --git a/conformance/stack-encrypt/v1/runtime.json b/conformance/stack-encrypt/v1/record-runtime-v1.cases.json similarity index 100% rename from conformance/stack-encrypt/v1/runtime.json rename to conformance/stack-encrypt/v1/record-runtime-v1.cases.json diff --git a/conformance/stack-encrypt/v1/cases.json b/conformance/stack-encrypt/v1/record-scalars-v1.cases.json similarity index 100% rename from conformance/stack-encrypt/v1/cases.json rename to conformance/stack-encrypt/v1/record-scalars-v1.cases.json diff --git a/conformance/stack-encrypt/v1/native.json b/conformance/stack-encrypt/v1/record-scalars-v1.expected.rust-native.json similarity index 100% rename from conformance/stack-encrypt/v1/native.json rename to conformance/stack-encrypt/v1/record-scalars-v1.expected.rust-native.json diff --git a/docs/plans/2026-10-31-stack-encrypt-conformance.md b/docs/plans/2026-10-31-stack-encrypt-conformance.md index 4f55a7031..1ac4ad892 100644 --- a/docs/plans/2026-10-31-stack-encrypt-conformance.md +++ b/docs/plans/2026-10-31-stack-encrypt-conformance.md @@ -154,7 +154,7 @@ Python and TypeScript consumers remain pending. Building on the read-compatibility profile, the [fixture contract](../../conformance/stack-encrypt/fixture-contract.md) and -[enrollment index](../../conformance/stack-encrypt/v1/fixture-profiles.json) +[enrollment index](../../conformance/stack-encrypt/v1/fixture-enrollment.json) assign stable requirement IDs to the existing `value_kinds.json` and `query_operators.json` evidence. The conformance task directly runs their existing Rust and Go tests. Typed Rust comparisons, generated Go methods, diff --git a/languages/golang/encrypt/conformance_test.go b/languages/golang/encrypt/conformance_test.go index 88039d9eb..f2cdd4dfc 100644 --- a/languages/golang/encrypt/conformance_test.go +++ b/languages/golang/encrypt/conformance_test.go @@ -126,7 +126,7 @@ func conformanceValue(t *testing.T, input conformanceInput) any { func TestStackConformance(t *testing.T) { root := filepath.Join("..", "..", "..", "conformance", "stack-encrypt", "v1") var corpus conformanceCorpus - readConformanceJSON(t, filepath.Join(root, "cases.json"), &corpus) + readConformanceJSON(t, filepath.Join(root, "record-scalars-v1.cases.json"), &corpus) if corpus.Version != 1 || corpus.Profile != conformanceProfile || len(corpus.Cases) == 0 { t.Fatal("unsupported or empty conformance corpus") } @@ -145,7 +145,7 @@ func TestStackConformance(t *testing.T) { } } var native conformanceArtifacts - readConformanceJSON(t, filepath.Join(root, "native.json"), &native) + readConformanceJSON(t, filepath.Join(root, "record-scalars-v1.expected.rust-native.json"), &native) if native.Version != 1 || native.Profile != conformanceProfile || native.Producer == "" || len(native.Cases) != len(positive) || len(positive) == 0 { t.Fatal("artifact profile or case set differs from the corpus") } diff --git a/languages/golang/encrypt/context_conformance_test.go b/languages/golang/encrypt/context_conformance_test.go index 7d921a76f..c457d2d0c 100644 --- a/languages/golang/encrypt/context_conformance_test.go +++ b/languages/golang/encrypt/context_conformance_test.go @@ -49,12 +49,12 @@ func TestContextConformance(t *testing.T) { RequiredCapabilities []string `json:"required_capabilities"` Cases []contextCase `json:"cases"` } - readConformanceJSON(t, filepath.Join(root, "contexts.json"), &corpus) + readConformanceJSON(t, filepath.Join(root, "record-contexts-v1.cases.json"), &corpus) if corpus.Version != 1 || corpus.Profile != "record-contexts-v1" || len(corpus.Cases) == 0 || !reflect.DeepEqual(corpus.RequiredCapabilities, []string{"composition", "typed-extensions", "context-fields", "frozen-open"}) { t.Fatal("unsupported context profile") } var vectors map[string]contextVector - readConformanceJSON(t, filepath.Join(root, "contexts-native.json"), &vectors) + readConformanceJSON(t, filepath.Join(root, "record-contexts-v1.expected.rust-native.json"), &vectors) if len(vectors) != len(corpus.Cases) { t.Fatal("vector case set differs") } @@ -255,8 +255,8 @@ func TestContextBoundaries(t *testing.T) { Cases []contextBoundary `json:"cases"` Queries []contextQueryBoundary `json:"queries"` } - readConformanceJSON(t, filepath.Join("..", "..", "..", "conformance", "stack-encrypt", "v1", "context-boundaries.json"), &corpus) - if corpus.Version != 1 || corpus.Profile != "context-boundaries-v1" || len(corpus.Cases) == 0 { + readConformanceJSON(t, filepath.Join("..", "..", "..", "conformance", "stack-encrypt", "v1", "record-context-boundaries-v1.cases.json"), &corpus) + if corpus.Version != 1 || corpus.Profile != "record-context-boundaries-v1" || len(corpus.Cases) == 0 { t.Fatal("unsupported boundary profile") } p := &record.Plan{ContextField: "tenant", Fields: []record.Field{{Name: "text", Kind: record.String, Outputs: []record.Output{record.Ciphertext, record.Equality}}}} diff --git a/languages/golang/encrypt/declaration_conformance_test.go b/languages/golang/encrypt/declaration_conformance_test.go index cd06c4382..965a4ad58 100644 --- a/languages/golang/encrypt/declaration_conformance_test.go +++ b/languages/golang/encrypt/declaration_conformance_test.go @@ -77,12 +77,12 @@ func TestDeclarationConformance(t *testing.T) { Resolver string `json:"resolver"` Cases []declarationCase `json:"cases"` } - readConformanceJSON(t, filepath.Join("..", "..", "..", "conformance", "stack-encrypt", "v1", "declarations.json"), &corpus) + readConformanceJSON(t, filepath.Join("..", "..", "..", "conformance", "stack-encrypt", "v1", "record-declarations-v1.cases.json"), &corpus) if corpus.Version != 1 || corpus.Profile != "record-declarations-v1" || corpus.Resolver != "none" || len(corpus.Cases) == 0 { t.Fatal("unsupported or empty declaration corpus") } var hostPlans map[string]record.Plan - readConformanceJSON(t, filepath.Join("..", "..", "..", "conformance", "stack-encrypt", "v1", "go-declarations.json"), &hostPlans) + readConformanceJSON(t, filepath.Join("..", "..", "..", "conformance", "stack-encrypt", "v1", "record-declarations-v1.inputs.go.json"), &hostPlans) for name, load := range map[string]func() ([]byte, error){"release": encrypt.PlainGuest, "deterministic": encrypt.DeterministicGuest} { t.Run(name, func(t *testing.T) { wasm, err := load() diff --git a/languages/golang/encrypt/fixture_conformance_test.go b/languages/golang/encrypt/fixture_conformance_test.go index e5b7720e4..eec80b529 100644 --- a/languages/golang/encrypt/fixture_conformance_test.go +++ b/languages/golang/encrypt/fixture_conformance_test.go @@ -47,7 +47,7 @@ func TestFixtureConformanceRequiresGuest(t *testing.T) { } `json:"requirements"` } `json:"profiles"` } - data, err := os.ReadFile(filepath.Join("..", "..", "..", "conformance", "stack-encrypt", "v1", "fixture-profiles.json")) + data, err := os.ReadFile(filepath.Join("..", "..", "..", "conformance", "stack-encrypt", "v1", "fixture-enrollment.json")) if err != nil { t.Fatal(err) } diff --git a/languages/golang/encrypt/read_conformance_test.go b/languages/golang/encrypt/read_conformance_test.go index bd63fbe1c..ba505ffec 100644 --- a/languages/golang/encrypt/read_conformance_test.go +++ b/languages/golang/encrypt/read_conformance_test.go @@ -139,7 +139,7 @@ func TestReadConformance(t *testing.T) { RequiredCapabilities []string `json:"required_capabilities"` Cases []readCase `json:"cases"` } - readConformanceJSON(t, filepath.Join("..", "..", "..", "conformance", "stack-encrypt", "v1", "reads.json"), &corpus) + readConformanceJSON(t, filepath.Join("..", "..", "..", "conformance", "stack-encrypt", "v1", "record-reads-v1.cases.json"), &corpus) if corpus.Version != 1 || corpus.Profile != "record-reads-v1" || len(corpus.Cases) == 0 { t.Fatal("unsupported or empty read corpus") } diff --git a/languages/golang/encrypt/runtime_conformance_test.go b/languages/golang/encrypt/runtime_conformance_test.go index 9b86f9b7a..c86491311 100644 --- a/languages/golang/encrypt/runtime_conformance_test.go +++ b/languages/golang/encrypt/runtime_conformance_test.go @@ -48,7 +48,7 @@ func TestRuntimeConformance(t *testing.T) { RequiredCapabilities []string `json:"required_capabilities"` Cases []runtimeCase `json:"cases"` } - readConformanceJSON(t, filepath.Join("..", "..", "..", "conformance", "stack-encrypt", "v1", "runtime.json"), &corpus) + readConformanceJSON(t, filepath.Join("..", "..", "..", "conformance", "stack-encrypt", "v1", "record-runtime-v1.cases.json"), &corpus) if corpus.Version != 1 || corpus.Profile != "record-runtime-v1" || len(corpus.Cases) == 0 { t.Fatal("unsupported or empty runtime corpus") } diff --git a/packages/stack-encrypt/tests/common/mod.rs b/packages/stack-encrypt/tests/common/mod.rs index 0bcd7faa3..c1d832a44 100644 --- a/packages/stack-encrypt/tests/common/mod.rs +++ b/packages/stack-encrypt/tests/common/mod.rs @@ -37,7 +37,7 @@ pub fn fixture_updates_requested_or_panic() -> bool { /// trusting source declarations that may be disabled by `cfg`. pub fn assert_fixture_tests_registered(binary: &str) { let index: serde_json::Value = serde_json::from_str(include_str!( - "../../../../conformance/stack-encrypt/v1/fixture-profiles.json" + "../../../../conformance/stack-encrypt/v1/fixture-enrollment.json" )) .expect("fixture enrollment index"); let profile = index["profiles"] diff --git a/packages/stack-encrypt/tests/conformance.rs b/packages/stack-encrypt/tests/conformance.rs index ade5592fb..f2875aa01 100644 --- a/packages/stack-encrypt/tests/conformance.rs +++ b/packages/stack-encrypt/tests/conformance.rs @@ -149,8 +149,10 @@ async fn open_scalar( #[tokio::test] async fn shared_record_scalar_profile() { - let corpus: Corpus = - serde_json::from_slice(&std::fs::read(root().join("cases.json")).unwrap()).unwrap(); + let corpus: Corpus = serde_json::from_slice( + &std::fs::read(root().join("record-scalars-v1.cases.json")).unwrap(), + ) + .unwrap(); assert_eq!(corpus.version, 1); assert_eq!(corpus.profile, PROFILE); assert_eq!( @@ -180,7 +182,7 @@ async fn shared_record_scalar_profile() { } else { let file = imported .map(PathBuf::from) - .unwrap_or_else(|| root().join("native.json")); + .unwrap_or_else(|| root().join("record-scalars-v1.expected.rust-native.json")); let artifacts: Artifacts = serde_json::from_slice(&std::fs::read(file).unwrap()).unwrap(); assert_eq!(artifacts.version, 1); assert_eq!(artifacts.profile, PROFILE); @@ -328,7 +330,7 @@ async fn shared_record_scalar_profile() { } if update { std::fs::write( - root().join("native.json"), + root().join("record-scalars-v1.expected.rust-native.json"), serde_json::to_string_pretty(&output).unwrap() + "\n", ) .unwrap(); diff --git a/packages/stack-encrypt/tests/context_conformance.rs b/packages/stack-encrypt/tests/context_conformance.rs index 1607cc9f2..956d02eba 100644 --- a/packages/stack-encrypt/tests/context_conformance.rs +++ b/packages/stack-encrypt/tests/context_conformance.rs @@ -160,7 +160,7 @@ struct Note { async fn shared_context_composition() { let update = common::fixture_updates_requested_or_panic(); let corpus: Corpus = serde_json::from_str(include_str!( - "../../../conformance/stack-encrypt/v1/contexts.json" + "../../../conformance/stack-encrypt/v1/record-contexts-v1.cases.json" )) .unwrap(); assert_eq!(corpus.version, 1); @@ -176,7 +176,7 @@ async fn shared_context_composition() { ); assert!(!corpus.cases.is_empty()); let path = std::path::Path::new(env!("CARGO_MANIFEST_DIR")) - .join("../../conformance/stack-encrypt/v1/contexts-native.json"); + .join("../../conformance/stack-encrypt/v1/record-contexts-v1.expected.rust-native.json"); let baseline: BTreeMap = if update { BTreeMap::new() } else { @@ -400,11 +400,11 @@ struct QueryBoundary { async fn shared_context_boundaries() { use std::sync::atomic::Ordering; let corpus: Boundaries = serde_json::from_str(include_str!( - "../../../conformance/stack-encrypt/v1/context-boundaries.json" + "../../../conformance/stack-encrypt/v1/record-context-boundaries-v1.cases.json" )) .unwrap(); assert_eq!(corpus.version, 1); - assert_eq!(corpus.profile, "context-boundaries-v1"); + assert_eq!(corpus.profile, "record-context-boundaries-v1"); assert!(!corpus.cases.is_empty()); let mut ids = BTreeSet::new(); let case = Case { diff --git a/packages/stack-encrypt/tests/declarations.rs b/packages/stack-encrypt/tests/declarations.rs index a5e9158ee..d56eac509 100644 --- a/packages/stack-encrypt/tests/declarations.rs +++ b/packages/stack-encrypt/tests/declarations.rs @@ -88,7 +88,7 @@ fn normalized(plan: &record::Plan) -> Value { #[test] fn shared_declaration_grammar() { let path = PathBuf::from(env!("CARGO_MANIFEST_DIR")) - .join("../../conformance/stack-encrypt/v1/declarations.json"); + .join("../../conformance/stack-encrypt/v1/record-declarations-v1.cases.json"); let corpus: Corpus = serde_json::from_slice(&std::fs::read(path).unwrap()).unwrap(); assert_eq!(corpus.version, 1); assert_eq!(corpus.profile, "record-declarations-v1"); diff --git a/packages/stack-encrypt/tests/read_conformance.rs b/packages/stack-encrypt/tests/read_conformance.rs index 031377b92..c8749ed5d 100644 --- a/packages/stack-encrypt/tests/read_conformance.rs +++ b/packages/stack-encrypt/tests/read_conformance.rs @@ -195,7 +195,7 @@ fn mutate(tree: &mut StackCipherText, mutation: &Mutation, context_name: &str) { #[test] fn shared_record_read_profile() { let corpus: Corpus = serde_json::from_str(include_str!( - "../../../conformance/stack-encrypt/v1/reads.json" + "../../../conformance/stack-encrypt/v1/record-reads-v1.cases.json" )) .unwrap(); assert_eq!(corpus.version, 1); @@ -331,7 +331,7 @@ async fn check_case(case: Case) { #[test] fn expected_context_must_be_a_nonempty_label_when_present() { let corpus: Value = serde_json::from_str(include_str!( - "../../../conformance/stack-encrypt/v1/reads.json" + "../../../conformance/stack-encrypt/v1/record-reads-v1.cases.json" )) .unwrap(); let mut case = corpus["cases"][0].clone(); diff --git a/packages/stack-encrypt/tests/runtime_conformance.rs b/packages/stack-encrypt/tests/runtime_conformance.rs index d7341c33f..c9faf2db0 100644 --- a/packages/stack-encrypt/tests/runtime_conformance.rs +++ b/packages/stack-encrypt/tests/runtime_conformance.rs @@ -73,7 +73,7 @@ fn rows(input: &[Row]) -> ValueFrame { #[tokio::test] async fn shared_record_runtime_profile() { let corpus: Corpus = serde_json::from_str(include_str!( - "../../../conformance/stack-encrypt/v1/runtime.json" + "../../../conformance/stack-encrypt/v1/record-runtime-v1.cases.json" )) .unwrap(); assert_eq!(corpus.version, 1); diff --git a/scripts/__tests__/stack-conformance-ci.test.mjs b/scripts/__tests__/stack-conformance-ci.test.mjs index 1bf80c4ec..b3cb09dac 100644 --- a/scripts/__tests__/stack-conformance-ci.test.mjs +++ b/scripts/__tests__/stack-conformance-ci.test.mjs @@ -58,7 +58,7 @@ describe('shared Stack Encrypt conformance is required in CI', () => { it('selects exactly the core runners and indexed fixture tests', () => { const index = JSON.parse( - readRepo('conformance/stack-encrypt/v1/fixture-profiles.json'), + readRepo('conformance/stack-encrypt/v1/fixture-enrollment.json'), ) const expected = [ ...new Set([ @@ -87,7 +87,7 @@ describe('shared Stack Encrypt conformance is required in CI', () => { it('enrolls existing fixture evidence without copying or dropping tests', () => { const index = JSON.parse( - readRepo('conformance/stack-encrypt/v1/fixture-profiles.json'), + readRepo('conformance/stack-encrypt/v1/fixture-enrollment.json'), ) expect(Object.keys(index).sort()).toEqual(['profiles', 'version']) expect(index.version).toBe(1) diff --git a/scripts/stack-conformance-selection.mjs b/scripts/stack-conformance-selection.mjs index 8725f360d..df58cd980 100644 --- a/scripts/stack-conformance-selection.mjs +++ b/scripts/stack-conformance-selection.mjs @@ -32,7 +32,7 @@ if (process.argv[1] === fileURLToPath(import.meta.url)) { const index = JSON.parse( readFileSync( new URL( - '../conformance/stack-encrypt/v1/fixture-profiles.json', + '../conformance/stack-encrypt/v1/fixture-enrollment.json', import.meta.url, ), 'utf8', From 9f33dae8882522e693ba02ceb72756e956926d5c Mon Sep 17 00:00:00 2001 From: Dan Draper Date: Tue, 3 Nov 2026 22:34:23 +1100 Subject: [PATCH 4/4] test(conformance): give every conformance file a profile header record-contexts-v1.expected.rust-native.json and record-declarations-v1.inputs.go.json were bare maps keyed by case ID, so neither said which profile it belonged to or what produced it. Each now carries `version` and `profile`, plus `producer` for an expected file and `runner` for an inputs file, as the scalar outputs already did. The Rust and Go readers check the header, and the Rust writer emits it when the context vectors are regenerated. The vectors themselves are unchanged; the diff re-indents them under `cases`. stack-conformance-ci.test.mjs now fails on a file in v1/ whose name is not ..json, whose header names another profile, or whose expected or inputs file has no cases file beside it. --- conformance/stack-encrypt/README.md | 2 + ...cord-contexts-v1.expected.rust-native.json | 327 +- .../v1/record-declarations-v1.inputs.go.json | 3065 +++++++++-------- .../encrypt/context_conformance_test.go | 13 +- .../encrypt/declaration_conformance_test.go | 13 +- .../tests/context_conformance.rs | 27 +- .../__tests__/stack-conformance-ci.test.mjs | 34 +- 7 files changed, 1783 insertions(+), 1698 deletions(-) diff --git a/conformance/stack-encrypt/README.md b/conformance/stack-encrypt/README.md index 3ef35fea5..d4dcb6651 100644 --- a/conformance/stack-encrypt/README.md +++ b/conformance/stack-encrypt/README.md @@ -23,6 +23,8 @@ from each profile's own version. `` is one of: Every file has a `version` and `profile` header. An `expected` file also names its `producer`, and an `inputs` file names its `runner`. +`scripts/__tests__/stack-conformance-ci.test.mjs` fails on a file that breaks +these rules. | File | Role | Rust reader | Go reader | |---|---|---|---| diff --git a/conformance/stack-encrypt/v1/record-contexts-v1.expected.rust-native.json b/conformance/stack-encrypt/v1/record-contexts-v1.expected.rust-native.json index 44089fc94..b71d12958 100644 --- a/conformance/stack-encrypt/v1/record-contexts-v1.expected.rust-native.json +++ b/conformance/stack-encrypt/v1/record-contexts-v1.expected.rust-native.json @@ -1,164 +1,169 @@ { - "field-base": { - "context_frame": "f0030000000a0700000074656e616e74730a0400000061636d650a0400000074657874", - "plan_context_frame": "f0010000000a0400000074657874", - "descriptor": "tenants/acme/text", - "aad": "0300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000074657874", - "prf": "0300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000074657874", - "ciphertext": "0100000000000000000000000000000000e42a8b64607989ab5b6197de58ff5f322000175d4e4987626d968634ab7dda022fdbc41dce31bf9183e729c1f562185b175202698f817c8ac5153febe0a12c6d017bedc729dcd3c0e6a34704bda1de65ba86edabb3", - "equality": "d07ccccb55205dbb5c69c80bd88c907496a18bbed9317f39d96a83f6fd308a85" - }, - "field-bytes": { - "context_frame": "f002000000f0030000000a0700000074656e616e74730a0400000061636d650a04000000746578740b020000006575", - "plan_context_frame": "f002000000f0010000000a04000000746578740b020000006575", - "descriptor": "(tenants/acme/text)/eu", - "aad": "020000000000000022010000000000000300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000074657874540000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311900000000000000766974616d696e632f636f6e746578742f62797465732f763102000000000000006575", - "prf": "020000000000000022010000000000000300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000074657874540000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311900000000000000766974616d696e632f636f6e746578742f62797465732f763102000000000000006575", - "ciphertext": "0100000000000000000000000000000000ab137bf75fe4fa4f42a20f099ff3a77f20001a726f50cba00da93aab760c6e038be772c928d0cf8a5877dcff2e9ecbafadfe02a07f63015a3bb45c950f1a6669327d39ea2dd813655ea0f990f66f83bca2a4d98c29", - "equality": "df58c27184a7707a4735ac1afd925fa3be93689eb1fa26e615cc96e78107524e" - }, - "field-empty-part": { - "context_frame": "f002000000f0030000000a0700000074656e616e74730a0400000061636d650a04000000746578740a00000000", - "plan_context_frame": "f002000000f0010000000a04000000746578740a00000000", - "descriptor": "(tenants/acme/text)/b64:", - "aad": "020000000000000022010000000000000300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000074657874510000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f76310000000000000000", - "prf": "020000000000000022010000000000000300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000074657874510000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f76310000000000000000", - "ciphertext": "01000000000000000000000000000000001c03b0987bb51494b2f64841124a60c12000deede8eb83519fef962190d1e9c7104f75ed87d19c0f221d7c85136429b3f4b702523faf4b8207654708e4699416aaa800f0917353fb5ad19c9ba9c9cdfe63697c11a9", - "equality": "6562228f385c28b4762ff6bcb92057ecf739847f85fc18c4c9b917e96b240ad6" - }, - "field-narrow": { - "context_frame": "f002000000f0030000000a0700000074656e616e74730a0400000061636d650a04000000746578740407000000", - "plan_context_frame": "f002000000f0010000000a04000000746578740407000000", - "descriptor": "(tenants/acme/text)/7u32", - "aad": "020000000000000022010000000000000300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000074657874570000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311a00000000000000766974616d696e632f636f6e746578742f6933322d6c652f7631040000000000000007000000", - "prf": "020000000000000022010000000000000300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000074657874570000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311a00000000000000766974616d696e632f636f6e746578742f6933322d6c652f7631040000000000000007000000", - "ciphertext": "0100000000000000000000000000000000d8767b62020fa04a135b492d4bed93e62000023c54cc52c80ba067235a64a801d702f3f558640fa9f80508c999afe98f7cbf0201d9c0bdd57345ca59e8758b8cbd823f2087c300740fc1f878fa15c2686f415164cf", - "equality": "a600febf5d31afa2e8fa9dfd0d47bbed76e2a6b69b3a26f3d9ba490d953c5c43" - }, - "field-one": { - "context_frame": "f002000000f0030000000a0700000074656e616e74730a0400000061636d650a0400000074657874070700000000000000", - "plan_context_frame": "f002000000f0010000000a0400000074657874070700000000000000", - "descriptor": "(tenants/acme/text)/7u64", - "aad": "020000000000000022010000000000000300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f76310400000000000000746578745b0000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311a00000000000000766974616d696e632f636f6e746578742f7536342d6c652f763108000000000000000700000000000000", - "prf": "020000000000000022010000000000000300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f76310400000000000000746578745b0000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311a00000000000000766974616d696e632f636f6e746578742f7536342d6c652f763108000000000000000700000000000000", - "ciphertext": "01000000000000000000000000000000004216f7c1c404ef65c06bf83fab7d67b720009c0baa84274beb7ccb55fb28f343ab4ad12e6d00803b3955cb5bcf9e6ed2b51202dc72629ac375399fc444afa1bb11918c9a62f1341d353ac50009aef27fdd5a5bcfaf", - "equality": "ef4fd47818a5ec5b472f768bed2e28e9f3e0ef1bf86b95fb85b7d23208fb215a" - }, - "field-ordered": { - "context_frame": "f002000000f002000000f0030000000a0700000074656e616e74730a0400000061636d650a04000000746578740707000000000000000a020000006575", - "plan_context_frame": "f002000000f002000000f0010000000a04000000746578740707000000000000000a020000006575", - "descriptor": "((tenants/acme/text)/7u64)/eu", - "aad": "02000000000000009501000000000000020000000000000022010000000000000300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f76310400000000000000746578745b0000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311a00000000000000766974616d696e632f636f6e746578742f7536342d6c652f763108000000000000000700000000000000530000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f763102000000000000006575", - "prf": "02000000000000009501000000000000020000000000000022010000000000000300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f76310400000000000000746578745b0000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311a00000000000000766974616d696e632f636f6e746578742f7536342d6c652f763108000000000000000700000000000000530000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f763102000000000000006575", - "ciphertext": "0100000000000000000000000000000000d25279cf7ce2862c0e1ead193d5a7698200054b497feda700c887e38cd921614aac2016a88eb2c367e4c0b72b5c6483f5b0c02df5aa44610fba1a90888e44cd3c8cb61d60ec171d4e6f1c026925ff97b0e8d090eb3", - "equality": "ad34715d40cf4bb40e35830f19c01180c08ef0492d40febc9d3267cab1464562" - }, - "field-reversed": { - "context_frame": "f002000000f002000000f0030000000a0700000074656e616e74730a0400000061636d650a04000000746578740a020000006575070700000000000000", - "plan_context_frame": "f002000000f002000000f0010000000a04000000746578740a020000006575070700000000000000", - "descriptor": "((tenants/acme/text)/eu)/7u64", - "aad": "02000000000000008d01000000000000020000000000000022010000000000000300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000074657874530000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631020000000000000065755b0000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311a00000000000000766974616d696e632f636f6e746578742f7536342d6c652f763108000000000000000700000000000000", - "prf": "02000000000000008d01000000000000020000000000000022010000000000000300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000074657874530000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631020000000000000065755b0000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311a00000000000000766974616d696e632f636f6e746578742f7536342d6c652f763108000000000000000700000000000000", - "ciphertext": "01000000000000000000000000000000000682e6a8c346478d0bed4f633f9f591720001d0ee4f5786d90124b1bb4a56452173e72b1dbdb1837bb1761a3dc3ba04574600237a1b985afbdc095b044282f1c8828dc9dd8ba5caa9defa6a3548ff6321e52608629", - "equality": "ac1ae266a8574b0a164b771046e0028bc290e4018c2e48ba351515dcf95a7801" - }, - "field-signed": { - "context_frame": "f002000000f0030000000a0700000074656e616e74730a0400000061636d650a0400000074657874050700000000000000", - "plan_context_frame": "f002000000f0010000000a0400000074657874050700000000000000", - "descriptor": "(tenants/acme/text)/7u64", - "aad": "020000000000000022010000000000000300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f76310400000000000000746578745b0000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311a00000000000000766974616d696e632f636f6e746578742f6936342d6c652f763108000000000000000700000000000000", - "prf": "020000000000000022010000000000000300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f76310400000000000000746578745b0000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311a00000000000000766974616d696e632f636f6e746578742f6936342d6c652f763108000000000000000700000000000000", - "ciphertext": "0100000000000000000000000000000000e0fa052c95f14a1b6e95adbfc952cf6e2000ee4082d049338a812d8b10673453127da1fe6dcb665dea4c96da426f52df55bf0260e25b0d83fd0287132f2071ca0aa61de3dee9f05a344fda170211c9c763d4c3240f", - "equality": "1bcd9b5f12c4218bbd584dce0bad0003138408cc312c1cc8bb69bc8861a8227e" - }, - "field-text": { - "context_frame": "f002000000f0030000000a0700000074656e616e74730a0400000061636d650a04000000746578740a020000006575", - "plan_context_frame": "f002000000f0010000000a04000000746578740a020000006575", - "descriptor": "(tenants/acme/text)/eu", - "aad": "020000000000000022010000000000000300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000074657874530000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f763102000000000000006575", - "prf": "020000000000000022010000000000000300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000074657874530000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f763102000000000000006575", - "ciphertext": "0100000000000000000000000000000000d21a6d4e940e4329994b30afc2a81cb12000203fb57f381e7ef64dc8ebe63bd33f546a772bedc94a624e4d47b6a6c5eb45f3029ae4e5e1d82d1a315fffbe7865cfdae1c2666e69d534b66a652dd42fa4396199f73c", - "equality": "76f509a979f486b783f2d4f872a1913abff9f3ef66d7007b01e3cab3a734e270" - }, - "literal-base": { - "context_frame": "f0030000000a0700000074656e616e74730a0400000061636d650a0400000074657874", - "plan_context_frame": "f0030000000a0700000074656e616e74730a0400000061636d650a0400000074657874", - "descriptor": "tenants/acme/text", - "aad": "0300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000074657874", - "prf": "0300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000074657874", - "ciphertext": "0100000000000000000000000000000000233bbec00515bcc147012024a94dda1d2000421022b5ace076acb43d72b4aa3de668968552535247283db06bed86af0def73029d811dd365ef1445b2a7cf7dff745881871bdcd379986f1b42e5f0b83176e321d0c9", - "equality": "d07ccccb55205dbb5c69c80bd88c907496a18bbed9317f39d96a83f6fd308a85" - }, - "literal-bytes": { - "context_frame": "f002000000f0030000000a0700000074656e616e74730a0400000061636d650a04000000746578740b020000006575", - "plan_context_frame": "f002000000f0030000000a0700000074656e616e74730a0400000061636d650a04000000746578740b020000006575", - "descriptor": "(tenants/acme/text)/eu", - "aad": "020000000000000022010000000000000300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000074657874540000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311900000000000000766974616d696e632f636f6e746578742f62797465732f763102000000000000006575", - "prf": "020000000000000022010000000000000300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000074657874540000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311900000000000000766974616d696e632f636f6e746578742f62797465732f763102000000000000006575", - "ciphertext": "0100000000000000000000000000000000f992ac91510557386b3bcfcc26e9d45d2000f9ba6397d1be9552b633ffeef89249a1a3b28a1b2a58d4c2808de639001020ae02301ac6e31ef94bd6f03880588e4aa15a0c70cf5b3d498e74eab105e29c688a0d2406", - "equality": "df58c27184a7707a4735ac1afd925fa3be93689eb1fa26e615cc96e78107524e" - }, - "literal-empty-part": { - "context_frame": "f002000000f0030000000a0700000074656e616e74730a0400000061636d650a04000000746578740a00000000", - "plan_context_frame": "f002000000f0030000000a0700000074656e616e74730a0400000061636d650a04000000746578740a00000000", - "descriptor": "(tenants/acme/text)/b64:", - "aad": "020000000000000022010000000000000300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000074657874510000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f76310000000000000000", - "prf": "020000000000000022010000000000000300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000074657874510000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f76310000000000000000", - "ciphertext": "010000000000000000000000000000000014850b0d9658afa82000b1c214375fd42000ea552a62dd1fdd018c2279f78de9f25c5283b33cd3d51fee029fb98da7e0c35a02c46ea737a9175b1f64ce252c907814ec151ab8265f860e2192a18a455f1fcbe488af", - "equality": "6562228f385c28b4762ff6bcb92057ecf739847f85fc18c4c9b917e96b240ad6" - }, - "literal-narrow": { - "context_frame": "f002000000f0030000000a0700000074656e616e74730a0400000061636d650a04000000746578740407000000", - "plan_context_frame": "f002000000f0030000000a0700000074656e616e74730a0400000061636d650a04000000746578740407000000", - "descriptor": "(tenants/acme/text)/7u32", - "aad": "020000000000000022010000000000000300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000074657874570000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311a00000000000000766974616d696e632f636f6e746578742f6933322d6c652f7631040000000000000007000000", - "prf": "020000000000000022010000000000000300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000074657874570000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311a00000000000000766974616d696e632f636f6e746578742f6933322d6c652f7631040000000000000007000000", - "ciphertext": "010000000000000000000000000000000006c936cf0e7c4804593649d96a530f3e20008295873720d38723e2a83007f0cc80867629e97736b65d8da267e09028679ba102304eb13d354282baadc0f719255db1b9dff7bf443aa167959fde52a210c540def110", - "equality": "a600febf5d31afa2e8fa9dfd0d47bbed76e2a6b69b3a26f3d9ba490d953c5c43" - }, - "literal-one": { - "context_frame": "f002000000f0030000000a0700000074656e616e74730a0400000061636d650a0400000074657874070700000000000000", - "plan_context_frame": "f002000000f0030000000a0700000074656e616e74730a0400000061636d650a0400000074657874070700000000000000", - "descriptor": "(tenants/acme/text)/7u64", - "aad": "020000000000000022010000000000000300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f76310400000000000000746578745b0000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311a00000000000000766974616d696e632f636f6e746578742f7536342d6c652f763108000000000000000700000000000000", - "prf": "020000000000000022010000000000000300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f76310400000000000000746578745b0000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311a00000000000000766974616d696e632f636f6e746578742f7536342d6c652f763108000000000000000700000000000000", - "ciphertext": "0100000000000000000000000000000000d4e2b614715887c3b0b8b7ec13c8854820008e13eaedd78736f2531e16737e2346916e314064927acfb970d613fb11c81ba10271d40c1b9b7a0df25f48606585fc1e7337e33ab6f0816b959654cf48a22370352e03", - "equality": "ef4fd47818a5ec5b472f768bed2e28e9f3e0ef1bf86b95fb85b7d23208fb215a" - }, - "literal-ordered": { - "context_frame": "f002000000f002000000f0030000000a0700000074656e616e74730a0400000061636d650a04000000746578740707000000000000000a020000006575", - "plan_context_frame": "f002000000f002000000f0030000000a0700000074656e616e74730a0400000061636d650a04000000746578740707000000000000000a020000006575", - "descriptor": "((tenants/acme/text)/7u64)/eu", - "aad": "02000000000000009501000000000000020000000000000022010000000000000300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f76310400000000000000746578745b0000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311a00000000000000766974616d696e632f636f6e746578742f7536342d6c652f763108000000000000000700000000000000530000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f763102000000000000006575", - "prf": "02000000000000009501000000000000020000000000000022010000000000000300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f76310400000000000000746578745b0000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311a00000000000000766974616d696e632f636f6e746578742f7536342d6c652f763108000000000000000700000000000000530000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f763102000000000000006575", - "ciphertext": "0100000000000000000000000000000000127e42644c439f781b72f98a90a1d0282000d1df0e854edc5616954c06efdfc883277cb48bc41b10f03e96a58d6e43e9660d022ca2c0feab076d352efd3acbf89362ce17e2433f7efc4765f2e898a074883a9e1558", - "equality": "ad34715d40cf4bb40e35830f19c01180c08ef0492d40febc9d3267cab1464562" - }, - "literal-reversed": { - "context_frame": "f002000000f002000000f0030000000a0700000074656e616e74730a0400000061636d650a04000000746578740a020000006575070700000000000000", - "plan_context_frame": "f002000000f002000000f0030000000a0700000074656e616e74730a0400000061636d650a04000000746578740a020000006575070700000000000000", - "descriptor": "((tenants/acme/text)/eu)/7u64", - "aad": "02000000000000008d01000000000000020000000000000022010000000000000300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000074657874530000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631020000000000000065755b0000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311a00000000000000766974616d696e632f636f6e746578742f7536342d6c652f763108000000000000000700000000000000", - "prf": "02000000000000008d01000000000000020000000000000022010000000000000300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000074657874530000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631020000000000000065755b0000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311a00000000000000766974616d696e632f636f6e746578742f7536342d6c652f763108000000000000000700000000000000", - "ciphertext": "01000000000000000000000000000000008b7f54275b6c8f7b0acf5eed3c74894420006442ff82abfe7e68dffd900ec2826f1bc9ed4ae95e55118ccb6914165f4bd0760219cdf75189337f6170a4841f4cf1047c99b38e519538cb731a2c06dd706aece87288", - "equality": "ac1ae266a8574b0a164b771046e0028bc290e4018c2e48ba351515dcf95a7801" - }, - "literal-signed": { - "context_frame": "f002000000f0030000000a0700000074656e616e74730a0400000061636d650a0400000074657874050700000000000000", - "plan_context_frame": "f002000000f0030000000a0700000074656e616e74730a0400000061636d650a0400000074657874050700000000000000", - "descriptor": "(tenants/acme/text)/7u64", - "aad": "020000000000000022010000000000000300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f76310400000000000000746578745b0000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311a00000000000000766974616d696e632f636f6e746578742f6936342d6c652f763108000000000000000700000000000000", - "prf": "020000000000000022010000000000000300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f76310400000000000000746578745b0000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311a00000000000000766974616d696e632f636f6e746578742f6936342d6c652f763108000000000000000700000000000000", - "ciphertext": "010000000000000000000000000000000096958fa4c5718b558f2a196c4c02b901200011ff26eaa183171680c1943ed6124393bd39aa8b86e3eaadeb381dce51cb4f3902d5dad6d829cd36e38316f37972ac0daa8686959c51ab8ea85306708edbc6f6bf84e8", - "equality": "1bcd9b5f12c4218bbd584dce0bad0003138408cc312c1cc8bb69bc8861a8227e" - }, - "literal-text": { - "context_frame": "f002000000f0030000000a0700000074656e616e74730a0400000061636d650a04000000746578740a020000006575", - "plan_context_frame": "f002000000f0030000000a0700000074656e616e74730a0400000061636d650a04000000746578740a020000006575", - "descriptor": "(tenants/acme/text)/eu", - "aad": "020000000000000022010000000000000300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000074657874530000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f763102000000000000006575", - "prf": "020000000000000022010000000000000300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000074657874530000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f763102000000000000006575", - "ciphertext": "01000000000000000000000000000000006379fc33f72fe6d1aa66fcdd2f394c342000e66e4d1f85d1e763c0244e39e813e1efc0af0620b0c18d2224e5183f49fc161402173f5892942f89e3a41297f4a9813e17da78930df22bb1f973085848b8fbaa74704a", - "equality": "76f509a979f486b783f2d4f872a1913abff9f3ef66d7007b01e3cab3a734e270" + "version": 1, + "profile": "record-contexts-v1", + "producer": "rust-native/stack-encrypt-0.3.0", + "cases": { + "field-base": { + "context_frame": "f0030000000a0700000074656e616e74730a0400000061636d650a0400000074657874", + "plan_context_frame": "f0010000000a0400000074657874", + "descriptor": "tenants/acme/text", + "aad": "0300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000074657874", + "prf": "0300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000074657874", + "ciphertext": "0100000000000000000000000000000000e42a8b64607989ab5b6197de58ff5f322000175d4e4987626d968634ab7dda022fdbc41dce31bf9183e729c1f562185b175202698f817c8ac5153febe0a12c6d017bedc729dcd3c0e6a34704bda1de65ba86edabb3", + "equality": "d07ccccb55205dbb5c69c80bd88c907496a18bbed9317f39d96a83f6fd308a85" + }, + "field-bytes": { + "context_frame": "f002000000f0030000000a0700000074656e616e74730a0400000061636d650a04000000746578740b020000006575", + "plan_context_frame": "f002000000f0010000000a04000000746578740b020000006575", + "descriptor": "(tenants/acme/text)/eu", + "aad": "020000000000000022010000000000000300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000074657874540000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311900000000000000766974616d696e632f636f6e746578742f62797465732f763102000000000000006575", + "prf": "020000000000000022010000000000000300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000074657874540000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311900000000000000766974616d696e632f636f6e746578742f62797465732f763102000000000000006575", + "ciphertext": "0100000000000000000000000000000000ab137bf75fe4fa4f42a20f099ff3a77f20001a726f50cba00da93aab760c6e038be772c928d0cf8a5877dcff2e9ecbafadfe02a07f63015a3bb45c950f1a6669327d39ea2dd813655ea0f990f66f83bca2a4d98c29", + "equality": "df58c27184a7707a4735ac1afd925fa3be93689eb1fa26e615cc96e78107524e" + }, + "field-empty-part": { + "context_frame": "f002000000f0030000000a0700000074656e616e74730a0400000061636d650a04000000746578740a00000000", + "plan_context_frame": "f002000000f0010000000a04000000746578740a00000000", + "descriptor": "(tenants/acme/text)/b64:", + "aad": "020000000000000022010000000000000300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000074657874510000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f76310000000000000000", + "prf": "020000000000000022010000000000000300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000074657874510000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f76310000000000000000", + "ciphertext": "01000000000000000000000000000000001c03b0987bb51494b2f64841124a60c12000deede8eb83519fef962190d1e9c7104f75ed87d19c0f221d7c85136429b3f4b702523faf4b8207654708e4699416aaa800f0917353fb5ad19c9ba9c9cdfe63697c11a9", + "equality": "6562228f385c28b4762ff6bcb92057ecf739847f85fc18c4c9b917e96b240ad6" + }, + "field-narrow": { + "context_frame": "f002000000f0030000000a0700000074656e616e74730a0400000061636d650a04000000746578740407000000", + "plan_context_frame": "f002000000f0010000000a04000000746578740407000000", + "descriptor": "(tenants/acme/text)/7u32", + "aad": "020000000000000022010000000000000300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000074657874570000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311a00000000000000766974616d696e632f636f6e746578742f6933322d6c652f7631040000000000000007000000", + "prf": "020000000000000022010000000000000300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000074657874570000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311a00000000000000766974616d696e632f636f6e746578742f6933322d6c652f7631040000000000000007000000", + "ciphertext": "0100000000000000000000000000000000d8767b62020fa04a135b492d4bed93e62000023c54cc52c80ba067235a64a801d702f3f558640fa9f80508c999afe98f7cbf0201d9c0bdd57345ca59e8758b8cbd823f2087c300740fc1f878fa15c2686f415164cf", + "equality": "a600febf5d31afa2e8fa9dfd0d47bbed76e2a6b69b3a26f3d9ba490d953c5c43" + }, + "field-one": { + "context_frame": "f002000000f0030000000a0700000074656e616e74730a0400000061636d650a0400000074657874070700000000000000", + "plan_context_frame": "f002000000f0010000000a0400000074657874070700000000000000", + "descriptor": "(tenants/acme/text)/7u64", + "aad": "020000000000000022010000000000000300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f76310400000000000000746578745b0000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311a00000000000000766974616d696e632f636f6e746578742f7536342d6c652f763108000000000000000700000000000000", + "prf": "020000000000000022010000000000000300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f76310400000000000000746578745b0000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311a00000000000000766974616d696e632f636f6e746578742f7536342d6c652f763108000000000000000700000000000000", + "ciphertext": "01000000000000000000000000000000004216f7c1c404ef65c06bf83fab7d67b720009c0baa84274beb7ccb55fb28f343ab4ad12e6d00803b3955cb5bcf9e6ed2b51202dc72629ac375399fc444afa1bb11918c9a62f1341d353ac50009aef27fdd5a5bcfaf", + "equality": "ef4fd47818a5ec5b472f768bed2e28e9f3e0ef1bf86b95fb85b7d23208fb215a" + }, + "field-ordered": { + "context_frame": "f002000000f002000000f0030000000a0700000074656e616e74730a0400000061636d650a04000000746578740707000000000000000a020000006575", + "plan_context_frame": "f002000000f002000000f0010000000a04000000746578740707000000000000000a020000006575", + "descriptor": "((tenants/acme/text)/7u64)/eu", + "aad": "02000000000000009501000000000000020000000000000022010000000000000300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f76310400000000000000746578745b0000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311a00000000000000766974616d696e632f636f6e746578742f7536342d6c652f763108000000000000000700000000000000530000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f763102000000000000006575", + "prf": "02000000000000009501000000000000020000000000000022010000000000000300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f76310400000000000000746578745b0000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311a00000000000000766974616d696e632f636f6e746578742f7536342d6c652f763108000000000000000700000000000000530000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f763102000000000000006575", + "ciphertext": "0100000000000000000000000000000000d25279cf7ce2862c0e1ead193d5a7698200054b497feda700c887e38cd921614aac2016a88eb2c367e4c0b72b5c6483f5b0c02df5aa44610fba1a90888e44cd3c8cb61d60ec171d4e6f1c026925ff97b0e8d090eb3", + "equality": "ad34715d40cf4bb40e35830f19c01180c08ef0492d40febc9d3267cab1464562" + }, + "field-reversed": { + "context_frame": "f002000000f002000000f0030000000a0700000074656e616e74730a0400000061636d650a04000000746578740a020000006575070700000000000000", + "plan_context_frame": "f002000000f002000000f0010000000a04000000746578740a020000006575070700000000000000", + "descriptor": "((tenants/acme/text)/eu)/7u64", + "aad": "02000000000000008d01000000000000020000000000000022010000000000000300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000074657874530000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631020000000000000065755b0000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311a00000000000000766974616d696e632f636f6e746578742f7536342d6c652f763108000000000000000700000000000000", + "prf": "02000000000000008d01000000000000020000000000000022010000000000000300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000074657874530000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631020000000000000065755b0000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311a00000000000000766974616d696e632f636f6e746578742f7536342d6c652f763108000000000000000700000000000000", + "ciphertext": "01000000000000000000000000000000000682e6a8c346478d0bed4f633f9f591720001d0ee4f5786d90124b1bb4a56452173e72b1dbdb1837bb1761a3dc3ba04574600237a1b985afbdc095b044282f1c8828dc9dd8ba5caa9defa6a3548ff6321e52608629", + "equality": "ac1ae266a8574b0a164b771046e0028bc290e4018c2e48ba351515dcf95a7801" + }, + "field-signed": { + "context_frame": "f002000000f0030000000a0700000074656e616e74730a0400000061636d650a0400000074657874050700000000000000", + "plan_context_frame": "f002000000f0010000000a0400000074657874050700000000000000", + "descriptor": "(tenants/acme/text)/7u64", + "aad": "020000000000000022010000000000000300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f76310400000000000000746578745b0000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311a00000000000000766974616d696e632f636f6e746578742f6936342d6c652f763108000000000000000700000000000000", + "prf": "020000000000000022010000000000000300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f76310400000000000000746578745b0000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311a00000000000000766974616d696e632f636f6e746578742f6936342d6c652f763108000000000000000700000000000000", + "ciphertext": "0100000000000000000000000000000000e0fa052c95f14a1b6e95adbfc952cf6e2000ee4082d049338a812d8b10673453127da1fe6dcb665dea4c96da426f52df55bf0260e25b0d83fd0287132f2071ca0aa61de3dee9f05a344fda170211c9c763d4c3240f", + "equality": "1bcd9b5f12c4218bbd584dce0bad0003138408cc312c1cc8bb69bc8861a8227e" + }, + "field-text": { + "context_frame": "f002000000f0030000000a0700000074656e616e74730a0400000061636d650a04000000746578740a020000006575", + "plan_context_frame": "f002000000f0010000000a04000000746578740a020000006575", + "descriptor": "(tenants/acme/text)/eu", + "aad": "020000000000000022010000000000000300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000074657874530000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f763102000000000000006575", + "prf": "020000000000000022010000000000000300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000074657874530000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f763102000000000000006575", + "ciphertext": "0100000000000000000000000000000000d21a6d4e940e4329994b30afc2a81cb12000203fb57f381e7ef64dc8ebe63bd33f546a772bedc94a624e4d47b6a6c5eb45f3029ae4e5e1d82d1a315fffbe7865cfdae1c2666e69d534b66a652dd42fa4396199f73c", + "equality": "76f509a979f486b783f2d4f872a1913abff9f3ef66d7007b01e3cab3a734e270" + }, + "literal-base": { + "context_frame": "f0030000000a0700000074656e616e74730a0400000061636d650a0400000074657874", + "plan_context_frame": "f0030000000a0700000074656e616e74730a0400000061636d650a0400000074657874", + "descriptor": "tenants/acme/text", + "aad": "0300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000074657874", + "prf": "0300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000074657874", + "ciphertext": "0100000000000000000000000000000000233bbec00515bcc147012024a94dda1d2000421022b5ace076acb43d72b4aa3de668968552535247283db06bed86af0def73029d811dd365ef1445b2a7cf7dff745881871bdcd379986f1b42e5f0b83176e321d0c9", + "equality": "d07ccccb55205dbb5c69c80bd88c907496a18bbed9317f39d96a83f6fd308a85" + }, + "literal-bytes": { + "context_frame": "f002000000f0030000000a0700000074656e616e74730a0400000061636d650a04000000746578740b020000006575", + "plan_context_frame": "f002000000f0030000000a0700000074656e616e74730a0400000061636d650a04000000746578740b020000006575", + "descriptor": "(tenants/acme/text)/eu", + "aad": "020000000000000022010000000000000300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000074657874540000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311900000000000000766974616d696e632f636f6e746578742f62797465732f763102000000000000006575", + "prf": "020000000000000022010000000000000300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000074657874540000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311900000000000000766974616d696e632f636f6e746578742f62797465732f763102000000000000006575", + "ciphertext": "0100000000000000000000000000000000f992ac91510557386b3bcfcc26e9d45d2000f9ba6397d1be9552b633ffeef89249a1a3b28a1b2a58d4c2808de639001020ae02301ac6e31ef94bd6f03880588e4aa15a0c70cf5b3d498e74eab105e29c688a0d2406", + "equality": "df58c27184a7707a4735ac1afd925fa3be93689eb1fa26e615cc96e78107524e" + }, + "literal-empty-part": { + "context_frame": "f002000000f0030000000a0700000074656e616e74730a0400000061636d650a04000000746578740a00000000", + "plan_context_frame": "f002000000f0030000000a0700000074656e616e74730a0400000061636d650a04000000746578740a00000000", + "descriptor": "(tenants/acme/text)/b64:", + "aad": "020000000000000022010000000000000300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000074657874510000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f76310000000000000000", + "prf": "020000000000000022010000000000000300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000074657874510000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f76310000000000000000", + "ciphertext": "010000000000000000000000000000000014850b0d9658afa82000b1c214375fd42000ea552a62dd1fdd018c2279f78de9f25c5283b33cd3d51fee029fb98da7e0c35a02c46ea737a9175b1f64ce252c907814ec151ab8265f860e2192a18a455f1fcbe488af", + "equality": "6562228f385c28b4762ff6bcb92057ecf739847f85fc18c4c9b917e96b240ad6" + }, + "literal-narrow": { + "context_frame": "f002000000f0030000000a0700000074656e616e74730a0400000061636d650a04000000746578740407000000", + "plan_context_frame": "f002000000f0030000000a0700000074656e616e74730a0400000061636d650a04000000746578740407000000", + "descriptor": "(tenants/acme/text)/7u32", + "aad": "020000000000000022010000000000000300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000074657874570000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311a00000000000000766974616d696e632f636f6e746578742f6933322d6c652f7631040000000000000007000000", + "prf": "020000000000000022010000000000000300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000074657874570000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311a00000000000000766974616d696e632f636f6e746578742f6933322d6c652f7631040000000000000007000000", + "ciphertext": "010000000000000000000000000000000006c936cf0e7c4804593649d96a530f3e20008295873720d38723e2a83007f0cc80867629e97736b65d8da267e09028679ba102304eb13d354282baadc0f719255db1b9dff7bf443aa167959fde52a210c540def110", + "equality": "a600febf5d31afa2e8fa9dfd0d47bbed76e2a6b69b3a26f3d9ba490d953c5c43" + }, + "literal-one": { + "context_frame": "f002000000f0030000000a0700000074656e616e74730a0400000061636d650a0400000074657874070700000000000000", + "plan_context_frame": "f002000000f0030000000a0700000074656e616e74730a0400000061636d650a0400000074657874070700000000000000", + "descriptor": "(tenants/acme/text)/7u64", + "aad": "020000000000000022010000000000000300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f76310400000000000000746578745b0000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311a00000000000000766974616d696e632f636f6e746578742f7536342d6c652f763108000000000000000700000000000000", + "prf": "020000000000000022010000000000000300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f76310400000000000000746578745b0000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311a00000000000000766974616d696e632f636f6e746578742f7536342d6c652f763108000000000000000700000000000000", + "ciphertext": "0100000000000000000000000000000000d4e2b614715887c3b0b8b7ec13c8854820008e13eaedd78736f2531e16737e2346916e314064927acfb970d613fb11c81ba10271d40c1b9b7a0df25f48606585fc1e7337e33ab6f0816b959654cf48a22370352e03", + "equality": "ef4fd47818a5ec5b472f768bed2e28e9f3e0ef1bf86b95fb85b7d23208fb215a" + }, + "literal-ordered": { + "context_frame": "f002000000f002000000f0030000000a0700000074656e616e74730a0400000061636d650a04000000746578740707000000000000000a020000006575", + "plan_context_frame": "f002000000f002000000f0030000000a0700000074656e616e74730a0400000061636d650a04000000746578740707000000000000000a020000006575", + "descriptor": "((tenants/acme/text)/7u64)/eu", + "aad": "02000000000000009501000000000000020000000000000022010000000000000300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f76310400000000000000746578745b0000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311a00000000000000766974616d696e632f636f6e746578742f7536342d6c652f763108000000000000000700000000000000530000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f763102000000000000006575", + "prf": "02000000000000009501000000000000020000000000000022010000000000000300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f76310400000000000000746578745b0000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311a00000000000000766974616d696e632f636f6e746578742f7536342d6c652f763108000000000000000700000000000000530000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f763102000000000000006575", + "ciphertext": "0100000000000000000000000000000000127e42644c439f781b72f98a90a1d0282000d1df0e854edc5616954c06efdfc883277cb48bc41b10f03e96a58d6e43e9660d022ca2c0feab076d352efd3acbf89362ce17e2433f7efc4765f2e898a074883a9e1558", + "equality": "ad34715d40cf4bb40e35830f19c01180c08ef0492d40febc9d3267cab1464562" + }, + "literal-reversed": { + "context_frame": "f002000000f002000000f0030000000a0700000074656e616e74730a0400000061636d650a04000000746578740a020000006575070700000000000000", + "plan_context_frame": "f002000000f002000000f0030000000a0700000074656e616e74730a0400000061636d650a04000000746578740a020000006575070700000000000000", + "descriptor": "((tenants/acme/text)/eu)/7u64", + "aad": "02000000000000008d01000000000000020000000000000022010000000000000300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000074657874530000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631020000000000000065755b0000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311a00000000000000766974616d696e632f636f6e746578742f7536342d6c652f763108000000000000000700000000000000", + "prf": "02000000000000008d01000000000000020000000000000022010000000000000300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000074657874530000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631020000000000000065755b0000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311a00000000000000766974616d696e632f636f6e746578742f7536342d6c652f763108000000000000000700000000000000", + "ciphertext": "01000000000000000000000000000000008b7f54275b6c8f7b0acf5eed3c74894420006442ff82abfe7e68dffd900ec2826f1bc9ed4ae95e55118ccb6914165f4bd0760219cdf75189337f6170a4841f4cf1047c99b38e519538cb731a2c06dd706aece87288", + "equality": "ac1ae266a8574b0a164b771046e0028bc290e4018c2e48ba351515dcf95a7801" + }, + "literal-signed": { + "context_frame": "f002000000f0030000000a0700000074656e616e74730a0400000061636d650a0400000074657874050700000000000000", + "plan_context_frame": "f002000000f0030000000a0700000074656e616e74730a0400000061636d650a0400000074657874050700000000000000", + "descriptor": "(tenants/acme/text)/7u64", + "aad": "020000000000000022010000000000000300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f76310400000000000000746578745b0000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311a00000000000000766974616d696e632f636f6e746578742f6936342d6c652f763108000000000000000700000000000000", + "prf": "020000000000000022010000000000000300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f76310400000000000000746578745b0000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311a00000000000000766974616d696e632f636f6e746578742f6936342d6c652f763108000000000000000700000000000000", + "ciphertext": "010000000000000000000000000000000096958fa4c5718b558f2a196c4c02b901200011ff26eaa183171680c1943ed6124393bd39aa8b86e3eaadeb381dce51cb4f3902d5dad6d829cd36e38316f37972ac0daa8686959c51ab8ea85306708edbc6f6bf84e8", + "equality": "1bcd9b5f12c4218bbd584dce0bad0003138408cc312c1cc8bb69bc8861a8227e" + }, + "literal-text": { + "context_frame": "f002000000f0030000000a0700000074656e616e74730a0400000061636d650a04000000746578740a020000006575", + "plan_context_frame": "f002000000f0030000000a0700000074656e616e74730a0400000061636d650a04000000746578740a020000006575", + "descriptor": "(tenants/acme/text)/eu", + "aad": "020000000000000022010000000000000300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000074657874530000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f763102000000000000006575", + "prf": "020000000000000022010000000000000300000000000000580000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631070000000000000074656e616e7473550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000061636d65550000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f7631040000000000000074657874530000000000000003000000000000001900000000000000766974616d696e632f636f6e746578742f76616c75652f76311800000000000000766974616d696e632f636f6e746578742f757466382f763102000000000000006575", + "ciphertext": "01000000000000000000000000000000006379fc33f72fe6d1aa66fcdd2f394c342000e66e4d1f85d1e763c0244e39e813e1efc0af0620b0c18d2224e5183f49fc161402173f5892942f89e3a41297f4a9813e17da78930df22bb1f973085848b8fbaa74704a", + "equality": "76f509a979f486b783f2d4f872a1913abff9f3ef66d7007b01e3cab3a734e270" + } } } diff --git a/conformance/stack-encrypt/v1/record-declarations-v1.inputs.go.json b/conformance/stack-encrypt/v1/record-declarations-v1.inputs.go.json index 85fd2eb11..712fabbaf 100644 --- a/conformance/stack-encrypt/v1/record-declarations-v1.inputs.go.json +++ b/conformance/stack-encrypt/v1/record-declarations-v1.inputs.go.json @@ -1,1533 +1,1538 @@ { - "ciphertext-untyped": { - "Context": ["users"], - "Fields": [ - { - "Name": "value", - "Identity": "value", - "Kind": "", - "Outputs": ["c"], - "Null": "" - } - ] - }, - "index-only": { - "Context": ["users"], - "Fields": [ - { - "Name": "value", - "Identity": "value", - "Kind": "int64", - "Outputs": ["eq"], - "Null": "" - } - ] - }, - "outputs-retain-order": { - "Context": ["users"], - "Fields": [ - { - "Name": "value", - "Identity": "value", - "Kind": "int64", - "Outputs": ["ore", "c", "eq"], - "Null": "" - } - ] - }, - "match-default-bare": { - "Context": ["users"], - "Fields": [ - { - "Name": "value", - "Identity": "value", - "Kind": "string", - "Outputs": ["match"], - "Null": "" - } - ] - }, - "flat-label-is-not-extension": { - "Context": ["users", "region"], - "Fields": [ - { - "Name": "value", - "Identity": "value", - "Kind": "", - "Outputs": ["c"], - "Null": "" - } - ] - }, - "sealed-null-equality": { - "Context": ["users"], - "Fields": [ - { - "Name": "value", - "Identity": "value", - "Kind": "int32", - "Outputs": ["c", "eq"], - "Null": "sealed" - } - ] - }, - "sealed-null-bool": { - "Context": ["users"], - "Fields": [ - { - "Name": "value", - "Identity": "value", - "Kind": "bool", - "Outputs": ["c"], - "Null": "sealed" - } - ] - }, - "sql-null-untyped": { - "Context": ["users"], - "Fields": [ - { - "Name": "value", - "Identity": "value", - "Kind": "", - "Outputs": ["c"], - "Null": "sql" - } - ] - }, - "type-bool-eq": { - "Context": ["users"], - "Fields": [ - { - "Name": "value", - "Identity": "value", - "Kind": "bool", - "Outputs": ["c", "eq"], - "Null": "" - } - ] - }, - "type-bool-match": { - "Context": ["users"], - "Fields": [ - { - "Name": "value", - "Identity": "value", - "Kind": "bool", - "Outputs": ["c", "match"], - "Null": "" - } - ] - }, - "type-bool-ore": { - "Context": ["users"], - "Fields": [ - { - "Name": "value", - "Identity": "value", - "Kind": "bool", - "Outputs": ["c", "ore"], - "Null": "" - } - ] - }, - "type-bool-ope": { - "Context": ["users"], - "Fields": [ - { - "Name": "value", - "Identity": "value", - "Kind": "bool", - "Outputs": ["c", "ope"], - "Null": "" - } - ] - }, - "type-bool-ore_block": { - "Context": ["users"], - "Fields": [ - { - "Name": "value", - "Identity": "value", - "Kind": "bool", - "Outputs": ["c", "ore_block"], - "Null": "" - } - ] - }, - "type-int8-eq": { - "Context": ["users"], - "Fields": [ - { - "Name": "value", - "Identity": "value", - "Kind": "int8", - "Outputs": ["c", "eq"], - "Null": "" - } - ] - }, - "type-int8-match": { - "Context": ["users"], - "Fields": [ - { - "Name": "value", - "Identity": "value", - "Kind": "int8", - "Outputs": ["c", "match"], - "Null": "" - } - ] - }, - "type-int8-ore": { - "Context": ["users"], - "Fields": [ - { - "Name": "value", - "Identity": "value", - "Kind": "int8", - "Outputs": ["c", "ore"], - "Null": "" - } - ] - }, - "type-int8-ope": { - "Context": ["users"], - "Fields": [ - { - "Name": "value", - "Identity": "value", - "Kind": "int8", - "Outputs": ["c", "ope"], - "Null": "" - } - ] - }, - "type-int8-ore_block": { - "Context": ["users"], - "Fields": [ - { - "Name": "value", - "Identity": "value", - "Kind": "int8", - "Outputs": ["c", "ore_block"], - "Null": "" - } - ] - }, - "type-int16-eq": { - "Context": ["users"], - "Fields": [ - { - "Name": "value", - "Identity": "value", - "Kind": "int16", - "Outputs": ["c", "eq"], - "Null": "" - } - ] - }, - "type-int16-match": { - "Context": ["users"], - "Fields": [ - { - "Name": "value", - "Identity": "value", - "Kind": "int16", - "Outputs": ["c", "match"], - "Null": "" - } - ] - }, - "type-int16-ore": { - "Context": ["users"], - "Fields": [ - { - "Name": "value", - "Identity": "value", - "Kind": "int16", - "Outputs": ["c", "ore"], - "Null": "" - } - ] - }, - "type-int16-ope": { - "Context": ["users"], - "Fields": [ - { - "Name": "value", - "Identity": "value", - "Kind": "int16", - "Outputs": ["c", "ope"], - "Null": "" - } - ] - }, - "type-int16-ore_block": { - "Context": ["users"], - "Fields": [ - { - "Name": "value", - "Identity": "value", - "Kind": "int16", - "Outputs": ["c", "ore_block"], - "Null": "" - } - ] - }, - "type-int32-eq": { - "Context": ["users"], - "Fields": [ - { - "Name": "value", - "Identity": "value", - "Kind": "int32", - "Outputs": ["c", "eq"], - "Null": "" - } - ] - }, - "type-int32-match": { - "Context": ["users"], - "Fields": [ - { - "Name": "value", - "Identity": "value", - "Kind": "int32", - "Outputs": ["c", "match"], - "Null": "" - } - ] - }, - "type-int32-ore": { - "Context": ["users"], - "Fields": [ - { - "Name": "value", - "Identity": "value", - "Kind": "int32", - "Outputs": ["c", "ore"], - "Null": "" - } - ] - }, - "type-int32-ope": { - "Context": ["users"], - "Fields": [ - { - "Name": "value", - "Identity": "value", - "Kind": "int32", - "Outputs": ["c", "ope"], - "Null": "" - } - ] - }, - "type-int32-ore_block": { - "Context": ["users"], - "Fields": [ - { - "Name": "value", - "Identity": "value", - "Kind": "int32", - "Outputs": ["c", "ore_block"], - "Null": "" - } - ] - }, - "type-int64-eq": { - "Context": ["users"], - "Fields": [ - { - "Name": "value", - "Identity": "value", - "Kind": "int64", - "Outputs": ["c", "eq"], - "Null": "" - } - ] - }, - "type-int64-match": { - "Context": ["users"], - "Fields": [ - { - "Name": "value", - "Identity": "value", - "Kind": "int64", - "Outputs": ["c", "match"], - "Null": "" - } - ] - }, - "type-int64-ore": { - "Context": ["users"], - "Fields": [ - { - "Name": "value", - "Identity": "value", - "Kind": "int64", - "Outputs": ["c", "ore"], - "Null": "" - } - ] - }, - "type-int64-ope": { - "Context": ["users"], - "Fields": [ - { - "Name": "value", - "Identity": "value", - "Kind": "int64", - "Outputs": ["c", "ope"], - "Null": "" - } - ] - }, - "type-int64-ore_block": { - "Context": ["users"], - "Fields": [ - { - "Name": "value", - "Identity": "value", - "Kind": "int64", - "Outputs": ["c", "ore_block"], - "Null": "" - } - ] - }, - "type-int128-eq": { - "Context": ["users"], - "Fields": [ - { - "Name": "value", - "Identity": "value", - "Kind": "int128", - "Outputs": ["c", "eq"], - "Null": "" - } - ] - }, - "type-int128-match": { - "Context": ["users"], - "Fields": [ - { - "Name": "value", - "Identity": "value", - "Kind": "int128", - "Outputs": ["c", "match"], - "Null": "" - } - ] - }, - "type-int128-ore": { - "Context": ["users"], - "Fields": [ - { - "Name": "value", - "Identity": "value", - "Kind": "int128", - "Outputs": ["c", "ore"], - "Null": "" - } - ] - }, - "type-int128-ope": { - "Context": ["users"], - "Fields": [ - { - "Name": "value", - "Identity": "value", - "Kind": "int128", - "Outputs": ["c", "ope"], - "Null": "" - } - ] - }, - "type-int128-ore_block": { - "Context": ["users"], - "Fields": [ - { - "Name": "value", - "Identity": "value", - "Kind": "int128", - "Outputs": ["c", "ore_block"], - "Null": "" - } - ] - }, - "type-uint8-eq": { - "Context": ["users"], - "Fields": [ - { - "Name": "value", - "Identity": "value", - "Kind": "uint8", - "Outputs": ["c", "eq"], - "Null": "" - } - ] - }, - "type-uint8-match": { - "Context": ["users"], - "Fields": [ - { - "Name": "value", - "Identity": "value", - "Kind": "uint8", - "Outputs": ["c", "match"], - "Null": "" - } - ] - }, - "type-uint8-ore": { - "Context": ["users"], - "Fields": [ - { - "Name": "value", - "Identity": "value", - "Kind": "uint8", - "Outputs": ["c", "ore"], - "Null": "" - } - ] - }, - "type-uint8-ope": { - "Context": ["users"], - "Fields": [ - { - "Name": "value", - "Identity": "value", - "Kind": "uint8", - "Outputs": ["c", "ope"], - "Null": "" - } - ] - }, - "type-uint8-ore_block": { - "Context": ["users"], - "Fields": [ - { - "Name": "value", - "Identity": "value", - "Kind": "uint8", - "Outputs": ["c", "ore_block"], - "Null": "" - } - ] - }, - "type-uint16-eq": { - "Context": ["users"], - "Fields": [ - { - "Name": "value", - "Identity": "value", - "Kind": "uint16", - "Outputs": ["c", "eq"], - "Null": "" - } - ] - }, - "type-uint16-match": { - "Context": ["users"], - "Fields": [ - { - "Name": "value", - "Identity": "value", - "Kind": "uint16", - "Outputs": ["c", "match"], - "Null": "" - } - ] - }, - "type-uint16-ore": { - "Context": ["users"], - "Fields": [ - { - "Name": "value", - "Identity": "value", - "Kind": "uint16", - "Outputs": ["c", "ore"], - "Null": "" - } - ] - }, - "type-uint16-ope": { - "Context": ["users"], - "Fields": [ - { - "Name": "value", - "Identity": "value", - "Kind": "uint16", - "Outputs": ["c", "ope"], - "Null": "" - } - ] - }, - "type-uint16-ore_block": { - "Context": ["users"], - "Fields": [ - { - "Name": "value", - "Identity": "value", - "Kind": "uint16", - "Outputs": ["c", "ore_block"], - "Null": "" - } - ] - }, - "type-uint32-eq": { - "Context": ["users"], - "Fields": [ - { - "Name": "value", - "Identity": "value", - "Kind": "uint32", - "Outputs": ["c", "eq"], - "Null": "" - } - ] - }, - "type-uint32-match": { - "Context": ["users"], - "Fields": [ - { - "Name": "value", - "Identity": "value", - "Kind": "uint32", - "Outputs": ["c", "match"], - "Null": "" - } - ] - }, - "type-uint32-ore": { - "Context": ["users"], - "Fields": [ - { - "Name": "value", - "Identity": "value", - "Kind": "uint32", - "Outputs": ["c", "ore"], - "Null": "" - } - ] - }, - "type-uint32-ope": { - "Context": ["users"], - "Fields": [ - { - "Name": "value", - "Identity": "value", - "Kind": "uint32", - "Outputs": ["c", "ope"], - "Null": "" - } - ] - }, - "type-uint32-ore_block": { - "Context": ["users"], - "Fields": [ - { - "Name": "value", - "Identity": "value", - "Kind": "uint32", - "Outputs": ["c", "ore_block"], - "Null": "" - } - ] - }, - "type-uint64-eq": { - "Context": ["users"], - "Fields": [ - { - "Name": "value", - "Identity": "value", - "Kind": "uint64", - "Outputs": ["c", "eq"], - "Null": "" - } - ] - }, - "type-uint64-match": { - "Context": ["users"], - "Fields": [ - { - "Name": "value", - "Identity": "value", - "Kind": "uint64", - "Outputs": ["c", "match"], - "Null": "" - } - ] - }, - "type-uint64-ore": { - "Context": ["users"], - "Fields": [ - { - "Name": "value", - "Identity": "value", - "Kind": "uint64", - "Outputs": ["c", "ore"], - "Null": "" - } - ] - }, - "type-uint64-ope": { - "Context": ["users"], - "Fields": [ - { - "Name": "value", - "Identity": "value", - "Kind": "uint64", - "Outputs": ["c", "ope"], - "Null": "" - } - ] - }, - "type-uint64-ore_block": { - "Context": ["users"], - "Fields": [ - { - "Name": "value", - "Identity": "value", - "Kind": "uint64", - "Outputs": ["c", "ore_block"], - "Null": "" - } - ] - }, - "type-uint128-eq": { - "Context": ["users"], - "Fields": [ - { - "Name": "value", - "Identity": "value", - "Kind": "uint128", - "Outputs": ["c", "eq"], - "Null": "" - } - ] - }, - "type-uint128-match": { - "Context": ["users"], - "Fields": [ - { - "Name": "value", - "Identity": "value", - "Kind": "uint128", - "Outputs": ["c", "match"], - "Null": "" - } - ] - }, - "type-uint128-ore": { - "Context": ["users"], - "Fields": [ - { - "Name": "value", - "Identity": "value", - "Kind": "uint128", - "Outputs": ["c", "ore"], - "Null": "" - } - ] - }, - "type-uint128-ope": { - "Context": ["users"], - "Fields": [ - { - "Name": "value", - "Identity": "value", - "Kind": "uint128", - "Outputs": ["c", "ope"], - "Null": "" - } - ] - }, - "type-uint128-ore_block": { - "Context": ["users"], - "Fields": [ - { - "Name": "value", - "Identity": "value", - "Kind": "uint128", - "Outputs": ["c", "ore_block"], - "Null": "" - } - ] - }, - "type-float32-eq": { - "Context": ["users"], - "Fields": [ - { - "Name": "value", - "Identity": "value", - "Kind": "float32", - "Outputs": ["c", "eq"], - "Null": "" - } - ] - }, - "type-float32-match": { - "Context": ["users"], - "Fields": [ - { - "Name": "value", - "Identity": "value", - "Kind": "float32", - "Outputs": ["c", "match"], - "Null": "" - } - ] - }, - "type-float32-ore": { - "Context": ["users"], - "Fields": [ - { - "Name": "value", - "Identity": "value", - "Kind": "float32", - "Outputs": ["c", "ore"], - "Null": "" - } - ] - }, - "type-float32-ope": { - "Context": ["users"], - "Fields": [ - { - "Name": "value", - "Identity": "value", - "Kind": "float32", - "Outputs": ["c", "ope"], - "Null": "" - } - ] - }, - "type-float32-ore_block": { - "Context": ["users"], - "Fields": [ - { - "Name": "value", - "Identity": "value", - "Kind": "float32", - "Outputs": ["c", "ore_block"], - "Null": "" - } - ] - }, - "type-float64-eq": { - "Context": ["users"], - "Fields": [ - { - "Name": "value", - "Identity": "value", - "Kind": "float64", - "Outputs": ["c", "eq"], - "Null": "" - } - ] - }, - "type-float64-match": { - "Context": ["users"], - "Fields": [ - { - "Name": "value", - "Identity": "value", - "Kind": "float64", - "Outputs": ["c", "match"], - "Null": "" - } - ] - }, - "type-float64-ore": { - "Context": ["users"], - "Fields": [ - { - "Name": "value", - "Identity": "value", - "Kind": "float64", - "Outputs": ["c", "ore"], - "Null": "" - } - ] - }, - "type-float64-ope": { - "Context": ["users"], - "Fields": [ - { - "Name": "value", - "Identity": "value", - "Kind": "float64", - "Outputs": ["c", "ope"], - "Null": "" - } - ] - }, - "type-float64-ore_block": { - "Context": ["users"], - "Fields": [ - { - "Name": "value", - "Identity": "value", - "Kind": "float64", - "Outputs": ["c", "ore_block"], - "Null": "" - } - ] - }, - "type-decimal-eq": { - "Context": ["users"], - "Fields": [ - { - "Name": "value", - "Identity": "value", - "Kind": "decimal", - "Outputs": ["c", "eq"], - "Null": "" - } - ] - }, - "type-decimal-match": { - "Context": ["users"], - "Fields": [ - { - "Name": "value", - "Identity": "value", - "Kind": "decimal", - "Outputs": ["c", "match"], - "Null": "" - } - ] - }, - "type-decimal-ore": { - "Context": ["users"], - "Fields": [ - { - "Name": "value", - "Identity": "value", - "Kind": "decimal", - "Outputs": ["c", "ore"], - "Null": "" - } - ] - }, - "type-decimal-ope": { - "Context": ["users"], - "Fields": [ - { - "Name": "value", - "Identity": "value", - "Kind": "decimal", - "Outputs": ["c", "ope"], - "Null": "" - } - ] - }, - "type-decimal-ore_block": { - "Context": ["users"], - "Fields": [ - { - "Name": "value", - "Identity": "value", - "Kind": "decimal", - "Outputs": ["c", "ore_block"], - "Null": "" - } - ] - }, - "type-date-eq": { - "Context": ["users"], - "Fields": [ - { - "Name": "value", - "Identity": "value", - "Kind": "date", - "Outputs": ["c", "eq"], - "Null": "" - } - ] - }, - "type-date-match": { - "Context": ["users"], - "Fields": [ - { - "Name": "value", - "Identity": "value", - "Kind": "date", - "Outputs": ["c", "match"], - "Null": "" - } - ] - }, - "type-date-ore": { - "Context": ["users"], - "Fields": [ - { - "Name": "value", - "Identity": "value", - "Kind": "date", - "Outputs": ["c", "ore"], - "Null": "" - } - ] - }, - "type-date-ope": { - "Context": ["users"], - "Fields": [ - { - "Name": "value", - "Identity": "value", - "Kind": "date", - "Outputs": ["c", "ope"], - "Null": "" - } - ] - }, - "type-date-ore_block": { - "Context": ["users"], - "Fields": [ - { - "Name": "value", - "Identity": "value", - "Kind": "date", - "Outputs": ["c", "ore_block"], - "Null": "" - } - ] - }, - "type-timestamp-eq": { - "Context": ["users"], - "Fields": [ - { - "Name": "value", - "Identity": "value", - "Kind": "timestamp", - "Outputs": ["c", "eq"], - "Null": "" - } - ] - }, - "type-timestamp-match": { - "Context": ["users"], - "Fields": [ - { - "Name": "value", - "Identity": "value", - "Kind": "timestamp", - "Outputs": ["c", "match"], - "Null": "" - } - ] - }, - "type-timestamp-ore": { - "Context": ["users"], - "Fields": [ - { - "Name": "value", - "Identity": "value", - "Kind": "timestamp", - "Outputs": ["c", "ore"], - "Null": "" - } - ] - }, - "type-timestamp-ope": { - "Context": ["users"], - "Fields": [ - { - "Name": "value", - "Identity": "value", - "Kind": "timestamp", - "Outputs": ["c", "ope"], - "Null": "" - } - ] - }, - "type-timestamp-ore_block": { - "Context": ["users"], - "Fields": [ - { - "Name": "value", - "Identity": "value", - "Kind": "timestamp", - "Outputs": ["c", "ore_block"], - "Null": "" - } - ] - }, - "type-string-eq": { - "Context": ["users"], - "Fields": [ - { - "Name": "value", - "Identity": "value", - "Kind": "string", - "Outputs": ["c", "eq"], - "Null": "" - } - ] - }, - "type-string-match": { - "Context": ["users"], - "Fields": [ - { - "Name": "value", - "Identity": "value", - "Kind": "string", - "Outputs": ["c", "match"], - "Null": "" - } - ] - }, - "type-string-ore": { - "Context": ["users"], - "Fields": [ - { - "Name": "value", - "Identity": "value", - "Kind": "string", - "Outputs": ["c", "ore"], - "Null": "" - } - ] - }, - "type-string-ope": { - "Context": ["users"], - "Fields": [ - { - "Name": "value", - "Identity": "value", - "Kind": "string", - "Outputs": ["c", "ope"], - "Null": "" - } - ] - }, - "type-string-ore_block": { - "Context": ["users"], - "Fields": [ - { - "Name": "value", - "Identity": "value", - "Kind": "string", - "Outputs": ["c", "ore_block"], - "Null": "" - } - ] - }, - "type-bytes-eq": { - "Context": ["users"], - "Fields": [ - { - "Name": "value", - "Identity": "value", - "Kind": "bytes", - "Outputs": ["c", "eq"], - "Null": "" - } - ] - }, - "type-bytes-match": { - "Context": ["users"], - "Fields": [ - { - "Name": "value", - "Identity": "value", - "Kind": "bytes", - "Outputs": ["c", "match"], - "Null": "" - } - ] - }, - "type-bytes-ore": { - "Context": ["users"], - "Fields": [ - { - "Name": "value", - "Identity": "value", - "Kind": "bytes", - "Outputs": ["c", "ore"], - "Null": "" - } - ] - }, - "type-bytes-ope": { - "Context": ["users"], - "Fields": [ - { - "Name": "value", - "Identity": "value", - "Kind": "bytes", - "Outputs": ["c", "ope"], - "Null": "" - } - ] - }, - "type-bytes-ore_block": { - "Context": ["users"], - "Fields": [ - { - "Name": "value", - "Identity": "value", - "Kind": "bytes", - "Outputs": ["c", "ore_block"], - "Null": "" - } - ] - }, - "type-array-eq": { - "Context": ["users"], - "Fields": [ - { - "Name": "value", - "Identity": "value", - "Kind": "array", - "Outputs": ["c", "eq"], - "Null": "" - } - ] - }, - "type-array-match": { - "Context": ["users"], - "Fields": [ - { - "Name": "value", - "Identity": "value", - "Kind": "array", - "Outputs": ["c", "match"], - "Null": "" - } - ] - }, - "type-array-ore": { - "Context": ["users"], - "Fields": [ - { - "Name": "value", - "Identity": "value", - "Kind": "array", - "Outputs": ["c", "ore"], - "Null": "" - } - ] - }, - "type-array-ope": { - "Context": ["users"], - "Fields": [ - { - "Name": "value", - "Identity": "value", - "Kind": "array", - "Outputs": ["c", "ope"], - "Null": "" - } - ] - }, - "type-array-ore_block": { - "Context": ["users"], - "Fields": [ - { - "Name": "value", - "Identity": "value", - "Kind": "array", - "Outputs": ["c", "ore_block"], - "Null": "" - } - ] - }, - "type-object-eq": { - "Context": ["users"], - "Fields": [ - { - "Name": "value", - "Identity": "value", - "Kind": "object", - "Outputs": ["c", "eq"], - "Null": "" - } - ] - }, - "type-object-match": { - "Context": ["users"], - "Fields": [ - { - "Name": "value", - "Identity": "value", - "Kind": "object", - "Outputs": ["c", "match"], - "Null": "" - } - ] - }, - "type-object-ore": { - "Context": ["users"], - "Fields": [ - { - "Name": "value", - "Identity": "value", - "Kind": "object", - "Outputs": ["c", "ore"], - "Null": "" - } - ] - }, - "type-object-ope": { - "Context": ["users"], - "Fields": [ - { - "Name": "value", - "Identity": "value", - "Kind": "object", - "Outputs": ["c", "ope"], - "Null": "" - } - ] - }, - "type-object-ore_block": { - "Context": ["users"], - "Fields": [ - { - "Name": "value", - "Identity": "value", - "Kind": "object", - "Outputs": ["c", "ore_block"], - "Null": "" - } - ] - }, - "type-json-eq": { - "Context": ["users"], - "Fields": [ - { - "Name": "value", - "Identity": "value", - "Kind": "json", - "Outputs": ["c", "eq"], - "Null": "" - } - ] - }, - "type-json-match": { - "Context": ["users"], - "Fields": [ - { - "Name": "value", - "Identity": "value", - "Kind": "json", - "Outputs": ["c", "match"], - "Null": "" - } - ] - }, - "type-json-ore": { - "Context": ["users"], - "Fields": [ - { - "Name": "value", - "Identity": "value", - "Kind": "json", - "Outputs": ["c", "ore"], - "Null": "" - } - ] - }, - "type-json-ope": { - "Context": ["users"], - "Fields": [ - { - "Name": "value", - "Identity": "value", - "Kind": "json", - "Outputs": ["c", "ope"], - "Null": "" - } - ] - }, - "type-json-ore_block": { - "Context": ["users"], - "Fields": [ - { - "Name": "value", - "Identity": "value", - "Kind": "json", - "Outputs": ["c", "ore_block"], - "Null": "" - } - ] - }, - "outputs-empty": { - "Context": ["users"], - "Fields": [ - { - "Name": "value", - "Identity": "value", - "Kind": "", - "Outputs": [], - "Null": "" - } - ] - }, - "output-unknown": { - "Context": ["users"], - "Fields": [ - { - "Name": "value", - "Identity": "value", - "Kind": "", - "Outputs": ["wat"], - "Null": "" - } - ] - }, - "output-duplicate": { - "Context": ["users"], - "Fields": [ - { - "Name": "value", - "Identity": "value", - "Kind": "", - "Outputs": ["c", "c"], - "Null": "" - } - ] - }, - "type-unknown": { - "Context": ["users"], - "Fields": [ - { - "Name": "value", - "Identity": "value", - "Kind": "uuid", - "Outputs": ["c"], - "Null": "" - } - ] - }, - "type-json-without-target": { - "Context": ["users"], - "Fields": [ - { - "Name": "value", - "Identity": "value", - "Kind": "json", - "Outputs": ["c"], - "Null": "" - } - ] - }, - "json-output-pending": { - "Context": ["users"], - "Fields": [ - { - "Name": "value", - "Identity": "value", - "Kind": "json", - "Outputs": ["json"], - "Null": "" - } - ] - }, - "context-segment-slash": { - "Context": ["users"], - "Fields": [ - { - "Name": "value", - "Identity": "a/b", - "Kind": "", - "Outputs": ["c"], - "Null": "" - } - ] - }, - "context-segment-digit": { - "Context": ["users"], - "Fields": [ - { - "Name": "value", - "Identity": "9", - "Kind": "", - "Outputs": ["c"], - "Null": "" - } - ] - }, - "shared-identity": { - "Context": ["users"], - "Fields": [ - { - "Name": "a", - "Identity": "same", - "Kind": "", - "Outputs": ["c"], - "Null": "" - }, - { - "Name": "b", - "Identity": "same", - "Kind": "", - "Outputs": ["c"], - "Null": "" - } - ] - }, - "null-mode-unknown": { - "Context": ["users"], - "Fields": [ - { - "Name": "value", - "Identity": "value", - "Kind": "", - "Outputs": ["c"], - "Null": "optional" - } - ] - }, - "sealed-null-untyped": { - "Context": ["users"], - "Fields": [ - { - "Name": "value", - "Identity": "value", - "Kind": "", - "Outputs": ["c"], - "Null": "sealed" - } - ] - }, - "sealed-null-text": { - "Context": ["users"], - "Fields": [ - { - "Name": "value", - "Identity": "value", - "Kind": "string", - "Outputs": ["c"], - "Null": "sealed" - } - ] - }, - "sealed-null-order": { - "Context": ["users"], - "Fields": [ - { - "Name": "value", - "Identity": "value", - "Kind": "int64", - "Outputs": ["c", "ore"], - "Null": "sealed" - } - ] - }, - "untyped-index": { - "Context": ["users"], - "Fields": [ - { - "Name": "value", - "Identity": "value", - "Kind": "", - "Outputs": ["eq"], - "Null": "" - } - ] + "version": 1, + "profile": "record-declarations-v1", + "runner": "go", + "cases": { + "ciphertext-untyped": { + "Context": ["users"], + "Fields": [ + { + "Name": "value", + "Identity": "value", + "Kind": "", + "Outputs": ["c"], + "Null": "" + } + ] + }, + "index-only": { + "Context": ["users"], + "Fields": [ + { + "Name": "value", + "Identity": "value", + "Kind": "int64", + "Outputs": ["eq"], + "Null": "" + } + ] + }, + "outputs-retain-order": { + "Context": ["users"], + "Fields": [ + { + "Name": "value", + "Identity": "value", + "Kind": "int64", + "Outputs": ["ore", "c", "eq"], + "Null": "" + } + ] + }, + "match-default-bare": { + "Context": ["users"], + "Fields": [ + { + "Name": "value", + "Identity": "value", + "Kind": "string", + "Outputs": ["match"], + "Null": "" + } + ] + }, + "flat-label-is-not-extension": { + "Context": ["users", "region"], + "Fields": [ + { + "Name": "value", + "Identity": "value", + "Kind": "", + "Outputs": ["c"], + "Null": "" + } + ] + }, + "sealed-null-equality": { + "Context": ["users"], + "Fields": [ + { + "Name": "value", + "Identity": "value", + "Kind": "int32", + "Outputs": ["c", "eq"], + "Null": "sealed" + } + ] + }, + "sealed-null-bool": { + "Context": ["users"], + "Fields": [ + { + "Name": "value", + "Identity": "value", + "Kind": "bool", + "Outputs": ["c"], + "Null": "sealed" + } + ] + }, + "sql-null-untyped": { + "Context": ["users"], + "Fields": [ + { + "Name": "value", + "Identity": "value", + "Kind": "", + "Outputs": ["c"], + "Null": "sql" + } + ] + }, + "type-bool-eq": { + "Context": ["users"], + "Fields": [ + { + "Name": "value", + "Identity": "value", + "Kind": "bool", + "Outputs": ["c", "eq"], + "Null": "" + } + ] + }, + "type-bool-match": { + "Context": ["users"], + "Fields": [ + { + "Name": "value", + "Identity": "value", + "Kind": "bool", + "Outputs": ["c", "match"], + "Null": "" + } + ] + }, + "type-bool-ore": { + "Context": ["users"], + "Fields": [ + { + "Name": "value", + "Identity": "value", + "Kind": "bool", + "Outputs": ["c", "ore"], + "Null": "" + } + ] + }, + "type-bool-ope": { + "Context": ["users"], + "Fields": [ + { + "Name": "value", + "Identity": "value", + "Kind": "bool", + "Outputs": ["c", "ope"], + "Null": "" + } + ] + }, + "type-bool-ore_block": { + "Context": ["users"], + "Fields": [ + { + "Name": "value", + "Identity": "value", + "Kind": "bool", + "Outputs": ["c", "ore_block"], + "Null": "" + } + ] + }, + "type-int8-eq": { + "Context": ["users"], + "Fields": [ + { + "Name": "value", + "Identity": "value", + "Kind": "int8", + "Outputs": ["c", "eq"], + "Null": "" + } + ] + }, + "type-int8-match": { + "Context": ["users"], + "Fields": [ + { + "Name": "value", + "Identity": "value", + "Kind": "int8", + "Outputs": ["c", "match"], + "Null": "" + } + ] + }, + "type-int8-ore": { + "Context": ["users"], + "Fields": [ + { + "Name": "value", + "Identity": "value", + "Kind": "int8", + "Outputs": ["c", "ore"], + "Null": "" + } + ] + }, + "type-int8-ope": { + "Context": ["users"], + "Fields": [ + { + "Name": "value", + "Identity": "value", + "Kind": "int8", + "Outputs": ["c", "ope"], + "Null": "" + } + ] + }, + "type-int8-ore_block": { + "Context": ["users"], + "Fields": [ + { + "Name": "value", + "Identity": "value", + "Kind": "int8", + "Outputs": ["c", "ore_block"], + "Null": "" + } + ] + }, + "type-int16-eq": { + "Context": ["users"], + "Fields": [ + { + "Name": "value", + "Identity": "value", + "Kind": "int16", + "Outputs": ["c", "eq"], + "Null": "" + } + ] + }, + "type-int16-match": { + "Context": ["users"], + "Fields": [ + { + "Name": "value", + "Identity": "value", + "Kind": "int16", + "Outputs": ["c", "match"], + "Null": "" + } + ] + }, + "type-int16-ore": { + "Context": ["users"], + "Fields": [ + { + "Name": "value", + "Identity": "value", + "Kind": "int16", + "Outputs": ["c", "ore"], + "Null": "" + } + ] + }, + "type-int16-ope": { + "Context": ["users"], + "Fields": [ + { + "Name": "value", + "Identity": "value", + "Kind": "int16", + "Outputs": ["c", "ope"], + "Null": "" + } + ] + }, + "type-int16-ore_block": { + "Context": ["users"], + "Fields": [ + { + "Name": "value", + "Identity": "value", + "Kind": "int16", + "Outputs": ["c", "ore_block"], + "Null": "" + } + ] + }, + "type-int32-eq": { + "Context": ["users"], + "Fields": [ + { + "Name": "value", + "Identity": "value", + "Kind": "int32", + "Outputs": ["c", "eq"], + "Null": "" + } + ] + }, + "type-int32-match": { + "Context": ["users"], + "Fields": [ + { + "Name": "value", + "Identity": "value", + "Kind": "int32", + "Outputs": ["c", "match"], + "Null": "" + } + ] + }, + "type-int32-ore": { + "Context": ["users"], + "Fields": [ + { + "Name": "value", + "Identity": "value", + "Kind": "int32", + "Outputs": ["c", "ore"], + "Null": "" + } + ] + }, + "type-int32-ope": { + "Context": ["users"], + "Fields": [ + { + "Name": "value", + "Identity": "value", + "Kind": "int32", + "Outputs": ["c", "ope"], + "Null": "" + } + ] + }, + "type-int32-ore_block": { + "Context": ["users"], + "Fields": [ + { + "Name": "value", + "Identity": "value", + "Kind": "int32", + "Outputs": ["c", "ore_block"], + "Null": "" + } + ] + }, + "type-int64-eq": { + "Context": ["users"], + "Fields": [ + { + "Name": "value", + "Identity": "value", + "Kind": "int64", + "Outputs": ["c", "eq"], + "Null": "" + } + ] + }, + "type-int64-match": { + "Context": ["users"], + "Fields": [ + { + "Name": "value", + "Identity": "value", + "Kind": "int64", + "Outputs": ["c", "match"], + "Null": "" + } + ] + }, + "type-int64-ore": { + "Context": ["users"], + "Fields": [ + { + "Name": "value", + "Identity": "value", + "Kind": "int64", + "Outputs": ["c", "ore"], + "Null": "" + } + ] + }, + "type-int64-ope": { + "Context": ["users"], + "Fields": [ + { + "Name": "value", + "Identity": "value", + "Kind": "int64", + "Outputs": ["c", "ope"], + "Null": "" + } + ] + }, + "type-int64-ore_block": { + "Context": ["users"], + "Fields": [ + { + "Name": "value", + "Identity": "value", + "Kind": "int64", + "Outputs": ["c", "ore_block"], + "Null": "" + } + ] + }, + "type-int128-eq": { + "Context": ["users"], + "Fields": [ + { + "Name": "value", + "Identity": "value", + "Kind": "int128", + "Outputs": ["c", "eq"], + "Null": "" + } + ] + }, + "type-int128-match": { + "Context": ["users"], + "Fields": [ + { + "Name": "value", + "Identity": "value", + "Kind": "int128", + "Outputs": ["c", "match"], + "Null": "" + } + ] + }, + "type-int128-ore": { + "Context": ["users"], + "Fields": [ + { + "Name": "value", + "Identity": "value", + "Kind": "int128", + "Outputs": ["c", "ore"], + "Null": "" + } + ] + }, + "type-int128-ope": { + "Context": ["users"], + "Fields": [ + { + "Name": "value", + "Identity": "value", + "Kind": "int128", + "Outputs": ["c", "ope"], + "Null": "" + } + ] + }, + "type-int128-ore_block": { + "Context": ["users"], + "Fields": [ + { + "Name": "value", + "Identity": "value", + "Kind": "int128", + "Outputs": ["c", "ore_block"], + "Null": "" + } + ] + }, + "type-uint8-eq": { + "Context": ["users"], + "Fields": [ + { + "Name": "value", + "Identity": "value", + "Kind": "uint8", + "Outputs": ["c", "eq"], + "Null": "" + } + ] + }, + "type-uint8-match": { + "Context": ["users"], + "Fields": [ + { + "Name": "value", + "Identity": "value", + "Kind": "uint8", + "Outputs": ["c", "match"], + "Null": "" + } + ] + }, + "type-uint8-ore": { + "Context": ["users"], + "Fields": [ + { + "Name": "value", + "Identity": "value", + "Kind": "uint8", + "Outputs": ["c", "ore"], + "Null": "" + } + ] + }, + "type-uint8-ope": { + "Context": ["users"], + "Fields": [ + { + "Name": "value", + "Identity": "value", + "Kind": "uint8", + "Outputs": ["c", "ope"], + "Null": "" + } + ] + }, + "type-uint8-ore_block": { + "Context": ["users"], + "Fields": [ + { + "Name": "value", + "Identity": "value", + "Kind": "uint8", + "Outputs": ["c", "ore_block"], + "Null": "" + } + ] + }, + "type-uint16-eq": { + "Context": ["users"], + "Fields": [ + { + "Name": "value", + "Identity": "value", + "Kind": "uint16", + "Outputs": ["c", "eq"], + "Null": "" + } + ] + }, + "type-uint16-match": { + "Context": ["users"], + "Fields": [ + { + "Name": "value", + "Identity": "value", + "Kind": "uint16", + "Outputs": ["c", "match"], + "Null": "" + } + ] + }, + "type-uint16-ore": { + "Context": ["users"], + "Fields": [ + { + "Name": "value", + "Identity": "value", + "Kind": "uint16", + "Outputs": ["c", "ore"], + "Null": "" + } + ] + }, + "type-uint16-ope": { + "Context": ["users"], + "Fields": [ + { + "Name": "value", + "Identity": "value", + "Kind": "uint16", + "Outputs": ["c", "ope"], + "Null": "" + } + ] + }, + "type-uint16-ore_block": { + "Context": ["users"], + "Fields": [ + { + "Name": "value", + "Identity": "value", + "Kind": "uint16", + "Outputs": ["c", "ore_block"], + "Null": "" + } + ] + }, + "type-uint32-eq": { + "Context": ["users"], + "Fields": [ + { + "Name": "value", + "Identity": "value", + "Kind": "uint32", + "Outputs": ["c", "eq"], + "Null": "" + } + ] + }, + "type-uint32-match": { + "Context": ["users"], + "Fields": [ + { + "Name": "value", + "Identity": "value", + "Kind": "uint32", + "Outputs": ["c", "match"], + "Null": "" + } + ] + }, + "type-uint32-ore": { + "Context": ["users"], + "Fields": [ + { + "Name": "value", + "Identity": "value", + "Kind": "uint32", + "Outputs": ["c", "ore"], + "Null": "" + } + ] + }, + "type-uint32-ope": { + "Context": ["users"], + "Fields": [ + { + "Name": "value", + "Identity": "value", + "Kind": "uint32", + "Outputs": ["c", "ope"], + "Null": "" + } + ] + }, + "type-uint32-ore_block": { + "Context": ["users"], + "Fields": [ + { + "Name": "value", + "Identity": "value", + "Kind": "uint32", + "Outputs": ["c", "ore_block"], + "Null": "" + } + ] + }, + "type-uint64-eq": { + "Context": ["users"], + "Fields": [ + { + "Name": "value", + "Identity": "value", + "Kind": "uint64", + "Outputs": ["c", "eq"], + "Null": "" + } + ] + }, + "type-uint64-match": { + "Context": ["users"], + "Fields": [ + { + "Name": "value", + "Identity": "value", + "Kind": "uint64", + "Outputs": ["c", "match"], + "Null": "" + } + ] + }, + "type-uint64-ore": { + "Context": ["users"], + "Fields": [ + { + "Name": "value", + "Identity": "value", + "Kind": "uint64", + "Outputs": ["c", "ore"], + "Null": "" + } + ] + }, + "type-uint64-ope": { + "Context": ["users"], + "Fields": [ + { + "Name": "value", + "Identity": "value", + "Kind": "uint64", + "Outputs": ["c", "ope"], + "Null": "" + } + ] + }, + "type-uint64-ore_block": { + "Context": ["users"], + "Fields": [ + { + "Name": "value", + "Identity": "value", + "Kind": "uint64", + "Outputs": ["c", "ore_block"], + "Null": "" + } + ] + }, + "type-uint128-eq": { + "Context": ["users"], + "Fields": [ + { + "Name": "value", + "Identity": "value", + "Kind": "uint128", + "Outputs": ["c", "eq"], + "Null": "" + } + ] + }, + "type-uint128-match": { + "Context": ["users"], + "Fields": [ + { + "Name": "value", + "Identity": "value", + "Kind": "uint128", + "Outputs": ["c", "match"], + "Null": "" + } + ] + }, + "type-uint128-ore": { + "Context": ["users"], + "Fields": [ + { + "Name": "value", + "Identity": "value", + "Kind": "uint128", + "Outputs": ["c", "ore"], + "Null": "" + } + ] + }, + "type-uint128-ope": { + "Context": ["users"], + "Fields": [ + { + "Name": "value", + "Identity": "value", + "Kind": "uint128", + "Outputs": ["c", "ope"], + "Null": "" + } + ] + }, + "type-uint128-ore_block": { + "Context": ["users"], + "Fields": [ + { + "Name": "value", + "Identity": "value", + "Kind": "uint128", + "Outputs": ["c", "ore_block"], + "Null": "" + } + ] + }, + "type-float32-eq": { + "Context": ["users"], + "Fields": [ + { + "Name": "value", + "Identity": "value", + "Kind": "float32", + "Outputs": ["c", "eq"], + "Null": "" + } + ] + }, + "type-float32-match": { + "Context": ["users"], + "Fields": [ + { + "Name": "value", + "Identity": "value", + "Kind": "float32", + "Outputs": ["c", "match"], + "Null": "" + } + ] + }, + "type-float32-ore": { + "Context": ["users"], + "Fields": [ + { + "Name": "value", + "Identity": "value", + "Kind": "float32", + "Outputs": ["c", "ore"], + "Null": "" + } + ] + }, + "type-float32-ope": { + "Context": ["users"], + "Fields": [ + { + "Name": "value", + "Identity": "value", + "Kind": "float32", + "Outputs": ["c", "ope"], + "Null": "" + } + ] + }, + "type-float32-ore_block": { + "Context": ["users"], + "Fields": [ + { + "Name": "value", + "Identity": "value", + "Kind": "float32", + "Outputs": ["c", "ore_block"], + "Null": "" + } + ] + }, + "type-float64-eq": { + "Context": ["users"], + "Fields": [ + { + "Name": "value", + "Identity": "value", + "Kind": "float64", + "Outputs": ["c", "eq"], + "Null": "" + } + ] + }, + "type-float64-match": { + "Context": ["users"], + "Fields": [ + { + "Name": "value", + "Identity": "value", + "Kind": "float64", + "Outputs": ["c", "match"], + "Null": "" + } + ] + }, + "type-float64-ore": { + "Context": ["users"], + "Fields": [ + { + "Name": "value", + "Identity": "value", + "Kind": "float64", + "Outputs": ["c", "ore"], + "Null": "" + } + ] + }, + "type-float64-ope": { + "Context": ["users"], + "Fields": [ + { + "Name": "value", + "Identity": "value", + "Kind": "float64", + "Outputs": ["c", "ope"], + "Null": "" + } + ] + }, + "type-float64-ore_block": { + "Context": ["users"], + "Fields": [ + { + "Name": "value", + "Identity": "value", + "Kind": "float64", + "Outputs": ["c", "ore_block"], + "Null": "" + } + ] + }, + "type-decimal-eq": { + "Context": ["users"], + "Fields": [ + { + "Name": "value", + "Identity": "value", + "Kind": "decimal", + "Outputs": ["c", "eq"], + "Null": "" + } + ] + }, + "type-decimal-match": { + "Context": ["users"], + "Fields": [ + { + "Name": "value", + "Identity": "value", + "Kind": "decimal", + "Outputs": ["c", "match"], + "Null": "" + } + ] + }, + "type-decimal-ore": { + "Context": ["users"], + "Fields": [ + { + "Name": "value", + "Identity": "value", + "Kind": "decimal", + "Outputs": ["c", "ore"], + "Null": "" + } + ] + }, + "type-decimal-ope": { + "Context": ["users"], + "Fields": [ + { + "Name": "value", + "Identity": "value", + "Kind": "decimal", + "Outputs": ["c", "ope"], + "Null": "" + } + ] + }, + "type-decimal-ore_block": { + "Context": ["users"], + "Fields": [ + { + "Name": "value", + "Identity": "value", + "Kind": "decimal", + "Outputs": ["c", "ore_block"], + "Null": "" + } + ] + }, + "type-date-eq": { + "Context": ["users"], + "Fields": [ + { + "Name": "value", + "Identity": "value", + "Kind": "date", + "Outputs": ["c", "eq"], + "Null": "" + } + ] + }, + "type-date-match": { + "Context": ["users"], + "Fields": [ + { + "Name": "value", + "Identity": "value", + "Kind": "date", + "Outputs": ["c", "match"], + "Null": "" + } + ] + }, + "type-date-ore": { + "Context": ["users"], + "Fields": [ + { + "Name": "value", + "Identity": "value", + "Kind": "date", + "Outputs": ["c", "ore"], + "Null": "" + } + ] + }, + "type-date-ope": { + "Context": ["users"], + "Fields": [ + { + "Name": "value", + "Identity": "value", + "Kind": "date", + "Outputs": ["c", "ope"], + "Null": "" + } + ] + }, + "type-date-ore_block": { + "Context": ["users"], + "Fields": [ + { + "Name": "value", + "Identity": "value", + "Kind": "date", + "Outputs": ["c", "ore_block"], + "Null": "" + } + ] + }, + "type-timestamp-eq": { + "Context": ["users"], + "Fields": [ + { + "Name": "value", + "Identity": "value", + "Kind": "timestamp", + "Outputs": ["c", "eq"], + "Null": "" + } + ] + }, + "type-timestamp-match": { + "Context": ["users"], + "Fields": [ + { + "Name": "value", + "Identity": "value", + "Kind": "timestamp", + "Outputs": ["c", "match"], + "Null": "" + } + ] + }, + "type-timestamp-ore": { + "Context": ["users"], + "Fields": [ + { + "Name": "value", + "Identity": "value", + "Kind": "timestamp", + "Outputs": ["c", "ore"], + "Null": "" + } + ] + }, + "type-timestamp-ope": { + "Context": ["users"], + "Fields": [ + { + "Name": "value", + "Identity": "value", + "Kind": "timestamp", + "Outputs": ["c", "ope"], + "Null": "" + } + ] + }, + "type-timestamp-ore_block": { + "Context": ["users"], + "Fields": [ + { + "Name": "value", + "Identity": "value", + "Kind": "timestamp", + "Outputs": ["c", "ore_block"], + "Null": "" + } + ] + }, + "type-string-eq": { + "Context": ["users"], + "Fields": [ + { + "Name": "value", + "Identity": "value", + "Kind": "string", + "Outputs": ["c", "eq"], + "Null": "" + } + ] + }, + "type-string-match": { + "Context": ["users"], + "Fields": [ + { + "Name": "value", + "Identity": "value", + "Kind": "string", + "Outputs": ["c", "match"], + "Null": "" + } + ] + }, + "type-string-ore": { + "Context": ["users"], + "Fields": [ + { + "Name": "value", + "Identity": "value", + "Kind": "string", + "Outputs": ["c", "ore"], + "Null": "" + } + ] + }, + "type-string-ope": { + "Context": ["users"], + "Fields": [ + { + "Name": "value", + "Identity": "value", + "Kind": "string", + "Outputs": ["c", "ope"], + "Null": "" + } + ] + }, + "type-string-ore_block": { + "Context": ["users"], + "Fields": [ + { + "Name": "value", + "Identity": "value", + "Kind": "string", + "Outputs": ["c", "ore_block"], + "Null": "" + } + ] + }, + "type-bytes-eq": { + "Context": ["users"], + "Fields": [ + { + "Name": "value", + "Identity": "value", + "Kind": "bytes", + "Outputs": ["c", "eq"], + "Null": "" + } + ] + }, + "type-bytes-match": { + "Context": ["users"], + "Fields": [ + { + "Name": "value", + "Identity": "value", + "Kind": "bytes", + "Outputs": ["c", "match"], + "Null": "" + } + ] + }, + "type-bytes-ore": { + "Context": ["users"], + "Fields": [ + { + "Name": "value", + "Identity": "value", + "Kind": "bytes", + "Outputs": ["c", "ore"], + "Null": "" + } + ] + }, + "type-bytes-ope": { + "Context": ["users"], + "Fields": [ + { + "Name": "value", + "Identity": "value", + "Kind": "bytes", + "Outputs": ["c", "ope"], + "Null": "" + } + ] + }, + "type-bytes-ore_block": { + "Context": ["users"], + "Fields": [ + { + "Name": "value", + "Identity": "value", + "Kind": "bytes", + "Outputs": ["c", "ore_block"], + "Null": "" + } + ] + }, + "type-array-eq": { + "Context": ["users"], + "Fields": [ + { + "Name": "value", + "Identity": "value", + "Kind": "array", + "Outputs": ["c", "eq"], + "Null": "" + } + ] + }, + "type-array-match": { + "Context": ["users"], + "Fields": [ + { + "Name": "value", + "Identity": "value", + "Kind": "array", + "Outputs": ["c", "match"], + "Null": "" + } + ] + }, + "type-array-ore": { + "Context": ["users"], + "Fields": [ + { + "Name": "value", + "Identity": "value", + "Kind": "array", + "Outputs": ["c", "ore"], + "Null": "" + } + ] + }, + "type-array-ope": { + "Context": ["users"], + "Fields": [ + { + "Name": "value", + "Identity": "value", + "Kind": "array", + "Outputs": ["c", "ope"], + "Null": "" + } + ] + }, + "type-array-ore_block": { + "Context": ["users"], + "Fields": [ + { + "Name": "value", + "Identity": "value", + "Kind": "array", + "Outputs": ["c", "ore_block"], + "Null": "" + } + ] + }, + "type-object-eq": { + "Context": ["users"], + "Fields": [ + { + "Name": "value", + "Identity": "value", + "Kind": "object", + "Outputs": ["c", "eq"], + "Null": "" + } + ] + }, + "type-object-match": { + "Context": ["users"], + "Fields": [ + { + "Name": "value", + "Identity": "value", + "Kind": "object", + "Outputs": ["c", "match"], + "Null": "" + } + ] + }, + "type-object-ore": { + "Context": ["users"], + "Fields": [ + { + "Name": "value", + "Identity": "value", + "Kind": "object", + "Outputs": ["c", "ore"], + "Null": "" + } + ] + }, + "type-object-ope": { + "Context": ["users"], + "Fields": [ + { + "Name": "value", + "Identity": "value", + "Kind": "object", + "Outputs": ["c", "ope"], + "Null": "" + } + ] + }, + "type-object-ore_block": { + "Context": ["users"], + "Fields": [ + { + "Name": "value", + "Identity": "value", + "Kind": "object", + "Outputs": ["c", "ore_block"], + "Null": "" + } + ] + }, + "type-json-eq": { + "Context": ["users"], + "Fields": [ + { + "Name": "value", + "Identity": "value", + "Kind": "json", + "Outputs": ["c", "eq"], + "Null": "" + } + ] + }, + "type-json-match": { + "Context": ["users"], + "Fields": [ + { + "Name": "value", + "Identity": "value", + "Kind": "json", + "Outputs": ["c", "match"], + "Null": "" + } + ] + }, + "type-json-ore": { + "Context": ["users"], + "Fields": [ + { + "Name": "value", + "Identity": "value", + "Kind": "json", + "Outputs": ["c", "ore"], + "Null": "" + } + ] + }, + "type-json-ope": { + "Context": ["users"], + "Fields": [ + { + "Name": "value", + "Identity": "value", + "Kind": "json", + "Outputs": ["c", "ope"], + "Null": "" + } + ] + }, + "type-json-ore_block": { + "Context": ["users"], + "Fields": [ + { + "Name": "value", + "Identity": "value", + "Kind": "json", + "Outputs": ["c", "ore_block"], + "Null": "" + } + ] + }, + "outputs-empty": { + "Context": ["users"], + "Fields": [ + { + "Name": "value", + "Identity": "value", + "Kind": "", + "Outputs": [], + "Null": "" + } + ] + }, + "output-unknown": { + "Context": ["users"], + "Fields": [ + { + "Name": "value", + "Identity": "value", + "Kind": "", + "Outputs": ["wat"], + "Null": "" + } + ] + }, + "output-duplicate": { + "Context": ["users"], + "Fields": [ + { + "Name": "value", + "Identity": "value", + "Kind": "", + "Outputs": ["c", "c"], + "Null": "" + } + ] + }, + "type-unknown": { + "Context": ["users"], + "Fields": [ + { + "Name": "value", + "Identity": "value", + "Kind": "uuid", + "Outputs": ["c"], + "Null": "" + } + ] + }, + "type-json-without-target": { + "Context": ["users"], + "Fields": [ + { + "Name": "value", + "Identity": "value", + "Kind": "json", + "Outputs": ["c"], + "Null": "" + } + ] + }, + "json-output-pending": { + "Context": ["users"], + "Fields": [ + { + "Name": "value", + "Identity": "value", + "Kind": "json", + "Outputs": ["json"], + "Null": "" + } + ] + }, + "context-segment-slash": { + "Context": ["users"], + "Fields": [ + { + "Name": "value", + "Identity": "a/b", + "Kind": "", + "Outputs": ["c"], + "Null": "" + } + ] + }, + "context-segment-digit": { + "Context": ["users"], + "Fields": [ + { + "Name": "value", + "Identity": "9", + "Kind": "", + "Outputs": ["c"], + "Null": "" + } + ] + }, + "shared-identity": { + "Context": ["users"], + "Fields": [ + { + "Name": "a", + "Identity": "same", + "Kind": "", + "Outputs": ["c"], + "Null": "" + }, + { + "Name": "b", + "Identity": "same", + "Kind": "", + "Outputs": ["c"], + "Null": "" + } + ] + }, + "null-mode-unknown": { + "Context": ["users"], + "Fields": [ + { + "Name": "value", + "Identity": "value", + "Kind": "", + "Outputs": ["c"], + "Null": "optional" + } + ] + }, + "sealed-null-untyped": { + "Context": ["users"], + "Fields": [ + { + "Name": "value", + "Identity": "value", + "Kind": "", + "Outputs": ["c"], + "Null": "sealed" + } + ] + }, + "sealed-null-text": { + "Context": ["users"], + "Fields": [ + { + "Name": "value", + "Identity": "value", + "Kind": "string", + "Outputs": ["c"], + "Null": "sealed" + } + ] + }, + "sealed-null-order": { + "Context": ["users"], + "Fields": [ + { + "Name": "value", + "Identity": "value", + "Kind": "int64", + "Outputs": ["c", "ore"], + "Null": "sealed" + } + ] + }, + "untyped-index": { + "Context": ["users"], + "Fields": [ + { + "Name": "value", + "Identity": "value", + "Kind": "", + "Outputs": ["eq"], + "Null": "" + } + ] + } } } diff --git a/languages/golang/encrypt/context_conformance_test.go b/languages/golang/encrypt/context_conformance_test.go index c457d2d0c..954a54af3 100644 --- a/languages/golang/encrypt/context_conformance_test.go +++ b/languages/golang/encrypt/context_conformance_test.go @@ -53,8 +53,17 @@ func TestContextConformance(t *testing.T) { if corpus.Version != 1 || corpus.Profile != "record-contexts-v1" || len(corpus.Cases) == 0 || !reflect.DeepEqual(corpus.RequiredCapabilities, []string{"composition", "typed-extensions", "context-fields", "frozen-open"}) { t.Fatal("unsupported context profile") } - var vectors map[string]contextVector - readConformanceJSON(t, filepath.Join(root, "record-contexts-v1.expected.rust-native.json"), &vectors) + var expected struct { + Version int `json:"version"` + Profile string `json:"profile"` + Producer string `json:"producer"` + Cases map[string]contextVector `json:"cases"` + } + readConformanceJSON(t, filepath.Join(root, "record-contexts-v1.expected.rust-native.json"), &expected) + if expected.Version != 1 || expected.Profile != corpus.Profile || expected.Producer == "" { + t.Fatal("unsupported context vectors") + } + vectors := expected.Cases if len(vectors) != len(corpus.Cases) { t.Fatal("vector case set differs") } diff --git a/languages/golang/encrypt/declaration_conformance_test.go b/languages/golang/encrypt/declaration_conformance_test.go index 965a4ad58..57e504aae 100644 --- a/languages/golang/encrypt/declaration_conformance_test.go +++ b/languages/golang/encrypt/declaration_conformance_test.go @@ -81,8 +81,17 @@ func TestDeclarationConformance(t *testing.T) { if corpus.Version != 1 || corpus.Profile != "record-declarations-v1" || corpus.Resolver != "none" || len(corpus.Cases) == 0 { t.Fatal("unsupported or empty declaration corpus") } - var hostPlans map[string]record.Plan - readConformanceJSON(t, filepath.Join("..", "..", "..", "conformance", "stack-encrypt", "v1", "record-declarations-v1.inputs.go.json"), &hostPlans) + var inputs struct { + Version int `json:"version"` + Profile string `json:"profile"` + Runner string `json:"runner"` + Cases map[string]record.Plan `json:"cases"` + } + readConformanceJSON(t, filepath.Join("..", "..", "..", "conformance", "stack-encrypt", "v1", "record-declarations-v1.inputs.go.json"), &inputs) + if inputs.Version != 1 || inputs.Profile != corpus.Profile || inputs.Runner != "go" { + t.Fatal("unsupported Go host plan inputs") + } + hostPlans := inputs.Cases for name, load := range map[string]func() ([]byte, error){"release": encrypt.PlainGuest, "deterministic": encrypt.DeterministicGuest} { t.Run(name, func(t *testing.T) { wasm, err := load() diff --git a/packages/stack-encrypt/tests/context_conformance.rs b/packages/stack-encrypt/tests/context_conformance.rs index 956d02eba..75fedd012 100644 --- a/packages/stack-encrypt/tests/context_conformance.rs +++ b/packages/stack-encrypt/tests/context_conformance.rs @@ -44,6 +44,15 @@ struct Vector { ciphertext: String, equality: String, } +/// The `record-contexts-v1` outputs of one producer, keyed by case ID. +#[derive(Deserialize, Serialize)] +#[serde(deny_unknown_fields)] +struct Expected { + version: u32, + profile: String, + producer: String, + cases: BTreeMap, +} fn part(input: &Input) -> ValueFrame { if input.kind == "bytes" { @@ -180,7 +189,11 @@ async fn shared_context_composition() { let baseline: BTreeMap = if update { BTreeMap::new() } else { - serde_json::from_slice(&std::fs::read(&path).unwrap()).unwrap() + let expected: Expected = serde_json::from_slice(&std::fs::read(&path).unwrap()).unwrap(); + assert_eq!(expected.version, 1); + assert_eq!(expected.profile, "record-contexts-v1"); + assert!(!expected.producer.is_empty()); + expected.cases }; let mut vectors = BTreeMap::new(); let mut ids = BTreeSet::new(); @@ -337,7 +350,17 @@ async fn shared_context_composition() { vectors.insert(case.id, vector); } if update { - std::fs::write(path, serde_json::to_string_pretty(&vectors).unwrap() + "\n").unwrap(); + let expected = Expected { + version: 1, + profile: "record-contexts-v1".into(), + producer: format!("rust-native/stack-encrypt-{}", env!("CARGO_PKG_VERSION")), + cases: vectors, + }; + std::fs::write( + path, + serde_json::to_string_pretty(&expected).unwrap() + "\n", + ) + .unwrap(); } else { assert_eq!( baseline.keys().collect::>(), diff --git a/scripts/__tests__/stack-conformance-ci.test.mjs b/scripts/__tests__/stack-conformance-ci.test.mjs index b3cb09dac..8c8c9c34e 100644 --- a/scripts/__tests__/stack-conformance-ci.test.mjs +++ b/scripts/__tests__/stack-conformance-ci.test.mjs @@ -1,5 +1,5 @@ import { spawnSync } from 'node:child_process' -import { readFileSync } from 'node:fs' +import { readdirSync, readFileSync } from 'node:fs' import { join } from 'node:path' import { describe, expect, it } from 'vitest' import { @@ -228,3 +228,35 @@ describe('shared Stack Encrypt conformance is required in CI', () => { expect(steps[exchange]['continue-on-error']).toBeUndefined() }) }) + +/** + * conformance/stack-encrypt/README.md ยง Files: every file in v1/ is + * ..json, with the profile in its header, so a file's name + * says which profile it belongs to and whether it holds cases, one + * producer's frozen outputs or one runner's extra inputs. + */ +describe('conformance files are named after their profile and role', () => { + const dir = 'conformance/stack-encrypt/v1' + const NAME = + /^(?[a-z0-9-]+-v\d+)\.(?cases|expected\.(?[a-z0-9-]+)|inputs\.(?[a-z0-9-]+))\.json$/ + const files = readdirSync(join(REPO_ROOT, dir)).filter( + (file) => file !== 'fixture-enrollment.json', + ) + + it.each(files)('%s follows the convention', (file) => { + const match = file.match(NAME) + expect(match, `${file} is not ..json`).not.toBeNull() + const { profile, role, producer, runner } = match.groups + const header = JSON.parse(readRepo(`${dir}/${file}`)) + expect(header.version).toBe(1) + expect(header.profile).toBe(profile) + if (role !== 'cases') { + expect(files, `${file} has no cases file`).toContain( + `${profile}.cases.json`, + ) + expect(Object.keys(header.cases ?? {}).length).toBeGreaterThan(0) + } + if (producer) expect(header.producer).toMatch(new RegExp(`^${producer}`)) + if (runner) expect(header.runner).toBe(runner) + }) +})