mirror of https://github.com/skeeto/enchive.git
New key derivation algorithm.
parent
9af373c94c
commit
c559458ee7
26
README.md
26
README.md
|
@ -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
|
||||
|
|
4
config.h
4
config.h
|
@ -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
|
||||
|
|
113
src/enchive.c
113
src/enchive.c
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue