import base64
import hmac
import os

from .crypto_backend import (
    AES_BLOCK_BYTES,
    aes_cfb8_encrypt,
    aes_cfb8_decrypt,
    aes_cfb128_encrypt,
    aes_cfb128_decrypt,
)

KEY_SIZE_BYTES = 32

SHA256_HMAC_BYTES = 32

def _xor_bytes(a, b):
    assert(len(a) == len(b))
    return bytes([ax ^ bx for ax, bx in zip(a, b)])

def _fold_key(buf):
    assert(len(buf) == 32)
    return _xor_bytes(buf[:16], buf[16:])

def generate_key():
    return os.urandom(KEY_SIZE_BYTES)

def sha256_hmac(buf, key):
    return hmac.new(key, buf, digestmod="sha256").digest()

def aes_cfb8_wrap(data, key):
    iv = os.urandom(AES_BLOCK_BYTES)
    ct = aes_cfb8_encrypt(data, key, iv)
    buf = iv + ct
    return sha256_hmac(buf, key) + buf

def aes_cfb8_unwrap(buf, key):
    mac, buf = buf[:SHA256_HMAC_BYTES], buf[SHA256_HMAC_BYTES:]
    if sha256_hmac(buf, key) != mac:
        raise IOError("MAC verification failed")
    iv, ct = buf[:AES_BLOCK_BYTES], buf[AES_BLOCK_BYTES:]
    return aes_cfb8_decrypt(ct, key, iv)

def aes_cfb128_wrap(data, key):
    iv = os.urandom(AES_BLOCK_BYTES)
    ct = aes_cfb128_encrypt(data, key, iv)
    buf = iv + ct
    return sha256_hmac(buf, key) + buf

def aes_cfb128_unwrap(buf, key):
    mac, buf = buf[:SHA256_HMAC_BYTES], buf[SHA256_HMAC_BYTES:]
    if sha256_hmac(buf, key) != mac:
        raise IOError("MAC verification failed")
    iv, ct = buf[:AES_BLOCK_BYTES], buf[AES_BLOCK_BYTES:]
    return aes_cfb128_decrypt(ct, key, iv)
