Joan edukira
Ridgun
Euskara

32 Ridgun Liburua

crypto lib-a — SHA-256, HMAC, PBKDF2, Argon2id eta ChaCha20-Poly1305 AEAD

32.1. Zergatik existitzen da crypto lib-a? (Benetako arazoa eta Ridgun filosofia)

«Zergatik behar dut crypto lib bat hutsetik Ridgun hutsean idatzia, betiko C liburutegi bat estekatu beharrean?»

Hori da edozein sistema-ingeniarik crypto-era iristean egiten duen lehen galdera. Eta erantzunak ridgun-libs-en filosofia osoa laburbiltzen du:

Izan gaitezen zintzoak: HMAC bat ==-rekin konparatzeak denbora-erasoei (timing attacks) atea irekitzen die, eta SHA-256 bat kalkulatzeko edo ChaCha20-Poly1305-ekin zifratzeko libssl.so-ren menpe egoteak edozein bitarren eramangarritasun estatikoa apurtzen du.

crypto lib-ak % 100ean Ridgun hutsean eta aurrez esleitutako Tensor[Int] gainean inplementatzen ditu: byte/bit eragiketak (RGF-019), FIPS 180-4 SHA-256, RFC 2104 HMAC-SHA-256, denbora konstanteko berdintasuna (ct equal), RFC 8018 PBKDF2-HMAC-SHA-256, 5 argumentuko Argon2id fatxada eta RFC 8439 ChaCha20-Poly1305 AEAD zifraketa autentifikatua (aead seal / aead open).

Ekosistema tradizional batean, CRYPTO domeinuko zereginak ebazteak C-ko kanpo-mendekotasunak arrastatzea, erakusle gordinekin borrokatzea, mota-bihurketa opakuak, egiaztatu gabeko salbuespenak edo zabor-biltzailearen geldialdi ustekabekoak jasatea dakar. Ridgunen, crypto lib-ak (src/main/ridgun/lib/crypto.rg) lau printzipio negoziatu ezinekin heltzen dio arazoari:

  1. Deklaratu import crypto (edo import crypto as alias) fitxategiaren hasieran: lib bakoitzak module :: crypto izen-espazioa definitzen du eta --strict moduan hitz anitzeko funtzioei zuriune naturalekin deitzen zaie beti.
  2. Kateatu zure datu-hodiak ezkerretik eskuinera -> bidez: idatzi sarrera lehen urratsa -> bigarren urratsa -> emaitza, erabili (+ _ _ +) maskarak sekuentziak desegituratzeko eta kendu _ = sententzia sekuentzialetan (edo baztertu -> () bidez).
  3. Isats-errekurtsioa TCOrekin (Tail-Call Optimization): crypto-eko barne-ibilbide guztiak $O(1)$ pila-espazioko begizta natiboetara konpilatzen dira.
  4. Memoria determinista (RMM1 / RMM2): tarteko egitura bakoitza bere azken irakurketa amaitzen den une zehatzean askatzen da, Garbage Collector gabe.

32.2. Artefaktu natiboak, inportazioa eta crypto APIaren katalogo osoa

ridgun-libs-en, crypto lehen mailako lib bat da, src/main/ridgun/lib/crypto.rg bidean kokatua (CRYPTO domeinua). stable bundle-ekin konpilatzean, hiru formatu natibotan paketatzen da:

ArtefaktuaBideaHelburua
Ridgun Bundle-atarget/bundles/crypto.rglibBanaketa eta inportazio zuzena import crypto bidez
Fitxategi konpilatuatarget/crypto.rgcridgunc-ek egiaztatutako tarteko artefaktua
ELF64 objektu partekatuatarget/libcrypto.soC edo libc mendekotasunik gabeko liburutegi dinamiko natiboa

Lib bakoitzak bere izen-espazioa deklaratzen du module :: crypto bidez: nahikoa da goiburuan import crypto (edo import crypto as alias) idaztea eta bere funtzioei zuriune naturalekin (nire funtzioa x) eta -> hodiekin (x nire funtzioa -> emaitza) deitzea.

32.2.1. crypto-eko familia funtzionalen laburpena

