transcrypto 1.7.0__py3-none-any.whl → 2.0.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.
Files changed (34) hide show
  1. transcrypto/__init__.py +1 -1
  2. transcrypto/cli/__init__.py +3 -0
  3. transcrypto/cli/aeshash.py +370 -0
  4. transcrypto/cli/bidsecret.py +336 -0
  5. transcrypto/cli/clibase.py +183 -0
  6. transcrypto/cli/intmath.py +429 -0
  7. transcrypto/cli/publicalgos.py +878 -0
  8. transcrypto/core/__init__.py +3 -0
  9. transcrypto/{aes.py → core/aes.py} +17 -29
  10. transcrypto/core/bid.py +161 -0
  11. transcrypto/{dsa.py → core/dsa.py} +28 -27
  12. transcrypto/{elgamal.py → core/elgamal.py} +33 -32
  13. transcrypto/core/hashes.py +96 -0
  14. transcrypto/core/key.py +735 -0
  15. transcrypto/{modmath.py → core/modmath.py} +91 -17
  16. transcrypto/{rsa.py → core/rsa.py} +51 -50
  17. transcrypto/{sss.py → core/sss.py} +27 -26
  18. transcrypto/profiler.py +29 -13
  19. transcrypto/transcrypto.py +60 -1996
  20. transcrypto/utils/__init__.py +3 -0
  21. transcrypto/utils/base.py +72 -0
  22. transcrypto/utils/human.py +278 -0
  23. transcrypto/utils/logging.py +139 -0
  24. transcrypto/utils/saferandom.py +102 -0
  25. transcrypto/utils/stats.py +360 -0
  26. transcrypto/utils/timer.py +175 -0
  27. {transcrypto-1.7.0.dist-info → transcrypto-2.0.0.dist-info}/METADATA +111 -109
  28. transcrypto-2.0.0.dist-info/RECORD +33 -0
  29. transcrypto/base.py +0 -1918
  30. transcrypto-1.7.0.dist-info/RECORD +0 -17
  31. /transcrypto/{constants.py → core/constants.py} +0 -0
  32. {transcrypto-1.7.0.dist-info → transcrypto-2.0.0.dist-info}/WHEEL +0 -0
  33. {transcrypto-1.7.0.dist-info → transcrypto-2.0.0.dist-info}/entry_points.txt +0 -0
  34. {transcrypto-1.7.0.dist-info → transcrypto-2.0.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,3 @@
1
+ # SPDX-FileCopyrightText: Copyright 2026 Daniel Balparda <balparda@github.com>
2
+ # SPDX-License-Identifier: Apache-2.0
3
+ """Core crypto logic."""
@@ -25,7 +25,8 @@ from cryptography.hazmat.primitives import hashes as hazmat_hashes
25
25
  from cryptography.hazmat.primitives.ciphers import algorithms, modes
26
26
  from cryptography.hazmat.primitives.kdf import pbkdf2 as hazmat_pbkdf2
27
27
 
28
- from . import base
28
+ from transcrypto.core import hashes, key
29
+ from transcrypto.utils import base, saferandom
29
30
 
30
31
  # these fixed salt/iterations are for password->key generation only; NEVER use them to
31
32
  # build a database of passwords because it would not be safe; NEVER change them or the
@@ -41,7 +42,7 @@ assert _PASSWORD_ITERATIONS == (6075308 + 1) // 3, 'should never happen: constan
41
42
 
42
43
 
43
44
  @dataclasses.dataclass(kw_only=True, slots=True, frozen=True, repr=False)
44
- class AESKey(base.CryptoKey, base.Encryptor, base.Decryptor):
45
+ class AESKey(key.CryptoKey, key.Encryptor, key.Decryptor):
45
46
  """Advanced Encryption Standard (AES) 256 bits key (32 bytes).
46
47
 
47
48
  No measures are taken here to prevent timing attacks.
@@ -57,7 +58,7 @@ class AESKey(base.CryptoKey, base.Encryptor, base.Decryptor):
57
58
  """Check data.
58
59
 
59
60
  Raises:
60
- InputError: invalid inputs
61
+ base.InputError: invalid inputs
61
62
 
62
63
  """
63
64
  if len(self.key256) != 32: # noqa: PLR2004
@@ -70,7 +71,7 @@ class AESKey(base.CryptoKey, base.Encryptor, base.Decryptor):
70
71
  string representation of AESKey without leaking secrets
71
72
 
72
73
  """
73
- return f'AESKey(key256={base.ObfuscateSecret(self.key256)})'
74
+ return f'AESKey(key256={hashes.ObfuscateSecret(self.key256)})'
74
75
 
75
76
  @classmethod
76
77
  def FromStaticPassword(cls, str_password: str, /) -> Self:
@@ -98,7 +99,7 @@ class AESKey(base.CryptoKey, base.Encryptor, base.Decryptor):
98
99
  AESKey crypto key to use (URL-safe base64-encoded 32-byte key)
99
100
 
100
101
  Raises:
101
- InputError: empty password
102
+ base.InputError: empty password
102
103
 
103
104
  """
104
105
  str_password = str_password.strip()
@@ -112,7 +113,7 @@ class AESKey(base.CryptoKey, base.Encryptor, base.Decryptor):
112
113
  )
113
114
  return cls(key256=kdf.derive(str_password.encode('utf-8')))
114
115
 
115
- class ECBEncoderClass(base.Encryptor, base.Decryptor):
116
+ class ECBEncoderClass(key.Encryptor, key.Decryptor):
116
117
  """The simplest encryption possible (UNSAFE if misused): 128 bit block AES-ECB, 256 bit key.
117
118
 
118
119
  Note: Due to ECB encoding, this class is only safe-ish for blocks of random-looking data,
@@ -162,7 +163,7 @@ class AESKey(base.CryptoKey, base.Encryptor, base.Decryptor):
162
163
  bytes: Ciphertext, a block of 128 bits (16 bytes)
163
164
 
164
165
  Raises:
165
- InputError: invalid inputs
166
+ base.InputError: invalid inputs
166
167
 
167
168
  """
168
169
  if associated_data is not None:
@@ -189,7 +190,7 @@ class AESKey(base.CryptoKey, base.Encryptor, base.Decryptor):
189
190
  bytes: Decrypted plaintext, a block of 128 bits (16 bytes)
190
191
 
191
192
  Raises:
192
- InputError: invalid inputs
193
+ base.InputError: invalid inputs
193
194
 
194
195
  """
195
196
  if associated_data is not None:
@@ -227,7 +228,7 @@ class AESKey(base.CryptoKey, base.Encryptor, base.Decryptor):
227
228
  str: encrypted hexadecimal block (length==64)
228
229
 
229
230
  Raises:
230
- InputError: invalid inputs
231
+ base.InputError: invalid inputs
231
232
 
232
233
  """
233
234
  if len(plaintext_hex) != 64: # noqa: PLR2004
@@ -262,7 +263,7 @@ class AESKey(base.CryptoKey, base.Encryptor, base.Decryptor):
262
263
  str: plaintext hexadecimal block (length==64)
263
264
 
264
265
  Raises:
265
- InputError: invalid inputs
266
+ base.InputError: invalid inputs
266
267
 
267
268
  """
268
269
  if len(ciphertext_hex) != 64: # noqa: PLR2004
@@ -297,7 +298,7 @@ class AESKey(base.CryptoKey, base.Encryptor, base.Decryptor):
297
298
  must encode it within the returned bytes (or document how to retrieve it)
298
299
 
299
300
  """
300
- iv: bytes = base.RandBytes(16)
301
+ iv: bytes = saferandom.RandBytes(16)
301
302
  cipher: ciphers.Cipher[modes.GCM] = ciphers.Cipher(
302
303
  algorithms.AES256(self.key256), modes.GCM(iv)
303
304
  )
@@ -339,12 +340,14 @@ class AESKey(base.CryptoKey, base.Encryptor, base.Decryptor):
339
340
  bytes: Decrypted plaintext bytes
340
341
 
341
342
  Raises:
342
- InputError: invalid inputs
343
- CryptoError: internal crypto failures, authentication failure, key mismatch, etc
343
+ base.InputError: invalid inputs
344
+ key.CryptoError: internal crypto failures, authentication failure, key mismatch, etc
344
345
 
345
346
  """
346
347
  if len(ciphertext) < 32: # noqa: PLR2004
347
348
  raise base.InputError(f'AES256+GCM should have ≥32 bytes IV/CT/tag: {len(ciphertext)}')
349
+ iv: bytes
350
+ tag: bytes
348
351
  iv, tag = ciphertext[:16], ciphertext[-16:]
349
352
  decryptor: ciphers.CipherContext = ciphers.Cipher(
350
353
  algorithms.AES256(self.key256), modes.GCM(iv, tag)
@@ -354,19 +357,4 @@ class AESKey(base.CryptoKey, base.Encryptor, base.Decryptor):
354
357
  try:
355
358
  return decryptor.update(ciphertext[16:-16]) + decryptor.finalize()
356
359
  except crypt_exceptions.InvalidTag as err:
357
- raise base.CryptoError('failed decryption') from err
358
-
359
-
360
- def _TestCryptoKeyEncoding(obj: base.CryptoKey, tp: type[base.CryptoKey]) -> None: # pyright: ignore[reportUnusedFunction]
361
- """Test encoding for a CryptoKey instance. Only for use from test modules."""
362
- assert tp.FromJSON(obj.json) == obj # noqa: S101
363
- assert tp.FromJSON(obj.formatted_json) == obj # noqa: S101
364
- assert tp.Load(obj.blob) == obj # noqa: S101
365
- assert tp.Load(obj.encoded) == obj # noqa: S101
366
- assert tp.Load(obj.hex) == obj # noqa: S101
367
- assert tp.Load(obj.raw) == obj # noqa: S101
368
- key = AESKey(key256=b'x' * 32)
369
- assert tp.Load(obj.Blob(key=key), key=key) == obj # noqa: S101
370
- assert tp.Load(obj.Encoded(key=key), key=key) == obj # noqa: S101
371
- assert tp.Load(obj.Hex(key=key), key=key) == obj # noqa: S101
372
- assert tp.Load(obj.Raw(key=key), key=key) == obj # noqa: S101
360
+ raise key.CryptoError('failed decryption') from err
@@ -0,0 +1,161 @@
1
+ # SPDX-FileCopyrightText: Copyright 2026 Daniel Balparda <balparda@github.com>
2
+ # SPDX-License-Identifier: Apache-2.0
3
+ """Balparda's TransCrypto bidding protocols."""
4
+
5
+ from __future__ import annotations
6
+
7
+ import dataclasses
8
+ from typing import Self
9
+
10
+ from transcrypto.core import hashes, key
11
+ from transcrypto.utils import base, saferandom
12
+
13
+
14
+ @dataclasses.dataclass(kw_only=True, slots=True, frozen=True, repr=False)
15
+ class PublicBid512(key.CryptoKey):
16
+ """Public commitment to a (cryptographically secure) bid that can be revealed/validated later.
17
+
18
+ Bid is computed as: public_hash = Hash512(public_key || private_key || secret_bid)
19
+
20
+ Everything is bytes. The public part is (public_key, public_hash) and the private
21
+ part is (private_key, secret_bid). The whole computation can be checked later.
22
+
23
+ No measures are taken here to prevent timing attacks (probably not a concern).
24
+
25
+ Attributes:
26
+ public_key (bytes): 512-bits random value
27
+ public_hash (bytes): SHA-512 hash of (public_key || private_key || secret_bid)
28
+
29
+ """
30
+
31
+ public_key: bytes
32
+ public_hash: bytes
33
+
34
+ def __post_init__(self) -> None:
35
+ """Check data.
36
+
37
+ Raises:
38
+ base.InputError: invalid inputs
39
+
40
+ """
41
+ if len(self.public_key) != 64 or len(self.public_hash) != 64: # noqa: PLR2004
42
+ raise base.InputError(f'invalid public_key or public_hash: {self}')
43
+
44
+ def __str__(self) -> str:
45
+ """Safe string representation of the PublicBid.
46
+
47
+ Returns:
48
+ string representation of PublicBid
49
+
50
+ """
51
+ return (
52
+ 'PublicBid512('
53
+ f'public_key={base.BytesToEncoded(self.public_key)}, '
54
+ f'public_hash={base.BytesToHex(self.public_hash)})'
55
+ )
56
+
57
+ def VerifyBid(self, private_key: bytes, secret: bytes, /) -> bool:
58
+ """Verify a bid. True if OK; False if failed verification.
59
+
60
+ Args:
61
+ private_key (bytes): 512-bits private key
62
+ secret (bytes): Any number of bytes (≥1) to bid on (e.g., UTF-8 encoded string)
63
+
64
+ Returns:
65
+ True if bid is valid, False otherwise
66
+
67
+ """
68
+ try:
69
+ # creating the PrivateBid object will validate everything; InputError we allow to propagate
70
+ PrivateBid512(
71
+ public_key=self.public_key,
72
+ public_hash=self.public_hash,
73
+ private_key=private_key,
74
+ secret_bid=secret,
75
+ )
76
+ return True # if we got here, all is good
77
+ except key.CryptoError:
78
+ return False # bid does not match the public commitment
79
+
80
+ @classmethod
81
+ def Copy(cls, other: PublicBid512, /) -> Self:
82
+ """Initialize a public bid by taking the public parts of a public/private bid.
83
+
84
+ Args:
85
+ other (PublicBid512): the bid to copy from
86
+
87
+ Returns:
88
+ Self: an initialized PublicBid512
89
+
90
+ """
91
+ return cls(public_key=other.public_key, public_hash=other.public_hash)
92
+
93
+
94
+ @dataclasses.dataclass(kw_only=True, slots=True, frozen=True, repr=False)
95
+ class PrivateBid512(PublicBid512):
96
+ """Private bid that can be revealed and validated against a public commitment (see PublicBid).
97
+
98
+ Attributes:
99
+ private_key (bytes): 512-bits random value
100
+ secret_bid (bytes): Any number of bytes (≥1) to bid on (e.g., UTF-8 encoded string)
101
+
102
+ """
103
+
104
+ private_key: bytes
105
+ secret_bid: bytes
106
+
107
+ def __post_init__(self) -> None:
108
+ """Check data.
109
+
110
+ Raises:
111
+ base.InputError: invalid inputs
112
+ key.CryptoError: bid does not match the public commitment
113
+
114
+ """
115
+ super(PrivateBid512, self).__post_init__()
116
+ if len(self.private_key) != 64 or len(self.secret_bid) < 1: # noqa: PLR2004
117
+ raise base.InputError(f'invalid private_key or secret_bid: {self}')
118
+ if self.public_hash != hashes.Hash512(self.public_key + self.private_key + self.secret_bid):
119
+ raise key.CryptoError(f'inconsistent bid: {self}')
120
+
121
+ def __str__(self) -> str:
122
+ """Safe (no secrets) string representation of the PrivateBid.
123
+
124
+ Returns:
125
+ string representation of PrivateBid without leaking secrets
126
+
127
+ """
128
+ return (
129
+ 'PrivateBid512('
130
+ f'{super(PrivateBid512, self).__str__()}, '
131
+ f'private_key={hashes.ObfuscateSecret(self.private_key)}, '
132
+ f'secret_bid={hashes.ObfuscateSecret(self.secret_bid)})'
133
+ )
134
+
135
+ @classmethod
136
+ def New(cls, secret: bytes, /) -> Self:
137
+ """Make the `secret` into a new bid.
138
+
139
+ Args:
140
+ secret (bytes): Any number of bytes (≥1) to bid on (e.g., UTF-8 encoded string)
141
+
142
+ Returns:
143
+ PrivateBid object ready for use (use PublicBid.Copy() to get the public part)
144
+
145
+ Raises:
146
+ base.InputError: invalid inputs
147
+
148
+ """
149
+ # test inputs
150
+ if len(secret) < 1:
151
+ raise base.InputError(f'invalid secret length: {len(secret)}')
152
+ # generate random values
153
+ public_key: bytes = saferandom.RandBytes(64) # 512 bits
154
+ private_key: bytes = saferandom.RandBytes(64) # 512 bits
155
+ # build object
156
+ return cls(
157
+ public_key=public_key,
158
+ public_hash=hashes.Hash512(public_key + private_key + secret),
159
+ private_key=private_key,
160
+ secret_bid=secret,
161
+ )
@@ -19,7 +19,8 @@ from typing import Self
19
19
 
20
20
  import gmpy2
21
21
 
22
- from . import base, constants, modmath
22
+ from transcrypto.core import constants, hashes, key, modmath
23
+ from transcrypto.utils import base, saferandom
23
24
 
24
25
  _MAX_KEY_GENERATION_FAILURES = 15
25
26
 
@@ -68,8 +69,8 @@ def NBitRandomDSAPrimes(
68
69
  that p % q == 1 and m == (p - 1) // q
69
70
 
70
71
  Raises:
71
- InputError: invalid inputs
72
- Error: prime search failed
72
+ base.InputError: invalid inputs
73
+ base.Error: prime search failed
73
74
 
74
75
  """
75
76
  # test inputs
@@ -140,7 +141,7 @@ def _PrimePSearchShard(q: int, p_bits: int) -> tuple[int | None, int | None]:
140
141
  return all(m % r != f for r, f in forbidden.items())
141
142
 
142
143
  # try searching starting here
143
- m: int = base.RandInt(min_m, max_m)
144
+ m: int = saferandom.RandInt(min_m, max_m)
144
145
  if m % 2:
145
146
  m += 1 # make even
146
147
  count: int = 0
@@ -158,7 +159,7 @@ def _PrimePSearchShard(q: int, p_bits: int) -> tuple[int | None, int | None]:
158
159
 
159
160
 
160
161
  @dataclasses.dataclass(kw_only=True, slots=True, frozen=True, repr=False)
161
- class DSASharedPublicKey(base.CryptoKey):
162
+ class DSASharedPublicKey(key.CryptoKey):
162
163
  """DSA shared public key. This key can be shared by a group.
163
164
 
164
165
  No measures are taken here to prevent timing attacks.
@@ -178,7 +179,7 @@ class DSASharedPublicKey(base.CryptoKey):
178
179
  """Check data.
179
180
 
180
181
  Raises:
181
- InputError: invalid inputs
182
+ base.InputError: invalid inputs
182
183
 
183
184
  """
184
185
  if self.prime_seed < 7 or not modmath.IsPrime(self.prime_seed): # noqa: PLR2004
@@ -227,16 +228,16 @@ class DSASharedPublicKey(base.CryptoKey):
227
228
  Hash512("prefix" || len(aad) || aad || message || salt)
228
229
 
229
230
  Raises:
230
- CryptoError: hash output is out of range
231
+ key.CryptoError: hash output is out of range
231
232
 
232
233
  """
233
234
  aad: bytes = b'' if associated_data is None else associated_data
234
235
  la: bytes = base.IntToFixedBytes(len(aad), 8)
235
236
  assert len(salt) == 64, 'should never happen: salt should be exactly 64 bytes' # noqa: PLR2004, S101
236
- y: int = base.BytesToInt(base.Hash512(_DSA_SIGNATURE_HASH_PREFIX + la + aad + message + salt))
237
+ y: int = base.BytesToInt(hashes.Hash512(_DSA_SIGNATURE_HASH_PREFIX + la + aad + message + salt))
237
238
  if not 1 < y < self.prime_seed - 1:
238
239
  # will only reasonably happen if prime seed is small
239
- raise base.CryptoError(f'hash output {y} is out of range/invalid {self.prime_seed}')
240
+ raise key.CryptoError(f'hash output {y} is out of range/invalid {self.prime_seed}')
240
241
  return y
241
242
 
242
243
  @classmethod
@@ -257,13 +258,13 @@ class DSASharedPublicKey(base.CryptoKey):
257
258
  # generate random number, create object (should never fail)
258
259
  g: int = 0
259
260
  while g < 3: # noqa: PLR2004
260
- h: int = base.RandBits(p_bits - 1)
261
+ h: int = saferandom.RandBits(p_bits - 1)
261
262
  g = int(gmpy2.powmod(h, m, p))
262
263
  return cls(prime_modulus=p, prime_seed=q, group_base=g)
263
264
 
264
265
 
265
266
  @dataclasses.dataclass(kw_only=True, slots=True, frozen=True, repr=False)
266
- class DSAPublicKey(DSASharedPublicKey, base.Verifier):
267
+ class DSAPublicKey(DSASharedPublicKey, key.Verifier):
267
268
  """DSA public key. This is an individual public key.
268
269
 
269
270
  No measures are taken here to prevent timing attacks.
@@ -279,7 +280,7 @@ class DSAPublicKey(DSASharedPublicKey, base.Verifier):
279
280
  """Check data.
280
281
 
281
282
  Raises:
282
- InputError: invalid inputs
283
+ base.InputError: invalid inputs
283
284
 
284
285
  """
285
286
  super(DSAPublicKey, self).__post_init__()
@@ -315,7 +316,7 @@ class DSAPublicKey(DSASharedPublicKey, base.Verifier):
315
316
  self.group_base,
316
317
  self.individual_base,
317
318
  }:
318
- ephemeral_key = base.RandBits(bit_length - 1)
319
+ ephemeral_key = saferandom.RandBits(bit_length - 1)
319
320
  return (ephemeral_key, modmath.ModInv(ephemeral_key, self.prime_seed))
320
321
 
321
322
  def RawVerify(self, message: int, signature: tuple[int, int], /) -> bool:
@@ -333,7 +334,7 @@ class DSAPublicKey(DSASharedPublicKey, base.Verifier):
333
334
  True if signature is valid, False otherwise
334
335
 
335
336
  Raises:
336
- InputError: invalid inputs
337
+ base.InputError: invalid inputs
337
338
 
338
339
  """
339
340
  # test inputs
@@ -371,7 +372,7 @@ class DSAPublicKey(DSASharedPublicKey, base.Verifier):
371
372
  True if signature is valid, False otherwise
372
373
 
373
374
  Raises:
374
- InputError: invalid inputs
375
+ base.InputError: invalid inputs
375
376
 
376
377
  """
377
378
  k: int = self.modulus_size[1] # use prime_seed size
@@ -409,7 +410,7 @@ class DSAPublicKey(DSASharedPublicKey, base.Verifier):
409
410
 
410
411
 
411
412
  @dataclasses.dataclass(kw_only=True, slots=True, frozen=True, repr=False)
412
- class DSAPrivateKey(DSAPublicKey, base.Signer):
413
+ class DSAPrivateKey(DSAPublicKey, key.Signer):
413
414
  """DSA private key.
414
415
 
415
416
  No measures are taken here to prevent timing attacks.
@@ -425,8 +426,8 @@ class DSAPrivateKey(DSAPublicKey, base.Signer):
425
426
  """Check data.
426
427
 
427
428
  Raises:
428
- InputError: invalid inputs
429
- CryptoError: modulus math is inconsistent with values
429
+ base.InputError: invalid inputs
430
+ key.CryptoError: modulus math is inconsistent with values
430
431
 
431
432
  """
432
433
  super(DSAPrivateKey, self).__post_init__()
@@ -436,7 +437,7 @@ class DSAPrivateKey(DSAPublicKey, base.Signer):
436
437
  }:
437
438
  raise base.InputError(f'invalid decrypt_exp: {self}')
438
439
  if gmpy2.powmod(self.group_base, self.decrypt_exp, self.prime_modulus) != self.individual_base:
439
- raise base.CryptoError(f'inconsistent g**d % p == i: {self}')
440
+ raise key.CryptoError(f'inconsistent g**d % p == i: {self}')
440
441
 
441
442
  def __str__(self) -> str:
442
443
  """Safe (no secrets) string representation of the DSAPrivateKey.
@@ -448,7 +449,7 @@ class DSAPrivateKey(DSAPublicKey, base.Signer):
448
449
  return (
449
450
  'DSAPrivateKey('
450
451
  f'{super(DSAPrivateKey, self).__str__()}, '
451
- f'decrypt_exp={base.ObfuscateSecret(self.decrypt_exp)})'
452
+ f'decrypt_exp={hashes.ObfuscateSecret(self.decrypt_exp)})'
452
453
  )
453
454
 
454
455
  def RawSign(self, message: int, /) -> tuple[int, int]:
@@ -465,7 +466,7 @@ class DSAPrivateKey(DSAPublicKey, base.Signer):
465
466
  signed message tuple ((int, int), 2 ≤ s1,s2 < prime_seed
466
467
 
467
468
  Raises:
468
- InputError: invalid inputs
469
+ base.InputError: invalid inputs
469
470
 
470
471
  """
471
472
  # test inputs
@@ -502,13 +503,13 @@ class DSAPrivateKey(DSAPublicKey, base.Signer):
502
503
  bytes: Signature; salt || Padded(s, k) - see above
503
504
 
504
505
  Raises:
505
- InputError: invalid inputs
506
+ base.InputError: invalid inputs
506
507
 
507
508
  """
508
509
  k: int = self.modulus_size[1] # use prime_seed size
509
510
  if k <= 64: # noqa: PLR2004
510
511
  raise base.InputError(f'modulus/seed too small for signing operations: {k} bytes')
511
- salt: bytes = base.RandBytes(64)
512
+ salt: bytes = saferandom.RandBytes(64)
512
513
  s_int: tuple[int, int] = self.RawSign(self._DomainSeparatedHash(message, associated_data, salt))
513
514
  s_bytes: bytes = base.IntToFixedBytes(s_int[0], k) + base.IntToFixedBytes(s_int[1], k)
514
515
  assert len(s_bytes) == 2 * k, 'should never happen: s_bytes should be exactly 2k bytes' # noqa: S101
@@ -525,8 +526,8 @@ class DSAPrivateKey(DSAPublicKey, base.Signer):
525
526
  DSAPrivateKey object ready for use
526
527
 
527
528
  Raises:
528
- InputError: invalid inputs
529
- CryptoError: failed generation
529
+ base.InputError: invalid inputs
530
+ key.CryptoError: failed generation
530
531
 
531
532
  """
532
533
  # test inputs
@@ -542,7 +543,7 @@ class DSAPrivateKey(DSAPublicKey, base.Signer):
542
543
  while (
543
544
  not 2 < decrypt_exp < shared_key.prime_seed or decrypt_exp == shared_key.group_base # noqa: PLR2004
544
545
  ):
545
- decrypt_exp = base.RandBits(bit_length - 1)
546
+ decrypt_exp = saferandom.RandBits(bit_length - 1)
546
547
  # make the object
547
548
  return cls(
548
549
  prime_modulus=shared_key.prime_modulus,
@@ -556,5 +557,5 @@ class DSAPrivateKey(DSAPublicKey, base.Signer):
556
557
  except base.InputError as err:
557
558
  failures += 1
558
559
  if failures >= _MAX_KEY_GENERATION_FAILURES:
559
- raise base.CryptoError(f'failed key generation {failures} times') from err
560
+ raise key.CryptoError(f'failed key generation {failures} times') from err
560
561
  logging.warning(err)