这是用来做什么的?

定期导出密码管理器的密钥加密备份,发送到邮箱。
所有操作在浏览器本地进行,无服务器参与。

以前搞丢过bitwarden的两步验证,只能删掉账号。
定期导出管理器的密钥
将公钥放到链接里面,方便每次备份,不用每次输入短语

带入参数

通过页面的一对密钥对 解密hash里面的data,解密成一个json

私钥 yNmVrcoS5D4xMTvjAPSkZe57HZqPZoIUxznm+SqWKFo=
公钥 dTj41nmwoLcguLpM9AntyKgg67xx6K4UAxc27CLIcFw=

json结构

{
    pubkey: string
    salt?: string
    ver?: string
    kdfHash?: string
    kdfIterations?: number
    type?: 'phrase' | 'pubkey'
    private?: string
}
字段 说明 其他
pubkey 公钥,用于加密和验证
salt 随机 salt,用于 PBKDF2 派生密钥 可选
ver KDF 版本,如 "2.0" 可选
kdfHash PBKDF2 哈希算法,如 "SHA-512" 可选
kdfIterations PBKDF2 迭代次数,如 210000 可选
type 密钥类型:'phrase'(密码短语派生)或 'pubkey'(直接公钥) 可选
private 私钥(Base64),保存后可免密码短语解密 可选,需用户确认

格式说明

结构

1字节 1字节 2字节 2字节 2字节 IV MAC 随机公钥 加密数据

下面short都是小端模式

过程

生成临时公钥私钥 tmpSecKey , tmpPubKey

sharedX(32 byte)= publickKey • tmpSecKey

buffer[96];
copy sharedX  => buffer[0..31]

/**
* 下面公钥按照小端模式比较,先比较高位置,再比较低位
*/
if tmpPubKey > publickKey
    copy publickKey => buffer[32...63]
    copy tmpPubKey => buffer[64...95]
else
    copy publickKey => buffer[32...63]
    copy tmpPubKey => buffer[64...95]
end

根据格式选择hash算法:

【旧格式 (format=0)】
利用hash算法 blake2b 生成64字节长度digest
digest = blake2b(buffer)

【新格式 (format=1)】
利用hash算法 SHA-512 生成64字节长度digest
digest = sha512(buffer)

其中 digest[0..31] 作为 AES-GCM 的密钥 aesKey,使用IV 一起加密密钥生成 加密数据 EncryptData(密文自带16字节tag)

其中 digest[32,63]作为计算mac的密钥 HKEY,输出32字节数据

【旧格式 MAC计算】
采用blake2b,指定输出32字节,key = HKEY
mac = blake2b(IV + tmpPubKey + EncryptData, key=HKEY)

【新格式 MAC计算】
采用HMAC-SHA-512,截取前32字节
mac = hmac_sha512(HKEY, IV + tmpPubKey + EncryptData)[0..31]


兼容性

根据密码短语生成密钥

步骤

  1. 根据书签 ver 与是否包含 salt 决定 key:
    • ver=2.0(新格式)且 hash 内有 salt:key = 密码短语(不拼接 prefix)
    • 旧格式(无 ver,或无 salt):key = prefix(如果有) + 密码短语
  2. PBKDF2(key) 生成32 字作为私钥, secKey = PBKDF2(key)
  3. 根据私钥生成 密钥对

PBKDF2 参数

PBKDF2参数 value
salt 新书签:每次生成书签时随机生成 33 字节 Base64 编码的 salt,存储在书签链接 hash 中。
旧书签:固定值(The California sea lion ...)
ver=1.0(旧书签)iteration / hash 123456 / SHA-256
ver=2.0(新书签)iteration / hash 210000 / SHA-512
outLen 256bit