buffered-encryption 0.2.1
Encrypt large files without loading the entire file into memory.
License: MIT
Requires Python: >=3.6, <4
Keywords: cryptography,big,large,encryption

Buffered Encryption
===================

Encrypt large data files chunk-by-chunk, securely.

This package uses AES in GCM mode to encrypt and decrypt file streams.

It relies on the cryptography library to perform the encryption.

```
big unencrypted file, verification data --> encrypt and sign --> encrypted file, iv, tag

big unencrypted file <-- decrypt and verify <-- encrypted file, iv, tag, verification data
```

Examples
--------

### AES in GCM mode

The `aesgcm` module provides a way to encrypt and decrypt entire files without loading the entire thing into memory. It does not provide a file-like interface to the encrypted file.

```python
import os
from buffered_encryption.aesgcm import EncryptionIterator, DecryptionIterator

plaintext = open("plain.txt","rb")

key = os.urandom(32)
sig = os.urandom(12)

enc = EncryptionIterator(plaintext,key,sig)
with open("cipher","wb") as ciphertext:
    for chunk in enc:
        ciphertext.write(chunk)

plaintext.close()

ciphertext = open("cipher","rb")

dec = DecryptionIterator(ciphertext,key,sig,enc.iv,enc.tag)
with open("plain.dec.txt","wb") as decrypted:
    for chunk in dec:
        decrypted.write(chunk)

ciphertext.close()
```

### AES in CTR mode

The `aesctr` module allows you to read and seek an encrypted file as if it was a normal file. This provides a file-like interface while the data on disk stays encrypted.

This will be on the disk:
```python
b"1\xb2<\xcco\xbb\xa5%\xa9\xce\xb0\xac\x12\xc1Cw {\xdd\x0c\xa1\x93\x1b'E=v4L\xb8\xb9\x0e\xd5\x90\x8d\xf3H \xeb\x99iX\xcf\xea\xfc\xac\x92\xe8\xff\xb3\xbbS\xcaM\xb2\xf3?\xdf\xd9\x80\xbf\xef\x06\xa2\xab\x977\xc0\xcc\x0f\xd6\xd6' ,"
```

This will be what you read into python:
```python
b"Hello, World!! This message is longer than the AES block size of 16 bytes!!"
```

Key and nonce used in the above:
```python
key = b'\x0e\x07)\xb8*\xda\x13\x19\xc7`"\x14\xc1i\xe3\xf1$\xa5\xc7w\xda\x1dU\t\x9c\x1f{\xf5tR\xa7b'

nonce = b'6\x03\xf5\xdd\x92\x17\x0cDg\xcc\x1a\x9f\xe1\x08\x98\x7f'

```

To recreate this:
```python
import os, io
from buffered_encryption.aesctr import EncryptionIterator, ReadOnlyEncryptedFile
key = os.urandom(32)
nonce = os.urandom(16)
plaintext = b"Hello, World!! This message is longer than the AES block size of 16 bytes!!"

# Write the ciphertext to a buffer (you can also write to a file)
ciphertext_buf = io.BytesIO()
enc = EncryptionIterator(io.BytesIO(plaintext),key,nonce)
for chunk in enc:
    ciphertext_buf.write(chunk)

ciphertext_buf.seek(0)

# Create our read-only encrypted file
ef = ReadOnlyEncryptedFile(ciphertext_buf,key,nonce)

# Read 12 bytes of data
ef.read(12) # returns b"Hello, World"

# Seekable
ef.seek(7)

# Keep reading
ef.read(18) # returns b"World!! This messa"
```

### Why read-only?

Read-only ensures you do not use the same nonce for different messages. You cannot write different data to a block using the same nonce, and still be cryptographically secure. So if you were to re-write to the encrypted file, you have now defeated your own encryption.