#include <time.h>
#include <unistd.h>
+#include <keyutils.h>
#include <linux/random.h>
-#include <libscrypt.h>
-#include <sodium/crypto_stream_chacha20.h>
+#include <sodium/crypto_pwhash_scryptsalsa208sha256.h>
+#include <uuid/uuid.h>
+#include "libbcachefs/checksum.h"
#include "crypto.h"
char *read_passphrase(const char *prompt)
{
- struct termios old, new;
char *buf = NULL;
size_t buflen = 0;
+ ssize_t len;
- fprintf(stderr, "%s", prompt);
- fflush(stderr);
+ if (isatty(STDIN_FILENO)) {
+ struct termios old, new;
- if (tcgetattr(fileno(stdin), &old))
- die("error getting terminal attrs");
+ fprintf(stderr, "%s", prompt);
+ fflush(stderr);
- new = old;
- new.c_lflag &= ~ECHO;
- if (tcsetattr(fileno(stdin), TCSAFLUSH, &new))
- die("error setting terminal attrs");
+ if (tcgetattr(STDIN_FILENO, &old))
+ die("error getting terminal attrs");
- if (getline(&buf, &buflen, stdin) <= 0)
+ new = old;
+ new.c_lflag &= ~ECHO;
+ if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &new))
+ die("error setting terminal attrs");
+
+ len = getline(&buf, &buflen, stdin);
+
+ tcsetattr(STDIN_FILENO, TCSAFLUSH, &old);
+ fprintf(stderr, "\n");
+ } else {
+ len = getline(&buf, &buflen, stdin);
+ }
+
+ if (len < 0)
die("error reading passphrase");
+ if (len && buf[len - 1] == '\n')
+ buf[len - 1] = '\0';
- tcsetattr(fileno(stdin), TCSAFLUSH, &old);
- fprintf(stderr, "\n");
return buf;
}
-void derive_passphrase(struct bcache_key *key, const char *passphrase)
+char *read_passphrase_twice(const char *prompt)
+{
+ char *pass = read_passphrase(prompt);
+
+ if (!isatty(STDIN_FILENO))
+ return pass;
+
+ char *pass2 = read_passphrase("Enter same passphrase again: ");
+
+ if (strcmp(pass, pass2)) {
+ memzero_explicit(pass, strlen(pass));
+ memzero_explicit(pass2, strlen(pass2));
+ die("Passphrases do not match");
+ }
+
+ memzero_explicit(pass2, strlen(pass2));
+ free(pass2);
+
+ return pass;
+}
+
+struct bch_key derive_passphrase(struct bch_sb_field_crypt *crypt,
+ const char *passphrase)
{
const unsigned char salt[] = "bcache";
+ struct bch_key key;
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);
+ switch (BCH_CRYPT_KDF_TYPE(crypt)) {
+ case BCH_KDF_SCRYPT:
+ ret = crypto_pwhash_scryptsalsa208sha256_ll(
+ (void *) passphrase, strlen(passphrase),
+ salt, sizeof(salt),
+ 1ULL << BCH_KDF_SCRYPT_N(crypt),
+ 1ULL << BCH_KDF_SCRYPT_R(crypt),
+ 1ULL << BCH_KDF_SCRYPT_P(crypt),
+ (void *) &key, sizeof(key));
+ if (ret)
+ die("scrypt error: %i", ret);
+ break;
+ default:
+ die("unknown kdf type %llu", BCH_CRYPT_KDF_TYPE(crypt));
+ }
+
+ return key;
}
-void disk_key_encrypt(struct cache_sb *sb,
- struct bcache_disk_key *disk_key,
- struct bcache_key *key)
+bool bch2_sb_is_encrypted(struct bch_sb *sb)
{
- __le32 nonce[2];
- int ret;
+ struct bch_sb_field_crypt *crypt;
- memcpy(nonce, &sb->set_magic, sizeof(sb->set_magic));
+ return (crypt = bch2_sb_get_crypt(sb)) &&
+ bch2_key_is_encrypted(&crypt->key);
+}
- ret = crypto_stream_chacha20_xor((void *) disk_key,
- (void *) disk_key, sizeof(*disk_key),
- (void *) nonce,
- (void *) key);
- if (ret)
- die("chacha20 error: %i", ret);
+void bch2_passphrase_check(struct bch_sb *sb, const char *passphrase,
+ struct bch_key *passphrase_key,
+ struct bch_encrypted_key *sb_key)
+{
+ struct bch_sb_field_crypt *crypt = bch2_sb_get_crypt(sb);
+ if (!crypt)
+ die("filesystem is not encrypted");
+
+ *sb_key = crypt->key;
+
+ if (!bch2_key_is_encrypted(sb_key))
+ die("filesystem does not have encryption key");
+
+ *passphrase_key = derive_passphrase(crypt, passphrase);
+
+ /* Check if the user supplied the correct passphrase: */
+ if (bch2_chacha_encrypt_key(passphrase_key, __bch2_sb_key_nonce(sb),
+ sb_key, sizeof(*sb_key)))
+ die("error encrypting key");
+
+ if (bch2_key_is_encrypted(sb_key))
+ die("incorrect passphrase");
+}
+
+void bch2_add_key(struct bch_sb *sb,
+ const char *type,
+ const char *keyring_str,
+ const char *passphrase)
+{
+ struct bch_key passphrase_key;
+ struct bch_encrypted_key sb_key;
+ int keyring;
+
+ if (!strcmp(keyring_str, "session"))
+ keyring = KEY_SPEC_SESSION_KEYRING;
+ else if (!strcmp(keyring_str, "user"))
+ keyring = KEY_SPEC_USER_KEYRING;
+ else if (!strcmp(keyring_str, "user_session"))
+ keyring = KEY_SPEC_USER_SESSION_KEYRING;
+ else
+ die("unknown keyring %s", keyring_str);
+
+ bch2_passphrase_check(sb, passphrase,
+ &passphrase_key,
+ &sb_key);
+
+ char uuid[40];
+ uuid_unparse_lower(sb->user_uuid.b, uuid);
+
+ char *description = mprintf("bcachefs:%s", uuid);
+
+ if (add_key(type,
+ description,
+ &passphrase_key, sizeof(passphrase_key),
+ keyring) < 0)
+ die("add_key error: %m");
+
+ memzero_explicit(description, strlen(description));
+ free(description);
+ memzero_explicit(&passphrase_key, sizeof(passphrase_key));
+ memzero_explicit(&sb_key, sizeof(sb_key));
}
-void disk_key_init(struct bcache_disk_key *disk_key)
+void bch_sb_crypt_init(struct bch_sb *sb,
+ struct bch_sb_field_crypt *crypt,
+ const char *passphrase)
{
- 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;
- }
+ crypt->key.magic = BCH_KEY_MAGIC;
+ get_random_bytes(&crypt->key.key, sizeof(crypt->key.key));
+
+ if (passphrase) {
+
+ SET_BCH_CRYPT_KDF_TYPE(crypt, BCH_KDF_SCRYPT);
+ SET_BCH_KDF_SCRYPT_N(crypt, ilog2(16384));
+ SET_BCH_KDF_SCRYPT_R(crypt, ilog2(8));
+ SET_BCH_KDF_SCRYPT_P(crypt, ilog2(16));
+
+ struct bch_key passphrase_key = derive_passphrase(crypt, passphrase);
+
+ assert(!bch2_key_is_encrypted(&crypt->key));
+
+ if (bch2_chacha_encrypt_key(&passphrase_key, __bch2_sb_key_nonce(sb),
+ &crypt->key, sizeof(crypt->key)))
+ die("error encrypting key");
+
+ assert(bch2_key_is_encrypted(&crypt->key));
+
+ memzero_explicit(&passphrase_key, sizeof(passphrase_key));
}
- close(fd);
-#endif
}