# Genkernel initramfs with LUKS+GPG key

## Xander314

I am using DM-Crypt LUKS for full disk encryption on my laptop. I have a keyfile on an external drive encrypted with GPG. When booting from a live CD I can unlock the dmcrypt volume with

gpg -qd /path/to/key/file | cryptsetup luksOpen /dev/sdc1 crypt_root

This works fine.

However, I am having trouble with my initramfs. I created it using genkernel with LVM, DMCrypt and GPG support. When I boot it finds the keyfile and asks for my GPG password. But after providing this, it simply says "No key available with this passphrase", despite the fact that the very same keyfile will unlock the disk when I call gpg and cryptsetup manually from the rescue CD.

Any ideas? What is going wrong here?

----------

## frostschutz

Is it asking for the GPG password or showing the LUKS prompt directly? In the latter case the Initramfs ignored your GPG key for some reason.

You could add the debug parameter to get a shell and see if the GPG key is there at all, if it's the correct file (md5sum it), what the correct path to the GPG key is inside the initramfs, to use with the root_key parameter. And last but not least whether your command works within the Initramfs shell.

Initramfs uses gpg 1.x as the 2.x is much more complicated to integrate; that's usually not a problem unless you do something special not supported by gpg 1.x

----------

## Xander314

Thanks for the help. I booted into the initramfs shell and tried to run the gpg command. I got the error "gpg: cannot open /dev/tty': No such device or address".

----------

## frostschutz

That's why I don't use GPG for encrypted keys... it just sucks so much in the Initramfs.

Basically you have to link /dev/tty to /dev/console here. Genkernel already does that before calling GPG; so that should not be your problem.

----------

## Xander314

Thanks. Having done that I can manually open the LUKS device. I'm currently investigating exactly what command the initramfs script is calling.

----------

## frostschutz

if you can add a set -x to the script it may print the commands for you

not sure if genkernel has an option to enable that automatically

----------

## Xander314

I think I found the problem. When I created the LUKS volume I called

```
gpg -qd /key | cryptsetup luksFormat ...
```

The initramfs script calls

```
gpg -qd /key | cryptsetup luksOpen -d - ...
```

I think I read somewhere that if you don't use the --key-file/-d option then it's possible that not all of the keyfile is read into cryptsetup, so this could be the problem. I'll update this when I know if it worked.

----------

## frostschutz

That'll be your issue, then.

It silently stops reading after newline, that's one major security pitfall in cryptsetup/LUKS, because if you happen to make that mistake and you're unlucky and the newline was within the first 4 bytes of your "keyfile", it's really easy to brute force ...  :Laughing: 

One way to avoid it (apart from doing it right in the first place) is to use a keyfile that can be typed in if you are truly desparate.

```
$ pwgen -sync 64 1

.?/Z\'mc$G,v|b3>Y#CZ8OpUm}5\Vk;d}"^*dTS[1HL?yhlN~hYV8L@,)LA"J(Js

$ openssl rand -base64 48

lina2hMQq4qR4OdUhyFr9KLpP2w5rWXjkJoF8VdoNY9Kf4zzEGCozysDd2scjhLS
```

Make sure the file does -not- have a newline at the end - that way it doesn't matter which method you use.

----------

## Xander314

Yep, that fixed it. I had already ready about this somewhere but it took a while for the significant to sink in. At least I know now... Thanks for all the help.

----------

## Massimo B.

Inspired by https://wiki.gentoo.org/wiki/Sakaki%27s_EFI_Install_Guide/Preparing_the_LUKS-LVM_Filesystem_and_Boot_USB_Key I try to create a similar setup.

I already created the gpg encrypted key file, and added the key to LUKS. For the genkernel initramfs I passed root_key=/boot/LUKS-KEY.gpg.

But it cannot find the device. The rescue prompt:

