mirror of https://github.com/digint/btrbk
btrbk: add key derivation for encrypted raw targets using external backend
parent
de7628ac7c
commit
422d52c063
1
Makefile
1
Makefile
|
@ -43,6 +43,7 @@ install-share:
|
||||||
install -pDm755 ssh_filter_btrbk.sh "$(DESTDIR)$(SCRIPTDIR)/ssh_filter_btrbk.sh"
|
install -pDm755 ssh_filter_btrbk.sh "$(DESTDIR)$(SCRIPTDIR)/ssh_filter_btrbk.sh"
|
||||||
install -pDm755 contrib/cron/btrbk-mail "$(DESTDIR)$(SCRIPTDIR)/btrbk-mail"
|
install -pDm755 contrib/cron/btrbk-mail "$(DESTDIR)$(SCRIPTDIR)/btrbk-mail"
|
||||||
install -pDm755 contrib/migration/raw_suffix2sidecar "$(DESTDIR)$(SCRIPTDIR)/raw_suffix2sidecar"
|
install -pDm755 contrib/migration/raw_suffix2sidecar "$(DESTDIR)$(SCRIPTDIR)/raw_suffix2sidecar"
|
||||||
|
install -pDm755 contrib/crypt/kdf_pbkdf2.py "$(DESTDIR)$(SCRIPTDIR)/kdf_pbkdf2.py"
|
||||||
|
|
||||||
install-man:
|
install-man:
|
||||||
@echo 'installing manpages...'
|
@echo 'installing manpages...'
|
||||||
|
|
66
btrbk
66
btrbk
|
@ -114,6 +114,10 @@ my %config_options = (
|
||||||
openssl_iv_size => { default => undef, accept => [ "no", accept_numeric => 1 ] },
|
openssl_iv_size => { default => undef, accept => [ "no", accept_numeric => 1 ] },
|
||||||
openssl_keyfile => { default => undef, accept_file => { absolute => 1 } },
|
openssl_keyfile => { default => undef, accept_file => { absolute => 1 } },
|
||||||
|
|
||||||
|
kdf_backend => { default => undef, accept_file => { absolute => 1 } },
|
||||||
|
kdf_keysize => { default => "32", accept_numeric => 1 },
|
||||||
|
kdf_keygen => { default => "once", accept => [ "once", "each" ] },
|
||||||
|
|
||||||
group => { default => undef, accept_regexp => qr/^$group_match(\s*,\s*$group_match)*$/, split => qr/\s*,\s*/ },
|
group => { default => undef, accept_regexp => qr/^$group_match(\s*,\s*$group_match)*$/, split => qr/\s*,\s*/ },
|
||||||
|
|
||||||
backend => { default => "btrfs-progs", accept => [ "btrfs-progs", "btrfs-progs-btrbk", "btrfs-progs-sudo" ] },
|
backend => { default => "btrfs-progs", accept => [ "btrfs-progs", "btrfs-progs-btrbk", "btrfs-progs-sudo" ] },
|
||||||
|
@ -236,6 +240,7 @@ my %raw_info_sort = (
|
||||||
encrypt => 11,
|
encrypt => 11,
|
||||||
cipher => 12,
|
cipher => 12,
|
||||||
iv => 13,
|
iv => 13,
|
||||||
|
# kdf_* (generated by kdf_backend)
|
||||||
);
|
);
|
||||||
|
|
||||||
my %url_cache; # map URL to btr_tree node
|
my %url_cache; # map URL to btr_tree node
|
||||||
|
@ -261,6 +266,8 @@ my @transaction_log;
|
||||||
my %config_override;
|
my %config_override;
|
||||||
my @tm_now; # current localtime ( sec, min, hour, mday, mon, year, wday, yday, isdst )
|
my @tm_now; # current localtime ( sec, min, hour, mday, mon, year, wday, yday, isdst )
|
||||||
my %warn_once;
|
my %warn_once;
|
||||||
|
my %kdf_vars;
|
||||||
|
my $kdf_session_key;
|
||||||
|
|
||||||
|
|
||||||
$SIG{__DIE__} = sub {
|
$SIG{__DIE__} = sub {
|
||||||
|
@ -1433,9 +1440,63 @@ sub btrfs_send_to_file($$$;$$)
|
||||||
$raw_info{iv} = $iv;
|
$raw_info{iv} = $iv;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
my $encrypt_key;
|
||||||
|
if($encrypt->{keyfile}) {
|
||||||
|
if($encrypt->{kdf_backend}) {
|
||||||
|
WARN "Both openssl_keyfile and kdf_backend are configured, ignoring kdf_backend!";
|
||||||
|
}
|
||||||
|
$encrypt_key = '$(cat ' . $encrypt->{keyfile} . ')';
|
||||||
|
}
|
||||||
|
elsif($encrypt->{kdf_backend}) {
|
||||||
|
if($encrypt->{kdf_keygen_each}) {
|
||||||
|
$kdf_session_key = undef;
|
||||||
|
%kdf_vars = ();
|
||||||
|
}
|
||||||
|
if($kdf_session_key) {
|
||||||
|
INFO "Reusing session key for: $vol_received->{PRINT}";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
# run kdf backend, set session key and vars
|
||||||
|
DEBUG "Generating session key for: $vol_received->{PRINT}";
|
||||||
|
my $kdf_backend_name = $encrypt->{kdf_backend};
|
||||||
|
$kdf_backend_name =~ s/^.*\///;
|
||||||
|
|
||||||
|
print STDOUT "\nGenerate session key for " . ($encrypt->{kdf_keygen_each} ? "\"$vol_received->{PRINT}\"" : "all raw backups") . ":\n";
|
||||||
|
my $kdf_values = run_cmd(cmd => [ $encrypt->{kdf_backend}, $encrypt->{kdf_keysize} ],
|
||||||
|
non_destructive => 1,
|
||||||
|
name => $kdf_backend_name
|
||||||
|
);
|
||||||
|
return undef unless(defined($kdf_values));
|
||||||
|
foreach(split("\n", $kdf_values)) {
|
||||||
|
chomp;
|
||||||
|
next if /^\s*$/; # ignore empty lines
|
||||||
|
if(/^KEY=([0-9a-fA-f]+)/) {
|
||||||
|
$kdf_session_key = $1;
|
||||||
|
}
|
||||||
|
elsif(/^([a-z_]+)=(.*)/) {
|
||||||
|
my $info_key = 'kdf_' . $1;
|
||||||
|
my $info_val = $2;
|
||||||
|
DEBUG "Adding raw_info from kdf_backend: $info_key=$info_val";
|
||||||
|
$kdf_vars{$info_key} = $info_val;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ERROR "Ambiguous line from kdf_backend: $encrypt->{kdf_backend}";
|
||||||
|
return undef;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
unless($kdf_session_key && (length($kdf_session_key) == ($encrypt->{kdf_keysize} * 2))) {
|
||||||
|
ERROR "Ambiguous key value from kdf_backend: $encrypt->{kdf_backend}";
|
||||||
|
return undef;
|
||||||
|
}
|
||||||
|
INFO "Generated session key for: $vol_received->{PRINT}";
|
||||||
|
}
|
||||||
|
$encrypt_key = $kdf_session_key;
|
||||||
|
%raw_info = ( %kdf_vars, %raw_info );
|
||||||
|
}
|
||||||
|
|
||||||
my @openssl_options = (
|
my @openssl_options = (
|
||||||
'-' . $encrypt->{ciphername},
|
'-' . $encrypt->{ciphername},
|
||||||
'-K $(cat ' . $encrypt->{keyfile} . ')',
|
'-K', $encrypt_key,
|
||||||
);
|
);
|
||||||
push @openssl_options, ('-iv', $iv) if($iv);
|
push @openssl_options, ('-iv', $iv) if($iv);
|
||||||
|
|
||||||
|
@ -2852,6 +2913,9 @@ sub config_encrypt_hash($$)
|
||||||
iv_size => config_key($config, "openssl_iv_size"),
|
iv_size => config_key($config, "openssl_iv_size"),
|
||||||
ciphername => config_key($config, "openssl_ciphername"),
|
ciphername => config_key($config, "openssl_ciphername"),
|
||||||
keyfile => config_key($config, "openssl_keyfile"),
|
keyfile => config_key($config, "openssl_keyfile"),
|
||||||
|
kdf_keygen_each => (config_key($config, "kdf_keygen") eq "each"),
|
||||||
|
kdf_backend => config_key($config, "kdf_backend"),
|
||||||
|
kdf_keysize => config_key($config, "kdf_keysize"),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,60 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
#
|
||||||
|
# kdf_pbkdf2.py - (kdf_backend for btrbk)
|
||||||
|
#
|
||||||
|
# Copyright (c) 2017 Axel Burri
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
# ---------------------------------------------------------------------
|
||||||
|
# The official btrbk website is located at:
|
||||||
|
# http://digint.ch/btrbk/
|
||||||
|
#
|
||||||
|
# Author:
|
||||||
|
# Axel Burri <axel@tty0.ch>
|
||||||
|
# ---------------------------------------------------------------------
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
import getpass
|
||||||
|
import hashlib
|
||||||
|
|
||||||
|
def passprompt():
|
||||||
|
pprompt = lambda: (getpass.getpass("Passphrase: "), getpass.getpass("Retype passphrase: "))
|
||||||
|
p1, p2 = pprompt()
|
||||||
|
while p1 != p2:
|
||||||
|
print("No match, please try again", file=sys.stderr)
|
||||||
|
p1, p2 = pprompt()
|
||||||
|
return p1
|
||||||
|
|
||||||
|
if len(sys.argv) <= 1:
|
||||||
|
print("Usage: {} <dklen>".format(sys.argv[0]), file=sys.stderr)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
hash_name = "sha256"
|
||||||
|
iterations = 300000
|
||||||
|
dklen = int(sys.argv[1])
|
||||||
|
salt = os.urandom(16)
|
||||||
|
password = passprompt().encode("utf-8")
|
||||||
|
|
||||||
|
dk = hashlib.pbkdf2_hmac(hash_name=hash_name, password=password, salt=salt, iterations=iterations, dklen=dklen)
|
||||||
|
|
||||||
|
salt_hex = "".join(["{:02x}".format(x) for x in salt])
|
||||||
|
dk_hex = "".join(["{:02x}".format(x) for x in dk])
|
||||||
|
|
||||||
|
print("KEY=" + dk_hex);
|
||||||
|
print("algoritm=pbkdf2_hmac");
|
||||||
|
print("hash_name=" + hash_name);
|
||||||
|
print("salt=" + salt_hex);
|
||||||
|
print("iterations=" + str(iterations));
|
|
@ -456,6 +456,12 @@ openssl_ciphername <name> (defaults to "aes-256-cbc")
|
||||||
openssl_iv_size <size-in-bytes>|no (depends on selected cipher)
|
openssl_iv_size <size-in-bytes>|no (depends on selected cipher)
|
||||||
.PP
|
.PP
|
||||||
openssl_keyfile <file>|no
|
openssl_keyfile <file>|no
|
||||||
|
.PP
|
||||||
|
kdf_backend <file>|no
|
||||||
|
.PP
|
||||||
|
kdf_keysize <size> (defaults to "32")
|
||||||
|
.PP
|
||||||
|
kdf_keygen once|each (defaults to "once")
|
||||||
.RE
|
.RE
|
||||||
.PD
|
.PD
|
||||||
.PP
|
.PP
|
||||||
|
|
Loading…
Reference in New Issue