From: Sergey Matveev Date: Sat, 17 May 2025 11:00:18 +0000 (+0300) Subject: Various spec fixes X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=ea5d8a6594d2883400624caa0bd2e78df1ee76c7620eb9a393dc6c2d2a6b4fa8;p=keks.git Various spec fixes --- diff --git a/spec/INSTALL b/spec/INSTALL index cfd68cd..7c25dcd 100644 --- a/spec/INSTALL +++ b/spec/INSTALL @@ -13,5 +13,6 @@ git://git.stargrave.org/keks.git git://y.git.stargrave.org/keks.git git://y.git.cypherpunks.su/keks.git -Also there is Yggdrasil accessible address: http://y.www.keks.cypherpunks.su/ +Also there is Yggdrasil accessible address: +=> http://y.www.keks.cypherpunks.su/ => https://yggdrasil-network.github.io/ Yggdrasil diff --git a/spec/cm/dem/kuznechik-ctr-hmac-kr b/spec/cm/dem/kuznechik-ctr-hmac-kr index 7ce03f2..756b0af 100644 --- a/spec/cm/dem/kuznechik-ctr-hmac-kr +++ b/spec/cm/dem/kuznechik-ctr-hmac-kr @@ -10,10 +10,11 @@ Data is split on 128 KiB chunks, each of which is encrypted the following way: IV = HKDF-Expand(H, prk=CKi, len=8, info="cm/encrypted/kuznechik-ctr-hmac-kr/iv") Kauth || KauthTail = HKDF-Expand(H, prk=CKi, info="cm/encrypted/kuznechik-ctr-hmac-kr/auth") - KauthTail = HKDF-Expand(H, prk=CKi, - info="cm/encrypted/kuznechik-ctr-hmac-kr/authTail") - CT = Kuznechik-CTR(key=Kenc, ctr=IV, data=chunk) - CT || HMAC(Streebog-256, key={Kauth|KauthTail}, data=CT) + CIPHERTEXT = Kuznechik-CTR(key=Kenc, ctr=IV, data=chunk) + CIPHERTEXT || HMAC(Streebog-256, key={Kauth|KauthTail}, data=CIPHERTEXT) + +Chaining key (CK) advances with every chunk. 256-bit encryption key and +randomised 64-bit nonce (initialisation vector) are derived from it. KauthTail is used only in the last chunk to explicitly signal that it is the last one. diff --git a/spec/cm/dem/xchapoly-krkc b/spec/cm/dem/xchapoly-krkc index 730141d..88f831d 100644 --- a/spec/cm/dem/xchapoly-krkc +++ b/spec/cm/dem/xchapoly-krkc @@ -8,7 +8,7 @@ Data is split on 128 KiB chunks, each of which is encrypted the following way: CKi = HKDF-Extract(H, salt="", ikm=CK{i-1}) KEY = HKDF-Expand(H, prk=CKi, info="cm/encrypted/xchapoly-krkc/key") IV = HKDF-Expand(H, prk=CKi, info="cm/encrypted/xchapoly-krkc/iv", len=24) - if last chunk { IV[23] |= 0x01 } else { IV[23] &= 0xFE } + if {last chunk} then { IV[23] |= 0x01 } else { IV[23] &= 0xFE } CIPHERTEXT || TAG = XChaCha20-Poly1305(key=KEY, ad="", nonce=IV, data=chunk) COMMITMENT = BLAKE2b-256(KEY || IV || TAG) CIPHERTEXT || TAG || COMMITMENT @@ -21,7 +21,6 @@ Nonce's lowest bit is set only if this is the last chunk we encrypting. "/payload"'s chunk length equals to 128KiB+16+32 bytes. => https://datatracker.ietf.org/doc/html/rfc5869.html RFC 5869, HKDF -=> https://www.blake2.net/ BLAKE2b is hashing algorithm -=> https://datatracker.ietf.org/doc/html/rfc7693.html RFC 7693, same +=> https://datatracker.ietf.org/doc/html/rfc7693.html RFC 7693, BLAKE2b => https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-xchacha XChaCha20-Poly1305 AEAD => https://datatracker.ietf.org/doc/html/rfc8439.html RFC 8439 diff --git a/spec/cm/dem/xchapoly-krmr b/spec/cm/dem/xchapoly-krmr index f505a4f..e3ac53c 100644 --- a/spec/cm/dem/xchapoly-krmr +++ b/spec/cm/dem/xchapoly-krmr @@ -1,17 +1,17 @@ XChaCha20-Poly1305 with key ratcheting and multi-recipient DEM. [cm/encrypted/]'s "/dem/a" equals to "xchapoly-krmr". -CEK consists of common 64 bytes long part equal in all KEMs (@code{CEK} itself), -and 64 bytes long per-KEM/per-recipient random MAC key (@code{prMAC}). +CEK consists of common 64-bytes part equal in all KEMs (CEK itself), +and 64 bytes long per-KEM/per-recipient random MAC key (prMACx). Data is split on 128 KiB chunks, each of which is encrypted the following way: H = BLAKE2b CK0, prMACx0 = CEK || prMACx CKi = HKDF-Extract(H, salt="", ikm=CK{i-1}) + prMACxi = HKDF-Extract(H, salt="", ikm=prMACx{i-1}) KEY = HKDF-Expand(H, prk=CKi, info="cm/encrypted/xchapoly-krmr/key") IV = HKDF-Expand(H, prk=CKi, info="cm/encrypted/xchapoly-krmr/iv", len=24) - if last chunk { IV[23] |= 0x01 } else { IV[23] &= 0xFE } + if {last chunk} then { IV[23] |= 0x01 } else { IV[23] &= 0xFE } CIPHERTEXT || TAG = XChaCha20-Poly1305(key=KEY, ad="", nonce=IV, data=chunk) - prMACxi = HKDF-Extract(H, salt="", ikm=prMACx{i-1}) MACx = BLAKE2b-256-MAC(key=prMACxi, H(CIPHERTEXT || TAG)) CIPHERTEXT || TAG || MACx || MAC{x+1} [|| MAC{x+2} ...] @@ -26,7 +26,6 @@ MACs are ordered the same way as KEMs in the list. "/payload"'s chunk length equals to 128KiB+16+32*recipients bytes. => https://datatracker.ietf.org/doc/html/rfc5869.html RFC 5869, HKDF -=> https://www.blake2.net/ BLAKE2b is hashing algorithm -=> https://datatracker.ietf.org/doc/html/rfc7693.html RFC 7693, same +=> https://datatracker.ietf.org/doc/html/rfc7693.html RFC 7693, BLAKE2b => https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-xchacha XChaCha20-Poly1305 AEAD => https://datatracker.ietf.org/doc/html/rfc8439.html RFC 8439 diff --git a/spec/cm/encrypted/index b/spec/cm/encrypted/index index 75d6326..d5bd6cf 100644 --- a/spec/cm/encrypted/index +++ b/spec/cm/encrypted/index @@ -41,6 +41,6 @@ identification. UUIDv4 is recommended. If absent, then null UUID is used in KDF. [cm/encrypted/authcrypt] -- authenticated public-key encryption -[cm/keywrap/] -- key wrapping mechanisms -[cm/dem/] -- data encapsulation mechanisms -[cm/kem/] -- key encapsulation mechanisms +[cm/keywrap/] | key wrapping mechanisms +[cm/dem/] | data encapsulation mechanisms +[cm/kem/] | key encapsulation mechanisms diff --git a/spec/cm/hashed/blake2b b/spec/cm/hashed/blake2b index 364531d..9f464fb 100644 --- a/spec/cm/hashed/blake2b +++ b/spec/cm/hashed/blake2b @@ -1,5 +1,4 @@ [cm/hashed/] with BLAKE2b. +=> https://datatracker.ietf.org/doc/html/rfc7693.html RFC 7693, BLAKE2b BLAKE2b with 512-bit output has "blake2b" algorithm identifier. 256-bit output has "blake2b256" algorithm identifier. -=> https://www.blake2.net/ BLAKE2b -=> https://datatracker.ietf.org/doc/html/rfc7693.html RFC 7693, same diff --git a/spec/cm/hashed/blake3 b/spec/cm/hashed/blake3 index 80b44bd..1ac13df 100644 --- a/spec/cm/hashed/blake3 +++ b/spec/cm/hashed/blake3 @@ -1,3 +1,3 @@ [cm/hashed/] with BLAKE3. -BLAKE3 with fixed 256-bit output has "blake3" algorithm identifier. => https://github.com/BLAKE3-team/BLAKE3/ BLAKE3 +BLAKE3 with fixed 256-bit output has "blake3" algorithm identifier. diff --git a/spec/cm/hashed/shake b/spec/cm/hashed/shake index f82f1c0..b7eb4d7 100644 --- a/spec/cm/hashed/shake +++ b/spec/cm/hashed/shake @@ -1,4 +1,4 @@ [cm/hashed/] with SHAKE. +=> https://keccak.team/ SHAKE SHAKE XOF function with fixed 256 (SHAKE128) or 512 (SHAKE256) bit output. "shake128", "shake256" algorithm identifiers are used. -=> https://keccak.team/ SHAKE diff --git a/spec/cm/hashed/skein512 b/spec/cm/hashed/skein512 index feaf9db..4564a7f 100644 --- a/spec/cm/hashed/skein512 +++ b/spec/cm/hashed/skein512 @@ -1,3 +1,3 @@ [cm/hashed/] with Skein-512. -"skein512" algorithm identifier is used. => https://www.schneier.com/academic/skein/ Skein-512 +"skein512" algorithm identifier is used. diff --git a/spec/cm/hashed/streebog b/spec/cm/hashed/streebog index 5fb3a05..ae72a9b 100644 --- a/spec/cm/hashed/streebog +++ b/spec/cm/hashed/streebog @@ -1,4 +1,4 @@ [cm/hashed/] with GOST R 34.11-2012. +=> https://datatracker.ietf.org/doc/html/rfc6986.html RFC 6986 Streebog must be big-endian serialised. "streebog256", "streebog512" algorithm identifiers are used. -=> https://datatracker.ietf.org/doc/html/rfc6986.html RFC 6986 diff --git a/spec/cm/hashed/xxh3-128 b/spec/cm/hashed/xxh3-128 index 1ebce9e..989dcc3 100644 --- a/spec/cm/hashed/xxh3-128 +++ b/spec/cm/hashed/xxh3-128 @@ -1,4 +1,4 @@ [cm/hashed/] with XXH3-128. +=> https://xxhash.com/ XXH3 128-bit XXH3 hash must be big-endian encoded. "xxh3-128" algorithm identifier is used. -=> https://xxhash.com/ XXH3 diff --git a/spec/cm/kem/balloon-blake2b-hkdf b/spec/cm/kem/balloon-blake2b-hkdf index de5f403..27c3691 100644 --- a/spec/cm/kem/balloon-blake2b-hkdf +++ b/spec/cm/kem/balloon-blake2b-hkdf @@ -1,17 +1,14 @@ -Balloon-BLAKE2b+HKDF KEM. +Balloon-BLAKE2b + HKDF KEM. +=> https://crypto.stanford.edu/balloon/ Balloon +=> https://datatracker.ietf.org/doc/html/rfc7693.html RFC 7693, BLAKE2b +=> https://datatracker.ietf.org/doc/html/rfc5869.html RFC 5869, HKDF << [schemas/kem-balloon-blake2b-hkdf.tcl] -Balloon memory-hardened password hasher must be used with BLAKE2b hash. -=> https://crypto.stanford.edu/balloon/ Balloon - H = BLAKE2b KEK = HKDF-Expand(H, - prk=balloon(H, passphrase, /kem/salt, s, t, p), + prk=Balloon(H, passphrase, /kem/salt, s, t, p), info="cm/encrypted/balloon-blake2b-hkdf" || /id) "/kem/*/cek" is wrapped with [cm/keywrap/xchapoly] mechanism. -=> https://datatracker.ietf.org/doc/html/rfc5869.html RFC 5869, HKDF -=> https://www.blake2.net/ BLAKE2b is hashing algorithm -=> https://datatracker.ietf.org/doc/html/rfc7693.html RFC 7693, same diff --git a/spec/cm/kem/gost3410-hkdf b/spec/cm/kem/gost3410-hkdf index b7938f0..8880380 100644 --- a/spec/cm/kem/gost3410-hkdf +++ b/spec/cm/kem/gost3410-hkdf @@ -1,7 +1,13 @@ -GOST R 34.10+HKDF KEM. +GOST R 34.10 + HKDF KEM. +=> https://datatracker.ietf.org/doc/html/rfc7091.html RFC 7091, GOST R 34.10-2012 +=> https://datatracker.ietf.org/doc/html/rfc5869.html RFC 5869, HKDF +=> https://datatracker.ietf.org/doc/html/rfc6986.html RFC 6986, Streebog, GOST R 34.11-2012 << [schemas/kem-gost3410-hkdf.tcl] +Recipient public key with [cm/pub/gost3410] algorithm must be used. +It should have "kem" key usage set. + GOST R 34.10-2012 VKO parameter set A/C ("gost3410-256A", "gost3410-512C") must be used for DH operation, with UKM taken from the structure. VKO's output is 512- or 1024-bit "BE(X)||BE(Y)" point, used in HKDF below: @@ -9,12 +15,9 @@ output is 512- or 1024-bit "BE(X)||BE(Y)" point, used in HKDF below: H = Streebog-512 DH(sk, pk) = GOSTR3410-VKO(prv=sk, pub=pk, ukm=UKM) PRK = HKDF-Extract(H, salt="", ikm=DH(e, s)) - if specified(sender): + if {specified sender} PRK = HKDF-Extract(H, salt=PRK, ikm=DH(s, s)) KEK = HKDF-Expand(H, prk=PRK, info="cm/encrypted/gost3410-hkdf" || /id) "/kem/*/cek" is wrapped with [cm/keywrap/kexp15] mechanism. -=> https://datatracker.ietf.org/doc/html/rfc5869.html RFC 5869, HKDF -=> https://datatracker.ietf.org/doc/html/rfc6986.html RFC 6986, Streebog, GOST R 34.11-2012 hashing algorithm -=> https://datatracker.ietf.org/doc/html/rfc7091.html RFC 7091, GOST R 34.10-2012 is signing/key-aggreement algorithm diff --git a/spec/cm/kem/mceliece6960119-x25519-hkdf-shake256 b/spec/cm/kem/mceliece6960119-x25519-hkdf-shake256 index 3b50269..fc88bca 100644 --- a/spec/cm/kem/mceliece6960119-x25519-hkdf-shake256 +++ b/spec/cm/kem/mceliece6960119-x25519-hkdf-shake256 @@ -1,4 +1,8 @@ -Classic McEliece 6960-119+X25519+HKDF-SHAKE256 KEM. +Classic McEliece 6960-119 + X25519 + HKDF-SHAKE256 KEM. +=> https://classic.mceliece.org/ Classic McEliece +=> https://datatracker.ietf.org/doc/html/rfc7748 X25519 +=> https://datatracker.ietf.org/doc/html/rfc5869.html RFC 5869, HKDF +=> https://keccak.team/ SHAKE XOF function << [schemas/kem-with-encap.tcl] @@ -6,9 +10,9 @@ Classic McEliece 6960-119+X25519+HKDF-SHAKE256 KEM. Recipient public key with [cm/pub/mceliece6960119-x25519] algorithm must be used. It should have "kem" key usage set. -Recipient's map "/kem/*/encap" field is a concatenation of -194 bytes of Classic McEliece 6960-119 ciphertext, containing -ephemeral key, with 32 bytes of ephemeral X25519 public key. +"/kem/*/encap" field is a concatenation of 194 bytes of +Classic McEliece 6960-119 ciphertext, containing ephemeral key, +with 32 bytes of ephemeral X25519 public key. Recipient performs X25519 and Classic McEliece computations to derive/decapsulate two 32-byte shared keys. Then it combines @@ -19,18 +23,16 @@ them to get the KEK decryption key of the CEK. mceliece6960119-shared-key || es-x25519-shared-key || H(mceliece6960119-sender-ciphertext || e-x25519-sender-public-key) || H(mceliece6960119-recipient-public-key || s-x25519-recipient-public-key)) - if specified(sender): + if {specified sender} PRK = HKDF-Extract(H, salt=PRK, ikm= ss-x25519-shared-key || s-x25519-sender-public-key || s-x25519-recipient-public-key) KEK = HKDF-Expand(H, prk=PRK, - info="cm/encrypted/mceliece6960119-x25519-hkdf-shake256" || /salt) + info="cm/encrypted/mceliece6960119-x25519-hkdf-shake256" || /id) "/kem/*/cek" is wrapped with [cm/keywrap/xchapoly] mechanism. -=> https://datatracker.ietf.org/doc/html/rfc5869.html RFC 5869, HKDF -=> https://keccak.team/ SHAKE XOF function KEM combiner nearly fully resembles: => https://datatracker.ietf.org/doc/draft-josefsson-chempat/ Chempat diff --git a/spec/cm/kem/pbkdf2 b/spec/cm/kem/pbkdf2 index 5ef507c..6c88337 100644 --- a/spec/cm/kem/pbkdf2 +++ b/spec/cm/kem/pbkdf2 @@ -1,11 +1,8 @@ PBKDF2 KEM. +=> https://datatracker.ietf.org/doc/html/rfc2898 RFC 2898, PBKDF2 << [schemas/kem-pbkdf2.tcl] -PBKDF2 is RFC 2898 algorithm. -Key length equal to key wrapping algorithm requirements. -=> https://datatracker.ietf.org/doc/html/rfc2898 RFC 2898 - Key wrapping algorithm may be one of: [cm/keywrap/xchapoly], [cm/keywrap/kexp15], depending on the hash chosen. diff --git a/spec/cm/kem/sntrup4591761-x25519-hkdf-blake2b b/spec/cm/kem/sntrup4591761-x25519-hkdf-blake2b index 074ec2b..fc9a3d6 100644 --- a/spec/cm/kem/sntrup4591761-x25519-hkdf-blake2b +++ b/spec/cm/kem/sntrup4591761-x25519-hkdf-blake2b @@ -1,4 +1,8 @@ -SNTRUP4591761+X25519+HKDF-BLAKE2b KEM. +Streamlined NTRU Prime 4591^761 + X25519 + HKDF-BLAKE2b KEM. +=> https://ntruprime.cr.yp.to/ Streamlined NTRU Prime KEM algorithm +=> https://datatracker.ietf.org/doc/html/rfc7748 X25519 +=> https://datatracker.ietf.org/doc/html/rfc5869.html RFC 5869, HKDF +=> https://datatracker.ietf.org/doc/html/rfc7693.html RFC 7693, BLAKE2b << [schemas/kem-with-encap.tcl] @@ -19,7 +23,7 @@ key of the CEK. sntrup4591761-shared-key || es-x25519-shared-key || H(sntrup4591761-sender-ciphertext || e-x25519-sender-public-key) || H(sntrup4591761-recipient-public-key || s-x25519-recipient-public-key)) - if specified(sender): + if {specified sender} PRK = HKDF-Extract(H, salt=PRK, ikm= ss-x25519-shared-key || s-x25519-sender-public-key || diff --git a/spec/cm/keywrap/kexp15 b/spec/cm/keywrap/kexp15 index 3f25302..dd8bbb8 100644 --- a/spec/cm/keywrap/kexp15 +++ b/spec/cm/keywrap/kexp15 @@ -5,6 +5,5 @@ cryptography algorithms. KEK is 32+8+32=72 bytes long. KExp15(Kenc, Kauth, IV, CEK) = Kuznechik-CTR( Kenc, CEK || Kuznechik-CMAC(Kauth, IV || CEK), IV=IV) -Kuznechik is GOST R 34.12-2015 encryption algorithm, RFC 7801. -=> https://datatracker.ietf.org/doc/html/rfc7801.html RFC 7801 +=> https://datatracker.ietf.org/doc/html/rfc7801.html RFC 7801, Kuznechik, GOST R 34.12-2015 CMAC (OMAC1) is described in GOST R 34.13-2015. diff --git a/spec/cm/keywrap/xchapoly b/spec/cm/keywrap/xchapoly index ca8f69a..355d4cd 100644 --- a/spec/cm/keywrap/xchapoly +++ b/spec/cm/keywrap/xchapoly @@ -1,5 +1,6 @@ XChaCha20-Poly1305 key wrapping mechanism. Key is encrypted using XChaCha20-Poly1305 algorithm. +=> https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-xchacha XChaCha20-Poly1305 Random 192-bit nonce is prepended to the ciphertext. KEK has 256-bit length. diff --git a/spec/cm/prv/mceliece6960119-x25519 b/spec/cm/prv/mceliece6960119-x25519 index 947e55e..0629193 100644 --- a/spec/cm/prv/mceliece6960119-x25519 +++ b/spec/cm/prv/mceliece6960119-x25519 @@ -1,4 +1,6 @@ -[cm/prv/] with Classic McEliece 6960-119+X25519. +[cm/prv/] with Classic McEliece 6960-119 + X25519. +=> https://classic.mceliece.org/ Classic McEliece +=> https://datatracker.ietf.org/doc/html/rfc7748 X25519 Concatenation of Classic McEliece 6960-119 13948-byte private key and X25519's 32-byte one. "mceliece6960119-x25519" algorithm identifier is used. diff --git a/spec/cm/prv/sntrup4591761-x25519 b/spec/cm/prv/sntrup4591761-x25519 index 34cfdd1..79fc2ff 100644 --- a/spec/cm/prv/sntrup4591761-x25519 +++ b/spec/cm/prv/sntrup4591761-x25519 @@ -1,4 +1,5 @@ -[cm/prv/] with SNTRUP4591761+X25519. -Concatenation of Streamlined NTRU Prime 4591^761's 1600-byte private key -and X25519's 32-byte one. +[cm/prv/] with Streamlined NTRU Prime 4591^761 + X25519. +=> https://ntruprime.cr.yp.to/ NTRU Prime +=> https://datatracker.ietf.org/doc/html/rfc7748 X25519 +It is a concatenation of SNTRUP's 1600-byte and X25519's 32-byte keys. "sntrup4591761-x25519" algorithm identifier is used. diff --git a/spec/cm/prv/sphincs+-shake-256s b/spec/cm/prv/sphincs+-shake-256s index 2f54cb6..75af180 100644 --- a/spec/cm/prv/sphincs+-shake-256s +++ b/spec/cm/prv/sphincs+-shake-256s @@ -1,6 +1,6 @@ [cm/prv/] with SPHINCS+-SHAKE256-256s. -255-bit security level, fast variant and simple parameters. +255-bit security level, small variant and simple parameters. => https://sphincs.org/ SPHINCS+ => https://keccak.team/ SHAKE256 Value is concatenation of private and public keys (128+64 bytes). -Algorithm identifier for the public key: "sphincs+-shake-256s". +"sphincs+-shake-256s" algorithm identifier is used. diff --git a/spec/cm/pub/ed25519-blake2b b/spec/cm/pub/ed25519-blake2b index a4996f6..5d391e8 100644 --- a/spec/cm/pub/ed25519-blake2b +++ b/spec/cm/pub/ed25519-blake2b @@ -5,10 +5,6 @@ Same calculation and serialisation rules must be used as with Public key's fingerprint should be calculated using BLAKE2b hash with 256 bit output length specified. +=> https://datatracker.ietf.org/doc/html/rfc7693.html RFC 7693, BLAKE2b Algorithm identifier for the public key: "ed25519-blake2b". - -=> https://cr.yp.to/ecdh.html X2559, key-agreement algorithm -=> https://datatracker.ietf.org/doc/html/rfc7748.html RFC 7748, same -=> https://www.blake2.net/ BLAKE2b hashing algorithm -=> https://datatracker.ietf.org/doc/html/rfc7693.html RFC 7693, same diff --git a/spec/cm/pub/gost3410 b/spec/cm/pub/gost3410 index da2aa94..b097acf 100644 --- a/spec/cm/pub/gost3410 +++ b/spec/cm/pub/gost3410 @@ -1,14 +1,4 @@ -[cm/pub/] with GOST R 34.10-2012. - -GOST R 34.10-2012 must be used with Streebog (GOST R 34.11-2012) -hash function. Its digest must be big-endian serialised. Public key -must be in "BE(X)||BE(Y)" format. - +[cm/pub/] with GOST R 34.10-2012 ([cm/signed/gost3410]). +Public key must be in "BE(X)||BE(Y)" format. Algorithm identifiers for the public key: "gost3410-256A", "gost3410-512C". - -Public key's fingerprint should be calculated using big-endian Streebog-256 hash. - -Streebog is GOST R 34.11-2012 hashing algorithm. -GOST R 34.10-2012 is signing/key-aggreement algorithm. -=> https://datatracker.ietf.org/doc/html/rfc6986.html RFC 6986, GOST R 34.11-2012 -=> https://datatracker.ietf.org/doc/html/rfc7091.html RFC 7091, GOST R 34.10-2012 +Public key's fingerprint should be calculated using big-endian Streebog-256. diff --git a/spec/cm/pub/index b/spec/cm/pub/index index a5ce71e..ec16a97 100644 --- a/spec/cm/pub/index +++ b/spec/cm/pub/index @@ -8,24 +8,24 @@ Its "/load/t" equals to "pub". "/load/v" contains "cm/pub/load": sub: Subject is a map of arbitrary strings. Currently no constraints on - what fields must be present. Each application and usage context - defines it on his own. But you may mimic X.509's subject with keys - like "CN", "C", "O" and similar ones. + what fields must be present. Each application defines them on its + own. But you may mimic X.509's subject with keys like "CN", "C", "O" + and similar ones. pub: Public key container itself may contain multiple public keys. That is *solely* intended for tasks requiring more than single key usage. For example NNCP uses one X25519 for (DH) encryption, one - curve25519 for online authentication and one ed25519 for signing - purposes. All those three keys are used together. That public key's - key usage field must contain something like "nncp". + X25519 for online authentication and one ed25519 for signing purposes. + All those three keys are used together. That public key's key usage + field should contain something like "nncp". => http://www.nncpgo.org NNCP If your keypair is intended for general purposes like signing of - arbitrary data, then single public key @strong{should} be used, with - a key usage like "sig". + arbitrary data, then single public key *should* be used, with a key + usage like "sig". id: Public key(s)'s fingerprint *should* be generated as 256-bit - hash hash of the encoded "pub" field. If not stated otherwise - for specific algorithm. + hash over the encoded "pub" field, if not stated otherwise for + specific algorithm. Exact hash algorithm depends on the public keys. ku: Intended public key(s) usage. Application-specific example with multiple public keys is described @@ -35,7 +35,7 @@ crit: ones may be placed outside that map, directly in cm/pub/load. It *must* be absent if empty. Values are extension specific. -[cm/signed/]'s "sig-tbs" *must* contain additional fields: +[cm/signed/]'s "tbs" *must* contain additional fields: << [schemas/pub-sig-tbs.tcl] @@ -43,7 +43,6 @@ sid: Signing public key's fingerprint. cid: Certification unique identifier. UUIDv7 is a good choice. But it may be UUIDv4, or any desired method of generation. exp: Certification expiration period. - It *must* contain TAI64 datetime (no nanoseconds). Example minimal certified public key may look like: diff --git a/spec/cm/pub/mceliece6960119-x25519 b/spec/cm/pub/mceliece6960119-x25519 index 35c17b4..3dedeb7 100644 --- a/spec/cm/pub/mceliece6960119-x25519 +++ b/spec/cm/pub/mceliece6960119-x25519 @@ -1,17 +1,15 @@ -[cm/pub/] with Classic McEliece 6960-119+X25519. +[cm/pub/] with Classic McEliece 6960-119 + X25519. +=> https://classic.mceliece.org/ Classic McEliece +=> https://datatracker.ietf.org/doc/html/rfc7748 X25519 Combined Classic McEliece 6960-119 and X25519 public keys are used for KEM purposes, so should have "kem" key usage set. -Its algorithm identifier is "mceliece6960119-x25519". Its -public key value is a concatenation of 1047319-byte +Its algorithm identifier is "mceliece6960119-x25519". +Its public key value is a concatenation of 1047319-byte mceliece6960119 public key and 32-byte X25519 one. Public key's fingerprint should be calculated using SHAKE128. - -=> https://cr.yp.to/ecdh.html X2559 key-agreement algorithm -=> https://datatracker.ietf.org/doc/html/rfc7748.html RFC 7748, same -=> https://classic.mceliece.org/ Classic McEliece KEM algorithm => https://keccak.team/ SHAKE XOF function Optional "/load/v/prehash" field can contain the SHAKE256 hash diff --git a/spec/cm/pub/sntrup4591761-x25519 b/spec/cm/pub/sntrup4591761-x25519 index 40247b2..42a0168 100644 --- a/spec/cm/pub/sntrup4591761-x25519 +++ b/spec/cm/pub/sntrup4591761-x25519 @@ -1,15 +1,14 @@ -[cm/pub/] with SNTRUP4591761+X25519. +[cm/pub/] with Streamlined NTRU Prime 4591^761 + X25519. +=> https://ntruprime.cr.yp.to/ Streamlined NTRU Prime KEM algorithm +=> https://datatracker.ietf.org/doc/html/rfc7748 X25519 Combined Streamlined NTRU Prime 4591^761 and X25519 public keys are used for KEM purposes, so should have "kem" key usage set. -Its algorithm identifier is "sntrup4591761-x25519". Its public -key value is a concatenation of 1218-byte SNTRUP4591761 public key -and 32-byte X25519 one. +Its algorithm identifier is "sntrup4591761-x25519". +Its public key value is a concatenation of 1218-byte +SNTRUP4591761 public key and 32-byte X25519 one. Public key's fingerprint should be calculated using BLAKE2b hash with 256 bit output length specified. - -=> https://cr.yp.to/ecdh.html X2559, key-agreement algorithm -=> https://datatracker.ietf.org/doc/html/rfc7748.html RFC 7748, same -=> https://ntruprime.cr.yp.to/ Streamlined NTRU Prime KEM algorithm +=> https://datatracker.ietf.org/doc/html/rfc7693.html RFC 7693, BLAKE2b diff --git a/spec/cm/pub/sphincs+-shake-256s b/spec/cm/pub/sphincs+-shake-256s index 3df24b1..7e8085f 100644 --- a/spec/cm/pub/sphincs+-shake-256s +++ b/spec/cm/pub/sphincs+-shake-256s @@ -1,5 +1,5 @@ [cm/pub/] with SPHINCS+-SHAKE256-256s. -255-bit security level, fast variant and simple parameters. +255-bit security level, small variant and simple parameters. => https://sphincs.org/ SPHINCS+ => https://keccak.team/ SHAKE256 "sphincs+-shake-256s" algorithm identifier is used. diff --git a/spec/cm/signed/ed25519-blake2b b/spec/cm/signed/ed25519-blake2b index 8ecc964..13245d8 100644 --- a/spec/cm/signed/ed25519-blake2b +++ b/spec/cm/signed/ed25519-blake2b @@ -2,14 +2,14 @@ EdDSA with Edwards25519 is used similarly as in RFC 8032. But BLAKE2b is used instead of SHA2-512 hash. -Strict ZIP-0215 validation rules should be used while verifying the signature. - => https://datatracker.ietf.org/doc/html/rfc8032 RFC 8032, EdDSA + +Strict ZIP-0215 validation rules should be used while verifying the signature. => https://zips.z.cash/zip-0215 ZIP-0215 -PureEdDSA *must" be used when no detached data exists and -"ed25519-blake2b" algorithm identifier is used for signature. +PureEdDSA *must* be used when no detached data exists and +"ed25519-blake2b" algorithm identifier is used in signature. -HashEdDSA *must" be used otherwise, using BLAKE2b-512 as a +HashEdDSA *must* be used otherwise, using BLAKE2b-512 as a hash, using "ed25519ph-blake2b" algorithm identifier for signature. diff --git a/spec/cm/signed/ed25519ph-blake2b-merkle b/spec/cm/signed/ed25519ph-blake2b-merkle index db38e95..447c7d3 100644 --- a/spec/cm/signed/ed25519ph-blake2b-merkle +++ b/spec/cm/signed/ed25519ph-blake2b-merkle @@ -1,4 +1,6 @@ [cm/signed/] with Ed25519-BLAKE2b with Merkle-tree hashing. -[cm/hashed/blake2b-merkle] hashing is used. -HashEdDSA mode is used with "ed25519ph-blake2b-merkle" -algorithm identifier for signature. + +It is similar to [cm/signed/ed25519-blake2b], but +HashEdDSA mode is used with [cm/hashed/blake2b-merkle] hashing. + +"ed25519ph-blake2b-merkle" algorithm identifier is used in signature. diff --git a/spec/cm/signed/gost3410 b/spec/cm/signed/gost3410 index 0ab49bc..0bc7ccd 100644 --- a/spec/cm/signed/gost3410 +++ b/spec/cm/signed/gost3410 @@ -1,12 +1,9 @@ [cm/signed/] with GOST R 34.10-2012. +=> https://datatracker.ietf.org/doc/html/rfc7091.html RFC 7091, GOST R 34.10-2012 GOST R 34.10-2012 must be used with Streebog (GOST R 34.11-2012) hash function. Its digest must be big-endian serialised. Signature is in "BE(R)||BE(S)" format. +=> https://datatracker.ietf.org/doc/html/rfc6986.html RFC 6986, GOST R 34.11-2012 Algorithm identifiers for the signature: "gost3410-256A", "gost3410-512C". - -Streebog is GOST R 34.11-2012 hashing algorithm. -GOST R 34.10-2012 is signing/key-aggreement algorithm. -=> https://datatracker.ietf.org/doc/html/rfc6986.html RFC 6986, GOST R 34.11-2012 -=> https://datatracker.ietf.org/doc/html/rfc7091.html RFC 7091, GOST R 34.10-2012 diff --git a/spec/cm/signed/gost3410-merkle b/spec/cm/signed/gost3410-merkle index afd506b..26c599a 100644 --- a/spec/cm/signed/gost3410-merkle +++ b/spec/cm/signed/gost3410-merkle @@ -1,4 +1,7 @@ [cm/signed/] with GOST R 34.10-2012 with Merkle-tree hashing. -[cm/hashed/streebog-merkle] hashing is used. + +It is similar to [cm/signed/gost3410], but [cm/hashed/streebog-merkle] +hashing is used. + Algorithm identifiers for the signature: "gost3410-256A-merkle", "gost3410-512C-merkle". diff --git a/spec/cm/signed/index b/spec/cm/signed/index index 0b910f0..48d6782 100644 --- a/spec/cm/signed/index +++ b/spec/cm/signed/index @@ -14,26 +14,26 @@ Signed container, some kind of analogue to ASN.1-based CMS SignedData. Stored in a file, it should begin with "cm/signed" [encoding/MAGIC], unless it is a [cm/pub/]lic key. +<< [schemas/signed.tcl] << [schemas/av.tcl] << [schemas/fpr.tcl] -<< [schemas/signed.tcl] Signature is created by signing the: [detached-data] || /load || sig-tbs If no "/load/v" is provided, then the data is detached from the -"cm/signed" structure itself and it is fed into hasher before that -structure. You can provide it any way you wish, but for keeping that -detached data closely to the "cm/signed", you should use the +"cm/signed" structure itself and is fed into hasher before that +structure. You can provide it any way you wish, but for keeping +that detached data closely to the "cm/signed", you should use the following approach: - cm/signed/prehash || BLOB(detached-data) || cm/signed + prehash || BLOB(detached-data) || cm/signed << [schemas/signed-prehash.tcl] -With "cm/signed/prehash" you initialise your hashers used during signing -process and feed BLOB's contents (not the encoded BLOB itself!) into the them. +With "prehash" you initialise your hashers used during signing process +and feed BLOB's contents (not the encoded BLOB itself!) into the them. "/sigs/*/tbs/when" is optional signing time. diff --git a/spec/cm/signed/sphincs+-shake-256s b/spec/cm/signed/sphincs+-shake-256s index e386834..84b0cf1 100644 --- a/spec/cm/signed/sphincs+-shake-256s +++ b/spec/cm/signed/sphincs+-shake-256s @@ -1,8 +1,8 @@ [cm/signed/] with SPHINCS+-SHAKE256-256s. -255-bit security level, fast variant, -simple parameters and deterministic signatures. +255-bit security level, small variant, simple parameters. => https://sphincs.org/ SPHINCS+ => https://keccak.team/ SHAKE256 + "sphincs+-shake-256s" algorithm identifier must be used for the signature in pure signing mode. "sphincs+-shake-256s-ph" is used in prehash mode. diff --git a/spec/cm/signed/sphincs+-shake-256s-merkle b/spec/cm/signed/sphincs+-shake-256s-merkle index 79a96b3..0b48b41 100644 --- a/spec/cm/signed/sphincs+-shake-256s-merkle +++ b/spec/cm/signed/sphincs+-shake-256s-merkle @@ -1,3 +1,6 @@ [cm/signed/] with SPHINCS+-SHAKE256-256s with Merkle-tree hashing. + +It is similar to [cm/signed/sphincs+-shake-256s], but [cm/hashed/shake-merkle] SHAKE256 Merkle-tree hashing is used. -"sphincs+-shake-256s-merkle" algorithm identifier must be used for the signature. + +"sphincs+-shake-256s-merkle" algorithm identifier is used for the signature. diff --git a/spec/encoding/FullTable b/spec/encoding/FullTable index a521744..022ced6 100644 --- a/spec/encoding/FullTable +++ b/spec/encoding/FullTable @@ -23,9 +23,9 @@ dec | hex | bin | vlen | 021 | 15 | 00010101 | 0 | 022 | 16 | 00010110 | 0 | 023 | 17 | 00010111 | 0 | -024 | 18 | 00011000 | 8 | [encoding/TAI64] -025 | 19 | 00011001 | 12 | [encoding/TAI64] N -026 | 1A | 00011010 | 16 | [encoding/TAI64] NA +024 | 18 | 00011000 | 8 | [encoding/TAI]64 +025 | 19 | 00011001 | 12 | [encoding/TAI]64N +026 | 1A | 00011010 | 16 | [encoding/TAI]64NA 027 | 1B | 00011011 | 0 | 028 | 1C | 00011100 | 0 | 029 | 1D | 00011101 | 0 | diff --git a/spec/encoding/INT b/spec/encoding/INT index 2a813eb..1717259 100644 --- a/spec/encoding/INT +++ b/spec/encoding/INT @@ -1,9 +1,9 @@ -Integers are encoded as ordinary binary [encoding/String], that has -prepended byte indicating is it positive or negative integer. +Integers are encoded as ordinary binary big-endian [encoding/String], +that has prepended byte indicating is it positive or negative integer. -Negative integers store their absolute value the same way as positive -integers. After decoding, their value is subtracted from -1. Negative -value encoded as 0x02 means: -1 - 0x02 => -3. +Negative integers store positive absolute value, minus one. +After decoding, their value is subtracted from -1. +Negative value encoded as 0x02 means: -1 - 0x02 => -3. Shortest possible form *must* be used, that means no leading zero byte. 0 and -1 values are empty strings, so even they won't have diff --git a/spec/encoding/MAP b/spec/encoding/MAP index 6b45ab8..923aa06 100644 --- a/spec/encoding/MAP +++ b/spec/encoding/MAP @@ -1,8 +1,9 @@ -MAP contains concatenation of string(key)-value pairs. -Keys *must* be non-empty, unique and length-first bytewise ascending ordered. +MAP contains concatenation of [encoding/String](key)⇔value pairs. MAP [STR(KEY0) || ITEM0 || STR(KEY1) || ITEM1 || ...] EOC +Keys *must* be non-empty, unique and length-first bytewise ascending ordered. + Hint: Encoding code for known format can be ordered itself to emit values in an already properly sorted way. diff --git a/spec/encoding/String b/spec/encoding/String index 51c14d1..75806c6 100644 --- a/spec/encoding/String +++ b/spec/encoding/String @@ -1,9 +1,8 @@ -There are two kinds of strings: binary (STR) and -UTF-8 (human-readable ones, STR). +There are two kinds of strings: +BIN (binary) and STR (human-readable, printable ones). -Most significant tag's bit is set for them. Seventh bit tells is it -UTF-8 string, binary otherwise. Next six bits contain the length of -the string. +Most significant tag's bit is set. Seventh bit tells is it UTF-8 string, +binary otherwise. Next six bits contain the length of the string. len +------+ @@ -15,8 +14,8 @@ the string. If length value equals to: 0-60 => Use as is. 61 => 61 + next 8-bits value. -62 => 62 + 255 + next big-endian 16-bits value. -63 => 63 + 255 + 65535 + next big-endian 64-bits value. +62 => 62 + 255 + next big-endian 16-bit value. +63 => 63 + 255 + 65535 + next big-endian 64-bit value. String's length *must* be encoded in shortest possible form. @@ -25,8 +24,9 @@ byte *is not* allowed. That should be normalized Unicode string. Example representations: -0-byte binary string => 80 -4-byte binary string 0x01 0x02 0x03 0x04 => 84 01 02 03 04 -64-byte binary string with 0x41 => BD 03 41 41 ... 41 -UTF-8 string "привет мир" ("hello world" in russian) => - D3 D0 BF D1 80 D0 B8 D0 B2 D0 B5 D1 82 20 D0 BC D0 B8 D1 80 +BIN "" | 80 +BIN [binary decode hex "01 02 03 04"] | 84 01 02 03 04 +BIN [string repeat "A" 64] | BD 03 41 41 ... 41 + +STR "hello world" | CB 68656C6C6F 20 776F726C64 +STR "привет мир" | D3 D0BFD180D0B8D0B2D0B5D182 20 D0BCD0B8D180 diff --git a/spec/encoding/TAI64 b/spec/encoding/TAI similarity index 76% rename from spec/encoding/TAI64 rename to spec/encoding/TAI index 0a9754c..d5168be 100644 --- a/spec/encoding/TAI64 +++ b/spec/encoding/TAI @@ -1,16 +1,16 @@ Datetime is represented in, so called, external TAI64 format. +=> http://cr.yp.to/libtai/tai64.html TAI64 TAI stands for Temps Atomique International, the current international real time standard. Unlike UTC, it takes leap seconds into account, making it monotonous. - -=> http://cr.yp.to/libtai/tai64.html TAI64 => http://cr.yp.to/proto/utctai.html TAI -You can convert TAI to UTC by subtracting number of leap seconds. +You can convert UTC to TAI by adding the number of leap seconds. -TAI64N format adds 32-bit big-endian number of nanoseconds -(up to 999999999) count. -TAI64NA adds another 32-big big-endian part of attoseconds count. +External TAI64 format is big-endian 64-bit number of TAI seconds passed +since 1970-01-01, plus 1<<62. TAI64N format adds 32-bit big-endian +number of nanoseconds (up to 999999999). TAI64NA adds another 32-big +big-endian part of attoseconds. Shortest form *must* be used: if number of nanoseconds equals to zero, then use TAI64 format. diff --git a/spec/encoding/index b/spec/encoding/index index dfa846a..3f512ee 100644 --- a/spec/encoding/index +++ b/spec/encoding/index @@ -23,9 +23,9 @@ dec | hex | bin | vlen | 019 | 13 | 00010011 | 16 | [encoding/FLOAT] 128 020 | 14 | 00010100 | 32 | [encoding/FLOAT] 256 ... | ... | ... | ... | ... -024 | 18 | 00011000 | 8 | [encoding/TAI64] -025 | 19 | 00011001 | 12 | [encoding/TAI64] N -026 | 1A | 00011010 | 16 | [encoding/TAI64] NA +024 | 18 | 00011000 | 8 | [encoding/TAI]64 +025 | 19 | 00011001 | 12 | [encoding/TAI]64N +026 | 1A | 00011010 | 16 | [encoding/TAI]64NA ... | ... | ... | ... | ... 075 | 4B | 01001011 | 15 | [encoding/MAGIC] ... | ... | ... | ... | ... diff --git a/spec/schema/cmds b/spec/schema/cmds index 259bd41..47a936d 100644 --- a/spec/schema/cmds +++ b/spec/schema/cmds @@ -5,42 +5,41 @@ First element of the command's list is a command name. Possible following elements are command-specific. Here is full list of structure validation commands, that should be -generated from higher level schema descriptions. Here comes human -readable command name, its string byte-code and possible arguments. +generated from higher level schema descriptions. -TAKE | "." | args: k +TAKE | [".", k] Take/choose the value of the "k" key in the map, if "k" is a string. If "k" is integer, then choose the k-th value in a list. If "k" equals to ".", then choose the element you are currently in. Command never fails, but key can be non-existent. -EXISTS | "E" +EXISTS | ["E"] Assure that chosen element exists. -!EXISTS | "!E" +!EXISTS | ["!E"] Assure that chosen element does not exist. -EACH | "*" +EACH | ["*"] Execute the next command against every element of the chosen (if it exists) list, or every value of the map. -TYPE | "T" | args: T0 [T1 ...] +TYPE | ["T", T0 [, T1, ...]] Check that chosen (if it exists) element's type is in (T0, T1 ...) - set. Possible types: BIN, BLOB, BOOL, HEXLET, INT, LIST, MAGIC, MAP, - NIL, STR, TAI. + set. Possible types: "BIN", "BLOB", "BOOL", "HEXLET", "INT", "LIST", + "MAGIC", "MAP", "NIL", "STR", "TAI". -GT | ">" | args: n +GT | [">", n] Check that chosen (if it exists) integer value is greater than "n". If chosen value is either list or map, then check their length. If the value is a string, then check its length. -LT | "<" | args: n +LT | ["<", n] Same as ">", but check that value is less than "n". -SCHEMA | "S" | args: s +SCHEMA | ["S", s] Check chosen (if it exists) element against schema named "s". -TIMEPREC | "TP" | args: p +TIMEPREC | ["TP", p] Check that chosen (if it exists) element, of time type, has value of maximal specified time precision. "p" is integer with following possible values: @@ -52,11 +51,11 @@ TIMEPREC | "TP" | args: p 15: only up to femtoseconds; 18: up to attoseconds; -UTC | "UTC" - Check that chosen (if it exists) element, of time type, +UTC | ["UTC"] + Check that chosen (if it exists) element, of TAI type, can be converted to UTC. -EQ | "=" | args: v +EQ | ["=", v] Check that chosen (if it exists) element's value equals to binary string "v". @@ -96,7 +95,7 @@ Corresponding schema can be: ["T", "STR"], ]} -Here is example with multiple schemas: +Here is an example with multiple schemas: latitude = -90..90 longitude = -180..180 @@ -113,7 +112,7 @@ Here is example with multiple schemas: ["<", 3], [".", "."], ["*"], - [".", "INT"], + ["T", "INT"], [".", 0], [">", -91], [".", 0], diff --git a/spec/schema/index b/spec/schema/index index 75e914f..610c99b 100644 --- a/spec/schema/index +++ b/spec/schema/index @@ -10,13 +10,14 @@ when you sanitise the structures. So suggestion is to specify those steps for some kind of very simple minimalistic validation machine, that interprets them, executing -validation commands against the provided data structures. That "machine" -should be simple enough to be able to implement it quickly and with sane -amount of code. Validation steps should be easily decodable and -conveniently parsed even in C-language. +validation commands ([schema/cmds]) against the provided data +structures. That "machine" should be simple enough to be able to +implement it quickly and with sane amount of code. Validation steps +should be easily decodable and conveniently parsed even in C-language. Let's use KEKS format itself for the serialised validation steps! And -generate them from higher level language/code, convenient for humans. +generate them from higher level language/code ([schema/tcl]), convenient +for humans. - cmds = KEKS-Encode([schema/tcl] -> [schema/cmds]) - validate(KEKS-Decode(cmds), KEKS-Decode(data)) + [schema/tcl] -> [schema/cmds] -> keks-encode() => schema + validate(schema, keks-decode(data)) diff --git a/spec/schema/tcl b/spec/schema/tcl index f02d15f..51f65eb 100644 --- a/spec/schema/tcl +++ b/spec/schema/tcl @@ -1,8 +1,8 @@ Validation commands are pretty low-level and are inconvenient to write by hand, at least because of huge quantity of TAKEs. tcl/schema.tcl utility gives ability to convert much more nicer schemas -written on Tcl language to the KEKS-encoded commands. We call that -Tcl-written schemas KEKS/Schema. +written on Tcl language to the KEKS-encoded commands. We call those +Tcl-written schemas "KEKS/Schema". Example with "our" structure ([schema/cmds]) can be written as: @@ -22,10 +22,10 @@ and [cm/pub/] as: << [schemas/pub-load.tcl] << [schemas/pub-sig-tbs.tcl] -schema.tcl calls "schemas {s0 cmds0 s1 cmds1 ...}" -commands to produce an encoded map with "cmds*" commands for -"s*" schemas. There is "field" command that helps creation of -commands related to the field. +schema.tcl calls "schemas {s0 cmds0 s1 cmds1 ...}" commands to produce +an encoded map with "cmds*" commands for "s*" schemas. + +"field" command helps creation of commands related to the field. Its first argument is either field's name in the map, or list's index or dot, meaning the self-structure itself. @@ -48,8 +48,8 @@ empty. "len=n" checks the exact length. "=v" checks that given element has specified string/binary value (use "len=" for integers). "prec=p" issues TIMEPREC command, but instead of specifying the raw -integer values, you choose one of: s, ms, us, ns, ps, fs, as. "utc" -issues UTC command. +integer values, you choose one of: s, ms, us, ns, ps, fs, as. +"utc" issues UTC command. "of s" argument issues checking of EACH element of the list or map against the specified schema, or against specified type if "s" is a diff --git a/tcl/schemas/signed.tcl b/tcl/schemas/signed.tcl index e81f2f9..8748d86 100644 --- a/tcl/schemas/signed.tcl +++ b/tcl/schemas/signed.tcl @@ -10,6 +10,7 @@ signed { load { {field . {map}} {field t {str} >0} + {# field v is optional, arbitrary type} } sig {