crypto/crypto/ AESRecipes1


Python

from Crypto.Cipher import AES

def pad_pcs7(x):
  l = len(x)
  a = l % 16
  b = 16 - a
  c = bytes([b]*b)
  return x + c

def enc(plaintext,phrase,iterations=1234):
  salt = os.urandom(256)
  iv = os.urandom(16)
  key = hashlib.pbkdf2_hmac("sha512",phrase.encode("utf8"),salt,iterations,32)
  aes = AES.new(key, AES.MODE_CBC, IV=iv)
  bplaintext = plaintext.encode("utf8")
  bplaintext = pad_pcs7(bplaintext)
  ciphertext = aes.encrypt(bplaintext)
  ciphertext_b64 = b64e(ciphertext)
  obj = {
    "ciphertext": ciphertext_b64.decode("utf8"),
    "salt": salt.hex(),
    "iv": iv.hex(),
    "iterations": iterations
  }
  return obj

I use this, for example, to create SelfDecryptingWebpage's in Python which can be decrypted with Javascript (using CryptoJS).

This example class comes from this stackexchange answer

import base64
import hashlib
from Crypto import Random
from Crypto.Cipher import AES

class AESCipher(object):

    def __init__(self, key):
        self.bs = AES.block_size
        self.key = hashlib.sha256(key.encode()).digest()

    def encrypt(self, raw):
        raw = self._pad(raw)
        iv = Random.new().read(AES.block_size)
        cipher = AES.new(self.key, AES.MODE_CBC, iv)
        return base64.b64encode(iv + cipher.encrypt(raw.encode()))

    def decrypt(self, enc):
        enc = base64.b64decode(enc)
        iv = enc[:AES.block_size]
        cipher = AES.new(self.key, AES.MODE_CBC, iv)
        return self._unpad(cipher.decrypt(enc[AES.block_size:])).decode('utf-8')

    def _pad(self, s):
        return s + (self.bs - len(s) % self.bs) * chr(self.bs - len(s) % self.bs)

    @staticmethod
    def _unpad(s):
        return s[:-ord(s[len(s)-1:])]

Javascript

This is compatible with the Python encrypt above. Using cryptojs on my gitub

<script src="aes.js"></script>
<script src="sha512.js"></script>
<script src="pbkdf2.js"></script>
...
const enc = (passphrase,plaintext,iterations=1977) => {
  const salt = CryptoJS.lib.WordArray.random(256)
  const iv = CryptoJS.lib.WordArray.random(16)
  const key = CryptoJS.PBKDF2(passphrase, salt, { hasher: CryptoJS.algo.SHA512, keySize: 64/8, iterations})
  const encrypted = CryptoJS.AES.encrypt(plaintext, key, { iv })
  const ciphertext = encrypted.toString()
  const obj = { ciphertext, salt: salt.toString(CryptoJS.enc.Hex), 
    iv: iv.toString(CryptoJS.enc.Hex), iterations }
  return obj
}
const decrypt = (passphrase,obj) => {
  const { ciphertext, iterations } = obj
  const salt = CryptoJS.enc.Hex.parse(obj.salt)
  const iv = CryptoJS.enc.Hex.parse(obj.iv)
  const key = CryptoJS.PBKDF2(passphrase, salt, { hasher: CryptoJS.algo.SHA512, keySize: 64/8, iterations})
  const decrypted = CryptoJS.AES.decrypt(ciphertext, key, { iv })
  return decrypted.toString(CryptoJS.enc.Utf8)
}

Php

This may not be compatible with the above

This also generates a hash, and stores the iv and hash in the ciphertext.

function encrypt($plaintext, $password) {
    $method = "AES-256-CBC";
    $key = hash('sha256', $password, true);
    $iv = openssl_random_pseudo_bytes(16);

    $ciphertext = openssl_encrypt($plaintext, $method, $key, OPENSSL_RAW_DATA, $iv);
    $hash = hash_hmac('sha256', $ciphertext . $iv, $key, true);

    return $iv . $hash . $ciphertext;
}

function decrypt($ivHashCiphertext, $password) {
    $method = "AES-256-CBC";
    $iv = substr($ivHashCiphertext, 0, 16);
    $hash = substr($ivHashCiphertext, 16, 32);
    $ciphertext = substr($ivHashCiphertext, 48);
    $key = hash('sha256', $password, true);

    if (!hash_equals(hash_hmac('sha256', $ciphertext . $iv, $key, true), $hash)) return null;

    return openssl_decrypt($ciphertext, $method, $key, OPENSSL_RAW_DATA, $iv);
}

Slightly less secure but slightly less data

This is perhaps slightly less secure, for situations where you want some crypto, but it's not critical enough to warrant generating a cryptographically random iv and storing a hmac.

<?php
function make_random_iv() {
  $iv = openssl_random_pseudo_bytes(16);
  return $iv;
}
function iv_from_hash($x) {
  $a = hash("sha256",$x,true);
  $b = substr($a,0,16);
  return $b;
}
function encrypt($plaintext, $password, $iv) {
  $method = "AES-256-CBC";
  $key = hash('sha256', $password, true);

  $ciphertext = openssl_encrypt($plaintext, $method, $key, OPENSSL_RAW_DATA, $iv);

  return $ciphertext;
}

function decrypt($ciphertext, $password, $iv) {
  $method = "AES-256-CBC";
  $key = hash('sha256', $password, true);

  return openssl_decrypt($ciphertext, $method, $key, OPENSSL_RAW_DATA, $iv);
}

and a simple test of the above

<?php
include_once("aescm.php");

$iv = make_random_iv();
$a = encrypt("hello","world",$iv);
$b = decrypt($a,"world",$iv);
$x = base64_encode($a);
echo "$x\n\n$b\n\n";
echo "-----\n";
$iv = iv_from_hash("boing");
$a = encrypt("hello","world",$iv);
$b = decrypt($a,"world",$iv);
$x = base64_encode($a);
echo "$x\n\n$b\n\n";

The main aim for this is to store encrypted data in cookies, and cookies have limited space, so we save the bytes from storing the iv and hmac in the ciphertext, and instead eschewing hmac and generating the iv using a hash. Potentially this is cryptographically weaker, but in its intended use it is secure enough, and is far from worth the effort to crack. (This is for things like making an adventure game in PHP where we use hashing and encrypting so as to store keys and inventories in the user's cookies but in such a way as to make them indecipherable and impossible to manipulate – nothing life and death or financial.)