Browse Source

Merge upstream version 9

Christoph Biedl 6 years ago
parent
commit
34a6d6731d

+ 1 - 0
Makefile.am

@@ -8,6 +8,7 @@ EXTRA_DIST = COPYING
 dist_man1_MANS = \
     doc/clevis-encrypt-tang.1 \
     doc/clevis-encrypt-http.1 \
+    doc/clevis-encrypt-tpm2.1 \
     doc/clevis-encrypt-sss.1 \
     doc/clevis-luks-unlock.1 \
     doc/clevis-luks-bind.1 \

+ 1 - 0
Makefile.in

@@ -366,6 +366,7 @@ EXTRA_DIST = COPYING
 dist_man1_MANS = \
     doc/clevis-encrypt-tang.1 \
     doc/clevis-encrypt-http.1 \
+    doc/clevis-encrypt-tpm2.1 \
     doc/clevis-encrypt-sss.1 \
     doc/clevis-luks-unlock.1 \
     doc/clevis-luks-bind.1 \

+ 19 - 19
configure

@@ -1,6 +1,6 @@
 #! /bin/sh
 # Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.69 for clevis 8.
+# Generated by GNU Autoconf 2.69 for clevis 9.
 #
 #
 # Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc.
@@ -577,8 +577,8 @@ MAKEFLAGS=
 # Identity of this package.
 PACKAGE_NAME='clevis'
 PACKAGE_TARNAME='clevis'
-PACKAGE_VERSION='8'
-PACKAGE_STRING='clevis 8'
+PACKAGE_VERSION='9'
+PACKAGE_STRING='clevis 9'
 PACKAGE_BUGREPORT=''
 PACKAGE_URL=''
 
@@ -1285,7 +1285,7 @@ if test "$ac_init_help" = "long"; then
   # Omit some internal or obsolete options to make the list less imposing.
   # This message is too long to be a string in the A/UX 3.1 sh.
   cat <<_ACEOF
