Compare commits

..

9 Commits
mods ... master

Author SHA1 Message Date
Ethan Kosakovsky
94408e5bc2 Add more gitignores 2020-04-19 07:08:14 +00:00
Ethan Kosakovsky
e0b4684bd6 Make it PyPi ready 2020-04-19 07:07:10 +00:00
Ethan Kosakovsky
89c0f85778 Bug fixes and implement Peter Gray's XPRV suggestion 2020-04-19 06:30:00 +00:00
Ethan Kosakovsky
bd853194ed Fix path bug 2020-04-19 06:29:55 +00:00
Ethan Kosakovsky
0cc3428052 Update gitignore 2020-04-19 06:29:39 +00:00
Ethan Kosakovsky
cecfc373c2 Fix HMAC param order and tests 2020-04-19 06:29:32 +00:00
Ethan Kosakovsky
d1d9654c00 Refactor for API layer 2020-04-19 06:29:24 +00:00
Peter D. Gray
d179b9a330 hex cases added 2020-04-19 14:25:01 +08:00
Peter D. Gray
d53ec6c957 bunch o mods 2020-04-19 14:25:01 +08:00
6 changed files with 130 additions and 39 deletions

3
.gitignore vendored
View File

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

64
bipentropy/app.py Normal file
View File

@ -0,0 +1,64 @@
#!/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,20 +24,23 @@ 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 = path.replace("m/", "").replace("'", "p")
path = self.__decorate_path(path)
node = xprv.subkey_for_path(path)
return self.__get_k_from_node(node)
def __hmac_sha512(self, message_k):
return hmac.new(message_k, msg=b'bip-entropy-from-k', digestmod=hashlib.sha512).digest()
return hmac.new(key=b'bip-entropy-from-k', msg=message_k, digestmod=hashlib.sha512).digest()
def bip39_mnemonic_to_entropy(self, path, mnemonic, passphrase=''):
bip39_seed = bip39.to_seed(mnemonic, passphrase=passphrase)
@ -50,23 +53,29 @@ class BIPEntropy(object):
raise ValueError('ERROR: Invalid xprv')
return self.__hmac_sha512(self.__derive_k(path, xprv))
def bip32_xprv_to_hex(self, index, width, xprv_string):
def bip32_xprv_to_hex(self, path, width, xprv_string):
# export entropy as hex
path = f"83696968p/128169p/{width}p/{index}p"
path = self.__decorate_path(path)
ent = self.bip32_xprv_to_entropy(path, xprv_string)
return ent[0:width].hex()
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)
def bip32_xprv_to_xprv(self, path, xprv_string):
path = self.__decorate_path(path)
ent = self.bip32_xprv_to_entropy(path, 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
# 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)
return node.hwif(as_private=True)

View File

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

View File

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

View File

@ -20,15 +20,17 @@
# 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 = '71356cdf2f6851c0499b47fe16ef121883f8c58e7d6fb4c33a9017df71f3b0fe21f92d043ee4f19676384a3d943554904caca131269dcb84151ecb2f7ca31902'
expected = 'efecfbccffea313214232d29e71563d941229afb4338c21f9517c41aaa0d16f00b83d2a09ef747e7a64e8e2bd5a14869e693da66ce94ac2da570ab7ee48618f7'
assert test.hex() == expected
# with password
@ -39,7 +41,7 @@ def test_mnemonic():
def test_xprv_to_entropy():
e = bipentropy.BIPEntropy()
test = e.bip32_xprv_to_entropy("m/83696968'/0'/0'", XPRV)
expected = '71356cdf2f6851c0499b47fe16ef121883f8c58e7d6fb4c33a9017df71f3b0fe21f92d043ee4f19676384a3d943554904caca131269dcb84151ecb2f7ca31902'
expected = 'efecfbccffea313214232d29e71563d941229afb4338c21f9517c41aaa0d16f00b83d2a09ef747e7a64e8e2bd5a14869e693da66ce94ac2da570ab7ee48618f7'
assert test.hex() == expected
@ -47,51 +49,62 @@ def test_entropy_to_mnemonic():
e = bipentropy.BIPEntropy()
entropy = e.bip32_xprv_to_entropy("m/83696968'/0'/0'", XPRV)
words12 = 'illness problem daughter gain lunar then chapter happy wrap resist setup corn'
words12 = 'useful guitar veteran zone perfect october explain grant clarify december flight recycle'
assert e.entropy_to_bip39(entropy, 12) == words12
words15 = 'illness problem daughter gain lunar then chapter happy wrap resist setup country display glare debate'
words15 = 'useful guitar veteran zone perfect october explain grant clarify december flight raw banana estate uncle'
assert e.entropy_to_bip39(entropy, 15) == words15
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'
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'
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'/0'/0'", XPRV)
assert e.entropy_to_wif(entropy) == 'L11mqGbaozsMYDHS7dfZ2bPGL2viSH6zHr69MKwvpxuw7cCR4M1u'
entropy = e.bip32_xprv_to_entropy("m/83696968'/2'/0'", XPRV)
entropy = entropy[:32]
assert e.entropy_to_wif(entropy) == 'Kzyv4uF39d4Jrw2W7UryTHwZr1zQVNk4dAFyqE6BuMrMh1Za7uhp'
def test_applications():
def test_mnemonic():
e = bipentropy.BIPEntropy()
entropy = e.bip32_xprv_to_entropy("m/83696968'/39'/0'/12'/0'", XPRV)
assert entropy[:16].hex() == 'f0337580e36fd50ef8734cd9dcfb9a78'
assert entropy[:16].hex() == '6250b68daf746d12a24d58b4787a714b'
assert e.entropy_to_bip39(entropy, 12) == \
'usual option gasp short wool manual tide hat supply treat track valve'
'girl mad pet galaxy egg matter matrix prison refuse sense ordinary nose'
entropy = e.bip32_xprv_to_entropy("m/83696968'/39'/0'/18'/0'", XPRV)
assert entropy[:24].hex() == '60529dbbf2707ea89e4cd41f7e26fcebf2b492b9a99c5e95'
assert entropy[:24].hex() == '938033ed8b12698449d4bbca3c853c66b293ea1b1ce9d9dc'
assert e.entropy_to_bip39(entropy, 18) == \
'gate neutral humble top among february junior once buyer van sand subject clip enable trade crime future protect'
'near account window bike charge season chef number sketch tomorrow excuse sniff circle vital hockey outdoor supply token'
entropy = e.bip32_xprv_to_entropy("m/83696968'/39'/0'/24'/0'", XPRV)
assert entropy[:32].hex() == '5166983339d6fc685abe49162327ac2e915fcc17132dad7a2b1e8f324b2f06bd'
assert entropy[:32].hex() == 'ae131e2312cdc61331542efe0d1077bac5ea803adf24b313a4f0e48e9c51f37f'
assert e.entropy_to_bip39(entropy, 24) == \
'fabric crumble art inhale hurt crouch helmet since bike bomb twelve frog bicycle toward fox grant pulp spend sibling bunker caution nurse brain prison'
'puppy ocean match cereal symbol another shed magic wrap hammer bulb intact gadget divorce twin tonight reason outdoor destroy simple truth cigar social volcano'
def test_xprv_application():
def test_xprv():
e = bipentropy.BIPEntropy()
result = e.bip32_xprv_to_xprv(0, XPRV)
assert result == 'xprv9s21ZrQH143K3KJoGoKpsDsWdDNDBKs1wqFymBpCGJtrYXrfKzykGDBadZq5SrNde22F83X9qhFZr4uyV9TptTgLqCBc6XFN9tssphdxVeg'
result = e.bip32_xprv_to_xprv("83696968'/32'/0'", XPRV)
assert result == 'xprv9s21ZrQH143K2srSbCSg4m4kLvPMzcWydgmKEnMmoZUurYuBuYG46c6P71UGXMzmriLzCCBvKQWBUv3vPB3m1SATMhp3uEjXHJ42jFg7myX'
@pytest.mark.parametrize('index, width, expect', [
(0, 32, '4f4ea2ef43af14e51f2453221d50762fc3767e2287dc524ca58f10e5225a6ead'),
(0, 64, '4fc6759ef9c0e12aed757ad874706a3955ef125c8f8eabb3909aeda028f5e285bf496a23265bac09f537f4cc5e1efa689f5625ded2cd996c042b2657263c6816'),
(1234, 64, 'e1a35aeae27cb3c93b0d437e94b64a891cdff9ac250537ec675d0e6a2680f1d2edf9927f4c5233b19b15fdea4063a8b62f85ff8666176d226741ed192075a8db'),
@pytest.mark.parametrize('path, width, expect', [
("83696968'/128169'/32'/0'", 32, 'ea3ceb0b02ee8e587779c63f4b7b3a21e950a213f1ec53cab608d13e8796e6dc'),
("83696968'/128169'/64'/0'", 64, '492db4698cf3b73a5a24998aa3e9d7fa96275d85724a91e71aa2d645442f878555d078fd1f1f67e368976f04137b1f7a0d19232136ca50c44614af72b5582a5c'),
("83696968'/128169'/64'/1234'", 64, '61d3c182f7388268463ef327c454a10bc01b3992fa9d2ee1b3891a6b487a5248793e61271066be53660d24e8cb76ff0cfdd0e84e478845d797324c195df9ab8e'),
])
def test_hex_application(index, width, expect):
def test_hex(path, width, expect):
e = bipentropy.BIPEntropy()
assert e.bip32_xprv_to_hex(index, width, XPRV) == expect
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'
if __name__ == "__main__":
pytest.main()