Skip to content
This repository was archived by the owner on Oct 16, 2024. It is now read-only.

Commit 05506cb

Browse files
committed
Segwit and new protcol support for full hash
1 parent a03ea4f commit 05506cb

File tree

2 files changed

+73
-19
lines changed

2 files changed

+73
-19
lines changed

btchip/bitcoinTransaction.py

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,10 +89,15 @@ def __init__(self, data=None):
8989
self.inputs = []
9090
self.outputs = []
9191
self.lockTime = ""
92+
self.witness = False
93+
self.witnessScript = ""
9294
if data is not None:
9395
offset = 0
9496
self.version = data[offset:offset + 4]
9597
offset += 4
98+
if (data[offset] == 0) and (data[offset + 1] <> 0):
99+
offset += 2
100+
self.witness = True
96101
inputSize = readVarint(data, offset)
97102
offset += inputSize['size']
98103
numInputs = inputSize['value']
@@ -107,18 +112,31 @@ def __init__(self, data=None):
107112
tmp = { 'buffer': data, 'offset' : offset}
108113
self.outputs.append(bitcoinOutput(tmp))
109114
offset = tmp['offset']
110-
self.lockTime = data[offset:offset + 4]
115+
if self.witness:
116+
self.witnessScript = data[offset : len(data) - 4]
117+
self.lockTime = data[len(data) - 4:]
118+
else:
119+
self.lockTime = data[offset:offset + 4]
111120

112-
def serialize(self, skipOutputLocktime=None):
121+
def serialize(self, skipOutputLocktime=False, skipWitness=False):
122+
if skipWitness or (not self.witness):
123+
useWitness = False
124+
else:
125+
useWitness = True
113126
result = []
114127
result.extend(self.version)
128+
if useWitness:
129+
result.append(0x00)
130+
result.append(0x01)
115131
writeVarint(len(self.inputs), result)
116132
for trinput in self.inputs:
117133
result.extend(trinput.serialize())
118134
if not skipOutputLocktime:
119135
writeVarint(len(self.outputs), result)
120136
for troutput in self.outputs:
121137
result.extend(troutput.serialize())
138+
if useWitness:
139+
result.extend(self.witnessScript)
122140
result.extend(self.lockTime)
123141
return result
124142

@@ -142,4 +160,6 @@ def __str__(self):
142160
buf += str(troutput)
143161
index+=1
144162
buf += "Locktime : " + hexlify(self.lockTime) + "\r\n"
163+
if self.witness:
164+
buf += "Witness script : " + hexlify(self.witnessScript) + "\r\n"
145165
return buf

btchip/btchip.py

Lines changed: 51 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ class btchip:
6262
BTCHIP_INS_EXT_CACHE_GET_FEATURES = 0x26
6363

6464
OPERATION_MODE_WALLET = 0x01
65-
OPERATION_MODE_RELAXED_WALLET = 0x02
65+
OPERATION_MODE_RELAXED_WALLET = 0x02
6666
OPERATION_MODE_SERVER = 0x04
6767
OPERATION_MODE_DEVELOPER = 0x08
6868

@@ -159,6 +159,7 @@ def getTrustedInput(self, transaction, index):
159159
apdu.extend(params)
160160
self.dongle.exchange(bytearray(apdu))
161161
# Each output
162+
indexOutput = 0
162163
for troutput in transaction.outputs:
163164
apdu = [ self.BTCHIP_CLA, self.BTCHIP_INS_GET_TRUSTED_INPUT, 0x80, 0x00 ]
164165
params = bytearray(troutput.amount)
@@ -187,7 +188,20 @@ def getTrustedInput(self, transaction, index):
187188

