def hex2base64_custom(hex_string):
    """
    Encode a hex string into base64 without using the base64 library
    https://tools.ietf.org/html/rfc3548.html#section-3
    """
    alphabet = list(
        'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=')

    # Split hex string into bytes, then group into threes
    bytes = [int(byte, 16) for byte in group(hex_string, 2)]
    triplets = group(bytes, 3)

    # Pack 24 bits together and convert into a bit string
    packed = [
        format(int.from_bytes(triplet, byteorder='big'),
               str(len(triplet) * 8) + 'b') for triplet in triplets
    ]

    # Split the bit string into groups of 6 bits
    input_group = [group(bits, 6) for bits in packed]

    # Add padding bits if the group is less than 24 bits long
    padding = 4 - len(input_group[-1])
    if padding:
        input_group[-1].extend([format(64, 'b')] * padding)

    # Flatten the list and convert to integer indexes
    input_group = list(itertools.chain.from_iterable(input_group))
    indexes = [int(index, 2) for index in input_group]

    return ''.join(alphabet[index] for index in indexes)
Exemple #2
0
def test_group_invalid():
    """Testing non positive sizes raise ValueError"""
    with pytest.raises(ValueError):
        list(group("example", 0))

    with pytest.raises(ValueError):
        list(group("example", -1))
Exemple #3
0
def calculate_prefix_length(encrypt, block_size):
    # This will fail if the prefix has two repeating blocks sequentially
    for i in range(0, block_size + 1):
        plaintext = b"A" * i + ((2 * block_size) * b"A")
        ciphertext = group(encrypt(plaintext), block_size)
        for j in range(0, len(ciphertext) - 1):
            if ciphertext[j] == ciphertext[j + 1]:
                print("j is " + str(j))
                print("i is " + str(i))
                if j == 0:
                    return 0
                else:
                    return (j * block_size) - i
STATIC_KEY = os.urandom(AES.block_size)
STATIC_IV = os.urandom(AES.block_size)


def encrypt(plaintext: str):
    """Enterprise Grade Super secure encryption function"""
    plaintext = plaintext.replace(';', '%3B').replace('=', '%3D')
    plaintext = b"comment1=cooking%20MCs;userdata=" + bytes(
        plaintext, 'ascii') + b";comment2=%20like%20a%20pound%20of%20bacon"
    print(plaintext)
    return cbc_encrypt(pad(plaintext), STATIC_KEY, STATIC_IV)


def decrypt(ciphertext, iv):
    """Decrypt and check for admin-true"""
    plaintext = cbc_decrypt(ciphertext, STATIC_KEY, iv)
    print(plaintext)
    return bytes(";admin=true;", 'ascii') in plaintext


# We need two blocks, the first to modify such that it flips the bits in the second block

ciphertext = encrypt("XXXXXXXXXXXXXXXX:admin-true")
blocks = group(ciphertext, AES.block_size)
blocks[2][0] ^= (ord(':') ^ ord(';'))
blocks[2][6] ^= (ord('-') ^ ord('='))
blocks = b''.join(blocks)

assert decrypt(blocks, STATIC_IV) is True
    return sum(c1 != c2 for c1, c2 in zip(a, b))


assert hamming_distance(str_to_bits("this is a test"),
                        str_to_bits("wokka wokka!!!")) == 37

ciphertext = b64decode(ciphertext)
decryptions = []

# Determine the keysize by brute forcing key sizes and checking the edit distance between byte groups
# If we have chosen the correct keysize then we compute dist(a XOR b) which will roughly 2-3 bits different in each byte
# However if we have chosen incorrectly then we compute dist(x XOR y) where x and y are effecitvely 'random' bytestrings
# which we expect to share approximately 4 bits due to probabilily. Because the correct key will show a lower edit distance
# we can keep track of the lowest edit distance through comparisons and then use that as an indicator of the correct key size
for keysize in range(2, 40):
    blocks = group(ciphertext, keysize)

    # 10 blocks is enough of an average to make the correct keysize appear on top
    NUM_BLOCKS = 10
    edit_distance = sum(
        hamming_distance(bytearray_to_bits(blocks[i]),
                         bytearray_to_bits(blocks[i + 1]))
        for i in range(NUM_BLOCKS))
    edit_distance /= (NUM_BLOCKS * keysize)

    decryption = Decryption(key=keysize, plaintext='', score=edit_distance)
    decryptions.append(decryption)

decryptions = sorted(decryptions)
keysize = decryptions[0].key
                                                  ('uid', 10),
                                                  ('role', 'user')])

# Now, two more easy functions. Generate a random AES key, then:

key = os.urandom(16)
cipher = AES.new(key, AES.MODE_ECB)

encrypted_profile = cipher.encrypt(
    pad(bytes(dict_to_form(profile_for("*****@*****.**")), 'ascii')))

# Tamper the block to make the ciphertext read role=admin
plaintext_profile = pad(
    bytes(dict_to_form(profile_for("*****@*****.**")), 'ascii'))
encrypted_profile = cipher.encrypt(plaintext_profile)
print("Profile: {0}, Encrypted Profile: {1}".format(plaintext_profile,
                                                    encrypted_profile))

#[email protected] maaaaaa&uid=10& role=user 7 padding bytes
#[email protected] maaaaaa&uid=10& role=admin 6 padding bytes

replacement_block = b"role=admin" + b"\x06" * 6
encrypted_replacement_block = cipher.encrypt(replacement_block)

blocks = group(encrypted_profile, AES.block_size)
blocks[-1] = encrypted_replacement_block

tampered_cipertext = b''.join(blocks)
print(tampered_cipertext)

print(cipher.decrypt(tampered_cipertext))
Exemple #7
0
def test_group_odd_length():
    """Testing group with odd length string"""
    assert group("example", 2) == ['ex', 'am', 'pl', 'e']
Exemple #8
0
def test_group_even_length():
    """Testing group with even length string"""
    assert group("test", 2) == ['te', 'st']