add personalization script
This commit is contained in:
parent
c6ecebb057
commit
8d42005e13
|
@ -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.
|
||||
|
||||
|
|
11
README.md
11
README.md
|
@ -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.
|
||||
|
|
|
@ -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()
|
Loading…
Reference in New Issue