New key derivation algorithm.

pull/2/head
Christopher Wellons 2017-03-07 22:16:54 -05:00
parent 9af373c94c
commit c559458ee7
3 changed files with 96 additions and 47 deletions

View File

@ -44,9 +44,9 @@ and output.
### Key management
One of the core features of Enchive is the ability to derive an
asymmetric key pair from a passphrase (PBKDF2-like). This means you
can store your archive key in your brain! To access this feature, use
the `--derive` (`-d`) option with the `keygen` command.
asymmetric key pair from a passphrase. This means you can store your
archive key in your brain! To access this feature, use the `--derive`
(`-d`) option with the `keygen` command.
$ enchive keygen --derive
@ -112,6 +112,26 @@ The process for decrypting a file:
6. Decrypt the ciphertext using ChaCha20.
7. Verify `HMAC(key, plaintext)`.
### Key derivation
Enchive uses an scrypt-like algorithm for key derivation, requiring a
lot of random access memory. Derivation is controlled by a single
difficulty exponent *D*. Secret key derivation requires 512MB of
memory (D=29) by default, and protection key derivation requires 32MB
by default (D=25). The salt for the secret key is all zeros.
1. Allocate a `(1 << D) + 32` byte buffer, *M*.
2. Compute `HMAC_SHA256(salt, passphrase)` and write this 32-byte
result to the beginning of *M*.
3. For each uninitialized 32-byte chunk in *M*, compute the SHA-256
hash of the previous 32-byte chunk.
4. Initialize a byte pointer *P* to the last 32-byte chunk of *M*.
5. Compute the SHA-256 of the 32 bytes at *P*.
6. Take the first *D* bits of this hash and use this value to set a
new *P* pointing elsewhere into *M*.
7. Repeat from step 5 `1 << (D - 5)` times.
8. *P* points to the result.
## Compilation
To build on any unix-like system, run `make`. The resulting binary has

View File

@ -16,11 +16,11 @@
#endif
#ifndef ENCHIVE_KEY_DERIVE_ITERATIONS
# define ENCHIVE_KEY_DERIVE_ITERATIONS 20
# define ENCHIVE_KEY_DERIVE_ITERATIONS 25 /* 32MB */
#endif
#ifndef ENCHIVE_SECKEY_DERIVE_ITERATIONS
# define ENCHIVE_SECKEY_DERIVE_ITERATIONS 24
# define ENCHIVE_SECKEY_DERIVE_ITERATIONS 29 /* 512MB */
#endif
#ifndef ENCHIVE_OPTION_RANDOM_DEVICE

View File

