def test_openssh_gauntlet(self): num_runs = 6 num_enc = num_runs // 3 for i in range(num_runs): dsa = DSA() passphrase = None if i < num_enc: passphrase = Bytes.random(Bytes.random(1).int()) priv = dsa.export_private_key(encoding=PKIEncoding.OpenSSH, encryption=b'aes256-ctr', passphrase=passphrase) pub_openssh = dsa.export_public_key(encoding=PKIEncoding.OpenSSH) pub_ssh2 = dsa.export_public_key(encoding=PKIEncoding.SSH2) new_priv = DSA.import_key(priv, passphrase=passphrase) new_pub_openssh = DSA.import_key(pub_openssh) new_pub_ssh2 = DSA.import_key(pub_ssh2) self.assertEqual( (new_priv.p, new_priv.q, new_priv.g, new_priv.x, new_priv.y), (dsa.p, dsa.q, dsa.g, dsa.x, dsa.y)) self.assertEqual((new_pub_openssh.p, new_pub_openssh.q, new_pub_openssh.g, new_pub_openssh.y), (dsa.p, dsa.q, dsa.g, dsa.y)) self.assertEqual((new_pub_ssh2.p, new_pub_ssh2.q, new_pub_ssh2.g, new_pub_ssh2.y), (dsa.p, dsa.q, dsa.g, dsa.y))
def test_crack(self): for i in range(5000): # Fuzz some variables into existence length = max(next_prime(Bytes.random(2).int() % 1000), 5) tap_dist = min(next_prime(length // 2), length - 1) feed = Bytes.random(2).int() % length tap = (feed - tap_dist) % length operation = LFG.ADD_OP if i % 2 else LFG.SUB_OP increment = ((i % 4) // 2) # Set up the LFGs lfg = LFG(state=[Bytes.random(8).int() for _ in range(length)], tap=tap, feed=feed, length=length, operation=operation, increment=increment) num_samples = length + max(1, Bytes.random(1).int() % 50) cracked_lfg = LFG(state=[0], tap=tap, feed=feed, length=length, operation=operation, increment=increment) cracked_lfg.crack([lfg.generate() for _ in range(num_samples)]) # Prove they will always be equivalent by generating a number of values greater than twice the LFG length. This guarantees that 1) the entire state has been modified, and 2) the modified state also produces an equivalent modified state. predicted_values = [cracked_lfg.generate() for _ in range(2000)] real_values = [lfg.generate() for _ in range(2000)] self.assertEqual(predicted_values, real_values)
def test_openssh_gauntlet(self): num_runs = 6 num_enc = num_runs // 3 for i in range(num_runs): bits = 128 + (Bytes.random(2).int() % (4096 - 128)) rsa = RSA(bits) passphrase = None if i < num_enc: passphrase = Bytes.random(Bytes.random(1).int()) priv = rsa.export_private_key(encoding=PKIEncoding.OpenSSH, encryption=b'aes256-ctr', passphrase=passphrase) pub_openssh = rsa.export_public_key(encoding=PKIEncoding.OpenSSH) pub_ssh2 = rsa.export_public_key(encoding=PKIEncoding.SSH2) new_priv = RSA.import_key(priv, passphrase=passphrase) new_pub_openssh = RSA.import_key(pub_openssh) new_pub_ssh2 = RSA.import_key(pub_ssh2) self.assertEqual( (new_priv.d, new_priv.e, new_priv.n, new_priv.p, new_priv.q), (rsa.d, rsa.e, rsa.n, rsa.p, rsa.q)) self.assertEqual((new_pub_openssh.e, new_pub_openssh.n), (rsa.e, rsa.n)) self.assertEqual((new_pub_ssh2.e, new_pub_ssh2.n), (rsa.e, rsa.n))
def __init__(self, h: int = 2, p: int = DiffieHellman.MODP_1536, key: bytes = None, exp1: int = None, exp2: int = None, validate: bool = True): """ Parameters: h (int): Generator. p (int): Prime modulus. key (bytes): Secret. exp1 (int): First random exponent. exp2 (int): Second random exponent. validate (bool): Whether or not to validate challenges to prevent exploits. """ self.h = h self.p = p self.key = Bytes.wrap(key).int() or Bytes.random(16).int() self.validate = validate # We do this explicitly with None so users can easily set these values to zero :) if exp1 is None: exp1 = Bytes.random(16).int() if exp2 is None: exp2 = Bytes.random(16).int() self.exp1 = exp1 self.exp2 = exp2 self.P_b = None self.P = None self.Q = None
def test_gauntlet(self): for block_size in range(8, 32): pkcs = PKCS7(block_size) for _ in range(1000): plaintext = Bytes.random(Bytes.random(1).int() % block_size) self.assertEqual(pkcs.unpad(pkcs.pad(plaintext)), plaintext)
def test_openssh_gauntlet(self): num_runs = 6 num_enc = num_runs // 3 curves = [P192, P224, P256, P384, P521] for i in range(num_runs): curve = random.choice(curves) ecdsa = ECDSA(curve.G) passphrase = None if i < num_enc: passphrase = Bytes.random(Bytes.random(1).int()) priv = ecdsa.export_private_key(encoding=PKIEncoding.OpenSSH, encryption=b'aes256-ctr', passphrase=passphrase) pub_openssh = ecdsa.export_public_key(encoding=PKIEncoding.OpenSSH) pub_ssh2 = ecdsa.export_public_key(encoding=PKIEncoding.SSH2) new_priv = ECDSA.import_key(priv, passphrase=passphrase) new_pub_openssh = ECDSA.import_key(pub_openssh) new_pub_ssh2 = ECDSA.import_key(pub_ssh2) self.assertEqual((new_priv.d, new_priv.G, new_priv.Q), (ecdsa.d, ecdsa.G, ecdsa.Q)) self.assertEqual((new_pub_openssh.G, new_pub_openssh.Q), (ecdsa.G, ecdsa.Q)) self.assertEqual((new_pub_ssh2.G, new_pub_ssh2.Q), (ecdsa.G, ecdsa.Q))
def crossover_func(parents): if Bytes.random(1).int() < 32: crossover_idx = Bytes.random(1).int() % len(hamlet_excerpt) ret = parents[0].state[:crossover_idx] + parents[1].state[ crossover_idx:] else: ret = parents[0].state return ret
def mutation_func(individual): if Bytes.random(1).int() < 32: mutation_idx = Bytes.random(1).int() % len(hamlet_excerpt) mutation = random.choice(valid_chars) individual.state = individual.state[: mutation_idx] + mutation + individual.state[ mutation_idx + 1:] return individual
def _run_tests(self, hash_type, reference_method): for i in range(hash_type().block_size // 8): for _ in range(100): key = Bytes.random(i * 8) in_bytes = Bytes.random(i * 32) samson_hash = HMAC(key=key, hash_obj=hash_type()) self.assertEqual( samson_hash.generate(in_bytes), pyhmac.HMAC(key, in_bytes, reference_method).digest())
def test_gauntlet(self): for _ in range(10): bits = max(16, Bytes.random(2).int() >> 4) rsa = RSA(bits, e=65537) for _ in range(10): plaintext = Bytes.random((bits // 8) - 1) ciphertext = rsa.encrypt(plaintext) self.assertEqual( rsa.decrypt(ciphertext).zfill(len(plaintext)), plaintext)
def test_gauntlet(self): rij = Rijndael(Bytes(0x0).zfill(32)) cts = ECBCTS(rij) for _ in range(100): plaintext = Bytes.random(Bytes.random(1).int() + 17) if len(plaintext) < 17: plaintext = plaintext.zfill(17) ciphertext = cts.encrypt(plaintext) self.assertEqual(cts.decrypt(ciphertext), plaintext)
def test_correctness(self): for _ in range(100): seed = Bytes.random(16).int() a = Bytes.random(2).int() m = Bytes.random(12).int() c = Bytes.random(2).int() lcg = LCG(X=seed, a=a, c=c, m=m) ref_lcg = wiki_lcg(m, a, c, seed).__next__ self.assertEqual([lcg.generate() for _ in range(10000)], [ref_lcg() for _ in range(10000)])
def test_import_enc_gauntlet(self): supported_algos = RFC1423_ALGOS.keys() for algo in supported_algos: for _ in range(10): rsa = RSA(512) key = Bytes.random(Bytes.random(1).int() + 1) enc_pem = rsa.export_private_key(encryption=algo, passphrase=key) dec_rsa = RSA.import_key(enc_pem, key) self.assertEqual((rsa.d, rsa.e, rsa.p, rsa.q), (dec_rsa.d, dec_rsa.e, dec_rsa.p, dec_rsa.q))
def test_import_enc_gauntlet(self): supported_algos = RFC1423_ALGOS.keys() for algo in supported_algos: for _ in range(10): dsa = DSA(None) key = Bytes.random(Bytes.random(1).int() + 1) enc_pem = dsa.export_private_key(encryption=algo, passphrase=key) dec_dsa = DSA.import_key(enc_pem, key) self.assertEqual((dsa.p, dsa.q, dsa.g, dsa.y), (dec_dsa.p, dec_dsa.q, dec_dsa.g, dec_dsa.y))
def test_import_enc_gauntlet(self): supported_algos = RFC1423_ALGOS.keys() for algo in supported_algos: for _ in range(10): ecdsa = ECDSA(G=P256.G, hash_obj=None) key = Bytes.random(Bytes.random(1).int() + 1) enc_pem = ecdsa.export_private_key(encryption=algo, passphrase=key) dec_ecdsa = ECDSA.import_key(enc_pem, key) self.assertEqual((ecdsa.G, ecdsa.d, ecdsa.Q), (dec_ecdsa.G, dec_ecdsa.d, dec_ecdsa.Q))
def encrypt(self, m: int) -> DGHVBit: """ Encrypts a bit `m`. Parameters: m (int): Number to encrypt. Returns: DGHVBit: Encrypted bit. """ q = Bytes.random(8).to_int() r = Bytes.random(8).to_int() % (self.p // 4) return DGHVBit(self.p * q + 2 * r + m)
def test_xor(self): dghv = DGHV() for _ in range(1000): a, b = Bytes.random(4).to_int(), Bytes.random(4).to_int() a_bin = [int(char) for char in bin(a)[2:].zfill(32)] b_bin = [int(char) for char in bin(b)[2:].zfill(32)] a_c = [dghv.encrypt(a_int) for a_int in a_bin] b_c = [dghv.encrypt(b_int) for b_int in b_bin] xord = [a_int + b_int for a_int, b_int in zip(a_c, b_c)] assert int(''.join([str(dghv.decrypt(x_int)) for x_int in xord]), 2) == (a ^ b)
def test_der_encode(self): for _ in range(20): bits = max(1, Bytes.random(2).int() >> 4) rsa = RSA(bits, e=65537) should_pem_encode = Bytes.random(1).int() & 1 der_bytes = rsa.export_private_key(should_pem_encode) recovered_rsa = RSA.import_key(der_bytes) self.assertEqual( (rsa.d, rsa.e, rsa.n, rsa.p, rsa.q), (recovered_rsa.d, recovered_rsa.e, recovered_rsa.n, recovered_rsa.p, recovered_rsa.q))
def test_whirlpool(self): whirlpool = Whirlpool() for i in range(9): for j in range(100): in_bytes = Bytes.random(i * j) self.assertEqual(whirlpool.hash(in_bytes), hashlib.new('whirlpool', in_bytes).digest())
def receive_initial_challenge(self, challenge: (int, int), r: int = None) -> (int, int): """ Receives the Diffie-Hellman challenges and produces the next challenge parameters. Parameters: challenge ((int, int)): Challenge from peer. r (int): Ephemeral random exponent. Returns: (int, int): P and Q values to send to its peer. """ h_a1, h_a2 = challenge if self.validate: assert h_a1 != 1 assert h_a2 != 1 r = r or Bytes.random(16).int() g, R = pow(h_a1, self.exp1, self.p), pow(h_a2, self.exp2, self.p) self.P = pow(R, r, self.p) self.Q = (pow(self.h, r, self.p) * pow(g, self.key, self.p)) % self.p return self.P, self.Q
def derive(self, password: bytes, salt: bytes=None, format_output: bool=True) -> Bytes: """ Derives the bcrypt hash. Parameters: password (bytes): Password. salt (bytes): Salt. format_output (bool): Whether or not to use bcrypt formatting or just output the hash. Returns: Bytes: Derived key/hash. """ if not salt: salt = Bytes.random(16) salt = Bytes.wrap(salt) password = Bytes.wrap(password) bf = self.eks_blowfish_setup(salt, password) ciphertext = self.constant ecb = ECB(bf) for _ in range(64): ciphertext = ecb.encrypt(ciphertext) to_return = ciphertext[:self.output_size] if format_output: to_return = Bytes(f'${self.version}$'.encode('utf-8') + str(self.cost).zfill(2).encode('utf-8') + b'$' + bcrypt_b64_encode(salt) + bcrypt_b64_encode(ciphertext[:self.output_size])) return to_return
def test_sha1(self): sha1 = SHA1() for i in range(9): for _ in range(100): in_bytes = Bytes.random(i * 32) self.assertEqual(sha1.hash(in_bytes), hashlib.sha1(in_bytes).digest())
def __init__(self, name: str, check_bytes: bytes = None, n: int = None, e: int = None, d: int = None, q_mod_p: int = None, p: int = None, q: int = None, host: bytes = None): """ Parameters: name (str): Name for bookkeeping purposes. check_bytes (bytes): Four random bytes repeated for OpenSSH to check if the decryption worked. n (int): RSA modulus. e (int): RSA public exponent. q_mod_p (int): RSA q^{-1} mod p. p (int): RSA secret prime. q (int): RSA secret prime. host (bytes): Host the key was generated on. """ self.name = name self.check_bytes = check_bytes or Bytes.random(4) * 2 self.n = n self.e = e self.d = d self.q_mod_p = q_mod_p self.p = p self.q = q self.host = host
def __init__(self, curve: TwistedEdwardsCurve = EdwardsCurve25519, hash_obj: object = SHA512(), d: int = None, A: TwistedEdwardsPoint = None, a: int = None, h: bytes = None, clamp: bool = True): """ Parameters: curve (TwistedEdwardsCurve): Curve used for calculations. hash_obj (object): Instantiated object with compatible hash interface. d (int): Private key. A (TwistedEdwardsPoint): (Optional) Public point. a (int): (Optional) Public scalar. h (bytes): (Optional) Hashed private key. clamp (bool): Whether or not to clamp the public scalar. """ Primitive.__init__(self) self.B = curve.B self.curve = curve self.d = Bytes.wrap(d or max(1, Bytes.random(hash_obj.digest_size).int())) self.H = hash_obj self.h = h or hash_obj.hash(self.d) a = a or self.h[:self.curve.b // 8].int() self.a = curve.clamp_to_curve(a, True) if clamp else a self.A = A or self.B * self.a
def test_against_random(self): for english_val in ENGLISH_VALUES: all_values = [Bytes.random(len(english_val)) for _ in range(1000)] + [english_val] shuffle(all_values) self._check_best_sample(english_val, all_values)
def test_md5(self): md5 = MD5() for i in range(9): for _ in range(100): in_bytes = Bytes.random(i * 32) self.assertEqual(md5.hash(in_bytes), hashlib.md5(in_bytes).digest())
def test_ripemd160(self): ripe = RIPEMD160() for i in range(258): for _ in range(10): test_bytes = Bytes.random(i) self.assertEqual(ripe.hash(test_bytes), hashlib.new('ripemd160', test_bytes).digest())
def test_gauntlet(self): for _ in range(100): plaintext = Bytes.random(16) rij = Rijndael(Bytes.random(16)) ocb = OCB2(rij) nonce = Bytes.random(16) def oracle_func(plaintext, data): return ocb.encrypt(nonce, plaintext, data) attack = OCBAuthForgeryAttack(ChosenPlaintextOracle(oracle_func)) tag, ct = attack.execute(plaintext) # OCB2 will automatically verify and throw an AssertException if the tag is incorrect ocb.decrypt(nonce, (tag, ct), verify=True)
def __init__(self, cipher: EncryptionAlg = None, iv: bytes = b'\x00' * 16): """ Parameters: cipher (EncryptionAlg): Instantiated encryption algorithm. iv (bytes): Initialization vector. """ self.cmac = CMAC(cipher or Rijndael(Bytes.random(32))) self.iv = iv
def __init__(self, cipher: EncryptionAlg=None, iv: bytes=b'\x00' * 16): """ Parameters: cipher (EncryptionAlg): Instantiated encryption algorithm. iv (bytes): Initialization vector for CBC mode. """ Primitive.__init__(self) self.cbc = CBC(cipher or Rijndael(Bytes.random(32)), iv)