transcrypto 1.2.0__py3-none-any.whl → 1.3.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.
transcrypto/rsa.py CHANGED
@@ -14,6 +14,8 @@ import logging
14
14
  # import pdb
15
15
  from typing import Self
16
16
 
17
+ import gmpy2 # type:ignore
18
+
17
19
  from . import base, modmath, aes
18
20
 
19
21
  __author__ = 'balparda@github.com'
@@ -100,7 +102,7 @@ class RSAPublicKey(base.CryptoKey, base.Encryptor, base.Verifier):
100
102
  if not 0 < message < self.public_modulus:
101
103
  raise base.InputError(f'invalid message: {message=}')
102
104
  # encrypt
103
- return modmath.ModExp(message, self.encrypt_exp, self.public_modulus)
105
+ return int(gmpy2.powmod(message, self.encrypt_exp, self.public_modulus)) # type:ignore # pylint:disable=no-member
104
106
 
105
107
  def Encrypt(self, plaintext: bytes, /, *, associated_data: bytes | None = None) -> bytes:
106
108
  """Encrypt `plaintext` and return `ciphertext`.
@@ -290,8 +292,8 @@ class RSAObfuscationPair(RSAPublicKey):
290
292
  if not 0 < message < self.public_modulus:
291
293
  raise base.InputError(f'invalid message: {message=}')
292
294
  # encrypt
293
- return (message * modmath.ModExp(
294
- self.random_key, self.encrypt_exp, self.public_modulus)) % self.public_modulus
295
+ return (message * int(gmpy2.powmod( # type:ignore # pylint:disable=no-member
296
+ self.random_key, self.encrypt_exp, self.public_modulus))) % self.public_modulus
295
297
 
296
298
  def RevealOriginalSignature(self, message: int, signature: int, /) -> int:
297
299
  """Recover original signature for `message` from obfuscated `signature`.
@@ -451,9 +453,9 @@ class RSAPrivateKey(RSAPublicKey, base.Decryptor, base.Signer): # pylint: disab
451
453
  if not 0 <= ciphertext < self.public_modulus:
452
454
  raise base.InputError(f'invalid message: {ciphertext=}')
453
455
  # decrypt using CRT (Chinese Remainder Theorem); 4x speedup; all the below is equivalent
454
- # of doing: return modmath.ModExp(ciphertext, self.decrypt_exp, self.public_modulus)
455
- m_p: int = modmath.ModExp(ciphertext % self.modulus_p, self.remainder_p, self.modulus_p)
456
- m_q: int = modmath.ModExp(ciphertext % self.modulus_q, self.remainder_q, self.modulus_q)
456
+ # of doing: return pow(ciphertext, self.decrypt_exp, self.public_modulus)
457
+ m_p: int = int(gmpy2.powmod(ciphertext % self.modulus_p, self.remainder_p, self.modulus_p)) # type:ignore # pylint:disable=no-member
458
+ m_q: int = int(gmpy2.powmod(ciphertext % self.modulus_q, self.remainder_q, self.modulus_q)) # type:ignore # pylint:disable=no-member
457
459
  h: int = (self.q_inverse_p * (m_p - m_q)) % self.modulus_p
458
460
  return (m_q + h * self.modulus_q) % self.public_modulus
459
461
 
@@ -568,21 +570,19 @@ class RSAPrivateKey(RSAPublicKey, base.Decryptor, base.Signer): # pylint: disab
568
570
  failures: int = 0
569
571
  while True:
570
572
  try:
571
- primes: list[int] = [modmath.NBitRandomPrime(bit_length // 2),
572
- modmath.NBitRandomPrime(bit_length // 2)]
573
- modulus: int = primes[0] * primes[1]
574
- while modulus.bit_length() != bit_length or primes[0] == primes[1]:
575
- primes.remove(min(primes))
576
- primes.append(modmath.NBitRandomPrime(
577
- bit_length // 2 + (bit_length % 2 if modulus.bit_length() < bit_length else 0)))
578
- modulus = primes[0] * primes[1]
573
+ primes: set[int] = set()
574
+ modulus: int = 0
575
+ p: int = 0
576
+ q: int = 0
577
+ while modulus.bit_length() != bit_length:
578
+ primes = modmath.NBitRandomPrimes((bit_length + 1) // 2, n_primes=2)
579
+ p, q = min(primes), max(primes) # "p" is always the smaller, "q" the larger
580
+ modulus = p * q
579
581
  # build object
580
- phi: int = (primes[0] - 1) * (primes[1] - 1)
582
+ phi: int = (p - 1) * (q - 1)
581
583
  prime_exp: int = (_SMALL_ENCRYPTION_EXPONENT if phi <= _BIG_ENCRYPTION_EXPONENT else
582
584
  _BIG_ENCRYPTION_EXPONENT)
583
585
  decrypt_exp: int = modmath.ModInv(prime_exp, phi)
584
- p: int = min(primes) # "p" is always the smaller
585
- q: int = max(primes) # "q" is always the larger
586
586
  return cls(
587
587
  modulus_p=p,
588
588
  modulus_q=q,
transcrypto/sss.py CHANGED
@@ -314,9 +314,7 @@ class ShamirSharedSecretPrivate(ShamirSharedSecretPublic):
314
314
  if bit_length < 10:
315
315
  raise base.InputError(f'invalid bit length: {bit_length=}')
316
316
  # make the primes
317
- unique_primes: set[int] = set()
318
- while len(unique_primes) < minimum_shares:
319
- unique_primes.add(modmath.NBitRandomPrime(bit_length))
317
+ unique_primes: set[int] = modmath.NBitRandomPrimes(bit_length, n_primes=minimum_shares)
320
318
  # get the largest prime for the modulus
321
319
  ordered_primes: list[int] = list(unique_primes)
322
320
  modulus: int = max(ordered_primes)
@@ -321,12 +321,12 @@ def _BuildParser() -> argparse.ArgumentParser: # pylint: disable=too-many-state
321
321
  ' poetry run transcrypto mod crt 6 7 127 13\n\n'
322
322
  ' # --- Hashing ---\n'
323
323
  ' poetry run transcrypto hash sha256 xyz\n'
324
- ' poetry run transcrypto --b64 hash sha512 eHl6\n'
324
+ ' poetry run transcrypto --b64 hash sha512 -- eHl6\n'
325
325
  ' poetry run transcrypto hash file /etc/passwd --digest sha512\n\n'
326
326
  ' # --- AES ---\n'
327
327
  ' poetry run transcrypto --out-b64 aes key "correct horse battery staple"\n'
328
- ' poetry run transcrypto --b64 --out-b64 aes encrypt -k "<b64key>" "secret"\n'
329
- ' poetry run transcrypto --b64 --out-b64 aes decrypt -k "<b64key>" "<ciphertext>"\n'
328
+ ' poetry run transcrypto --b64 --out-b64 aes encrypt -k "<b64key>" -- "secret"\n'
329
+ ' poetry run transcrypto --b64 --out-b64 aes decrypt -k "<b64key>" -- "<ciphertext>"\n'
330
330
  ' poetry run transcrypto aes ecb -k "<b64key>" encrypt "<128bithexblock>"\n' # cspell:disable-line
331
331
  ' poetry run transcrypto aes ecb -k "<b64key>" decrypt "<128bithexblock>"\n\n' # cspell:disable-line
332
332
  ' # --- RSA ---\n'
@@ -335,6 +335,10 @@ def _BuildParser() -> argparse.ArgumentParser: # pylint: disable=too-many-state
335
335
  ' poetry run transcrypto -p rsa-key.priv rsa rawdecrypt <ciphertext>\n'
336
336
  ' poetry run transcrypto -p rsa-key.priv rsa rawsign <message>\n'
337
337
  ' poetry run transcrypto -p rsa-key.pub rsa rawverify <message> <signature>\n\n'
338
+ ' poetry run transcrypto --bin --out-b64 -p rsa-key.pub rsa encrypt -a <aad> <plaintext>\n'
339
+ ' poetry run transcrypto --b64 --out-bin -p rsa-key.priv rsa decrypt -a <aad> -- <ciphertext>\n'
340
+ ' poetry run transcrypto --bin --out-b64 -p rsa-key.priv rsa sign <message>\n'
341
+ ' poetry run transcrypto --b64 -p rsa-key.pub rsa verify -- <message> <signature>\n\n'
338
342
  ' # --- ElGamal ---\n'
339
343
  ' poetry run transcrypto -p eg-key elgamal shared --bits 2048\n'
340
344
  ' poetry run transcrypto -p eg-key elgamal new\n'
@@ -342,19 +346,27 @@ def _BuildParser() -> argparse.ArgumentParser: # pylint: disable=too-many-state
342
346
  ' poetry run transcrypto -p eg-key.priv elgamal rawdecrypt <c1:c2>\n'
343
347
  ' poetry run transcrypto -p eg-key.priv elgamal rawsign <message>\n'
344
348
  ' poetry run transcrypto-p eg-key.pub elgamal rawverify <message> <s1:s2>\n\n'
349
+ ' poetry run transcrypto --bin --out-b64 -p eg-key.pub elgamal encrypt <plaintext>\n'
350
+ ' poetry run transcrypto --b64 --out-bin -p eg-key.priv elgamal decrypt -- <ciphertext>\n'
351
+ ' poetry run transcrypto --bin --out-b64 -p eg-key.priv elgamal sign <message>\n'
352
+ ' poetry run transcrypto --b64 -p eg-key.pub elgamal verify -- <message> <signature>\n\n'
345
353
  ' # --- DSA ---\n'
346
354
  ' poetry run transcrypto -p dsa-key dsa shared --p-bits 2048 --q-bits 256\n'
347
355
  ' poetry run transcrypto -p dsa-key dsa new\n'
348
356
  ' poetry run transcrypto -p dsa-key.priv dsa rawsign <message>\n'
349
357
  ' poetry run transcrypto -p dsa-key.pub dsa rawverify <message> <s1:s2>\n\n'
358
+ ' poetry run transcrypto --bin --out-b64 -p dsa-key.priv dsa sign <message>\n'
359
+ ' poetry run transcrypto --b64 -p dsa-key.pub dsa verify -- <message> <signature>\n\n'
350
360
  ' # --- Public Bid ---\n'
351
361
  ' poetry run transcrypto --bin bid new "tomorrow it will rain"\n'
352
362
  ' poetry run transcrypto --out-bin bid verify\n\n'
353
363
  ' # --- Shamir Secret Sharing (SSS) ---\n'
354
364
  ' poetry run transcrypto -p sss-key sss new 3 --bits 1024\n'
355
- ' poetry run transcrypto -p sss-key sss rawshares <secret> 5\n'
365
+ ' poetry run transcrypto -p sss-key sss rawshares <secret> <n>\n'
356
366
  ' poetry run transcrypto -p sss-key sss rawrecover\n'
357
367
  ' poetry run transcrypto -p sss-key sss rawverify <secret>'
368
+ ' poetry run transcrypto --bin -p sss-key sss shares <secret> <n>\n'
369
+ ' poetry run transcrypto --out-bin -p sss-key sss recover\n'
358
370
  ),
359
371
  formatter_class=argparse.RawTextHelpFormatter)
360
372
  sub = parser.add_subparsers(dest='command')
@@ -368,7 +380,10 @@ def _BuildParser() -> argparse.ArgumentParser: # pylint: disable=too-many-state
368
380
  # --hex/--b64/--bin for input mode (default hex)
369
381
  in_grp = parser.add_mutually_exclusive_group()
370
382
  in_grp.add_argument('--hex', action='store_true', help='Treat inputs as hex string (default)')
371
- in_grp.add_argument('--b64', action='store_true', help='Treat inputs as base64url')
383
+ in_grp.add_argument(
384
+ '--b64', action='store_true',
385
+ help=('Treat inputs as base64url; sometimes base64 will start with "-" and that can '
386
+ 'conflict with flags, so use "--" before positional args if needed'))
372
387
  in_grp.add_argument('--bin', action='store_true', help='Treat inputs as binary (bytes)')
373
388
 
374
389
  # --out-hex/--out-b64/--out-bin for output mode (default hex)
@@ -557,7 +572,7 @@ def _BuildParser() -> argparse.ArgumentParser: # pylint: disable=too-many-state
557
572
  help='SHA-256 of input `data`.',
558
573
  epilog=('--bin hash sha256 xyz\n'
559
574
  '3608bca1e44ea6c4d268eb6db02260269892c0b42b86bbf1e77a6fa16c3c9282 $$'
560
- '--b64 hash sha256 eHl6 # "xyz" in base-64\n'
575
+ '--b64 hash sha256 -- eHl6 # "xyz" in base-64\n'
561
576
  '3608bca1e44ea6c4d268eb6db02260269892c0b42b86bbf1e77a6fa16c3c9282'))
562
577
  p_h256.add_argument('data', type=str, help='Input data (raw text; or use --hex/--b64/--bin)')
563
578
 
@@ -568,7 +583,7 @@ def _BuildParser() -> argparse.ArgumentParser: # pylint: disable=too-many-state
568
583
  epilog=('--bin hash sha512 xyz\n'
569
584
  '4a3ed8147e37876adc8f76328e5abcc1b470e6acfc18efea0135f983604953a5'
570
585
  '8e183c1a6086e91ba3e821d926f5fdeb37761c7ca0328a963f5e92870675b728 $$'
571
- '--b64 hash sha512 eHl6 # "xyz" in base-64\n'
586
+ '--b64 hash sha512 -- eHl6 # "xyz" in base-64\n'
572
587
  '4a3ed8147e37876adc8f76328e5abcc1b470e6acfc18efea0135f983604953a5'
573
588
  '8e183c1a6086e91ba3e821d926f5fdeb37761c7ca0328a963f5e92870675b728'))
574
589
  p_h512.add_argument('data', type=str, help='Input data (raw text; or use --hex/--b64/--bin)')
@@ -614,10 +629,10 @@ def _BuildParser() -> argparse.ArgumentParser: # pylint: disable=too-many-state
614
629
  'can use `--bin`/`--hex`/`--b64` flags. Attention: if you provide `-a`/`--aad` '
615
630
  '(associated data, AAD), you will need to provide the same AAD when decrypting '
616
631
  'and it is NOT included in the `ciphertext`/CT returned by this method!'),
617
- epilog=('--b64 --out-b64 aes encrypt -k DbWJ_ZrknLEEIoq_NpoCQwHYfjskGokpueN2O_eY0es= ' # cspell:disable-line
632
+ epilog=('--b64 --out-b64 aes encrypt -k DbWJ_ZrknLEEIoq_NpoCQwHYfjskGokpueN2O_eY0es= -- ' # cspell:disable-line
618
633
  'AAAAAAB4eXo=\nF2_ZLrUw5Y8oDnbTP5t5xCUWX8WtVILLD0teyUi_37_4KHeV-YowVA== $$ ' # cspell:disable-line
619
634
  '--b64 --out-b64 aes encrypt -k DbWJ_ZrknLEEIoq_NpoCQwHYfjskGokpueN2O_eY0es= -a eHl6 ' # cspell:disable-line
620
- 'AAAAAAB4eXo=\nxOlAHPUPpeyZHId-f3VQ_QKKMxjIW0_FBo9WOfIBrzjn0VkVV6xTRA==')) # cspell:disable-line
635
+ '-- AAAAAAB4eXo=\nxOlAHPUPpeyZHId-f3VQ_QKKMxjIW0_FBo9WOfIBrzjn0VkVV6xTRA==')) # cspell:disable-line
621
636
  p_aes_enc.add_argument('plaintext', type=str, help='Input data to encrypt (PT)')
