Compare commits

..

2 Commits
master ... mods

Author SHA1 Message Date
Peter D. Gray
cedb7765d9
hex cases added 2020-04-17 12:33:36 -04:00
Peter D. Gray
a338c764be
bunch o mods 2020-04-17 12:18:49 -04:00
6 changed files with 39 additions and 130 deletions

3
.gitignore vendored
View File

@ -1,4 +1 @@
__pycache__
.pytest_cache
dist/
build/

View File

@ -1,64 +0,0 @@
#!/usr/bin/env python
#
# Copyright (c) 2020 Ethan Kosakovsky <ethankosakovsky@protonmail.com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy of
# this software and associated documentation files (the "Software"), to deal in
# the Software without restriction, including without limitation the rights to
# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
# of the Software, and to permit persons to whom the Software is furnished to do
# so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
import bipentropy
def bip39(xprv_string, language, words, index):
# 83696968'/39'/language'/words'/index'
language_lookup = {
'english': 0,
'japanese': 1,
'korean': 2,
'spanish': 3,
'chinese_simplified': 4,
'chinese_traditional': 5,
'french': 6,
'italian': 7,
'czech': 8
}
lang_code = language_lookup[language]
e = bipentropy.BIPEntropy()
path = f"83696968p/39p/{lang_code}p/{words}p/{index}p"
entropy = e.bip32_xprv_to_entropy(path, xprv_string)
return e.entropy_to_bip39(entropy, words, language)
def wif(xprv_string, index):
# m/83696968'/2'/index'
e = bipentropy.BIPEntropy()
path = f"83696968p/2p/{index}p"
return e.entropy_to_wif(e.bip32_xprv_to_entropy(path, xprv_string))
def hex(xprv_string, index, width):
# m/83696968'/128169p'/index'
e = bipentropy.BIPEntropy()
path = f"83696968p/128169p/{width}p/{index}p"
return e.bip32_xprv_to_hex(path, width, xprv_string)
def xprv(xprv_string, index):
# 83696968'/32'/index'
e = bipentropy.BIPEntropy()
path = f"83696968p/32p/{index}p"
return e.bip32_xprv_to_xprv(path, xprv_string)

View File

@ -24,23 +24,20 @@ import hashlib
from mnemonic import Mnemonic as bip39
from pycoin.symbols.btc import network as BTC
from pycoin.encoding.bytes32 import from_bytes_32, to_bytes_32
import base58
class BIPEntropy(object):
def __decorate_path(self, path):
return path.replace("m/", "").replace("'", "p")
def __get_k_from_node(self, node):
return to_bytes_32(node.secret_exponent())
def __derive_k(self, path, xprv):
path = self.__decorate_path(path)
path = path.replace("m/", "").replace("'", "p")
node = xprv.subkey_for_path(path)
return self.__get_k_from_node(node)
def __hmac_sha512(self, message_k):
return hmac.new(key=b'bip-entropy-from-k', msg=message_k, digestmod=hashlib.sha512).digest()
return hmac.new(message_k, msg=b'bip-entropy-from-k', digestmod=hashlib.sha512).digest()
def bip39_mnemonic_to_entropy(self, path, mnemonic, passphrase=''):
bip39_seed = bip39.to_seed(mnemonic, passphrase=passphrase)
@ -53,29 +50,23 @@ class BIPEntropy(object):
raise ValueError('ERROR: Invalid xprv')
return self.__hmac_sha512(self.__derive_k(path, xprv))
def bip32_xprv_to_hex(self, path, width, xprv_string):
def bip32_xprv_to_hex(self, index, width, xprv_string):
# export entropy as hex
path = self.__decorate_path(path)
path = f"83696968p/128169p/{width}p/{index}p"
ent = self.bip32_xprv_to_entropy(path, xprv_string)
return ent[0:width].hex()
def bip32_xprv_to_xprv(self, path, xprv_string):
path = self.__decorate_path(path)
ent = self.bip32_xprv_to_entropy(path, xprv_string)
def bip32_xprv_to_xprv(self, index, xprv_string):
# app_no = 32 => XPRV to XPRV
path = f"83696968p/32p/{index}p"
node = BTC.parse.bip32_prv(xprv_string).subkey_for_path(path)
# From Peter Gray
# Taking 64 bytes of the HMAC digest, the first 32 bytes are the chain code, and second 32 bytes are the private
# key for BIP32 XPRV value. Child number, depth, and parent fingerprint are forced to zero.
prefix = b'\x04\x88\xad\xe4'
depth = b'\x00'
parent_fingerprint = b'\x00\x00\x00\x00'
child_num = b'\x00\x00\x00\x00'
chain_code = ent[:32]
private_key = b'\x00' + ent[32:]
extended_key = prefix + depth + parent_fingerprint + child_num + chain_code + private_key
checksum = hashlib.sha256(hashlib.sha256(extended_key).digest()).digest()[:4]
derived_xprv_string = base58.b58encode(extended_key + checksum).decode()
node = BTC.parse(derived_xprv_string)
# if API to pycoin hadn't been shitcoined:
# return BIP32Node(node.chain_code(), secret_exponent=node.secret_exponent()).hwif()
node._depth = 0
node._parent_fingerprint = bytes(4)
node._child_index = 0
return node.hwif(as_private=True)

