recode-stamper 0.1.2__tar.gz → 0.1.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {recode_stamper-0.1.2/recode_stamper.egg-info → recode_stamper-0.1.4}/PKG-INFO +38 -2
- {recode_stamper-0.1.2 → recode_stamper-0.1.4}/README.md +37 -1
- {recode_stamper-0.1.2 → recode_stamper-0.1.4}/pyproject.toml +1 -1
- {recode_stamper-0.1.2 → recode_stamper-0.1.4}/recode_stamper/__init__.py +114 -4
- {recode_stamper-0.1.2 → recode_stamper-0.1.4/recode_stamper.egg-info}/PKG-INFO +38 -2
- {recode_stamper-0.1.2 → recode_stamper-0.1.4}/LICENSE +0 -0
- {recode_stamper-0.1.2 → recode_stamper-0.1.4}/MANIFEST.in +0 -0
- {recode_stamper-0.1.2 → recode_stamper-0.1.4}/recode_stamper/wordlist4096.txt +0 -0
- {recode_stamper-0.1.2 → recode_stamper-0.1.4}/recode_stamper.egg-info/SOURCES.txt +0 -0
- {recode_stamper-0.1.2 → recode_stamper-0.1.4}/recode_stamper.egg-info/dependency_links.txt +0 -0
- {recode_stamper-0.1.2 → recode_stamper-0.1.4}/recode_stamper.egg-info/entry_points.txt +0 -0
- {recode_stamper-0.1.2 → recode_stamper-0.1.4}/recode_stamper.egg-info/top_level.txt +0 -0
- {recode_stamper-0.1.2 → recode_stamper-0.1.4}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: recode-stamper
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.4
|
|
4
4
|
Summary: Encode and decode UTF-8 strings into a 12- or 24-word RECODE mnemonic.
|
|
5
5
|
Author-email: avra911 <java-hug-circulate@duck.com>
|
|
6
6
|
License: MIT License
|
|
@@ -46,7 +46,7 @@ A small Python utility that encodes UTF-8 text into a 12- or 24-word mnemonic (R
|
|
|
46
46
|
pip install recode-stamper==0.1.2
|
|
47
47
|
```
|
|
48
48
|
|
|
49
|
-
##
|
|
49
|
+
## Quick start
|
|
50
50
|
|
|
51
51
|
After installation, use the CLI entry point:
|
|
52
52
|
|
|
@@ -65,6 +65,15 @@ recode encode "MyPassword123!"
|
|
|
65
65
|
recode decode "artist scavenge intimate airdrop humdrum curfew hexagon candy comedy bunion brevity aardvark"
|
|
66
66
|
```
|
|
67
67
|
|
|
68
|
+
For interactive use, the CLI can also prompt for text securely without echoing it in the terminal:
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
recode --encode
|
|
72
|
+
recode encode
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
This is useful for avoiding accidental exposure in shell history or terminal logs.
|
|
76
|
+
|
|
68
77
|
Show help:
|
|
69
78
|
|
|
70
79
|
```bash
|
|
@@ -78,6 +87,33 @@ python recode.py --encode "MyPassword123!"
|
|
|
78
87
|
python recode.py --decode "artist scavenge intimate airdrop humdrum curfew hexagon candy comedy bunion brevity aardvark"
|
|
79
88
|
```
|
|
80
89
|
|
|
90
|
+
## How the format works
|
|
91
|
+
|
|
92
|
+
The RECODE format uses a fixed 4096-word list.
|
|
93
|
+
|
|
94
|
+
- Each word is mapped to a 12-bit value
|
|
95
|
+
- 2^12 = 4096 unique values
|
|
96
|
+
- A word index is therefore a number from 0 to 4095
|
|
97
|
+
|
|
98
|
+
The format automatically selects between 12 and 24 words:
|
|
99
|
+
|
|
100
|
+
- up to 16 bytes of data -> 12 words
|
|
101
|
+
- 17 to 32 bytes of data -> 24 words
|
|
102
|
+
- more than 32 bytes -> rejected
|
|
103
|
+
|
|
104
|
+
This keeps the output compact while preserving enough space for metadata and a checksum.
|
|
105
|
+
|
|
106
|
+
You can also force the format explicitly:
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
recode --encode "MyPassword123!" --plates 12
|
|
110
|
+
recode --encode "MyPassword123!" --plates 24
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## Why UTF-8
|
|
114
|
+
|
|
115
|
+
The encoder accepts UTF-8 text and converts it to bytes before packing the bit stream. This means it works for regular ASCII strings as well as non-ASCII text, while preserving deterministic behavior and a fixed wordlist layout.
|
|
116
|
+
|
|
81
117
|
## Wordlist source
|
|
82
118
|
|
|
83
119
|
The file `wordlist4096.txt` is based on the public wordlist from:
|
|
@@ -8,7 +8,7 @@ A small Python utility that encodes UTF-8 text into a 12- or 24-word mnemonic (R
|
|
|
8
8
|
pip install recode-stamper==0.1.2
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
##
|
|
11
|
+
## Quick start
|
|
12
12
|
|
|
13
13
|
After installation, use the CLI entry point:
|
|
14
14
|
|
|
@@ -27,6 +27,15 @@ recode encode "MyPassword123!"
|
|
|
27
27
|
recode decode "artist scavenge intimate airdrop humdrum curfew hexagon candy comedy bunion brevity aardvark"
|
|
28
28
|
```
|
|
29
29
|
|
|
30
|
+
For interactive use, the CLI can also prompt for text securely without echoing it in the terminal:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
recode --encode
|
|
34
|
+
recode encode
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
This is useful for avoiding accidental exposure in shell history or terminal logs.
|
|
38
|
+
|
|
30
39
|
Show help:
|
|
31
40
|
|
|
32
41
|
```bash
|
|
@@ -40,6 +49,33 @@ python recode.py --encode "MyPassword123!"
|
|
|
40
49
|
python recode.py --decode "artist scavenge intimate airdrop humdrum curfew hexagon candy comedy bunion brevity aardvark"
|
|
41
50
|
```
|
|
42
51
|
|
|
52
|
+
## How the format works
|
|
53
|
+
|
|
54
|
+
The RECODE format uses a fixed 4096-word list.
|
|
55
|
+
|
|
56
|
+
- Each word is mapped to a 12-bit value
|
|
57
|
+
- 2^12 = 4096 unique values
|
|
58
|
+
- A word index is therefore a number from 0 to 4095
|
|
59
|
+
|
|
60
|
+
The format automatically selects between 12 and 24 words:
|
|
61
|
+
|
|
62
|
+
- up to 16 bytes of data -> 12 words
|
|
63
|
+
- 17 to 32 bytes of data -> 24 words
|
|
64
|
+
- more than 32 bytes -> rejected
|
|
65
|
+
|
|
66
|
+
This keeps the output compact while preserving enough space for metadata and a checksum.
|
|
67
|
+
|
|
68
|
+
You can also force the format explicitly:
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
recode --encode "MyPassword123!" --plates 12
|
|
72
|
+
recode --encode "MyPassword123!" --plates 24
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Why UTF-8
|
|
76
|
+
|
|
77
|
+
The encoder accepts UTF-8 text and converts it to bytes before packing the bit stream. This means it works for regular ASCII strings as well as non-ASCII text, while preserving deterministic behavior and a fixed wordlist layout.
|
|
78
|
+
|
|
43
79
|
## Wordlist source
|
|
44
80
|
|
|
45
81
|
The file `wordlist4096.txt` is based on the public wordlist from:
|
|
@@ -15,6 +15,7 @@ The script encodes UTF-8 text into a 12- or 24-word mnemonic and decodes it back
|
|
|
15
15
|
import argparse
|
|
16
16
|
import hashlib
|
|
17
17
|
from pathlib import Path
|
|
18
|
+
import getpass
|
|
18
19
|
|
|
19
20
|
|
|
20
21
|
class Recode:
|
|
@@ -675,6 +676,24 @@ def parse_mnemonic_input(raw_text):
|
|
|
675
676
|
]
|
|
676
677
|
|
|
677
678
|
|
|
679
|
+
def prompt_mnemonic_input():
|
|
680
|
+
"""Prompt for each mnemonic word without echoing it."""
|
|
681
|
+
|
|
682
|
+
raw_count = input("Number of mnemonic words (12 or 24): ")
|
|
683
|
+
try:
|
|
684
|
+
word_count = int(raw_count)
|
|
685
|
+
except ValueError:
|
|
686
|
+
raise ValueError("Mnemonic must contain 12 or 24 words")
|
|
687
|
+
|
|
688
|
+
if word_count not in (12, 24):
|
|
689
|
+
raise ValueError("Mnemonic must contain 12 or 24 words")
|
|
690
|
+
|
|
691
|
+
return [
|
|
692
|
+
getpass.getpass(f"Word {position}/{word_count}: ")
|
|
693
|
+
for position in range(1, word_count + 1)
|
|
694
|
+
]
|
|
695
|
+
|
|
696
|
+
|
|
678
697
|
def build_parser():
|
|
679
698
|
"""Create the command-line parser."""
|
|
680
699
|
|
|
@@ -685,6 +704,8 @@ def build_parser():
|
|
|
685
704
|
parser.add_argument(
|
|
686
705
|
"--encode",
|
|
687
706
|
dest="encode_text",
|
|
707
|
+
nargs="?",
|
|
708
|
+
const="",
|
|
688
709
|
help="UTF-8 string to encode into mnemonic words",
|
|
689
710
|
metavar="TEXT",
|
|
690
711
|
)
|
|
@@ -692,6 +713,8 @@ def build_parser():
|
|
|
692
713
|
parser.add_argument(
|
|
693
714
|
"--decode",
|
|
694
715
|
dest="decode_text",
|
|
716
|
+
nargs="?",
|
|
717
|
+
const="",
|
|
695
718
|
help="Mnemonic phrase to decode back into a UTF-8 string",
|
|
696
719
|
metavar="TEXT",
|
|
697
720
|
)
|
|
@@ -700,7 +723,7 @@ def build_parser():
|
|
|
700
723
|
"--plates",
|
|
701
724
|
type=int,
|
|
702
725
|
choices=(12, 24),
|
|
703
|
-
help="Force 12 or 24 plates when encoding",
|
|
726
|
+
help="Force 12 or 24 plates when encoding, or use it during decoding to avoid prompting",
|
|
704
727
|
)
|
|
705
728
|
|
|
706
729
|
subparsers = parser.add_subparsers(
|
|
@@ -713,6 +736,7 @@ def build_parser():
|
|
|
713
736
|
)
|
|
714
737
|
encode_parser.add_argument(
|
|
715
738
|
"text",
|
|
739
|
+
nargs="?",
|
|
716
740
|
help="UTF-8 string to encode",
|
|
717
741
|
)
|
|
718
742
|
encode_parser.add_argument(
|
|
@@ -728,20 +752,64 @@ def build_parser():
|
|
|
728
752
|
)
|
|
729
753
|
decode_parser.add_argument(
|
|
730
754
|
"text",
|
|
755
|
+
nargs="?",
|
|
731
756
|
help="Mnemonic words separated by spaces",
|
|
732
757
|
)
|
|
758
|
+
decode_parser.add_argument(
|
|
759
|
+
"--plates",
|
|
760
|
+
type=int,
|
|
761
|
+
choices=(12, 24),
|
|
762
|
+
help="Force 12 or 24 plates when decoding to avoid prompting",
|
|
763
|
+
)
|
|
733
764
|
|
|
734
765
|
return parser
|
|
735
766
|
|
|
736
767
|
|
|
768
|
+
def _normalize_flagged_text_args(argv):
|
|
769
|
+
"""Reorder --encode/--decode with --plates so the text stays attached to the flag."""
|
|
770
|
+
|
|
771
|
+
if argv is None:
|
|
772
|
+
return None
|
|
773
|
+
|
|
774
|
+
args = list(argv)
|
|
775
|
+
i = 0
|
|
776
|
+
while i < len(args) - 3:
|
|
777
|
+
flag = args[i]
|
|
778
|
+
if flag in ("--encode", "--decode") and args[i + 1] == "--plates":
|
|
779
|
+
plates_value = args[i + 2]
|
|
780
|
+
text_value = args[i + 3]
|
|
781
|
+
|
|
782
|
+
if text_value.startswith("-"):
|
|
783
|
+
i += 1
|
|
784
|
+
continue
|
|
785
|
+
|
|
786
|
+
args = (
|
|
787
|
+
args[:i]
|
|
788
|
+
+ [flag, text_value, "--plates", plates_value]
|
|
789
|
+
+ args[i + 4 :]
|
|
790
|
+
)
|
|
791
|
+
i += 1
|
|
792
|
+
else:
|
|
793
|
+
i += 1
|
|
794
|
+
|
|
795
|
+
return args
|
|
796
|
+
|
|
797
|
+
|
|
737
798
|
def main(argv=None):
|
|
738
799
|
"""Run the CLI."""
|
|
739
800
|
|
|
740
801
|
parser = build_parser()
|
|
802
|
+
argv = _normalize_flagged_text_args(argv)
|
|
741
803
|
args = parser.parse_args(argv)
|
|
742
804
|
|
|
743
805
|
if args.command == "encode":
|
|
744
806
|
text = args.text
|
|
807
|
+
if text is None:
|
|
808
|
+
text = getpass.getpass("Text to encode: ")
|
|
809
|
+
confirmation = getpass.getpass("Confirm text to encode: ")
|
|
810
|
+
if text != confirmation:
|
|
811
|
+
print("Error: input values do not match.")
|
|
812
|
+
return 1
|
|
745
813
|
plates = args.plates
|
|
746
814
|
mnemonic = r.encode(
|
|
747
815
|
text.encode("utf-8"),
|
|
@@ -751,21 +819,63 @@ def main(argv=None):
|
|
|
751
819
|
return 0
|
|
752
820
|
|
|
753
821
|
if args.command == "decode":
|
|
754
|
-
|
|
822
|
+
text = args.text
|
|
823
|
+
if text is None:
|
|
824
|
+
if args.plates is not None:
|
|
825
|
+
word_count = args.plates
|
|
826
|
+
mnemonic = [
|
|
827
|
+
getpass.getpass(f"Word {position}/{word_count}: ")
|
|
828
|
+
for position in range(1, word_count + 1)
|
|
829
|
+
]
|
|
830
|
+
else:
|
|
831
|
+
mnemonic = prompt_mnemonic_input()
|
|
832
|
+
else:
|
|
833
|
+
mnemonic = parse_mnemonic_input(text)
|
|
834
|
+
if args.plates is not None and len(mnemonic) != args.plates:
|
|
835
|
+
raise ValueError(
|
|
836
|
+
f"Provided mnemonic has {len(mnemonic)} words but --plates={args.plates} expects {args.plates}"
|
|
837
|
+
)
|
|
838
|
+
if args.plates is not None and len(mnemonic) != args.plates:
|
|
839
|
+
raise ValueError(
|
|
840
|
+
f"Provided mnemonic has {len(mnemonic)} words but --plates={args.plates} expects {args.plates}"
|
|
841
|
+
)
|
|
755
842
|
decoded = r.decode(mnemonic).decode("utf-8")
|
|
756
843
|
print(decoded)
|
|
757
844
|
return 0
|
|
758
845
|
|
|
759
846
|
if args.encode_text is not None:
|
|
847
|
+
if args.encode_text == "":
|
|
848
|
+
text = getpass.getpass("Text to encode: ")
|
|
849
|
+
confirmation = getpass.getpass("Confirm text to encode: ")
|
|
850
|
+
if text != confirmation:
|
|
851
|
+
print("Error: input values do not match.")
|
|
852
|
+
return 1
|
|
853
|
+
else:
|
|
854
|
+
text = args.encode_text
|
|
760
855
|
mnemonic = r.encode(
|
|
761
|
-
|
|
856
|
+
text.encode("utf-8"),
|
|
762
857
|
plates=args.plates,
|
|
763
858
|
)
|
|
764
859
|
print(" ".join(mnemonic))
|
|
765
860
|
return 0
|
|
766
861
|
|
|
767
862
|
if args.decode_text is not None:
|
|
768
|
-
|
|
863
|
+
text = args.decode_text
|
|
864
|
+
if text == "":
|
|
865
|
+
if args.plates is not None:
|
|
866
|
+
word_count = args.plates
|
|
867
|
+
mnemonic = [
|
|
868
|
+
getpass.getpass(f"Word {position}/{word_count}: ")
|
|
869
|
+
for position in range(1, word_count + 1)
|
|
870
|
+
]
|
|
871
|
+
else:
|
|
872
|
+
mnemonic = prompt_mnemonic_input()
|
|
873
|
+
else:
|
|
874
|
+
mnemonic = parse_mnemonic_input(text)
|
|
875
|
+
if args.plates is not None and len(mnemonic) != args.plates:
|
|
876
|
+
raise ValueError(
|
|
877
|
+
f"Provided mnemonic has {len(mnemonic)} words but --plates={args.plates} expects {args.plates}"
|
|
878
|
+
)
|
|
769
879
|
decoded = r.decode(mnemonic).decode("utf-8")
|
|
770
880
|
print(decoded)
|
|
771
881
|
return 0
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: recode-stamper
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.4
|
|
4
4
|
Summary: Encode and decode UTF-8 strings into a 12- or 24-word RECODE mnemonic.
|
|
5
5
|
Author-email: avra911 <java-hug-circulate@duck.com>
|
|
6
6
|
License: MIT License
|
|
@@ -46,7 +46,7 @@ A small Python utility that encodes UTF-8 text into a 12- or 24-word mnemonic (R
|
|
|
46
46
|
pip install recode-stamper==0.1.2
|
|
47
47
|
```
|
|
48
48
|
|
|
49
|
-
##
|
|
49
|
+
## Quick start
|
|
50
50
|
|
|
51
51
|
After installation, use the CLI entry point:
|
|
52
52
|
|
|
@@ -65,6 +65,15 @@ recode encode "MyPassword123!"
|
|
|
65
65
|
recode decode "artist scavenge intimate airdrop humdrum curfew hexagon candy comedy bunion brevity aardvark"
|
|
66
66
|
```
|
|
67
67
|
|
|
68
|
+
For interactive use, the CLI can also prompt for text securely without echoing it in the terminal:
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
recode --encode
|
|
72
|
+
recode encode
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
This is useful for avoiding accidental exposure in shell history or terminal logs.
|
|
76
|
+
|
|
68
77
|
Show help:
|
|
69
78
|
|
|
70
79
|
```bash
|
|
@@ -78,6 +87,33 @@ python recode.py --encode "MyPassword123!"
|
|
|
78
87
|
python recode.py --decode "artist scavenge intimate airdrop humdrum curfew hexagon candy comedy bunion brevity aardvark"
|
|
79
88
|
```
|
|
80
89
|
|
|
90
|
+
## How the format works
|
|
91
|
+
|
|
92
|
+
The RECODE format uses a fixed 4096-word list.
|
|
93
|
+
|
|
94
|
+
- Each word is mapped to a 12-bit value
|
|
95
|
+
- 2^12 = 4096 unique values
|
|
96
|
+
- A word index is therefore a number from 0 to 4095
|
|
97
|
+
|
|
98
|
+
The format automatically selects between 12 and 24 words:
|
|
99
|
+
|
|
100
|
+
- up to 16 bytes of data -> 12 words
|
|
101
|
+
- 17 to 32 bytes of data -> 24 words
|
|
102
|
+
- more than 32 bytes -> rejected
|
|
103
|
+
|
|
104
|
+
This keeps the output compact while preserving enough space for metadata and a checksum.
|
|
105
|
+
|
|
106
|
+
You can also force the format explicitly:
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
recode --encode "MyPassword123!" --plates 12
|
|
110
|
+
recode --encode "MyPassword123!" --plates 24
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## Why UTF-8
|
|
114
|
+
|
|
115
|
+
The encoder accepts UTF-8 text and converts it to bytes before packing the bit stream. This means it works for regular ASCII strings as well as non-ASCII text, while preserving deterministic behavior and a fixed wordlist layout.
|
|
116
|
+
|
|
81
117
|
## Wordlist source
|
|
82
118
|
|
|
83
119
|
The file `wordlist4096.txt` is based on the public wordlist from:
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|