diff --git a/ledgerblue/deleteApp.py b/ledgerblue/deleteApp.py index 876afe4..ba58852 100644 --- a/ledgerblue/deleteApp.py +++ b/ledgerblue/deleteApp.py @@ -19,7 +19,7 @@ from secp256k1 import PrivateKey from ledgerblue.comm import getDongle -from ledgerblue.deployed import getDeployedSecret +from ledgerblue.deployed import getDeployedSecretV1, getDeployedSecretV2 from ledgerblue.hexLoader import HexLoader import argparse @@ -31,13 +31,14 @@ parser.add_argument("--targetId", help="Set the chip target ID", type=auto_int) parser.add_argument("--appName", help="Set the application name") parser.add_argument("--rootPrivateKey", help="Set the root private key") parser.add_argument("--apdu", help="Display APDU log", action='store_true') +parser.add_argument("--deployLegacy", help="Use legacy deployment API", action='store_true') args = parser.parse_args() if args.appName == None: raise Exception("Missing appName") if args.targetId == None: - args.targetId = 0x31000001 + args.targetId = 0x31000002 if args.rootPrivateKey == None: privateKey = PrivateKey() publicKey = str(privateKey.pubkey.serialize(compressed=False)).encode('hex') @@ -46,6 +47,9 @@ if args.rootPrivateKey == None: dongle = getDongle(args.apdu) -secret = getDeployedSecret(dongle, bytearray.fromhex(args.rootPrivateKey), args.targetId) +if args.deployLegacy: + secret = getDeployedSecretV1(dongle, bytearray.fromhex(args.rootPrivateKey), args.targetId) +else: + secret = getDeployedSecretV2(dongle, bytearray.fromhex(args.rootPrivateKey), args.targetId) loader = HexLoader(dongle, 0xe0, True, secret) loader.deleteApp(args.appName) diff --git a/ledgerblue/deployed.py b/ledgerblue/deployed.py index 80a7be7..915ede2 100644 --- a/ledgerblue/deployed.py +++ b/ledgerblue/deployed.py @@ -18,12 +18,13 @@ """ from secp256k1 import PrivateKey, PublicKey +import os import sys import struct from .hexParser import IntelHexParser from .hexLoader import HexLoader -def getDeployedSecret(dongle, masterPrivate, targetid): +def getDeployedSecretV1(dongle, masterPrivate, targetid): testMaster = PrivateKey(bytes(masterPrivate)) testMasterPublic = bytearray(testMaster.pubkey.serialize(compressed=False)) targetid = bytearray(struct.pack('>I', targetid)) @@ -72,3 +73,74 @@ def getDeployedSecret(dongle, masterPrivate, targetid): dongle.exchange(bytearray.fromhex('E053000000')) secret = last_pub_key.ecdh(bytes(ephemeralPrivate.serialize().decode('hex'))) return str(secret[0:16]) + +def getDeployedSecretV2(dongle, masterPrivate, targetid): + testMaster = PrivateKey(bytes(masterPrivate)) + testMasterPublic = bytearray(testMaster.pubkey.serialize(compressed=False)) + targetid = bytearray(struct.pack('>I', targetid)) + + # identify + apdu = bytearray([0xe0, 0x04, 0x00, 0x00]) + bytearray([len(targetid)]) + targetid + dongle.exchange(apdu) + + # walk the chain + nonce = os.urandom(8) + apdu = bytearray([0xe0, 0x50, 0x00, 0x00]) + bytearray([len(nonce)]) + nonce + auth_info = dongle.exchange(apdu) + batch_signer_serial = auth_info[0:4] + deviceNonce = auth_info[4:12] + + # if not found, get another pair + #if cardKey <> testMasterPublic: + # raise Exception("Invalid batch public key") + + # provide the ephemeral certificate + ephemeralPrivate = PrivateKey() + ephemeralPublic = bytearray(ephemeralPrivate.pubkey.serialize(compressed=False)) + print "Using ephemeral key " + str(ephemeralPublic).encode('hex') + dataToSign = bytes(bytearray([0x11]) + nonce + deviceNonce + ephemeralPublic) + signature = testMaster.ecdsa_sign(bytes(dataToSign)) + signature = testMaster.ecdsa_serialize(signature) + certificate = bytearray([len(ephemeralPublic)]) + ephemeralPublic + bytearray([len(signature)]) + signature + apdu = bytearray([0xE0, 0x51, 0x80, 0x00]) + bytearray([len(certificate)]) + certificate + dongle.exchange(apdu) + + # walk the device certificates to retrieve the public key to use for authentication + index = 0 + last_pub_key = PublicKey(bytes(testMasterPublic), raw=True) + while True: + if index == 0: + certificate = bytearray(dongle.exchange(bytearray.fromhex('E052000000'))) + elif index == 1: + certificate = bytearray(dongle.exchange(bytearray.fromhex('E052800000'))) + else: + break + if len(certificate) == 0: + break + offset = 1 + certificateHeader = certificate[offset : offset + certificate[offset-1]] + offset += certificate[offset-1] + 1 + certificatePublicKey = certificate[offset : offset + certificate[offset-1]] + offset += certificate[offset-1] + 1 + certificateSignatureArray = certificate[offset : offset + certificate[offset-1]] + certificateSignature = last_pub_key.ecdsa_deserialize(bytes(certificateSignatureArray)) + # first cert contains a header field which holds the certificate's public key role + if index == 0: + certificateSignedData = bytearray([0x02]) + certificateHeader + certificatePublicKey + # Could check if the device certificate is signed by the issuer public key + # ephemeral key certificate + else: + certificateSignedData = bytearray([0x12]) + deviceNonce + nonce + certificatePublicKey + if not last_pub_key.ecdsa_verify(bytes(certificateSignedData), certificateSignature): + if index == 0: + # Not an error if loading from user key + print "Broken certificate chain - loading from user key" + else: + raise Exception("Broken certificate chain") + last_pub_key = PublicKey(bytes(certificatePublicKey), raw=True) + index = index + 1 + + # Commit device ECDH channel + dongle.exchange(bytearray.fromhex('E053000000')) + secret = last_pub_key.ecdh(bytes(ephemeralPrivate.serialize().decode('hex'))) + return str(secret[0:16]) diff --git a/ledgerblue/loadApp.py b/ledgerblue/loadApp.py index f0f2e2e..a7c5093 100644 --- a/ledgerblue/loadApp.py +++ b/ledgerblue/loadApp.py @@ -21,7 +21,7 @@ from secp256k1 import PrivateKey from ledgerblue.comm import getDongle from ledgerblue.hexParser import IntelHexParser from ledgerblue.hexLoader import HexLoader -from ledgerblue.deployed import getDeployedSecret +from ledgerblue.deployed import getDeployedSecretV1, getDeployedSecretV2 import argparse def auto_int(x): @@ -35,11 +35,12 @@ parser.add_argument("--appFlags", help="Set the application flags", type=auto_in parser.add_argument("--bootAddr", help="Set the boot address", type=auto_int) parser.add_argument("--rootPrivateKey", help="Set the root private key") parser.add_argument("--apdu", help="Display APDU log", action='store_true') +parser.add_argument("--deployLegacy", help="Use legacy deployment API", action='store_true') args = parser.parse_args() if args.targetId == None: - args.targetId = 0x31000001 + args.targetId = 0x31000002 if args.fileName == None: raise Exception("Missing fileName") if args.appName == None: @@ -57,7 +58,10 @@ if args.rootPrivateKey == None: parser = IntelHexParser(args.fileName) dongle = getDongle(args.apdu) -secret = getDeployedSecret(dongle, bytearray.fromhex(args.rootPrivateKey), args.targetId) +if args.deployLegacy: + secret = getDeployedSecretV1(dongle, bytearray.fromhex(args.rootPrivateKey), args.targetId) +else: + secret = getDeployedSecretV2(dongle, bytearray.fromhex(args.rootPrivateKey), args.targetId) loader = HexLoader(dongle, 0xe0, True, secret) if (not (args.appFlags & 2)):