```
!! Media not found

>> Please insert removable device for root

>> Looking for the key

>> Attepmting to mount media: /dev/sda1

>> Attepmting to mount media: /dev/sda2

>> Attepmting to mount media: /dev/sdb

>> Attepmting to mount media: /dev/sdc

>> Attepmting to mount media: /dev/sdd1

!! Media not found

!! Removable device not found

!! Could not find the root key device in .

!! Please specify another value or:

!! - press Enter for the same

!! - type "shell" for a shell

!! - type "q" to skip...

root key device() :: /dev/sd1

>> Using key device /dev/sdd1.

>> Removable device /dev/sdd1 mounted.

!! Key {LUKS_KEY} on device /dev/sdd1 not found.

!! Could not find the root key in /boot/LUKS-KEY.gpg.

!! Please specify another value or:

!! - press Enter for the same

!! - type "shell" for a shell

!! - type "q" to skip...

root key(/boot/LUKS-KEY.gpg) :: LUKS-KEY.gpg

!! Could not find the root device in /dev/sdd1..

!! Please specify another value or:

!! - press Enter for the same

!! - type "shell" for a shell

!! - type "q" to skip...

root key device(/dev/sdd1) ::

>> Using key device /dev/sdd1.

>> Removable device /dev/sdd1 mounted.

>> LUKS-KEY.gpg on device /dev/sdd1 found

Maximum keyfile size exceeded
```

Curiously it only finds the key in the second run. But then the size is exceeded. I followed the sizing in the howto:

```
livecd ~ #export GPG_TTY=$(tty)

livecd ~ #dd if=/dev/urandom bs=8388607 count=1 | gpg --symmetric --cipher-algo AES256 --output /tmp/efiboot/luks-key.gpg
```

Does genkernel initramfs generally supports gpg encrypted key files? What is the size supported?

----------

## Massimo B.

frostschutz, in https://unix.stackexchange.com/questions/183666/booting-gentoo-on-lvm-inside-luks-with-gpg-encrypted-keyfile you already said that using gpg is not the preferred way. How do I use LUKS itself?

I though about moving the LUKS header to the SD Card.

What I like to achieve is the 2-way security, that will need the SD-Card and my passwort to open the LUKS at boot time.

----------

## frostschutz

 *Quote:*   

> dd if=/dev/urandom bs=8388607 count=1

 

8388607 bytes as a random key sounds a bit much. 8 bytes would be fine, 512 bytes would be plenty... I mean, LUKS does support upto 8MiB there but still... doesn't mean you have to use that much.  :Wink: 

(Keys that large will actually be reduced to their hash, so there is a 20-odd something byte key that will also work instead.)

The error message "Maximum keyfile size exceeded" could mean that either this is your problem, or the GPG encrypted variant is larger than 8MiB and the Initramfs is not trying to decrypt it.

I'm not familiar with genkernel so I can't help you there (according to the manpage there is a --gpg option)... I am using this method: https://wiki.gentoo.org/wiki/Custom_Initramfs#Encrypted_keyfile

----------

## Massimo B.

I tried again with bs=2048.

But the genkernel initrd does not try to decrypt it:

```
>> Removable device /dev/sdd1 mounted.

>> LUKS-KEY.gpg on device /dev/sdd1 found

No key available with this passphrase.
```

However I'm able to open it myself:

```
# mkfifo /tmp/gpgpipe

# gpg --decrypt /mnt/boot/LUKS-KEY.gpg | cat - >/tmp/gpgpipe

# cryptsetup --key-file=/tmp/gpgpipe luksOpen /dev/sdb _dev_sdb
```

Curiously eventhouth the man page of cryptsetup describes it like that, a pipe is always broken:

```

# gpg --decrypt /mnt/boot/LUKS-KEY.gpg | cryptsetup --key-file=- luksOpen /dev/sdb _dev_sdb
```

----------

## Massimo B.

I see, genkernel had no GPG support, trying to enable GPG support in genkernel fails at creating the initramfs:

```
[31;01m*[0mrijndael.c:2073:2: [1m[Kwarning[m[K: dereferencing type-punned pointer will break strict-aliasing rules [-Wstrict-aliasing]

[31;01m*[0m[36m[K--[m[K

[31;01m*[0mgcc  -Os -Wall -Wno-pointer-sign  -static -o mpicalc mpicalc.o ../cipher/libcipher.a ../mpi/libmpi.a ../util/libutil.a      

[31;01m*[0mgcc  -Os -Wall -Wno-pointer-sign  -static -o make-dns-cert make-dns-cert.o  

[31;01m*[0mmv -f .deps/shmtest.Tpo .deps/shmtest.Po

[31;01m*[0mgcc  -Os -Wall -Wno-pointer-sign  -static -o shmtest shmtest.o ../cipher/libcipher.a ../mpi/libmpi.a ../util/libutil.a      

[31;01m*[0m../cipher/libcipher.a(idea-stub.o): In function `idea_get_info':

