|
| 1 | +# Envelope Salted Values |
| 2 | + |
| 3 | +This document defines a standard **Salted Value** pattern for Gordian Envelope, enabling optional decorrelation of small or easily enumerable values that may be elided. |
| 4 | + |
| 5 | +## BCR-2026-004 |
| 6 | + |
| 7 | +**© 2026 Blockchain Commons** |
| 8 | + |
| 9 | +Authors: Wolf McNally, Christopher Allen<br/> |
| 10 | +Date: February 4, 2026 |
| 11 | + |
| 12 | +--- |
| 13 | + |
| 14 | +## Abstract |
| 15 | + |
| 16 | +Gordian Envelope supports *holder-based elision*: selectively withholding parts of an envelope while preserving verification through the digest tree. However, elided elements are replaced by their digests, and **digests are correlatable**: repeated elisions of the same underlying value yield identical digests, enabling cross-presentation linkage and (for small value spaces) dictionary attacks. |
| 17 | + |
| 18 | +[BCR-2024-007](bcr-2024-007-envelope-decorrelation.md) introduces opt-in decorrelation in Gordian Envelope using `'salt': Salt` assertions. This document defines a **Salted Value** pattern—an Envelope-native wrapper that standardizes how to salt “atomic” values (strings, small enums, etc.) while keeping them readable when disclosed. |
| 19 | + |
| 20 | +A value MAY be represented directly (unsalted): |
| 21 | + |
| 22 | +```envelope |
| 23 | +"Indiana" |
| 24 | +``` |
| 25 | + |
| 26 | +Or as a Salted Value using **Unit** as subject: |
| 27 | + |
| 28 | +```envelope |
| 29 | +'' [ |
| 30 | + 'salt': Salt(<random-data>) |
| 31 | + 'value': "Indiana" |
| 32 | +] |
| 33 | +``` |
| 34 | + |
| 35 | +This document also explains how envelope schemas may define assertions as: |
| 36 | + |
| 37 | +- optionally accepting Salted Values, |
| 38 | +- requiring Salted Values, or |
| 39 | +- forbidding Salted Values, |
| 40 | + |
| 41 | +depending on privacy needs, future elision expectations, and determinism requirements. |
| 42 | + |
| 43 | +--- |
| 44 | + |
| 45 | +## Status of This Document |
| 46 | + |
| 47 | +📙 This document is research and a proposed specification. It defines a reusable pattern intended for broad application across Envelope schemas, but it does not yet have a reference implementation. |
| 48 | + |
| 49 | +--- |
| 50 | + |
| 51 | +## Background |
| 52 | + |
| 53 | +### Unit and record-like values |
| 54 | + |
| 55 | +[BCR-2026-001](bcr-2026-001-unit.md) defines **Unit** (`''`, Known Value 0) as deliberate emptiness: a position that carries zero informational content and must not be replaced with any other value. In Gordian Envelope, Unit is used as the subject when an envelope’s meaning is conveyed entirely by its assertions, without implying a subject identity. |
| 56 | + |
| 57 | +Salted Values are *record-like wrappers*: they exist to carry assertions (`'salt'` and `'value'`). They are not independently-identified entities. Therefore, Salted Values use **Unit** as their subject. |
| 58 | + |
| 59 | +### Salt |
| 60 | + |
| 61 | +[BCR-2023-017](bcr-2023-017-salt.md) defines `salt` as random bytes used as an additional input to one-way algorithms where similar inputs should not yield the same outputs (“decorrelation”). Salts are not usually secret. |
| 62 | + |
| 63 | +In Envelope, salt is carried as a `Salt` object with CBOR tag `#6.40018(bytes)`. |
| 64 | + |
| 65 | +### Decorrelation and elision |
| 66 | + |
| 67 | +[BCR-2024-007](bcr-2024-007-envelope-decorrelation.md) explains correlatability in the context of Envelope’s Merkle-like digest tree: |
| 68 | + |
| 69 | +- When an element is elided, it is replaced with its digest, preserving the digest tree. |
| 70 | +- Digests are correlatable: identical hidden values produce identical digests. |
| 71 | +- Envelope supports opt-in decorrelation by adding `'salt': Salt` assertions at the correct level (subject, object, or assertion) to ensure elided digests do not correlate. |
| 72 | + |
| 73 | +This BCR defines a standard wrapper that makes it easy for schemas to say: “this value may be salted in a consistent way.” |
| 74 | + |
| 75 | +--- |
| 76 | + |
| 77 | +## Motivation |
| 78 | + |
| 79 | +Many real-world fields are drawn from **small or enumerable sets**, and are often **privacy-sensitive** when withheld: |
| 80 | + |
| 81 | +- US state codes (`"ID"`, `"CA"`, ...) |
| 82 | +- cities/towns (often enumerable within a jurisdiction) |
| 83 | +- standardized roles (`"Release Manager"`, `"Witness"`) |
| 84 | +- categorical labels used in workflows |
| 85 | + |
| 86 | +If such values are elided without decorrelation, their digests can be: |
| 87 | + |
| 88 | +1. **Linked** across different presentations (correlation), and/or |
| 89 | +2. **Recovered** via dictionary attack if the value space is small. |
| 90 | + |
| 91 | +The Salted Value pattern provides a simple, schema-friendly way to represent these values so that if the *entire value object* is elided, its resulting digest is decorrelated. |
| 92 | + |
| 93 | +--- |
| 94 | + |
| 95 | +## The Salted Value Pattern |
| 96 | + |
| 97 | +### Definitions |
| 98 | + |
| 99 | +A **Salted Value** is an envelope with: |
| 100 | + |
| 101 | +- subject: **Unit** (`''`) |
| 102 | +- required assertions: |
| 103 | + - `'salt': Salt(...)` |
| 104 | + - `'value': <any value>` |
| 105 | + |
| 106 | +A Salted Value is semantically equivalent (for interpretation) to its underlying `'value'`, except that it carries a salt that changes its digest for decorrelation purposes. |
| 107 | + |
| 108 | +### Representation |
| 109 | + |
| 110 | +#### Unsalted value |
| 111 | + |
| 112 | +Any ordinary Envelope value: |
| 113 | + |
| 114 | +```envelope |
| 115 | +"Indiana" |
| 116 | +``` |
| 117 | + |
| 118 | +#### Salted value |
| 119 | + |
| 120 | +A Salted Value wrapper: |
| 121 | + |
| 122 | +```envelope |
| 123 | +'' [ |
| 124 | + 'salt': Salt(<random-data>) |
| 125 | + 'value': "Indiana" |
| 126 | +] |
| 127 | +``` |
| 128 | + |
| 129 | +### Required predicates |
| 130 | + |
| 131 | +- `'salt'` is an existing Known Value (see [BCR-2024-007](bcr-2024-007-envelope-decorrelation.md)). |
| 132 | +- `'value'` is a proposed new Known Value (code point TBD) representing the wrapped underlying value. |
| 133 | + |
| 134 | +Pattern invariants: |
| 135 | + |
| 136 | +- The subject MUST be Unit (`''`) per [BCR-2026-001](bcr-2026-001-unit.md). |
| 137 | +- There MUST be exactly one `'salt'` assertion, and its object must be a `Salt` of at least 8 bytes (see below for further guidance), |
| 138 | +- There MUST be exactly one `'value'` assertion, and its object may be anything. |
| 139 | +- There MUST NOT be any other assertions. |
| 140 | + |
| 141 | +--- |
| 142 | + |
| 143 | +## How Salted Values Provide Decorrelation |
| 144 | + |
| 145 | +A Salted Value is itself an envelope element. Because it contains a `'salt': Salt(...)` assertion, its digest is altered by random data. If the Salted Value is elided as an *object* in some larger envelope, the resulting digest is decorrelated. |
| 146 | + |
| 147 | +Example (field value is salted): |
| 148 | + |
| 149 | +```envelope |
| 150 | +'region': '' [ |
| 151 | + 'salt': Salt(<random-data>) |
| 152 | + 'value': "CA" |
| 153 | +] |
| 154 | +``` |
| 155 | + |
| 156 | +If the holder later elides the entire object: |
| 157 | + |
| 158 | +```envelope |
| 159 | +'region': ELIDED |
| 160 | +``` |
| 161 | + |
| 162 | +…the digest standing in for the elided object is now a digest of the **salted wrapper**, not of `"CA"` directly. This prevents correlation and makes dictionary attacks impractical (absent disclosure of the wrapper). |
| 163 | + |
| 164 | +### Important disclosure guidance |
| 165 | + |
| 166 | +If concealment is the goal, presentations SHOULD elide the **entire Salted Value object**, not merely the `'value'` assertion inside it. |
| 167 | + |
| 168 | +Revealing the wrapper while eliding only `'value'`: |
| 169 | + |
| 170 | +```envelope |
| 171 | +'' [ |
| 172 | + 'salt': Salt(<random-data>) |
| 173 | + 'value': ELIDED |
| 174 | +] |
| 175 | +``` |
| 176 | + |
| 177 | +…can leak enough structure to enable brute-force recovery in small value spaces (because the elided leaf digest corresponds to the underlying value). For small enumerated fields, that defeats the purpose. |
| 178 | + |
| 179 | +One *can* disclose the `'value`' without disclosing the `'salt'`, although this provides little additional security benefit: |
| 180 | + |
| 181 | +```envelope |
| 182 | +'' [ |
| 183 | + 'salt': ELIDED |
| 184 | + 'value': "CA" |
| 185 | +] |
| 186 | +``` |
| 187 | + |
| 188 | +**Rule of thumb:** |
| 189 | + |
| 190 | +- To *reveal* the value: disclose the wrapper (or disclose the unsalted value, if allowed). |
| 191 | +- To *conceal* the value: elide the wrapper object entirely. |
| 192 | + |
| 193 | +--- |
| 194 | + |
| 195 | +## Salt Selection Guidance |
| 196 | + |
| 197 | +[BCR-2024-007](bcr-2024-007-envelope-decorrelation.md) recommends salt sizes chosen to minimize correlation and quasicorrelation: |
| 198 | + |
| 199 | +- For small objects, salt is generally 8…16 bytes. |
| 200 | +- For larger objects, salt is generally 5%…25% of object size. |
| 201 | + |
| 202 | +For Salted Values (which are commonly used for small strings/enums), producers SHOULD use random salts in the **8…16 byte** range, and MAY choose a random length within that range to reduce size-based leakage. |
| 203 | + |
| 204 | +Salts MUST be random (see [BCR-2023-017](bcr-2023-017-salt.md)). |
| 205 | + |
| 206 | +Salts SHOULD be added during envelope construction *before* applying any signatures that depend on the digest tree (see [BCR-2024-007](bcr-2024-007-envelope-decorrelation.md)). |
| 207 | + |
| 208 | +--- |
| 209 | + |
| 210 | +## Schema Integration |
| 211 | + |
| 212 | +Salted Values are most useful when schemas explicitly support them, so that producers and verifiers share expectations. |
| 213 | + |
| 214 | +A schema may treat a field as: |
| 215 | + |
| 216 | +1. **Optionally salted** (accept either form) |
| 217 | +2. **Required salted** (always use the Salted Value wrapper) |
| 218 | +3. **Forbidden salted** (the default: only accept the direct/unsalted form) |
| 219 | + |
| 220 | +### Optionally salted |
| 221 | + |
| 222 | +A schema MAY define a field as accepting either: |
| 223 | + |
| 224 | +- the direct value, or |
| 225 | +- a Salted Value wrapping that value |
| 226 | + |
| 227 | +Example schema intent: |
| 228 | + |
| 229 | +- `region` may be `"CA"` or `''['salt':..., 'value':"CA"]` |
| 230 | + |
| 231 | +This choice is appropriate when: |
| 232 | + |
| 233 | +- the field is usually disclosed, but |
| 234 | +- the field might later be selectively withheld, and |
| 235 | +- producers want the option to pre-commit in a decorrelatable way. |
| 236 | + |
| 237 | +**Reasonable use case:** a location object where region/locality are usually present, but may be withheld in some presentations for privacy. |
| 238 | + |
| 239 | +### Required salted |
| 240 | + |
| 241 | +A schema MAY require that a field always be represented as a Salted Value wrapper. |
| 242 | + |
| 243 | +This choice is appropriate when: |
| 244 | + |
| 245 | +- the value space is small and correlation/dictionary attacks are a concern, and |
| 246 | +- withholding is expected or common, and |
| 247 | +- determinism (same inputs → same digests) is not required for that field. |
| 248 | + |
| 249 | +**Reasonable use case:** `locality`, `role`, or other fields that are frequently elided and have high correlation risk. |
| 250 | + |
| 251 | +### Forbidden salted |
| 252 | + |
| 253 | +A schema MAY forbid Salted Value wrappers for fields that must remain deterministic or directly comparable at the digest level. |
| 254 | + |
| 255 | +This choice is appropriate when: |
| 256 | + |
| 257 | +- the field participates in canonical identifiers, keys, indexes, or deterministic commitments, or |
| 258 | +- correlation is desired (e.g., you want equal values to be obviously equal across envelopes), or |
| 259 | +- the field is always public and never elided. |
| 260 | + |
| 261 | +**Reasonable use case:** fields intended as stable identifiers, fixed policy URIs, or other values where non-determinism would be harmful. |
| 262 | + |
| 263 | +Where a schema is silent on whether Salted Values are allowed, the default is to forbid them. |
| 264 | + |
| 265 | +--- |
| 266 | + |
| 267 | +## Examples |
| 268 | + |
| 269 | +### Example 1: Optional salted region |
| 270 | + |
| 271 | +Unsalted: |
| 272 | + |
| 273 | +```envelope |
| 274 | +'region': "CA" |
| 275 | +``` |
| 276 | + |
| 277 | +Salted: |
| 278 | + |
| 279 | +```envelope |
| 280 | +'region': '' [ |
| 281 | + 'salt': Salt(<random-data>) |
| 282 | + 'value': "CA" |
| 283 | +] |
| 284 | +``` |
| 285 | + |
| 286 | +### Example 2: Required salted locality |
| 287 | + |
| 288 | +Schema requires locality to be salted because it may be elided and is easily enumerable: |
| 289 | + |
| 290 | +```envelope |
| 291 | +'locality': '' [ |
| 292 | + 'salt': Salt(<random-data>) |
| 293 | + 'value': "Sacramento" |
| 294 | +] |
| 295 | +``` |
| 296 | + |
| 297 | +Concealed presentation: |
| 298 | + |
| 299 | +```envelope |
| 300 | +'locality': ELIDED |
| 301 | +``` |
| 302 | + |
| 303 | +### Example 3: Combining with higher-level structures |
| 304 | + |
| 305 | +A place object that supports optional salted small fields: |
| 306 | + |
| 307 | +```envelope |
| 308 | +'' [ |
| 309 | + 'country': "US" |
| 310 | + 'region': '' [ |
| 311 | + 'salt': Salt(<random-data>) |
| 312 | + 'value': "CA" |
| 313 | + ] |
| 314 | + 'locality': '' [ |
| 315 | + 'salt': Salt(<random-data>) |
| 316 | + 'value': "Sacramento" |
| 317 | + ] |
| 318 | + 'timeZone': "America/Los_Angeles" |
| 319 | +] |
| 320 | +``` |
| 321 | + |
| 322 | +--- |
| 323 | + |
| 324 | +## Security Considerations |
| 325 | + |
| 326 | +1. **Salt is not secrecy.** Salt is not usually secret; its purpose is decorrelation of projections, not encryption (see [BCR-2023-017](bcr-2023-017-salt.md)). |
| 327 | +2. **Elide at the right level.** To conceal a value while benefiting from decorrelation, elide the Salted Value object as a whole. Revealing the wrapper while eliding only `'value'` can enable dictionary attacks in small value spaces. |
| 328 | +3. **Non-determinism is intentional.** Salted Values break deterministic equality-at-the-digest-level. Schemas should forbid Salted Values where determinism is required. |
| 329 | +4. **Add salt before signing.** Because salts change the digest tree, they must be part of the envelope before signatures that commit to those digests (see [BCR-2024-007](bcr-2024-007-envelope-decorrelation.md)). |
| 330 | +5. **Fresh salt per value.** Reusing the same salt (or copying an entire Salted Value wrapper) across contexts reintroduces correlatability. Salts should be freshly generated per wrapped value. |
| 331 | +6. **Eliding a parent node inherits its child salts.** When a parent node with salted children is elided, the parent MAY be considered effectively salted as well, as the child salt(s) also serve to decorrelate the parent. |
| 332 | + |
| 333 | +--- |
| 334 | + |
| 335 | +## Future Work |
| 336 | + |
| 337 | +- Define schema notation patterns (human- and machine-readable) for expressing “optional salted” vs “required salted” field constraints. |
| 338 | + |
| 339 | +--- |
| 340 | + |
| 341 | +## References |
| 342 | + |
| 343 | +### Internal (BCR) |
| 344 | + |
| 345 | +- [BCR-2026-001: Unit: The Known Value for Deliberate Emptiness](bcr-2026-001-unit.md) |
| 346 | +- [BCR-2023-017: UR Type Definition for Random Salt](bcr-2023-017-salt.md) |
| 347 | +- [BCR-2024-007: Decorrelation in Gordian Envelope](bcr-2024-007-envelope-decorrelation.md) |
| 348 | +- [BCR-2023-002: Known Values](bcr-2023-002-known-value.md) |
| 349 | +- [BCR-2024-009: Signatures with Metadata in Gordian Envelope](bcr-2024-009-signature-metadata.md) |
| 350 | + |
| 351 | +### External |
| 352 | + |
| 353 | +- https://en.wikipedia.org/wiki/Salt_(cryptography) (background concept; Envelope use is defined by the BCRs above) |
0 commit comments