Sinadura / Funtzioa / MotaZer egiten duen eta noiz erabili
sha256 bytes / sha256 hexFIPS 180-4 SHA-256 32 byteko laburpen bitarra edo 64 karaktereko hamaseitarra
hmac sha256 / hmac sha256 hex / ct equalRFC 2104 gakodun MACa eta denbora konstanteko metagailu-konparazioa
pbkdf2 hmac sha256 : String -> String -> Int -> Int -> StringRFC 8018 gako-eratorpena aurrez kalkulatutako SHA-256 tarteko egoerekin
argon2id hash / argon2id verify5 argumentuko RFC 9106 PHC pasahitz-hashinga eta denbora konstanteko egiaztapena
aead seal / aead openRFC 8439 ChaCha20-Poly1305 zifraketa autentifikatua eta etiketa egiaztatuko deszifraketa
sha512 bytes / sha512 hex64 byteko laburpen bitarra edo 128 karaktereko hamaseitarra FIPS 180-4 SHA-512
ed25519 public key / ed25519 sign / ed25519 verifyGako publikoa, denbora konstanteko sinadura determinista eta RFC 8032 §5.1 Ed25519 egiaztapen kanonikoa
minisign sign / minisign verify / minisign trusted commentminisign tresna estandarrarekin bateragarriak diren gako publikoak eta .minisig sinadurak

32.2.2. src/main/ridgun/lib/crypto.rg-eko mota eta sinadura guztien katalogoa

Jarraian duzu src/main/ridgun/lib/crypto.rg fitxategian definitutako mota aljebraiko (type), eragile (op ::) eta funtzio-sinadura (fn ::) guztien inbentario osoa (2 mota, 0 eragile eta 109 funtzio):

crypto-eko mota aljebraikoak eta erregistroak (type):

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
#Funtzioa / Sinadura (fn ::)Itzulera-mota
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. crypto sakonki esploratzen: Funtzionalitate bakoitzaren urratsez urratseko adibideak

Ez da nahikoa sinadura-taula ikustea: crypto lib-aren funtzionalitate bakoitza Ridgunen exekutagarriak diren adibide progresiboen bidez esploratuko dugu. Eguneroko bi errezeta taularatuekin hasiko gara eta ondoren xehetasunez ikusiko ditugu src/test/ridgun/test crypto.rg-en egiaztatutako agertoki funtzional eta muga-kasu guztiak.

32.3.1. 1. adibidea: Fluxu nagusia estilo taularatuan crypto erabiliz

Ikusi nola Ridgunen = ikurrak eta crypto bidezko eraldaketa-urratsak taula garbi bat bezala lerrokatuta geratzen diren, beharrezkoak ez diren aldagai aldakorrik edo ezkutuko albo-ondoriorik gabe:

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. 2. adibidea: Konposizio segurua eta muga-kasuen kudeaketa

crypto lib-eko funtzio oro | guarda boolearrekin eta ridgun-libs-eko gainerako 32 lib-ekin konposatzeko diseinatuta dago:

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. 3. adibidea: 0..255 byte gordinen manipulazioa (byte str, bytes from list, bytes to list) eta 32 biteko bit-eragiketak (band, bor, bxor, shl, shr)

Adibide praktiko honetan crypto bloke funtzional honetan nola portatzen den sakonki aztertzen dugu, sarrera estandarrak, parametro-konbinazioak eta muga-balioak barne:

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. 4. adibidea: SHA-256 (FIPS 180-4), HMAC-SHA-256 (RFC 4231), denbora konstanteko konparazioa (ct equal) eta PBKDF2-HMAC-SHA-256 (RFC 6070) bektore ofizialak

Adibide praktiko honetan crypto bloke funtzional honetan nola portatzen den sakonki aztertzen dugu, sarrera estandarrak, parametro-konbinazioak eta muga-balioak barne:

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. 5. adibidea: 5 argumentuko Argon2id pasahitzen hashing-a (argon2id hash, argon2id verify) eta memoria-gogortasuna (RFC 9106)

Adibide praktiko honetan crypto bloke funtzional honetan nola portatzen den sakonki aztertzen dugu, sarrera estandarrak, parametro-konbinazioak eta muga-balioak barne:

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. 6. adibidea: ChaCha20 korronte-zifraketa, Poly1305 autentifikatzailea eta ChaCha20-Poly1305 AEAD zigilatze/irekitze autentifikatua (RFC 8439)

Adibide praktiko honetan crypto bloke funtzional honetan nola portatzen den sakonki aztertzen dugu, sarrera estandarrak, parametro-konbinazioak eta muga-balioak barne:

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. 7. Adibidea: FIPS 180-4 SHA-512 (sha512 bytes, sha512 hex), RFC 8032 §5.1 Ed25519 sinadurak (ed25519 public key, ed25519 sign, ed25519 verify) eta Minisign formatua (minisign sign, minisign verify)

