]> git.sesse.net Git - bcachefs-tools-debian/blobdiff - crypto.c
fix list_journal for nochanges
[bcachefs-tools-debian] / crypto.c
index e98e486754eedaa188cbd83fb8f5928e9817accb..32671bd84c332e90deb0510743443faf41df6c45 100644 (file)
--- a/crypto.c
+++ b/crypto.c
 #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_field_get(sb, crypt)) &&
+               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_field_get(sb, crypt);
+       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
 }