mirror of
https://github.com/NixOS/nixpkgs.git
synced 2024-11-01 23:22:37 +00:00
Merge branch 'yubikey' of git://github.com/Calrama/nixpkgs
This commit is contained in:
commit
12315a278c
@ -39,153 +39,123 @@ let
|
||||
${optionalString (luks.yubikeySupport && (yubikey != null)) ''
|
||||
|
||||
rbtohex() {
|
||||
od -An -vtx1 | tr -d ' \n'
|
||||
( 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
|
||||
( tr '[:lower:]' '[:upper:]' | sed -e 's/\([0-9A-F]\{2\}\)/\\\\\\x\1/gI' | xargs printf )
|
||||
}
|
||||
|
||||
open_yubikey() {
|
||||
|
||||
# Make all of these local to this function
|
||||
# to prevent their values being leaked
|
||||
local salt
|
||||
local iterations
|
||||
local k_user
|
||||
local challenge
|
||||
local response
|
||||
local k_luks
|
||||
local opened
|
||||
local new_salt
|
||||
local new_iterations
|
||||
local new_challenge
|
||||
local new_response
|
||||
local new_k_luks
|
||||
|
||||
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)"
|
||||
''}
|
||||
salt="$(cat ${yubikey.storage.mountPoint}${yubikey.storage.path} | sed -n 1p | tr -d '\n')"
|
||||
iterations="$(cat ${yubikey.storage.mountPoint}${yubikey.storage.path} | sed -n 2p | tr -d '\n')"
|
||||
challenge="$(echo -n $salt | openssl-wrap dgst -binary -sha512 | rbtohex)"
|
||||
response="$(ykchalresp -${toString yubikey.slot} -x $challenge 2>/dev/null)"
|
||||
|
||||
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 ''
|
||||
if [ ! -z "$k_user" ]; then
|
||||
k_luks="$(echo -n $k_user | pbkdf2-sha512 ${toString yubikey.keyLength} $iterations $response | rbtohex)"
|
||||
else
|
||||
checksum_correct=0
|
||||
k_luks="$(echo | pbkdf2-sha512 ${toString yubikey.keyLength} $iterations $response | rbtohex)"
|
||||
fi
|
||||
|
||||
echo -n "$k_luks" | hextorb | cryptsetup luksOpen ${device} ${name} ${optionalString allowDiscards "--allow-discards"} --key-file=-
|
||||
|
||||
if [ $? == "0" ]; then
|
||||
opened=true
|
||||
break
|
||||
else
|
||||
opened=false
|
||||
echo "Authentication failed!"
|
||||
fi
|
||||
''}
|
||||
done
|
||||
|
||||
if [ "$checksum_correct" != "1" ]; then
|
||||
if [ "$opened" == false ]; 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)"
|
||||
echo -n "Gathering entropy for new salt (please enter random keys to generate entropy if this blocks for long)..."
|
||||
for i in $(seq ${toString yubikey.saltLength}); do
|
||||
byte="$(dd if=/dev/random bs=1 count=1 2>/dev/null | rbtohex)";
|
||||
new_salt="$new_salt$byte";
|
||||
echo -n .
|
||||
done;
|
||||
echo "ok"
|
||||
|
||||
local k_luks
|
||||
k_luks="$(echo -n $aes_blob_decrypted | drop 40 | take 128)"
|
||||
new_iterations="$iterations"
|
||||
${optionalString (yubikey.iterationStep > 0) ''
|
||||
new_iterations="$(($new_iterations + ${toString yubikey.iterationStep}))"
|
||||
''}
|
||||
|
||||
echo -n "$k_luks" | hextorb | cryptsetup luksOpen ${device} ${name} ${optionalString allowDiscards "--allow-discards"} --key-file=-
|
||||
new_challenge="$(echo -n $new_salt | openssl-wrap dgst -binary -sha512 | rbtohex)"
|
||||
|
||||
update_failed=false
|
||||
new_response="$(ykchalresp -${toString yubikey.slot} -x $new_challenge 2>/dev/null)"
|
||||
|
||||
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}
|
||||
''}
|
||||
if [ ! -z "$k_user" ]; then
|
||||
new_k_luks="$(echo -n $k_user | pbkdf2-sha512 ${toString yubikey.keyLength} $new_iterations $new_response | rbtohex)"
|
||||
else
|
||||
echo "Warning: Could not obtain new UUID, current challenge persists!"
|
||||
new_k_luks="$(echo | pbkdf2-sha512 ${toString yubikey.keyLength} $new_iterations $new_response | rbtohex)"
|
||||
fi
|
||||
|
||||
mkdir -p ${yubikey.ramfsMountPoint}
|
||||
# A ramfs is used here to ensure that the file used to update
|
||||
# the key slot with cryptsetup will never get swapped out.
|
||||
# Warning: Do NOT replace with tmpfs!
|
||||
mount -t ramfs none ${yubikey.ramfsMountPoint}
|
||||
|
||||
echo -n "$new_k_luks" | hextorb > ${yubikey.ramfsMountPoint}/new_key
|
||||
echo -n "$k_luks" | hextorb | cryptsetup luksChangeKey ${device} --key-file=- ${yubikey.ramfsMountPoint}/new_key
|
||||
|
||||
if [ $? == "0" ]; then
|
||||
echo -ne "$new_salt\n$new_iterations" > ${yubikey.storage.mountPoint}${yubikey.storage.path}
|
||||
else
|
||||
echo "Warning: Could not update LUKS key, current challenge persists!"
|
||||
fi
|
||||
|
||||
rm -f ${yubikey.ramfsMountPoint}/new_key
|
||||
umount ${yubikey.ramfsMountPoint}
|
||||
rm -rf ${yubikey.ramfsMountPoint}
|
||||
|
||||
umount ${yubikey.storage.mountPoint}
|
||||
}
|
||||
|
||||
${optionalString (yubikey.gracePeriod > 0) ''
|
||||
echo -n "Waiting ${toString yubikey.gracePeriod} seconds as grace..."
|
||||
for i in $(seq ${toString yubikey.gracePeriod}); do
|
||||
sleep 1
|
||||
echo -n .
|
||||
done
|
||||
echo "ok"
|
||||
''}
|
||||
|
||||
yubikey_missing=true
|
||||
ykinfo -v 1>/dev/null 2>&1
|
||||
if [ $? != "0" ]; then
|
||||
@ -336,21 +306,45 @@ in
|
||||
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";
|
||||
};
|
||||
|
||||
saltLength = mkOption {
|
||||
default = 16;
|
||||
type = types.int;
|
||||
description = "Length of the new salt in byte (64 is the effective maximum)";
|
||||
};
|
||||
|
||||
keyLength = mkOption {
|
||||
default = 64;
|
||||
type = types.int;
|
||||
description = "Length of the LUKS slot key derived with PBKDF2 in byte";
|
||||
};
|
||||
|
||||
iterationStep = mkOption {
|
||||
default = 0;
|
||||
type = types.int;
|
||||
description = "How much the iteration count for PBKDF2 is increased at each successful authentication";
|
||||
};
|
||||
|
||||
gracePeriod = mkOption {
|
||||
default = 2;
|
||||
type = types.int;
|
||||
description = "Time in seconds to wait before attempting to find the Yubikey";
|
||||
};
|
||||
|
||||
ramfsMountPoint = mkOption {
|
||||
default = "/crypt-ramfs";
|
||||
type = types.string;
|
||||
description = "Path where the ramfs used to update the LUKS key will be mounted in stage-1";
|
||||
};
|
||||
|
||||
storage = mkOption {
|
||||
type = types.optionSet;
|
||||
description = "Options related to the authentication record";
|
||||
description = "Options related to the storing the salt";
|
||||
|
||||
options = {
|
||||
device = mkOption {
|
||||
@ -358,7 +352,7 @@ in
|
||||
type = types.path;
|
||||
description = ''
|
||||
An unencrypted device that will temporarily be mounted in stage-1.
|
||||
Must contain the authentication record for this LUKS device.
|
||||
Must contain the current salt to create the challenge for this LUKS device.
|
||||
'';
|
||||
};
|
||||
|
||||
@ -378,7 +372,7 @@ in
|
||||
default = "/crypt-storage/default";
|
||||
type = types.string;
|
||||
description = ''
|
||||
Absolute path of the authentication record on the unencrypted device with
|
||||
Absolute path of the salt on the unencrypted device with
|
||||
that device's root directory as "/".
|
||||
'';
|
||||
};
|
||||
@ -420,11 +414,13 @@ in
|
||||
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
|
||||
|
||||
cc -O3 -I${pkgs.openssl}/include -L${pkgs.openssl}/lib ${./pbkdf2-sha512.c} -o $out/bin/pbkdf2-sha512 -lcrypto
|
||||
strip -s $out/bin/pbkdf2-sha512
|
||||
|
||||
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
|
||||
@ -444,7 +440,6 @@ EOF
|
||||
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
|
||||
|
38
nixos/modules/system/boot/pbkdf2-sha512.c
Normal file
38
nixos/modules/system/boot/pbkdf2-sha512.c
Normal file
@ -0,0 +1,38 @@
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <openssl/evp.h>
|
||||
|
||||
void hextorb(uint8_t* hex, uint8_t* rb)
|
||||
{
|
||||
while(sscanf(hex, "%2x", rb) == 1)
|
||||
{
|
||||
hex += 2;
|
||||
rb += 1;
|
||||
}
|
||||
*rb = '\0';
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
uint8_t k_user[2048];
|
||||
uint8_t salt[2048];
|
||||
uint8_t key[4096];
|
||||
|
||||
uint32_t key_length = atoi(argv[1]);
|
||||
uint32_t iteration_count = atoi(argv[2]);
|
||||
|
||||
hextorb(argv[3], salt);
|
||||
uint32_t salt_length = strlen(argv[3]) / 2;
|
||||
|
||||
fgets(k_user, 2048, stdin);
|
||||
uint32_t k_user_length = strlen(k_user);
|
||||
if(k_user[k_user_length - 1] == '\n') {
|
||||
k_user[k_user_length - 1] = '\0';
|
||||
}
|
||||
|
||||
PKCS5_PBKDF2_HMAC(k_user, k_user_length, salt, salt_length, iteration_count, EVP_sha512(), key_length, key);
|
||||
fwrite(key, 1, key_length, stdout);
|
||||
|
||||
return 0;
|
||||
}
|
Loading…
Reference in New Issue
Block a user