Skip to content

Commit

Permalink
Add PKCS#11 pin basic functionality
Browse files Browse the repository at this point in the history
Introducing new PKCS#11 pin that allows disk
unlocking with PKCS#11 devices. For extended
information on installation and configuration,
check README.md

Signed-off-by: Sergio Arroutbi <[email protected]>
Co-authored-by: Sergio Correia <[email protected]>
  • Loading branch information
sarroutbi and sergio-correia committed Aug 13, 2024
1 parent a4dd9dd commit ba8c6b4
Show file tree
Hide file tree
Showing 22 changed files with 1,780 additions and 11 deletions.
12 changes: 7 additions & 5 deletions .github/workflows/install-dependencies
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,7 @@ debian:*|ubuntu:*)
while ! apt-get -y install ${COMMON} \
build-essential pkg-config libssl-dev libjansson-dev libjose-dev \
luksmeta libluksmeta-dev libpwquality-tools libglib2.0-dev \
libudisks2-dev libaudit-dev systemd; do

libudisks2-dev libaudit-dev systemd opensc pcscd libsofthsm2-dev; do
sleep 5
done
;;
Expand All @@ -33,8 +32,10 @@ debian:*|ubuntu:*)
printf 'max_parallel_downloads=10\nfastestmirror=1\n' >> /etc/dnf/dnf.conf
dnf -y clean all
dnf -y --setopt=deltarpm=0 update
dnf -y install dnf-utils jq socat cryptsetup keyutils cracklib-dicts lsof
command -v dnf5 && dnf5 -y install dnf5-command\(builddep\) || dnf -y install dnf-command\(builddep\)
dnf -y install dnf-utils jq socat cryptsetup keyutils cracklib-dicts lsof \
opensc pcsc-lite softhsm
command -v dnf5 && dnf5 -y install dnf5-command\(builddep\) \
|| dnf -y install dnf-command\(builddep\)
dnf -y builddep clevis
;;

Expand All @@ -48,7 +49,8 @@ debian:*|ubuntu:*)
yum -y --allowerasing install ${COMMON}
yum -y install pkgconfig openssl-devel openssl zlib-devel \
jansson-devel findutils gcc libjose-devel luksmeta libluksmeta-devel \
audit-libs-devel tpm2-tools desktop-file-utils cracklib-dicts
audit-libs-devel tpm2-tools desktop-file-utils cracklib-dicts opensc \
pcsc-lite softhsm
sed -i 's|>=1\.0\.2|>=1\.0\.1|' meson.build
;;
esac
Expand Down
208 changes: 208 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,214 @@ $ echo hi | clevis encrypt tpm2 '{}' > hi.jwe
Clevis store the public and private keys of the encrypted key in the JWE object,
so those can be fetched on decryption to unseal the key encrypted using the TPM2.

#### PIN: PKCS#11

Clevis can perform the role of a PKCS#11 application, as described in the [RFC 7512: The PKCS#11 URI Scheme](https://www.rfc-editor.org/rfc/rfc7512.html).

PKCS#11 protocol determines that a PIN (Personal Identity Number) must be configured into the hardware device so that the unlocking process is successful. Clevis will allow users to unlock a particular encrypted disk, and will provide a way to get the PIN. There will be two possibilities:

1 - Provide the PIN at boot time: In this first case, Clevis will detect PKCS#11 device and will prompt for its PIN.
In case PIN is wrong, Clevis will prompt for the PIN again. It is the user's responsibility to be aware of the possible lock / brick of the device in case PIN is unknown.

2 - Provide the PIN at Clevis configuration time: In this second case, Clevis will be configured with the PIN value.

Initially, RFC7512 defines a mechanism to specify a special kind of URI (the `pkcs11` URI), that allows identifying both a device and also the information required for it to be unlocked. Special attention deserves the parameters `pin-value`, which allow specifying the value of the PIN or the location of the PIN respectively. Clevis will understand, initially, the 'pin-value' parameter. Below you can find and example of PKCS#11 URIs using previous parameter:

* PKCS#11 URI with `pin-value` defined:

```
pkcs11:token=Software%20PKCS%2311%20softtoken;manufacturer=Snake%20Oil,%20Inc.?pin-value=the-pin
```

In the next section, Clevis configuration examples are provided, so that it is clarified what are the different options for a PKCS#11 device to be bound to an encrypted disk.

