/usr/lib/python2.7/dist-packages/mnemonic/mnemonic.py is in python-mnemonic 0.12-1.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 | #
# Copyright (c) 2013 Pavol Rusnak
#
# 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 binascii
import hashlib
import hmac
import os
import sys
import unicodedata
from pbkdf2 import PBKDF2
PBKDF2_ROUNDS = 2048
class Mnemonic(object):
def __init__(self, language):
self.radix = 2048
with open('%s/%s.txt' % (self._get_directory(), language), 'r') as f:
self.wordlist = [w.strip() for w in f.readlines()]
if len(self.wordlist) != self.radix:
raise Exception('Wordlist should contain %d words, but it contains %d words.' % (self.radix, len(self.wordlist)))
@classmethod
def _get_directory(cls):
return os.path.join(os.path.dirname(__file__), 'wordlist')
@classmethod
def list_languages(cls):
return [ f.split('.')[0] for f in os.listdir(cls._get_directory()) if f.endswith('.txt') ]
@classmethod
def normalize_string(cls, txt):
if isinstance(txt, str if sys.version < '3' else bytes):
utxt = txt.decode('utf8')
elif isinstance(txt, unicode if sys.version < '3' else str):
utxt = txt
else:
raise Exception("String value expected")
return unicodedata.normalize('NFKD', utxt)
@classmethod
def detect_language(cls, code):
first = code.split(' ')[0]
languages = cls.list_languages()
for lang in languages:
mnemo = cls(lang)
if first in mnemo.wordlist:
return lang
raise Exception("Language not detected")
def generate(self, strength = 128):
if strength % 32 > 0:
raise Exception('Strength should be divisible by 32, but it is not (%d).' % strength)
return self.to_mnemonic(os.urandom(strength // 8))
def to_mnemonic(self, data):
if len(data) % 4 > 0:
raise Exception('Data length in bits should be divisible by 32, but it is not (%d bytes = %d bits).' % (len(data), len(data) * 8))
h = hashlib.sha256(data).hexdigest()
b = bin(int(binascii.hexlify(data), 16))[2:].zfill(len(data) * 8) + \
bin(int(h, 16))[2:].zfill(256)[:len(data) * 8 // 32]
result = []
for i in range(len(b) // 11):
idx = int(b[i * 11:(i + 1) * 11], 2)
result.append(self.wordlist[idx])
if self.detect_language(' '.join(result)) == 'japanese': # Japanese must be joined by ideographic space.
result_phrase = '\xe3\x80\x80'.join(result)
else:
result_phrase = ' '.join(result)
return result_phrase
def check(self, mnemonic):
if self.detect_language(mnemonic.replace('\xe3\x80\x80', ' ')) == 'japanese':
mnemonic = mnemonic.replace('\xe3\x80\x80', ' ') # Japanese will likely input with ideographic space.
mnemonic = mnemonic.split(' ')
if len(mnemonic) % 3 > 0:
return False
try:
idx = map(lambda x: bin(self.wordlist.index(x))[2:].zfill(11), mnemonic)
b = ''.join(idx)
except:
return False
l = len(b)
d = b[:l // 33 * 32]
h = b[-l // 33:]
nd = binascii.unhexlify(hex(int(d, 2))[2:].rstrip('L').zfill(l // 33 * 8))
nh = bin(int(hashlib.sha256(nd).hexdigest(), 16))[2:].zfill(256)[:l // 33]
return h == nh
@classmethod
def to_seed(cls, mnemonic, passphrase = ''):
mnemonic = cls.normalize_string(mnemonic)
passphrase = cls.normalize_string(passphrase)
return PBKDF2(mnemonic, u'mnemonic' + passphrase, iterations=PBKDF2_ROUNDS, macmodule=hmac, digestmodule=hashlib.sha512).read(64)
|