622
637
  p_aes_enc.add_argument(
623
638
  '-k', '--key', type=str, default='', help='Key if `-p`/`--key-path` wasn\'t used (32 bytes)')
@@ -632,10 +647,10 @@ def _BuildParser() -> argparse.ArgumentParser: # pylint: disable=too-many-state
632
647
  '`-p`/`--key-path` keyfile. All inputs are raw, or you '
633
648
  'can use `--bin`/`--hex`/`--b64` flags. Attention: if you provided `-a`/`--aad` '
634
649
  '(associated data, AAD) during encryption, you will need to provide the same AAD now!'),
635
- epilog=('--b64 --out-b64 aes decrypt -k DbWJ_ZrknLEEIoq_NpoCQwHYfjskGokpueN2O_eY0es= ' # cspell:disable-line
636
- 'F2_ZLrUw5Y8oDnbTP5t5xCUWX8WtVILLD0teyUi_37_4KHeV-YowVA==\nAAAAAAB4eXo= $$ ' # cspell:disable-line
637
- '--b64 --out-b64 aes decrypt -k DbWJ_ZrknLEEIoq_NpoCQwHYfjskGokpueN2O_eY0es= ' # cspell:disable-line
638
- '-a eHl6 xOlAHPUPpeyZHId-f3VQ_QKKMxjIW0_FBo9WOfIBrzjn0VkVV6xTRA==\nAAAAAAB4eXo=')) # cspell:disable-line
650
+ epilog=('--b64 --out-b64 aes decrypt -k DbWJ_ZrknLEEIoq_NpoCQwHYfjskGokpueN2O_eY0es= -- ' # cspell:disable-line
651
+ 'F2_ZLrUw5Y8oDnbTP5t5xCUWX8WtVILLD0teyUi_37_4KHeV-YowVA==\nAAAAAAB4eXo= $$ ' # cspell:disable-line
652
+ '--b64 --out-b64 aes decrypt -k DbWJ_ZrknLEEIoq_NpoCQwHYfjskGokpueN2O_eY0es= ' # cspell:disable-line
653
+ '-a eHl6 -- xOlAHPUPpeyZHId-f3VQ_QKKMxjIW0_FBo9WOfIBrzjn0VkVV6xTRA==\nAAAAAAB4eXo=')) # cspell:disable-line
639
654
  p_aes_dec.add_argument('ciphertext', type=str, help='Input data to decrypt (CT)')
640
655
  p_aes_dec.add_argument(
641
656
  '-k', '--key', type=str, default='', help='Key if `-p`/`--key-path` wasn\'t used (32 bytes)')
