扩展库 / argon2

argon2

加密安全
v2.0.0 C 混编 0 依赖 ABI 1

Argon2 密码哈希(RFC 9106)— 2015 年密码哈希竞赛(PHC)冠军算法,具备内存硬特性,有效抵抗 GPU/ASIC 暴力破解。C 层实现 Blake2b + Argon2 完整核心,xlang 层提供安全、易用的高层 API,提供当前包公开的密码哈希与 PHC 验证能力。

当前合同与验证

2026-09-26 原生 ABI 与错误边界更新。公开 C 声明为 include/argon2_native.h, 专项门禁为 tools/check_extlib_argon2_bloom.ps1,执行记录绑定源码和宿主哈希。

典型场景

用户密码存储 会话令牌派生 密钥拉伸 参数升级 防篡改绑定 凭证迁移

快速开始

quick_start.xl
import argon2

func main(...) as int
    // 注册:一行哈希(自动生成 CSPRNG 随机盐)
    string stored = argon2.hash("user_password")

    // 登录:一行验证
    if argon2.verify("user_password", stored)
        println("登录成功")
    end
    return 0
end

核心特性

包信息

包名argon2
版本2.0.0
实现方式C 混编(Blake2b + Argon2 核心 C,API 层 xlang)
依赖无
ABI 版本1
导出符号hash, hashRaw, verify, checkNeedsRehash, extract*, generateSalt, recommended*
源文件extlibs/argon2/src/argon2.xl
核心函数
export func hash(string password, string salt = "", string secret = "", string ad = "", int timeCost = 3, int memoryCost = 65536, int parallelism = 4, int hashLen = 32, int saltLen = 16, int type = ARGON2ID) as string

计算密码哈希,返回 PHC 编码字符串。salt 为空时自动生成 saltLen 字节的 CSPRNG 随机盐。

输出格式:$argon2id$v=19$m=65536,t=3,p=4$<salt_b64>$<hash_b64>,包含算法、成本、盐和哈希;secret/ad 不在其中。
export func hashRaw(string password, ...) as string

参数与 hash 完全相同,返回原始哈希的 hex 字符串(不含元数据)。适用于自行管理存储格式的场景。

export func verify(string password, string encoded) as bool

验证密码是否匹配 PHC 编码字符串。自动解析编码中的算法变体、全部参数、盐和哈希值,重新计算后以恒定时间比较。

当前 verify 缺少 secret/ad 参数,无法验证使用这些附加输入的结果。这是待补能力,不是完整的 pepper 认证接口;不要用普通字符串比较替代验证。
export func checkNeedsRehash(string encoded, int timeCost = 3, int memoryCost = 65536, int parallelism = 4) as bool

判断已有哈希的参数是否与期望值不同。参数不完全匹配即返回 true。典型用法:用户登录成功后检查,若需升级则重新 hash 并更新存储。

参数提取
export func extractType(string encoded) as int export func extractTimeCost(string encoded) as int export func extractMemoryCost(string encoded) as int export func extractParallelism(string encoded) as int

从 PHC 编码中提取各项参数,无需手动解码 base64。适用于安全审计与批量迁移场景。解析失败返回 -1。

盐生成
export func generateSalt(int len = 16) as string

生成 CSPRNG 随机盐,返回 hex 字符串(len 字节 → len*2 个 hex 字符)。范围 8-64 字节。

推荐参数
export func recommendedTime() as int // 3 export func recommendedMemory() as int // 65536 (64 MiB) export func recommendedParallelism() as int // 4 export func recommendedHashLen() as int // 32 export func recommendedSaltLen() as int // 16

返回当前推荐的安全参数,作为 checkNeedsRehash 的默认对比基准。

常量
export const int ARGON2D = 0 // 数据依赖寻址 export const int ARGON2I = 1 // 数据无关寻址,抗侧信道 export const int ARGON2ID = 2 // 混合(推荐,默认值)

变体选择常量,传入 hash 的 type 参数。

export const int PROFILE_LOW_MEMORY_TIME = 3 // RFC 9106 §4 低内存 export const int PROFILE_LOW_MEMORY_MEMORY = 65536 // ~64 MiB export const int PROFILE_LOW_MEMORY_PARALLELISM = 4 export const int PROFILE_HIGH_MEMORY_TIME = 1 // RFC 9106 §4 高内存 export const int PROFILE_HIGH_MEMORY_MEMORY = 2097152 // 2 GiB export const int PROFILE_HIGH_MEMORY_PARALLELISM = 4

