codekingpro/portable-devtools
114k
1# This file is dual licensed under the terms of the Apache License, Version2# 2.0, and the BSD License. See the LICENSE file in the root of this repository3# for complete details.4 5from __future__ import annotations6 7import abc8import random9import typing10from math import gcd, lcm11 12from cryptography.hazmat.bindings._rust import openssl as rust_openssl13from cryptography.hazmat.primitives import _serialization, hashes14from cryptography.hazmat.primitives._asymmetric import AsymmetricPadding15from cryptography.hazmat.primitives.asymmetric import utils as asym_utils16 17 18class RSAPrivateKey(metaclass=abc.ABCMeta):19 @abc.abstractmethod20 def decrypt(self, ciphertext: bytes, padding: AsymmetricPadding) -> bytes:21 """22 Decrypts the provided ciphertext.23 """24 25 @property26 @abc.abstractmethod27 def key_size(self) -> int:28 """29 The bit length of the public modulus.30 """31 32 @abc.abstractmethod33 def public_key(self) -> RSAPublicKey:34 """35 The RSAPublicKey associated with this private key.36 """37 38 @abc.abstractmethod39 def sign(40 self,41 data: bytes,42 padding: AsymmetricPadding,43 algorithm: asym_utils.Prehashed44 | hashes.HashAlgorithm45 | asym_utils.NoDigestInfo,46 ) -> bytes:47 """48 Signs the data.49 """50 51 @abc.abstractmethod52 def private_numbers(self) -> RSAPrivateNumbers:53 """54 Returns an RSAPrivateNumbers.55 """56 57 @abc.abstractmethod58 def private_bytes(59 self,60 encoding: _serialization.Encoding,61 format: _serialization.PrivateFormat,62 encryption_algorithm: _serialization.KeySerializationEncryption,63 ) -> bytes:64 """65 Returns the key serialized as bytes.66 """67 68 @abc.abstractmethod69 def __copy__(self) -> RSAPrivateKey:70 """71 Returns a copy.72 """73 74 @abc.abstractmethod75 def __deepcopy__(self, memo: dict) -> RSAPrivateKey:76 """77 Returns a deep copy.78 """79 80 81RSAPrivateKeyWithSerialization = RSAPrivateKey82RSAPrivateKey.register(rust_openssl.rsa.RSAPrivateKey)83 84 85class RSAPublicKey(metaclass=abc.ABCMeta):86 @abc.abstractmethod87 def encrypt(self, plaintext: bytes, padding: AsymmetricPadding) -> bytes:88 """89 Encrypts the given plaintext.90 """91 92 @property93 @abc.abstractmethod94 def key_size(self) -> int:95 """96 The bit length of the public modulus.97 """98 99 @abc.abstractmethod100 def public_numbers(self) -> RSAPublicNumbers:101 """102 Returns an RSAPublicNumbers103 """104 105 @abc.abstractmethod106 def public_bytes(107 self,108 encoding: _serialization.Encoding,109 format: _serialization.PublicFormat,110 ) -> bytes:111 """112 Returns the key serialized as bytes.113 """114 115 @abc.abstractmethod116 def verify(117 self,118 signature: bytes,119 data: bytes,120 padding: AsymmetricPadding,121 algorithm: asym_utils.Prehashed | hashes.HashAlgorithm,122 ) -> None:123 """124 Verifies the signature of the data.125 """126 127 @abc.abstractmethod128 def recover_data_from_signature(129 self,130 signature: bytes,131 padding: AsymmetricPadding,132 algorithm: hashes.HashAlgorithm | asym_utils.NoDigestInfo | None,133 ) -> bytes:134 """135 Recovers the original data from the signature.136 """137 138 @abc.abstractmethod139 def __eq__(self, other: object) -> bool:140 """141 Checks equality.142 """143 144 @abc.abstractmethod145 def __copy__(self) -> RSAPublicKey:146 """147 Returns a copy.148 """149 150 @abc.abstractmethod151 def __deepcopy__(self, memo: dict) -> RSAPublicKey:152 """153 Returns a deep copy.154 """155 156 157RSAPublicKeyWithSerialization = RSAPublicKey158RSAPublicKey.register(rust_openssl.rsa.RSAPublicKey)159 160RSAPrivateNumbers = rust_openssl.rsa.RSAPrivateNumbers161RSAPublicNumbers = rust_openssl.rsa.RSAPublicNumbers162 163 164def generate_private_key(165 public_exponent: int,166 key_size: int,167 backend: typing.Any = None,168) -> RSAPrivateKey:169 _verify_rsa_parameters(public_exponent, key_size)170 return rust_openssl.rsa.generate_private_key(public_exponent, key_size)171 172 173def _verify_rsa_parameters(public_exponent: int, key_size: int) -> None:174 if public_exponent not in (3, 65537):175 raise ValueError(176 "public_exponent must be either 3 (for legacy compatibility) or "177 "65537. Almost everyone should choose 65537 here!"178 )179 180 if key_size < 1024:181 raise ValueError("key_size must be at least 1024-bits.")182 183 184def _modinv(e: int, m: int) -> int:185 """186 Modular Multiplicative Inverse. Returns x such that: (x*e) mod m == 1187 """188 x1, x2 = 1, 0189 a, b = e, m190 while b > 0:191 q, r = divmod(a, b)192 xn = x1 - q * x2193 a, b, x1, x2 = b, r, x2, xn194 return x1 % m195 196 197def rsa_crt_iqmp(p: int, q: int) -> int:198 """199 Compute the CRT (q ** -1) % p value from RSA primes p and q.200 """201 if p <= 1 or q <= 1:202 raise ValueError("Values can't be <= 1")203 return _modinv(q, p)204 205 206def rsa_crt_dmp1(private_exponent: int, p: int) -> int:207 """208 Compute the CRT private_exponent % (p - 1) value from the RSA209 private_exponent (d) and p.210 """211 if private_exponent <= 1 or p <= 1:212 raise ValueError("Values can't be <= 1")213 return private_exponent % (p - 1)214 215 216def rsa_crt_dmq1(private_exponent: int, q: int) -> int:217 """218 Compute the CRT private_exponent % (q - 1) value from the RSA219 private_exponent (d) and q.220 """221 if private_exponent <= 1 or q <= 1:222 raise ValueError("Values can't be <= 1")223 return private_exponent % (q - 1)224 225 226def rsa_recover_private_exponent(e: int, p: int, q: int) -> int:227 """228 Compute the RSA private_exponent (d) given the public exponent (e)229 and the RSA primes p and q.230 231 This uses the Carmichael totient function to generate the232 smallest possible working value of the private exponent.233 """234 # This lambda_n is the Carmichael totient function.235 # The original RSA paper uses the Euler totient function236 # here: phi_n = (p - 1) * (q - 1)237 # Either version of the private exponent will work, but the238 # one generated by the older formulation may be larger239 # than necessary. (lambda_n always divides phi_n)240 if e <= 1 or p <= 1 or q <= 1:241 raise ValueError("Values can't be <= 1")242 return _modinv(e, lcm(p - 1, q - 1))243 244 245# Controls the number of iterations rsa_recover_prime_factors will perform246# to obtain the prime factors.247_MAX_RECOVERY_ATTEMPTS = 500248 249 250def rsa_recover_prime_factors(n: int, e: int, d: int) -> tuple[int, int]:251 """252 Compute factors p and q from the private exponent d. We assume that n has253 no more than two factors. This function is adapted from code in PyCrypto.254 """255 # reject invalid values early256 if d <= 1 or e <= 1:257 raise ValueError("d, e can't be <= 1")258 if 17 != pow(17, e * d, n):259 raise ValueError("n, d, e don't match")260 # See 8.2.2(i) in Handbook of Applied Cryptography.261 ktot = d * e - 1262 # The quantity d*e-1 is a multiple of phi(n), even,263 # and can be represented as t*2^s.264 t = ktot265 while t % 2 == 0:266 t = t // 2267 # Cycle through all multiplicative inverses in Zn.268 # The algorithm is non-deterministic, but there is a 50% chance269 # any candidate a leads to successful factoring.270 # See "Digitalized Signatures and Public Key Functions as Intractable271 # as Factorization", M. Rabin, 1979272 spotted = False273 tries = 0274 while not spotted and tries < _MAX_RECOVERY_ATTEMPTS:275 a = random.randint(2, n - 1)276 tries += 1277 k = t278 # Cycle through all values a^{t*2^i}=a^k279 while k < ktot:280 cand = pow(a, k, n)281 # Check if a^k is a non-trivial root of unity (mod n)282 if cand != 1 and cand != (n - 1) and pow(cand, 2, n) == 1:283 # We have found a number such that (cand-1)(cand+1)=0 (mod n).284 # Either of the terms divides n.285 p = gcd(cand + 1, n)286 spotted = True287 break288 k *= 2289 if not spotted:290 raise ValueError("Unable to compute factors p and q from exponent d.")291 # Found !292 q, r = divmod(n, p)293 assert r == 0294 p, q = sorted((p, q), reverse=True)295 return (p, q)296 