Dice: diceNumber, diceWord, diceWords and diceList, as wordkey's dice.go

The port of dice.go of datekeys-go at 92e7154, for whoever does not trust
the random numbers of a computer: five dice for each word give a number
from 11111 to 66666, the position of the word in a list of 7776, the first
die the most significant. diceNumber gives the dice of a position,
diceWord the word of five dice, diceWords the words of several numbers
separated by white space, at least 6 and never the same word twice, and
diceList the list numbered for dice, as the EFF publishes its own: for
en, its UTF-8 bytes are the file of the EFF. The same checks in the same
order and Go's texts, in a WordKeyException. The numbers are split as
Go's strings.Fields splits a string, at unicode.IsSpace (goIsSpace), the
white space at which normalizeWords splits, and nothing else changes.
lib/datekeys.dart exports them; diceWordUtf8 and diceWordsUtf8, on the
bytes of a Go string, are internal, for the tests.

testdata and wordlists at datekeys-go 92e7154: only wordlists/README.md
changes, with the SHA-256 of each list numbered for dice, and the commit
of both SOURCE.json. tool/wordlist_go_vectors.go writes the dice too, run
at 92e7154: DiceNumber, DiceWord and DiceWords on the lists of Go and on
lists of indices, DiceList of each list, and DiceWords with every code
point of planes 0, 1 and 14 between two numbers. The rest of the vectors
is the same but for their source and description. The tests port
TestDiceNumber, TestDiceWord, TestDiceWords and TestDiceList, and run the
vectors on the VM and on Node.js.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
v0.15
dev 9 hours ago
parent bf0f3d35d4
commit 00f4ad4582

@ -4,6 +4,22 @@ Cambios notables de la librería Dart. El proyecto usa versionado semántico; mi
## Especificación 0.15, en la rama `v0.15` — sin versión
### Los dados de la llave de palabras (07-10-2026)
- `datekeys-go` añade en `92e7154`, en la rama `v0.15`, los dados que decidió el autor, para quien no se fía de los números al azar de un ordenador: cinco dados por palabra, leídos en un orden fijo, dan un número de 11111 a 66666, la posición de la palabra en una lista de 7776, el primer dado el más significativo. Son `wordkey.DiceNumber`, `DiceWord`, `DiceWords` y `DiceList`, con `encrypt -dice` y `datekeys wordlist` en la CLI. No cambia ningún formato ni ninguna derivación.
- **`lib/src/wordlist.dart`** porta `dice.go`, con las mismas comprobaciones en el mismo orden y los mismos textos de error, en una `WordKeyException`:
- `diceNumber` da los dados de una posición, y `diceListSize` es 7776;
- `diceWord` da la palabra de cinco dados, después de comprobar el tamaño de la lista: cinco bytes, cada uno una cifra del 1 al 6;
- `diceWords` da las palabras de varios números separados por espacios, y rechaza, por este orden, una lista de otro tamaño; menos de 6 números; y, número a número, uno que no es de cinco dados o que da una palabra que ya salió, y hay que volver a tirar. Separa los números como `strings.Fields` de Go, en los espacios de `unicode.IsSpace`, que son los del §38.1 en los que `normalizeWords` separa las palabras, y no cambia nada más: una cifra de ancho completo o una marca combinante no es un dado;
- `diceList` escribe la lista numerada para los dados, una línea por palabra con sus dados y un tabulador, como publica la EFF la suya: para `en`, sus bytes UTF-8 son el fichero de la EFF, byte a byte (SHA-256 `addd3553…`), y para `es` tienen el SHA-256 `611f779a…`, los dos de `wordlists/README.md`.
`lib/datekeys.dart` los exporta. `diceWordUtf8` y `diceWordsUtf8`, sobre los bytes de un string de Go, son internas, para las pruebas. Los espacios son los de `goIsSpace` de `go_unicode.dart`, el `unicode.IsSpace` de Go.
- **`testdata` y `wordlists`** pasan a `92e7154`: solo cambian `wordlists/README.md`, con el SHA-256 de cada lista numerada para los dados, y el commit de los dos `SOURCE.json`.
- **Vectores de Go:** `tool/wordlist_go_vectors.go` escribe también los dados, en una exportación de `datekeys-go` en `92e7154`: `DiceNumber` en 24 posiciones; `DiceWord` en 63 casos y `DiceWords` en 78, sobre las listas de Go y sobre listas de índices, con los casos de `TestDiceWord` y `TestDiceWords`, cada espacio de `unicode.IsSpace` entre los números y puntos de código y bytes que no lo son, el orden de las comprobaciones, listas con una palabra más de una vez, también con caracteres que `%q` escapa, y listas de otro tamaño; el SHA-256 de `DiceList` de cada lista; y `DiceWords` con cada punto de código de los planos 0, 1 y 14 entre los dos primeros números, como el SHA-256 de sus resultados. El resto de los vectores sale igual: cambian solo su campo `source` y su descripción.
- **Pruebas.** 12 nuevas en la VM y 7 en Node.js: `test/wordlist_test.dart`, también compilado a JavaScript, con los casos de `TestDiceNumber` de Go, los de `TestDiceWord` y `TestDiceList` que no necesitan una lista de Go, sobre una lista de índices, los vectores sobre los bytes de Go y sobre Strings, y los espacios del plano 0; y `test/wordlist_vm_test.dart`, con los casos de `TestDiceWord`, `TestDiceWords` y `TestDiceList` sobre las dos listas de `wordlists/`, los vectores sobre ellas y los espacios de los planos 0, 1 y 14, que son los de `normalizeWords`. En total, 2243 en la VM y 712 en Node.js.
- **Fallos inyectados** en los dados, uno a uno y revertidos: los 17, en las cifras y su orden, los límites, el orden de las comprobaciones, los espacios, los bytes que no son UTF-8 válido, la palabra que sale dos veces, la lista numerada y los textos, los detectan pruebas que corren también en Node.js. Uno se escapaba al principio, la palabra repetida sin el `%q` de Go, porque las palabras de una lista no tienen nada que escapar, y los vectores tienen ahora una lista con caracteres que `%q` escapa.
- **Diferencias con Go:** ninguna. `diceWord` y `diceWords` toman un String como sus bytes UTF-8, como el resto de `wordlist.dart`, y los bytes UTF-8 del String de `diceList` son el string de Go.
### Las palabras al azar de la llave de palabras (07-10-2026)
- `datekeys-go` saca al azar, en la rama `v0.15` después del tag `spec-v0.15` (`c49c67c` y `27a75ee`), las palabras de una llave de palabras, el SHOULD del §38.1 de ofrecer palabras al azar de una lista pública: `wordkey.Generate`, `List`, `CheckList`, `Bits` y la lista española `wordkey/lists/es.txt`, de 7776 palabras, un borrador sin revisar. No cambia ningún formato ni ninguna derivación.

