Follow DateKeys v0.8.2 at f6f2e9f (canonical points, fixed error texts)

testdata is synced from datekeys-go f6f2e9f: mutations.json has 65
cases, the ten new §64 ones at steps 10 and 11 (phase 2, skipped and
counted: 31 run, 34 skipped).

The reference no longer copies library error text into errors, so the
TypeScript uses its fixed texts too: every unreadable age header is
"agewrap: not an age v1 header: malformed, truncated or beyond the
parser limits", with the parser's reason in the error's cause, which the
tests still compare with age's own texts; the profile messages for an
unknown drand scheme and a non-canonical public key follow Go. Against
Go at f6f2e9f, 407,196 differential inputs agree on verdict, code, step,
error text, check details and the full inspect -json output.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
main
dev 2 weeks ago
parent bdd04941dd
commit effbf0afde

@ -18,7 +18,7 @@ Sin dependencias de ejecución. Funciona en navegadores y en Node 20+: solo usa
`crypto.subtle` solo existe en contextos seguros: `https`, o `http` en `localhost`. La página del paso 5 servida por `http` desde una IP de la red local (por ejemplo `vite --host` para probar en un móvil) no lo tiene, y `inspect` rechaza entonces con un `Error` que lo dice (`SHA-256 needs Web Crypto (crypto.subtle), …`) en vez de dar un veredicto. El registro por defecto no memoriza ese fallo: la siguiente llamada lo vuelve a intentar.
| Fichero | Contenido | Equivale en Go (`692cf87`) |
| Fichero | Contenido | Equivale en Go (`f6f2e9f`) |
|---|---|---|
| `errors.ts` | `DateKeysError` con el código normativo de §69 (`ERR_*`); mensajes con la forma `contexto: CÓDIGO` de Go | `errors.go` |
| `bytes.ts` | Hex, UTF-8 estricto, `goQuote` (el `%q` de Go, con la tabla de `strconv.IsPrint` de Go 1.26 fijada en el código) y `sha256` (Web Crypto) | `strconv`, `unicode/utf8` |
@ -39,7 +39,9 @@ Los tests (`*.test.ts`) están junto a cada fichero.
### Equivalencia con la referencia Go
El comportamiento se contrastó con la librería Go en `3820066` (`692cf87` solo cambia la regla de UTF-8 de `dk1_` descrita abajo) mediante un oráculo diferencial fuera del repositorio: 483 527 entradas de tres semillas. Son mutaciones de los cinco `.dkc` oficiales de todas las clases (bits, bytes, truncados, inserciones, borrados, longitudes y campos del prelude, cabeceras reescritas con cambios de CBOR, DateKeys, arrays de extensiones con y sin registro de extensiones, cabeceras `age` de SEALED_CONTROL y de PAYLOAD_AGE, argumentos del stanza tlock, rondas, secciones cambiadas de sitio y combinaciones de varios defectos); CBOR de cada esquema (PUBLIC_HEADER, CONTROL_CBOR, cuerpo y fichero `.dkk`, Provider Profile, arrays de extensiones); `walk`, `peek` y `checkSchema`; cabeceras `age`, preludes, cadenas `dk1_`, rondas y fechas. En todas coinciden el veredicto, el código y el paso, y también el texto del error, el valor decodificado y la salida entera de `datekeys inspect -json`, byte a byte.
El comportamiento se contrastó con la librería Go en `3820066` (`692cf87` solo cambia la regla de UTF-8 de `dk1_` descrita abajo) mediante un oráculo diferencial fuera del repositorio: 483 527 entradas de tres semillas. Son mutaciones de los cinco `.dkc` oficiales de todas las clases (bits, bytes, truncados, inserciones, borrados, longitudes y campos del prelude, cabeceras reescritas con cambios de CBOR, DateKeys, arrays de extensiones con y sin registro de extensiones, cabeceras `age` de SEALED_CONTROL y de PAYLOAD_AGE, argumentos del stanza tlock, rondas, secciones cambiadas de sitio y combinaciones de varios defectos); CBOR de cada esquema (PUBLIC_HEADER, CONTROL_CBOR, cuerpo y fichero `.dkk`, Provider Profile, arrays de extensiones); `walk`, `peek` y `checkSchema`; cabeceras `age`, preludes, cadenas `dk1_`, rondas y fechas. En todas coinciden el veredicto, el código y el paso, y también el texto del error, el valor decodificado y la salida entera de `datekeys inspect -json`, byte a byte. Un segundo diferencial con otro generador, de 407 196 entradas, se repitió contra `f6f2e9f` (la enmienda de canonicidad de puntos) con el mismo resultado, textos incluidos.
Como la referencia desde `f6f2e9f`, los errores no copian el texto de una librería: una cabecera `age` que no se puede leer da siempre `agewrap: not an age v1 header: malformed, truncated or beyond the parser limits` (`ERR_INTEGRITY`). El motivo del parser, con las palabras de `age`, queda en `cause` del error, fuera del mensaje, y los tests lo comparan con los textos de `age` para comprobar que se rechaza por la misma razón.
Precedencia de errores (§69.1): primero la trama; después el tipo y la versión de esquema (`checkSchema`); después el perfil CBOR y el CDDL, con los límites de implementación de §74, en una sola decodificación (`unmarshal` con el decodificador del esquema, que ya comprueba tipos, tamaños, rangos, `access_policy`, `extension_version`, el máximo de 64 extensiones y su orden); y solo entonces los campos con código propio, en orden de clave: DateKey, perfil fijado y extensiones críticas en PUBLIC_HEADER; `access_type` y `access_material` en la `.dkk`; los campos y la autocomprobación de `chain_hash` en el Provider Profile. Entre pasos decide el orden de §63. Un PUBLIC_HEADER que rompe a la vez el CDDL y la DateKey da `ERR_NON_CANONICAL_CBOR`, como en la referencia de `3820066`; la de `afb44a3` leía `access_policy` y `extension_version` como `uint64` y los acotaba después de la DateKey, y la vía `wideUint` que lo imitaba ya no existe.
@ -136,7 +138,7 @@ Umbrales de cobertura (`vitest.config.ts`): `cbor.ts` al 100 % en líneas, ramas
- `testdata/fixtures/*.inspect.json`: la vista de `inspect` de cada `.dkc`, escrita con `inspectJSON` como la imprime la CLI (`file` incluido), es idéntica byte a byte al fichero, también el texto de cada paso.
- `testdata/vectors/dk1.json` (con los tres vectores de los refinamientos de §19: LF dentro del Base64, CR y LF después, y la versión `1.0000000000000001`), `quicknet_rounds.json` y `profile_quicknet.json`: se ejecutan todos.
- `testdata/vectors/cbor.json`: cada vector genérico (`accept` y `reject`) pasa por `walk` con los `max_depth` y `max_len` del fichero; los enteros aceptados comparan su `value` (número o, por encima de 2⁵³ − 1, `bigint`), y los rechazados «above max_len» o «above max_depth» se aceptan sin ese límite. Cada vector de `schemas` pasa por el decodificador de su esquema (`decodeProfile`, `decodeHeader`, `decodeControl`, `decodeAccessKeyBody`) con el código exacto, y un objeto aceptado se reescribe a los mismos bytes.
- `testdata/vectors/mutations.json`: se leen los 55 casos enteros (ediciones sobre un fixture o hex congelado, release, reloj, registro, extensiones, `.dkk` e identidades). Los 31 de los pasos 1 a 8 pasan por `inspect` con su registro y sus extensiones, y dan el mismo código y el mismo paso; los 24 de los pasos 9 a 18 necesitan `open` (fase 2) y se saltan uno a uno con ese motivo, y un test fija los dos recuentos. Las `.dkk` ofrecidas se decodifican.
- `testdata/vectors/mutations.json`: se leen los 65 casos enteros (ediciones sobre un fixture o hex congelado, release, reloj, registro, extensiones, `.dkk` e identidades). Los 31 de los pasos 1 a 8 pasan por `inspect` con su registro y sus extensiones, y dan el mismo código y el mismo paso; los 34 de los pasos 9 a 18 (entre ellos las 10 mutaciones de la enmienda de canonicidad de puntos, en los pasos 10 y 11) necesitan `open` (fase 2) y se saltan uno a uno con ese motivo, y un test fija los dos recuentos. Las `.dkk` ofrecidas se decodifican.
- `testdata/vectors/inspect_differential.json`: las 1 825 mutaciones dan el mismo veredicto, código y paso que Go; los `bases` se comprueban por su SHA-256.
- Todo se lee con los formatos de `testdata/README.md` (`testing/vectors.ts`): una clave desconocida o que falta, un valor de otro tipo, un código que no es de §69 o una edición fuera de su base hacen fallar el fichero con su motivo; nada se salta en silencio.
- Todo fichero de `testdata/` tiene que ejecutarlo algún test: un nombre nuevo exportado por Go (otro `vectors/*.json`, un fichero de fixture que ningún JSON nombra) hace fallar `testdata/ holds no file that no test runs` hasta que se le añade su bloque.
@ -175,7 +177,7 @@ Comprueba que los ficheros coinciden con `SOURCE.json`, sin faltantes ni sobrant
`.gitattributes` marca `testdata/**` como binario para que git no altere ningún byte.
Copia actual: la de `testdata/SOURCE.json` (commit `692cf87`, rama `v0.8.2`).
Copia actual: la de `testdata/SOURCE.json` (commit `f6f2e9f`, rama `v0.8.2`).
## Licencia

