Salta al contingut
Ridgun
Català

32 El Llibre de Ridgun

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

32.1. Per què existeix la lib crypto? (El problema real i la filosofia Ridgun)

«Per què necessito una lib crypto escrita des de zero en Ridgun pur en lloc d'enllaçar una llibreria de C de tota la vida?»

Aquesta és la primera pregunta que es fa qualsevol enginyer de sistemes en arribar a crypto. I la resposta resumeix tota la filosofia de ridgun-libs:

Siguem sincers: comparar un HMAC amb == obre la porta a atacs de temporització (timing attacks), i dependre de libssl.so per calcular un SHA-256 o xifrar amb ChaCha20-Poly1305 trenca la portabilitat estàtica de qualsevol binari.

La lib crypto implementa al 100 % en Ridgun pur sobre Tensor[Int] preassignat: operacions de bytes/bits (RGF-019), FIPS 180-4 SHA-256, RFC 2104 HMAC-SHA-256, igualtat en temps constant (ct equal), RFC 8018 PBKDF2-HMAC-SHA-256 amb precomputació de l'estat intermedi SHA-256, façana Argon2id de 5 arguments i xifratge autenticat RFC 8439 ChaCha20-Poly1305 AEAD (aead seal / aead open).

En un ecosistema tradicional, resoldre les tasques del domini CRYPTO implica arrossegar dependències externes en C, lluitar amb punters crus, conversions opaques de tipus, excepcions no verificades o pauses impredictibles del recol·lector de brossa. En Ridgun, la lib crypto (src/main/ridgun/lib/crypto.rg) aborda el problema amb quatre principis innegociables:

  1. Declara import crypto (o import crypto as alias) a l'inici del fitxer: cada lib defineix el seu espai de noms module :: crypto i en mode --strict les funcions multi-paraula s'invoquen sempre amb espais naturals.
  2. Encadena les teves canonades d'esquerra a dreta amb ->: escriu entrada pas u -> pas dos -> resultat, fes servir màscares (+ _ _ +) per desestructurar seqüències i omet _ = en sentències seqüencials (o descarta amb -> ()).
  3. Recursió de cua amb TCO (Tail-Call Optimization): tots els recorreguts interns de crypto es compilen a bucles natius en espai de pila $O(1)$.
  4. Memòria determinista (RMM1 / RMM2): cada estructura intermèdia s'allibera en l'instant exacte en què acaba la seva última lectura, sense Garbage Collector.

32.2. Artefactes natius, importació i catàleg complet de l'API de crypto

A ridgun-libs, crypto és una lib de primera classe situada a src/main/ridgun/lib/crypto.rg (domini CRYPTO). En compilar amb stable bundle, s'empaqueta en tres formats natius:

ArtefacteRutaPropòsit
Bundle Ridguntarget/bundles/crypto.rglibDistribució i importació directa amb import crypto
Fitxer compilattarget/crypto.rgcArtefacte intermedi verificat per ridgunc
Objecte compartit ELF64target/libcrypto.soBiblioteca dinàmica nativa sense dependències de C ni libc

Cada lib declara el seu propi espai de noms amb module :: crypto: n'hi ha prou d'escriure import crypto (o import crypto as alias) a la capçalera i cridar les seves funcions amb espais naturals (la meva funcio x) i canonades -> (x la meva funcio -> resultat).

32.2.1. Resum de famílies funcionals de crypto

