Monday, September 03, 2018

[fanlevif] CBC encryption and decryption

We present, for pedagogical purposes, a polymorphic implementation in Haskell of the Cipher Block Chaining mode of operation for a block cipher.

Encryption is an unfold, reflecting the fact that CBC encryption is inherently serial: each block depends on all the previous blocks.

{-# LANGUAGE ScopedTypeVariables #-}
encrypt_cbc :: forall block . (block -> block) -> (block -> block -> block) -> block -> [block] -> [block];
encrypt_cbc cipher combine initialization_vector input_plaintext = let {
    one_encrypt :: (block,[block]) -> Maybe (block,(block,[block]));
    one_encrypt (previous_ciphertext, plaintext) = case plaintext of {
      [] -> Nothing;
      ( (p::block) : (rest::[block]) ) -> let {
        ciphertext :: block;
        ciphertext = cipher $ combine p previous_ciphertext;
      } in Just (ciphertext,(ciphertext,rest));
    }} in unfoldr one_encrypt (initialization_vector, input_plaintext);

Decryption is a zip, illustrating that decryption is parallelizable.

decrypt_cbc :: forall block . (block -> block) -> (block -> block -> block) -> block -> [block] -> [block];
decrypt_cbc decipher uncombine initialization_vector ciphertext = let {
    one_decrypt :: block -> block -> block;
    one_decrypt previous_ciphertext this_ciphertext = uncombine (decipher this_ciphertext) previous_ciphertext;
  } in zipWith one_decrypt (initialization_vector:ciphertext) ciphertext;

The cipher and decipher function arguments are the underlying block cipher operating in the encryption and decryption directions respectively.

The combine and uncombine binary operations take two blocks and combine them into one.  They are both typically implemented as XOR.  It also seems possible to use addition and subtraction modulo N, but I have never seen that explored.

No comments :