@ -4,6 +4,16 @@ import { quicknet } from './profile.ts';
import { expectCode, hx, readBytes, readJSON } from './testing/testdata.ts';
const te = new TextEncoder();
// The reason behind the fixed ERR_INTEGRITY text of parseAgeHeader, in the
// words of age (the error's cause).
const causeOf = (f: () => unknown): string => {
try {
f();
} catch (err) {
return ((err as Error).cause as Error | undefined)?.message ?? '';
}
return '';
};
const INTRO = 'age-encryption.org/v1';
const MAC = '--- ' + 'A'.repeat(43);
const age = (...lines: string[]): Uint8Array => te.encode(lines.join('\n') + '\n');
@ -126,7 +136,8 @@ describe('parseAgeHeader', () => {
expect(parseAgeHeader(at).length).toBe(limit);
const over = header(limit + 1);
expect(over.length).toBe(limit + 1);
expect(() => parseAgeHeader(over)).toThrow(/header exceeds 2 MiB/);
expect(() => parseAgeHeader(over)).toThrow(/^agewrap: not an age v1 header: .*: ERR_INTEGRITY$/);
expect(causeOf(() => parseAgeHeader(over))).toMatch(/header exceeds 2 MiB$/);
});
it('rejects everything age rejects', () => {
@ -179,12 +190,17 @@ describe('parseAgeHeader', () => {
expect(cases).toHaveLength(GO_MESSAGES.length);
for (const [i, c] of cases.entries()) {
let msg = '';
let cause = '';
try {
parseAgeHeader(c);
} catch (err) {
msg = (err as Error).message;
cause = ((err as Error).cause as Error).message;
}
expect(msg, `case ${i}`).toBe(`agewrap: not a valid age file: failed to read header: ${GO_MESSAGES[i]!}: ERR_INTEGRITY`);
// The message is the reference's fixed text; the cause keeps the reason
// in the words of age, so the parser still rejects for the same reason.
expect(msg, `case ${i}`).toBe(`agewrap: not an age v1 header: malformed, truncated or beyond the parser limits: ERR_INTEGRITY`);
expect(cause, `case ${i}`).toBe(`failed to read header: ${GO_MESSAGES[i]!}`);
}
});
@ -199,13 +215,13 @@ describe('parseAgeHeader', () => {
];
for (const [arg, quoted] of lines) {
const file = new Uint8Array([...te.encode(`${INTRO}\n-> X25519 `), ...arg, 0x0a]);
let msg = '';
let cause = '';
try {
ageStanzas(file);
} catch (err) {
msg = (err as Error).message;
cause = ((err as Error).cause as Error).message;
}
expect(msg).toBe(`agewrap: not a valid age file: failed to read header: failed to parse header: malformed stanza: "-> X25519 ${quoted}${B}n": ERR_INTEGRITY`);
expect(cause).toBe(`failed to read header: failed to parse header: malformed stanza: "-> X25519 ${quoted}${B}n"`);
}
});
@ -218,15 +234,19 @@ describe('parseAgeHeader', () => {
expect(build(fit).length).toBeLessThanOrEqual(2 << 20);
expect(parseAgeHeader(build(fit)).stanzas[0]!.body).toHaveLength(fit * 48);
expect(build(fit + 1).length).toBeGreaterThan(2 << 20);
expectCode(() => parseAgeHeader(build(fit + 1)), 'ERR_INTEGRITY', /: failed to read header: failed to read header: parsing age header: header exceeds 2 MiB: /);
expectCode(() => parseAgeHeader(build(fit + 1)), 'ERR_INTEGRITY');
expect(causeOf(() => parseAgeHeader(build(fit + 1)))).toBe('failed to read header: failed to read header: parsing age header: header exceeds 2 MiB');
// A line cut by the limit, in a larger file, and the three bytes of "---".
const cut = new Uint8Array((2 << 20) + 10).fill(0x41);
cut.set(te.encode(prefix));
expectCode(() => parseAgeHeader(cut), 'ERR_INTEGRITY', /: failed to parse header: failed to read line: parsing age header: header exceeds 2 MiB: /);
expectCode(() => parseAgeHeader(cut), 'ERR_INTEGRITY');
expect(causeOf(() => parseAgeHeader(cut))).toMatch(/: failed to parse header: failed to read line: parsing age header: header exceeds 2 MiB$/);
const edge = build(fit);
const tail = new Uint8Array(edge.length - MAC.length - 1);
tail.set(edge.subarray(0, tail.length));
expectCode(() => parseAgeHeader(new Uint8Array([...tail, 0x2d, 0x2d])), 'ERR_INTEGRITY', /: parsing age header: failed to read header: EOF: /);
const eof = new Uint8Array([...tail, 0x2d, 0x2d]);
expectCode(() => parseAgeHeader(eof), 'ERR_INTEGRITY');
expect(causeOf(() => parseAgeHeader(eof))).toMatch(/: parsing age header: failed to read header: EOF$/);
});
});

