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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: recode-stamper
3
- Version: 0.1.2
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
- ## How to run
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
- ## How to run
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:
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "recode-stamper"
7
- version = "0.1.2"
7
+ version = "0.1.4"
8
8
  description = "Encode and decode UTF-8 strings into a 12- or 24-word RECODE mnemonic."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.9"
@@ -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
- mnemonic = parse_mnemonic_input(args.text)
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
- args.encode_text.encode("utf-8"),
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
- mnemonic = parse_mnemonic_input(args.decode_text)
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.2
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
- ## How to run
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