##### Clevis configuration

Clevis will provide a mechanism for the user to bind a particular PKCS#11 device to an encrypted device. The name of the new pin for Clevis will be `pkcs11`, and the way to configure it will be the same that is currently used:

```
$ clevis luks bind -h
```

```
Usage: clevis luks bind [-y] [-f] [-s SLT] [-k KEY] [-t TOKEN_ID] [-e EXISTING_TOKEN_ID] -d DEV PIN CFG
```

##### Configuration to provide a PKCS#11 URI to Clevis
As first example, a user can provide the information of the device by specifying its URI to Clevis:

```
$ clevis luks bind -d /dev/sda1 pkcs11 '{"uri": "pkcs11:model=PKCS%2315%20emulated;manufacturer=piv_II;
serial=0a35ba26b062b9c5;token=clevis;id=%02;object=Encryption%20Key"}'
```

##### Configuration to bind Clevis to the first PKCS#11 device found
An additional option is to provide Clevis a configuration so that the first PKCS#11 device found by Clevis is bound. To do so, an empty URI can be provided as shown below:

```
$ clevis luks bind -d /dev/sda1 pkcs11 '{"uri":, "pkcs11:"}'
```

In this case, Clevis will be responsible for the detection of the device and, if no device is found, responsible for dumping the corresponding error.

It must be clarified that providing an empty URI will make Clevis to prompt also to select one of the available keys matched on the token to avoid accidentally encryption with unwanted keys.

##### Configuration to provide a module path to Clevis PKCS#11 pin:
A module path can be provided to Clevis, so that it uses that module to access a device. This is only required in case the card is not supported by underlying Clevis software (OpenSC). For this reason, the module path field is completely optional. To provide the module location the user can provide the "module-path" to the "uri" Clevis configuration:

```
$ clevis-luks-bind -d /dev/sda1 pkcs11 '{"uri": "pkcs11:model=PKCS%2315%20emulated;manufacturer=piv_II;
serial=0a35ba26b062b9c5;token=clevis;id=%02;object=Encryption%20Key?
module-path=/usr/local/lib64/libmypkcs11.so"}'
```

As it happens with the rest of devices, encrypted disks that have been bound to a PKCS#11 device can be checked with `clevis luks list` command:

```
$ clevis luks list -d /dev/sda1
```

```
1: pkcs11 '{"uri": "pkcs11:model=PKCS%2315%20emulated;manufacturer=piv_II;
serial=0a35ba26b062b9c5;token=clevis;id=%02;object=Encryption%20Key?
module-path=/usr/local/lib64/libmypkcs11.so"}'
```

##### Configuration to provide PKCS#11 tool a different mechanism

