Hoppa till innehållet
Ridgun
Svenska

33 Boken om Ridgun

Lib:en argon2id — RFC 9106 Argon2id över Tensor[U64 * 8 * 16], Blake2b och OWASP-profiler

33.1. Varför finns lib:en argon2id? (Det verkliga problemet och Ridguns filosofi)

”Varför behöver jag en argon2id-lib skriven från grunden i ren Ridgun i stället för att bara länka in ett gammalt C-bibliotek?”

Det är den första fråga varje systemingenjör ställer sig när man möter argon2id. Och svaret sammanfattar hela filosofin bakom ridgun-libs:

Låt oss vara ärliga: att hasha lösenord med enkel SHA-256 låter en modern GPU prova miljarder gissningar per sekund, men att länka in externa C-bibliotek för Argon2id leder ofta till kompileringsproblem.

Lib:en argon2id implementerar RFC 9106 Argon2id och RFC 7693 Blake2b-512 i 100 % ren Ridgun direkt över inbyggda Tensor[U64 * 8 * 16]-block (1 024 byte per block i sammanhängande RMM2-minne), med PHC-stränggenerering ($argon2id$v=19$m=...,t=...,p=...$), OWASP-profiler (owaspfirst, owaspsecond), pepper-stöd (argon2id hash ctx), nyckelhärledning (argon2id derive key) och detektering av parameteruppgradering (argon2id needs rehash).

I ett traditionellt ekosystem innebär uppgifter inom domänen CRYPTO ofta att man drar in externa C-beroenden, brottas med råa pekare, ogenomskinliga typkonverteringar, okontrollerade undantag eller oförutsägbara skräpsamlarpauser. I Ridgun löser lib:en argon2id (src/main/ridgun/lib/argon2id.rg) problemet genom fyra kompromisslösa principer:

  1. Deklarera import argon2id (eller import argon2id as alias) överst i filen: varje lib definierar sin module :: argon2id-namnrymd och i --strict-läge anropas flerordsfunktioner alltid med naturliga mellanslag.
  2. Kedja dina datapipelines från vänster till höger med ->: skriv indata steg ett -> steg två -> resultat, använd (+ _ _ +)-sekvensmasker för att packa upp tupler och utelämna _ = vid sekventiella satser (eller kasta med -> ()).
  3. Svansrekursion med TCO (Tail-Call Optimization): alla interna genomlöpningar i argon2id kompileras till inbyggda hårdvaruloopar med $O(1)$ stackutrymme.
  4. Deterministiskt minne (RMM1 / RMM2): varje mellanliggande struktur frigörs i exakt det ögonblick dess sista läsning avslutas, helt utan Garbage Collector.

33.2. Inbyggda artefakter, import och komplett API-katalog för argon2id

I ridgun-libs är argon2id en förstklassig lib placerad i src/main/ridgun/lib/argon2id.rg (domän CRYPTO). När du bygger med stable bundle paketeras den i tre inbyggda format:

ArtefaktSökvägSyfte
Ridgun-bundletarget/bundles/argon2id.rglibPortabel distribution och direkt import med import argon2id
Kompilerat arkivtarget/argon2id.rgcTypkontrollerad mellanartefakt verifierad av ridgunc
Delat ELF64-objekttarget/libargon2id.soInbyggt dynamiskt bibliotek helt utan C- eller libc-beroenden

Varje lib deklarerar sin egen namnrymd med module :: argon2id: skriv bara import argon2id (eller import argon2id as alias) överst och anropa dess funktioner med naturliga mellanslag (min funktion x) och ->-pipelines (x min funktion -> resultat).

33.2.1. Översikt över funktionella familjer i argon2id

Signatur / Funktion / TypVad den gör och när den används
argon2id hash / argon2id verify6-arguments RFC 9106 PHC-hashning (pwd, salt, m_kib, t, p, tag_len) och konstanttidsverifiering
argon2id hash with preset / argon2id params presetNamngivna profiler: "interactive", "owaspfirst" (19456 KiB, t=2), "owaspsecond" (47104 KiB, t=1), "moderate", "sensitive"
argon2id hash ctx / argon2id verify ctxRFC 9106-hashning med hemlig pepper ($K$) och associerad data ($X$)
argon2id derive key / argon2id needs rehashRå binär nyckelhärledning och detektering av rehash-behov för lagrade PHC-strängar
blake2b / blake2b hexAllmän kryptografisk RFC 7693 Blake2b-digest (1..64 byte)

33.2.2. Komplett katalog över typer och signaturer i src/main/ridgun/lib/argon2id.rg

Nedan följer hela förteckningen över algebraiska datatyper (type), mixfix-operatorer (op ::) och funktionssignaturer (fn ::) som definieras i src/main/ridgun/lib/argon2id.rg (10 typer, 0 operatorer och 134 funktioner):

Algebraiska datatyper och poster (type) i argon2id:

