rtty-soda 0.1.5__py3-none-any.whl → 0.2.0__py3-none-any.whl

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.

Potentially problematic release.


This version of rtty-soda might be problematic. Click here for more details.

rtty_soda/archivers.py CHANGED
@@ -1,7 +1,5 @@
1
- import bz2
2
- import lzma
3
- import zlib
4
1
  from collections.abc import Callable
2
+ from compression import bz2, lzma, zlib, zstd
5
3
 
6
4
  __all__ = ["ARCHIVERS", "UNARCHIVERS", "Archiver"]
7
5
 
@@ -9,6 +7,10 @@ __all__ = ["ARCHIVERS", "UNARCHIVERS", "Archiver"]
9
7
  type Archiver = Callable[[bytes], bytes]
10
8
 
11
9
 
10
+ def compress_zstd(data: bytes) -> bytes:
11
+ return zstd.compress(data, level=20)
12
+
13
+
12
14
  def compress_zlib(data: bytes) -> bytes:
13
15
  return zlib.compress(data, level=9)
14
16
 
@@ -26,6 +28,10 @@ def compress_lzma(data: bytes) -> bytes:
26
28
  )
27
29
 
28
30
 
31
+ def decompress_zstd(data: bytes) -> bytes:
32
+ return zstd.decompress(data)
33
+
34
+
29
35
  def decompress_zlib(data: bytes) -> bytes:
30
36
  return zlib.decompress(data)
31
37
 
@@ -43,6 +49,7 @@ def noop(data: bytes) -> bytes:
43
49
 
44
50
 
