bcachefs-tools/c_src/crypto.c

223 lines
5.1 KiB
C
Raw Normal View History

2016-10-04 06:22:17 +03:00
#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>
2017-03-01 13:45:15 +03:00
#include <keyutils.h>
2016-10-04 06:22:17 +03:00
#include <linux/random.h>
#include <sodium/crypto_pwhash_scryptsalsa208sha256.h>
2017-03-01 13:45:15 +03:00
#include <uuid/uuid.h>
2016-10-04 06:22:17 +03:00
#include "libbcachefs/checksum.h"
2016-10-04 06:22:17 +03:00
#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)
2016-10-04 06:22:17 +03:00
{
const unsigned char salt[] = "bcache";
struct bch_key key;
2016-10-04 06:22:17 +03:00
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));
2016-10-04 06:22:17 +03:00
if (ret)
die("scrypt error: %i", ret);
break;
default:
die("unknown kdf type %llu", BCH_CRYPT_KDF_TYPE(crypt));
}
return key;
2016-10-04 06:22:17 +03:00
}
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)
2016-10-04 06:22:17 +03:00
{
struct bch_sb_field_crypt *crypt = bch2_sb_field_get(sb, crypt);
2017-03-01 13:45:15 +03:00
if (!crypt)
die("filesystem is not encrypted");
2016-10-04 06:22:17 +03:00
*sb_key = crypt->key;
if (!bch2_key_is_encrypted(sb_key))
2017-03-01 13:45:15 +03:00
die("filesystem does not have encryption key");
2016-10-04 06:22:17 +03:00
*passphrase_key = derive_passphrase(crypt, passphrase);
2016-10-04 06:22:17 +03:00
2017-03-01 13:45:15 +03:00
/* 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)))
2017-03-01 13:45:15 +03:00
die("error encrypting key");
if (bch2_key_is_encrypted(sb_key))
2017-03-01 13:45:15 +03:00
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);
2017-03-01 13:45:15 +03:00
char uuid[40];
uuid_unparse_lower(sb->user_uuid.b, uuid);
char *description = mprintf("bcachefs:%s", uuid);
2017-03-01 13:45:15 +03:00
if (add_key(type,
description,
2017-03-01 13:45:15 +03:00
&passphrase_key, sizeof(passphrase_key),
keyring) < 0)
2017-04-15 07:40:50 +03:00
die("add_key error: %m");
2017-03-01 13:45:15 +03:00
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));
2016-10-04 06:22:17 +03:00
crypt->key.magic = BCH_KEY_MAGIC;
crypt->key.key = key;
2016-10-04 06:22:17 +03:00
bch_crypt_update_passphrase(sb, crypt, &key, passphrase);
}
2016-10-04 06:22:17 +03:00
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)) {
2017-03-01 13:45:15 +03:00
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));
}
2016-10-04 06:22:17 +03:00
struct bch_key passphrase_key = derive_passphrase(crypt, new_passphrase);
2017-03-01 13:45:15 +03:00
if (bch2_chacha_encrypt_key(&passphrase_key, __bch2_sb_key_nonce(sb),
&new_key, sizeof(new_key)))
die("error encrypting key");
2017-03-01 13:45:15 +03:00
memzero_explicit(&passphrase_key, sizeof(passphrase_key));
2017-03-01 13:45:15 +03:00
crypt->key = new_key;
assert(bch2_key_is_encrypted(&crypt->key));
2016-10-04 06:22:17 +03:00
}