@ -237,14 +237,19 @@ function parse(file: Uint8Array): AgeHeader {
/**
* Parses the age header at the start of `file`, as age's format.Parse, and
* returns its stanzas and MAC. Bytes after the header are not read. Throws
* ERR_INTEGRITY, with the error text of age, for anything age would not
* parse.
* ERR_INTEGRITY for anything age would not parse, with the fixed text of the
* reference (agewrap.Stanzas) as its message and the parser's reason, in the
* words of age, as its cause.
*/
export function parseAgeHeader(file: Uint8Array): AgeHeader {
try {
return parse(file);
} catch (err) {
throw new DateKeysError('ERR_INTEGRITY', `agewrap: not a valid age file: failed to read header: ${(err as Error).message}`);
throw new DateKeysError(
'ERR_INTEGRITY',
'agewrap: not an age v1 header: malformed, truncated or beyond the parser limits',
new AgeError(`failed to read header: ${(err as Error).message}`),
);
}
}

@ -37,16 +37,19 @@ export class DateKeysError extends Error {
* @param code the normative code.
* @param context the text before the code; the message is
* `${context}: ${code}`, or the bare code without context.
* @param cause an internal reason that stays out of the message, such as
* the parser detail behind a fixed text (the reference never copies a
* library's error text into its errors).
*/
constructor(code: ErrorCode, context?: string) {
super(context === undefined || context === '' ? code : `${context}: ${code}`);
constructor(code: ErrorCode, context?: string, cause?: unknown) {
super(context === undefined || context === '' ? code : `${context}: ${code}`, cause === undefined ? undefined : { cause });
this.name = 'DateKeysError';
this.code = code;
}
/** Returns a copy of this error with `prefix: ` prepended to the message. */
wrap(prefix: string): DateKeysError {
const e = new DateKeysError(this.code);
const e = new DateKeysError(this.code, undefined, this.cause);
e.message = `${prefix}: ${this.message}`;
return e;
}

@ -167,7 +167,7 @@ describe('inspect', () => {
const late = run(frame({ ...parts, header: header({ dk: t(dkRound(83903165812)) }), payload: noStanza(parts.payload) }));
expect(last(late)).toEqual([6, 'payload structure', false, 'ERR_INTEGRITY']);
expect(late.checks.at(-1)!.detail).toBe(
'capsule: PAYLOAD_AGE: agewrap: not a valid age file: failed to read header: parsing age header: no recipient stanzas: ERR_INTEGRITY',
'capsule: PAYLOAD_AGE: agewrap: not an age v1 header: malformed, truncated or beyond the parser limits: ERR_INTEGRITY',
);
});
@ -184,7 +184,7 @@ describe('inspect', () => {
const cr = run(frame({ ...parts, sealed: replaceText(parts.sealed, `${ch}\n`, `${ch}\r\n`) }));
expect(last(cr)).toEqual([5, 'sealed control structure', false, 'ERR_INTEGRITY']);
expect(cr.checks.at(-1)!.detail).toBe(
`capsule: SEALED_CONTROL: agewrap: not a valid age file: failed to read header: failed to parse header: malformed stanza: "-> tlock 2000 ${ch}\\r\\n": ERR_INTEGRITY`,
'capsule: SEALED_CONTROL: agewrap: not an age v1 header: malformed, truncated or beyond the parser limits: ERR_INTEGRITY',
);
});
@ -224,7 +224,7 @@ describe('inspect', () => {
const outer = (sealed: Uint8Array): Inspection => run(frame({ ...parts, sealed }));
const payload = (p: Uint8Array): Inspection => run(frame({ ...parts, payload: p }));
expect(last(outer(new Uint8Array(10)))).toEqual([5, 'sealed control structure', false, 'ERR_INTEGRITY']);
expect(outer(new Uint8Array(10)).checks.at(-1)!.detail).toMatch(/^capsule: SEALED_CONTROL: agewrap: not a valid age file: /);
expect(outer(new Uint8Array(10)).checks.at(-1)!.detail).toMatch(/^capsule: SEALED_CONTROL: agewrap: not an age v1 header: /);
const twoStanzas = replaceText(parts.sealed, '\n---', '\n-> X25519 AAAA\n\n---');
const two = outer(twoStanzas);
expect(last(two)).toEqual([5, 'sealed control structure', false, 'ERR_POLICY_STRUCTURE_MISMATCH']);

@ -352,11 +352,11 @@ async function validateDrand(p: Profile): Promise<void> {
if (OTHER_DRAND_SCHEMES.has(p.scheme)) {
throw new DateKeysError('ERR_UNKNOWN_PROFILE', `profile ${p.id}: scheme ${goQuote(p.scheme)} is not supported by tlock`);
}
throw new DateKeysError('ERR_UNKNOWN_PROFILE', `profile ${p.id}: invalid scheme name ${goQuote(p.scheme)}`);
throw new DateKeysError('ERR_UNKNOWN_PROFILE', `profile ${p.id}: ${goQuote(p.scheme)} is not a drand scheme`);
}
const point = checkCompressedPoint(group, p.publicKey);
if (point === 'invalid') {
throw new DateKeysError('ERR_UNKNOWN_PROFILE', `profile ${p.id}: public key is not a ${p.scheme} group element`);
throw new DateKeysError('ERR_UNKNOWN_PROFILE', `profile ${p.id}: public key is not the canonical encoding of a point of the key group of ${p.scheme}`);
}
if (point === 'identity') throw new DateKeysError('ERR_UNKNOWN_PROFILE', `profile ${p.id}: public key is the identity element`);
const hash = await chainInfoHash(p);

@ -502,14 +502,16 @@ describe('vectors/mutations.json', () => {
const inspected = f.filter((c) => c.step <= LAST_INSPECT_STEP);
const opened = f.filter((c) => c.step > LAST_INSPECT_STEP);
it('has the cases README counts: 55, the 23 of §64 first, 31 in steps 1 to 8 (13 of them from §64), 24 after step 8', () => {
expect(f).toHaveLength(55);
expect(f.slice(0, 23).every((c) => c.spec)).toBe(true);
expect(f.slice(23).some((c) => c.spec)).toBe(false);
it('has the cases README counts: 65, the 33 of §64 first, 31 in steps 1 to 8 (13 of them from §64), 34 after step 8', () => {
// 3820066 had 55 cases (23 from §64); the v0.8.2 amendment on canonical
// point encoding (f6f2e9f) added ten §64 cases at steps 10 and 11.
expect(f).toHaveLength(65);
expect(f.slice(0, 33).every((c) => c.spec)).toBe(true);
expect(f.slice(33).some((c) => c.spec)).toBe(false);
expect(inspected.length).toBe(31);
expect(inspected.filter((c) => c.spec).length).toBe(13);
// The cases that are skipped below, and only those.
expect(opened.length).toBe(24);
expect(opened.length).toBe(34);
expect(inspected.length + opened.length).toBe(f.length);
expect(new Set(f.map((c) => c.name)).size, 'unique names').toBe(f.length);
});

@ -133,6 +133,9 @@ describe('readCapsule', () => {
const endless = concat(intro, new Uint8Array(3 << 20).fill(0x41));
const dkc = frame({ ...parts, payload: endless });
await samePrefixVerdict(dkc);
expect(inspectWith(dkc, registry).checks.at(-1)!.detail).toMatch(/header exceeds 2 MiB/);
// The reference reports every malformed age header with one fixed text.
expect(inspectWith(dkc, registry).checks.at(-1)!.detail).toBe(
'capsule: PAYLOAD_AGE: agewrap: not an age v1 header: malformed, truncated or beyond the parser limits: ERR_INTEGRITY',
);
});
});

30
testdata/README.md vendored

@ -32,7 +32,7 @@ Conventions for every file:
| `vectors/quicknet_rounds.json` | date → round resolution | §15, §16, §65 |
| `vectors/dk1.json` | canonical `dk1_` strings, and rejected encodings with their code | §18, §19, §66 |
| `vectors/cbor.json` | the CBOR profile, and one block of vectors per schema | §58, CDDL |
| `vectors/mutations.json` | the mutation corpus: 23 mutations of §64 and further cases | §63, §64 |
| `vectors/mutations.json` | the mutation corpus: 33 mutations of §64 and further cases | §63, §64 |
| `vectors/inspect_differential.json` | 1825 mutations of the fixtures with the verdict of steps 1 to 8 | §63 |
| `fixtures/<name>.dkc`, `<name>.json` | official capsules and every intermediate value | §67 |
| `fixtures/<name>.dkk`, `<name>.dkk.json` | official access keys | §68 |
@ -173,7 +173,7 @@ reading flow (`capsule.Open`, §63) must fail.
```
- `name`: unique, stable.
- `spec`: true for the 23 mutations listed in spec §64 (the first 23 cases),
- `spec`: true for the 33 mutations listed in spec §64 (the first 33 cases),
false for the further cases of the reference.
- `dkc`: the capsule, as edits of a fixture (see above). The reader gets it as a
seekable file, so that the `capsule_digest` of an offered `.dkk` is checked
@ -184,8 +184,9 @@ reading flow (`capsule.Open`, §63) must fail.
credentials; absent when none.
- `release`: what the release source answers to every request, whatever round
is asked for. The reader must verify it (§51): a case may serve a release of
another round, or a round with the signature of another. `null` means that
no release is available (`ERR_RELEASE_UNAVAILABLE`).
another round, a round with the signature of another, or a signature that is
not the canonical encoding of a point (§12.2). `null` means that no release
is available (`ERR_RELEASE_UNAVAILABLE`).
- `now`: the reader's clock, RFC 3339. No release is requested before the round
time of the DateKey.
- `registry`: `default` pins exactly the Quicknet profile of
@ -224,6 +225,27 @@ file (steps 11, 13 and 17), are those of spec §63 as well. In this corpus:
- every `.dkk` offered decodes: the corpus checks step 9.a, not the decoding
of a `.dkk`, whose errors spec §63 also places at step 9.a.
### Point encodings: steps 10 and 11
Ten cases of §64 test the canonical point encoding of spec §12.2 and the tlock
stanza body `U || V || W` of spec §63 step 11:
- five edit the tlock stanza body of `time_only.dkc`: U with c0 + p, U the
point at infinity (the byte 0xc0, then zeros), U with the infinity flag
over its own coordinate bits, and bodies of 127 and 129 bytes. Each recomputes the header
MAC of OUTER_TIME_AGE with FK_TIME, so the age header stays authentic and
only the rules of step 11 reject the capsule (`ERR_INTEGRITY`); a reader
whose decoder reduces coordinates modulo p opens the first one;
- three serve the release of `time_only.dkc` with its signature edited: the
point at infinity, the infinity flag over its own coordinate bits, and the
negated signature (the sort flag flipped: a canonical point that does not
verify); a fourth serves the published signature of round 1004 re-encoded
as x + p, over a frozen capsule for round 1004, because no fixture round
has a signature whose x + p fits in 381 bits. All four fail at step 10
(`ERR_RELEASE_INVALID`);
- the last one serves the negated signature with U with c0 + p: step 10
comes first.
## The checks of steps 1 to 8
`mutations.json` and `inspect_differential.json` follow the rules of the

@ -1,8 +1,8 @@
{
"module": "g.activething.com/go/DateKeys",
"commit": "692cf87db1ea88a88624b59e2a95400fbc236e08",
"commit": "f6f2e9f55f01d8773b2fe7872b15541012704004",
"files": {
"README.md": "f8f41632ee3e7cd3f7a3d3fa4459bc7bdbd201d905109ba8a9f9c6a5defcda78",
"README.md": "720070e7d4f42b426f66f58f41a0c9e640b11701cf175a0e51f0e555ee4d889f",
"fixtures/empty_payload.dkc": "871e9bf05b52bbae17f3adfbbf97b46e7f0e53aa8f57bcaa506e43f36f53a9d4",
"fixtures/empty_payload.inspect.json": "dd2b21faefdfe3aa59b5eeef9130a6896070ae30a2b16933f3aefd146828207e",
"fixtures/empty_payload.json": "490a6e7f7acc882bcb956b297ad8c817d7b21da29e8d4c4a2cd31c852707eee0",
@ -32,7 +32,7 @@
"vectors/cbor.json": "444fe6104476fbcda91e1873c12752186dbf7e55ad0769e1eb0678ed9673c9d6",
"vectors/dk1.json": "3a345330fed244662b0a335f0a6d5581c200ceeb09bfb74c4d1c342daea72864",
"vectors/inspect_differential.json": "fe207b67d307e3ae7a78295b7a882d98669adbf498b725443ab4757eb9da03f7",
"vectors/mutations.json": "0ab1f3dd30f4aef86c39a69b314a2843d0819193153a97fd3b9959199d0dbd65",
"vectors/mutations.json": "c4599d1ecc4ea5dcc64e3d963db0714b28661db8daf8277346dc8238218e7c33",
"vectors/profile_quicknet.json": "4f9de60475d0807aa580f59cf3e6df38cba2a55a62f590aefa2a55753ddb0055",
"vectors/quicknet_rounds.json": "22c764aac454ce320daf7e65b9494ff2d207cac974c68d07e84f4ec18c3ed920"
}

@ -458,6 +458,199 @@
"error": "ERR_NON_CANONICAL_CBOR",
"step": 4
},
{
"name": "tlock stanza U re-encoded with c0 + p",
"spec": true,
"dkc": {
"base": "time_only.dkc",
"edits": [
[303, 156, "4b3974334342577331793673785069396157564b384579504b612b796b6762676b4a46782f5a763371667465747a654b4f7a3852534856786467582f6e37506e0a62435948626e705136755561775854674a5342647748507232587a4e56733234454758492b3676375543770a2d2d2d20675a464c773845307137726b6e384b675355414e76345a316679413258534e446c584653584b617456716f"]
]
},
"release": {
"round": 1000,
"signature": "b44679b9a59af2ec876b1a6b1ad52ea9b1615fc3982b19576350f93447cb1125e342b73a8dd2bacbe47e4b6b63ed5e39"
},
"now": "2023-08-23T15:59:24Z",
"registry": "default",
"network": true,
"frozen": false,
"error": "ERR_INTEGRITY",
"step": 11
},
{
"name": "tlock stanza U is the point at infinity",
"spec": true,
"dkc": {
"base": "time_only.dkc",
"edits": [
[238, 221, "774141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141410a414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141414141410a62435948626e705136755561775854674a5342647748507232587a4e56733234454758492b3676375543770a2d2d2d20472f6d426f3779325364374b6f4a567364384734547a357557346f6a77384e6467453772384b717a514534"]
]
},
"release": {
"round": 1000,
"signature": "b44679b9a59af2ec876b1a6b1ad52ea9b1615fc3982b19576350f93447cb1125e342b73a8dd2bacbe47e4b6b63ed5e39"
},
"now": "2023-08-23T15:59:24Z",
"registry": "default",
"network": true,
"frozen": false,
"error": "ERR_INTEGRITY",
"step": 11
},
{
"name": "tlock stanza U with the infinity flag and a payload",
"spec": true,
"dkc": {
"base": "time_only.dkc",
"edits": [
[238, 220, "77492b4f36746b50507576754c6d3456314a4369713563794644554e463532634b662f49424f59532b50335557385053725068564c437337746d5a7634776c490a4564706c48647773384a5268715645484a686d65474f67583369712f445051684b574366584b564773396441437a654c69657352534c74796467582f6f416b380a62435948626e705136755561775854674a5342647748507232587a4e56733234454758492b3676375543770a2d2d2d20336a65787a7a2f586e7359324459467751754c706d6b6f2b6d465373366b786c6c775273487a6e51346e"]
]
},
"release": {
"round": 1000,
"signature": "b44679b9a59af2ec876b1a6b1ad52ea9b1615fc3982b19576350f93447cb1125e342b73a8dd2bacbe47e4b6b63ed5e39"
},
"now": "2023-08-23T15:59:24Z",
"registry": "default",
"network": true,
"frozen": false,
"error": "ERR_INTEGRITY",
"step": 11
},
{
"name": "tlock stanza body of 127 bytes",
"spec": true,
"dkc": {
"base": "time_only.dkc",
"edits": [
[15, 444, "bda5006a646174656b657963617001010250ad4d676812b134ff8a3de263f77018b4037853646b315f65794a325a584a7a61573975496a6f784c434a755a58523362334a72496a6f695a4746305a57746c65584d3663585670593274755a585136646a45694c434a79623356755a4349364d5441774d483004006167652d656e6372797074696f6e2e6f72672f76310a2d3e20746c6f636b203130303020353264623962613730653063633066366561663738303364643037343437613166353437373733356664336636363137393262613934363030633834653937310a67492b4f36746b50507576754c6d3456314a4369713563794644554e463532634b662f49424f59532b50335557385053725068564c437337746d5a7634776c490a4564706c48647773384a5268715645484a686d65474f67583369712f445051684b574366584b564773396441437a654c69657352534c74796467582f6f416b380a62435948626e705136755561775854674a5342647748507232587a4e56733234454758492b36763755410a2d2d2d206d6c44362b7a79424a6b524852363657776857654f5362584a5030394c6373734f57796c4962412f6c5649"]
]
},
"release": {
"round": 1000,
"signature": "b44679b9a59af2ec876b1a6b1ad52ea9b1615fc3982b19576350f93447cb1125e342b73a8dd2bacbe47e4b6b63ed5e39"
},
"now": "2023-08-23T15:59:24Z",
"registry": "default",
"network": true,
"frozen": false,
"error": "ERR_INTEGRITY",
"step": 11
},
{
"name": "tlock stanza body of 129 bytes",
"spec": true,
"dkc": {
"base": "time_only.dkc",
"edits": [
[15, 444, "bfa5006a646174656b657963617001010250ad4d676812b134ff8a3de263f77018b4037853646b315f65794a325a584a7a61573975496a6f784c434a755a58523362334a72496a6f695a4746305a57746c65584d3663585670593274755a585136646a45694c434a79623356755a4349364d5441774d483004006167652d656e6372797074696f6e2e6f72672f76310a2d3e20746c6f636b203130303020353264623962613730653063633066366561663738303364643037343437613166353437373733356664336636363137393262613934363030633834653937310a67492b4f36746b50507576754c6d3456314a4369713563794644554e463532634b662f49424f59532b50335557385053725068564c437337746d5a7634776c490a4564706c48647773384a5268715645484a686d65474f67583369712f445051684b574366584b564773396441437a654c69657352534c74796467582f6f416b380a62435948626e705136755561775854674a5342647748507232587a4e56733234454758492b367637554377410a2d2d2d20325a524d54626a4b62363170795949635756664f79336d75474f584f777056366b4d4b5a4b553434484a30"]
]
},
"release": {
"round": 1000,
"signature": "b44679b9a59af2ec876b1a6b1ad52ea9b1615fc3982b19576350f93447cb1125e342b73a8dd2bacbe47e4b6b63ed5e39"
},
"now": "2023-08-23T15:59:24Z",
"registry": "default",
"network": true,
"frozen": false,
"error": "ERR_INTEGRITY",
"step": 11
},
{
"name": "release signature re-encoded with x + p",
"spec": true,
"dkc": {
"edits": [
[0, 0, "444b43310100000000000079000001bea5006a646174656b657963617001010250a7336e7ec2e3ae0b86694ddd7be97da9037853646b315f65794a325a584a7a61573975496a6f784c434a755a58523362334a72496a6f695a4746305a57746c65584d3663585670593274755a585136646a45694c434a79623356755a4349364d5441774e483004006167652d656e6372797074696f6e2e6f72672f76310a2d3e20746c6f636b203130303420353264623962613730653063633066366561663738303364643037343437613166353437373733356664336636363137393262613934363030633834653937310a7439545361546770564a6e392b4861653971615944366a5a746e2f493243716d4b542b77756732614a51394c612f4855474d515a337475412f713846475571510a41712b48753433514e6a6f5062703647727678526461354448532f6a50714667504736485a6a3259387a4771523274714c2f692b744a5a46464e44684e375a620a6d5a796e7a55717a6d7659346753324c48507342686c504c792b57766f6d694f5643784d6b35334c612f670a2d2d2d205173416550494e6e34734247574f525258596a6579614d52364a4e644f734259646d6351416e65516e6b410a15bc97bcfc7d847e72586753b8c90e8d39a7d42905a881f8e6152c51d7dc51e41c1d5c81df60fa04c03ba568ef6ec0962867c157c2422f6e04327433ba2c741b8a9217a58d27376a1e7280c1ec7fe6eebadb72ee0882b55d32e167fd72491ac77407b4aaff6c5b972bc61a07401988371fbb0b2b6ebf1307cb8b576167652d656e6372797074696f6e2e6f72672f76310a2d3e2058323535313920573365486f4233743353593277524f46506c6d326e47594e33627363714c714a7a476d35754575787748300a665333744877494255434d36497241336b4a4179353663483836756d7a5436477a63497268526b466a51670a2d2d2d2074694d326b676a652f384c2b494b69736930397076525555304450593371564758493437356831413969380ad2636c0ca74ce080748ad38ba4abe73f7527099ed68830e9fa83512290bcb69401acb6a51ef8017216a42c0fe6da88a310"]
]
},
"release": {
"round": 1004,
"signature": "be076aa25a40df5b4d22f9f7bcedaff30dcac20d94556107b8e652c7b54b2a440ce796f9fa53ad28023c84b40a580f55"
},
"now": "2024-08-23T15:09:27Z",
"registry": "default",
"network": true,
"frozen": true,
"error": "ERR_RELEASE_INVALID",
"step": 10
},
{
"name": "release signature is the point at infinity",
"spec": true,
"dkc": {
"base": "time_only.dkc",
"edits": []
},
"release": {
"round": 1000,
"signature": "c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
},
"now": "2023-08-23T15:59:24Z",
"registry": "default",
"network": true,
"frozen": false,
"error": "ERR_RELEASE_INVALID",
"step": 10
},
{
"name": "release signature with the infinity flag and a payload",
"spec": true,
"dkc": {
"base": "time_only.dkc",
"edits": []
},
"release": {
"round": 1000,
"signature": "d44679b9a59af2ec876b1a6b1ad52ea9b1615fc3982b19576350f93447cb1125e342b73a8dd2bacbe47e4b6b63ed5e39"
},
"now": "2023-08-23T15:59:24Z",
"registry": "default",
"network": true,
"frozen": false,
"error": "ERR_RELEASE_INVALID",
"step": 10
},
{
"name": "release signature negated",
"spec": true,
"dkc": {
"base": "time_only.dkc",
"edits": []
},
"release": {
"round": 1000,
"signature": "944679b9a59af2ec876b1a6b1ad52ea9b1615fc3982b19576350f93447cb1125e342b73a8dd2bacbe47e4b6b63ed5e39"
},
"now": "2023-08-23T15:59:24Z",
"registry": "default",
"network": true,
"frozen": false,
"error": "ERR_RELEASE_INVALID",
"step": 10
},
{
"name": "negated release signature and U re-encoded with c0 + p",
"spec": true,
"dkc": {
"base": "time_only.dkc",
"edits": [
[303, 156, "4b3974334342577331793673785069396157564b384579504b612b796b6762676b4a46782f5a763371667465747a654b4f7a3852534856786467582f6e37506e0a62435948626e705136755561775854674a5342647748507232587a4e56733234454758492b3676375543770a2d2d2d20675a464c773845307137726b6e384b675355414e76345a316679413258534e446c584653584b617456716f"]
]
},
"release": {
"round": 1000,
"signature": "944679b9a59af2ec876b1a6b1ad52ea9b1615fc3982b19576350f93447cb1125e342b73a8dd2bacbe47e4b6b63ed5e39"
},
"now": "2023-08-23T15:59:24Z",
"registry": "default",
"network": true,
"frozen": false,
"error": "ERR_RELEASE_INVALID",
"step": 10
},
{
"name": "magic",
"spec": false,

Loading…
Cancel
Save

Powered by TurnKey Linux.