Expand description
§Vitamin C Normalize
Normalized equality terms for the plaintext data model of
vitaminc-aead. Part of the
Vitamin C framework.
An equality index derives a key from a value. Two values that are equal must derive the same key whichever width, scale or spelling wrote them, and two values that merely look alike (a date and the integer it is stored as, a uuid and the same sixteen bytes) must not. This crate decides both: each scalar kind that can be indexed has a frozen domain label and a normalization to bytes, and everything else has no term.
There is no cryptography here and no PAE: the crate’s own dependencies
are the Unicode tables. (It builds on vitaminc-aead for the data model,
which brings that crate’s dependencies with it.) The encodings are its
own, including the decimal one, which is ported from the frozen
orderable-bytes release and checked against it byte for byte. A PRF crate such as
vitaminc-prf takes a Normalized
and derives under its domain.
§One scalar
use vitaminc_normalize::{domain, normalize};
use vitaminc_aead::{Int, Logical, Physical};
use vitaminc_protected::Controlled;
// Every integer width is one term.
let a = normalize(None, Physical::Int(Int::I8(34)))?;
let b = normalize(None, Physical::Int(Int::U64(34)))?;
assert_eq!(a.domain, domain::INTEGER);
assert_eq!(a.bytes.risky_ref(), b.bytes.risky_ref());
// A date is not the integer it is stored as.
let day = normalize(Some(Logical::Date), Physical::Int(Int::I32(34)))?;
assert_eq!(day.domain, domain::DATE);
assert_ne!(day.bytes.risky_ref(), a.bytes.risky_ref());Logical types opt in: a date, a timestamp, a decimal and a uuid have
domains of their own. A null, a boolean, an undefined and a logical type
this crate does not know have no term, and normalize refuses them with
NormalizeError::NoDomain.
§A whole value, beside encryption
Terms is a sink that collects one Term per indexable scalar, with
the path to it. Teed with the encrypting sink it sees the same walk, so
the terms are complete before any cipher call:
use std::collections::BTreeMap;
use vitaminc_normalize::{domain, encrypt_with_terms, Step};
let mut user = BTreeMap::new();
user.insert("email", "ada@example.com");
user.insert("name", "Ada");
let (ciphertext, terms) = encrypt_with_terms(user, &cipher, "users/v1")?;
assert_eq!(terms.len(), 2);
assert_eq!(terms[0].path.steps(), [Step::Key("email".into())]);
assert_eq!(terms[0].normalized.domain, domain::TEXT);encrypt_with_terms_async does the same through an async cipher and has
every term before its first await. plan_with_terms walks without
sealing at all, and terms collects the terms with no encryption in the
walk.
terms refuses every value encryption refuses, for the same reason and
at the same path, so an index never holds terms for a value that cannot
be stored. It keeps the same shape rules, through the same code: no
duplicate key, no value nested too deep, every scalar valid for its
logical type, every passthrough framable, and something sealed in every
container with children, even though it seals nothing itself. Read such a
refusal with TermsError::refusal; encryption reports the same one as
EncryptError::Shape.
A scalar whose kind has no domain (a null, a boolean, an undefined)
produces no term and does not stop the walk; passthrough subtrees get none
either. A scalar whose logical type has no domain, one from a newer
client, stops the walk: encrypting it with no term and no error would leave
an index silently incomplete. That refusal and an unassigned code point
arrive as EncryptError::Source carrying a TermError (from terms, as
TermsError::term), which holds the NormalizeError and the path to the
refused field. Downcast to TermError; the NormalizeError is also its
source(), so a reporter that walks the error chain reaches it. The
message names neither the field nor the value, because map keys can be
user data; read the path with TermError::path.
§Encodings
These are protocol data. Changing any of them, or a domain label, means a new label.
| Kind | Domain | Bytes |
|---|---|---|
| integer, any width | vitaminc/prf/value/integer-orderable/v1 | sign byte (0x00 negative, 0x01 otherwise), then the big-endian 128-bit two’s-complement word |
| float, either width | vitaminc/prf/value/float-orderable/v1 | the f64 value’s orderable bytes; f32 widened exactly, zero signs and NaN payloads folded |
| date | vitaminc/prf/value/date-orderable/v1 | orderable days from 0001-01-01 |
| timestamp, any unit | vitaminc/prf/value/timestamp-micros-orderable/v1 | orderable seconds, then nanoseconds truncated to microseconds |
| decimal | vitaminc/prf/value/decimal-orderable/v1 | the 14-byte orderable encoding of the value (see decimal), scale and signed zero normalized |
| text | vitaminc/prf/value/text-nfc/unicode-16/v1 | Unicode 16 NFC; a code point unassigned in Unicode 16 is refused |
| bytes | vitaminc/prf/value/bytes/v1 | as they are |
| uuid | vitaminc/prf/value/uuid/v1 | the sixteen raw bytes |
Text normalization is variable-time. The integer and float paths use no floating-point arithmetic.
Modules§
- domain
- Frozen domain labels for equality terms, one per kind that has one.
Structs§
- Equality
- The equality normalizer:
normalize. - Normalized
- The normalized bytes for one equality term and the domain they belong to.
- Path
- The steps from the root of a value to one node in it, root first. A node at the root has no steps.
- Term
- One term: where the scalar sits in the value, and what the normalizer
made of it. An equality term, a
Normalized, unlessTerms::bywas given another normalizer. - Term
Error - A scalar
Termsrefused: why, and where in the value. - Terms
- A sink that collects one
Termper scalar with an equality domain, in walk order. - Terms
Error - What
termsfails with: whatTermsrefuses, or the source’s own failure. - Terms
Map - The map side of
Terms: each entry’s path is the map’s plus its key. - Terms
Seq - The sequence side of
Terms: each element’s path is the sequence’s plus its index.
Enums§
- Normalize
Error - Why a scalar has no term. Never includes plaintext.
- Step
- One step from a container to one of its children.
Traits§
Functions§
- bytes
- Bytes, as they are.
- date
- A date’s day count since 1970-01-01, as the orderable bytes of its
count of days from 0001-01-01 (the same bytes chrono’s
NaiveDateorders by).Noneoutside the date range. - decimal
- A decimal’s mantissa and scale, as the 14-byte orderable encoding of
its value, ported from the frozen
orderable-bytesrelease and checked against it, which normalizes the scale and a signed zero.Noneif the mantissa does not fit 96 bits or the scale is above 28, the rangerust_decimalrepresents. - domain_
of - The opt-in table: the domain for a scalar, or
Nonefor one that has no term. A logical type decides on its own; a value without one goes by its kind. Neither looks at the value:normalizechecks that. - encrypt_
with_ terms - Encrypt
valueand produce its equality terms in the same walk. - encrypt_
with_ terms_ async - Encrypt
valuethrough an async cipher and produce its equality terms in the same walk. The walk, and so every term, is complete before the future is returned, so the future holds only the planned leaves and isSendeven when the source is not. - float
- A float of either width, as the orderable bytes of its
f64value with zero signs and NaN payloads folded. - integer
- An integer of any width: a sign byte, then the big-endian 128-bit word.
- normalize
- Encode one scalar for equality, with the domain its term belongs to.
- plan_
with_ terms - Walk
valueinto the encrypting sink and the terms sink at once, without sealing: the planned tree of pending leaves, and every term. - terms
- Every equality term of
value, in walk order, without encrypting it. - terms_
by - Every term of
valueunder the normalizerby, in walk order, without encrypting it:termsfor an index of another kind. - text
- NFC in the pinned Unicode version, without case or accent folding.
- timestamp
- A count of
units since the Unix epoch, as orderable seconds then big-endian nanoseconds truncated to microseconds (the same bytes chrono’sDateTime<Utc>orders by, truncated).Noneoutside the timestamp range. - uuid
- A uuid’s sixteen raw bytes.
Nonefor any other length.
Type Aliases§
- With
Terms - A ciphertext tree and the terms of the value it was sealed from, in walk order.