Vai al contenuto
Ridgun
Italiano

32 Il Libro di Ridgun

La lib crypto — SHA-256, HMAC, PBKDF2, Argon2id e ChaCha20-Poly1305 AEAD

32.1. Perché esiste la lib crypto? (Il problema reale e la filosofia Ridgun)

«Perché ho bisogno di una lib crypto scritta da zero in Ridgun puro invece di collegare una libreria C tradizionale?»

Questa è la prima domanda che si pone qualsiasi ingegnere di sistema arrivando a crypto. E la risposta riassume l'intera filosofia di ridgun-libs:

Siamo sinceri: confrontare un HMAC con == apre la porta agli attacchi temporali (timing attacks), e dipendere da libssl.so per calcolare uno SHA-256 o cifrare con ChaCha20-Poly1305 rompe la portabilità statica di qualsiasi binario.

La lib crypto implementa al 100% in Ridgun puro su Tensor[Int] preallocato: operazioni su byte/bit (RGF-019), FIPS 180-4 SHA-256, RFC 2104 HMAC-SHA-256, uguaglianza in tempo costante (ct equal), RFC 8018 PBKDF2-HMAC-SHA-256 con precalcolo dello stato intermedio SHA-256, facciata Argon2id a 5 argomenti e cifratura autenticata RFC 8439 ChaCha20-Poly1305 AEAD (aead seal / aead open).

In un ecosistema tradizionale, risolvere i compiti del dominio CRYPTO comporta trascinare dipendenze esterne in C, gestire puntatori grezzi, conversioni opache di tipo, eccezioni non verificate o pause imprevedibili del garbage collector. In Ridgun, la lib crypto (src/main/ridgun/lib/crypto.rg) affronta il problema con quattro principi non negoziabili:

  1. Dichiara import crypto (o import crypto as alias) all'inizio del file: ogni lib definisce il proprio spazio dei nomi module :: crypto e in modalità --strict le funzioni multi-parola si invocano sempre con spazi naturali.
  2. Concatena le tue pipeline da sinistra a destra con ->: scrivi input passo uno -> passo due -> risultato, usa le maschere (+ _ _ +) per destrutturare le sequenze e ometti _ = nelle istruzioni sequenziali (o scarta il valore con -> ()).
  3. Ricorsione di coda con TCO (Tail-Call Optimization): tutte le scansioni interne di crypto vengono compilate in cicli nativi in spazio di stack $O(1)$.
  4. Memoria deterministica (RMM1 / RMM2): ogni struttura intermedia viene liberata nell'istante esatto in cui termina la sua ultima lettura, senza Garbage Collector.

32.2. Artefatti nativi, importazione e catalogo completo dell'API di crypto

In ridgun-libs, crypto è una lib di prima classe situata in src/main/ridgun/lib/crypto.rg (dominio CRYPTO). Compilando con stable bundle, viene impacchettata in tre artefatti nativi:

ArtefattoPercorsoScopo
Bundle Ridguntarget/bundles/crypto.rglibDistribuzione e importazione diretta con import crypto
File compilatotarget/crypto.rgcArtefatto intermedio verificato da ridgunc
Oggetto condiviso ELF64target/libcrypto.soLibreria dinamica nativa senza dipendenze C né libc

Ogni lib dichiara il proprio spazio dei nomi con module :: crypto: basta scrivere import crypto (o import crypto as alias) nell'intestazione e invocare le sue funzioni con spazi naturali (mia funzione x) e pipeline -> (x mia funzione -> risultato).

32.2.1. Riepilogo delle famiglie funzionali di crypto

Firma / Funzione / TipoCosa fa e quando usarla
sha256 bytes / sha256 hexDigest binario da 32 byte o esadecimale da 64 caratteri FIPS 180-4 SHA-256
hmac sha256 / hmac sha256 hex / ct equalMAC con chiave RFC 2104 e confronto con accumulatore in tempo costante
pbkdf2 hmac sha256 : String -> String -> Int -> Int -> StringDerivazione di chiave RFC 8018 con stati intermedi SHA-256 precalcolati
argon2id hash / argon2id verifyHashing di password PHC RFC 9106 a 5 argomenti e verifica in tempo costante
aead seal / aead openCifratura autenticata RFC 8439 ChaCha20-Poly1305 e decifratura con verifica del tag
sha512 bytes / sha512 hexDigest binario da 64 byte o esadecimale da 128 caratteri FIPS 180-4 SHA-512
ed25519 public key / ed25519 sign / ed25519 verifyChiave pubblica, firma deterministica in tempo costante e verifica canonica RFC 8032 §5.1 Ed25519
minisign sign / minisign verify / minisign trusted commentChiavi pubbliche e firme .minisig compatibili con lo strumento standard minisign

32.2.2. Catalogo esaustivo dei tipi e delle firme in src/main/ridgun/lib/crypto.rg

Di seguito trovi l'inventario completo dei tipi algebrici (type), degli operatori (op ::) e delle firme di funzione (fn ::) definiti in src/main/ridgun/lib/crypto.rg (2 tipi, 0 operatori e 109 funzioni):

Tipi algebrici e record (type) di crypto:

