AegixPass 是一种确定性派生密码生成算法,其输出只依赖于输入的主密码、区分密钥和预设配置。该算法不使用任何外部随机源,因此只要输入相同,输出密码总是相同的。
其主要特点和设计原则如下:
算法的运作依赖于以下三个核心输入:
password_source
)用户持有的核心秘密,程序永不存储。
distinguish_key
)用于为不同服务(如网站域名或应用名称)生成不同密码的变量,作为区分生成结果的盐值。
preset
)一个 JSON 对象,定义了密码生成的所有参数。其结构如下:
{
"name": "AegixPass - Default",
"version": 1,
"hashAlgorithm": "argon2id",
"rngAlgorithm": "chaCha20",
"shuffleAlgorithm": "fisherYates",
"length": 16,
"platformId": "aegixpass.takuron.com",
"charsets": [
"0123456789",
"abcdefghijklmnopqrstuvwxyz",
"ABCDEFGHIJKLMNOPQRSTUVWXYZ",
"!@#$%^&*_+-="
]
}
length
: 密码总长度。version
: 算法的版本。charsets
: 一个字符串数组,定义了密码应包含的字符分组。例如,数字、小写字母、大写字母和符号。hashAlgorithm
: 用于生成主种子的哈希算法,如 sha256
, blake3
, sha3_256
, argon2id
, scrypt
。rngAlgorithm
: 确定性随机数生成器算法,目前实现为 chaCha20
。shuffleAlgorithm
: 洗牌算法,固定为 fisherYates
。platformId
: 平台ID,作为一个额外可以变动的盐值用于算法使用者做区分。密码的生成过程严格遵循以下几个关键阶段:
在开始计算前,程序会进行严格的输入检查,以避免产生不安全或无效的结果。
length
) 必须大于或等于字符集分组的数量 (charsets.length
)。这是因为后续步骤需要为每个字符集分组至少选择一个字符。这是整个确定性算法的基石。一个 32 字节的主种子是后续所有伪随机操作的唯一来源。
"AegixPass_V{version}:{platform_id}:{length}:{password_source}:{distinguish_key}:{charsets_json}"
AegixPass_V1:aegixpass.takuron.com:16:MySecretPassword123!:example.com:["0123456789","abc...","ABC...","!@#..."]
charsets
的顺序)发生变化,都会生成一个完全不同的种子。为了满足现代密码的复杂度要求,算法会确定性地从每个 charsets
分组中挑选一个字符,放入最终的密码中。
charsets
数组,对于第 i 个字符集分组:%
),得到一个索引。此时,密码数组中已经包含了满足基本复杂度的字符,接下来需要用更多“随机”字符填充至用户指定的 length
。
创建确定性 RNG:使用整个 32 字节主种子来初始化一个确定性的随机数生成器(ChaCha20)。
合并字符集:将 charsets
数组中的所有字符合并成一个大的字符池。
填充字符:循环填充剩余长度的每一个位置:
secure_random_range_u32
)。它通过“拒绝采样”方法避免了简单取模运算带来的偏差,确保大字符池中的每个字符被选中的概率完全相等。为了消除阶段 C 中引入的、保证性字符位置的任何可预测性,需要对整个密码数组进行最后一次确定性的洗牌。
i
,使用 RNG 生成一个 [0, i]
范围内的随机索引 j
,然后交换位置 i
和 j
的字符。同样,这里也会使用无偏的范围随机数生成逻辑。将最终洗牌后的字符数组组合成一个字符串,并返回给用户。
通过以上步骤,AegixPass 算法确保了在任何兼容的实现上,只要输入完全一致,输出的密码也必然完全相同,同时保证了密码的强度和安全性。