mirror of
https://github.com/koverstreet/bcachefs-tools.git
synced 2025-01-22 00:04:31 +03:00
45468a9a6f
This also fixes a bug where the set-passphrase command failed to derive the key for disks initially formatted with --encrypted and --no_passphrase, due to bch_sb_crypt_init not configuring the KDF params if the passphrase wasn't specified during formatting. Signed-off-by: Mae Kasza <git@badat.dev> Tested-by: Jérôme Poulin <jeromepoulin@gmail.com>
223 lines
5.1 KiB
C
223 lines
5.1 KiB
C
#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)
|
|
{
|
|
struct bch_key key;
|
|
get_random_bytes(&key, sizeof(key));
|
|
|
|
crypt->key.magic = BCH_KEY_MAGIC;
|
|
crypt->key.key = key;
|
|
|
|
bch_crypt_update_passphrase(sb, crypt, &key, passphrase);
|
|
}
|
|
|
|
void bch_crypt_update_passphrase(
|
|
struct bch_sb *sb,
|
|
struct bch_sb_field_crypt *crypt,
|
|
struct bch_key *key,
|
|
const char *new_passphrase)
|
|
{
|
|
|
|
struct bch_encrypted_key new_key;
|
|
new_key.magic = BCH_KEY_MAGIC;
|
|
new_key.key = *key;
|
|
|
|
if(!new_passphrase) {
|
|
crypt->key = new_key;
|
|
return;
|
|
}
|
|
|
|
// If crypt already has an encrypted key reuse it's encryption params
|
|
if (!bch2_key_is_encrypted(&crypt->key)) {
|
|
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, new_passphrase);
|
|
|
|
if (bch2_chacha_encrypt_key(&passphrase_key, __bch2_sb_key_nonce(sb),
|
|
&new_key, sizeof(new_key)))
|
|
die("error encrypting key");
|
|
|
|
memzero_explicit(&passphrase_key, sizeof(passphrase_key));
|
|
|
|
crypt->key = new_key;
|
|
assert(bch2_key_is_encrypted(&crypt->key));
|
|
}
|