type CT16    = ct0: A, ct1: A, ct2: A, ct3: A, ct4: A, ct5: A, ct6: A, ct7: A, ct8: A, ct9: A, ct10: A, ct11: A, ct12: A, ct13: A, ct14: A, ct15: A
type CPbkCtx = cptbl: CT16, cpipad: String, cpopad: String
type CA2Ctx  = cr_pass: Int, cs_slice: Int, cl_lane: Int, cp_lanes: Int, cq_cols: Int, cseg_len: Int, ct_iters: Int
#Funzione / Firma (fn ::)Tipo di Ritorno
1fn :: norm byte mod IntInt
2fn :: norm byte IntInt
3fn :: byte str norm IntString
4fn :: byte str IntString
5fn :: bytes from list [Int]String
6fn :: bytes to list loop String Int Int[Int]
7fn :: bytes to list String[Int]
8fn :: u32 mod IntInt
9fn :: u32 val IntInt
10fn :: byte band Int IntInt
11fn :: byte bor Int IntInt
12fn :: byte bxor Int IntInt
13fn :: band Int IntInt
14fn :: bor Int IntInt
15fn :: c u32 bxor Int IntInt
16fn :: bxor Int IntInt
17fn :: shl Int IntInt
18fn :: shr Int IntInt
19fn :: rotl32 Int IntInt
20fn :: hextable IntString
21fn :: crypto byte to hex IntString
22fn :: bytes to hex str StringString
23fn :: bytes list to hex [Int]String
24fn :: crypto hex val IntInt
25fn :: hex str to bytes loop String Int Int[Int]
26fn :: hex to byte str StringString
27fn :: c list len [Int]Int
28fn :: c nth int [Int] IntInt
29fn :: c take ints [Int] Int[Int]
30fn :: c drop ints [Int] Int[Int]
31fn :: byte slice String Int IntString
32fn :: sha256 hex StringString
33fn :: sha256 bytes StringString
34fn :: pad zeroes Int[Int]
35fn :: bitxor key pad [Int] Int[Int]
36fn :: raw hmac key bytes String[Int]
37fn :: normalize hmac key String[Int]
38fn :: hmac sha256 String StringString
39fn :: ct diff str String String Int Int IntInt
40fn :: ct equal String StringBool
41fn :: bitxor byte lists [Int] [Int][Int]
42fn :: bitxor byte strings String StringString
43fn :: be32 bytes IntString
44fn :: c get16 CT16 IntA
45fn :: c set16 CT16 Int ACT16
46fn :: c build b16 IntCT16
47fn :: c build b256CT16
48fn :: c get b256 CT16 Int IntString
49fn :: c hex nib IntInt
50fn :: c hex pair CT16 String IntString
51fn :: c hex2raw4 CT16 String IntString
52fn :: c hex2raw32 CT16 StringString
53fn :: c hex8 word String IntU64
54fn :: c bxor acc8 Tensor[U64 * 8] StringTensor[U64 * 8]
55fn :: c word32 to raw4 CT16 U64String
56fn :: c acc8 to raw CT16 Tensor[U64 * 8]String
57fn :: c list to raw CT16 [Int]String
58fn :: pbkdf2 iter loop CPbkCtx String Tensor[U64 * 8] IntTensor[U64 * 8]
59fn :: pbkdf2 block CPbkCtx String Int IntString
60fn :: pbkdf2 blocks loop CPbkCtx String Int Int Int StringString
61fn :: pbkdf2 hmac sha256 String String Int IntString
62fn :: f blamka U64 U64U64
63fn :: blamka g U64 U64 U64 U64U64 U64 U64 U64
64fn :: blamka p16 Tensor[U64 * 16]()
65fn :: ca2 blamka col pair Tensor[U64 * 8 * 16] IntTensor[U64 * 8 * 16]
66fn :: ca2 blamka cols loop Tensor[U64 * 8 * 16] IntTensor[U64 * 8 * 16]
67fn :: compress block Tensor[U64 * 8 * 16] Tensor[U64 * 8 * 16]Tensor[U64 * 8 * 16]
68fn :: ca2 take ints [Int] Int[Int]
69fn :: ca2 drop ints [Int] Int[Int]
70fn :: ca2 pad zeroes Int[Int]
71fn :: ca2 u64 lh Int IntU64
72fn :: ca2 u32 to le4 Int[Int]
73fn :: ca2 bytes to u32 le [Int] IntInt
74fn :: ca2 zero w16Tensor[U64 * 16]
75fn :: ca2 zero blk8Tensor[U64 * 8 * 16]
76fn :: ca2 b2b iv w16Tensor[U64 * 16]
77fn :: ca2 b2b g4 U64 U64 U64 U64 U64 U64U64 U64 U64 U64
78fn :: ca2 b2b sigma Int[Int]
79fn :: ca2 b2b round Tensor[U64 * 16] Tensor[U64 * 16] IntTensor[U64 * 16]
80fn :: ca2 b2b rounds Tensor[U64 * 16] Tensor[U64 * 16] IntTensor[U64 * 16]
81fn :: ca2 b2b compress Tensor[U64 * 16] Tensor[U64 * 16] Int BoolTensor[U64 * 16]
82fn :: ca2 bytes to u64 [Int] IntU64
83fn :: ca2 bytes to w16 [Int] IntTensor[U64 * 16]
84fn :: ca2 u64 to le8 U64[Int]
85fn :: ca2 h8 to bytes Tensor[U64 * 16][Int]
86fn :: ca2 b2b loop Tensor[U64 * 16] [Int] IntTensor[U64 * 16]
87fn :: ca2 blake2b [Int] Int[Int]
88fn :: ca2 b2b long loop [Int] Int Int Int[Int]
89fn :: ca2 blake2b long [Int] Int[Int]
90fn :: ca2 bitxor blk8 Tensor[U64 * 8 * 16] Tensor[U64 * 8 * 16]Tensor[U64 * 8 * 16]
91fn :: ca2 get blk8 Tensor[U64 * 8 * 16] IntU64
92fn :: ca2 bytes to blk8 row Tensor[U64 * 8 * 16] [Int] Int IntTensor[U64 * 8 * 16]
93fn :: ca2 bytes to blk8 loop Tensor[U64 * 8 * 16] [Int] IntTensor[U64 * 8 * 16]
94fn :: ca2 bytes to blk8 [Int]Tensor[U64 * 8 * 16]
95fn :: ca2 blk8 row to bytes Tensor[U64 * 8 * 16] Int Int[Int]
96fn :: ca2 blk8 to bytes loop Tensor[U64 * 8 * 16] Int[Int]
97fn :: ca2 blk8 to bytes Tensor[U64 * 8 * 16][Int]
98fn :: ca2 zero memCT16
99fn :: ca2 nth blk CT16 IntTensor[U64 * 8 * 16]
100fn :: ca2 set blk CT16 Int Tensor[U64 * 8 * 16]CT16
101fn :: ca2 init all lanes loop CT16 [Int] Int Int IntCT16
102fn :: ca2 init all lanes [Int] Int Int IntCT16
103fn :: ca2 make addr blk CA2Ctx IntTensor[U64 * 8 * 16]
104fn :: ca2 calc w size p0 CA2Ctx Int BoolInt
105fn :: ca2 calc w size pn CA2Ctx Int BoolInt
106fn :: ca2 calc w size CA2Ctx Int BoolInt
107fn :: ca2 step addr Bool CA2Ctx Int Tensor[U64 * 8 * 16]Tensor[U64 * 8 * 16]
108fn :: ca2 step jpair Bool Tensor[U64 * 8 * 16] Int CT16 IntU64
109fn :: ca2 ref lane Int Int Int Int IntInt
110fn :: ca2 st pos Int Int IntInt
111fn :: ca2 fin blk Int Tensor[U64 * 8 * 16] CT16 IntTensor[U64 * 8 * 16]
112fn :: ca2 fill segment CT16 CA2Ctx Int Tensor[U64 * 8 * 16]CT16
113fn :: ca2 start j CA2CtxInt
114fn :: ca2 run lanes CT16 CA2Ctx IntCT16
115fn :: ca2 run slices CT16 CA2Ctx IntCT16
116fn :: ca2 run passes CT16 CA2Ctx IntCT16
117fn :: ca2 bitxor final lanes CT16 Int Int Int Tensor[U64 * 8 * 16]Tensor[U64 * 8 * 16]
118fn :: ca2 derive raw tag [Int] [Int] Int Int Int Int[Int]
119fn :: ca2 b64 alphabetString
120fn :: ca2 b64 char IntString
121fn :: ca2 b64 encode [Int]String
122fn :: ca2 b64 char val IntInt
123fn :: ca2 valid b64 chars String Int IntBool
124fn :: ca2 is valid b64 StringBool
125fn :: ca2 b64 decode rem String Int Int Int[Int]
126fn :: ca2 b64 decode loop String Int Int[Int]
127fn :: ca2 b64 decode String[Int]
128fn :: ca2 split str String Int Int Int Int[String]
129fn :: ca2 nth str [String] IntString
130fn :: ca2 str list len [String]Int
131fn :: ca2 is digits String Int IntBool
132fn :: ca2 parse kv String StringInt
133fn :: ca2 verify decoded String [Int] [Int] Int Int Int IntBool
134fn :: ca2 verify kvs String [String] String StringBool
135fn :: ca2 verify parts String [String]Bool
136fn :: ca2 hash checked String [Int] Int Int IntString
137fn :: argon2id hash String String Int Int IntString
138fn :: argon2id hash phc String String Int Int IntString
139fn :: argon2id verify String StringBool
140fn :: le32 at [Int] IntInt
141fn :: u32 to le4 Int[Int]
142fn :: chacha qr Int Int Int IntInt Int Int Int
143fn :: chacha double round Tensor[Int * 16]Tensor[Int * 16]
144fn :: chacha rounds loop Tensor[Int * 16] IntTensor[Int * 16]
145fn :: words16 tensor to bytes Tensor[Int * 16] Tensor[Int * 16] Int[Int]
146fn :: chacha20 init st [Int] [Int] IntTensor[Int * 16]
147fn :: chacha20 block [Int] [Int] Int[Int]
148fn :: chacha20 bitxor loop [Int] [Int] Int [Int][Int]
149fn :: poly1305 clamp r [Int][Int]
150fn :: poly1305 r limbs [Int][Int]
151fn :: poly1305 block limbs [Int][Int]
152fn :: poly1305 step [Int] [Int] [Int][Int]
153fn :: poly1305 blocks loop [Int] [Int] [Int][Int]
154fn :: select reduced limbs Int [Int] [Int][Int]
155fn :: poly1305 finalize [Int] [Int][Int]
156fn :: pad16 zeroes Int[Int]
157fn :: u64 le8 Int[Int]
158fn :: poly1305 aead mac [Int] [Int] [Int][Int]
159fn :: aead seal String String String StringString
160fn :: decrypt verified aead Bool [Int] [Int] [Int]Int String
161fn :: aead open String String String StringInt String
162fn :: sha512 bytes StringString
163fn :: sha512 hex StringString
164fn :: ed25519 public key StringString
165fn :: ed25519 sign String StringString
166fn :: ed25519 verify String String StringBool
167fn :: minisign encode pubkey String StringString
168fn :: minisign decode pubkey StringString String
169fn :: minisign sign String String String String StringString
170fn :: minisign trusted comment StringString
171fn :: minisign verify String String StringBool