45
51
  ARCHIVERS: dict[str, Archiver] = {
52
+ "zstd": compress_zstd,
46
53
  "zlib": compress_zlib,
47
54
  "bz2": compress_bz2,
48
55
  "lzma": compress_lzma,
@@ -50,6 +57,7 @@ ARCHIVERS: dict[str, Archiver] = {
50
57
  }
51
58
 
52
59
  UNARCHIVERS: dict[str, Archiver] = {
60
+ "zstd": decompress_zstd,
53
61
  "zlib": decompress_zlib,
54
62
  "bz2": decompress_bz2,
55
63
  "lzma": decompress_lzma,
rtty_soda/cli_io.py CHANGED
@@ -1,21 +1,25 @@
1
1
  import random
2
2
  import re
3
3
  import string
4
- from pathlib import Path
5
- from typing import TextIO, cast
4
+ from typing import TYPE_CHECKING, TextIO, cast
5
+
6
+ if TYPE_CHECKING:
7
+ from pathlib import Path
6
8
 
7
9
  import click
8
10
 
9
- from rtty_soda.encoders import Encoder, RawEncoder, encode_str
11
+ from rtty_soda.encoders import Encoder, decode_bytes, encode_str
10
12
 
11
13
  __all__ = [
14
+ "pad_newlines",
12
15
  "print_stats",
13
16
  "read_bytes",
14
- "read_ciphertext_bytes",
15
- "read_clean_bytes",
16
- "read_plaintext_bytes",
17
+ "read_encoded_stripped",
18
+ "read_key_bytes",
19
+ "read_password_bytes",
17
20
  "read_str",
18
21
  "remove_whitespace",
22
+ "split_groups",
19
23
  "write_bytes_atomic",
20
24
  "write_output",
21
25
  ]
@@ -23,35 +27,33 @@ __all__ = [
23
27
 
24
28
  def read_str(source: Path) -> str:
25
29
  with click.open_file(source, mode="rt", encoding="utf-8", errors="strict") as fd:
26
- return cast("TextIO", fd).read().strip()
27
-
28
-
29
- def read_bytes(source: Path) -> bytes:
30
- return encode_str(read_str(source))
30
+ return cast("TextIO", fd).read()
31
31
 
32
32
 
33
33
  def remove_whitespace(data: str) -> str:
34
34
  return re.sub(r"\s", "", data)
35
35
 
36
36
 
37
- def read_clean_bytes(source: Path) -> bytes:
37
+ def read_encoded_stripped(source: Path) -> bytes:
38
38
  data = read_str(source)
39
39
  data = remove_whitespace(data)
40
40
  return encode_str(data)
41
41
 
42
42
 
43
- def read_plaintext_bytes(source: Path, in_enc: Encoder) -> bytes:
44
- if in_enc == RawEncoder:
43
+ def read_bytes(source: Path, is_binary: bool) -> bytes:
44
+ if is_binary:
45
45
  return source.read_bytes()
46
46
 
47
- return read_bytes(source)
47
+ return read_encoded_stripped(source)
48
48
 
49
49
 
50
- def read_ciphertext_bytes(source: Path, in_enc: Encoder) -> bytes:
51
- if in_enc == RawEncoder:
52
- return source.read_bytes()
50
+ def read_key_bytes(source: Path, is_binary: bool, encoder: Encoder) -> bytes:
51
+ key = read_bytes(source, is_binary)
52
+ return encoder.decode(key)
53
+
53
54
 
54
- return read_clean_bytes(source)
55
+ def read_password_bytes(source: Path) -> bytes:
56
+ return encode_str(read_str(source).strip())
55
57
 
56
58
 
57
59
  def write_bytes_atomic(target: Path, data: bytes) -> None:
@@ -61,16 +63,51 @@ def write_bytes_atomic(target: Path, data: bytes) -> None:
61
63
  temp_path.replace(target)
62
64
 
63
65
 
64
- def write_output(target: Path | None, data: bytes, out_enc: Encoder) -> None:
65
- if target is None or target.stem == "-":
66
- if out_enc == RawEncoder:
67
- click.confirm(
68
- "Print binary output to the terminal?", default=False, abort=True
69
- )
66
+ def split_groups(data: str, group_len: int, line_len: int) -> str:
67
+ step = group_len if group_len > 0 else line_len - 1
68
+ groups = (data[i : i + step] for i in range(0, len(data), step))
69
+ result: list[str] = []
70
+ line: list[str] = []
71
+ i = 0
72
+ gpl = line_len // (step + 1)
73
+ for group in groups:
74
+ line.append(group)
75
+ i += 1
76
+ if i == gpl:
77
+ result.append(" ".join(line))
78
+ i = 0
79
+ line = []
80
+
81
+ if line:
82
+ result.append(" ".join(line))
83
+
84
+ return "\n".join(result)
85
+
86
+
87
+ def pad_newlines(data: bytes, count: int) -> bytes:
88
+ padding = b"\n" * count
89
+ return padding + data + padding
90
+
91
+
92
+ def write_output(
93
+ target: Path | None,
94
+ data: bytes,
95
+ is_binary: bool,
96
+ group_len: int,
97
+ line_len: int,
98
+ padding: int,
99
+ ) -> None:
100
+ if not is_binary and (group_len > 0 or line_len > 0):
101
+ text = decode_bytes(data)
102
+ text = split_groups(text, group_len, line_len)
103
+ data = encode_str(text)
104
+
105
+ if padding > 0:
106
+ data = pad_newlines(data, padding)
70
107
 
71
- click.echo()
72
- click.echo(data)
73
- click.echo()
108
+ if target is None or target.stem == "-":
109
+ add_nl = not data.endswith(b"\n")
110
+ click.echo(data, nl=add_nl)
74
111
  else:
75
112
  if target.exists():
76
113
  click.confirm(
@@ -1,3 +1,4 @@
1
+ from nacl.encoding import RawEncoder
1
2
  from nacl.hash import blake2b
2
3
  from nacl.public import PrivateKey
3
4
  from nacl.pwhash import argon2id
@@ -11,8 +12,6 @@ from nacl.pwhash.argon2id import (
11
12
  SALTBYTES,
12
13
  )
13
14
 
14
- from rtty_soda.encoders import Encoder, RawEncoder
15
-
16
15
  __all__ = ["KDF_PROFILES", "KdfProfile", "hash_salt", "kdf"]
17
16
 
18
17
  type KdfProfile = tuple[int, int]
@@ -34,9 +33,17 @@ certainty;
34
33
 
35
34
 
36
35
  def hash_salt(salt: bytes) -> bytes:
37
- return blake2b(salt, digest_size=SALTBYTES, encoder=RawEncoder)
36
+ return blake2b(data=salt, digest_size=SALTBYTES, encoder=RawEncoder)
38
37
 
39
38
 
40
- def kdf(password: bytes, profile: KdfProfile, out_enc: Encoder) -> bytes:
39
+ def kdf(password: bytes, profile: KdfProfile) -> bytes:
41
40
  salt = hash_salt(password + SALT_MOD)
42
- return argon2id.kdf(PrivateKey.SIZE, password, salt, *profile, encoder=out_enc)
41
+ ops, mem = profile
42
+ return argon2id.kdf(
43
+ size=PrivateKey.SIZE,
44
+ password=password,
45
+ salt=salt,
46
+ opslimit=ops,
47
+ memlimit=mem,
48
+ encoder=RawEncoder,
49
+ )
@@ -1,20 +1,20 @@
1
+ from typing import TYPE_CHECKING
2
+
3
+ from nacl.encoding import RawEncoder
1
4
  from nacl.public import Box, PrivateKey, PublicKey
2
- from nacl.utils import EncryptedMessage
3
5
 
4
- from rtty_soda.encoders import Encoder
6
+ if TYPE_CHECKING:
7
+ from nacl.utils import EncryptedMessage
8
+
5
9
 
6
10
  __all__ = ["decrypt", "encrypt"]
7
11
 
8
12
 
9
- def encrypt(
10
- private: PrivateKey, public: PublicKey, data: bytes, out_enc: Encoder
11
- ) -> EncryptedMessage:
12
- box = Box(private, public)
13
- return box.encrypt(data, encoder=out_enc)
13
+ def encrypt(private: PrivateKey, public: PublicKey, data: bytes) -> EncryptedMessage:
14
+ box = Box(private_key=private, public_key=public)
15
+ return box.encrypt(plaintext=data, encoder=RawEncoder)
14
16
 
15
17
 
16
- def decrypt(
17
- private: PrivateKey, public: PublicKey, data: bytes, in_enc: Encoder
18
- ) -> bytes:
19
- box = Box(private, public)
20
- return box.decrypt(data, encoder=in_enc)
18
+ def decrypt(private: PrivateKey, public: PublicKey, data: bytes) -> bytes:
19
+ box = Box(private_key=private, public_key=public)
20
+ return box.decrypt(ciphertext=data, encoder=RawEncoder)
@@ -1,17 +1,20 @@
1
- from nacl.encoding import Encoder
1
+ from typing import TYPE_CHECKING
2
+
3
+ from nacl.encoding import RawEncoder
2
4
  from nacl.secret import SecretBox
3
- from nacl.utils import EncryptedMessage
5
+
6
+ if TYPE_CHECKING:
7
+ from nacl.utils import EncryptedMessage
8
+
4
9
 
5
10
  __all__ = ["decrypt", "encrypt"]
6
11
 
7
12
 
8
- def encrypt(
9
- key: bytes, data: bytes, key_enc: Encoder, out_enc: Encoder
10
- ) -> EncryptedMessage:
11
- box = SecretBox(key, key_enc)
12
- return box.encrypt(data, encoder=out_enc)
13
+ def encrypt(key: bytes, data: bytes) -> EncryptedMessage:
14
+ box = SecretBox(key=key, encoder=RawEncoder)
15
+ return box.encrypt(plaintext=data, encoder=RawEncoder)
13
16
 
14
17
 
15
- def decrypt(key: bytes, data: bytes, key_enc: Encoder, in_enc: Encoder) -> bytes:
16
- box = SecretBox(key, key_enc)
17
- return box.decrypt(data, encoder=in_enc)
18
+ def decrypt(key: bytes, data: bytes) -> bytes:
19
+ box = SecretBox(key=key, encoder=RawEncoder)
20
+ return box.decrypt(ciphertext=data, encoder=RawEncoder)
@@ -1,13 +1,16 @@
1
- from nacl.encoding import Base64Encoder, Encoder, RawEncoder
2
-
3
1
  from .base26_encoder import Base26Encoder
2
+ from .base31_encoder import Base31Encoder
4
3
  from .base36_encoder import Base36Encoder
4
+ from .base64_encoder import Base64Encoder
5
5
  from .base94_encoder import Base94Encoder
6
+ from .encoder import Encoder
6
7
  from .functions import decode_bytes, encode_str
8
+ from .raw_encoder import RawEncoder
7
9
 
8
10
  __all__ = [
9
11
  "ENCODERS",
10
12
  "Base26Encoder",
13
+ "Base31Encoder",
11
14
  "Base36Encoder",
12
15
  "Base64Encoder",
13
16
  "Base94Encoder",
@@ -19,6 +22,7 @@ __all__ = [
19
22
 
20
23
  ENCODERS: dict[str, Encoder] = {
21
24
  "base26": Base26Encoder,
25
+ "base31": Base31Encoder,
22
26
  "base36": Base36Encoder,
23
27
  "base64": Base64Encoder,
24
28
  "base94": Base94Encoder,
@@ -1,7 +1,6 @@
1
1
  import string
2
2
 
3
- from nacl.encoding import _Encoder as EncoderABC # pyright: ignore [reportPrivateUsage]
4
-
3
+ from .encoder import Encoder
5
4
  from .functions import base_to_bytes, bytes_to_base, decode_bytes, encode_str
6
5
 
7
6
  __all__ = ["ALPHABET", "Base26Encoder"]
@@ -9,7 +8,7 @@ __all__ = ["ALPHABET", "Base26Encoder"]
9
8
  ALPHABET = string.ascii_uppercase
10
9
 
11
10
 
12
- class Base26Encoder(EncoderABC):
11
+ class Base26Encoder(Encoder):
13
12
  @staticmethod
14
13
  def encode(data: bytes) -> bytes:
15
14
  return encode_str(bytes_to_base(data, ALPHABET))
@@ -0,0 +1,16 @@
1
+ from .encoder import Encoder
2
+ from .functions import base_to_bytes, bytes_to_base, decode_bytes, encode_str
3
+
4
+ __all__ = ["ALPHABET", "Base31Encoder"]
5
+
6
+ ALPHABET = "АБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЬЫЭЮЯ"
7
+
8
+
9
+ class Base31Encoder(Encoder):
10
+ @staticmethod
11
+ def encode(data: bytes) -> bytes:
12
+ return encode_str(bytes_to_base(data, ALPHABET))
13
+
14
+ @staticmethod
15
+ def decode(data: bytes) -> bytes:
16
+ return base_to_bytes(decode_bytes(data), ALPHABET)
@@ -1,7 +1,6 @@
1
1
  import string
2
2
 
3
- from nacl.encoding import _Encoder as EncoderABC # pyright: ignore [reportPrivateUsage]
4
-
3
+ from .encoder import Encoder
5
4
  from .functions import base_to_bytes, bytes_to_base, decode_bytes, encode_str
6
5
 
7
6
  __all__ = ["ALPHABET", "Base36Encoder"]
@@ -9,7 +8,7 @@ __all__ = ["ALPHABET", "Base36Encoder"]
9
8
  ALPHABET = string.digits + string.ascii_uppercase
10
9
 
11
10
 
12
- class Base36Encoder(EncoderABC):
11
+ class Base36Encoder(Encoder):
13
12
  @staticmethod
14
13
  def encode(data: bytes) -> bytes:
15
14
  return encode_str(bytes_to_base(data, ALPHABET))
@@ -0,0 +1,15 @@
1
+ import base64
2
+
3
+ from .encoder import Encoder
4
+
5
+ __all__ = ["Base64Encoder"]
6
+
7
+
8
+ class Base64Encoder(Encoder):
9
+ @staticmethod
10
+ def encode(data: bytes) -> bytes:
11
+ return base64.b64encode(data)
12
+
13
+ @staticmethod
14
+ def decode(data: bytes) -> bytes:
15
+ return base64.b64decode(data)
@@ -1,5 +1,4 @@
1
- from nacl.encoding import _Encoder as EncoderABC # pyright: ignore [reportPrivateUsage]
2
-
1
+ from .encoder import Encoder
3
2
  from .functions import base_to_bytes, bytes_to_base, decode_bytes, encode_str
4
3
 
5
4
  __all__ = ["ALPHABET", "Base94Encoder"]
@@ -7,7 +6,7 @@ __all__ = ["ALPHABET", "Base94Encoder"]
7
6
  ALPHABET = "".join([chr(i) for i in range(33, 127)])
8
7
 
9
8
 
10
- class Base94Encoder(EncoderABC):
9
+ class Base94Encoder(Encoder):
11
10
  @staticmethod
12
11
  def encode(data: bytes) -> bytes:
13
12
  return encode_str(bytes_to_base(data, ALPHABET))
@@ -0,0 +1,11 @@
1
+ from typing import Protocol
2
+
3
+ __all__ = ["Encoder"]
4
+
5
+
6
+ class Encoder(Protocol):
7
+ @staticmethod
8
+ def encode(data: bytes) -> bytes: ...
9
+
10
+ @staticmethod
11
+ def decode(data: bytes) -> bytes: ...
@@ -1,3 +1,5 @@
1
+ from typing import cast
2
+
1
3
  __all__ = ["base_to_bytes", "bytes_to_base", "decode_bytes", "encode_str"]
2
4
 
3
5
 
@@ -27,7 +29,7 @@ def base_to_int(source: str, alphabet: str) -> int:
27
29
  number = 0
28
30
  base = len(alphabet)
29
31
  for index, digit in enumerate(reversed(source)):
30
- number += alphabet.index(digit) * (base**index)
32
+ number += alphabet.index(digit) * cast("int", base**index)
31
33
 
32
34
  return number
33
35
 
@@ -0,0 +1,13 @@
1
+ from .encoder import Encoder
2
+
3
+ __all__ = ["RawEncoder"]
4
+
5
+
6
+ class RawEncoder(Encoder):
7
+ @staticmethod
8
+ def encode(data: bytes) -> bytes:
9
+ return data
10
+
11
+ @staticmethod
12
+ def decode(data: bytes) -> bytes:
13
+ return data