Bitcoin Forum
May 03, 2024, 07:15:03 AM *
News: Latest Bitcoin Core release: 27.0 [Torrent]
 
   Home   Help Search Login Register More  
Pages: [1]
  Print  
Author Topic: PY21 - A simple BIP39 mnemonic generator in PYTHON  (Read 231 times)
apogio (OP)
Sr. Member
****
Offline Offline

Activity: 434
Merit: 960



View Profile WWW
March 03, 2024, 02:26:19 PM
Last edit: March 04, 2024, 05:01:20 PM by apogio
Merited by NotATether (5), ranochigo (4), BlackHatCoiner (4), RickDeckard (4), ABCbits (3), vapourminer (2), Forsyth Jones (2), DdmrDdmr (1)
 #1

As the title suggests, I developed an easy to use script that generates a BIP39 mnemonic.

I implemented it for fun. I don't plan to use it for real money.

The script:
Code:
# contact: apogio@proton.me
from secrets import token_hex
from hashlib import sha256

# read bip39 wordlist from file and import in a list
bip39_wordlist_file = open("bip39_wordlist.txt", "r")
bip39_wordlist = bip39_wordlist_file.read().split('\n')

# entropy
entropy = bin(int(token_hex(16), 16))[2:]
while len(entropy) != 128:
    entropy = bin(int(token_hex(16), 16))[2:]
print('---------')
print('ENTROPY: ')
print('---------')
print(entropy)

# calculate SHA256
sha256_hex = sha256(bytes.fromhex(hex(int(entropy,2))[2:])).hexdigest()
sha256_bin = bin(int(sha256_hex, 16))[2:]

# calculate checksum
checksum = sha256_bin[0:4]

# final seed to be converted into BIP39 mnemonic
final = entropy + checksum

num_of_words = 12
word_length = len(final) // num_of_words

# calculate mnemonic
res = []
for idx in range(0, len(final), word_length):
    res.append(final[idx : idx + word_length])

mnemonic = []
for idx in range(0, num_of_words):
    binary_place = res[idx]
    decimal_place = int(binary_place,2)
    mnemonic.append(bip39_wordlist[decimal_place])

print('\n-------------')   
print('BIP39 PHRASE: ')
print('-------------')
for w in range(0, len(mnemonic)):
    print(str(w+1) + ': ' + mnemonic[w])

How to run:
1. Create a file on your machine (example mnemonic_gen.py).
2. Copy - paste the code from above.
3. Create a file on your machine, called bip39_wordlist.txt and copy-paste the wordlist into the file.
4. Make sure to have both files in the same directory.
5. Just run python mnemonic_gen.py

Sample output:
Code:
---------
ENTROPY:
---------
11101110101000001011111101111000111100001001001100010000100001110011110100010011010100011000011001100100011111100100010111011100

-------------
BIP39 PHRASE:
-------------
1: upgrade
2: album
3: taste
4: thrive
5: country
6: drum
7: violin
8: health
9: major
10: catalog
11: multiply
12: ride

Extra notes:
1. The script uses secrets module to generate entropy. It is essentially a CSPRNG and is the recommended approach to generate pseudo-random numbers in Python. Internally, it makes use of os.urandom as well.
2. For best security, use it offline, by just running the script on an airgapped device.
3. This is not a complete wallet. You must import the seed phrase on an offline wallet that you like, in order to convert the BIP39 phrase into a seed and produce the corresponding xpriv and xpub.
4. This method is only recommended if you don't trust the entropy source of your device and you want to use CSPRNG on an airgapped computer though python libraries.
5. It is similar to Ian Coleman's BIP39 implementation, in a sense that they both must be executed offline. The difference lies in the libraries that are used, as Ian's implementation uses javascript, whereas the script above uses python libraries.

1714720503
Hero Member
*
Offline Offline

Posts: 1714720503

View Profile Personal Message (Offline)

Ignore
1714720503
Reply with quote  #2

1714720503
Report to moderator
Bitcoin mining is now a specialized and very risky industry, just like gold mining. Amateur miners are unlikely to make much money, and may even lose money. Bitcoin is much more than just mining, though!
Advertised sites are not endorsed by the Bitcoin Forum. They may be unsafe, untrustworthy, or illegal in your jurisdiction.
1714720503
Hero Member
*
Offline Offline

Posts: 1714720503

View Profile Personal Message (Offline)

Ignore
1714720503
Reply with quote  #2

1714720503
Report to moderator
ranochigo
Legendary
*
Offline Offline

Activity: 2954
Merit: 4165


View Profile
March 04, 2024, 02:19:34 AM
Last edit: March 07, 2024, 02:19:32 AM by ranochigo
Merited by vapourminer (1), ABCbits (1), apogio (1)
 #2

It would be a small improvement to remove zfill, since it doesn't do much other than padding with zeros for which it isn't needed when there is a SHA256 hashing after. That probably should be removed eitherways, if the entropy is lower than required, then they should be able to run the entropy generation again. In a similar vein, a sanity check on the entropy before the SHA256 hashing would improve the security as well.

Edit: I stand corrected on the point about regeneration of entropy; should not be strictly enforced at 128.

.
.HUGE.
▄██████████▄▄
▄█████████████████▄
▄█████████████████████▄
▄███████████████████████▄
▄█████████████████████████▄
███████▌██▌▐██▐██▐████▄███
████▐██▐████▌██▌██▌██▌██
█████▀███▀███▀▐██▐██▐█████

▀█████████████████████████▀

▀███████████████████████▀

▀█████████████████████▀

▀█████████████████▀

▀██████████▀▀
█▀▀▀▀











█▄▄▄▄
▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
.
CASINSPORTSBOOK
▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
▀▀▀▀█











▄▄▄▄█
apogio (OP)
Sr. Member
****
Offline Offline

Activity: 434
Merit: 960



View Profile WWW
March 04, 2024, 05:02:23 PM
 #3

It would be a small improvement to remove zfill, since it doesn't do much other than padding with zeros for which it isn't needed when there is a SHA256 hashing after. That probably should be removed eitherways, if the entropy is lower than required, then they should be able to run the entropy generation again. In a similar vein, a sanity check on the entropy before the SHA256 hashing would improve the security as well.

Hello, I just updated the OP with this:

Code:
entropy = bin(int(token_hex(16), 16))[2:]
while len(entropy) != 128:
    entropy = bin(int(token_hex(16), 16))[2:]

so now, if the entropy is less than 128, it repeats the process until it is 128 bits long.

Thanks for the feedback.

PowerGlove
Hero Member
*****
hacker
Offline Offline

Activity: 510
Merit: 3991



View Profile
March 06, 2024, 12:23:19 PM
Merited by apogio (7), ABCbits (6), ranochigo (4), RickDeckard (4), EFS (2), vapourminer (1), DdmrDdmr (1)
 #4

Hello, I just updated the OP with this:

Code:
entropy = bin(int(token_hex(16), 16))[2:]
while len(entropy) != 128:
    entropy = bin(int(token_hex(16), 16))[2:]

so now, if the entropy is less than 128, it repeats the process until it is 128 bits long.
You've introduced a small bias by doing that...

This is a pattern that I see pretty often: an attempt to make things "more secure" that ends up backfiring.

Think of it like this: by predicating that loop on the entropy being exactly 128 bits long, you've removed the possibility of your program ever generating a 128-bit value with the high-order bit set to 0. That means, in hexadecimal terms, your entropy will always start with an 8, 9, A, B, C, D, E, or F (0 through 7 are no longer possible). In BIP39 terms, the first word of your mnemonic will always start with one of the letters L through Z (no matter how many times you run your script, you'll never generate a mnemonic that starts with an A-word, for example).

