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
|
### 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
|
||||||
|
|
4
config.h
4
config.h
|
@ -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
|
||||||
|
|
113
src/enchive.c
113
src/enchive.c
|
@ -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;
|
||||||
|
|
Loading…
Reference in New Issue