32.3. Esplorare crypto a fondo: Esempi passo dopo passo di ogni funzionalità

Non basta consultare la tabella delle firme: esploreremo ciascuna delle funzionalità della lib crypto tramite esempi progressivi ed eseguibili in Ridgun. Inizieremo con due ricette tabulari di uso quotidiano e poi percorreremo nel dettaglio tutti gli scenari funzionali, le trasformazioni e i casi limite verificati in src/test/ridgun/test crypto.rg.

32.3.1. Esempio 1: Flusso principale in stile tabulare con crypto

Osserva come in Ridgun i segni = e i passaggi di trasformazione con crypto risultino allineati come una tabella pulita, senza variabili mutabili superflue né effetti collaterali nascosti:

import crypto

fn :: cifrar mensaje sesion String String => Int String
      cifrar mensaje sesion secreto texto =
        secreto sha256 raw -> key32
        "0123456789ab" -> nonce12
        "canal-autenticado" -> aad
        aead seal key32 nonce12 aad texto -> sellado
        aead open key32 nonce12 aad sellado

32.3.2. Esempio 2: Composizione sicura e gestione dei casi limite

Ogni funzione della lib crypto è progettata per comporsi con le guardie booleane | e con il resto delle 34 lib di ridgun-libs:

import crypto

fn :: verificar firma webhook String String String => Bool
      verificar firma webhook clave cuerpo firma_hex =
        hmac sha256 clave cuerpo -> esperada
        ct equal esperada firma_hex

32.3.3. Esempio 3: Manipolazione di byte grezzi 0..255 (byte str, bytes from list, bytes to list) e operazioni bit a bit a 32 bit (band, bor, bxor, shl, shr)

In questo esempio pratico esploriamo in modo esaustivo come si comporta crypto in questo blocco funzionale, inclusi input standard, combinazioni di parametri e valori di frontiera:

fn :: test rgf019 bytes and bitops => ()
      test rgf019 bytes and bitops =
        -- 1. Raw byte str lengths, wraparound normalization, and full 0..255 round-trip
        assert int equals (str len (byte str 0)) 1 "len byte_str 0"
        assert int equals (str len (byte str 127)) 1 "len byte_str 127"
        assert int equals (str len (byte str 128)) 1 "len byte_str 128"
        assert int equals (str len (byte str 255)) 1 "len byte_str 255"
        assert str eq (byte str (0 - 1)) (byte str 255) "byte_str -1 wraps to 255"
        assert str eq (byte str 256) (byte str 0) "byte_str 256 wraps to 0"
        b5   = bytes from list [0, 1, 127, 128, 255]
        assert int equals (str len b5) 5 "len b5 == 5"
        rt   = bytes to list b5
        assert int equals (c nth int rt 0) 0 "rt 0"
        assert int equals (c nth int rt 1) 1 "rt 1"
        assert int equals (c nth int rt 2) 127 "rt 2"
        assert int equals (c nth int rt 3) 128 "rt 3"
        assert int equals (c nth int rt 4) 255 "rt 4"
        b256 = bytes from list (range bytes 0 255)
        assert int equals (str len b256) 256 "len b256 == 256"
        r256 = bytes to list b256
        assert int equals (c list len r256) 256 "r256 list len == 256"
        assert int equals (c nth int r256 0) 0 "r256[0] == 0"
        assert int equals (c nth int r256 128) 128 "r256[128] == 128"
        assert int equals (c nth int r256 255) 255 "r256[255] == 255"

        -- 2. RGF-019 32-bit unsigned bitwise operations and boundary shifts
        assert int equals (band 4294967295 255) 255 "band"
        assert int equals (band 305419896 0) 0 "band with 0"
        assert int equals (bor 240 15) 255 "bor"
        assert int equals (bor 305419896 0) 305419896 "bor with 0"
        assert int equals (bxor 305419896 4294967295) 3989547399 "bxor"
        assert int equals (bxor 305419896 305419896) 0 "bxor self == 0"
        assert int equals (shl 1 0) 1 "shl 0"
        assert int equals (shl 1 (0 - 1)) 0 "shl negative shift"
        assert int equals (shl 1 31) 2147483648 "shl 31"
        assert int equals (shl 1 32) 0 "shl 32"
        assert int equals (shr 2147483648 0) 2147483648 "shr 0"
        assert int equals (shr 2147483648 (0 - 1)) 0 "shr negative shift"
        assert int equals (shr 2147483648 31) 1 "shr 31"
        assert int equals (shr 2147483648 32) 0 "shr 32"
        ()

32.3.4. Esempio 4: Vettori ufficiali SHA-256 (FIPS 180-4), HMAC-SHA-256 (RFC 4231), confronto in tempo costante (ct equal) e PBKDF2-HMAC-SHA-256 (RFC 6070)

In questo esempio pratico esploriamo in modo esaustivo come si comporta crypto in questo blocco funzionale, inclusi input standard, combinazioni di parametri e valori di frontiera:

