From 4f7885495bd6ea666cb444519090fb5f3ea46752 Mon Sep 17 00:00:00 2001 From: dev Date: Wed, 7 Oct 2026 18:28:44 +0200 Subject: [PATCH] v0.16: SpecVersion 0.16, the annex of the draft, and the tests in 76 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SpecVersion is 0.16, and so is the spec field of every file of testdata, the frozen security_cms.json and locator.json included. annex/recovery.md is §79 of the draft v0.16, with the key of words in 79.7. The draft lists the two new fixtures in 67, says in 79 what recovery_check.sh opens, and names in 76 the tests that now exist. The CHANGELOG has its section. Co-Authored-By: Claude Opus 5.5 --- CHANGELOG.md | 37 +++++++++++++++ annex/recovery.md | 45 +++++++++++++++---- annex_test.go | 2 +- capsule/cmsvectors_test.go | 4 +- internal/testkit/genfixtures/cmsvectors.go | 2 +- spec/DateKeys_Protocol_Specification_v0.16.md | 15 ++++--- spec/README.md | 4 +- testdata/fixtures/empty_payload.json | 2 +- testdata/fixtures/format2_empty_payload.json | 2 +- .../format2_time_and_key_portable.dkk.json | 2 +- .../format2_time_and_key_portable.json | 2 +- .../format2_time_and_key_recipients.dkk.json | 2 +- .../format2_time_and_key_recipients.json | 2 +- .../format2_time_and_key_sixteen.json | 2 +- testdata/fixtures/format2_time_only.json | 2 +- .../fixtures/format2_time_only_bloque256.json | 2 +- .../format2_time_only_extensions.json | 2 +- testdata/fixtures/format3_area_1024.json | 2 +- testdata/fixtures/format3_bloque256.json | 2 +- testdata/fixtures/format3_comment_only.json | 2 +- testdata/fixtures/format3_full_chunk.json | 2 +- testdata/fixtures/format3_note.json | 2 +- .../fixtures/format3_seal_unsupported.json | 2 +- testdata/fixtures/format3_sealed.json | 2 +- testdata/fixtures/format3_security_v2.json | 2 +- .../format3_signature_unsupported.json | 2 +- testdata/fixtures/format3_signed.json | 2 +- testdata/fixtures/format3_signed_cms.json | 2 +- testdata/fixtures/format3_single.json | 2 +- .../format3_time_and_key_portable.dkk.json | 2 +- .../format3_time_and_key_portable.json | 2 +- .../fixtures/format3_time_and_key_words.json | 2 +- testdata/fixtures/format3_tree.json | 2 +- testdata/fixtures/format3_unsigned.json | 2 +- .../fixtures/time_and_key_portable.dkk.json | 2 +- testdata/fixtures/time_and_key_portable.json | 2 +- .../time_and_key_portable_extension.dkk.json | 2 +- .../fixtures/time_and_key_recipients.dkk.json | 2 +- .../fixtures/time_and_key_recipients.json | 2 +- testdata/fixtures/time_only.json | 2 +- testdata/fixtures/time_only_extensions.json | 2 +- testdata/vectors/cbor.json | 2 +- testdata/vectors/dk1.json | 2 +- testdata/vectors/ed25519_strict.json | 2 +- testdata/vectors/head_schema.json | 2 +- testdata/vectors/inspect_differential.json | 2 +- testdata/vectors/locator.json | 2 +- testdata/vectors/mutations.json | 2 +- testdata/vectors/note.json | 2 +- testdata/vectors/padding.json | 2 +- testdata/vectors/path_fold.json | 2 +- testdata/vectors/paths.json | 2 +- testdata/vectors/profile_quicknet.json | 2 +- testdata/vectors/quicknet_rounds.json | 2 +- testdata/vectors/release.json | 2 +- testdata/vectors/resolved_ip.json | 2 +- testdata/vectors/security.json | 2 +- testdata/vectors/security_cms.json | 4 +- testdata/vectors/tlock_ibe.json | 2 +- testdata/vectors/tlock_steps.json | 2 +- testdata/vectors/wordkey.json | 2 +- version.go | 6 +-- 62 files changed, 146 insertions(+), 79 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 85425f7..de41280 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,43 @@ All notable changes to this module are documented here. The project follows semantic versioning; `v0.x` versions make no API stability promise. +## Unreleased — specification v0.16 + +Implements the draft of the DateKeys Protocol Specification v0.16, of 7 +October 2026, on the branch `v0.16`: the review of v0.15 by Astra, with the +recommendation of each decision. It changes no format; it changes the +verdict of a seal without accuracy and the reading of drand's JSON. +`SpecVersion` is 0.16, and so is the `spec` field of every file of +`testdata`, also of the frozen `security_cms.json` and `locator.json`. + +- **A seal without accuracy** (§29.7, §29.11). `cms.Token` gains + `HasAccuracy`, `Policy` and `BTSP`. A valid seal is S4 only with accuracy + and t plus the accuracy before the round time; otherwise S5, whose text + gives its reason, `capsule.SealReason`: sealed after or too close, no + accuracy under the BTSP policy of ETSI EN 319 421, or no accuracy. + `Detail.SealReason` and `SignerLine.Reason` carry it, and the line of a + signer of F6 whose seal does not prove it says «sin acreditar que fuera + antes de la fecha de apertura» and the reason. `EncryptFiles` returns the + verdicts of the area it wrote in `Result.Security`, so that a writer warns + of a seal without accuracy (§62.1 rule 19). `security_cms.json` is made + again: 143 cases with `seal_reason`. +- **drand's JSON, strict** (§47.1). `provider.ParseDrandJSON`, the one + reader of it, for a release in hand and for the answers of the relays: + no object repeats a name, names compared exactly once their escapes are + decoded, no lone surrogate, and round a number without sign, fraction or + exponent from 1 to 2^53 - 1. `encoding/json` kept the last of two repeated + names and matched `ROUND` to `round`. `release.json` gains 25 cases. +- **The key of words in the annex** (§79, 79.7). `scripts/recovery` opens + with `-words FILE`, with the normalization without tables of the annex or, + with `-unicodedata FILE`, the full one from `UnicodeData.txt` of Unicode + 18.0.0, checked by its SHA-256. The fixture `format3_time_and_key_words` + opens with the text of the vector of the annex, kept in `words_text`. +- **A full last chunk** (79.5). The fixture `format3_full_chunk`, whose + `PAYLOAD_AGE` is one full STREAM chunk; `scripts/recovery_check.sh` opens + it, and the one with words. +- **The annex.** `annex/recovery.md` is §79 of the draft v0.16, with the + key of words in 79.7. + ## Unreleased — specification v0.15 Implements the DateKeys Protocol Specification v0.15, which its author diff --git a/annex/recovery.md b/annex/recovery.md index f28e3ec..30d5d19 100644 --- a/annex/recovery.md +++ b/annex/recovery.md @@ -1,6 +1,6 @@ # Cómo abrir una cápsula DateKeys sin software de DateKeys -Este texto acompaña a una cápsula del tiempo de DateKeys, un fichero `.dkc`: dice cómo abrirla, llegada su fecha, sin ningún software de DateKeys, por si ya no existe. Es el anexo informativo §79 de la especificación del protocolo DateKeys v0.15, cuyo texto tiene el SHA-256 45105e693be4187af4dd30f4d254402612587b6427c746f5d29f07a541c1e3f3. Es el mismo para toda cápsula: no lleva ningún dato de esta. Su licencia es CC BY-ND 4.0, Atribución-SinDerivadas 4.0 Internacional (https://creativecommons.org/licenses/by-nd/4.0/deed.es): se puede copiar y compartir sin cambios, citando su origen. +Este texto acompaña a una cápsula del tiempo de DateKeys, un fichero `.dkc`: dice cómo abrirla, llegada su fecha, sin ningún software de DateKeys, por si ya no existe. Es el anexo informativo §79 de la especificación del protocolo DateKeys v0.16, cuyo texto tiene el SHA-256 c13b598fa688f6cd74b7223f34896c30ff091e05df11dad8a02f1ea6bcbb6fa7. Es el mismo para toda cápsula: no lleva ningún dato de esta. Su licencia es CC BY-ND 4.0, Atribución-SinDerivadas 4.0 Internacional (https://creativecommons.org/licenses/by-nd/4.0/deed.es): se puede copiar y compartir sin cambios, citando su origen. ## 79. Anexo informativo: recuperación sin software DateKeys @@ -13,11 +13,12 @@ Hace falta: - el `.dkc`; - el release de su ronda, de cualquier fuente: un relay de drand, un archivo de releases, un servicio de caché (§50) o cualquier copia. No hace falta confiar en quien lo da: se verifica con la clave pública de 79.1 (79.3); - en `time_and_key`, una credencial: la `.dkk`, la identity `age` de un recipient o las palabras de una llave de palabras (§38.1); -- una librería de BLS12-381 con pairing y con el hash a G1 de RFC 9380, SHA-256, HMAC-SHA256, HKDF-SHA256 (RFC 5869), ChaCha20-Poly1305 (RFC 8439), un decodificador de CBOR y la herramienta `age` (§77) o una librería compatible. +- una librería de BLS12-381 con pairing y con el hash a G1 de RFC 9380, SHA-256, HMAC-SHA256, HKDF-SHA256 (RFC 5869), ChaCha20-Poly1305 (RFC 8439), un decodificador de CBOR y la herramienta `age` (§77) o una librería compatible; +- con una llave de palabras, PBKDF2-HMAC-SHA256 (RFC 8018) y, si las palabras llevan otras letras que las de 79.7, `UnicodeData.txt` de Unicode 18.0.0. No sirven las herramientas de drand: `tle` pide el release a la red y no acepta uno dado, y `age` no acepta una file key, que es lo que da el stanza tlock (79.4). Por eso este anexo describe esos dos pasos enteros (79.4 y 79.5). -La implementación de referencia lo sigue en `scripts/recovery`, un programa que no importa ningún paquete de DateKeys, tlock ni drand: solo la librería estándar de Go, `golang.org/x/crypto`, `filippo.io/age` y la librería BLS12-381 `drand/kyber-bls12381`. `scripts/recovery_check.sh` abre con él una cápsula `time_only` y otra `time_and_key` de los fixtures oficiales. +La implementación de referencia lo sigue en `scripts/recovery`, un programa que no importa ningún paquete de DateKeys, tlock ni drand: solo la librería estándar de Go, `golang.org/x/crypto`, `filippo.io/age` y la librería BLS12-381 `drand/kyber-bls12381`, con las interfaces de `drand/kyber`. `scripts/recovery_check.sh` abre con él una cápsula `time_only`, otra `time_and_key` con su `.dkk`, otra con una llave de palabras y otra cuyo `PAYLOAD_AGE` acaba en un bloque completo, de los fixtures oficiales. Las palabras se le dan en un fichero de texto, y `UnicodeData.txt`, si hace falta, en otro. ### 79.1 Parámetros de Quicknet @@ -108,7 +109,7 @@ Es la especificación `age` v1 de C2SP (§77), resumida. Un fichero `age` es una ```text age-encryption.org/v1 -> - + --- ``` @@ -117,20 +118,48 @@ Con la file key FK, de 16 bytes: 1. La cabecera: clave_mac = HKDF-SHA256(ikm = FK, salt = vacío, info = `header`), 32 bytes. El MAC es HMAC-SHA256(clave_mac, la cabecera desde `age-encryption.org/v1` hasta `---` inclusive, sin el espacio que lo sigue). Si no coincide con el de la línea `---`, la file key o la cabecera son otras. 2. El payload empieza tras el salto de línea del MAC por un nonce de 16 bytes. clave = HKDF-SHA256(ikm = FK, salt = nonce, info = `payload`), 32 bytes. -3. Lo demás son bloques de ChaCha20-Poly1305 de 65 536 bytes de texto, 65 552 cifrados, el último más corto. El nonce de 12 bytes del bloque n, desde 0, es n en 11 bytes big-endian seguido de 0x01 en el último bloque y de 0x00 en los demás. No hay datos asociados. El último bloque solo puede estar vacío si es el único, y nada sigue al último bloque. +3. Lo demás son bloques de ChaCha20-Poly1305 de 65 536 bytes de texto, 65 552 cifrados; el último puede ser más corto, o estar completo: un plaintext de 65 536 bytes es un solo bloque, completo y marcado como último. El nonce de 12 bytes del bloque n, desde 0, es n en 11 bytes big-endian seguido de 0x01 en el último bloque y de 0x00 en los demás. No hay datos asociados. El último bloque solo puede estar vacío si es el único, y nada sigue al último bloque. ### 79.6 Las capas siguientes El plaintext de `SEALED_CONTROL` es: - en `time_only`, `CONTROL_CBOR`; -- en `time_and_key`, otro fichero `age`, `INNER_ACCESS_AGE`, con stanzas X25519, uno por credencial y señuelos hasta 16 en los formatos 2 y 3. Se abre con `age -d -i clave.txt`, con la identity de la credencial en `clave.txt`. La de una `.dkk` es su `access_material`. La `.dkk` empieza por 12 bytes, `DKK1`, `01`, `00`, `00 00` y `BODY_LEN` en 4 bytes big-endian (§40), y le sigue un mapa CBOR cuya clave 5 es ese `access_material`, 32 bytes (§41), y cuya clave 3 es el `capsule_id` de su cápsula. Una llave de palabras da la identity con §38.1. +- en `time_and_key`, otro fichero `age`, `INNER_ACCESS_AGE`, con stanzas X25519, uno por credencial y señuelos hasta 16 en los formatos 2 y 3. Se abre con `age -d -i clave.txt`, con la identity de la credencial en `clave.txt`. La de una `.dkk` es su `access_material`. La `.dkk` empieza por 12 bytes, `DKK1`, `01`, `00`, `00 00` y `BODY_LEN` en 4 bytes big-endian (§40), y le sigue un mapa CBOR cuya clave 5 es ese `access_material`, 32 bytes (§41), y cuya clave 3 es el `capsule_id` de su cápsula. Una llave de palabras da la identity con 79.7. `CONTROL_CBOR` es un mapa CBOR (§31). Su clave 3 es `I_PAYLOAD`, otra identity X25519 de 32 bytes, y en los formatos 2 y 3 su clave 6 es L, una cadena de 8 bytes con un entero big-endian, no un entero CBOR. Con `I_PAYLOAD`, `age -d -i payload.txt` abre `PAYLOAD_AGE`. Una identity X25519 de 32 bytes se escribe para `age` en Bech32 (BIP 173, §77), no Bech32m: el prefijo `age-secret-key-`, los 32 bytes reagrupados de 8 en 5 bits con ceros al final, que dan 52 caracteres, y la suma de comprobación de BIP 173, calculada con el prefijo en minúsculas. Después, todo en mayúsculas: `AGE-SECRET-KEY-1…`. -### 79.7 El contenido +### 79.7 La llave de palabras + +Unas palabras dan la identity X25519 de una credencial (§38.1): + +```text +P = las palabras normalizadas, en UTF-8, separadas por un espacio (0x20) +S = "DateKeys llave de palabras v2|" || chain_hash || "|" || ronda || "|" || capsule_id +id = PBKDF2-HMAC-SHA256(P, S, 600000 iteraciones, 32 bytes) ; RFC 8018 +``` + +En S, `chain_hash` es el de 79.1 y `capsule_id` el de 79.2, en hexadecimal en minúsculas, y la ronda, la de la DateKey en decimal, sin ceros a la izquierda. `id` es la identity, que se escribe para `age` como dice 79.6. + +**Normalización sin tablas.** Si el texto solo lleva caracteres ASCII imprimibles (U+0021 a U+007E), los espacios U+0009 a U+000D y U+0020, las letras á, é, í, ó, ú, ü y ñ y sus mayúsculas, y marcas de U+0300 a U+036F, que es lo que da cualquier palabra de las listas de DateKeys, las palabras normalizadas salen así: + +1. se quitan las marcas de U+0300 a U+036F; +2. á y Á pasan a a; é y É, a e; í e Í, a i; ó y Ó, a o; ú, ü, Ú y Ü, a u; ñ y Ñ, a n; +3. A a Z pasan a a a z; +4. se parte el texto por los espacios, sin palabras vacías. + +El resto se queda: la puntuación y las cifras cuentan, y «perro,» no es «perro». + +**Normalización completa.** Para cualquier otro texto, en este orden: la NFD de UAX #15, con la descomposición canónica de cada punto de código (el campo 5 de `UnicodeData.txt`, sin las que llevan una etiqueta entre `<` y `>`, aplicada hasta el final), la de las sílabas Hangul, que se calcula, y la reordenación canónica por la clase de combinación (campo 3); se quitan los puntos de código de U+0300 a U+036F; cada punto de código pasa a su minúscula simple (campo 13), si la tiene; y se parte por los espacios U+0009 a U+000D, U+0020, U+0085, U+00A0, U+1680, U+2000 a U+200A, U+2028, U+2029, U+202F, U+205F y U+3000, sin palabras vacías. Sirve cualquier copia de `UnicodeData.txt` de Unicode 18.0.0 cuyo SHA-256 sea `0736451de439ae7baf1425136617da495e09ee5afbe6e394374db7009ea08950`; unicode.org la publica en `https://www.unicode.org/Public/18.0.0/ucd/UnicodeData.txt`. Otra versión de Unicode puede dar otras palabras: una que asigne un punto de código nuevo, o que cambie una descomposición o una minúscula. + +Vectores, con el chain hash de Quicknet, la ronda 1000 y `capsule_id` = `000102030405060708090a0b0c0d0e0f`: + +- «perro luna casa verde tren mar» da `id` = `fceec4d8ca8de86c85a1f26ed49f82a2b38431bd0ce36db995ae7dfd49b96e41`; +- el texto «Ñandú», dos espacios, «PINGÜINO», un tabulador (U+0009) y «camión árbol Éter ola» da «nandu pinguino camion arbol eter ola» e `id` = `273295d29370126a3be50b743132718d3cd9137fb3bb4cb20aa23163d2e19bb7`, lo mismo que ese texto con las tildes, la diéresis y la tilde de la eñe escritas como marcas sueltas detrás de su letra (U+0301, U+0308 y U+0303). + +### 79.8 El contenido El plaintext de `PAYLOAD_AGE` es: @@ -153,6 +182,6 @@ La clave 5 del head es la lista de ficheros (§29.4). Cada uno es un mapa: Sus bytes van de start a end, sin incluir end, contados desde el origen. Las claves 3 y 4 del head son el comentario y el autor declarado: textos del creador que no prueban nada (§29.7). Una ruta que saldría de la carpeta de destino no se escribe. -### 79.8 Lo que el anexo no comprueba +### 79.9 Lo que el anexo no comprueba Este anexo comprueba lo que decide que el resultado es el correcto: la firma del release, r·G2 == U, los MAC de cada fichero `age` y el SHA-256 de cada fichero. No comprueba, entre otras cosas, la codificación canónica de cada objeto, `header_binding` (§26), los 16 stanzas de `INNER_ACCESS_AGE` (§39), los ceros del relleno (§29.1) ni las reglas de las rutas (§29.5). Una cápsula que el lector de §63 rechazaría puede abrirse siguiendo este anexo; su contenido es el que sellaron las MAC de `age`, pero no tiene la garantía de un lector conforme. diff --git a/annex_test.go b/annex_test.go index f963126..b2311de 100644 --- a/annex_test.go +++ b/annex_test.go @@ -44,7 +44,7 @@ func TestRecoveryAnnex(t *testing.T) { if RecoveryAnnex != want { t.Fatalf("annex/recovery.md is not §79 of the specification %s: write it again with DATEKEYS_WRITE_ANNEX=1 go test -run TestRecoveryAnnex .", SpecVersion) } - for _, s := range []string{"## 79. Anexo informativo: recuperación sin software DateKeys", "### 79.8 Lo que el anexo no comprueba", "DateKeys v" + SpecVersion + ","} { + for _, s := range []string{"## 79. Anexo informativo: recuperación sin software DateKeys", "### 79.7 La llave de palabras", "### 79.9 Lo que el anexo no comprueba", "DateKeys v" + SpecVersion + ","} { if !strings.Contains(RecoveryAnnex, s) { t.Errorf("the annex lacks %q", s) } diff --git a/capsule/cmsvectors_test.go b/capsule/cmsvectors_test.go index b9640d9..d002bf2 100644 --- a/capsule/cmsvectors_test.go +++ b/capsule/cmsvectors_test.go @@ -17,13 +17,13 @@ import ( // testdata/vectors/security_cms.json is frozen: each case is evaluated again, // with its context, and must give its verdicts, the result of each signer, // the authority and the time of a valid seal, and its lines, byte for byte -// (spec v0.12 §29.7, §29.10, §29.11). +// (spec v0.16 §29.7, §29.10, §29.11). func TestCMSVectors(t *testing.T) { var f testkit.CMSVectorFile if err := testkit.ReadJSON(filepath.Join("..", "testdata", "vectors", "security_cms.json"), &f); err != nil { t.Fatal(err) } - if f.Spec != datekeys.SpecVersion || !strings.Contains(f.Description, "v0.12") || len(f.Cases) < 100 { + if f.Spec != datekeys.SpecVersion || !strings.Contains(f.Description, "v0.16") || len(f.Cases) < 140 { t.Fatalf("spec %q, %d cases: %s", f.Spec, len(f.Cases), f.Description) } seen := map[string]bool{} diff --git a/internal/testkit/genfixtures/cmsvectors.go b/internal/testkit/genfixtures/cmsvectors.go index 88a392c..eda9b67 100644 --- a/internal/testkit/genfixtures/cmsvectors.go +++ b/internal/testkit/genfixtures/cmsvectors.go @@ -372,7 +372,7 @@ func securityCMSVectors() (any, error) { g.file = testkit.CMSVectorFile{ Spec: cmsVectorSpec, Description: "SECURITY_CBOR with an author signature of alg 2 or a time seal of seal_type 2, the context of its capsule, " + - "and the verdicts, the result of each signer and the lines of spec v0.12 29.7, 29.10 and 29.11. " + + "and the verdicts, the result of each signer and the lines of spec v0.16 29.7, 29.10 and 29.11. " + "Certificates and tokens are made once with test keys and the file is frozen. See testdata/README.md.", } g.signatureCases() diff --git a/spec/DateKeys_Protocol_Specification_v0.16.md b/spec/DateKeys_Protocol_Specification_v0.16.md index a527b45..4b1ce8d 100644 --- a/spec/DateKeys_Protocol_Specification_v0.16.md +++ b/spec/DateKeys_Protocol_Specification_v0.16.md @@ -3518,9 +3518,10 @@ Los de formato 3 cubren, como mínimo: - el área de 32 KiB sin firma; - una firma de `alg` 1 (F4, o F3 con su clave guardada); - esa firma con un sello de `seal_type` 2 (S4), y un fichero cuya mtime es posterior al sello, que se muestra como incoherencia; -- una firma de `alg` 2 de dos firmantes, con ECDSA P-256 y con RSA de 2048 bits, cada uno con su sello CAdES-T (F6). +- una firma de `alg` 2 de dos firmantes, con ECDSA P-256 y con RSA de 2048 bits, cada uno con su sello CAdES-T (F6); +- desde la v0.16, una cápsula `time_and_key` cuya única credencial es una llave de palabras, la del segundo vector de 79.7, y una cuyo `PAYLOAD_AGE` acaba en un bloque completo de 65 536 bytes de texto (79.5). -Son `format3_single.dkc`, `format3_tree.dkc`, `format3_comment_only.dkc`, `format3_bloque256.dkc`, `format3_time_and_key_portable.dkc` con su `.dkk`, `format3_area_1024.dkc`, `format3_security_v2.dkc`, `format3_signature_unsupported.dkc`, `format3_seal_unsupported.dkc`, `format3_unsigned.dkc`, `format3_signed.dkc`, `format3_sealed.dkc` y `format3_signed_cms.dkc`. Los vectores de rutas y del head van en `testdata/vectors/paths.json`, `head_schema.json` y `path_fold.json`; los de `security`, en `security.json` y `security_cms.json`; los de Ed25519 estricto, en `ed25519_strict.json`; los de la nota, en `note.json`; y los de `datekeys.capsule`, en `locator.json`. +Son `format3_single.dkc`, `format3_tree.dkc`, `format3_comment_only.dkc`, `format3_bloque256.dkc`, `format3_time_and_key_portable.dkc` con su `.dkk`, `format3_area_1024.dkc`, `format3_security_v2.dkc`, `format3_signature_unsupported.dkc`, `format3_seal_unsupported.dkc`, `format3_unsigned.dkc`, `format3_signed.dkc`, `format3_sealed.dkc`, `format3_signed_cms.dkc`, `format3_time_and_key_words.dkc`, con el texto de sus palabras en su registro, y `format3_full_chunk.dkc`. Los vectores de rutas y del head van en `testdata/vectors/paths.json`, `head_schema.json` y `path_fold.json`; los de `security`, en `security.json` y `security_cms.json`; los de Ed25519 estricto, en `ed25519_strict.json`; los de la nota, en `note.json`; y los de `datekeys.capsule`, en `locator.json`. Los vectores de rutas y del head cubren, como mínimo: U+00A0 y U+3000 al principio y al final de un segmento, con el veredicto que dan las tablas, que para U+3000 es el rechazo por R6c; best-fit; alias 8.3, «~1» incluido; Cn; `["b/..", "a"]`; U+206A a U+206F, las etiquetas y otros ignorables fuera de la lista blanca; «.» seguido de ZWJ y un segmento hecho solo de ZWJ; 127 veces «ΐ»; U+F03A; «.datekeys-x» en el primer nivel y en otro; el orden de U+FF5E y U+1F600; «ab» con y sin ZWNJ; «¿», «§» y «♥», que se aceptan; VS16 tras un emoji que lo admite, que se acepta, y tras «a», que no; ZWJ al principio, al final y dos seguidos; y la bandera arcoíris, que se acepta, y la de Escocia, que no. Los de `head_schema.json` cubren además un comentario con etiquetas y con selectores de variante sueltos. Los de `security.json` cubren: el mapa exterior con la clave 2 que no es una cadena de bytes, con una clave 4 o con un byte de más dentro de `SECURITY_LEN` (X); `alg` 0 o una clave vacía dentro de la clave 2 (F1, con el sello intacto); y un `seal` que incumple su schema con un `seal_type` desconocido (S2). @@ -4481,18 +4482,18 @@ La v0.16 corrige lo que encontró la revisión de Astra de la v0.15, el 7 de oct - Cambio: S4 exige que el token lleve `accuracy` y que t + precisión < round_time; sin `accuracy`, un sello válido es S5. S5 pasa a «No acredita que se sellara antes de la fecha de apertura: ‹motivo›.», con tres motivos, uno de ellos para un token de la política BTSP de ETSI, que exige `accuracy`. En F6, la línea de un firmante cuyo sello no la lleva no dice «antes de la fecha de apertura». El escritor avisa de un sello sin `accuracy`. - Motivo: con «0 si no viene», un sello sin `accuracy` acreditaba anterioridad con una precisión que nadie había declarado. RFC 3161 (§2.4.2) deja entonces la precisión a la política de la TSA, que el lector no conoce, y ningún margen fijo vale para toda TSA. El texto de S5, «Sellado después de la fecha de apertura», era además falso para un sello anterior a round_time cuya precisión llegaba a ella (revisión de Astra, punto 1). - Caso: «seal: before the round time: S4» de `security_cms.json`, un token sin `accuracy` sellado el 30 de septiembre de 2026 en una cápsula de 2030, daba S4 con una precisión de 0; ahora da S5, «el sello no dice su precisión». `format3_sealed` lleva una `accuracy` de 1 s y no cambia. - - Pruebas previstas: `security_cms.json`, con los casos sin `accuracy` en S5 y casos nuevos: un token sin `accuracy` de la política BTSP, uno con una `accuracy` de 0 segundos y un firmante de `alg` 2 cuyo sello no la lleva. + - Pruebas: `security_cms.json`, hecho de nuevo, 143 casos: los sellos de los casos que prueban otra cosa llevan una `accuracy` de 1 s, y hay casos nuevos sin `accuracy`, años antes de la fecha y después, de la política BTSP con ella y sin ella, con una `accuracy` de 0 segundos y vacía, y de un firmante de `alg` 2 cuyo sello no la lleva, también BTSP, cada uno con su `seal_reason`; `capsule.TestEvaluateSeal` y `TestEncryptFilesCMSAndSeal`, que comprueba que el escritor devuelve el veredicto S5 de un sello sin `accuracy` para avisar de él. - Descartado: un margen fijo para el sello sin `accuracy`, y una tabla de políticas con su precisión, que queda como trabajo futuro (§74). 2. **La llave de palabras en el anexo** (§79, 79.6, 79.7). - Cambio: el anexo trae la derivación de §38.1 y su normalización: una receta sin tablas, exacta para el texto que dan las listas de DateKeys (ASCII, á, é, í, ó, ú, ü, ñ y sus mayúsculas, y las marcas de U+0300 a U+036F), y para cualquier otro texto, la normalización completa con `UnicodeData.txt` de Unicode 18.0.0, que nombra por su SHA-256; y dos vectores. Lo que hace falta incluye PBKDF2-HMAC-SHA256. - Motivo: el anexo remitía a §38.1, que no contiene, y §38.1 a las tablas de §29.5.1: sin software de DateKeys no se podía abrir una cápsula de una llave de palabras (revisión de Astra, punto 2). - Caso: el texto «Ñandú», dos espacios, «PINGÜINO», un tabulador y «camión árbol Éter ola» da `id` = `273295d29370126a3be50b743132718d3cd9137fb3bb4cb20aa23163d2e19bb7` en la cápsula del vector de §38.1, lo mismo que «nandu pinguino camion arbol eter ola» y que el texto con sus marcas sueltas. - - Pruebas previstas: `scripts/recovery` con las palabras, también sobre el texto del vector, y su comprobación sobre un fixture con una llave de palabras; `wordkey.json`, con el vector nuevo. + - Pruebas: `scripts/recovery` abre con `-words` y, si hace falta, `-unicodedata`; `TestAnnexWordVectors` comprueba los dos vectores, `TestNormalizeVectors`, que las dos normalizaciones dan las palabras de cada caso de `wordkey.json`, y `TestRecoverWithWords`, el fixture nuevo `format3_time_and_key_words`, que `scripts/recovery_check.sh` abre también; `wordkey.json` gana el texto del vector, también con sus marcas sueltas, y su llave. 3. **El último bloque de `age`** (79.5). - Cambio: «el último más corto» pasa a «el último puede ser más corto, o estar completo»: un plaintext de 65 536 bytes es un solo bloque completo, marcado como último. La última línea del cuerpo de un stanza puede estar vacía. - Motivo: lo dice la especificación `age` de C2SP (§77). Quien siguiera el anexo rechazaría un fichero `age` cuyo plaintext mide un múltiplo de 65 536 bytes (revisión de Astra, punto 3). `scripts/recovery` ya lo hacía bien. - Caso: ningún fixture oficial tiene un `PAYLOAD_AGE` que acabe en un bloque completo, así que `scripts/recovery_check.sh` no lo comprobaba. - - Pruebas previstas: un fixture nuevo cuyo `PAYLOAD_AGE` acaba en un bloque completo, abierto por `scripts/recovery_check.sh`. + - Pruebas: `format3_full_chunk`, cuyo BODY y cuya P miden 65 536 bytes, abierto por `scripts/recovery_check.sh` y por `TestRecoverFixtures`. 4. **Las evidencias de la firma con certificado** (§29.10, §62.1 reglas 21 y 22). - Cambio: la regla 21 deja de recomendar podar la cadena a un certificado por firmante: el escritor SHOULD guardar las cadenas de los firmantes y de las autoridades de sellado sin sus raíces, y las respuestas OCSP; si no caben, MUST decir qué deja fuera y SHOULD ofrecer exportarlo. La regla 22 pide lo mismo para la cadena de la autoridad de un sello de la clave 3. §29.10 promete solo lo que la cápsula conserva. - Motivo: un validador de largo plazo necesita los certificados intermedios y la revocación del momento de la firma, y años después quizá no pueda pedirlos. Podar la cadena los perdía en silencio, y §29.10 prometía la respuesta OCSP aunque no cupiera (revisión de Astra, punto 4). @@ -4502,7 +4503,7 @@ La v0.16 corrige lo que encontró la revisión de Astra de la v0.15, el 7 de oct - Cambio: ningún objeto del JSON repite un nombre; los nombres se comparan exactos, tras decodificar sus escapes; un sustituto suelto hace el JSON mal formado; y `round` es un número sin signo, sin fracción ni exponente, de 1 a 2⁵³ − 1. Lo demás es `ERR_RELEASE_INVALID`. - Motivo: «`round` es un número entero» dejaba la lectura a cada librería de JSON. Las tres implementaciones leen como `encoding/json` de Go, que se queda con el último de dos nombres repetidos y no distingue mayúsculas, y otro lector con `JSON.parse` o con el `json` de Python distingue mayúsculas: la misma entrada daba dos rondas (revisión de Astra, punto 5). - Caso: `{"round":1000,"ROUND":1001,"signature":…}`, con la firma de la ronda 1000, da `ERR_ROUND_MISMATCH` con `time_only.dkc` en las tres implementaciones, que leen la ronda 1001, y abre la cápsula con un lector que distingue mayúsculas; y `{"round":1000,"round":1001,…}` da la ronda 1001 en las tres, y la 1000 en un lector que se queda con el primero. - - Pruebas previstas: `release.json`, con los casos nuevos de §64. + - Pruebas: `release.json`, con 25 casos nuevos del JSON de drand (§64), y `provider.TestStrictJSON`, con los bordes de la gramática de RFC 8259 y UTF-8 inválido. `provider/drand` lee las respuestas de los relays con el mismo lector. 6. **Erratas** (§1, §74, §77, §79). - Cambio: §1 junta en una línea la Release API, la Release Cache y el objeto release; la lista de provisionales de §74 acaba en punto; §77 añade ETSI EN 319 421 y 319 422; §79 nombra las interfaces de `drand/kyber`, que `scripts/recovery` también importa; y la cabecera ya no dice «prevista» de la implementación de referencia. - Pruebas previstas: ninguna. @@ -4640,7 +4641,7 @@ Hace falta: No sirven las herramientas de drand: `tle` pide el release a la red y no acepta uno dado, y `age` no acepta una file key, que es lo que da el stanza tlock (79.4). Por eso este anexo describe esos dos pasos enteros (79.4 y 79.5). -La implementación de referencia lo sigue en `scripts/recovery`, un programa que no importa ningún paquete de DateKeys, tlock ni drand: solo la librería estándar de Go, `golang.org/x/crypto`, `filippo.io/age` y la librería BLS12-381 `drand/kyber-bls12381`, con las interfaces de `drand/kyber`. `scripts/recovery_check.sh` abre con él una cápsula `time_only`, otra `time_and_key` y otra con una llave de palabras de los fixtures oficiales. +La implementación de referencia lo sigue en `scripts/recovery`, un programa que no importa ningún paquete de DateKeys, tlock ni drand: solo la librería estándar de Go, `golang.org/x/crypto`, `filippo.io/age` y la librería BLS12-381 `drand/kyber-bls12381`, con las interfaces de `drand/kyber`. `scripts/recovery_check.sh` abre con él una cápsula `time_only`, otra `time_and_key` con su `.dkk`, otra con una llave de palabras y otra cuyo `PAYLOAD_AGE` acaba en un bloque completo, de los fixtures oficiales. Las palabras se le dan en un fichero de texto, y `UnicodeData.txt`, si hace falta, en otro. ### 79.1 Parámetros de Quicknet diff --git a/spec/README.md b/spec/README.md index 81ce98d..6494b22 100644 --- a/spec/README.md +++ b/spec/README.md @@ -67,7 +67,7 @@ the tlock IBE. Its §76 records each change with its case. - `DateKeys_Protocol_Specification_v0.15.md`: frozen copy of the normative draft v0.15 (7 October 2026), approved by its author on that date and - tagged `spec-v0.15`; this module implements it. SHA-256: + tagged `spec-v0.15`. SHA-256: `45105e693be4187af4dd30f4d254402612587b6427c746f5d29f07a541c1e3f3`. It covers the long-term recovery of capsules: the release object, the release of a @@ -81,7 +81,7 @@ valid release in hand opens a capsule with a clock behind its round time. Its §76 records each change with its case. - `DateKeys_Protocol_Specification_v0.16.md`: the draft v0.16 (7 October - 2026), not yet approved, on the branch `v0.16`. It fixes what Astra's + 2026), not yet approved, on the branch `v0.16`; this module implements it. It fixes what Astra's review of v0.15 found: a valid seal without `accuracy` no longer proves that it came before the unlock date (S5, with its reason); the recovery annex derives a key of words without DateKeys software, with a recipe diff --git a/testdata/fixtures/empty_payload.json b/testdata/fixtures/empty_payload.json index 7407172..a39e630 100644 --- a/testdata/fixtures/empty_payload.json +++ b/testdata/fixtures/empty_payload.json @@ -1,6 +1,6 @@ { "description": "time_only capsule with an empty payload", - "spec": "0.15", + "spec": "0.16", "format": 1, "file": "empty_payload.dkc", "sha256": "871e9bf05b52bbae17f3adfbbf97b46e7f0e53aa8f57bcaa506e43f36f53a9d4", diff --git a/testdata/fixtures/format2_empty_payload.json b/testdata/fixtures/format2_empty_payload.json index 5ebc622..45f6f20 100644 --- a/testdata/fixtures/format2_empty_payload.json +++ b/testdata/fixtures/format2_empty_payload.json @@ -1,6 +1,6 @@ { "description": "format 2 time_only capsule with an empty content: L = 0, P = 256", - "spec": "0.15", + "spec": "0.16", "format": 2, "file": "format2_empty_payload.dkc", "sha256": "7aea2b5aa48b1a46053716f733d50fab9cd0b80b1be67631bcc06c5bb765dc21", diff --git a/testdata/fixtures/format2_time_and_key_portable.dkk.json b/testdata/fixtures/format2_time_and_key_portable.dkk.json index 65e01e3..3f6cbf5 100644 --- a/testdata/fixtures/format2_time_and_key_portable.dkk.json +++ b/testdata/fixtures/format2_time_and_key_portable.dkk.json @@ -1,6 +1,6 @@ { "description": "portable X25519 .dkk of format2_time_and_key_portable.dkc", - "spec": "0.15", + "spec": "0.16", "file": "format2_time_and_key_portable.dkk", "sha256": "095b7bc516a22bf0c2366f0af3cd48bfe857a2354d6e2a9b285278b95e450fe0", "credential_id": "e3c7be83cbf1fbd6b115c96411b3bd01", diff --git a/testdata/fixtures/format2_time_and_key_portable.json b/testdata/fixtures/format2_time_and_key_portable.json index 1b201a5..4855ee4 100644 --- a/testdata/fixtures/format2_time_and_key_portable.json +++ b/testdata/fixtures/format2_time_and_key_portable.json @@ -1,6 +1,6 @@ { "description": "format 2 time_and_key capsule with one credential, a portable .dkk, and 15 dummies", - "spec": "0.15", + "spec": "0.16", "format": 2, "file": "format2_time_and_key_portable.dkc", "sha256": "600892659fe4890223e895876275f656995d170fda42b07fb2bec0ca51ce4b43", diff --git a/testdata/fixtures/format2_time_and_key_recipients.dkk.json b/testdata/fixtures/format2_time_and_key_recipients.dkk.json index fee2fd3..ff78606 100644 --- a/testdata/fixtures/format2_time_and_key_recipients.dkk.json +++ b/testdata/fixtures/format2_time_and_key_recipients.dkk.json @@ -1,6 +1,6 @@ { "description": "portable X25519 .dkk of format2_time_and_key_recipients.dkc", - "spec": "0.15", + "spec": "0.16", "file": "format2_time_and_key_recipients.dkk", "sha256": "2ad99b1556086ec311d7f0b3bd3aaba05e75f45c4fa22490b0d5e8bb0b1a222e", "credential_id": "93cedf68421710e83908ec683b104436", diff --git a/testdata/fixtures/format2_time_and_key_recipients.json b/testdata/fixtures/format2_time_and_key_recipients.json index 30ab2f7..23c8f77 100644 --- a/testdata/fixtures/format2_time_and_key_recipients.json +++ b/testdata/fixtures/format2_time_and_key_recipients.json @@ -1,6 +1,6 @@ { "description": "format 2 time_and_key capsule for three known X25519 recipients and a portable .dkk, and 12 dummies", - "spec": "0.15", + "spec": "0.16", "format": 2, "file": "format2_time_and_key_recipients.dkc", "sha256": "1a44fd8708c92e2e0a10cfcb1d864a71331ea9af25d97e1a42e969dc898959e3", diff --git a/testdata/fixtures/format2_time_and_key_sixteen.json b/testdata/fixtures/format2_time_and_key_sixteen.json index e13dfbf..49c8945 100644 --- a/testdata/fixtures/format2_time_and_key_sixteen.json +++ b/testdata/fixtures/format2_time_and_key_sixteen.json @@ -1,6 +1,6 @@ { "description": "format 2 time_and_key capsule for sixteen known X25519 recipients, without dummies", - "spec": "0.15", + "spec": "0.16", "format": 2, "file": "format2_time_and_key_sixteen.dkc", "sha256": "7aaac5c18f216bf53df326ecc817179640a53408cf25dfd50488910a762dc381", diff --git a/testdata/fixtures/format2_time_only.json b/testdata/fixtures/format2_time_only.json index 78877e6..677d12f 100644 --- a/testdata/fixtures/format2_time_only.json +++ b/testdata/fixtures/format2_time_only.json @@ -1,6 +1,6 @@ { "description": "format 2 time_only capsule, padding code 2 (reforzado): L = 78000, P = 79872, two STREAM chunks", - "spec": "0.15", + "spec": "0.16", "format": 2, "file": "format2_time_only.dkc", "sha256": "f5a40ac6b8a08a0c12db6114c2bca23522d6a77b512b509a217fb15f367813c4", diff --git a/testdata/fixtures/format2_time_only_bloque256.json b/testdata/fixtures/format2_time_only_bloque256.json index 3a7fe89..2840808 100644 --- a/testdata/fixtures/format2_time_only_bloque256.json +++ b/testdata/fixtures/format2_time_only_bloque256.json @@ -1,6 +1,6 @@ { "description": "format 2 time_only capsule with the content of format2_time_only and padding code 1 (bloque256): L = 78000, P = 78080", - "spec": "0.15", + "spec": "0.16", "format": 2, "file": "format2_time_only_bloque256.dkc", "sha256": "aae769c30d04920801d8b293d30864fbe223c9c9353ec2b4907a1ee1996e39f9", diff --git a/testdata/fixtures/format2_time_only_extensions.json b/testdata/fixtures/format2_time_only_extensions.json index 713efa6..a3a8dde 100644 --- a/testdata/fixtures/format2_time_only_extensions.json +++ b/testdata/fixtures/format2_time_only_extensions.json @@ -1,6 +1,6 @@ { "description": "format 2 time_only capsule with a noncritical PUBLIC_HEADER extension and a noncritical CONTROL_CBOR extension", - "spec": "0.15", + "spec": "0.16", "format": 2, "file": "format2_time_only_extensions.dkc", "sha256": "fb406100d5703a2e888983b3175ed34a09a34469cc722256e5cf535dd728fbe9", diff --git a/testdata/fixtures/format3_area_1024.json b/testdata/fixtures/format3_area_1024.json index 0595abb..2fbad82 100644 --- a/testdata/fixtures/format3_area_1024.json +++ b/testdata/fixtures/format3_area_1024.json @@ -1,6 +1,6 @@ { "description": "format 3 time_only capsule with a security area of 1024 bytes, as a later version may write it, holding the empty security", - "spec": "0.15", + "spec": "0.16", "format": 3, "file": "format3_area_1024.dkc", "sha256": "41ea2eed0293e4fef7f4a307b7f16aaf1339f5bf6f4ded7a6a9ae1aebeb0133c", diff --git a/testdata/fixtures/format3_bloque256.json b/testdata/fixtures/format3_bloque256.json index 080f546..0d09eac 100644 --- a/testdata/fixtures/format3_bloque256.json +++ b/testdata/fixtures/format3_bloque256.json @@ -1,6 +1,6 @@ { "description": "format 3 time_only capsule with padding code 1 (bloque256) and one file of 20000 bytes", - "spec": "0.15", + "spec": "0.16", "format": 3, "file": "format3_bloque256.dkc", "sha256": "ff18444f434164ba8e7b26d38c76c7855dc6b0593b2fc8b4e9a95dbf9252d55d", diff --git a/testdata/fixtures/format3_comment_only.json b/testdata/fixtures/format3_comment_only.json index fa7ca4f..44b8e54 100644 --- a/testdata/fixtures/format3_comment_only.json +++ b/testdata/fixtures/format3_comment_only.json @@ -1,6 +1,6 @@ { "description": "format 3 time_only capsule with a comment of two lines, the second one with a TAB, a declared author and no files", - "spec": "0.15", + "spec": "0.16", "format": 3, "file": "format3_comment_only.dkc", "sha256": "7f98a89413f08655bbbab28b96585dfa6173c1705dd81a900deba2100d19f2ef", diff --git a/testdata/fixtures/format3_full_chunk.json b/testdata/fixtures/format3_full_chunk.json index 58af7f7..366e5a0 100644 --- a/testdata/fixtures/format3_full_chunk.json +++ b/testdata/fixtures/format3_full_chunk.json @@ -1,6 +1,6 @@ { "description": "format 3 time_only capsule with padding code 1 (bloque256) and one file, whose BODY and P are 65536 bytes: PAYLOAD_AGE ends in a full STREAM chunk, which the annex of spec v0.16 (79.5) allows", - "spec": "0.15", + "spec": "0.16", "format": 3, "file": "format3_full_chunk.dkc", "sha256": "af658967b0b2c79379e25edfe3a785095e9aa2686203e93e9f30dacf27a38684", diff --git a/testdata/fixtures/format3_note.json b/testdata/fixtures/format3_note.json index eecc6b8..4971931 100644 --- a/testdata/fixtures/format3_note.json +++ b/testdata/fixtures/format3_note.json @@ -1,6 +1,6 @@ { "description": "format 3 time_only capsule with a single file, nota.txt, and the public note «Cartas del viaje a Lisboa» in the noncritical array of PUBLIC_HEADER (spec v0.11, §24.1)", - "spec": "0.15", + "spec": "0.16", "format": 3, "file": "format3_note.dkc", "sha256": "da1bee54231252a0fd98439e24588125c5521f6e5a2c6641b499e6b22192c0eb", diff --git a/testdata/fixtures/format3_seal_unsupported.json b/testdata/fixtures/format3_seal_unsupported.json index 175dc52..205f78b 100644 --- a/testdata/fixtures/format3_seal_unsupported.json +++ b/testdata/fixtures/format3_seal_unsupported.json @@ -1,6 +1,6 @@ { "description": "format 3 time_only capsule with an author-signature of alg 4294967295, as in format3_signature_unsupported, and a seal of seal_type 4294967295, reserved for tests, with a random token of 32 bytes: verdicts F1 and S1", - "spec": "0.15", + "spec": "0.16", "format": 3, "file": "format3_seal_unsupported.dkc", "sha256": "ae3219fbdbd1de4cef6fade1a3fb3f6e5d5e2e8af54d9516b05f0a48136913ad", diff --git a/testdata/fixtures/format3_sealed.json b/testdata/fixtures/format3_sealed.json index 426ea0c..8833ef2 100644 --- a/testdata/fixtures/format3_sealed.json +++ b/testdata/fixtures/format3_sealed.json @@ -1,6 +1,6 @@ { "description": "format 3 time_only capsule with a single file, nota.txt, signed with alg 1 by the test key of format3_signed and sealed with seal_type 2 by a test time-stamping authority before the round time: verdicts F4 and S4, with SEAL_SUBJECT and the token in the record", - "spec": "0.15", + "spec": "0.16", "format": 3, "file": "format3_sealed.dkc", "sha256": "dde5a072d8783227d28279d06d3d226a1fb967c766da626f889d1c6fd76ac9c7", diff --git a/testdata/fixtures/format3_security_v2.json b/testdata/fixtures/format3_security_v2.json index 97d2049..f7c2d8b 100644 --- a/testdata/fixtures/format3_security_v2.json +++ b/testdata/fixtures/format3_security_v2.json @@ -1,6 +1,6 @@ { "description": "format 3 time_only capsule whose security is of version 2: verdict X", - "spec": "0.15", + "spec": "0.16", "format": 3, "file": "format3_security_v2.dkc", "sha256": "3d02b39ace010d74604554e378d22fe5ce00cecd998c0f797d657b17620b8912", diff --git a/testdata/fixtures/format3_signature_unsupported.json b/testdata/fixtures/format3_signature_unsupported.json index 66e88a3..5bdaa75 100644 --- a/testdata/fixtures/format3_signature_unsupported.json +++ b/testdata/fixtures/format3_signature_unsupported.json @@ -1,6 +1,6 @@ { "description": "format 3 time_only capsule with an author-signature of alg 4294967295, a random key of 32 bytes and a random signature of 64: verdicts F1 and S0", - "spec": "0.15", + "spec": "0.16", "format": 3, "file": "format3_signature_unsupported.dkc", "sha256": "e8e3106d8d73bb7b845062e0fe42af21df7d7cd8f63c335cab8dedb3e690df31", diff --git a/testdata/fixtures/format3_signed.json b/testdata/fixtures/format3_signed.json index 11d09cd..3e018c0 100644 --- a/testdata/fixtures/format3_signed.json +++ b/testdata/fixtures/format3_signed.json @@ -1,6 +1,6 @@ { "description": "format 3 time_only capsule with a single file, nota.txt, signed with alg 1 by a test key whose seed the record gives: verdict F4, and the commitments and the message of the signature", - "spec": "0.15", + "spec": "0.16", "format": 3, "file": "format3_signed.dkc", "sha256": "3c7d3c9e24c02853a0c7761b93bea1120b27fce396468d8d0f68e53aeb668c5e", diff --git a/testdata/fixtures/format3_signed_cms.json b/testdata/fixtures/format3_signed_cms.json index 2bcfc1f..e7c8d3c 100644 --- a/testdata/fixtures/format3_signed_cms.json +++ b/testdata/fixtures/format3_signed_cms.json @@ -1,6 +1,6 @@ { "description": "format 3 time_only capsule with a single file, nota.txt, signed with alg 2 by two test certificates, an ECDSA P-256 one and an RSA 2048 one, each sealed by a test time-stamping authority before the round time: verdict F6, with the certificates, the commitments, SIGNERS and the result of each signer in the record", - "spec": "0.15", + "spec": "0.16", "format": 3, "file": "format3_signed_cms.dkc", "sha256": "d658f8d5ac2c5550c07b8f8fd6883b2f6dc02ceafc47d436ea02d8950b2548d2", diff --git a/testdata/fixtures/format3_single.json b/testdata/fixtures/format3_single.json index 25d7e9a..415b7a2 100644 --- a/testdata/fixtures/format3_single.json +++ b/testdata/fixtures/format3_single.json @@ -1,6 +1,6 @@ { "description": "format 3 time_only capsule with a single file, nota.txt, with its mtime", - "spec": "0.15", + "spec": "0.16", "format": 3, "file": "format3_single.dkc", "sha256": "9f68664af8733255084be9036a100b75d27bd16106bf0acff94ce469dd1d1743", diff --git a/testdata/fixtures/format3_time_and_key_portable.dkk.json b/testdata/fixtures/format3_time_and_key_portable.dkk.json index 5c26b0b..73576c3 100644 --- a/testdata/fixtures/format3_time_and_key_portable.dkk.json +++ b/testdata/fixtures/format3_time_and_key_portable.dkk.json @@ -1,6 +1,6 @@ { "description": "portable X25519 .dkk of format3_time_and_key_portable.dkc", - "spec": "0.15", + "spec": "0.16", "file": "format3_time_and_key_portable.dkk", "sha256": "54cc64d849395234b3e093e47f432b72781ccc13f455c9ef394e3554ab566751", "credential_id": "bdb483fba42daf0b409f44d23033f362", diff --git a/testdata/fixtures/format3_time_and_key_portable.json b/testdata/fixtures/format3_time_and_key_portable.json index aa07b87..b1bbcea 100644 --- a/testdata/fixtures/format3_time_and_key_portable.json +++ b/testdata/fixtures/format3_time_and_key_portable.json @@ -1,6 +1,6 @@ { "description": "format 3 time_and_key capsule with one credential, a portable .dkk, and 15 dummies", - "spec": "0.15", + "spec": "0.16", "format": 3, "file": "format3_time_and_key_portable.dkc", "sha256": "680d29962e575689a31543df28433dae7737abd9a793e9cae92ef40920d09636", diff --git a/testdata/fixtures/format3_time_and_key_words.json b/testdata/fixtures/format3_time_and_key_words.json index e013674..35111b3 100644 --- a/testdata/fixtures/format3_time_and_key_words.json +++ b/testdata/fixtures/format3_time_and_key_words.json @@ -1,6 +1,6 @@ { "description": "format 3 time_and_key capsule with one credential, a key of words, and 15 dummies: the text of the vector of the annex of spec v0.16 (79.7), «Ñandú», two spaces, «PINGÜINO», a tab and «camión árbol Éter ola», whose words are «nandu pinguino camion arbol eter ola»", - "spec": "0.15", + "spec": "0.16", "format": 3, "file": "format3_time_and_key_words.dkc", "sha256": "64a11824630b6134892087a4d4ad3ee6e27941513507ad17fc87a7a2b4421e33", diff --git a/testdata/fixtures/format3_tree.json b/testdata/fixtures/format3_tree.json index 637af25..bd59c5f 100644 --- a/testdata/fixtures/format3_tree.json +++ b/testdata/fixtures/format3_tree.json @@ -1,6 +1,6 @@ { "description": "format 3 time_only capsule with five files in three folders, one of them over two STREAM chunks and one without mtime, a comment of two lines and a declared author", - "spec": "0.15", + "spec": "0.16", "format": 3, "file": "format3_tree.dkc", "sha256": "217f378faaf795f6a9c416b564fb8931bb2e896918aee870120fd14f9a5da7d1", diff --git a/testdata/fixtures/format3_unsigned.json b/testdata/fixtures/format3_unsigned.json index 57c857a..3c3f74c 100644 --- a/testdata/fixtures/format3_unsigned.json +++ b/testdata/fixtures/format3_unsigned.json @@ -1,6 +1,6 @@ { "description": "format 3 time_only capsule with a single file, nota.txt, as format3_signed, without a signature: the area of 32 KiB of spec v0.11 holds the empty security, and P is the one of format3_signed", - "spec": "0.15", + "spec": "0.16", "format": 3, "file": "format3_unsigned.dkc", "sha256": "317ab722ae3812a25ddd78b4c98c586363e5587c8d3634c881ce7c421af19168", diff --git a/testdata/fixtures/time_and_key_portable.dkk.json b/testdata/fixtures/time_and_key_portable.dkk.json index 3e1cff3..7e87941 100644 --- a/testdata/fixtures/time_and_key_portable.dkk.json +++ b/testdata/fixtures/time_and_key_portable.dkk.json @@ -1,6 +1,6 @@ { "description": "portable X25519 .dkk of time_and_key_portable.dkc", - "spec": "0.15", + "spec": "0.16", "file": "time_and_key_portable.dkk", "sha256": "e528fa2c832c91119f0684bb9d6fb3c4c2d0d55183482890e7c4fe92f668426a", "credential_id": "3955e944a3c60cfa1fd6485e9693c77d", diff --git a/testdata/fixtures/time_and_key_portable.json b/testdata/fixtures/time_and_key_portable.json index c74b1db..6a61b6d 100644 --- a/testdata/fixtures/time_and_key_portable.json +++ b/testdata/fixtures/time_and_key_portable.json @@ -1,6 +1,6 @@ { "description": "time_and_key capsule whose only recipient is a portable .dkk", - "spec": "0.15", + "spec": "0.16", "format": 1, "file": "time_and_key_portable.dkc", "sha256": "2e97878078bae6358037a9c264f379a3cbe839f767d69836b0343f35657b2972", diff --git a/testdata/fixtures/time_and_key_portable_extension.dkk.json b/testdata/fixtures/time_and_key_portable_extension.dkk.json index ff74e62..91ed929 100644 --- a/testdata/fixtures/time_and_key_portable_extension.dkk.json +++ b/testdata/fixtures/time_and_key_portable_extension.dkk.json @@ -1,6 +1,6 @@ { "description": "portable X25519 .dkk of time_and_key_portable.dkc with a noncritical extension: the credential of time_and_key_portable.dkk re-issued with org.example.delivery", - "spec": "0.15", + "spec": "0.16", "file": "time_and_key_portable_extension.dkk", "sha256": "0bf463a7c65627b7dda2234d728df89ec5b835816a2a37b91497d8fecc5ea548", "credential_id": "3955e944a3c60cfa1fd6485e9693c77d", diff --git a/testdata/fixtures/time_and_key_recipients.dkk.json b/testdata/fixtures/time_and_key_recipients.dkk.json index 2c42bf8..800b3ab 100644 --- a/testdata/fixtures/time_and_key_recipients.dkk.json +++ b/testdata/fixtures/time_and_key_recipients.dkk.json @@ -1,6 +1,6 @@ { "description": "portable X25519 .dkk of time_and_key_recipients.dkc", - "spec": "0.15", + "spec": "0.16", "file": "time_and_key_recipients.dkk", "sha256": "19f6c47150c3194712d454f43c7392b7344e6b4e7b074d83e9ca5f563a8e072f", "credential_id": "b89292aedf6d05d584cec9a871ce8735", diff --git a/testdata/fixtures/time_and_key_recipients.json b/testdata/fixtures/time_and_key_recipients.json index e30b5cf..83ca70d 100644 --- a/testdata/fixtures/time_and_key_recipients.json +++ b/testdata/fixtures/time_and_key_recipients.json @@ -1,6 +1,6 @@ { "description": "time_and_key capsule for two known X25519 recipients and a portable .dkk", - "spec": "0.15", + "spec": "0.16", "format": 1, "file": "time_and_key_recipients.dkc", "sha256": "69ac110380f5d768b5b6afaa157a50ed17d8ceccfbd4604ffa5b6da38539b635", diff --git a/testdata/fixtures/time_only.json b/testdata/fixtures/time_only.json index 8279e55..db3c257 100644 --- a/testdata/fixtures/time_only.json +++ b/testdata/fixtures/time_only.json @@ -1,6 +1,6 @@ { "description": "time_only capsule, two STREAM chunks, no extensions", - "spec": "0.15", + "spec": "0.16", "format": 1, "file": "time_only.dkc", "sha256": "99e915810d595f1092700b728f5e5081d78efe83f5343e76325b1bcc2c33ccf2", diff --git a/testdata/fixtures/time_only_extensions.json b/testdata/fixtures/time_only_extensions.json index ec3ae30..04fb864 100644 --- a/testdata/fixtures/time_only_extensions.json +++ b/testdata/fixtures/time_only_extensions.json @@ -1,6 +1,6 @@ { "description": "time_only capsule with a noncritical PUBLIC_HEADER extension and a noncritical CONTROL_CBOR extension", - "spec": "0.15", + "spec": "0.16", "format": 1, "file": "time_only_extensions.dkc", "sha256": "0446c9b73e267adcb24e5cc89afba2544a386ec9a050016e06517a4a57aa2085", diff --git a/testdata/vectors/cbor.json b/testdata/vectors/cbor.json index 6966756..1e80a95 100644 --- a/testdata/vectors/cbor.json +++ b/testdata/vectors/cbor.json @@ -1,5 +1,5 @@ { - "spec": "0.15", + "spec": "0.16", "description": "CBOR profile of spec §58 and the schemas of spec/datekeys.cddl, generated by the reference implementation. accept and reject are walked as one data item of the profile with the limits of walk; schemas are decoded with the decoder of their schema. See testdata/README.md.", "walk": { "max_depth": 3, diff --git a/testdata/vectors/dk1.json b/testdata/vectors/dk1.json index c6a025b..69766f2 100644 --- a/testdata/vectors/dk1.json +++ b/testdata/vectors/dk1.json @@ -1,5 +1,5 @@ { - "spec": "0.15", + "spec": "0.16", "description": "Canonical dk1_ strings and rejected encodings (spec §18, §19, §66), generated by the reference implementation.", "vectors": [ { diff --git a/testdata/vectors/ed25519_strict.json b/testdata/vectors/ed25519_strict.json index 36c1e50..22b8251 100644 --- a/testdata/vectors/ed25519_strict.json +++ b/testdata/vectors/ed25519_strict.json @@ -1,5 +1,5 @@ { - "spec": "0.15", + "spec": "0.16", "description": "Ed25519 signatures and the result of the strict profile of the author signature (spec v0.11, §29.9), after the cases of «Taming the many EdDSAs»; stdlib is the result of crypto/ed25519 of Go, for the record. Generated by the reference implementation. See testdata/README.md.", "vectors": [ { diff --git a/testdata/vectors/head_schema.json b/testdata/vectors/head_schema.json index 42e5899..2af3c5d 100644 --- a/testdata/vectors/head_schema.json +++ b/testdata/vectors/head_schema.json @@ -1,5 +1,5 @@ { - "spec": "0.15", + "spec": "0.16", "description": "HEAD_CBOR of format 3 (spec §29.4 to §29.6) and the result of decoding it with no extension known, generated by the reference implementation: layer 2 (type tag and version), layer 3 (the CDDL with R1 and R8), then layer 4 in key order (spec §69.1). See testdata/README.md.", "heads": [ { diff --git a/testdata/vectors/inspect_differential.json b/testdata/vectors/inspect_differential.json index fac3c9d..d72094b 100644 --- a/testdata/vectors/inspect_differential.json +++ b/testdata/vectors/inspect_differential.json @@ -1,5 +1,5 @@ { - "spec": "0.15", + "spec": "0.16", "description": "Differential corpus of the pre-unlock checks (spec §63 steps 1 to 8): deterministic mutations of the official .dkc fixtures with the verdict of the reference implementation. See testdata/README.md.", "format": "Each mutation is bases[base].file (in testdata/fixtures) with its edits applied. An edit is [at, delete, insert]: the delete bytes at offset at of the base are replaced by the bytes of the hex string insert. The edits of one mutation refer to offsets of the unmodified base, are sorted by offset and do not overlap. result is the verdict of steps 1 to 8 of spec §63 (capsule.Inspect, the Quicknet profile pinned, no extension known, no network, no secret): ok, or the normative error code, with step the step that failed. kind names the generator of the mutation and is informative.", "seed": 20260925, diff --git a/testdata/vectors/locator.json b/testdata/vectors/locator.json index 7d59b64..745bc73 100644 --- a/testdata/vectors/locator.json +++ b/testdata/vectors/locator.json @@ -1,6 +1,6 @@ { "description": "The extension datekeys.capsule of a .dkk and what it points to (spec v0.12, 44.1): an envelope of age with its header apart from its rest, the rest hidden in a host file, the locator sealed with tlock for round 1000, and the data of the extension. On the same envelope, what a reader rejects and what it uses (64): addresses, a locator with rejected and usable addresses, resources of the rest, data of the extension and plaintexts of the locator. Frozen. See testdata/README.md.", - "spec": "0.15", + "spec": "0.16", "round": 1000, "datekey": "dk1_eyJ2ZXJzaW9uIjoxLCJuZXR3b3JrIjoiZGF0ZWtleXM6cXVpY2tuZXQ6djEiLCJyb3VuZCI6MTAwMH0", "note": "Cartas del viaje a Lisboa", diff --git a/testdata/vectors/mutations.json b/testdata/vectors/mutations.json index 7984317..acb565d 100644 --- a/testdata/vectors/mutations.json +++ b/testdata/vectors/mutations.json @@ -1,5 +1,5 @@ { - "spec": "0.15", + "spec": "0.16", "description": "Mutation corpus of spec §64 and further cases of capsule.TestMutationCorpus, generated by the reference implementation: each case is a .dkc and what the reader is given, with the normative error and the step of spec §63 at which capsule.Open fails. See testdata/README.md.", "cases": [ { diff --git a/testdata/vectors/note.json b/testdata/vectors/note.json index 7e795dc..3df6c05 100644 --- a/testdata/vectors/note.json +++ b/testdata/vectors/note.json @@ -1,5 +1,5 @@ { - "spec": "0.15", + "spec": "0.16", "description": "The data of the public note, datekeys.note version 1 in the noncritical array of PUBLIC_HEADER (spec §24.1): the text in UTF-8, from 1 to 1024 bytes, that meets the rules of the declared author of §29.6. See testdata/README.md.", "notes": [ { diff --git a/testdata/vectors/padding.json b/testdata/vectors/padding.json index ad8da11..3ac3c80 100644 --- a/testdata/vectors/padding.json +++ b/testdata/vectors/padding.json @@ -1,5 +1,5 @@ { - "spec": "0.15", + "spec": "0.16", "description": "Padding rules of the payload of a format 2 capsule (spec §29.1): for each content length L, P with code 1 (bloque256) and code 2 (reforzado), and the length of PAYLOAD_AGE for each. e, s and last_bits are informative. Generated by the reference implementation. See testdata/README.md.", "l_max": 8936830510563328, "vectors": [ diff --git a/testdata/vectors/path_fold.json b/testdata/vectors/path_fold.json index fc99fdd..f0c8a1f 100644 --- a/testdata/vectors/path_fold.json +++ b/testdata/vectors/path_fold.json @@ -1,5 +1,5 @@ { - "spec": "0.15", + "spec": "0.16", "description": "The key of R7 (spec §29.5) of segments, with the Unicode 18.0.0 tables of §29.5.1, generated by the reference implementation: nfd is NFD(segment) and key is NFD(fold(NFD(s'))), s' the segment without ZWNJ, ZWJ, VS15 and VS16. See testdata/README.md.", "unicode_version": "18.0.0", "tables_digest": "07cf5d54aea1cd13a3ecef14a06976cc49a3cdad755cf9bc10395178b93aeb07", diff --git a/testdata/vectors/paths.json b/testdata/vectors/paths.json index 9c61ec4..f94a7ef 100644 --- a/testdata/vectors/paths.json +++ b/testdata/vectors/paths.json @@ -1,5 +1,5 @@ { - "spec": "0.15", + "spec": "0.16", "description": "Paths of a format 3 head (spec §29.5) with the Unicode 18.0.0 and best-fit tables of §29.5.1, generated by the reference implementation. paths: one path and the rules of one entry, R2 to R6c and R10; trees: the paths of a head, of 0 bytes each, and the result of decoding it. See testdata/README.md.", "unicode_version": "18.0.0", "tables_digest": "07cf5d54aea1cd13a3ecef14a06976cc49a3cdad755cf9bc10395178b93aeb07", diff --git a/testdata/vectors/profile_quicknet.json b/testdata/vectors/profile_quicknet.json index c14e804..d9c7891 100644 --- a/testdata/vectors/profile_quicknet.json +++ b/testdata/vectors/profile_quicknet.json @@ -1,5 +1,5 @@ { - "spec": "0.15", + "spec": "0.16", "description": "Quicknet Provider Profile V1: exact Deterministic CBOR and profile_hash (spec §11, §12, §75 item 2), generated by the reference implementation.", "profile_id": "datekeys:quicknet:v1", "provider": "drand", diff --git a/testdata/vectors/quicknet_rounds.json b/testdata/vectors/quicknet_rounds.json index 78a03be..d0f9657 100644 --- a/testdata/vectors/quicknet_rounds.json +++ b/testdata/vectors/quicknet_rounds.json @@ -1,5 +1,5 @@ { - "spec": "0.15", + "spec": "0.16", "profile": "datekeys:quicknet:v1", "description": "Quicknet date to round resolution (spec §15, §16, §65), generated by the reference implementation.", "vectors": [ diff --git a/testdata/vectors/release.json b/testdata/vectors/release.json index d60af10..a219e0b 100644 --- a/testdata/vectors/release.json +++ b/testdata/vectors/release.json @@ -1,5 +1,5 @@ { - "spec": "0.15", + "spec": "0.16", "description": "The release object (spec v0.15, §47.1), and drand's JSON as the input of the caller, each checked against the pinned Quicknet profile and the round of a DateKey as step 10 of spec §63 checks a release that the caller supplies; and the lookups of a local release archive (spec v0.15, §50). See testdata/README.md.", "profile": "datekeys:quicknet:v1", "objects": [ diff --git a/testdata/vectors/resolved_ip.json b/testdata/vectors/resolved_ip.json index 55e2a31..5209fc8 100644 --- a/testdata/vectors/resolved_ip.json +++ b/testdata/vectors/resolved_ip.json @@ -1,5 +1,5 @@ { - "spec": "0.15", + "spec": "0.16", "description": "The IP address that the name of an https address of a locator resolves to, and whether a reader may connect (spec v0.13, 44.1): a public address, or an address of NAT64 (RFC 6052) of 64:ff9b::/96 or of the NAT64 prefix of the network, whose IPv4 address inside is public. See testdata/README.md.", "cases": [ { diff --git a/testdata/vectors/security.json b/testdata/vectors/security.json index 78e8956..09a3ea2 100644 --- a/testdata/vectors/security.json +++ b/testdata/vectors/security.json @@ -1,5 +1,5 @@ { - "spec": "0.15", + "spec": "0.16", "description": "SECURITY_CBOR of format 3, exactly its SECURITY_LEN bytes, the verdicts of the signature and of the seal in the context of the file, and their lines (spec §29.3, §29.7, §29.9). See testdata/README.md.", "context": { "control_commit": "0101010101010101010101010101010101010101010101010101010101010101", diff --git a/testdata/vectors/security_cms.json b/testdata/vectors/security_cms.json index 6f089c6..d683163 100644 --- a/testdata/vectors/security_cms.json +++ b/testdata/vectors/security_cms.json @@ -1,6 +1,6 @@ { - "description": "SECURITY_CBOR with an author signature of alg 2 or a time seal of seal_type 2, the context of its capsule, and the verdicts, the result of each signer and the lines of spec v0.12 29.7, 29.10 and 29.11. Certificates and tokens are made once with test keys and the file is frozen. See testdata/README.md.", - "spec": "0.15", + "description": "SECURITY_CBOR with an author signature of alg 2 or a time seal of seal_type 2, the context of its capsule, and the verdicts, the result of each signer and the lines of spec v0.16 29.7, 29.10 and 29.11. Certificates and tokens are made once with test keys and the file is frozen. See testdata/README.md.", + "spec": "0.16", "cases": [ { "name": "alg 2: two signers, each sealed before the round time: F6", diff --git a/testdata/vectors/tlock_ibe.json b/testdata/vectors/tlock_ibe.json index 9390c99..329b4f6 100644 --- a/testdata/vectors/tlock_ibe.json +++ b/testdata/vectors/tlock_ibe.json @@ -1,5 +1,5 @@ { - "spec": "0.15", + "spec": "0.16", "description": "H2 of the IBE-CCA of tlock (spec §63 step 11): SHA-256 of \"IBE-H2\" and the 576 bytes of an element of GT, c1 before c0 at every level of the tower and each coordinate of Fp in 48 bytes big-endian (the order of kilic/bls12-381), truncated to 16 bytes. Generated by the reference implementation with drand/kyber-bls12381, the pairing of tlock.", "vectors": [ { diff --git a/testdata/vectors/tlock_steps.json b/testdata/vectors/tlock_steps.json index 5fd6261..fd67ecf 100644 --- a/testdata/vectors/tlock_steps.json +++ b/testdata/vectors/tlock_steps.json @@ -1,5 +1,5 @@ { - "spec": "0.15", + "spec": "0.16", "description": "Steps 10 and 11 of spec §63 for Quicknet, value by value, over published releases. Step 10: M = SHA-256(uint64_be(round)), H(M) the hash to G1 of RFC 9380 with the suite BLS12381G1_XMD:SHA-256_SSWU_RO_ and the DST dst, and e(H(M), public_key) = e(signature, G2). Step 11: a stanza body U || V || W built with the sigma and the file key of the vector; H2 = SHA-256(\"IBE-H2\" || e(signature, U))[:16], sigma = V XOR H2, H4 = SHA-256(\"IBE-H4\" || sigma)[:16], file_key = W XOR H4, and r = H3(sigma, file_key): h3_base = SHA-256(\"IBE-H3\" || sigma || file_key), then for i = 1, 2, ... d = SHA-256(uint16_le(i) || h3_base), its first byte shifted one bit to the right, until it is below the order of the group; r·G2 = U. Generated by the reference implementation and checked against drand, kyber, tlock and agewrap. See testdata/README.md.", "profile": "datekeys:quicknet:v1", "scheme": "bls-unchained-g1-rfc9380", diff --git a/testdata/vectors/wordkey.json b/testdata/vectors/wordkey.json index 57cee09..2575539 100644 --- a/testdata/vectors/wordkey.json +++ b/testdata/vectors/wordkey.json @@ -1,5 +1,5 @@ { - "spec": "0.15", + "spec": "0.16", "description": "The key of words (spec §38.1): the words of a text, after NFD, without U+0300 to U+036F, in simple lower case of Unicode 18.0.0 and split by the spaces of the list; what a writer refuses; and the identity, PBKDF2-HMAC-SHA256 of 600000 rounds, for a chain hash, a round and a capsule_id. See testdata/README.md.", "normalize": [ { diff --git a/version.go b/version.go index f085e90..d9d3162 100644 --- a/version.go +++ b/version.go @@ -6,11 +6,11 @@ import ( ) // SpecVersion is the version of the DateKeys Protocol Specification that this -// module implements: spec/DateKeys_Protocol_Specification_v0.11.md, tagged -// spec-v0.11 in its repository. It is neither the version of the module (see +// module implements: spec/DateKeys_Protocol_Specification_v.md, +// tagged spec-v in its repository once its author approves it. It is neither the version of the module (see // Version) nor the versions inside the objects: the capsule format, 1 to 3, // and the schema versions (spec §22, §70). -const SpecVersion = "0.15" +const SpecVersion = "0.16" // modulePath is the path of this module: the import path of its root // package, whatever the module is called.