[31;01m*[0midea-stub.c:(.text+0x86): [1m[Kwarning[m[K: Using 'dlopen' in statically linked applications requires at runtime the shared libraries from the glibc version used for linking

[31;01m*[0m../util/libutil.a(fileutil.o): In function `make_filename':

[31;01m*[0mfileutil.c:(.text+0x1aa): [1m[Kwarning[m[K: Using 'getpwnam' in statically linked applications requires at runtime the shared libraries from the glibc version used for linking

[31;01m*[0mfileutil.c:(.text+0x149): [1m[Kwarning[m[K: Using 'getpwuid' in statically linked applications requires at runtime the shared libraries from the glibc version used for linking

[31;01m*[0m[36m[K--[m[K

[31;01m*[0mmpi-cmp.c:(.text+0xb6): multiple definition of `mpihelp_sub_1'

[31;01m*[0m../mpi/libmpi.a(mpi-add.o):mpi-add.c:(.text+0xb6): first defined here

[31;01m*[0m../mpi/libmpi.a(mpi-cmp.o): In function `mpihelp_sub':

[31;01m*[0mmpi-cmp.c:(.text+0x113): multiple definition of `mpihelp_sub'

[31;01m*[0m../mpi/libmpi.a(mpi-add.o):mpi-add.c:(.text+0x113): first defined here

[31;01m*[0mcollect2: [1m[Kerror:[m[K ld returned 1 exit status

[31;01m*[0mmake[2]: *** [Makefile:411: mpicalc] [1m[KError [m[K1

[31;01m*[0mmake[2]: *** Waiting for unfinished jobs....

[31;01m*[0mmv -f .deps/gpgsplit.Tpo .deps/gpgsplit.Po

[31;01m*[0mmake[2]: Leaving directory '/var/tmp/genkernel/32047.12507.9387.19062/gnupg-1.4.11/tools'

[31;01m*[0mmake[1]: *** [Makefile:363: all-recursive] [1m[KError [m[K1

[31;01m*[0mmake[1]: Leaving directory '/var/tmp/genkernel/32047.12507.9387.19062/gnupg-1.4.11'

[31;01m*[0mmake: *** [Makefile:300: all] [1m[KError [m[K2

[31;01m*[0m[36m[K--[m[K

[31;01m*[0m Running with options: all

[31;01m*[0m Using genkernel.conf from /etc/genkernel.conf

[31;01m*[0m Sourcing arch-specific config.sh from /usr/share/genkernel/arch/x86_64/config.sh ..

[31;01m*[0m Sourcing arch-specific modules_load from /usr/share/genkernel/arch/x86_64/modules_load ..

[31;01m*[0m

[31;01m*[0m [1m[KERROR:[m[K [1m[KFailed[m[K to compile the "" target...
```

----------

## frostschutz

This is exactly why I use LUKS for encrypted keyfiles, no need to build yet another encryption tool or jump through any of these hoops, well, if genkernel supported it which it doesn't.

Seems it's trying to build gpg-1 statically (which is not possible, or at least a lot more complicated, for gpg-2) and fails doing so.

Maybe you're missing some static-libs?

```
USE="-* static readline bzip2 zlib" emerge -vp '<app-crypt/gnupg-2'
```

You could try and see if that complains about dependencies or builds at all. Not sure if genkernel would use it if it did, or still try to build its own binary. I just don't know genkernel well enough, sorry.

----------

## Massimo B.

If encrypting the keyfile with LUKS I would need an initramfs that supports LUKS encrypted key files. genkernel does not support that as far as I know.

----------

## Massimo B.

 *Massimo B. wrote:*   

> I see, genkernel had no GPG support, trying to enable GPG support in genkernel fails at creating the initramfs:...

 

Everything is fine so far, genkernel is btrfs ready and the btrfs-on-LUKS does ask for the password at the beginning...

But the next step for storing the GPG-encrypted key on the USB key does not work as I still can't get genkernel to create a initramfs with GPG support:

```
 gnupg: >> Configuring...

* gnupg: >> Compiling...

* ERROR: Failed to compile the "" target...

* 

* -- Grepping log... --

* 

*checking whether gcc and cc understand -c and -o together... yes

*checking how to run the C preprocessor... gcc -E

*checking for ranlib... ranlib

*checking for ar... ar

*checking for perl... /usr/bin/perl

*checking for strerror in -lcposix... no

*--

*make[2]: Entering directory '/var/tmp/genkernel/9801.4320.27932.6125/gnupg-1.4.11/util'

*gcc -DHAVE_CONFIG_H -I. -I.. -I.. -I../include -I../intl    -Os -Wall -Wno-pointer-sign -MT logger.o -MD -MP -MF .deps/logger.Tpo -c -o logger.o logger.c

*mv -f .deps/logger.Tpo .deps/logger.Po

*gcc -DHAVE_CONFIG_H -I. -I.. -I.. -I../include -I../intl    -Os -Wall -Wno-pointer-sign -MT strgutil.o -MD -MP -MF .deps/strgutil.Tpo -c -o strgutil.o strgutil.c

*strgutil.c: In function ‘set_native_charset’:

*strgutil.c:509:17: warning: variable ‘full_newset’ set but not used [-Wunused-but-set-variable]

*--

*mv -f .deps/mpi-inv.Tpo .deps/mpi-inv.Po

*gcc -DHAVE_CONFIG_H -I. -I.. -I.. -I../include    -Os -Wall -Wno-pointer-sign -MT mpi-mul.o -MD -MP -MF .deps/mpi-mul.Tpo -c -o mpi-mul.o mpi-mul.c

*mv -f .deps/mpi-mul.Tpo .deps/mpi-mul.Po

*gcc -DHAVE_CONFIG_H -I. -I.. -I.. -I../include    -Os -Wall -Wno-pointer-sign -MT mpi-pow.o -MD -MP -MF .deps/mpi-pow.Tpo -c -o mpi-pow.o mpi-pow.c

*mpi-pow.c: In function ‘mpi_powm’:

*mpi-pow.c:45:16: warning: variable ‘esign’ set but not used [-Wunused-but-set-variable]

*--

*mv -f .deps/mpi-mpow.Tpo .deps/mpi-mpow.Po

*gcc -DHAVE_CONFIG_H -I. -I.. -I.. -I../include    -Os -Wall -Wno-pointer-sign -MT mpi-scan.o -MD -MP -MF .deps/mpi-scan.Tpo -c -o mpi-scan.o mpi-scan.c

*mv -f .deps/mpi-scan.Tpo .deps/mpi-scan.Po

*gcc -DHAVE_CONFIG_H -I. -I.. -I.. -I../include    -Os -Wall -Wno-pointer-sign -MT mpicoder.o -MD -MP -MF .deps/mpicoder.Tpo -c -o mpicoder.o mpicoder.c

*mpicoder.c: In function ‘mpi_fromstr’:

*mpicoder.c:203:9: warning: variable ‘hexmode’ set but not used [-Wunused-but-set-variable]

*--

*gcc -DHAVE_CONFIG_H -I. -I.. -I.. -I../include    -Os -Wall -Wno-pointer-sign -MT mpih-cmp.o -MD -MP -MF .deps/mpih-cmp.Tpo -c -o mpih-cmp.o mpih-cmp.c

*mv -f .deps/mpih-cmp.Tpo .deps/mpih-cmp.Po

*gcc -DHAVE_CONFIG_H -I. -I.. -I.. -I../include    -Os -Wall -Wno-pointer-sign -MT mpih-div.o -MD -MP -MF .deps/mpih-div.Tpo -c -o mpih-div.o mpih-div.c

*In file included from mpih-div.c:32:0:

*mpih-div.c: In function ‘mpihelp_mod_1’:

*mpi-internal.h:160:17: warning: variable ‘_ql’ set but not used [-Wunused-but-set-variable]

*  mpi_limb_t _q, _ql, _r;         \

*                 ^

*mpih-div.c:99:3: note: in expansion of macro ‘UDIV_QRNND_PREINV’

*   UDIV_QRNND_PREINV(dummy, r, r,

*   ^

  ^

...

...

...

*rijndael.c:2053:2: warning: dereferencing type-punned pointer will break strict-aliasing rules [-Wstrict-aliasing]

*rijndael.c:2070:2: warning: dereferencing type-punned pointer will break strict-aliasing rules [-Wstrict-aliasing]

*  *((u32*)(b   )) ^= *((u32*)rk[0][0]);

*  ^

*rijndael.c:2071:2: warning: dereferencing type-punned pointer will break strict-aliasing rules [-Wstrict-aliasing]

*  *((u32*)(b+ 4)) ^= *((u32*)rk[0][1]);

*  ^

*rijndael.c:2072:2: warning: dereferencing type-punned pointer will break strict-aliasing rules [-Wstrict-aliasing]

*  *((u32*)(b+ 8)) ^= *((u32*)rk[0][2]);

*  ^

*rijndael.c:2073:2: warning: dereferencing type-punned pointer will break strict-aliasing rules [-Wstrict-aliasing]

*--

*mv -f .deps/sha512.Tpo .deps/sha512.Po

*gcc -DHAVE_CONFIG_H -I. -I..  -I.. -I../include -I../intl -DGNUPG_LIBDIR="\"//lib/gnupg\""   -Os -Wall -Wno-pointer-sign -MT idea-stub.o -MD -MP -MF .deps/idea-stub.Tpo -c -o idea-stub.o idea-stub.c

*mv -f .deps/idea-stub.Tpo .deps/idea-stub.Po

*gcc -DHAVE_CONFIG_H -I. -I..  -I.. -I../include -I../intl -DGNUPG_LIBDIR="\"//lib/gnupg\""   -Os -Wall -Wno-pointer-sign -MT md.o -MD -MP -MF .deps/md.Tpo -c -o md.o md.c

*md.c: In function ‘md_stop_debug’:

*md.c:524:21: warning: variable ‘c’ set but not used [-Wunused-but-set-variable]

*--

*mv -f .deps/bftest.Tpo .deps/bftest.Po

*gcc -DHAVE_CONFIG_H -I. -I..  -I../include -I../intl -DLOCALEDIR="\"//share/locale\""   -Os -Wall -Wno-pointer-sign -MT mpicalc.o -MD -MP -MF .deps/mpicalc.Tpo -c -o mpicalc.o mpicalc.c

*mv -f .deps/mpicalc.Tpo .deps/mpicalc.Po

*gcc  -Os -Wall -Wno-pointer-sign  -static -o bftest bftest.o ../cipher/libcipher.a ../mpi/libmpi.a ../util/libutil.a      -ldl    

*../cipher/libcipher.a(idea-stub.o): In function `idea_get_info':

*idea-stub.c:(.text+0x86): warning: Using 'dlopen' in statically linked applications requires at runtime the shared libraries from the glibc version used for linking

*../util/libutil.a(fileutil.o): In function `make_filename':

*fileutil.c:(.text+0x1aa): warning: Using 'getpwnam' in statically linked applications requires at runtime the shared libraries from the glibc version used for linking

*fileutil.c:(.text+0x149): warning: Using 'getpwuid' in statically linked applications requires at runtime the shared libraries from the glibc version used for linking

*--

*mpi-cmp.c:(.text+0xb6): multiple definition of `mpihelp_sub_1'

*../mpi/libmpi.a(mpi-add.o):mpi-add.c:(.text+0xb6): first defined here

*../mpi/libmpi.a(mpi-cmp.o): In function `mpihelp_sub':

*mpi-cmp.c:(.text+0x113): multiple definition of `mpihelp_sub'

*../mpi/libmpi.a(mpi-add.o):mpi-add.c:(.text+0x113): first defined here

*collect2: error: ld returned 1 exit status

*make[2]: *** [Makefile:411: mpicalc] Error 1

*make[2]: Leaving directory '/var/tmp/genkernel/9801.4320.27932.6125/gnupg-1.4.11/tools'

*make[1]: *** [Makefile:363: all-recursive] Error 1

*make[1]: Leaving directory '/var/tmp/genkernel/9801.4320.27932.6125/gnupg-1.4.11'

*make: *** [Makefile:300: all] Error 2

*--

* Running with options: all

* Using genkernel.conf from /etc/genkernel.conf

* Sourcing arch-specific config.sh from /usr/share/genkernel/arch/x86_64/config.sh ..

* Sourcing arch-specific modules_load from /usr/share/genkernel/arch/x86_64/modules_load ..

*

* ERROR: Failed to compile the "" target...

* 

* -- End log... --

* 

* Please consult /var/log/genkernel.log for more information and any

* errors that were reported above.

* 

* Report any genkernel bugs to bugs.gentoo.org and

* assign your bug to genkernel@gentoo.org. Please include

* as much information as you can in your bug report; attaching

* /var/log/genkernel.log so that your issue can be dealt with effectively.

* 

* Please do *not* report compilation failures as genkernel bugs!

```

I shortened the output and hope I have not dropped important things...

----------

## Massimo B.

-> bug 599704

----------

## Massimo B.

This issue breaks almost all wikis and howtos describing the 2-level security by having an GPG-ecnrypted LUKS key on a hardware key:

https://wiki.gentoo.org/wiki/Dm-crypt_full_disk_encryption#Generating_a_GnuPG_encrypted_keyfile

And the very sophisticated:

https://wiki.gentoo.org/wiki/Sakaki%27s_EFI_Install_Guide/Preparing_the_LUKS-LVM_Filesystem_and_Boot_USB_Key#Creating_a_Password-Protected_Keyfile_for_LUKS

An alternative approach of a 2-level security requiring a user-password + hardware key would be a Detached_LUKS_header. From usage point of view it would be the same, inserting the hardware-key, entering password. This would be even more secure regarding there is no information at all about the LUKS on the LUKS container anymore. But then I don't know if the initramfs of genkernel can be configured to open a detached header as described with cryptsetup in that wiki.

----------

## rwmailing

 *Massimo B. wrote:*   

> -> bug 599704

 

I just wanted to mention that I have the exact same issue with sys-kernel/genkernel-3.4.52.4

My last succesful compile with genkernel was on April 5th, since then I have been having this issue.

Not sure what developers would need in order to tackle this issue, but please let me know.

----------

## SATtva

The issue is caused by new GCC versions pickiness in regard to some legacy GnuPG code. You can work around the compilation error by updating genkernel to a recent GnuPG 1.4 branch version. It can be achieved this way:

```

# Fetch GnuPG 1.4.21 source code:

emerge -f1 =app-crypt/gnupg-1.4.21

# Move it into genkernel tools directory:

mv -v /usr/portage/distfiles/gnupg-1.4.21.tar.bz2 /usr/share/genkernel/distfiles

# Configure genkernel to use the new GnuPG source:

nano /usr/share/genkernel/defaults/software.sh

# Find GPG_VER var and update it to "${GPG_VER:-1.4.21}"

```

Then just run genkernel again, it should work as intended.

----------

## R0b0t1

I use the following, as I have yet to find out how to fix GnuPG in an initramfs environment:

```
./gpw | gpg --no-tty --passphrase-fd=0 -dq <keyfile>
```

```
gcc -std=gnu99 -Wall -pedantic -o gpw gpw.c
```

```
#include <stdlib.h>

#include <stdio.h>

#include <termios.h>

ssize_t

getpw(char **lineptr, size_t *n, FILE *stream)

{

   int nread;

   struct termios old, new;

   // Turn echo off, fail upon error.

   if(0 != tcgetattr(fileno(stream), &old))

   {

      fputs("Unable to get termios attributes.\n", stderr);

      return -1;

   }

   new = old;

   new.c_lflag &= ~ECHO;

   if(0 != tcsetattr(fileno(stream), TCSAFLUSH, &new))

   {

      fputs("Unable to set termios attributes.\n", stderr);

      return -1;

   }

   // Read.

   if(-1 == (nread = getline(lineptr, n, stream)))

   {

      fputs("getline() failed.\n", stderr);

      return -1;

   }

   // Reset.

   (void)tcsetattr(fileno(stream), TCSAFLUSH, &old);

   return nread;

}

int

main(int argc, char *argv[])

{

   char *line = NULL;

   size_t len = 0;

   ssize_t got = 0;

   got = getpw(&line, &len, stdin);

   line[got - 1] = 0;

   printf("%s", line);

   free(line);

   exit(EXIT_SUCCESS);

}
```

If you abort the password entry or if the program crashes you will need to type "reset" or exit and reenter the prompt for character echoing to be turned back on.

----------

