# -*- coding: utf-8 -*-
# BitcoinLib - Python Cryptocurrency Library
# MNEMONIC class for BIP0039 Mnemonic Key management
# © 2016 - 2018 June - 1200 Web Development <http://1200wd.com/>
# © 2019 July - mkbeh
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import hashlib
from aiobitcoin.encoding import change_base, normalize_string, to_bytes
from aiobitcoin.config.config import *
from aiobitcoin.config.secp256k1 import *
[docs]class Mnemonic:
def __init__(self, language=DEFAULT_LANGUAGE):
"""
Init Mnemonic class and read wordlist of specified language
:param language: use specific wordlist, i.e. chinese, dutch (in development), english, french, italian,
japanese or spanish. Leave empty for default 'english'
:type language: str
"""
self._wordlist = []
with open(os.path.join(WORDLIST_DIR, '%s.txt' % language)) as f:
self._wordlist = [w.strip() for w in f.readlines()]
[docs] @staticmethod
def checksum(data):
"""
Calculates checksum for given data key
:param data: key string
:type data: bytes, hexstring
:return str: Checksum of key in bits
"""
data = to_bytes(data)
if len(data) % 4 > 0:
raise ValueError('Data length in bits should be divisible by 32, but it is not (%d bytes = %d bits).' %
(len(data), len(data) * 8))
tx_hash = hashlib.sha256(data).digest()
return change_base(tx_hash, 256, 2, 256)[:len(data) * 8 // 32]
[docs] def to_mnemonic(self, data, add_checksum=True, check_on_curve=True):
"""
Convert key data entropy to Mnemonic sentence
:param data: Key data entropy
:type data: bytes, hexstring
:param add_checksum: Included a checksum? Default is True
:type add_checksum: bool
:param check_on_curve: Check if data integer value is on secp256k1 curve. Should be enabled when not
testing and working with crypto
:type check_on_curve: bool
:return str: Mnemonic passphrase consisting of a space seperated list of words
"""
data = to_bytes(data)
data_int = change_base(data, 256, 10)
if check_on_curve and not 0 < data_int < secp256k1_n:
raise ValueError("Integer value of data should be in secp256k1 domain between 1 and secp256k1_n-1")
if add_checksum:
binresult = change_base(data_int, 10, 2, len(data) * 8) + self.checksum(data)
wi = change_base(binresult, 2, 2048)
else:
wi = change_base(data_int, 10, 2048)
return normalize_string(' '.join([self._wordlist[i] for i in wi]))
[docs] def generate(self, strength=128, add_checksum=True, encoding=True):
"""
Generate a random Mnemonic key
Uses cryptographically secure os.urandom() function to generate data. Then creates a Mnemonic sentence with
the 'to_mnemonic' method.
:param strength: Key strength in number of bits, default is 128 bits. It advised to specify 128 bits or more,
i.e.: 128, 256, 512 or 1024
:type strength: int
:param add_checksum: Included a checksum? Default is True
:type add_checksum: bool
:param encoding
:type encoding: bool
:return str: Mnemonic passphrase consisting of a space seperated list of words
"""
if strength % 32 > 0:
raise ValueError("Strenght should be divisible by 32")
data = os.urandom(strength // 8)
ceed = self.to_mnemonic(data, add_checksum=add_checksum)
return ceed.encode('utf-8') if encoding else ceed