一、题目

某车型数字钥匙系统支持 BLE 唤醒和 UWB 测距。安全团队导出了一份车端数字钥匙取证日志,怀疑签名模块在某些认证会话中复用了随机数。请分析日志,恢复受保护的车端取证结果。

用DB Browser查看SQLite:

表名 作用
auth_signatures 数字钥匙认证过程里的签名记录
protected_vehicle_blob 加密后的车端取证结果
meta 车辆基本信息(VIN、车型)、公钥曲线
ble_advertisements BLW蓝牙唤醒日志
uwb_ranging UWB测距日志

二、思路

Meta

meta给出了公钥曲线的参数,且提示Two successful auth signatures share the same nonce_tag. In ECDSA this is fatal.说两条签名用了同一个随机数k

public_key_curve secp256r1
public_key_x 16aaeee15966846319a785c529d07407aa05287b1d1a0581d7ab23edcf9a0e41
public_key_y 26ce7aa4f29c0448e4220b287da05f195549d54f59e290b98b5fa3036733762c

auth_signatures

给了两组(z,r,s),其中两条r是相同的,且题目告知k也是相同的

由ECDSA签名公式:$s=k^{-1}(z+r*d) \pmod n$

则:$s_1-s_2=k^{-1}(z_1-z_2) \Rightarrow k=(z_1-z_2)(s_1-s_2)^{-1} \pmod n$

然后:$d=(s_1k-z_1)r^{-1} \pmod n$即可恢复私钥

protected_vehicle_blob

key_hint sha256(recovered_ecdsa_private_key_32bytes_big_endian)[:16]
alg SM4-CBC-PKCS7
iv_hex 20260729000102030405060708090a0b
ciphertext_b64 VpQQ28WTcnOzLz4oZbJ2K+n45+lXynIcBOmIH61oAIbY3RkimUPcp7gtwXl6yLnhZWSBswbDYrdBRK5pPyc3tXEr204LX7dHx9YzRmw0QkZBky9jultrztbZNUeYBEW4ZJhX1ze+3eqQbTfLmOt6kHGGeY7IM6ccxFRJ2fhKkpY=

用私钥d派生出SM4解密密钥,后再用SM4-CBC解密即可

三、解答

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
import zipfile
import sqlite3
import tempfile
import base64
from hashlib import sha256
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes

zip_path = "digital_key_trace.sqlite.zip"

with zipfile.ZipFile(zip_path, "r") as z:
db_data = z.read("digital_key_trace.sqlite")

with tempfile.NamedTemporaryFile(suffix=".sqlite") as f:
f.write(db_data)
f.flush()

conn = sqlite3.connect(f.name)
rows = conn.execute("""
SELECT digest_hex, r_hex, s_hex
FROM auth_signatures
WHERE nonce_tag = 'rng-slot-07'
""").fetchall()

z1 = int(rows[0][0], 16)
r = int(rows[0][1], 16)
s1 = int(rows[0][2], 16)

z2 = int(rows[1][0], 16)
s2 = int(rows[1][2], 16)

# secp256r1 / NIST P-256 order
n = int("FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551", 16)

k = ((z1 - z2) * pow((s1 - s2) % n, -1, n)) % n
d = ((s1 * k - z1) * pow(r, -1, n)) % n

key = sha256(d.to_bytes(32, "big")).digest()[:16]

alg, key_hint, iv_hex, ciphertext_b64 = conn.execute("""
SELECT alg, key_hint, iv_hex, ciphertext_b64
FROM protected_vehicle_blob
WHERE id = 1
""").fetchone()

iv = bytes.fromhex(iv_hex)
ct = base64.b64decode(ciphertext_b64)

cipher = Cipher(algorithms.SM4(key), modes.CBC(iv))
dec = cipher.decryptor()
pt = dec.update(ct) + dec.finalize()

pad = pt[-1]
pt = pt[:-pad]
print(pt.decode())