fn :: test sha256 hmac and pbkdf2 => ()
      test sha256 hmac and pbkdf2 =
        -- 1. SHA-256 (empty string "" and "abc" -> FIPS 180-4 vectors)
        h_emp = sha256 hex ""
        assert str eq h_emp "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" "sha256_hex empty"
        h_hex = sha256 hex "abc"
        assert str eq h_hex "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad" "sha256_hex abc"
        h_raw = sha256 bytes "abc"
        assert eq (str len h_raw) 32
        assert str eq (bytes to hex str h_raw) h_hex "sha256_bytes matches sha256_hex"

        -- 2. HMAC-SHA-256 (RFC 4231 Test Case 2 and >64-byte key reduction)
        mac   = hmac sha256 "Jefe" "what do ya want for nothing?"
        assert eq (str len mac) 32
        assert str eq (bytes to hex str mac) "5bdcc146bf60754e6a042426089575c75a003f089d2739839dec58b964ec3843" "RFC 4231 Test Case 2"
        long_k = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef_extra_key_bytes"
        mac_lk = hmac sha256 long_k "message"
        assert eq (str len mac_lk) 32
        assert true (ct equal mac_lk (hmac sha256 (sha256 bytes long_k) "message")) "HMAC >64B key hashed to 32B"

        -- 3. Constant-time comparison (`ct equal`)
        assert true (ct equal "" "") "ct_equal empty strings"
        assert true (ct equal mac mac) "ct_equal identical"
        assert false (ct equal mac (sha256 bytes "abc")) "ct_equal different 32B"
        assert false (ct equal "short" "longer") "ct_equal different lengths"

        -- 4. PBKDF2-HMAC-SHA-256 (RFC 6070 c=1, c=2, multi-block dk_len=64, sub-block dk_len=16, invalid args)
        dk1   = pbkdf2 hmac sha256 "password" "salt" 1 32
        assert eq (str len dk1) 32
        assert str eq (bytes to hex str dk1) "120fb6cffcf8b32c43e7225256c4f837a86548c92ccc35480805987cb70be17b" "RFC 6070 PBKDF2 c=1"
        dk2   = pbkdf2 hmac sha256 "password" "salt" 2 32
        assert str eq (bytes to hex str dk2) "ae4d0c95af6b46d32d0adff928f06dd02a303f8ef3c251dfd6e2d85a95474c43" "RFC 6070 PBKDF2 c=2"
        dk16  = pbkdf2 hmac sha256 "password" "salt" 1 16
        assert eq (str len dk16) 16
        assert str eq (bytes to hex str dk16) "120fb6cffcf8b32c43e7225256c4f837" "PBKDF2 16-byte prefix"
        dk64  = pbkdf2 hmac sha256 "password" "salt" 1 64
        assert eq (str len dk64) 64
        assert str eq (byte slice dk64 0 32) dk1 "PBKDF2 64-byte block 1 matches dk1"
        assert str eq (pbkdf2 hmac sha256 "password" "salt" 0 32) "" "PBKDF2 iterations=0 rejected"
        assert str eq (pbkdf2 hmac sha256 "password" "salt" 1 0) "" "PBKDF2 dk_len=0 rejected"
        ()

32.3.5. Esempio 5: Hashing di password Argon2id a 5 argomenti (argon2id hash, argon2id verify) e resistenza in memoria (RFC 9106)

In questo esempio pratico esploriamo in modo esaustivo come si comporta crypto in questo blocco funzionale, inclusi input standard, combinazioni di parametri e valori di frontiera:

fn :: test argon2id five arg and perf => ()
      test argon2id five arg and perf =
        phc = argon2id hash "correct horse battery staple" "0123456789abcdef" 64 2 1
        assert true (str starts with phc "$argon2id$v=19$m=64,t=2,p=1$") "PHC header format"
        assert true (argon2id verify "correct horse battery staple" phc) "argon2id_verify matching (pwd, phc)"
        assert false (argon2id verify "wrong password" phc) "argon2id_verify wrong password"
        assert false (argon2id verify "correct horse battery staple" "$argon2id$v=19$m=64,t=2,p=1$bad$bad") "argon2id_verify bad PHC"

        -- Negative parameter guards for 5-arg argon2id hash
        assert str eq (argon2id hash "password" "short" 64 2 1) "" "salt < 8 bytes rejected"
        assert str eq (argon2id hash "password" "0123456789abcdef" 4 2 1) "" "m_cost < 8*p rejected"
        assert str eq (argon2id hash "password" "0123456789abcdef" 64 0 1) "" "t_cost < 1 rejected"
        assert str eq (argon2id hash "password" "0123456789abcdef" 64 2 0) "" "p_cost < 1 rejected"

        -- RG-050 / RG-051: Verify 100% in-process pure-Ridgun Argon2id and PBKDF2-HMAC-SHA256
        -- (OpenSSL 3.5 / RFC 9106 vectors + 4096-iteration PBKDF2 vector)
        phc_s       = argon2id hash "correct horse" "saltsaltsaltsalt" 64 2 1
        argon_exact = phc_s == "$argon2id$v=19$m=64,t=2,p=1$c2FsdHNhbHRzYWx0c2FsdA$5jdMIzWoFbzSOl6k3wai9uU3f8LD+HmGXQgo1uhHQPI"
        ok_s        = argon2id verify "correct horse" phc_s
        bad_s       = argon2id verify "wrong horse" phc_s
        bad_empty   = argon2id verify "correct horse" "$argon2id$v=19$m=64,t=2,p=1$c2FsdHNhbHRzYWx0c2FsdA$"
        phc_ok      = str starts with phc_s "$argon2id$v=19$m=64,t=2,p=1$"
        pb4k        = bytes to hex str (pbkdf2 hmac sha256 "correct horse battery staple" "0123456789abcdef" 4096 32)
        assert true ok_s "Solstice RGF-015 argon_ok"
        assert false bad_s "Solstice RGF-015 argon_bad"
        assert false bad_empty "RG-051 reject PHC with empty tag"
        assert true phc_ok "Solstice RGF-015 phc prefix"
        assert true argon_exact "Solstice RGF-015 exact OpenSSL 3.5 / libsodium PHC"
        assert str eq pb4k "b87f4b27148d3b05ad7985e3df997f0d9afa6e768e6384555bab71ba10f0c1a5" "Solstice RGF-015 PBKDF2 4096"
        ()

32.3.6. Esempio 6: Cifrario di flusso ChaCha20, autenticatore Poly1305 e sigillatura/apertura autenticata AEAD ChaCha20-Poly1305 (RFC 8439)

In questo esempio pratico esploriamo in modo esaustivo come si comporta crypto in questo blocco funzionale, inclusi input standard, combinazioni di parametri e valori di frontiera:

