From 6f1f13892628e41b39ceb59257d160b3db8711c8 Mon Sep 17 00:00:00 2001 From: Christoph Biedl Date: Sat, 23 May 2020 13:41:52 +0200 Subject: [PATCH 1/2] Provide a template for new pins This should ease writing new pins a lot, for newcomers and for experienced authors as well. --- src/pins/template/README.md | 86 +++++++++++ src/pins/template/clevis-decrypt-@pin@ | 73 +++++++++ src/pins/template/clevis-encrypt-@pin@ | 140 ++++++++++++++++++ src/pins/template/clevis-encrypt-@pin@.1.adoc | 51 +++++++ src/pins/template/dracut.module-setup.sh.in | 27 ++++ src/pins/template/initramfs.in | 47 ++++++ src/pins/template/meson.build | 66 +++++++++ src/pins/template/pin-@pin@ | 40 +++++ 8 files changed, 530 insertions(+) create mode 100644 src/pins/template/README.md create mode 100755 src/pins/template/clevis-decrypt-@pin@ create mode 100755 src/pins/template/clevis-encrypt-@pin@ create mode 100644 src/pins/template/clevis-encrypt-@pin@.1.adoc create mode 100755 src/pins/template/dracut.module-setup.sh.in create mode 100755 src/pins/template/initramfs.in create mode 100644 src/pins/template/meson.build create mode 100755 src/pins/template/pin-@pin@ diff --git a/src/pins/template/README.md b/src/pins/template/README.md new file mode 100644 index 00000000..a895725b --- /dev/null +++ b/src/pins/template/README.md @@ -0,0 +1,86 @@ + +# Writing your own pin + +The files here should give you an introduction into writing your own pin. + +## Overall workflow + +The `encrypt` script reads a plain text from stdin, encrypts it using +`jose jwe enc` which writes the result to stdout, together with some +information how to re-create the plain text later. The encryption key +itself *must* *not* be included here. + +The encryption key is provided or created by the pin and stashed away +in some way. That is the core logic of a pin. + +A configuration in the JSON format is provided as the first parameter, +it controls the pin's operation. + +The `decrypt` script reads the encrypted information from stdin, +decrypts it using `ose jwe dec` which again writes the result to +stdout. The information provided by `encrypt` above is available, this +must be sufficient to restore the encryption key. + +## How to use this template + +Copy all the files here (except for this one) into a new subdirectory +of `src/pins/`, named as your pin. + +Replace @pin@ with the name of your pin everywhere, including file names. + +The `clevis-{en,de}crypt-@pin@` scripts require the most attention. + +Have a man page in `clevis-encrypt-@pin@.1.adoc`. + +Adjust `meson.build`. + +Provide a test in `pin-@pin@`. + +Adjust dracut configuration in `dracut.module-setup.sh.in`. + +Adjust initramfs configuration in `initramfs.in`. + +Optionally add something to `clevis-luks-list`. + +Finally, add your pin in `../meson.build`. + +## Comments + +An extra form of comments is used to explain concepts. They all should +be removed before sending out patches/merge requests. + + #%# some generic information + #!# things worth to know, gotchas + #?# some bits that require more understanding + +## Nameing your pin and configuration variables + +The pin name should be short and reflect the purpose. To avoid trouble +or extra work, the name should start with a letter, followed by letters, +digits, or underscore. + +Parameter names for the pin configuration should follow the same +syntax. These templates assume they can be used as a shell variable. + +## Templates variables + +The templates use `@...@` to mark places that can semi-automatically +be adjusted to your needs. Variables are + +* `@pin@`: The name of this pin, see above +* `@PIN@`: The name of this pin, uppercase +* `@year@`: Current year +* `@name@`: Your name +* `@email@`: Your e-mail address +* `@mand1@`: The name of a mandatory parameter +* `@mand2@`: The name of another mandatory parameter +* `@opt1@`: The name of an optional parameter +* `@param1@`: The name of a parameter needed for decryption +* `@param2@`: Another name + +If you have more parameters, extend accordingly + +Any `@@` requires attention in wording. + +Make sure you've replaced *all* occurances of template variables. +Else the build will probably fail. diff --git a/src/pins/template/clevis-decrypt-@pin@ b/src/pins/template/clevis-decrypt-@pin@ new file mode 100755 index 00000000..621de719 --- /dev/null +++ b/src/pins/template/clevis-decrypt-@pin@ @@ -0,0 +1,73 @@ +#%# Creating an decrypting pin +#%# +#%# Read README.md and clevis-encrypt-@pin@ first, this file aims to +#%# to avoid information duplication. +#%# Unfortunately, this one uses a bashism (read -d) that is not at +#%# all easy to eliminate. +#!/bin/bash + +set -eu + +# Copyright (c) @year@ @name@ +# Author: @name@ <@email@> +# +# 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 . +# + +#%# This program takes no options - everything needed to know will be +#%# read from stdin. +[ $# -eq 1 ] && [ "${1:-}" = "--summary" ] && exit 2 + +if [ -t 0 ] ; then + echo >&2 + echo 'Usage: clevis decrypt @pin@ < JWE > PLAINTEXT' >&2 + echo >&2 + exit 1 +fi + +#%# The input is concatenated using the dot. Read the first element +#%# but leave everything else in the buffer. Only read -d can to that. +read -d . hdr64 +#%# The header is base64-encoded. Decode now and also verify this is valid JSON +if ! hdr="$(jose fmt --quote="$hdr64" --string --b64load --object --output=-)" ; then + echo 'JWE header corrupt' >&2 + exit 1 +fi + +#%# Input validation: The pin must exist by name. +if [ "$(jose fmt --json="$hdr" --get clevis --get pin --unquote=-)" != '@pin@' ] ; then + echo 'JWE pin mismatch!' >&2 + exit 1 +fi + +#%# Load the parameters into shell variables. +if ! @param1@="$(jose fmt --json="$hdr" --get clevis --get @pin@ --get @param1@ --unquote=-)" ; then + echo 'JWE missing 'clevis.@pin@.@param1@' header parameter!' >&2 + exit 1 +fi +if ! @param2@="$(jose fmt --json="$hdr" --get clevis --get @pin@ --get @param2@ --unquote=-)" ; then + echo 'JWE missing 'clevis.@pin@.@param2@' header parameter!' >&2 + exit 1 +fi + +#%# Possibly some pre-checks on your parameters are needed. + +#%# Now everything is set up for your pin's business logic +#%# +#%# Your job: Somehow bring the key into `jwk`. +jwk="$(load_jwk)" + +#%# Finally, forward everything to `jose jwe dec` which does the +#%# decryption job. +( printf '%s' "$jwk$hdr64." ; cat ) | exec jose jwe dec --key=- --input=- diff --git a/src/pins/template/clevis-encrypt-@pin@ b/src/pins/template/clevis-encrypt-@pin@ new file mode 100755 index 00000000..ee01834e --- /dev/null +++ b/src/pins/template/clevis-encrypt-@pin@ @@ -0,0 +1,140 @@ +#%# Creating an encrypting pin +#%# +#%# Read README.md first. +#%# +#%# The shell interpreter. For portability, it should have as little +#%# requirements as possibly. Especially the decrypt should be executable +#%# in busybox' ash as well +#!/bin/sh + +#%# Some hardening. Safeguard against coding errors. +set -eu + +#%# Legal stuff. Put your name etc. here. +#%# Of course you're not bound to GPL-3+ but it will certainly ease +#%# inclusion in upstream clevis if you use that. +# Copyright (c) @year@ @name@ +# Author: @name@ <@email@> +# +# 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 . +# + +#%# A one-line summary. Will be used in the help messages below. +SUMMARY='Encrypts using a @pin@ @@ policy' + +#%# Some option parsing, very simple. +#%# Don't touch, it's hardcoded in the `clevis` program. +if [ "${1:-}" = '--summary' ] ; then + echo "$SUMMARY" + exit 0 +fi + +#%# Regular operation assumes output goes to a file. If not, print +#%# some usage information and bail out. +if [ -t 0 ] ; then +#!# Since this script runs in a pipe, *all* operational messages +#!# must go to stderr. + exec >&2 + echo +#%# Don't be confused: This script is called from `clevis`, so +#%# the usage text has spaces, not dashes. +#%# Also, the configuration is in $1, see below. + echo 'Usage: clevis encrypt @pin@ CONFIG < PLAINTEXT > JWE' + echo + echo "$SUMMARY" + echo + echo 'This command uses the following configuration properties:' + echo +#%# For the sake of users: Give a good explanation of your pin's +#%# parameters. +#%# Mandatory parameters should contain the string "REQUIRED" + echo ' @mand1@: One parameter @@ (REQUIRED)' + echo + echo ' @mand2@: Another parameter @@ (REQUIRED)' + echo +#%# Optional parameters should mention the default value. + echo ' @opt1@: An optional parameter @@ (default: @@)' + echo +#%# Pure visual: Make sure the short descriptions are aligned to the +#%# same column. + exit 2 +fi + +#%# The CONFIG parameter in $1 has to be valid JSON +if ! cfg="$(jose fmt --json="${1:-}" --object --output=- 2>/dev/null)" ; then + echo 'Configuration is malformed!' >&2 + exit 1 +fi + +#%# Load the values from the configuration into shell variables. +#%# Re-using the name is certainly a good idea +#%# +#%# For mandatory parameters it's like that: +if ! @mand1@="$(jose fmt --json="$cfg" --object --get @mand1@ --unquote=-)" ; then + echo 'Missing the required @mand1@ property!' >&2 + exit 1 +fi +if ! @mand2@="$(jose fmt --json="$cfg" --object --get @mand2@ --unquote=-)" ; then + echo 'Missing the required @mand2@ property!' >&2 + exit 1 +fi + +#%# For optional parameters, use: +@opt1@="$(jose fmt --json="$cfg" --object --get @opt1@ --unquote=-)" || @opt1@='@@' + +#%# Possibly validate parameters. If a check can be done at *en*crypt +#%# time, it should be done now. + +#%# Now everything is set up for your pin's business logic. +#%# +#%# Your jobs, in no particular order: +#%# +#%# 1. Have the key in `jwk`: +#%# If you want to create a new key: +jwk="$(jose jwk gen --input='{"alg":"A256GCM"}')" +#%# Feel free to use different algorithms for `"alg"`, +#%# jose-jwk-gen(1) and jose-alg(1) have more on all this. +#%# +#%# Or, if you want to use an existing key, just load it into `jwk` +#%# from wherever you got it from: +jwk="$(somehow_get_the_key)" +#%# Remember the result must be a valid jwk object. + +#%# 2. Store the key somewhere. That's your logic. +store_the_jwk "$jwk" +#%# It is a good idea to store the entire `$jwk`. If you want to +#%# extract the actual key, use +#%# jose fmt --json="$jwk" --object --get k --unquote=- +#%# ... but you're probably wrong if you want to do that. + +#%# 3. Assemble all the information you will need to re-create +#%# the key later in `jwe`: +#%# +#%# First create a skeleton that declares the pin, and creates a store. +jwe='{"protected":{"clevis":{"pin":"@pin@","@pin@":{}}}}' +#%# Then populate that store. Possibly you'll just have to pass +#%# the parameters. Leave out those you will not need for decryption. +#%# NB: The long form of the `-U` parameter of `jose fmt` is "--unwind" +jwe="$(jose fmt --json="$jwe" --get protected --get clevis --get @pin@ --quote "$@mand1@" --set @mand1@ -UUUU --output=-)" +jwe="$(jose fmt --json="$jwe" --get protected --get clevis --get @pin@ --quote "$@opt1@" --set @opt1@ -UUUU --output=-)" + +#%# Almost there! +#%# Forward everything to `jose jwe enc` which does the encryption job - +#%# including reading the plaintext from stdin which gets replicated +#%# using `cat`. +( printf '%s' "$jwe$jwk" ; cat ) | exec jose jwe enc --input=- --key=- --detached=- --compact +#%# Anything that follows is executed only if jose failed. +#%# +#!# When using mktemp or the like, cleaning up has to be done +#!# manually. See clevis-{en,de}crypt-tpm2 for an example. diff --git a/src/pins/template/clevis-encrypt-@pin@.1.adoc b/src/pins/template/clevis-encrypt-@pin@.1.adoc new file mode 100644 index 00000000..34539f84 --- /dev/null +++ b/src/pins/template/clevis-encrypt-@pin@.1.adoc @@ -0,0 +1,51 @@ +#%# Align the equal signs after substitution +CLEVIS-ENCRYPT-@PIN@(1) +======================= +:doctype: manpage + + +== NAME + +clevis-encrypt-@pin@ - Encrypts using a @@ policy + +== SYNOPSIS + +*clevis encrypt @pin@* CONFIG < PT > JWE + +== OVERVIEW + +The *clevis encrypt @pin@* command encrypts using a @@ policy. +Its only argument is the JSON configuration object. + +#%# And so on ... + +Encrypting data using the @pin@ pin works like this: + + $ clevis encrypt @pin@ '{"@mand1@":"@@","@mand2":"@@"}' < PT > JWE + +To decrypt the data, just pass it to the *clevis decrypt* command: + + $ clevis decrypt < JWE > PT + +== CONFIG + +This command uses the following configuration properties: + +#%# Keep this in sync with the short help in clevis-encrypt-@pin@ + +* *@mand1@* (string) : + @@ (REQUIRED) + +* *@mand2@* (string) : + @@ (REQUIRED) + +* *@opt1@* (string) : + @@ (default: @@) + +== BUGS + +#%# List any flaws and gotchas here. + +== SEE ALSO + +link:clevis-decrypt.1.adoc[*clevis-decrypt*(1)] diff --git a/src/pins/template/dracut.module-setup.sh.in b/src/pins/template/dracut.module-setup.sh.in new file mode 100755 index 00000000..89b6d6ae --- /dev/null +++ b/src/pins/template/dracut.module-setup.sh.in @@ -0,0 +1,27 @@ +#!/bin/sh +# +# Copyright (c) @year@ @name@ +# Author: @name@ <@email@> +# +# 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 . +# + +depends() { + echo clevis + return 0 +} + +install() { + inst clevis-decrypt-@pin@ +} diff --git a/src/pins/template/initramfs.in b/src/pins/template/initramfs.in new file mode 100755 index 00000000..2fb343f7 --- /dev/null +++ b/src/pins/template/initramfs.in @@ -0,0 +1,47 @@ +#!/bin/sh +# +# Copyright (c) @year@ @name@ +# Author: @name@ <@email@> +# +# 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 . +# + +case $1 in +prereqs) + exit 0 + ;; +esac + +. @initramfstoolsdir@/hook-functions + +die() { + code="$1" + msg="$2" + echo " (ERROR): $msg" >&2 + exit $1 +} + +copy_exec @bindir@/clevis-decrypt-@pin@ || die 1 "@bindir@/clevis-decrypt-@pin@ not found" + +#%# Possibly you to need to install additional binaries: +#%# +#%# find_binary() { +#%# bin_name="$1" +#%# resolved=$(which ${bin_name}) +#%# [ -z "$resolved" ] && die 1 "Unable to find ${bin_name}" +#%# echo "$resolved" +#%# } +#%# +#%# curl_bin=$(find_binary "curl") +#%# copy_exec "${curl_bin}" || die 2 "Unable to copy ${curl_bin} to initrd image" diff --git a/src/pins/template/meson.build b/src/pins/template/meson.build new file mode 100644 index 00000000..49a31c69 --- /dev/null +++ b/src/pins/template/meson.build @@ -0,0 +1,66 @@ +#%# This controls build and test +#%# +#%# Feel free to peek into other tests to get an idea what can be done. +#%# +#%# Possibly you'll need some extra programs, probe for them. + +#%# curl is just an example +curl = find_program('curl', required: false) +#%# dracut and initramfs_tools are needed for early userland +dracut = dependency('dracut', required: false) +initramfs_tools = find_program('update-initramfs', required: false) + +if curl.found() +#%# BUild the programs + bins += join_paths(meson.current_source_dir(), 'clevis-decrypt-@pin@') + bins += join_paths(meson.current_source_dir(), 'clevis-encrypt-@pin@') +#%# and the manpage + mans += join_paths(meson.current_source_dir(), 'clevis-encrypt-@pin@.1') + +#%# Set up a test environment + env = environment() + env.append('PATH', + join_paths(meson.source_root(), 'src'), + meson.current_source_dir(), + '/usr/libexec', + libexecdir, + separator: ':' + ) + +#%# Run the test. + test('pin-@pin@', find_program('./pin-@pin@'), env: env) +else + warning('Will not install @pin@ pin due to missing dependencies!') +endif + +#%# dracut support +if dracut.found() + dracutdir = dracut.get_pkgconfig_variable('dracutmodulesdir') + '/60' + meson.project_name() + '-pin-@pin@' +#?# In general, substituation is not needed but it's better to +#?# stay flexible. + configure_file( + input: 'dracut.module-setup.sh.in', + output: 'module-setup.sh', + install_dir: dracutdir, + configuration: data, + ) +else + warning('Will not install dracut module clevis-pin-@pin@ due to missing dependencies!') +endif + +#%# initramfs support +if initramfs_tools.found() + initramfstools_dir = '/usr/share/initramfs-tools' + initramfs_hooks_dir = '/usr/share/initramfs-tools/hooks' + initramfs_data = configuration_data() + initramfs_data.merge_from(data) + initramfs_data.set('initramfstoolsdir', initramfstools_dir) + configure_file( + input: 'initramfs.in', + output: 'clevis-pin-@pin@', + install_dir: initramfs_hooks_dir, + configuration: initramfs_data, + ) +else + warning('Will not install initramfs module clevis-pin-@pin@ due to missing dependencies!') +endif diff --git a/src/pins/template/pin-@pin@ b/src/pins/template/pin-@pin@ new file mode 100755 index 00000000..c740e3e0 --- /dev/null +++ b/src/pins/template/pin-@pin@ @@ -0,0 +1,40 @@ +#%# Of course you should provide some test +#%# +#!/bin/sh + +#%# Maximum verbosity is desired +set -ex + +# Copyright (c) @year@ @name@ +# Author: @name@ <@email@> +# +# 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 . +# + +#%# A simple test will encrypt a text, decrypt, and compare. +#%# Some work might be necessary to set up a test bed. +#%# +#%# Feel free to peek into other tests to get an idea what can be done. +#%# +#!# Your test might require root privileges but your script might +#!# not have them. So probe, and exit mit the magical code 77 to skip +#!# that test instead of failing. + +#%# Create your configuration +cfg="$(printf '{"@mand1@":"%s","@mand2@":"%s"}' "@@" "@@")" +#%# The input +inp='hi' +enc="$(printf '%s' "$inp" | clevis encrypt @pin@ "$cfg")" +dec="$(printf '%s' "$enc" | clevis decrypt)" +test "$dec" = "$inp" From b44e13e0029592934b3b690d736c073c08739bd6 Mon Sep 17 00:00:00 2001 From: Christoph Biedl Date: Sat, 28 Jan 2023 17:30:49 +0100 Subject: [PATCH 2/2] Implement a pin to store the key in a single file This is a very simple pin, and possibly rather for educational purposes: Store the jwk at a given place in the file system. --- src/pins/file/clevis-decrypt-file | 59 +++++++++++++++++++++ src/pins/file/clevis-encrypt-file | 65 ++++++++++++++++++++++++ src/pins/file/clevis-encrypt-file.1.adoc | 44 ++++++++++++++++ src/pins/file/dracut.module-setup.sh.in | 27 ++++++++++ src/pins/file/initramfs.in | 35 +++++++++++++ src/pins/file/meson.build | 46 +++++++++++++++++ src/pins/file/pin-file | 30 +++++++++++ src/pins/meson.build | 1 + 8 files changed, 307 insertions(+) create mode 100755 src/pins/file/clevis-decrypt-file create mode 100755 src/pins/file/clevis-encrypt-file create mode 100644 src/pins/file/clevis-encrypt-file.1.adoc create mode 100755 src/pins/file/dracut.module-setup.sh.in create mode 100755 src/pins/file/initramfs.in create mode 100644 src/pins/file/meson.build create mode 100755 src/pins/file/pin-file diff --git a/src/pins/file/clevis-decrypt-file b/src/pins/file/clevis-decrypt-file new file mode 100755 index 00000000..d1a06b08 --- /dev/null +++ b/src/pins/file/clevis-decrypt-file @@ -0,0 +1,59 @@ +#!/bin/bash + +set -eu + +# Copyright (c) 2020 Christoph Biedl +# Author: Christoph Biedl +# +# 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 . +# + +[ $# -eq 1 ] && [ "${1:-}" = "--summary" ] && exit 2 + +if [ -t 0 ] ; then + echo >&2 + echo 'Usage: clevis decrypt file < JWE > PLAINTEXT' >&2 + echo >&2 + exit 1 +fi + +read -d . hdr64 +if ! hdr="$(jose fmt --quote="$hdr64" --string --b64load --object --output=-)" ; then + echo 'JWE header corrupt' >&2 + exit 1 +fi + +if [ "$(jose fmt --json="$hdr" --get clevis --get pin --unquote=-)" != 'file' ] ; then + echo 'JWE pin mismatch!' >&2 + exit 1 +fi + +if ! name="$(jose fmt --json="$hdr" --get clevis --get file --get name --unquote=-)" ; then + echo 'JWE missing 'clevis.file.name' header parameter!' >&2 + exit 1 +fi + +if [ ! -f "$name" ] ; then + echo "Key file $name not found" >&2 + exit 1 +fi + +jwk="$(cat "$name")" + +if ! jose fmt --json="$jwk" --object --output=/dev/null 2>/dev/null ; then + echo "Key file $name is malformed" >&2 + exit 1 +fi + +( printf '%s' "$jwk$hdr64." ; cat ) | exec jose jwe dec --key=- --input=- diff --git a/src/pins/file/clevis-encrypt-file b/src/pins/file/clevis-encrypt-file new file mode 100755 index 00000000..b1bfac4e --- /dev/null +++ b/src/pins/file/clevis-encrypt-file @@ -0,0 +1,65 @@ +#!/bin/sh + +set -eu + +# Copyright (c) 2020 Christoph Biedl +# Author: Christoph Biedl +# +# 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 . +# + +SUMMARY='Encrypts using a jwk stored in a file policy' + +if [ "${1:-}" = '--summary' ] ; then + echo "$SUMMARY" + exit 0 +fi + +if [ -t 0 ] ; then + exec >&2 + echo + echo 'Usage: clevis encrypt file CONFIG < PLAINTEXT > JWE' + echo + echo "$SUMMARY" + echo + echo 'his command uses the following configuration properties:' + echo + echo ' name: The file that holds the encryption key (REQUIRED)' + echo + exit 2 +fi + +if ! cfg="$(jose fmt --json="$1" --object --output=- 2>/dev/null)" ; then + echo 'Configuration is malformed!' >&2 + exit 1 +fi + +if ! name="$(jose fmt --json="$cfg" --object --get name --unquote=-)" ; then + echo 'Missing the required name property!' >&2 + exit 1 +fi + +if [ -e "$name" ] ; then + echo "File $name already exists" >&2 + exit 1 +fi + +jwk="$(jose jwk gen --input='{"alg":"A256GCM"}')" + +( umask 0377 ; echo "$jwk" >"$name" ) + +jwe='{"protected":{"clevis":{"pin":"file","file":{}}}}' +jwe="$(jose fmt --json="$jwe" --get protected --get clevis --get file --quote "$name" --set name -UUUU --output=-)" + +( printf '%s' "$jwe$jwk" ; cat ) | exec jose jwe enc --input=- --key=- --detached=- --compact diff --git a/src/pins/file/clevis-encrypt-file.1.adoc b/src/pins/file/clevis-encrypt-file.1.adoc new file mode 100644 index 00000000..bc898689 --- /dev/null +++ b/src/pins/file/clevis-encrypt-file.1.adoc @@ -0,0 +1,44 @@ +CLEVIS-ENCRYPT-FILE(1) +====================== +:doctype: manpage + + +== NAME + +clevis-encrypt-file - Encrypts using a file policy + +== SYNOPSIS + +*clevis encrypt file* CONFIG < PT > JWE + +== OVERVIEW + +The *clevis encrypt file* command encrypts using a file policy. +Its only argument is the JSON configuration object. + +Encrypting data using the file pin works like this: + + $ clevis encrypt file '{"name":"/path/to/file"}' < PT > JWE + +The given file must not exist yet. + +To decrypt the data, just pass it to the *clevis decrypt* command: + + $ clevis decrypt < JWE > PT + +== CONFIG + +This command uses the following configuration properties: + +* *name* (string) : + The name to the file where the jwk is stored (REQUIRED) + +== BUGS + +Requires that directories for that file already exist. + +Rather for educational purposes. + +== SEE ALSO + +link:clevis-decrypt.1.adoc[*clevis-decrypt*(1)] diff --git a/src/pins/file/dracut.module-setup.sh.in b/src/pins/file/dracut.module-setup.sh.in new file mode 100755 index 00000000..765bfb0f --- /dev/null +++ b/src/pins/file/dracut.module-setup.sh.in @@ -0,0 +1,27 @@ +#!/bin/sh +# +# Copyright (c) 2020 Christoph Biedl +# Author: Christoph Biedl +# +# 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 . +# + +depends() { + echo clevis + return 0 +} + +install() { + inst clevis-decrypt-file +} diff --git a/src/pins/file/initramfs.in b/src/pins/file/initramfs.in new file mode 100755 index 00000000..77f9afd1 --- /dev/null +++ b/src/pins/file/initramfs.in @@ -0,0 +1,35 @@ +#!/bin/sh +# +# Copyright (c) 2020 Christoph Biedl +# Author: Christoph Biedl +# +# 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 . +# + +case $1 in +prereqs) + exit 0 + ;; +esac + +. @initramfstoolsdir@/hook-functions + +die() { + code="$1" + msg="$2" + echo " (ERROR): $msg" >&2 + exit "$code" +} + +copy_exec @bindir@/clevis-decrypt-file || die 1 "@bindir@/clevis-decrypt-file not found" diff --git a/src/pins/file/meson.build b/src/pins/file/meson.build new file mode 100644 index 00000000..ff16acbf --- /dev/null +++ b/src/pins/file/meson.build @@ -0,0 +1,46 @@ + +dracut = dependency('dracut', required: false) +initramfs_tools = find_program('update-initramfs', required: false) + +bins += join_paths(meson.current_source_dir(), 'clevis-decrypt-file') +bins += join_paths(meson.current_source_dir(), 'clevis-encrypt-file') +mans += join_paths(meson.current_source_dir(), 'clevis-encrypt-file.1') + +env = environment() +env.append('PATH', + join_paths(meson.source_root(), 'src'), + meson.current_source_dir(), + '/usr/libexec', + libexecdir, + separator: ':' +) + +test('pin-file', find_program('./pin-file'), env: env) + +if dracut.found() + dracutdir = dracut.get_pkgconfig_variable('dracutmodulesdir') + '/60' + meson.project_name() + '-pin-file' + configure_file( + input: 'dracut.module-setup.sh.in', + output: 'module-setup.sh', + install_dir: dracutdir, + configuration: data, + ) +else + warning('Will not install dracut module clevis-pin-file due to missing dependencies!') +endif + +if initramfs_tools.found() + initramfstools_dir = '/usr/share/initramfs-tools' + initramfs_hooks_dir = '/usr/share/initramfs-tools/hooks' + initramfs_data = configuration_data() + initramfs_data.merge_from(data) + initramfs_data.set('initramfstoolsdir', initramfstools_dir) + configure_file( + input: 'initramfs.in', + output: 'clevis-pin-file', + install_dir: initramfs_hooks_dir, + configuration: initramfs_data, + ) +else + warning('Will not install initramfs module clevis-pin-file due to missing dependencies!') +endif diff --git a/src/pins/file/pin-file b/src/pins/file/pin-file new file mode 100755 index 00000000..887b946e --- /dev/null +++ b/src/pins/file/pin-file @@ -0,0 +1,30 @@ +#!/bin/sh + +set -e + +# Copyright (c) 2020 Christoph Biedl +# Author: Christoph Biedl +# +# 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 . +# + +TMP="$(mktemp -d)" +# shellcheck disable=SC2064 # Expanding TMP right now is intended +trap "rm -rf \"$TMP\"" EXIT + +cfg="$(printf '{"name":"%s"}' "$TMP/key")" +inp='hi' +enc="$(printf '%s' "$inp" | clevis encrypt file "$cfg")" +dec="$(printf '%s' "$enc" | clevis decrypt)" +test "$dec" = "$inp" diff --git a/src/pins/meson.build b/src/pins/meson.build index 12670ae8..20edfa72 100644 --- a/src/pins/meson.build +++ b/src/pins/meson.build @@ -1,3 +1,4 @@ +subdir('file') subdir('sss') subdir('tang') subdir('tpm2')