add personalization script

This commit is contained in:
Michele Balistreri 2018-08-14 13:27:52 +03:00
parent c6ecebb057
commit 8d42005e13
3 changed files with 69 additions and 0 deletions

View File

@ -139,4 +139,6 @@ so that the current key path matches m/1/1 and only then will the EXPORT KEY com
5. If you test your client against our fork of jCardSim instead of a real card, keep in mind that it supports unassisted
key derivation, but you shouldn't use it because, as explained above, it wouldn't work on the card.
6. If using jCardSim, only use our fork, since some of the needed algorithms are unsupported in the upstream version.
7. The pairing code is a randomly generated password (using whatever password generation algorithm is desired). This
password must be converted to a 256-bit key using PBKDF2 with the salt "Status Hardware Wallet Lite" and 50000 iterations.

View File

@ -55,6 +55,17 @@ im.status.gradle.gpshell.kvn=0
im.status.wallet.test.simulated=false
```
## Alternative installation method
This method does not require the JavaCard SDK but requires an already compiled CAP file. The cards generated this way
have a random PUK and pairing code so they have better security. However applet installation/removal is not disabled,
because the script is still meant to be used during the development phase.
1. Install GPShell and Python 3
2. Put the wallet.cap file in the same directory as status_hw_perso.py
3. Disconnect all card reader terminals from the system, except the one with the card where you want to install the applet
4. Run the status_hw_perso.py script with no arguments.
5. Take note of the pairing code and PUK output by the script
## Implementation notes
* The applet requires JavaCard 3.0.4 or later.

56
status_hw_perso.py Normal file
View File

@ -0,0 +1,56 @@
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()