In the first phase of development, Clevis will be used in top of OpenSC to provide PKCS#11 functionality.
OpenSC, and, in particular, `pkcs11-tool`, provides an option to indicate the mechanism to use for decryption.
For testing purposes, some libraries, such as [SoftHSM](https://www.opendnssec.org/softhsm)), don't work with default `pkcs11-tool` mechanism,
so it is required to provide a particular mechanism to use. For this reason, Clevis can be provided with
the mechanism to use, in case the default one, `RSA-PKCS-OAEP`, is not valid:

```
$ clevis luks bind -d /dev/sda1 pkcs11 '{"uri": "pkcs11:", "mechanism":"RSA-PKCS"}'
```

It must be highlighted that the RSA-PKCS mechanism (PKCS#1.5 padding for encryption) is [considered to be not secure](https://people.redhat.com/~hkario/marvin/) and it is mostly provided for compatibility, but it is not recommended using it in production.

##### Multi-device configuration
Clevis will allow specifying the slot where a PKCS#11 device is located through the parameters provided to the URI:

```
$ clevis luks bind -d /dev/sda1 pkcs11 '{"uri": "pkcs11:slot-id=0"}'
```

It must be clarified that providing just the slot information will make Clevis to guess one of the available keys matched on the token in the selected slot, which could cause accidentally encryption with unwanted keys. **It is not recommended to use slot as device selector, as slot id is a number that is not guaranteed to be stable across PKCS#11 module initializations**. However, there are certain libraries and modules that provide stable slot identifiers, so it can be used for these particular cases.

There are two better options to distinguish between different PKCS#11 devices:

1 - Multi-device configuration with public key object (**recommended**):

With recent versions of `OpenSC`, `pkcs11-tool`, which is used by Clevis to handle most of the PKCS#11 commands, the PKCS#11 URI is dumped for both tokens and objects of a particular token:

```
$ pkcs11-tool -L | grep uri

Check warning on line 183 in README.md

View workflow job for this annotation

GitHub Actions / Language tool & Misspell check

[LanguageTool] reported by reviewdog 🐶 Possible typo: you repeated a word (ENGLISH_WORD_REPEAT_RULE) Suggestions: `uri` Rule: https://community.languagetool.org/rule/show/ENGLISH_WORD_REPEAT_RULE?lang=en-US Category: MISC Raw Output: README.md:183:24: Possible typo: you repeated a word (ENGLISH_WORD_REPEAT_RULE) Suggestions: `uri` Rule: https://community.languagetool.org/rule/show/ENGLISH_WORD_REPEAT_RULE?lang=en-US Category: MISC
uri : pkcs11:model=PKCS%2315%20emulated;manufacturer=piv_II;serial=42facd1f749ece7f;token=clevis
uri : pkcs11:model=PKCS%2315%20emulated;manufacturer=OpenPGP%20project;serial=000f06080f4f;token=OpenPGP%20card%20%28User%20PIN%29
$ pkcs11-tool -O --slot-index 1 --type pubkey | grep uri
ising slot 0 with a present token (0x0)
uri: pkcs11:model=PKCS%2315%20emulated;manufacturer=OpenPGP%20project;serial=000f06080f4f;token=OpenPGP%20card%20%28User%20PIN%29;id=%03;object=Authentication%20key;type=public
```

In this particular cases, when multiple PKCS#11 devices exist, select the public key of the particular device and bind it to Clevis:

```
$ clevis luks bind -d /dev/sda pkcs11 '{"uri":"pkcs11:model=PKCS%2315%20emulated;manufacturer=OpenPGP%20project;serial=000f06080f4f;token=OpenPGP%20card%20%28User%20PIN%29;id=%03;object=Authentication%20key;type=public"}'
```
**In case you are using module-path, you will have to use the one returned when providing --module option:**

```
$ pkcs11-tool --module /usr/lib64/libykcs11.so -O --type pubkey | grep uri
/usr/local/bin/pkcs11-tool.manual --module /usr/lib64/libykcs11.so -O --type pubkey | grep uri
Using slot 0 with a present token (0x0)
uri: pkcs11:model=YubiKey%20YK5;manufacturer=Yubico%20%28www.yubico.com%29;serial=28083311;token=YubiKey%20PIV%20%2328083311;id=%03;object=Public%20key%20for%20Key%20Management;type=public
uri: pkcs11:model=YubiKey%20YK5;manufacturer=Yubico%20%28www.yubico.com%29;serial=28083311;token=YubiKey%20PIV%20%2328083311;id=%19;object=Public%20key%20for%20PIV%20Attestation;type=public
$ clevis luks bind -d /dev/sda pkcs11 '{"uri":"pkcs11:model=YubiKey%20YK5;manufacturer=Yubico%20%28www.yubico.com%29;serial=28083311;token=YubiKey%20PIV%20%2328083311;id=%03;object=Public%20key%20for%20Key%20Management;type=public;module-path=/usr/lib64/libykcs11.so"}'
```

2 - Multi-device configuration with serial + token specification:

**For versions where `pkcs11-tool` does not dump the URI for the tokens/objects**, specific identification will be "tried" by Clevis by using the device `serial` + `token` pair.
In this type of scenarios, identification can be performed with these two parameters, although `model` should be provided also to ease Clevis informing about the device when asking for the PIN:

```
$ pkcs11-tool -L | grep uri

Check warning on line 213 in README.md

View workflow job for this annotation

GitHub Actions / Language tool & Misspell check

[LanguageTool] reported by reviewdog 🐶 Possible typo: you repeated a word (ENGLISH_WORD_REPEAT_RULE) Suggestions: `uri` Rule: https://community.languagetool.org/rule/show/ENGLISH_WORD_REPEAT_RULE?lang=en-US Category: MISC Raw Output: README.md:213:24: Possible typo: you repeated a word (ENGLISH_WORD_REPEAT_RULE) Suggestions: `uri` Rule: https://community.languagetool.org/rule/show/ENGLISH_WORD_REPEAT_RULE?lang=en-US Category: MISC
uri : pkcs11:model=PKCS%2315%20emulated;manufacturer=piv_II;serial=42facd1f749ece7f;token=clevis
uri : pkcs11:model=PKCS%2315%20emulated;manufacturer=OpenPGP%20project;serial=000f06080f4f;token=OpenPGP%20card%20%28User%20PIN%29
$ clevis luks bind -d /dev/sda pkcs11 '{"uri":"pkcs11:model=PKCS%2315%20emulated;serial=000f06080f4f;token=OpenPGP%20card%20%28User%20PIN%29"}'
```

##### Clevis PKCS#11 installation and configuration

For installation and configuration of the clevis PKCS#11 feature, next steps must be followed:

1 - Install Clevis required dependencies, including PKCS#11 dependencies:

```
$ sudo dnf install -y opensc pcsc-lite openssl
```

2 - The PKCS11 device must be accessible by “pkcs11-tool”:

```
$ pkcs11-tool -L
pkcs11-tool -L
Available slots:
Slot 0 (0x0): Yubico YubiKey OTP+CCID 00 00
token label : clevis

Check warning on line 236 in README.md

View workflow job for this annotation

GitHub Actions / Language tool & Misspell check

[LanguageTool] reported by reviewdog 🐶 Possible agreement error. The noun ‘label’ seems to be countable. (CD_NN[1]) Suggestions: `labels` Rule: https://community.languagetool.org/rule/show/CD_NN?lang=en-US&subId=1 Category: GRAMMAR Raw Output: README.md:236:4: Possible agreement error. The noun ‘label’ seems to be countable. (CD_NN[1]) Suggestions: `labels` Rule: https://community.languagetool.org/rule/show/CD_NN?lang=en-US&subId=1 Category: GRAMMAR
...
uri : pkcs11:model=PKCS%2315%20emulated;manufacturer=piv_II;serial=42facd1f749ece7f;token=clevis
```

3 - Configure device to bind with clevis:

```
$ sudo clevis luks bind -d /dev/sda5 pkcs11 '{"uri":"pkcs11:"}'
```

In case it is required to provide the module to use, it can be done through `module-path` URI parameter:

```
$ sudo clevis luks bind -d /dev/sda5 pkcs11 '{"uri":"pkcs11:module-path=/usr/lib64/libykcs11.so.2"}'
```

4 - Enable clevis-luks-pkcs11-askpass.socket unit:

```
$ sudo systemctl enable --now clevis-luks-pkcs11-askpass.socket
```

5 - /etc/crypttab configuration:

As described in [Integration with systemd](#integration-with-systemd) section, `crypttab` must be configured so that systemd uses an AF\_UNIX socket to wait for the keyphrase that will unlock the disk and not to prompt it through the console.

Clevis PKCS#11 unit file will configure a socket in path `/run/systemd/clevis-pkcs11.sock` to send and receive information about disk unlocking. For disks that will be unlocked through PKCS#11 Clevis pin, that socket file must be configured as key file. So, next change must be introduced in `/etc/crypttab` for unlocking to take place:

```
$ sudo diff -Nuar /etc/crypttab.ori /etc/crypttab
--- /etc/crypttab.ori 2024-07-04 10:46:16.295073739 +0200
+++ /etc/crypttab 2024-07-03 17:14:27.764743860 +0200
@@ -1 +1,2 @@
-luks-6e38d5e1-7f83-43cc-819a-7416bcbf9f84 UUID=6e38d5e1-7f83-43cc-819a-7416bcbf9f84 - -
+luks-6e38d5e1-7f83-43cc-819a-7416bcbf9f84 UUID=6e38d5e1-7f83-43cc-819a-7416bcbf9f84 /run/systemd/clevis-pkcs11.sock keyfile-timeout=30s
```

It is highly recommended setting a `keyfile-timeout` option to configure a fall-through mechanism in case some unlocking error occurs and passphrase is required to be entered manually through console.

6 - Reboot and test:

System should boot and ask for the PKCS#11 device PIN, and decrypt the corresponding configured encrypted disk only in case PIN is correct.

7 - In case no boot process needs to be tested, encrypt and decrypt with next command (note it is necessary to provide the PIN value for it to work appropriately) and check encryption/decryption of a string can be performed with this one-liner, and no error takes place:

```
$ echo "top secret" | clevis encrypt pkcs11 '{"uri":"pkcs11:module-path=/usr/lib64/libykcs11.so.2?pin-value=123456"}' | clevis decrypt
```

The `top secret` string should be returned

#### PIN: Shamir Secret Sharing

Clevis provides a way to mix pins together to provide sophisticated unlocking
Expand Down
29 changes: 24 additions & 5 deletions src/luks/clevis-luks-common-functions.in
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,22 @@ clevis_luks_print_pin_config() {

local pin=
case "${P}" in
pkcs11)
local uri
uri="$(jose fmt -j- -g uri -u- <<< "${content}")"
mechanism="$(jose fmt -j- -g mechanism -u- <<< "${content}")"
if [ -z "${mechanism}" ]; then
pin=$(printf '{"uri":"%s"}' "${uri}")
else
pin=$(printf '{"uri":"%s", "mechanism":"%s"}' "${uri}" "${mechanism}")
fi
printf "pkcs11 '%s'" "${pin}"
;;
sss)
local threshold
threshold=$(jose fmt -j- -Og t -o- <<< "${content}")
clevis_luks_process_sss_pin "${content}" "${threshold}"
;;
tang)
local url
url="$(jose fmt -j- -g url -u- <<< "${content}")"
Expand All @@ -197,11 +213,6 @@ clevis_luks_print_pin_config() {
pin=${pin/#,/}
printf "tpm2 '{%s}'" "${pin}"
;;
sss)
local threshold
threshold=$(jose fmt -j- -Og t -o- <<< "${content}")
clevis_luks_process_sss_pin "${content}" "${threshold}"
;;
*)
printf "unknown pin '%s'" "${P}"
;;
Expand Down Expand Up @@ -241,6 +252,7 @@ clevis_luks_process_sss_pin() {
local jwe="${1}"
local threshold="${2}"

local sss_pkcs11
local sss_tang
local sss_tpm2
local sss
Expand All @@ -255,6 +267,9 @@ clevis_luks_process_sss_pin() {
fi
read -r pin cfg <<< "${pin_cfg}"
case "${pin}" in
pkcs11)
sss_pkcs11="${sss_pkcs11},${cfg}"
;;
tang)
sss_tang="${sss_tang},${cfg}"
;;
Expand All @@ -276,6 +291,10 @@ clevis_luks_process_sss_pin() {
cfg="${cfg},"$(clevis_luks_join_sss_cfg "tpm2" "${sss_tpm2}")
fi

if [ -n "${sss_pkcs11}" ]; then
cfg="${cfg},"$(clevis_luks_join_sss_cfg "pkcs11" "${sss_pkcs11}")
fi

if [ -n "${sss}" ]; then
cfg=$(printf '%s,"sss":%s' "${cfg}" "${sss}")
fi
Expand Down
21 changes: 21 additions & 0 deletions src/luks/dracut/clevis-pin-pkcs11/clevis-pkcs11-hook.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#!/bin/sh
#
# Copyright (c) 2024 Red Hat, Inc.
# Author: Sergio Arroutbi <[email protected]>
#
# 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/>.
#
clevis-pkcs11-afunix-socket-unlock -f /run/systemd/clevis-pkcs11.sock -s 120 &
pcscd --disable-polkit
/usr/libexec/clevis-luks-pkcs11-askpin -d
18 changes: 18 additions & 0 deletions src/luks/dracut/clevis-pin-pkcs11/meson.build
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
dracut = dependency('dracut', required: false)

if dracut.found()
dracutdir = dracut.get_pkgconfig_variable('dracutmodulesdir') + '/60' + meson.project_name() + '-pin-pkcs11'

configure_file(
input: 'module-setup.sh.in',
output: 'module-setup.sh',
install_dir: dracutdir,
configuration: data,
)

# TODO: install hook for pcscd start
install_data('clevis-pkcs11-hook.sh', install_dir: dracutdir)

else
warning('Will not install dracut module clevis-pin-pkcs11 due to missing dependencies!')
endif
Loading

0 comments on commit ba8c6b4

Please sign in to comment.