v0.16: the escapes of the JSON vectors, restored

The editor had written the escapes of the sources as their characters:
the cases "round escaped as round" and "round twice, once escaped as
round" of release.json had a plain round, and the surrogate pair of
"a surrogate pair in a value" a plain emoji, so the shared vectors tested
no escaped name. TestStrictJSON had lost its escaped é, its pair and the
escape after a lone high surrogate. They are escapes again, and the texts
of words_test.go too, so that no mark or invisible character hides in the
source. release.json gained 26 cases of drand's JSON, not 25 as b570338
says; the draft and the CHANGELOG now say 26.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
main
dev 7 hours ago
parent 4f7885495b
commit 3fd0e9372b

@ -28,7 +28,7 @@ verdict of a seal without accuracy and the reading of drand's JSON.
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.
names and matched `ROUND` to `round`. `release.json` gains 26 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

@ -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.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.
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 a3cbdc6ef99aeb3ec77e40c4ce838f6f5b8950af6191383e4033761d2ca1df80. 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

@ -256,9 +256,9 @@ func ReleaseVectors() (ReleaseVectorFile, error) {
// compared exactly once their escapes are decoded, and the round an
// integer from 1 to 2^53 - 1 without fraction or exponent.
{"round twice", `{"round":1000,"round":1001,"signature":"` + s1000 + `"}`, 1000, invalid},
{`round twice, once escaped as round`, `{"round":1000,"round":1000,"signature":"` + s1000 + `"}`, 1000, invalid},
{`round twice, once escaped as \u0072ound`, `{"round":1000,"\u0072ound":1000,"signature":"` + s1000 + `"}`, 1000, invalid},
{"round twice, the second null", `{"round":1000,"round":null,"signature":"` + s1000 + `"}`, 1000, invalid},
{`round escaped as round`, `{"round":1000,"signature":"` + s1000 + `"}`, 1000, ok},
{`round escaped as \u0072ound`, `{"\u0072ound":1000,"signature":"` + s1000 + `"}`, 1000, ok},
{"Round instead of round", `{"Round":1000,"signature":"` + s1000 + `"}`, 1000, invalid},
{"ROUND beside round: another name, ignored", `{"round":1000,"ROUND":1001,"signature":"` + s1000 + `"}`, 1000, ok},
{"round null", `{"round":null,"signature":"` + s1000 + `"}`, 1000, invalid},
@ -278,7 +278,7 @@ func ReleaseVectors() (ReleaseVectorFile, error) {
{"nested objects and arrays, ignored", `{"round":1000,"signature":"` + s1000 + `","meta":{"a":[1,{"a":2}],"b":{},"c":[]}}`, 1000, ok},
{"a lone surrogate in another name", `{"round":1000,"signature":"` + s1000 + `","\ud800":1}`, 1000, invalid},
{"a lone low surrogate in a value", `{"round":1000,"signature":"` + s1000 + `","note":"\udc00"}`, 1000, invalid},
{"a surrogate pair in a value", `{"round":1000,"signature":"` + s1000 + `","note":"😀"}`, 1000, ok},
{"a surrogate pair in a value", `{"round":1000,"signature":"` + s1000 + `","note":"\ud83d\ude00"}`, 1000, ok},
{"a tab inside a string", `{"round":1000,"signature":"` + s1000 + `","note":"a` + "\t" + `b"}`, 1000, invalid},
{"something after the object", `{"round":1000,"signature":"` + s1000 + `"}{}`, 1000, invalid},
}

@ -8,7 +8,7 @@ import (
// The strict reading of drand's JSON (spec v0.16, §47.1): JSON of RFC 8259,
// in UTF-8, whose value is an object; no object of the JSON repeats a name,
// and names are compared exactly, code point by code point, once their
// escapes are decoded, so that "round" is round and Round is another
// escapes are decoded, so that "\u0072ound" is round and Round is another
// name; and an escape of a surrogate that does not pair with the next one
// makes the JSON malformed. A common JSON reader keeps the last of two
// repeated names, or does not tell upper from lower case in them, and two

@ -19,8 +19,8 @@ func TestStrictJSON(t *testing.T) {
{`{"a":[1,2,[3,{}]]}`, true},
{`{"a":true,"b":false,"c":null}`, true},
{`{"a":-0,"b":0.5,"c":1E+2,"d":1e-2,"e":-12.25e3}`, true},
{`{"a":"\"\\\/\b\f\n\r\té"}`, true},
{`{"é":1,"é":2}`, false}, // one name, escaped and not
{`{"a":"\"\\\/\b\f\n\r\t\u00e9"}`, true},
{`{"\u00e9":1,"é":2}`, false}, // one name, escaped and not
{`{"a":1,"a":2}`, false},
{`{"a":{"b":1},"c":{"b":2}}`, true}, // the same name in two objects
{`{"a":[{"b":1,"b":1}]}`, false},
@ -36,10 +36,10 @@ func TestStrictJSON(t *testing.T) {
{`{"a":"\u12"}`, false},
{`{"a":"\u12g4"}`, false},
{`{"a":"\ud800"}`, false},
{`{"a":"\ud800A"}`, false},
{`{"a":"\ud800\u0041"}`, false},
{`{"a":"\ud800x"}`, false},
{`{"a":"\udfff\ud800"}`, false},
{`{"a":"😀"}`, true},
{`{"a":"\uD83D\uDE00"}`, true},
{`{"a":"` + "\x01" + `"}`, false},
{`{"a":"` + "\xff" + `"}`, false},
{"{\"a\":\"\xed\xa0\x80\"}", false}, // a surrogate in UTF-8
@ -61,7 +61,7 @@ func TestStrictJSON(t *testing.T) {
t.Errorf("%q: %v, want %v", tc.in, ok, tc.ok)
}
}
m, ok := strictJSON([]byte(`{"round":1000,"note":"a😀","n":-1.5}`))
m, ok := strictJSON([]byte(`{"\u0072ound":1000,"note":"a\ud83d\ude00","n":-1.5}`))
if !ok || len(m) != 3 || m[0].name != "round" || string(m[0].raw) != "1000" || m[1].str != "a\U0001F600" || m[2].kind != '0' || string(m[2].raw) != "-1.5" {
t.Errorf("members %+v", m)
}

@ -34,8 +34,8 @@ func TestAnnexWordVectors(t *testing.T) {
id := unhex(t, "000102030405060708090a0b0c0d0e0f")
for _, c := range []struct{ text, words, key string }{
{"perro luna casa verde tren mar", "perro luna casa verde tren mar", "fceec4d8ca8de86c85a1f26ed49f82a2b38431bd0ce36db995ae7dfd49b96e41"},
{"Ñandú PINGÜINO\tcamión árbol Éter ola", "nandu pinguino camion arbol eter ola", "273295d29370126a3be50b743132718d3cd9137fb3bb4cb20aa23163d2e19bb7"},
{"Ñandú PINGÜINO\tcamión árbol Éter ola", "nandu pinguino camion arbol eter ola", "273295d29370126a3be50b743132718d3cd9137fb3bb4cb20aa23163d2e19bb7"},
{"\u00d1and\u00fa PING\u00dcINO\tcami\u00f3n \u00e1rbol \u00c9ter ola", "nandu pinguino camion arbol eter ola", "273295d29370126a3be50b743132718d3cd9137fb3bb4cb20aa23163d2e19bb7"},
{"N\u0303andu\u0301 PINGU\u0308INO\tcamio\u0301n a\u0301rbol E\u0301ter ola", "nandu pinguino camion arbol eter ola", "273295d29370126a3be50b743132718d3cd9137fb3bb4cb20aa23163d2e19bb7"},
} {
words, ok := normalizeSimple(c.text)
if !ok || strings.Join(words, " ") != c.words {
@ -51,7 +51,7 @@ func TestAnnexWordVectors(t *testing.T) {
// The recipe without tables refuses what it does not cover, and then the
// words need UnicodeData.txt.
func TestNormalizeSimpleRefuses(t *testing.T) {
for _, text := range []string{"ça va", "straße", "à la", "σασ", "a b", "Å", "a​b"} {
for _, text := range []string{"\u00e7a va", "stra\u00dfe", "\u00e0 la", "\u03c3\u03b1\u03c3", "a\u00a0b", "\u212b", "a\u200bb"} {
if _, ok := normalizeSimple(text); ok {
t.Errorf("%q: the recipe without tables does not cover it", text)
}

@ -4503,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: `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.
- Pruebas: `release.json`, con 26 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.

@ -375,7 +375,7 @@ one of CBOR (RFC 8949).
failure to read it is `ERR_RELEASE_INVALID`, and so is one of more than
8192 bytes; then the round and the signature, as for an object. Since
v0.16 it is read strictly: no object of the JSON repeats a name, names are
compared exactly once their escapes are decoded (`"round"` is
compared exactly once their escapes are decoded (`"\u0072ound"` is
`round`, and `ROUND` another name, which is ignored), an escape of a lone
surrogate is malformed, `round` is a number without sign, fraction or
exponent from 1 to 2^53 − 1, and `signature` and `randomness` are strings.

@ -441,8 +441,8 @@
"text": "provider: drand JSON: malformed, or without round or signature: ERR_RELEASE_INVALID"
},
{
"name": "round twice, once escaped as round",
"input": "{\"round\":1000,\"round\":1000,\"signature\":\"b44679b9a59af2ec876b1a6b1ad52ea9b1615fc3982b19576350f93447cb1125e342b73a8dd2bacbe47e4b6b63ed5e39\"}",
"name": "round twice, once escaped as \\u0072ound",
"input": "{\"round\":1000,\"\\u0072ound\":1000,\"signature\":\"b44679b9a59af2ec876b1a6b1ad52ea9b1615fc3982b19576350f93447cb1125e342b73a8dd2bacbe47e4b6b63ed5e39\"}",
"round": 1000,
"result": "ERR_RELEASE_INVALID",
"text": "provider: drand JSON: malformed, or without round or signature: ERR_RELEASE_INVALID"
@ -455,8 +455,8 @@
"text": "provider: drand JSON: malformed, or without round or signature: ERR_RELEASE_INVALID"
},
{
"name": "round escaped as round",
"input": "{\"round\":1000,\"signature\":\"b44679b9a59af2ec876b1a6b1ad52ea9b1615fc3982b19576350f93447cb1125e342b73a8dd2bacbe47e4b6b63ed5e39\"}",
"name": "round escaped as \\u0072ound",
"input": "{\"\\u0072ound\":1000,\"signature\":\"b44679b9a59af2ec876b1a6b1ad52ea9b1615fc3982b19576350f93447cb1125e342b73a8dd2bacbe47e4b6b63ed5e39\"}",
"round": 1000,
"release": {
"round": 1000,
@ -609,7 +609,7 @@
},
{
"name": "a surrogate pair in a value",
"input": "{\"round\":1000,\"signature\":\"b44679b9a59af2ec876b1a6b1ad52ea9b1615fc3982b19576350f93447cb1125e342b73a8dd2bacbe47e4b6b63ed5e39\",\"note\":\"😀\"}",
"input": "{\"round\":1000,\"signature\":\"b44679b9a59af2ec876b1a6b1ad52ea9b1615fc3982b19576350f93447cb1125e342b73a8dd2bacbe47e4b6b63ed5e39\",\"note\":\"\\ud83d\\ude00\"}",
"round": 1000,
"release": {
"round": 1000,

Loading…
Cancel
Save

Powered by TurnKey Linux.