@@ -724,8 +739,8 @@ def _BuildParser() -> argparse.ArgumentParser: # pylint: disable=too-many-state
724
739
  p_rsa_dec_safe: argparse.ArgumentParser = rsa_sub.add_parser(
725
740
  'decrypt',
726
741
  help='Decrypt `ciphertext` with private key.',
727
- epilog=('--b64 --out-bin -p rsa-key.priv rsa decrypt "AO6knI6xwq6TGR…Qy22jiFhXi1eQ==" '
728
- '-a "eHl6"\nabcde'))
742
+ epilog=('--b64 --out-bin -p rsa-key.priv rsa decrypt -a eHl6 -- '
743
+ 'AO6knI6xwq6TGR…Qy22jiFhXi1eQ== \nabcde'))
729
744
  p_rsa_dec_safe.add_argument('ciphertext', type=str, help='Ciphertext to decrypt')
730
745
  p_rsa_dec_safe.add_argument(
731
746
  '-a', '--aad', type=str, default='',
@@ -763,10 +778,10 @@ def _BuildParser() -> argparse.ArgumentParser: # pylint: disable=too-many-state
763
778
  p_rsa_ver_safe: argparse.ArgumentParser = rsa_sub.add_parser(
764
779
  'verify',
765
780
  help='Verify `signature` for `message` with public key.',
766
- epilog=('--b64 -p rsa-key.pub rsa verify "eHl6" '
767
- '"91TS7gC6LORiL…6RD23Aejsfxlw=="\nRSA signature: OK $$ ' # cspell:disable-line
768
- '--b64 -p rsa-key.pub rsa verify "eLl6" '
769
- '"91TS7gC6LORiL…6RD23Aejsfxlw=="\nRSA signature: INVALID')) # cspell:disable-line
781
+ epilog=('--b64 -p rsa-key.pub rsa verify -- eHl6 '
782
+ '91TS7gC6LORiL…6RD23Aejsfxlw==\nRSA signature: OK $$ ' # cspell:disable-line
783
+ '--b64 -p rsa-key.pub rsa verify -- eLl6 '
784
+ '91TS7gC6LORiL…6RD23Aejsfxlw==\nRSA signature: INVALID')) # cspell:disable-line
770
785
  p_rsa_ver_safe.add_argument('message', type=str, help='Message that was signed earlier')
771
786
  p_rsa_ver_safe.add_argument('signature', type=str, help='Putative signature for `message`')
