diff options
Diffstat (limited to 'crypto.c')
-rw-r--r-- | crypto.c | 130 |
1 files changed, 130 insertions, 0 deletions
diff --git a/crypto.c b/crypto.c new file mode 100644 index 0000000..3f68835 --- /dev/null +++ b/crypto.c @@ -0,0 +1,130 @@ +#include <errno.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <termios.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <time.h> +#include <unistd.h> + +#include <linux/random.h> +#include <libscrypt.h> +#include <sodium/crypto_stream_chacha20.h> + +#include "crypto.h" + +char *read_passphrase(const char *prompt) +{ + struct termios old, new; + char *buf = NULL; + size_t buflen = 0; + ssize_t ret; + + fprintf(stderr, "%s", prompt); + fflush(stderr); + + if (tcgetattr(fileno(stdin), &old)) + die("error getting terminal attrs"); + + new = old; + new.c_lflag &= ~ECHO; + if (tcsetattr(fileno(stdin), TCSAFLUSH, &new)) + die("error setting terminal attrs"); + + ret = getline(&buf, &buflen, stdin); + if (ret <= 0) + die("error reading passphrase"); + + tcsetattr(fileno(stdin), TCSAFLUSH, &old); + fprintf(stderr, "\n"); + return buf; +} + +void derive_passphrase(struct bcache_key *key, const char *passphrase) +{ + const unsigned char salt[] = "bcache"; + int ret; + + ret = libscrypt_scrypt((void *) passphrase, strlen(passphrase), + salt, sizeof(salt), + SCRYPT_N, SCRYPT_r, SCRYPT_p, + (void *) key, sizeof(*key)); + if (ret) + die("scrypt error: %i", ret); +} + +void disk_key_encrypt(struct bcache_disk_key *disk_key, + struct bcache_key *key) +{ + int ret; + + ret = crypto_stream_chacha20_xor((void *) disk_key, + (void *) disk_key, sizeof(*disk_key), + (void *) &bch_master_key_nonce, + (void *) key); + if (ret) + die("chacha20 error: %i", ret); +} + +void disk_key_init(struct bcache_disk_key *disk_key) +{ + ssize_t ret; + + memcpy(&disk_key->header, bch_key_header, sizeof(bch_key_header)); +#if 0 + ret = getrandom(disk_key->key, sizeof(disk_key->key), GRND_RANDOM); + if (ret != sizeof(disk_key->key)) + die("error getting random bytes for key"); +#else + int fd = open("/dev/random", O_RDONLY|O_NONBLOCK); + if (fd < 0) + die("error opening /dev/random"); + + size_t n = 0; + struct timespec start; + bool printed = false; + + clock_gettime(CLOCK_MONOTONIC, &start); + + while (n < sizeof(disk_key->key)) { + struct timeval timeout = { 1, 0 }; + fd_set set; + + FD_ZERO(&set); + FD_SET(fd, &set); + + if (select(fd + 1, &set, NULL, NULL, &timeout) < 0) + die("select error"); + + ret = read(fd, + (void *) disk_key->key + n, + sizeof(disk_key->key) - n); + if (ret == -1 && errno != EINTR && errno != EAGAIN) + die("error reading from /dev/random"); + if (ret > 0) + n += ret; + + struct timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + + now.tv_sec -= start.tv_sec; + now.tv_nsec -= start.tv_nsec; + + while (now.tv_nsec < 0) { + long nsec_per_sec = 1000 * 1000 * 1000; + long sec = now.tv_nsec / nsec_per_sec - 1; + now.tv_nsec -= sec * nsec_per_sec; + now.tv_sec += sec; + } + + if (!printed && now.tv_sec >= 3) { + printf("Reading from /dev/random is taking a long time...\n)"); + printed = true; + } + } + close(fd); +#endif +} |