View File

@ -1,4 +1,3 @@
pycoin
mnemonic
pytest
base58

View File

@ -20,8 +20,7 @@ setup(
description="Implementation of Bitcoin BIP Entropy",
long_description=read("README.md"),
url="https://github.com/ethankosakovsky/bipentropy",
download_url="https://github.com/ethankosakovsky/bipentropy/archive/0.1.tar.gz",
install_requires=["mnemonic", "pycoin", "base58", "pytest"],
install_requires=["mnemonic", "pycoin"],
zip_safe=False,
classifiers=[
"License :: OSI Approved :: MIT License",

View File

@ -20,17 +20,15 @@
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
from bipentropy import bipentropy
from bipentropy import app
import pytest
XPRV = 'xprv9s21ZrQH143K2LBWUUQRFXhucrQqBpKdRRxNVq2zBqsx8HVqFk2uYo8kmbaLLHRdqtQpUm98uKfu3vca1LqdGhUtyoFnCNkfmXRyPXLjbKb'
def test_mnemonic():
e = bipentropy.BIPEntropy()
mnemonic = 'install scatter logic circle pencil average fall shoe quantum disease suspect usage'
test = e.bip39_mnemonic_to_entropy("m/83696968'/0'/0'", mnemonic)
expected = 'efecfbccffea313214232d29e71563d941229afb4338c21f9517c41aaa0d16f00b83d2a09ef747e7a64e8e2bd5a14869e693da66ce94ac2da570ab7ee48618f7'
expected = '71356cdf2f6851c0499b47fe16ef121883f8c58e7d6fb4c33a9017df71f3b0fe21f92d043ee4f19676384a3d943554904caca131269dcb84151ecb2f7ca31902'
assert test.hex() == expected
# with password
@ -41,7 +39,7 @@ def test_mnemonic():
def test_xprv_to_entropy():
e = bipentropy.BIPEntropy()
test = e.bip32_xprv_to_entropy("m/83696968'/0'/0'", XPRV)
expected = 'efecfbccffea313214232d29e71563d941229afb4338c21f9517c41aaa0d16f00b83d2a09ef747e7a64e8e2bd5a14869e693da66ce94ac2da570ab7ee48618f7'
expected = '71356cdf2f6851c0499b47fe16ef121883f8c58e7d6fb4c33a9017df71f3b0fe21f92d043ee4f19676384a3d943554904caca131269dcb84151ecb2f7ca31902'
assert test.hex() == expected
@ -49,62 +47,51 @@ def test_entropy_to_mnemonic():
e = bipentropy.BIPEntropy()
entropy = e.bip32_xprv_to_entropy("m/83696968'/0'/0'", XPRV)
words12 = 'useful guitar veteran zone perfect october explain grant clarify december flight recycle'
words12 = 'illness problem daughter gain lunar then chapter happy wrap resist setup corn'
assert e.entropy_to_bip39(entropy, 12) == words12
words15 = 'useful guitar veteran zone perfect october explain grant clarify december flight raw banana estate uncle'
words15 = 'illness problem daughter gain lunar then chapter happy wrap resist setup country display glare debate'
assert e.entropy_to_bip39(entropy, 15) == words15
words24 = 'useful guitar veteran zone perfect october explain grant clarify december flight raw banana estate unfair grow search witness echo market primary alley forward boring'
words24 = 'illness problem daughter gain lunar then chapter happy wrap resist setup country display glare delay pupil regular border piano cook warfare what sentence supreme'
assert e.entropy_to_bip39(entropy, 24) == words24
def test_wif_from_entropy():
# PDG: not sure about this?
e = bipentropy.BIPEntropy()
entropy = e.bip32_xprv_to_entropy("m/83696968'/2'/0'", XPRV)
entropy = entropy[:32]
assert e.entropy_to_wif(entropy) == 'Kzyv4uF39d4Jrw2W7UryTHwZr1zQVNk4dAFyqE6BuMrMh1Za7uhp'
entropy = e.bip32_xprv_to_entropy("m/83696968'/0'/0'", XPRV)
assert e.entropy_to_wif(entropy) == 'L11mqGbaozsMYDHS7dfZ2bPGL2viSH6zHr69MKwvpxuw7cCR4M1u'
def test_mnemonic():
def test_applications():
e = bipentropy.BIPEntropy()
entropy = e.bip32_xprv_to_entropy("m/83696968'/39'/0'/12'/0'", XPRV)
assert entropy[:16].hex() == '6250b68daf746d12a24d58b4787a714b'
assert entropy[:16].hex() == 'f0337580e36fd50ef8734cd9dcfb9a78'
assert e.entropy_to_bip39(entropy, 12) == \
'girl mad pet galaxy egg matter matrix prison refuse sense ordinary nose'
'usual option gasp short wool manual tide hat supply treat track valve'
entropy = e.bip32_xprv_to_entropy("m/83696968'/39'/0'/18'/0'", XPRV)
assert entropy[:24].hex() == '938033ed8b12698449d4bbca3c853c66b293ea1b1ce9d9dc'
assert entropy[:24].hex() == '60529dbbf2707ea89e4cd41f7e26fcebf2b492b9a99c5e95'
assert e.entropy_to_bip39(entropy, 18) == \
'near account window bike charge season chef number sketch tomorrow excuse sniff circle vital hockey outdoor supply token'
'gate neutral humble top among february junior once buyer van sand subject clip enable trade crime future protect'
entropy = e.bip32_xprv_to_entropy("m/83696968'/39'/0'/24'/0'", XPRV)
assert entropy[:32].hex() == 'ae131e2312cdc61331542efe0d1077bac5ea803adf24b313a4f0e48e9c51f37f'
assert entropy[:32].hex() == '5166983339d6fc685abe49162327ac2e915fcc17132dad7a2b1e8f324b2f06bd'
assert e.entropy_to_bip39(entropy, 24) == \
'puppy ocean match cereal symbol another shed magic wrap hammer bulb intact gadget divorce twin tonight reason outdoor destroy simple truth cigar social volcano'
'fabric crumble art inhale hurt crouch helmet since bike bomb twelve frog bicycle toward fox grant pulp spend sibling bunker caution nurse brain prison'
def test_xprv():
def test_xprv_application():
e = bipentropy.BIPEntropy()
result = e.bip32_xprv_to_xprv("83696968'/32'/0'", XPRV)
assert result == 'xprv9s21ZrQH143K2srSbCSg4m4kLvPMzcWydgmKEnMmoZUurYuBuYG46c6P71UGXMzmriLzCCBvKQWBUv3vPB3m1SATMhp3uEjXHJ42jFg7myX'
result = e.bip32_xprv_to_xprv(0, XPRV)
assert result == 'xprv9s21ZrQH143K3KJoGoKpsDsWdDNDBKs1wqFymBpCGJtrYXrfKzykGDBadZq5SrNde22F83X9qhFZr4uyV9TptTgLqCBc6XFN9tssphdxVeg'
@pytest.mark.parametrize('path, width, expect', [
("83696968'/128169'/32'/0'", 32, 'ea3ceb0b02ee8e587779c63f4b7b3a21e950a213f1ec53cab608d13e8796e6dc'),
("83696968'/128169'/64'/0'", 64, '492db4698cf3b73a5a24998aa3e9d7fa96275d85724a91e71aa2d645442f878555d078fd1f1f67e368976f04137b1f7a0d19232136ca50c44614af72b5582a5c'),
("83696968'/128169'/64'/1234'", 64, '61d3c182f7388268463ef327c454a10bc01b3992fa9d2ee1b3891a6b487a5248793e61271066be53660d24e8cb76ff0cfdd0e84e478845d797324c195df9ab8e'),
@pytest.mark.parametrize('index, width, expect', [
(0, 32, '4f4ea2ef43af14e51f2453221d50762fc3767e2287dc524ca58f10e5225a6ead'),
(0, 64, '4fc6759ef9c0e12aed757ad874706a3955ef125c8f8eabb3909aeda028f5e285bf496a23265bac09f537f4cc5e1efa689f5625ded2cd996c042b2657263c6816'),
(1234, 64, 'e1a35aeae27cb3c93b0d437e94b64a891cdff9ac250537ec675d0e6a2680f1d2edf9927f4c5233b19b15fdea4063a8b62f85ff8666176d226741ed192075a8db'),
])
def test_hex(path, width, expect):
def test_hex_application(index, width, expect):
e = bipentropy.BIPEntropy()
assert e.bip32_xprv_to_hex(path, width, XPRV) == expect
def test_bipentropy_applications():
assert app.bip39(XPRV, 'english', 18, 0) == \
'near account window bike charge season chef number sketch tomorrow excuse sniff circle vital hockey outdoor supply token'
assert app.xprv(XPRV, 0) == \
'xprv9s21ZrQH143K2srSbCSg4m4kLvPMzcWydgmKEnMmoZUurYuBuYG46c6P71UGXMzmriLzCCBvKQWBUv3vPB3m1SATMhp3uEjXHJ42jFg7myX'
assert app.wif(XPRV, 0) == 'Kzyv4uF39d4Jrw2W7UryTHwZr1zQVNk4dAFyqE6BuMrMh1Za7uhp'
assert app.hex(XPRV, 0, 32) == 'ea3ceb0b02ee8e587779c63f4b7b3a21e950a213f1ec53cab608d13e8796e6dc'
assert e.bip32_xprv_to_hex(index, width, XPRV) == expect
if __name__ == "__main__":
pytest.main()