fn :: test rfc8439 chacha20 poly1305 aead => ()
      test rfc8439 chacha20 poly1305 aead =
        key   = bytes from list (range bytes 128 159)
        nonce = bytes from list [7, 0, 0, 0, 64, 65, 66, 67, 68, 69, 70, 71]
        aad   = bytes from list [80, 81, 82, 83, 192, 193, 194, 195, 196, 197, 198, 199]
        pt    = "Ladies and Gentlemen of the class of '99: If I could offer you only one tip for the future, sunscreen would be it."

        sealed = aead seal key nonce aad pt
        assert eq (str len sealed) ((str len pt) + 16)

        -- Verify first 16 ciphertext bytes and 16-byte Poly1305 tag match RFC 8439 §2.8.2 bit-for-bit
        ct_prefix_hex = bytes to hex str (byte slice sealed 0 16)
        tag_hex       = bytes to hex str (byte slice sealed 114 130)
        assert str eq ct_prefix_hex "d31a8d34648e60db7b86afbc53ef7ec2" "RFC 8439 §2.8.2 ciphertext prefix"
        assert str eq tag_hex "1ae10b594f09e26a7e902ecbd0600691" "RFC 8439 §2.8.2 Poly1305 tag"

        -- Verify aead open succeeds with (0, pt)
        res_ok   = aead open key nonce aad sealed
        assert eq (aead rc res_ok) 0
        assert str eq (aead pt res_ok) pt "aead_open decrypted plaintext"

        -- Verify empty plaintext and empty AAD round-trip
        sealed_empty = aead seal key nonce "" ""
        assert eq (str len sealed_empty) 16
        res_empty    = aead open key nonce "" sealed_empty
        assert eq (aead rc res_empty) 0
        assert str eq (aead pt res_empty) "" "aead_open empty plaintext"

        -- Verify tamper detection (-12 AUTH_FAILED) on modified AAD, modified nonce, modified ciphertext, or modified tag
        res_aad  = aead open key nonce "tampered-aad" sealed
        assert eq (aead rc res_aad) (0 - 12)
        assert str eq (aead pt res_aad) "" "tampered AAD returns empty plaintext"

        bad_nonce = bytes from list [8, 0, 0, 0, 64, 65, 66, 67, 68, 69, 70, 71]
        res_nonce = aead open key bad_nonce aad sealed
        assert eq (aead rc res_nonce) (0 - 12)
        assert str eq (aead pt res_nonce) "" "tampered nonce returns empty plaintext"

        bad_ct   = (byte str 0) + (byte slice sealed 1 130)
        res_ct   = aead open key nonce aad bad_ct
        assert eq (aead rc res_ct) (0 - 12)
        assert str eq (aead pt res_ct) "" "tampered ciphertext byte returns empty plaintext"

        bad_seal = (byte slice sealed 0 129) + (byte str 0)
        res_tag  = aead open key nonce aad bad_seal
        assert eq (aead rc res_tag) (0 - 12)
        assert str eq (aead pt res_tag) "" "tampered tag returns empty plaintext"

        -- Verify invalid key/nonce/ciphertext length returns (-7, "") or ""
        assert str eq (aead seal "short-key" nonce aad pt) "" "aead_seal invalid key length"
        assert str eq (aead seal key "short-nonce" aad pt) "" "aead_seal invalid nonce length"
        res_inv  = aead open "short-key" nonce aad sealed
        assert eq (aead rc res_inv) (0 - 7)
        res_ninv = aead open key "short-nonce" aad sealed
        assert eq (aead rc res_ninv) (0 - 7)
        res_tinv = aead open key nonce aad "short-tag"
        assert eq (aead rc res_tinv) (0 - 7)
        ()

32.3.7. Esempio 7: Digest FIPS 180-4 SHA-512 (sha512 bytes, sha512 hex), firme RFC 8032 §5.1 Ed25519 (ed25519 public key, ed25519 sign, ed25519 verify) e formato Minisign (minisign sign, minisign verify)

In questo esempio pratico verifichiamo i vettori ufficiali FIPS 180-4 SHA-512, la generazione di chiavi e firme Ed25519 in tempo costante (con rifiuto rigoroso di malleabilità $S \ge L$ e punti fuori curva) e la firma/verifica dei manifesti di rilascio in formato .minisig:

import crypto
import test

fn :: verificar sha512 ed25519 y minisign => ()
      verificar sha512 ed25519 y minisign =
        -- 1. FIPS 180-4 SHA-512 ("abc" -> 128 caracteres hexadecimales / 64 bytes crudos)
        "abc" sha512 hex -> h_abc
        assert str eq h_abc "ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f" "SHA-512 abc"

        -- 2. RFC 8032 §7.1 Ed25519 (derivación de clave pública, firma y verificación canónica)
        "9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60" hex to byte str -> sk
        sk ed25519 public key -> pk
        ed25519 sign sk "" -> sig
        assert true (ed25519 verify pk "" sig) "RFC 8032 Test 1"
        assert false (ed25519 verify pk "manipulado" sig) "Mensaje alterado rechazado"

        -- 3. Formato Minisign (clave pública RW... y archivo .minisig con comentario firmado)
        minisign encode pubkey "01234567" pk -> pub_txt
        "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855  ridgun-linux-x86_64.tar.gz\n" -> sums
        minisign sign sk "01234567" sums "ridgun release key" "timestamp:1761667200\tfile:SHA256SUMS" -> msig
        assert true (minisign verify pub_txt sums msig) "Minisign válido"
        assert str eq (minisign trusted comment msig) "timestamp:1761667200\tfile:SHA256SUMS" "Comentario autenticado" -> ()

32.4. Sotto il cofano: come è costruita crypto in Ridgun puro (src/main/ridgun/lib/crypto.rg)

Uno dei maggiori vantaggi di ridgun-libs è che non ci sono scatole nere in C. Puoi aprire src/main/ridgun/lib/crypto.rg e leggere esattamente come è implementata ogni operazione usando pattern matching, alberi di guardie | / |. e ricorsione di coda (TCO). Vediamo i pezzi chiave della sua implementazione reale:

32.4.1. Implementazione interna: RGF-019 Raw Byte Manipulation (byte str, bytes from list, bytes to list)

Nota come crypto.rg strutturi prima le firme fn :: ... => ... e risolva ogni caso tramite equazioni allineate e chiamate di coda:

module :: crypto

fn :: norm byte mod Int => Int
      norm byte mod m   =
        | m < 0     = m + 256
        | otherwise = m

fn :: norm byte Int => Int
      norm byte b   =  norm byte mod (b % 256)

fn :: byte str norm Int => String
      byte str norm ub  =
        | ub >= 1 and ub <= 127 = char from code ub
        | ub == 0 = str slice (sha256 raw "00679") 0 1
        | otherwise =
          idx = (ub - 128) * 5
          lut = "0056400101002080010000179000010024100109003430040100361007870009100353001870030400471000870020200071002360012501171004140236900275001810016001398004440080400017005250027000314001400024700004006350015700183005940027600207007720028400601003400038000839006850084900617003060113500277000320006900483000580019200151008700008500193000100057000070002880002600326006460038800018009190028900103006000043900073011700038900628007210077300074004740006000041001410019900066003680001900383001730005700558000670024500065002030031900000008250001100128001360034900030003750039600228002380008400618005900031800062001770087900076002570009600034002200001600161"
          str slice (sha256 raw (str slice lut idx (idx + 5))) 0 1

fn :: byte str Int => String
      byte str n   =
        byte str norm (norm byte n)

fn :: c sb append3 String String => String String String String
      c sb append3 n2 c3 =
        | (str len n2) < 65536 = ("", "", n2, c3)
        | otherwise            = ("", "", "", c3 + n2)

