ELPass 加密分析

文件结构

分解分析

Index

首先 Index 是利用了 MsgPack 压缩的,利用 Python 脚本解密

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# pip install u-msgpack-python

with open('Index', 'rb') as f:
index = umsgpack.unpack(f)

masterPasswordSalt = index['s']
encryptedDescriptorData = index['d']
encryptedDescriptorDataNonce = index['dn']
version = index['v']

print(len(masterPasswordSalt), masterPasswordSalt.hex())
print(len(encryptedDescriptorData), encryptedDescriptorData.hex())
print(len(encryptedDescriptorDataNonce), encryptedDescriptorDataNonce.hex())
print(version)

拿到的 index 是一个有四个键的字典,这四个键含义分别为

  • s masterPasswordSalt 主密码的盐,固定为 16 Bytes
  • d encryptedDescriptorData
  • dn encryptedDescriptorDataNonce 加密 descriptorData 使用的随机数,固定为 24 Bytes
  • v version 当前数据库版本(目前固定为 1)

下一步就是根据用户的主密码 masterPassword 和盐 masterPasswordSalt 生成用于加密的 masterKey 了,这用到了 Argon2id 算法,利用 Python 实现代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 这里使用了与 ELPass 相同的 libsodium 库
# Mac 使用 brew install libsodium 即可安装
# 然后还需要安装 Python 的包装函数以简化使用
# pip install pysodium

from pysodium import crypto_pwhash
from pysodium import crypto_pwhash_OPSLIMIT_SENSITIVE, crypto_pwhash_MEMLIMIT_MODERATE, crypto_pwhash_ALG_DEFAULT

masterKey = crypto_pwhash(
32, masterPassword, masterPasswordSalt,
crypto_pwhash_OPSLIMIT_SENSITIVE, crypto_pwhash_MEMLIMIT_MODERATE, crypto_pwhash_ALG_DEFAULT
)

print(len(masterKey), masterKey.hex())

生成 masterKey 后利用之前的 encryptedDescriptorData 校验 masterKey 是否正确

1
2
3
4
5
6
from pysodium import crypto_secretbox_open

descriptorData = crypto_secretbox_open(encryptedDescriptorData, encryptedDescriptorDataNonce, masterKey)
unzippedDescriptorData = umsgpack.loads(descriptorData)

realMasterKey = unzippedDescriptorData['masterKey']

参考信息

How Elpass Encrypt Your Data

surge-networks/Elpass-Core

jedisct1/swift-sodium

Password hashing

stef/pysodium