772
787
  p_rsa_ver_safe.add_argument(
@@ -814,7 +829,7 @@ def _BuildParser() -> argparse.ArgumentParser: # pylint: disable=too-many-state
814
829
  p_eg_enc_safe: argparse.ArgumentParser = eg_sub.add_parser(
815
830
  'encrypt',
816
831
  help='Encrypt `message` with public key.',
817
- epilog=(' --bin --out-b64 -p eg-key.pub elgamal encrypt "abcde" -a "xyz"\n'
832
+ epilog=('--bin --out-b64 -p eg-key.pub elgamal encrypt "abcde" -a "xyz"\n'
818
833
  'CdFvoQ_IIPFPZLua…kqjhcUTspISxURg==')) # cspell:disable-line
819
834
  p_eg_enc_safe.add_argument('plaintext', type=str, help='Message to encrypt')
820
835
  p_eg_enc_safe.add_argument(
@@ -834,8 +849,8 @@ def _BuildParser() -> argparse.ArgumentParser: # pylint: disable=too-many-state
834
849
  p_eg_dec_safe: argparse.ArgumentParser = eg_sub.add_parser(
835
850
  'decrypt',
836
851
  help='Decrypt `ciphertext` with private key.',
837
- epilog=('--b64 --out-bin -p eg-key.priv elgamal decrypt '
838
- '"CdFvoQ_IIPFPZLua…kqjhcUTspISxURg==" -a "eHl6"\nabcde')) # cspell:disable-line
852
+ epilog=('--b64 --out-bin -p eg-key.priv elgamal decrypt -a eHl6 -- '
853
+ 'CdFvoQ_IIPFPZLua…kqjhcUTspISxURg==\nabcde')) # cspell:disable-line
839
854
  p_eg_dec_safe.add_argument('ciphertext', type=str, help='Ciphertext to decrypt')
840
855
  p_eg_dec_safe.add_argument(
841
856
  '-a', '--aad', type=str, default='',
@@ -877,9 +892,9 @@ def _BuildParser() -> argparse.ArgumentParser: # pylint: disable=too-many-state
877
892
  p_eg_ver_safe: argparse.ArgumentParser = eg_sub.add_parser(
878
893
  'verify',
879
894
  help='Verify `signature` for `message` with public key.',
880
- epilog=('--b64 -p eg-key.pub elgamal verify "eHl6" "Xl4hlYK8SHVGw…0fCKJE1XVzA=="\n' # cspell:disable-line
895
+ epilog=('--b64 -p eg-key.pub elgamal verify -- eHl6 Xl4hlYK8SHVGw…0fCKJE1XVzA==\n' # cspell:disable-line
881
896
  'El-Gamal signature: OK $$ '
882
- '--b64 -p eg-key.pub elgamal verify "eLl6" "Xl4hlYK8SHVGw…0fCKJE1XVzA=="\n' # cspell:disable-line
897
+ '--b64 -p eg-key.pub elgamal verify -- eLl6 Xl4hlYK8SHVGw…0fCKJE1XVzA==\n' # cspell:disable-line
883
898
  'El-Gamal signature: INVALID'))
884
899
  p_eg_ver_safe.add_argument('message', type=str, help='Message that was signed earlier')
885
900
  p_eg_ver_safe.add_argument('signature', type=str, help='Putative signature for `message`')
@@ -956,9 +971,9 @@ def _BuildParser() -> argparse.ArgumentParser: # pylint: disable=too-many-state
956
971
  p_dsa_verify_safe: argparse.ArgumentParser = dsa_sub.add_parser(
957
972
  'verify',
958
973
  help='Verify `signature` for `message` with public key.',
959
- epilog=('--b64 -p dsa-key.pub dsa verify "eHl6" "yq8InJVpViXh9…BD4par2XuA="\n'
974
+ epilog=('--b64 -p dsa-key.pub dsa verify -- eHl6 yq8InJVpViXh9…BD4par2XuA=\n'
960
975
  'DSA signature: OK $$ '
961
- '--b64 -p dsa-key.pub dsa verify "eLl6" "yq8InJVpViXh9…BD4par2XuA="\n'
976
+ '--b64 -p dsa-key.pub dsa verify -- eLl6 yq8InJVpViXh9…BD4par2XuA=\n'
962
977
  'DSA signature: INVALID'))
963
978
  p_dsa_verify_safe.add_argument('message', type=str, help='Message that was signed earlier')
964
979
  p_dsa_verify_safe.add_argument('signature', type=str, help='Putative signature for `message`')
@@ -1544,7 +1559,7 @@ def main(argv: list[str] | None = None, /) -> int: # pylint: disable=invalid-na
1544
1559
  case 'bytes':
1545
1560
  print(base.BytesToHex(base.RandBytes(args.n)))
1546
1561
  case 'prime':
1547
- print(modmath.NBitRandomPrime(args.bits))
1562
+ print(modmath.NBitRandomPrimes(args.bits).pop())
1548
1563
  case _:
1549
1564
  raise NotImplementedError()
1550
1565
  case 'hash':
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: transcrypto
3
- Version: 1.2.0
3
+ Version: 1.3.0
4
4
  Summary: Basic crypto primitives, not intended for actual use, but as a companion to --Criptografia, Métodos e Algoritmos--
5
5
  Author-email: Daniel Balparda <balparda@github.com>
6
6
  License-Expression: Apache-2.0
@@ -62,28 +62,40 @@ Started in July/2025, by Daniel Balparda. Since version 1.0.2 it is PyPI package
62
62
  - [`rsa`](#rsa)
63
63
  - [`rsa new`](#rsa-new)
64
64
  - [`rsa rawencrypt`](#rsa-rawencrypt)
65
+ - [`rsa encrypt`](#rsa-encrypt)
65
66
  - [`rsa rawdecrypt`](#rsa-rawdecrypt)
67
+ - [`rsa decrypt`](#rsa-decrypt)
66
68
  - [`rsa rawsign`](#rsa-rawsign)
69
+ - [`rsa sign`](#rsa-sign)
67
70
  - [`rsa rawverify`](#rsa-rawverify)
71
+ - [`rsa verify`](#rsa-verify)
68
72
  - [`elgamal`](#elgamal)
69
73
  - [`elgamal shared`](#elgamal-shared)
70
74
  - [`elgamal new`](#elgamal-new)
71
75
  - [`elgamal rawencrypt`](#elgamal-rawencrypt)
76
+ - [`elgamal encrypt`](#elgamal-encrypt)
72
77
  - [`elgamal rawdecrypt`](#elgamal-rawdecrypt)
78
+ - [`elgamal decrypt`](#elgamal-decrypt)
73
79
  - [`elgamal rawsign`](#elgamal-rawsign)
80
+ - [`elgamal sign`](#elgamal-sign)
74
81
  - [`elgamal rawverify`](#elgamal-rawverify)
82
+ - [`elgamal verify`](#elgamal-verify)
75
83
  - [`dsa`](#dsa)
76
84
  - [`dsa shared`](#dsa-shared)
77
85
  - [`dsa new`](#dsa-new)
78
86
  - [`dsa rawsign`](#dsa-rawsign)
87
+ - [`dsa sign`](#dsa-sign)
79
88
  - [`dsa rawverify`](#dsa-rawverify)
89
+ - [`dsa verify`](#dsa-verify)
80
90
  - [`bid`](#bid)
81
91
  - [`bid new`](#bid-new)
82
92
  - [`bid verify`](#bid-verify)
83
93
  - [`sss`](#sss)
84
94
  - [`sss new`](#sss-new)
85
95
  - [`sss rawshares`](#sss-rawshares)
96
+ - [`sss shares`](#sss-shares)
86
97
  - [`sss rawrecover`](#sss-rawrecover)
98
+ - [`sss recover`](#sss-recover)
87
99
  - [`sss rawverify`](#sss-rawverify)
88
100
  - [`doc`](#doc)
89
101
  - [`doc md`](#doc-md)
@@ -185,7 +197,7 @@ poetry run transcrypto <command> [sub-command] [options...]
185
197
  |---|---|
186
198
  | `-v, --verbose` | Increase verbosity (use -v/-vv/-vvv/-vvvv for ERROR/WARN/INFO/DEBUG) |
187
199
  | `--hex` | Treat inputs as hex string (default) |
188
- | `--b64` | Treat inputs as base64url |
200
+ | `--b64` | Treat inputs as base64url; sometimes base64 will start with "-" and that can conflict with flags, so use "--" before positional args if needed |
189
201
  | `--bin` | Treat inputs as binary (bytes) |
190
202
  | `--out-hex` | Outputs as hex (default) |
191
203
  | `--out-b64` | Outputs as base64url |
@@ -237,13 +249,13 @@ Examples:
237
249
 
238
250
  # --- Hashing ---
239
251
  poetry run transcrypto hash sha256 xyz
240
- poetry run transcrypto --b64 hash sha512 eHl6
252
+ poetry run transcrypto --b64 hash sha512 -- eHl6
241
253
  poetry run transcrypto hash file /etc/passwd --digest sha512
242
254
 
243
255
  # --- AES ---
244
256
  poetry run transcrypto --out-b64 aes key "correct horse battery staple"
245
- poetry run transcrypto --b64 --out-b64 aes encrypt -k "<b64key>" "secret"
246
- poetry run transcrypto --b64 --out-b64 aes decrypt -k "<b64key>" "<ciphertext>"
257
+ poetry run transcrypto --b64 --out-b64 aes encrypt -k "<b64key>" -- "secret"
258
+ poetry run transcrypto --b64 --out-b64 aes decrypt -k "<b64key>" -- "<ciphertext>"
247
259
  poetry run transcrypto aes ecb -k "<b64key>" encrypt "<128bithexblock>"
248
260
  poetry run transcrypto aes ecb -k "<b64key>" decrypt "<128bithexblock>"
249
261
 
@@ -254,6 +266,11 @@ Examples:
254
266
  poetry run transcrypto -p rsa-key.priv rsa rawsign <message>
255
267
  poetry run transcrypto -p rsa-key.pub rsa rawverify <message> <signature>
256
268
 
269
+ poetry run transcrypto --bin --out-b64 -p rsa-key.pub rsa encrypt -a <aad> <plaintext>
270
+ poetry run transcrypto --b64 --out-bin -p rsa-key.priv rsa decrypt -a <aad> -- <ciphertext>
271
+ poetry run transcrypto --bin --out-b64 -p rsa-key.priv rsa sign <message>
272
+ poetry run transcrypto --b64 -p rsa-key.pub rsa verify -- <message> <signature>
273
+
257
274
  # --- ElGamal ---
258
275
  poetry run transcrypto -p eg-key elgamal shared --bits 2048
259
276
  poetry run transcrypto -p eg-key elgamal new
@@ -262,21 +279,31 @@ Examples:
262
279
  poetry run transcrypto -p eg-key.priv elgamal rawsign <message>
263
280
  poetry run transcrypto-p eg-key.pub elgamal rawverify <message> <s1:s2>
264
281
 
282
+ poetry run transcrypto --bin --out-b64 -p eg-key.pub elgamal encrypt <plaintext>
283
+ poetry run transcrypto --b64 --out-bin -p eg-key.priv elgamal decrypt -- <ciphertext>
284
+ poetry run transcrypto --bin --out-b64 -p eg-key.priv elgamal sign <message>
285
+ poetry run transcrypto --b64 -p eg-key.pub elgamal verify -- <message> <signature>
286
+
265
287
  # --- DSA ---
266
288
  poetry run transcrypto -p dsa-key dsa shared --p-bits 2048 --q-bits 256
267
289
  poetry run transcrypto -p dsa-key dsa new
268
290
  poetry run transcrypto -p dsa-key.priv dsa rawsign <message>
269
291
  poetry run transcrypto -p dsa-key.pub dsa rawverify <message> <s1:s2>
270
292
 
293
+ poetry run transcrypto --bin --out-b64 -p dsa-key.priv dsa sign <message>
294
+ poetry run transcrypto --b64 -p dsa-key.pub dsa verify -- <message> <signature>
295
+
271
296
  # --- Public Bid ---
272
297
  poetry run transcrypto --bin bid new "tomorrow it will rain"
273
298
  poetry run transcrypto --out-bin bid verify
274
299
 
275
300
  # --- Shamir Secret Sharing (SSS) ---
276
301
  poetry run transcrypto -p sss-key sss new 3 --bits 1024
277
- poetry run transcrypto -p sss-key sss rawshares <secret> 5
302
+ poetry run transcrypto -p sss-key sss rawshares <secret> <n>
278
303
  poetry run transcrypto -p sss-key sss rawrecover
279
- poetry run transcrypto -p sss-key sss rawverify <secret>
304
+ poetry run transcrypto -p sss-key sss rawverify <secret> poetry run transcrypto --bin -p sss-key sss shares <secret> <n>
305
+ poetry run transcrypto --out-bin -p sss-key sss recover
306
+
280
307
  ```
281
308
 
282
309
  ---
@@ -671,7 +698,7 @@ poetry run transcrypto hash sha256 [-h] data
671
698
  ```bash
672
699
  $ poetry run transcrypto --bin hash sha256 xyz
673
700
  3608bca1e44ea6c4d268eb6db02260269892c0b42b86bbf1e77a6fa16c3c9282
674
- $ poetry run transcrypto --b64 hash sha256 eHl6 # "xyz" in base-64
701
+ $ poetry run transcrypto --b64 hash sha256 -- eHl6 # "xyz" in base-64
675
702
  3608bca1e44ea6c4d268eb6db02260269892c0b42b86bbf1e77a6fa16c3c9282
676
703
  ```
677
704
 
@@ -692,7 +719,7 @@ poetry run transcrypto hash sha512 [-h] data
692
719
  ```bash
693
720
  $ poetry run transcrypto --bin hash sha512 xyz
694
721
  4a3ed8147e37876adc8f76328e5abcc1b470e6acfc18efea0135f983604953a58e183c1a6086e91ba3e821d926f5fdeb37761c7ca0328a963f5e92870675b728
695
- $ poetry run transcrypto --b64 hash sha512 eHl6 # "xyz" in base-64
722
+ $ poetry run transcrypto --b64 hash sha512 -- eHl6 # "xyz" in base-64
696
723
  4a3ed8147e37876adc8f76328e5abcc1b470e6acfc18efea0135f983604953a58e183c1a6086e91ba3e821d926f5fdeb37761c7ca0328a963f5e92870675b728
697
724
  ```
698
725
 
@@ -764,9 +791,9 @@ poetry run transcrypto aes encrypt [-h] [-k KEY] [-a AAD] plaintext
764
791
  **Example:**
765
792
 
766
793
  ```bash
767
- $ poetry run transcrypto --b64 --out-b64 aes encrypt -k DbWJ_ZrknLEEIoq_NpoCQwHYfjskGokpueN2O_eY0es= AAAAAAB4eXo=
794
+ $ poetry run transcrypto --b64 --out-b64 aes encrypt -k DbWJ_ZrknLEEIoq_NpoCQwHYfjskGokpueN2O_eY0es= -- AAAAAAB4eXo=
768
795
  F2_ZLrUw5Y8oDnbTP5t5xCUWX8WtVILLD0teyUi_37_4KHeV-YowVA==
769
- $ poetry run transcrypto --b64 --out-b64 aes encrypt -k DbWJ_ZrknLEEIoq_NpoCQwHYfjskGokpueN2O_eY0es= -a eHl6 AAAAAAB4eXo=
796
+ $ poetry run transcrypto --b64 --out-b64 aes encrypt -k DbWJ_ZrknLEEIoq_NpoCQwHYfjskGokpueN2O_eY0es= -a eHl6 -- AAAAAAB4eXo=
770
797
  xOlAHPUPpeyZHId-f3VQ_QKKMxjIW0_FBo9WOfIBrzjn0VkVV6xTRA==
771
798
  ```
772
799
 
@@ -787,9 +814,9 @@ poetry run transcrypto aes decrypt [-h] [-k KEY] [-a AAD] ciphertext
787
814
  **Example:**
788
815
 
789
816
  ```bash
790
- $ poetry run transcrypto --b64 --out-b64 aes decrypt -k DbWJ_ZrknLEEIoq_NpoCQwHYfjskGokpueN2O_eY0es= F2_ZLrUw5Y8oDnbTP5t5xCUWX8WtVILLD0teyUi_37_4KHeV-YowVA==
817
+ $ poetry run transcrypto --b64 --out-b64 aes decrypt -k DbWJ_ZrknLEEIoq_NpoCQwHYfjskGokpueN2O_eY0es= -- F2_ZLrUw5Y8oDnbTP5t5xCUWX8WtVILLD0teyUi_37_4KHeV-YowVA==
791
818
  AAAAAAB4eXo=
792
- $ poetry run transcrypto --b64 --out-b64 aes decrypt -k DbWJ_ZrknLEEIoq_NpoCQwHYfjskGokpueN2O_eY0es= -a eHl6 xOlAHPUPpeyZHId-f3VQ_QKKMxjIW0_FBo9WOfIBrzjn0VkVV6xTRA==
819
+ $ poetry run transcrypto --b64 --out-b64 aes decrypt -k DbWJ_ZrknLEEIoq_NpoCQwHYfjskGokpueN2O_eY0es= -a eHl6 -- xOlAHPUPpeyZHId-f3VQ_QKKMxjIW0_FBo9WOfIBrzjn0VkVV6xTRA==
793
820
  AAAAAAB4eXo=
794
821
  ```
795
822
 
@@ -947,7 +974,7 @@ poetry run transcrypto rsa decrypt [-h] [-a AAD] ciphertext
947
974
  **Example:**
948
975
 
949
976
  ```bash
950
- $ poetry run transcrypto --b64 --out-bin -p rsa-key.priv rsa decrypt "AO6knI6xwq6TGR…Qy22jiFhXi1eQ==" -a "eHl6"
977
+ $ poetry run transcrypto --b64 --out-bin -p rsa-key.priv rsa decrypt -a eHl6 -- AO6knI6xwq6TGR…Qy22jiFhXi1eQ==
951
978
  abcde
952
979
  ```
953
980
 
@@ -1029,9 +1056,9 @@ poetry run transcrypto rsa verify [-h] [-a AAD] message signature
1029
1056
  **Example:**
1030
1057
 
1031
1058
  ```bash
1032
- $ poetry run transcrypto --b64 -p rsa-key.pub rsa verify "eHl6" "91TS7gC6LORiL…6RD23Aejsfxlw=="
1059
+ $ poetry run transcrypto --b64 -p rsa-key.pub rsa verify -- eHl6 91TS7gC6LORiL…6RD23Aejsfxlw==
1033
1060
  RSA signature: OK
1034
- $ poetry run transcrypto --b64 -p rsa-key.pub rsa verify "eLl6" "91TS7gC6LORiL…6RD23Aejsfxlw=="
1061
+ $ poetry run transcrypto --b64 -p rsa-key.pub rsa verify -- eLl6 91TS7gC6LORiL…6RD23Aejsfxlw==
1035
1062
  RSA signature: INVALID
1036
1063
  ```
1037
1064
 
@@ -1154,7 +1181,7 @@ poetry run transcrypto elgamal decrypt [-h] [-a AAD] ciphertext
1154
1181
  **Example:**
1155
1182
 
1156
1183
  ```bash
1157
- $ poetry run transcrypto --b64 --out-bin -p eg-key.priv elgamal decrypt "CdFvoQ_IIPFPZLua…kqjhcUTspISxURg==" -a "eHl6"
1184
+ $ poetry run transcrypto --b64 --out-bin -p eg-key.priv elgamal decrypt -a eHl6 -- CdFvoQ_IIPFPZLua…kqjhcUTspISxURg==
1158
1185
  abcde
1159
1186
  ```
1160
1187
 
@@ -1236,9 +1263,9 @@ poetry run transcrypto elgamal verify [-h] [-a AAD] message signature
1236
1263
  **Example:**
1237
1264
 
1238
1265
  ```bash
1239
- $ poetry run transcrypto --b64 -p eg-key.pub elgamal verify "eHl6" "Xl4hlYK8SHVGw…0fCKJE1XVzA=="
1266
+ $ poetry run transcrypto --b64 -p eg-key.pub elgamal verify -- eHl6 Xl4hlYK8SHVGw…0fCKJE1XVzA==
1240
1267
  El-Gamal signature: OK
1241
- $ poetry run transcrypto --b64 -p eg-key.pub elgamal verify "eLl6" "Xl4hlYK8SHVGw…0fCKJE1XVzA=="
1268
+ $ poetry run transcrypto --b64 -p eg-key.pub elgamal verify -- eLl6 Xl4hlYK8SHVGw…0fCKJE1XVzA==
1242
1269
  El-Gamal signature: INVALID
1243
1270
  ```
1244
1271
 
@@ -1367,9 +1394,9 @@ poetry run transcrypto dsa verify [-h] [-a AAD] message signature
1367
1394
  **Example:**
1368
1395
 
1369
1396
  ```bash
1370
- $ poetry run transcrypto --b64 -p dsa-key.pub dsa verify "eHl6" "yq8InJVpViXh9…BD4par2XuA="
1397
+ $ poetry run transcrypto --b64 -p dsa-key.pub dsa verify -- eHl6 yq8InJVpViXh9…BD4par2XuA=
1371
1398
  DSA signature: OK
1372
- $ poetry run transcrypto --b64 -p dsa-key.pub dsa verify "eLl6" "yq8InJVpViXh9…BD4par2XuA="
1399
+ $ poetry run transcrypto --b64 -p dsa-key.pub dsa verify -- eLl6 yq8InJVpViXh9…BD4par2XuA=
1373
1400
  DSA signature: INVALID
1374
1401
  ```
1375
1402
 
@@ -1947,7 +1974,7 @@ for p in modmath.PrimeGenerator(1_000_000):
1947
1974
  break
1948
1975
 
1949
1976
  # Secure random 384-bit prime (for RSA/ECC experiments)
1950
- p384 = modmath.NBitRandomPrime(384)
1977
+ p384 = modmath.NBitRandomPrimes(384).pop()
1951
1978
 
1952
1979
  for k, m_p, perfect in modmath.MersennePrimesGenerator(0):
1953
1980
  print(f'p = {k:>8} M = {m_p} perfect = {perfect}')
@@ -2437,7 +2464,7 @@ To activate like a regular environment do:
2437
2464
  ```sh
2438
2465
  poetry env activate
2439
2466
  # will print activation command which you next execute, or you can do:
2440
- source .env/bin/activate # if .env is local to the project
2467
+ source .venv/bin/activate # if .venv is local to the project
2441
2468
  source "$(poetry env info --path)/bin/activate" # for other paths
2442
2469
 
2443
2470
  pytest # or other commands
@@ -2505,11 +2532,31 @@ poetry run transcrypto doc md > CLI.md
2505
2532
  You can find the 10 top slowest tests by running:
2506
2533
 
2507
2534
  ```sh
2508
- poetry run pytest -vvv -q --durations=10
2535
+ poetry run pytest -vvv -q --durations=30
2536
+
2537
+ poetry run pytest -vvv -q --durations=30 -m "not slow" # find slow > 0.1s
2538
+ poetry run pytest -vvv -q --durations=30 -m "not veryslow" # find veryslow > 1s
2539
+
2540
+ poetry run pytest -vvv -q --durations=30 -m slow # check
2541
+ poetry run pytest -vvv -q --durations=30 -m veryslow # check
2509
2542
  ```
2510
2543
 
2511
- You can search for flaky tests by running all tests 100 times:
2544
+ You can search for flaky tests by running all tests 100 times, or more:
2512
2545
 
2513
2546
  ```sh
2514
2547
  poetry run pytest --flake-finder --flake-runs=100
2548
+ poetry run pytest --flake-finder --flake-runs=500 -m "not veryslow"
2549
+ poetry run pytest --flake-finder --flake-runs=10000 -m "not slow"
2515
2550
  ```
2551
+
2552
+ You can instrument your code to find bottlenecks:
2553
+
2554
+ ```sh
2555
+ $ source .venv/bin/activate
2556
+ $ which transcrypto
2557
+ /path/to/.venv/bin/transcrypto # place this in the command below:
2558
+ $ pyinstrument -r html -o dsa_shared.html -- /path/to/.venv/bin/transcrypto -p rsa-key rsa new
2559
+ $ deactivate
2560
+ ```
2561
+
2562
+ Hint: 85%+ is inside `MillerRabinIsPrime()`/`ModExp()`...
@@ -0,0 +1,15 @@
1
+ transcrypto/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ transcrypto/aes.py,sha256=bCMz4-mXuDJl931kMztOCRdhagmRjguCovY5KFafX0c,11446
3
+ transcrypto/base.py,sha256=5TZr85BPdgApxNsa1KVc7ZVfnLnBujNdJyoWfkhyJdY,37582
4
+ transcrypto/dsa.py,sha256=wxpaOinxyeQfEsuc05WI8Go243NPQjz9-1Gv6VxRwRM,19706
5
+ transcrypto/elgamal.py,sha256=uR0bJ7A13UrAZynW6QRWJZhe86ZvoozaO5U9rDpam_I,21350
6
+ transcrypto/modmath.py,sha256=EEVQyRyWUl0pFXLhuUlYgLFD2Hl7b8dwPC5c_WMamrg,56400
7
+ transcrypto/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
+ transcrypto/rsa.py,sha256=3SYUEGOdHn_8UbezzocyQA8bkytL7d_d9rudsv4Zx9A,24926
9
+ transcrypto/sss.py,sha256=Jl7pa1fleNQ-1kvneaeO-B2PfM0ilNKTYw4vVJ5WGwc,17100
10
+ transcrypto/transcrypto.py,sha256=yv9gZN4rpxvGVXnkg1She05xI_h9JnQAV2nsi4cD-W0,78242
11
+ transcrypto-1.3.0.dist-info/licenses/LICENSE,sha256=DVQuDIgE45qn836wDaWnYhSdxoLXgpRRKH4RuTjpRZQ,10174
12
+ transcrypto-1.3.0.dist-info/METADATA,sha256=NtNwNQUWjEquUMUMbXkb_iGGLRBwmMM91tcTGOpoVVc,80426
13
+ transcrypto-1.3.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
14
+ transcrypto-1.3.0.dist-info/top_level.txt,sha256=9IfB0nGtVzQbYok5QIYNOy3coDv2UKX2OZtlFyxFDDQ,12
15
+ transcrypto-1.3.0.dist-info/RECORD,,
@@ -1,15 +0,0 @@
1
- transcrypto/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- transcrypto/aes.py,sha256=bCMz4-mXuDJl931kMztOCRdhagmRjguCovY5KFafX0c,11446
3
- transcrypto/base.py,sha256=NH3TR7RGr1yaWv2bP0ZYk3lg8utZkLVIjfI0TM-nV1A,37582
4
- transcrypto/dsa.py,sha256=QBWivto9qxK3ArDTltdJAJPUhKDT8YU2QLyI23Zm0Dg,17524
5
- transcrypto/elgamal.py,sha256=VE6i7TD6fWND2ZDYAvrrYwbp0gMZ-d7rCjeFI7-9zZs,20886
6
- transcrypto/modmath.py,sha256=FL9rwOOuzNzFSf7ScMVPwb98sKPzhyA2Cu6uyqLz65M,18616
7
- transcrypto/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
- transcrypto/rsa.py,sha256=E01NtWUw03kqKy5FeFnhOQxFMUWlt998JHLSJagW1Gk,25003
9
- transcrypto/sss.py,sha256=-SBsHNCyAPvQPDprjILqjVP6GbArWRxi8D6pBBvk0yo,17152
10
- transcrypto/transcrypto.py,sha256=55mKAHmk6OexgMU38MuB_E9EM6O470icbJC3hAMXixg,76935
11
- transcrypto-1.2.0.dist-info/licenses/LICENSE,sha256=DVQuDIgE45qn836wDaWnYhSdxoLXgpRRKH4RuTjpRZQ,10174
12
- transcrypto-1.2.0.dist-info/METADATA,sha256=oRLeX2jwv2s2JIYFuf3Zvrv_fG6fCJDqoJgPXqENqEk,78084
13
- transcrypto-1.2.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
14
- transcrypto-1.2.0.dist-info/top_level.txt,sha256=9IfB0nGtVzQbYok5QIYNOy3coDv2UKX2OZtlFyxFDDQ,12
15
- transcrypto-1.2.0.dist-info/RECORD,,