; DateKeys Protocol Specification v0.8.1 - CBOR schemas (RFC 8610 CDDL). ; ; Normative companion of spec/DateKeys_Protocol_Specification_v0.8.1.md, as ; implemented by the reference implementation g.activething.com/go/DateKeys. ; ; Encoding rules that CDDL cannot express (spec section 58, 58.1): ; - Every structure is Deterministic CBOR (RFC 8949 section 4.2.1): map keys ; sorted by their encoded bytes, shortest-form integers and lengths, ; definite lengths only, no tags. ; - 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 "" 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). ; - Within one object an extension_id appears at most once and never in both ; extension arrays; arrays are sorted by the UTF-8 bytes of extension_id and ; then by extension_version. ; Spec section 11 and 12. 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 => bstr, ; group public key 7 => uint .ge 1, ; period in seconds 8 => 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). 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 = { 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 } ; 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 = [+ extension] extension = { 0 => extension-id, 1 => uint, ; extension_version ? 2 => any, ; data, not interpreted by the base protocol } 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" ; Implementation limits of the reference implementation (spec section 74 ; leaves definitive field limits open). profile-id = tstr .regexp "[a-z0-9][a-z0-9:._-]{0,127}" name = tstr .regexp "[a-z0-9][a-z0-9._-]{0,63}" extension-id = tstr .size (1..256) ; Spec section 18 and 19: "dk1_" + unpadded Base64URL of the canonical JSON ; {"version":1,"network":,"round":}, round in 1..2^53-1. compact-datekey = tstr .regexp "dk1_[A-Za-z0-9_-]+"