def test_gauntlet(self): for i in range(3): pkcs = PKCS1v15Padding(1024, block_type=i) for _ in range(10000): plaintext = Bytes.random(8) unpadded = pkcs.unpad(pkcs.pad(plaintext)) if i == 0: unpadded = unpadded.zfill(len(plaintext)) self.assertEqual(unpadded, plaintext)
def decode_point(x_y_bytes: bytes): x_y_bytes = Bytes.wrap(x_y_bytes) # Uncompressed Point if x_y_bytes[0] == 4: x_y_bytes = x_y_bytes[1:] else: raise NotImplementedError( "Support for ECPoint decompression not implemented.") x, y = x_y_bytes[:len(x_y_bytes) // 2].int(), x_y_bytes[len(x_y_bytes) // 2:].int() return x, y
def __init__(self, key: bytes, nonce: bytes, r: bytes, cipher=Rijndael): """ Parameters: key (bytes): Bytes-like object to key the underlying cipher. nonce (bytes): Bytes-like nonce. r (bytes): Bytes-like polynomial. cipher (class): Instantiable class representing a block cipher. """ Primitive.__init__(self) self.key = key self.nonce = nonce self.r = Bytes.wrap(r, byteorder='little').to_int() self.cipher = cipher
def decrypt(self, key_and_ciphertext: (int, int)) -> Bytes: """ Decrypts `key_and_ciphertext`. Parameters: key_and_ciphertext ((int, int)): Ephemeral key and ciphertext. Returns: Bytes: Plaintext. """ c_1, ciphertext = key_and_ciphertext s = pow(c_1, self.key, self.p) return Bytes((mod_inv(s, self.p) * ciphertext) % self.p)
def encrypt(self, plaintext: bytes) -> Bytes: """ Encrypts `plaintext`. Parameters: plaintext (bytes): Bytes-like object to be encrypted. Returns: Bytes: Resulting ciphertext. """ keystream = Bytes(b'') plaintext = Bytes.wrap(plaintext) num_blocks = ceil(len(plaintext) / self.cipher.block_size) for _ in range(num_blocks): keystream += self.cipher.encrypt( self.nonce + self.counter.to_bytes(self.cipher.block_size - len(self.nonce), self.byteorder)) self.counter += 1 return keystream[:len(plaintext)] ^ plaintext
def pack(self, value: bytes, force_pack: bool = False) -> Bytes: """ Packs bytes-coercible objects into length-encoded bytes. Parameters: value (bytes): Value to encode. force_pack (bool): Whether or not to pack zero-length values. Returns: bytes: Packed bytes. """ val = Bytes.wrap(value, byteorder=self.endianness) if issubclass(type(value), int): val = val.zfill(math.ceil((value.bit_length() + 1) / 8)) if len(val) > 0 or force_pack: length = Bytes(len(val)).zfill(4) else: length = b'' return length + val
def compression_func(chunk, state): """Process a chunk of data and return the new digest variables.""" assert len(chunk) == 64 w = [0] * 80 # Break chunk into sixteen 4-byte big-endian words w[i] for i in range(16): w[i] = struct.unpack(b'>I', chunk[i * 4:i * 4 + 4])[0] # Extend the sixteen 4-byte words into eighty 4-byte words for i in range(16, 80): w[i] = left_rotate(w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16], 1) # Initialize hash value for this chunk h0, h1, h2, h3, h4 = bytes_to_state(state) a = h0 b = h1 c = h2 d = h3 e = h4 for i in range(80): if 0 <= i <= 19: # Use alternative 1 for f from FIPS PB 180-1 to avoid bitwise not f = d ^ (b & (c ^ d)) k = 0x5A827999 elif 20 <= i <= 39: f = b ^ c ^ d k = 0x6ED9EBA1 elif 40 <= i <= 59: f = (b & c) | (b & d) | (c & d) k = 0x8F1BBCDC elif 60 <= i <= 79: f = b ^ c ^ d k = 0xCA62C1D6 a, b, c, d, e = ((left_rotate(a, 5) + f + e + k + w[i]) & 0xffffffff, a, left_rotate(b, 30), c, d) # Add this chunk's hash to result so far h0 = (h0 + a) & 0xffffffff h1 = (h1 + b) & 0xffffffff h2 = (h2 + c) & 0xffffffff h3 = (h3 + d) & 0xffffffff h4 = (h4 + e) & 0xffffffff state = [h0, h1, h2, h3, h4] return Bytes(state_to_bytes(state))
def hash(self, message: bytes) -> Bytes: """ Hashes the `message`. Parameters: message (bytes): Message to be hashed. Returns: Bytes: The hash digest. """ message = Bytes(message, 'little') state = deepcopy(self.IV) last_block_size = len(message) % self.IMPL_BLOCK_SIZE if last_block_size == 0 and len(message) > 0: last_block_size = self.IMPL_BLOCK_SIZE state[0] ^= (0x0101 << 16) + (len(self.key) << 8) + (self.digest_size) if len(self.key) > 0: message = self.padding_func(self.key) + message padded_msg = self.padding_func(message) bytes_compressed = 0 msg_chunks = padded_msg.chunk(self.IMPL_BLOCK_SIZE) for i, chunk in enumerate(msg_chunks): is_last_block = i == (len(msg_chunks) - 1) bytes_compressed += last_block_size if is_last_block else self.IMPL_BLOCK_SIZE state = self.compress(state, self.IV, chunk, bytes_compressed, is_last_block) return sum([ Bytes(h, byteorder='little').zfill(self.WORD_SIZE // 8) for h in state ])[:self.digest_size]
def pad(self, plaintext: bytes, seed: bytes=None) -> Bytes: """ Pads the `plaintext`. Parameters: plaintext (bytes): Plaintext to pad. seed (bytes): (Optional) Random seed for the MGF. Returns: Bytes: Padded plaintext. """ plaintext = Bytes.wrap(plaintext) k = (self.modulus_len + 7) // 8 # Step 1: Length checking h_len = self.hash_obj.digest_size m_len = len(plaintext) ps_len = k - m_len - (2 * h_len) - 2 if ps_len < 0: raise ValueError("Plaintext is too long") # Step 2: EME-OAEP encoding l_hash = self.hash_obj.hash(self.label) ps = Bytes(b'').zfill(ps_len) db = l_hash + ps + b'\x01' + plaintext seed = seed or Bytes.random(h_len) db_mask = self.mgf(seed, k - h_len - 1) masked_db = db ^ db_mask seed_mask = self.mgf(masked_db, h_len) masked_seed = seed ^ seed_mask return b'\x00' + masked_seed + masked_db
def test_all_vecs(self): for key, nonce, data, size, plaintext, expected_ciphertext in TEST_VECTORS: key = Bytes(key) nonce = Bytes(nonce) data = Bytes(data) data = data.zfill(len(data) + 1) plaintext = Bytes(plaintext) ccm = CCM(Rijndael(key), size) ciphertext = ccm.encrypt(nonce, plaintext, data) self.assertEqual(ciphertext, Bytes(expected_ciphertext)) self.assertEqual(ccm.decrypt(nonce, ciphertext, data), plaintext)
def decrypt(self, ciphertext: bytes) -> Bytes: """ Decrypts `ciphertext`. Parameters: ciphertext (bytes): Bytes-like object to be decrypted. Returns: Bytes: Resulting plaintext. """ ciphertext = Bytes.wrap(ciphertext) C = ciphertext.int() kw = [self.kw[2], self.kw[3], self.kw[0], self.kw[1]] ke = self.ke[::-1] k = self.k[::-1] D1 = C >> 64 D2 = C & MASK64 D1 = D1 ^ kw[0] D2 = D2 ^ kw[1] num_super_rounds = 3 if len(self.key) == 16 else 4 for i in range(num_super_rounds): if i > 0: D1 = FL (D1, ke[(i-1) * 2]) D2 = FLINV(D2, ke[(i-1) * 2 + 1]) for j in range(0, 6, 2): D2 ^= self.F(D1, k[i*6 + j]) D1 ^= self.F(D2, k[i*6 + j +1]) D2 = D2 ^ kw[2] D1 = D1 ^ kw[3] return Bytes(((D2 & MASK64) << 64) | (D1 & MASK64)).zfill(16)
def _generate_mac(self, nonce: bytes, plaintext: bytes, data: bytes) -> bytes: data_len, _q, _flags, b_0 = self._calculate_formatting_params(nonce, plaintext, data) data_len_encoded = b'' if data_len > 0: if data_len < ((2 ** 16) - (2 ** 8)): size = 2 elif data_len < (2 ** 32): data_len_encoded = b'\xFF\xFE' size = 4 else: data_len_encoded = b'\xFF\xFF' size = 8 data_len_encoded += int.to_bytes(data_len, size, 'big') padded_data = Bytes.wrap(data_len_encoded + data).pad_congruent_right(16) padded_plaintext = Bytes.wrap(plaintext).pad_congruent_right(16) T = self.cmac.generate(b_0 + padded_data + padded_plaintext, pad=False) return T
def __init__(self, cost: int, constant: bytes=CONSTANT, output_size: int=23, version: str='2a', use_specs_eks: bool=False): """ Parameters: cost (int): Cost factor. constant (bytes): (Optional) The constant or magic to use for bcrypt. output_size (int): (Optional) Size to limit output to. version (str): Version of bcrypt to use. Only supports '2a' for now (pads with a NUL byte if '2a'). use_specs_eks (bool): Use the original specification's order for processing salt and password. """ self.cost = cost self.constant = Bytes.wrap(constant) self.output_size = output_size self.version = version self.use_specs_eks = use_specs_eks
def __init__(self, oracle: PaddingOracle, iv: bytes, block_size: int=16, alphabet=[byte for byte in range(256)], batch_requests: bool=False): """ Parameters: oracle (PaddingOracle): An oracle that takes in a bytes-like object and returns a boolean indicating whether the padding was correct. iv (bytes): Initialization vector (or previous ciphertext block) of the ciphertext to crack. block_size (int): Block size of the block cipher being used. """ self.oracle = oracle self.iv = Bytes.wrap(iv) self.block_size = block_size self.alphabet = alphabet self.batch_requests = batch_requests
def decode(buffer: bytes, **kwargs): from samson.protocols.diffie_hellman import DiffieHellman items = bytes_to_der_sequence(buffer) y_sequence_bytes = Bytes(int(items[1])) y = int(decoder.decode(y_sequence_bytes)[0]) p, g = [int(item) for item in items[0][1]] dh = DiffieHellman(p=p, g=g) # Err, where do we put this? dh.y = y return dh
def encode(ecdsa_key: object, **kwargs): zero_fill = math.ceil(ecdsa_key.G.curve.q.bit_length() / 8) encoded = export_der([ 1, Bytes(ecdsa_key.d).zfill(zero_fill), ber_decoder.decode(b'\x06' + bytes([len(ecdsa_key.G.curve.oid)]) + ecdsa_key.G.curve.oid)[0].asTuple(), ecdsa_key.format_public_point() ], item_types=[ Integer, OctetString, NamedCurve, PublicPoint ]) encoded = PKCS1ECDSAPrivateKey.transport_encode(encoded, **kwargs) return encoded
def test_crack(self): for _ in range(10): seed = Bytes.random(16).int() ref_lcg = LCG(X=seed, a=1103515245, c=12345, m=2**31) outputs = [ref_lcg.generate() for _ in range(15)] cracked_lcg = LCG.crack(outputs) self.assertTrue( all([ ref_lcg.generate() == cracked_lcg.generate() for _ in range(10000) ]))
def decrypt(self, authenticator: bytes, encrypted_password: bytes) -> Bytes: """ Decrypts the `encrypted_password`. Parameters: authenticator (bytes): Client authenticator. encrypted_password (bytes): RADIUS-encrypted password. Returns: Bytes: Plaintext password. """ return Bytes( self.encrypt(authenticator, encrypted_password).rstrip(b'\x00'))
def break_vigenere(ciphertext: str, alphabet: bytes = bytes(string.ascii_lowercase, 'utf-8'), expected_distribution=letter_distribution, min_key_length: int = 1, max_key_length: int = 20): ciphertext = Bytes.wrap(ciphertext) cipher_scores = [] cipher_len = len(ciphertext) analyzer = EnglishAnalyzer() for i in range(min_key_length, max_key_length): transposed = ciphertext.transpose(i) total_key_score = 1 top_chunk_scores = [] for chunk in transposed.chunk(cipher_len // i): curr_chunk_scores = [] for char in alphabet: tmp_vig = Vigenere(bytes([char])) new_chunk = tmp_vig.decrypt(chunk) chunk_score = chisquare(count_items(new_chunk), expected_distribution) curr_chunk_scores.append((char, chunk_score)) top_chunk_scores.append( sorted(curr_chunk_scores, key=lambda chunk_score: chunk_score[1], reverse=False)[0]) for chunk_score in top_chunk_scores: total_key_score += chunk_score[1] cipher_scores.append((total_key_score, top_chunk_scores)) analyzer_scores = [] for _total_score, top_chunk_scores in cipher_scores: vig = Vigenere(bytes([char for char, score in top_chunk_scores]), alphabet=alphabet) analyzer_scores.append( (analyzer.analyze(vig.decrypt(ciphertext)) / len(vig.key), vig)) top_analyzed_score = sorted(analyzer_scores, key=lambda kv: kv[0], reverse=True)[0] return top_analyzed_score[1]
def __init__(self, key: bytes): """ Parameters: key (bytes): Bytes-like object to key the cipher. """ Primitive.__init__(self) key = Bytes.wrap(key) if not len(key) in [8, 16, 24]: raise ValueError('`key` size must be in [8, 16, 24]') self.key = key self.des_arr = [DES(subkey.zfill(8)) for subkey in key.chunk(8)] self.block_size = 8
def encrypt(self, authenticator: bytes, password: bytes) -> Bytes: """ Encrypts the `password`. Parameters: authenticator (bytes): Client authenticator. password (bytes): Password. Returns: Bytes: RADIUS-encrypted password. """ if len(password) > 128: raise ValueError('Password exceeds maximum of 128 bytes') password = Bytes.wrap(password).pad_congruent_right(16) md5 = MD5() result, last = Bytes(), authenticator for chunk in password.chunk(16): result += md5.hash(self.key + last) ^ chunk last = result[-16:] return result
def decode(len_bytes: bytes) -> object: encoding_len = len(len_bytes) if encoding_len == 1: length = Bytes.wrap(len_bytes).int() elif encoding_len == 2: length = ((len_bytes[0] - 192) << 8) + len_bytes[1] + 192 else: length = (len_bytes[1] << 24) + (len_bytes[2] << 16) + ( len_bytes[3] << 8) + len_bytes[4] return PGPLength(length)
def encode(ec_key: object, **kwargs) -> str: """ Encodes the key as a JWK JSON string. Parameters: ec_key (ECDSA): ECDSA key to encode. Returns: str: JWK JSON string. """ jwk = JWKECPublicKey.build_pub(ec_key) jwk['d'] = url_b64_encode(Bytes(ec_key.d)).decode() return json.dumps(jwk).encode('utf-8')
def downconvert(self, attack_model: AttackModel, generator: FunctionType = lambda: Bytes.random(8)): if attack_model == self.ATTACK_MODEL: return self elif self.ATTACK_MODEL.implies(attack_model): return ChosenCiphertextOracle(self.request).downconvert( attack_model, generator) else: raise ValueError( f"{self.ATTACK_MODEL} cannot be downconverted to {attack_model}" )
def generate_fixed_point(block_cipher: object, message: bytes, block_size: int): """ Generates a Davies-Meyer fixed point. A fixed point is a state in which its output matches its input, and, therefore, infinitely produces itself. Parameters: block_cipher (type): Block cipher type. message (bytes): Message you want to be fixed point. block_size (int): Block size of `block_cipher`. Returns: DaviesMeyerConstruction: A DaviesMeyerConstruction with the initial state set to the fixed point. """ message = Bytes.wrap(message) first_block = message.chunk(block_size)[0] initial_state = block_cipher(first_block).decrypt( Bytes(b'').zfill(block_size)) return DaviesMeyerConstruction( initial_state=initial_state, encryptor=lambda key, msg: block_cipher(key).encrypt(msg))
def test_k_derivation(self): ecdsa = ECDSA(P256.G) k = Bytes.random(32).int() msgA = b'my first message' msgB = b'uh oh, two messages?!' sigA = ecdsa.sign(msgA, k) sigB = ecdsa.sign(msgB, k) found_k = ecdsa.derive_k_from_sigs(msgA, sigA, msgB, sigB) self.assertEqual(found_k, k) d = ecdsa.d self.assertEqual(ecdsa.derive_x_from_k(msgA, found_k, sigA), d)
def parse(token: bytes) -> object: """ Parses a compact bytestring `token` into a JWS object. Parameters: token (bytes): The JWS token to parse. Returns: JWS: JWS representation. """ header, body, signature = [ url_b64_decode(part) for part in token.split(b'.') ] return JWS(header, body, Bytes.wrap(signature))
def __init__(self, identity: bytes, password: bytes, g: int = 2, N: int = MODP_1024, hash_obj: object = SHA256(), a: int = None): """ Parameters: identity (bytes): Username. password (bytes): Password. g (int): Generator. N (int): Prime modulus. hash_obj (object): Instantiated object with compatible hash interface. a (int): Random private value. """ self.a = a or Bytes.random(4).int() % N self.g = g self.A = pow(g, self.a, N) self.identity = identity self.password = password self.N = N self.hash_obj = hash_obj self.k = hash_obj.hash(Bytes(N) + self.PAD(g)).int()
def sign(self, plaintext: bytes, salt: bytes=None) -> Bytes: """ Pads and hashes the `plaintext`. Parameters: plaintext (bytes): Plaintext to sign. salt (bytes): (Optional) Random salt. Returns: Bytes: Probabilistic signature. """ plaintext = Bytes.wrap(plaintext) mHash = self.hash_obj.hash(plaintext) salt = salt or Bytes.random(self.salt_len) m_prime = b'\x00' * 8 + mHash + salt H = self.hash_obj.hash(m_prime) em_bits = self.modulus_len - 1 em_len = (em_bits + 7) // 8 ps_len = em_len - self.hash_obj.digest_size - self.salt_len - 2 if ps_len < 0: raise ValueError("Plaintext is too long") PS = Bytes(b'').zfill(ps_len) DB = PS + b'\x01' + salt db_mask = self.mgf(H, em_len - self.hash_obj.digest_size - 1) masked_db = DB ^ db_mask # Set the leftmost 8emLen - emBits bits of the leftmost octet in maskedDB to zero. masked_db &= (2**(len(masked_db) * 8) - 1) >> ((8 * em_len) - em_bits) return masked_db + H + b'\xbc'
def encrypt_and_auth(self, key: bytes, iv: bytes, plaintext: bytes, auth_data: bytes) -> (Bytes, Bytes): mac_key, enc_key = key.chunk(self.chunk_size) rij = Rijndael(enc_key) cbc = CBC(rij, iv=iv) ciphertext = cbc.encrypt(plaintext) hmac = HMAC( mac_key, self.hash_obj).generate(auth_data + iv + ciphertext + Bytes(len(auth_data) * 8).zfill(8))[:self.chunk_size] return ciphertext, hmac