def __init__(self, x, y): self._x = Integer(x) self._y = Integer(y) # Buffers self._common = Integer(0) self._tmp1 = Integer(0) self._x3 = Integer(0) self._y3 = Integer(0)
def test_probable_prime(candidate, randfunc=None): """Test if a number is prime. A number is qualified as prime if it passes a certain number of Miller-Rabin tests (dependent on the size of the number, but such that probability of a false positive is less than 10^-30) and a single Lucas test. For instance, a 1024-bit candidate will need to pass 4 Miller-Rabin tests. :Parameters: candidate : integer The number to test for primality. randfunc : callable The routine to draw random bytes from to select Miller-Rabin bases. :Returns: ``PROBABLE_PRIME`` if the number if prime with very high probability. ``COMPOSITE`` if the number is a composite. For efficiency reasons, ``COMPOSITE`` is also returned for small primes. """ if randfunc is None: randfunc = Random.new().read if not isinstance(candidate, Integer): candidate = Integer(candidate) # First, check trial division by the smallest primes if int(candidate) in _sieve_base: return PROBABLY_PRIME try: map(candidate.fail_if_divisible_by, _sieve_base) except ValueError: return COMPOSITE # These are the number of Miller-Rabin iterations s.t. p(k, t) < 1E-30, # with p(k, t) being the probability that a randomly chosen k-bit number # is composite but still survives t MR iterations. mr_ranges = ((220, 30), (280, 20), (390, 15), (512, 10), (620, 7), (740, 6), (890, 5), (1200, 4), (1700, 3), (3700, 2)) bit_size = candidate.size_in_bits() try: mr_iterations = list(filter(lambda x: bit_size < x[0], mr_ranges))[0][1] except IndexError: mr_iterations = 1 if miller_rabin_test(candidate, mr_iterations, randfunc=randfunc) == COMPOSITE: return COMPOSITE if lucas_test(candidate) == COMPOSITE: return COMPOSITE return PROBABLY_PRIME
def test_several_lengths(self): prng = SHAKE128.new().update(b('Test')) for length in range(1, 100): base = Integer.from_bytes(prng.read(length)) modulus2 = Integer.from_bytes(prng.read(length)) | 1 exponent2 = Integer.from_bytes(prng.read(length)) expected = pow(base, exponent2, modulus2) result = monty_pow(base, exponent2, modulus2) self.assertEqual(result, expected)
def test_random_exact_bits(self): for _ in xrange(1000): a = IntegerGeneric.random(exact_bits=8) self.failIf(a < 128) self.failIf(a >= 256) for bits_value in xrange(1024, 1024 + 8): a = IntegerGeneric.random(exact_bits=bits_value) self.failIf(a < 2**(bits_value - 1)) self.failIf(a >= 2**bits_value)
def test_random_max_bits(self): flag = False for _ in xrange(1000): a = IntegerGeneric.random(max_bits=8) flag = flag or a < 128 self.failIf(a>=256) self.failUnless(flag) for bits_value in xrange(1024, 1024 + 8): a = IntegerGeneric.random(max_bits=bits_value) self.failIf(a >= 2**bits_value)
def _sign(self, M, K): if (not hasattr(self, 'x')): raise TypeError('Private key not available in this object') p1=self.p-1 K = Integer(K) if (K.gcd(p1)!=1): raise ValueError('Bad K value: GCD(K,p-1)!=1') a=pow(self.g, K, self.p) t=(Integer(M)-self.x*a) % p1 while t<0: t=t+p1 b=(t*K.inverse(p1)) % p1 return [int(a), int(b)]
def verify(self, msg_hash, signature): """Verify that a certain DSS signature is authentic. This function checks if the party holding the private half of the key really signed the message. :Parameters: msg_hash : hash object The hash that was carried out over the message. This is an object belonging to the `Cryptodome.Hash` module. Under mode *'fips-186-3'*, the hash must be a FIPS approved secure hash (SHA-1 or a member of the SHA-2 family), of cryptographic strength appropriate for the DSA key. For instance, a 3072/256 DSA key can only be used in combination with SHA-512. signature : byte string The signature that needs to be validated. :Raise ValueError: If the signature is not authentic. """ if not self._valid_hash(msg_hash): raise ValueError("Hash does not belong to SHS") if self._encoding == 'binary': if len(signature) != (2 * self._order_bytes): raise ValueError("The signature is not authentic (length)") r_prime, s_prime = [Integer.from_bytes(x) for x in (signature[:self._order_bytes], signature[self._order_bytes:])] else: try: der_seq = DerSequence().decode(signature) except (ValueError, IndexError): raise ValueError("The signature is not authentic (DER)") if len(der_seq) != 2 or not der_seq.hasOnlyInts(): raise ValueError("The signature is not authentic (DER content)") r_prime, s_prime = der_seq[0], der_seq[1] if not (0 < r_prime < self._order) or not (0 < s_prime < self._order): raise ValueError("The signature is not authentic (d)") z = Integer.from_bytes(msg_hash.digest()[:self._order_bytes]) result = self._key._verify(z, (r_prime, s_prime)) if not result: raise ValueError("The signature is not authentic") # Make PyCryptodome code to fail return False
def __mul__(self, scalar): """Return a new point, the scalar product of this one""" if scalar < 0: raise ValueError("Scalar multiplication only defined for non-negative integers") # Trivial results if scalar == 0 or self.is_point_at_infinity(): return self.point_at_infinity() elif scalar == 1: return self.copy() # Scalar randomization scalar_blind = Integer.random(exact_bits=64) * _curve.order + scalar # Montgomery key ladder r = [self.point_at_infinity().copy(), self.copy()] bit_size = int(scalar_blind.size_in_bits()) scalar_int = int(scalar_blind) for i in range(bit_size, -1, -1): di = scalar_int >> i & 1 r[di ^ 1] += r[di] r[di].double() return r[0]
def _import_public_der(curve_name, publickey): # We only support P-256 named curves for now if curve_name != _curve.oid: raise ValueError("Unsupport curve") # ECPoint ::= OCTET STRING # We support only uncompressed points order_bytes = _curve.order.size_in_bytes() if len(publickey) != (1 + 2 * order_bytes) or bord(publickey[0]) != 4: raise ValueError("Only uncompressed points are supported") point_x = Integer.from_bytes(publickey[1:order_bytes+1]) point_y = Integer.from_bytes(publickey[order_bytes+1:]) return construct(curve="P-256", point_x=point_x, point_y=point_y)
def _decrypt(self, ciphertext): if not 0 < ciphertext < self._n: raise ValueError("Ciphertext too large") if not self.has_private(): raise TypeError("This is not a private key") # Blinded RSA decryption (to prevent timing attacks): # Step 1: Generate random secret blinding factor r, # such that 0 < r < n-1 r = Integer.random_range(min_inclusive=1, max_exclusive=self._n) # Step 2: Compute c' = c * r**e mod n cp = Integer(ciphertext) * pow(r, self._e, self._n) % self._n # Step 3: Compute m' = c'**d mod n (ordinary RSA decryption) m1 = pow(cp, self._d % (self._p - 1), self._p) m2 = pow(cp, self._d % (self._q - 1), self._q) h = m2 - m1 while h < 0: h += self._q h = (h * self._u) % self._q mp = h * self._p + m1 # Step 4: Compute m = m**(r-1) mod n result = (r.inverse(self._n) * mp) % self._n # Verify no faults occured if ciphertext != pow(result, self._e, self._n): raise ValueError("Fault detected in RSA decryption") return result
def _import_public_der(curve_oid, ec_point): """Convert an encoded EC point into an EccKey object curve_name: string with the OID of the curve ec_point: byte string with the EC point (not DER encoded) """ for curve_name, curve in _curves.items(): if curve.oid == curve_oid: break else: raise UnsupportedEccFeature("Unsupported ECC curve (OID: %s)" % curve_oid) # See 2.2 in RFC5480 and 2.3.3 in SEC1 # The first byte is: # - 0x02: compressed, only X-coordinate, Y-coordinate is even # - 0x03: compressed, only X-coordinate, Y-coordinate is odd # - 0x04: uncompressed, X-coordinate is followed by Y-coordinate # # PAI is in theory encoded as 0x00. modulus_bytes = curve.p.size_in_bytes() point_type = bord(ec_point[0]) # Uncompressed point if point_type == 0x04: if len(ec_point) != (1 + 2 * modulus_bytes): raise ValueError("Incorrect EC point length") x = Integer.from_bytes(ec_point[1:modulus_bytes + 1]) y = Integer.from_bytes(ec_point[modulus_bytes + 1:]) # Compressed point elif point_type in (0x02, 0x3): if len(ec_point) != (1 + modulus_bytes): raise ValueError("Incorrect EC point length") x = Integer.from_bytes(ec_point[1:]) y = (x**3 - x * 3 + curve.b).sqrt(curve.p) # Short Weierstrass if point_type == 0x02 and y.is_odd(): y = curve.p - y if point_type == 0x03 and y.is_even(): y = curve.p - y else: raise ValueError("Incorrect EC point encoding") return construct(curve=curve_name, point_x=x, point_y=y)
def discrete_log4(key, x): '''Finds the discrete log with respect to 4 for a really big number. Does it naively be counting up by powers of two until we get to x''' key_public = key s_inv = pow( Integer(4), key_public.p - 2, key_public.p ) # modular multiplicative inverse https://stackoverflow.com/questions/4798654/modular-multiplicative-inverse-function-in-python tmp = Integer(x) ans = 0 while tmp != 1: tmp = tmp * s_inv % key_public.p ans = ans + 1 if ans % 100000 == 0: print('.', end='', flush=True) print() return ans
def verify(self, msg_hash, signature): """Check if a certain (EC)DSA signature is authentic. Args: msg_hash (hash object): The hash that was carried out over the message. This is an object belonging to the :mod:`Cryptodome.Hash` module. Under mode ``'fips-186-3'``, the hash must be a FIPS approved secure hash (SHA-2 or SHA-3). signature (``bytes``): The signature that needs to be validated. :raise ValueError: if the signature is not authentic """ if not self._valid_hash(msg_hash): raise ValueError("Hash is not sufficiently strong") if self._encoding == 'binary': if len(signature) != (2 * self._order_bytes): raise ValueError("The signature is not authentic (length)") r_prime, s_prime = [ Integer.from_bytes(x) for x in (signature[:self._order_bytes], signature[self._order_bytes:]) ] else: try: der_seq = DerSequence().decode(signature, strict=True) except (ValueError, IndexError): raise ValueError("The signature is not authentic (DER)") if len(der_seq) != 2 or not der_seq.hasOnlyInts(): raise ValueError( "The signature is not authentic (DER content)") r_prime, s_prime = Integer(der_seq[0]), Integer(der_seq[1]) if not (0 < r_prime < self._order) or not (0 < s_prime < self._order): raise ValueError("The signature is not authentic (d)") z = Integer.from_bytes(msg_hash.digest()[:self._order_bytes]) result = self._key._verify(z, (r_prime, s_prime)) if not result: raise ValueError("The signature is not authentic") # Make PyCryptodome code to fail return False
def __init__(self, key, n): self.key = key self.n = n # Figure out sizes needed for array for t in 'BHILQ': if array.array(t).itemsize * 8 > np.log(n) / np.log(2): typecode = t break y = Integer(1) table = [array.array(typecode) for _ in range(2**16)] for x in range(n): h = xxh64_intdigest(str(y)) % 2**16 table[h].append(x) y = Integer(y) * Integer(4) % key.p if x % 1000000 == 0: print(x) self.table = table
def __add__(self, other): if (self == self.curve.O): return other if (other == self.curve.O): return self p = self.curve.p if (self.x == other.x): if ((self.y + other.y) % p == 0): return self.curve.O else: l = (((3 * self.x**2 + self.curve.A) % p) * int(Integer(2 * self.y).inverse(p))) % p else: l = ((other.y - self.y) * int(Integer(other.x - self.x).inverse(p))) % p x = (pow(l, 2, p) - self.x - other.x) % p y = (l * (self.x - x) - self.y) % p return ElPoint(x, y, self.curve)
def check_mul(self, val: int, n: Integer): Q = self.curve.O.project() while val: if val & 1: Q = Q + self assert Q.z != 0 and n.gcd(Q.z) self = self + self val >>= 1 return Q
def __init__(self, key, encoding, order, randfunc): super(FipsDsaSigScheme, self).__init__(key, encoding, order) self._randfunc = randfunc L = Integer(key.p).size_in_bits() if (L, self._order_bits) not in self._fips_186_3_L_N: error = ("L/N (%d, %d) is not compliant to FIPS 186-3" % (L, self._order_bits)) raise ValueError(error)
def _bits2int(self, bstr): """See 2.3.2 in RFC6979""" result = Integer.from_bytes(bstr) q_len = self._order.size_in_bits() b_len = len(bstr) * 8 if b_len > q_len: result >>= (b_len - q_len) return result
def construct(**kwargs): """Build a new ECC key (private or public) starting from some base components. :Keywords: curve : string Mandatory. It must be "P-256", "prime256v1" or "secp256r1". d : integer Only for a private key. It must be in the range ``[1..order-1]``. point_x : integer Mandatory for a public key. X coordinate (affine) of the ECC point. point_y : integer Mandatory for a public key. Y coordinate (affine) of the ECC point. """ point_x = kwargs.pop("point_x", None) point_y = kwargs.pop("point_y", None) if "point" in kwargs: raise TypeError("Unknown keyword: point") if None not in (point_x, point_y): kwargs["point"] = EccPoint(point_x, point_y) # Validate that the point is on the P-256 curve eq1 = pow(Integer(point_y), 2, _curve.p) x = Integer(point_x) eq2 = pow(x, 3, _curve.p) x *= -3 eq2 += x eq2 += _curve.b eq2 %= _curve.p if eq1 != eq2: raise ValueError("The point is not on the curve") # Validate that the private key matches the public one d = kwargs.get("d", None) if d is not None and "point" in kwargs: pub_key = _curve.G * d if pub_key.x != point_x or pub_key.y != point_y: raise ValueError("Private and public ECC keys do not match") return EccKey(**kwargs)
def generate_probable_prime(**kwargs): """Generate a random probable prime. The prime will not have any specific properties (e.g. it will not be a *strong* prime). Random numbers are evaluated for primality until one passes all tests, consisting of a certain number of Miller-Rabin tests with random bases followed by a single Lucas test. The number of Miller-Rabin iterations is chosen such that the probability that the output number is a non-prime is less than 1E-30 (roughly 2^{-100}). This approach is compliant to `FIPS PUB 186-4`__. :Keywords: exact_bits : integer The desired size in bits of the probable prime. It must be at least 160. randfunc : callable An RNG function where candidate primes are taken from. prime_filter : callable A function that takes an Integer as parameter and returns True if the number can be passed to further primality tests, False if it should be immediately discarded. :Return: A probable prime in the range 2^exact_bits > p > 2^(exact_bits-1). .. __: http://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-4.pdf """ exact_bits = kwargs.pop("exact_bits", None) randfunc = kwargs.pop("randfunc", None) prime_filter = kwargs.pop("prime_filter", lambda x: True) if kwargs: raise ValueError("Unknown parameters: " + kwargs.keys()) if exact_bits is None: raise ValueError("Missing exact_bits parameter") if exact_bits < 160: raise ValueError("Prime number is not big enough.") if randfunc is None: randfunc = Random.new().read result = COMPOSITE while result == COMPOSITE: candidate = Integer.random(exact_bits=exact_bits, randfunc=randfunc) | 1 if not prime_filter(candidate): continue result = test_probable_prime(candidate, randfunc) return candidate
def generate_probable_prime(**kwargs): """Generate a random probable prime. The prime will not have any specific properties (e.g. it will not be a *strong* prime). Random numbers are evaluated for primality until one passes all tests, consisting of a certain number of Miller-Rabin tests with random bases followed by a single Lucas test. The number of Miller-Rabin iterations is chosen such that the probability that the output number is a non-prime is less than 1E-30 (roughly 2^{-100}). This approach is compliant to `FIPS PUB 186-4`__. :Keywords: exact_bits : integer The desired size in bits of the probable prime. It must be at least 160. randfunc : callable An RNG function where candidate primes are taken from. prime_filter : callable A function that takes an Integer as parameter and returns True if the number can be passed to further primality tests, False if it should be immediately discarded. :Return: A probable prime in the range 2^exact_bits > p > 2^(exact_bits-1). .. __: http://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-4.pdf """ exact_bits = kwargs.pop("exact_bits", None) randfunc = kwargs.pop("randfunc", None) prime_filter = kwargs.pop("prime_filter", lambda x: True) if kwargs: print "Unknown parameters:", kwargs.keys() if exact_bits is None: raise ValueError("Missing exact_bits parameter") if exact_bits < 160: raise ValueError("Prime number is not big enough.") if randfunc is None: randfunc = Random.new().read result = COMPOSITE while result == COMPOSITE: candidate = Integer.random(exact_bits=exact_bits, randfunc=randfunc) | 1 if not prime_filter(candidate): continue result = test_probable_prime(candidate, randfunc) return candidate
def _verify(self, m, sig): r, s = sig y, q, p, g = [self._key[comp] for comp in ['y', 'q', 'p', 'g']] if not (0 < r < q) or not (0 < s < q): return False w = Integer(s).inverse(q) u1 = (w * m) % q u2 = (w * r) % q v = (pow(g, u1, p) * pow(y, u2, p) % p) % q return v == r
def _verify(self, M, sig): sig = [Integer(x) for x in sig] if sig[0] < 1 or sig[0] > self.p - 1: return 0 v1 = pow(self.y, sig[0], self.p) v1 = (v1 * pow(sig[0], sig[1], self.p)) % self.p v2 = pow(self.g, M, self.p) if v1 == v2: return 1 return 0
def _import_public_der(curve_oid, ec_point): """Convert an encoded EC point into an EccKey object curve_name: string with the OID of the curve ec_point: byte string with the EC point (not DER encoded) """ # We only support P-256 named curves for now if curve_oid != _curve.oid: raise UnsupportedEccFeature("Unsupported ECC curve (OID: %s)" % curve_oid) # See 2.2 in RFC5480 and 2.3.3 in SEC1 # The first byte is: # - 0x02: compressed, only X-coordinate, Y-coordinate is even # - 0x03: compressed, only X-coordinate, Y-coordinate is odd # - 0x04: uncompressed, X-coordinate is followed by Y-coordinate # # PAI is in theory encoded as 0x00. order_bytes = _curve.order.size_in_bytes() point_type = bord(ec_point[0]) # Uncompressed point if point_type == 0x04: if len(ec_point) != (1 + 2 * order_bytes): raise ValueError("Incorrect EC point length") x = Integer.from_bytes(ec_point[1:order_bytes+1]) y = Integer.from_bytes(ec_point[order_bytes+1:]) # Compressed point elif point_type in (0x02, 0x3): if len(ec_point) != (1 + order_bytes): raise ValueError("Incorrect EC point length") x = Integer.from_bytes(ec_point[1:]) y = (x**3 - x*3 + _curve.b).sqrt(_curve.p) # Short Weierstrass if point_type == 0x02 and y.is_odd(): y = _curve.p - y if point_type == 0x03 and y.is_even(): y = _curve.p - y else: raise ValueError("Incorrect EC point encoding") return construct(curve="P-256", point_x=x, point_y=y)
def _decrypt(self, M): if (not hasattr(self, 'x')): raise TypeError('Private key not available in this object') r = Integer.random_range(min_inclusive=2, max_exclusive=self.p - 1, randfunc=self._randfunc) a_blind = (pow(self.g, r, self.p) * M[0]) % self.p ax = pow(a_blind, self.x, self.p) plaintext_blind = (ax.inverse(self.p) * M[1]) % self.p plaintext = (plaintext_blind * pow(self.y, r, self.p)) % self.p return int(plaintext)
def _get_weak_domain(self): from Cryptodome.Math.Numbers import Integer from Cryptodome.Math import Primality p = Integer(4) while p.size_in_bits() != 1024 or Primality.test_probable_prime(p) != Primality.PROBABLY_PRIME: q1 = Integer.random(exact_bits=80) q2 = Integer.random(exact_bits=80) q = q1 * q2 z = Integer.random(exact_bits=1024-160) p = z * q + 1 h = Integer(2) g = 1 while g == 1: g = pow(h, z, p) h += 1 return (p, q, g)
def _decrypt(self, M): if (not hasattr(self, 'x')): raise TypeError('Private key not available in this object') r = Integer.random_range(min_inclusive=2, max_exclusive=self.p-1, randfunc=self._randfunc) a_blind = (pow(self.g, r, self.p) * M[0]) % self.p ax=pow(a_blind, self.x, self.p) plaintext_blind = (ax.inverse(self.p) * M[1] ) % self.p plaintext = (plaintext_blind * pow(self.y, r, self.p)) % self.p return int(plaintext)
def _sign_message(privkey, msg, k=None): """ create DSA signed message @arg privkey ... privatekey as DSA obj @arg msg ... message to sign @arg k ... override random k """ k = k or random.StrongRandom().randint(2, privkey.q - 1) # generate msg hash # sign the messages using privkey h = SHA1.new(msg).digest() # taken from pycryptodome # Perform signature using the raw API order_bytes = (Integer(privkey.q).size_in_bits() - 1) // 8 + 1 z = Integer.from_bytes(h[:order_bytes]) r, s = privkey._sign(z, k) return h, (r, s), privkey.publickey()
def _sign(self, z, k): assert 0 < k < _curve.order blind = Integer.random_range(min_inclusive=1, max_exclusive=_curve.order) blind_d = self._d * blind inv_blind_k = (blind * k).inverse(_curve.order) r = (_curve.G * k).x % _curve.order s = inv_blind_k * (blind * z + blind_d * r) % _curve.order return (r, s)
def test_random_bits_custom_rng(self): class CustomRNG(object): def __init__(self): self.counter = 0 def __call__(self, size): self.counter += size return bchr(0) * size custom_rng = CustomRNG() a = IntegerGeneric.random(exact_bits=32, randfunc=custom_rng) self.assertEqual(custom_rng.counter, 4)
def add_ssh_keys(params, endpoint, record, temp_files, non_shared): # type: (KeeperParams, str, Record, [str], dict) -> [bytes] rs = [] key_prefix = 'connect:{0}:ssh-key'.format(endpoint) ssh_socket_path = os.environ.get('SSH_AUTH_SOCK') for cf in record.custom_fields: cf_name = cf['name'] # type: str if cf_name.startswith(key_prefix): key_name = cf_name[len(key_prefix)+1:] or 'Commander' cf_value = cf['value'] # type: str parsed_values = [] while True: m = endpoint_parameter_pattern.search(cf_value) if not m: break p = m.group(1) val = ConnectCommand.get_parameter_value(params, record, p, temp_files, non_shared) if not val: raise Exception('Add ssh-key. Failed to resolve key parameter: {0}'.format(p)) parsed_values.append(val) cf_value = cf_value[m.end():] if len(parsed_values) > 0: cf_value = cf_value.strip() if cf_value: parsed_values.append(cf_value) private_key = RSA.importKey(parsed_values[0], parsed_values[1] if len(parsed_values) > 0 else None) with ConnectSshAgent(ssh_socket_path) as fd: payload = SSH2_AGENTC_ADD_IDENTITY.to_bytes(1, byteorder='big') payload += ConnectCommand.ssh_agent_encode_str('ssh-rsa') payload += ConnectCommand.ssh_agent_encode_long(private_key.n) payload += ConnectCommand.ssh_agent_encode_long(private_key.e) payload += ConnectCommand.ssh_agent_encode_long(private_key.d) payload += ConnectCommand.ssh_agent_encode_long(int(Integer(private_key.q).inverse(private_key.p))) payload += ConnectCommand.ssh_agent_encode_long(private_key.p) payload += ConnectCommand.ssh_agent_encode_long(private_key.q) payload += ConnectCommand.ssh_agent_encode_str(key_name) # windows ssh implementation does not support constrained identities #payload += SSH_AGENT_CONSTRAIN_LIFETIME.to_bytes(1, byteorder='big') #payload += int(10).to_bytes(4, byteorder='big') recv_payload = fd.send(payload) if recv_payload and recv_payload[0] == SSH_AGENT_FAILURE: raise Exception('Add ssh-key. Failed to add ssh key \"{0}\" to ssh-agent'.format(key_name)) payload = ConnectCommand.ssh_agent_encode_str('ssh-rsa') payload += ConnectCommand.ssh_agent_encode_long(private_key.e) payload += ConnectCommand.ssh_agent_encode_long(private_key.n) payload = SSH2_AGENTC_REMOVE_IDENTITY.to_bytes(1, byteorder='big') + ConnectCommand.ssh_agent_encode_bytes(payload) rs.append(payload) return rs
def sign(r, key): r_prime = r * key.d.inverse(key._curve.order) date = int(time.time()) nonce = Integer.random_range(min_inclusive=1, max_exclusive=key._curve.order) z = f'{nonce}||{date}' R = r_prime + (key._curve.G * hash(z)) s = (key.d - hash(z)) % key._curve.order # return (R, s, z) # we can not give away z or this is unsafe: x = s+h(z) return R, s
def encrypt(key, message): """Encrypts an integer message using the provided ElGamal key Includes a copy of key in the output (so be sure to use a public key; will assert error out otherwise) Note that this is stochastic because we randomly generate an ephemeral key each time ******VERY IMPORTANT******* The message must be a quadratic residue for DDH to hold. We do not ensure this in this encrypt function, because we carefully control what input messages are possible. Do *NOT* use this for general purpose encryption unless you first encode the message as a quadratic residue. Using a non-quadratic residue will look like it works, but will not be secure. *************************** """ assert (not key.has_private()) z = randint(2, int(key.p - 1)) # Ephemeral key c_1 = pow(key.g, z, key.p) # first part of ciphertext s = pow(key.y, z, key.p) # shared secret m = message c_2 = (Integer(m) * Integer(s)) % key.p return CipherText(key, c_1, c_2)
def deserialize_ElGamalPublicKey(bytestring): '''constructs an ElGamal Public Key from a bytestring''' padded_size = len(bytestring) // 3 assert padded_size * 3 == len(bytestring), "Wrong bytestring length" p = Integer.from_bytes(bytestring[:padded_size]) g = Integer.from_bytes(bytestring[padded_size:2 * padded_size]) y = Integer.from_bytes(bytestring[2 * padded_size:]) return ElGamal.construct((p, g, y)) def export_val(self): '''Returns a bytestring.''' padded_size = int(np.round(key.p.size_in_bits() / 8)) return self.c1.to_bytes(padded_size) + self.c2.to_bytes(padded_size) @classmethod def byte_init(cls, key, bytestring): '''Returns a Ciphertext from a bytestring containing c1 and c2''' padded_size = int(np.round(key.p.size_in_bits() / 8)) assert (len(bytestring) == 2 * padded_size) c1 = Integer.from_bytes(bytestring[:padded_size]) c2 = Integer.from_bytes(bytestring[padded_size:]) return cls(key, c1, c2)
def hospital_round0a(key_template, key_x=None): '''The partial key generation phase If key_x is set, we'll use that instead of a new random integer Returns a dictionary with: key_x: a new Elgamal private key secret share private_key: an ElGamal private key using key_x curr_y: a partial public key share transmit: a bytestring encoding of curr_y to send to hub time: amount of time elapsed for this function ''' start = time.perf_counter() if key_x: x = key_x else: x = Integer(randint(2, int(key_template.p - 1))) curr_y = pow(key_template.g, x, key_template.p) padded_size = int(np.round(key_template.p.size_in_bits() / 8)) curr_y_bytestring = curr_y.to_bytes(padded_size) private_key = construct_key(key_template, x) assert (private_key.y == curr_y) elapsed = time.perf_counter() - start assert (Integer.from_bytes(curr_y_bytestring) == curr_y) answer_dict = {} answer_dict['key_x'] = x answer_dict['key_x_bytestring'] = x.to_bytes(padded_size) answer_dict['private_key'] = private_key answer_dict['private_key_export'] = serialize_ElGamalPrivateKey( private_key) answer_dict['curr_y'] = curr_y answer_dict['transmit'] = curr_y_bytestring answer_dict['time'] = elapsed return answer_dict
def generate_rsa_key_pair(): rsa_key = RSA.generate(2048) private_key = DerSequence([ 0, rsa_key.n, rsa_key.e, rsa_key.d, rsa_key.p, rsa_key.q, rsa_key.d % (rsa_key.p - 1), rsa_key.d % (rsa_key.q - 1), Integer(rsa_key.q).inverse(rsa_key.p) ]).encode() pub_key = rsa_key.publickey() public_key = DerSequence([pub_key.n, pub_key.e]).encode() return private_key, public_key
def __repr__(self): attrs = [] for k in self._keydata: if k == 'p': bits = Integer(self.p).size_in_bits() attrs.append("p(%d)" % (bits, )) elif hasattr(self, k): attrs.append(k) if self.has_private(): attrs.append("private") # PY3K: This is meant to be text, do not change to bytes (data) return "<%s @0x%x %s>" % (self.__class__.__name__, id(self), ",".join(attrs))
def generate_p_q_g(): start2 = timeit.default_timer() p = Integer(4) while p.size_in_bits() != 1024 or Primality.test_probable_prime( p) != Primality.PROBABLY_PRIME: q = random.randrange(1 << 159, 1 << 160) if (is_prime_4(q)): z = Integer.random(exact_bits=1024 - 160) p = z * q + 1 stop2 = timeit.default_timer() print("Time Reqiued_1: " + str(stop2 - start2)) start1 = timeit.default_timer() h = Integer(2) g = 1 while g == 1: g = power(int(h), int(z), int(p)) h += 1 stop1 = timeit.default_timer() print("Time Reqiued_2: " + str(stop1 - start1)) return (p, q, g)
def construct(tup): r"""Construct an ElGamal key from a tuple of valid ElGamal components. The modulus *p* must be a prime. The following conditions must apply: .. math:: \begin{align} &1 < g < p-1 \\ &g^{p-1} = 1 \text{ mod } 1 \\ &1 < x < p-1 \\ &g^x = y \text{ mod } p \end{align} Args: tup (tuple): A tuple with either 3 or 4 integers, in the following order: 1. Modulus (*p*). 2. Generator (*g*). 3. Public key (*y*). 4. Private key (*x*). Optional. Raises: ValueError: when the key being imported fails the most basic ElGamal validity checks. Returns: an :class:`ElGamalKey` object """ obj = ElGamalKey() if len(tup) not in [3, 4]: raise ValueError('argument for construct() wrong length') for i in range(len(tup)): field = obj._keydata[i] setattr(obj, field, Integer(tup[i])) fmt_error = test_probable_prime(obj.p) == COMPOSITE fmt_error |= obj.g <= 1 or obj.g >= obj.p fmt_error |= pow(obj.g, obj.p - 1, obj.p) != 1 fmt_error |= obj.y < 1 or obj.y >= obj.p if len(tup) == 4: fmt_error |= obj.x <= 1 or obj.x >= obj.p fmt_error |= pow(obj.g, obj.x, obj.p) != obj.y if fmt_error: raise ValueError("Invalid ElGamal key components") return obj
def generate_p_q_g(): global L_bit global N_bit start_1 = timeit.default_timer() p = Integer(4) while p.size_in_bits() != N_bit or Primality.test_probable_prime(p) != Primality.PROBABLY_PRIME: q=random.randrange(1 << L_bit-1, 1 << L_bit) if(is_prime(q)): z = Integer.random(exact_bits=N_bit-L_bit) p = z * q + 1 stop_1 = timeit.default_timer() print("Time Required for P & Q: "+str(stop_1-start_1)) start_2 = timeit.default_timer() h = Integer(2) g = 1 while g == 1: g = powmod(int(h), int(z), int(p)) h += 1 stop_2 = timeit.default_timer() print("Time Required for G: "+str(stop_2-start_2)) return (p, q, g)
def _decrypt(self, ciphertext): if not 0 <= ciphertext < self._n: raise ValueError("Ciphertext too large") if not self.has_private(): raise TypeError("This is not a private key") # Blinded RSA decryption (to prevent timing attacks): # Step 1: Generate random secret blinding factor r, # such that 0 < r < n-1 r = Integer.random_range(min_inclusive=1, max_exclusive=self._n) # Step 2: Compute c' = c * r**e mod n cp = Integer(ciphertext) * pow(r, self._e, self._n) % self._n # Step 3: Compute m' = c'**d mod n (normal RSA decryption) m1 = pow(cp, self._dp, self._p) m2 = pow(cp, self._dq, self._q) h = ((m2 - m1) * self._u) % self._q mp = h * self._p + m1 # Step 4: Compute m = m**(r-1) mod n result = (r.inverse(self._n) * mp) % self._n # Verify no faults occurred if ciphertext != pow(result, self._e, self._n): raise ValueError("Fault detected in RSA decryption") return result
def __init__(self, key, n): self.key = key self.n = n # Figure out sizes needed for array for t in 'BHILQ': if array.array(t).itemsize * 8 > np.log(n) / np.log(2): typecode = t break y = Integer(1) table = [array.array(typecode) for _ in range(2**16)] for x in range(n): h = xxh64_intdigest(str(y)) % 2**16 table[h].append(x) y = Integer(y) * Integer(4) % key.p if x % 1000000 == 0: print(x) # Figure out equivalent numpy sizes s = array.array(typecode).itemsize * 8 if s <= 8: nptype = np.uint8 elif s <= 16: nptype = np.uint16 elif s <= 32: nptype = np.uint32 elif s <= 64: nptype = np.uint64 else: raise TypeError("No numpy type large enough to hold array") maxtable_length = max([len(t) for t in table]) table_of_nps = [] for t in table: t_np = np.array(t, dtype=nptype) t_np.resize(maxtable_length) table_of_nps.append(t_np) self.table = np.asarray(table_of_nps)
def _generateRSAKey(bits, randfunc=None, e=65537): """Modified version of pycryptodome's Crypto.RSA.generate to allow keys of any size.""" if e % 2 == 0 or e < 3: raise ValueError( "RSA public exponent must be a positive, odd integer larger than 2." ) if randfunc is None: randfunc = Random.get_random_bytes d = n = Integer(1) e = Integer(e) while n.size_in_bits() != bits and d < (1 << (bits // 2)): # Generate the prime factors of n: p and q. # By construciton, their product is always # 2^{bits-1} < p*q < 2^bits. size_q = bits // 2 size_p = bits - size_q min_p = min_q = (Integer(1) << (2 * size_q - 1)).sqrt() if size_q != size_p: min_p = (Integer(1) << (2 * size_p - 1)).sqrt() def filter_p(candidate): return candidate > min_p and (candidate - 1).gcd(e) == 1 p = _generateProbablePrime(exact_bits=size_p, randfunc=randfunc, prime_filter=filter_p) min_distance = Integer(1) << max(0, bits // 2 - 100) def filter_q(candidate): return (candidate > min_q and (candidate - 1).gcd(e) == 1 and abs(candidate - p) > min_distance) q = _generateProbablePrime(exact_bits=size_q, randfunc=randfunc, prime_filter=filter_q) n = p * q lcm = (p - 1).lcm(q - 1) d = e.inverse(lcm) if p > q: p, q = q, p u = p.inverse(q) return RSA.RsaKey(n=n, e=e, d=d, p=p, q=q, u=u)
def _generate_domain(L, randfunc): """Generate a new set of DSA domain parameters""" N = { 1024:160, 2048:224, 3072:256 }.get(L) if N is None: raise ValueError("Invalid modulus length (%d)" % L) outlen = SHA256.digest_size * 8 n = (L + outlen - 1) // outlen - 1 # ceil(L/outlen) -1 b_ = L - 1 - (n * outlen) # Generate q (A.1.1.2) q = Integer(4) upper_bit = 1 << (N - 1) while test_probable_prime(q, randfunc) != PROBABLY_PRIME: seed = randfunc(64) U = Integer.from_bytes(SHA256.new(seed).digest()) & (upper_bit - 1) q = U | upper_bit | 1 assert(q.size_in_bits() == N) # Generate p (A.1.1.2) offset = 1 upper_bit = 1 << (L - 1) while True: V = [ SHA256.new(seed + Integer(offset + j).to_bytes()).digest() for j in iter_range(n + 1) ] V = [ Integer.from_bytes(v) for v in V ] W = sum([V[i] * (1 << (i * outlen)) for i in iter_range(n)], (V[n] & ((1 << b_) - 1)) * (1 << (n * outlen))) X = Integer(W + upper_bit) # 2^{L-1} < X < 2^{L} assert(X.size_in_bits() == L) c = X % (q * 2) p = X - (c - 1) # 2q divides (p-1) if p.size_in_bits() == L and \ test_probable_prime(p, randfunc) == PROBABLY_PRIME: break offset += n + 1 # Generate g (A.2.3, index=1) e = (p - 1) // q for count in itertools.count(1): U = seed + b"ggen" + bchr(1) + Integer(count).to_bytes() W = Integer.from_bytes(SHA256.new(U).digest()) g = pow(W, e, p) if g != 1: break return (p, q, g, seed)
def _sign(self, m, k): if not self.has_private(): raise TypeError("DSA public key cannot be used for signing") if not (1 < k < self.q): raise ValueError("k is not between 2 and q-1") x, q, p, g = [self._key[comp] for comp in ['x', 'q', 'p', 'g']] blind_factor = Integer.random_range(min_inclusive=1, max_exclusive=q) inv_blind_k = (blind_factor * k).inverse(q) blind_x = x * blind_factor r = pow(g, k, p) % q # r = (g**k mod p) mod q s = (inv_blind_k * (blind_factor * m + blind_x * r)) % q return map(int, (r, s))
def sign(self, msg_hash): """Produce the DSS signature of a message. :Parameters: msg_hash : hash object The hash that was carried out over the message. The object belongs to the `Cryptodome.Hash` package. Under mode *'fips-186-3'*, the hash must be a FIPS approved secure hash (SHA-1 or a member of the SHA-2 family), of cryptographic strength appropriate for the DSA key. For instance, a 3072/256 DSA key can only be used in combination with SHA-512. :Return: The signature encoded as a byte string. :Raise ValueError: If the hash algorithm is incompatible to the DSA key. :Raise TypeError: If the DSA key has no private half. """ if not self._valid_hash(msg_hash): raise ValueError("Hash is not sufficiently strong") # Generate the nonce k (critical!) nonce = self._compute_nonce(msg_hash) # Perform signature using the raw API z = Integer.from_bytes(msg_hash.digest()[:self._order_bytes]) sig_pair = self._key._sign(z, nonce) # Encode the signature into a single byte string if self._encoding == 'binary': output = b("").join([long_to_bytes(x, self._order_bytes) for x in sig_pair]) else: # Dss-sig ::= SEQUENCE { # r OCTET STRING, # s OCTET STRING # } output = DerSequence(sig_pair).encode() return output
def generate(**kwargs): """Generate a new private key on the given curve. :Keywords: curve : string Mandatory. It must be "P-256", "prime256v1" or "secp256r1". randfunc : callable Optional. The RNG to read randomness from. If ``None``, the system source is used. """ curve = kwargs.pop("curve") randfunc = kwargs.pop("randfunc", get_random_bytes) if kwargs: raise TypeError("Unknown parameters: " + str(kwargs)) d = Integer.random_range(min_inclusive=1, max_exclusive=_curve.order, randfunc=randfunc) return EccKey(curve=curve, d=d)
def _import_private_der(encoded, passphrase, curve_name=None): # ECPrivateKey ::= SEQUENCE { # version INTEGER { ecPrivkeyVer1(1) } (ecPrivkeyVer1), # privateKey OCTET STRING, # parameters [0] ECParameters {{ NamedCurve }} OPTIONAL, # publicKey [1] BIT STRING OPTIONAL # } private_key = DerSequence().decode(encoded, nr_elements=(3, 4)) if private_key[0] != 1: raise ValueError("Incorrect ECC private key version") try: curve_name = DerObjectId(explicit=0).decode(private_key[2]).value except ValueError: pass if curve_name != _curve.oid: raise UnsupportedEccFeature("Unsupported ECC curve (OID: %s)" % curve_name) scalar_bytes = DerOctetString().decode(private_key[1]).payload order_bytes = _curve.order.size_in_bytes() if len(scalar_bytes) != order_bytes: raise ValueError("Private key is too small") d = Integer.from_bytes(scalar_bytes) # Decode public key (if any, it must be P-256) if len(private_key) == 4: public_key_enc = DerBitString(explicit=1).decode(private_key[3]).value public_key = _import_public_der(curve_name, public_key_enc) point_x = public_key.pointQ.x point_y = public_key.pointQ.y else: point_x = point_y = None return construct(curve="P-256", d=d, point_x=point_x, point_y=point_y)
def import_key(extern_key, passphrase=None): """Import a DSA key. Args: extern_key (string or byte string): The DSA key to import. The following formats are supported for a DSA **public** key: - X.509 certificate (binary DER or PEM) - X.509 ``subjectPublicKeyInfo`` (binary DER or PEM) - OpenSSH (ASCII one-liner, see `RFC4253`_) The following formats are supported for a DSA **private** key: - `PKCS#8`_ ``PrivateKeyInfo`` or ``EncryptedPrivateKeyInfo`` DER SEQUENCE (binary or PEM) - OpenSSL/OpenSSH custom format (binary or PEM) For details about the PEM encoding, see `RFC1421`_/`RFC1423`_. passphrase (string): In case of an encrypted private key, this is the pass phrase from which the decryption key is derived. Encryption may be applied either at the `PKCS#8`_ or at the PEM level. Returns: :class:`DsaKey` : a DSA key object Raises: ValueError : when the given key cannot be parsed (possibly because the pass phrase is wrong). .. _RFC1421: http://www.ietf.org/rfc/rfc1421.txt .. _RFC1423: http://www.ietf.org/rfc/rfc1423.txt .. _RFC4253: http://www.ietf.org/rfc/rfc4253.txt .. _PKCS#8: http://www.ietf.org/rfc/rfc5208.txt """ extern_key = tobytes(extern_key) if passphrase is not None: passphrase = tobytes(passphrase) if extern_key.startswith(b'-----'): # This is probably a PEM encoded key (der, marker, enc_flag) = PEM.decode(tostr(extern_key), passphrase) if enc_flag: passphrase = None return _import_key_der(der, passphrase, None) if extern_key.startswith(b'ssh-dss '): # This is probably a public OpenSSH key keystring = binascii.a2b_base64(extern_key.split(b' ')[1]) keyparts = [] while len(keystring) > 4: length = struct.unpack(">I", keystring[:4])[0] keyparts.append(keystring[4:4 + length]) keystring = keystring[4 + length:] if keyparts[0] == b"ssh-dss": tup = [Integer.from_bytes(keyparts[x]) for x in (4, 3, 1, 2)] return construct(tup) if bord(extern_key[0]) == 0x30: # This is probably a DER encoded key return _import_key_der(extern_key, passphrase, None) raise ValueError("DSA key format is not supported")
def generate(bits, randfunc=None, e=65537): """Create a new RSA key. The algorithm closely follows NIST `FIPS 186-4`_ in its sections B.3.1 and B.3.3. The modulus is the product of two non-strong probable primes. Each prime passes a suitable number of Miller-Rabin tests with random bases and a single Lucas test. :Parameters: bits : integer Key length, or size (in bits) of the RSA modulus. It must be at least 1024. The FIPS standard only defines 1024, 2048 and 3072. randfunc : callable Function that returns random bytes. The default is `Cryptodome.Random.get_random_bytes`. e : integer Public RSA exponent. It must be an odd positive integer. It is typically a small number with very few ones in its binary representation. The FIPS standard requires the public exponent to be at least 65537 (the default). :Return: An RSA key object (`RsaKey`). .. _FIPS 186-4: http://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-4.pdf """ if bits < 1024: raise ValueError("RSA modulus length must be >= 1024") if e % 2 == 0 or e < 3: raise ValueError("RSA public exponent must be a positive, odd integer larger than 2.") if randfunc is None: randfunc = Random.get_random_bytes d = n = Integer(1) e = Integer(e) while n.size_in_bits() != bits and d < (1 << (bits // 2)): # Generate the prime factors of n: p and q. # By construciton, their product is always # 2^{bits-1} < p*q < 2^bits. size_q = bits // 2 size_p = bits - size_q min_p = min_q = (Integer(1) << (2 * size_q - 1)).sqrt() if size_q != size_p: min_p = (Integer(1) << (2 * size_p - 1)).sqrt() def filter_p(candidate): return candidate > min_p and (candidate - 1).gcd(e) == 1 p = generate_probable_prime(exact_bits=size_p, randfunc=randfunc, prime_filter=filter_p) min_distance = Integer(1) << (bits // 2 - 100) def filter_q(candidate): return (candidate > min_q and (candidate - 1).gcd(e) == 1 and abs(candidate - p) > min_distance) q = generate_probable_prime(exact_bits=size_q, randfunc=randfunc, prime_filter=filter_q) n = p * q lcm = (p - 1).lcm(q - 1) d = e.inverse(lcm) if p > q: p, q = q, p u = p.inverse(q) return RsaKey(n=n, e=e, d=d, p=p, q=q, u=u)
def generate(bits, randfunc): """Randomly generate a fresh, new ElGamal key. The key will be safe for use for both encryption and signature (although it should be used for **only one** purpose). Args: bits (int): Key length, or size (in bits) of the modulus *p*. The recommended value is 2048. randfunc (callable): Random number generation function; it should accept a single integer *N* and return a string of random *N* random bytes. Return: an :class:`ElGamalKey` object """ obj=ElGamalKey() # Generate a safe prime p # See Algorithm 4.86 in Handbook of Applied Cryptography obj.p = generate_probable_safe_prime(exact_bits=bits, randfunc=randfunc) q = (obj.p - 1) >> 1 # Generate generator g while 1: # Choose a square residue; it will generate a cyclic group of order q. obj.g = pow(Integer.random_range(min_inclusive=2, max_exclusive=obj.p, randfunc=randfunc), 2, obj.p) # We must avoid g=2 because of Bleichenbacher's attack described # in "Generating ElGamal signatures without knowning the secret key", # 1996 if obj.g in (1, 2): continue # Discard g if it divides p-1 because of the attack described # in Note 11.67 (iii) in HAC if (obj.p - 1) % obj.g == 0: continue # g^{-1} must not divide p-1 because of Khadir's attack # described in "Conditions of the generator for forging ElGamal # signature", 2011 ginv = obj.g.inverse(obj.p) if (obj.p - 1) % ginv == 0: continue # Found break # Generate private key x obj.x = Integer.random_range(min_inclusive=2, max_exclusive=obj.p-1, randfunc=randfunc) # Generate public key y obj.y = pow(obj.g, obj.x, obj.p) return obj
def miller_rabin_test(candidate, iterations, randfunc=None): """Perform a Miller-Rabin primality test on an integer. The test is specified in Section C.3.1 of `FIPS PUB 186-4`__. :Parameters: candidate : integer The number to test for primality. iterations : integer The maximum number of iterations to perform before declaring a candidate a probable prime. randfunc : callable An RNG function where bases are taken from. :Returns: ``Primality.COMPOSITE`` or ``Primality.PROBABLY_PRIME``. .. __: http://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-4.pdf """ if not isinstance(candidate, Integer): candidate = Integer(candidate) if candidate.is_even(): return COMPOSITE one = Integer(1) minus_one = Integer(candidate - 1) if randfunc is None: randfunc = Random.new().read # Step 1 and 2 m = Integer(minus_one) a = 0 while m.is_even(): m >>= 1 a += 1 # Skip step 3 # Step 4 for i in xrange(iterations): # Step 4.1-2 base = 1 while base in (one, minus_one): base = Integer.random_range(min_inclusive=2, max_inclusive=candidate - 2) assert(2 <= base <= candidate - 2) # Step 4.3-4.4 z = pow(base, m, candidate) if z in (one, minus_one): continue # Step 4.5 for j in xrange(1, a): z = pow(z, 2, candidate) if z == minus_one: break if z == one: return COMPOSITE else: return COMPOSITE # Step 5 return PROBABLY_PRIME
def generate(bits, randfunc=None, domain=None): """Generate a new DSA key pair. The algorithm follows Appendix A.1/A.2 and B.1 of `FIPS 186-4`_, respectively for domain generation and key pair generation. Args: bits (integer): Key length, or size (in bits) of the DSA modulus *p*. It must be 1024, 2048 or 3072. randfunc (callable): Random number generation function; it accepts a single integer N and return a string of random data N bytes long. If not specified, :func:`Cryptodome.Random.get_random_bytes` is used. domain (tuple): The DSA domain parameters *p*, *q* and *g* as a list of 3 integers. Size of *p* and *q* must comply to `FIPS 186-4`_. If not specified, the parameters are created anew. Returns: :class:`DsaKey` : a new DSA key object Raises: ValueError : when **bits** is too little, too big, or not a multiple of 64. .. _FIPS 186-4: http://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-4.pdf """ if randfunc is None: randfunc = Random.get_random_bytes if domain: p, q, g = map(Integer, domain) ## Perform consistency check on domain parameters # P and Q must be prime fmt_error = test_probable_prime(p) == COMPOSITE fmt_error = test_probable_prime(q) == COMPOSITE # Verify Lagrange's theorem for sub-group fmt_error |= ((p - 1) % q) != 0 fmt_error |= g <= 1 or g >= p fmt_error |= pow(g, q, p) != 1 if fmt_error: raise ValueError("Invalid DSA domain parameters") else: p, q, g, _ = _generate_domain(bits, randfunc) L = p.size_in_bits() N = q.size_in_bits() if L != bits: raise ValueError("Mismatch between size of modulus (%d)" " and 'bits' parameter (%d)" % (L, bits)) if (L, N) not in [(1024, 160), (2048, 224), (2048, 256), (3072, 256)]: raise ValueError("Lengths of p and q (%d, %d) are not compatible" "to FIPS 186-3" % (L, N)) if not 1 < g < p: raise ValueError("Incorrent DSA generator") # B.1.1 c = Integer.random(exact_bits=N + 64) x = c % (q - 1) + 1 # 1 <= x <= q-1 y = pow(g, x, p) key_dict = { 'y':y, 'g':g, 'p':p, 'q':q, 'x':x } return DsaKey(key_dict)
def lucas_test(candidate): """Perform a Lucas primality test on an integer. The test is specified in Section C.3.3 of `FIPS PUB 186-4`__. :Parameters: candidate : integer The number to test for primality. :Returns: ``Primality.COMPOSITE`` or ``Primality.PROBABLY_PRIME``. .. __: http://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-4.pdf """ if not isinstance(candidate, Integer): candidate = Integer(candidate) # Step 1 if candidate.is_even() or candidate.is_perfect_square(): return COMPOSITE # Step 2 def alternate(): sgn = 1 value = 5 for x in xrange(10): yield sgn * value sgn, value = -sgn, value + 2 for D in alternate(): js = Integer.jacobi_symbol(D, candidate) if js == 0: return COMPOSITE if js == -1: break else: return COMPOSITE # Found D. P=1 and Q=(1-D)/4 (note that Q is guaranteed to be an integer) # Step 3 # This is \delta(n) = n - jacobi(D/n) K = candidate + 1 # Step 4 r = K.size_in_bits() - 1 # Step 5 # U_1=1 and V_1=P U_i = Integer(1) V_i = Integer(1) U_temp = Integer(0) V_temp = Integer(0) # Step 6 for i in xrange(r - 1, -1, -1): # Square # U_temp = U_i * V_i % candidate U_temp.set(U_i) U_temp *= V_i U_temp %= candidate # V_temp = (((V_i ** 2 + (U_i ** 2 * D)) * K) >> 1) % candidate V_temp.set(U_i) V_temp *= U_i V_temp *= D V_temp.multiply_accumulate(V_i, V_i) if V_temp.is_odd(): V_temp += candidate V_temp >>= 1 V_temp %= candidate # Multiply if K.get_bit(i): # U_i = (((U_temp + V_temp) * K) >> 1) % candidate U_i.set(U_temp) U_i += V_temp if U_i.is_odd(): U_i += candidate U_i >>= 1 U_i %= candidate # V_i = (((V_temp + U_temp * D) * K) >> 1) % candidate V_i.set(V_temp) V_i.multiply_accumulate(U_temp, D) if V_i.is_odd(): V_i += candidate V_i >>= 1 V_i %= candidate else: U_i.set(U_temp) V_i.set(V_temp) # Step 7 if U_i == 0: return PROBABLY_PRIME return COMPOSITE
def construct(rsa_components, consistency_check=True): """Construct an RSA key from a tuple of valid RSA components. The modulus **n** must be the product of two primes. The public exponent **e** must be odd and larger than 1. In case of a private key, the following equations must apply: - e != 1 - p*q = n - e*d = 1 mod lcm[(p-1)(q-1)] - p*u = 1 mod q :Parameters: rsa_components : tuple A tuple of long integers, with at least 2 and no more than 6 items. The items come in the following order: 1. RSA modulus (*n*). 2. Public exponent (*e*). 3. Private exponent (*d*). Only required if the key is private. 4. First factor of *n* (*p*). Optional, but factor q must also be present. 5. Second factor of *n* (*q*). Optional. 6. CRT coefficient, *(1/p) mod q* (*u*). Optional. consistency_check : boolean If *True*, the library will verify that the provided components fulfil the main RSA properties. :Raise ValueError: When the key being imported fails the most basic RSA validity checks. :Return: An RSA key object (`RsaKey`). """ class InputComps(object): pass input_comps = InputComps() for (comp, value) in zip(('n', 'e', 'd', 'p', 'q', 'u'), rsa_components): setattr(input_comps, comp, Integer(value)) n = input_comps.n e = input_comps.e if not hasattr(input_comps, 'd'): key = RsaKey(n=n, e=e) else: d = input_comps.d if hasattr(input_comps, 'q'): p = input_comps.p q = input_comps.q else: # Compute factors p and q from the private exponent d. # We assume that n has no more than two factors. # See 8.2.2(i) in Handbook of Applied Cryptography. ktot = d * e - 1 # The quantity d*e-1 is a multiple of phi(n), even, # and can be represented as t*2^s. t = ktot while t % 2 == 0: t //= 2 # Cycle through all multiplicative inverses in Zn. # The algorithm is non-deterministic, but there is a 50% chance # any candidate a leads to successful factoring. # See "Digitalized Signatures and Public Key Functions as Intractable # as Factorization", M. Rabin, 1979 spotted = False a = Integer(2) while not spotted and a < 100: k = Integer(t) # Cycle through all values a^{t*2^i}=a^k while k < ktot: cand = pow(a, k, n) # Check if a^k is a non-trivial root of unity (mod n) if cand != 1 and cand != (n - 1) and pow(cand, 2, n) == 1: # We have found a number such that (cand-1)(cand+1)=0 (mod n). # Either of the terms divides n. p = Integer(n).gcd(cand + 1) spotted = True break k *= 2 # This value was not any good... let's try another! a += 2 if not spotted: raise ValueError("Unable to compute factors p and q from exponent d.") # Found ! assert ((n % p) == 0) q = n // p if hasattr(input_comps, 'u'): u = input_comps.u else: u = p.inverse(q) # Build key object key = RsaKey(n=n, e=e, d=d, p=p, q=q, u=u) # Very consistency of the key fmt_error = False if consistency_check: # Modulus and public exponent must be coprime fmt_error = e <= 1 or e >= n fmt_error |= Integer(n).gcd(e) != 1 # For RSA, modulus must be odd fmt_error |= not n & 1 if not fmt_error and key.has_private(): # Modulus and private exponent must be coprime fmt_error = d <= 1 or d >= n fmt_error |= Integer(n).gcd(d) != 1 # Modulus must be product of 2 primes fmt_error |= (p * q != n) fmt_error |= test_probable_prime(p) == COMPOSITE fmt_error |= test_probable_prime(q) == COMPOSITE # See Carmichael theorem phi = (p - 1) * (q - 1) lcm = phi // (p - 1).gcd(q - 1) fmt_error |= (e * d % int(lcm)) != 1 if hasattr(key, 'u'): # CRT coefficient fmt_error |= u <= 1 or u >= q fmt_error |= (p * u % q) != 1 else: fmt_error = True if fmt_error: raise ValueError("Invalid RSA key components") return key
def import_key(extern_key, passphrase=None): """Import an RSA key (public or private half), encoded in standard form. :Parameter extern_key: The RSA key to import, encoded as a byte string. An RSA public key can be in any of the following formats: - X.509 certificate (binary or PEM format) - X.509 ``subjectPublicKeyInfo`` DER SEQUENCE (binary or PEM encoding) - `PKCS#1`_ ``RSAPublicKey`` DER SEQUENCE (binary or PEM encoding) - OpenSSH (textual public key only) An RSA private key can be in any of the following formats: - PKCS#1 ``RSAPrivateKey`` DER SEQUENCE (binary or PEM encoding) - `PKCS#8`_ ``PrivateKeyInfo`` or ``EncryptedPrivateKeyInfo`` DER SEQUENCE (binary or PEM encoding) - OpenSSH (textual public key only) For details about the PEM encoding, see `RFC1421`_/`RFC1423`_. The private key may be encrypted by means of a certain pass phrase either at the PEM level or at the PKCS#8 level. :Type extern_key: string :Parameter passphrase: In case of an encrypted private key, this is the pass phrase from which the decryption key is derived. :Type passphrase: string :Return: An RSA key object (`RsaKey`). :Raise ValueError/IndexError/TypeError: When the given key cannot be parsed (possibly because the pass phrase is wrong). .. _RFC1421: http://www.ietf.org/rfc/rfc1421.txt .. _RFC1423: http://www.ietf.org/rfc/rfc1423.txt .. _`PKCS#1`: http://www.ietf.org/rfc/rfc3447.txt .. _`PKCS#8`: http://www.ietf.org/rfc/rfc5208.txt """ extern_key = tobytes(extern_key) if passphrase is not None: passphrase = tobytes(passphrase) if extern_key.startswith(b('-----')): # This is probably a PEM encoded key. (der, marker, enc_flag) = PEM.decode(tostr(extern_key), passphrase) if enc_flag: passphrase = None return _import_keyDER(der, passphrase) if extern_key.startswith(b('ssh-rsa ')): # This is probably an OpenSSH key keystring = binascii.a2b_base64(extern_key.split(b(' '))[1]) keyparts = [] while len(keystring) > 4: l = struct.unpack(">I", keystring[:4])[0] keyparts.append(keystring[4:4 + l]) keystring = keystring[4 + l:] e = Integer.from_bytes(keyparts[1]) n = Integer.from_bytes(keyparts[2]) return construct([n, e]) if bord(extern_key[0]) == 0x30: # This is probably a DER encoded key return _import_keyDER(extern_key, passphrase) raise ValueError("RSA key format is not supported")