fn :: c sb append2 String String String => String String String String
      c sb append2 n1 c2 c3 =
        | (str len n1) < 8192 = ("", n1, c2, c3)
        | otherwise           = c sb append3 (c2 + n1) c3

fn :: c sb append1 String String String String => String String String String
      c sb append1 n0 c1 c2 c3 =
        | (str len n0) < 512 = (n0, c1, c2, c3)
        | otherwise          = c sb append2 (c1 + n0) c2 c3

fn :: c sb append String String String String String => String String String String
      c sb append chunk c0 c1 c2 c3 =
        c sb append1 (c0 + chunk) c1 c2 c3

fn :: c sb finish String String String String => String
      c sb finish c0 c1 c2 c3 =  ((c3 + c2) + c1) + c0

fn :: c lb append2 [Int] [Int] Int => [Int] [Int] [Int] Int
      c lb append2 n1 l2 cnt =
        | (cnt % 1024) == 0 = ([], [], l2 :: n1, cnt)
        | otherwise         = ([], n1, l2, cnt)

fn :: c lb append1 [Int] [Int] [Int] Int => [Int] [Int] [Int] Int
      c lb append1 n0 l1 l2 cnt =
        | (cnt % 32) == 0 = c lb append2 (l1 :: n0) l2 cnt
        | otherwise       = (n0, l1, l2, cnt)

fn :: c lb append Int [Int] [Int] [Int] Int => [Int] [Int] [Int] Int
      c lb append item l0 l1 l2 cnt =
        c lb append1 (l0 :: [item]) l1 l2 (cnt + 1)

fn :: c lb finish [Int] [Int] [Int] => [Int]
      c lb finish l0 l1 l2 =  l2 :: (l1 :: l0)

fn :: bytes from list loop CT16 [Int] String String String String => String
      bytes from list loop _   []       c0 c1 c2 c3 =  c sb finish c0 c1 c2 c3
      bytes from list loop tbl (b:rest) c0 c1 c2 c3 =
        ub = norm byte b
        ch = c get b256 tbl (ub / 16) (ub % 16)
        n0, n1, n2, n3 = c sb append ch c0 c1 c2 c3
        bytes from list loop tbl rest n0 n1 n2 n3

fn :: bytes from list [Int]    => String
      bytes from list []       =  ""
      bytes from list bs       =  bytes from list loop (c build b256) bs "" "" "" ""

fn :: bytes to list loop String Int Int [Int] [Int] [Int] Int => [Int]
      bytes to list loop s idx slen l0 l1 l2 cnt =
        | idx >= slen = c lb finish l0 l1 l2
        | otherwise   =
          n0, n1, n2, ncnt = c lb append (norm byte (str char at s idx)) l0 l1 l2 cnt
          bytes to list loop s (idx + 1) slen n0 n1 n2 ncnt

fn :: bytes to list String => [Int]
      bytes to list s      =
        bytes to list loop s 0 (str len s) [] [] [] 0

32.4.2. Implementazione interna: Hex & List Utilities

Nota come crypto.rg strutturi prima le firme fn :: ... => ... e risolva ogni caso tramite equazioni allineate e chiamate di coda:

fn :: hextable Int => String
      hextable _   =  "0123456789abcdef"

fn :: crypto byte to hex Int => String
      crypto byte to hex b   =
        ub = norm byte b
        hi = (ub / 16) % 16
        lo = ub % 16
        (str slice (hextable 0) hi (hi + 1)) + (str slice (hextable 0) lo (lo + 1))

fn :: bytes list to hex loop [Int] String String String String => String
      bytes list to hex loop []       c0 c1 c2 c3 = c sb finish c0 c1 c2 c3
      bytes list to hex loop (b:rest) c0 c1 c2 c3 =
        n0, n1, n2, n3 = c sb append (crypto byte to hex b) c0 c1 c2 c3
        bytes list to hex loop rest n0 n1 n2 n3

fn :: bytes list to hex [Int] => String
      bytes list to hex bs    =  bytes list to hex loop bs "" "" "" ""

fn :: bytes to hex str String => String
      bytes to hex str raw    =
        bytes list to hex (bytes to list raw)

fn :: crypto hex val Int => Int
      crypto hex val c   =
        | c >= 48 and c <= 57  = c - 48
        | c >= 97 and c <= 102 = (c - 97) + 10
        | c >= 65 and c <= 70  = (c - 65) + 10
        | otherwise            = 0

fn :: hex str to bytes loop String Int Int [Int] [Int] [Int] Int => [Int]
      hex str to bytes loop h idx hlen l0 l1 l2 cnt =
        | (idx + 1) >= hlen = c lb finish l0 l1 l2
        | otherwise         =
          hi = crypto hex val (str char at h idx)
          lo = crypto hex val (str char at h (idx + 1))
          n0, n1, n2, ncnt = c lb append ((hi * 16) + lo) l0 l1 l2 cnt
          hex str to bytes loop h (idx + 2) hlen n0 n1 n2 ncnt

fn :: hex to byte str String => String
      hex to byte str h      =
        bytes from list (hex str to bytes loop h 0 (str len h) [] [] [] 0)

fn :: c list len loop [Int] Int => Int
      c list len loop []       acc = acc
      c list len loop (_:rest) acc = c list len loop rest (acc + 1)

fn :: c list len [Int] => Int
      c list len xs    =  c list len loop xs 0

fn :: c nth int [Int]    Int => Int
      c nth int []       _   =  0
      c nth int (x:rest) idx =
        | idx <= 0  = x
        | otherwise = c nth int rest (idx - 1)

fn :: c take ints loop [Int] Int [Int] => [Int]
      c take ints loop []       _ acc = acc
      c take ints loop (x:rest) n acc =
        | n <= 0    = acc
        | otherwise = c take ints loop rest (n - 1) (acc :: [x])

fn :: c take ints [Int] Int => [Int]
      c take ints xs    n   =  c take ints loop xs n []

fn :: c drop ints [Int]    Int => [Int]
      c drop ints []       _   =  []
      c drop ints (x:rest) n   =
        | n <= 0    = [x] :: rest
        | otherwise = c drop ints rest (n - 1)

fn :: c byte slice loop CT16 String Int Int String String String String => String
      c byte slice loop tbl s idx end_idx c0 c1 c2 c3 =
        | idx >= end_idx = c sb finish c0 c1 c2 c3
        | otherwise      =
          ub = norm byte (str char at s idx)
          n0, n1, n2, n3 = c sb append (c get b256 tbl (ub / 16) (ub % 16)) c0 c1 c2 c3
          c byte slice loop tbl s (idx + 1) end_idx n0 n1 n2 n3

fn :: byte slice String Int Int => String
      byte slice s start end_idx =
        slen = str len s
        st   =
          | start < 0 = 0
          | start > slen = slen
          | otherwise = start
        en   =
          | end_idx < st = st
          | end_idx > slen = slen
          | otherwise = end_idx
        c byte slice loop (c build b256) s st en "" "" "" ""

32.4.3. Implementazione interna: RGF-015 Pure-Ridgun RFC 9106 Argon2id (argon2id hash, argon2id verify)

