‹ ir al inicio

Verificar este resultado

Cómo se llega al , paso a paso — y por qué no se pudo elegir.

Los pasos 1 y 2 comprueban los dos insumos; el 3 produce el número con ellos; el 4 comprueba que el archivo que lo declara es nuestro. Ninguno necesita confiar en nosotros.

| turno %%Fecha%% %%Turno%% UTC | versión

La hora del turno es la referencia. El número nace en la hora del turno y se publica unos segundos después: lo que tarda leerlo, firmarlo y anclarlo. Antes de esa hora no existe, y nadie —nosotros incluidos— puede conocerlo ni calcularlo. Esa hora está fijada de antemano y es la misma para todo el mundo.
comprobando…
1 / 4

El compromiso

La semilla se reveló recién en el -emission. Su SHA-256 tiene que dar exactamente lo que ya estaba publicado en el -commitment, anclado antes de que existiera la ronda de drand.

entra · seed revelada en el -emission — editala y mirá qué pasa
SHA-256( bytes )
sale · lo que da tu semilla
tiene que ser · seed_sha256 del -commitment
%%SeedSHA%%
Si tu código da otro valor, casi siempre es esto: hashear el texto hexadecimal en vez de los 32 bytes que representa. Con esta semilla, el texto da — si ese es el tuyo, ya sabés dónde está.
2 / 4

La ronda de drand

No hay que creerle a nuestro archivo: la ronda se comprueba contra la firma BLS del grupo de drand, o se pide directo a cualquiera de sus servidores.

round
%%Ronda%%
1 · mensaje = SHA-256( previous_signature seguido de round )

«Seguido de» es pegar, no sumar. Los bytes de uno y después los del otro, sin separador y sin operación aritmética entre ellos:

96 bytes de previous_signature + 8 bytes de round = 104 bytes que se hashean juntos

Y round no va como texto: son 8 bytes, el número en binario, empezando por el más significativo — lo que se llama big-endian. La ronda %%Ronda%% es %%RondaBE%%, no "%%Ronda%%".

Este mensaje es lo que el grupo firmó. No entra en nuestro cálculo — sirve para comprobar que la ronda es auténtica.

entra · previous_signature
entra · round — 8 bytes big-endian
sale · mensaje — el único campo que no se edita: se calcula
2 · BLS.Verify( clave del grupo, mensaje, signature )
entra 1 · drand_public_key — la clave del grupo, viaja en el archivo verla en drand
entra 2 · mensaje
el que acaba de salir del paso 1
entra 3 · round.signature — la firma del grupo, en el archivo
sale · true o false

Se hace acá mismo, en tu navegador. Tarda un instante: son emparejamientos sobre BLS12-381.

3 · randomness == SHA-256( signature )
sale · tiene que dar el randomness

Las dos comprobaciones hacen falta: sin la segunda, alguien en el medio podría entregar un par randomness/signature inventado pero coherente entre sí.

randomnessesto sí entra en el cálculo, como salt del HKDF abrir esa ronda
firma BLS válida contra la clave del grupo
Ojo: previous_signature es insumo del mensaje BLS. No es un encadenamiento entre nuestras emisiones — eso no existe en este formato.
3 / 4

La derivación de

Acá salen los números. Elegí la variante y el cálculo se rehace entero.

Nada de esto es propio: HKDF-SHA256 (RFC 5869) convierte la semilla en bytes, rejection sampling —muestreo por rechazo— los pasa a números sin sesgo, y Fisher-Yates hace las selecciones sin reposición. Tres algoritmos publicados, para que cualquiera los reimplemente.

entra · la variante
entra · version — con qué reglas salió
entra · drand_round — de qué ronda
entra · ikm = seed — la del paso 1, editala allá
entra · salt = randomness — el del paso 2, editalo allá
HKDF-SHA256( ikm = seed , salt = randomness , info = "version|variante|ronda|bloque|entregados" ) → flujo de bytes
El bloque es el índice dentro de la variante — sin él, los dos bloques de una variante compuesta quedarían correlacionados. Los entregados son los bytes ya consumidos: el flujo se estira de a 64 y ese contador hace que cada tramo sea distinto del anterior. Más abajo se ve el info armado con los valores de este turno.
sale · el resultado
El rejection sampling no es opcional. Tomar byte MOD 10 a secas mete sesgo: con 256 valores posibles y 10 resultados, los dígitos 0..5 saldrían también con los bytes 250..255 y quedarían sobrerrepresentados — 26 bytes cada uno contra 25, un 4 % más seguido. Si el byte cae ahí se descarta y se toma el siguiente.

El corte es (espacio DIV universo) × universo, donde DIV es la división entera — se descarta la parte decimal. Con universo 10 da (256 DIV 10) × 10 = 250; con universo 37, (256 DIV 37) × 37 = 222.
4 / 4

La firma del archivo

Ed25519 sobre cabecera_b64 . payload_b64 exactamente como vienen. La clave pública sale del registro, y su época tiene que cubrir la fecha que el archivo declara.

entra · kid — qué clave dice el archivo que lo firmó
Del registro de claves: e1 y e2. Cambialo a e1 y la firma deja de validar, porque la clave es otra.
entra · la clave pública de ese kid, en hexadecimal
Cambiar el kid de arriba la repone sola. Restaurar vuelve a la del kid que esté puesto.
entra · la cabecera — primera parte del .jws
entra · el payload — segunda parte
se firma esto: cabecera.payload — del archivo %%Ruta%%-emission.jws
entra · la firma — lo que va después del segundo punto
64 bytes en base64url.
Ed25519.Verify( clave pública , "cabecera.payload" , firma )
sale · el veredicto
No re-serialices el JSON. Un espacio de diferencia rompe la firma. Y el campo alg de la cabecera se ignora: aceptarlo es el ataque alg:none.

El paso 1 en cinco lenguajes. Lo demás está en la especificación. Los cinco verificadores completos, en: GitHub o GitLab

Go Python JavaScript C++ Delphi
seed, _ := hex.DecodeString("%%Seed%%")
suma := sha256.Sum256(seed)          // sobre los BYTES, no sobre el texto
ok := hex.EncodeToString(suma[:]) == "%%SeedSHA%%"
Ver el archivo firmado
el JWS tal cual se publica — cabecera.payload.firma
%%Cab%%.%%Pay%%.%%Fir%%
cabecera — qué algoritmo y qué kid payload — los datos y los 34 resultados firma — los 64 bytes de Ed25519

Se firma cabecera.payload: las dos primeras partes con el punto del medio, tal cual están ahí arriba.

el payload decodificado — para leer y copiar valores

Lo que se firma es el texto cabecera.payload tal como está arriba. El JSON de abajo es solo para leerlo: re-serializarlo rompe la firma.

Descargar el archivo firmado | Verlo anclado en: GitHub o GitLab | Especificación del formato en: GitHub o GitLab