Signatura / Funció / TipusQuè fa i quan fer-la servir
sha256 bytes / sha256 hexResum binari de 32 bytes o hexadecimal de 64 caràcters FIPS 180-4 SHA-256
hmac sha256 / hmac sha256 hex / ct equalMAC amb clau RFC 2104 i comparació d'acumulador en temps constant
pbkdf2 hmac sha256 : String -> String -> Int -> Int -> StringDerivació de claus RFC 8018 amb estats intermedis SHA-256 precomputats
argon2id hash / argon2id verifyHashing de contrasenyes PHC RFC 9106 de 5 arguments i verificació en temps constant
aead seal / aead openXifratge autenticat RFC 8439 ChaCha20-Poly1305 i desxifratge amb verificació de tag
sha512 bytes / sha512 hexResum binari de 64 bytes o hexadecimal de 128 caràcters FIPS 180-4 SHA-512
ed25519 public key / ed25519 sign / ed25519 verifyClau pública, signatura determinista en temps constant i verificació canònica RFC 8032 §5.1 Ed25519
minisign sign / minisign verify / minisign trusted commentClaus públiques i signatures .minisig compatibles amb l'eina estàndard minisign

32.2.2. Catàleg exhaustiu de tipus i signatures a src/main/ridgun/lib/crypto.rg

A continuació tens l'inventari complet de tipus algebraics (type), operadors (op ::) i signatures de funcions (fn ::) definits a src/main/ridgun/lib/crypto.rg (2 tipus, 0 operadors i 109 funcions):

Tipus algebraics i registres (type) de 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
#Funció / Signatura (fn ::)Tipus de Retorn
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. Explorant crypto a fons: Exemples pas a pas de cada funcionalitat

No n'hi ha prou de veure la taula de signatures: explorarem cadascuna de les funcionalitats de la lib crypto mitjançant exemples progressius i executables en Ridgun. Començarem per les dues receptes tabulars d'ús quotidià i després recorrerem en detall tots els escenaris funcionals, transformacions i casos límit verificats a src/test/ridgun/test crypto.rg.

32.3.1. Exemple 1: Flux principal en estil tabular amb crypto

Observa com en Ridgun els signes = i els passos de transformació amb crypto queden alineats com una taula neta, sense variables mutables innecessàries ni efectes laterals ocults:

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. Exemple 2: Composició segura i gestió de casos de vora

Tota funció de la lib crypto està dissenyada per compondre's amb guardes booleanes | i amb la resta de les 32 libs de 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. Exemple 3: Manipulació de bytes crus 0..255 (byte str, bytes from list, bytes to list) i operacions bit a bit de 32 bits (band, bor, bxor, shl, shr)

En aquest exemple pràctic explorem de manera exhaustiva com es comporta crypto en aquest bloc funcional, incloent-hi entrades estàndard, combinacions de paràmetres i valors frontera:

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. Exemple 4: Vectors oficials SHA-256 (FIPS 180-4), HMAC-SHA-256 (RFC 4231), comparació en temps constant (ct equal) i PBKDF2-HMAC-SHA-256 (RFC 6070)

En aquest exemple pràctic explorem de manera exhaustiva com es comporta crypto en aquest bloc funcional, incloent-hi entrades estàndard, combinacions de paràmetres i valors frontera:

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. Exemple 5: Hashing de contrasenyes Argon2id de 5 arguments (argon2id hash, argon2id verify) i resistència en memòria (RFC 9106)

En aquest exemple pràctic explorem de manera exhaustiva com es comporta crypto en aquest bloc funcional, incloent-hi entrades estàndard, combinacions de paràmetres i valors frontera:

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. Exemple 6: Xifratge de flux ChaCha20, autenticador Poly1305 i segellat/obertura autenticada AEAD ChaCha20-Poly1305 (RFC 8439)

En aquest exemple pràctic explorem de manera exhaustiva com es comporta crypto en aquest bloc funcional, incloent-hi entrades estàndard, combinacions de paràmetres i valors frontera:

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. Exemple 7: Resum FIPS 180-4 SHA-512 (sha512 bytes, sha512 hex), signatures RFC 8032 §5.1 Ed25519 (ed25519 public key, ed25519 sign, ed25519 verify) i format Minisign (minisign sign, minisign verify)

