def miller_rabin_primality_testing(n, k): """Calculates whether n is composite (which is always correct) or prime (which theoretically is incorrect with error probability 4**-k), by applying Miller-Rabin primality testing. For reference and implementation example, see: # :param n: Integer to be tested for primality. :type n: int :param k: Number of rounds (witnesses) of Miller-Rabin testing. :type k: int :return: False if the number is composite, True if it's probably prime. :rtype: bool """ # prevent potential infinite loop when d = 0 if n < 2: return False # Decompose (n - 1) to write it as (2 ** r) * d # While d is even, divide it by 2 and increase the exponent. d = n - 1 r = 0 while not (d & 1): r += 1 d >>= 1 # Test k witnesses. for _ in range(k): # Generate random integer a, where 2 <= a <= (n - 2) a = rsa.randnum.randint(n - 3) + 1 x = pow(a, d, n) if x == 1 or x == n - 1: continue for _ in range(r - 1): x = pow(x, 2, n) if x == 1: # n is composite. return False if x == n - 1: # Exit inner loop and continue with next witness. break else: # If loop doesn't break, n is composite. return False return True
def miller_rabin_primality_testing(n, k): """Calculates whether n is composite (which is always correct) or prime (which theoretically is incorrect with error probability 4**-k), by applying Miller-Rabin primality testing. For reference and implementation example, see: https://en.wikipedia.org/wiki/Miller%E2%80%93Rabin_primality_test :param n: Integer to be tested for primality. :type n: int :param k: Number of rounds (witnesses) of Miller-Rabin testing. :type k: int :return: False if the number is composite, True if it's probably prime. :rtype: bool """ # prevent potential infinite loop when d = 0 if n < 2: return False # Decompose (n - 1) to write it as (2 ** r) * d # While d is even, divide it by 2 and increase the exponent. d = n - 1 r = 0 while not (d & 1): r += 1 d >>= 1 # Test k witnesses. for _ in range(k): # Generate random integer a, where 2 <= a <= (n - 2) a = rsa.randnum.randint(n - 3) + 1 x = pow(a, d, n) if x == 1 or x == n - 1: continue for _ in range(r - 1): x = pow(x, 2, n) if x == 1: # n is composite. return False if x == n - 1: # Exit inner loop and continue with next witness. break else: # If loop doesn't break, n is composite. return False return True
def verify(message, signature, pub_key, hasher='SHA-1', salt_len=None): # type: (bytes, bytes, PublicKey, str, int) -> bool # Determine the size of the hash output (hLen) try: h_len = pkcs1.HASH_METHODS[hasher]().digest_size except KeyError: raise ValueError( 'Invalid `hasher` specified. Please select one of: {hash_list}'. format(hash_list=', '.join(sorted(pkcs1.HASH_METHODS.keys())))) # Determine the size of the public key in bytes (k) k = common.byte_size(pub_key.n) mod_bits = k * 8 - 1 em_len = math.ceil(mod_bits / 8) s = transform.bytes2int(signature) m = core.decrypt_int( s, pub_key.e, pub_key.n) # Use encrypt_int because of the additional range-checking em = transform.int2bytes(m, em_len) # EMSA-PSS-VERIFY (m, em, mod_bits) if len(message) > 2**61 - 1: raise VerificationError('Incorrect signature') m_hash = pkcs1.compute_hash(message, hasher) s_len = salt_len if salt_len is not None else h_len if em_len < h_len + s_len + 2: raise VerificationError('Incorrect signature') if em[-1] != 0xbc: raise VerificationError('Incorrect signature') masked_db, h = em[:em_len - h_len - 1], em[em_len - h_len - 1:-1] for i in range(8 * em_len - mod_bits): if masked_db[0] & (1 << (7 - i)) != 0: raise VerificationError('Incorrect signature') db_mask = mgf1(h, em_len - h_len - 1, hasher) db = bytearray(common.xor(masked_db, db_mask)) a = 0xff for _ in range(8 * em_len - mod_bits): a = a >> 1 db[0] &= a for i in range(em_len - h_len - s_len - 2): if db[i] != 0: raise VerificationError('Incorrect signature') if db[em_len - h_len - s_len - 2] != 0x01: raise VerificationError('Incorrect signature') salt = db[-s_len:] if s_len > 0 else b'' m2 = b''.join((b'\x00' * 8, m_hash, salt)) h2 = pkcs1.compute_hash(m2, hasher) return h == h2
def test_load_malformed_private_key(self, der_decode): """Test loading malformed private DER keys.""" # Decode returns an invalid exp2 value. der_decode.return_value = ( [0, 3727264081, 65537, 3349121513, 65063, 57287, 55063, 0, 50797], 0, ) with warnings.catch_warnings(record=True) as w: # Always print warnings warnings.simplefilter('always') # Load 3 keys for _ in range(3): key = rsa.key.PrivateKey.load_pkcs1(PRIVATE_DER, 'DER') # Check that 3 warnings were generated. self.assertEqual(3, len(w)) for warning in w: self.assertTrue(issubclass(warning.category, UserWarning)) self.assertIn('malformed', str(warning.message)) # Check that we are creating the key with correct values self.assertEqual(key.exp1, 55063) self.assertEqual(key.exp2, 10095) self.assertEqual(key.coef, 50797)
def save_pem(contents, pem_marker): """Saves a PEM file. :param contents: the contents to encode in PEM format :param pem_marker: the marker of the PEM content, such as 'RSA PRIVATE KEY' when your file has '-----BEGIN RSA PRIVATE KEY-----' and '-----END RSA PRIVATE KEY-----' markers. :return: the base64-encoded content between the start and end markers, as bytes. """ (pem_start, pem_end) = _markers(pem_marker) b64 = base64.standard_b64encode(contents).replace(b('\n'), b('')) pem_lines = [pem_start] for block_start in range(0, len(b64), 64): block = b64[block_start:block_start + 64] pem_lines.append(block) pem_lines.append(pem_end) pem_lines.append(b('')) return b('\n').join(pem_lines)
def save_pem(contents, pem_marker): """Saves a PEM file. :param contents: the contents to encode in PEM format :param pem_marker: the marker of the PEM content, such as 'RSA PRIVATE KEY' when your file has '-----BEGIN RSA PRIVATE KEY-----' and '-----END RSA PRIVATE KEY-----' markers. :return: the base64-encoded content between the start and end markers, as bytes. """ (pem_start, pem_end) = _markers(pem_marker) b64 = ubinascii.b2a_base64(contents).replace(b'\n', b'') pem_lines = [pem_start] for block_start in range(0, len(b64), 64): block = b64[block_start:block_start + 64] pem_lines.append(block) pem_lines.append(pem_end) pem_lines.append(b'') return b'\n'.join(pem_lines)
def mgf1(seed, length, hasher='SHA-1'): """ MGF1 is a Mask Generation Function based on a hash function. A mask generation function takes an octet string of variable length and a desired output length as input, and outputs an octet string of the desired length. The plaintext-awareness of RSAES-OAEP relies on the random nature of the output of the mask generation function, which in turn relies on the random nature of the underlying hash. :param bytes seed: seed from which mask is generated, an octet string :param int length: intended length in octets of the mask, at most 2^32(hLen) :param str hasher: hash function (hLen denotes the length in octets of the hash function output) :return: mask, an octet string of length `length` :rtype: bytes :raise OverflowError: when `length` is too large for the specified `hasher` :raise ValueError: when specified `hasher` is invalid """ try: hash_length = pkcs1.HASH_METHODS[hasher]().digest_size except KeyError: raise ValueError( 'Invalid `hasher` specified. Please select one of: {hash_list}'.format( hash_list=', '.join(sorted(pkcs1.HASH_METHODS.keys())) ) ) # If l > 2^32(hLen), output "mask too long" and stop. if length > (2**32 * hash_length): raise OverflowError( "Desired length should be at most 2**32 times the hasher's output " "length ({hash_length} for {hasher} function)".format( hash_length=hash_length, hasher=hasher, ) ) # Looping `counter` from 0 to ceil(l / hLen)-1, build `output` based on the # hashes formed by (`seed` + C), being `C` an octet string of length 4 # generated by converting `counter` with the primitive I2OSP output = b''.join( pkcs1.compute_hash( seed + transform.int2bytes(counter, fill_size=4), method_name=hasher, ) for counter in range(common.ceil_div(length, hash_length) + 1) ) # Output the leading `length` octets of `output` as the octet string mask. return output[:length]
def split_data_block(_db, _h_len): # type: (bytes, bytes) -> (bytes, bytes) _l_hash = _db[0:_h_len] i = _h_len for i in range(_h_len, len(_db)): d = _db[i] if d != 0: break if _db[i] != 0x01: raise DecryptionError('Decryption error') _m = _db[i + 1:] return _l_hash, _m
def test_is_prime(self): """Test some common primes.""" # Test some trivial numbers self.assertFalse(rsa.prime.is_prime(-1)) self.assertFalse(rsa.prime.is_prime(0)) self.assertFalse(rsa.prime.is_prime(1)) self.assertTrue(rsa.prime.is_prime(2)) self.assertFalse(rsa.prime.is_prime(42)) self.assertTrue(rsa.prime.is_prime(41)) # Test some slightly larger numbers self.assertEqual( [907, 911, 919, 929, 937, 941, 947, 953, 967, 971, 977, 983, 991, 997], [x for x in range(901, 1000) if rsa.prime.is_prime(x)] ) # Test around the 50th millionth known prime. self.assertTrue(rsa.prime.is_prime(982451653)) self.assertFalse(rsa.prime.is_prime(982451653 * 961748941))
def getprime(nbits, poolsize): """Returns a prime number that can be stored in 'nbits' bits. Works in multiple threads at the same time. >>> p = getprime(128, 3) >>> rsa.prime.is_prime(p-1) False >>> rsa.prime.is_prime(p) True >>> rsa.prime.is_prime(p+1) False >>> from rsa import common >>> common.bit_size(p) == 128 True """ (pipe_recv, pipe_send) = mp.Pipe(duplex=False) # Create processes try: procs = [ mp.Process(target=_find_prime, args=(nbits, pipe_send)) for _ in range(poolsize) ] # Start processes for p in procs: p.start() result = pipe_recv.recv() finally: pipe_recv.close() pipe_send.close() # Terminate processes for p in procs: p.terminate() return result
def sign(message, priv_key, hasher='SHA-1', salt_len=None): # type: (bytes, PrivateKey, str, int) -> bytes # Determine the size of the hash output (hLen) try: h_len = pkcs1.HASH_METHODS[hasher]().digest_size except KeyError: raise ValueError( 'Invalid `hasher` specified. Please select one of: {hash_list}'. format(hash_list=', '.join(sorted(pkcs1.HASH_METHODS.keys())))) # Determine the size of the public key in bytes (k) k = common.byte_size(priv_key.n) mod_bits = k * 8 - 1 em_len = math.ceil(mod_bits / 8) if len(message) > 2**61 - 1: raise OverflowError('message too long') m_hash = pkcs1.compute_hash(message, hasher) s_len = salt_len if salt_len is not None else h_len if em_len < h_len + s_len + 2: raise SigningError('Encoding error') salt = b'' if s_len == 0 else randnum.read_random_bits(s_len * 8) m2 = b''.join((b'\x00' * 8, m_hash, salt)) h = pkcs1.compute_hash(m2, hasher) ps = b'\x00' * (em_len - s_len - h_len - 2) db = b'\x01'.join((ps, salt)) db_mask = mgf1(h, em_len - h_len - 1, hasher) masked_db = bytearray(common.xor(db, db_mask)) a = 0xff for _ in range(8 * em_len - mod_bits): a = a >> 1 masked_db[0] &= a # for i in range(8*em_len - mod_bits + 1): # masked_db[0] &= ~(1 << (7-i)) em = b''.join((masked_db, h, b'\xbc')) m = transform.bytes2int(em) # s = priv_key.blinded_encrypt(m) s = core.encrypt_int(m, priv_key.d, priv_key.n) sig = transform.int2bytes(s, k) return sig
def getprime(nbits, poolsize): """Returns a prime number that can be stored in 'nbits' bits. Works in multiple threads at the same time. >>> p = getprime(128, 3) >>> rsa.prime.is_prime(p-1) False >>> rsa.prime.is_prime(p) True >>> rsa.prime.is_prime(p+1) False >>> from rsa import common >>> common.bit_size(p) == 128 True """ (pipe_recv, pipe_send) = mp.Pipe(duplex=False) # Create processes try: procs = [mp.Process(target=_find_prime, args=(nbits, pipe_send)) for _ in range(poolsize)] # Start processes for p in procs: p.start() result = pipe_recv.recv() finally: pipe_recv.close() pipe_send.close() # Terminate processes for p in procs: p.terminate() return result
:param clearsig: full padded ASN1 and hash. :return: the used hash method. :raise VerificationFailed: when the hash method cannot be found """ for (hashname, asn1code) in HASH_ASN1.items(): if asn1code in clearsig: return hashname raise VerificationError('Verification failed') __all__ = [ 'encrypt', 'decrypt', 'sign', 'verify', 'DecryptionError', 'VerificationError', 'CryptoError' ] if __name__ == '__main__': print('Running doctests 1000x or until failure') import doctest for count in range(1000): (failures, tests) = doctest.testmod() if failures: break if count and count % 100 == 0: print('%i times' % count) print('Doctests done')
def test_byte(self): for i in range(256): byt = byte(i) self.assertTrue(is_bytes(byt)) self.assertEqual(ord(byt), i)
# Generate the key components (p, q, e, d) = gen_keys(nbits, getprime_func, accurate=accurate, exponent=exponent) # Create the key objects n = p * q return ( PublicKey(n, e), PrivateKey(n, e, d, p, q) ) __all__ = ['PublicKey', 'PrivateKey', 'newkeys'] if __name__ == '__main__': import doctest try: for count in range(100): (failures, tests) = doctest.testmod() if failures: break if (count and count % 10 == 0) or count == 1: print('%i times' % count) except KeyboardInterrupt: print('Aborted') else: print('Doctests done')
def _get_blinding_factor(self): for _ in range(1000): blind_r = rsa.randnum.randint(self.n - 1) if rsa.prime.are_relatively_prime(self.n, blind_r): return blind_r raise RuntimeError('unable to find blinding factor')