*****************************
** GECRYPT-0.3 FILE FORMAT **
*****************************
2007-07-31 Dwayne C. Litzenberger
** DEFINITIONS **
Let AES-CTR(K, N, M) represent the encryption (or decryption, which is
equivalent) of M using AES in counter mode using key K and nonce N.
Define the constants (represented as 128-bit big-endian integers):
FILE_ID = 0x616d1d67ca294e2eb98bc01ff0470100
HEADER_ID = 0x616d1d67ca294e2eb98bc01ff0470101
Let || represent the string concatenation operator.
Let S * N represent the string S repeated N times.
Let Len(X) represent the length (in octets) of X.
Let Int8(N) represent the unsigned 8-bit big-endian representation of N.
Let Int16(N) represent the unsigned 16-bit big-endian representation of N.
Let Int64(N) represent the unsigned 64-bit big-endian representation of N.
** HOW TO WRITE A GECRYPT-0.3 FILE **
1. Get from the user:
- The arbitrary-length master key K_M
- The plaintext message M
- The 16-bit iteration count c (must be >= 10. If the master key is a
passphrase, c should be at least 1000.)
- An optional, 64-bit padding size p. The default is 0 if none is
specified.
2. Randomly generate:
- 128-bit salt: S
- 64-bit nonces: N_H, N_P
- 128-, 192-, or 256-bit payload cipher key: K_P
3. Using the password-based key derivation function PBKDF2(P, S, c, dkLen) from
RSA PKCS#5 v2.0, compute the session key (using HMAC-SHA256 as the underlying
pseudorandom function):
K = PBKDF2(K_M, FILE_ID || S, c, 96)
4. Set the authentication key K_A to be the first 512 bits of the session key:
K_A = K<0..63>
5. Set the header cipher key K_H to be the last 256 bits of the session key:
K_H = K<64..95>
6. Generate the encrypted portion of the header:
C_H = AES-CTR(K_H, N_H, HEADER_ID || N_P || Int8(Len(K_P)) || K_P)
7. Generate the header:
HEADER = FILE_ID || S || N_H || Int16(c) || C_H
8. Initialize the payload cipher. Since AES-CTR is a stream cipher, define an
oracle E(x) that returns the AES-CTR encryption of x using key K_P and nonce
N_P, such that no part of the keystream is re-used and no part of the keystream
is skipped over. For example, given three octet strings a, b, and c:
Let x = E(a)
Let y = E(b)
Let z = E(c)
Then,
x || y || z == AES-CTR(K_P, N_P, a || b || c)
9. Split the plaintext into n chunks M_1, M_2, ... M_n such that the length
(in octets) of each chunk falls within the interval [1..2^16-1].
10. Generate the padding:
PAD = Int8(0) * p ; zero repeated p times
11. Split the padding into m chunks P_1, P_2, ... P_m such that the length
(in octets) of each chunk falls within the interval [1..2^16-1].
12. Generate the ciphertext:
C_0 = HEADER
A_0 = HMAC-SHA256(K_A, C_0)
L_1 = Int16(Len(M_1))
C_1 = C_0 || A_0 || E(L_1 || M_1)
A_1 = HMAC-SHA256(K_A, C_1)
L_2 = Int16(Len(M_2))
C_2 = C_1 || A_1 || E(L_2 || M_2)
A_2 = HMAC-SHA256(K_A, C_2)
L_3 = Int16(Len(M_3))
C_3 = C_2 || A_2 || E(L_3 || M_3)
A_3 = HMAC-SHA256(K_A, C_3)
. . .
L_n = Int16(Len(M_n))
C_n = C_{n-1} || A_{n-1} || E(L_n || M_n)
A_n = HMAC-SHA256(K_A, C_n)
LP_0 = Int16(0)
CP_0 = C_n || A_n || E(LP_0)
AP_0 = HMAC-SHA256(K_A, CP_0)
LP_1 = Int16(Len(P_1))
CP_1 = CP_0 || AP_0 || E(LP_1 || P_1)
AP_1 = HMAC-SHA256(K_A, CP_1)
LP_2 = Int16(Len(P_2))
CP_2 = CP_1 || AP_1 || E(LP_2 || P_2)
AP_2 = HMAC-SHA256(K_A, CP_2)
. . .
LP_m = Int16(Len(P_m))
CP_m = CP_{m-1} || AP_{m-1} || E(LP_m || P_m)
AP_m = HMAC-SHA256(K_A, CP_m)
LP_{m+1} = Int16(0)
CP_{m+1} = CP_m || AP_m || E(LP_{m+1})
AP_{m+1} = HMAC-SHA256(K_A, CP_{m+1})
CIPHERTEXT = CP_{m+1} || AP_{m+1}
Note: If the plaintext is empty, then n = 0, therefore:
C_0 = HEADER
A_0 = HMAC-SHA256(K_A, C_0)
LP_0 = Int16(0)
CP_0 = C_0 || A_0 || E(LP_0)
AP_0 = HMAC-SHA256(K_A, CP_0)
Note: If the padding is empty (i.e. no padding is desired), then m = 0,
therefore:
LP_1 = Int16(0)
CP_1 = CP_0 || AP_0 || E(LP_1)
AP_1 = HMAC-SHA256(K_A, CP_1)
13. Output CIPHERTEXT.
** EOF **