HMAC-SHA-256 ChaCha20 Curve25519
HMAC key length = 512-bits KDF key length = 1024-bits
KDF (key, secret): encryption_key = HMAC(key[0:512], secret) nonce = HMAC(key[512:1024], secret)[0:64]
return encryption_key, nonce
ENC (key, plaintext): Run ChaCha20 with a key of key.encryption_key and a 64-bit nonce of key.nonce
#Block# Notes: Deterministic encryption; the same block will encrypt to the same ciphertext with the same id, etc.
Storage Format: Key: 32 block id
Value:
* ciphertext
32 mac
Keys: K0, K1 = HMAC keys K2 = KDF key K3 = HMAC key
Encrypt: # Inputs B = input plaintext
# Algrorithm
secret = HMAC(K0, B)
id = HMAC(K1, secret)
key = KDF(K2, secret)
ciphertext = ENC(key, B)
mac = HMAC(K3, id + ciphertext)
# Outputs
id # Use to refer to this block in a Key-Value store (this is the Key)
ciphertext + mac # The encrypted payload (in a Key-Value store, this is Value)
secret # Store this in the archive. The block id and encryption keys can be regenerated from it.
Fetch: Fetching a block can be achived by calculating the block's id from the block's secret (which is stored in the archive). id = HMAC(K1, secret) The fetch the block
Decrypt: # Inputs ciphertext = input ciphertext mac = input mac secret = input secret
# Algorithm
id = KDF(K1, secret)
assert(HMAC(K3, id + ciphertext) == mac)
key = KDF(K2, secret)
plaintext = ENC(key, ciphertext)
# Outputs
plaintext
#Archive Name# Notes: Deterministic encryption; the same archive name will encrypt to the same encrypted name. Archive names are UTF-8. Archive names cannot exceed 127 bytes. Encrypted archive names will not exceed 255 bytes.
Storage Format: base64: 32 id * ciphertext 32 mac
Keys: K0 = HMAC key K1 = KDF key K2 = HMAC key
Encrypt: # Inputs N = archive name
# Algorithm
id = HMAC(K0, N)
key = KDF(K1, id)
ciphertext = ENC(key, N)
mac = HMAC(K2, id + ciphertext)
# Outputs
base64(id + ciphertext + mac) # Encrypted payload
Decrypt: # Inputs id = input id ciphertext = input ciphertext mac = input mac
# Algorithm
assert(HMAC(K2, id + ciphertext) == mac)
key = KDF(K1, id)
plaintext = ENC(key, ciphertext)
# Outputs
plaintext
#Archive# Notes: Uses public key encryption. HMAC is calculated with encrypted archive name included so that malicious parties can't mix up archive names and archive contents. We use the encrypted archive name to allow verifying backup integrity without decrypting anything.
Storage Format: Key: * encrypted archive name
Value:
* ciphertext
32 mac
Keys: K0 = KDF key K1 = HMAC key p, P = Curve25519 keypair
Encrypt: # Inputs X = encrypted archive name (before base64) A = plaintext archive
# Algorithm
e = random(32)
E = curve25519_base(e)
shared = curve25519(e, P)
key = KDF(K0, shared)
ciphertext = ENC(key, A)
mac = HMAC(K1, X + E + ciphertext)
# Outputs
E + ciphertext + mac # Encrypted payload