-\`configure' configures clevis 8 to adapt to many kinds of systems.
+\`configure' configures clevis 9 to adapt to many kinds of systems.
 
 Usage: $0 [OPTION]... [VAR=VALUE]...
 
@@ -1356,7 +1356,7 @@ fi
 
 if test -n "$ac_init_help"; then
   case $ac_init_help in
-     short | recursive ) echo "Configuration of clevis 8:";;
+     short | recursive ) echo "Configuration of clevis 9:";;
    esac
   cat <<\_ACEOF
 
@@ -1489,7 +1489,7 @@ fi
 test -n "$ac_init_help" && exit $ac_status
 if $ac_init_version; then
   cat <<\_ACEOF
-clevis configure 8
+clevis configure 9
 generated by GNU Autoconf 2.69
 
 Copyright (C) 2012 Free Software Foundation, Inc.
@@ -1544,7 +1544,7 @@ cat >config.log <<_ACEOF
 This file contains any messages produced by compilers while
 running configure, to aid debugging if configure makes a mistake.
 
-It was created by clevis $as_me 8, which was
+It was created by clevis $as_me 9, which was
 generated by GNU Autoconf 2.69.  Invocation command line was
 
   $ $0 $@
@@ -3768,7 +3768,7 @@ fi
 
 # Define the identity of the package.
  PACKAGE='clevis'
- VERSION='8'
+ VERSION='9'
 
 
 cat >>confdefs.h <<_ACEOF
@@ -4797,12 +4797,12 @@ if test -n "$audit_CFLAGS"; then
     pkg_cv_audit_CFLAGS="$audit_CFLAGS"
  elif test -n "$PKG_CONFIG"; then
     if test -n "$PKG_CONFIG" && \
-    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"audit >> 2.7.8\""; } >&5
-  ($PKG_CONFIG --exists --print-errors "audit >> 2.7.8") 2>&5
+    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"audit >= 2.7.8\""; } >&5
+  ($PKG_CONFIG --exists --print-errors "audit >= 2.7.8") 2>&5
   ac_status=$?
   $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
   test $ac_status = 0; }; then
-  pkg_cv_audit_CFLAGS=`$PKG_CONFIG --cflags "audit >> 2.7.8" 2>/dev/null`
+  pkg_cv_audit_CFLAGS=`$PKG_CONFIG --cflags "audit >= 2.7.8" 2>/dev/null`
 		      test "x$?" != "x0" && pkg_failed=yes
 else
   pkg_failed=yes
@@ -4814,12 +4814,12 @@ if test -n "$audit_LIBS"; then
     pkg_cv_audit_LIBS="$audit_LIBS"
  elif test -n "$PKG_CONFIG"; then
     if test -n "$PKG_CONFIG" && \
-    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"audit >> 2.7.8\""; } >&5
-  ($PKG_CONFIG --exists --print-errors "audit >> 2.7.8") 2>&5
+    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"audit >= 2.7.8\""; } >&5
+  ($PKG_CONFIG --exists --print-errors "audit >= 2.7.8") 2>&5
   ac_status=$?
   $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
   test $ac_status = 0; }; then
-  pkg_cv_audit_LIBS=`$PKG_CONFIG --libs "audit >> 2.7.8" 2>/dev/null`
+  pkg_cv_audit_LIBS=`$PKG_CONFIG --libs "audit >= 2.7.8" 2>/dev/null`
 		      test "x$?" != "x0" && pkg_failed=yes
 else
   pkg_failed=yes
@@ -4840,14 +4840,14 @@ else
         _pkg_short_errors_supported=no
 fi
         if test $_pkg_short_errors_supported = yes; then
-	        audit_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "audit >> 2.7.8" 2>&1`
+	        audit_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "audit >= 2.7.8" 2>&1`
         else
-	        audit_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "audit >> 2.7.8" 2>&1`
+	        audit_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "audit >= 2.7.8" 2>&1`
         fi
 	# Put the nasty error message in config.log where it belongs
 	echo "$audit_PKG_ERRORS" >&5
 
-	as_fn_error $? "Package requirements (audit >> 2.7.8) were not met:
+	as_fn_error $? "Package requirements (audit >= 2.7.8) were not met:
 
 $audit_PKG_ERRORS
 
@@ -5603,7 +5603,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
 # report actual input values of CONFIG_FILES etc. instead of their
 # values after options handling.
 ac_log="
-This file was extended by clevis $as_me 8, which was
+This file was extended by clevis $as_me 9, which was
 generated by GNU Autoconf 2.69.  Invocation command line was
 
   CONFIG_FILES    = $CONFIG_FILES
@@ -5660,7 +5660,7 @@ _ACEOF
 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
 ac_cs_version="\\
-clevis config.status 8
+clevis config.status 9
 configured by $0, generated by GNU Autoconf 2.69,
   with options \\"\$ac_cs_config\\"
 

+ 2 - 2
configure.ac

@@ -1,5 +1,5 @@
 AC_PREREQ(2.59)
-AC_INIT(clevis, 8)
+AC_INIT(clevis, 9)
 AC_CANONICAL_SYSTEM
 AC_PROG_CC_C99
 AC_PROG_RANLIB
@@ -18,7 +18,7 @@ PKG_CHECK_MODULES([udisks2], [udisks2])
 PKG_CHECK_MODULES([jose], [jose >= 8])
 PKG_CHECK_MODULES([systemd], [systemd])
 PKG_CHECK_MODULES([dracut], [dracut])
-PKG_CHECK_MODULES([audit], [audit >> 2.7.8])
+PKG_CHECK_MODULES([audit], [audit >= 2.7.8])
 
 AC_CHECK_PROG([PWMAKE], [pwmake], [yes])
 test -n "$PWMAKE" || AC_MSG_ERROR([pwmake required!])

+ 1 - 1
doc/clevis-decrypt.1

@@ -1,6 +1,6 @@
 .\" Automatically generated by Pandoc 1.19.1
 .\"
-.TH "CLEVIS\-DECRYPT" "1" "Sepember 2017" "" ""
+.TH "CLEVIS\-DECRYPT" "1" "September 2017" "" ""
 .hy
 .SH NAME
 .PP

+ 1 - 1
doc/clevis-encrypt-http.1

@@ -1,6 +1,6 @@
 .\" Automatically generated by Pandoc 1.19.1
 .\"
-.TH "CLEVIS\-ENCRYPT\-HTTP" "1" "Sepember 2017" "" ""
+.TH "CLEVIS\-ENCRYPT\-HTTP" "1" "September 2017" "" ""
 .hy
 .SH NAME
 .PP

+ 1 - 1
doc/clevis-encrypt-sss.1

@@ -1,6 +1,6 @@
 .\" Automatically generated by Pandoc 1.19.1
 .\"
-.TH "CLEVIS\-ENCRYPT\-SSS" "1" "Sepember 2017" "" ""
+.TH "CLEVIS\-ENCRYPT\-SSS" "1" "September 2017" "" ""
 .hy
 .SH NAME
 .PP

+ 1 - 1
doc/clevis-encrypt-tang.1

@@ -1,6 +1,6 @@
 .\" Automatically generated by Pandoc 1.19.1
 .\"
-.TH "CLEVIS\-ENCRYPT\-TANG" "1" "Sepember 2017" "" ""
+.TH "CLEVIS\-ENCRYPT\-TANG" "1" "September 2017" "" ""
 .hy
 .SH NAME
 .PP

+ 142 - 0
doc/clevis-encrypt-tpm2.1

@@ -0,0 +1,142 @@
+.\" Automatically generated by Pandoc 1.19.1
+.\"
+.TH "CLEVIS\-ENCRYPT\-TPM2" "1" "November 2017" "" ""
+.hy
+.SH NAME
+.PP
+clevis\-encrypt\-tpm2 \-\- Encrypts using a TPM2.0 chip binding policy
+.SH SYNOPSIS
+.PP
+\f[C]clevis\ encrypt\ tpm2\f[] CONFIG < PT > JWE
+.SH OVERVIEW
+.PP
+The \f[C]clevis\ encrypt\ tpm2\f[] command encrypts using a Trusted
+Platform Module 2.0 (TPM2) chip.
+Its only argument is the JSON configuration object.
+.PP
+When using the tpm2 pin, we create a new, cryptographically\-strong,
+random key.
+This key is encrypted using the TPM2 chip.
+Then at decryption time, the key is decrypted again using the TPM2 chip.
+.IP
+.nf
+\f[C]
+$\ clevis\ encrypt\ tpm2\ \[aq]{}\[aq]\ <\ PT\ >\ JWE
+\f[]
+.fi
+.PP
+The pin has reasonable defaults for its configuration, but a different
+hierarchy, hash, and key algorithms can be choosen if the defaults used
+are not suitable:
+.IP
+.nf
+\f[C]
+$\ clevis\ encrypt\ tpm2\ \[aq]{"hash":"sha1","key":"rsa"}\[aq]\ <\ PT\ >\ JWE
+\f[]
+.fi
+.PP
+To decrypt the data, simply provide the ciphertext (JWE):
+.IP
+.nf
+\f[C]
+$\ clevis\ decrypt\ <\ JWE\ >\ PT
+\f[]
+.fi
+.PP
+Note that like other pins no configuration is used for decryption, this
+is due clevis storing the public and private keys to unseal the TPM2
+encrypted object in the JWE so clevis can fetch that information from
+there.
+.PP
+The pin also supports sealing data to a Platform Configuration Registers
+(PCR) state.
+That way the data can only be unsealed if the PCRs hashes values match
+the policy used when sealing.
+.PP
+For example, to seal the data to the PCR with index 0 and 1 for the SHA1
+bank:
+.IP
+.nf
+\f[C]
+$\ clevis\ encrypt\ tpm2\ \[aq]{"pcr_bank":"sha1","pcr_ids":"0,1"}\[aq]\ <\ PT\ >\ JWE
+\f[]
+.fi
+.PP
+The PCR digest values are looked up from the current hash values for the
+PCRs, but a digest can also be provided if the data needs to be sealed
+with values different to the current ones, by passing the binary hash
+encoded in base64:
+.IP
+.nf
+\f[C]
+$\ clevis\ encrypt\ tpm2\ \[aq]{"pcr_ids":"0","pcr_digest":"xy7J5svCtqlfM03d1lE5gdoA8MI"}\[aq]\ <\ PT\ >\ JWE
+\f[]
+.fi
+.SH Threat model
+.PP
+The Clevis security model relies in the fact that an attacker will not
+be able to access both the encrypted data and the decryption key.
+.PP
+For most Clevis pins, the decryption key is not locally stored, so the
+decryption policy is only satisfied if the decryption key can be
+remotely accessed.
+It could for example be stored in a remote server or in a hardware
+authentication device that has to be plugged into the machine.
+.PP
+The tpm2 pin is different in this regard, since a key is wrapped by a
+TPM2 chip that is always present in the machine.
+This does not mean that there are not use cases for this pin, but it is
+important to understand the fact that an attacker that has access to
+both the encrypted data and the local TPM2 chip will be able to decrypt
+the data.
+.SH CONFIG
+.PP
+This command uses the following configuration properties:
+.IP \[bu] 2
+\f[C]hash\f[] (string) : Hash algorithm used in the computation of the
+object name (default: sha256)
+.PP
+It must be one of the following:
+.IP \[bu] 2
+\f[C]sha1\f[]
+.IP \[bu] 2
+\f[C]sha256\f[]
+.IP \[bu] 2
+\f[C]sha384\f[]
+.IP \[bu] 2
+\f[C]sha512\f[]
+.IP \[bu] 2
+\f[C]sm3_256\f[]
+.IP \[bu] 2
+\f[C]key\f[] (string) : Algorithm type for the generated key (default:
+ecc)
+.PP
+It must be one of the following:
+.IP \[bu] 2
+\f[C]rsa\f[]
+.IP \[bu] 2
+\f[C]keyedhash\f[]
+.IP \[bu] 2
+\f[C]ecc\f[]
+.IP \[bu] 2
+\f[C]symcipher\f[]
+.IP \[bu] 2
+\f[C]pcr_bank\f[] (string) : PCR algorithm bank to use for policy
+(default: sha1)
+.PP
+It must be one of the following:
+.IP \[bu] 2
+\f[C]sha1\f[]
+.IP \[bu] 2
+\f[C]sha256\f[]
+.IP \[bu] 2
+\f[C]pcr_ids\f[] (string) : Comma separated list of PCR used for policy.
+If not present, no policy is used
+.IP \[bu] 2
+\f[C]pcr_digest\f[] (string) : Binary PCR hashes encoded in base64.
+If not present, the hash values are looked up
+.SH SEE ALSO
+.PP
+\f[C]clevis\-decrypt\f[](1)
+.SH AUTHORS
+Javier Martinez Canillas <javierm@redhat.com>.

+ 1 - 1
doc/clevis-luks-bind.1

@@ -1,6 +1,6 @@
 .\" Automatically generated by Pandoc 1.19.1
 .\"
-.TH "CLEVIS\-LUKS\-BIND" "1" "Sepember 2017" "" ""
+.TH "CLEVIS\-LUKS\-BIND" "1" "September 2017" "" ""
 .hy
 .SH NAME
 .PP

+ 1 - 1
doc/clevis-luks-unlock.1

@@ -1,6 +1,6 @@
 .\" Automatically generated by Pandoc 1.19.1
 .\"
-.TH "CLEVIS\-LUKS\-UNLOCK" "1" "Sepember 2017" "" ""
+.TH "CLEVIS\-LUKS\-UNLOCK" "1" "September 2017" "" ""
 .hy
 .SH NAME
 .PP

+ 39 - 3
doc/clevis.1

@@ -1,6 +1,6 @@
 .\" Automatically generated by Pandoc 1.19.1
 .\"
-.TH "CLEVIS" "1" "Sepember 2017" "" ""
+.TH "CLEVIS" "1" "September 2017" "" ""
 .hy
 .SH NAME
 .PP
@@ -80,6 +80,42 @@ $\ clevis\ decrypt\ <\ JWE\ >\ PT
 .fi
 .PP
 For more information, see \f[C]clevis\-encrypt\-tang\f[](1).
+.SH TPM2 BINDING
+.PP
+Clevis provides support to encrypt a key in a Trusted Platform Module
+2.0 (TPM2) chip.
+The cryptographically\-strong, random key used for encryption is
+encrypted using the TPM2 chip, and then at decryption time is decrypted
+using the TPM2 to allow clevis to decrypt the secret stored in the JWE.
+.PP
+Encrypting data using the tpm2 pin works the same than the pins
+mentioned above:
+.IP
+.nf
+\f[C]
+$\ clevis\ encrypt\ tpm2\ \[aq]{}\[aq]\ <\ PT\ >\ JWE
+\f[]
+.fi
+.PP
+The pin has reasonable defaults for its configuration, but a different
+hierarchy, hash, and key algorithms can be choosen if the defaults used
+are not suitable.
+.PP
+Decryption also works similar to other pins, only the JWE needs to be
+provided:
+.IP
+.nf
+\f[C]
+$\ clevis\ decrypt\ <\ JWE\ >\ PT
+\f[]
+.fi
+.PP
+Note that like other pins no configuration is used for decryption, this
+is due clevis storing the public and private keys to unseal the TPM2
+encrypted object in the JWE so clevis can fetch that information from
+there.
+.PP
+For more information see \f[C]clevis\-encrypt\-tpm2\f[](1).
 .SH SHAMIR\[aq]S SECRET SHARING
 .PP
 Clevis provides a way to mix pins together to create sophisticated
@@ -151,7 +187,7 @@ For more information, see \f[C]clevis\-luks\-bind\f[](1).
 .SH SEE ALSO
 .PP
 \f[C]clevis\-encrypt\-http\f[](1), \f[C]clevis\-encrypt\-tang\f[](1),
-\f[C]clevis\-encrypt\-sss\f[](1), \f[C]clevis\-luks\-bind\f[](1),
-\f[C]clevis\-decrypt\f[](1)
+\f[C]clevis\-encrypt\-tpm2\f[](1), \f[C]clevis\-encrypt\-sss\f[](1),
+\f[C]clevis\-luks\-bind\f[](1), \f[C]clevis\-decrypt\f[](1)
 .SH AUTHORS
 Nathaniel McCallum <npmccallum@redhat.com>.

+ 2 - 0
src/Makefile.am

@@ -17,8 +17,10 @@ bin_PROGRAMS = \
 dist_bin_SCRIPTS = \
     clevis-encrypt-http \
     clevis-encrypt-tang \
+    clevis-encrypt-tpm2 \
     clevis-decrypt-http \
     clevis-decrypt-tang \
+    clevis-decrypt-tpm2 \
     clevis-bind-luks \
     clevis-luks-unlock \
     clevis-luks-bind \

+ 2 - 0
src/Makefile.in

@@ -376,8 +376,10 @@ dist_check_SCRIPTS = \
 dist_bin_SCRIPTS = \
     clevis-encrypt-http \
     clevis-encrypt-tang \
+    clevis-encrypt-tpm2 \
     clevis-decrypt-http \
     clevis-decrypt-tang \
+    clevis-decrypt-tpm2 \
     clevis-bind-luks \
     clevis-luks-unlock \
     clevis-luks-bind \

+ 126 - 0
src/clevis-decrypt-tpm2

@@ -0,0 +1,126 @@
+#!/bin/bash -e
+# vim: set tabstop=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80:
+#
+# Copyright (c) 2017 Red Hat, Inc.
+# Author: Javier Martinez Canillas <javierm@redhat.com>
+#
+# 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 owner hierarchy is the one that should be used by the Operating System.
+auth="o"
+
+function on_exit() {
+    if ! rm -r $TMP; then
+        echo "Delete temporary files failed!" >&2
+        exit 1
+    fi
+}
+
+[ $# -eq 1 -a "$1" == "--summary" ] && exit 1
+
+if [ -t 0 ]; then
+    echo >&2
+    echo "Usage: clevis decrypt tpm2 < JWE > PLAINTEXT" >&2
+    echo >&2
+    exit 1
+fi
+
+export TPM2TOOLS_TCTI_NAME=device
+export TPM2TOOLS_DEVICE_FILE=`ls /dev/tpmrm? 2>/dev/null`
+
+if [ -z "${TPM2TOOLS_DEVICE_FILE[0]}" ]; then
+    echo "A TPM2 device with the in-kernel resource manager is needed!" >&2
+    exit 1
+fi
+
+if ! [[ -r "${TPM2TOOLS_DEVICE_FILE[0]}" && -w "${TPM2TOOLS_DEVICE_FILE[0]}" ]]; then
+    echo "The ${TPM2TOOLS_DEVICE_FILE[0]} device must be readable and writable!" >&2
+    exit 1
+fi
+
+read -d . hdr
+
+if ! jhd=`jose b64 dec -i- <<< "$hdr"`; then
+    echo "Error decoding JWE protected header!" >&2
+    exit 1
+fi
+
+if [ `jose fmt -j- -Og clevis -g pin -u- <<< "$jhd"` != "tpm2" ]; then
+    echo "JWE pin mismatch!" >&2
+    exit 1
+fi
+
+if ! hash=`jose fmt -j- -Og clevis -g tpm2 -g hash -Su- <<< "$jhd"`; then
+    echo "JWE missing required 'hash' header parameter!" >&2
+    exit 1
+fi
+
+if ! key=`jose fmt -j- -Og clevis -g tpm2 -g key -Su- <<< "$jhd"`; then
+    echo "JWE missing required 'key' header parameter!" >&2
+    exit 1
+fi
+
+if ! jwk_pub=`jose fmt -j- -Og clevis -g tpm2 -g jwk_pub -Su- <<< "$jhd"`; then
+    echo "JWE missing required 'key' header parameter!" >&2
+    exit 1
+fi
+
+if ! jwk_priv=`jose fmt -j- -Og clevis -g tpm2 -g jwk_priv -Su- <<< "$jhd"`; then
+    echo "JWE missing required 'key' header parameter!" >&2
+    exit 1
+fi
+
+if ! TMP=`mktemp -d -p ~`; then
+    echo "Creating a temporary dir for TPM files failed!" >&2
+    exit 1
+fi
+
+trap 'on_exit' EXIT
+
+pcr_ids=`jose fmt -j- -Og clevis -g tpm2 -g pcr_ids -Su- <<< "$jhd"` || true
+
+if [ -n "$pcr_ids" ]; then
+    pcr_bank=`jose fmt -j- -Og clevis -g tpm2 -g pcr_bank -Su- <<< "$jhd"`
+    policy_options="-L $pcr_bank:$pcr_ids"
+fi
+
+if ! `jose b64 dec -i- -O $TMP/jwk.pub <<< "$jwk_pub"`; then
+    echo "Decoding jwk.pub from Base64 failed!" >&2
+    exit 1
+fi
+
+if ! `jose b64 dec -i- -O $TMP/jwk.priv <<< "$jwk_priv"`; then
+    echo "Decoding jwk.priv from Base64 failed!" >&2
+    exit 1
+fi
+
+if ! tpm2_createprimary -Q -H "$auth" -g "$hash" -G "$key" \
+     -C $TMP/primary.context 2>/dev/null; then
+    echo "Creating TPM2 primary key failed!" >&2
+    exit 1
+fi
+
+if ! tpm2_load -Q -c $TMP/primary.context -u $TMP/jwk.pub -r $TMP/jwk.priv \
+     -C $TMP/load.context 2>/dev/null; then
+    echo "Loading jwk to TPM2 failed!" >&2
+    exit 1
+fi
+
+if ! jwk=`tpm2_unseal -c $TMP/load.context $policy_options 2>/dev/null`; then
+    echo "Unsealing jwk from TPM failed!" >&2
+    exit 1
+fi
+
+jose jwe dec -k- -i- < <(echo -n "$jwk$hdr."; cat)

+ 156 - 0
src/clevis-encrypt-tpm2

@@ -0,0 +1,156 @@
+#!/bin/bash -e
+# vim: set tabstop=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80:
+#
+# Copyright (c) 2017 Red Hat, Inc.
+# Author: Javier Martinez Canillas <javierm@redhat.com>
+#
+# 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/>.
+#
+
+SUMMARY="Encrypts using a TPM2.0 chip binding policy"
+# The owner hierarchy is the one that should be used by the Operating System.
+auth="o"
+# Algorithm type must be keyedhash for object with user provided sensitive data.
+alg_create_key="keyedhash"
+# Attributes for the created TPM2 object with the JWK as sensitive data.
+obj_attr="fixedtpm|fixedparent|sensitivedataorigin|noda|adminwithpolicy"
+
+function on_exit() {
+    if ! rm -rf $TMP; then
+        echo "Delete temporary files failed!" >&2
+        exit 1
+    fi
+}
+
+if [ "$1" == "--summary" ]; then
+    echo "$SUMMARY"
+    exit 0
+fi
+
+if [ -t 0 ]; then
+    echo >&2
+    echo "Usage: clevis encrypt tpm2 CONFIG < PLAINTEXT > JWE" >&2
+    echo >&2
+    echo $SUMMARY >&2
+    echo >&2
+    echo "This command uses the following configuration properties:" >&2
+    echo >&2
+    echo "  hash: <string>  Hash algorithm used in the computation of the object name (default: sha256)" >&2
+    echo >&2
+    echo "  key: <string>   Algorithm type for the generated key (default: ecc)" >&2
+    echo >&2
+    echo "  pcr_bank: <string>   PCR algorithm bank to use for policy (default: sha1)" >&2
+    echo >&2
+    echo "  pcr_ids: <string>   PCR list used for policy. If not present, no policy is used" >&2
+    echo >&2
+    echo "  pcr_digest: <string>   Binary PCR hashes encoded in base64. If not present, the hash values are looked up" >&2
+    echo >&2
+    exit 1
+fi
+
+export TPM2TOOLS_TCTI_NAME=device
+export TPM2TOOLS_DEVICE_FILE=`ls /dev/tpmrm? 2>/dev/null`
+
+if [ -z "${TPM2TOOLS_DEVICE_FILE[0]}" ]; then
+    echo "A TPM2 device with the in-kernel resource manager is needed!" >&2
+    exit 1
+fi
+
+if ! [[ -r "${TPM2TOOLS_DEVICE_FILE[0]}" && -w "${TPM2TOOLS_DEVICE_FILE[0]}" ]]; then
+    echo "The ${TPM2TOOLS_DEVICE_FILE[0]} device must be readable and writable!" >&2
+    exit 1
+fi
+
+if ! cfg=`jose fmt -j "$1" -Oo- 2>/dev/null`; then
+    echo "Configuration is malformed!" >&2
+    exit 1
+fi
+
+hash=`jose fmt -j- -Og hash -u- <<< "$cfg"` || hash="sha256"
+
+key=`jose fmt -j- -Og key -u- <<< "$cfg"` || key="ecc"
+
+pcr_bank=`jose fmt -j- -Og pcr_hash -u- <<< "$cfg"` || pcr_bank="sha1"
+
+pcr_ids=`jose fmt -j- -Og pcr_ids -u- <<< "$cfg"` || true
+
+pcr_digest=`jose fmt -j- -Og pcr_digest -u- <<< "$cfg"` || true
+
+if ! jwk=`jose jwk gen -i '{"alg":"A256GCM"}'`; then
+    echo "Generating a jwk failed!" >&2
+    exit 1
+fi
+
+if ! TMP=`mktemp -d -p ~`; then
+    echo "Creating a temporary dir for TPM files failed!" >&2
+    exit 1
+fi
+
+trap 'on_exit' EXIT
+
+if ! tpm2_createprimary -Q -H "$auth" -g "$hash" -G "$key" -C $TMP/primary.context; then
+    echo "Creating TPM2 primary key failed!" >&2
+    exit 1
+fi
+
+if [ -n "$pcr_ids" ]; then
+    if [ -z "$pcr_digest" ]; then
+        if ! tpm2_pcrlist -Q -L "$pcr_bank":"$pcr_ids" -o $TMP/pcr.digest; then
+            echo "Creating PCR hashes file failed!" >&2
+            exit 1
+        fi
+    else
+        if ! jose b64 dec -i- -O "$TMP"/pcr.digest <<< "$pcr_digest"; then
+            echo "Error decoding PCR digest!" >&2
+            exit 1
+        fi
+    fi
+
+    if ! tpm2_createpolicy -Q -P -L "$pcr_bank":"$pcr_ids" -F $TMP/pcr.digest -f $TMP/pcr.policy; then
+        echo "create policy fail, please check the environment or parameters!"
+        exit 1
+    fi
+
+    policy_options="-L $TMP/pcr.policy"
+fi
+
+if ! tpm2_create -Q -g "$hash" -G "$alg_create_key" -c $TMP/primary.context -u $TMP/jwk.pub \
+     -r $TMP/jwk.priv -A "$obj_attr" $policy_options -I- <<< "$jwk"; then
+    echo "Creating TPM2 object for jwk failed!" >&2
+    exit 1
+fi
+
+if ! jwk_pub=`jose b64 enc -I $TMP/jwk.pub`; then
+    echo "Encoding jwk.pub in Base64 failed!" >&2
+    exit 1
+fi
+
+if ! jwk_priv=`jose b64 enc -I $TMP/jwk.priv`; then
+    echo "Encoding jwk.priv in Base64 failed!" >&2
+    exit 1
+fi
+
+jwe='{"protected":{"clevis":{"pin":"tpm2","tpm2":{}}}}'
+jwe=`jose fmt -j "$jwe" -g protected -g clevis -g tpm2 -q "$hash" -s hash -UUUUo-`
+jwe=`jose fmt -j "$jwe" -g protected -g clevis -g tpm2 -q "$key" -s key -UUUUo-`
+
+if [ -n "$pcr_ids" ]; then
+    jwe=`jose fmt -j "$jwe" -g protected -g clevis -g tpm2 -q "$pcr_bank" -s pcr_bank -UUUUo-`
+    jwe=`jose fmt -j "$jwe" -g protected -g clevis -g tpm2 -q "$pcr_ids" -s pcr_ids -UUUUo-`
+fi
+
+jwe=`jose fmt -j "$jwe" -g protected -g clevis -g tpm2 -q "$jwk_pub" -s jwk_pub -UUUUo-`
+jwe=`jose fmt -j "$jwe" -g protected -g clevis -g tpm2 -q "$jwk_priv" -s jwk_priv -UUUUo-`
+
+jose jwe enc -i- -k- -I- -c < <(echo -n "$jwe$jwk"; cat)

+ 9 - 0
src/dracut/module-setup.sh.in

@@ -36,11 +36,16 @@ install() {
     inst_multiple /etc/services \
         clevis-decrypt-http \
         clevis-decrypt-tang \
+        clevis-decrypt-tpm2 \
         clevis-decrypt-sss \
         @libexecdir@/clevis-luks-askpass \
         clevis-decrypt \
+        tpm2_createprimary \
+        tpm2_unseal \
+        tpm2_load \
         luksmeta \
         clevis \
+        mktemp \
         curl \
         jose \
         nc
@@ -48,3 +53,7 @@ install() {
     dracut_need_initqueue
 }
 
+installkernel() {
+    hostonly='' instmods =drivers/char/tpm
+}
+

+ 48 - 21
src/systemd/clevis-luks-askpass

@@ -23,26 +23,53 @@ UUID=cb6e8904-81ff-40da-a84a-07ab9ab5715e
 
 shopt -s nullglob
 
-for question in /run/systemd/ask-password/ask.*; do
-    d=
-    s=
-
-    while read line; do
-        case "$line" in
-            Id=cryptsetup:*) d="${line##Id=cryptsetup:}";;
-            Socket=*) s="${line##Socket=}";;
-        esac
-    done < "$question"
-
-    [ -z "$d" -o -z "$s" ] && continue
-
-    luksmeta show -d "$d" | while read -r slot state uuid; do
-        [ "$state" != "active" ] && continue
-        [ "$uuid" != "$UUID" ] && continue
-
-        if pt="`luksmeta load -d $d -s $slot -u $UUID | clevis decrypt`"; then
-            echo -n "+$pt" | nc -U -u --send-only "$s"
-            break
-        fi
+while getopts ":l" o; do
+    case "$o" in
+    l) loop=true;;
+    esac
+done
+
+while true; do
+    todo=0
+
+    for question in /run/systemd/ask-password/ask.*; do
+        metadata=false
+        unlocked=false
+        d=
+        s=
+
+        while read line; do
+            case "$line" in
+                Id=cryptsetup:*) d="${line##Id=cryptsetup:}";;
+                Socket=*) s="${line##Socket=}";;
+            esac
+        done < "$question"
+
+        [ -z "$d" -o -z "$s" ] && continue
+
+        # If the device is not initialized, sliently skip it.
+        luksmeta test -d "$d" || continue
+
+        while read -r slot state uuid; do
+            [ "$state" != "active" ] && continue
+            [ "$uuid" != "$UUID" ] && continue
+            metadata=true
+
+            if pt="`luksmeta load -d $d -s $slot -u $UUID | clevis decrypt`"; then
+                echo -n "+$pt" | nc -U -u --send-only "$s"
+                unlocked=true
+                break
+            fi
+        done < <(luksmeta show -d "$d")
+
+        [ $metadata == true ] || continue
+        [ $unlocked == true ] && continue
+        todo=$((todo + 1))
     done
+
+    if [ $todo -eq 0 ] || [ "$loop" != "true" ]; then
+        break;
+    fi
+
+    sleep 0.5
 done

+ 1 - 1
src/systemd/clevis-luks-askpass.service.in

@@ -5,4 +5,4 @@ After=network-online.target
 
 [Service]
 Type=oneshot
-ExecStart=@libexecdir@/clevis-luks-askpass
+ExecStart=@libexecdir@/clevis-luks-askpass -l