RFC 9106 预定义参数集,直接传入 hash 对应参数即可使用。

教学范例

以下范例逐步展示全部 API,从最简用法到综合实战。完整源文件:examples/extlibs/argon2.xl

1. 最简用法:一行哈希,一行验证

自动盐 + verify
import argon2

// hash() 自动生成 CSPRNG 随机盐,返回 PHC 标准编码字符串
string encoded = argon2.hash("my_secure_password")
println("哈希: ", encoded)
// 输出: $argon2id$v=19$m=65536,t=3,p=4$<salt_b64>$<hash_b64>

// verify() 自动解析编码中的算法、参数、盐,重新计算并比较
println("正确密码: ", argon2.verify("my_secure_password", encoded))  // true
println("错误密码: ", argon2.verify("wrong_password", encoded))       // false

// 每次 hash() 调用产生不同盐 → 不同编码,但都能验证通过
string encoded2 = argon2.hash("my_secure_password")
println("两次哈希不同: ", encoded != encoded2)  // true

2. 手动盐(确定性输出)

固定盐
// 传入固定盐 → 相同输入产生相同输出(用于测试/互操作)
string salt = "my_fixed_salt_16"  // 至少 8 字节
string h1 = argon2.hash("password", salt = salt)
string h2 = argon2.hash("password", salt = salt)
println("确定性: ", h1 == h2)  // true

// generateSalt() 生成随机盐的 hex 字符串
string randomSalt = argon2.generateSalt(16)
println("随机盐: ", randomSalt)  // 32 个 hex 字符

3. 参数调节

timeCost / memoryCost / parallelism
// timeCost: 迭代次数(越大越慢,抗暴力破解)
// memoryCost: 内存消耗 KiB(越大越抗 GPU/ASIC)
// parallelism: 并行度(影响哈希值,不只是性能)
string custom = argon2.hash("pw",
    timeCost = 5,          // 5 次迭代
    memoryCost = 131072,   // 128 MiB
    parallelism = 2,       // 2 路并行
    hashLen = 64,          // 64 字节输出
    saltLen = 32           // 32 字节盐
)

// 验证时不需要传参数 — 全部从 PHC 编码中自动解析
println("验证: ", argon2.verify("pw", custom))  // true

4. 三种变体

Argon2d / Argon2i / Argon2id
// Argon2d: 数据依赖寻址
// Argon2i: 数据无关寻址
// Argon2id: 混合(前半 i + 后半 d),RFC 推荐,默认值
string hd  = argon2.hash("test", salt = salt, type = argon2.ARGON2D)
string hi  = argon2.hash("test", salt = salt, type = argon2.ARGON2I)
string hid = argon2.hash("test", salt = salt, type = argon2.ARGON2ID)

// 所有变体都能正确验证(verify 自动识别类型)
println("验证 d: ", argon2.verify("test", hd))   // true
println("验证 i: ", argon2.verify("test", hi))   // true
println("验证 id: ", argon2.verify("test", hid)) // true

5. secret 密钥哈希(pepper)

附加输入演示
// secret 不写入 PHC 编码
// 此处仅演示附加输入,不是当前 verify 可完成的认证流程
string serverKey = "my_server_secret_key_2024"
string withKey = argon2.hash("user_password", secret = serverKey)

// 注意:verify() 不含 secret 参数
// 当前验证 API 缺少 secret/ad 参数,不能用普通字符串比较自行补认证逻辑

6. ad 附加数据(绑定上下文)

附加数据演示
// ad 将哈希绑定到特定上下文(如用户ID、租户ID)
// ad 不写入 PHC;当前 verify 不能验证此结果
string withAd = argon2.hash("password", ad = "tenant=acme;user=42")

7. hashRaw:原始 hex 输出

自行管理存储格式
// 不生成 PHC 编码,直接返回 hex 字符串
string raw = argon2.hashRaw("password", salt = salt, hashLen = 32)
println("Raw hex: ", raw)       // 64 个 hex 字符
println("长度: ", raw.length)   // 64(32字节 × 2)

8. checkNeedsRehash:参数升级检测

安全策略升级
// 场景:系统升级后提高了安全参数
string oldHash = argon2.hash("pw", salt = salt,
    timeCost = 1, memoryCost = 4096, parallelism = 1)

// 对比当前推荐参数
bool needsUpgrade = argon2.checkNeedsRehash(oldHash,
    timeCost = 3, memoryCost = 65536, parallelism = 4)
println("需要升级: ", needsUpgrade)  // true

