diff options
Diffstat (limited to 'c_src/crypto.c')
-rw-r--r-- | c_src/crypto.c | 201 |
1 files changed, 201 insertions, 0 deletions
diff --git a/c_src/crypto.c b/c_src/crypto.c new file mode 100644 index 00000000..32671bd8 --- /dev/null +++ b/c_src/crypto.c @@ -0,0 +1,201 @@ +#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 <keyutils.h> +#include <linux/random.h> +#include <sodium/crypto_pwhash_scryptsalsa208sha256.h> +#include <uuid/uuid.h> + +#include "libbcachefs/checksum.h" +#include "crypto.h" + +char *read_passphrase(const char *prompt) +{ + char *buf = NULL; + size_t buflen = 0; + ssize_t len; + + if (isatty(STDIN_FILENO)) { + struct termios old, new; + + fprintf(stderr, "%s", prompt); + fflush(stderr); + + if (tcgetattr(STDIN_FILENO, &old)) + die("error getting terminal attrs"); + + 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'; + + return buf; +} + +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; + + 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; +} + +bool bch2_sb_is_encrypted(struct bch_sb *sb) +{ + struct bch_sb_field_crypt *crypt; + + return (crypt = bch2_sb_field_get(sb, crypt)) && + bch2_key_is_encrypted(&crypt->key); +} + +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 bch_sb_crypt_init(struct bch_sb *sb, + struct bch_sb_field_crypt *crypt, + const char *passphrase) +{ + 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)); + } +} |