|
33 | 33 | "academic acid beard romp believe impulse species holiday demand building" \ |
34 | 34 | " earth warn lunar olympic clothes piece campus alpha short endless" |
35 | 35 |
|
36 | | - |
37 | | -SD_SEED_FRAME = 'Seed Source: Input or Create your Seed Entropy here' |
| 36 | +SD_SEED_FRAME = 'Seed Source: Create your Seed Entropy here' |
| 37 | +SE_SEED_FRAME = 'Seed Extra Randomness' |
38 | 38 |
|
39 | 39 |
|
40 | 40 | def theme_color( thing, theme=None ): |
@@ -259,8 +259,9 @@ def groups_layout( |
259 | 259 | ], key='-SD-SEED-F-', **F_kwds_big ), |
260 | 260 | ], |
261 | 261 | ] + [ |
| 262 | + |
262 | 263 | [ |
263 | | - sg.Frame( 'Seed Extra Randomness (Trust but Verify ;-)', [ |
| 264 | + sg.Frame( SE_SEED_FRAME, [ |
264 | 265 | [ |
265 | 266 | sg.Radio( "None", "SE", key='-SE-NON-', visible=LO_REC, |
266 | 267 | default=True, **B_kwds ), |
@@ -641,7 +642,7 @@ def update_seed_data( event, window, values ): |
641 | 642 | window['-SD-SEED-F-'].update( f"{SD_SEED_FRAME}; {sigs_rate}, {shan_rate}" ) |
642 | 643 | disp_dur,analysis = timing( display_entropy, instrument=True )( sigs, shan, what=f"{len(seed_bytes)*8}-bit Seed Source" ) |
643 | 644 | update_seed_data.entropy = (sigs, shan, analysis) |
644 | | - log.debug( f"Entropy Analysis took {scan_dur:.3f}s + {disp_dur:.3f}s == {scan_dur+disp_dur:.3f}s: {analysis}" ) |
| 645 | + log.debug( f"Seed Data Entropy Analysis took {scan_dur:.3f}s + {disp_dur:.3f}s == {scan_dur+disp_dur:.3f}s: {analysis}" ) |
645 | 646 | elif changed: |
646 | 647 | log.info( f"Seed Data requests __TIMEOUT__ w/ current source: {update_seed_data.src!r}" ) |
647 | 648 | values['__TIMEOUT__'] = .5 |
@@ -703,63 +704,132 @@ def stretch_seed_entropy( entropy, n, bits, encoding=None ): |
703 | 704 | return entropy[:octets] |
704 | 705 |
|
705 | 706 |
|
706 | | -def update_seed_entropy( window, values ): |
| 707 | +def update_seed_entropy( event, window, values ): |
707 | 708 | """Respond to changes in the extra Seed Entropy, and recover/generate the -SE-SEED-. It is |
708 | 709 | expected to be exactly the same size as the -SD-SEED- data. Stores the last known state of the |
709 | | - -SE-... radio buttons, updating visibilties on change. |
| 710 | + -SE-... radio buttons, updating visibilities on change. |
710 | 711 |
|
| 712 | + We analyze the entropy of the *input* data (as UTF-8), not the resultant entropy. |
711 | 713 | """ |
712 | | - dat = values['-SE-DATA-'] |
713 | | - for src in [ |
| 714 | + SE_CONTROLS = [ |
714 | 715 | '-SE-NON-', |
715 | 716 | '-SE-HEX-', |
716 | 717 | '-SE-SHA-', |
717 | | - ]: |
| 718 | + ] |
| 719 | + data = values['-SE-DATA-'] |
| 720 | + try: |
| 721 | + data_bytes = data.encode( 'UTF-8' ) |
| 722 | + except Exception as exc: |
| 723 | + status = f"Invalid data {data!r}: {exc}" |
| 724 | + |
| 725 | + for src in SE_CONTROLS: |
718 | 726 | if values[src] and update_seed_entropy.src != src: |
719 | 727 | # If selected radio-button for Seed Entropy source changed, save last source's working |
720 | 728 | # data and restore what was, last time we were working on this source. |
721 | | - if update_seed_entropy.src: |
722 | | - update_seed_entropy.was[update_seed_entropy.src] = dat |
723 | | - dat = update_seed_entropy.was.get( src, '' ) |
| 729 | + data = update_seed_entropy.was.get( src, '' ) |
724 | 730 | update_seed_entropy.src = src |
725 | | - window['-SE-DATA-'].update( dat ) |
726 | | - values['-SE-DATA-'] = dat |
| 731 | + window['-SE-DATA-'].update( data ) |
| 732 | + values['-SE-DATA-'] = data |
727 | 733 | if 'HEX' in update_seed_entropy.src: |
728 | 734 | window['-SE-DATA-T-'].update( "Hex digits: " ) |
729 | 735 | else: |
730 | 736 | window['-SE-DATA-T-'].update( "Die rolls, etc.: " ) |
731 | 737 |
|
| 738 | + # Evaluate the nature of the extra entropy, and place interpretation to analyze in data_bytes. |
| 739 | + # Binary "hex" data should be neutral harmonically and in Shannon entropy. However, some data |
| 740 | + # such as dice rolls may exhibit restricted symbol values; try to deduce this case, restricting |
| 741 | + # Shannon Entropy 'N' to binary (coin-flip) or 4, 6 and 10-sided dice. |
732 | 742 | status = None |
733 | | - seed_data = values.get( '-SD-SEED-', window['-SD-SEED-'].get() ) |
734 | | - bits = len( seed_data ) * 4 |
735 | | - log.debug( f"Computing Extra Entropy, for {bits}-bit Seed Data: {seed_data!r}" ) |
| 743 | + seed = values.get( '-SD-SEED-', window['-SD-SEED-'].get() ) |
| 744 | + bits = len( seed ) * 4 |
| 745 | + |
| 746 | + strides = 8 |
| 747 | + overlap = False |
| 748 | + ignore_dc = True |
| 749 | + N = None |
| 750 | + interpretation = 'Trust but Verify ;-' |
| 751 | + log.debug( f"Computing Extra Entropy, for {bits}-bit Seed Data: {seed!r}" ) |
736 | 752 | if 'NON' in update_seed_entropy.src: |
737 | 753 | window['-SE-DATA-F-'].update( visible=False ) |
738 | 754 | extra_entropy = b'' |
739 | 755 | elif 'HEX' in update_seed_entropy.src: |
740 | 756 | window['-SE-DATA-F-'].update( visible=True ) |
741 | 757 | try: |
742 | | - # 0-fill and truncate any supplied hex data to the desired bit length |
743 | | - extra_entropy = stretch_seed_entropy( dat, n=0, bits=bits, encoding='hex_codec' ) |
| 758 | + # 0-fill and truncate any supplied hex data to the desired bit length, SHA-512 stretch |
| 759 | + extra_entropy = stretch_seed_entropy( data, n=0, bits=bits, encoding='hex_codec' ) |
744 | 760 | except Exception as exc: |
745 | | - status = f"Invalid Hex {dat!r} for {bits}-bit extra seed entropy: {exc}" |
| 761 | + status = f"Invalid Hex {data!r} for {bits}-bit extra seed entropy: {exc}" |
| 762 | + else: |
| 763 | + data_bytes = codecs.decode( f"{data:<0{bits // 4}.{bits // 4}}", 'hex_codec' ) |
| 764 | + interpretation = "Hexadecimal Data" |
| 765 | + strides = None |
| 766 | + overlap = True |
| 767 | + ignore_dc = False |
746 | 768 | else: |
747 | 769 | window['-SE-DATA-F-'].update( visible=True ) |
748 | 770 | try: |
749 | 771 | # SHA-512 stretch and possibly truncate supplied Entropy (show for 1st Seed) |
750 | | - extra_entropy = stretch_seed_entropy( dat, n=0, bits=bits, encoding='UTF-8' ) |
| 772 | + extra_entropy = stretch_seed_entropy( data, n=0, bits=bits, encoding='UTF-8' ) |
751 | 773 | except Exception as exc: |
752 | | - status = f"Invalid data {dat!r} for {bits}-bit extra seed entropy: {exc}" |
753 | | - |
754 | | - # Compute the Seed Entropy as hex. Will be 128-, 256- or 512-bit hex data. |
| 774 | + status = f"Invalid data {data!r} for {bits}-bit extra seed entropy: {exc}" |
| 775 | + if data and all( '0' <= c <= '9' for c in data ): |
| 776 | + N = { |
| 777 | + '0': 2, '1': 2, # Coin flips |
| 778 | + '2': 6, '3': 6, '4': 6, '5': 6, '6': 6, # 6-sided (regular) dice |
| 779 | + '7': 10, '8': 10, '9': 10, # 10-sided (enter 0 for the 10 side) |
| 780 | + }[max( data )] |
| 781 | + interpretation = f"{N}-sided Dice" if N > 2 else "Coin-flips/Binary" |
| 782 | + |
| 783 | + # Compute the Seed Entropy as hex. Will be 128-, 256- or 512-bit hex data. Ensure |
| 784 | + # extra_{bytes,entropy} are bytes and ASCII (hex, or -) respectively. |
755 | 785 | if status or not extra_entropy: |
756 | | - extra_entropy = '-' * (bits // 4) |
| 786 | + extra_entropy = '-' * ( bits // 4 ) |
| 787 | + extra_bytes = b'' |
757 | 788 | elif type( extra_entropy ) is bytes: |
758 | | - extra_entropy = codecs.encode( extra_entropy, 'hex_codec' ).decode( 'ascii' ) |
| 789 | + extra_bytes = extra_entropy |
| 790 | + extra_entropy = codecs.encode( extra_bytes, 'hex_codec' ).decode( 'ascii' ) |
| 791 | + else: |
| 792 | + extra_bytes = codecs.decode( extra_entropy, 'hex_codec' ) |
| 793 | + |
| 794 | + if window['-SE-SEED-'].get() != extra_entropy: |
| 795 | + update_seed_entropy.entropy = None |
| 796 | + |
| 797 | + se_seed_frame = f"{SE_SEED_FRAME} ({interpretation})" |
| 798 | + if status is None and len( data_bytes ) >= 8 and not update_seed_entropy.entropy: |
| 799 | + if event == '__TIMEOUT__': |
| 800 | + scan_dur,(sigs,shan) = timing( scan_entropy, instrument=True )( # could be (None,None) |
| 801 | + data_bytes, |
| 802 | + strides = strides, |
| 803 | + overlap = overlap, |
| 804 | + ignore_dc = ignore_dc, |
| 805 | + N = N, |
| 806 | + signal_threshold = 300/100, |
| 807 | + shannon_threshold = 10/100, |
| 808 | + show_details = True |
| 809 | + ) |
| 810 | + sigs_rate = f"{rate_dB( max( sigs ).dB if sigs else None, what='Harmonics')}" |
| 811 | + shan_rate = f"{rate_dB( max( shan ).dB if shan else None, what='Shannon Entropy')}" |
| 812 | + window['-SE-SEED-F-'].update( f"{se_seed_frame}: {sigs_rate}, {shan_rate}" ) |
| 813 | + disp_dur,analysis = timing( display_entropy, instrument=True )( sigs, shan, what=f"{len(extra_bytes)*8}-bit Extra Seed Entropy" ) |
| 814 | + update_seed_entropy.entropy = (sigs, shan, analysis) |
| 815 | + log.debug( f"Seed Extra Entropy Analysis took {scan_dur:.3f}s + {disp_dur:.3f}s == {scan_dur+disp_dur:.3f}s: {analysis}" ) |
| 816 | + else: |
| 817 | + log.info( f"Seed Extra requests __TIMEOUT__ w/ current source: {update_seed_entropy.src!r}" ) |
| 818 | + values['__TIMEOUT__'] = .5 |
| 819 | + elif status: |
| 820 | + window['-SE-SEED-F-'].update( f"{se_seed_frame}: Invalid" ) |
| 821 | + else: |
| 822 | + window['-SE-SEED-F-'].update( f"{se_seed_frame}" ) |
| 823 | + |
759 | 824 | values['-SE-SEED-'] = extra_entropy |
760 | 825 | window['-SE-SEED-'].update( extra_entropy ) |
761 | | -update_seed_entropy.src = None # noqa: E305 |
| 826 | + |
| 827 | + update_seed_entropy.was[update_seed_entropy.src] = data |
| 828 | + return status |
| 829 | + |
| 830 | +update_seed_entropy.src = '-SE-NON-' # noqa: E305 |
762 | 831 | update_seed_entropy.was = {} |
| 832 | +update_seed_entropy.entropy = None |
763 | 833 |
|
764 | 834 |
|
765 | 835 | def using_BIP39( values ): |
@@ -1089,7 +1159,7 @@ def app( |
1089 | 1159 | # first SLIP-39 encoding. |
1090 | 1160 | master_secret_was = window['-SEED-'].get() |
1091 | 1161 | status_sd = update_seed_data( event, window, values ) |
1092 | | - status_se = update_seed_entropy( window, values ) |
| 1162 | + status_se = update_seed_entropy( event, window, values ) |
1093 | 1163 | status_ms = None |
1094 | 1164 | try: |
1095 | 1165 | master_secret = compute_master_secret( window, values, n=0 ) |
|
0 commit comments