type Argon2Params = m_cost: Int, t_cost: Int, lanes: Int, tag_len: Int, version: Int
type Argon2Phc    = params: Argon2Params, salt_b64: String, tag_b64: String
type Argon2Res    = Argon2Ok String | Argon2Err String
type Argon2KeyRes = Argon2KeyOk [Int] | Argon2KeyErr String
type Argon2PhcRes = Argon2PhcOk Argon2Phc | Argon2PhcErr String
type A2M16   = mb0: Tensor[U64 * 8 * 16], mb1: Tensor[U64 * 8 * 16], mb2: Tensor[U64 * 8 * 16], mb3: Tensor[U64 * 8 * 16], mb4: Tensor[U64 * 8 * 16], mb5: Tensor[U64 * 8 * 16], mb6: Tensor[U64 * 8 * 16], mb7: Tensor[U64 * 8 * 16], mb8: Tensor[U64 * 8 * 16], mb9: Tensor[U64 * 8 * 16], mb10: Tensor[U64 * 8 * 16], mb11: Tensor[U64 * 8 * 16], mb12: Tensor[U64 * 8 * 16], mb13: Tensor[U64 * 8 * 16], mb14: Tensor[U64 * 8 * 16], mb15: Tensor[U64 * 8 * 16]
type A2M256  = mr0: A2M16, mr1: A2M16, mr2: A2M16, mr3: A2M16, mr4: A2M16, mr5: A2M16, mr6: A2M16, mr7: A2M16, mr8: A2M16, mr9: A2M16, mr10: A2M16, mr11: A2M16, mr12: A2M16, mr13: A2M16, mr14: A2M16, mr15: A2M16
type A2M4096 = mp0: A2M256, mp1: A2M256, mp2: A2M256, mp3: A2M256, mp4: A2M256, mp5: A2M256, mp6: A2M256, mp7: A2M256, mp8: A2M256, mp9: A2M256, mp10: A2M256, mp11: A2M256, mp12: A2M256, mp13: A2M256, mp14: A2M256, mp15: A2M256
type A2Mem   = mt0: A2M4096, mt1: A2M4096, mt2: A2M4096, mt3: A2M4096, mt4: A2M4096, mt5: A2M4096, mt6: A2M4096, mt7: A2M4096, mt8: A2M4096, mt9: A2M4096, mt10: A2M4096, mt11: A2M4096, mt12: A2M4096, mt13: A2M4096, mt14: A2M4096, mt15: A2M4096
type A2Ctx   = r_pass: Int, s_slice: Int, l_lane: Int, p_lanes: Int, q_cols: Int, seg_len: Int, t_iters: Int
#Funktion / Signatur (fn ::)Returtyp
1fn :: a2 list len [Int]Int
2fn :: a2 str list len [String]Int
3fn :: a2 nth int [Int] IntInt
4fn :: a2 nth str [String] IntString
5fn :: a2 take ints [Int] Int[Int]
6fn :: a2 drop ints [Int] Int[Int]
7fn :: a2 pad zeroes Int[Int]
8fn :: a2 raw byte ub IntString
9fn :: a2 raw byte IntString
10fn :: a2 str to bytes loop String Int Int[Int]
11fn :: a2 str to bytes String[Int]
12fn :: a2 bytes to str [Int]String
13fn :: hex digitsString
14fn :: byte to hex IntString
15fn :: bytes to hex [Int]String
16fn :: byte bitxor Int IntInt
17fn :: u32 norm IntInt
18fn :: u32 add Int IntInt
19fn :: u32 bitxor Int IntInt
20fn :: a2 u64 lh Int IntU64
21fn :: a2 f blamka U64 U64U64
22fn :: a2 u32 to le4 Int[Int]
23fn :: a2 bytes to u32 le [Int] IntInt
24fn :: a2 zero w16Tensor[U64 * 16]
25fn :: a2 zero blk8Tensor[U64 * 8 * 16]
26fn :: a2 b2b iv w16Tensor[U64 * 16]
27fn :: a2 b2b g4 U64 U64 U64 U64 U64 U64U64 U64 U64 U64
28fn :: a2 b2b sigma Int[Int]
29fn :: a2 b2b round Tensor[U64 * 16] Tensor[U64 * 16] IntTensor[U64 * 16]
30fn :: a2 b2b rounds Tensor[U64 * 16] Tensor[U64 * 16] IntTensor[U64 * 16]
31fn :: a2 b2b compress Tensor[U64 * 16] Tensor[U64 * 16] Int BoolTensor[U64 * 16]
32fn :: a2 bytes to u64 [Int] IntU64
33fn :: a2 bytes to w16 [Int] IntTensor[U64 * 16]
34fn :: a2 u64 to le8 U64[Int]
35fn :: a2 h8 to bytes Tensor[U64 * 16][Int]
36fn :: a2 b2b loop Tensor[U64 * 16] [Int] IntTensor[U64 * 16]
37fn :: a2 blake2b [Int] Int[Int]
38fn :: a2 b2b long loop [Int] Int Int Int[Int]
39fn :: a2 blake2b long [Int] Int[Int]
40fn :: a2 blamka g U64 U64 U64 U64U64 U64 U64 U64
41fn :: a2 blamka p16 Tensor[U64 * 16]()
42fn :: a2 blamka col pair Tensor[U64 * 8 * 16] IntTensor[U64 * 8 * 16]
43fn :: a2 blamka cols loop Tensor[U64 * 8 * 16] IntTensor[U64 * 8 * 16]
44fn :: a2 bitxor blk8 Tensor[U64 * 8 * 16] Tensor[U64 * 8 * 16]Tensor[U64 * 8 * 16]
45fn :: a2 compress blk8 Tensor[U64 * 8 * 16] Tensor[U64 * 8 * 16]Tensor[U64 * 8 * 16]
46fn :: a2 get blk8 Tensor[U64 * 8 * 16] IntU64
47fn :: a2 bytes to blk8 row Tensor[U64 * 8 * 16] Int [Int] IntTensor[U64 * 8 * 16]
48fn :: a2 bytes to blk8 loop Tensor[U64 * 8 * 16] Int [Int]Tensor[U64 * 8 * 16]
49fn :: a2 bytes to blk8 [Int]Tensor[U64 * 8 * 16]
50fn :: a2 blk8 row to bytes Tensor[U64 * 8 * 16] Int Int[Int]
51fn :: a2 blk8 to bytes loop Tensor[U64 * 8 * 16] Int[Int]
52fn :: a2 blk8 to bytes Tensor[U64 * 8 * 16][Int]
53fn :: a2 zero memA2Mem
54fn :: a2 get m16 A2M16 IntTensor[U64 * 8 * 16]
55fn :: a2 get m256 A2M256 Int IntTensor[U64 * 8 * 16]
56fn :: a2 get m4096 A2M4096 Int Int IntTensor[U64 * 8 * 16]
57fn :: a2 get mem A2Mem Int Int Int IntTensor[U64 * 8 * 16]
58fn :: a2 nth blk A2Mem IntTensor[U64 * 8 * 16]
59fn :: a2 set m16 A2M16 Int Tensor[U64 * 8 * 16]A2M16
60fn :: a2 set m256 A2M256 Int Int Tensor[U64 * 8 * 16]A2M256
61fn :: a2 set m4096 A2M4096 Int Int Int Tensor[U64 * 8 * 16]A2M4096
62fn :: a2 set mem A2Mem Int Int Int Int Tensor[U64 * 8 * 16]A2Mem
63fn :: a2 set blk A2Mem Int Tensor[U64 * 8 * 16]A2Mem
64fn :: a2 init all lanes loop A2Mem [Int] Int Int IntA2Mem
65fn :: a2 init all lanes [Int] Int Int IntA2Mem
66fn :: a2 make addr blk A2Ctx IntTensor[U64 * 8 * 16]
67fn :: a2 calc w size A2Ctx Int BoolInt
68fn :: a2 fill segment A2Mem A2Ctx Int Tensor[U64 * 8 * 16]A2Mem
69fn :: a2 run lanes A2Mem A2Ctx IntA2Mem
70fn :: a2 run slices A2Mem A2Ctx IntA2Mem
71fn :: a2 run passes A2Mem A2Ctx IntA2Mem
72fn :: a2 bitxor final lanes A2Mem Int Int Int Tensor[U64 * 8 * 16]Tensor[U64 * 8 * 16]
73fn :: derive raw tag ctx [Int] [Int] String String Argon2Params[Int]
74fn :: derive raw tag [Int] [Int] Argon2Params[Int]
75fn :: b64 alphabetString
76fn :: b64 char IntString
77fn :: b64 encode nopad [Int]String
78fn :: b64 char val IntInt
79fn :: valid b64 chars String Int IntBool
80fn :: is valid b64 nopad StringBool
81fn :: b64 decode rem String Int Int Int[Int]
82fn :: b64 decode loop String Int Int[Int]
83fn :: b64 decode nopad String[Int]
84fn :: default argon2 paramsArgon2Params
85fn :: argon2 interactive paramsArgon2Params
86fn :: argon2 moderate paramsArgon2Params
87fn :: argon2 sensitive paramsArgon2Params
88fn :: argon2 params Int Int Int IntArgon2Params
89fn :: clamp salt len IntInt
90fn :: a2 getrandom call Int Int IntInt
91fn :: a2 getrandom step Int Int IntInt
92fn :: a2 getrandom loop Int IntInt
93fn :: argon2id generate salt IntString
94fn :: validate argon2 params Argon2Params [Int]String
95fn :: finish derive key checked [Int] IntArgon2KeyRes
96fn :: finish derive key String [Int] Argon2Params StringArgon2KeyRes
97fn :: argon2id derive key String String Argon2ParamsArgon2KeyRes
98fn :: key res to hex Argon2KeyResArgon2Res
99fn :: argon2id derive key hex String String Argon2ParamsArgon2Res
100fn :: build phc from tag [Int] [Int] Argon2ParamsArgon2Res
101fn :: finish argon2id hash ctx String [Int] String String Argon2Params StringArgon2Res
102fn :: argon2id hash ctx String String String String Argon2ParamsArgon2Res
103fn :: argon2id hash bytes String [Int] Argon2ParamsArgon2Res
104fn :: argon2id hash params String String Argon2ParamsArgon2Res
105fn :: argon2id hash password String Argon2ParamsArgon2Res
106fn :: ct diff loop [Int] [Int] IntInt
107fn :: constant time bytes eq [Int] [Int]Bool
108fn :: constant time eq String StringBool
109fn :: split str loop String Int Int Int Int[String]
110fn :: a2 is digits loop String Int IntBool
111fn :: parse kv int String StringInt
112fn :: build parsed phc String String Argon2Params StringArgon2PhcRes
113fn :: decode phc fields String String Int Int IntArgon2PhcRes
114fn :: parse phc mtp Int Int Int [String]Argon2PhcRes
115fn :: parse phc kvs [String] [String]Argon2PhcRes
116fn :: parse phc parts [String]Argon2PhcRes
117fn :: argon2id parse phc StringArgon2PhcRes
118fn :: check rehash parsed Argon2PhcRes Argon2ParamsBool
119fn :: argon2id needs rehash String Argon2ParamsBool
120fn :: verify parsed ctx Argon2PhcRes String String StringBool
121fn :: argon2id verify ctx String String String StringBool
122fn :: argon2id verify String StringBool
123fn :: unwrap phc res Argon2ResString
124fn :: argon2id hash String String Int Int IntString
125fn :: argon2id hash phc String String Int Int IntString

33.3. Djupdykning i argon2id: Steg-för-steg-exempel för varje funktion

Det räcker inte med en signaturtabell: låt oss utforska varje funktionalitet i lib:en argon2id genom progressiva, körbara Ridgun-exempel. Vi börjar med två tabellbaserade vardagsrecept och går därefter igenom alla funktionella scenarier, transformationer och gränsfall som verifieras i src/test/ridgun/test argon2id.rg.

33.3.1. Exempel 1: Huvudflöde i tabellform med argon2id

Lägg märke till hur =-tecknen och transformationsstegen med argon2id linjeras som en ren tabell utan onödiga muterbara variabler eller dolda sidoeffekter:

import argon2id

fn :: proteger clave usuario String => String Bool Bool
      proteger clave usuario clave =
        argon2id hash clave "sal-unica-16byte" 32 2 1 -> phc
        argon2id verify clave phc -> valido
        argon2id needs rehash phc (owasp default params) -> rehash
        (phc, valido, rehash)

33.3.2. Exempel 2: Säker komposition och hantering av gränsfall

Varje funktion i lib:en argon2id är utformad för att kombineras sömlöst med booleska vakter | och med övriga 32 libs i ridgun-libs:

import argon2id

fn :: derivar clave maestra String String => Argon2KeyRes
      derivar clave maestra frase sal =
        -- Deriva 32 bytes listos para ChaCha20-Poly1305 AEAD
        argon2id derive key frase sal 64 2 1 32

33.3.3. Exempel 3: OWASP-säkerhetsprofiler (Interactive, Moderate, Sensitive), generering av PHC-strängar $argon2id$v=19$... och konstanttidskontroll

I detta praktiska exempel undersöker vi grundligt hur argon2id beter sig inom detta funktionsområde, inklusive standardindata, parameterkombinationer och gränsvärden:

fn :: test argon2id phc hashing and verification => ()
      test argon2id phc hashing and verification =
        -- LIB-1: OpenSSL 3.5 / libsodium reference vectors
        v1 = unwrap a2 phc (argon2id derive key hex "correct horse" "saltsaltsaltsalt" (argon2 params 8 1 1 32))
        v2 = unwrap a2 phc (argon2id derive key hex "correct horse" "saltsaltsaltsalt" (argon2 params 64 2 1 32))
        v3 = unwrap a2 phc (argon2id derive key hex "correct horse" "saltsaltsaltsalt" (argon2 params 256 2 1 32))
        assert str eq v1 "b7940bccc95f48f2cc7b36f55ff851c356ca4d3a6bc8cd4986a0692f2aac78ec" "LIB-1 vector m=8,t=1,p=1"
        assert str eq v2 "e6374c2335a815bcd23a5ea4df06a2f6e5377fc2c3f879865d0828d6e84740f2" "LIB-1 vector m=64,t=2,p=1"
        assert str eq v3 "8f65ef99a71b1e98450f2c80d78604af3644276f965db52045bbed139b03b5fd" "LIB-1 vector m=256,t=2,p=1"

        -- LIB-1: RFC 9106 §5.3 official test vector (pwd=32*0x01, salt=16*0x02, secret=8*0x03, ad=12*0x04, m=32, t=3, p=4, tag=32)
        pwd32  = [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
        salt16 = [2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2]
        sec8   = a2 bytes to str [3, 3, 3, 3, 3, 3, 3, 3]
        ad12   = a2 bytes to str [4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4]
        rfc_h  = bytes to hex (derive raw tag ctx pwd32 salt16 sec8 ad12 (argon2 params 32 3 4 32))
        assert str eq rfc_h "0d640df58d78766c08c037a34a8b53c9d01ef0452d75b65eb52520e96b01e659" "RFC 9106 §5.3 official vector"

        params   = argon2 params 32 2 2 16
        res1     = argon2id hash params "correct-horse-battery-staple" "somesalt1234" params
        res2     = argon2id hash params "correct-horse-battery-staple" "somesalt1234" params
        res_diff = argon2id hash params "correct-horse-battery-staple" "othersalt999" params
        assert ok (is a2 ok res1)
        assert ok (is a2 ok res2)
        assert ok (is a2 ok res_diff)
        phc1     = unwrap a2 phc res1
        phc2     = unwrap a2 phc res2
        phc_diff = unwrap a2 phc res_diff
        assert eq phc1 phc2
        assert eq (phc1 == phc_diff) False
        assert ok (str starts with phc1 "$argon2id$v=19$m=32,t=2,p=2$")
        bad_t    = argon2id hash params "password" "somesalt1234" (argon2 params 32 0 2 16)
        bad_p    = argon2id hash params "password" "somesalt1234" (argon2 params 32 2 0 16)
        bad_m    = argon2id hash params "password" "somesalt1234" (argon2 params 8 2 2 16)
        bad_tag  = argon2id hash params "password" "somesalt1234" (argon2 params 32 2 2 2)
        bad_salt = argon2id hash params "password" "short" params
        assert eq (is a2 ok bad_t) False
        assert eq (is a2 ok bad_p) False
        assert eq (is a2 ok bad_m) False
        assert eq (is a2 ok bad_tag) False
        assert eq (is a2 ok bad_salt) False

        -- LIB-7: Fixed (password, phc) argument order; never swaps if password starts with "$argon2id$"
        assert ok (argon2id verify "correct-horse-battery-staple" phc1)
        assert eq (argon2id verify phc1 "correct-horse-battery-staple") False
        assert eq (argon2id verify "wrong-password" phc1) False
        tricky_pwd = "$argon2id$v=19$m=32,t=2,p=2$not-a-hash-just-a-password"
        tricky_phc = argon2id hash tricky_pwd "0123456789abcdef" 32 2 1
        assert ok (argon2id verify tricky_pwd tricky_phc)
        assert eq (argon2id verify tricky_phc tricky_pwd) False
        assert eq (argon2id verify "correct-horse-battery-staple" "$argon2i$v=19$m=32,t=2,p=2$c29tZXNhbHQxMjM0$AAAA") False
        assert eq (argon2id verify "correct-horse-battery-staple" "$argon2id$v=18$m=32,t=2,p=2$c29tZXNhbHQxMjM0$AAAA") False
        assert eq (argon2id verify "correct-horse-battery-staple" "malformed_phc_string") False
        assert ok (constant time eq "abc123xyz" "abc123xyz")
        assert eq (constant time eq "abc123xyz" "abc123xyw") False

33.3.4. Exempel 4: Härledning av binära nycklar (argon2id derive key), CSPRNG-saltgenerering (argon2id gen salt) och detektering av parameteruppgradering (argon2id needs rehash)

I detta praktiska exempel undersöker vi grundligt hur argon2id beter sig inom detta funktionsområde, inklusive standardindata, parameterkombinationer och gränsvärden:

fn :: test argon2id key derivation salting and rehash => ()
      test argon2id key derivation salting and rehash =
        -- LIB-3: Non-deterministic CSPRNG raw byte salt generation
        salt16    = argon2id generate salt 16
        salt16_b  = argon2id generate salt 16
        salt_min  = argon2id generate salt 2
        assert eq (str len salt16) 16
        assert eq (str len salt16_b) 16
        assert eq (str len salt_min) 16
        assert eq (salt16 == salt16_b) False

        -- LIB-4: Presets at or above OWASP (19456 KiB) and RFC 9106 §4 (65536 KiB)
        p_def     = default argon2 params
        p_inter   = argon2 interactive params
        p_mod     = argon2 moderate params
        p_sens    = argon2 sensitive params
        assert eq (m_cost p_def) 19456
        assert eq (t_cost p_def) 2
        assert eq (lanes p_def) 1
        assert eq (tag_len p_def) 32
        assert eq (m_cost p_inter) 19456
        assert eq (m_cost p_mod) 47104
        assert eq (m_cost p_sens) 65536
        assert eq (t_cost p_sens) 3
        assert eq (lanes p_sens) 4

        p_fast    = argon2 params 64 2 2 32
        auto_res  = argon2id hash password "my-secret-passphrase" p_fast
        assert ok (is a2 ok auto_res)
        auto_phc  = unwrap a2 phc auto_res
        assert ok (argon2id verify "my-secret-passphrase" auto_phc)
        assert eq (argon2id verify "other-passphrase" auto_phc) False
        parsed    = argon2id parse phc auto_phc
        assert ok (is phc parsed ok parsed)
        assert eq (is phc parsed ok (argon2id parse phc "invalid_phc")) False
        assert eq (argon2id needs rehash auto_phc p_fast) False
        assert ok (argon2id needs rehash auto_phc p_inter)
        assert ok (argon2id needs rehash auto_phc (argon2 params 64 3 2 32))
        assert ok (argon2id needs rehash auto_phc (argon2 params 64 2 4 32))
        assert ok (argon2id needs rehash auto_phc (argon2 params 64 2 2 64))
        assert ok (argon2id needs rehash "invalid_phc" p_fast)
        k_res     = argon2id derive key "encryption-passphrase" "1234567890abcdef" p_fast
        kh_res    = argon2id derive key hex "encryption-passphrase" "1234567890abcdef" p_fast
        k_bad     = argon2id derive key "encryption-passphrase" "short" p_fast
        kh_bad    = argon2id derive key hex "encryption-passphrase" "short" p_fast
        assert ok (is key ok k_res)
        assert ok (is a2 ok kh_res)
        assert eq (is key ok k_bad) False
        assert eq (is a2 ok kh_bad) False
        assert eq (a2 list len (unwrap key bytes k_res)) 32
        assert eq (str len (unwrap a2 phc kh_res)) 64
        ctx_res   = argon2id hash ctx "user-password" "1234567890abcdef" "app-pepper-key" "tenant:42" p_fast
        ctx_bad   = argon2id hash ctx "user-password" "short" "app-pepper-key" "tenant:42" p_fast
        assert ok (is a2 ok ctx_res)
        assert eq (is a2 ok ctx_bad) False
        ctx_phc   = unwrap a2 phc ctx_res
        assert ok (argon2id verify ctx "user-password" ctx_phc "app-pepper-key" "tenant:42")
        assert eq (argon2id verify ctx "user-password" ctx_phc "wrong-pepper" "tenant:42") False
        assert eq (argon2id verify ctx "user-password" ctx_phc "app-pepper-key" "tenant:99") False

        -- RG-051: PHC string with empty tag segment must be rejected by parse & verify
        assert eq (is phc parsed ok (argon2id parse phc "$argon2id$v=19$m=64,t=2,p=1$c2FsdHNhbHRzYWx0c2FsdA$")) False
        assert eq (argon2id verify "correct horse" "$argon2id$v=19$m=64,t=2,p=1$c2FsdHNhbHRzYWx0c2FsdA$") False

33.3.5. Exempel 5: Fullständigt autentiseringsflöde och transparent migrering av säkerhetspolicyer med argon2id verify och argon2id needs rehash

I detta praktiska exempel undersöker vi grundligt hur argon2id beter sig inom detta funktionsområde, inklusive standardindata, parameterkombinationer och gränsvärden:

import argon2id

fn :: verificar y evaluar politica (password : String) (phc_guardado : String) => (Bool, Bool)
      verificar y evaluar politica password phc_guardado =
        es_valida       = argon2id verify password phc_guardado
        requiere_mejora = argon2id needs rehash phc_guardado (argon2id params moderate)
        (es_valida, requiere_mejora)

33.4. Under huven: hur argon2id är byggd i ren Ridgun (src/main/ridgun/lib/argon2id.rg)

En av de största fördelarna med ridgun-libs är att det inte finns några svarta lådor i C. Du kan öppna src/main/ridgun/lib/argon2id.rg och läsa exakt hur varje operation är implementerad med mönstermatchning, | / |.-vaktträd och svansrekursion (TCO). Låt oss titta på nyckeldelarna i den faktiska implementationen:

33.4.1. Intern implementation: Core Implementation Part 1

Se hur argon2id.rg deklarerar explicita fn :: ... => ...-kontrakt först och löser varje fall genom linjerade ekvationer och svansanrop:

module :: argon2id

type Argon2Params = m_cost: Int, t_cost: Int, lanes: Int, tag_len: Int, version: Int
type Argon2Phc    = params: Argon2Params, salt_b64: String, tag_b64: String
type Argon2Res    = Argon2Ok String | Argon2Err String
type Argon2KeyRes = Argon2KeyOk [Int] | Argon2KeyErr String
type Argon2PhcRes = Argon2PhcOk Argon2Phc | Argon2PhcErr String

type A2M16   = mb0: Tensor[U64 * 256], mb1: Tensor[U64 * 256], mb2: Tensor[U64 * 256], mb3: Tensor[U64 * 256], mb4: Tensor[U64 * 256], mb5: Tensor[U64 * 256], mb6: Tensor[U64 * 256], mb7: Tensor[U64 * 256], mb8: Tensor[U64 * 256], mb9: Tensor[U64 * 256], mb10: Tensor[U64 * 256], mb11: Tensor[U64 * 256], mb12: Tensor[U64 * 256], mb13: Tensor[U64 * 256], mb14: Tensor[U64 * 256], mb15: Tensor[U64 * 256]
type A2M256  = mr0: A2M16, mr1: A2M16, mr2: A2M16, mr3: A2M16, mr4: A2M16, mr5: A2M16, mr6: A2M16, mr7: A2M16, mr8: A2M16, mr9: A2M16, mr10: A2M16, mr11: A2M16, mr12: A2M16, mr13: A2M16, mr14: A2M16, mr15: A2M16
type A2M4096 = mp0: A2M256, mp1: A2M256, mp2: A2M256, mp3: A2M256, mp4: A2M256, mp5: A2M256, mp6: A2M256, mp7: A2M256, mp8: A2M256, mp9: A2M256, mp10: A2M256, mp11: A2M256, mp12: A2M256, mp13: A2M256, mp14: A2M256, mp15: A2M256
type A2Mem   = mt0: A2M4096, mt1: A2M4096, mt2: A2M4096, mt3: A2M4096, mt4: A2M4096, mt5: A2M4096, mt6: A2M4096, mt7: A2M4096, mt8: A2M4096, mt9: A2M4096, mt10: A2M4096, mt11: A2M4096, mt12: A2M4096, mt13: A2M4096, mt14: A2M4096, mt15: A2M4096
type A2Ctx   = r_pass: Int, s_slice: Int, l_lane: Int, p_lanes: Int, q_cols: Int, seg_len: Int, t_iters: Int

fn :: a2 list len [Int]    => Int
      a2 list len []       =  0
      a2 list len (_:rest) =  1 + (a2 list len rest)

fn :: a2 str list len [String] => Int
      a2 str list len []       =  0
      a2 str list len (_:rest) =  1 + (a2 str list len rest)

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

fn :: a2 nth str [String] Int => String
      a2 nth str []       _   =  ""
      a2 nth str (x:rest) idx =
        | idx <= 0  = x
        | otherwise = a2 nth str rest (idx - 1)

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

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

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

fn :: a2 raw byte ub Int => String
      a2 raw byte ub 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 :: a2 raw byte Int => String
      a2 raw byte b   =  a2 raw byte ub (((b % 256) + 256) % 256)

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

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

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

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

fn :: a2 str to bytes loop String Int Int [Int] [Int] [Int] Int => [Int]
      a2 str to bytes loop s idx len l0 l1 l2 cnt =
        | idx >= len = a2 lb finish l0 l1 l2
        | otherwise  =
          n0, n1, n2, ncnt = a2 lb append ((((str char at s idx) % 256) + 256) % 256) l0 l1 l2 cnt
          a2 str to bytes loop s (idx + 1) len n0 n1 n2 ncnt

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

fn :: a2 bytes to str loop [Int] String => String
      a2 bytes to str loop []       acc = acc
      a2 bytes to str loop (b:rest) acc = a2 bytes to str loop rest (acc + (a2 raw byte b))

fn :: a2 bytes to str [Int] => String
      a2 bytes to str bs    =  a2 bytes to str loop bs ""

fn :: hex digits => String
      hex digits =  "0123456789abcdef"

fn :: byte to hex Int => String
      byte to hex b   =
        ub = b % 256
        hi = (ub / 16) % 16
        lo = ub % 16
        (str slice (hex digits) hi (hi + 1)) + (str slice (hex digits) lo (lo + 1))

fn :: bytes to hex [Int]    => String
      bytes to hex []       =  ""
      bytes to hex (b:rest) =  (byte to hex b) + (bytes to hex rest)

fn :: byte bitxor Int Int => Int
      byte bitxor a   b   =
        (a xor b) and 255

fn :: u32 norm Int => Int
      u32 norm x   =  x and 0xFFFFFFFF

fn :: u32 add Int Int => Int
      u32 add a   b   =  (a + b) and 0xFFFFFFFF

fn :: u32 bitxor Int Int => Int
      u32 bitxor a   b   =
        (a xor b) and 0xFFFFFFFF

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

fn :: a2 f blamka U64 U64 => U64
      a2 f blamka x   y   =  x + y + 2 * ((x and 0xFFFFFFFF) * (y and 0xFFFFFFFF))

fn :: a2 u32 to le4 Int => [Int]
      a2 u32 to le4 w   =
        uw = u32 norm w
        [uw and 255, (uw shr 8) and 255, (uw shr 16) and 255, (uw shr 24) and 255]

fn :: a2 bytes to u32 le [Int] Int => Int
      a2 bytes to u32 le bs off =
        (a2 nth int bs off) or ((a2 nth int bs (off + 1)) shl 8) or ((a2 nth int bs (off + 2)) shl 16) or ((a2 nth int bs (off + 3)) shl 24)

fn :: a2 zero w16 => Tensor[U64 * 32]
      a2 zero w16 =  Tensor[U64 * 32]

fn :: a2 zero blk8 => Tensor[U64 * 256]
      a2 zero blk8 =  Tensor[U64 * 256]

fn :: a2 w16 get Tensor[U64 * 32] Int => U64
      a2 w16 get w                idx =  w_(2 * idx) or (w_((2 * idx) + 1) shl 32)

fn :: a2 w16 set Tensor[U64 * 32] Int U64 => Tensor[U64 * 32]
      a2 w16 set w                idx val =
        w_(2 * idx)       = val and 0xFFFFFFFF
        w_((2 * idx) + 1) = (val shr 32) and 0xFFFFFFFF
        w

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

fn :: a2 b2b g4 U64 U64 U64 U64 U64 U64 => U64 U64 U64 U64
      a2 b2b g4 a b c d mx my =
        mut a = a + b + mx
        mut d = (d xor a) rotr 32
        mut c = c + d
        mut b = (b xor c) rotr 24
        a = a + b + my
        d = (d xor a) rotr 16
        c = c + d
        b = (b xor c) rotr 63
        (a, b, c, d)

fn :: a2 b2b sigma Int => [Int]
      a2 b2b sigma r   =
        | (r % 10) == 0 = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
        | (r % 10) == 1 = [14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3]
        | (r % 10) == 2 = [11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4]
        | (r % 10) == 3 = [7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8]
        | (r % 10) == 4 = [9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13]
        | (r % 10) == 5 = [2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9]
        | (r % 10) == 6 = [12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11]
        | (r % 10) == 7 = [13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10]
        | (r % 10) == 8 = [6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5]
        | otherwise     = [10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0]

fn :: a2 b2b round Tensor[U64 * 32] Tensor[U64 * 32] Int => Tensor[U64 * 32]
      a2 b2b round v m r =
        s       = a2 b2b sigma r
        mut v0  = a2 w16 get v 0
        mut v1  = a2 w16 get v 1
        mut v2  = a2 w16 get v 2
        mut v3  = a2 w16 get v 3
        mut v4  = a2 w16 get v 4
        mut v5  = a2 w16 get v 5
        mut v6  = a2 w16 get v 6
        mut v7  = a2 w16 get v 7
        mut v8  = a2 w16 get v 8
        mut v9  = a2 w16 get v 9
        mut v10 = a2 w16 get v 10
        mut v11 = a2 w16 get v 11
        mut v12 = a2 w16 get v 12
        mut v13 = a2 w16 get v 13
        mut v14 = a2 w16 get v 14
        mut v15 = a2 w16 get v 15
        v0, v4, v8,  v12 = a2 b2b g4 v0 v4 v8  v12 (a2 w16 get m (a2 nth int s 0))  (a2 w16 get m (a2 nth int s 1))
        v1, v5, v9,  v13 = a2 b2b g4 v1 v5 v9  v13 (a2 w16 get m (a2 nth int s 2))  (a2 w16 get m (a2 nth int s 3))
        v2, v6, v10, v14 = a2 b2b g4 v2 v6 v10 v14 (a2 w16 get m (a2 nth int s 4))  (a2 w16 get m (a2 nth int s 5))
        v3, v7, v11, v15 = a2 b2b g4 v3 v7 v11 v15 (a2 w16 get m (a2 nth int s 6))  (a2 w16 get m (a2 nth int s 7))
        v0, v5, v10, v15 = a2 b2b g4 v0 v5 v10 v15 (a2 w16 get m (a2 nth int s 8))  (a2 w16 get m (a2 nth int s 9))
        v1, v6, v11, v12 = a2 b2b g4 v1 v6 v11 v12 (a2 w16 get m (a2 nth int s 10)) (a2 w16 get m (a2 nth int s 11))
        v2, v7, v8,  v13 = a2 b2b g4 v2 v7 v8  v13 (a2 w16 get m (a2 nth int s 12)) (a2 w16 get m (a2 nth int s 13))
        v3, v4, v9,  v14 = a2 b2b g4 v3 v4 v9  v14 (a2 w16 get m (a2 nth int s 14)) (a2 w16 get m (a2 nth int s 15))
        _ = a2 w16 set v 0  v0
        _ = a2 w16 set v 1  v1
        _ = a2 w16 set v 2  v2
        _ = a2 w16 set v 3  v3
        _ = a2 w16 set v 4  v4
        _ = a2 w16 set v 5  v5
        _ = a2 w16 set v 6  v6
        _ = a2 w16 set v 7  v7
        _ = a2 w16 set v 8  v8
        _ = a2 w16 set v 9  v9
        _ = a2 w16 set v 10 v10
        _ = a2 w16 set v 11 v11
        _ = a2 w16 set v 12 v12
        _ = a2 w16 set v 13 v13
        _ = a2 w16 set v 14 v14
        a2 w16 set v 15 v15

fn :: a2 b2b rounds Tensor[U64 * 32] Tensor[U64 * 32] Int => Tensor[U64 * 32]
      a2 b2b rounds v m r =
        | r >= 12   = v
        | otherwise = a2 b2b rounds (a2 b2b round v m r) m (r + 1)

fn :: a2 b2b compress Tensor[U64 * 32] Tensor[U64 * 32] Int Bool => Tensor[U64 * 32]
      a2 b2b compress h m t is_last =
        iv  = a2 b2b iv w16
        iv6 = a2 w16 get iv 6
        v14 =
          | is_last   = iv6 xor (not 0)
          | otherwise = iv6
        v0  = Tensor[U64 * 32]
        _   = a2 w16 set v0 0  (a2 w16 get h 0)
        _   = a2 w16 set v0 1  (a2 w16 get h 1)
        _   = a2 w16 set v0 2  (a2 w16 get h 2)
        _   = a2 w16 set v0 3  (a2 w16 get h 3)
        _   = a2 w16 set v0 4  (a2 w16 get h 4)
        _   = a2 w16 set v0 5  (a2 w16 get h 5)
        _   = a2 w16 set v0 6  (a2 w16 get h 6)
        _   = a2 w16 set v0 7  (a2 w16 get h 7)
        _   = a2 w16 set v0 8  (a2 w16 get iv 0)
        _   = a2 w16 set v0 9  (a2 w16 get iv 1)
        _   = a2 w16 set v0 10 (a2 w16 get iv 2)
        _   = a2 w16 set v0 11 (a2 w16 get iv 3)
        _   = a2 w16 set v0 12 ((a2 w16 get iv 4) xor (t and 0xFFFFFFFF))
        _   = a2 w16 set v0 13 (a2 w16 get iv 5)
        _   = a2 w16 set v0 14 v14
        _   = a2 w16 set v0 15 (a2 w16 get iv 7)
        vf  = a2 b2b rounds v0 m 0
        out = Tensor[U64 * 32]
        _   = a2 w16 set out 0 (((a2 w16 get h 0) xor (a2 w16 get vf 0)) xor (a2 w16 get vf 8))
        _   = a2 w16 set out 1 (((a2 w16 get h 1) xor (a2 w16 get vf 1)) xor (a2 w16 get vf 9))
        _   = a2 w16 set out 2 (((a2 w16 get h 2) xor (a2 w16 get vf 2)) xor (a2 w16 get vf 10))
        _   = a2 w16 set out 3 (((a2 w16 get h 3) xor (a2 w16 get vf 3)) xor (a2 w16 get vf 11))
        _   = a2 w16 set out 4 (((a2 w16 get h 4) xor (a2 w16 get vf 4)) xor (a2 w16 get vf 12))
        _   = a2 w16 set out 5 (((a2 w16 get h 5) xor (a2 w16 get vf 5)) xor (a2 w16 get vf 13))
        _   = a2 w16 set out 6 (((a2 w16 get h 6) xor (a2 w16 get vf 6)) xor (a2 w16 get vf 14))
        a2 w16 set out 7 (((a2 w16 get h 7) xor (a2 w16 get vf 7)) xor (a2 w16 get vf 15))

fn :: a2 bytes to u64 [Int] Int => U64
      a2 bytes to u64 bs off =
        a2 u64 lh (a2 bytes to u32 le bs off) (a2 bytes to u32 le bs (off + 4))

fn :: a2 bytes to w16 [Int] Int => Tensor[U64 * 32]
      a2 bytes to w16 bs off =
        w = Tensor[U64 * 32]
        _ = a2 w16 set w 0  (a2 bytes to u64 bs off)
        _ = a2 w16 set w 1  (a2 bytes to u64 bs (off + 8))
        _ = a2 w16 set w 2  (a2 bytes to u64 bs (off + 16))
        _ = a2 w16 set w 3  (a2 bytes to u64 bs (off + 24))
        _ = a2 w16 set w 4  (a2 bytes to u64 bs (off + 32))
        _ = a2 w16 set w 5  (a2 bytes to u64 bs (off + 40))
        _ = a2 w16 set w 6  (a2 bytes to u64 bs (off + 48))
        _ = a2 w16 set w 7  (a2 bytes to u64 bs (off + 56))
        _ = a2 w16 set w 8  (a2 bytes to u64 bs (off + 64))
        _ = a2 w16 set w 9  (a2 bytes to u64 bs (off + 72))
        _ = a2 w16 set w 10 (a2 bytes to u64 bs (off + 80))
        _ = a2 w16 set w 11 (a2 bytes to u64 bs (off + 88))
        _ = a2 w16 set w 12 (a2 bytes to u64 bs (off + 96))
        _ = a2 w16 set w 13 (a2 bytes to u64 bs (off + 104))
        _ = a2 w16 set w 14 (a2 bytes to u64 bs (off + 112))
        a2 w16 set w 15 (a2 bytes to u64 bs (off + 120))

fn :: a2 u64 to le8 U64 => [Int]
      a2 u64 to le8 x   =
        (a2 u32 to le4 (x and 0xFFFFFFFF)) :: (a2 u32 to le4 ((x shr 32) and 0xFFFFFFFF))

fn :: a2 h8 to bytes Tensor[U64 * 32] => [Int]
      a2 h8 to bytes h                =
        (a2 u64 to le8 (a2 w16 get h 0)) :: (a2 u64 to le8 (a2 w16 get h 1)) :: (a2 u64 to le8 (a2 w16 get h 2)) :: (a2 u64 to le8 (a2 w16 get h 3)) :: (a2 u64 to le8 (a2 w16 get h 4)) :: (a2 u64 to le8 (a2 w16 get h 5)) :: (a2 u64 to le8 (a2 w16 get h 6)) :: (a2 u64 to le8 (a2 w16 get h 7))

fn :: a2 b2b loop Tensor[U64 * 32] [Int] Int => Tensor[U64 * 32]
      a2 b2b loop h rem t =
        | (a2 list len rem) <= 128 =
          rlen   = a2 list len rem
          padded = rem :: (a2 pad zeroes (128 - rlen))
          a2 b2b compress h (a2 bytes to w16 padded 0) (t + rlen) True
        | otherwise =
          blk  = a2 take ints rem 128
          rest = a2 drop ints rem 128
          h1   = a2 b2b compress h (a2 bytes to w16 blk 0) (t + 128) False
          a2 b2b loop h1 rest (t + 128)

fn :: a2 blake2b [Int] Int    => [Int]
      a2 blake2b msg   outlen =
        h_in = a2 b2b iv w16
        _    = a2 w16 set h_in 0 ((a2 w16 get h_in 0) xor ((16842752 + outlen) and 0xFFFFFFFF))
        h_fi = a2 b2b loop h_in msg 0
        a2 take ints (a2 h8 to bytes h_fi) outlen

fn :: a2 b2b long loop [Int] Int Int Int => [Int]
      a2 b2b long loop v idx r outlen =
        | idx > r =
          rem = outlen - (32 * r)
          a2 blake2b v rem
        | otherwise =
          v_next = a2 blake2b v 64
          (a2 take ints v_next 32) :: (a2 b2b long loop v_next (idx + 1) r outlen)

fn :: a2 blake2b long [Int] Int    => [Int]
      a2 blake2b long data  outlen =
        | outlen <= 64 = a2 blake2b ((a2 u32 to le4 outlen) :: data) outlen
        | otherwise =
          r  = ((outlen + 31) / 32) - 2
          v1 = a2 blake2b ((a2 u32 to le4 outlen) :: data) 64
          (a2 take ints v1 32) :: (a2 b2b long loop v1 2 r outlen)

fn :: a2 blamka g idx Tensor[U64 * 256] Int Int Int Int => Int
      a2 blamka g idx z ia ib ic id =
        mut al = z_(ia)
        mut ah = z_(ia + 1)
        mut bl = z_(ib)
        mut bh = z_(ib + 1)
        mut cl = z_(ic)
        mut ch = z_(ic + 1)
        mut dl = z_(id)
        mut dh = z_(id + 1)
        mut t  = (al and 0x3FFFFFFF) * bl
        mut u  = ((al shr 30) * bl) + (t shr 30)
        t      = al + bl + ((t and 0x3FFFFFFF) shl 1) + ((u and 1) shl 31)
        al     = t and 0xFFFFFFFF
        ah     = (ah + bh + (u shr 1) + (t shr 32)) and 0xFFFFFFFF
        t      = dl xor al
        dl     = dh xor ah
        dh     = t
        t      = (cl and 0x3FFFFFFF) * dl
        u      = ((cl shr 30) * dl) + (t shr 30)
        t      = cl + dl + ((t and 0x3FFFFFFF) shl 1) + ((u and 1) shl 31)
        cl     = t and 0xFFFFFFFF
        ch     = (ch + dh + (u shr 1) + (t shr 32)) and 0xFFFFFFFF
        t      = bl xor cl
        u      = bh xor ch
        bl     = ((t shr 24) or (u shl 8)) and 0xFFFFFFFF
        bh     = ((u shr 24) or (t shl 8)) and 0xFFFFFFFF
        t      = (al and 0x3FFFFFFF) * bl
        u      = ((al shr 30) * bl) + (t shr 30)
        t      = al + bl + ((t and 0x3FFFFFFF) shl 1) + ((u and 1) shl 31)
        al     = t and 0xFFFFFFFF
        ah     = (ah + bh + (u shr 1) + (t shr 32)) and 0xFFFFFFFF
        t      = dl xor al
        u      = dh xor ah
        dl     = ((t shr 16) or (u shl 16)) and 0xFFFFFFFF
        dh     = ((u shr 16) or (t shl 16)) and 0xFFFFFFFF
        t      = (cl and 0x3FFFFFFF) * dl
        u      = ((cl shr 30) * dl) + (t shr 30)
        t      = cl + dl + ((t and 0x3FFFFFFF) shl 1) + ((u and 1) shl 31)
        cl     = t and 0xFFFFFFFF
        ch     = (ch + dh + (u shr 1) + (t shr 32)) and 0xFFFFFFFF
        t      = bl xor cl
        u      = bh xor ch
        bl     = ((t shl 1) or (u shr 31)) and 0xFFFFFFFF
        bh     = ((u shl 1) or (t shr 31)) and 0xFFFFFFFF
        z_(ia)     = al
        z_(ia + 1) = ah
        z_(ib)     = bl
        z_(ib + 1) = bh
        z_(ic)     = cl
        z_(ic + 1) = ch
        z_(id)     = dl
        z_(id + 1) = dh
        0

fn :: a2 blamka rows loop Tensor[U64 * 256] Int => Tensor[U64 * 256]
      a2 blamka rows loop z b =
        | b >= 256  = z
        | otherwise =
          mut k = a2 blamka g idx z b       (b + 8)  (b + 16) (b + 24)
          k     = a2 blamka g idx z (b + 2) (b + 10) (b + 18) (b + 26)
          k     = a2 blamka g idx z (b + 4) (b + 12) (b + 20) (b + 28)
          k     = a2 blamka g idx z (b + 6) (b + 14) (b + 22) (b + 30)
          k     = a2 blamka g idx z b       (b + 10) (b + 20) (b + 30)
          k     = a2 blamka g idx z (b + 2) (b + 12) (b + 22) (b + 24)
          k     = a2 blamka g idx z (b + 4) (b + 14) (b + 16) (b + 26)
          k     = a2 blamka g idx z (b + 6) (b + 8)  (b + 18) (b + 28)
          a2 blamka rows loop z (b + 32 + k)

fn :: a2 blamka cols loop Tensor[U64 * 256] Int => Tensor[U64 * 256]
      a2 blamka cols loop z c =
        | c >= 32   = z
        | otherwise =
          mut k = a2 blamka g idx z c        (c + 64) (c + 128) (c + 192)
          k     = a2 blamka g idx z (c + 2)  (c + 66) (c + 130) (c + 194)
          k     = a2 blamka g idx z (c + 32) (c + 96) (c + 160) (c + 224)
          k     = a2 blamka g idx z (c + 34) (c + 98) (c + 162) (c + 226)
          k     = a2 blamka g idx z c        (c + 66) (c + 160) (c + 226)
          k     = a2 blamka g idx z (c + 2)  (c + 96) (c + 162) (c + 192)
          k     = a2 blamka g idx z (c + 32) (c + 98) (c + 128) (c + 194)
          k     = a2 blamka g idx z (c + 34) (c + 64) (c + 130) (c + 224)
          a2 blamka cols loop z (c + 4 + k)

fn :: a2 bitxor blk8 Tensor[U64 * 256] Tensor[U64 * 256] => Tensor[U64 * 256]
      a2 bitxor blk8 a b = a_ij xor b_ij

fn :: a2 compress blk8 Tensor[U64 * 256] Tensor[U64 * 256] => Tensor[U64 * 256]
      a2 compress blk8 x y =
        r_ij = x_ij xor y_ij
        z_ij = r_ij
        z1   = a2 blamka rows loop z 0
        z2   = a2 blamka cols loop z1 0
        z2_ij xor r_ij

fn :: a2 compress blk8 bxor Tensor[U64 * 256] Tensor[U64 * 256] Tensor[U64 * 256] => Tensor[U64 * 256]
      a2 compress blk8 bxor x y old =
        r_ij  = x_ij xor y_ij
        ro_ij = r_ij xor old_ij
        z1    = a2 blamka rows loop r 0
        z2    = a2 blamka cols loop z1 0
        z2_ij xor ro_ij

fn :: a2 bytes to blk8 loop Tensor[U64 * 256] [Int] Int => Tensor[U64 * 256]
      a2 bytes to blk8 loop blk bs c =
        | c >= 128  = blk
        | otherwise =
          blk_(2 * c)       = a2 bytes to u32 le bs (c * 8)
          blk_((2 * c) + 1) = a2 bytes to u32 le bs ((c * 8) + 4)
          a2 bytes to blk8 loop blk bs (c + 1)

fn :: a2 bytes to blk8 [Int] => Tensor[U64 * 256]
      a2 bytes to blk8 bs    =
        a2 bytes to blk8 loop (Tensor[U64 * 256]) bs 0

fn :: a2 blk8 to bytes loop Tensor[U64 * 256] Int => [Int]
      a2 blk8 to bytes loop b c =
        | c >= 128  = []
        | otherwise = (a2 u32 to le4 b_(2 * c)) :: (a2 u32 to le4 b_((2 * c) + 1)) :: (a2 blk8 to bytes loop b (c + 1))

fn :: a2 blk8 to bytes Tensor[U64 * 256] => [Int]
      a2 blk8 to bytes b                 =
        a2 blk8 to bytes loop b 0

fn :: a2 zero mem => A2Mem
      a2 zero mem =
        zb = a2 zero blk8
        m1 = A2M16 zb zb zb zb zb zb zb zb zb zb zb zb zb zb zb zb
        m2 = A2M256 m1 m1 m1 m1 m1 m1 m1 m1 m1 m1 m1 m1 m1 m1 m1 m1
        m3 = A2M4096 m2 m2 m2 m2 m2 m2 m2 m2 m2 m2 m2 m2 m2 m2 m2 m2
        A2Mem m3 m3 m3 m3 m3 m3 m3 m3 m3 m3 m3 m3 m3 m3 m3 m3

fn :: a2 get m16 A2M16 Int => Tensor[U64 * 256]
      a2 get m16 m     i   =
        | i < 8 =
          | i < 4 =
            | i == 0    = mb0 m
            | i == 1    = mb1 m
            | i == 2    = mb2 m
            | otherwise = mb3 m |.
          | otherwise =
            | i == 4    = mb4 m
            | i == 5    = mb5 m
            | i == 6    = mb6 m
            | otherwise = mb7 m |.
        | otherwise =
          | i < 12 =
            | i == 8    = mb8 m
            | i == 9    = mb9 m
            | i == 10   = mb10 m
            | otherwise = mb11 m |.
          | otherwise =
            | i == 12   = mb12 m
            | i == 13   = mb13 m
            | i == 14   = mb14 m
            | otherwise = mb15 m

fn :: a2 get m256 A2M256 Int Int => Tensor[U64 * 256]
      a2 get m256 m i1 i0 =
        | i1 < 8 =
          | i1 < 4 =
            | i1 == 0   = a2 get m16 (mr0 m) i0
            | i1 == 1   = a2 get m16 (mr1 m) i0
            | i1 == 2   = a2 get m16 (mr2 m) i0
            | otherwise = a2 get m16 (mr3 m) i0 |.
          | otherwise =
            | i1 == 4   = a2 get m16 (mr4 m) i0
            | i1 == 5   = a2 get m16 (mr5 m) i0
            | i1 == 6   = a2 get m16 (mr6 m) i0
            | otherwise = a2 get m16 (mr7 m) i0 |.
        | otherwise =
          | i1 < 12 =
            | i1 == 8   = a2 get m16 (mr8 m) i0
            | i1 == 9   = a2 get m16 (mr9 m) i0
            | i1 == 10  = a2 get m16 (mr10 m) i0
            | otherwise = a2 get m16 (mr11 m) i0 |.
          | otherwise =
            | i1 == 12  = a2 get m16 (mr12 m) i0
            | i1 == 13  = a2 get m16 (mr13 m) i0
            | i1 == 14  = a2 get m16 (mr14 m) i0
            | otherwise = a2 get m16 (mr15 m) i0

fn :: a2 get m4096 A2M4096 Int Int Int => Tensor[U64 * 256]
      a2 get m4096 m i2 i1 i0 =
        | i2 < 8 =
          | i2 < 4 =
            | i2 == 0   = a2 get m256 (mp0 m) i1 i0
            | i2 == 1   = a2 get m256 (mp1 m) i1 i0
            | i2 == 2   = a2 get m256 (mp2 m) i1 i0
            | otherwise = a2 get m256 (mp3 m) i1 i0 |.
          | otherwise =
            | i2 == 4   = a2 get m256 (mp4 m) i1 i0
            | i2 == 5   = a2 get m256 (mp5 m) i1 i0
            | i2 == 6   = a2 get m256 (mp6 m) i1 i0
            | otherwise = a2 get m256 (mp7 m) i1 i0 |.
        | otherwise =
          | i2 < 12 =
            | i2 == 8   = a2 get m256 (mp8 m) i1 i0
            | i2 == 9   = a2 get m256 (mp9 m) i1 i0
            | i2 == 10  = a2 get m256 (mp10 m) i1 i0
            | otherwise = a2 get m256 (mp11 m) i1 i0 |.
          | otherwise =
            | i2 == 12  = a2 get m256 (mp12 m) i1 i0
            | i2 == 13  = a2 get m256 (mp13 m) i1 i0
            | i2 == 14  = a2 get m256 (mp14 m) i1 i0
            | otherwise = a2 get m256 (mp15 m) i1 i0

fn :: a2 get mem A2Mem Int Int Int Int => Tensor[U64 * 256]
      a2 get mem m i3 i2 i1 i0 =
        | i3 < 8 =
          | i3 < 4 =
            | i3 == 0   = a2 get m4096 (mt0 m) i2 i1 i0
            | i3 == 1   = a2 get m4096 (mt1 m) i2 i1 i0
            | i3 == 2   = a2 get m4096 (mt2 m) i2 i1 i0
            | otherwise = a2 get m4096 (mt3 m) i2 i1 i0 |.
          | otherwise =
            | i3 == 4   = a2 get m4096 (mt4 m) i2 i1 i0
            | i3 == 5   = a2 get m4096 (mt5 m) i2 i1 i0
            | i3 == 6   = a2 get m4096 (mt6 m) i2 i1 i0
            | otherwise = a2 get m4096 (mt7 m) i2 i1 i0 |.
        | otherwise =
          | i3 < 12 =
            | i3 == 8   = a2 get m4096 (mt8 m) i2 i1 i0
            | i3 == 9   = a2 get m4096 (mt9 m) i2 i1 i0
            | i3 == 10  = a2 get m4096 (mt10 m) i2 i1 i0
            | otherwise = a2 get m4096 (mt11 m) i2 i1 i0 |.
          | otherwise =
            | i3 == 12  = a2 get m4096 (mt12 m) i2 i1 i0
            | i3 == 13  = a2 get m4096 (mt13 m) i2 i1 i0
            | i3 == 14  = a2 get m4096 (mt14 m) i2 i1 i0
            | otherwise = a2 get m4096 (mt15 m) i2 i1 i0

fn :: a2 nth blk A2Mem Int => Tensor[U64 * 256]
      a2 nth blk m     idx =
        a2 get mem m ((idx shr 12) and 15) ((idx shr 8) and 15) ((idx shr 4) and 15) (idx and 15)

fn :: a2 set m16 A2M16 Int Tensor[U64 * 256] => A2M16
      a2 set m16 m i val =
        | i < 8 =
          | i < 4 =
            | i == 0    = m with mb0 = val
            | i == 1    = m with mb1 = val
            | i == 2    = m with mb2 = val
            | otherwise = m with mb3 = val |.
          | otherwise =
            | i == 4    = m with mb4 = val
            | i == 5    = m with mb5 = val
            | i == 6    = m with mb6 = val
            | otherwise = m with mb7 = val |.
        | otherwise =
          | i < 12 =
            | i == 8    = m with mb8 = val
            | i == 9    = m with mb9 = val
            | i == 10   = m with mb10 = val
            | otherwise = m with mb11 = val |.
          | otherwise =
            | i == 12   = m with mb12 = val
            | i == 13   = m with mb13 = val
            | i == 14   = m with mb14 = val
            | otherwise = m with mb15 = val

fn :: a2 set m256 A2M256 Int Int Tensor[U64 * 256] => A2M256
      a2 set m256 m i1 i0 val =
        | i1 < 8 =
          | i1 < 4 =
            | i1 == 0   = m with mr0 = (a2 set m16 (mr0 m) i0 val)
            | i1 == 1   = m with mr1 = (a2 set m16 (mr1 m) i0 val)
            | i1 == 2   = m with mr2 = (a2 set m16 (mr2 m) i0 val)
            | otherwise = m with mr3 = (a2 set m16 (mr3 m) i0 val) |.
          | otherwise =
            | i1 == 4   = m with mr4 = (a2 set m16 (mr4 m) i0 val)
            | i1 == 5   = m with mr5 = (a2 set m16 (mr5 m) i0 val)
            | i1 == 6   = m with mr6 = (a2 set m16 (mr6 m) i0 val)
            | otherwise = m with mr7 = (a2 set m16 (mr7 m) i0 val) |.
        | otherwise =
          | i1 < 12 =
            | i1 == 8   = m with mr8 = (a2 set m16 (mr8 m) i0 val)
            | i1 == 9   = m with mr9 = (a2 set m16 (mr9 m) i0 val)
            | i1 == 10  = m with mr10 = (a2 set m16 (mr10 m) i0 val)
            | otherwise = m with mr11 = (a2 set m16 (mr11 m) i0 val) |.
          | otherwise =
            | i1 == 12  = m with mr12 = (a2 set m16 (mr12 m) i0 val)
            | i1 == 13  = m with mr13 = (a2 set m16 (mr13 m) i0 val)
            | i1 == 14  = m with mr14 = (a2 set m16 (mr14 m) i0 val)
            | otherwise = m with mr15 = (a2 set m16 (mr15 m) i0 val)

fn :: a2 set m4096 A2M4096 Int Int Int Tensor[U64 * 256] => A2M4096
      a2 set m4096 m i2 i1 i0 val =
        | i2 < 8 =
          | i2 < 4 =
            | i2 == 0   = m with mp0 = (a2 set m256 (mp0 m) i1 i0 val)
            | i2 == 1   = m with mp1 = (a2 set m256 (mp1 m) i1 i0 val)
            | i2 == 2   = m with mp2 = (a2 set m256 (mp2 m) i1 i0 val)
            | otherwise = m with mp3 = (a2 set m256 (mp3 m) i1 i0 val) |.
          | otherwise =
            | i2 == 4   = m with mp4 = (a2 set m256 (mp4 m) i1 i0 val)
            | i2 == 5   = m with mp5 = (a2 set m256 (mp5 m) i1 i0 val)
            | i2 == 6   = m with mp6 = (a2 set m256 (mp6 m) i1 i0 val)
            | otherwise = m with mp7 = (a2 set m256 (mp7 m) i1 i0 val) |.
        | otherwise =
          | i2 < 12 =
            | i2 == 8   = m with mp8 = (a2 set m256 (mp8 m) i1 i0 val)
            | i2 == 9   = m with mp9 = (a2 set m256 (mp9 m) i1 i0 val)
            | i2 == 10  = m with mp10 = (a2 set m256 (mp10 m) i1 i0 val)
            | otherwise = m with mp11 = (a2 set m256 (mp11 m) i1 i0 val) |.
          | otherwise =
            | i2 == 12  = m with mp12 = (a2 set m256 (mp12 m) i1 i0 val)
            | i2 == 13  = m with mp13 = (a2 set m256 (mp13 m) i1 i0 val)
            | i2 == 14  = m with mp14 = (a2 set m256 (mp14 m) i1 i0 val)
            | otherwise = m with mp15 = (a2 set m256 (mp15 m) i1 i0 val)

fn :: a2 set mem A2Mem Int Int Int Int Tensor[U64 * 256] => A2Mem
      a2 set mem m i3 i2 i1 i0 val =
        | i3 < 8 =
          | i3 < 4 =
            | i3 == 0   = m with mt0 = (a2 set m4096 (mt0 m) i2 i1 i0 val)
            | i3 == 1   = m with mt1 = (a2 set m4096 (mt1 m) i2 i1 i0 val)
            | i3 == 2   = m with mt2 = (a2 set m4096 (mt2 m) i2 i1 i0 val)
            | otherwise = m with mt3 = (a2 set m4096 (mt3 m) i2 i1 i0 val) |.
          | otherwise =
            | i3 == 4   = m with mt4 = (a2 set m4096 (mt4 m) i2 i1 i0 val)
            | i3 == 5   = m with mt5 = (a2 set m4096 (mt5 m) i2 i1 i0 val)
            | i3 == 6   = m with mt6 = (a2 set m4096 (mt6 m) i2 i1 i0 val)
            | otherwise = m with mt7 = (a2 set m4096 (mt7 m) i2 i1 i0 val) |.
        | otherwise =
          | i3 < 12 =
            | i3 == 8   = m with mt8 = (a2 set m4096 (mt8 m) i2 i1 i0 val)
            | i3 == 9   = m with mt9 = (a2 set m4096 (mt9 m) i2 i1 i0 val)
            | i3 == 10  = m with mt10 = (a2 set m4096 (mt10 m) i2 i1 i0 val)
            | otherwise = m with mt11 = (a2 set m4096 (mt11 m) i2 i1 i0 val) |.
          | otherwise =
            | i3 == 12  = m with mt12 = (a2 set m4096 (mt12 m) i2 i1 i0 val)
            | i3 == 13  = m with mt13 = (a2 set m4096 (mt13 m) i2 i1 i0 val)
            | i3 == 14  = m with mt14 = (a2 set m4096 (mt14 m) i2 i1 i0 val)
            | otherwise = m with mt15 = (a2 set m4096 (mt15 m) i2 i1 i0 val)

33.4.2. Intern implementation: Core Implementation Part 9

Se hur argon2id.rg deklarerar explicita fn :: ... => ...-kontrakt först och löser varje fall genom linjerade ekvationer och svansanrop:

fn :: a2 blamka g U64 U64 U64 U64 => U64 U64 U64 U64
      a2 blamka g a b c d =
        mut a = a2 f blamka a b
        mut d = (d xor a) rotr 32
        mut c = a2 f blamka c d
        mut b = (b xor c) rotr 24
        a = a2 f blamka a b
        d = (d xor a) rotr 16
        c = a2 f blamka c d
        b = (b xor c) rotr 63
        (a, b, c, d)

fn :: a2 blamka p16 Tensor[U64 * 16] => ()
      a2 blamka p16 v                =
        v_(0), v_(4), v_(8),  v_(12) = a2 blamka g v_(0) v_(4) v_(8)  v_(12)
        v_(1), v_(5), v_(9),  v_(13) = a2 blamka g v_(1) v_(5) v_(9)  v_(13)
        v_(2), v_(6), v_(10), v_(14) = a2 blamka g v_(2) v_(6) v_(10) v_(14)
        v_(3), v_(7), v_(11), v_(15) = a2 blamka g v_(3) v_(7) v_(11) v_(15)
        v_(0), v_(5), v_(10), v_(15) = a2 blamka g v_(0) v_(5) v_(10) v_(15)
        v_(1), v_(6), v_(11), v_(12) = a2 blamka g v_(1) v_(6) v_(11) v_(12)
        v_(2), v_(7), v_(8),  v_(13) = a2 blamka g v_(2) v_(7) v_(8)  v_(13)
        v_(3), v_(4), v_(9),  v_(14) = a2 blamka g v_(3) v_(4) v_(9)  v_(14)

fn :: a2 blamka col pair Tensor[U64 * 8 * 16] Int => Tensor[U64 * 8 * 16]
      a2 blamka col pair z j =
        c  = 2 * j
        c1 = c + 1
        z_(0, c),  z_(2, c),  z_(4, c),  z_(6, c)  = a2 blamka g z_(0, c)  z_(2, c)  z_(4, c)  z_(6, c)
        z_(0, c1), z_(2, c1), z_(4, c1), z_(6, c1) = a2 blamka g z_(0, c1) z_(2, c1) z_(4, c1) z_(6, c1)
        z_(1, c),  z_(3, c),  z_(5, c),  z_(7, c)  = a2 blamka g z_(1, c)  z_(3, c)  z_(5, c)  z_(7, c)
        z_(1, c1), z_(3, c1), z_(5, c1), z_(7, c1) = a2 blamka g z_(1, c1) z_(3, c1) z_(5, c1) z_(7, c1)
        z_(0, c),  z_(2, c1), z_(5, c),  z_(7, c1) = a2 blamka g z_(0, c)  z_(2, c1) z_(5, c)  z_(7, c1)
        z_(0, c1), z_(3, c),  z_(5, c1), z_(6, c)  = a2 blamka g z_(0, c1) z_(3, c)  z_(5, c1) z_(6, c)
        z_(1, c),  z_(3, c1), z_(4, c),  z_(6, c1) = a2 blamka g z_(1, c)  z_(3, c1) z_(4, c)  z_(6, c1)
        z_(1, c1), z_(2, c),  z_(4, c1), z_(7, c)  = a2 blamka g z_(1, c1) z_(2, c)  z_(4, c1) z_(7, c)
        z

33.4.3. Intern implementation: Core Implementation Part 18

Se hur argon2id.rg deklarerar explicita fn :: ... => ...-kontrakt först och löser varje fall genom linjerade ekvationer och svansanrop:

fn :: derive raw tag ctx [Int] [Int] String String Argon2Params => [Int]
      derive raw tag ctx pwd_raw salt_bytes secret ad params =
        sec_b = a2 str to bytes secret
        ad_b  = a2 str to bytes ad
        m     = m_cost params
        t     = t_cost params
        p     = lanes params
        tlen  = tag_len params
        h0_in = (a2 u32 to le4 p) :: (a2 u32 to le4 tlen) :: (a2 u32 to le4 m) :: (a2 u32 to le4 t) :: (a2 u32 to le4 19) :: (a2 u32 to le4 2) :: (a2 u32 to le4 (a2 list len pwd_raw)) :: pwd_raw :: (a2 u32 to le4 (a2 list len salt_bytes)) :: salt_bytes :: (a2 u32 to le4 (a2 list len sec_b)) :: sec_b :: (a2 u32 to le4 (a2 list len ad_b)) :: ad_b
        h0    = a2 blake2b h0_in 64
        sl    = m / (4 * p)
        q     = sl * 4
        mem0  = a2 init all lanes h0 0 p q
        ctx   = A2Ctx 0 0 0 p q sl t
        memf  = a2 run passes mem0 ctx 0
        c0    = a2 nth blk memf (q - 1)
        c_fin = a2 bitxor final lanes memf 1 p q c0
        a2 blake2b long (a2 blk8 to bytes c_fin) tlen

fn :: derive raw tag [Int] [Int] Argon2Params => [Int]
      derive raw tag pwd_raw salt_bytes params = derive raw tag ctx pwd_raw salt_bytes "" "" params

-- Unpadded Base64 Encoding & Decoding (PHC Specification)

fn :: b64 alphabet => String
      b64 alphabet =  "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"

fn :: b64 char Int => String
      b64 char idx =  str slice (b64 alphabet) (idx % 64) ((idx % 64) + 1)

fn :: b64 encode nopad [Int]           => String
      b64 encode nopad []              =  ""
      b64 encode nopad (b0:[])         =
        c0 = b0 / 4
        c1 = (b0 % 4) * 16
        (b64 char c0) + (b64 char c1)
      b64 encode nopad (b0:b1:[])      =
        c0 = b0 / 4
        c1 = ((b0 % 4) * 16) + (b1 / 16)
        c2 = (b1 % 16) * 4
        (b64 char c0) + (b64 char c1) + (b64 char c2)
      b64 encode nopad (b0:b1:b2:rest) =
        c0 = b0 / 4
        c1 = ((b0 % 4) * 16) + (b1 / 16)
        c2 = ((b1 % 16) * 4) + (b2 / 64)
        c3 = b2 % 64
        (b64 char c0) + (b64 char c1) + (b64 char c2) + (b64 char c3) + (b64 encode nopad rest)

33.4.4. Intern implementation: Core Implementation Part 26

Se hur argon2id.rg deklarerar explicita fn :: ... => ...-kontrakt först och löser varje fall genom linjerade ekvationer och svansanrop:

fn :: argon2id needs rehash String Argon2Params => Bool
      argon2id needs rehash phc    target       =  check rehash parsed (argon2id parse phc phc) target

fn :: verify parsed ctx Argon2PhcRes String String String => Bool
      verify parsed ctx (Argon2PhcErr _) _ _ _ = False
      verify parsed ctx (Argon2PhcOk parsed) pwd secret ad =
        salt_bytes = b64 decode nopad (salt_b64 parsed)
        tag_bytes  = b64 decode nopad (tag_b64 parsed)
        computed   = derive raw tag ctx (a2 str to bytes pwd) salt_bytes secret ad (params parsed)
        constant time bytes eq computed tag_bytes

fn :: argon2id verify ctx String String String String => Bool
      argon2id verify ctx pwd phc secret ad = verify parsed ctx (argon2id parse phc phc) pwd secret ad

fn :: argon2id verify String String => Bool
      argon2id verify pwd    phc    =
        | not (str starts with phc "$argon2id$") = False
        | otherwise                              = argon2id verify ctx pwd phc "" ""

fn :: unwrap phc res Argon2Res      => String
      unwrap phc res (Argon2Ok phc) =  phc
      unwrap phc res (Argon2Err _)  =  ""

fn :: argon2id hash String String Int Int Int => String
      argon2id hash pwd salt m_kib t_iters p_lanes =
        unwrap phc res (argon2id hash params pwd salt (Argon2Params m_kib t_iters p_lanes 32 19))

fn :: argon2id hash phc String String Int Int Int => String
      argon2id hash phc pwd salt m_kib t_iters p_lanes =
        argon2id hash pwd salt m_kib t_iters p_lanes

33.5. Algoritmiska garantier, RMM1/RMM2-minne och de 5 Gyllene Reglerna för argon2id

  • Algoritmisk garanti och säkerhet: Byte-för-byte-överensstämmelse med de officiella testvektorerna i RFC 9106 §5.3 och OpenSSL 3.5, direkt över Tensor[U64 * 8 * 16] utan en enda rad C.
  • $O(1)$ stackkomplexitet med TCO: Alla rekursiva loopar i src/main/ridgun/lib/argon2id.rg ligger i svansposition, så ridgunc kompilerar dem till inbyggda hopp utan stacktillväxt.
  • Deterministiskt minne (RMM1/RMM2): Noll skräpsamlarpauser; alla temporära strukturer frigörs deterministiskt när räckvidden lämnas.
  • Kontinuerlig verifiering: Statiskt kontrollerad med stable check och verifierad av den automatiserade testsviten src/test/ridgun/test argon2id.rg.

De 5 Gyllene Reglerna när du programmerar med argon2id

  1. Deklarera import argon2id överst i filen och utnyttja att mellanslag och understreck är utbytbara så att koden läses som naturlig prosa.
  2. Linjera dina datapipelines med =: kedja samman argon2id-anrop i namngivna mellansteg och låt stable format linjera kolumnerna.
  3. Utnyttja argon2id:s totala funktioner i dina |-vakter: kontrollera returvärden eller packa upp ADT:er med mönstermatchning i stället för att anta att extern indata alltid är giltig.
  4. Undvik att kopiera stora strukturer när du bara läser ett fält: kom ihåg att i RMM2 medför inargument noll referensräkningskostnad (+0 / -0).
  5. Kombinera argon2id med test (import test): lås alltid fast dina domäninvarianter med assert equals eller assert true.

I Kapitel 34 går vi vidare och utforskar lib:en jwt på djupet (Lib:en jwt — RFC 7519 HS256 JSON Web Tokens och ChaCha20-Poly1305-krypterade Tokens).