def aes_cbc_encrypt(key, plaintext, iv=None, pad=True): """ Encrypt the `plaintext` using the given `key` and `iv` (if given, or using a random initialization vector if not) using AES in CBC mode, optionally padding using pkcs5 if `pad` is True (plaintext must be exact multiple of `blocksize` if `pad` is False). """ keybytes = key2bytes(key) ivbytes = iv2bytes(iv) ptbytes = b16decode(plaintext, True) if pad: ptbytes = aes_pad(ptbytes) if len(ptbytes) % BLOCKSIZE != 0: raise ValueError("Invalid plaintext not a multiple of 16: %s" % ptbytes) ciphertext_blocks = [ivbytes] while True: ptblock, ptbytes = split_block(ptbytes) block_input = ''.join(xor_(list(ciphertext_blocks[-1]), list(ptblock))) block_enc_output = AES.new(keybytes).encrypt(block_input) ciphertext_blocks.append(block_enc_output) if not ptbytes: break return ''.join(ciphertext_blocks)
def enc_aes_cbc_padding_block(key, last_block): """ For the given key and last block of AES ciphertext in CBC mode, generate the ciphertext block for a full last-block of padding. """ keybytes = b16decode(key, True) blocksize = A.BLOCKSIZE bs = b16decode(last_block, True) assert len(bs) == A.BLOCKSIZE xbytes = ''.join( chr(b) for b in B.xor_(map(ord, bs), [blocksize] * blocksize)) return AES.new(keybytes).encrypt(xbytes)
def enc_aes_cbc_padding_block(key, last_block): """ For the given key and last block of AES ciphertext in CBC mode, generate the ciphertext block for a full last-block of padding. """ keybytes = b16decode(key, True) blocksize = A.BLOCKSIZE bs = b16decode(last_block, True) assert len(bs) == A.BLOCKSIZE xbytes = ''.join(chr(b) for b in B.xor_(map(ord, bs), [blocksize] * blocksize)) return AES.new(keybytes).encrypt(xbytes)
def aes_ctr_encrypt(key, plaintext, nonce_ctr): keybytes = key2bytes(key) if not nonce_ctr: raise ValueError() nonce_ctr_bytes = iv2bytes(nonce_ctr) nonce, ctr = split_at(nonce_ctr_bytes) ptbytes = b16decode(plaintext, True) ciphertext_blocks = [] ptblock, ptbytes = split_block(ptbytes) while ptblock: cbytes = AES.new(keybytes).encrypt(nonce + ctr) ctext_bytes = ''.join(xor_(list(ptblock), list(cbytes), truncate=True)) ciphertext_blocks.append(ctext_bytes) if not ptbytes: break ctr = aes_ctr_incr(ctr) ptblock, ptbytes = split_block(ptbytes) return ''.join(ciphertext_blocks)
def aes_cbc_decrypt(key, ciphertext, raw=False): """ Decrypt the AES-CBC-encrypted `ciphertext` using the given `key`, stripping off the IV and the pkcs5 padding bytes if `raw` is false. """ keybytes = key2bytes(key) ctbytes = b16decode(ciphertext, True) if len(ctbytes) % BLOCKSIZE != 0: raise ValueError('Invalid ciphertext length: %s' % (ciphertext,)) iv, ctbytes = split_block(ctbytes) plaintext_blocks = [iv] prev_ciphertext = iv while True: ctblock, ctbytes = split_block(ctbytes) block_bytes = AES.new(keybytes).decrypt(ctblock) block_bytes = ''.join(xor_(list(prev_ciphertext), list(block_bytes))) plaintext_blocks.append(block_bytes) prev_ciphertext = ctblock if not ctbytes: break if not raw: plaintext_blocks = plaintext_blocks[1:] # strip off IV last_block = plaintext_blocks[-1] last_byte = ord(last_block[-1]) if last_byte == 0 or last_byte > BLOCKSIZE: raise ValueError("Invalid last byte: %s" % last_byte) last_block_bytes, last_block_padding = (last_block[:-last_byte], last_block[-last_byte:]) padding_bytes = set(list(last_block_padding)) if len(padding_bytes) != 1: raise ValueError("Invalid padding: %s" % last_block_padding) if last_block_bytes: plaintext_blocks[-1] = last_block_bytes else: plaintext_blocks.pop() return ''.join(plaintext_blocks)
def aes_cbc_decrypt(key, ciphertext, raw=False): """ Decrypt the AES-CBC-encrypted `ciphertext` using the given `key`, stripping off the IV and the pkcs5 padding bytes if `raw` is false. """ keybytes = key2bytes(key) ctbytes = b16decode(ciphertext, True) if len(ctbytes) % BLOCKSIZE != 0: raise ValueError('Invalid ciphertext length: %s' % (ciphertext, )) iv, ctbytes = split_block(ctbytes) plaintext_blocks = [iv] prev_ciphertext = iv while True: ctblock, ctbytes = split_block(ctbytes) block_bytes = AES.new(keybytes).decrypt(ctblock) block_bytes = ''.join(xor_(list(prev_ciphertext), list(block_bytes))) plaintext_blocks.append(block_bytes) prev_ciphertext = ctblock if not ctbytes: break if not raw: plaintext_blocks = plaintext_blocks[1:] # strip off IV last_block = plaintext_blocks[-1] last_byte = ord(last_block[-1]) if last_byte == 0 or last_byte > BLOCKSIZE: raise ValueError("Invalid last byte: %s" % last_byte) last_block_bytes, last_block_padding = (last_block[:-last_byte], last_block[-last_byte:]) padding_bytes = set(list(last_block_padding)) if len(padding_bytes) != 1: raise ValueError("Invalid padding: %s" % last_block_padding) if last_block_bytes: plaintext_blocks[-1] = last_block_bytes else: plaintext_blocks.pop() return ''.join(plaintext_blocks)
def test_xor_same_size(): assert B.xor_([1, 2], [3, 4]) == [1 ^ 3, 2 ^ 4]
def test_xor_bytes(): assert B.xor_(chr(3), chr(4)) == chr(3 ^ 4)
def test_xor_ints(): assert B.xor_(3, 4) == 3 ^ 4
def test_xor_first_larger(): assert B.xor_([1, 1, 3, 4], [3]) == [1 ^ 3, 1, 3, 4] assert B.xor_([1, 2, 3, 4, 5, 6], [1, 2, 3]) == [0, 0, 0, 4, 5, 6]
def test_xor_first_smaller(): assert B.xor_([1], [3, 4]) == [1 ^ 3, 4] assert B.xor_([1, 2], [3, 4, 5, 6]) == [1 ^ 3, 2 ^ 4, 5, 6]