@ -333,6 +333,39 @@ secure_creat(const char *file)
}
#endif
/**
* Initialize a SHA-256 context for HMAC-SHA256.
* All message data will go into the resulting context.
*/
static void
hmac_init(SHA256_CTX *ctx, const u8 *key)
{
int i;
u8 pad[SHA256_BLOCK_SIZE];
sha256_init(ctx);
for (i = 0; i < SHA256_BLOCK_SIZE; i++)
pad[i] = key[i] ^ 0x36U;
sha256_update(ctx, pad, sizeof(pad));
}
/**
* Compute the final HMAC-SHA256 MAC.
* The key must be the same as used for initialization.
*/
static void
hmac_final(SHA256_CTX *ctx, const u8 *key, u8 *hash)
{
int i;
u8 pad[SHA256_BLOCK_SIZE];
sha256_final(ctx, hash);
sha256_init(ctx);
for (i = 0; i < SHA256_BLOCK_SIZE; i++)
pad[i] = key[i] ^ 0x5cU;
sha256_update(ctx, pad, sizeof(pad));
sha256_update(ctx, hash, SHA256_BLOCK_SIZE);
sha256_final(ctx, hash);
}
/**
* Derive a 32-byte key from null-terminated passphrase into buf.
* Optionally provide an 8-byte salt.
@ -340,21 +373,50 @@ secure_creat(const char *file)
static void
key_derive(const char *passphrase, u8 *buf, int iexp, const u8 *salt)
{
static const u8 empty[8] = {0};
size_t len = strlen(passphrase);
unsigned long i;
SHA256_CTX ctx[1];
unsigned long iterations = 1UL << iexp;
sha256_init(ctx);
unsigned long i;
unsigned long memlen = 1UL << iexp;
unsigned long mask = memlen - 1;
unsigned long iterations = 1UL << (iexp - 5);
u8 *memory, *memptr, *p;
memory = malloc(memlen + SHA256_BLOCK_SIZE);
if (!memory)
fatal("not enough memory for key derivation");
if (!salt)
salt = empty;
hmac_init(ctx, salt);
sha256_update(ctx, (u8 *)passphrase, len);
if (salt)
sha256_update(ctx, salt, 8);
sha256_final(ctx, buf);
for (i = 0; i < iterations; i++) {
hmac_final(ctx, salt, memory);
for (p = memory + SHA256_BLOCK_SIZE;
p < memory + memlen + SHA256_BLOCK_SIZE;
p += SHA256_BLOCK_SIZE) {
sha256_init(ctx);
sha256_update(ctx, buf, sizeof(buf));
sha256_update(ctx, p - SHA256_BLOCK_SIZE, SHA256_BLOCK_SIZE);
sha256_update(ctx, (u8 *)passphrase, len);
sha256_final(ctx, p);
}
memptr = memory + memlen - SHA256_BLOCK_SIZE;
for (i = 0; i < iterations; i++) {
unsigned long offset;
sha256_init(ctx);
sha256_update(ctx, memptr, SHA256_BLOCK_SIZE);
sha256_update(ctx, (u8 *)passphrase, len);
sha256_final(ctx, buf);
offset = ((unsigned long)buf[3] << 24 |
(unsigned long)buf[2] << 16 |
(unsigned long)buf[1] << 8 |
(unsigned long)buf[0] << 0);
memptr = memory + (offset & mask);
}
memcpy(buf, memptr, SHA256_BLOCK_SIZE);
free(memory);
}
/**
@ -423,39 +485,6 @@ compute_shared(u8 *sh, const u8 *s, const u8 *p)
curve25519_donna(sh, s, p);
}
/**
* Initialize a SHA-256 context for HMAC-SHA256.
* All message data will go into the resulting context.
*/
static void
hmac_init(SHA256_CTX *ctx, const u8 *key)
{
int i;
u8 pad[SHA256_BLOCK_SIZE];
sha256_init(ctx);
for (i = 0; i < SHA256_BLOCK_SIZE; i++)
pad[i] = key[i] ^ 0x36U;
sha256_update(ctx, pad, sizeof(pad));
}
/**
* Compute the final HMAC-SHA256 MAC.
* The key must be the same as used for initialization.
*/
static void
hmac_final(SHA256_CTX *ctx, const u8 *key, u8 *hash)
{
int i;
u8 pad[SHA256_BLOCK_SIZE];
sha256_final(ctx, hash);
sha256_init(ctx);
for (i = 0; i < SHA256_BLOCK_SIZE; i++)
pad[i] = key[i] ^ 0x5cU;
sha256_update(ctx, pad, sizeof(pad));
sha256_update(ctx, hash, SHA256_BLOCK_SIZE);
sha256_final(ctx, hash);
}
/**
* Encrypt from file to file using key/iv, aborting on any error.
*/
@ -852,7 +881,7 @@ command_keygen(struct optparse *options)
n = strtol(arg, &p, 10);
if (errno || *p)
fatal("invalid argument -- %s", arg);
if (n < 0 || n > 31)
if (n < 5 || n > 31)
fatal("--derive argument must be 0 <= n <= 31 -- %s",
arg);
seckey_derive_iterations = n;