Nota come crypto.rg strutturi prima le firme fn :: ... => ... e risolva ogni caso tramite equazioni allineate e chiamate di coda:

fn :: ca2 take ints [Int]    Int => [Int]
      ca2 take ints []       _   =  []
      ca2 take ints (x:rest) n   =
        | n <= 0    = []
        | otherwise = [x] :: (ca2 take ints rest (n - 1))

fn :: ca2 drop ints [Int]    Int => [Int]
      ca2 drop ints []       _   =  []
      ca2 drop ints (x:rest) n   =
        | n <= 0    = [x] :: rest
        | otherwise = ca2 drop ints rest (n - 1)

fn :: ca2 pad zeroes Int => [Int]
      ca2 pad zeroes n   =
        | n <= 0    = []
        | otherwise = [0] :: (ca2 pad zeroes (n - 1))

fn :: ca2 u64 lh Int Int => U64
      ca2 u64 lh lo  hi  =
        ((hi and 0xFFFFFFFF) shl 32) or (lo and 0xFFFFFFFF)

fn :: ca2 u32 to le4 Int => [Int]
      ca2 u32 to le4 w   =
        uw = u32 val w
        [uw % 256, (uw / 256) % 256, (uw / 65536) % 256, (uw / 16777216) % 256]

fn :: ca2 bytes to u32 le [Int] Int => Int
      ca2 bytes to u32 le bs off =
        (c nth int bs off) + ((c nth int bs (off + 1)) * 256) + ((c nth int bs (off + 2)) * 65536) + ((c nth int bs (off + 3)) * 16777216)

fn :: ca2 zero w16 => Tensor[U64 * 16]
      ca2 zero w16 =
        Tensor[U64 * 16]

fn :: ca2 zero blk8 => Tensor[U64 * 8 * 16]
      ca2 zero blk8 =
        Tensor[U64 * 8 * 16]

fn :: ca2 b2b iv w16 => Tensor[U64 * 16]
      ca2 b2b iv w16 =
        iv = Tensor[U64 * 16]
        iv_(0) = ca2 u64 lh 4089235720 1779033703
        iv_(1) = ca2 u64 lh 2227873595 3144134277
        iv_(2) = ca2 u64 lh 4271175723 1013904242
        iv_(3) = ca2 u64 lh 1595750129 2773480762
        iv_(4) = ca2 u64 lh 2917565137 1359893119
        iv_(5) = ca2 u64 lh 725511199  2600822924
        iv_(6) = ca2 u64 lh 4215389547 528734635
        iv_(7) = ca2 u64 lh 327033209  1541459225
        iv

32.4.4. Implementazione interna: RFC 8439 Poly1305 One-Time Authenticator (5 x 26-bit limbs)

Nota come crypto.rg strutturi prima le firme fn :: ... => ... e risolva ogni caso tramite equazioni allineate e chiamate di coda:

fn :: poly1305 clamp r [Int] => [Int]
      poly1305 clamp r otk   =
        [c nth int otk 0, c nth int otk 1, c nth int otk 2, byte band (c nth int otk 3) 15, byte band (c nth int otk 4) 252, c nth int otk 5, c nth int otk 6, byte band (c nth int otk 7) 15, byte band (c nth int otk 8) 252, c nth int otk 9, c nth int otk 10, byte band (c nth int otk 11) 15, byte band (c nth int otk 12) 252, c nth int otk 13, c nth int otk 14, byte band (c nth int otk 15) 15]

fn :: poly1305 r limbs [Int] => [Int]
      poly1305 r limbs r_bs  =
        t0 = le32 at r_bs 0
        t1 = le32 at r_bs 4
        t2 = le32 at r_bs 8
        t3 = le32 at r_bs 12
        r0 = t0 % 67108864
        r1 = ((t0 / 67108864) + ((t1 % 1048576) * 64)) % 67108864
        r2 = ((t1 / 1048576) + ((t2 % 16384) * 4096)) % 67108864
        r3 = ((t2 / 16384) + ((t3 % 256) * 262144)) % 67108864
        r4 = t3 / 256
        [r0, r1, r2, r3, r4]

fn :: poly1305 block limbs [Int] => [Int]
      poly1305 block limbs blk   =
        t0 = le32 at blk 0
        t1 = le32 at blk 4
        t2 = le32 at blk 8
        t3 = le32 at blk 12
        b0 = t0 % 67108864
        b1 = ((t0 / 67108864) + ((t1 % 1048576) * 64)) % 67108864
        b2 = ((t1 / 1048576) + ((t2 % 16384) * 4096)) % 67108864
        b3 = ((t2 / 16384) + ((t3 % 256) * 262144)) % 67108864
        b4 = (t3 / 256) + 16777216
        [b0, b1, b2, b3, b4]

fn :: poly1305 step [Int] [Int] [Int] => [Int]
      poly1305 step h     r     blk   =
        b  = poly1305 block limbs blk
        h0 = (c nth int h 0) + (c nth int b 0)
        h1 = (c nth int h 1) + (c nth int b 1)
        h2 = (c nth int h 2) + (c nth int b 2)
        h3 = (c nth int h 3) + (c nth int b 3)
        h4 = (c nth int h 4) + (c nth int b 4)
        r0 = c nth int r 0
        r1 = c nth int r 1
        r2 = c nth int r 2
        r3 = c nth int r 3
        r4 = c nth int r 4
        s1 = r1 * 5
        s2 = r2 * 5
        s3 = r3 * 5
        s4 = r4 * 5
        d0 = (h0 * r0) + (h1 * s4) + (h2 * s3) + (h3 * s2) + (h4 * s1)
        d1 = (h0 * r1) + (h1 * r0) + (h2 * s4) + (h3 * s3) + (h4 * s2)
        d2 = (h0 * r2) + (h1 * r1) + (h2 * r0) + (h3 * s4) + (h4 * s3)
        d3 = (h0 * r3) + (h1 * r2) + (h2 * r1) + (h3 * r0) + (h4 * s4)
        d4 = (h0 * r4) + (h1 * r3) + (h2 * r2) + (h3 * r1) + (h4 * r0)
        c0 = d0 / 67108864
        n0 = d0 % 67108864
        e1 = d1 + c0
        c1 = e1 / 67108864
        n1 = e1 % 67108864
        e2 = d2 + c1
        c2 = e2 / 67108864
        n2 = e2 % 67108864
        e3 = d3 + c2
        c3 = e3 / 67108864
        n3 = e3 % 67108864
        e4 = d4 + c3
        c4 = e4 / 67108864
        n4 = e4 % 67108864
        f0 = n0 + (c4 * 5)
        cf = f0 / 67108864
        [f0 % 67108864, n1 + cf, n2, n3, n4]

fn :: poly1305 blocks loop [Int] [Int] [Int] => [Int]
      poly1305 blocks loop h _ [] = h
      poly1305 blocks loop h r msg =
        blk  = c take ints msg 16
        rest = c drop ints msg 16
        poly1305 blocks loop (poly1305 step h r blk) r rest

fn :: select reduced limbs Int [Int] [Int] => [Int]
      select reduced limbs g4 g_set h_set =
        | g4 >= 0   = g_set
        | otherwise = h_set

