Skip to content

Commit

Permalink
Provide a template for new pins
Browse files Browse the repository at this point in the history
This should ease writing new pins a lot, for newcomers and for
experienced authors as well.
  • Loading branch information
Christoph Biedl committed May 23, 2020
1 parent 0bb86d3 commit 6f1f138
Show file tree
Hide file tree
Showing 8 changed files with 530 additions and 0 deletions.
86 changes: 86 additions & 0 deletions src/pins/template/README.md
Original file line number Diff line number Diff line change
@@ -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-@[email protected]`.

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.
73 changes: 73 additions & 0 deletions src/pins/template/clevis-decrypt-@pin@
Original file line number Diff line number Diff line change
@@ -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 <https://www.gnu.org/licenses/>.
#

#%# 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=-
140 changes: 140 additions & 0 deletions src/pins/template/clevis-encrypt-@pin@
Original file line number Diff line number Diff line change
@@ -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 <https://www.gnu.org/licenses/>.
#

#%# 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@: <string> One parameter @@ (REQUIRED)'
echo
echo ' @mand2@: <string> Another parameter @@ (REQUIRED)'
echo
#%# Optional parameters should mention the default value.
echo ' @opt1@: <string> 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.
51 changes: 51 additions & 0 deletions src/pins/template/clevis-encrypt-@[email protected]
Original file line number Diff line number Diff line change
@@ -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)]
27 changes: 27 additions & 0 deletions src/pins/template/dracut.module-setup.sh.in
Original file line number Diff line number Diff line change
@@ -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 <http://www.gnu.org/licenses/>.
#

depends() {
echo clevis
return 0
}

install() {
inst clevis-decrypt-@pin@
}
Loading

0 comments on commit 6f1f138

Please sign in to comment.