Adibide praktiko honetan FIPS 180-4 SHA-512 bektore ofizialak, denbora konstanteko Ed25519 gako eta sinadurak ($S \ge L$ maleabilitatearen ukapen zorrotzarekin) eta .minisig formatuko argitalpen-manifestuen sinadura/egiaztapena probatzen ditugu:

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 "aldatua" sig) "Mezu aldatua baztertua"
        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 baliozkoa"
        assert str eq (minisign trusted comment msig) "timestamp:1761667200\tfile:SHA256SUMS" "Iruzkin autentifikatua" -> ()

32.4. Kaputaren azpian: nola dagoen eraikia crypto Ridgun hutsean (src/main/ridgun/lib/crypto.rg)

ridgun-libs-en abantaila handienetako bat C-ko kutxa beltzik ez egotea da. src/main/ridgun/lib/crypto.rg ireki dezakezu eta eragiketa bakoitza pattern matching, | / |. guarda-zuhaitzak eta isats-errekurtsioa (TCO) erabiliz nola inplementatuta dagoen irakurri. Ikus ditzagun bere benetako inplementazioaren funtsezko piezak:

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

Ikusi nola crypto.rg-ek lehenik fn :: ... => ... sinadurak egituratzen dituen eta kasu bakoitza lerrokatutako ekuazioen eta isats-deien bidez ebazten duen:

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. Barne-inplementazioa: Hex & List Utilities

Ikusi nola crypto.rg-ek lehenik fn :: ... => ... sinadurak egituratzen dituen eta kasu bakoitza lerrokatutako ekuazioen eta isats-deien bidez ebazten duen:

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

Ikusi nola crypto.rg-ek lehenik fn :: ... => ... sinadurak egituratzen dituen eta kasu bakoitza lerrokatutako ekuazioen eta isats-deien bidez ebazten duen:

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

Ikusi nola crypto.rg-ek lehenik fn :: ... => ... sinadurak egituratzen dituen eta kasu bakoitza lerrokatutako ekuazioen eta isats-deien bidez ebazten duen:

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. Berme algoritmikoak, RMM1/RMM2 memoria eta crypto-en Urrezko 5 Arauak

  • Berme algoritmikoa eta segurtasunekoa: aead open-ek 16 byteko Poly1305 etiketa denbora konstantean egiaztatzen du testu lauko byte bakar bat itzuli aurretik (huts egiten badu, (-1, "") itzultzen du).
  • $O(1)$ pila-konplexutasuna TCOrekin: src/main/ridgun/lib/crypto.rg-eko begizta errekurtsibo guztiak isats-posizioan daude; beraz, ridgunc-ek pila-hazkuntzarik gabeko jauzi natiboetara konpilatzen ditu.
  • Memoria determinista (RMM1/RMM2): Zero zabor-biltzaile etenaldi; tarteko egitura guztiak modu deterministan askatzen dira esparrutik irtetean.
  • Etengabeko egiaztapena: Zorrotz probatua stable check eta src/test/ridgun/test crypto.rg suite automatizatuaren bidez.

crypto-ekin programatzeko Urrezko 5 Arauak

  1. Deklaratu import crypto fitxategiaren hasieran eta baliatu zuriuneen eta azpimarren arteko baliokidetasuna zure kodea prosa bezala irakur dadin.
  2. Lerrokatu zure datu-hodiak = ikurrarekin: kateatu crypto-erako deiak izen propioko urratsetan eta utzi stable format-i zutabeak lerrokatzen.
  3. Baliatu crypto-eko funtzio osoak zure | guardetan: egiaztatu itzulera-balioak edo askatu ADTak pattern matching bidez, kanpoko sarrera beti baliozkoa dela suposatu beharrean.
  4. Saihestu egitura handiak kopiatzea eremu bat kontsultatu besterik behar ez baduzu: gogoratu RMM2-n sarrerako argumentuek ez dituztela kontagailuak handitzen edo txikitzen (+0 / -0).
  5. Konbinatu crypto eta test (import test): idatzi beti asertzioak assert equals edo assert true erabiliz zure domeinuaren inbarianteak finkatzeko.

33. kapituluan argon2id lib-a sakonki esploratuko dugu (argon2id lib-a — RFC 9106 Argon2id Tensor[U64 * 8 * 16] gainean, Blake2b eta OWASP Aurrezarpenak).