forked from mirrors/nixpkgs
Merge branch 'yubikey' of git://github.com/Calrama/nixpkgs
This commit is contained in:
commit
2062abfd4f
|
@ -18,6 +18,7 @@
|
|||
bjornfor = "Bjørn Forsman <bjorn.forsman@gmail.com>";
|
||||
bluescreen303 = "Mathijs Kwik <mathijs@bluescreen303.nl>";
|
||||
bodil = "Bodil Stokke <nix@bodil.org>";
|
||||
calrama = "Moritz Maxeiner <moritz@ucworks.org>";
|
||||
chaoflow = "Florian Friesdorf <flo@chaoflow.net>";
|
||||
coconnor = "Corey O'Connor <coreyoconnor@gmail.com>";
|
||||
coroa = "Jonas Hörsch <jonas@chaoflow.net>";
|
||||
|
|
|
@ -5,7 +5,7 @@ with pkgs.lib;
|
|||
let
|
||||
luks = config.boot.initrd.luks;
|
||||
|
||||
openCommand = { name, device, keyFile, keyFileSize, allowDiscards, ... }: ''
|
||||
openCommand = { name, device, keyFile, keyFileSize, allowDiscards, yubikey, ... }: ''
|
||||
# Wait for luksRoot to appear, e.g. if on a usb drive.
|
||||
# XXX: copied and adapted from stage-1-init.sh - should be
|
||||
# available as a function.
|
||||
|
@ -31,9 +31,191 @@ let
|
|||
fi
|
||||
''}
|
||||
|
||||
open_normally() {
|
||||
cryptsetup luksOpen ${device} ${name} ${optionalString allowDiscards "--allow-discards"} \
|
||||
${optionalString (keyFile != null) "--key-file=${keyFile} ${optionalString (keyFileSize != null) "--keyfile-size=${toString keyFileSize}"}"}
|
||||
}
|
||||
|
||||
${optionalString (luks.yubikeySupport && (yubikey != null)) ''
|
||||
|
||||
rbtohex() {
|
||||
od -An -vtx1 | tr -d ' \n'
|
||||
}
|
||||
|
||||
hextorb() {
|
||||
tr '[:lower:]' '[:upper:]' | sed -e 's|\([0-9A-F]\{2\}\)|\\\\\\x\1|gI' | xargs printf
|
||||
}
|
||||
|
||||
take() {
|
||||
local c="$1"
|
||||
shift
|
||||
head -c $c "$@"
|
||||
}
|
||||
|
||||
drop() {
|
||||
local c="$1"
|
||||
shift
|
||||
if [ -e "$1" ]; then
|
||||
cat "$1" | ( dd of=/dev/null bs="$c" count=1 2>/dev/null ; dd 2>/dev/null )
|
||||
else
|
||||
( dd of=/dev/null bs="$c" count=1 2>/dev/null ; dd 2>/dev/null )
|
||||
fi
|
||||
}
|
||||
|
||||
open_yubikey() {
|
||||
|
||||
mkdir -p ${yubikey.storage.mountPoint}
|
||||
mount -t ${yubikey.storage.fsType} ${toString yubikey.storage.device} ${yubikey.storage.mountPoint}
|
||||
|
||||
local uuid_r
|
||||
local k_user
|
||||
local challenge
|
||||
local k_blob
|
||||
local aes_blob_decrypted
|
||||
local checksum_correct
|
||||
local checksum
|
||||
local uuid_luks
|
||||
local user_record
|
||||
|
||||
uuid_luks="$(cryptsetup luksUUID ${device} | take 36 | tr -d '-')"
|
||||
|
||||
${optionalString (!yubikey.multiUser) ''
|
||||
user_record="$(cat ${yubikey.storage.mountPoint}${yubikey.storage.path})"
|
||||
uuid_r="$(echo -n $user_record | take 32)"
|
||||
''}
|
||||
|
||||
for try in $(seq 3); do
|
||||
|
||||
${optionalString yubikey.multiUser ''
|
||||
local user_id
|
||||
echo -n "Enter user id: "
|
||||
read -s user_id
|
||||
echo
|
||||
''}
|
||||
|
||||
${optionalString yubikey.twoFactor ''
|
||||
echo -n "Enter two-factor passphrase: "
|
||||
read -s k_user
|
||||
echo
|
||||
''}
|
||||
|
||||
${optionalString yubikey.multiUser ''
|
||||
local user_id_hash
|
||||
user_id_hash="$(echo -n $user_id | openssl-wrap dgst -binary -sha512 | rbtohex)"
|
||||
|
||||
user_record="$(sed -n -e /^$user_id_hash[^$]*$/p ${yubikey.storage.mountPoint}${yubikey.storage.path} | tr -d '\n')"
|
||||
|
||||
if [ ! -z "$user_record" ]; then
|
||||
user_record="$(echo -n $user_record | drop 128)"
|
||||
uuid_r="$(echo -n $user_record | take 32)"
|
||||
''}
|
||||
|
||||
challenge="$(echo -n $k_user$uuid_r$uuid_luks | openssl-wrap dgst -binary -sha1 | rbtohex)"
|
||||
|
||||
k_blob="$(ykchalresp -${toString yubikey.slot} -x $challenge 2>/dev/null)"
|
||||
|
||||
aes_blob_decrypted="$(echo -n $user_record | drop 32 | hextorb | openssl-wrap enc -d -aes-256-ctr -K $k_blob -iv $uuid_r | rbtohex)"
|
||||
|
||||
checksum="$(echo -n $aes_blob_decrypted | drop 168)"
|
||||
if [ "$(echo -n $aes_blob_decrypted | hextorb | take 84 | openssl-wrap dgst -binary -sha512 | rbtohex)" == "$checksum" ]; then
|
||||
checksum_correct=1
|
||||
break
|
||||
else
|
||||
checksum_correct=0
|
||||
echo "Authentication failed!"
|
||||
fi
|
||||
|
||||
${optionalString yubikey.multiUser ''
|
||||
else
|
||||
checksum_correct=0
|
||||
echo "Authentication failed!"
|
||||
fi
|
||||
''}
|
||||
done
|
||||
|
||||
if [ "$checksum_correct" != "1" ]; then
|
||||
umount ${yubikey.storage.mountPoint}
|
||||
echo "Maximum authentication errors reached"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
local k_yubi
|
||||
k_yubi="$(echo -n $aes_blob_decrypted | take 40)"
|
||||
|
||||
local k_luks
|
||||
k_luks="$(echo -n $aes_blob_decrypted | drop 40 | take 128)"
|
||||
|
||||
echo -n "$k_luks" | hextorb | cryptsetup luksOpen ${device} ${name} ${optionalString allowDiscards "--allow-discards"} --key-file=-
|
||||
|
||||
update_failed=false
|
||||
|
||||
local new_uuid_r
|
||||
new_uuid_r="$(uuidgen)"
|
||||
if [ $? != "0" ]; then
|
||||
for try in $(seq 10); do
|
||||
sleep 1
|
||||
new_uuid_r="$(uuidgen)"
|
||||
if [ $? == "0" ]; then break; fi
|
||||
if [ $try -eq 10 ]; then update_failed=true; fi
|
||||
done
|
||||
fi
|
||||
|
||||
if [ "$update_failed" == false ]; then
|
||||
new_uuid_r="$(echo -n $new_uuid_r | take 36 | tr -d '-')"
|
||||
|
||||
local new_challenge
|
||||
new_challenge="$(echo -n $k_user$new_uuid_r$uuid_luks | openssl-wrap dgst -binary -sha1 | rbtohex)"
|
||||
|
||||
local new_k_blob
|
||||
new_k_blob="$(echo -n $new_challenge | hextorb | openssl-wrap dgst -binary -sha1 -mac HMAC -macopt hexkey:$k_yubi | rbtohex)"
|
||||
|
||||
local new_aes_blob
|
||||
new_aes_blob=$(echo -n "$k_yubi$k_luks$checksum" | hextorb | openssl-wrap enc -e -aes-256-ctr -K "$new_k_blob" -iv "$new_uuid_r" | rbtohex)
|
||||
|
||||
${optionalString yubikey.multiUser ''
|
||||
sed -i -e "s|^$user_id_hash$user_record|$user_id_hash$new_uuid_r$new_aes_blob|1"
|
||||
''}
|
||||
|
||||
${optionalString (!yubikey.multiUser) ''
|
||||
echo -n "$new_uuid_r$new_aes_blob" > ${yubikey.storage.mountPoint}${yubikey.storage.path}
|
||||
''}
|
||||
else
|
||||
echo "Warning: Could not obtain new UUID, current challenge persists!"
|
||||
fi
|
||||
|
||||
umount ${yubikey.storage.mountPoint}
|
||||
}
|
||||
|
||||
yubikey_missing=true
|
||||
ykinfo -v 1>/dev/null 2>&1
|
||||
if [ $? != "0" ]; then
|
||||
echo -n "waiting 10 seconds for yubikey to appear..."
|
||||
for try in $(seq 10); do
|
||||
sleep 1
|
||||
ykinfo -v 1>/dev/null 2>&1
|
||||
if [ $? == "0" ]; then
|
||||
yubikey_missing=false
|
||||
break
|
||||
fi
|
||||
echo -n .
|
||||
done
|
||||
echo "ok"
|
||||
else
|
||||
yubikey_missing=false
|
||||
fi
|
||||
|
||||
if [ "$yubikey_missing" == true ]; then
|
||||
echo "no yubikey found, falling back to non-yubikey open procedure"
|
||||
open_normally
|
||||
else
|
||||
open_yubikey
|
||||
fi
|
||||
''}
|
||||
|
||||
# open luksRoot and scan for logical volumes
|
||||
cryptsetup luksOpen ${device} ${name} ${optionalString allowDiscards "--allow-discards"} \
|
||||
${optionalString (keyFile != null) "--key-file=${keyFile} ${optionalString (keyFileSize != null) "--keyfile-size=${toString keyFileSize}"}"}
|
||||
${optionalString ((!luks.yubikeySupport) || (yubikey == null)) ''
|
||||
open_normally
|
||||
''}
|
||||
'';
|
||||
|
||||
isPreLVM = f: f.preLVM;
|
||||
|
@ -139,10 +321,84 @@ in
|
|||
'';
|
||||
};
|
||||
|
||||
};
|
||||
yubikey = mkOption {
|
||||
default = null;
|
||||
type = types.nullOr types.optionSet;
|
||||
description = ''
|
||||
The options to use for this LUKS device in Yubikey-PBA.
|
||||
If null (the default), Yubikey-PBA will be disabled for this device.
|
||||
'';
|
||||
|
||||
options = {
|
||||
twoFactor = mkOption {
|
||||
default = true;
|
||||
type = types.bool;
|
||||
description = "Whether to use a passphrase and a Yubikey (true), or only a Yubikey (false)";
|
||||
};
|
||||
|
||||
multiUser = mkOption {
|
||||
default = false;
|
||||
type = types.bool;
|
||||
description = "Whether to allow multiple users to authenticate with a Yubikey";
|
||||
};
|
||||
|
||||
slot = mkOption {
|
||||
default = 2;
|
||||
type = types.int;
|
||||
description = "Which slot on the Yubikey to challenge";
|
||||
};
|
||||
|
||||
storage = mkOption {
|
||||
type = types.optionSet;
|
||||
description = "Options related to the authentication record";
|
||||
|
||||
options = {
|
||||
device = mkOption {
|
||||
default = /dev/sda1;
|
||||
type = types.path;
|
||||
description = ''
|
||||
An unencrypted device that will temporarily be mounted in stage-1.
|
||||
Must contain the authentication record for this LUKS device.
|
||||
'';
|
||||
};
|
||||
|
||||
fsType = mkOption {
|
||||
default = "vfat";
|
||||
type = types.string;
|
||||
description = "The filesystem of the unencrypted device";
|
||||
};
|
||||
|
||||
mountPoint = mkOption {
|
||||
default = "/crypt-storage";
|
||||
type = types.string;
|
||||
description = "Path where the unencrypted device will be mounted in stage-1";
|
||||
};
|
||||
|
||||
path = mkOption {
|
||||
default = "/crypt-storage/default";
|
||||
type = types.string;
|
||||
description = ''
|
||||
Absolute path of the authentication record on the unencrypted device with
|
||||
that device's root directory as "/".
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
};
|
||||
};
|
||||
|
||||
boot.initrd.luks.yubikeySupport = mkOption {
|
||||
default = false;
|
||||
type = types.bool;
|
||||
description = ''
|
||||
Enables support for authenticating with a Yubikey on LUKS devices.
|
||||
See the NixOS wiki for information on how to properly setup a LUKS device
|
||||
and a Yubikey to work with this feature.
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
config = mkIf (luks.devices != []) {
|
||||
|
@ -157,15 +413,47 @@ in
|
|||
# copy the cryptsetup binary and it's dependencies
|
||||
boot.initrd.extraUtilsCommands = ''
|
||||
cp -pdv ${pkgs.cryptsetup}/sbin/cryptsetup $out/bin
|
||||
# XXX: do we have a function that does this?
|
||||
for lib in $(ldd $out/bin/cryptsetup |grep '=>' |grep /nix/store/ |cut -d' ' -f3); do
|
||||
cp -pdvn $lib $out/lib
|
||||
cp -pvn $(readlink -f $lib) $out/lib
|
||||
done
|
||||
|
||||
cp -pdv ${pkgs.libgcrypt}/lib/libgcrypt*.so.* $out/lib
|
||||
cp -pdv ${pkgs.libgpgerror}/lib/libgpg-error*.so.* $out/lib
|
||||
cp -pdv ${pkgs.cryptsetup}/lib/libcryptsetup*.so.* $out/lib
|
||||
cp -pdv ${pkgs.popt}/lib/libpopt*.so.* $out/lib
|
||||
|
||||
${optionalString luks.yubikeySupport ''
|
||||
cp -pdv ${pkgs.utillinux}/bin/uuidgen $out/bin
|
||||
cp -pdv ${pkgs.ykpers}/bin/ykchalresp $out/bin
|
||||
cp -pdv ${pkgs.ykpers}/bin/ykinfo $out/bin
|
||||
cp -pdv ${pkgs.openssl}/bin/openssl $out/bin
|
||||
|
||||
cp -pdv ${pkgs.libusb1}/lib/libusb*.so.* $out/lib
|
||||
cp -pdv ${pkgs.ykpers}/lib/libykpers*.so.* $out/lib
|
||||
cp -pdv ${pkgs.libyubikey}/lib/libyubikey*.so.* $out/lib
|
||||
cp -pdv ${pkgs.openssl}/lib/libssl*.so.* $out/lib
|
||||
cp -pdv ${pkgs.openssl}/lib/libcrypto*.so.* $out/lib
|
||||
|
||||
mkdir -p $out/etc/ssl
|
||||
cp -pdv ${pkgs.openssl}/etc/ssl/openssl.cnf $out/etc/ssl
|
||||
|
||||
cat > $out/bin/openssl-wrap <<EOF
|
||||
#!$out/bin/sh
|
||||
EOF
|
||||
chmod +x $out/bin/openssl-wrap
|
||||
''}
|
||||
'';
|
||||
|
||||
boot.initrd.extraUtilsCommandsTest = ''
|
||||
$out/bin/cryptsetup --version
|
||||
${optionalString luks.yubikeySupport ''
|
||||
$out/bin/uuidgen --version
|
||||
$out/bin/ykchalresp -V
|
||||
$out/bin/ykinfo -V
|
||||
cat > $out/bin/openssl-wrap <<EOF
|
||||
#!$out/bin/sh
|
||||
export OPENSSL_CONF=$out/etc/ssl/openssl.cnf
|
||||
$out/bin/openssl "\$@"
|
||||
EOF
|
||||
$out/bin/openssl-wrap version
|
||||
''}
|
||||
'';
|
||||
|
||||
boot.initrd.preLVMCommands = concatMapStrings openCommand preLVM;
|
||||
|
|
23
pkgs/applications/misc/ykpers/default.nix
Normal file
23
pkgs/applications/misc/ykpers/default.nix
Normal file
|
@ -0,0 +1,23 @@
|
|||
{stdenv, fetchurl, pkgconfig, libusb1, libyubikey}:
|
||||
|
||||
stdenv.mkDerivation rec
|
||||
{
|
||||
version = "1.15.0";
|
||||
name = "ykpers-${version}";
|
||||
|
||||
src = fetchurl
|
||||
{
|
||||
url = "http://opensource.yubico.com/yubikey-personalization/releases/${name}.tar.gz";
|
||||
sha256 = "1n4s8kk31q5zh2rm7sj9qmv86yl8ibimdnpvk9ny391a88qlypyd";
|
||||
};
|
||||
|
||||
buildInputs = [pkgconfig libusb1 libyubikey];
|
||||
|
||||
meta =
|
||||
{
|
||||
homepage = "http://opensource.yubico.com/yubikey-personalization/";
|
||||
description = "YubiKey Personalization cross-platform library and tool";
|
||||
license = "bsd";
|
||||
maintainers = [ stdenv.lib.maintainers.calrama ];
|
||||
};
|
||||
}
|
21
pkgs/development/libraries/libyubikey/default.nix
Normal file
21
pkgs/development/libraries/libyubikey/default.nix
Normal file
|
@ -0,0 +1,21 @@
|
|||
{stdenv, fetchurl}:
|
||||
|
||||
stdenv.mkDerivation rec
|
||||
{
|
||||
version = "1.11";
|
||||
name = "libyubikey-${version}";
|
||||
|
||||
src = fetchurl
|
||||
{
|
||||
url = "http://opensource.yubico.com/yubico-c/releases/${name}.tar.gz";
|
||||
sha256 = "19pm4rqsnm9r0n5j26bqkxa1jpimdavzcvg5g7p416vkjhxc6lw9";
|
||||
};
|
||||
|
||||
meta =
|
||||
{
|
||||
homepage = "http://opensource.yubico.com/yubico-c/";
|
||||
description = "C library for manipulating Yubico YubiKey One-Time Passwords (OTPs)";
|
||||
license = "bsd";
|
||||
maintainers = [ stdenv.lib.maintainers.calrama ];
|
||||
};
|
||||
}
|
|
@ -5063,6 +5063,8 @@ let
|
|||
libyamlcpp = callPackage ../development/libraries/libyaml-cpp { };
|
||||
libyamlcpp03 = callPackage ../development/libraries/libyaml-cpp/0.3.x.nix { };
|
||||
|
||||
libyubikey = callPackage ../development/libraries/libyubikey {};
|
||||
|
||||
libzip = callPackage ../development/libraries/libzip { };
|
||||
|
||||
libzrtpcpp = callPackage ../development/libraries/libzrtpcpp { };
|
||||
|
@ -9153,6 +9155,8 @@ let
|
|||
|
||||
qgis = callPackage ../applications/misc/qgis {};
|
||||
|
||||
ykpers = callPackage ../applications/misc/ykpers {};
|
||||
|
||||
yoshimi = callPackage ../applications/audio/yoshimi {
|
||||
fltk = fltk13;
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue