status-keycard/status_hw_perso.py

56 lines
2.5 KiB
Python

import secrets
import hmac
import hashlib
import os
import struct
import subprocess
gpshell_template = """
mode_211
enable_trace
establish_context
card_connect
select -AID A000000151000000
open_sc -security 1 -keyind 0 -keyver 0 -mac_key 404142434445464748494a4b4c4d4e4f -enc_key 404142434445464748494a4b4c4d4e4f -kek_key 404142434445464748494a4b4c4d4e4f
send_apdu_nostop -sc 1 -APDU 80E400800E4F0C53746174757357616C6C6574
install_for_load -pkgAID 53746174757357616C6C6574
load -file wallet.cap
send_apdu -sc 1 -APDU 80E60C005F0C53746174757357616C6C65740F53746174757357616C6C65744170700F53746174757357616C6C657441707001002EC92C{:s}{:s}00
card_disconnect
release_context
"""
def pbkdf2(digestmod, password: 'bytes', salt, count, dk_length) -> 'bytes':
def pbkdf2_function(pw, salt, count, i):
# in the first iteration, the hmac message is the salt
# concatinated with the block number in the form of \x00\x00\x00\x01
r = u = hmac.new(pw, salt + struct.pack(">i", i), digestmod).digest()
for i in range(2, count + 1):
# in subsequent iterations, the hmac message is the
# previous hmac digest. The key is always the users password
# see the hmac specification for notes on padding and stretching
u = hmac.new(pw, u, digestmod).digest()
# this is the exclusive or of the two byte-strings
r = bytes(i ^ j for i, j in zip(r, u))
return r
dk, h_length = b'', digestmod().digest_size
# we generate as many blocks as are required to
# concatinate to the desired key size:
blocks = (dk_length // h_length) + (1 if dk_length % h_length else 0)
for i in range(1, blocks + 1):
dk += pbkdf2_function(password, salt, count, i)
# The length of the key wil be dk_length to the nearest
# hash block size, i.e. larger than or equal to it. We
# slice it to the desired length befor returning it.
return dk[:dk_length]
def run():
puk = '{:012d}'.format(secrets.randbelow(999999999999))
pairing = secrets.token_urlsafe(12)
pairing_key = pbkdf2(hashlib.sha256, pairing.encode('utf-8'), 'Status Hardware Wallet Lite'.encode('utf-8'), 50000, 32).hex()
perso_script = gpshell_template.format(puk.encode('utf-8').hex(), pairing_key)
subprocess.run("gpshell", shell=True, check=True, input=perso_script.encode('utf-8'))
print('\n**************************************\nPairing password: {:s}\nPUK: {:s}'.format(pairing, puk))
if __name__ == '__main__':
run()