I think that what you were doing before with the zfill was better. (But, if you change it back, you should be aware that that will reveal a different bug that's hiding in your code: bytes.fromhex always expects a string with an even number of nibbles. That is, bytes.fromhex('abcd') will return b'\xab\xcd', but bytes.fromhex('abc') won't return b'\x0a\xbc', as you might expect, instead, it will raise a ValueError. So, there's a ~6% chance that your script will fail on any given invocation. Try it yourself, run the original version of your script again and again in a terminal and you'll encounter that problem eventually and get a traceback that'll point to the offending line.)

Please don't feel discouraged. It's cool that you're learning Bitcoin by programming little bits and pieces of it yourself, and then sharing your code: that's a really effective way to learn in my experience, so keep it up, dude. Wink

I don't know about you, but I really appreciate seeing other people's takes on problems that I've recently solved myself, especially if they involve techniques that I may not be familiar with. So, here's my take on a 12-word BIP39 mnemonic generator in Python:

Code:
#!/usr/bin/env python3

# Don't use this for anything serious; it *probably* works as intended, but I haven't tested it much (I wrote it quickly for apogio).

import base64

import lzma

import secrets

import hashlib

bip39_words: list[str] = lzma.decompress(base64.b85decode('{Wp48S^xk9=GL@E0stWa761SMbT8$j;4?ZDBwYY7n21WR2~SMNj7l(<JR*YqdhorTGI^h5s6rBe;k9*`P;vADxW!uq6ud9G`Ryq|uIakUrKX0S{>^H;DHJAH%e&gKef~1EXSD0Us}VywB$hQSCL24wlCsKYCV<&UARZHOh)&>5DZ-~ie;w#}??6<JOl|mq9eKdcew;FDEcSIuPSsz9ifo`zRLj%vg=z0~cu*vAMA6cb5GSaXO7n>$%xTl&8@gxbgANqLq@*=qXFu|h=a2+%Xss$^84V29ubWU|Jis}ZeS^SfW}jww7nw9K&Cx%HKy;0Y37#)`+p3zY1bZ`7k+YIMR3u^uY^9ccN{kp=S?u6N4zisDC8gkIJo$^^m6wM^a$8H3bAYEsAIc966#xe{9j&hY0zxEiT${N!QqS^qX$el?jg>&rnPf$K-yrI;)DRZN%5N67_SO#n=yj@!>$4Hz)evAV5=sYEQS$z7tp@GUj@>>S;S=L=!OK{Jtif0|-p%w0w#bxt71|pkbpS}&H*pv#^=Et7p$ITGaVb29^yoJQ!zsLIcy9`T(hCW;CliZjzHlUHk5C~^7<s${pyjkGSHqDb$3%6Ql)O^B*BWTyC_6sP9t*>_nJ1EUY6*S-;1uIhueo7A2kS$(TLIuv1k&86r%i)z0*-`orY@!pD9im@rths?CuQ90QmSY+!iP@8%nQ%d2Mnv#BVLo#w-6V9c5aG@e2d9O-C!?y>GSCUs5I^54+iV}u#dj~6%nU^oJ!C{P_)~vNh$*wcdG_&J;l!^sRRdSF->(8X?IogT__SWD7&M;TnqE9)DN*23c>sJsQxXi3;5J6DDh(_H0P%C?nxktxwmY48AW19?`wuF+F(0QfgOO}Ojr7j3KfbBaY+=fiqt?}oNK#yaFde;{ARhlk@2*Yh;H++vcQgmvVuv4wKzHFO&Ru(cfJW=QuOvQu>M0i6U6^04<N3c${Mzk9WOh3^h|Bx=-7U{ac`Tojgk$wjm&2*;dbo8XCFVx7vftB*T#;^eJ6Rv>JXbcxiR-dL7D_vpPfb6ZAR`2uHBG)agXV6VC2h%>;B+vg*v^Xsw3(RIEe8ls@DQbyP-deH3-Hb)O_0nXteO2F_5u@cpMV^*D4qiuN<at4zRaHb_%+w4o!3z1x|G(S0Q4-8w6cmtHv6}zRL00x0v3*+4}~6x$RUNbD{rKGJe~n>SK-`U!)6zJN$A6{Hhkj+D@VTMcYtaNfsu>Wn{j~^6xu22NN$^PmEmEl*lr<Iq~?ayQ?Mq+_SAf1u3uKb)!FG*pd}Y`;RydQ_jhBV3IJyYbiO@nL6xCOD-B1(}2VUEWj<scNhas{F%K!5FcMIvQCKN3TK-vuB#u@5a)O*CA;1@r33JxK6ZcOff;l0Rb)DDIUIdWU2Lrg_nT~!OmX{$$FBD%4V7TQ-RCP1rhG&giyB}eTE*eNPEK0{F<j^TrG8NOn8ylmgdaL)Ull(-8%}RdS!ufXK%-KAb8qY!&DKNMg|}L7tSo2p<DN>~k&2zu1nq>@!y1*ypM67Y`iGod_AKq<bEWq&l@ZliOe+DODTaDj3TR|GzSq{ygZN5;^38}PCZxI0%CH=5UB@k%2Mgh6zQ_-s+Kb_kVRfhG^$Y0oOW2zmlc2Ay=$M;wjEA_a!!f;^Si^ZbTV#OV)if)Fr5(zM@iXYVO_6G(ooKRa&RFfYbR~k(4Pj|mmG17{dcNunx0U|P=4}B;hEo8nj=OBMER5&p=;yBKPt~g@_oib!r>_r-*jlOyBOA{KNrg}5Ac0RMtO%k2jizS0XF!`YtPkPQ`ImjU%rS#KF}#sq+r|(L72zb1TC2;dOS~ikeihcHtjY!p{N`Z*Q+Cx&oG&bsXVozbAQj&R&`HmZr4)YnH?nY^m}If@mU2OBIw{C4^z1meuyEu;2#(@;2Osppn@|UV6U2xJvDC)cQ<bcDUd=$-N=fQT+iEI(H+Q?DeL|cvnMVG}N$??c0$9l~YqMpU!4~#FrCo0^q9I#GD;e^^M&&}sesOl$cqh^&2$>`(jFILY>=0`+;VLBTS|!3}0QE&E(EzgOrO8NIJOzT>^p}ISTqQSGa0npbKH^kf2A+%8xf8FWGBc;!h)Vd2IuWu!v6lNZ{yVZpjQ$0$y<=)J7sK?*>Vv>n?iROPvbL3X*R&VFHz8cX8#e-z!_RyxYIU<(6gQ_4b(OE?b)!x=RJf;~4bi0p=4VlSVImRd!l7a7cE+)`2rKn4pAw_LqwZx&RWLHTaB*I{-F6zLlIF;yxKK4;IvNZ6t|?^jhonkM3>Vwmf(Vk0@EB2?UeVdB;Yb|Kd;W%R3JWCWlnF(sPu^{(m8)`sb8503#A0k-zZPD`M9T*&C+y`J9JD_0->(14<!m6W^O9yW6f0a!h*#%-1<bd5h^ziFcpp3$isdCU5BaIyH%tA&lv4a|IxD^z(ppfd%c?gp?Gm_dVP{9B9W#N5rKm47h>W>=Y!~_Y8&AfDjoh)LpK6g5a&}4OZ^ryKhc3JF+hme1Be32X32Sb{U6qNKLDpmXx~u;TgGnn6m@#M;b$CnfIq7~tRFqGO>Nx0>tP_m`QW>a`i!TRAZ%h<ymlLNjB`3I{j`y3}_zXntLR;@~&3)~<SBdxVe0Vg1MkQ0{lkTd<V%5*B=I_`q1$F~Qo;N&nXC7gDm!Z10oRnT>#-uM7BMo<Ke_<Kq)X1`x)B&Tdx7}z^L;s$YvfhGzZb!JuDEVsZ{LZI)c>C{dn$iVRKwHz-B)^APT2#W+FMad$_X#-BM+bg%Rf2oYfSf2eeAFo8OyWD>GfI>IduIKFJlGhOb<V;Yf~s`G!GBuy(f*5CW8~~c@!93tes7Fv+xF^laoI;vh^GK8{~-#$l}0k8xt46LDlynQhn%mpy?WuiuL+BT|KVa4iu(b)bcYP332V(60%B)Cf6xDm6x1cy=}#}NP4<RI&@N3E1HwLQ8&=yzW5DJ>tF)718Oq4s#zK*RIUGa7k|k$4HOG>9RiJM;@2yK=NBbEC6*Kd&2VV<iJZVy`q}J4-fIAR*IEm-W-NnbBMc#b?aqo*UzFd_rO0R?FJ!b=1U)c-%YZf(a552_x+NG~95C6Wn-z7rXYz#GCH#5xo1SKQEpVCZu?gKLp>qPUxacYwN@0mswW469o32{DJf=y*CU9V7)?7loOwYQ9_8JLIotvhv~&Wz%i0Z!05V1|QY@4L4jp;%<jo0f%IrpPHnzrf*|6Y7jJ9)F6*Of5>JOM*dix({v*!!8Z=C&nDTk&RqqACXz9fO2kwe61+}5DG1WkN7Hp0~$f1@=n~e=))pi@!)Tac?masVzC?^p0gZWN%vmU+jb}&3M!$|S+EHr+mGIU0i;)%3RPlu5L-Y@1H-swrrU`hjMq%4nK}+=J_f-N8;rWk>@YoZbR~hDP?=07HhnK7Yha4mRW0EOgl>Q5h&N?zt#<Q0@0*u}Wxt8X07JQtoelXwG`h;N8T#%3L&tG0GNEUaabE7}x+<Lgx`Z&u4!lj*=snoQ$;S(g#IGnq-t_g+aeXg(;3P=6(JW$!nYc^o*ime*%~>)JjrC*Ls77Nt7Cp(!Js+pn%*(NBy}Z)CsN&bjh^gC{rL~LauR(COgtSd>bE4IHnvS$>ovdGkG|VU^PL1*cw*dz6l9aH($Kat5LTU9BKbaVBD}e6ZT~A9kcNB{KYbj~K(7<a;8LX;x(jRm@9cLv$#Ubz)U+r%6B=(k#J4~F{f2Dp|S=!P9+VIP!E0)0ACz+VZ1ocmMTx{#U%TEl$b)$F#cXLOkdjKf96euOn^vW>{m$+D85|@#74?ms4(<invY(H$jEQ@mBDlK!7g0v_6PbZsmKeG&CZ_=OxUAWu5noVNzVGPSdTV|)SfD>7n$~>n>pTX`Y%sMm%<C%kmjvbs2e9LX<Xwf`1468A!!DT?({~n(tbR2T+BP&;2T-gLIDE6WyR!4c^kVJkqiP=y%%%!oFop}TE2PE`;yCH?lBC;}cOBDBEgoY8|c0PvpKy4m$AY+7VB#&H!!?B!AN@NJ|@m6sk4c&+a)?7~B)okfH0O<%izMeJF%U32P2``~k=g*GgtsAigqU>q37$sm}KmutW;E;=^VLusQ?lO6n`0TFjQAc`g)`IHG4G<%c8sRkb*u>;O8etvo0@=>SAhL77BuaNdi|LI0L>I=<SB0FAKD`FJgI`1dUT(!pnk|Fqh#3|&RGc&2Zn4PvdB#)D8VV=k>?nF^ZO%Qae<~ZG5pz#9ZAFRpFG%-Y8{Rmd*f!oYYX*iSsjQjl`TqEOaP?mMm@Y?2^|vWS<d{Wgc+ei9L|6u@Mq+Ca2Ye|jk<aZp!HU7svd6bVPRW5f2-cA#!#+?e5;zB^Yv-jciE^4X@vk&3PUvMV`XONl0g075#_BW}IKzoR+MK{H*tr?MSo$t78He^ujn2A-{c_4kuR}oR=DWs1M&H%)+tT7#;p6YG>Qqhp#Ro~xw3f(fI6x=i)ocaQIs|Dvg?AX3|G_enP}P7N_?{nab4X|xO;Bvcny;Mgm&n;}#^N?ht)QPa=pK=OJT&Nc@`)s2y%=xX{IUe}??&l&?LPPJ^bTBK*!;mW_~-<`zW8)>h0fy7dX3AT&!LmnKb*<tO`uM31<E}QIi4(}F}*{;`DAc_54LkLj<F~)gS_T9>*OFB-IsN$aD^LPcO{na32FZCS;P!p)sbnR{<1H8&Io*XH>^%{X5H?vtC_<5Yl)4H{;T?eR=xo4zMgGxrMG9p@1UtXLg|+1$C`Eo$g|L<Yt^E6lO6<?C7)VU#sV|G#;?62fC+<Stf5u}&+HgfUyeK(+kt_|hB~wI!OX|rIDi8kN5ZTptl?S!(d)!^kVogoZ;Jy6A0lGcjW*8E7^RvXr9`%t#!wlS_{uYo3bIrq*+sg!XgdRxFP?|RU?>x#e~g(<!|jcAl*qWW5zFc-Cp!f#!4`-P!JKI$%ARE-?%J#N76u(2k~%CFlA;w~_Ya!UPM-^Y-*@X9d&bT=b@Bwf{NF;xPk+}}5b@n^AL<#=X$kmPGY}~)i!?Id#94?7+&Q}rbtqs<e$ZmK#mehUtZ4_kmP^*D*5g48xD`p-tL`#h;Ejz$C~bzq0RK6<dTPL14$y`OE%LvEP-YHm0IeHmaf&pqN@c~lk&p>V3q>l<cvC>QZ}FvGh-tAjMoOTg=^RKYDoUIaU;*WhzRh@}-9nE11k#_w`r+2d7A@2gw`AU7O5Nt!32Lo0+$xOMdW67%8<KSxW5_hKx+mzyiB~Xy_TH`h4@;WRx{mgu5ISw;cds3N5;RceKJ2smo_&0j=uTKzWkG%Pu*a%TjraU^)b@IqT^Cear{D9!^dpbhC#I+yMXT4-ZH3gx6?#;j-QayOMtWK2>`w@1^w*jNQ!&uSKfge%M28%OO{Ws}_mw5k2)UX#8EE`$J&ery(PFRTPRe+8ME$%+0(8%omCTo5iz3vJ+obP2NWfe{?nUg>q@;gbs0zwWscj2jCr*C32c$lxQ+=4Exp3VPq2xzPUNKpQcy~h@N$a;FQxng%f-{r<UClnTh9ydm{A5_q{y0Eu!Gm=NpL<$<kbhpOSka0Fptz<~z7f9}|C(xH4#M#c*jMr(w<S_)isH-o4PWu3RNb$D*#lbp1>N>P_yc?!P-}$P_?H9E>1g+ea9y4H{m9vJ169o>9(N|lI$0M}Uv<HhktAiB(G5f;!YMp;EL|)g@Xd!c`ZQ!%qXO<i+qr%DB8lNVf50y0>$@25BS_d%2FJ&gv3Sf0%>(OD;tg#mF(VF6GK7(DK*r9%wu?|AusPFcGjp-H&mfb5eGek@LU1s(Jk*`7!ur*UU_J@K4Ie>Su?<+apZL;q7d8Z?MJJ{Ew(x%93*MI1EI#&X%iGGdJ9!(|UhI+i*`+>jZn?h;K-w}>M~lFsBSVmjSI*Py=BlvSgU<FZ^kK7TE7492B40FV>|RO*TT1Fa$2+@ie+S506rPh`84?h7$4MDe6a2Od4|YI$7k1`A|K_FixJ(d#P~6R>uB8!?^*`G~OIz3q_gyhleNle+a{7n>fPT}9i)}$yGwM~j_|YsLYVAJhKBb?=7uZ*07(_qNd_N5&8E8#c2Wq82=IEJY5s<tvxtANYUK>;WXJU|dRpVRJ1*7P<K2BqFho5sZb9;f#Fa}mkJyugV{pzo~(v;S*x4Ud@#36SMN4NU0e%Wuby<lQ~v!enb5G3wB+uzj^T5?dm0Q|lG{%y1t)C^@)jw**HtXvk`DHWfkux=43HqlH`UDhI<W9HoH!z&b20*n8<{YKY8YUCu+cW-jj;p$f4l7ZV)KPx8?pzgX4*5~JA^Vxi9xiVdLAJh}?MjU;xUVu1C-b>~Kwj{V<?j3e4K`8fl0+$0=Vv{vSC!7tpAn!|enyUN7FO2A&_W)R%XD@N=#HDZ}&4S(V7`PjSE#ElpYR7qEvP<b!_Zz<$)8b$6bIENcS`0ATm^j}wB_RSVl;Gr-gH^sJp+4a62hKv_@J#f;QF0~_g9TU2Gr(Mhcq{jaI`x7LV*`QpT-T8>%l9Wc;(#+4XM)!#giyS6FL_7O`DvOWUo6zO(FggIxM-;oSqeP>I^_M`6)E1WsCDM2O~XMf-t1*O+P)yvRgmiUb)kFO@;xD+2G{@qPq#);P$7Wt00F=#yJi3YrO+j%vBYQl0ssI200dcD')).decode('ascii').split('\n')

entropy: int = secrets.randbits(128)

checksum: int = hashlib.sha256(entropy.to_bytes(16, 'big')).digest()[0] >> 4

combined: int = (entropy << 4) | checksum

mnemonic: list[str] = [bip39_words[(combined >> (index * 11)) & 2047] for index in reversed(range(12))]

print(f'Entropy (128 bits, hexadecimal): {hex(entropy)[2:].zfill(32)}')

print(f'Checksum (4 bits, binary): {bin(checksum)[2:].zfill(4)}')

print(f'Mnemonic (12 words, english): {" ".join(mnemonic)}')

And here's the same script but generalized to produce either 12, 15, 18, 21, or 24-word mnemonics:

Code:
#!/usr/bin/env python3

# Don't use this for anything serious; it *probably* works as intended, but I haven't tested it much (I wrote it quickly for apogio).

import base64

import lzma

import secrets

import hashlib

bip39_words: list[str] = lzma.decompress(base64.b85decode('{Wp48S^xk9=GL@E0stWa761SMbT8$j;4?ZDBwYY7n21WR2~SMNj7l(<JR*YqdhorTGI^h5s6rBe;k9*`P;vADxW!uq6ud9G`Ryq|uIakUrKX0S{>^H;DHJAH%e&gKef~1EXSD0Us}VywB$hQSCL24wlCsKYCV<&UARZHOh)&>5DZ-~ie;w#}??6<JOl|mq9eKdcew;FDEcSIuPSsz9ifo`zRLj%vg=z0~cu*vAMA6cb5GSaXO7n>$%xTl&8@gxbgANqLq@*=qXFu|h=a2+%Xss$^84V29ubWU|Jis}ZeS^SfW}jww7nw9K&Cx%HKy;0Y37#)`+p3zY1bZ`7k+YIMR3u^uY^9ccN{kp=S?u6N4zisDC8gkIJo$^^m6wM^a$8H3bAYEsAIc966#xe{9j&hY0zxEiT${N!QqS^qX$el?jg>&rnPf$K-yrI;)DRZN%5N67_SO#n=yj@!>$4Hz)evAV5=sYEQS$z7tp@GUj@>>S;S=L=!OK{Jtif0|-p%w0w#bxt71|pkbpS}&H*pv#^=Et7p$ITGaVb29^yoJQ!zsLIcy9`T(hCW;CliZjzHlUHk5C~^7<s${pyjkGSHqDb$3%6Ql)O^B*BWTyC_6sP9t*>_nJ1EUY6*S-;1uIhueo7A2kS$(TLIuv1k&86r%i)z0*-`orY@!pD9im@rths?CuQ90QmSY+!iP@8%nQ%d2Mnv#BVLo#w-6V9c5aG@e2d9O-C!?y>GSCUs5I^54+iV}u#dj~6%nU^oJ!C{P_)~vNh$*wcdG_&J;l!^sRRdSF->(8X?IogT__SWD7&M;TnqE9)DN*23c>sJsQxXi3;5J6DDh(_H0P%C?nxktxwmY48AW19?`wuF+F(0QfgOO}Ojr7j3KfbBaY+=fiqt?}oNK#yaFde;{ARhlk@2*Yh;H++vcQgmvVuv4wKzHFO&Ru(cfJW=QuOvQu>M0i6U6^04<N3c${Mzk9WOh3^h|Bx=-7U{ac`Tojgk$wjm&2*;dbo8XCFVx7vftB*T#;^eJ6Rv>JXbcxiR-dL7D_vpPfb6ZAR`2uHBG)agXV6VC2h%>;B+vg*v^Xsw3(RIEe8ls@DQbyP-deH3-Hb)O_0nXteO2F_5u@cpMV^*D4qiuN<at4zRaHb_%+w4o!3z1x|G(S0Q4-8w6cmtHv6}zRL00x0v3*+4}~6x$RUNbD{rKGJe~n>SK-`U!)6zJN$A6{Hhkj+D@VTMcYtaNfsu>Wn{j~^6xu22NN$^PmEmEl*lr<Iq~?ayQ?Mq+_SAf1u3uKb)!FG*pd}Y`;RydQ_jhBV3IJyYbiO@nL6xCOD-B1(}2VUEWj<scNhas{F%K!5FcMIvQCKN3TK-vuB#u@5a)O*CA;1@r33JxK6ZcOff;l0Rb)DDIUIdWU2Lrg_nT~!OmX{$$FBD%4V7TQ-RCP1rhG&giyB}eTE*eNPEK0{F<j^TrG8NOn8ylmgdaL)Ull(-8%}RdS!ufXK%-KAb8qY!&DKNMg|}L7tSo2p<DN>~k&2zu1nq>@!y1*ypM67Y`iGod_AKq<bEWq&l@ZliOe+DODTaDj3TR|GzSq{ygZN5;^38}PCZxI0%CH=5UB@k%2Mgh6zQ_-s+Kb_kVRfhG^$Y0oOW2zmlc2Ay=$M;wjEA_a!!f;^Si^ZbTV#OV)if)Fr5(zM@iXYVO_6G(ooKRa&RFfYbR~k(4Pj|mmG17{dcNunx0U|P=4}B;hEo8nj=OBMER5&p=;yBKPt~g@_oib!r>_r-*jlOyBOA{KNrg}5Ac0RMtO%k2jizS0XF!`YtPkPQ`ImjU%rS#KF}#sq+r|(L72zb1TC2;dOS~ikeihcHtjY!p{N`Z*Q+Cx&oG&bsXVozbAQj&R&`HmZr4)YnH?nY^m}If@mU2OBIw{C4^z1meuyEu;2#(@;2Osppn@|UV6U2xJvDC)cQ<bcDUd=$-N=fQT+iEI(H+Q?DeL|cvnMVG}N$??c0$9l~YqMpU!4~#FrCo0^q9I#GD;e^^M&&}sesOl$cqh^&2$>`(jFILY>=0`+;VLBTS|!3}0QE&E(EzgOrO8NIJOzT>^p}ISTqQSGa0npbKH^kf2A+%8xf8FWGBc;!h)Vd2IuWu!v6lNZ{yVZpjQ$0$y<=)J7sK?*>Vv>n?iROPvbL3X*R&VFHz8cX8#e-z!_RyxYIU<(6gQ_4b(OE?b)!x=RJf;~4bi0p=4VlSVImRd!l7a7cE+)`2rKn4pAw_LqwZx&RWLHTaB*I{-F6zLlIF;yxKK4;IvNZ6t|?^jhonkM3>Vwmf(Vk0@EB2?UeVdB;Yb|Kd;W%R3JWCWlnF(sPu^{(m8)`sb8503#A0k-zZPD`M9T*&C+y`J9JD_0->(14<!m6W^O9yW6f0a!h*#%-1<bd5h^ziFcpp3$isdCU5BaIyH%tA&lv4a|IxD^z(ppfd%c?gp?Gm_dVP{9B9W#N5rKm47h>W>=Y!~_Y8&AfDjoh)LpK6g5a&}4OZ^ryKhc3JF+hme1Be32X32Sb{U6qNKLDpmXx~u;TgGnn6m@#M;b$CnfIq7~tRFqGO>Nx0>tP_m`QW>a`i!TRAZ%h<ymlLNjB`3I{j`y3}_zXntLR;@~&3)~<SBdxVe0Vg1MkQ0{lkTd<V%5*B=I_`q1$F~Qo;N&nXC7gDm!Z10oRnT>#-uM7BMo<Ke_<Kq)X1`x)B&Tdx7}z^L;s$YvfhGzZb!JuDEVsZ{LZI)c>C{dn$iVRKwHz-B)^APT2#W+FMad$_X#-BM+bg%Rf2oYfSf2eeAFo8OyWD>GfI>IduIKFJlGhOb<V;Yf~s`G!GBuy(f*5CW8~~c@!93tes7Fv+xF^laoI;vh^GK8{~-#$l}0k8xt46LDlynQhn%mpy?WuiuL+BT|KVa4iu(b)bcYP332V(60%B)Cf6xDm6x1cy=}#}NP4<RI&@N3E1HwLQ8&=yzW5DJ>tF)718Oq4s#zK*RIUGa7k|k$4HOG>9RiJM;@2yK=NBbEC6*Kd&2VV<iJZVy`q}J4-fIAR*IEm-W-NnbBMc#b?aqo*UzFd_rO0R?FJ!b=1U)c-%YZf(a552_x+NG~95C6Wn-z7rXYz#GCH#5xo1SKQEpVCZu?gKLp>qPUxacYwN@0mswW469o32{DJf=y*CU9V7)?7loOwYQ9_8JLIotvhv~&Wz%i0Z!05V1|QY@4L4jp;%<jo0f%IrpPHnzrf*|6Y7jJ9)F6*Of5>JOM*dix({v*!!8Z=C&nDTk&RqqACXz9fO2kwe61+}5DG1WkN7Hp0~$f1@=n~e=))pi@!)Tac?masVzC?^p0gZWN%vmU+jb}&3M!$|S+EHr+mGIU0i;)%3RPlu5L-Y@1H-swrrU`hjMq%4nK}+=J_f-N8;rWk>@YoZbR~hDP?=07HhnK7Yha4mRW0EOgl>Q5h&N?zt#<Q0@0*u}Wxt8X07JQtoelXwG`h;N8T#%3L&tG0GNEUaabE7}x+<Lgx`Z&u4!lj*=snoQ$;S(g#IGnq-t_g+aeXg(;3P=6(JW$!nYc^o*ime*%~>)JjrC*Ls77Nt7Cp(!Js+pn%*(NBy}Z)CsN&bjh^gC{rL~LauR(COgtSd>bE4IHnvS$>ovdGkG|VU^PL1*cw*dz6l9aH($Kat5LTU9BKbaVBD}e6ZT~A9kcNB{KYbj~K(7<a;8LX;x(jRm@9cLv$#Ubz)U+r%6B=(k#J4~F{f2Dp|S=!P9+VIP!E0)0ACz+VZ1ocmMTx{#U%TEl$b)$F#cXLOkdjKf96euOn^vW>{m$+D85|@#74?ms4(<invY(H$jEQ@mBDlK!7g0v_6PbZsmKeG&CZ_=OxUAWu5noVNzVGPSdTV|)SfD>7n$~>n>pTX`Y%sMm%<C%kmjvbs2e9LX<Xwf`1468A!!DT?({~n(tbR2T+BP&;2T-gLIDE6WyR!4c^kVJkqiP=y%%%!oFop}TE2PE`;yCH?lBC;}cOBDBEgoY8|c0PvpKy4m$AY+7VB#&H!!?B!AN@NJ|@m6sk4c&+a)?7~B)okfH0O<%izMeJF%U32P2``~k=g*GgtsAigqU>q37$sm}KmutW;E;=^VLusQ?lO6n`0TFjQAc`g)`IHG4G<%c8sRkb*u>;O8etvo0@=>SAhL77BuaNdi|LI0L>I=<SB0FAKD`FJgI`1dUT(!pnk|Fqh#3|&RGc&2Zn4PvdB#)D8VV=k>?nF^ZO%Qae<~ZG5pz#9ZAFRpFG%-Y8{Rmd*f!oYYX*iSsjQjl`TqEOaP?mMm@Y?2^|vWS<d{Wgc+ei9L|6u@Mq+Ca2Ye|jk<aZp!HU7svd6bVPRW5f2-cA#!#+?e5;zB^Yv-jciE^4X@vk&3PUvMV`XONl0g075#_BW}IKzoR+MK{H*tr?MSo$t78He^ujn2A-{c_4kuR}oR=DWs1M&H%)+tT7#;p6YG>Qqhp#Ro~xw3f(fI6x=i)ocaQIs|Dvg?AX3|G_enP}P7N_?{nab4X|xO;Bvcny;Mgm&n;}#^N?ht)QPa=pK=OJT&Nc@`)s2y%=xX{IUe}??&l&?LPPJ^bTBK*!;mW_~-<`zW8)>h0fy7dX3AT&!LmnKb*<tO`uM31<E}QIi4(}F}*{;`DAc_54LkLj<F~)gS_T9>*OFB-IsN$aD^LPcO{na32FZCS;P!p)sbnR{<1H8&Io*XH>^%{X5H?vtC_<5Yl)4H{;T?eR=xo4zMgGxrMG9p@1UtXLg|+1$C`Eo$g|L<Yt^E6lO6<?C7)VU#sV|G#;?62fC+<Stf5u}&+HgfUyeK(+kt_|hB~wI!OX|rIDi8kN5ZTptl?S!(d)!^kVogoZ;Jy6A0lGcjW*8E7^RvXr9`%t#!wlS_{uYo3bIrq*+sg!XgdRxFP?|RU?>x#e~g(<!|jcAl*qWW5zFc-Cp!f#!4`-P!JKI$%ARE-?%J#N76u(2k~%CFlA;w~_Ya!UPM-^Y-*@X9d&bT=b@Bwf{NF;xPk+}}5b@n^AL<#=X$kmPGY}~)i!?Id#94?7+&Q}rbtqs<e$ZmK#mehUtZ4_kmP^*D*5g48xD`p-tL`#h;Ejz$C~bzq0RK6<dTPL14$y`OE%LvEP-YHm0IeHmaf&pqN@c~lk&p>V3q>l<cvC>QZ}FvGh-tAjMoOTg=^RKYDoUIaU;*WhzRh@}-9nE11k#_w`r+2d7A@2gw`AU7O5Nt!32Lo0+$xOMdW67%8<KSxW5_hKx+mzyiB~Xy_TH`h4@;WRx{mgu5ISw;cds3N5;RceKJ2smo_&0j=uTKzWkG%Pu*a%TjraU^)b@IqT^Cear{D9!^dpbhC#I+yMXT4-ZH3gx6?#;j-QayOMtWK2>`w@1^w*jNQ!&uSKfge%M28%OO{Ws}_mw5k2)UX#8EE`$J&ery(PFRTPRe+8ME$%+0(8%omCTo5iz3vJ+obP2NWfe{?nUg>q@;gbs0zwWscj2jCr*C32c$lxQ+=4Exp3VPq2xzPUNKpQcy~h@N$a;FQxng%f-{r<UClnTh9ydm{A5_q{y0Eu!Gm=NpL<$<kbhpOSka0Fptz<~z7f9}|C(xH4#M#c*jMr(w<S_)isH-o4PWu3RNb$D*#lbp1>N>P_yc?!P-}$P_?H9E>1g+ea9y4H{m9vJ169o>9(N|lI$0M}Uv<HhktAiB(G5f;!YMp;EL|)g@Xd!c`ZQ!%qXO<i+qr%DB8lNVf50y0>$@25BS_d%2FJ&gv3Sf0%>(OD;tg#mF(VF6GK7(DK*r9%wu?|AusPFcGjp-H&mfb5eGek@LU1s(Jk*`7!ur*UU_J@K4Ie>Su?<+apZL;q7d8Z?MJJ{Ew(x%93*MI1EI#&X%iGGdJ9!(|UhI+i*`+>jZn?h;K-w}>M~lFsBSVmjSI*Py=BlvSgU<FZ^kK7TE7492B40FV>|RO*TT1Fa$2+@ie+S506rPh`84?h7$4MDe6a2Od4|YI$7k1`A|K_FixJ(d#P~6R>uB8!?^*`G~OIz3q_gyhleNle+a{7n>fPT}9i)}$yGwM~j_|YsLYVAJhKBb?=7uZ*07(_qNd_N5&8E8#c2Wq82=IEJY5s<tvxtANYUK>;WXJU|dRpVRJ1*7P<K2BqFho5sZb9;f#Fa}mkJyugV{pzo~(v;S*x4Ud@#36SMN4NU0e%Wuby<lQ~v!enb5G3wB+uzj^T5?dm0Q|lG{%y1t)C^@)jw**HtXvk`DHWfkux=43HqlH`UDhI<W9HoH!z&b20*n8<{YKY8YUCu+cW-jj;p$f4l7ZV)KPx8?pzgX4*5~JA^Vxi9xiVdLAJh}?MjU;xUVu1C-b>~Kwj{V<?j3e4K`8fl0+$0=Vv{vSC!7tpAn!|enyUN7FO2A&_W)R%XD@N=#HDZ}&4S(V7`PjSE#ElpYR7qEvP<b!_Zz<$)8b$6bIENcS`0ATm^j}wB_RSVl;Gr-gH^sJp+4a62hKv_@J#f;QF0~_g9TU2Gr(Mhcq{jaI`x7LV*`QpT-T8>%l9Wc;(#+4XM)!#giyS6FL_7O`DvOWUo6zO(FggIxM-;oSqeP>I^_M`6)E1WsCDM2O~XMf-t1*O+P)yvRgmiUb)kFO@;xD+2G{@qPq#);P$7Wt00F=#yJi3YrO+j%vBYQl0ssI200dcD')).decode('ascii').split('\n')

mnemonic_length: int = 12

assert mnemonic_length in {12, 15, 18, 21, 24}

mnemonic_factor: int = mnemonic_length // 3

entropy: int = secrets.randbits(mnemonic_factor * 32)

checksum: int = hashlib.sha256(entropy.to_bytes(mnemonic_factor * 4, 'big')).digest()[0] >> (8 - mnemonic_factor)

combined: int = (entropy << mnemonic_factor) | checksum

mnemonic: list[str] = [bip39_words[(combined >> (index * 11)) & 2047] for index in reversed(range(mnemonic_length))]

print(f'Entropy ({mnemonic_factor * 32} bits, hexadecimal): {hex(entropy)[2:].zfill(mnemonic_factor * 8)}')

print(f'Checksum ({mnemonic_factor} bits, binary): {bin(checksum)[2:].zfill(mnemonic_factor)}')

print(f'Mnemonic ({mnemonic_length} words, english): {" ".join(mnemonic)}')
apogio (OP)
Sr. Member
****
Offline Offline

Activity: 434
Merit: 960



View Profile WWW
March 06, 2024, 12:30:32 PM
 #5

Thanks PowerGlove, I find your answer very helpful.
I will check it in detail later today and I will comment, if I find anything important.
As you said, this is the approach that I like to follow. I code in order to understand something better.

Do you think it would be better if I generated more bits of entropy and then keeping 128 of them? Instead of zfill or repeating the process if bits are less than 128.

NotATether
Legendary
*
Offline Offline

Activity: 1596
Merit: 6726


bitcoincleanup.com / bitmixlist.org


View Profile WWW
March 07, 2024, 08:09:14 AM
 #6

Do you think it would be better if I generated more bits of entropy and then keeping 128 of them? Instead of zfill or repeating the process if bits are less than 128.

If you generate more rounds of entropy then the amount that you end up with will be the average of the entropy of all the rounds. in other words: (E(Loop1) + E(Loop2) + ...) / N. Unless you select the bits from a specific place in the total amount in which case the calculation becomes slightly different but still follows through.

It doesn't make much of a difference, and if the reason you are doing this is to cover for a potential insecurity in the random number generator, it will not work well. OpenSSL's RNG is much better for that purpose.

.
.BLACKJACK ♠ FUN.
█████████
██████████████
████████████
█████████████████
████████████████▄▄
░█████████████▀░▀▀
██████████████████
░██████████████
████████████████
░██████████████
████████████
███████████████░██
██████████
CRYPTO CASINO &
SPORTS BETTING
▄▄███████▄▄
▄███████████████▄
███████████████████
█████████████████████
███████████████████████
█████████████████████████
█████████████████████████
█████████████████████████
███████████████████████
█████████████████████
███████████████████
▀███████████████▀
█████████
.
PowerGlove
Hero Member
*****
hacker
Offline Offline

Activity: 510
Merit: 3991



View Profile
March 08, 2024, 01:39:38 AM
Merited by apogio (7), RickDeckard (2)
 #7

Do you think it would be better if I generated more bits of entropy and then keeping 128 of them? Instead of zfill or repeating the process if bits are less than 128.
Yup, in the context of your script, slicing a length-128 string from a larger string of random 1s and 0s would work (that is, it would correct the bias, because the high-order bit would then have a 50/50 chance of being either 1 or 0).

But, the way that you posed that option as an alternative to zfill makes me think you don't fully understand the source of this bias. I hope you don't find the following explanation tiresome, I just know that if it were me in your shoes, I'd really appreciate someone taking pains to help me understand the potential hole in my thinking:

Let's imagine a toy version of this problem where you're trying to generate just 4 (rather than 128) bits of entropy with a coin. Let's not go down the rabbit hole of coin fairness, entropy extraction, and tossing technique. (I have a sometimes-sophomoric, or, as my wife would say, "bloody stupid" sense of humor, especially when I'm in a good mood, so "tossing technique" just gave me the giggles. Don't toss while flipping coins, yeah?) Cheesy

There are only 16 possible ways for 4 coin-tosses-in-a-row to end up, so let's put them all in a table (ignore the last 4 columns for now):

OutcomePatternBinaryDecimalf1($pcv)f2($pcv)f3($pcv)f4($pcv)
#1HHHH111115"0b1111""1111""1111""HHHH"
#2HHHT111014"0b1110""1110""1110""HHHT"
#3HHTH110113"0b1101""1101""1101""HHTH"
#4HHTT110012"0b1100""1100""1100""HHTT"
#5HTHH101111"0b1011""1011""1011""HTHH"
#6HTHT101010"0b1010""1010""1010""HTHT"
#7HTTH10019"0b1001""1001""1001""HTTH"
#8HTTT10008"0b1000""1000""1000""HTTT"
#9THHH01117"0b111""111""0111""THHH"
#10THHT01106"0b110""110""0110""THHT"
#11THTH01015"0b101""101""0101""THTH"
#12THTT01004"0b100""100""0100""THTT"
#13TTHH00113"0b11""11""0011""TTHH"
#14TTHT00102"0b10""10""0010""TTHT"
#15TTTH00011"0b1""1""0001""TTTH"
#16TTTT00000"0b0""0""0000""TTTT"

The first four columns are: 1. the possibility/outcome # (1 through 16, so, every possible outcome accounted for), 2. the heads-or-tails pattern corresponding to each outcome (H = heads, T = tails), 3. the same heads-or-tails pattern but in base-2 (1 = heads, 0 = tails), and 4. the heads-or-tails pattern converted from base-2 into base-10.

The last four columns involve the imaginary variable $pcv (previous column's value) and the following definitions:

Code:
f1 = lambda x: bin(x)
f2 = lambda x: x[2:]
f3 = lambda x: x.zfill(4)
f4 = lambda x: x.replace('0', 'T').replace('1', 'H')

Now, the first thing to think about when looking at that table is if there are any "bad" or "low entropy" outcomes in it? The answer to that is no: as long as each outcome is equally probable, then any given outcome is just as "entropic" as any other outcome. HTTH is just as good as TTTT, savvy? That means that, tempting though it is, it's a mistake to use the string length of the values in the f2($pcv) column to decide whether or not you "have enough entropy" (to be clear, it's a mistake to base that decision on anything about a single outcome). If you discard outcomes whenever len(f2($pcv)) != 4, then you're only ensuring that the pattern will always begin with a heads instead of a tails (check the table to confirm that).

The second thing to think about when looking at that table is if the final patterns in the f4($pcv) column ever disagree with the source patterns in column 2? They don't. So, you can rest assured that the zfill(128) that was present in your original script was correct (that is, it was only restoring 0s that, in some sense, were there from the start, and were "lost" during an int to str conversion). You can/should pat yourself on the back for getting it right the first time. Wink

There are at least two other bugs in your script though (beyond the bias bug, which we'll call Bug #1):

Bug #2

You need a zfill(32) before passing the hexadecimal representation of your entropy into bytes.fromhex (otherwise, you'll occasionally pass it an odd number of nibbles, which will cause it to raise a ValueError).

Bug #3

You need a zfill(256) at the end of the line that calculates your sha256_bin variable (otherwise, your checksum, and therefore the last word of your mnemonic, will be wrong 50% of the time).

For an example, try manually setting your entropy to cc399b43e82bfbc07f2fe3fd1f13ed41.

Your script will spit out the following mnemonic: slow smoke special space sausage then witness wise wonder weasel win lobster

Ian's script (and mine) will spit out this instead: slow smoke special space sausage then witness wise wonder weasel win lion

The reason your wonder weasel only managed to win a lobster instead of a lion, is because you're miscalculating the checksum bits as 1001 instead of 0001. Smiley

(I'm going to be too busy to respond for a while. I'll take a peek at this thread again in a few weeks' time.)
Pages: [1]
  Print  
 
Jump to:  

Powered by MySQL Powered by PHP Powered by SMF 1.1.19 | SMF © 2006-2009, Simple Machines Valid XHTML 1.0! Valid CSS!