188189
def startUntrustedTransaction(self, newTransaction, inputIndex, outputList, redeemScript):
189190
# Start building a fake transaction with the passed inputs
190-
apdu = [ self.BTCHIP_CLA, self.BTCHIP_INS_HASH_INPUT_START, 0x00, (0x00 if newTransaction else 0x80) ]
191+
segwit = False
192+
if newTransaction:
193+
for passedOutput in outputList:
194+
if ('witness' in passedOutput) and passedOutput['witness']:
195+
segwit = True
196+
break
197+
if newTransaction:
198+
if segwit:
199+
p2 = 0x02
200+
else:
201+
p2 = 0x00
202+
else:
203+
p2 = 0x80
204+
apdu = [ self.BTCHIP_CLA, self.BTCHIP_INS_HASH_INPUT_START, 0x00, p2 ]
191205
params = bytearray([0x01, 0x00, 0x00, 0x00]) # default version
192206
writeVarint(len(outputList), params)
193207
apdu.append(len(params))
@@ -199,11 +213,13 @@ def startUntrustedTransaction(self, newTransaction, inputIndex, outputList, rede
199213
apdu = [ self.BTCHIP_CLA, self.BTCHIP_INS_HASH_INPUT_START, 0x80, 0x00 ]
200214
params = []
201215
script = redeemScript
202-
if passedOutput['trustedInput']:
216+
if ('witness' in passedOutput) and passedOutput['witness']:
217+
params.append(0x02)
218+
elif ('trustedInput' in passedOutput) and passedOutput['trustedInput']:
203219
params.append(0x01)
204220
else:
205221
params.append(0x00)
206-
if passedOutput['trustedInput']:
222+
if ('trustedInput' in passedOutput) and passedOutput['trustedInput']:
207223
params.append(len(passedOutput['value']))
208224
params.extend(passedOutput['value'])
209225
if currentIndex <> inputIndex:
@@ -236,6 +252,7 @@ def finalizeInput(self, outputAddress, amount, fees, changePath, rawTx=None):
236252
if self.needKeyCache:
237253
self.resolvePublicKeysInPath(changePath)
238254
result = {}
255+
outputs = None
239256
if rawTx is not None:
240257
try:
241258
fullTx = bitcoinTransaction(bytearray(rawTx))
@@ -278,7 +295,14 @@ def finalizeInput(self, outputAddress, amount, fees, changePath, rawTx=None):
278295
result['keycardData'] = response[offset : offset + keycardDataLength]
279296
offset = offset + keycardDataLength
280297
result['secureScreenData'] = response[offset:]
281-
result['outputData'] = response[1 : 1 + response[0]]
298+
if result['confirmationType'] == 0x04:
299+
offset = 1 + response[0] + 1
300+
keycardDataLength = response[offset]
301+
result['keycardData'] = response[offset + 1 : offset + 1 + keycardDataLength]
302+
if outputs == None:
303+
result['outputData'] = response[1 : 1 + response[0]]
304+
else:
305+
result['outputData'] = outputs
282306
return result
283307

284308
def finalizeInputFull(self, outputData):
@@ -299,20 +323,22 @@ def finalizeInputFull(self, outputData):
299323
response = self.dongle.exchange(bytearray(apdu))
300324
encryptedOutputData = encryptedOutputData + response[1 : 1 + response[0]]
301325
offset += dataLength
302-
result['confirmationNeeded'] = response[1 + response[0]] <> 0x00
303-
result['confirmationType'] = response[1 + response[0]]
304-
if result['confirmationType'] == 0x02:
326+
result['confirmationNeeded'] = response[1 + response[0]] <> 0x00
327+
result['confirmationType'] = response[1 + response[0]]
328+
if result['confirmationType'] == 0x02:
329+
result['keycardData'] = response[1 + response[0] + 1:] # legacy
330+
if result['confirmationType'] == 0x03:
305331
offset = 1 + response[0] + 1
306332
keycardDataLength = response[offset]
307-
result['keycardData'] = response[offset : offset + keycardDataLength]
308-
if result['confirmationType'] == 0x03:
309-
offset = 1 + response[0] + 1
310-
keycardDataLength = response[offset]
311-
offset = offset + 1
312-
result['keycardData'] = response[offset : offset + keycardDataLength]
313-
offset = offset + keycardDataLength
314-
result['secureScreenData'] = response[offset:]
315-
result['encryptedOutputData'] = encryptedOutputData
333+
offset = offset + 1
334+
result['keycardData'] = response[offset : offset + keycardDataLength]
335+
offset = offset + keycardDataLength
336+
result['secureScreenData'] = response[offset:]
337+
result['encryptedOutputData'] = encryptedOutputData
338+
if result['confirmationType'] == 0x04:
339+
offset = 1 + response[0] + 1
340+
keycardDataLength = response[offset]
341+
result['keycardData'] = response[offset + 1 : offset + 1 + keycardDataLength]
316342
return result
317343

318344
def untrustedHashSign(self, path, pin="", lockTime=0, sighashType=0x01):
@@ -435,6 +461,14 @@ def setOperationMode(self, operationMode):
435461
apdu = [ self.BTCHIP_CLA, self.BTCHIP_INS_SET_OPERATION_MODE, 0x00, 0x00, 0x01, operationMode ]
436462
self.dongle.exchange(bytearray(apdu))
437463

464+
def enableAlternate2fa(self, persistent):
465+
if persistent:
466+
p1 = 0x02
467+
else:
468+
p1 = 0x01
469+
apdu = [ self.BTCHIP_CLA, self.BTCHIP_INS_SET_OPERATION_MODE, p1, 0x00, 0x01, btchip.OPERATION_MODE_WALLET ]
470+
self.dongle.exchange(bytearray(apdu))
471+
438472
def getFirmwareVersion(self):
439473
result = {}
440474
apdu = [ self.BTCHIP_CLA, self.BTCHIP_INS_GET_FIRMWARE_VERSION, 0x00, 0x00, 0x00 ]

0 commit comments

Comments
 (0)