// 用新参数重新哈希后,不再需要升级
string newHash = argon2.hash("pw", timeCost = 3, memoryCost = 65536, parallelism = 4)
println("升级后: ", argon2.checkNeedsRehash(newHash,
    timeCost = 3, memoryCost = 65536, parallelism = 4))  // false

9. 参数提取(审计/迁移)

从 PHC 编码读取参数
string sample = argon2.hash("x", salt = salt,
    timeCost = 5, memoryCost = 1024, parallelism = 2)

println("类型: ", argon2.extractType(sample))         // 2 (ARGON2ID)
println("迭代: ", argon2.extractTimeCost(sample))     // 5
println("内存: ", argon2.extractMemoryCost(sample))   // 1024
println("并行: ", argon2.extractParallelism(sample))  // 2

10. 推荐参数与 Profiles

RFC 9106 预定义参数集
println("推荐迭代: ", argon2.recommendedTime())          // 3
println("推荐内存: ", argon2.recommendedMemory())        // 65536 KiB
println("推荐并行: ", argon2.recommendedParallelism())   // 4

// 使用 RFC 9106 低内存 Profile
string profiled = argon2.hash("pw",
    timeCost = argon2.PROFILE_LOW_MEMORY_TIME,
    memoryCost = argon2.PROFILE_LOW_MEMORY_MEMORY,
    parallelism = argon2.PROFILE_LOW_MEMORY_PARALLELISM)

11. 综合实战:用户注册/登录流程

完整业务流程
// ── 注册 ──
string username = "alice"
string password = "S3cur3!Pass#2024"

// 一行完成:自动盐 + 推荐参数 + PHC 编码
string stored = argon2.hash(password)

// ── 登录 ──
string inputPassword = "S3cur3!Pass#2024"
assert argon2.verify(inputPassword, stored), "registration/login must accept the correct password"
assert not argon2.verify("hacker_guess", stored), "registration/login must reject the wrong password"

if argon2.verify(inputPassword, stored) then
    println("登录成功!")

    // 检查是否需要升级参数(安全策略更新后)
    if argon2.checkNeedsRehash(stored) then
        println("参数已过期,重新哈希...")
        stored = argon2.hash(inputPassword)
        // UPDATE users SET password_hash = stored WHERE ...
    end
else
    println("密码错误!")
end

运行范例

终端
cd release
xl.exe run examples\extlibs\argon2.xl

测试覆盖

测试文件:test/cases/extlib_argon2.xl,覆盖全部公开 API:

测试片段(完整边界以仓库套件为准)

extlib_argon2.xl
import argon2