@ -24,9 +24,9 @@ Están hechas las etapas 0 a 5 del plan (`docs/PLAN_dart.md` del espacio de trab
- la etapa 7, en dos partes:
- la 7a, el localizador y el sobre de `datekeys.capsule`: los datos de la extensión, la lectura y la apertura del localizador, las reglas de sus direcciones y de la IP a la que resuelve un nombre, y el resto del sobre;
- la 7b, las claves de autor `dkauthor1…`, con su firma Ed25519 y su fichero cifrado con scrypt, y el sellado del localizador y la creación del sobre, sobre el escritor de `age` de la 6a;
- fuera del plan, con la v0.15, las palabras al azar de la llave de palabras: las listas de palabras de `datekeys-go`, tomadas solo con su SHA-256 fijado y comprobadas con el alfabeto de su idioma, las palabras sacadas al azar de una y su fuerza en bits.
- fuera del plan, con la v0.15, las palabras al azar de la llave de palabras: las listas de palabras de `datekeys-go`, tomadas solo con su SHA-256 fijado y comprobadas con el alfabeto de su idioma, las palabras sacadas al azar de una y su fuerza en bits, y las de cinco dados cada una, con la lista numerada para los dados.
La librería ya abre cápsulas reales, de los tres formatos, con todas sus credenciales, y evalúa toda su área de seguridad como Go: la firma de `alg` 1, la de `alg` 2 con certificados, el sello de `seal_type` 2 y todos los veredictos de su forma. Y ya escribe cápsulas del formato 3, con todas sus credenciales, su firma y su sello, byte a byte como Go con los mismos valores aleatorios, y Go las abre. Firma con una clave de autor y sella localizadores. Y saca al azar las palabras de una llave de palabras de las listas de `datekeys-go`, que toma solo con su SHA-256 fijado.
La librería ya abre cápsulas reales, de los tres formatos, con todas sus credenciales, y evalúa toda su área de seguridad como Go: la firma de `alg` 1, la de `alg` 2 con certificados, el sello de `seal_type` 2 y todos los veredictos de su forma. Y ya escribe cápsulas del formato 3, con todas sus credenciales, su firma y su sello, byte a byte como Go con los mismos valores aleatorios, y Go las abre. Firma con una clave de autor y sella localizadores. Y saca al azar las palabras de una llave de palabras de las listas de `datekeys-go`, que toma solo con su SHA-256 fijado, o las da de unos dados.
Las etapas 2 y 3 se hicieron en paralelo, en ramas aparte desde la etapa 1, y se integraron el 5 de octubre de 2026. Las partes 4a y 4b también: la 4a, en la rama `stage4a`, se integró encima de la 4b el mismo día. La 4c se hizo después, en la rama `v0.11`, y la 5b también, en paralelo con la 5a, el lector de CMS, que se hizo en la rama `stage5a` y se integró encima de la 5b el mismo día. La 5c, que necesitaba las dos, se hizo después, en la rama `v0.11`. La 6a se hizo en la rama `v0.11`, en paralelo con la 7a, la lectura del localizador, en la rama `stage7a`. La 7a se integró encima de la 6a el mismo día. La 6b, el escritor de cápsulas, se hizo en la rama `v0.11`, en paralelo con la 7b, las claves de autor y el sellado del localizador, en la rama `stage7b`, desde `b23a0ee`. La 7b se integró encima de la 6b el mismo día.
@ -91,20 +91,21 @@ La especificación 0.15 da forma de dato al release de una ronda y distingue dos
### Las palabras al azar de la llave de palabras (v0.15)
El §38.1 pide que un escritor ofrezca por defecto palabras al azar de una lista pública: al menos 6 de una lista de 2048 o más. `datekeys-go` las añade en la rama `v0.15`, después del tag `spec-v0.15` (`c49c67c` y `27a75ee`), sin cambiar ningún formato ni ninguna derivación, y `wordlist.dart` las porta de `generate.go` de `wordkey` en `27a75ee`, con las mismas comprobaciones en el mismo orden y los mismos textos de error:
El §38.1 pide que un escritor ofrezca por defecto palabras al azar de una lista pública: al menos 6 de una lista de 2048 o más. `datekeys-go` las añade en la rama `v0.15`, después del tag `spec-v0.15` (`c49c67c` y `27a75ee`), y los dados en `92e7154`, sin cambiar ningún formato ni ninguna derivación, y `wordlist.dart` las porta de `generate.go` de `wordkey` en `27a75ee` y de `dice.go` en `92e7154`, con las mismas comprobaciones en el mismo orden y los mismos textos de error:
| Módulo | Contenido | En Go |
|---|---|---|
| `lib/src/wordlist.dart` | `readWordList`, las palabras del fichero de una lista; `wordListSha256`, el SHA-256 fijado de cada lista, y `wordListLanguages`; `checkWordList`, las reglas de una lista, con el alfabeto de su idioma; `generateWords`, las palabras sacadas al azar de una lista; `wordBits`, su fuerza en bits; y `defaultWordCount` y `minListSize` | `List`, `Languages`, `CheckList`, `Generate`, `Bits`, `DefaultCount` y `MinListSize` de `wordkey` |
| `lib/src/wordlist.dart` | `readWordList`, las palabras del fichero de una lista; `wordListSha256`, el SHA-256 fijado de cada lista, y `wordListLanguages`; `checkWordList`, las reglas de una lista, con el alfabeto de su idioma; `generateWords`, las palabras sacadas al azar de una lista; `wordBits`, su fuerza en bits; `defaultWordCount` y `minListSize`; y los dados: `diceWords`, las palabras de cinco dados cada una; `diceWord`, la de cinco dados; `diceNumber`, los dados de una posición; `diceList`, la lista numerada para los dados; y `diceListSize` | `List`, `Languages`, `CheckList`, `Generate`, `Bits`, `DefaultCount` y `MinListSize` de `wordkey`; y `DiceWords`, `DiceWord`, `DiceNumber`, `DiceList` y `DiceListSize` |
Notas:
- **Las listas no van en la librería.** Go las lleva dentro del módulo; aquí la app descarga o empaqueta el fichero de una lista, `wordkey/lists/<idioma>.txt` de `datekeys-go`, y se lo da a `readWordList`, que no se fía de él: su SHA-256 debe ser el de `wordListSha256`, con el texto «the list "es" has the SHA-256 …, not …», el mismo de `datekeys-ts`, y sus palabras deben pasar `checkWordList`. Una lista cambia solo con su hash. Hoy hay una, `es`: 7776 palabras, un borrador que todavía no ha revisado un hablante nativo. `wordlists/` tiene la copia de las listas (ver «Datos de prueba»).
- **Las listas no van en la librería.** Go las lleva dentro del módulo; aquí la app descarga o empaqueta el fichero de una lista, `wordkey/lists/<idioma>.txt` de `datekeys-go`, y se lo da a `readWordList`, que no se fía de él: su SHA-256 debe ser el de `wordListSha256`, con el texto «the list "es" has the SHA-256 …, not …», el mismo de `datekeys-ts`, y sus palabras deben pasar `checkWordList`. Una lista cambia solo con su hash. Hoy hay dos, de 7776 palabras cada una: `en`, la lista grande de la EFF, y `es`, un borrador que todavía no ha revisado un hablante nativo. `wordlists/` tiene la copia de las listas (ver «Datos de prueba»).
- **El alfabeto** de cada idioma lo da el código, nunca la lista: para `es`, de la `a` a la `z`, `á`, `é`, `í`, `ó`, `ú`, `ü` y `ñ`, en minúscula y NFC. Una letra de otra escritura que se parece a una de ellas, como la `а` cirílica (U+0430), se volvería a teclear con la latina, y la cápsula no se abriría. Tampoco pasan una mayúscula, una cifra, el CR de un fichero con líneas CRLF ni dos palabras que son una una vez normalizadas, como «papa» y «papá», que valdrían por una.
- **El sorteo** es el de Go: cada índice como lo saca `crypto/rand.Int`, con `randomIndex` de la parte 6a, y un índice que ya salió se saca otra vez, así que los mismos bytes dan las mismas palabras que Go. Son 7 palabras por defecto: 7 de 7776 son unos 90 bits (`wordBits`), y 6 de 2048, el mínimo, unos 66.
- **`wordBits`** suma el `math.Log2` de Go, portado: `Frexp` y el `Log` de FreeBSD que porta Go, con sus operaciones en su orden, así que el double es el de Go en la VM y en la web, bit a bit. El `log` de la plataforma no es el de Go, y `math.log(x) / math.ln2` no da exacto ni el log2 de 2^29 en la VM: la CLI de Go trunca los bits a un entero.
- **Los dados**, para quien no se fía de los números al azar de un ordenador: cinco dados por palabra, leídos en un orden fijo, dan un número de 11111 a 66666, la posición de la palabra en una lista de 7776 (`diceListSize`), el primer dado la cifra más significativa en base 6. `diceWord` toma cinco bytes, cada uno una cifra del 1 al 6, después de comprobar el tamaño de la lista. `diceWords` toma varios números separados por espacios, al menos 6, y nunca da dos veces la misma palabra: hay que volver a tirar los dados. Los números se separan como los separa `strings.Fields` de Go, en los espacios de `unicode.IsSpace`, que son los del §38.1 en los que `normalizeWords` separa las palabras, y nada más cambia: una cifra de ancho completo o una marca combinante no es un dado. Unos dados justos sacan cada palabra como `generateWords`, así que las palabras son igual de fuertes. `diceList` escribe la lista numerada para los dados, para imprimirla: una línea por palabra, con sus dados, un tabulador y la palabra, como publica la EFF la suya. Para `en`, sus bytes UTF-8 son el fichero de la EFF, byte a byte (SHA-256 `addd3553…`), y para `es` tienen el SHA-256 `611f779a…`, como los recoge `wordlists/README.md`.
- **Las diferencias con Go.** Una `RandomSource` no se acaba: lo que lance la fuente sale de `generateWords` tal cual, donde un `io.Reader` de Go que se acaba da `wordkey: EOF`. `readWordList` comprueba el SHA-256, que Go no necesita para sus listas. El fichero se lee como lee Go un string, como bytes: un byte que no es UTF-8 válido es U+FFFD, que ningún alfabeto tiene, así que la comprobación lo rechaza con el texto de Go, y las palabras que pasan se decodifican en estricto.
- **La licencia.** Las listas no son código: `es.txt` es una adaptación de FrequencyWords de Hermit Dave y tiene licencia CC BY-SA 4.0, no Apache-2.0. `wordlists/README.md`, copiado de Go, recoge su fuente, su método y su licencia.
- **Lo que se exporta.** `lib/datekeys.dart` exporta `readWordList`, `checkWordList`, `generateWords`, `wordBits`, `wordListSha256`, `wordListLanguages`, `defaultWordCount` y `minListSize`, como el paquete público `wordkey` de Go. `checkWordListUtf8`, sobre bytes, y `parseWordList`, `readWordList` sin el SHA-256, son internas, para las pruebas. `wordkey.dart` comparte con `wordlist.dart` la comprobación de cada runa, `checkWordRune`, también interna.
- **La licencia.** Las listas no son código: `en.txt` es material original de la EFF, con licencia CC BY 4.0, y `es.txt` es una adaptación de FrequencyWords de Hermit Dave y tiene licencia CC BY-SA 4.0, no Apache-2.0. `wordlists/README.md`, copiado de Go, recoge su fuente, su método y su licencia.
- **Lo que se exporta.** `lib/datekeys.dart` exporta `readWordList`, `checkWordList`, `generateWords`, `wordBits`, `wordListSha256`, `wordListLanguages`, `defaultWordCount` y `minListSize`, y `diceNumber`, `diceWord`, `diceWords`, `diceList` y `diceListSize`, como el paquete público `wordkey` de Go. `checkWordListUtf8`, `diceWordUtf8` y `diceWordsUtf8`, sobre bytes, y `parseWordList`, `readWordList` sin el SHA-256, son internas, para las pruebas. `wordkey.dart` comparte con `wordlist.dart` la comprobación de cada runa, `checkWordRune`, también interna.
La parte 4a de la etapa 4 porta `internal/pathrule` y `wordkey` de `datekeys-go` en `c531e93`, la cabeza de la rama `v0.12`, que sigue la v0.11 con las correcciones de la revisión del 2 de octubre; los dos paquetes no cambian desde el tag `spec-v0.11`. Tienen las mismas comprobaciones, en el mismo orden y con los mismos textos de error:
@ -599,12 +600,12 @@ tool/check.sh
## Datos de prueba
`testdata/` es una copia de `datekeys-go/testdata` en un commit fijo. `testdata/SOURCE.json` registra el commit y el SHA-256 de cada fichero, igual que en `datekeys-ts`. La copia actual es la de `datekeys-go` en `e671032`, la rama `v0.15` después del tag `spec-v0.15`, cuyo `testdata/` es el del tag, `fe50885`, byte a byte: 142 ficheros, con `release.json` y `releases/`, el objeto release, el JSON de drand y el archivo de releases de la v0.15, con el campo `source` de `mutations.json`, con `tlock_steps.json`, los pasos 10 y 11 de Quicknet valor a valor, con `resolved_ip.json`, con `wordkey.json`, los vectores compartidos de la llave de palabras, y los veredictos y las líneas de la v0.12. Cada fichero dice `"spec": "0.15"`, como el `SpecVersion` de Go.
`testdata/` es una copia de `datekeys-go/testdata` en un commit fijo. `testdata/SOURCE.json` registra el commit y el SHA-256 de cada fichero, igual que en `datekeys-ts`. La copia actual es la de `datekeys-go` en `92e7154`, la rama `v0.15` después del tag `spec-v0.15`, cuyo `testdata/` es el del tag, `fe50885`, byte a byte: 142 ficheros, con `release.json` y `releases/`, el objeto release, el JSON de drand y el archivo de releases de la v0.15, con el campo `source` de `mutations.json`, con `tlock_steps.json`, los pasos 10 y 11 de Quicknet valor a valor, con `resolved_ip.json`, con `wordkey.json`, los vectores compartidos de la llave de palabras, y los veredictos y las líneas de la v0.12. Cada fichero dice `"spec": "0.15"`, como el `SpecVersion` de Go.
`wordlists/` es la copia de `wordkey/lists` del mismo commit: las listas de palabras de `wordkey.Generate`, hoy `en.txt`, la de la EFF, y `es.txt`, y su `README.md`, con la fuente, el método y la licencia de cada lista. `wordlists/SOURCE.json` tiene el formato de `testdata/SOURCE.json`, y `.gitattributes` guarda sus bytes exactos, como los de `testdata/`: `lib/src/wordlist.dart` fija el SHA-256 de cada lista.
`wordlists/` es la copia de `wordkey/lists` del mismo commit: las listas de palabras de `wordkey.Generate`, hoy `en.txt`, la de la EFF, y `es.txt`, y su `README.md`, con la fuente, el método y la licencia de cada lista, y el SHA-256 de cada lista numerada para los dados. `wordlists/SOURCE.json` tiene el formato de `testdata/SOURCE.json`, y `.gitattributes` guarda sus bytes exactos, como los de `testdata/`: `lib/src/wordlist.dart` fija el SHA-256 de cada lista.
```bash
dart run tool/sync_testdata.dart sync --commit e671032
dart run tool/sync_testdata.dart sync --commit 92e7154
```
```bash
@ -614,7 +615,7 @@ dart run tool/sync_testdata.dart check --against ../datekeys-go
- `sync` copia los dos árboles del mismo commit y lee los ficheros con git, así que nunca entran cambios sin commit del repositorio de Go. Lo lee todo antes de tocar las copias, y un árbol que falta en el commit es un error.
- `check` comprueba los dos árboles con las mismas reglas, sin ficheros que falten, sobren o cambien, y escribe una línea por árbol, la de `testdata` primero.
- Los ficheros de `testdata/` y de `wordlists/` no se editan ni se generan aquí.
- En cada `dart test`, `test/testdata_test.dart` comprueba las dos copias, y que cada fichero de `testdata/` nombre `specVersion`. `test/wordlist_vm_test.dart` lee la lista española de `wordlists/` con `readWordList`.
- En cada `dart test`, `test/testdata_test.dart` comprueba las dos copias, y que cada fichero de `testdata/` nombre `specVersion`. `test/wordlist_vm_test.dart` lee las dos listas de `wordlists/` con `readWordList`, y numera las dos para los dados.
- `test/tlock_steps_vm_test.dart` recorre `tlock_steps.json` valor a valor con el código de la librería: M, H(M), la ecuación del emparejamiento, las partes del stanza, e(firma, U), H2, sigma, H4, la file key, cada intento de H3 y r, r·G2 = U, y las comprobaciones negativas del generador de Go (otro DST, la ronda sin SHA-256 y H3 que borra solo el bit más alto). `test/tlock_steps_test.dart` hace lo mismo compilado a JavaScript, sobre `test/vectors/tlock_steps.g.dart`, la copia del fichero que escribe `dart run tool/tlock_steps_copy.dart` tras cada `sync`; la prueba de la VM comprueba que es el fichero byte a byte.
- `test/release_object_vm_test.dart` recorre `release.json` caso a caso, con el resultado y el texto de Go: los objetos, con sus capas y el paso 10, el release que dice cada uno y su codificación otra vez byte a byte; el JSON de drand; y las búsquedas en el archivo. Comprueba además cada fichero de `releases/` y los textos de `provider.Archive` sobre archivos editados (`test/vectors/release_archive_texts.json`). `test/release_object_test.dart` hace lo mismo compilado a JavaScript, sobre `test/vectors/release.g.dart`, que escribe `dart run tool/release_copy.dart` tras cada `sync`.
- `test/open_corpus_test.dart` abre cada caso de `mutations.json` con su `source`: `supplied`, el objeto release de su `release` en `OpenOptions.release`, o `network`, una fuente que verifica el release y lo descarta si no cumple el paso 10, como `testkit` de Go.
@ -722,10 +723,10 @@ cp tool/age_writer_go_vectors.go "$tmp"
rm -rf "$tmp"
```
El de las listas de palabras corre en una exportación del commit de `wordlists/SOURCE.json`, para que sus vectores sean los de esas listas: `tool/wordlist_go_vectors.go` lee `wordkey/lists` y da a `Generate`, en lugar de `crypto/rand`, el keystream de `SeededRandomSource`, bloques de SHA-256 o bytes que se acaban. La salida es la misma en cada ejecución:
El de las listas de palabras corre en una exportación del commit de `wordlists/SOURCE.json`, para que sus vectores sean los de esas listas: `tool/wordlist_go_vectors.go` lee `wordkey/lists`, da a `Generate`, en lugar de `crypto/rand`, el keystream de `SeededRandomSource`, bloques de SHA-256 o bytes que se acaban, y tira los dados sobre las listas de Go y sobre listas de índices. La salida es la misma en cada ejecución:
```bash
commit=$(git -C ../datekeys-go rev-parse e671032)
commit=$(git -C ../datekeys-go rev-parse 92e7154)
out=$PWD/test/vectors
tmp=$(mktemp -d)
git -C ../datekeys-go archive "$commit" | tar -x -C "$tmp"
@ -816,7 +817,7 @@ rm -rf "$tmp"
| `pathrule_vectors.g.dart` | El mismo JSON como constante de Dart |
| `wordkey_vectors.json` | La llave de palabras de `wordkey`: 400 textos con sus palabras, 515 listas de palabras con el resultado de `Check`, y cuatro llaves con su contraseña P y su sal S, el PBKDF2 de 1000 iteraciones para Node.js y el recipient; la primera es el vector del §38.1 |
| `wordkey_vectors.g.dart` | El mismo JSON como constante de Dart |
| `wordlist_vectors.json` | Las listas de palabras de `wordkey` (`generate.go`), en `e671032`: el SHA-256, los bytes y las palabras de cada lista de Go; el cuerpo de `List` sobre 19 textos de una lista base, con y sin el LF final, con líneas CRLF, una línea vacía, una marca de orden de bytes y bytes que no son UTF-8 válido; `CheckList` sobre 83 listas editadas, con los casos de `TestCheckList`; `CheckList` con cada punto de código de los planos 0, 1 y 14 en la primera palabra, como el SHA-256 de sus resultados, y uno a uno hasta U+017F; `Generate` en 51 casos, sobre la lista española y listas de 12 a 2^20 + 1 palabras, con el keystream de `SeededRandomSource`, bloques de SHA-256 y bytes que se acaban, entre ellos la semilla de `TestGenerate`, que solo saca dos índices y se queda sin bytes; y `Bits`, el double de Go bit a bit, en 221 casos y cuatro resúmenes |
| `wordlist_vectors.json` | Las listas de palabras de `wordkey` (`generate.go`) y sus dados (`dice.go`), en `92e7154`: el SHA-256, los bytes y las palabras de cada lista de Go; el cuerpo de `List` sobre 19 textos de una lista base, con y sin el LF final, con líneas CRLF, una línea vacía, una marca de orden de bytes y bytes que no son UTF-8 válido; `CheckList` sobre 83 listas editadas, con los casos de `TestCheckList`; `CheckList` con cada punto de código de los planos 0, 1 y 14 en la primera palabra, como el SHA-256 de sus resultados, y uno a uno hasta U+017F; `Generate` en 51 casos, sobre la lista española y listas de 12 a 2^20 + 1 palabras, con el keystream de `SeededRandomSource`, bloques de SHA-256 y bytes que se acaban, entre ellos la semilla de `TestGenerate`, que solo saca dos índices y se queda sin bytes; `Bits`, el double de Go bit a bit, en 221 casos y cuatro resúmenes; y los dados: `DiceNumber` en 24 posiciones; `DiceWord` en 63 casos y `DiceWords` en 78, sobre las listas de Go y sobre listas de índices, con los casos de `TestDiceWord` y `TestDiceWords`, cada espacio de `unicode.IsSpace` entre los números y puntos de código y bytes que no lo son, el orden de las comprobaciones, listas con una palabra más de una vez, también con caracteres que `%q` escapa, y listas de otro tamaño; el SHA-256 de `DiceList` de cada lista, el fichero de la EFF para `en`; y `DiceWords` con cada punto de código de los planos 0, 1 y 14 entre los dos primeros números, como el SHA-256 de sus resultados |
| `wordlist_vectors.g.dart` | El mismo JSON como constante de Dart |
| `mutation_texts.json` | De `tool/mutation_go_texts.go`, port de `scripts/mutation-go-texts.go` de `datekeys-ts` sobre el `testdata/` de este repositorio: el texto del error de `capsule.Open` en cada caso del corpus de mutaciones, `ok` si se abre, y sus comprobaciones con el detalle de cada paso. Si Go y el corpus discreparan en el código o el paso de un caso, lo diría con `go_error` y `go_step`: en ninguno de los 222 casos del corpus de la v0.15 lo hacen con Go en `fe50885`, que abre cada caso con su `source`, como `testkit` |
| `open_cases.json` | `capsule.Open` sobre cada fixture con cada una de sus credenciales, y sobre fixtures editados o con otras opciones en cada paso que el corpus no alcanza: la trama, los campos, las extensiones y los vínculos de una `.dkk` en el paso 9.a, el reloj y los fallos de la fuente del release, las cabeceras `age` de los pasos 11 y 17, un stanza X25519 mal formado en `INNER_ACCESS_AGE`, `CONTROL_CBOR` sellado otra vez, `BODY` del formato 3 sellado otra vez, los sinks y la salida que fallan, el rechazo de `Accept` y las extensiones inutilizables de cada objeto. Cada caso tiene el resultado, el texto, el paso, las comprobaciones con su detalle, las peticiones del release, el estado del sink y el contenido o los ficheros, y los del formato 3 que se abren, sus veredictos con sus líneas, también con la clave de autor guardada |

@ -42,7 +42,8 @@
/// the word lists of datekeys-go, which [readWordList] takes only with the
/// SHA-256 that [wordListSha256] pins and checks against the alphabet of
/// their language, the words that [generateWords] draws from one, and their
/// strength in bits, [wordBits].
/// strength in bits, [wordBits]; and the words of five dice each,
/// [diceWords], with the list numbered for dice, [diceList].
///
/// DER, the primitives, the reading and the writing of age, the recipients
/// and the random sources, agewrap, the curve arithmetic, the IBE of tlock
@ -130,6 +131,11 @@ export 'src/wordlist.dart'
show
checkWordList,
defaultWordCount,
diceList,
diceListSize,
diceNumber,
diceWord,
diceWords,
generateWords,
minListSize,
readWordList,

@ -4,7 +4,10 @@
/// (generate.go): the word lists of datekeys-go, taken only with the
/// SHA-256 pinned here and checked against the alphabet of their language;
/// the words drawn from one, uniformly, with a [RandomSource]; and their
/// strength in bits.
/// strength in bits. And the words of dice, as DiceNumber, DiceWord,
/// DiceWords and DiceList (dice.go), for whoever does not trust the random
/// numbers of a computer: five dice for each word of a list of
/// [diceListSize] words, and the list numbered for dice, to print it.
///
/// Go builds its lists into the module; this library holds none. The app
/// downloads or bundles the file of a list, `wordkey/lists/<lang>.txt` of
@ -26,6 +29,7 @@ import 'dart:math' as math;
import 'dart:typed_data';
import 'bytes.dart';
import 'go_unicode.dart' show goIsSpace;
import 'pathrule.dart' show codePointName;
import 'random.dart';
import 'sha256.dart';
@ -296,3 +300,164 @@ double _log(double x) {
final hfsq = 0.5 * f * f;
return k * ln2Hi - ((hfsq - (s * (hfsq + r) + k * ln2Lo)) - f);
}
/// The size of a list that dice draw from, Go's DiceListSize: five dice,
/// each from 1 to 6, give 6^5 positions. Whoever does not trust the random
/// numbers of a computer rolls them, and looks the words up in the list
/// numbered for dice ([diceList]).
const diceListSize = 7776;
/// The dice of the word at position [i] of a list of [diceListSize] words,
/// as Go's DiceNumber: five digits from 1 to 6, each one more than a digit
/// of [i] in base 6, the first the most significant, so that 11111 is the
/// first word and 66666 the last, as in the list of the EFF. Throws a
/// [WordKeyException] with Go's text, `wordkey: no dice give position 7776
/// of a list of 7776 words`, for a position outside the list.
String diceNumber(int i) {
if (i < 0 || i >= diceListSize) {
throw WordKeyException(
'wordkey: no dice give position $i of a list of $diceListSize words',
);
}
final dice = Uint8List(5);
var n = i;
for (var k = dice.length - 1; k >= 0; k--) {
dice[k] = 0x31 + n % 6;
n ~/= 6;
}
return String.fromCharCodes(dice);
}
/// The word of [list] that [dice] give, as Go's DiceWord: five digits from
/// 1 to 6, as [diceNumber] numbers the words. The list must have
/// [diceListSize] words, and is taken as it is: one that [readWordList]
/// gave. [dice] is taken as its UTF-8 bytes, as Go takes a string: five
/// bytes, each a digit from 1 to 6, and nothing else, so that white space
/// around them or a full-width digit is refused. Throws a
/// [WordKeyException] with Go's text: `wordkey: dice draw from a list of
/// 7776 words, not 2048` for a list of another size, first; then
/// `wordkey: "1116" is not five dice: five digits from 1 to 6`.
String diceWord(List<String> list, String dice) =>
diceWordUtf8(list, utf8Bytes(dice));
/// [diceWord] of the bytes of a Go string, [dice].
String diceWordUtf8(List<String> list, List<int> dice) {
_checkDiceList(list);
return list[_diceIndex(dice)];
}
/// The words that [dice] give, in their order, as Go's DiceWords: one
/// number of five dice for each word, as [diceWord] reads it, separated by
/// white space, at least [minWords] of them, and never the same word twice,
/// which whoever rolls rolls again. Fair dice draw each word as
/// [generateWords] does, so the words are as strong ([wordBits]).
///
/// [dice] is split as Go's strings.Fields splits its UTF-8 bytes: at the
/// white space of Go's unicode.IsSpace, the set of spec §38.1 at which
/// [normalizeWords] splits (the space, the tab, the line feed, U+00A0,
/// U+3000 and the others), and nothing else changes: the numbers are not
/// normalized, and a combining mark stays in its number. Throws a
/// [WordKeyException] with Go's text for the first failure, in this order:
/// the size of [list], as [diceWord]; fewer than [minWords] numbers,
/// `wordkey: a key of words needs at least 6 words, not 5`; then, number by
/// number, one that is not five dice, with the text of [diceWord], or one
/// that gives a word already given, `wordkey: the dice 11111 give "abacus"
/// a second time; roll them again`.
List<String> diceWords(List<String> list, String dice) =>
diceWordsUtf8(list, utf8Bytes(dice));
/// [diceWords] of the bytes of a Go string, [dice].
List<String> diceWordsUtf8(List<String> list, List<int> dice) {
_checkDiceList(list);
final numbers = _fields(dice);
if (numbers.length < minWords) {
throw WordKeyException(
'wordkey: a key of words needs at least $minWords words, not '
'${numbers.length}',
);
}
// The words given so far: Go compares the words, not their dice, and a
// list that holds a word twice gives it twice from two numbers.
final seen = <String>{};
final words = <String>[];
for (final n in numbers) {
final w = list[_diceIndex(n)];
if (!seen.add(w)) {
// Five digits from 1 to 6, as Go's %s writes their bytes.
throw WordKeyException(
'wordkey: the dice ${String.fromCharCodes(n)} give ${_q(w)} a '
'second time; roll them again',
);
}
words.add(w);
}
return words;
}
/// [list] numbered for dice, to print it, as Go's DiceList: a line for each
/// word, its dice ([diceNumber]), a tab, the word and a line feed, as the
/// EFF publishes its list. For the English list, its UTF-8 bytes are the
/// file of the EFF, byte for byte. Throws a [WordKeyException] with Go's
/// text for a list of another size, as [diceWord].
String diceList(List<String> list) {
_checkDiceList(list);
final out = StringBuffer();
for (var i = 0; i < list.length; i++) {
out
..write(diceNumber(i))
..write('\t')
..write(list[i])
..write('\n');
}
return out.toString();
}
// The first check of DiceWord, DiceWords and DiceList of Go: the size of
// the list.
void _checkDiceList(List<String> list) {
if (list.length != diceListSize) {
throw WordKeyException(
'wordkey: dice draw from a list of $diceListSize words, not '
'${list.length}',
);
}
}
// The position that [dice], the bytes of a Go string, give in a list of
// diceListSize words, as Go's DiceWord reads them: five bytes, then each a
// digit from 1 to 6, the first the most significant digit in base 6.
int _diceIndex(List<int> dice) {
if (dice.length != 5) throw _notDice(dice);
var i = 0;
for (final c in dice) {
if (c < 0x31 || c > 0x36) throw _notDice(dice);
i = i * 6 + c - 0x31;
}
return i;
}
WordKeyException _notDice(List<int> dice) => WordKeyException(
'wordkey: ${goQuote(dice)} is not five dice: five digits from 1 to 6',
);
// The fields of [text], the bytes of a Go string, as Go's strings.Fields
// cuts them: the runs between the runes of Go's unicode.IsSpace, read as
// Go's `for range` reads them, so that a byte that is not valid UTF-8 is
// U+FFFD, which is not white space and stays in its field.
List<Uint8List> _fields(List<int> text) {
final b = text is Uint8List ? text : Uint8List.fromList(text);
final fields = <Uint8List>[];
var start = -1;
for (var i = 0; i < b.length;) {
final (r, size) = b[i] < 0x80 ? (b[i], 1) : decodeRune(b, i);
if (goIsSpace(r)) {
if (start >= 0) fields.add(Uint8List.sublistView(b, start, i));
start = -1;
} else if (start < 0) {
start = i;
}
i += size;
}
if (start >= 0) fields.add(Uint8List.sublistView(b, start));
return fields;
}

@ -5,14 +5,16 @@
/// The text of test/vectors/wordlist_vectors.json.
const wordlistVectorsJson = r'''
{
"description": "The word lists of package wordkey of the Go reference (generate.go, spec \u00a738.1) for lib/src/wordlist.dart; see tool/wordlist_go_vectors.go. Binary values and words are lower-case hex. The base list is word i = pal and three letters, a + i/676, a + i/26%26 and a + i%26; a case of size n takes its first n words, replaces the word at each at of set, and adds those of append. lists: the SHA-256, bytes and words of each built-in list. parse: the body of List on the text prefix, the base list joined by sep, and suffix (sha256 is that of the text): the words read, and ok or the error. check_list: CheckList, ok or the error (sha256 is that of the words joined by LF). alphabet: CheckList of the base list of 2048 whose first word is pala, a code point and zz, for each code point of the planes but the surrogates: the SHA-256 of the lines U+XXXX result, the code points of the words it accepts, and alphabet_results, each result up to U+017F. generate: Generate on the list of the Spanish words (list es) or on a list of size words whose indices they are, reading a stream: seeded, the keystream of SeededRandomSource of the seed; counter, SHA-256 of the label and a 32-bit big-endian counter from 0, block after block; bytes, hex repeated repeat times, which end. The indices drawn, the bytes read and the next four of an endless stream, or the error. bits: Bits, its shortest decimal and the hex of its float64. bits_digests: the SHA-256 of the 8 big-endian bytes of each Bits of the sizes and counts, inclusive, sizes outer.",
"description": "The word lists of package wordkey of the Go reference (generate.go and dice.go, spec \u00a738.1) for lib/src/wordlist.dart; see tool/wordlist_go_vectors.go. Binary values and words are lower-case hex. The base list is word i = pal and three letters, a + i/676, a + i/26%26 and a + i%26; a case of size n takes its first n words, replaces the word at each at of set, and adds those of append. lists: the SHA-256, bytes and words of each built-in list. parse: the body of List on the text prefix, the base list joined by sep, and suffix (sha256 is that of the text): the words read, and ok or the error. check_list: CheckList, ok or the error (sha256 is that of the words joined by LF). alphabet: CheckList of the base list of 2048 whose first word is pala, a code point and zz, for each code point of the planes but the surrogates: the SHA-256 of the lines U+XXXX result, the code points of the words it accepts, and alphabet_results, each result up to U+017F. generate: Generate on the list of the Spanish words (list es) or on a list of size words whose indices they are, reading a stream: seeded, the keystream of SeededRandomSource of the seed; counter, SHA-256 of the label and a 32-bit big-endian counter from 0, block after block; bytes, hex repeated repeat times, which end. The indices drawn, the bytes read and the next four of an endless stream, or the error. bits: Bits, its shortest decimal and the hex of its float64. bits_digests: the SHA-256 of the 8 big-endian bytes of each Bits of the sizes and counts, inclusive, sizes outer. dice_list_size: DiceListSize. dice_space: DiceWords of the text 11111, a code point and 11112 11113 11114 11115 11116 11121 on the list of the 7776 indices, for each code point of the planes but the surrogates: the SHA-256 of the lines U+XXXX result, and split, the code points at which the numbers are split. dice_number: DiceNumber of i, the dice or the error. dice_word and dice_words: DiceWord and DiceWords of the bytes dice, in hex, on the English or the Spanish list (list en or es) or on a list of size words, each its index in decimal, modulo mod if there is one, followed by suffix: the word or the words, as text, or the error. dice_list: DiceList of the list, the SHA-256 and the bytes of its text, or the error.",
"generator": "tool/wordlist_go_vectors.go, go1.26.8",
"source": "e671032e04a3cd0999cf073ebedf50c45b088683",
"source": "92e7154c23945c9b9a2893750b6d4d128f848d3e",
"default_count": 7,
"min_list_size": 2048,
"min_words": 6,
"min_letters": 3,
"alphabet": {"word":"pala, the code point and zz, the first word of the base list of 2048","planes":[0,1,14],"count":194560,"sha256":"9119a1741586b087cc8fdcce99b00931d34c926f5dc54d7fa0ff250402a11ba9","ok":["a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z","\u00e1","\u00e9","\u00ed","\u00f1","\u00f3","\u00fa","\u00fc"]},
"dice_list_size": 7776,
"dice_space": {"text":"11111, the code point and 11112 11113 11114 11115 11116 11121, on the list of the 7776 indices","planes":[0,1,14],"count":194560,"sha256":"cf34ff425b6da4822dd798828c6172861df2e8497502991bc96e67b36a9c64d1","split":[9,10,11,12,13,32,133,160,5760,8192,8193,8194,8195,8196,8197,8198,8199,8200,8201,8202,8232,8233,8239,8287,12288]},
"lists": [
{"lang":"en","sha256":"6d557f0693958fb5e650b68b5bee585eb82cf4da32965505c789e924743bc522","bytes":62144,"words":7776},
{"lang":"es","sha256":"ff77b487765c000da97cca58fe94a2cdb947303e7a07460614d7d95d800034fe","bytes":61843,"words":7776}
@ -790,6 +792,187 @@ const wordlistVectorsJson = r'''
{"name":"6 to 8 words of 2048 to 10000","sizes":[2048,10000],"counts":[6,8],"sha256":"9e055008813b6d2ec5b9477a777c44d30d650134cd890eeb679552403297332e"},
{"name":"0 to 600 words of 7776","sizes":[7776,7776],"counts":[0,600],"sha256":"b6e756155be0121f6db294a47b3dbb947652cc931fb078f1db5a9e39643c905a"},
{"name":"1 word of 2^32 - 4096 to 2^32 + 4096","sizes":[4294963200,4294971392],"counts":[1,1],"sha256":"a95430f39eda0ef2865e11d1b92c8b0e604e4a9387ac881905d77af880240d2d"}
],
"dice_number": [
{"i":0,"dice":"11111"},
{"i":1,"dice":"11112"},
{"i":5,"dice":"11116"},
{"i":6,"dice":"11121"},
{"i":35,"dice":"11166"},
{"i":36,"dice":"11211"},
{"i":215,"dice":"11666"},
{"i":216,"dice":"12111"},
{"i":1295,"dice":"16666"},
{"i":1296,"dice":"21111"},
{"i":2047,"dice":"24362"},
{"i":2048,"dice":"24363"},
{"i":3495,"dice":"35214"},
{"i":7774,"dice":"66665"},
{"i":7775,"dice":"66666"},
{"i":-1,"error":"wordkey: no dice give position -1 of a list of 7776 words"},
{"i":7776,"error":"wordkey: no dice give position 7776 of a list of 7776 words"},
{"i":7777,"error":"wordkey: no dice give position 7777 of a list of 7776 words"},
{"i":-7776,"error":"wordkey: no dice give position -7776 of a list of 7776 words"},
{"i":2147483648,"error":"wordkey: no dice give position 2147483648 of a list of 7776 words"},
{"i":-2147483648,"error":"wordkey: no dice give position -2147483648 of a list of 7776 words"},
{"i":4294967296,"error":"wordkey: no dice give position 4294967296 of a list of 7776 words"},
{"i":9007199254740991,"error":"wordkey: no dice give position 9007199254740991 of a list of 7776 words"},
{"i":-9007199254740991,"error":"wordkey: no dice give position -9007199254740991 of a list of 7776 words"}
],
"dice_word": [
{"name":"en 11111","list":"en","size":7776,"dice":"3131313131","word":"abacus"},
{"name":"en 11112","list":"en","size":7776,"dice":"3131313132","word":"abdomen"},
{"name":"en 35214","list":"en","size":7776,"dice":"3335323134","word":"jovial"},
{"name":"en 66666","list":"en","size":7776,"dice":"3636363636","word":"zoom"},
{"name":"es 11111","list":"es","size":7776,"dice":"3131313131","word":"abad"},
{"name":"es 35214","list":"es","size":7776,"dice":"3335323134","word":"glaciar"},
{"name":"es 66666","list":"es","size":7776,"dice":"3636363636","word":"\u00fatil"},
{"name":"11111","size":7776,"dice":"3131313131","word":"0"},
{"name":"11112","size":7776,"dice":"3131313132","word":"1"},
{"name":"11116","size":7776,"dice":"3131313136","word":"5"},
{"name":"11121","size":7776,"dice":"3131313231","word":"6"},
{"name":"11166","size":7776,"dice":"3131313636","word":"35"},
{"name":"11211","size":7776,"dice":"3131323131","word":"36"},
{"name":"16666","size":7776,"dice":"3136363636","word":"1295"},
{"name":"21111","size":7776,"dice":"3231313131","word":"1296"},
{"name":"12345","size":7776,"dice":"3132333435","word":"310"},
{"name":"35214","size":7776,"dice":"3335323134","word":"3495"},
{"name":"65432","size":7776,"dice":"3635343332","word":"7465"},
{"name":"66665","size":7776,"dice":"3636363635","word":"7774"},
{"name":"66666","size":7776,"dice":"3636363636","word":"7775"},
{"name":"no dice","size":7776,"dice":"","error":"wordkey: \"\" is not five dice: five digits from 1 to 6"},
{"name":"four dice","size":7776,"dice":"31313131","error":"wordkey: \"1111\" is not five dice: five digits from 1 to 6"},
{"name":"six dice","size":7776,"dice":"313131313131","error":"wordkey: \"111111\" is not five dice: five digits from 1 to 6"},
{"name":"a 0","size":7776,"dice":"3131313130","error":"wordkey: \"11110\" is not five dice: five digits from 1 to 6"},
{"name":"a 7","size":7776,"dice":"3131313137","error":"wordkey: \"11117\" is not five dice: five digits from 1 to 6"},
{"name":"a letter","size":7776,"dice":"6131313131","error":"wordkey: \"a1111\" is not five dice: five digits from 1 to 6"},
{"name":"four dice and a space","size":7776,"dice":"3131313120","error":"wordkey: \"1111 \" is not five dice: five digits from 1 to 6"},
{"name":"a full-width 1 and four dice","size":7776,"dice":"efbc9131313131","error":"wordkey: \"\uff111111\" is not five dice: five digits from 1 to 6"},
{"name":"a space and four dice","size":7776,"dice":"2031313131","error":"wordkey: \" 1111\" is not five dice: five digits from 1 to 6"},
{"name":"a space and five dice","size":7776,"dice":"203131313131","error":"wordkey: \" 11111\" is not five dice: five digits from 1 to 6"},
{"name":"five dice and a space","size":7776,"dice":"313131313120","error":"wordkey: \"11111 \" is not five dice: five digits from 1 to 6"},
{"name":"five dice and LF","size":7776,"dice":"31313131310a","error":"wordkey: \"11111\\n\" is not five dice: five digits from 1 to 6"},
{"name":"a tab and four dice","size":7776,"dice":"0931313131","error":"wordkey: \"\\t1111\" is not five dice: five digits from 1 to 6"},
{"name":"two and two dice around a space","size":7776,"dice":"3131203131","error":"wordkey: \"11 11\" is not five dice: five digits from 1 to 6"},
{"name":"NUL","size":7776,"dice":"3131313100","error":"wordkey: \"1111\\x00\" is not five dice: five digits from 1 to 6"},
{"name":"DEL","size":7776,"dice":"313131317f","error":"wordkey: \"1111\\x7f\" is not five dice: five digits from 1 to 6"},
{"name":"a quotation mark","size":7776,"dice":"3131313122","error":"wordkey: \"1111\\\"\" is not five dice: five digits from 1 to 6"},
{"name":"a backslash","size":7776,"dice":"313131315c","error":"wordkey: \"1111\\\\\" is not five dice: five digits from 1 to 6"},
{"name":"00000","size":7776,"dice":"3030303030","error":"wordkey: \"00000\" is not five dice: five digits from 1 to 6"},
{"name":"77777","size":7776,"dice":"3737373737","error":"wordkey: \"77777\" is not five dice: five digits from 1 to 6"},
{"name":"99999","size":7776,"dice":"3939393939","error":"wordkey: \"99999\" is not five dice: five digits from 1 to 6"},
{"name":"a slash, the byte before 0","size":7776,"dice":"313131312f","error":"wordkey: \"1111/\" is not five dice: five digits from 1 to 6"},
{"name":"a colon, the byte after 9","size":7776,"dice":"313131313a","error":"wordkey: \"1111:\" is not five dice: five digits from 1 to 6"},
{"name":"five full-width digits","size":7776,"dice":"efbc91efbc92efbc93efbc94efbc95","error":"wordkey: \"\uff11\uff12\uff13\uff14\uff15\" is not five dice: five digits from 1 to 6"},
{"name":"five Arabic-Indic digits","size":7776,"dice":"d9a1d9a2d9a3d9a4d9a5","error":"wordkey: \"\u0661\u0662\u0663\u0664\u0665\" is not five dice: five digits from 1 to 6"},
{"name":"a superscript 1","size":7776,"dice":"31313131c2b9","error":"wordkey: \"1111\u00b9\" is not five dice: five digits from 1 to 6"},
{"name":"a circled 1 and two dice: five bytes","size":7776,"dice":"e291a03131","error":"wordkey: \"\u246011\" is not five dice: five digits from 1 to 6"},
{"name":"a mathematical bold 1 and a die: five bytes","size":7776,"dice":"f09d9f8f31","error":"wordkey: \"\ud835\udfcf1\" is not five dice: five digits from 1 to 6"},
{"name":"four dice and an e with an acute accent: five characters","size":7776,"dice":"31313131c3a9","error":"wordkey: \"1111\u00e9\" is not five dice: five digits from 1 to 6"},
{"name":"three dice and an e with an acute accent: five bytes","size":7776,"dice":"313131c3a9","error":"wordkey: \"111\u00e9\" is not five dice: five digits from 1 to 6"},
{"name":"five dice and a combining acute accent","size":7776,"dice":"3131313131cc81","error":"wordkey: \"11111\u0301\" is not five dice: five digits from 1 to 6"},
{"name":"three dice and the emoji of a die: five UTF-16 code units","size":7776,"dice":"313131f09f8eb2","error":"wordkey: \"111\ud83c\udfb2\" is not five dice: five digits from 1 to 6"},
{"name":"NBSP and four dice","size":7776,"dice":"c2a031313131","error":"wordkey: \"\\u00a01111\" is not five dice: five digits from 1 to 6"},
{"name":"an ideographic space and five dice","size":7776,"dice":"e380803131313131","error":"wordkey: \"\\u300011111\" is not five dice: five digits from 1 to 6"},
{"name":"two dice and a surrogate in UTF-8 bytes: five bytes","size":7776,"dice":"3131eda080","error":"wordkey: \"11\\xed\\xa0\\x80\" is not five dice: five digits from 1 to 6"},
{"name":"four dice and a byte that is not valid UTF-8","size":7776,"dice":"31313131ff","error":"wordkey: \"1111\\xff\" is not five dice: five digits from 1 to 6"},
{"name":"five bytes that are not valid UTF-8","size":7776,"dice":"fffefdfcfb","error":"wordkey: \"\\xff\\xfe\\xfd\\xfc\\xfb\" is not five dice: five digits from 1 to 6"},
{"name":"four dice and a sequence cut short","size":7776,"dice":"31313131c3","error":"wordkey: \"1111\\xc3\" is not five dice: five digits from 1 to 6"},
{"name":"\"11111\" on a list of 2048 words","size":2048,"dice":"3131313131","error":"wordkey: dice draw from a list of 7776 words, not 2048"},
{"name":"\"11111\" on a list of 7775 words","size":7775,"dice":"3131313131","error":"wordkey: dice draw from a list of 7776 words, not 7775"},
{"name":"\"66666\" on a list of 7777 words","size":7777,"dice":"3636363636","error":"wordkey: dice draw from a list of 7776 words, not 7777"},
{"name":"\"\" on a list of 0 words","size":0,"dice":"","error":"wordkey: dice draw from a list of 7776 words, not 0"},
{"name":"\"x\" on a list of 1 words","size":1,"dice":"78","error":"wordkey: dice draw from a list of 7776 words, not 1"}
],
"dice_words": [
{"name":"en, seven numbers among white space","list":"en","size":7776,"dice":"2031313131312031313131320931313131330a313131313420313131313520313131313620313131323120","words":["abacus","abdomen","abdominal","abide","abiding","ability","ablaze"]},
{"name":"en, five numbers","list":"en","size":7776,"dice":"3131313131203131313132203131313133203131313134203131313135","error":"wordkey: a key of words needs at least 6 words, not 5"},
{"name":"en, no numbers","list":"en","size":7776,"dice":"","error":"wordkey: a key of words needs at least 6 words, not 0"},
{"name":"en, four dice","list":"en","size":7776,"dice":"31313131312031313131322031313131332031313131342031313131352031313136","error":"wordkey: \"1116\" is not five dice: five digits from 1 to 6"},
{"name":"en, 11111 twice","list":"en","size":7776,"dice":"3131313131203131313132203131313133203131313134203131313135203131313131","error":"wordkey: the dice 11111 give \"abacus\" a second time; roll them again"},
{"name":"en, a comma between two numbers","list":"en","size":7776,"dice":"31313131312c3131313132203131313133203131313134203131313135203131313136203131313231","error":"wordkey: \"11111,11112\" is not five dice: five digits from 1 to 6"},
{"name":"seven numbers among white space","size":7776,"dice":"2031313131312031313131320931313131330a313131313420313131313520313131313620313131323120","words":["0","1","2","3","4","5","6"]},
{"name":"five numbers","size":7776,"dice":"3131313131203131313132203131313133203131313134203131313135","error":"wordkey: a key of words needs at least 6 words, not 5"},
{"name":"no numbers","size":7776,"dice":"","error":"wordkey: a key of words needs at least 6 words, not 0"},
{"name":"four dice","size":7776,"dice":"31313131312031313131322031313131332031313131342031313131352031313136","error":"wordkey: \"1116\" is not five dice: five digits from 1 to 6"},
{"name":"11111 twice","size":7776,"dice":"3131313131203131313132203131313133203131313134203131313135203131313131","error":"wordkey: the dice 11111 give \"0\" a second time; roll them again"},
{"name":"a comma between two numbers","size":7776,"dice":"31313131312c3131313132203131313133203131313134203131313135203131313136203131313231","error":"wordkey: \"11111,11112\" is not five dice: five digits from 1 to 6"},
{"name":"a list of 7775 words","size":7775,"dice":"3131313131203131313132203131313133203131313134203131313135203131313136","error":"wordkey: dice draw from a list of 7776 words, not 7775"},
{"name":"es, seven numbers","list":"es","size":7776,"dice":"3636363636203131313132203335323134203132333435203534333231203233343536203131313131","words":["\u00fatil","abad\u00eda","glaciar","alimentar","propicio","contener","abad"]},
{"name":"es, 66666 twice","list":"es","size":7776,"dice":"3636363636203131313131203335323134203132333435203534333231203233343536203636363636","error":"wordkey: the dice 66666 give \"\u00fatil\" a second time; roll them again"},
{"name":"six numbers","size":7776,"dice":"3131313132203131313133203131313134203131313135203131313136203131313231","words":["1","2","3","4","5","6"]},
{"name":"twelve numbers","size":7776,"dice":"3131313131203232323232203333333333203434343434203535353535203636363636203132333435203233343536203334353631203435363132203536313233203631323334","words":["0","1555","3110","4665","6220","7775","310","1865","3414","4933","6272","6531"]},
{"name":"white space only","size":7776,"dice":"20090ac2a0e3808020","error":"wordkey: a key of words needs at least 6 words, not 0"},
{"name":"U+0009 between the numbers","size":7776,"dice":"3131313131093131313132093131313133093131313134093131313135093131313136093131313231","words":["0","1","2","3","4","5","6"]},
{"name":"U+000A between the numbers","size":7776,"dice":"31313131310a31313131320a31313131330a31313131340a31313131350a31313131360a3131313231","words":["0","1","2","3","4","5","6"]},
{"name":"U+000B between the numbers","size":7776,"dice":"31313131310b31313131320b31313131330b31313131340b31313131350b31313131360b3131313231","words":["0","1","2","3","4","5","6"]},
{"name":"U+000C between the numbers","size":7776,"dice":"31313131310c31313131320c31313131330c31313131340c31313131350c31313131360c3131313231","words":["0","1","2","3","4","5","6"]},
{"name":"U+000D between the numbers","size":7776,"dice":"31313131310d31313131320d31313131330d31313131340d31313131350d31313131360d3131313231","words":["0","1","2","3","4","5","6"]},
{"name":"U+0020 between the numbers","size":7776,"dice":"3131313131203131313132203131313133203131313134203131313135203131313136203131313231","words":["0","1","2","3","4","5","6"]},
{"name":"U+0085 between the numbers","size":7776,"dice":"3131313131c2853131313132c2853131313133c2853131313134c2853131313135c2853131313136c2853131313231","words":["0","1","2","3","4","5","6"]},
{"name":"U+00A0 between the numbers","size":7776,"dice":"3131313131c2a03131313132c2a03131313133c2a03131313134c2a03131313135c2a03131313136c2a03131313231","words":["0","1","2","3","4","5","6"]},
{"name":"U+1680 between the numbers","size":7776,"dice":"3131313131e19a803131313132e19a803131313133e19a803131313134e19a803131313135e19a803131313136e19a803131313231","words":["0","1","2","3","4","5","6"]},
{"name":"U+2000 between the numbers","size":7776,"dice":"3131313131e280803131313132e280803131313133e280803131313134e280803131313135e280803131313136e280803131313231","words":["0","1","2","3","4","5","6"]},
{"name":"U+2001 between the numbers","size":7776,"dice":"3131313131e280813131313132e280813131313133e280813131313134e280813131313135e280813131313136e280813131313231","words":["0","1","2","3","4","5","6"]},
{"name":"U+2002 between the numbers","size":7776,"dice":"3131313131e280823131313132e280823131313133e280823131313134e280823131313135e280823131313136e280823131313231","words":["0","1","2","3","4","5","6"]},
{"name":"U+2003 between the numbers","size":7776,"dice":"3131313131e280833131313132e280833131313133e280833131313134e280833131313135e280833131313136e280833131313231","words":["0","1","2","3","4","5","6"]},
{"name":"U+2004 between the numbers","size":7776,"dice":"3131313131e280843131313132e280843131313133e280843131313134e280843131313135e280843131313136e280843131313231","words":["0","1","2","3","4","5","6"]},
{"name":"U+2005 between the numbers","size":7776,"dice":"3131313131e280853131313132e280853131313133e280853131313134e280853131313135e280853131313136e280853131313231","words":["0","1","2","3","4","5","6"]},
{"name":"U+2006 between the numbers","size":7776,"dice":"3131313131e280863131313132e280863131313133e280863131313134e280863131313135e280863131313136e280863131313231","words":["0","1","2","3","4","5","6"]},
{"name":"U+2007 between the numbers","size":7776,"dice":"3131313131e280873131313132e280873131313133e280873131313134e280873131313135e280873131313136e280873131313231","words":["0","1","2","3","4","5","6"]},
{"name":"U+2008 between the numbers","size":7776,"dice":"3131313131e280883131313132e280883131313133e280883131313134e280883131313135e280883131313136e280883131313231","words":["0","1","2","3","4","5","6"]},
{"name":"U+2009 between the numbers","size":7776,"dice":"3131313131e280893131313132e280893131313133e280893131313134e280893131313135e280893131313136e280893131313231","words":["0","1","2","3","4","5","6"]},
{"name":"U+200A between the numbers","size":7776,"dice":"3131313131e2808a3131313132e2808a3131313133e2808a3131313134e2808a3131313135e2808a3131313136e2808a3131313231","words":["0","1","2","3","4","5","6"]},
{"name":"U+2028 between the numbers","size":7776,"dice":"3131313131e280a83131313132e280a83131313133e280a83131313134e280a83131313135e280a83131313136e280a83131313231","words":["0","1","2","3","4","5","6"]},
{"name":"U+2029 between the numbers","size":7776,"dice":"3131313131e280a93131313132e280a93131313133e280a93131313134e280a93131313135e280a93131313136e280a93131313231","words":["0","1","2","3","4","5","6"]},
{"name":"U+202F between the numbers","size":7776,"dice":"3131313131e280af3131313132e280af3131313133e280af3131313134e280af3131313135e280af3131313136e280af3131313231","words":["0","1","2","3","4","5","6"]},
{"name":"U+205F between the numbers","size":7776,"dice":"3131313131e2819f3131313132e2819f3131313133e2819f3131313134e2819f3131313135e2819f3131313136e2819f3131313231","words":["0","1","2","3","4","5","6"]},
{"name":"U+3000 between the numbers","size":7776,"dice":"3131313131e380803131313132e380803131313133e380803131313134e380803131313135e380803131313136e380803131313231","words":["0","1","2","3","4","5","6"]},
{"name":"U+001C between the first two numbers","size":7776,"dice":"31313131311c3131313132203131313133203131313134203131313135203131313136203131313231","error":"wordkey: \"11111\\x1c11112\" is not five dice: five digits from 1 to 6"},
{"name":"U+001D between the first two numbers","size":7776,"dice":"31313131311d3131313132203131313133203131313134203131313135203131313136203131313231","error":"wordkey: \"11111\\x1d11112\" is not five dice: five digits from 1 to 6"},
{"name":"U+001E between the first two numbers","size":7776,"dice":"31313131311e3131313132203131313133203131313134203131313135203131313136203131313231","error":"wordkey: \"11111\\x1e11112\" is not five dice: five digits from 1 to 6"},
{"name":"U+001F between the first two numbers","size":7776,"dice":"31313131311f3131313132203131313133203131313134203131313135203131313136203131313231","error":"wordkey: \"11111\\x1f11112\" is not five dice: five digits from 1 to 6"},
{"name":"U+00AD between the first two numbers","size":7776,"dice":"3131313131c2ad3131313132203131313133203131313134203131313135203131313136203131313231","error":"wordkey: \"11111\\u00ad11112\" is not five dice: five digits from 1 to 6"},
{"name":"U+0301 between the first two numbers","size":7776,"dice":"3131313131cc813131313132203131313133203131313134203131313135203131313136203131313231","error":"wordkey: \"11111\u030111112\" is not five dice: five digits from 1 to 6"},
{"name":"U+180E between the first two numbers","size":7776,"dice":"3131313131e1a08e3131313132203131313133203131313134203131313135203131313136203131313231","error":"wordkey: \"11111\\u180e11112\" is not five dice: five digits from 1 to 6"},
{"name":"U+200B between the first two numbers","size":7776,"dice":"3131313131e2808b3131313132203131313133203131313134203131313135203131313136203131313231","error":"wordkey: \"11111\\u200b11112\" is not five dice: five digits from 1 to 6"},
{"name":"U+200C between the first two numbers","size":7776,"dice":"3131313131e2808c3131313132203131313133203131313134203131313135203131313136203131313231","error":"wordkey: \"11111\\u200c11112\" is not five dice: five digits from 1 to 6"},
{"name":"U+200D between the first two numbers","size":7776,"dice":"3131313131e2808d3131313132203131313133203131313134203131313135203131313136203131313231","error":"wordkey: \"11111\\u200d11112\" is not five dice: five digits from 1 to 6"},
{"name":"U+2060 between the first two numbers","size":7776,"dice":"3131313131e281a03131313132203131313133203131313134203131313135203131313136203131313231","error":"wordkey: \"11111\\u206011112\" is not five dice: five digits from 1 to 6"},
{"name":"U+2800 between the first two numbers","size":7776,"dice":"3131313131e2a0803131313132203131313133203131313134203131313135203131313136203131313231","error":"wordkey: \"11111\u280011112\" is not five dice: five digits from 1 to 6"},
{"name":"U+3164 between the first two numbers","size":7776,"dice":"3131313131e385a43131313132203131313133203131313134203131313135203131313136203131313231","error":"wordkey: \"11111\u316411112\" is not five dice: five digits from 1 to 6"},
{"name":"U+FEFF between the first two numbers","size":7776,"dice":"3131313131efbbbf3131313132203131313133203131313134203131313135203131313136203131313231","error":"wordkey: \"11111\\ufeff11112\" is not five dice: five digits from 1 to 6"},
{"name":"U+FFFD between the first two numbers","size":7776,"dice":"3131313131efbfbd3131313132203131313133203131313134203131313135203131313136203131313231","error":"wordkey: \"11111\ufffd11112\" is not five dice: five digits from 1 to 6"},
{"name":"the byte A0 alone, which is not NBSP","size":7776,"dice":"3131313131a03131313132203131313133203131313134203131313135203131313136203131313231","error":"wordkey: \"11111\\xa011112\" is not five dice: five digits from 1 to 6"},
{"name":"the byte 85 alone, which is not NEL","size":7776,"dice":"3131313131853131313132203131313133203131313134203131313135203131313136203131313231","error":"wordkey: \"11111\\x8511112\" is not five dice: five digits from 1 to 6"},
{"name":"a byte that is not valid UTF-8 after a number","size":7776,"dice":"3131313131ff203131313132203131313133203131313134203131313135203131313136203131313231","error":"wordkey: \"11111\\xff\" is not five dice: five digits from 1 to 6"},
{"name":"a surrogate in UTF-8 bytes after a number","size":7776,"dice":"3131313131eda080203131313132203131313133203131313134203131313135203131313136203131313231","error":"wordkey: \"11111\\xed\\xa0\\x80\" is not five dice: five digits from 1 to 6"},
{"name":"five full-width digits","size":7776,"dice":"efbc91efbc91efbc91efbc91efbc91203131313132203131313133203131313134203131313135203131313136203131313231","error":"wordkey: \"\uff11\uff11\uff11\uff11\uff11\" is not five dice: five digits from 1 to 6"},
{"name":"a combining acute accent after a number","size":7776,"dice":"3131313131cc81203131313132203131313133203131313134203131313135203131313136203131313231","error":"wordkey: \"11111\u0301\" is not five dice: five digits from 1 to 6"},
{"name":"a combining acute accent before a number","size":7776,"dice":"cc813131313131203131313132203131313133203131313134203131313135203131313136203131313231","error":"wordkey: \"\u030111111\" is not five dice: five digits from 1 to 6"},
{"name":"five fields that are not dice: the count first","size":7776,"dice":"612062206320642065","error":"wordkey: a key of words needs at least 6 words, not 5"},
{"name":"six fields, the first not dice","size":7776,"dice":"616263203131313131203131313132203131313133203131313134203131313135","error":"wordkey: \"abc\" is not five dice: five digits from 1 to 6"},
{"name":"not dice before a word given twice","size":7776,"dice":"313131313120313131313220616263203131313131203131313133203131313134","error":"wordkey: \"abc\" is not five dice: five digits from 1 to 6"},
{"name":"a word given twice before not dice","size":7776,"dice":"313131313120313131313220313131313320313131313420313131313520313131313120616263","error":"wordkey: the dice 11111 give \"0\" a second time; roll them again"},
{"name":"indices modulo 1: 11112 11113 11114 11115 11116 11121","size":7776,"mod":1,"dice":"3131313132203131313133203131313134203131313135203131313136203131313231","error":"wordkey: the dice 11113 give \"0\" a second time; roll them again"},
{"name":"indices modulo 7: 11111 11112 11113 11114 11115 11116 11121","size":7776,"mod":7,"dice":"3131313131203131313132203131313133203131313134203131313135203131313136203131313231","words":["0","1","2","3","4","5","6"]},
{"name":"indices modulo 7: 11111 11112 11113 11114 11115 11116 11121 11122","size":7776,"mod":7,"dice":"3131313131203131313132203131313133203131313134203131313135203131313136203131313231203131313232","error":"wordkey: the dice 11122 give \"0\" a second time; roll them again"},
{"name":"indices modulo 1000: 11111 11112 11113 11114 11115 15455","size":7776,"mod":1000,"dice":"3131313131203131313132203131313133203131313134203131313135203135343535","error":"wordkey: the dice 15455 give \"0\" a second time; roll them again"},
{"name":"indices modulo 7 and a suffix: 11111 11112 11113 11114 11115 11116 11121","size":7776,"mod":7,"suffix":"\"\\\t\u00a0\u200b","dice":"3131313131203131313132203131313133203131313134203131313135203131313136203131313231","words":["0\"\\\t\u00a0\u200b","1\"\\\t\u00a0\u200b","2\"\\\t\u00a0\u200b","3\"\\\t\u00a0\u200b","4\"\\\t\u00a0\u200b","5\"\\\t\u00a0\u200b","6\"\\\t\u00a0\u200b"]},
{"name":"indices modulo 7 and a suffix: 11111 11112 11113 11114 11115 11116 11121 11122","size":7776,"mod":7,"suffix":"\"\\\t\u00a0\u200b","dice":"3131313131203131313132203131313133203131313134203131313135203131313136203131313231203131313232","error":"wordkey: the dice 11122 give \"0\\\"\\\\\\t\\u00a0\\u200b\" a second time; roll them again"},
{"name":"\"\" on a list of 0 words","size":0,"dice":"","error":"wordkey: dice draw from a list of 7776 words, not 0"},
{"name":"\"11111 11112 11113 11114 11115 11116 11121\" on a list of 2048 words","size":2048,"dice":"3131313131203131313132203131313133203131313134203131313135203131313136203131313231","error":"wordkey: dice draw from a list of 7776 words, not 2048"},
{"name":"\"x\" on a list of 7777 words","size":7777,"dice":"78","error":"wordkey: dice draw from a list of 7776 words, not 7777"}
],
"dice_list": [
{"list":"en","size":7776,"sha256":"addd35536511597a02fa0a9ff1e5284677b8883b83e986e43f15a3db996b903e","bytes":108800},
{"list":"es","size":7776,"sha256":"611f779a33df74587e9dfb486bb0185a9dfd4d1384e75993a21247ad31cefddb","bytes":108499},
{"size":7776,"sha256":"267618e6e9109b66da2804c1d77097273dd6bc3144bf60fd35988322293db59e","bytes":84426},
{"size":0,"error":"wordkey: dice draw from a list of 7776 words, not 0"},
{"size":1,"error":"wordkey: dice draw from a list of 7776 words, not 1"},
{"size":2048,"error":"wordkey: dice draw from a list of 7776 words, not 2048"},
{"size":7775,"error":"wordkey: dice draw from a list of 7776 words, not 7775"},
{"size":7777,"error":"wordkey: dice draw from a list of 7776 words, not 7777"}
]
}
''';

@ -1,12 +1,14 @@
{
"description": "The word lists of package wordkey of the Go reference (generate.go, spec \u00a738.1) for lib/src/wordlist.dart; see tool/wordlist_go_vectors.go. Binary values and words are lower-case hex. The base list is word i = pal and three letters, a + i/676, a + i/26%26 and a + i%26; a case of size n takes its first n words, replaces the word at each at of set, and adds those of append. lists: the SHA-256, bytes and words of each built-in list. parse: the body of List on the text prefix, the base list joined by sep, and suffix (sha256 is that of the text): the words read, and ok or the error. check_list: CheckList, ok or the error (sha256 is that of the words joined by LF). alphabet: CheckList of the base list of 2048 whose first word is pala, a code point and zz, for each code point of the planes but the surrogates: the SHA-256 of the lines U+XXXX result, the code points of the words it accepts, and alphabet_results, each result up to U+017F. generate: Generate on the list of the Spanish words (list es) or on a list of size words whose indices they are, reading a stream: seeded, the keystream of SeededRandomSource of the seed; counter, SHA-256 of the label and a 32-bit big-endian counter from 0, block after block; bytes, hex repeated repeat times, which end. The indices drawn, the bytes read and the next four of an endless stream, or the error. bits: Bits, its shortest decimal and the hex of its float64. bits_digests: the SHA-256 of the 8 big-endian bytes of each Bits of the sizes and counts, inclusive, sizes outer.",
"description": "The word lists of package wordkey of the Go reference (generate.go and dice.go, spec \u00a738.1) for lib/src/wordlist.dart; see tool/wordlist_go_vectors.go. Binary values and words are lower-case hex. The base list is word i = pal and three letters, a + i/676, a + i/26%26 and a + i%26; a case of size n takes its first n words, replaces the word at each at of set, and adds those of append. lists: the SHA-256, bytes and words of each built-in list. parse: the body of List on the text prefix, the base list joined by sep, and suffix (sha256 is that of the text): the words read, and ok or the error. check_list: CheckList, ok or the error (sha256 is that of the words joined by LF). alphabet: CheckList of the base list of 2048 whose first word is pala, a code point and zz, for each code point of the planes but the surrogates: the SHA-256 of the lines U+XXXX result, the code points of the words it accepts, and alphabet_results, each result up to U+017F. generate: Generate on the list of the Spanish words (list es) or on a list of size words whose indices they are, reading a stream: seeded, the keystream of SeededRandomSource of the seed; counter, SHA-256 of the label and a 32-bit big-endian counter from 0, block after block; bytes, hex repeated repeat times, which end. The indices drawn, the bytes read and the next four of an endless stream, or the error. bits: Bits, its shortest decimal and the hex of its float64. bits_digests: the SHA-256 of the 8 big-endian bytes of each Bits of the sizes and counts, inclusive, sizes outer. dice_list_size: DiceListSize. dice_space: DiceWords of the text 11111, a code point and 11112 11113 11114 11115 11116 11121 on the list of the 7776 indices, for each code point of the planes but the surrogates: the SHA-256 of the lines U+XXXX result, and split, the code points at which the numbers are split. dice_number: DiceNumber of i, the dice or the error. dice_word and dice_words: DiceWord and DiceWords of the bytes dice, in hex, on the English or the Spanish list (list en or es) or on a list of size words, each its index in decimal, modulo mod if there is one, followed by suffix: the word or the words, as text, or the error. dice_list: DiceList of the list, the SHA-256 and the bytes of its text, or the error.",
"generator": "tool/wordlist_go_vectors.go, go1.26.8",
"source": "e671032e04a3cd0999cf073ebedf50c45b088683",
"source": "92e7154c23945c9b9a2893750b6d4d128f848d3e",
"default_count": 7,
"min_list_size": 2048,
"min_words": 6,
"min_letters": 3,
"alphabet": {"word":"pala, the code point and zz, the first word of the base list of 2048","planes":[0,1,14],"count":194560,"sha256":"9119a1741586b087cc8fdcce99b00931d34c926f5dc54d7fa0ff250402a11ba9","ok":["a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z","\u00e1","\u00e9","\u00ed","\u00f1","\u00f3","\u00fa","\u00fc"]},
"dice_list_size": 7776,
"dice_space": {"text":"11111, the code point and 11112 11113 11114 11115 11116 11121, on the list of the 7776 indices","planes":[0,1,14],"count":194560,"sha256":"cf34ff425b6da4822dd798828c6172861df2e8497502991bc96e67b36a9c64d1","split":[9,10,11,12,13,32,133,160,5760,8192,8193,8194,8195,8196,8197,8198,8199,8200,8201,8202,8232,8233,8239,8287,12288]},
"lists": [
{"lang":"en","sha256":"6d557f0693958fb5e650b68b5bee585eb82cf4da32965505c789e924743bc522","bytes":62144,"words":7776},
{"lang":"es","sha256":"ff77b487765c000da97cca58fe94a2cdb947303e7a07460614d7d95d800034fe","bytes":61843,"words":7776}
@ -784,5 +786,186 @@
{"name":"6 to 8 words of 2048 to 10000","sizes":[2048,10000],"counts":[6,8],"sha256":"9e055008813b6d2ec5b9477a777c44d30d650134cd890eeb679552403297332e"},
{"name":"0 to 600 words of 7776","sizes":[7776,7776],"counts":[0,600],"sha256":"b6e756155be0121f6db294a47b3dbb947652cc931fb078f1db5a9e39643c905a"},
{"name":"1 word of 2^32 - 4096 to 2^32 + 4096","sizes":[4294963200,4294971392],"counts":[1,1],"sha256":"a95430f39eda0ef2865e11d1b92c8b0e604e4a9387ac881905d77af880240d2d"}
],
"dice_number": [
{"i":0,"dice":"11111"},
{"i":1,"dice":"11112"},
{"i":5,"dice":"11116"},
{"i":6,"dice":"11121"},
{"i":35,"dice":"11166"},
{"i":36,"dice":"11211"},
{"i":215,"dice":"11666"},
{"i":216,"dice":"12111"},
{"i":1295,"dice":"16666"},
{"i":1296,"dice":"21111"},
{"i":2047,"dice":"24362"},
{"i":2048,"dice":"24363"},
{"i":3495,"dice":"35214"},
{"i":7774,"dice":"66665"},
{"i":7775,"dice":"66666"},
{"i":-1,"error":"wordkey: no dice give position -1 of a list of 7776 words"},
{"i":7776,"error":"wordkey: no dice give position 7776 of a list of 7776 words"},
{"i":7777,"error":"wordkey: no dice give position 7777 of a list of 7776 words"},
{"i":-7776,"error":"wordkey: no dice give position -7776 of a list of 7776 words"},
{"i":2147483648,"error":"wordkey: no dice give position 2147483648 of a list of 7776 words"},
{"i":-2147483648,"error":"wordkey: no dice give position -2147483648 of a list of 7776 words"},
{"i":4294967296,"error":"wordkey: no dice give position 4294967296 of a list of 7776 words"},
{"i":9007199254740991,"error":"wordkey: no dice give position 9007199254740991 of a list of 7776 words"},
{"i":-9007199254740991,"error":"wordkey: no dice give position -9007199254740991 of a list of 7776 words"}
],
"dice_word": [
{"name":"en 11111","list":"en","size":7776,"dice":"3131313131","word":"abacus"},
{"name":"en 11112","list":"en","size":7776,"dice":"3131313132","word":"abdomen"},
{"name":"en 35214","list":"en","size":7776,"dice":"3335323134","word":"jovial"},
{"name":"en 66666","list":"en","size":7776,"dice":"3636363636","word":"zoom"},
{"name":"es 11111","list":"es","size":7776,"dice":"3131313131","word":"abad"},
{"name":"es 35214","list":"es","size":7776,"dice":"3335323134","word":"glaciar"},
{"name":"es 66666","list":"es","size":7776,"dice":"3636363636","word":"\u00fatil"},
{"name":"11111","size":7776,"dice":"3131313131","word":"0"},
{"name":"11112","size":7776,"dice":"3131313132","word":"1"},
{"name":"11116","size":7776,"dice":"3131313136","word":"5"},
{"name":"11121","size":7776,"dice":"3131313231","word":"6"},
{"name":"11166","size":7776,"dice":"3131313636","word":"35"},
{"name":"11211","size":7776,"dice":"3131323131","word":"36"},
{"name":"16666","size":7776,"dice":"3136363636","word":"1295"},
{"name":"21111","size":7776,"dice":"3231313131","word":"1296"},
{"name":"12345","size":7776,"dice":"3132333435","word":"310"},
{"name":"35214","size":7776,"dice":"3335323134","word":"3495"},
{"name":"65432","size":7776,"dice":"3635343332","word":"7465"},
{"name":"66665","size":7776,"dice":"3636363635","word":"7774"},
{"name":"66666","size":7776,"dice":"3636363636","word":"7775"},
{"name":"no dice","size":7776,"dice":"","error":"wordkey: \"\" is not five dice: five digits from 1 to 6"},
{"name":"four dice","size":7776,"dice":"31313131","error":"wordkey: \"1111\" is not five dice: five digits from 1 to 6"},
{"name":"six dice","size":7776,"dice":"313131313131","error":"wordkey: \"111111\" is not five dice: five digits from 1 to 6"},
{"name":"a 0","size":7776,"dice":"3131313130","error":"wordkey: \"11110\" is not five dice: five digits from 1 to 6"},
{"name":"a 7","size":7776,"dice":"3131313137","error":"wordkey: \"11117\" is not five dice: five digits from 1 to 6"},
{"name":"a letter","size":7776,"dice":"6131313131","error":"wordkey: \"a1111\" is not five dice: five digits from 1 to 6"},
{"name":"four dice and a space","size":7776,"dice":"3131313120","error":"wordkey: \"1111 \" is not five dice: five digits from 1 to 6"},
{"name":"a full-width 1 and four dice","size":7776,"dice":"efbc9131313131","error":"wordkey: \"\uff111111\" is not five dice: five digits from 1 to 6"},
{"name":"a space and four dice","size":7776,"dice":"2031313131","error":"wordkey: \" 1111\" is not five dice: five digits from 1 to 6"},
{"name":"a space and five dice","size":7776,"dice":"203131313131","error":"wordkey: \" 11111\" is not five dice: five digits from 1 to 6"},
{"name":"five dice and a space","size":7776,"dice":"313131313120","error":"wordkey: \"11111 \" is not five dice: five digits from 1 to 6"},
{"name":"five dice and LF","size":7776,"dice":"31313131310a","error":"wordkey: \"11111\\n\" is not five dice: five digits from 1 to 6"},
{"name":"a tab and four dice","size":7776,"dice":"0931313131","error":"wordkey: \"\\t1111\" is not five dice: five digits from 1 to 6"},
{"name":"two and two dice around a space","size":7776,"dice":"3131203131","error":"wordkey: \"11 11\" is not five dice: five digits from 1 to 6"},
{"name":"NUL","size":7776,"dice":"3131313100","error":"wordkey: \"1111\\x00\" is not five dice: five digits from 1 to 6"},
{"name":"DEL","size":7776,"dice":"313131317f","error":"wordkey: \"1111\\x7f\" is not five dice: five digits from 1 to 6"},
{"name":"a quotation mark","size":7776,"dice":"3131313122","error":"wordkey: \"1111\\\"\" is not five dice: five digits from 1 to 6"},
{"name":"a backslash","size":7776,"dice":"313131315c","error":"wordkey: \"1111\\\\\" is not five dice: five digits from 1 to 6"},
{"name":"00000","size":7776,"dice":"3030303030","error":"wordkey: \"00000\" is not five dice: five digits from 1 to 6"},
{"name":"77777","size":7776,"dice":"3737373737","error":"wordkey: \"77777\" is not five dice: five digits from 1 to 6"},
{"name":"99999","size":7776,"dice":"3939393939","error":"wordkey: \"99999\" is not five dice: five digits from 1 to 6"},
{"name":"a slash, the byte before 0","size":7776,"dice":"313131312f","error":"wordkey: \"1111/\" is not five dice: five digits from 1 to 6"},
{"name":"a colon, the byte after 9","size":7776,"dice":"313131313a","error":"wordkey: \"1111:\" is not five dice: five digits from 1 to 6"},
{"name":"five full-width digits","size":7776,"dice":"efbc91efbc92efbc93efbc94efbc95","error":"wordkey: \"\uff11\uff12\uff13\uff14\uff15\" is not five dice: five digits from 1 to 6"},
{"name":"five Arabic-Indic digits","size":7776,"dice":"d9a1d9a2d9a3d9a4d9a5","error":"wordkey: \"\u0661\u0662\u0663\u0664\u0665\" is not five dice: five digits from 1 to 6"},
{"name":"a superscript 1","size":7776,"dice":"31313131c2b9","error":"wordkey: \"1111\u00b9\" is not five dice: five digits from 1 to 6"},
{"name":"a circled 1 and two dice: five bytes","size":7776,"dice":"e291a03131","error":"wordkey: \"\u246011\" is not five dice: five digits from 1 to 6"},
{"name":"a mathematical bold 1 and a die: five bytes","size":7776,"dice":"f09d9f8f31","error":"wordkey: \"\ud835\udfcf1\" is not five dice: five digits from 1 to 6"},
{"name":"four dice and an e with an acute accent: five characters","size":7776,"dice":"31313131c3a9","error":"wordkey: \"1111\u00e9\" is not five dice: five digits from 1 to 6"},
{"name":"three dice and an e with an acute accent: five bytes","size":7776,"dice":"313131c3a9","error":"wordkey: \"111\u00e9\" is not five dice: five digits from 1 to 6"},
{"name":"five dice and a combining acute accent","size":7776,"dice":"3131313131cc81","error":"wordkey: \"11111\u0301\" is not five dice: five digits from 1 to 6"},
{"name":"three dice and the emoji of a die: five UTF-16 code units","size":7776,"dice":"313131f09f8eb2","error":"wordkey: \"111\ud83c\udfb2\" is not five dice: five digits from 1 to 6"},
{"name":"NBSP and four dice","size":7776,"dice":"c2a031313131","error":"wordkey: \"\\u00a01111\" is not five dice: five digits from 1 to 6"},
{"name":"an ideographic space and five dice","size":7776,"dice":"e380803131313131","error":"wordkey: \"\\u300011111\" is not five dice: five digits from 1 to 6"},
{"name":"two dice and a surrogate in UTF-8 bytes: five bytes","size":7776,"dice":"3131eda080","error":"wordkey: \"11\\xed\\xa0\\x80\" is not five dice: five digits from 1 to 6"},
{"name":"four dice and a byte that is not valid UTF-8","size":7776,"dice":"31313131ff","error":"wordkey: \"1111\\xff\" is not five dice: five digits from 1 to 6"},
{"name":"five bytes that are not valid UTF-8","size":7776,"dice":"fffefdfcfb","error":"wordkey: \"\\xff\\xfe\\xfd\\xfc\\xfb\" is not five dice: five digits from 1 to 6"},
{"name":"four dice and a sequence cut short","size":7776,"dice":"31313131c3","error":"wordkey: \"1111\\xc3\" is not five dice: five digits from 1 to 6"},
{"name":"\"11111\" on a list of 2048 words","size":2048,"dice":"3131313131","error":"wordkey: dice draw from a list of 7776 words, not 2048"},
{"name":"\"11111\" on a list of 7775 words","size":7775,"dice":"3131313131","error":"wordkey: dice draw from a list of 7776 words, not 7775"},
{"name":"\"66666\" on a list of 7777 words","size":7777,"dice":"3636363636","error":"wordkey: dice draw from a list of 7776 words, not 7777"},
{"name":"\"\" on a list of 0 words","size":0,"dice":"","error":"wordkey: dice draw from a list of 7776 words, not 0"},
{"name":"\"x\" on a list of 1 words","size":1,"dice":"78","error":"wordkey: dice draw from a list of 7776 words, not 1"}
],
"dice_words": [
{"name":"en, seven numbers among white space","list":"en","size":7776,"dice":"2031313131312031313131320931313131330a313131313420313131313520313131313620313131323120","words":["abacus","abdomen","abdominal","abide","abiding","ability","ablaze"]},
{"name":"en, five numbers","list":"en","size":7776,"dice":"3131313131203131313132203131313133203131313134203131313135","error":"wordkey: a key of words needs at least 6 words, not 5"},
{"name":"en, no numbers","list":"en","size":7776,"dice":"","error":"wordkey: a key of words needs at least 6 words, not 0"},
{"name":"en, four dice","list":"en","size":7776,"dice":"31313131312031313131322031313131332031313131342031313131352031313136","error":"wordkey: \"1116\" is not five dice: five digits from 1 to 6"},
{"name":"en, 11111 twice","list":"en","size":7776,"dice":"3131313131203131313132203131313133203131313134203131313135203131313131","error":"wordkey: the dice 11111 give \"abacus\" a second time; roll them again"},
{"name":"en, a comma between two numbers","list":"en","size":7776,"dice":"31313131312c3131313132203131313133203131313134203131313135203131313136203131313231","error":"wordkey: \"11111,11112\" is not five dice: five digits from 1 to 6"},
{"name":"seven numbers among white space","size":7776,"dice":"2031313131312031313131320931313131330a313131313420313131313520313131313620313131323120","words":["0","1","2","3","4","5","6"]},
{"name":"five numbers","size":7776,"dice":"3131313131203131313132203131313133203131313134203131313135","error":"wordkey: a key of words needs at least 6 words, not 5"},
{"name":"no numbers","size":7776,"dice":"","error":"wordkey: a key of words needs at least 6 words, not 0"},
{"name":"four dice","size":7776,"dice":"31313131312031313131322031313131332031313131342031313131352031313136","error":"wordkey: \"1116\" is not five dice: five digits from 1 to 6"},
{"name":"11111 twice","size":7776,"dice":"3131313131203131313132203131313133203131313134203131313135203131313131","error":"wordkey: the dice 11111 give \"0\" a second time; roll them again"},
{"name":"a comma between two numbers","size":7776,"dice":"31313131312c3131313132203131313133203131313134203131313135203131313136203131313231","error":"wordkey: \"11111,11112\" is not five dice: five digits from 1 to 6"},
{"name":"a list of 7775 words","size":7775,"dice":"3131313131203131313132203131313133203131313134203131313135203131313136","error":"wordkey: dice draw from a list of 7776 words, not 7775"},
{"name":"es, seven numbers","list":"es","size":7776,"dice":"3636363636203131313132203335323134203132333435203534333231203233343536203131313131","words":["\u00fatil","abad\u00eda","glaciar","alimentar","propicio","contener","abad"]},
{"name":"es, 66666 twice","list":"es","size":7776,"dice":"3636363636203131313131203335323134203132333435203534333231203233343536203636363636","error":"wordkey: the dice 66666 give \"\u00fatil\" a second time; roll them again"},
{"name":"six numbers","size":7776,"dice":"3131313132203131313133203131313134203131313135203131313136203131313231","words":["1","2","3","4","5","6"]},
{"name":"twelve numbers","size":7776,"dice":"3131313131203232323232203333333333203434343434203535353535203636363636203132333435203233343536203334353631203435363132203536313233203631323334","words":["0","1555","3110","4665","6220","7775","310","1865","3414","4933","6272","6531"]},
{"name":"white space only","size":7776,"dice":"20090ac2a0e3808020","error":"wordkey: a key of words needs at least 6 words, not 0"},
{"name":"U+0009 between the numbers","size":7776,"dice":"3131313131093131313132093131313133093131313134093131313135093131313136093131313231","words":["0","1","2","3","4","5","6"]},
{"name":"U+000A between the numbers","size":7776,"dice":"31313131310a31313131320a31313131330a31313131340a31313131350a31313131360a3131313231","words":["0","1","2","3","4","5","6"]},
{"name":"U+000B between the numbers","size":7776,"dice":"31313131310b31313131320b31313131330b31313131340b31313131350b31313131360b3131313231","words":["0","1","2","3","4","5","6"]},
{"name":"U+000C between the numbers","size":7776,"dice":"31313131310c31313131320c31313131330c31313131340c31313131350c31313131360c3131313231","words":["0","1","2","3","4","5","6"]},
{"name":"U+000D between the numbers","size":7776,"dice":"31313131310d31313131320d31313131330d31313131340d31313131350d31313131360d3131313231","words":["0","1","2","3","4","5","6"]},
{"name":"U+0020 between the numbers","size":7776,"dice":"3131313131203131313132203131313133203131313134203131313135203131313136203131313231","words":["0","1","2","3","4","5","6"]},
{"name":"U+0085 between the numbers","size":7776,"dice":"3131313131c2853131313132c2853131313133c2853131313134c2853131313135c2853131313136c2853131313231","words":["0","1","2","3","4","5","6"]},
{"name":"U+00A0 between the numbers","size":7776,"dice":"3131313131c2a03131313132c2a03131313133c2a03131313134c2a03131313135c2a03131313136c2a03131313231","words":["0","1","2","3","4","5","6"]},
{"name":"U+1680 between the numbers","size":7776,"dice":"3131313131e19a803131313132e19a803131313133e19a803131313134e19a803131313135e19a803131313136e19a803131313231","words":["0","1","2","3","4","5","6"]},
{"name":"U+2000 between the numbers","size":7776,"dice":"3131313131e280803131313132e280803131313133e280803131313134e280803131313135e280803131313136e280803131313231","words":["0","1","2","3","4","5","6"]},
{"name":"U+2001 between the numbers","size":7776,"dice":"3131313131e280813131313132e280813131313133e280813131313134e280813131313135e280813131313136e280813131313231","words":["0","1","2","3","4","5","6"]},
{"name":"U+2002 between the numbers","size":7776,"dice":"3131313131e280823131313132e280823131313133e280823131313134e280823131313135e280823131313136e280823131313231","words":["0","1","2","3","4","5","6"]},
{"name":"U+2003 between the numbers","size":7776,"dice":"3131313131e280833131313132e280833131313133e280833131313134e280833131313135e280833131313136e280833131313231","words":["0","1","2","3","4","5","6"]},
{"name":"U+2004 between the numbers","size":7776,"dice":"3131313131e280843131313132e280843131313133e280843131313134e280843131313135e280843131313136e280843131313231","words":["0","1","2","3","4","5","6"]},
{"name":"U+2005 between the numbers","size":7776,"dice":"3131313131e280853131313132e280853131313133e280853131313134e280853131313135e280853131313136e280853131313231","words":["0","1","2","3","4","5","6"]},
{"name":"U+2006 between the numbers","size":7776,"dice":"3131313131e280863131313132e280863131313133e280863131313134e280863131313135e280863131313136e280863131313231","words":["0","1","2","3","4","5","6"]},
{"name":"U+2007 between the numbers","size":7776,"dice":"3131313131e280873131313132e280873131313133e280873131313134e280873131313135e280873131313136e280873131313231","words":["0","1","2","3","4","5","6"]},
{"name":"U+2008 between the numbers","size":7776,"dice":"3131313131e280883131313132e280883131313133e280883131313134e280883131313135e280883131313136e280883131313231","words":["0","1","2","3","4","5","6"]},
{"name":"U+2009 between the numbers","size":7776,"dice":"3131313131e280893131313132e280893131313133e280893131313134e280893131313135e280893131313136e280893131313231","words":["0","1","2","3","4","5","6"]},
{"name":"U+200A between the numbers","size":7776,"dice":"3131313131e2808a3131313132e2808a3131313133e2808a3131313134e2808a3131313135e2808a3131313136e2808a3131313231","words":["0","1","2","3","4","5","6"]},
{"name":"U+2028 between the numbers","size":7776,"dice":"3131313131e280a83131313132e280a83131313133e280a83131313134e280a83131313135e280a83131313136e280a83131313231","words":["0","1","2","3","4","5","6"]},
{"name":"U+2029 between the numbers","size":7776,"dice":"3131313131e280a93131313132e280a93131313133e280a93131313134e280a93131313135e280a93131313136e280a93131313231","words":["0","1","2","3","4","5","6"]},
{"name":"U+202F between the numbers","size":7776,"dice":"3131313131e280af3131313132e280af3131313133e280af3131313134e280af3131313135e280af3131313136e280af3131313231","words":["0","1","2","3","4","5","6"]},
{"name":"U+205F between the numbers","size":7776,"dice":"3131313131e2819f3131313132e2819f3131313133e2819f3131313134e2819f3131313135e2819f3131313136e2819f3131313231","words":["0","1","2","3","4","5","6"]},
{"name":"U+3000 between the numbers","size":7776,"dice":"3131313131e380803131313132e380803131313133e380803131313134e380803131313135e380803131313136e380803131313231","words":["0","1","2","3","4","5","6"]},
{"name":"U+001C between the first two numbers","size":7776,"dice":"31313131311c3131313132203131313133203131313134203131313135203131313136203131313231","error":"wordkey: \"11111\\x1c11112\" is not five dice: five digits from 1 to 6"},
{"name":"U+001D between the first two numbers","size":7776,"dice":"31313131311d3131313132203131313133203131313134203131313135203131313136203131313231","error":"wordkey: \"11111\\x1d11112\" is not five dice: five digits from 1 to 6"},
{"name":"U+001E between the first two numbers","size":7776,"dice":"31313131311e3131313132203131313133203131313134203131313135203131313136203131313231","error":"wordkey: \"11111\\x1e11112\" is not five dice: five digits from 1 to 6"},
{"name":"U+001F between the first two numbers","size":7776,"dice":"31313131311f3131313132203131313133203131313134203131313135203131313136203131313231","error":"wordkey: \"11111\\x1f11112\" is not five dice: five digits from 1 to 6"},
{"name":"U+00AD between the first two numbers","size":7776,"dice":"3131313131c2ad3131313132203131313133203131313134203131313135203131313136203131313231","error":"wordkey: \"11111\\u00ad11112\" is not five dice: five digits from 1 to 6"},
{"name":"U+0301 between the first two numbers","size":7776,"dice":"3131313131cc813131313132203131313133203131313134203131313135203131313136203131313231","error":"wordkey: \"11111\u030111112\" is not five dice: five digits from 1 to 6"},
{"name":"U+180E between the first two numbers","size":7776,"dice":"3131313131e1a08e3131313132203131313133203131313134203131313135203131313136203131313231","error":"wordkey: \"11111\\u180e11112\" is not five dice: five digits from 1 to 6"},
{"name":"U+200B between the first two numbers","size":7776,"dice":"3131313131e2808b3131313132203131313133203131313134203131313135203131313136203131313231","error":"wordkey: \"11111\\u200b11112\" is not five dice: five digits from 1 to 6"},
{"name":"U+200C between the first two numbers","size":7776,"dice":"3131313131e2808c3131313132203131313133203131313134203131313135203131313136203131313231","error":"wordkey: \"11111\\u200c11112\" is not five dice: five digits from 1 to 6"},
{"name":"U+200D between the first two numbers","size":7776,"dice":"3131313131e2808d3131313132203131313133203131313134203131313135203131313136203131313231","error":"wordkey: \"11111\\u200d11112\" is not five dice: five digits from 1 to 6"},
{"name":"U+2060 between the first two numbers","size":7776,"dice":"3131313131e281a03131313132203131313133203131313134203131313135203131313136203131313231","error":"wordkey: \"11111\\u206011112\" is not five dice: five digits from 1 to 6"},
{"name":"U+2800 between the first two numbers","size":7776,"dice":"3131313131e2a0803131313132203131313133203131313134203131313135203131313136203131313231","error":"wordkey: \"11111\u280011112\" is not five dice: five digits from 1 to 6"},
{"name":"U+3164 between the first two numbers","size":7776,"dice":"3131313131e385a43131313132203131313133203131313134203131313135203131313136203131313231","error":"wordkey: \"11111\u316411112\" is not five dice: five digits from 1 to 6"},
{"name":"U+FEFF between the first two numbers","size":7776,"dice":"3131313131efbbbf3131313132203131313133203131313134203131313135203131313136203131313231","error":"wordkey: \"11111\\ufeff11112\" is not five dice: five digits from 1 to 6"},
{"name":"U+FFFD between the first two numbers","size":7776,"dice":"3131313131efbfbd3131313132203131313133203131313134203131313135203131313136203131313231","error":"wordkey: \"11111\ufffd11112\" is not five dice: five digits from 1 to 6"},
{"name":"the byte A0 alone, which is not NBSP","size":7776,"dice":"3131313131a03131313132203131313133203131313134203131313135203131313136203131313231","error":"wordkey: \"11111\\xa011112\" is not five dice: five digits from 1 to 6"},
{"name":"the byte 85 alone, which is not NEL","size":7776,"dice":"3131313131853131313132203131313133203131313134203131313135203131313136203131313231","error":"wordkey: \"11111\\x8511112\" is not five dice: five digits from 1 to 6"},
{"name":"a byte that is not valid UTF-8 after a number","size":7776,"dice":"3131313131ff203131313132203131313133203131313134203131313135203131313136203131313231","error":"wordkey: \"11111\\xff\" is not five dice: five digits from 1 to 6"},
{"name":"a surrogate in UTF-8 bytes after a number","size":7776,"dice":"3131313131eda080203131313132203131313133203131313134203131313135203131313136203131313231","error":"wordkey: \"11111\\xed\\xa0\\x80\" is not five dice: five digits from 1 to 6"},
{"name":"five full-width digits","size":7776,"dice":"efbc91efbc91efbc91efbc91efbc91203131313132203131313133203131313134203131313135203131313136203131313231","error":"wordkey: \"\uff11\uff11\uff11\uff11\uff11\" is not five dice: five digits from 1 to 6"},
{"name":"a combining acute accent after a number","size":7776,"dice":"3131313131cc81203131313132203131313133203131313134203131313135203131313136203131313231","error":"wordkey: \"11111\u0301\" is not five dice: five digits from 1 to 6"},
{"name":"a combining acute accent before a number","size":7776,"dice":"cc813131313131203131313132203131313133203131313134203131313135203131313136203131313231","error":"wordkey: \"\u030111111\" is not five dice: five digits from 1 to 6"},
{"name":"five fields that are not dice: the count first","size":7776,"dice":"612062206320642065","error":"wordkey: a key of words needs at least 6 words, not 5"},
{"name":"six fields, the first not dice","size":7776,"dice":"616263203131313131203131313132203131313133203131313134203131313135","error":"wordkey: \"abc\" is not five dice: five digits from 1 to 6"},
{"name":"not dice before a word given twice","size":7776,"dice":"313131313120313131313220616263203131313131203131313133203131313134","error":"wordkey: \"abc\" is not five dice: five digits from 1 to 6"},
{"name":"a word given twice before not dice","size":7776,"dice":"313131313120313131313220313131313320313131313420313131313520313131313120616263","error":"wordkey: the dice 11111 give \"0\" a second time; roll them again"},
{"name":"indices modulo 1: 11112 11113 11114 11115 11116 11121","size":7776,"mod":1,"dice":"3131313132203131313133203131313134203131313135203131313136203131313231","error":"wordkey: the dice 11113 give \"0\" a second time; roll them again"},
{"name":"indices modulo 7: 11111 11112 11113 11114 11115 11116 11121","size":7776,"mod":7,"dice":"3131313131203131313132203131313133203131313134203131313135203131313136203131313231","words":["0","1","2","3","4","5","6"]},
{"name":"indices modulo 7: 11111 11112 11113 11114 11115 11116 11121 11122","size":7776,"mod":7,"dice":"3131313131203131313132203131313133203131313134203131313135203131313136203131313231203131313232","error":"wordkey: the dice 11122 give \"0\" a second time; roll them again"},
{"name":"indices modulo 1000: 11111 11112 11113 11114 11115 15455","size":7776,"mod":1000,"dice":"3131313131203131313132203131313133203131313134203131313135203135343535","error":"wordkey: the dice 15455 give \"0\" a second time; roll them again"},
{"name":"indices modulo 7 and a suffix: 11111 11112 11113 11114 11115 11116 11121","size":7776,"mod":7,"suffix":"\"\\\t\u00a0\u200b","dice":"3131313131203131313132203131313133203131313134203131313135203131313136203131313231","words":["0\"\\\t\u00a0\u200b","1\"\\\t\u00a0\u200b","2\"\\\t\u00a0\u200b","3\"\\\t\u00a0\u200b","4\"\\\t\u00a0\u200b","5\"\\\t\u00a0\u200b","6\"\\\t\u00a0\u200b"]},
{"name":"indices modulo 7 and a suffix: 11111 11112 11113 11114 11115 11116 11121 11122","size":7776,"mod":7,"suffix":"\"\\\t\u00a0\u200b","dice":"3131313131203131313132203131313133203131313134203131313135203131313136203131313231203131313232","error":"wordkey: the dice 11122 give \"0\\\"\\\\\\t\\u00a0\\u200b\" a second time; roll them again"},
{"name":"\"\" on a list of 0 words","size":0,"dice":"","error":"wordkey: dice draw from a list of 7776 words, not 0"},
{"name":"\"11111 11112 11113 11114 11115 11116 11121\" on a list of 2048 words","size":2048,"dice":"3131313131203131313132203131313133203131313134203131313135203131313136203131313231","error":"wordkey: dice draw from a list of 7776 words, not 2048"},
{"name":"\"x\" on a list of 7777 words","size":7777,"dice":"78","error":"wordkey: dice draw from a list of 7776 words, not 7777"}
],
"dice_list": [
{"list":"en","size":7776,"sha256":"addd35536511597a02fa0a9ff1e5284677b8883b83e986e43f15a3db996b903e","bytes":108800},
{"list":"es","size":7776,"sha256":"611f779a33df74587e9dfb486bb0185a9dfd4d1384e75993a21247ad31cefddb","bytes":108499},
{"size":7776,"sha256":"267618e6e9109b66da2804c1d77097273dd6bc3144bf60fd35988322293db59e","bytes":84426},
{"size":0,"error":"wordkey: dice draw from a list of 7776 words, not 0"},
{"size":1,"error":"wordkey: dice draw from a list of 7776 words, not 1"},
{"size":2048,"error":"wordkey: dice draw from a list of 7776 words, not 2048"},
{"size":7775,"error":"wordkey: dice draw from a list of 7776 words, not 7775"},
{"size":7777,"error":"wordkey: dice draw from a list of 7776 words, not 7777"}
]
}

@ -1,7 +1,8 @@
// Helpers of the tests of the word lists: the base list of TestCheckList of
// Go and its edits, as test/vectors/wordlist_vectors.json writes them; a
// list of words that are their indices; and the random sources of the
// vectors of Generate. They read no file.
// list of words that are their indices; the random sources of the vectors
// of Generate; and the String of the bytes of the dice of a case. They read
// no file.
import 'dart:collection';
import 'dart:typed_data';
@ -10,6 +11,9 @@ import 'package:datekeys/src/bytes.dart';
import 'package:datekeys/src/random.dart';
import 'package:datekeys/src/sha256.dart';
import 'package:datekeys/src/wordkey.dart' show WordKeyException;
import 'package:test/test.dart';
import 'wordkey_support.dart' show fromWtf8;
typedef Json = Map<String, Object?>;
@ -66,11 +70,16 @@ double hexDouble(String hex) =>
/// The words "0", "1", …, one less than [length]: a list whose words are
/// their indices, without storing them. generateWords reads only its length
/// and the words it draws.
/// and the words it draws, and the dice the same. With [mod], each word is
/// its index modulo [mod], so that a word comes back every [mod] positions,
/// and with [suffix], the index is followed by it: the lists of the dice
/// cases of the vectors with `mod` and `suffix`.
final class IndexWords extends ListBase<String> {
IndexWords(this._length);
IndexWords(this._length, [this._mod = 0, this._suffix = '']);
final int _length;
final int _mod;
final String _suffix;
@override
int get length => _length;
@ -81,7 +90,7 @@ final class IndexWords extends ListBase<String> {
@override
String operator [](int i) {
RangeError.checkValidIndex(i, this);
return '$i';
return '${_mod == 0 ? i : i % _mod}$_suffix';
}
@override
@ -166,3 +175,24 @@ RandomSource streamOf(Json s) => switch (s['kind']) {
/// The bytes that crypto/rand.Int reads for each draw of an index below
/// [size]: those of size − 1.
int drawSize(int size) => ((size - 1).bitLength + 7) ~/ 8;
/// The String whose [utf8Bytes] are [bytes], also when they hold a lone
/// surrogate, or null when no String has them, such as a byte FF: the
/// dice of a case of the vectors as diceWord and diceWords take them.
String? stringOf(Uint8List bytes) {
final s = fromWtf8(bytes);
return s != null && equalBytes(utf8Bytes(s), bytes) ? s : null;
}
/// Expects [f] to give what a case [c] of dice_word or dice_words of the
/// vectors gives, its word or its words, or to throw a [WordKeyException]
/// with the text of its error.
void expectDice(Object? Function() f, Json c) {
final name = c['name'];
final error = c['error'] as String?;
if (error == null) {
expect(f(), c['word'] ?? c['words'], reason: '$name');
} else {
expect(outcome(f), error, reason: '$name');
}
}

@ -3,11 +3,16 @@
// computed (tool/wordlist_go_vectors.go): the constants and the pinned
// lists; the text of a list read as wordkey.List reads it; wordkey.CheckList
// with its texts, also on each code point up to U+017F; the words that
// wordkey.Generate draws from the same bytes; and wordkey.Bits, bit for bit.
// And the cases of TestCheckList, TestGenerate and TestBits of Go, as Go
// writes them. The vectors come from a Dart constant, so that these tests
// also run compiled to JavaScript; the Spanish list, the CSPRNG of the
// platform and the alphabet of every code point are in wordlist_vm_test.dart.
// wordkey.Generate draws from the same bytes; wordkey.Bits, bit for bit;
// and the dice of wordkey on lists of indices: DiceNumber, DiceWord and
// DiceWords with their texts, on the bytes of Go and on Strings, the white
// space of plane 0 at which the numbers are split, and the SHA-256 of
// DiceList. And the cases of TestCheckList, TestGenerate, TestBits and
// TestDiceNumber of Go, and those of TestDiceWord and TestDiceList that need
// no list of Go, as Go writes them. The vectors come from a Dart constant,
// so that these tests also run compiled to JavaScript; the lists of
// wordlists/, the CSPRNG of the platform and the alphabet and the white
// space of every code point are in wordlist_vm_test.dart.
import 'dart:convert';
import 'dart:math' as math;
@ -37,6 +42,7 @@ void main() {
expect(vectors['min_list_size'], minListSize);
expect(vectors['min_words'], minWords);
expect(vectors['min_letters'], minLetters);
expect(vectors['dice_list_size'], diceListSize);
// Languages of Go, and the SHA-256 of the file of each of its lists.
final lists = cases('lists');
expect(wordListLanguages(), [for (final l in lists) l['lang']]);
@ -52,6 +58,11 @@ void main() {
expect(datekeys.wordListSha256, same(wordListSha256));
expect(datekeys.defaultWordCount, defaultWordCount);
expect(datekeys.minListSize, minListSize);
expect(datekeys.diceNumber, diceNumber);
expect(datekeys.diceWord, diceWord);
expect(datekeys.diceWords, diceWords);
expect(datekeys.diceList, diceList);
expect(datekeys.diceListSize, diceListSize);
});
group('checkWordList', () {
@ -363,4 +374,199 @@ void main() {
}
});
});
group('dice', () {
// A list of 7776 words that are their indices: the dice read only the
// size of a list and the words they give. The lists of Go are in
// wordlist_vm_test.dart.
final indices = IndexWords(diceListSize);
test('the cases of TestDiceNumber of Go', () {
for (final (i, want) in [
(0, '11111'),
(1, '11112'),
(5, '11116'),
(6, '11121'),
(35, '11166'),
(36, '11211'),
(1295, '16666'),
(1296, '21111'),
(7775, '66666'),
]) {
expect(diceNumber(i), want, reason: '$i');
}
for (final i in [-1, 7776]) {
expect(
outcome(() => diceNumber(i)),
'wordkey: no dice give position $i of a list of 7776 words',
);
}
});
test('diceNumber is DiceNumber of Go', () {
final all = cases('dice_number');
for (final c in all) {
final i = c['i']! as int;
final error = c['error'] as String?;
if (error == null) {
expect(diceNumber(i), c['dice'], reason: '$i');
} else {
expect(outcome(() => diceNumber(i)), error, reason: '$i');
}
}
expect(all, hasLength(greaterThan(20)));
});
test('the cases of TestDiceWord of Go, on the list of the indices', () {
// Every position, there and back.
for (var i = 0; i < diceListSize; i++) {
expect(diceWord(indices, diceNumber(i)), '$i');
}
// Go quotes none of their characters: the full-width 1 of U+FF11 is
// printable.
for (final dice in [
'',
'1111',
'111111',
'11110',
'11117',
'a1111',
'1111 ',
'\uff111111',
]) {
expect(
outcome(() => diceWord(indices, dice)),
'wordkey: "$dice" is not five dice: five digits from 1 to 6',
);
}
expect(
outcome(() => diceWord(IndexWords(2048), '11111')),
'wordkey: dice draw from a list of 7776 words, not 2048',
);
});
test('diceWord is DiceWord of Go, on lists of indices', () {
var count = 0;
var strings = 0;
for (final c in cases('dice_word')) {
if (c['list'] != null) continue;
final list = IndexWords(c['size']! as int);
final dice = fromHex(c['dice']! as String);
expectDice(() => diceWordUtf8(list, dice), c);
final s = stringOf(dice);
if (s != null) {
expectDice(() => diceWord(list, s), c);
strings++;
}
count++;
}
expect(count, greaterThan(50));
// All but three, whose bytes no String has: a byte FF, five bytes
// from FF to FB and a sequence cut short.
expect(strings, count - 3);
});
test('diceWords is DiceWords of Go, on lists of indices', () {
var count = 0;
var strings = 0;
for (final c in cases('dice_words')) {
if (c['list'] != null) continue;
final list = IndexWords(
c['size']! as int,
c['mod'] as int? ?? 0,
c['suffix'] as String? ?? '',
);
final dice = fromHex(c['dice']! as String);
expectDice(() => diceWordsUtf8(list, dice), c);
final s = stringOf(dice);
if (s != null) {
expectDice(() => diceWords(list, s), c);
strings++;
}
count++;
}
expect(count, greaterThan(60));
// All but three, whose bytes no String has: the bytes A0 and 85
// alone, which are not U+00A0 and U+0085, and a byte FF.
expect(strings, count - 3);
});
test('diceWords splits at the white space of Go, and changes nothing '
'else', () {
// The tab, the line feed, U+00A0 and U+3000 separate the numbers.
expect(
diceWords(
indices,
'\u300011111\t11112\n11113\u00a011114\u300011115 11116\u00a0',
),
['0', '1', '2', '3', '4', '5'],
);
// A combining mark stays in its number, which is then not five dice:
// no NFD and no mark dropped, as normalizeWords would. Go's %q writes
// the mark as it is.
expect(
outcome(
() => diceWords(indices, '11111\u0301 11112 11113 11114 11115 11116'),
),
'wordkey: "11111\u0301" is not five dice: five digits from 1 to 6',
);
expect(
outcome(
() => diceWords(
indices,
'11111\u0301\u3000\u0301 11112 11113 11114 11115 11116',
),
),
'wordkey: "11111\u0301" is not five dice: five digits from 1 to 6',
);
// Nor is a full-width digit made an ASCII one.
expect(
outcome(
() => diceWords(
indices,
'\uff11\uff11\uff11\uff11\uff11 11112 11113 11114 11115 11116',
),
),
'wordkey: "\uff11\uff11\uff11\uff11\uff11" is not five dice: '
'five digits from 1 to 6',
);
// Every code point of plane 0 at which Go splits the numbers, and no
// other (the vectors have every plane in dice_space).
final split = <int>[];
for (var r = 0; r <= 0xffff; r++) {
if (r >= 0xd800 && r <= 0xdfff) continue;
final ch = String.fromCharCode(r);
final text = '11111${ch}11112 11113 11114 11115 11116 11121';
if (outcome(() => diceWords(indices, text)) == 'ok') split.add(r);
}
expect(split, (vectors['dice_space']! as Json)['split']);
});
test('diceList is DiceList of Go, on lists of indices', () {
for (final c in cases('dice_list')) {
if (c['list'] != null) continue;
final list = IndexWords(c['size']! as int);
final error = c['error'] as String?;
if (error != null) {
expect(
outcome(() => diceList(list)),
error,
reason: '${list.length}',
);
continue;
}
final text = diceList(list);
expect(text, startsWith('11111\t0\n11112\t1\n11113\t2\n'));
expect(text, endsWith('66665\t7774\n66666\t7775\n'));
final bytes = utf8Bytes(text);
expect(toHex(sha256(bytes)), c['sha256']);
expect(bytes, hasLength(c['bytes']));
}
// TestDiceList of Go: a list of 2048 words.
expect(
outcome(() => diceList(List.filled(2048, ''))),
'wordkey: dice draw from a list of 7776 words, not 2048',
);
});
});
}

@ -7,8 +7,13 @@
// built into it (TestBuiltInLists), and generateWords draws from the Spanish
// one the words of Go; and,
// as TestGenerate and TestGenerateUniform of Go, the words drawn with the
// CSPRNG of the platform are a key, and each about as likely. On the VM
// only: they read files, use Random.secure or take a few seconds.
// CSPRNG of the platform are a key, and each about as likely. And the dice
// on the two lists: the cases of TestDiceWord, TestDiceWords and
// TestDiceList of Go, the English list numbered for dice being the file of
// the EFF, byte for byte; the dice of the vectors on the lists of Go; and
// the white space of every code point of planes 0, 1 and 14, which is that
// of normalizeWords. On the VM only: they read files, use Random.secure or
// take a few seconds.
@TestOn('vm')
library;
@ -181,4 +186,182 @@ void main() {
]);
});
});
group('the dice on the lists of wordlists/', () {
final lists = {
for (final lang in wordListLanguages())
lang: readWordList(lang, File('wordlists/$lang.txt').readAsBytesSync()),
};
final en = lists['en']!;
final es = lists['es']!;
List<Json> cases(String name) => (vectors[name]! as List).cast<Json>();
test('the cases of TestDiceWord of Go', () {
// Every position, there and back.
for (var i = 0; i < en.length; i++) {
expect(diceWord(en, diceNumber(i)), en[i]);
}
for (final (list, dice, want) in [
(en, '11111', 'abacus'),
(en, '11112', 'abdomen'),
(en, '35214', 'jovial'),
(en, '66666', 'zoom'),
(es, '11111', 'abad'),
(es, '35214', 'glaciar'),
(es, '66666', '\u00fatil'),
]) {
expect(diceWord(list, dice), want, reason: dice);
}
for (final dice in [
'',
'1111',
'111111',
'11110',
'11117',
'a1111',
'1111 ',
'\uff111111',
]) {
expect(
outcome(() => diceWord(en, dice)),
'wordkey: "$dice" is not five dice: five digits from 1 to 6',
);
}
expect(
outcome(() => diceWord(en.sublist(0, 2048), '11111')),
'wordkey: dice draw from a list of 7776 words, not 2048',
);
});
test('the cases of TestDiceWords of Go', () {
final words = diceWords(
en,
' 11111 11112\t11113\n11114 11115 11116 11121 ',
);
expect(
words.join(' '),
'abacus abdomen abdominal abide abiding ability ablaze',
);
checkWords(normalizeWords(words.join(' ')));
for (final (dice, want) in [
(
'11111 11112 11113 11114 11115',
'wordkey: a key of words needs at least 6 words, not 5',
),
('', 'wordkey: a key of words needs at least 6 words, not 0'),
(
'11111 11112 11113 11114 11115 1116',
'wordkey: "1116" is not five dice: five digits from 1 to 6',
),
(
'11111 11112 11113 11114 11115 11111',
'wordkey: the dice 11111 give "abacus" a second time; roll them '
'again',
),
(
'11111,11112 11113 11114 11115 11116 11121',
'wordkey: "11111,11112" is not five dice: five digits from 1 to 6',
),
]) {
expect(outcome(() => diceWords(en, dice)), want, reason: dice);
}
expect(
outcome(
() => diceWords(en.sublist(1), '11111 11112 11113 11114 11115 11116'),
),
'wordkey: dice draw from a list of 7776 words, not 7775',
);
});
test('the cases of TestDiceList of Go: for en, the file of the EFF', () {
// The SHA-256 of each list numbered for dice, as Go's TestDiceList
// and wordlists/README.md record them.
const hashes = {
'en':
'addd35536511597a02fa0a9ff1e5284677b8883b83e986e43f15a3db996b903e',
'es':
'611f779a33df74587e9dfb486bb0185a9dfd4d1384e75993a21247ad31cefddb',
};
final readme = File('wordlists/README.md').readAsStringSync();
expect(hashes.keys, wordListLanguages());
for (final MapEntry(key: lang, value: want) in hashes.entries) {
final list = lists[lang]!;
final text = diceList(list);
expect(toHex(sha256(utf8Bytes(text))), want, reason: lang);
expect(readme, contains('| `$lang` | `$want`'), reason: lang);
expect(text, endsWith('\n'), reason: lang);
final lines = text.substring(0, text.length - 1).split('\n');
expect(lines, hasLength(diceListSize), reason: lang);
expect(lines.first, '11111\t${list.first}', reason: lang);
expect(lines.last, '66666\t${list.last}', reason: lang);
}
expect(
outcome(() => diceList(List.filled(2048, ''))),
'wordkey: dice draw from a list of 7776 words, not 2048',
);
});
test('the dice of the vectors on the lists of Go', () {
var count = 0;
for (final name in ['dice_word', 'dice_words']) {
for (final c in cases(name)) {
final lang = c['list'] as String?;
if (lang == null) continue;
final list = lists[lang]!;
final dice = fromHex(c['dice']! as String);
final s = stringOf(dice)!;
if (name == 'dice_word') {
expectDice(() => diceWordUtf8(list, dice), c);
expectDice(() => diceWord(list, s), c);
} else {
expectDice(() => diceWordsUtf8(list, dice), c);
expectDice(() => diceWords(list, s), c);
}
count++;
}
}
for (final c in cases('dice_list')) {
final lang = c['list'] as String?;
if (lang == null) continue;
final text = utf8Bytes(diceList(lists[lang]!));
expect(toHex(sha256(text)), c['sha256'], reason: lang);
expect(text, hasLength(c['bytes']), reason: lang);
count++;
}
expect(count, greaterThan(15));
});
test('the white space of every code point of planes 0, 1 and 14', () {
// 11111, the code point and six more numbers on the list of the
// indices: the lines of every result, but for the surrogates, have
// Go's SHA-256, and the numbers are split at the white space of spec
// §38.1, at which normalizeWords splits the words of a key.
final s = vectors['dice_space']! as Json;
final indices = IndexWords(diceListSize);
final digest = Sha256();
final split = <int>[];
final words = <int>[];
var count = 0;
for (final p in (s['planes']! as List).cast<int>()) {
for (var r = p << 16; r <= (p << 16 | 0xffff); r++) {
if (r >= 0xd800 && r <= 0xdfff) continue;
final c = String.fromCharCode(r);
final result = outcome(
() => diceWords(
indices,
'11111${c}11112 11113 11114 11115 11116 11121',
),
);
digest.add(utf8Bytes('${codePointName(r)} $result\n'));
count++;
if (result == 'ok') split.add(r);
if (normalizeWords('a${c}b').length == 2) words.add(r);
}
}
expect(count, s['count']);
expect(toHex(digest.finish()), s['sha256']);
expect(split, s['split']);
expect(words, split);
});
});
}

@ -1,6 +1,6 @@
{
"module": "g.activething.com/go/DateKeys",
"commit": "e671032e04a3cd0999cf073ebedf50c45b088683",
"commit": "92e7154c23945c9b9a2893750b6d4d128f848d3e",
"files": {
"README.md": "d26b3f507edf5afe89600f063cb509b9ef62a908b93ed476bff964b5a443bf4b",
"fixtures/empty_payload.dkc": "871e9bf05b52bbae17f3adfbbf97b46e7f0e53aa8f57bcaa506e43f36f53a9d4",

@ -3,7 +3,8 @@
// Writes test/vectors/wordlist_vectors.json and the same JSON as a Dart
// constant, wordlist_vectors.g.dart: the results of the word lists of
// package wordkey of the Go reference (generate.go: List, CheckList,
// Generate and Bits, the random words of spec §38.1) for
// Generate and Bits, the random words of spec §38.1; and dice.go:
// DiceNumber, DiceWord, DiceWords and DiceList, the words of dice) for
// lib/src/wordlist.dart. Every expected value is computed here by the Go
// reference; none is written by hand.
//
@ -36,14 +37,35 @@
// - bits: Bits of sizes and counts, as the 64 bits of the float64 and its
// shortest decimal; and bits_digests, the SHA-256 of the 8 big-endian
// bytes of each Bits over ranges of sizes and counts, sizes outer.
// - dice_number: DiceNumber of positions in the list of 7776 words, where
// a digit carries, and out of it, some of them large.
// - dice_word: DiceWord of five dice, as bytes, on the English and the
// Spanish lists (the cases of TestDiceWord) and on a list of the 7776
// indices: dice, and what is not five dice, such as white space around
// them, other digits, a combining mark, five bytes that are not five
// characters and bytes that are not valid UTF-8; and lists of other
// sizes.
// - dice_words: DiceWords of a text, as bytes, on the same lists and on
// lists of indices modulo a number, which give a word twice, also
// followed by characters that %q escapes: the cases of TestDiceWords;
// each white space of unicode.IsSpace between the numbers, and code
// points and bytes that are not; the order of the checks; and lists of
// other sizes.
// - dice_list: DiceList of each list of Go, the file of the EFF for en,
// and of a list of the 7776 indices, as the SHA-256 and the length of
// its text; and lists of other sizes.
// - dice_space: DiceWords of 11111, a code point and six more numbers on
// the list of the 7776 indices, for every code point of planes 0, 1 and
// 14 but the surrogates: the SHA-256 of the lines "U+XXXX result\n",
// and the code points at which the numbers are split.
//
// Binary values are lower-case hexadecimal, and the JSON is ASCII, so that
// the Dart constant is too. The output is the same on every run. It imports
// the package wordkey and reads wordkey/lists, so it runs in an export of
// datekeys-go made with git archive, without changing the repository, at
// e671032, the branch v0.15 after the tag spec-v0.15:
// 92e7154, the branch v0.15 after the tag spec-v0.15:
//
// commit=$(git -C ../datekeys-go rev-parse e671032)
// commit=$(git -C ../datekeys-go rev-parse 92e7154)
// out=$PWD/test/vectors
// tmp=$(mktemp -d)
// git -C ../datekeys-go archive "$commit" | tar -x -C "$tmp"
@ -68,6 +90,7 @@ import (
"runtime"
"strconv"
"strings"
"unicode"
"golang.org/x/crypto/chacha20"
@ -611,6 +634,307 @@ func bitsDigests() []bitsDigest {
}
}
// ---------------------------------------------------------------------------
// Dice
// diceListOf is a list for the dice: the built-in list of the language
// list or, when list is empty, size words, each its index in decimal,
// modulo mod when mod is not 0, so that a word comes back every mod
// positions, followed by suffix.
func diceListOf(list string, size, mod int, suffix string) []string {
if list != "" {
l, err := wordkey.List(list)
check(err)
return l
}
l := numbers(size)
for i := range l {
if mod != 0 {
l[i] = strconv.Itoa(i % mod)
}
l[i] += suffix
}
return l
}
// errText is the text of err, or nothing.
func errText(err error) string {
if err != nil {
return err.Error()
}
return ""
}
type diceNumberCase struct {
I int `json:"i"`
Dice string `json:"dice,omitempty"`
Error string `json:"error,omitempty"`
}
func diceNumberCases() []diceNumberCase {
var out []diceNumberCase
// The positions of TestDiceNumber and others where a digit carries,
// then positions out of the list, some of them large.
for _, i := range []int{0, 1, 5, 6, 35, 36, 215, 216, 1295, 1296, 2047, 2048, 3495, 7774, 7775,
-1, 7776, 7777, -7776, 1 << 31, -1 << 31, 1 << 32, 1<<53 - 1, -(1<<53 - 1)} {
d, err := wordkey.DiceNumber(i)
out = append(out, diceNumberCase{i, d, errText(err)})
}
return out
}
type diceWordCase struct {
Name string `json:"name"`
List string `json:"list,omitempty"`
Size int `json:"size"`
Dice string `json:"dice"`
Word string `json:"word,omitempty"`
Error string `json:"error,omitempty"`
}
// diceWordOf runs DiceWord on the list of diceListOf and the bytes dice,
// which the case keeps in hex.
func diceWordOf(name, list string, size int, dice string) diceWordCase {
l := diceListOf(list, size, 0, "")
w, err := wordkey.DiceWord(l, dice)
return diceWordCase{name, list, len(l), h(dice), w, errText(err)}
}
func diceWordCases() []diceWordCase {
var out []diceWordCase
add := func(c diceWordCase) { out = append(out, c) }
// TestDiceWord, on the lists of Go.
for _, c := range []struct{ list, dice string }{
{"en", "11111"}, {"en", "11112"}, {"en", "35214"}, {"en", "66666"},
{"es", "11111"}, {"es", "35214"}, {"es", "66666"},
} {
add(diceWordOf(c.list+" "+c.dice, c.list, 0, c.dice))
}
// Five dice on the list of the indices: where a digit carries, and
// others.
for _, dice := range []string{"11111", "11112", "11116", "11121", "11166", "11211", "16666", "21111", "12345", "35214", "65432", "66665", "66666"} {
add(diceWordOf(dice, "", wordkey.DiceListSize, dice))
}
// Not five dice: the cases of TestDiceWord, then white space, other
// digits and characters, marks, five bytes that are not five
// characters, and bytes that are not valid UTF-8.
for _, c := range []struct{ name, dice string }{
{"no dice", ""},
{"four dice", "1111"},
{"six dice", "111111"},
{"a 0", "11110"},
{"a 7", "11117"},
{"a letter", "a1111"},
{"four dice and a space", "1111 "},
{"a full-width 1 and four dice", "\uff111111"},
{"a space and four dice", " 1111"},
{"a space and five dice", " 11111"},
{"five dice and a space", "11111 "},
{"five dice and LF", "11111\n"},
{"a tab and four dice", "\t1111"},
{"two and two dice around a space", "11 11"},
{"NUL", "1111\x00"},
{"DEL", "1111\x7f"},
{"a quotation mark", "1111\""},
{"a backslash", "1111\\"},
{"00000", "00000"},
{"77777", "77777"},
{"99999", "99999"},
{"a slash, the byte before 0", "1111/"},
{"a colon, the byte after 9", "1111:"},
{"five full-width digits", "\uff11\uff12\uff13\uff14\uff15"},
{"five Arabic-Indic digits", "\u0661\u0662\u0663\u0664\u0665"},
{"a superscript 1", "1111\u00b9"},
{"a circled 1 and two dice: five bytes", "\u246011"},
{"a mathematical bold 1 and a die: five bytes", "\U0001d7cf1"},
{"four dice and an e with an acute accent: five characters", "1111\u00e9"},
{"three dice and an e with an acute accent: five bytes", "111\u00e9"},
{"five dice and a combining acute accent", "11111\u0301"},
{"three dice and the emoji of a die: five UTF-16 code units", "111\U0001f3b2"},
{"NBSP and four dice", "\u00a01111"},
{"an ideographic space and five dice", "\u300011111"},
{"two dice and a surrogate in UTF-8 bytes: five bytes", "11\xed\xa0\x80"},
{"four dice and a byte that is not valid UTF-8", "1111\xff"},
{"five bytes that are not valid UTF-8", "\xff\xfe\xfd\xfc\xfb"},
{"four dice and a sequence cut short", "1111\xc3"},
} {
add(diceWordOf(c.name, "", wordkey.DiceListSize, c.dice))
}
// A list of another size, refused before the dice.
for _, c := range []struct {
size int
dice string
}{{2048, "11111"}, {7775, "11111"}, {7777, "66666"}, {0, ""}, {1, "x"}} {
add(diceWordOf(fmt.Sprintf("%q on a list of %d words", c.dice, c.size), "", c.size, c.dice))
}
return out
}
type diceWordsCase struct {
Name string `json:"name"`
List string `json:"list,omitempty"`
Size int `json:"size"`
Mod int `json:"mod,omitempty"`
Suffix string `json:"suffix,omitempty"`
Dice string `json:"dice"`
Words []string `json:"words,omitempty"`
Error string `json:"error,omitempty"`
}
// diceWordsOf runs DiceWords on the list of diceListOf and the bytes dice,
// which the case keeps in hex.
func diceWordsOf(name, list string, size, mod int, suffix, dice string) diceWordsCase {
l := diceListOf(list, size, mod, suffix)
words, err := wordkey.DiceWords(l, dice)
return diceWordsCase{name, list, len(l), mod, suffix, h(dice), words, errText(err)}
}
func diceWordsCases() []diceWordsCase {
var out []diceWordsCase
add := func(c diceWordsCase) { out = append(out, c) }
const size = wordkey.DiceListSize
// Seven numbers, and the six after the first.
seven := "11111 11112 11113 11114 11115 11116 11121"
six := seven[len("11111 "):]
// TestDiceWords, on the English list and on the list of the indices.
for _, list := range []string{"en", ""} {
for _, c := range []struct{ name, dice string }{
{"seven numbers among white space", " 11111 11112\t11113\n11114 11115 11116 11121 "},
{"five numbers", "11111 11112 11113 11114 11115"},
{"no numbers", ""},
{"four dice", "11111 11112 11113 11114 11115 1116"},
{"11111 twice", "11111 11112 11113 11114 11115 11111"},
{"a comma between two numbers", "11111,11112 11113 11114 11115 11116 11121"},
} {
name := c.name
if list != "" {
name = list + ", " + name
}
add(diceWordsOf(name, list, size, 0, "", c.dice))
}
}
add(diceWordsOf("a list of 7775 words", "", 7775, 0, "", "11111 11112 11113 11114 11115 11116"))
// The Spanish list: words with accents, and the last word given twice.
add(diceWordsOf("es, seven numbers", "es", 0, 0, "", "66666 11112 35214 12345 54321 23456 11111"))
add(diceWordsOf("es, 66666 twice", "es", 0, 0, "", "66666 11111 35214 12345 54321 23456 66666"))
// The fewest numbers, more, and none.
add(diceWordsOf("six numbers", "", size, 0, "", six))
add(diceWordsOf("twelve numbers", "", size, 0, "", "11111 22222 33333 44444 55555 66666 12345 23456 34561 45612 56123 61234"))
add(diceWordsOf("white space only", "", size, 0, "", " \t\n\u00a0\u3000 "))
// Each white space of unicode.IsSpace between the numbers; then runes
// and bytes that are not white space between the first two, or around
// a number, which is then not five dice.
for r := rune(0); r <= unicode.MaxRune; r++ {
if unicode.IsSpace(r) {
add(diceWordsOf(fmt.Sprintf("U+%04X between the numbers", r), "", size, 0, "", strings.ReplaceAll(seven, " ", string(r))))
}
}
for _, r := range []rune{0x1c, 0x1d, 0x1e, 0x1f, 0xad, 0x301, 0x180e, 0x200b, 0x200c, 0x200d, 0x2060, 0x2800, 0x3164, 0xfeff, 0xfffd} {
add(diceWordsOf(fmt.Sprintf("U+%04X between the first two numbers", r), "", size, 0, "", "11111"+string(r)+six))
}
for _, c := range []struct{ name, dice string }{
{"the byte A0 alone, which is not NBSP", "11111\xa0" + six},
{"the byte 85 alone, which is not NEL", "11111\x85" + six},
{"a byte that is not valid UTF-8 after a number", "11111\xff " + six},
{"a surrogate in UTF-8 bytes after a number", "11111\xed\xa0\x80 " + six},
{"five full-width digits", "\uff11\uff11\uff11\uff11\uff11 " + six},
{"a combining acute accent after a number", "11111\u0301 " + six},
{"a combining acute accent before a number", "\u030111111 " + six},
// The order of the checks.
{"five fields that are not dice: the count first", "a b c d e"},
{"six fields, the first not dice", "abc 11111 11112 11113 11114 11115"},
{"not dice before a word given twice", "11111 11112 abc 11111 11113 11114"},
{"a word given twice before not dice", "11111 11112 11113 11114 11115 11111 abc"},
} {
add(diceWordsOf(c.name, "", size, 0, "", c.dice))
}
// Lists that hold a word more than once: DiceWords refuses the word,
// whatever the dice that give it.
n1000, err := wordkey.DiceNumber(1000)
check(err)
for _, c := range []struct {
mod int
dice string
}{{1, six}, {7, seven}, {7, seven + " 11122"}, {1000, "11111 11112 11113 11114 11115 " + n1000}} {
add(diceWordsOf(fmt.Sprintf("indices modulo %d: %s", c.mod, c.dice), "", size, c.mod, "", c.dice))
}
// Words that %q escapes: a quotation mark, a backslash, a tab, NBSP and
// ZWSP after each index modulo 7, so that the eighth number gives the
// word of the first again.
for _, dice := range []string{seven, seven + " 11122"} {
add(diceWordsOf("indices modulo 7 and a suffix: "+dice, "", size, 7, "\"\\\t\u00a0\u200b", dice))
}
// A list of another size, refused before the numbers.
for _, c := range []struct {
size int
dice string
}{{0, ""}, {2048, seven}, {7777, "x"}} {
add(diceWordsOf(fmt.Sprintf("%q on a list of %d words", c.dice, c.size), "", c.size, 0, "", c.dice))
}
return out
}
type diceListCase struct {
List string `json:"list,omitempty"`
Size int `json:"size"`
Sha256 string `json:"sha256,omitempty"`
Bytes int `json:"bytes,omitempty"`
Error string `json:"error,omitempty"`
}
func diceListCases() []diceListCase {
var out []diceListCase
add := func(list string, size int) {
l := diceListOf(list, size, 0, "")
text, err := wordkey.DiceList(l)
c := diceListCase{List: list, Size: len(l), Error: errText(err)}
if err == nil {
c.Sha256, c.Bytes = sum(text), len(text)
}
out = append(out, c)
}
for _, lang := range wordkey.Languages() {
add(lang, 0)
}
for _, size := range []int{wordkey.DiceListSize, 0, 1, 2048, 7775, 7777} {
add("", size)
}
return out
}
type diceSpaceHead struct {
Text string `json:"text"`
Planes []int `json:"planes"`
Count int `json:"count"`
Sha256 string `json:"sha256"`
Split []int `json:"split"`
}
// diceSpace runs DiceWords of 11111, a code point and six more numbers on
// the list of the indices, for every code point of the planes but the
// surrogates.
func diceSpace() diceSpaceHead {
planes := []int{0, 1, 14}
l := numbers(wordkey.DiceListSize)
digest := sha256.New()
var split []int
count := 0
for _, p := range planes {
for r := rune(p << 16); r <= rune(p<<16|0xffff); r++ {
if r >= 0xd800 && r <= 0xdfff {
continue
}
_, err := wordkey.DiceWords(l, "11111"+string(r)+"11112 11113 11114 11115 11116 11121")
fmt.Fprintf(digest, "U+%04X %s\n", r, result(err))
count++
if err == nil {
split = append(split, int(r))
}
}
}
return diceSpaceHead{"11111, the code point and 11112 11113 11114 11115 11116 11121, on the list of the 7776 indices", planes, count, hex.EncodeToString(digest.Sum(nil)), split}
}
// ---------------------------------------------------------------------------
// JSON
@ -672,6 +996,11 @@ func main() {
gen := generateCases()
bits := bitsCases()
digests := bitsDigests()
space := diceSpace()
diceNumbers := diceNumberCases()
diceWord := diceWordCases()
diceWords := diceWordsCases()
diceLists := diceListCases()
var w bytes.Buffer
w.WriteString("{\n")
@ -679,12 +1008,15 @@ func main() {
key string
v any
}{
{"description", "The word lists of package wordkey of the Go reference (generate.go, spec \u00a738.1) for lib/src/wordlist.dart; see tool/wordlist_go_vectors.go. " +
{"description", "The word lists of package wordkey of the Go reference (generate.go and dice.go, spec \u00a738.1) for lib/src/wordlist.dart; see tool/wordlist_go_vectors.go. " +
"Binary values and words are lower-case hex. The base list is word i = pal and three letters, a + i/676, a + i/26%26 and a + i%26; a case of size n takes its first n words, replaces the word at each at of set, and adds those of append. " +
"lists: the SHA-256, bytes and words of each built-in list. parse: the body of List on the text prefix, the base list joined by sep, and suffix (sha256 is that of the text): the words read, and ok or the error. " +
"check_list: CheckList, ok or the error (sha256 is that of the words joined by LF). alphabet: CheckList of the base list of 2048 whose first word is pala, a code point and zz, for each code point of the planes but the surrogates: the SHA-256 of the lines U+XXXX result, the code points of the words it accepts, and alphabet_results, each result up to U+017F. " +
"generate: Generate on the list of the Spanish words (list es) or on a list of size words whose indices they are, reading a stream: seeded, the keystream of SeededRandomSource of the seed; counter, SHA-256 of the label and a 32-bit big-endian counter from 0, block after block; bytes, hex repeated repeat times, which end. The indices drawn, the bytes read and the next four of an endless stream, or the error. " +
"bits: Bits, its shortest decimal and the hex of its float64. bits_digests: the SHA-256 of the 8 big-endian bytes of each Bits of the sizes and counts, inclusive, sizes outer."},
"bits: Bits, its shortest decimal and the hex of its float64. bits_digests: the SHA-256 of the 8 big-endian bytes of each Bits of the sizes and counts, inclusive, sizes outer. " +
"dice_list_size: DiceListSize. dice_space: DiceWords of the text 11111, a code point and 11112 11113 11114 11115 11116 11121 on the list of the 7776 indices, for each code point of the planes but the surrogates: the SHA-256 of the lines U+XXXX result, and split, the code points at which the numbers are split. " +
"dice_number: DiceNumber of i, the dice or the error. dice_word and dice_words: DiceWord and DiceWords of the bytes dice, in hex, on the English or the Spanish list (list en or es) or on a list of size words, each its index in decimal, modulo mod if there is one, followed by suffix: the word or the words, as text, or the error. " +
"dice_list: DiceList of the list, the SHA-256 and the bytes of its text, or the error."},
{"generator", "tool/wordlist_go_vectors.go, " + runtime.Version()},
{"source", *source},
{"default_count", wordkey.DefaultCount},
@ -692,6 +1024,8 @@ func main() {
{"min_words", wordkey.MinWords},
{"min_letters", wordkey.MinLetters},
{"alphabet", alpha},
{"dice_list_size", wordkey.DiceListSize},
{"dice_space", space},
}
for _, kv := range head {
fmt.Fprintf(&w, " %q: %s,\n", kv.key, marshal(kv.v))
@ -702,7 +1036,11 @@ func main() {
list(&w, "alphabet_results", runes, false)
list(&w, "generate", gen, false)
list(&w, "bits", bits, false)
list(&w, "bits_digests", digests, true)
list(&w, "bits_digests", digests, false)
list(&w, "dice_number", diceNumbers, false)
list(&w, "dice_word", diceWord, false)
list(&w, "dice_words", diceWords, false)
list(&w, "dice_list", diceLists, true)
w.WriteString("}\n")
var parsed any
@ -714,8 +1052,9 @@ func main() {
}
path := filepath.Join(*outDir, "wordlist_vectors.json")
check(os.WriteFile(path, w.Bytes(), 0o644))
fmt.Printf("wrote %s, %d bytes: %d lists, %d texts, %d lists checked, %d code points, %d draws, %d bits, %d digests\n",
path, w.Len(), len(lists), len(parse), len(checks), alpha.Count, len(gen), len(bits), len(digests))
fmt.Printf("wrote %s, %d bytes: %d lists, %d texts, %d lists checked, %d code points, %d draws, %d bits, %d digests; dice: %d code points, %d numbers, %d words, %d texts, %d lists\n",
path, w.Len(), len(lists), len(parse), len(checks), alpha.Count, len(gen), len(bits), len(digests),
space.Count, len(diceNumbers), len(diceWord), len(diceWords), len(diceLists))
dart := "// Generated by tool/wordlist_go_vectors.go from wordlist_vectors.json, for\n" +
"// the tests that also run compiled to JavaScript, where no file can be\n" +
"// read. Do not edit.\n\n" +

@ -13,7 +13,14 @@ from the normalized text of the words, whatever list they came from.
Each list has 7776 = 6^5 words, sorted, so that five dice give a word: each
die minus one is a digit of the position of the word in base 6, the first
die the most significant. The dice 11111 give the first word and 66666 the
last, as in the list of the EFF.
last, as in the list of the EFF. `datekeys wordlist` (`wordkey.DiceList`)
writes a list numbered for dice, to print it: a line for each word, its
dice, a tab and the word, as the EFF publishes its list.
| List numbered for dice | SHA-256 |
|---|---|
| `en` | `addd35536511597a02fa0a9ff1e5284677b8883b83e986e43f15a3db996b903e`, the file of the EFF, byte for byte |
| `es` | `611f779a33df74587e9dfb486bb0185a9dfd4d1384e75993a21247ad31cefddb` |
A list changes only with its hash in this file and in `generate_test.go`:
an application that downloads a list pins its SHA-256 and refuses any other.

@ -1,8 +1,8 @@
{
"module": "g.activething.com/go/DateKeys",
"commit": "e671032e04a3cd0999cf073ebedf50c45b088683",
"commit": "92e7154c23945c9b9a2893750b6d4d128f848d3e",
"files": {
"README.md": "d73a477d2ad4a03c6f1d61c21eeb8b754ec5a758346af62e6426979934eda097",
"README.md": "a29122e04cd8dac3e44d6f271da2ca99ac98e28b27e88c16b58162fe3eebedad",
"en.txt": "6d557f0693958fb5e650b68b5bee585eb82cf4da32965505c789e924743bc522",
"es.txt": "ff77b487765c000da97cca58fe94a2cdb947303e7a07460614d7d95d800034fe"
}

Loading…
Cancel
Save

Powered by TurnKey Linux.