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 ### Key management
One of the core features of Enchive is the ability to derive an One of the core features of Enchive is the ability to derive an
asymmetric key pair from a passphrase (PBKDF2-like). This means you asymmetric key pair from a passphrase. This means you can store your
can store your archive key in your brain! To access this feature, use archive key in your brain! To access this feature, use the `--derive`
the `--derive` (`-d`) option with the `keygen` command. (`-d`) option with the `keygen` command.
$ enchive keygen --derive $ enchive keygen --derive
@ -112,6 +112,26 @@ The process for decrypting a file:
6. Decrypt the ciphertext using ChaCha20. 6. Decrypt the ciphertext using ChaCha20.
7. Verify `HMAC(key, plaintext)`. 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 ## Compilation
To build on any unix-like system, run `make`. The resulting binary has To build on any unix-like system, run `make`. The resulting binary has

View File

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

View File

@ -333,6 +333,39 @@ secure_creat(const char *file)
} }
#endif #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. * Derive a 32-byte key from null-terminated passphrase into buf.
* Optionally provide an 8-byte salt. * Optionally provide an 8-byte salt.
@ -340,21 +373,50 @@ secure_creat(const char *file)
static void static void
key_derive(const char *passphrase, u8 *buf, int iexp, const u8 *salt) key_derive(const char *passphrase, u8 *buf, int iexp, const u8 *salt)
{ {
static const u8 empty[8] = {0};
size_t len = strlen(passphrase); size_t len = strlen(passphrase);
unsigned long i;
SHA256_CTX ctx[1]; SHA256_CTX ctx[1];
unsigned long iterations = 1UL << iexp; unsigned long i;
sha256_init(ctx); 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); sha256_update(ctx, (u8 *)passphrase, len);
if (salt) hmac_final(ctx, salt, memory);
sha256_update(ctx, salt, 8);
sha256_final(ctx, buf); for (p = memory + SHA256_BLOCK_SIZE;
for (i = 0; i < iterations; i++) { p < memory + memlen + SHA256_BLOCK_SIZE;
p += SHA256_BLOCK_SIZE) {
sha256_init(ctx); 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_update(ctx, (u8 *)passphrase, len);
sha256_final(ctx, buf); 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); 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. * 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); n = strtol(arg, &p, 10);
if (errno || *p) if (errno || *p)
fatal("invalid argument -- %s", arg); fatal("invalid argument -- %s", arg);
if (n < 0 || n > 31) if (n < 5 || n > 31)
fatal("--derive argument must be 0 <= n <= 31 -- %s", fatal("--derive argument must be 0 <= n <= 31 -- %s",
arg); arg);
seckey_derive_iterations = n; seckey_derive_iterations = n;