You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
322 lines
15 KiB
322 lines
15 KiB
; DateKeys Protocol Specification v0.14 - CBOR schemas (RFC 8610 CDDL).
|
|
;
|
|
; Normative companion of spec/DateKeys_Protocol_Specification_v0.14.md, which
|
|
; the reference implementation g.activething.com/go/DateKeys implements. The
|
|
; schemas have not changed since v0.12. They include the three control
|
|
; versions: 1, of capsule format 1 (v0.8.2), 2, of format 2 (v0.9), and 3, of
|
|
; format 3, and the security and head objects of format 3.
|
|
;
|
|
; Encoding rules that CDDL cannot express (spec section 58, 58.1):
|
|
; - Every structure uses the CBOR profile of the protocol (spec section 58):
|
|
; Deterministic CBOR (RFC 8949 section 4.2.1) restricted to major types 0,
|
|
; 2, 3, 4 and 5, with unsigned integer map keys in strictly ascending
|
|
; order, shortest-form integers and lengths, definite lengths only and
|
|
; valid UTF-8 text. Negative integers, tags, floats, simple values
|
|
; (including null) and indefinite lengths are rejected.
|
|
; - A decoder re-encodes what it decoded and rejects any byte difference
|
|
; (ERR_NON_CANONICAL_CBOR).
|
|
; - A semantically absent optional field is omitted. Empty arrays, empty
|
|
; maps, null, "" and h'' never stand for absence; the .size and non-empty
|
|
; constraints below make those forms invalid.
|
|
; - Maps are closed: keys not listed here are rejected. New semantics go in
|
|
; extensions (spec section 54).
|
|
; - The elements of each extension array are in strictly ascending order of
|
|
; the UTF-8 bytes of extension_id (spec section 54): bytes compared as
|
|
; unsigned integers, the first differing byte decides and a proper prefix
|
|
; sorts first; never UTF-16 code units, case folding, Unicode
|
|
; normalization or a locale collation. U+FF61 (ef bd a1) sorts before
|
|
; U+10000 (f0 90 80 80). The strict order also forbids a repeated
|
|
; extension_id within an array, and no extension_id appears in both
|
|
; arrays of one object, so an extension_id appears at most once per
|
|
; object.
|
|
; - A violation of a normative rule of this schema, including .size,
|
|
; integer ranges and the 64-extension maximum, is ERR_NON_CANONICAL_CBOR
|
|
; unless spec section 57 names a more specific error (schema version,
|
|
; compact_datekey, access_type and access_material, Provider Profile
|
|
; names and public key). Those rules are checked with the fields, after
|
|
; the rest of this schema; here a field needs only the CBOR type of its
|
|
; rule, and another type is ERR_NON_CANONICAL_CBOR. The implementation
|
|
; limits marked below are not normative; spec section 74 lists them,
|
|
; with the limits of the reference outside this schema, and an
|
|
; implementation that applies them uses the same mapping.
|
|
; - The schema version of a control (key 1) is the one spec section 22
|
|
; assigns to the format of its capsule, the VERSION of the PRELUDE:
|
|
; control-v1 only in format 1, control-v2 only in format 2 and
|
|
; control-v3 only in format 3. A decoder
|
|
; picks the rule by the format, and another version is
|
|
; ERR_UNSUPPORTED_VERSION, read before the rest of the schema (spec
|
|
; section 69.1, layer 2). CDDL cannot express that link.
|
|
; - payload-length holds L as an unsigned 64-bit big-endian integer in
|
|
; exactly 8 bytes, whatever its value, and a decoder reads all 8 bytes;
|
|
; the value is at most max-payload-length (spec section 29.1, 31). A
|
|
; larger value is ERR_NON_CANONICAL_CBOR, like any rule of this schema.
|
|
; - When bytes break several rules, the code is the one of the first
|
|
; failing layer (spec section 69.1): frame, then type tag and schema
|
|
; version (keys 0 and 1; for a control, the version of its capsule
|
|
; format), then this schema, then the fields with codes of their own in
|
|
; ascending key order.
|
|
; - In format 3, security and head sit inside BODY, the plaintext of
|
|
; PAYLOAD_AGE (spec section 29.2), whose 12-byte frame (AREA_LEN,
|
|
; SECURITY_LEN, HEAD_LEN) CDDL cannot express. A failure of security
|
|
; never has an error code: it only changes the verdicts (spec section
|
|
; 29.3, 29.7). In head, path lengths (R1) and the path order (R8) are
|
|
; rules of this schema (ERR_NON_CANONICAL_CBOR); the other path rules
|
|
; and the text rules are checked with the fields (ERR_HEAD_INVALID,
|
|
; spec section 29.4 to 29.6), and the paths sort by their UTF-8 bytes,
|
|
; like extension_id.
|
|
|
|
; Spec section 11 and 12. Spec section 12.1 adds the rules a profile must
|
|
; follow to be pinned (ERR_UNKNOWN_PROFILE), among them period at most 2^32-1
|
|
; and a public key that is the canonical encoding (spec section 12.2) of a
|
|
; point of the key group of the scheme, 48 or 96 bytes, other than the point
|
|
; at infinity, and the chain-hash self-check, SHA-256(uint32_be(period) ||
|
|
; int64_be(genesis_time) || public_key || genesis_seed || network), network
|
|
; left out when it is "default" (ERR_PROFILE_MISMATCH).
|
|
provider-profile = {
|
|
0 => "datekeys-provider-profile",
|
|
1 => 1,
|
|
2 => profile-id,
|
|
3 => name, ; provider, "drand"
|
|
4 => name, ; provider network identifier, "quicknet"
|
|
5 => bstr .size 32, ; chain_hash
|
|
6 => public-key, ; group public key
|
|
7 => period, ; seconds; spec section 11: 1..max-safe-uint
|
|
8 => 0..max-safe-uint, ; genesis_time, Unix seconds
|
|
9 => name, ; scheme, "bls-unchained-g1-rfc9380"
|
|
10 => bstr .size 32, ; genesis_seed
|
|
}
|
|
|
|
; Spec section 24. Stored as exact bytes after the 16-byte PRELUDE and covered
|
|
; by header_binding = SHA-256(PRELUDE || PUBLIC_HEADER). The same header,
|
|
; schema version 1, in all three capsule formats.
|
|
public-header = {
|
|
0 => "datekeycap",
|
|
1 => 1,
|
|
2 => capsule-id,
|
|
3 => compact-datekey, ; the only source of the profile
|
|
4 => access-policy,
|
|
? 5 => extensions, ; critical_extensions
|
|
? 6 => extensions, ; noncritical_extensions
|
|
}
|
|
|
|
; Spec section 31. Sealed inside OUTER_TIME_AGE (time_only) or inside
|
|
; INNER_ACCESS_AGE inside OUTER_TIME_AGE (time_and_key).
|
|
control = control-v1 / control-v2 / control-v3
|
|
|
|
; Capsule format 1 (v0.8.2).
|
|
control-v1 = {
|
|
0 => "datekeys-control",
|
|
1 => 1,
|
|
2 => bstr .size 32, ; header_binding
|
|
3 => bstr .size 32, ; payload_identity, raw X25519 identity I_PAYLOAD
|
|
? 4 => extensions, ; critical_extensions
|
|
? 5 => extensions, ; noncritical_extensions
|
|
}
|
|
|
|
; Capsule format 2 (v0.9). 103 bytes without extensions.
|
|
control-v2 = {
|
|
0 => "datekeys-control",
|
|
1 => 2,
|
|
2 => bstr .size 32, ; header_binding
|
|
3 => bstr .size 32, ; payload_identity, raw X25519 identity I_PAYLOAD
|
|
? 4 => extensions, ; critical_extensions
|
|
? 5 => extensions, ; noncritical_extensions
|
|
6 => payload-length, ; payload_length, L, the length of the content (spec section 29.1)
|
|
7 => padding-scheme, ; padding, the code of the padding rule of PAYLOAD_AGE (spec section 29.1)
|
|
}
|
|
|
|
; Capsule format 3 (v0.10). The keys of control-v2; L is the length of BODY
|
|
; (spec section 29.2). 103 bytes without extensions.
|
|
control-v3 = {
|
|
0 => "datekeys-control",
|
|
1 => 3,
|
|
2 => bstr .size 32, ; header_binding
|
|
3 => bstr .size 32, ; payload_identity, raw X25519 identity I_PAYLOAD
|
|
? 4 => extensions, ; critical_extensions
|
|
? 5 => extensions, ; noncritical_extensions
|
|
6 => payload-length, ; payload_length, L, the length of BODY
|
|
7 => padding-scheme, ; padding, the code of the padding rule of PAYLOAD_AGE
|
|
}
|
|
|
|
; Fixed width, so that the length of CONTROL_CBOR, visible in
|
|
; SEALED_CONTROL_LEN, never depends on L (spec section 55.2). Value at most
|
|
; max-payload-length (see the rules above). L = 0 is 48 0000000000000000.
|
|
payload-length = bstr .size 8
|
|
; No code stands for "no padding" (spec section 29.1).
|
|
padding-scheme = &(bloque256: 1, reforzado: 2)
|
|
|
|
; 2^53 - 2^46: the largest L whose padded length P stays within
|
|
; max-safe-uint under both padding rules (spec section 29.1).
|
|
max-payload-length = 8936830510563328
|
|
|
|
; Spec section 29.3. SECURITY_CBOR of format 3, inside the area of BODY.
|
|
; Version 1 in every later version of the spec that keeps format 3. Keys 2
|
|
; and 3 hold separately encoded CBOR, author-signature and seal, with the
|
|
; CBOR profile; a failure of their content only changes its own verdict.
|
|
; v0.11 defines alg 1 (Ed25519, spec section 29.9), alg 2 (CMS with X.509
|
|
; certificates, 29.10) and seal_type 2 (RFC 3161, 29.11); seal_type 1
|
|
; (DateKeys) and 3 (OpenTimestamps) stay reserved. A writer writes security
|
|
; empty (22 bytes) in a capsule without signature or seal, and never key 3
|
|
; with alg 2.
|
|
security = {
|
|
0 => "datekeys-security",
|
|
1 => 1,
|
|
? 2 => bstr .size (1..65536), ; author-signature, encoded on its own
|
|
? 3 => bstr .size (1..65536), ; seal, encoded on its own
|
|
}
|
|
author-signature = {
|
|
0 => 1..4294967295, ; alg
|
|
1 => bstr, ; public key
|
|
2 => bstr, ; signature
|
|
}
|
|
seal = {
|
|
0 => 1..4294967295, ; seal_type
|
|
1 => bstr, ; token
|
|
}
|
|
|
|
; The author-signature of each alg this version defines (spec sections 29.9
|
|
; and 29.10). A reader that does not implement an alg checks only the
|
|
; generic author-signature above.
|
|
author-signature-ed25519 = {
|
|
0 => 1,
|
|
1 => bstr .size 32, ; public key A
|
|
2 => bstr .size 64, ; signature R || S
|
|
}
|
|
author-signature-cms = {
|
|
0 => 2,
|
|
1 => bstr .cbor signers, ; required signers, encoded on its own
|
|
2 => bstr, ; ContentInfo of type SignedData, DER, detached
|
|
}
|
|
; SHA-256 of the DER certificate of each required signer, in strictly
|
|
; ascending byte order, without repetitions.
|
|
signers = [1*16 bstr .size 32]
|
|
seal-rfc3161 = {
|
|
0 => 2,
|
|
1 => bstr, ; TimeStampToken, DER
|
|
}
|
|
|
|
; The data rules below, of registered extensions, are checked only by an
|
|
; implementation that knows the extension, and a violation makes the
|
|
; extension unusable (spec sections 54 and 57): never ERR_NON_CANONICAL_CBOR.
|
|
|
|
; Spec section 24.1. data of extension datekeys.note, version 1, in the
|
|
; noncritical array of PUBLIC_HEADER: UTF-8 text with the rules of the
|
|
; declared author of section 29.6, one line, not CBOR.
|
|
note-data = bstr .size (1..1024)
|
|
|
|
; Spec section 44.1. data of extension datekeys.capsule, version 1, in the
|
|
; noncritical array of a .dkk.
|
|
capsule-data = {
|
|
? 0 => tstr .size (1..1024), ; note, a copy of the public note
|
|
1 => tstr, ; compact_datekey, canonical dk1_
|
|
? 2 => bstr, ; locator: an age file with one tlock stanza
|
|
; for the round and chain of key 1
|
|
}
|
|
; Plaintext of the locator, readable at the unlock date: exactly 4096
|
|
; bytes, or the least multiple of 4096 that holds it, with key 6. The
|
|
; envelope is an age file of the .dkc for I_SOBRE: its header goes here, and
|
|
; only the rest, nonce and STREAM, is stored outside, alone or in a host file.
|
|
capsule-locator = {
|
|
0 => [1*8 capsule-address], ; where the rest of the envelope is
|
|
1 => bstr .size 32, ; I_SOBRE, identity of the envelope
|
|
2 => bstr .size (1..1024), ; age header of the envelope, up to its MAC
|
|
3 => bstr .size 32, ; SHA-256 of the rest
|
|
4 => 0..max-safe-uint, ; size of the rest in bytes
|
|
5 => bstr .size 32, ; capsule_digest
|
|
? 6 => bstr .size (1..), ; zero padding, at least one byte
|
|
}
|
|
capsule-address = {
|
|
0 => tstr .size (1..1024), ; https or ipfs URI
|
|
? 1 => 1..max-safe-uint, ; offset of the rest in that resource; 0 is written by leaving it out
|
|
}
|
|
|
|
; Spec section 29.4. HEAD_CBOR of format 3, at most 16 MiB. Always version 1
|
|
; in format 3: a new version needs a new format (spec section 22). These
|
|
; limits are normative and fixed with the format.
|
|
head = {
|
|
0 => "datekeys-head",
|
|
1 => 1,
|
|
2 => bstr .size 32, ; salt, fresh from a CSPRNG
|
|
? 3 => tstr .size (1..16384), ; comment (spec section 29.6)
|
|
? 4 => tstr .size (1..256), ; declared_author (spec section 29.6)
|
|
? 5 => [1*65535 file], ; strictly ascending UTF-8 bytes of path
|
|
? 6 => extensions, ; critical_extensions
|
|
? 7 => extensions, ; noncritical_extensions
|
|
}
|
|
file = {
|
|
0 => tstr .size (1..1024), ; path (spec section 29.5)
|
|
1 => 0..max-payload-length, ; size
|
|
2 => 0..max-payload-length, ; start
|
|
3 => 0..max-payload-length, ; end, exclusive
|
|
4 => bstr .size 32, ; SHA-256 of the file
|
|
? 5 => 0..253402300799, ; mtime, UTC seconds, informative
|
|
}
|
|
|
|
; Spec section 41. BODY_CBOR of a .dkk, after the 12-byte DKK1 prelude.
|
|
access-key-body = {
|
|
0 => "datekeys-access-key",
|
|
1 => 1,
|
|
2 => bstr .size 16, ; credential_id
|
|
3 => capsule-id,
|
|
4 => access-type,
|
|
5 => access-material,
|
|
? 6 => verification-metadata,
|
|
? 7 => extensions, ; critical_extensions
|
|
? 8 => extensions, ; noncritical_extensions
|
|
}
|
|
|
|
; Spec section 43. Present only when it holds a digest: never an empty map.
|
|
verification-metadata = {
|
|
0 => bstr .size 32, ; capsule_digest = SHA-256(exact .dkc bytes)
|
|
}
|
|
|
|
; Spec section 31 and 54.
|
|
extensions = [1*64 extension]
|
|
extension = {
|
|
0 => extension-id,
|
|
1 => extension-version,
|
|
? 2 => extension-data,
|
|
}
|
|
extension-version = uint .le 4294967295
|
|
; Opaque bytes: the base protocol never decodes or validates the content, and
|
|
; an extension without data omits key 2. The effective bound is the frame of
|
|
; the containing object (spec section 57); 67108864 bytes (64 MiB) is the
|
|
; largest frame, SEALED_CONTROL. Each registered extension declares its own
|
|
; maximum (spec section 72).
|
|
extension-data = bstr .size (1..67108864)
|
|
|
|
capsule-id = bstr .size 16
|
|
access-policy = &(time_only: 0, time_and_key: 1)
|
|
access-type = "x25519"
|
|
access-material = bstr .size 32 ; for access-type "x25519"
|
|
|
|
; 2^53-1: every unsigned integer of the protocol is exact as an IEEE 754
|
|
; double (spec section 58).
|
|
max-safe-uint = 9007199254740991
|
|
|
|
; Names of the Provider Profile (spec section 12.1): the alphabets are
|
|
; normative, the maximum lengths of 128 and 64 bytes are implementation
|
|
; limits of the reference (spec section 74). A violation is
|
|
; ERR_UNKNOWN_PROFILE (spec section 57). profile-id also bounds the network
|
|
; of a dk1_ DateKey (ERR_DATEKEY_INVALID there), whose canonical JSON has no
|
|
; escapes (spec section 18).
|
|
profile-id = tstr .regexp "[a-z0-9][a-z0-9:._-]{0,127}"
|
|
name = tstr .regexp "[a-z0-9][a-z0-9._-]{0,63}"
|
|
|
|
; Spec section 31: at least 1 byte of valid UTF-8, normative; at most 256
|
|
; bytes, an implementation limit of the reference (spec section 74).
|
|
extension-id = tstr .size (1..256)
|
|
|
|
; Implementation limits of the reference implementation, listed in spec
|
|
; section 74, which leaves definitive field limits open.
|
|
public-key = bstr .size (1..1024) ; spec section 12.1: 48 or 96 bytes by scheme
|
|
period = 1..86400 ; at most one day; spec section 11: 1..max-safe-uint
|
|
|
|
; Spec section 18 and 19: "dk1_" + unpadded Base64URL of the canonical JSON
|
|
; {"version":1,"network":<profile-id>,"round":<round>}, round in 1..2^53-1.
|
|
; Spec section 15 further bounds the round time of the round to
|
|
; 9999-12-31T23:59:59Z under the pinned profile (step 7 of section 63). A
|
|
; violation is ERR_DATEKEY_INVALID or ERR_DATEKEY_NON_CANONICAL (spec section
|
|
; 19), checked with the fields, not ERR_NON_CANONICAL_CBOR.
|
|
compact-datekey = tstr .regexp "dk1_[A-Za-z0-9_-]+"
|