Skip to content

Latest commit

 

History

History
166 lines (116 loc) · 2.9 KB

crypto-spec.md

File metadata and controls

166 lines (116 loc) · 2.9 KB

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