pax_global_header 0000666 0000000 0000000 00000000064 13736561771 0014532 g ustar 00root root 0000000 0000000 52 comment=17f27c1996c75145b8eb5d16583bddcb6e2bf691
btchip-python-0.1.31/ 0000775 0000000 0000000 00000000000 13736561771 0014404 5 ustar 00root root 0000000 0000000 btchip-python-0.1.31/.gitignore 0000664 0000000 0000000 00000000007 13736561771 0016371 0 ustar 00root root 0000000 0000000 *.pyc
btchip-python-0.1.31/.gitmodules 0000664 0000000 0000000 00000000000 13736561771 0016547 0 ustar 00root root 0000000 0000000 btchip-python-0.1.31/LICENSE 0000664 0000000 0000000 00000026136 13736561771 0015421 0 ustar 00root root 0000000 0000000
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
btchip-python-0.1.31/MANIFEST.in 0000664 0000000 0000000 00000000023 13736561771 0016135 0 ustar 00root root 0000000 0000000 include README.md
btchip-python-0.1.31/README.md 0000664 0000000 0000000 00000002531 13736561771 0015664 0 ustar 00root root 0000000 0000000 btchip-python
=============
Python communication library for Ledger Hardware Wallet products
Requirements
-------------
This API is available on pip - install with pip install btchip-python
Building on a Unix platform requires libusb-1.0-0-dev and libudev-dev installed previously
Interim Debian packages have also been built by Richard Ulrich at https://launchpad.net/~richi-paraeasy/+archive/ubuntu/bitcoin/ (btchip-python, hidapi and python-hidapi)
For optional BIP 39 support during dongle setup, also install https://github.com/trezor/python-mnemonic - also available as a Debian package at the previous link (python-mnemonic)
Building on Windows
--------------------
- Download and install the latest Python 2.7 version from https://www.python.org/downloads/windows/
- Install Microsoft Visual C++ Compiler for Python 2.7 from http://www.microsoft.com/en-us/download/details.aspx?id=44266
- Download and install PyQt4 for Python 2.7 from https://www.riverbankcomputing.com/software/pyqt/download
- Install the btchip library (open a command prompt and enter c:\python27\scripts\pip install btchip)
Building/Installing on FreeBSD
------------------------------
On FreeBSD you can install the packages:
pkg install security/py-btchip-python
or build via ports:
cd /usr/ports/security/py-btchip-python
make install clean
btchip-python-0.1.31/btchip/ 0000775 0000000 0000000 00000000000 13736561771 0015655 5 ustar 00root root 0000000 0000000 btchip-python-0.1.31/btchip/__init__.py 0000664 0000000 0000000 00000001527 13736561771 0017773 0 ustar 00root root 0000000 0000000 """
*******************************************************************************
* BTChip Bitcoin Hardware Wallet Python API
* (c) 2014 BTChip - 1BTChip7VfTnrPra5jqci7ejnMguuHogTn
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
********************************************************************************
"""
__version__ = "0.1.31"
btchip-python-0.1.31/btchip/bitcoinTransaction.py 0000664 0000000 0000000 00000011412 13736561771 0022063 0 ustar 00root root 0000000 0000000 """
*******************************************************************************
* BTChip Bitcoin Hardware Wallet Python API
* (c) 2014 BTChip - 1BTChip7VfTnrPra5jqci7ejnMguuHogTn
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
********************************************************************************
"""
from .bitcoinVarint import *
from binascii import hexlify
class bitcoinInput:
def __init__(self, bufferOffset=None):
self.prevOut = ""
self.script = ""
self.sequence = ""
if bufferOffset is not None:
buf = bufferOffset['buffer']
offset = bufferOffset['offset']
self.prevOut = buf[offset:offset + 36]
offset += 36
scriptSize = readVarint(buf, offset)
offset += scriptSize['size']
self.script = buf[offset:offset + scriptSize['value']]
offset += scriptSize['value']
self.sequence = buf[offset:offset + 4]
offset += 4
bufferOffset['offset'] = offset
def serialize(self):
result = []
result.extend(self.prevOut)
writeVarint(len(self.script), result)
result.extend(self.script)
result.extend(self.sequence)
return result
def __str__(self):
buf = "Prevout : " + hexlify(self.prevOut) + "\r\n"
buf += "Script : " + hexlify(self.script) + "\r\n"
buf += "Sequence : " + hexlify(self.sequence) + "\r\n"
return buf
class bitcoinOutput:
def __init__(self, bufferOffset=None):
self.amount = ""
self.script = ""
if bufferOffset is not None:
buf = bufferOffset['buffer']
offset = bufferOffset['offset']
self.amount = buf[offset:offset + 8]
offset += 8
scriptSize = readVarint(buf, offset)
offset += scriptSize['size']
self.script = buf[offset:offset + scriptSize['value']]
offset += scriptSize['value']
bufferOffset['offset'] = offset
def serialize(self):
result = []
result.extend(self.amount)
writeVarint(len(self.script), result)
result.extend(self.script)
return result
def __str__(self):
buf = "Amount : " + hexlify(self.amount) + "\r\n"
buf += "Script : " + hexlify(self.script) + "\r\n"
return buf
class bitcoinTransaction:
def __init__(self, data=None):
self.version = ""
self.inputs = []
self.outputs = []
self.lockTime = ""
self.witness = False
self.witnessScript = ""
if data is not None:
offset = 0
self.version = data[offset:offset + 4]
offset += 4
if (data[offset] == 0) and (data[offset + 1] != 0):
offset += 2
self.witness = True
inputSize = readVarint(data, offset)
offset += inputSize['size']
numInputs = inputSize['value']
for i in range(numInputs):
tmp = { 'buffer': data, 'offset' : offset}
self.inputs.append(bitcoinInput(tmp))
offset = tmp['offset']
outputSize = readVarint(data, offset)
offset += outputSize['size']
numOutputs = outputSize['value']
for i in range(numOutputs):
tmp = { 'buffer': data, 'offset' : offset}
self.outputs.append(bitcoinOutput(tmp))
offset = tmp['offset']
if self.witness:
self.witnessScript = data[offset : len(data) - 4]
self.lockTime = data[len(data) - 4:]
else:
self.lockTime = data[offset:offset + 4]
def serialize(self, skipOutputLocktime=False, skipWitness=False):
if skipWitness or (not self.witness):
useWitness = False
else:
useWitness = True
result = []
result.extend(self.version)
if useWitness:
result.append(0x00)
result.append(0x01)
writeVarint(len(self.inputs), result)
for trinput in self.inputs:
result.extend(trinput.serialize())
if not skipOutputLocktime:
writeVarint(len(self.outputs), result)
for troutput in self.outputs:
result.extend(troutput.serialize())
if useWitness:
result.extend(self.witnessScript)
result.extend(self.lockTime)
return result
def serializeOutputs(self):
result = []
writeVarint(len(self.outputs), result)
for troutput in self.outputs:
result.extend(troutput.serialize())
return result
def __str__(self):
buf = "Version : " + hexlify(self.version) + "\r\n"
index = 1
for trinput in self.inputs:
buf += "Input #" + str(index) + "\r\n"
buf += str(trinput)
index+=1
index = 1
for troutput in self.outputs:
buf += "Output #" + str(index) + "\r\n"
buf += str(troutput)
index+=1
buf += "Locktime : " + hexlify(self.lockTime) + "\r\n"
if self.witness:
buf += "Witness script : " + hexlify(self.witnessScript) + "\r\n"
return buf
btchip-python-0.1.31/btchip/bitcoinVarint.py 0000664 0000000 0000000 00000003751 13736561771 0021050 0 ustar 00root root 0000000 0000000 """
*******************************************************************************
* BTChip Bitcoin Hardware Wallet Python API
* (c) 2014 BTChip - 1BTChip7VfTnrPra5jqci7ejnMguuHogTn
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
********************************************************************************
"""
from .btchipException import BTChipException
def readVarint(buffer, offset):
varintSize = 0
value = 0
if (buffer[offset] < 0xfd):
value = buffer[offset]
varintSize = 1
elif (buffer[offset] == 0xfd):
value = (buffer[offset + 2] << 8) | (buffer[offset + 1])
varintSize = 3
elif (buffer[offset] == 0xfe):
value = (buffer[offset + 4] << 24) | (buffer[offset + 3] << 16) | (buffer[offset + 2] << 8) | (buffer[offset + 1])
varintSize = 5
else:
raise BTChipException("unsupported varint")
return { "value": value, "size": varintSize }
def writeVarint(value, buffer):
if (value < 0xfd):
buffer.append(value)
elif (value <= 0xffff):
buffer.append(0xfd)
buffer.append(value & 0xff)
buffer.append((value >> 8) & 0xff)
elif (value <= 0xffffffff):
buffer.append(0xfe)
buffer.append(value & 0xff)
buffer.append((value >> 8) & 0xff)
buffer.append((value >> 16) & 0xff)
buffer.append((value >> 24) & 0xff)
else:
raise BTChipException("unsupported encoding")
return buffer
def getVarintSize(value):
if (value < 0xfd):
return 1
elif (value <= 0xffff):
return 3
elif (value <= 0xffffffff):
return 5
else:
raise BTChipException("unsupported encoding")
btchip-python-0.1.31/btchip/btchip.py 0000664 0000000 0000000 00000063041 13736561771 0017504 0 ustar 00root root 0000000 0000000 """
*******************************************************************************
* BTChip Bitcoin Hardware Wallet Python API
* (c) 2014 BTChip - 1BTChip7VfTnrPra5jqci7ejnMguuHogTn
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
********************************************************************************
"""
from .btchipComm import *
from .bitcoinTransaction import *
from .bitcoinVarint import *
from .btchipException import *
from .btchipHelpers import *
from .btchipKeyRecovery import *
from binascii import hexlify, unhexlify
class btchip:
BTCHIP_CLA = 0xe0
BTCHIP_JC_EXT_CLA = 0xf0
BTCHIP_INS_SET_ALTERNATE_COIN_VERSION = 0x14
BTCHIP_INS_SETUP = 0x20
BTCHIP_INS_VERIFY_PIN = 0x22
BTCHIP_INS_GET_OPERATION_MODE = 0x24
BTCHIP_INS_SET_OPERATION_MODE = 0x26
BTCHIP_INS_SET_KEYMAP = 0x28
BTCHIP_INS_SET_COMM_PROTOCOL = 0x2a
BTCHIP_INS_GET_WALLET_PUBLIC_KEY = 0x40
BTCHIP_INS_GET_TRUSTED_INPUT = 0x42
BTCHIP_INS_HASH_INPUT_START = 0x44
BTCHIP_INS_HASH_INPUT_FINALIZE = 0x46
BTCHIP_INS_HASH_SIGN = 0x48
BTCHIP_INS_HASH_INPUT_FINALIZE_FULL = 0x4a
BTCHIP_INS_GET_INTERNAL_CHAIN_INDEX = 0x4c
BTCHIP_INS_SIGN_MESSAGE = 0x4e
BTCHIP_INS_GET_TRANSACTION_LIMIT = 0xa0
BTCHIP_INS_SET_TRANSACTION_LIMIT = 0xa2
BTCHIP_INS_IMPORT_PRIVATE_KEY = 0xb0
BTCHIP_INS_GET_PUBLIC_KEY = 0xb2
BTCHIP_INS_DERIVE_BIP32_KEY = 0xb4
BTCHIP_INS_SIGNVERIFY_IMMEDIATE = 0xb6
BTCHIP_INS_GET_RANDOM = 0xc0
BTCHIP_INS_GET_ATTESTATION = 0xc2
BTCHIP_INS_GET_FIRMWARE_VERSION = 0xc4
BTCHIP_INS_COMPOSE_MOFN_ADDRESS = 0xc6
BTCHIP_INS_GET_POS_SEED = 0xca
BTCHIP_INS_EXT_GET_HALF_PUBLIC_KEY = 0x20
BTCHIP_INS_EXT_CACHE_PUT_PUBLIC_KEY = 0x22
BTCHIP_INS_EXT_CACHE_HAS_PUBLIC_KEY = 0x24
BTCHIP_INS_EXT_CACHE_GET_FEATURES = 0x26
OPERATION_MODE_WALLET = 0x01
OPERATION_MODE_RELAXED_WALLET = 0x02
OPERATION_MODE_SERVER = 0x04
OPERATION_MODE_DEVELOPER = 0x08
FEATURE_UNCOMPRESSED_KEYS = 0x01
FEATURE_RFC6979 = 0x02
FEATURE_FREE_SIGHASHTYPE = 0x04
FEATURE_NO_2FA_P2SH = 0x08
QWERTY_KEYMAP = bytearray(unhexlify("000000000000000000000000760f00d4ffffffc7000000782c1e3420212224342627252e362d3738271e1f202122232425263333362e37381f0405060708090a0b0c0d0e0f101112131415161718191a1b1c1d2f3130232d350405060708090a0b0c0d0e0f101112131415161718191a1b1c1d2f313035"))
QWERTZ_KEYMAP = bytearray(unhexlify("000000000000000000000000760f00d4ffffffc7000000782c1e3420212224342627252e362d3738271e1f202122232425263333362e37381f0405060708090a0b0c0d0e0f101112131415161718191a1b1d1c2f3130232d350405060708090a0b0c0d0e0f101112131415161718191a1b1d1c2f313035"))
AZERTY_KEYMAP = bytearray(unhexlify("08000000010000200100007820c8ffc3feffff07000000002c38202030341e21222d352e102e3637271e1f202122232425263736362e37101f1405060708090a0b0c0d0e0f331112130415161718191d1b1c1a2f64302f2d351405060708090a0b0c0d0e0f331112130415161718191d1b1c1a2f643035"))
def __init__(self, dongle):
self.dongle = dongle
self.needKeyCache = False
try:
firmware = self.getFirmwareVersion()['version']
self.multiOutputSupported = tuple(map(int, (firmware.split(".")))) >= (1, 1, 4)
if self.multiOutputSupported:
self.scriptBlockLength = 50
else:
self.scriptBlockLength = 255
except Exception:
pass
try:
result = self.getJCExtendedFeatures()
self.needKeyCache = (result['proprietaryApi'] == False)
except Exception:
pass
def setAlternateCoinVersion(self, versionRegular, versionP2SH):
apdu = [ self.BTCHIP_CLA, self.BTCHIP_INS_SET_ALTERNATE_COIN_VERSION, 0x00, 0x00, 0x02, versionRegular, versionP2SH]
self.dongle.exchange(bytearray(apdu))
def verifyPin(self, pin):
if isinstance(pin, str):
pin = pin.encode('utf-8')
apdu = [ self.BTCHIP_CLA, self.BTCHIP_INS_VERIFY_PIN, 0x00, 0x00, len(pin) ]
apdu.extend(bytearray(pin))
self.dongle.exchange(bytearray(apdu))
def getVerifyPinRemainingAttempts(self):
apdu = [ self.BTCHIP_CLA, self.BTCHIP_INS_VERIFY_PIN, 0x80, 0x00, 0x01 ]
apdu.extend(bytearray(b'0'))
try:
self.dongle.exchange(bytearray(apdu))
except BTChipException as e:
if ((e.sw & 0xfff0) == 0x63c0):
return e.sw - 0x63c0
raise e
def getWalletPublicKey(self, path, showOnScreen=False, segwit=False, segwitNative=False, cashAddr=False):
result = {}
donglePath = parse_bip32_path(path)
if self.needKeyCache:
self.resolvePublicKeysInPath(path)
apdu = [ self.BTCHIP_CLA, self.BTCHIP_INS_GET_WALLET_PUBLIC_KEY, 0x01 if showOnScreen else 0x00, 0x03 if cashAddr else 0x02 if segwitNative else 0x01 if segwit else 0x00, len(donglePath) ]
apdu.extend(donglePath)
response = self.dongle.exchange(bytearray(apdu))
offset = 0
result['publicKey'] = response[offset + 1 : offset + 1 + response[offset]]
offset = offset + 1 + response[offset]
result['address'] = str(response[offset + 1 : offset + 1 + response[offset]])
offset = offset + 1 + response[offset]
result['chainCode'] = response[offset : offset + 32]
return result
def getTrustedInput(self, transaction, index):
result = {}
# Header
apdu = [ self.BTCHIP_CLA, self.BTCHIP_INS_GET_TRUSTED_INPUT, 0x00, 0x00 ]
params = bytearray.fromhex("%.8x" % (index))
params.extend(transaction.version)
writeVarint(len(transaction.inputs), params)
apdu.append(len(params))
apdu.extend(params)
self.dongle.exchange(bytearray(apdu))
# Each input
for trinput in transaction.inputs:
apdu = [ self.BTCHIP_CLA, self.BTCHIP_INS_GET_TRUSTED_INPUT, 0x80, 0x00 ]
params = bytearray(trinput.prevOut)
writeVarint(len(trinput.script), params)
apdu.append(len(params))
apdu.extend(params)
self.dongle.exchange(bytearray(apdu))
offset = 0
while True:
blockLength = 251
if ((offset + blockLength) < len(trinput.script)):
dataLength = blockLength
else:
dataLength = len(trinput.script) - offset
params = bytearray(trinput.script[offset : offset + dataLength])
if ((offset + dataLength) == len(trinput.script)):
params.extend(trinput.sequence)
apdu = [ self.BTCHIP_CLA, self.BTCHIP_INS_GET_TRUSTED_INPUT, 0x80, 0x00, len(params) ]
apdu.extend(params)
self.dongle.exchange(bytearray(apdu))
offset += dataLength
if (offset >= len(trinput.script)):
break
# Number of outputs
apdu = [ self.BTCHIP_CLA, self.BTCHIP_INS_GET_TRUSTED_INPUT, 0x80, 0x00 ]
params = []
writeVarint(len(transaction.outputs), params)
apdu.append(len(params))
apdu.extend(params)
self.dongle.exchange(bytearray(apdu))
# Each output
indexOutput = 0
for troutput in transaction.outputs:
apdu = [ self.BTCHIP_CLA, self.BTCHIP_INS_GET_TRUSTED_INPUT, 0x80, 0x00 ]
params = bytearray(troutput.amount)
writeVarint(len(troutput.script), params)
apdu.append(len(params))
apdu.extend(params)
self.dongle.exchange(bytearray(apdu))
offset = 0
while (offset < len(troutput.script)):
blockLength = 255
if ((offset + blockLength) < len(troutput.script)):
dataLength = blockLength
else:
dataLength = len(troutput.script) - offset
apdu = [ self.BTCHIP_CLA, self.BTCHIP_INS_GET_TRUSTED_INPUT, 0x80, 0x00, dataLength ]
apdu.extend(troutput.script[offset : offset + dataLength])
self.dongle.exchange(bytearray(apdu))
offset += dataLength
# Locktime
apdu = [ self.BTCHIP_CLA, self.BTCHIP_INS_GET_TRUSTED_INPUT, 0x80, 0x00, len(transaction.lockTime) ]
apdu.extend(transaction.lockTime)
response = self.dongle.exchange(bytearray(apdu))
result['trustedInput'] = True
result['value'] = response
return result
def startUntrustedTransaction(self, newTransaction, inputIndex, outputList, redeemScript, version=0x01, cashAddr=False, continueSegwit=False):
# Start building a fake transaction with the passed inputs
segwit = False
if newTransaction:
for passedOutput in outputList:
if ('witness' in passedOutput) and passedOutput['witness']:
segwit = True
break
if newTransaction:
if segwit:
p2 = 0x03 if cashAddr else 0x02
else:
p2 = 0x00
else:
p2 = 0x10 if continueSegwit else 0x80
apdu = [ self.BTCHIP_CLA, self.BTCHIP_INS_HASH_INPUT_START, 0x00, p2 ]
params = bytearray([version, 0x00, 0x00, 0x00])
writeVarint(len(outputList), params)
apdu.append(len(params))
apdu.extend(params)
self.dongle.exchange(bytearray(apdu))
# Loop for each input
currentIndex = 0
for passedOutput in outputList:
if ('sequence' in passedOutput) and passedOutput['sequence']:
sequence = bytearray(unhexlify(passedOutput['sequence']))
else:
sequence = bytearray([0xFF, 0xFF, 0xFF, 0xFF]) # default sequence
apdu = [ self.BTCHIP_CLA, self.BTCHIP_INS_HASH_INPUT_START, 0x80, 0x00 ]
params = []
script = bytearray(redeemScript)
if ('trustedInput' in passedOutput) and passedOutput['trustedInput']:
params.append(0x01)
elif ('witness' in passedOutput) and passedOutput['witness']:
params.append(0x02)
else:
params.append(0x00)
if ('trustedInput' in passedOutput) and passedOutput['trustedInput']:
params.append(len(passedOutput['value']))
params.extend(passedOutput['value'])
if currentIndex != inputIndex:
script = bytearray()
writeVarint(len(script), params)
apdu.append(len(params))
apdu.extend(params)
self.dongle.exchange(bytearray(apdu))
offset = 0
while(offset < len(script)):
blockLength = 255
if ((offset + blockLength) < len(script)):
dataLength = blockLength
else:
dataLength = len(script) - offset
params = script[offset : offset + dataLength]
if ((offset + dataLength) == len(script)):
params.extend(sequence)
apdu = [ self.BTCHIP_CLA, self.BTCHIP_INS_HASH_INPUT_START, 0x80, 0x00, len(params) ]
apdu.extend(params)
self.dongle.exchange(bytearray(apdu))
offset += blockLength
if len(script) == 0:
apdu = [ self.BTCHIP_CLA, self.BTCHIP_INS_HASH_INPUT_START, 0x80, 0x00, len(sequence) ]
apdu.extend(sequence)
self.dongle.exchange(bytearray(apdu))
currentIndex += 1
def finalizeInput(self, outputAddress, amount, fees, changePath, rawTx=None):
alternateEncoding = False
donglePath = parse_bip32_path(changePath)
if self.needKeyCache:
self.resolvePublicKeysInPath(changePath)
result = {}
outputs = None
if rawTx is not None:
try:
fullTx = bitcoinTransaction(bytearray(rawTx))
outputs = fullTx.serializeOutputs()
if len(donglePath) != 0:
apdu = [ self.BTCHIP_CLA, self.BTCHIP_INS_HASH_INPUT_FINALIZE_FULL, 0xFF, 0x00 ]
params = []
params.extend(donglePath)
apdu.append(len(params))
apdu.extend(params)
response = self.dongle.exchange(bytearray(apdu))
offset = 0
while (offset < len(outputs)):
blockLength = self.scriptBlockLength
if ((offset + blockLength) < len(outputs)):
dataLength = blockLength
p1 = 0x00
else:
dataLength = len(outputs) - offset
p1 = 0x80
apdu = [ self.BTCHIP_CLA, self.BTCHIP_INS_HASH_INPUT_FINALIZE_FULL, \
p1, 0x00, dataLength ]
apdu.extend(outputs[offset : offset + dataLength])
response = self.dongle.exchange(bytearray(apdu))
offset += dataLength
alternateEncoding = True
except Exception:
pass
if not alternateEncoding:
apdu = [ self.BTCHIP_CLA, self.BTCHIP_INS_HASH_INPUT_FINALIZE, 0x02, 0x00 ]
params = []
params.append(len(outputAddress))
params.extend(bytearray(outputAddress))
writeHexAmountBE(btc_to_satoshi(str(amount)), params)
writeHexAmountBE(btc_to_satoshi(str(fees)), params)
params.extend(donglePath)
apdu.append(len(params))
apdu.extend(params)
response = self.dongle.exchange(bytearray(apdu))
result['confirmationNeeded'] = response[1 + response[0]] != 0x00
result['confirmationType'] = response[1 + response[0]]
if result['confirmationType'] == 0x02:
result['keycardData'] = response[1 + response[0] + 1:]
if result['confirmationType'] == 0x03:
offset = 1 + response[0] + 1
keycardDataLength = response[offset]
offset = offset + 1
result['keycardData'] = response[offset : offset + keycardDataLength]
offset = offset + keycardDataLength
result['secureScreenData'] = response[offset:]
if result['confirmationType'] == 0x04:
offset = 1 + response[0] + 1
keycardDataLength = response[offset]
result['keycardData'] = response[offset + 1 : offset + 1 + keycardDataLength]
if outputs == None:
result['outputData'] = response[1 : 1 + response[0]]
else:
result['outputData'] = outputs
return result
def finalizeInputFull(self, outputData):
result = {}
offset = 0
encryptedOutputData = b""
while (offset < len(outputData)):
blockLength = self.scriptBlockLength
if ((offset + blockLength) < len(outputData)):
dataLength = blockLength
p1 = 0x00
else:
dataLength = len(outputData) - offset
p1 = 0x80
apdu = [ self.BTCHIP_CLA, self.BTCHIP_INS_HASH_INPUT_FINALIZE_FULL, \
p1, 0x00, dataLength ]
apdu.extend(outputData[offset : offset + dataLength])
response = self.dongle.exchange(bytearray(apdu))
encryptedOutputData = encryptedOutputData + response[1 : 1 + response[0]]
offset += dataLength
if len(response) > 1:
result['confirmationNeeded'] = response[1 + response[0]] != 0x00
result['confirmationType'] = response[1 + response[0]]
else:
# Support for old style API before 1.0.2
result['confirmationNeeded'] = response[0] != 0x00
result['confirmationType'] = response[0]
if result['confirmationType'] == 0x02:
result['keycardData'] = response[1 + response[0] + 1:] # legacy
if result['confirmationType'] == 0x03:
offset = 1 + response[0] + 1
keycardDataLength = response[offset]
offset = offset + 1
result['keycardData'] = response[offset : offset + keycardDataLength]
offset = offset + keycardDataLength
result['secureScreenData'] = response[offset:]
result['encryptedOutputData'] = encryptedOutputData
if result['confirmationType'] == 0x04:
offset = 1 + response[0] + 1
keycardDataLength = response[offset]
result['keycardData'] = response[offset + 1 : offset + 1 + keycardDataLength]
return result
def untrustedHashSign(self, path, pin="", lockTime=0, sighashType=0x01):
if isinstance(pin, str):
pin = pin.encode('utf-8')
donglePath = parse_bip32_path(path)
if self.needKeyCache:
self.resolvePublicKeysInPath(path)
apdu = [ self.BTCHIP_CLA, self.BTCHIP_INS_HASH_SIGN, 0x00, 0x00 ]
params = []
params.extend(donglePath)
params.append(len(pin))
params.extend(bytearray(pin))
writeUint32BE(lockTime, params)
params.append(sighashType)
apdu.append(len(params))
apdu.extend(params)
result = self.dongle.exchange(bytearray(apdu))
result[0] = 0x30
return result
def signMessagePrepareV1(self, path, message):
donglePath = parse_bip32_path(path)
if self.needKeyCache:
self.resolvePublicKeysInPath(path)
result = {}
apdu = [ self.BTCHIP_CLA, self.BTCHIP_INS_SIGN_MESSAGE, 0x00, 0x00 ]
params = []
params.extend(donglePath)
params.append(len(message))
params.extend(bytearray(message))
apdu.append(len(params))
apdu.extend(params)
response = self.dongle.exchange(bytearray(apdu))
result['confirmationNeeded'] = response[0] != 0x00
result['confirmationType'] = response[0]
if result['confirmationType'] == 0x02:
result['keycardData'] = response[1:]
if result['confirmationType'] == 0x03:
result['secureScreenData'] = response[1:]
return result
def signMessagePrepareV2(self, path, message):
donglePath = parse_bip32_path(path)
if self.needKeyCache:
self.resolvePublicKeysInPath(path)
result = {}
offset = 0
encryptedOutputData = b""
while (offset < len(message)):
params = [];
if offset == 0:
params.extend(donglePath)
params.append((len(message) >> 8) & 0xff)
params.append(len(message) & 0xff)
p2 = 0x01
else:
p2 = 0x80
blockLength = 255 - len(params)
if ((offset + blockLength) < len(message)):
dataLength = blockLength
else:
dataLength = len(message) - offset
params.extend(bytearray(message[offset : offset + dataLength]))
apdu = [ self.BTCHIP_CLA, self.BTCHIP_INS_SIGN_MESSAGE, 0x00, p2 ]
apdu.append(len(params))
apdu.extend(params)
response = self.dongle.exchange(bytearray(apdu))
encryptedOutputData = encryptedOutputData + response[1 : 1 + response[0]]
offset += blockLength
result['confirmationNeeded'] = response[1 + response[0]] != 0x00
result['confirmationType'] = response[1 + response[0]]
if result['confirmationType'] == 0x03:
offset = 1 + response[0] + 1
result['secureScreenData'] = response[offset:]
result['encryptedOutputData'] = encryptedOutputData
return result
def signMessagePrepare(self, path, message):
try:
result = self.signMessagePrepareV2(path, message)
except BTChipException as e:
if (e.sw == 0x6b00): # Old firmware version, try older method
result = self.signMessagePrepareV1(path, message)
else:
raise
return result
def signMessageSign(self, pin=""):
if isinstance(pin, str):
pin = pin.encode('utf-8')
apdu = [ self.BTCHIP_CLA, self.BTCHIP_INS_SIGN_MESSAGE, 0x80, 0x00 ]
params = []
if pin is not None:
params.append(len(pin))
params.extend(bytearray(pin))
else:
params.append(0x00)
apdu.append(len(params))
apdu.extend(params)
response = self.dongle.exchange(bytearray(apdu))
return response
def setup(self, operationModeFlags, featuresFlag, keyVersion, keyVersionP2SH, userPin, wipePin, keymapEncoding, seed=None, developerKey=None):
if isinstance(userPin, str):
userPin = userPin.encode('utf-8')
result = {}
apdu = [ self.BTCHIP_CLA, self.BTCHIP_INS_SETUP, 0x00, 0x00 ]
params = [ operationModeFlags, featuresFlag, keyVersion, keyVersionP2SH ]
params.append(len(userPin))
params.extend(bytearray(userPin))
if wipePin is not None:
if isinstance(wipePin, str):
wipePin = wipePin.encode('utf-8')
params.append(len(wipePin))
params.extend(bytearray(wipePin))
else:
params.append(0x00)
if seed is not None:
if len(seed) < 32 or len(seed) > 64:
raise BTChipException("Invalid seed length")
params.append(len(seed))
params.extend(seed)
else:
params.append(0x00)
if developerKey is not None:
params.append(len(developerKey))
params.extend(developerKey)
else:
params.append(0x00)
apdu.append(len(params))
apdu.extend(params)
response = self.dongle.exchange(bytearray(apdu))
result['trustedInputKey'] = response[0:16]
result['developerKey'] = response[16:]
self.setKeymapEncoding(keymapEncoding)
try:
self.setTypingBehaviour(0xff, 0xff, 0xff, 0x10)
except BTChipException as e:
if (e.sw == 0x6700): # Old firmware version, command not supported
pass
else:
raise
return result
def setKeymapEncoding(self, keymapEncoding):
apdu = [ self.BTCHIP_CLA, self.BTCHIP_INS_SET_KEYMAP, 0x00, 0x00 ]
apdu.append(len(keymapEncoding))
apdu.extend(keymapEncoding)
self.dongle.exchange(bytearray(apdu))
def setTypingBehaviour(self, unitDelayStart, delayStart, unitDelayKey, delayKey):
apdu = [ self.BTCHIP_CLA, self.BTCHIP_INS_SET_KEYMAP, 0x01, 0x00 ]
params = []
writeUint32BE(unitDelayStart, params)
writeUint32BE(delayStart, params)
writeUint32BE(unitDelayKey, params)
writeUint32BE(delayKey, params)
apdu.append(len(params))
apdu.extend(params)
self.dongle.exchange(bytearray(apdu))
def getOperationMode(self):
apdu = [ self.BTCHIP_CLA, self.BTCHIP_INS_GET_OPERATION_MODE, 0x00, 0x00, 0x00]
response = self.dongle.exchange(bytearray(apdu))
return response[0]
def setOperationMode(self, operationMode):
if operationMode != btchip.OPERATION_MODE_WALLET \
and operationMode != btchip.OPERATION_MODE_RELAXED_WALLET \
and operationMode != btchip.OPERATION_MODE_SERVER \
and operationMode != btchip.OPERATION_MODE_DEVELOPER:
raise BTChipException("Invalid operation mode")
apdu = [ self.BTCHIP_CLA, self.BTCHIP_INS_SET_OPERATION_MODE, 0x00, 0x00, 0x01, operationMode ]
self.dongle.exchange(bytearray(apdu))
def enableAlternate2fa(self, persistent):
if persistent:
p1 = 0x02
else:
p1 = 0x01
apdu = [ self.BTCHIP_CLA, self.BTCHIP_INS_SET_OPERATION_MODE, p1, 0x00, 0x01, btchip.OPERATION_MODE_WALLET ]
self.dongle.exchange(bytearray(apdu))
def getFirmwareVersion(self):
result = {}
apdu = [ self.BTCHIP_CLA, self.BTCHIP_INS_GET_FIRMWARE_VERSION, 0x00, 0x00, 0x00 ]
try:
response = self.dongle.exchange(bytearray(apdu))
except BTChipException as e:
if (e.sw == 0x6985):
response = [0x00, 0x00, 0x01, 0x04, 0x03 ]
pass
else:
raise
result['compressedKeys'] = (response[0] == 0x01)
result['version'] = "%d.%d.%d" % (response[2], response[3], response[4])
result['specialVersion'] = response[1]
return result
def getRandom(self, size):
if size > 255:
raise BTChipException("Invalid size")
apdu = [ self.BTCHIP_CLA, self.BTCHIP_INS_GET_RANDOM, 0x00, 0x00, size ]
return self.dongle.exchange(bytearray(apdu))
def getPOSSeedKey(self):
result = {}
apdu = [ self.BTCHIP_CLA, self.BTCHIP_INS_GET_POS_SEED, 0x01, 0x00, 0x00 ]
return self.dongle.exchange(bytearray(apdu))
def getPOSEncryptedSeed(self):
result = {}
apdu = [ self.BTCHIP_CLA, self.BTCHIP_INS_GET_POS_SEED, 0x02, 0x00, 0x00 ]
return self.dongle.exchange(bytearray(apdu))
def importPrivateKey(self, data, isSeed=False):
apdu = [ self.BTCHIP_CLA, self.BTCHIP_INS_IMPORT_PRIVATE_KEY, (0x02 if isSeed else 0x01), 0x00 ]
apdu.append(len(data))
apdu.extend(data)
return self.dongle.exchange(bytearray(apdu))
def getPublicKey(self, encodedPrivateKey):
result = {}
apdu = [ self.BTCHIP_CLA, self.BTCHIP_INS_GET_PUBLIC_KEY, 0x00, 0x00 ]
apdu.append(len(encodedPrivateKey) + 1)
apdu.append(len(encodedPrivateKey))
apdu.extend(encodedPrivateKey)
response = self.dongle.exchange(bytearray(apdu))
offset = 1
result['publicKey'] = response[offset + 1 : offset + 1 + response[offset]]
offset = offset + 1 + response[offset]
if response[0] == 0x02:
result['chainCode'] = response[offset : offset + 32]
offset = offset + 32
result['depth'] = response[offset]
offset = offset + 1
result['parentFingerprint'] = response[offset : offset + 4]
offset = offset + 4
result['childNumber'] = response[offset : offset + 4]
return result
def deriveBip32Key(self, encodedPrivateKey, path):
donglePath = parse_bip32_path(path)
if self.needKeyCache:
self.resolvePublicKeysInPath(path)
offset = 1
currentEncodedPrivateKey = encodedPrivateKey
while (offset < len(donglePath)):
apdu = [ self.BTCHIP_CLA, self.BTCHIP_INS_DERIVE_BIP32_KEY, 0x00, 0x00 ]
apdu.append(len(currentEncodedPrivateKey) + 1 + 4)
apdu.append(len(currentEncodedPrivateKey))
apdu.extend(currentEncodedPrivateKey)
apdu.extend(donglePath[offset : offset + 4])
currentEncodedPrivateKey = self.dongle.exchange(bytearray(apdu))
offset = offset + 4
return currentEncodedPrivateKey
def signImmediate(self, encodedPrivateKey, data, deterministic=True):
apdu = [ self.BTCHIP_CLA, self.BTCHIP_INS_SIGNVERIFY_IMMEDIATE, 0x00, (0x80 if deterministic else 0x00) ]
apdu.append(len(encodedPrivateKey) + len(data) + 2);
apdu.append(len(encodedPrivateKey))
apdu.extend(encodedPrivateKey)
apdu.append(len(data))
apdu.extend(data)
return self.dongle.exchange(bytearray(apdu))
# Functions dedicated to the Java Card interface when no proprietary API is available
def parse_bip32_path_internal(self, path):
if len(path) == 0:
return []
result = []
elements = path.split('/')
for pathElement in elements:
element = pathElement.split('\'')
if len(element) == 1:
result.append(int(element[0]))
else:
result.append(0x80000000 | int(element[0]))
return result
def serialize_bip32_path_internal(self, path):
result = []
for pathElement in path:
writeUint32BE(pathElement, result)
return bytearray([ len(path) ] + result)
def resolvePublicKey(self, path):
expandedPath = self.serialize_bip32_path_internal(path)
apdu = [ self.BTCHIP_JC_EXT_CLA, self.BTCHIP_INS_EXT_CACHE_HAS_PUBLIC_KEY, 0x00, 0x00 ]
apdu.append(len(expandedPath))
apdu.extend(expandedPath)
result = self.dongle.exchange(bytearray(apdu))
if (result[0] == 0):
# Not present, need to be inserted into the cache
apdu = [ self.BTCHIP_JC_EXT_CLA, self.BTCHIP_INS_EXT_GET_HALF_PUBLIC_KEY, 0x00, 0x00 ]
apdu.append(len(expandedPath))
apdu.extend(expandedPath)
result = self.dongle.exchange(bytearray(apdu))
hashData = result[0:32]
keyX = result[32:64]
signature = result[64:]
keyXY = recoverKey(signature, hashData, keyX)
apdu = [ self.BTCHIP_JC_EXT_CLA, self.BTCHIP_INS_EXT_CACHE_PUT_PUBLIC_KEY, 0x00, 0x00 ]
apdu.append(len(expandedPath) + 65)
apdu.extend(expandedPath)
apdu.extend(keyXY)
self.dongle.exchange(bytearray(apdu))
def resolvePublicKeysInPath(self, path):
splitPath = self.parse_bip32_path_internal(path)
# Locate the first public key in path
offset = 0
startOffset = 0
while(offset < len(splitPath)):
if (splitPath[offset] < 0x80000000):
startOffset = offset
break
offset = offset + 1
if startOffset != 0:
searchPath = splitPath[0:startOffset - 1]
offset = startOffset - 1
while(offset < len(splitPath)):
searchPath = searchPath + [ splitPath[offset] ]
self.resolvePublicKey(searchPath)
offset = offset + 1
self.resolvePublicKey(splitPath)
def getJCExtendedFeatures(self):
result = {}
apdu = [ self.BTCHIP_JC_EXT_CLA, self.BTCHIP_INS_EXT_CACHE_GET_FEATURES, 0x00, 0x00, 0x00 ]
response = self.dongle.exchange(bytearray(apdu))
result['proprietaryApi'] = ((response[0] & 0x01) != 0)
return result
btchip-python-0.1.31/btchip/btchipComm.py 0000664 0000000 0000000 00000016115 13736561771 0020320 0 ustar 00root root 0000000 0000000 """
*******************************************************************************
* BTChip Bitcoin Hardware Wallet Python API
* (c) 2014 BTChip - 1BTChip7VfTnrPra5jqci7ejnMguuHogTn
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
********************************************************************************
"""
from abc import ABCMeta, abstractmethod
from .btchipException import *
from .ledgerWrapper import wrapCommandAPDU, unwrapResponseAPDU
from binascii import hexlify
import time
import os
import struct
import socket
try:
import hid
HID = True
except ImportError:
HID = False
try:
from smartcard.Exceptions import NoCardException
from smartcard.System import readers
from smartcard.util import toHexString, toBytes
SCARD = True
except ImportError:
SCARD = False
class DongleWait(object):
__metaclass__ = ABCMeta
@abstractmethod
def waitFirstResponse(self, timeout):
pass
class Dongle(object):
__metaclass__ = ABCMeta
@abstractmethod
def exchange(self, apdu, timeout=20000):
pass
@abstractmethod
def close(self):
pass
def setWaitImpl(self, waitImpl):
self.waitImpl = waitImpl
class HIDDongleHIDAPI(Dongle, DongleWait):
def __init__(self, device, ledger=False, debug=False):
self.device = device
self.ledger = ledger
self.debug = debug
self.waitImpl = self
self.opened = True
def exchange(self, apdu, timeout=20000):
if self.debug:
print("=> %s" % hexlify(apdu))
if self.ledger:
apdu = wrapCommandAPDU(0x0101, apdu, 64)
padSize = len(apdu) % 64
tmp = apdu
if padSize != 0:
tmp.extend([0] * (64 - padSize))
offset = 0
while(offset != len(tmp)):
data = tmp[offset:offset + 64]
data = bytearray([0]) + data
self.device.write(data)
offset += 64
dataLength = 0
dataStart = 2
result = self.waitImpl.waitFirstResponse(timeout)
if not self.ledger:
if result[0] == 0x61: # 61xx : data available
self.device.set_nonblocking(False)
dataLength = result[1]
dataLength += 2
if dataLength > 62:
remaining = dataLength - 62
while(remaining != 0):
if remaining > 64:
blockLength = 64
else:
blockLength = remaining
result.extend(bytearray(self.device.read(65))[0:blockLength])
remaining -= blockLength
swOffset = dataLength
dataLength -= 2
self.device.set_nonblocking(True)
else:
swOffset = 0
else:
self.device.set_nonblocking(False)
while True:
response = unwrapResponseAPDU(0x0101, result, 64)
if response is not None:
result = response
dataStart = 0
swOffset = len(response) - 2
dataLength = len(response) - 2
self.device.set_nonblocking(True)
break
result.extend(bytearray(self.device.read(65)))
sw = (result[swOffset] << 8) + result[swOffset + 1]
response = result[dataStart : dataLength + dataStart]
if self.debug:
print("<= %s%.2x" % (hexlify(response), sw))
if sw != 0x9000:
raise BTChipException("Invalid status %04x" % sw, sw)
return response
def waitFirstResponse(self, timeout):
start = time.time()
data = ""
while len(data) == 0:
data = self.device.read(65)
if not len(data):
if time.time() - start > timeout:
raise BTChipException("Timeout")
time.sleep(0.02)
return bytearray(data)
def close(self):
if self.opened:
try:
self.device.close()
except Exception:
pass
self.opened = False
class DongleSmartcard(Dongle):
def __init__(self, device, debug=False):
self.device = device
self.debug = debug
self.waitImpl = self
self.opened = True
def exchange(self, apdu, timeout=20000):
if self.debug:
print("=> %s" % hexlify(apdu))
response, sw1, sw2 = self.device.transmit(toBytes(hexlify(apdu)))
sw = (sw1 << 8) | sw2
if self.debug:
print("<= %s%.2x" % (toHexString(response).replace(" ", ""), sw))
if sw != 0x9000:
raise BTChipException("Invalid status %04x" % sw, sw)
return bytearray(response)
def close(self):
if self.opened:
try:
self.device.disconnect()
except Exception:
pass
self.opened = False
class DongleServer(Dongle):
def __init__(self, server, port, debug=False):
self.server = server
self.port = port
self.debug = debug
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
self.socket.connect((self.server, self.port))
except Exception:
raise BTChipException("Proxy connection failed")
def exchange(self, apdu, timeout=20000):
if self.debug:
print("=> %s" % hexlify(apdu))
self.socket.send(struct.pack(">I", len(apdu)))
self.socket.send(apdu)
size = struct.unpack(">I", self.socket.recv(4))[0]
response = self.socket.recv(size)
sw = struct.unpack(">H", self.socket.recv(2))[0]
if self.debug:
print("<= %s%.2x" % (hexlify(response), sw))
if sw != 0x9000:
raise BTChipException("Invalid status %04x" % sw, sw)
return bytearray(response)
def close(self):
try:
self.socket.close()
except Exception:
pass
def getDongle(debug=False):
dev = None
hidDevicePath = None
ledger = False
if HID:
for hidDevice in hid.enumerate(0, 0):
if hidDevice['vendor_id'] == 0x2581 and hidDevice['product_id'] == 0x2b7c:
hidDevicePath = hidDevice['path']
if hidDevice['vendor_id'] == 0x2581 and hidDevice['product_id'] == 0x3b7c:
hidDevicePath = hidDevice['path']
ledger = True
if hidDevice['vendor_id'] == 0x2581 and hidDevice['product_id'] == 0x4b7c:
hidDevicePath = hidDevice['path']
ledger = True
if hidDevice['vendor_id'] == 0x2c97:
if ('interface_number' in hidDevice and hidDevice['interface_number'] == 0) or ('usage_page' in hidDevice and hidDevice['usage_page'] == 0xffa0):
hidDevicePath = hidDevice['path']
ledger = True
if hidDevice['vendor_id'] == 0x2581 and hidDevice['product_id'] == 0x1807:
hidDevicePath = hidDevice['path']
if hidDevicePath is not None:
dev = hid.device()
dev.open_path(hidDevicePath)
dev.set_nonblocking(True)
return HIDDongleHIDAPI(dev, ledger, debug)
if SCARD:
connection = None
for reader in readers():
try:
connection = reader.createConnection()
connection.connect()
response, sw1, sw2 = connection.transmit(toBytes("00A4040010FF4C4547522E57414C5430312E493031"))
sw = (sw1 << 8) | sw2
if sw == 0x9000:
break
else:
connection.disconnect()
connection = None
except Exception:
connection = None
pass
if connection is not None:
return DongleSmartcard(connection, debug)
if (os.getenv("LEDGER_PROXY_ADDRESS") is not None) and (os.getenv("LEDGER_PROXY_PORT") is not None):
return DongleServer(os.getenv("LEDGER_PROXY_ADDRESS"), int(os.getenv("LEDGER_PROXY_PORT")), debug)
raise BTChipException("No dongle found")
btchip-python-0.1.31/btchip/btchipException.py 0000664 0000000 0000000 00000001774 13736561771 0021370 0 ustar 00root root 0000000 0000000 """
*******************************************************************************
* BTChip Bitcoin Hardware Wallet Python API
* (c) 2014 BTChip - 1BTChip7VfTnrPra5jqci7ejnMguuHogTn
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
********************************************************************************
"""
class BTChipException(Exception):
def __init__(self, message, sw=0x6f00):
self.message = message
self.sw = sw
def __str__(self):
buf = "Exception : " + self.message
return buf
btchip-python-0.1.31/btchip/btchipFirmwareWizard.py 0000664 0000000 0000000 00000001775 13736561771 0022370 0 ustar 00root root 0000000 0000000 """
*******************************************************************************
* BTChip Bitcoin Hardware Wallet Python API
* (c) 2014 BTChip - 1BTChip7VfTnrPra5jqci7ejnMguuHogTn
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
********************************************************************************
"""
def checkFirmware(version):
return True
def updateFirmware():
raise Exception("Unsupported BTChip firmware - please update your firmware from https://firmwareupdate.hardwarewallet.com")
btchip-python-0.1.31/btchip/btchipHelpers.py 0000664 0000000 0000000 00000005231 13736561771 0021024 0 ustar 00root root 0000000 0000000 """
*******************************************************************************
* BTChip Bitcoin Hardware Wallet Python API
* (c) 2014 BTChip - 1BTChip7VfTnrPra5jqci7ejnMguuHogTn
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
********************************************************************************
"""
import decimal
import re
# from pycoin
SATOSHI_PER_COIN = decimal.Decimal(1e8)
COIN_PER_SATOSHI = decimal.Decimal(1)/SATOSHI_PER_COIN
def satoshi_to_btc(satoshi_count):
if satoshi_count == 0:
return decimal.Decimal(0)
r = satoshi_count * COIN_PER_SATOSHI
return r.normalize()
def btc_to_satoshi(btc):
return int(decimal.Decimal(btc) * SATOSHI_PER_COIN)
# /from pycoin
def writeUint32BE(value, buffer):
buffer.append((value >> 24) & 0xff)
buffer.append((value >> 16) & 0xff)
buffer.append((value >> 8) & 0xff)
buffer.append(value & 0xff)
return buffer
def writeUint32LE(value, buffer):
buffer.append(value & 0xff)
buffer.append((value >> 8) & 0xff)
buffer.append((value >> 16) & 0xff)
buffer.append((value >> 24) & 0xff)
return buffer
def writeHexAmount(value, buffer):
buffer.append(value & 0xff)
buffer.append((value >> 8) & 0xff)
buffer.append((value >> 16) & 0xff)
buffer.append((value >> 24) & 0xff)
buffer.append((value >> 32) & 0xff)
buffer.append((value >> 40) & 0xff)
buffer.append((value >> 48) & 0xff)
buffer.append((value >> 56) & 0xff)
return buffer
def writeHexAmountBE(value, buffer):
buffer.append((value >> 56) & 0xff)
buffer.append((value >> 48) & 0xff)
buffer.append((value >> 40) & 0xff)
buffer.append((value >> 32) & 0xff)
buffer.append((value >> 24) & 0xff)
buffer.append((value >> 16) & 0xff)
buffer.append((value >> 8) & 0xff)
buffer.append(value & 0xff)
return buffer
def parse_bip32_path(path):
if len(path) == 0:
return bytearray([ 0 ])
result = []
elements = path.split('/')
if len(elements) > 10:
raise BTChipException("Path too long")
for pathElement in elements:
element = re.split('\'|h|H', pathElement)
if len(element) == 1:
writeUint32BE(int(element[0]), result)
else:
writeUint32BE(0x80000000 | int(element[0]), result)
return bytearray([ len(elements) ] + result)
btchip-python-0.1.31/btchip/btchipKeyRecovery.py 0000664 0000000 0000000 00000003500 13736561771 0021666 0 ustar 00root root 0000000 0000000 # From Electrum
import ecdsa
from ecdsa.curves import SECP256k1
from ecdsa.ellipticcurve import Point
from ecdsa.util import string_to_number, number_to_string
class MyVerifyingKey(ecdsa.VerifyingKey):
@classmethod
def from_signature(klass, sig, recid, h, curve):
""" See http://www.secg.org/download/aid-780/sec1-v2.pdf, chapter 4.1.6 """
from ecdsa import util, numbertheory
import msqr
curveFp = curve.curve
G = curve.generator
order = G.order()
# extract r,s from signature
r, s = util.sigdecode_string(sig, order)
# 1.1
x = r + (recid/2) * order
# 1.3
alpha = ( x * x * x + curveFp.a() * x + curveFp.b() ) % curveFp.p()
beta = msqr.modular_sqrt(alpha, curveFp.p())
y = beta if (beta - recid) % 2 == 0 else curveFp.p() - beta
# 1.4 the constructor checks that nR is at infinity
R = Point(curveFp, x, y, order)
# 1.5 compute e from message:
e = string_to_number(h)
minus_e = -e % order
# 1.6 compute Q = r^-1 (sR - eG)
inv_r = numbertheory.inverse_mod(r,order)
Q = inv_r * ( s * R + minus_e * G )
return klass.from_public_point( Q, curve )
def point_to_ser(P):
return ( '04'+('%064x'%P.x())+('%064x'%P.y()) ).decode('hex')
def recoverKey(signature, hashValue, keyX):
rLength = signature[3]
r = signature[4 : 4 + rLength]
sLength = signature[4 + rLength + 1]
s = signature[4 + rLength + 2:]
if rLength == 33:
r = r[1:]
if sLength == 33:
s = s[1:]
r = str(r)
s = str(s)
for i in range(4):
try:
key = MyVerifyingKey.from_signature(r + s, i, hashValue, curve = SECP256k1)
candidate = point_to_ser(key.pubkey.point)
if candidate[1:33] == keyX:
return candidate
except Exception:
pass
raise Exception("Key recovery failed")
btchip-python-0.1.31/btchip/btchipPersoWizard.py 0000664 0000000 0000000 00000030173 13736561771 0021676 0 ustar 00root root 0000000 0000000 """
*******************************************************************************
* BTChip Bitcoin Hardware Wallet Python API
* (c) 2014 BTChip - 1BTChip7VfTnrPra5jqci7ejnMguuHogTn
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
********************************************************************************
"""
import sys
from PyQt4 import QtCore, QtGui
from PyQt4.QtGui import QDialog, QMessageBox
try:
from mnemonic import Mnemonic
MNEMONIC = True
except Exception:
MNEMONIC = False
from .btchipComm import getDongle, DongleWait
from .btchip import btchip
from .btchipUtils import compress_public_key,format_transaction, get_regular_input_script
from .bitcoinTransaction import bitcoinTransaction
from .btchipException import BTChipException
import ui.personalization00start
import ui.personalization01seed
import ui.personalization02security
import ui.personalization03config
import ui.personalization04finalize
import ui.personalizationseedbackup01
import ui.personalizationseedbackup02
import ui.personalizationseedbackup03
import ui.personalizationseedbackup04
BTCHIP_DEBUG = False
def waitDongle(currentDialog, persoData):
try:
if persoData['client'] != None:
try:
persoData['client'].dongle.close()
except Exception:
pass
dongle = getDongle(BTCHIP_DEBUG)
persoData['client'] = btchip(dongle)
persoData['client'].getFirmwareVersion()['version'].split(".")
return True
except BTChipException as e:
if e.sw == 0x6faa:
QMessageBox.information(currentDialog, "BTChip Setup", "Please unplug the dongle and plug it again", "OK")
return False
if QMessageBox.question(currentDialog, "BTChip setup", "BTChip dongle not found. It might be in the wrong mode. Try unplugging und plugging it back in again, then press 'OK'", QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes) == QMessageBox.Yes:
return False
else:
raise Exception("Aborted by user")
except Exception as e:
if QMessageBox.question(currentDialog, "BTChip setup", "BTChip dongle not found. It might be in the wrong mode. Try unplugging und plugging it back in again, then press 'OK'", QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes) == QMessageBox.Yes:
return False
else:
raise Exception("Aborted by user")
class StartBTChipPersoDialog(QtGui.QDialog):
def __init__(self):
QDialog.__init__(self, None)
self.ui = ui.personalization00start.Ui_Dialog()
self.ui.setupUi(self)
self.ui.NextButton.clicked.connect(self.processNext)
self.ui.CancelButton.clicked.connect(self.processCancel)
def processNext(self):
persoData = {}
persoData['currencyCode'] = 0x00
persoData['currencyCodeP2SH'] = 0x05
persoData['client'] = None
dialog = SeedDialog(persoData, self)
persoData['main'] = self
dialog.exec_()
pass
def processCancel(self):
self.reject()
class SeedDialog(QtGui.QDialog):
def __init__(self, persoData, parent = None):
QDialog.__init__(self, parent)
self.persoData = persoData
self.ui = ui.personalization01seed.Ui_Dialog()
self.ui.setupUi(self)
self.ui.seed.setEnabled(False)
self.ui.RestoreWalletButton.toggled.connect(self.restoreWalletToggled)
self.ui.NextButton.clicked.connect(self.processNext)
self.ui.CancelButton.clicked.connect(self.processCancel)
if MNEMONIC:
self.mnemonic = Mnemonic('english')
self.ui.mnemonicNotAvailableLabel.hide()
def restoreWalletToggled(self, toggled):
self.ui.seed.setEnabled(toggled)
def processNext(self):
self.persoData['seed'] = None
if self.ui.RestoreWalletButton.isChecked():
# Check if it's an hexa string
seedText = str(self.ui.seed.text())
if len(seedText) == 0:
QMessageBox.warning(self, "Error", "Please enter a seed", "OK")
return
if seedText[-1] == 'X':
seedText = seedText[0:-1]
try:
self.persoData['seed'] = seedText.decode('hex')
except Exception:
pass
if self.persoData['seed'] == None:
if not MNEMONIC:
QMessageBox.warning(self, "Error", "Mnemonic API not available. Please install https://github.com/trezor/python-mnemonic", "OK")
return
if not self.mnemonic.check(seedText):
QMessageBox.warning(self, "Error", "Invalid mnemonic", "OK")
return
self.persoData['seed'] = Mnemonic.to_seed(seedText)
else:
if (len(self.persoData['seed']) < 32) or (len(self.persoData['seed']) > 64):
QMessageBox.warning(self, "Error", "Invalid seed length", "OK")
return
dialog = SecurityDialog(self.persoData, self)
self.hide()
dialog.exec_()
def processCancel(self):
self.reject()
self.persoData['main'].reject()
class SecurityDialog(QtGui.QDialog):
def __init__(self, persoData, parent = None):
QDialog.__init__(self, parent)
self.persoData = persoData
self.ui = ui.personalization02security.Ui_Dialog()
self.ui.setupUi(self)
self.ui.NextButton.clicked.connect(self.processNext)
self.ui.CancelButton.clicked.connect(self.processCancel)
def processNext(self):
if (self.ui.pin1.text() != self.ui.pin2.text()):
self.ui.pin1.setText("")
self.ui.pin2.setText("")
QMessageBox.warning(self, "Error", "PINs are not matching", "OK")
return
if (len(self.ui.pin1.text()) < 4):
QMessageBox.warning(self, "Error", "PIN must be at least 4 characteres long", "OK")
return
if (len(self.ui.pin1.text()) > 32):
QMessageBox.warning(self, "Error", "PIN is too long", "OK")
return
self.persoData['pin'] = str(self.ui.pin1.text())
self.persoData['hardened'] = self.ui.HardenedButton.isChecked()
dialog = ConfigDialog(self.persoData, self)
self.hide()
dialog.exec_()
def processCancel(self):
self.reject()
self.persoData['main'].reject()
class ConfigDialog(QtGui.QDialog):
def __init__(self, persoData, parent = None):
QDialog.__init__(self, parent)
self.persoData = persoData
self.ui = ui.personalization03config.Ui_Dialog()
self.ui.setupUi(self)
self.ui.NextButton.clicked.connect(self.processNext)
self.ui.CancelButton.clicked.connect(self.processCancel)
def processNext(self):
if (self.ui.qwertyButton.isChecked()):
self.persoData['keyboard'] = btchip.QWERTY_KEYMAP
elif (self.ui.qwertzButton.isChecked()):
self.persoData['keyboard'] = btchip.QWERTZ_KEYMAP
elif (self.ui.azertyButton.isChecked()):
self.persoData['keyboard'] = btchip.AZERTY_KEYMAP
try:
while not waitDongle(self, self.persoData):
pass
except Exception as e:
self.reject()
self.persoData['main'].reject()
mode = btchip.OPERATION_MODE_WALLET
if not self.persoData['hardened']:
mode = mode | btchip.OPERATION_MODE_SERVER
try:
self.persoData['client'].setup(mode, btchip.FEATURE_RFC6979, self.persoData['currencyCode'],
self.persoData['currencyCodeP2SH'], self.persoData['pin'], None,
self.persoData['keyboard'], self.persoData['seed'])
except BTChipException as e:
if e.sw == 0x6985:
QMessageBox.warning(self, "Error", "Dongle is already set up. Please insert a different one", "OK")
return
except Exception as e:
QMessageBox.warning(self, "Error", "Error performing setup", "OK")
return
if self.persoData['seed'] is None:
dialog = SeedBackupStart(self.persoData, self)
self.hide()
dialog.exec_()
else:
dialog = FinalizeDialog(self.persoData, self)
self.hide()
dialog.exec_()
def processCancel(self):
self.reject()
self.persoData['main'].reject()
class FinalizeDialog(QtGui.QDialog):
def __init__(self, persoData, parent = None):
QDialog.__init__(self, parent)
self.persoData = persoData
self.ui = ui.personalization04finalize.Ui_Dialog()
self.ui.setupUi(self)
self.ui.FinishButton.clicked.connect(self.finish)
try:
while not waitDongle(self, self.persoData):
pass
except Exception as e:
self.reject()
self.persoData['main'].reject()
attempts = self.persoData['client'].getVerifyPinRemainingAttempts()
self.ui.remainingAttemptsLabel.setText("Remaining attempts " + str(attempts))
def finish(self):
if (len(self.ui.pin1.text()) < 4):
QMessageBox.warning(self, "Error", "PIN must be at least 4 characteres long", "OK")
return
if (len(self.ui.pin1.text()) > 32):
QMessageBox.warning(self, "Error", "PIN is too long", "OK")
return
try:
self.persoData['client'].verifyPin(str(self.ui.pin1.text()))
except BTChipException as e:
if ((e.sw == 0x63c0) or (e.sw == 0x6985)):
QMessageBox.warning(self, "Error", "Invalid PIN - dongle has been reset. Please personalize again", "OK")
self.reject()
self.persoData['main'].reject()
if ((e.sw & 0xfff0) == 0x63c0):
attempts = e.sw - 0x63c0
self.ui.remainingAttemptsLabel.setText("Remaining attempts " + str(attempts))
QMessageBox.warning(self, "Error", "Invalid PIN - please unplug the dongle and plug it again before retrying", "OK")
try:
while not waitDongle(self, self.persoData):
pass
except Exception as e:
self.reject()
self.persoData['main'].reject()
return
except Exception as e:
QMessageBox.warning(self, "Error", "Unexpected error verifying PIN - aborting", "OK")
self.reject()
self.persoData['main'].reject()
return
if not self.persoData['hardened']:
try:
self.persoData['client'].setOperationMode(btchip.OPERATION_MODE_SERVER)
except Exception:
QMessageBox.warning(self, "Error", "Error switching to non hardened mode", "OK")
self.reject()
self.persoData['main'].reject()
return
QMessageBox.information(self, "BTChip Setup", "Setup completed. Please unplug the dongle and plug it again before use", "OK")
self.accept()
self.persoData['main'].accept()
class SeedBackupStart(QtGui.QDialog):
def __init__(self, persoData, parent = None):
QDialog.__init__(self, parent)
self.persoData = persoData
self.ui = ui.personalizationseedbackup01.Ui_Dialog()
self.ui.setupUi(self)
self.ui.NextButton.clicked.connect(self.processNext)
def processNext(self):
dialog = SeedBackupUnplug(self.persoData, self)
self.hide()
dialog.exec_()
class SeedBackupUnplug(QtGui.QDialog):
def __init__(self, persoData, parent = None):
QDialog.__init__(self, parent)
self.persoData = persoData
self.ui = ui.personalizationseedbackup02.Ui_Dialog()
self.ui.setupUi(self)
self.ui.NextButton.clicked.connect(self.processNext)
def processNext(self):
dialog = SeedBackupInstructions(self.persoData, self)
self.hide()
dialog.exec_()
class SeedBackupInstructions(QtGui.QDialog):
def __init__(self, persoData, parent = None):
QDialog.__init__(self, parent)
self.persoData = persoData
self.ui = ui.personalizationseedbackup03.Ui_Dialog()
self.ui.setupUi(self)
self.ui.NextButton.clicked.connect(self.processNext)
def processNext(self):
dialog = SeedBackupVerify(self.persoData, self)
self.hide()
dialog.exec_()
class SeedBackupVerify(QtGui.QDialog):
def __init__(self, persoData, parent = None):
QDialog.__init__(self, parent)
self.persoData = persoData
self.ui = ui.personalizationseedbackup04.Ui_Dialog()
self.ui.setupUi(self)
self.ui.seedOkButton.clicked.connect(self.seedOK)
self.ui.seedKoButton.clicked.connect(self.seedKO)
def seedOK(self):
dialog = FinalizeDialog(self.persoData, self)
self.hide()
dialog.exec_()
def seedKO(self):
finished = False
while not finished:
try:
while not waitDongle(self, self.persoData):
pass
except Exception as e:
pass
try:
self.persoData['client'].verifyPin("0")
except BTChipException as e:
if e.sw == 0x63c0:
QMessageBox.information(self, "BTChip Setup", "Dongle is reset and can be repersonalized", "OK")
finished = True
pass
if e.sw == 0x6faa:
QMessageBox.information(self, "BTChip Setup", "Please unplug the dongle and plug it again", "OK")
pass
except Exception as e:
pass
self.reject()
self.persoData['main'].reject()
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
dialog = StartBTChipPersoDialog()
dialog.show()
app.exec_()
btchip-python-0.1.31/btchip/btchipUtils.py 0000664 0000000 0000000 00000006530 13736561771 0020525 0 ustar 00root root 0000000 0000000 """
*******************************************************************************
* BTChip Bitcoin Hardware Wallet Python API
* (c) 2014 BTChip - 1BTChip7VfTnrPra5jqci7ejnMguuHogTn
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
********************************************************************************
"""
from .btchipException import *
from .bitcoinTransaction import *
from .btchipHelpers import *
def compress_public_key(publicKey):
if publicKey[0] == 0x04:
if (publicKey[64] & 1) != 0:
prefix = 0x03
else:
prefix = 0x02
result = [prefix]
result.extend(publicKey[1:33])
return bytearray(result)
elif publicKey[0] == 0x03 or publicKey[0] == 0x02:
return publicKey
else:
raise BTChipException("Invalid public key format")
def format_transaction(dongleOutputData, trustedInputsAndInputScripts, version=0x01, lockTime=0):
transaction = bitcoinTransaction()
transaction.version = []
writeUint32LE(version, transaction.version)
for item in trustedInputsAndInputScripts:
newInput = bitcoinInput()
newInput.prevOut = item[0][4:4+36]
newInput.script = item[1]
if len(item) > 2:
newInput.sequence = bytearray(item[2].decode('hex'))
else:
newInput.sequence = bytearray([0xff, 0xff, 0xff, 0xff])
transaction.inputs.append(newInput)
result = transaction.serialize(True)
result.extend(dongleOutputData)
writeUint32LE(lockTime, result)
return bytearray(result)
def get_regular_input_script(sigHashtype, publicKey):
if len(sigHashtype) >= 0x4c:
raise BTChipException("Invalid sigHashtype")
if len(publicKey) >= 0x4c:
raise BTChipException("Invalid publicKey")
result = [ len(sigHashtype) ]
result.extend(sigHashtype)
result.append(len(publicKey))
result.extend(publicKey)
return bytearray(result)
def write_pushed_data_size(data, buffer):
if (len(data) > 0xffff):
raise BTChipException("unsupported encoding")
if (len(data) < 0x4c):
buffer.append(len(data))
elif (len(data) > 255):
buffer.append(0x4d)
buffer.append(len(data) & 0xff)
buffer.append((len(data) >> 8) & 0xff)
else:
buffer.append(0x4c)
buffer.append(len(data))
return buffer
def get_p2sh_input_script(redeemScript, sigHashtypeList):
result = [ 0x00 ]
for sigHashtype in sigHashtypeList:
write_pushed_data_size(sigHashtype, result)
result.extend(sigHashtype)
write_pushed_data_size(redeemScript, result)
result.extend(redeemScript)
return bytearray(result)
def get_p2pk_input_script(sigHashtype):
if len(sigHashtype) >= 0x4c:
raise BTChipException("Invalid sigHashtype")
result = [ len(sigHashtype) ]
result.extend(sigHashtype)
return bytearray(result)
def get_output_script(amountScriptArray):
result = [ len(amountScriptArray) ]
for amountScript in amountScriptArray:
writeHexAmount(btc_to_satoshi(str(amountScript[0])), result)
writeVarint(len(amountScript[1]), result)
result.extend(amountScript[1])
return bytearray(result)
btchip-python-0.1.31/btchip/ledgerWrapper.py 0000664 0000000 0000000 00000006230 13736561771 0021033 0 ustar 00root root 0000000 0000000 """
*******************************************************************************
* BTChip Bitcoin Hardware Wallet Python API
* (c) 2014 BTChip - 1BTChip7VfTnrPra5jqci7ejnMguuHogTn
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
********************************************************************************
"""
import struct
from .btchipException import BTChipException
def wrapCommandAPDU(channel, command, packetSize):
if packetSize < 3:
raise BTChipException("Can't handle Ledger framing with less than 3 bytes for the report")
sequenceIdx = 0
offset = 0
result = struct.pack(">HBHH", channel, 0x05, sequenceIdx, len(command))
sequenceIdx = sequenceIdx + 1
if len(command) > packetSize - 7:
blockSize = packetSize - 7
else:
blockSize = len(command)
result += command[offset : offset + blockSize]
offset = offset + blockSize
while offset != len(command):
result += struct.pack(">HBH", channel, 0x05, sequenceIdx)
sequenceIdx = sequenceIdx + 1
if (len(command) - offset) > packetSize - 5:
blockSize = packetSize - 5
else:
blockSize = len(command) - offset
result += command[offset : offset + blockSize]
offset = offset + blockSize
while (len(result) % packetSize) != 0:
result += b"\x00"
return bytearray(result)
def unwrapResponseAPDU(channel, data, packetSize):
sequenceIdx = 0
offset = 0
if ((data is None) or (len(data) < 7 + 5)):
return None
if struct.unpack(">H", data[offset : offset + 2])[0] != channel:
raise BTChipException("Invalid channel")
offset += 2
if data[offset] != 0x05:
raise BTChipException("Invalid tag")
offset += 1
if struct.unpack(">H", data[offset : offset + 2])[0] != sequenceIdx:
raise BTChipException("Invalid sequence")
offset += 2
responseLength = struct.unpack(">H", data[offset : offset + 2])[0]
offset += 2
if len(data) < 7 + responseLength:
return None
if responseLength > packetSize - 7:
blockSize = packetSize - 7
else:
blockSize = responseLength
result = data[offset : offset + blockSize]
offset += blockSize
while (len(result) != responseLength):
sequenceIdx = sequenceIdx + 1
if (offset == len(data)):
return None
if struct.unpack(">H", data[offset : offset + 2])[0] != channel:
raise BTChipException("Invalid channel")
offset += 2
if data[offset] != 0x05:
raise BTChipException("Invalid tag")
offset += 1
if struct.unpack(">H", data[offset : offset + 2])[0] != sequenceIdx:
raise BTChipException("Invalid sequence")
offset += 2
if (responseLength - len(result)) > packetSize - 5:
blockSize = packetSize - 5
else:
blockSize = responseLength - len(result)
result += data[offset : offset + blockSize]
offset += blockSize
return bytearray(result)
btchip-python-0.1.31/btchip/msqr.py 0000664 0000000 0000000 00000004500 13736561771 0017210 0 ustar 00root root 0000000 0000000 # from http://eli.thegreenplace.net/2009/03/07/computing-modular-square-roots-in-python/
def modular_sqrt(a, p):
""" Find a quadratic residue (mod p) of 'a'. p
must be an odd prime.
Solve the congruence of the form:
x^2 = a (mod p)
And returns x. Note that p - x is also a root.
0 is returned is no square root exists for
these a and p.
The Tonelli-Shanks algorithm is used (except
for some simple cases in which the solution
is known from an identity). This algorithm
runs in polynomial time (unless the
generalized Riemann hypothesis is false).
"""
# Simple cases
#
if legendre_symbol(a, p) != 1:
return 0
elif a == 0:
return 0
elif p == 2:
return p
elif p % 4 == 3:
return pow(a, (p + 1) / 4, p)
# Partition p-1 to s * 2^e for an odd s (i.e.
# reduce all the powers of 2 from p-1)
#
s = p - 1
e = 0
while s % 2 == 0:
s /= 2
e += 1
# Find some 'n' with a legendre symbol n|p = -1.
# Shouldn't take long.
#
n = 2
while legendre_symbol(n, p) != -1:
n += 1
# Here be dragons!
# Read the paper "Square roots from 1; 24, 51,
# 10 to Dan Shanks" by Ezra Brown for more
# information
#
# x is a guess of the square root that gets better
# with each iteration.
# b is the "fudge factor" - by how much we're off
# with the guess. The invariant x^2 = ab (mod p)
# is maintained throughout the loop.
# g is used for successive powers of n to update
# both a and b
# r is the exponent - decreases with each update
#
x = pow(a, (s + 1) / 2, p)
b = pow(a, s, p)
g = pow(n, s, p)
r = e
while True:
t = b
m = 0
for m in xrange(r):
if t == 1:
break
t = pow(t, 2, p)
if m == 0:
return x
gs = pow(g, 2 ** (r - m - 1), p)
g = (gs * gs) % p
x = (x * gs) % p
b = (b * g) % p
r = m
def legendre_symbol(a, p):
""" Compute the Legendre symbol a|p using
Euler's criterion. p is a prime, a is
relatively prime to p (if p divides
a, then a|p = 0)
Returns 1 if a has a square root modulo
p, -1 otherwise.
"""
ls = pow(a, (p - 1) / 2, p)
return -1 if ls == p - 1 else ls
btchip-python-0.1.31/btchip/ui/ 0000775 0000000 0000000 00000000000 13736561771 0016272 5 ustar 00root root 0000000 0000000 btchip-python-0.1.31/btchip/ui/__init__.py 0000664 0000000 0000000 00000001510 13736561771 0020400 0 ustar 00root root 0000000 0000000 """
*******************************************************************************
* BTChip Bitcoin Hardware Wallet Python API
* (c) 2014 BTChip - 1BTChip7VfTnrPra5jqci7ejnMguuHogTn
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
********************************************************************************
"""
btchip-python-0.1.31/btchip/ui/personalization00start.py 0000664 0000000 0000000 00000005513 13736561771 0023307 0 ustar 00root root 0000000 0000000 # -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'personalization-00-start.ui'
#
# Created: Fri Sep 19 15:03:25 2014
# by: PyQt4 UI code generator 4.9.1
#
# WARNING! All changes made in this file will be lost!
from PyQt4 import QtCore, QtGui
try:
_fromUtf8 = QtCore.QString.fromUtf8
except AttributeError:
_fromUtf8 = lambda s: s
class Ui_Dialog(object):
def setupUi(self, Dialog):
Dialog.setObjectName(_fromUtf8("Dialog"))
Dialog.resize(400, 231)
self.TitleLabel = QtGui.QLabel(Dialog)
self.TitleLabel.setGeometry(QtCore.QRect(120, 20, 231, 31))
font = QtGui.QFont()
font.setPointSize(20)
font.setBold(True)
font.setItalic(True)
font.setWeight(75)
self.TitleLabel.setFont(font)
self.TitleLabel.setObjectName(_fromUtf8("TitleLabel"))
self.IntroLabel = QtGui.QLabel(Dialog)
self.IntroLabel.setGeometry(QtCore.QRect(20, 60, 351, 61))
self.IntroLabel.setWordWrap(True)
self.IntroLabel.setObjectName(_fromUtf8("IntroLabel"))
self.NextButton = QtGui.QPushButton(Dialog)
self.NextButton.setGeometry(QtCore.QRect(310, 200, 75, 25))
self.NextButton.setObjectName(_fromUtf8("NextButton"))
self.arningLabel = QtGui.QLabel(Dialog)
self.arningLabel.setGeometry(QtCore.QRect(20, 120, 351, 81))
self.arningLabel.setWordWrap(True)
self.arningLabel.setObjectName(_fromUtf8("arningLabel"))
self.CancelButton = QtGui.QPushButton(Dialog)
self.CancelButton.setGeometry(QtCore.QRect(20, 200, 75, 25))
self.CancelButton.setObjectName(_fromUtf8("CancelButton"))
self.retranslateUi(Dialog)
QtCore.QMetaObject.connectSlotsByName(Dialog)
def retranslateUi(self, Dialog):
Dialog.setWindowTitle(QtGui.QApplication.translate("Dialog", "BTChip setup", None, QtGui.QApplication.UnicodeUTF8))
self.TitleLabel.setText(QtGui.QApplication.translate("Dialog", "BTChip setup", None, QtGui.QApplication.UnicodeUTF8))
self.IntroLabel.setText(QtGui.QApplication.translate("Dialog", "Your BTChip dongle is not set up - you\'ll be able to create a new wallet, or restore an existing one, and choose your security profile.", None, QtGui.QApplication.UnicodeUTF8))
self.NextButton.setText(QtGui.QApplication.translate("Dialog", "Next", None, QtGui.QApplication.UnicodeUTF8))
self.arningLabel.setText(QtGui.QApplication.translate("Dialog", "Sensitive information including your dongle PIN will be exchanged during this setup phase - it is recommended to execute it on a secure computer, disconnected from any network, especially if you restore a wallet backup.", None, QtGui.QApplication.UnicodeUTF8))
self.CancelButton.setText(QtGui.QApplication.translate("Dialog", "Cancel", None, QtGui.QApplication.UnicodeUTF8))
btchip-python-0.1.31/btchip/ui/personalization01seed.py 0000664 0000000 0000000 00000010236 13736561771 0023071 0 ustar 00root root 0000000 0000000 # -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'personalization-01-seed.ui'
#
# Created: Thu Aug 28 22:26:22 2014
# by: PyQt4 UI code generator 4.9.1
#
# WARNING! All changes made in this file will be lost!
from PyQt4 import QtCore, QtGui
try:
_fromUtf8 = QtCore.QString.fromUtf8
except AttributeError:
_fromUtf8 = lambda s: s
class Ui_Dialog(object):
def setupUi(self, Dialog):
Dialog.setObjectName(_fromUtf8("Dialog"))
Dialog.resize(400, 300)
self.TitleLabel = QtGui.QLabel(Dialog)
self.TitleLabel.setGeometry(QtCore.QRect(50, 20, 311, 31))
font = QtGui.QFont()
font.setPointSize(20)
font.setBold(True)
font.setItalic(True)
font.setWeight(75)
self.TitleLabel.setFont(font)
self.TitleLabel.setObjectName(_fromUtf8("TitleLabel"))
self.IntroLabel = QtGui.QLabel(Dialog)
self.IntroLabel.setGeometry(QtCore.QRect(20, 60, 351, 61))
self.IntroLabel.setWordWrap(True)
self.IntroLabel.setObjectName(_fromUtf8("IntroLabel"))
self.NewWalletButton = QtGui.QRadioButton(Dialog)
self.NewWalletButton.setGeometry(QtCore.QRect(20, 130, 94, 21))
self.NewWalletButton.setChecked(True)
self.NewWalletButton.setObjectName(_fromUtf8("NewWalletButton"))
self.buttonGroup = QtGui.QButtonGroup(Dialog)
self.buttonGroup.setObjectName(_fromUtf8("buttonGroup"))
self.buttonGroup.addButton(self.NewWalletButton)
self.RestoreWalletButton = QtGui.QRadioButton(Dialog)
self.RestoreWalletButton.setGeometry(QtCore.QRect(20, 180, 171, 21))
self.RestoreWalletButton.setObjectName(_fromUtf8("RestoreWalletButton"))
self.buttonGroup.addButton(self.RestoreWalletButton)
self.seed = QtGui.QLineEdit(Dialog)
self.seed.setEnabled(False)
self.seed.setGeometry(QtCore.QRect(50, 210, 331, 21))
self.seed.setEchoMode(QtGui.QLineEdit.Normal)
self.seed.setObjectName(_fromUtf8("seed"))
self.CancelButton = QtGui.QPushButton(Dialog)
self.CancelButton.setGeometry(QtCore.QRect(10, 270, 75, 25))
self.CancelButton.setObjectName(_fromUtf8("CancelButton"))
self.NextButton = QtGui.QPushButton(Dialog)
self.NextButton.setGeometry(QtCore.QRect(320, 270, 75, 25))
self.NextButton.setObjectName(_fromUtf8("NextButton"))
self.mnemonicNotAvailableLabel = QtGui.QLabel(Dialog)
self.mnemonicNotAvailableLabel.setGeometry(QtCore.QRect(130, 240, 171, 31))
font = QtGui.QFont()
font.setItalic(True)
self.mnemonicNotAvailableLabel.setFont(font)
self.mnemonicNotAvailableLabel.setWordWrap(True)
self.mnemonicNotAvailableLabel.setObjectName(_fromUtf8("mnemonicNotAvailableLabel"))
self.retranslateUi(Dialog)
QtCore.QMetaObject.connectSlotsByName(Dialog)
def retranslateUi(self, Dialog):
Dialog.setWindowTitle(QtGui.QApplication.translate("Dialog", "BTChip setup - seed", None, QtGui.QApplication.UnicodeUTF8))
self.TitleLabel.setText(QtGui.QApplication.translate("Dialog", "BTChip setup - seed (1/3)", None, QtGui.QApplication.UnicodeUTF8))
self.IntroLabel.setText(QtGui.QApplication.translate("Dialog", "Please select an option : either create a new wallet or restore an existing one", None, QtGui.QApplication.UnicodeUTF8))
self.NewWalletButton.setText(QtGui.QApplication.translate("Dialog", "New Wallet", None, QtGui.QApplication.UnicodeUTF8))
self.RestoreWalletButton.setText(QtGui.QApplication.translate("Dialog", "Restore wallet backup", None, QtGui.QApplication.UnicodeUTF8))
self.seed.setPlaceholderText(QtGui.QApplication.translate("Dialog", "Enter an hexadecimal seed or a BIP 39 mnemonic code", None, QtGui.QApplication.UnicodeUTF8))
self.CancelButton.setText(QtGui.QApplication.translate("Dialog", "Cancel", None, QtGui.QApplication.UnicodeUTF8))
self.NextButton.setText(QtGui.QApplication.translate("Dialog", "Next", None, QtGui.QApplication.UnicodeUTF8))
self.mnemonicNotAvailableLabel.setText(QtGui.QApplication.translate("Dialog", "Mnemonic API is not available", None, QtGui.QApplication.UnicodeUTF8))
btchip-python-0.1.31/btchip/ui/personalization02security.py 0000664 0000000 0000000 00000013364 13736561771 0024026 0 ustar 00root root 0000000 0000000 # -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'personalization-02-security.ui'
#
# Created: Thu Aug 28 22:26:22 2014
# by: PyQt4 UI code generator 4.9.1
#
# WARNING! All changes made in this file will be lost!
from PyQt4 import QtCore, QtGui
try:
_fromUtf8 = QtCore.QString.fromUtf8
except AttributeError:
_fromUtf8 = lambda s: s
class Ui_Dialog(object):
def setupUi(self, Dialog):
Dialog.setObjectName(_fromUtf8("Dialog"))
Dialog.resize(400, 503)
self.TitleLabel = QtGui.QLabel(Dialog)
self.TitleLabel.setGeometry(QtCore.QRect(20, 20, 361, 31))
font = QtGui.QFont()
font.setPointSize(20)
font.setBold(True)
font.setItalic(True)
font.setWeight(75)
self.TitleLabel.setFont(font)
self.TitleLabel.setObjectName(_fromUtf8("TitleLabel"))
self.IntroLabel = QtGui.QLabel(Dialog)
self.IntroLabel.setGeometry(QtCore.QRect(10, 60, 351, 61))
self.IntroLabel.setWordWrap(True)
self.IntroLabel.setObjectName(_fromUtf8("IntroLabel"))
self.HardenedButton = QtGui.QRadioButton(Dialog)
self.HardenedButton.setGeometry(QtCore.QRect(20, 110, 81, 21))
self.HardenedButton.setChecked(True)
self.HardenedButton.setObjectName(_fromUtf8("HardenedButton"))
self.buttonGroup = QtGui.QButtonGroup(Dialog)
self.buttonGroup.setObjectName(_fromUtf8("buttonGroup"))
self.buttonGroup.addButton(self.HardenedButton)
self.HardenedButton_2 = QtGui.QRadioButton(Dialog)
self.HardenedButton_2.setGeometry(QtCore.QRect(20, 210, 81, 21))
self.HardenedButton_2.setObjectName(_fromUtf8("HardenedButton_2"))
self.buttonGroup.addButton(self.HardenedButton_2)
self.IntroLabel_2 = QtGui.QLabel(Dialog)
self.IntroLabel_2.setGeometry(QtCore.QRect(50, 140, 351, 61))
self.IntroLabel_2.setWordWrap(True)
self.IntroLabel_2.setObjectName(_fromUtf8("IntroLabel_2"))
self.IntroLabel_3 = QtGui.QLabel(Dialog)
self.IntroLabel_3.setGeometry(QtCore.QRect(50, 230, 351, 61))
self.IntroLabel_3.setWordWrap(True)
self.IntroLabel_3.setObjectName(_fromUtf8("IntroLabel_3"))
self.CancelButton = QtGui.QPushButton(Dialog)
self.CancelButton.setGeometry(QtCore.QRect(10, 470, 75, 25))
self.CancelButton.setObjectName(_fromUtf8("CancelButton"))
self.NextButton = QtGui.QPushButton(Dialog)
self.NextButton.setGeometry(QtCore.QRect(310, 470, 75, 25))
self.NextButton.setObjectName(_fromUtf8("NextButton"))
self.IntroLabel_4 = QtGui.QLabel(Dialog)
self.IntroLabel_4.setGeometry(QtCore.QRect(10, 300, 351, 61))
self.IntroLabel_4.setWordWrap(True)
self.IntroLabel_4.setObjectName(_fromUtf8("IntroLabel_4"))
self.IntroLabel_5 = QtGui.QLabel(Dialog)
self.IntroLabel_5.setGeometry(QtCore.QRect(20, 380, 161, 31))
self.IntroLabel_5.setWordWrap(True)
self.IntroLabel_5.setObjectName(_fromUtf8("IntroLabel_5"))
self.pin1 = QtGui.QLineEdit(Dialog)
self.pin1.setGeometry(QtCore.QRect(210, 380, 161, 21))
self.pin1.setEchoMode(QtGui.QLineEdit.Password)
self.pin1.setObjectName(_fromUtf8("pin1"))
self.pin2 = QtGui.QLineEdit(Dialog)
self.pin2.setGeometry(QtCore.QRect(210, 420, 161, 21))
self.pin2.setEchoMode(QtGui.QLineEdit.Password)
self.pin2.setObjectName(_fromUtf8("pin2"))
self.IntroLabel_6 = QtGui.QLabel(Dialog)
self.IntroLabel_6.setGeometry(QtCore.QRect(20, 420, 171, 31))
self.IntroLabel_6.setWordWrap(True)
self.IntroLabel_6.setObjectName(_fromUtf8("IntroLabel_6"))
self.retranslateUi(Dialog)
QtCore.QMetaObject.connectSlotsByName(Dialog)
def retranslateUi(self, Dialog):
Dialog.setWindowTitle(QtGui.QApplication.translate("Dialog", "BTChip setup - security", None, QtGui.QApplication.UnicodeUTF8))
self.TitleLabel.setText(QtGui.QApplication.translate("Dialog", "BTChip setup - security (2/3)", None, QtGui.QApplication.UnicodeUTF8))
self.IntroLabel.setText(QtGui.QApplication.translate("Dialog", "Please choose a security profile", None, QtGui.QApplication.UnicodeUTF8))
self.HardenedButton.setText(QtGui.QApplication.translate("Dialog", "Hardened", None, QtGui.QApplication.UnicodeUTF8))
self.HardenedButton_2.setText(QtGui.QApplication.translate("Dialog", "PIN only", None, QtGui.QApplication.UnicodeUTF8))
self.IntroLabel_2.setText(QtGui.QApplication.translate("Dialog", "You need to remove the dongle and insert it again to get a second factor validation of all operations. Recommended for expert users and to be fully protected against malwares.", None, QtGui.QApplication.UnicodeUTF8))
self.IntroLabel_3.setText(QtGui.QApplication.translate("Dialog", "You only need to enter a PIN once when inserting the dongle. Transactions are not protected against malwares", None, QtGui.QApplication.UnicodeUTF8))
self.CancelButton.setText(QtGui.QApplication.translate("Dialog", "Cancel", None, QtGui.QApplication.UnicodeUTF8))
self.NextButton.setText(QtGui.QApplication.translate("Dialog", "Next", None, QtGui.QApplication.UnicodeUTF8))
self.IntroLabel_4.setText(QtGui.QApplication.translate("Dialog", "Please choose a PIN associated to the BTChip dongle. The PIN protects the dongle in case it is stolen, and can be up to 32 characters. The dongle is wiped if a wrong PIN is entered 3 times in a row.", None, QtGui.QApplication.UnicodeUTF8))
self.IntroLabel_5.setText(QtGui.QApplication.translate("Dialog", "Enter the new PIN : ", None, QtGui.QApplication.UnicodeUTF8))
self.IntroLabel_6.setText(QtGui.QApplication.translate("Dialog", "Repeat the new PIN :", None, QtGui.QApplication.UnicodeUTF8))
btchip-python-0.1.31/btchip/ui/personalization03config.py 0000664 0000000 0000000 00000006745 13736561771 0023432 0 ustar 00root root 0000000 0000000 # -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'personalization-03-config.ui'
#
# Created: Thu Aug 28 22:26:22 2014
# by: PyQt4 UI code generator 4.9.1
#
# WARNING! All changes made in this file will be lost!
from PyQt4 import QtCore, QtGui
try:
_fromUtf8 = QtCore.QString.fromUtf8
except AttributeError:
_fromUtf8 = lambda s: s
class Ui_Dialog(object):
def setupUi(self, Dialog):
Dialog.setObjectName(_fromUtf8("Dialog"))
Dialog.resize(400, 243)
self.TitleLabel = QtGui.QLabel(Dialog)
self.TitleLabel.setGeometry(QtCore.QRect(30, 10, 361, 31))
font = QtGui.QFont()
font.setPointSize(20)
font.setBold(True)
font.setItalic(True)
font.setWeight(75)
self.TitleLabel.setFont(font)
self.TitleLabel.setObjectName(_fromUtf8("TitleLabel"))
self.IntroLabel = QtGui.QLabel(Dialog)
self.IntroLabel.setGeometry(QtCore.QRect(20, 50, 351, 61))
self.IntroLabel.setWordWrap(True)
self.IntroLabel.setObjectName(_fromUtf8("IntroLabel"))
self.qwertyButton = QtGui.QRadioButton(Dialog)
self.qwertyButton.setGeometry(QtCore.QRect(50, 110, 94, 21))
self.qwertyButton.setChecked(True)
self.qwertyButton.setObjectName(_fromUtf8("qwertyButton"))
self.keyboardGroup = QtGui.QButtonGroup(Dialog)
self.keyboardGroup.setObjectName(_fromUtf8("keyboardGroup"))
self.keyboardGroup.addButton(self.qwertyButton)
self.qwertzButton = QtGui.QRadioButton(Dialog)
self.qwertzButton.setGeometry(QtCore.QRect(50, 140, 94, 21))
self.qwertzButton.setObjectName(_fromUtf8("qwertzButton"))
self.keyboardGroup.addButton(self.qwertzButton)
self.azertyButton = QtGui.QRadioButton(Dialog)
self.azertyButton.setGeometry(QtCore.QRect(50, 170, 94, 21))
self.azertyButton.setObjectName(_fromUtf8("azertyButton"))
self.keyboardGroup.addButton(self.azertyButton)
self.CancelButton = QtGui.QPushButton(Dialog)
self.CancelButton.setGeometry(QtCore.QRect(10, 210, 75, 25))
self.CancelButton.setObjectName(_fromUtf8("CancelButton"))
self.NextButton = QtGui.QPushButton(Dialog)
self.NextButton.setGeometry(QtCore.QRect(320, 210, 75, 25))
self.NextButton.setObjectName(_fromUtf8("NextButton"))
self.retranslateUi(Dialog)
QtCore.QMetaObject.connectSlotsByName(Dialog)
def retranslateUi(self, Dialog):
Dialog.setWindowTitle(QtGui.QApplication.translate("Dialog", "BTChip setup", None, QtGui.QApplication.UnicodeUTF8))
self.TitleLabel.setText(QtGui.QApplication.translate("Dialog", "BTChip setup - config (3/3)", None, QtGui.QApplication.UnicodeUTF8))
self.IntroLabel.setText(QtGui.QApplication.translate("Dialog", "Please select your keyboard type to type the second factor confirmation", None, QtGui.QApplication.UnicodeUTF8))
self.qwertyButton.setText(QtGui.QApplication.translate("Dialog", "QWERTY", None, QtGui.QApplication.UnicodeUTF8))
self.qwertzButton.setText(QtGui.QApplication.translate("Dialog", "QWERTZ", None, QtGui.QApplication.UnicodeUTF8))
self.azertyButton.setText(QtGui.QApplication.translate("Dialog", "AZERTY", None, QtGui.QApplication.UnicodeUTF8))
self.CancelButton.setText(QtGui.QApplication.translate("Dialog", "Cancel", None, QtGui.QApplication.UnicodeUTF8))
self.NextButton.setText(QtGui.QApplication.translate("Dialog", "Next", None, QtGui.QApplication.UnicodeUTF8))
btchip-python-0.1.31/btchip/ui/personalization04finalize.py 0000664 0000000 0000000 00000006057 13736561771 0023763 0 ustar 00root root 0000000 0000000 # -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'personalization-04-finalize.ui'
#
# Created: Thu Aug 28 22:26:22 2014
# by: PyQt4 UI code generator 4.9.1
#
# WARNING! All changes made in this file will be lost!
from PyQt4 import QtCore, QtGui
try:
_fromUtf8 = QtCore.QString.fromUtf8
except AttributeError:
_fromUtf8 = lambda s: s
class Ui_Dialog(object):
def setupUi(self, Dialog):
Dialog.setObjectName(_fromUtf8("Dialog"))
Dialog.resize(400, 267)
self.TitleLabel = QtGui.QLabel(Dialog)
self.TitleLabel.setGeometry(QtCore.QRect(20, 20, 361, 31))
font = QtGui.QFont()
font.setPointSize(20)
font.setBold(True)
font.setItalic(True)
font.setWeight(75)
self.TitleLabel.setFont(font)
self.TitleLabel.setObjectName(_fromUtf8("TitleLabel"))
self.FinishButton = QtGui.QPushButton(Dialog)
self.FinishButton.setGeometry(QtCore.QRect(320, 230, 75, 25))
self.FinishButton.setObjectName(_fromUtf8("FinishButton"))
self.IntroLabel_4 = QtGui.QLabel(Dialog)
self.IntroLabel_4.setGeometry(QtCore.QRect(10, 70, 351, 61))
self.IntroLabel_4.setWordWrap(True)
self.IntroLabel_4.setObjectName(_fromUtf8("IntroLabel_4"))
self.IntroLabel_5 = QtGui.QLabel(Dialog)
self.IntroLabel_5.setGeometry(QtCore.QRect(50, 140, 121, 21))
self.IntroLabel_5.setWordWrap(True)
self.IntroLabel_5.setObjectName(_fromUtf8("IntroLabel_5"))
self.pin1 = QtGui.QLineEdit(Dialog)
self.pin1.setGeometry(QtCore.QRect(200, 140, 181, 21))
self.pin1.setEchoMode(QtGui.QLineEdit.Password)
self.pin1.setObjectName(_fromUtf8("pin1"))
self.remainingAttemptsLabel = QtGui.QLabel(Dialog)
self.remainingAttemptsLabel.setGeometry(QtCore.QRect(120, 170, 171, 31))
font = QtGui.QFont()
font.setItalic(True)
self.remainingAttemptsLabel.setFont(font)
self.remainingAttemptsLabel.setWordWrap(True)
self.remainingAttemptsLabel.setObjectName(_fromUtf8("remainingAttemptsLabel"))
self.retranslateUi(Dialog)
QtCore.QMetaObject.connectSlotsByName(Dialog)
def retranslateUi(self, Dialog):
Dialog.setWindowTitle(QtGui.QApplication.translate("Dialog", "BTChip setup - security", None, QtGui.QApplication.UnicodeUTF8))
self.TitleLabel.setText(QtGui.QApplication.translate("Dialog", "BTChip setup - completed", None, QtGui.QApplication.UnicodeUTF8))
self.FinishButton.setText(QtGui.QApplication.translate("Dialog", "Finish", None, QtGui.QApplication.UnicodeUTF8))
self.IntroLabel_4.setText(QtGui.QApplication.translate("Dialog", "BTChip setup is completed. Please enter your PIN to validate it then press Finish", None, QtGui.QApplication.UnicodeUTF8))
self.IntroLabel_5.setText(QtGui.QApplication.translate("Dialog", "BTChip PIN :", None, QtGui.QApplication.UnicodeUTF8))
self.remainingAttemptsLabel.setText(QtGui.QApplication.translate("Dialog", "Remaining attempts", None, QtGui.QApplication.UnicodeUTF8))
btchip-python-0.1.31/btchip/ui/personalizationseedbackup01.py 0000664 0000000 0000000 00000007232 13736561771 0024261 0 ustar 00root root 0000000 0000000 # -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'personalization-seedbackup-01.ui'
#
# Created: Thu Aug 28 22:26:22 2014
# by: PyQt4 UI code generator 4.9.1
#
# WARNING! All changes made in this file will be lost!
from PyQt4 import QtCore, QtGui
try:
_fromUtf8 = QtCore.QString.fromUtf8
except AttributeError:
_fromUtf8 = lambda s: s
class Ui_Dialog(object):
def setupUi(self, Dialog):
Dialog.setObjectName(_fromUtf8("Dialog"))
Dialog.resize(400, 300)
self.TitleLabel = QtGui.QLabel(Dialog)
self.TitleLabel.setGeometry(QtCore.QRect(30, 20, 351, 31))
font = QtGui.QFont()
font.setPointSize(20)
font.setBold(True)
font.setItalic(True)
font.setWeight(75)
self.TitleLabel.setFont(font)
self.TitleLabel.setObjectName(_fromUtf8("TitleLabel"))
self.NextButton = QtGui.QPushButton(Dialog)
self.NextButton.setGeometry(QtCore.QRect(320, 270, 75, 25))
self.NextButton.setObjectName(_fromUtf8("NextButton"))
self.IntroLabel = QtGui.QLabel(Dialog)
self.IntroLabel.setGeometry(QtCore.QRect(10, 100, 351, 31))
self.IntroLabel.setWordWrap(True)
self.IntroLabel.setObjectName(_fromUtf8("IntroLabel"))
self.IntroLabel_2 = QtGui.QLabel(Dialog)
self.IntroLabel_2.setGeometry(QtCore.QRect(10, 140, 351, 31))
self.IntroLabel_2.setWordWrap(True)
self.IntroLabel_2.setObjectName(_fromUtf8("IntroLabel_2"))
self.IntroLabel_3 = QtGui.QLabel(Dialog)
self.IntroLabel_3.setGeometry(QtCore.QRect(10, 180, 351, 41))
self.IntroLabel_3.setWordWrap(True)
self.IntroLabel_3.setObjectName(_fromUtf8("IntroLabel_3"))
self.TitleLabel_2 = QtGui.QLabel(Dialog)
self.TitleLabel_2.setGeometry(QtCore.QRect(90, 60, 251, 31))
font = QtGui.QFont()
font.setPointSize(20)
font.setBold(True)
font.setItalic(True)
font.setWeight(75)
self.TitleLabel_2.setFont(font)
self.TitleLabel_2.setObjectName(_fromUtf8("TitleLabel_2"))
self.IntroLabel_4 = QtGui.QLabel(Dialog)
self.IntroLabel_4.setGeometry(QtCore.QRect(10, 220, 351, 41))
self.IntroLabel_4.setWordWrap(True)
self.IntroLabel_4.setObjectName(_fromUtf8("IntroLabel_4"))
self.retranslateUi(Dialog)
QtCore.QMetaObject.connectSlotsByName(Dialog)
def retranslateUi(self, Dialog):
Dialog.setWindowTitle(QtGui.QApplication.translate("Dialog", "BTChip setup", None, QtGui.QApplication.UnicodeUTF8))
self.TitleLabel.setText(QtGui.QApplication.translate("Dialog", "BTChip setup - seed backup", None, QtGui.QApplication.UnicodeUTF8))
self.NextButton.setText(QtGui.QApplication.translate("Dialog", "Next", None, QtGui.QApplication.UnicodeUTF8))
self.IntroLabel.setText(QtGui.QApplication.translate("Dialog", "A new seed has been generated for your wallet.", None, QtGui.QApplication.UnicodeUTF8))
self.IntroLabel_2.setText(QtGui.QApplication.translate("Dialog", "You must backup this seed and keep it out of reach of hackers (typically by keeping it on paper).", None, QtGui.QApplication.UnicodeUTF8))
self.IntroLabel_3.setText(QtGui.QApplication.translate("Dialog", "You can use this seed to restore your dongle if you lose it or access your funds with any other compatible wallet.", None, QtGui.QApplication.UnicodeUTF8))
self.TitleLabel_2.setText(QtGui.QApplication.translate("Dialog", "READ CAREFULLY", None, QtGui.QApplication.UnicodeUTF8))
self.IntroLabel_4.setText(QtGui.QApplication.translate("Dialog", "Press Next to start the backuping process.", None, QtGui.QApplication.UnicodeUTF8))
btchip-python-0.1.31/btchip/ui/personalizationseedbackup02.py 0000664 0000000 0000000 00000003633 13736561771 0024263 0 ustar 00root root 0000000 0000000 # -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'personalization-seedbackup-02.ui'
#
# Created: Thu Aug 28 22:26:22 2014
# by: PyQt4 UI code generator 4.9.1
#
# WARNING! All changes made in this file will be lost!
from PyQt4 import QtCore, QtGui
try:
_fromUtf8 = QtCore.QString.fromUtf8
except AttributeError:
_fromUtf8 = lambda s: s
class Ui_Dialog(object):
def setupUi(self, Dialog):
Dialog.setObjectName(_fromUtf8("Dialog"))
Dialog.resize(400, 300)
self.TitleLabel = QtGui.QLabel(Dialog)
self.TitleLabel.setGeometry(QtCore.QRect(30, 20, 351, 31))
font = QtGui.QFont()
font.setPointSize(20)
font.setBold(True)
font.setItalic(True)
font.setWeight(75)
self.TitleLabel.setFont(font)
self.TitleLabel.setObjectName(_fromUtf8("TitleLabel"))
self.IntroLabel = QtGui.QLabel(Dialog)
self.IntroLabel.setGeometry(QtCore.QRect(20, 70, 351, 31))
self.IntroLabel.setWordWrap(True)
self.IntroLabel.setObjectName(_fromUtf8("IntroLabel"))
self.NextButton = QtGui.QPushButton(Dialog)
self.NextButton.setGeometry(QtCore.QRect(320, 270, 75, 25))
self.NextButton.setObjectName(_fromUtf8("NextButton"))
self.retranslateUi(Dialog)
QtCore.QMetaObject.connectSlotsByName(Dialog)
def retranslateUi(self, Dialog):
Dialog.setWindowTitle(QtGui.QApplication.translate("Dialog", "BTChip setup", None, QtGui.QApplication.UnicodeUTF8))
self.TitleLabel.setText(QtGui.QApplication.translate("Dialog", "BTChip setup - seed backup", None, QtGui.QApplication.UnicodeUTF8))
self.IntroLabel.setText(QtGui.QApplication.translate("Dialog", "Please disconnect the dongle then press Next", None, QtGui.QApplication.UnicodeUTF8))
self.NextButton.setText(QtGui.QApplication.translate("Dialog", "Next", None, QtGui.QApplication.UnicodeUTF8))
btchip-python-0.1.31/btchip/ui/personalizationseedbackup03.py 0000664 0000000 0000000 00000011431 13736561771 0024257 0 ustar 00root root 0000000 0000000 # -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'personalization-seedbackup-03.ui'
#
# Created: Thu Aug 28 22:26:22 2014
# by: PyQt4 UI code generator 4.9.1
#
# WARNING! All changes made in this file will be lost!
from PyQt4 import QtCore, QtGui
try:
_fromUtf8 = QtCore.QString.fromUtf8
except AttributeError:
_fromUtf8 = lambda s: s
class Ui_Dialog(object):
def setupUi(self, Dialog):
Dialog.setObjectName(_fromUtf8("Dialog"))
Dialog.resize(400, 513)
self.TitleLabel = QtGui.QLabel(Dialog)
self.TitleLabel.setGeometry(QtCore.QRect(20, 10, 351, 31))
font = QtGui.QFont()
font.setPointSize(20)
font.setBold(True)
font.setItalic(True)
font.setWeight(75)
self.TitleLabel.setFont(font)
self.TitleLabel.setObjectName(_fromUtf8("TitleLabel"))
self.IntroLabel = QtGui.QLabel(Dialog)
self.IntroLabel.setGeometry(QtCore.QRect(20, 50, 351, 61))
self.IntroLabel.setWordWrap(True)
self.IntroLabel.setObjectName(_fromUtf8("IntroLabel"))
self.IntroLabel_2 = QtGui.QLabel(Dialog)
self.IntroLabel_2.setGeometry(QtCore.QRect(20, 120, 351, 31))
self.IntroLabel_2.setWordWrap(True)
self.IntroLabel_2.setObjectName(_fromUtf8("IntroLabel_2"))
self.IntroLabel_3 = QtGui.QLabel(Dialog)
self.IntroLabel_3.setGeometry(QtCore.QRect(20, 160, 351, 51))
self.IntroLabel_3.setWordWrap(True)
self.IntroLabel_3.setObjectName(_fromUtf8("IntroLabel_3"))
self.IntroLabel_4 = QtGui.QLabel(Dialog)
self.IntroLabel_4.setGeometry(QtCore.QRect(20, 220, 351, 51))
self.IntroLabel_4.setWordWrap(True)
self.IntroLabel_4.setObjectName(_fromUtf8("IntroLabel_4"))
self.IntroLabel_5 = QtGui.QLabel(Dialog)
self.IntroLabel_5.setGeometry(QtCore.QRect(20, 280, 351, 71))
self.IntroLabel_5.setWordWrap(True)
self.IntroLabel_5.setObjectName(_fromUtf8("IntroLabel_5"))
self.IntroLabel_6 = QtGui.QLabel(Dialog)
self.IntroLabel_6.setGeometry(QtCore.QRect(20, 350, 351, 51))
self.IntroLabel_6.setWordWrap(True)
self.IntroLabel_6.setObjectName(_fromUtf8("IntroLabel_6"))
self.IntroLabel_7 = QtGui.QLabel(Dialog)
self.IntroLabel_7.setGeometry(QtCore.QRect(20, 410, 351, 51))
self.IntroLabel_7.setWordWrap(True)
self.IntroLabel_7.setObjectName(_fromUtf8("IntroLabel_7"))
self.NextButton = QtGui.QPushButton(Dialog)
self.NextButton.setGeometry(QtCore.QRect(310, 480, 75, 25))
self.NextButton.setObjectName(_fromUtf8("NextButton"))
self.retranslateUi(Dialog)
QtCore.QMetaObject.connectSlotsByName(Dialog)
def retranslateUi(self, Dialog):
Dialog.setWindowTitle(QtGui.QApplication.translate("Dialog", "BTChip setup", None, QtGui.QApplication.UnicodeUTF8))
self.TitleLabel.setText(QtGui.QApplication.translate("Dialog", "BTChip setup - seed backup", None, QtGui.QApplication.UnicodeUTF8))
self.IntroLabel.setText(QtGui.QApplication.translate("Dialog", "If you do not trust this computer, perform the following steps on a trusted one or a different device. Anything supporting keyboard input will work (smartphone, TV box ...)", None, QtGui.QApplication.UnicodeUTF8))
self.IntroLabel_2.setText(QtGui.QApplication.translate("Dialog", "Open a text editor, set the focus on the text editor, then insert the dongle", None, QtGui.QApplication.UnicodeUTF8))
self.IntroLabel_3.setText(QtGui.QApplication.translate("Dialog", "After a very short time, the dongle will type the seed as hexadecimal (0..9 A..F) characters, starting with \"seed\" and ending with \"X\"", None, QtGui.QApplication.UnicodeUTF8))
self.IntroLabel_4.setText(QtGui.QApplication.translate("Dialog", "If you perform those steps on Windows, a new device driver will be loaded the first time and the seed will not be typed. This is normal.", None, QtGui.QApplication.UnicodeUTF8))
self.IntroLabel_5.setText(QtGui.QApplication.translate("Dialog", "If you perform those steps on Mac, you\'ll get a popup asking you to select a keyboard type the first time and the seed will not be typed. This is normal, just close the popup.", None, QtGui.QApplication.UnicodeUTF8))
self.IntroLabel_6.setText(QtGui.QApplication.translate("Dialog", "If you did not see the seed for any reason, keep the focus on the text editor, unplug and plug the dongle again twice.", None, QtGui.QApplication.UnicodeUTF8))
self.IntroLabel_7.setText(QtGui.QApplication.translate("Dialog", "Then press Next once you wrote the seed to a safe medium (i.e. paper) and unplugged the dongle", None, QtGui.QApplication.UnicodeUTF8))
self.NextButton.setText(QtGui.QApplication.translate("Dialog", "Next", None, QtGui.QApplication.UnicodeUTF8))
btchip-python-0.1.31/btchip/ui/personalizationseedbackup04.py 0000664 0000000 0000000 00000004560 13736561771 0024265 0 ustar 00root root 0000000 0000000 # -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'personalization-seedbackup-04.ui'
#
# Created: Thu Aug 28 22:26:23 2014
# by: PyQt4 UI code generator 4.9.1
#
# WARNING! All changes made in this file will be lost!
from PyQt4 import QtCore, QtGui
try:
_fromUtf8 = QtCore.QString.fromUtf8
except AttributeError:
_fromUtf8 = lambda s: s
class Ui_Dialog(object):
def setupUi(self, Dialog):
Dialog.setObjectName(_fromUtf8("Dialog"))
Dialog.resize(554, 190)
self.TitleLabel = QtGui.QLabel(Dialog)
self.TitleLabel.setGeometry(QtCore.QRect(30, 10, 351, 31))
font = QtGui.QFont()
font.setPointSize(20)
font.setBold(True)
font.setItalic(True)
font.setWeight(75)
self.TitleLabel.setFont(font)
self.TitleLabel.setObjectName(_fromUtf8("TitleLabel"))
self.IntroLabel = QtGui.QLabel(Dialog)
self.IntroLabel.setGeometry(QtCore.QRect(10, 50, 351, 51))
self.IntroLabel.setWordWrap(True)
self.IntroLabel.setObjectName(_fromUtf8("IntroLabel"))
self.seedOkButton = QtGui.QPushButton(Dialog)
self.seedOkButton.setGeometry(QtCore.QRect(20, 140, 501, 25))
self.seedOkButton.setObjectName(_fromUtf8("seedOkButton"))
self.seedKoButton = QtGui.QPushButton(Dialog)
self.seedKoButton.setGeometry(QtCore.QRect(20, 110, 501, 25))
self.seedKoButton.setObjectName(_fromUtf8("seedKoButton"))
self.retranslateUi(Dialog)
QtCore.QMetaObject.connectSlotsByName(Dialog)
def retranslateUi(self, Dialog):
Dialog.setWindowTitle(QtGui.QApplication.translate("Dialog", "BTChip setup", None, QtGui.QApplication.UnicodeUTF8))
self.TitleLabel.setText(QtGui.QApplication.translate("Dialog", "BTChip setup - seed backup", None, QtGui.QApplication.UnicodeUTF8))
self.IntroLabel.setText(QtGui.QApplication.translate("Dialog", "Did you see the seed correctly displayed and did you backup it properly ?", None, QtGui.QApplication.UnicodeUTF8))
self.seedOkButton.setText(QtGui.QApplication.translate("Dialog", "Yes, the seed is backed up properly and kept in a safe place, move on", None, QtGui.QApplication.UnicodeUTF8))
self.seedKoButton.setText(QtGui.QApplication.translate("Dialog", "No, I didn\'t see the seed. Wipe the dongle and start over", None, QtGui.QApplication.UnicodeUTF8))
btchip-python-0.1.31/samples/ 0000775 0000000 0000000 00000000000 13736561771 0016050 5 ustar 00root root 0000000 0000000 btchip-python-0.1.31/samples/getFirmwareVersion.py 0000664 0000000 0000000 00000001711 13736561771 0022244 0 ustar 00root root 0000000 0000000 """
*******************************************************************************
* BTChip Bitcoin Hardware Wallet Python API
* (c) 2014 BTChip - 1BTChip7VfTnrPra5jqci7ejnMguuHogTn
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
********************************************************************************
"""
from btchip.btchip import *
import sys
dongle = getDongle(True)
app = btchip(dongle)
print(app.getFirmwareVersion()['version'])
btchip-python-0.1.31/samples/runScript.py 0000664 0000000 0000000 00000003130 13736561771 0020410 0 ustar 00root root 0000000 0000000 """
*******************************************************************************
* BTChip Bitcoin Hardware Wallet Python API
* (c) 2014 BTChip - 1BTChip7VfTnrPra5jqci7ejnMguuHogTn
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
********************************************************************************
"""
from btchip.btchip import *
import sys
import binascii
if len(sys.argv) < 2:
print("Usage : %s script to run" % sys.argv[0])
sys.exit(2)
dongle = getDongle(True)
scriptFile = open(sys.argv[1], "r")
line = scriptFile.readline()
while line:
if (len(line) == 0) or (line[0] == '#') or (line.find('[') >= 0) or (line.find(']') >= 0):
line = scriptFile.readline()
continue
line = line.replace('\"', '')
line = line.replace(',', '')
cancelResponse = (line[0] == '!')
timeout = 10000
if cancelResponse:
line = line[1:]
timeout = 1
try:
line = line.strip()
if len(line) == 0:
continue
dongle.exchange(bytearray(binascii.unhexlify(line)), timeout)
except Exception:
if cancelResponse:
pass
else:
raise
line = scriptFile.readline()
scriptFile.close()
btchip-python-0.1.31/setup.py 0000664 0000000 0000000 00000001636 13736561771 0016124 0 ustar 00root root 0000000 0000000 #from distribute_setup import use_setuptools
#use_setuptools()
from setuptools import setup, find_packages
from os.path import dirname, join
here = dirname(__file__)
import btchip
setup(
name='btchip-python',
version=btchip.__version__,
author='BTChip',
author_email='hello@ledger.fr',
description='Python library to communicate with Ledger Nano dongle',
long_description=open(join(here, 'README.md')).read(),
url='https://github.com/LedgerHQ/btchip-python',
packages=find_packages(),
install_requires=['hidapi>=0.7.99'],
extras_require = {
'smartcard': [ 'python-pyscard>=1.6.12-4build1', 'ecdsa>=0.9' ]
},
include_package_data=True,
zip_safe=False,
classifiers=[
'License :: OSI Approved :: Apache Software License',
'Operating System :: POSIX :: Linux',
'Operating System :: Microsoft :: Windows',
'Operating System :: MacOS :: MacOS X'
]
)
btchip-python-0.1.31/tests/ 0000775 0000000 0000000 00000000000 13736561771 0015546 5 ustar 00root root 0000000 0000000 btchip-python-0.1.31/tests/coinkite-cosigning/ 0000775 0000000 0000000 00000000000 13736561771 0021331 5 ustar 00root root 0000000 0000000 btchip-python-0.1.31/tests/coinkite-cosigning/testGetPublicKeyDev.js 0000664 0000000 0000000 00000007034 13736561771 0025561 0 ustar 00root root 0000000 0000000 """
*******************************************************************************
* BTChip Bitcoin Hardware Wallet Python API
* (c) 2014 BTChip - 1BTChip7VfTnrPra5jqci7ejnMguuHogTn
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
********************************************************************************
"""
# Coinkite co-signer provisioning
# To be run with the dongle in developer mode, PIN verified
import hashlib
from btchip.btchip import *
from btchip.btchipUtils import *
from base64 import b64encode
# Replace with your own seed (preferably import it and store it), key path, and Testnet flag
SEED = bytearray("fe721b95503a18a14d93914e02ff153f924737c336b01f98f2ff39395f630187".decode('hex'))
KEYPATH = "0'/2/0"
TESTNET = True
# From Electrum
__b58chars = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
__b58base = len(__b58chars)
def b58encode(v):
""" encode v, which is a string of bytes, to base58."""
long_value = 0L
for (i, c) in enumerate(v[::-1]):
long_value += (256**i) * ord(c)
result = ''
while long_value >= __b58base:
div, mod = divmod(long_value, __b58base)
result = __b58chars[mod] + result
long_value = div
result = __b58chars[long_value] + result
# Bitcoin does a little leading-zero-compression:
# leading 0-bytes in the input become leading-1s
nPad = 0
for c in v:
if c == '\0': nPad += 1
else: break
return (__b58chars[0]*nPad) + result
def EncodeBase58Check(vchIn):
hash = Hash(vchIn)
return b58encode(vchIn + hash[0:4])
def sha256(x):
return hashlib.sha256(x).digest()
def Hash(x):
if type(x) is unicode: x=x.encode('utf-8')
return sha256(sha256(x))
def i4b(self, x):
return pack('>I', x)
# /from Electrum
def getXpub(publicKeyData, testnet=False):
header = ("043587CF" if testnet else "0488B21E")
result = header.decode('hex') + chr(publicKeyData['depth']) + str(publicKeyData['parentFingerprint']) + str(publicKeyData['childNumber']) + str(publicKeyData['chainCode']) + str(compress_public_key(publicKeyData['publicKey']))
return EncodeBase58Check(result)
def signMessage(encodedPrivateKey, data):
messageData = bytearray("\x18Bitcoin Signed Message:\n")
writeVarint(len(data), messageData)
messageData.extend(data)
messageHash = Hash(messageData)
signature = app.signImmediate(encodedPrivateKey, messageHash)
# Parse the ASN.1 signature
rLength = signature[3]
r = signature[4 : 4 + rLength]
sLength = signature[4 + rLength + 1]
s = signature[4 + rLength + 2:]
if rLength == 33:
r = r[1:]
if sLength == 33:
s = s[1:]
r = str(r)
s = str(s)
# And convert it
return b64encode(chr(27 + 4 + (signature[0] & 0x01)) + r + s)
dongle = getDongle(True)
app = btchip(dongle)
seed = app.importPrivateKey(SEED, TESTNET)
privateKey = app.deriveBip32Key(seed, KEYPATH)
publicKeyData = app.getPublicKey(privateKey)
print getXpub(publicKeyData, TESTNET)
print signMessage(privateKey, "Coinkite")
dongle.close()
btchip-python-0.1.31/tests/coinkite-cosigning/testSignDev.js 0000664 0000000 0000000 00000012230 13736561771 0024124 0 ustar 00root root 0000000 0000000 """
*******************************************************************************
* BTChip Bitcoin Hardware Wallet Python API
* (c) 2014 BTChip - 1BTChip7VfTnrPra5jqci7ejnMguuHogTn
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
********************************************************************************
"""
# Coinkite co-signer signing
# To be run with the dongle in developer mode, PIN verified
# Pass the JSON request to sign as parameter
# TODO : verify Coinkite signature in the request
import hashlib
import sys
import json
from btchip.btchip import *
from btchip.btchipUtils import *
from base64 import b64encode
# Replace with your own seed (preferably import it and store it), key path, and Testnet flag
SEED = bytearray("fe721b95503a18a14d93914e02ff153f924737c336b01f98f2ff39395f630187".decode('hex'))
KEYPATH = "0'/2/0"
TESTNET = True
# From Electrum
__b58chars = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
__b58base = len(__b58chars)
def b58encode(v):
""" encode v, which is a string of bytes, to base58."""
long_value = 0L
for (i, c) in enumerate(v[::-1]):
long_value += (256**i) * ord(c)
result = ''
while long_value >= __b58base:
div, mod = divmod(long_value, __b58base)
result = __b58chars[mod] + result
long_value = div
result = __b58chars[long_value] + result
# Bitcoin does a little leading-zero-compression:
# leading 0-bytes in the input become leading-1s
nPad = 0
for c in v:
if c == '\0': nPad += 1
else: break
return (__b58chars[0]*nPad) + result
def EncodeBase58Check(vchIn):
hash = Hash(vchIn)
return b58encode(vchIn + hash[0:4])
def sha256(x):
return hashlib.sha256(x).digest()
def hash_160(public_key):
try:
md = hashlib.new('ripemd160')
md.update(sha256(public_key))
return md.digest()
except Exception:
import ripemd
md = ripemd.new(sha256(public_key))
return md.digest()
def Hash(x):
if type(x) is unicode: x=x.encode('utf-8')
return sha256(sha256(x))
def i4b(self, x):
return pack('>I', x)
# /from Electrum
def getXpub(publicKeyData, testnet=False):
header = ("043587CF" if testnet else "0488B21E")
result = header.decode('hex') + chr(publicKeyData['depth']) + str(publicKeyData['parentFingerprint']) + str(publicKeyData['childNumber']) + str(publicKeyData['chainCode']) + str(compress_public_key(publicKeyData['publicKey']))
return EncodeBase58Check(result)
def getPub(publicKeyData, testnet=False):
header = ("6F" if testnet else "00")
keyData = hash_160(str(compress_public_key(publicKeyData)))
return EncodeBase58Check(header.decode('hex') + keyData)
def signMessage(encodedPrivateKey, data):
messageData = bytearray("\x18Bitcoin Signed Message:\n")
writeVarint(len(data), messageData)
messageData.extend(data)
messageHash = Hash(messageData)
signature = app.signImmediate(encodedPrivateKey, messageHash)
# Parse the ASN.1 signature
rLength = signature[3]
r = signature[4 : 4 + rLength]
sLength = signature[4 + rLength + 1]
s = signature[4 + rLength + 2:]
if rLength == 33:
r = r[1:]
if sLength == 33:
s = s[1:]
r = str(r)
s = str(s)
# And convert it
return b64encode(chr(27 + 4 + (signature[0] & 0x01)) + r + s)
f = open(sys.argv[1], 'r')
signData = json.load(f)
requestData = json.loads(signData['contents'])
result = {}
result['cosigner'] = requestData['cosigner']
result['request'] = requestData['request']
result['signatures'] = []
dongle = getDongle(True)
app = btchip(dongle)
seed = app.importPrivateKey(SEED, TESTNET)
privateKey = app.deriveBip32Key(seed, KEYPATH)
publicKeyData = app.getPublicKey(privateKey)
wallets = {}
for key in requestData['req_keys'].keys():
privateKeyDiv = app.deriveBip32Key(privateKey, key)
publicKeyDataDiv = app.getPublicKey(privateKeyDiv)
if getPub(publicKeyDataDiv['publicKey'], TESTNET) == requestData['req_keys'][key][0]:
wallets[key] = privateKeyDiv
else:
raise "Invalid wallet, could not match key"
for signInput in requestData['inputs']:
sigHash = signInput[1].decode('hex')
signature = "\x30" + str(app.signImmediate(wallets[signInput[0]], sigHash)[1:])
signature = signature + "\x01"
result['signatures'].append([signature.encode('hex'), sigHash.encode('hex'), signInput[0]])
body = {}
body['_humans'] = "Upload this set of signatures to Coinkite."
body['content'] = json.dumps(result)
body['signature'] = signMessage(privateKey, body['content'])
body['signed_by'] = getPub(publicKeyData['publicKey'], False) # Bug, should use network
print json.dumps(body)
dongle.close()
btchip-python-0.1.31/tests/testConnectivity.py 0000664 0000000 0000000 00000002203 13736561771 0021473 0 ustar 00root root 0000000 0000000 """
*******************************************************************************
* BTChip Bitcoin Hardware Wallet Python API
* (c) 2014 BTChip - 1BTChip7VfTnrPra5jqci7ejnMguuHogTn
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
********************************************************************************
"""
from btchip.btchip import *
#from btchip.btchipUtils import *
# Run on any dongle to test connectivity.
dongle = getDongle(True)
app = btchip(dongle)
print('btchip firmware version:')
print(app.getFirmwareVersion())
print('some random number from the dongle:')
print(map(hex, app.getRandom(20)))
dongle.close()
btchip-python-0.1.31/tests/testMessageSignature.py 0000664 0000000 0000000 00000004633 13736561771 0022274 0 ustar 00root root 0000000 0000000 """
*******************************************************************************
* BTChip Bitcoin Hardware Wallet Python API
* (c) 2014 BTChip - 1BTChip7VfTnrPra5jqci7ejnMguuHogTn
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
********************************************************************************
"""
from btchip.btchip import *
from btchip.btchipUtils import *
# Run on non configured dongle or dongle configured with test seed below
SEED = bytearray("1762F9A3007DBC825D0DD9958B04880284E88F10C57CF569BB3DADF7B1027F2D".decode('hex'))
MESSAGE = "Campagne de Sarkozy : une double comptabilite chez Bygmalion"
SECONDFACTOR_1 = "Powercycle then confirm signature of .Campagne de Sarkozy : une double comptabilite chez Bygmalion. for address 17JusYNVXLPm3hBPzzRQkARYDMUBgRUMVc with PIN"
SIGNATURE = bytearray("30450221009a0d28391c0535aec1077bbb86614c8f3c384a3e9aa1a124bfb9ce9649196b7e02200efa1adc010a7bdde4784ee98441e402f93b3c50a2760cb09dda07501e02c81f".decode('hex'))
# Optional setup
dongle = getDongle(True)
app = btchip(dongle)
try:
app.setup(btchip.OPERATION_MODE_WALLET, btchip.FEATURE_RFC6979, 0x00, 0x05, "1234", None, btchip.QWERTY_KEYMAP, SEED)
except Exception:
pass
# Authenticate
app.verifyPin("1234")
# Start signing
app.signMessagePrepare("0'/0/0", MESSAGE)
dongle.close()
# Wait for the second factor confirmation
# Done on the same application for test purposes, this is typically done in another window
# or another computer for bigger transactions
response = raw_input("Powercycle the dongle to get the second factor and powercycle again : ")
if not response.startswith(SECONDFACTOR_1):
raise BTChipException("Invalid second factor")
# Get a reference to the dongle again, as it was disconnected
dongle = getDongle(True)
app = btchip(dongle)
# Compute the signature
signature = app.signMessageSign(response[len(response) - 4:])
if signature <> SIGNATURE:
raise BTChipException("Invalid signature")
btchip-python-0.1.31/tests/testMultisigArmory.py 0000664 0000000 0000000 00000023072 13736561771 0022013 0 ustar 00root root 0000000 0000000 """
*******************************************************************************
* BTChip Bitcoin Hardware Wallet Python API
* (c) 2014 BTChip - 1BTChip7VfTnrPra5jqci7ejnMguuHogTn
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
********************************************************************************
"""
from btchip.btchip import *
from btchip.btchipUtils import *
import json
"""
Signs a TX generated by Armory. That TX:
{
'inputs': [{
'p2shscript': '52210269694830114e4b1f6ef565ce4efb933681032d30333c80df713df6b60a4c62832102f43b905e9e35ccd22757faedf9eceb652dc9ba198a3904d43f4298def0213eb521037b9e3578dd3b5559d613bc2641931e6ce7d55a9d081b07347888d7d17a2b910253ae',
'supporttxhash_be': '0c1676b8fc1adaca53221290e242b8eb80fd6b89aa83f2fa0106f87e13388300',
'sequence': 4294967295,
'keys': [{
'dersighex': '',
'pubkeyhex': '0269694830114e4b1f6ef565ce4efb933681032d30333c80df713df6b60a4c6283',
'wltlochex': ''
}, {
'dersighex': '',
'pubkeyhex': '02f43b905e9e35ccd22757faedf9eceb652dc9ba198a3904d43f4298def0213eb5',
'wltlochex': ''
}, {
'dersighex': '',
'pubkeyhex': '037b9e3578dd3b5559d613bc2641931e6ce7d55a9d081b07347888d7d17a2b9102',
'wltlochex': ''
}],
'contriblabel': u '',
'supporttxhash_le': '008338137ef80601faf283aa896bfd80ebb842e290122253cada1afcb876160c',
'contribid': 'JLBercZk',
'version': 1,
'inputvalue': 46000000,
'outpoint': '008338137ef80601faf283aa896bfd80ebb842e290122253cada1afcb876160c00000000',
'magicbytes': '0b110907',
'supporttx': '01000000013e9fe12917d854a0e093b982eaa46990289e2262f2db9fc1bd3f13718f3c806e010000006b483045022100af668e482e3ed363f51b36ddabad7cdf20d177104c92b8676a5b14f51107179602206c4ecd67544c74c6689ca453e2157d0c0b8a4608d85956429d2615275a51c66f01210374db359a004626daf2fcf10b8601f5f39438848a6733c768e88ce0ad398ae79dffffffff0280e7bd020000000017a914e2a227eb40dfce902f2c1d80ddafa798b16d22c3876c8fc846000000001976a914af58f09cf65b213bb9bd181a94e133b4ad4d6b2788ac00000000',
'numkeys': 3,
'supporttxhash': '0c1676b8fc1adaca53221290e242b8eb80fd6b89aa83f2fa0106f87e13388300',
'supporttxoutindex': 0
}],
'fee': 10000,
'locktimeint': 0,
'outputs': [{
'txoutvalue': 10000000,
'authdata': '',
'contriblabel': '',
'p2shscript': '',
'scripttypeint': 4,
'isp2sh': True,
'txoutscript': 'a914c0c3b6ada732c797881d00de6c350eec96e3d22287',
'authmethod': 'NONE',
'hasaddrstr': True,
'contribid': '',
'version': 1,
'ismultisig': False,
'magicbytes': '0b110907',
'addrstr': '2NApUBXv4NB8pm834pHUajiUL6rvFaaj6N8',
'scripttypestr': 'Standard (P2SH)',
'wltlocator': ''
}, {
'txoutvalue': 35990000,
'authdata': '',
'contriblabel': '',
'p2shscript': '',
'scripttypeint': 4,
'isp2sh': True,
'txoutscript': 'a914e2a227eb40dfce902f2c1d80ddafa798b16d22c387',
'authmethod': 'NONE',
'hasaddrstr': True,
'contribid': '',
'version': 1,
'ismultisig': False,
'magicbytes': '0b110907',
'addrstr': '2NDuYxRrmAs2fRcMj4ew2F41aFp2PN9yiV1',
'scripttypestr': 'Standard (P2SH)',
'wltlocator': ''
}],
'sumoutputs': 45990000,
'suminputs': 46000000,
'version': 1,
'numoutputs': 2,
'magicbytes': '0b110907',
'locktimedate': '',
'locktimeblock': 0,
'id': '8jkccikU',
'numinputs': 1
}
Input comes from vout[0] of 0c1676b8fc1adaca53221290e242b8eb80fd6b89aa83f2fa0106f87e13388300.
TX I want to generate is 0.10 to 2NApUBXv4NB8pm834pHUajiUL6rvFaaj6N8
The multisig address 2NDuYxRrmAs2fRcMj4ew2F41aFp2PN9yiV1 contains 0.46 BTC, and is generated
using the public keys 0'/0/0, 0'/0/1, and 0'/0/2 from the seed below.
"""
# Run on non configured dongle or dongle configured with test seed below
SEED = bytearray("1762F9A3007DBC825D0DD9958B04880284C88A10C57CF569BB3DADF7B1027F2D".decode('hex'))
# Armory supporttx
UTX = bytearray("01000000013e9fe12917d854a0e093b982eaa46990289e2262f2db9fc1bd3f13718f3c806e010000006b483045022100af668e482e3ed363f51b36ddabad7cdf20d177104c92b8676a5b14f51107179602206c4ecd67544c74c6689ca453e2157d0c0b8a4608d85956429d2615275a51c66f01210374db359a004626daf2fcf10b8601f5f39438848a6733c768e88ce0ad398ae79dffffffff0280e7bd020000000017a914e2a227eb40dfce902f2c1d80ddafa798b16d22c3876c8fc846000000001976a914af58f09cf65b213bb9bd181a94e133b4ad4d6b2788ac00000000".decode('hex'))
UTXO_INDEX = 0
OUTPUT = bytearray("02809698000000000017a914c0c3b6ada732c797881d00de6c350eec96e3d22287f02925020000000017a914e2a227eb40dfce902f2c1d80ddafa798b16d22c387".decode('hex'))
# Armory p2shscript
REDEEMSCRIPT = bytearray("52210269694830114e4b1f6ef565ce4efb933681032d30333c80df713df6b60a4c62832102f43b905e9e35ccd22757faedf9eceb652dc9ba198a3904d43f4298def0213eb521037b9e3578dd3b5559d613bc2641931e6ce7d55a9d081b07347888d7d17a2b910253ae".decode('hex'))
SIGNATURE_0 = bytearray("3044022056cb1b781fd04cfe6c04756ad56d02e5512f3fe7f411bc22d1594da5c815a393022074ad7f4d47af7c3f8a7ddf0ba2903f986a88649b0018ce1538c379b304a6a23801".decode('hex'))
SIGNATURE_1 = bytearray("304402205545419c4aded39c7f194b3f8c828f90e8d9352c756f7c131ed50e189c02f29a02201b160503d7310df49055b04a327e185fc22dfe68f433594ed7ce526d99a5026001".decode('hex'))
SIGNATURE_2 = bytearray("30440220634fbbfaaea74d42280a8c9e56c97418af04539f93458e85285d15462aec7712022041ba27a5644642a2f5b3c02610235ec2c6115bf4137bb51181cbc0a3a54dc0db01".decode('hex'))
TRANSACTION = bytearray("0100000001008338137ef80601faf283aa896bfd80ebb842e290122253cada1afcb876160c00000000fc004730440220634fbbfaaea74d42280a8c9e56c97418af04539f93458e85285d15462aec7712022041ba27a5644642a2f5b3c02610235ec2c6115bf4137bb51181cbc0a3a54dc0db0147304402205545419c4aded39c7f194b3f8c828f90e8d9352c756f7c131ed50e189c02f29a02201b160503d7310df49055b04a327e185fc22dfe68f433594ed7ce526d99a50260014c6952210269694830114e4b1f6ef565ce4efb933681032d30333c80df713df6b60a4c62832102f43b905e9e35ccd22757faedf9eceb652dc9ba198a3904d43f4298def0213eb521037b9e3578dd3b5559d613bc2641931e6ce7d55a9d081b07347888d7d17a2b910253aeffffffff02809698000000000017a914c0c3b6ada732c797881d00de6c350eec96e3d22287f02925020000000017a914e2a227eb40dfce902f2c1d80ddafa798b16d22c38700000000".decode('hex'))
SECONDFACTOR_1 = "RELAXED MODE Powercycle then confirm use of 0.46 BTC with PIN"
# Armory txoutscript
output = get_output_script([["0.1", bytearray("a914c0c3b6ada732c797881d00de6c350eec96e3d22287".decode('hex'))], ["0.3599", bytearray("a914e2a227eb40dfce902f2c1d80ddafa798b16d22c387".decode('hex'))]]);
if output<>OUTPUT:
raise BTChipException("Invalid output script encoding");
# Optional setup
dongle = getDongle(True)
app = btchip(dongle)
try:
app.setup(btchip.OPERATION_MODE_RELAXED_WALLET, btchip.FEATURE_RFC6979, 111, 196, "1234", None, btchip.QWERTY_KEYMAP, SEED)
except Exception:
pass
# Authenticate
app.verifyPin("1234")
# Get the trusted input associated to the UTXO
transaction = bitcoinTransaction(UTX)
print transaction
trustedInput = app.getTrustedInput(transaction, UTXO_INDEX)
# Start composing the transaction
app.startUntrustedTransaction(True, 0, [trustedInput], REDEEMSCRIPT)
app.finalizeInputFull(OUTPUT)
dongle.close()
# Wait for the second factor confirmation
# Done on the same application for test purposes, this is typically done in another window
# or another computer for bigger transactions
response = raw_input("Powercycle the dongle to get the second factor and powercycle again : ")
if not response.startswith(SECONDFACTOR_1):
raise BTChipException("Invalid second factor")
# Get a reference to the dongle again, as it was disconnected
dongle = getDongle(True)
app = btchip(dongle)
# Replay the transaction, this time continue it since the second factor is ready
app.startUntrustedTransaction(False, 0, [trustedInput], REDEEMSCRIPT)
app.finalizeInputFull(OUTPUT)
# Provide the second factor to finalize the signature
signature1 = app.untrustedHashSign("0'/0/1", response[len(response) - 4:])
if signature1 <> SIGNATURE_1:
raise BTChipException("Invalid signature1")
# Same thing for the second signature
app.verifyPin("1234")
app.startUntrustedTransaction(True, 0, [trustedInput], REDEEMSCRIPT)
app.finalizeInputFull(OUTPUT)
dongle.close()
response = raw_input("Powercycle the dongle to get the second factor and powercycle again : ")
if not response.startswith(SECONDFACTOR_1):
raise BTChipException("Invalid second factor")
dongle = getDongle(True)
app = btchip(dongle)
app.startUntrustedTransaction(False, 0, [trustedInput], REDEEMSCRIPT)
app.finalizeInputFull(OUTPUT)
signature2 = app.untrustedHashSign("0'/0/2", response[len(response) - 4:])
if signature2 <> SIGNATURE_2:
raise BTChipException("Invalid signature2")
# Finalize the transaction - build the redeem script and put everything together
inputScript = get_p2sh_input_script(REDEEMSCRIPT, [signature2, signature1])
transaction = format_transaction(OUTPUT, [ [ trustedInput['value'], inputScript] ])
print "Generated transaction : " + str(transaction).encode('hex')
if transaction <> TRANSACTION:
raise BTChipException("Invalid transaction")
# The transaction is ready to be broadcast, enjoy
btchip-python-0.1.31/tests/testMultisigArmoryNo2FA.py 0000664 0000000 0000000 00000020556 13736561771 0022605 0 ustar 00root root 0000000 0000000 """
*******************************************************************************
* BTChip Bitcoin Hardware Wallet Python API
* (c) 2014 BTChip - 1BTChip7VfTnrPra5jqci7ejnMguuHogTn
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
********************************************************************************
"""
from btchip.btchip import *
from btchip.btchipUtils import *
import json
"""
Signs a TX generated by Armory. That TX:
{
'inputs': [{
'p2shscript': '52210269694830114e4b1f6ef565ce4efb933681032d30333c80df713df6b60a4c62832102f43b905e9e35ccd22757faedf9eceb652dc9ba198a3904d43f4298def0213eb521037b9e3578dd3b5559d613bc2641931e6ce7d55a9d081b07347888d7d17a2b910253ae',
'supporttxhash_be': '0c1676b8fc1adaca53221290e242b8eb80fd6b89aa83f2fa0106f87e13388300',
'sequence': 4294967295,
'keys': [{
'dersighex': '',
'pubkeyhex': '0269694830114e4b1f6ef565ce4efb933681032d30333c80df713df6b60a4c6283',
'wltlochex': ''
}, {
'dersighex': '',
'pubkeyhex': '02f43b905e9e35ccd22757faedf9eceb652dc9ba198a3904d43f4298def0213eb5',
'wltlochex': ''
}, {
'dersighex': '',
'pubkeyhex': '037b9e3578dd3b5559d613bc2641931e6ce7d55a9d081b07347888d7d17a2b9102',
'wltlochex': ''
}],
'contriblabel': u '',
'supporttxhash_le': '008338137ef80601faf283aa896bfd80ebb842e290122253cada1afcb876160c',
'contribid': 'JLBercZk',
'version': 1,
'inputvalue': 46000000,
'outpoint': '008338137ef80601faf283aa896bfd80ebb842e290122253cada1afcb876160c00000000',
'magicbytes': '0b110907',
'supporttx': '01000000013e9fe12917d854a0e093b982eaa46990289e2262f2db9fc1bd3f13718f3c806e010000006b483045022100af668e482e3ed363f51b36ddabad7cdf20d177104c92b8676a5b14f51107179602206c4ecd67544c74c6689ca453e2157d0c0b8a4608d85956429d2615275a51c66f01210374db359a004626daf2fcf10b8601f5f39438848a6733c768e88ce0ad398ae79dffffffff0280e7bd020000000017a914e2a227eb40dfce902f2c1d80ddafa798b16d22c3876c8fc846000000001976a914af58f09cf65b213bb9bd181a94e133b4ad4d6b2788ac00000000',
'numkeys': 3,
'supporttxhash': '0c1676b8fc1adaca53221290e242b8eb80fd6b89aa83f2fa0106f87e13388300',
'supporttxoutindex': 0
}],
'fee': 10000,
'locktimeint': 0,
'outputs': [{
'txoutvalue': 10000000,
'authdata': '',
'contriblabel': '',
'p2shscript': '',
'scripttypeint': 4,
'isp2sh': True,
'txoutscript': 'a914c0c3b6ada732c797881d00de6c350eec96e3d22287',
'authmethod': 'NONE',
'hasaddrstr': True,
'contribid': '',
'version': 1,
'ismultisig': False,
'magicbytes': '0b110907',
'addrstr': '2NApUBXv4NB8pm834pHUajiUL6rvFaaj6N8',
'scripttypestr': 'Standard (P2SH)',
'wltlocator': ''
}, {
'txoutvalue': 35990000,
'authdata': '',
'contriblabel': '',
'p2shscript': '',
'scripttypeint': 4,
'isp2sh': True,
'txoutscript': 'a914e2a227eb40dfce902f2c1d80ddafa798b16d22c387',
'authmethod': 'NONE',
'hasaddrstr': True,
'contribid': '',
'version': 1,
'ismultisig': False,
'magicbytes': '0b110907',
'addrstr': '2NDuYxRrmAs2fRcMj4ew2F41aFp2PN9yiV1',
'scripttypestr': 'Standard (P2SH)',
'wltlocator': ''
}],
'sumoutputs': 45990000,
'suminputs': 46000000,
'version': 1,
'numoutputs': 2,
'magicbytes': '0b110907',
'locktimedate': '',
'locktimeblock': 0,
'id': '8jkccikU',
'numinputs': 1
}
Input comes from vout[0] of 0c1676b8fc1adaca53221290e242b8eb80fd6b89aa83f2fa0106f87e13388300.
TX I want to generate is 0.10 to 2NApUBXv4NB8pm834pHUajiUL6rvFaaj6N8
The multisig address 2NDuYxRrmAs2fRcMj4ew2F41aFp2PN9yiV1 contains 0.46 BTC, and is generated
using the public keys 0'/0/0, 0'/0/1, and 0'/0/2 from the seed below.
"""
# Run on non configured dongle or dongle configured with test seed below
SEED = bytearray("1762F9A3007DBC825D0DD9958B04880284C88A10C57CF569BB3DADF7B1027F2D".decode('hex'))
UTX = bytearray("01000000013e9fe12917d854a0e093b982eaa46990289e2262f2db9fc1bd3f13718f3c806e010000006b483045022100af668e482e3ed363f51b36ddabad7cdf20d177104c92b8676a5b14f51107179602206c4ecd67544c74c6689ca453e2157d0c0b8a4608d85956429d2615275a51c66f01210374db359a004626daf2fcf10b8601f5f39438848a6733c768e88ce0ad398ae79dffffffff0280e7bd020000000017a914e2a227eb40dfce902f2c1d80ddafa798b16d22c3876c8fc846000000001976a914af58f09cf65b213bb9bd181a94e133b4ad4d6b2788ac00000000".decode('hex'))
UTXO_INDEX = 0
OUTPUT = bytearray("02809698000000000017a914c0c3b6ada732c797881d00de6c350eec96e3d22287f02925020000000017a914e2a227eb40dfce902f2c1d80ddafa798b16d22c387".decode('hex'))
# Armory p2shscript
REDEEMSCRIPT = bytearray("52210269694830114e4b1f6ef565ce4efb933681032d30333c80df713df6b60a4c62832102f43b905e9e35ccd22757faedf9eceb652dc9ba198a3904d43f4298def0213eb521037b9e3578dd3b5559d613bc2641931e6ce7d55a9d081b07347888d7d17a2b910253ae".decode('hex'))
SIGNATURE_0 = bytearray("3044022056cb1b781fd04cfe6c04756ad56d02e5512f3fe7f411bc22d1594da5c815a393022074ad7f4d47af7c3f8a7ddf0ba2903f986a88649b0018ce1538c379b304a6a23801".decode('hex'))
SIGNATURE_1 = bytearray("304402205545419c4aded39c7f194b3f8c828f90e8d9352c756f7c131ed50e189c02f29a02201b160503d7310df49055b04a327e185fc22dfe68f433594ed7ce526d99a5026001".decode('hex'))
SIGNATURE_2 = bytearray("30440220634fbbfaaea74d42280a8c9e56c97418af04539f93458e85285d15462aec7712022041ba27a5644642a2f5b3c02610235ec2c6115bf4137bb51181cbc0a3a54dc0db01".decode('hex'))
# Armory supporttx
TRANSACTION = bytearray("0100000001008338137ef80601faf283aa896bfd80ebb842e290122253cada1afcb876160c00000000fc004730440220634fbbfaaea74d42280a8c9e56c97418af04539f93458e85285d15462aec7712022041ba27a5644642a2f5b3c02610235ec2c6115bf4137bb51181cbc0a3a54dc0db0147304402205545419c4aded39c7f194b3f8c828f90e8d9352c756f7c131ed50e189c02f29a02201b160503d7310df49055b04a327e185fc22dfe68f433594ed7ce526d99a50260014c6952210269694830114e4b1f6ef565ce4efb933681032d30333c80df713df6b60a4c62832102f43b905e9e35ccd22757faedf9eceb652dc9ba198a3904d43f4298def0213eb521037b9e3578dd3b5559d613bc2641931e6ce7d55a9d081b07347888d7d17a2b910253aeffffffff02809698000000000017a914c0c3b6ada732c797881d00de6c350eec96e3d22287f02925020000000017a914e2a227eb40dfce902f2c1d80ddafa798b16d22c38700000000".decode('hex'))
# Armory txoutscript
output = get_output_script([["0.1", bytearray("a914c0c3b6ada732c797881d00de6c350eec96e3d22287".decode('hex'))], ["0.3599", bytearray("a914e2a227eb40dfce902f2c1d80ddafa798b16d22c387".decode('hex'))]]);
if output<>OUTPUT:
raise BTChipException("Invalid output script encoding");
# Optional setup
dongle = getDongle(True)
app = btchip(dongle)
try:
app.setup(btchip.OPERATION_MODE_RELAXED_WALLET, btchip.FEATURE_RFC6979|btchip.FEATURE_NO_2FA_P2SH, 111, 196, "1234", None, btchip.QWERTY_KEYMAP, SEED)
except Exception:
pass
# Authenticate
app.verifyPin("1234")
# Get the trusted input associated to the UTXO
transaction = bitcoinTransaction(UTX)
print transaction
trustedInput = app.getTrustedInput(transaction, UTXO_INDEX)
# Start composing the transaction
app.startUntrustedTransaction(True, 0, [trustedInput], REDEEMSCRIPT)
app.finalizeInputFull(OUTPUT)
signature1 = app.untrustedHashSign("0'/0/1", "")
if signature1 <> SIGNATURE_1:
raise BTChipException("Invalid signature1")
# Same thing for the second signature
app.startUntrustedTransaction(True, 0, [trustedInput], REDEEMSCRIPT)
app.finalizeInputFull(OUTPUT)
signature2 = app.untrustedHashSign("0'/0/2", "")
if signature2 <> SIGNATURE_2:
raise BTChipException("Invalid signature2")
# Finalize the transaction - build the redeem script and put everything together
inputScript = get_p2sh_input_script(REDEEMSCRIPT, [signature2, signature1])
transaction = format_transaction(OUTPUT, [ [ trustedInput['value'], inputScript] ])
print "Generated transaction : " + str(transaction).encode('hex')
if transaction <> TRANSACTION:
raise BTChipException("Invalid transaction")
# The transaction is ready to be broadcast, enjoy
btchip-python-0.1.31/tests/testSimpleTransaction.py 0000664 0000000 0000000 00000010670 13736561771 0022463 0 ustar 00root root 0000000 0000000 """
*******************************************************************************
* BTChip Bitcoin Hardware Wallet Python API
* (c) 2014 BTChip - 1BTChip7VfTnrPra5jqci7ejnMguuHogTn
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
********************************************************************************
"""
from btchip.btchip import *
from btchip.btchipUtils import *
# Run on non configured dongle or dongle configured with test seed below
SEED = bytearray("1762F9A3007DBC825D0DD9958B04880284E88F10C57CF569BB3DADF7B1027F2D".decode('hex'))
UTX = bytearray("01000000014ea60aeac5252c14291d428915bd7ccd1bfc4af009f4d4dc57ae597ed0420b71010000008a47304402201f36a12c240dbf9e566bc04321050b1984cd6eaf6caee8f02bb0bfec08e3354b022012ee2aeadcbbfd1e92959f57c15c1c6debb757b798451b104665aa3010569b49014104090b15bde569386734abf2a2b99f9ca6a50656627e77de663ca7325702769986cf26cc9dd7fdea0af432c8e2becc867c932e1b9dd742f2a108997c2252e2bdebffffffff0281b72e00000000001976a91472a5d75c8d2d0565b656a5232703b167d50d5a2b88aca0860100000000001976a9144533f5fb9b4817f713c48f0bfe96b9f50c476c9b88ac00000000".decode('hex'))
UTXO_INDEX = 1
ADDRESS = "1BTChipvU14XH6JdRiK9CaenpJ2kJR9RnC"
AMOUNT = "0.0009"
FEES = "0.0001"
SECONDFACTOR_1 = "Powercycle then confirm transfer of 0.0009 BTC to 1BTChipvU14XH6JdRiK9CaenpJ2kJR9RnC fees 0.0001 BTC change 0 BTC with PIN"
SIGNATURE = bytearray("3045022100ea6df031b47629590daf5598b6f0680ad0132d8953b401577f01e8cc46393fe602202201b7a19d706a0213dcfeb7033719b92c6fd58a2d1d53411de71c4d8353154b01".decode('hex'))
TRANSACTION = bytearray("0100000001c773da236484dae8f0fdba3d7e0ba1d05070d1a34fc44943e638441262a04f10010000006b483045022100ea6df031b47629590daf5598b6f0680ad0132d8953b401577f01e8cc46393fe602202201b7a19d706a0213dcfeb7033719b92c6fd58a2d1d53411de71c4d8353154b01210348bb1fade0adde1bf202726e6db5eacd2063fce7ecf8bbfd17377f09218d5814ffffffff01905f0100000000001976a91472a5d75c8d2d0565b656a5232703b167d50d5a2b88ac00000000".decode('hex'))
# Optional setup
dongle = getDongle(True)
app = btchip(dongle)
try:
app.setup(btchip.OPERATION_MODE_WALLET, btchip.FEATURE_RFC6979, 0x00, 0x05, "1234", None, btchip.QWERTY_KEYMAP, SEED)
except Exception:
pass
# Authenticate
app.verifyPin("1234")
# Get the public key and compress it
publicKey = compress_public_key(app.getWalletPublicKey("0'/0/0")['publicKey'])
# Get the trusted input associated to the UTXO
transaction = bitcoinTransaction(UTX)
outputScript = transaction.outputs[UTXO_INDEX].script
trustedInput = app.getTrustedInput(transaction, UTXO_INDEX)
# Start composing the transaction
app.startUntrustedTransaction(True, 0, [trustedInput], outputScript)
outputData = app.finalizeInput(ADDRESS, AMOUNT, FEES, "0'/1/0")
dongle.close()
# Wait for the second factor confirmation
# Done on the same application for test purposes, this is typically done in another window
# or another computer for bigger transactions
response = raw_input("Powercycle the dongle to get the second factor and powercycle again : ")
if not response.startswith(SECONDFACTOR_1):
raise BTChipException("Invalid second factor")
# Get a reference to the dongle again, as it was disconnected
dongle = getDongle(True)
app = btchip(dongle)
# Replay the transaction, this time continue it since the second factor is ready
app.startUntrustedTransaction(False, 0, [trustedInput], outputScript)
app.finalizeInput(ADDRESS, "0.0009", "0.0001", "0'/1/0")
# Provide the second factor to finalize the signature
signature = app.untrustedHashSign("0'/0/0", response[len(response) - 4:])
if signature <> SIGNATURE:
raise BTChipException("Invalid signature")
# Finalize the transaction - build the redeem script and put everything together
inputScript = get_regular_input_script(signature, publicKey)
transaction = format_transaction(outputData['outputData'], [ [ trustedInput['value'], inputScript] ])
print "Generated transaction : " + str(transaction).encode('hex')
if transaction <> TRANSACTION:
raise BTChipException("Invalid transaction")
# The transaction is ready to be broadcast, enjoy
btchip-python-0.1.31/ui/ 0000775 0000000 0000000 00000000000 13736561771 0015021 5 ustar 00root root 0000000 0000000 btchip-python-0.1.31/ui/make.sh 0000775 0000000 0000000 00000001367 13736561771 0016304 0 ustar 00root root 0000000 0000000 #!/bin/bash
pyuic4 personalization-00-start.ui -o ../btchip/ui/personalization00start.py
pyuic4 personalization-01-seed.ui -o ../btchip/ui/personalization01seed.py
pyuic4 personalization-02-security.ui -o ../btchip/ui/personalization02security.py
pyuic4 personalization-03-config.ui -o ../btchip/ui/personalization03config.py
pyuic4 personalization-04-finalize.ui -o ../btchip/ui/personalization04finalize.py
pyuic4 personalization-seedbackup-01.ui -o ../btchip/ui/personalizationseedbackup01.py
pyuic4 personalization-seedbackup-02.ui -o ../btchip/ui/personalizationseedbackup02.py
pyuic4 personalization-seedbackup-03.ui -o ../btchip/ui/personalizationseedbackup03.py
pyuic4 personalization-seedbackup-04.ui -o ../btchip/ui/personalizationseedbackup04.py
btchip-python-0.1.31/ui/personalization-00-start.ui 0000664 0000000 0000000 00000004553 13736561771 0022160 0 ustar 00root root 0000000 0000000
Dialog
0
0
400
231
BTChip setup
120
20
231
31
20
75
true
true
BTChip setup
20
60
351
61
Your BTChip dongle is not set up - you'll be able to create a new wallet, or restore an existing one, and choose your security profile.
true
310
200
75
25
Next
20
120
351
81
Sensitive information including your dongle PIN will be exchanged during this setup phase - it is recommended to execute it on a secure computer, disconnected from any network, especially if you restore a wallet backup.
true
20
200
75
25
Cancel
btchip-python-0.1.31/ui/personalization-01-seed.ui 0000664 0000000 0000000 00000007136 13736561771 0021744 0 ustar 00root root 0000000 0000000
Dialog
0
0
400
300
BTChip setup - seed
50
20
311
31
20
75
true
true
BTChip setup - seed (1/3)
20
60
351
61
Please select an option : either create a new wallet or restore an existing one
true
20
130
94
21
New Wallet
true
buttonGroup
20
180
171
21
Restore wallet backup
buttonGroup
false
50
210
331
21
QLineEdit::Normal
Enter an hexadecimal seed or a BIP 39 mnemonic code
10
270
75
25
Cancel
320
270
75
25
Next
130
240
171
31
true
Mnemonic API is not available
true
btchip-python-0.1.31/ui/personalization-02-security.ui 0000664 0000000 0000000 00000012506 13736561771 0022671 0 ustar 00root root 0000000 0000000
Dialog
0
0
400
503
BTChip setup - security
20
20
361
31
20
75
true
true
BTChip setup - security (2/3)
10
60
351
61
Please choose a security profile
true
20
110
81
21
Hardened
true
buttonGroup
20
210
81
21
PIN only
buttonGroup
50
140
351
61
You need to remove the dongle and insert it again to get a second factor validation of all operations. Recommended for expert users and to be fully protected against malwares.
true
50
230
351
61
You only need to enter a PIN once when inserting the dongle. Transactions are not protected against malwares
true
10
470
75
25
Cancel
310
470
75
25
Next
10
300
351
61
Please choose a PIN associated to the BTChip dongle. The PIN protects the dongle in case it is stolen, and can be up to 32 characters. The dongle is wiped if a wrong PIN is entered 3 times in a row.
true
20
380
161
31
Enter the new PIN :
true
210
380
161
21
QLineEdit::Password
210
420
161
21
QLineEdit::Password
20
420
171
31
Repeat the new PIN :
true
btchip-python-0.1.31/ui/personalization-03-config.ui 0000664 0000000 0000000 00000006003 13736561771 0022263 0 ustar 00root root 0000000 0000000
Dialog
0
0
400
243
BTChip setup
30
10
361
31
20
75
true
true
BTChip setup - config (3/3)
20
50
351
61
Please select your keyboard type to type the second factor confirmation
true
50
110
94
21
QWERTY
true
keyboardGroup
50
140
94
21
QWERTZ
keyboardGroup
50
170
94
21
AZERTY
keyboardGroup
10
210
75
25
Cancel
320
210
75
25
Next
btchip-python-0.1.31/ui/personalization-04-finalize.ui 0000664 0000000 0000000 00000005225 13736561771 0022625 0 ustar 00root root 0000000 0000000
Dialog
0
0
400
267
BTChip setup - security
20
20
361
31
20
75
true
true
BTChip setup - completed
320
230
75
25
Finish
10
70
351
61
BTChip setup is completed. Please enter your PIN to validate it then press Finish
true
50
140
121
21
BTChip PIN :
true
200
140
181
21
QLineEdit::Password
120
170
171
31
true
Remaining attempts
true
btchip-python-0.1.31/ui/personalization-seedbackup-01.ui 0000664 0000000 0000000 00000006221 13736561771 0023124 0 ustar 00root root 0000000 0000000
Dialog
0
0
400
300
BTChip setup
30
20
351
31
20
75
true
true
BTChip setup - seed backup
320
270
75
25
Next
10
100
351
31
A new seed has been generated for your wallet.
true
10
140
351
31
You must backup this seed and keep it out of reach of hackers (typically by keeping it on paper).
true
10
180
351
41
You can use this seed to restore your dongle if you lose it or access your funds with any other compatible wallet.
true
90
60
251
31
20
75
true
true
READ CAREFULLY
10
220
351
41
Press Next to start the backuping process.
true
btchip-python-0.1.31/ui/personalization-seedbackup-02.ui 0000664 0000000 0000000 00000002735 13736561771 0023133 0 ustar 00root root 0000000 0000000
Dialog
0
0
400
300
BTChip setup
30
20
351
31
20
75
true
true
BTChip setup - seed backup
20
70
351
31
Please disconnect the dongle then press Next
true
320
270
75
25
Next
btchip-python-0.1.31/ui/personalization-seedbackup-03.ui 0000664 0000000 0000000 00000010456 13736561771 0023133 0 ustar 00root root 0000000 0000000
Dialog
0
0
400
513
BTChip setup
20
10
351
31
20
75
true
true
BTChip setup - seed backup
20
50
351
61
If you do not trust this computer, perform the following steps on a trusted one or a different device. Anything supporting keyboard input will work (smartphone, TV box ...)
true
20
120
351
31
Open a text editor, set the focus on the text editor, then insert the dongle
true
20
160
351
51
After a very short time, the dongle will type the seed as hexadecimal (0..9 A..F) characters, starting with "seed" and ending with "X"
true
20
220
351
51
If you perform those steps on Windows, a new device driver will be loaded the first time and the seed will not be typed. This is normal.
true
20
280
351
71
If you perform those steps on Mac, you'll get a popup asking you to select a keyboard type the first time and the seed will not be typed. This is normal, just close the popup.
true
20
350
351
51
If you did not see the seed for any reason, keep the focus on the text editor, unplug and plug the dongle again twice.
true
20
410
351
51
Then press Next once you wrote the seed to a safe medium (i.e. paper) and unplugged the dongle
true
310
480
75
25
Next
btchip-python-0.1.31/ui/personalization-seedbackup-04.ui 0000664 0000000 0000000 00000003610 13736561771 0023126 0 ustar 00root root 0000000 0000000
Dialog
0
0
554
190
BTChip setup
30
10
351
31
20
75
true
true
BTChip setup - seed backup
10
50
351
51
Did you see the seed correctly displayed and did you backup it properly ?
true
20
140
501
25
Yes, the seed is backed up properly and kept in a safe place, move on
20
110
501
25
No, I didn't see the seed. Wipe the dongle and start over