En aquest exemple pràctic verifiquem els vectors oficials FIPS 180-4 SHA-512, la generació de claus i signatures Ed25519 en temps constant (amb rebuig estricte de mal·leabilitat $S \ge L$ i punts fora de la corba) i la signatura/verificació de manifestos de publicació en format .minisig:

import crypto
import test

fn :: verificar sha512 ed25519 y minisign => ()
      verificar sha512 ed25519 y minisign =
        "abc" sha512 hex -> h_abc
        assert str eq h_abc "ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f" "SHA-512 abc"
        "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 "manipulat" sig) "Missatge alterat rebutjat"
        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àlid"
        assert str eq (minisign trusted comment msig) "timestamp:1761667200\tfile:SHA256SUMS" "Comentari autenticat" -> ()

32.4. Sota el capó: com està construïda crypto en Ridgun pur (src/main/ridgun/lib/crypto.rg)

Un dels majors avantatges de ridgun-libs és que no hi ha caixes negres en C. Pots obrir src/main/ridgun/lib/crypto.rg i llegir exactament com està implementada cada operació fent servir pattern matching, arbres de guardes | / |. i recursió de cua (TCO). Vegem les peces clau de la seva implementació real:

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

Fixa't en com crypto.rg estructura les signatures fn :: ... => ... primer i resol cada cas mitjançant equacions alineades i crides de cua:

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. Implementació interna: Hex & List Utilities

Fixa't en com crypto.rg estructura les signatures fn :: ... => ... primer i resol cada cas mitjançant equacions alineades i crides de cua:

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. Implementació interna: RGF-015 Pure-Ridgun RFC 9106 Argon2id (argon2id hash, argon2id verify)

Fixa't en com crypto.rg estructura les signatures fn :: ... => ... primer i resol cada cas mitjançant equacions alineades i crides de cua:

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. Implementació interna: RFC 8439 Poly1305 One-Time Authenticator (5 x 26-bit limbs)

Fixa't en com crypto.rg estructura les signatures fn :: ... => ... primer i resol cada cas mitjançant equacions alineades i crides de cua:

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. Garanties algorísmiques, memòria RMM1/RMM2 i les 5 Regles d'Or de crypto

  • Garantia algorísmica i de seguretat: aead open verifica el tag Poly1305 de 16 bytes en temps constant abans de retornar un sol byte de text pla (si falla, retorna (-1, "")).
  • Complexitat de pila $O(1)$ amb TCO: Tots els bucles recursius a src/main/ridgun/lib/crypto.rg estan en posició de cua, de manera que ridgunc els compila a salts natius sense creixement de pila.
  • Memòria determinista (RMM1/RMM2): Zero pauses de recol·lector de brossa; totes les estructures intermèdies s'alliberen de forma determinista en sortir de l'àmbit.
  • Verificació contínua: Provada exhaustivament amb stable check i la suite automatitzada src/test/ridgun/test crypto.rg.

Les 5 Regles d'Or en programar amb crypto

  1. Declara import crypto a l'inici del fitxer i aprofita l'equivalència entre espais i guions baixos perquè el teu codi es llegeixi com a prosa.
  2. Alinea les teves canonades de dades amb =: encadena les crides a crypto en passos amb nom propi i deixa que stable format alineï les columnes.
  3. Aprofita les funcions totals de crypto a les teves guardes |: comprova els valors de retorn o desembolica els ADTs amb pattern matching en lloc d'assumir que l'entrada externa sempre és vàlida.
  4. Evita copiar grans estructures si només necessites consultar un camp: recorda que en RMM2 els arguments d'entrada no incrementen ni decrementen comptadors (+0 / -0).
  5. Combina crypto amb test (import test): escriu sempre assercions amb assert equals o assert true per fixar els invariants del teu domini.

Al Capítol 33 passarem a explorar en profunditat la lib argon2id (La lib argon2id — RFC 9106 Argon2id sobre Tensor[U64 * 8 * 16], Blake2b i Presets OWASP).