func main(...) as int
    // ═══ 1. 自动盐 ═══
    string encoded = argon2.hash("password123", timeCost = 2, memoryCost = 64, parallelism = 1)
    assert encoded.length > 0, "hash should produce output"
    assert encoded[0] == '$', "PHC starts with $"

    string encoded2 = argon2.hash("password123", timeCost = 2, memoryCost = 64, parallelism = 1)
    assert encoded != encoded2, "auto salt produces different hashes"
    assert argon2.verify("password123", encoded), "verify auto-salt 1"
    assert argon2.verify("password123", encoded2), "verify auto-salt 2"
    assert not argon2.verify("wrong", encoded), "wrong password fails"

    // ═══ 2. 手动盐(确定性) ═══
    string salt = "somesalt12345678"
    string h1 = argon2.hash("pw", salt = salt, timeCost = 2, memoryCost = 64, parallelism = 1)
    string h2 = argon2.hash("pw", salt = salt, timeCost = 2, memoryCost = 64, parallelism = 1)
    assert h1 == h2, "same salt = same hash (deterministic)"

    // ═══ 3. secret 密钥哈希 ═══
    string withSecret = argon2.hash("pw", salt = salt, secret = "server_key",
        timeCost = 2, memoryCost = 64, parallelism = 1)
    string noSecret = argon2.hash("pw", salt = salt, timeCost = 2, memoryCost = 64, parallelism = 1)
    assert withSecret != noSecret, "secret changes the hash"

    // ═══ 4. ad 附加数据 ═══
    string withAd = argon2.hash("pw", salt = salt, ad = "user_id=42",
        timeCost = 2, memoryCost = 64, parallelism = 1)
    assert withAd != noSecret, "ad changes the hash"

    // ═══ 5. parallelism 可配 ═══
    string p1 = argon2.hash("pw", salt = salt, timeCost = 2, memoryCost = 64, parallelism = 1)
    string p2 = argon2.hash("pw", salt = salt, timeCost = 2, memoryCost = 64, parallelism = 2)
    assert p1 != p2, "different parallelism = different hash"
    assert argon2.verify("pw", p1), "verify p=1"
    assert argon2.verify("pw", p2), "verify p=2"

    // ═══ 6. 三变体 ═══
    string enc_d = argon2.hash("test", salt = salt, timeCost = 2, memoryCost = 64,
        parallelism = 1, type = argon2.ARGON2D)
    string enc_i = argon2.hash("test", salt = salt, timeCost = 2, memoryCost = 64,
        parallelism = 1, type = argon2.ARGON2I)
    string enc_id = argon2.hash("test", salt = salt, timeCost = 2, memoryCost = 64,
        parallelism = 1, type = argon2.ARGON2ID)
    assert enc_d != enc_i, "argon2d != argon2i"
    assert enc_i != enc_id, "argon2i != argon2id"
    assert argon2.verify("test", enc_d), "verify argon2d"
    assert argon2.verify("test", enc_i), "verify argon2i"
    assert argon2.verify("test", enc_id), "verify argon2id"

    // ═══ 7. hashRaw ═══
    string raw = argon2.hashRaw("pw", salt = salt, timeCost = 2, memoryCost = 64,
        parallelism = 1, hashLen = 32)
    assert raw.length == 64, "32-byte = 64 hex chars"
    string raw2 = argon2.hashRaw("pw", salt = salt, timeCost = 2, memoryCost = 64,
        parallelism = 1, hashLen = 32)
    assert raw == raw2, "hashRaw deterministic"

    // ═══ 8. checkNeedsRehash ═══
    string weak = argon2.hash("pw", salt = salt, timeCost = 1, memoryCost = 64, parallelism = 1)
    assert argon2.checkNeedsRehash(weak, timeCost = 3, memoryCost = 65536, parallelism = 4),
        "weak needs rehash"
    string strong = argon2.hash("pw", salt = salt, timeCost = 3, memoryCost = 65536, parallelism = 4)
    assert not argon2.checkNeedsRehash(strong, timeCost = 3, memoryCost = 65536, parallelism = 4),
        "strong ok"

    // ═══ 9. 参数提取 ═══
    string sample = argon2.hash("x", salt = salt, timeCost = 5, memoryCost = 1024,
        parallelism = 2, hashLen = 48)
    assert argon2.extractType(sample) == argon2.ARGON2ID, "extract type"
    assert argon2.extractTimeCost(sample) == 5, "extract time"
    assert argon2.extractMemoryCost(sample) == 1024, "extract memory"
    assert argon2.extractParallelism(sample) == 2, "extract parallelism"

    // ═══ 10. generateSalt ═══
    string s1 = argon2.generateSalt(16)
    string s2 = argon2.generateSalt(16)
    assert s1.length == 32, "16 bytes = 32 hex"
    assert s1 != s2, "random salts differ"

    // ═══ 11. 推荐参数 ═══
    assert argon2.recommendedTime() == 3, "rec time"
    assert argon2.recommendedMemory() == 65536, "rec memory"
    assert argon2.recommendedParallelism() == 4, "rec parallelism"
    assert argon2.recommendedHashLen() == 32, "rec hashlen"
    assert argon2.recommendedSaltLen() == 16, "rec saltlen"

    // ═══ 12. Profiles ═══
    assert argon2.PROFILE_LOW_MEMORY_TIME == 3, "low mem time"
    assert argon2.PROFILE_LOW_MEMORY_MEMORY == 65536, "low mem memory"
    assert argon2.PROFILE_LOW_MEMORY_PARALLELISM == 4, "low mem p"
    assert argon2.PROFILE_HIGH_MEMORY_TIME == 1, "high mem time"
    assert argon2.PROFILE_HIGH_MEMORY_MEMORY == 2097152, "high mem memory"
    assert argon2.PROFILE_HIGH_MEMORY_PARALLELISM == 4, "high mem p"

    // ═══ 13. 不同 hashLen ═══
    string h16 = argon2.hashRaw("pw", salt = salt, timeCost = 2, memoryCost = 64,
        parallelism = 1, hashLen = 16)
    string h64 = argon2.hashRaw("pw", salt = salt, timeCost = 2, memoryCost = 64,
        parallelism = 1, hashLen = 64)
    assert h16.length == 32, "16-byte = 32 hex"
    assert h64.length == 128, "64-byte = 128 hex"

    println("argon2: all tests passed")
    return 0
end

运行测试

终端
cd release
xl.exe run ..\test\cases\extlib_argon2.xl
← 返回扩展库列表