fn :: poly1305 finalize [Int] [Int] => [Int]
      poly1305 finalize h     otk   =
        c1   = (c nth int h 1) / 67108864
        h1   = (c nth int h 1) % 67108864
        t2   = (c nth int h 2) + c1
        c2   = t2 / 67108864
        h2   = t2 % 67108864
        t3   = (c nth int h 3) + c2
        c3   = t3 / 67108864
        h3   = t3 % 67108864
        t4   = (c nth int h 4) + c3
        c4   = t4 / 67108864
        h4   = t4 % 67108864
        t0   = (c nth int h 0) + (c4 * 5)
        c0   = t0 / 67108864
        h0   = t0 % 67108864
        h1_f = h1 + c0

        g0_t = h0 + 5
        gc0  = g0_t / 67108864
        g0   = g0_t % 67108864
        g1_t = h1_f + gc0
        gc1  = g1_t / 67108864
        g1   = g1_t % 67108864
        g2_t = h2 + gc1
        gc2  = g2_t / 67108864
        g2   = g2_t % 67108864
        g3_t = h3 + gc2
        gc3  = g3_t / 67108864
        g3   = g3_t % 67108864
        g4   = (h4 + gc3) - 67108864

        fin  = select reduced limbs g4 [g0, g1, g2, g3, g4] [h0, h1_f, h2, h3, h4]
        f0   = c nth int fin 0
        f1   = c nth int fin 1
        f2   = c nth int fin 2
        f3   = c nth int fin 3
        f4   = c nth int fin 4

        w0   = (f0 + ((f1 % 64) * 67108864)) % 4294967296
        w1   = ((f1 / 64) + ((f2 % 4096) * 1048576)) % 4294967296
        w2   = ((f2 / 4096) + ((f3 % 262144) * 16384)) % 4294967296
        w3   = ((f3 / 262144) + (f4 * 256)) % 4294967296

        s0   = le32 at otk 16
        s1   = le32 at otk 20
        s2   = le32 at otk 24
        s3   = le32 at otk 28

        sum0 = w0 + s0
        sum1 = w1 + s1 + (sum0 / 4294967296)
        sum2 = w2 + s2 + (sum1 / 4294967296)
        sum3 = w3 + s3 + (sum2 / 4294967296)
        (u32 to le4 (sum0 % 4294967296)) :: (u32 to le4 (sum1 % 4294967296)) :: (u32 to le4 (sum2 % 4294967296)) :: (u32 to le4 (sum3 % 4294967296))

fn :: pad16 zeroes Int => [Int]
      pad16 zeroes len =
        | (len % 16) == 0 = []
        | otherwise       = pad zeroes (16 - (len % 16))

fn :: u64 le8 Int => [Int]
      u64 le8 n   =
        (u32 to le4 (n % 4294967296)) :: (u32 to le4 (n / 4294967296))

fn :: poly1305 aead mac [Int] [Int] [Int] => [Int]
      poly1305 aead mac otk aad_b ct_b =
        aad_b c list len -> alen
        ct_b c list len -> clen
        aad_b :: (pad16 zeroes alen) :: ct_b :: (pad16 zeroes clen) :: (u64 le8 alen) :: (u64 le8 clen) -> msg
        otk poly1305 clamp r -> poly1305 r limbs -> r
        poly1305 blocks loop [0, 0, 0, 0, 0] r msg -> h_fin
        poly1305 finalize h_fin otk

fn :: aead seal String String String String => String
      aead seal key nonce aad plaintext =
        | (str len key) != 32 or (str len nonce) != 12 = ""
        | otherwise                                    =
          key_bs   = bytes to list key
          nonce_bs = bytes to list nonce
          aad_bs   = bytes to list aad
          pt_bs    = bytes to list plaintext
          otk      = c take ints (chacha20 block key_bs nonce_bs 0) 32
          ct_bs    = chacha20 bitxor loop key_bs nonce_bs 1 pt_bs [] [] [] 0
          tag_bs   = poly1305 aead mac otk aad_bs ct_bs
          bytes from list (ct_bs :: tag_bs)

fn :: decrypt verified aead Bool [Int] [Int] [Int] => Int String
      decrypt verified aead False _ _ _ = (0 - 12, "")
      decrypt verified aead True key_bs nonce_bs ct_bs =
        chacha20 bitxor loop key_bs nonce_bs 1 ct_bs [] [] [] 0 -> pt_bs
        (0, bytes from list pt_bs)

fn :: aead open String String String String => Int String
      aead open key nonce aad ciphertext_and_tag =
        | (str len key) != 32 or (str len nonce) != 12 or (str len ciphertext_and_tag) < 16 = (0 - 7, "")
        | otherwise =
          total_len = str len ciphertext_and_tag
          ct_len    = total_len - 16
          ct_str    = byte slice ciphertext_and_tag 0 ct_len
          tag_str   = byte slice ciphertext_and_tag ct_len total_len
          key_bs    = bytes to list key
          nonce_bs  = bytes to list nonce
          aad_bs    = bytes to list aad
          ct_bs     = bytes to list ct_str
          otk       = c take ints (chacha20 block key_bs nonce_bs 0) 32
          exp_tag   = bytes from list (poly1305 aead mac otk aad_bs ct_bs)
          decrypt verified aead (ct equal tag_str exp_tag) key_bs nonce_bs ct_bs

32.5. Garanzie algoritmiche, memoria RMM1/RMM2 e le 5 Regole d'Oro di crypto

  • Garanzia algoritmica e di sicurezza: aead open verifica il tag Poly1305 da 16 byte in tempo costante prima di restituire anche un solo byte di testo in chiaro (in caso di errore restituisce (-1, "")).
  • Complessità dello stack $O(1)$ con TCO: Tutti i cicli ricorsivi in src/main/ridgun/lib/crypto.rg sono in posizione di coda, perciò ridgunc li compila in salti nativi senza crescita dello stack.
  • Memoria deterministica (RMM1/RMM2): Zero pause del garbage collector; tutte le strutture intermedie vengono liberate in modo deterministico all'uscita dallo scope.
  • Verifica continua: Testata in modo esaustivo con stable check e la suite automatizzata src/test/ridgun/test crypto.rg.

Le 5 Regole d'Oro quando programmi con crypto

  1. Dichiara import crypto all'inizio del file e usa le funzioni multi-parola con spazi naturali affinché il tuo codice si legga come prosa.
  2. Allinea le tue pipeline di dati con = e ->: concatena le chiamate a crypto in passaggi con nomi chiari e lascia che stable format allinei le colonne.
  3. Sfrutta le funzioni totali di crypto nelle tue guardie |: controlla i valori di ritorno o destruttura gli ADT con pattern matching invece di dare per scontato che l'input esterno sia sempre valido.
  4. Evita di copiare grandi strutture se devi solo consultare un campo: ricorda che in RMM2 gli argomenti di input sono prestati e non incrementano né decrementano i contatori (+0 / -0).
  5. Combina crypto con test (import test): scrivi sempre asserzioni con assert equals o assert true per fissare gli invarianti del tuo dominio.

Nel Capitolo 33 passeremo a esplorare in profondità la lib argon2id (La lib argon2id — RFC 9106 Argon2id su Tensor[U64 * 8 * 16], Blake2b e Preset OWASP).