### Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
bin_SCRIPTS =
+sbin_SCRIPTS =
dist_pkglib_SCRIPTS =
+dist_pkglib_DATA =
pkglib_DATA =
noinst_SCRIPTS =
PYTHON="$(PYTHON)" \
bindir="$(bindir)" \
pkgconfdir="$(sysconfdir)/$(PACKAGE)" \
+ pkgstatedir="$(localstatedir)/$(PACKAGE)" \
pkglibdir="$(pkglibdir)"
SUBST = $(AM_V_GEN)$(confsubst)
$(SUBST) $(srcdir)/shamir.in $(SUBSTVARS) >shamir.new && \
chmod +x shamir.new && mv shamir.new shamir
+## Property expansion.
+bin_SCRIPTS += extract-profile
+EXTRA_DIST += extract-profile.in
+CLEANFILES += extract-profile
+extract-profile: extract-profile.in Makefile
+ $(SUBST) $(srcdir)/extract-profile.in $(SUBSTVARS) \
+ >extract-profile.new && \
+ chmod +x extract-profile.new && \
+ mv extract-profile.new extract-profile
+
+###--------------------------------------------------------------------------
+### Crypto operations.
+
+## Main driver program.
+sbin_SCRIPTS += cryptop
+EXTRA_DIST += cryptop.in
+CLEANFILES += cryptop
+cryptop: cryptop.in Makefile
+ $(SUBST) $(srcdir)/cryptop.in $(SUBSTVARS) >cryptop.new && \
+ chmod +x cryptop.new && mv cryptop.new cryptop
+
+## Key type libraries.
+dist_pkglib_DATA += ktype.gnupg
+dist_pkglib_DATA += ktype.seccure
+
+## Commands.
+dist_pkglib_SCRIPTS += cryptop.genkey
+dist_pkglib_SCRIPTS += cryptop.delkey
+dist_pkglib_SCRIPTS += cryptop.recover
+dist_pkglib_SCRIPTS += cryptop.info
+dist_pkglib_SCRIPTS += cryptop.public
+dist_pkglib_SCRIPTS += cryptop.encrypt
+dist_pkglib_SCRIPTS += cryptop.decrypt
+dist_pkglib_SCRIPTS += cryptop.sign
+dist_pkglib_SCRIPTS += cryptop.verify
+
###--------------------------------------------------------------------------
### Main driver program and commands.
mv keyfunc.sh.new keyfunc.sh
## Commands.
-dist_pkglib_SCRIPTS += keeper-cards
-dist_pkglib_SCRIPTS += new-keeper
-dist_pkglib_SCRIPTS += new-recov
-dist_pkglib_SCRIPTS += recover
-dist_pkglib_SCRIPTS += reveal
-dist_pkglib_SCRIPTS += stash
+dist_pkglib_SCRIPTS += keys.conceal
+dist_pkglib_SCRIPTS += keys.keeper-cards
+dist_pkglib_SCRIPTS += keys.new-keeper
+dist_pkglib_SCRIPTS += keys.new-recov
+dist_pkglib_SCRIPTS += keys.recover
+dist_pkglib_SCRIPTS += keys.reveal
+dist_pkglib_SCRIPTS += keys.stash
###----- That's all, folks --------------------------------------------------
--- /dev/null
+#! /bin/sh
+###
+### Decrypt data using a user key
+###
+### (c) 2011 Mark Wooding
+###
+
+###----- Licensing notice ---------------------------------------------------
+###
+### This file is part of the distorted.org.uk key management suite.
+###
+### distorted-keys 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 2 of the License, or
+### (at your option) any later version.
+###
+### distorted-keys 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 distorted-keys; if not, write to the Free Software Foundation,
+### Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+set -e
+case "${KEYSLIB+t}" in t) ;; *) echo >&2 "$0: KEYSLIB unset"; exit 1 ;; esac
+. "$KEYSLIB"/keyfunc.sh
+
+defhelp <<HELP
+KEY
+Decrypt the ciphertext from standard input using the named KEY. The
+plaintext is written to standard output.
+HELP
+
+case $# in 1) ;; *) usage_err ;; esac
+key=$1
+
+mktmp
+prepare "$key" decrypt
+c_decrypt $kdir $knub
+
+###----- That's all, folks --------------------------------------------------
--- /dev/null
+#! /bin/sh
+###
+### Delete a user key
+###
+### (c) 2011 Mark Wooding
+###
+
+###----- Licensing notice ---------------------------------------------------
+###
+### This file is part of the distorted.org.uk key management suite.
+###
+### distorted-keys 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 2 of the License, or
+### (at your option) any later version.
+###
+### distorted-keys 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 distorted-keys; if not, write to the Free Software Foundation,
+### Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+set -e
+case "${KEYSLIB+t}" in t) ;; *) echo >&2 "$0: KEYSLIB unset"; exit 1 ;; esac
+. "$KEYSLIB"/keyfunc.sh
+
+defhelp <<HELP
+KEY
+Delete the named user KEY.
+HELP
+
+case $# in 1) ;; *) usage_err ;; esac
+key=$1
+
+parse_keylabel "$key"
+rm -rf $kdir
+rm -f $knub
+rm -f $KEYS/recov/*/current/$kowner/$klabel.recov
+
+###----- That's all, folks --------------------------------------------------
--- /dev/null
+#! /bin/sh
+###
+### Encrypt data using a user key
+###
+### (c) 2011 Mark Wooding
+###
+
+###----- Licensing notice ---------------------------------------------------
+###
+### This file is part of the distorted.org.uk key management suite.
+###
+### distorted-keys 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 2 of the License, or
+### (at your option) any later version.
+###
+### distorted-keys 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 distorted-keys; if not, write to the Free Software Foundation,
+### Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+set -e
+case "${KEYSLIB+t}" in t) ;; *) echo >&2 "$0: KEYSLIB unset"; exit 1 ;; esac
+. "$KEYSLIB"/keyfunc.sh
+
+defhelp <<HELP
+KEY
+Encrypt the data from standard input using the named KEY. The ciphertext is
+written to standard output.
+HELP
+
+case $# in 1) ;; *) usage_err ;; esac
+key=$1
+
+mktmp
+prepare "$key" encrypt
+c_encrypt $kdir $knub
+
+###----- That's all, folks --------------------------------------------------
--- /dev/null
+#! /bin/sh
+###
+### Generate a user key
+###
+### (c) 2011 Mark Wooding
+###
+
+###----- Licensing notice ---------------------------------------------------
+###
+### This file is part of the distorted.org.uk key management suite.
+###
+### distorted-keys 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 2 of the License, or
+### (at your option) any later version.
+###
+### distorted-keys 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 distorted-keys; if not, write to the Free Software Foundation,
+### Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+set -e
+case "${KEYSLIB+t}" in t) ;; *) echo >&2 "$0: KEYSLIB unset"; exit 1 ;; esac
+. "$KEYSLIB"/keyfunc.sh
+
+defhelp <<HELP
+[-f] KEY PROFILE [ARGUMENTS ...]
+Generate a named KEY, according to the PROFILE.
+
+The ARGUMENTS are passed to the key-type handler selected by the profile.
+
+Options:
+ -f Force overwriting an existing key.
+HELP
+
+genhook_recov () {
+ base=$1 nub=$2
+ for recov in $kopt_recovery; do
+ (stash $recov $kowner/$klabel <"$nub")
+ done
+}
+
+## Parse command-line arguments.
+forcep=nil
+while getopts "f" opt; do
+ case "$opt" in
+ f) forcep=t ;;
+ *) usage_err ;;
+ esac
+done
+shift $(( $OPTIND - 1 ))
+case $# in 0 | 1) usage_err ;; esac
+key=$1 profile=$2; shift 2
+
+## Check the key name carefully. This is where we actually create files,
+## so it's reallly important to make sure that we don't make any stupid
+## mistakes.
+mktmp
+parse_keylabel "$key"
+case "$kowner" in
+ "$USERV_USER") ;;
+ *) echo >&2 "$quis: invalid owner \`$kowner' for key"; exit 1 ;;
+esac
+case $forcep in
+ nil)
+ if [ -d $kdir ]; then
+ echo >&2 "$quis: key \`$key' already exists"
+ exit 1
+ fi
+ ;;
+esac
+
+## Check the profile name. We shouldn't allow users to refer to each
+## other's profiles.
+case "$profile" in
+ :*)
+ label=${profile#:}
+ ;;
+ *:*)
+ powner=${profile%%:*} label=${profile#*:}
+ case "$powner" in
+ "$USERV_USER") ;;
+ *) echo >&2 "$quis: invalid owner \`$powner' for profile"; exit 1 ;;
+ esac
+ ;;
+ *)
+ label=$profile
+ ;;
+esac
+checkword "profile label" "$label"
+read_profile $profile
+
+## Generate the key.
+c_genkey $profile $kdir $knub genhook_recov "$@"
+
+###----- That's all, folks --------------------------------------------------
--- /dev/null
+#! /bin/sh
+###
+### User cryptographic operations
+###
+### (c) 2011 Mark Wooding
+###
+
+###----- Licensing notice ---------------------------------------------------
+###
+### This file is part of the distorted.org.uk key management suite.
+###
+### distorted-keys 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 2 of the License, or
+### (at your option) any later version.
+###
+### distorted-keys 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 distorted-keys; if not, write to the Free Software Foundation,
+### Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+set -e
+: ${ETC=@pkgconfdir@}
+: ${KEYS=@pkgstatedir@}
+: ${KEYSLIB=@pkglibdir@}
+export ETC KEYS KEYSLIB
+
+. "$KEYSLIB"/keyfunc.sh
+
+usage="usage: $quis COMMAND [ARGUMENTS ...]"
+prefix=cryptop
+
+## Fake up caller credentials if not called via userv.
+case "${USERV_USER+t}" in
+ t) ;;
+ *) USERV_USER=${LOGNAME-${USER-$(id -un)}} USERV_UID=$(id -u) ;;
+esac
+case "${USERV_GROUP+t}" in
+ t) ;;
+ *) USERV_GROUP=$(id -Gn) USERV_GID=$(id -gn) ;;
+esac
+export USERV_USER USERV_UID USERV_GROUP USERV_GID
+
+## Parse options.
+while getopts "hv" opt; do
+ case "$opt" in
+ h) cmd_help; exit ;;
+ v) version; exit ;;
+ *) usage_err ;;
+ esac
+done
+shift $(( $OPTIND - 1 ))
+
+## Dispatch.
+dispatch "$@"
+
+###----- That's all, folks --------------------------------------------------
--- /dev/null
+#! /bin/sh
+###
+### Show information about a key
+###
+### (c) 2011 Mark Wooding
+###
+
+###----- Licensing notice ---------------------------------------------------
+###
+### This file is part of the distorted.org.uk key management suite.
+###
+### distorted-keys 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 2 of the License, or
+### (at your option) any later version.
+###
+### distorted-keys 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 distorted-keys; if not, write to the Free Software Foundation,
+### Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+set -e
+case "${KEYSLIB+t}" in t) ;; *) echo >&2 "$0: KEYSLIB unset"; exit 1 ;; esac
+. "$KEYSLIB"/keyfunc.sh
+
+defhelp <<HELP
+KEY
+Show information about the named user KEY.
+HELP
+
+case $# in 1) ;; *) usage_err ;; esac
+key=$1
+
+mktmp
+prepare "$key" info
+set | sed -n '/^kprop_/{s///;y/_/-/;p}'
+k_info $kdir
+
+###----- That's all, folks --------------------------------------------------
--- /dev/null
+#! /bin/sh
+###
+### Show a public key
+###
+### (c) 2011 Mark Wooding
+###
+
+###----- Licensing notice ---------------------------------------------------
+###
+### This file is part of the distorted.org.uk key management suite.
+###
+### distorted-keys 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 2 of the License, or
+### (at your option) any later version.
+###
+### distorted-keys 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 distorted-keys; if not, write to the Free Software Foundation,
+### Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+set -e
+case "${KEYSLIB+t}" in t) ;; *) echo >&2 "$0: KEYSLIB unset"; exit 1 ;; esac
+. "$KEYSLIB"/keyfunc.sh
+
+defhelp <<HELP
+KEY
+If the user KEY is asymmetric, write the public key to stdout.
+HELP
+
+case $# in 1) ;; *) usage_err ;; esac
+key=$1
+
+mktmp
+prepare "$key" info
+if [ -f $kdir/pub ]; then
+ cat $kdir/pub
+else
+ echo >&2 "$quis: \`$key' has no public part"
+ exit 1
+fi
+
+###----- That's all, folks --------------------------------------------------
--- /dev/null
+#! /bin/sh
+###
+### Recover a user key nub
+###
+### (c) 2011 Mark Wooding
+###
+
+###----- Licensing notice ---------------------------------------------------
+###
+### This file is part of the distorted.org.uk key management suite.
+###
+### distorted-keys 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 2 of the License, or
+### (at your option) any later version.
+###
+### distorted-keys 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 distorted-keys; if not, write to the Free Software Foundation,
+### Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+set -e
+case "${KEYSLIB+t}" in t) ;; *) echo >&2 "$0: KEYSLIB unset"; exit 1 ;; esac
+. "$KEYSLIB"/keyfunc.sh
+
+defhelp <<HELP
+KEY RECOV
+Recover the named user KEY using a blob protected using the recovery key
+RECOV; it is an error if RECOV is not currently revealed.
+HELP
+
+case $# in 2) ;; *) usage_err ;; esac
+key=$1 recov=$2
+parse_keylabel "$key"
+if [ ! -d $kdir ]; then echo >&2 "$quis: unknown key \`$key'"; exit 1; fi
+checkword "recovery key label" "$recov"
+
+mktmp
+nubid=$(cat $kdir/nubid)
+readmeta $kdir
+read_profile "$profile"
+if [ -f $knub ]; then
+ nubbin=$(nubid <$knub)
+ case "$nubbin" in
+ "$nubid")
+ echo >&2 "$quis: key \`$key' doesn't need recovery"
+ exit 1
+ ;;
+ esac
+fi
+
+umask 077
+recover $recov $kowner/$klabel >$knub.new
+nubbin=$(nubid <$knub.new)
+case "$nubbin" in
+ "$nubid") ;;
+ *)
+ echo >&2 "$quis: recovery produced incorrect nub"
+ exit 1
+ ;;
+esac
+mv $knub.new $knub
+
+###----- That's all, folks --------------------------------------------------
--- /dev/null
+#! /bin/sh
+###
+### Sign data using a user key
+###
+### (c) 2011 Mark Wooding
+###
+
+###----- Licensing notice ---------------------------------------------------
+###
+### This file is part of the distorted.org.uk key management suite.
+###
+### distorted-keys 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 2 of the License, or
+### (at your option) any later version.
+###
+### distorted-keys 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 distorted-keys; if not, write to the Free Software Foundation,
+### Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+set -e
+case "${KEYSLIB+t}" in t) ;; *) echo >&2 "$0: KEYSLIB unset"; exit 1 ;; esac
+. "$KEYSLIB"/keyfunc.sh
+
+defhelp <<HELP
+KEY
+Sign the data from standard input using the named KEY. The signature is
+written to standard output.
+HELP
+
+case $# in 1) ;; *) usage_err ;; esac
+key=$1
+
+mktmp
+prepare "$key" sign
+c_sign $kdir $knub
+
+###----- That's all, folks --------------------------------------------------
--- /dev/null
+#! /bin/sh
+###
+### Verify signed data using a user key
+###
+### (c) 2011 Mark Wooding
+###
+
+###----- Licensing notice ---------------------------------------------------
+###
+### This file is part of the distorted.org.uk key management suite.
+###
+### distorted-keys 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 2 of the License, or
+### (at your option) any later version.
+###
+### distorted-keys 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 distorted-keys; if not, write to the Free Software Foundation,
+### Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+set -e
+case "${KEYSLIB+t}" in t) ;; *) echo >&2 "$0: KEYSLIB unset"; exit 1 ;; esac
+. "$KEYSLIB"/keyfunc.sh
+
+defhelp <<HELP
+KEY SIGNATURE
+Verify the message on standard input against a literla SIGNATURE using the
+named KEY.
+HELP
+
+case $# in 2) ;; *) usage_err ;; esac
+key=$1 sig=$2
+
+mktmp
+prepare "$key" sign
+c_sign $kdir $knub "$sig"
+
+###----- That's all, folks --------------------------------------------------
--- /dev/null
+#! @PYTHON@
+###
+### Parse a profile definition file and emit a particular section
+###
+### (c) 2011 Mark Wooding
+###
+
+###----- Licensing notice ---------------------------------------------------
+###
+### This file is part of the distorted.org.uk key management suite.
+###
+### distorted-keys 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 2 of the License, or
+### (at your option) any later version.
+###
+### distorted-keys 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 distorted-keys; if not, write to the Free Software Foundation,
+### Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+from __future__ import with_statement
+
+import sys as SYS
+import os as OS
+import UserDict as UD
+import optparse as O
+from cStringIO import StringIO
+
+PACKAGE = "@PACKAGE@"
+VERSION = "@VERSION@"
+
+###--------------------------------------------------------------------------
+### Utilities.
+
+class struct (object):
+ def __init__(me, **kw):
+ me.__dict__.update(kw)
+
+class UserError (Exception): pass
+
+###--------------------------------------------------------------------------
+### Configuration section management.
+
+class prop (struct): pass
+
+class Section (object, UD.DictMixin):
+ """
+ A section of a profile configuration file.
+ """
+
+ ## States for depth-first traversal.
+ V_WHITE = 0 # Not yet visited.
+ V_GREY = 1 # Currently being visited.
+ V_BLACK = 2 # Visited previously and processed.
+
+ def __init__(me, name, dict = None):
+ """
+ Initialize a Section object.
+
+ The DICT provides the initial (direct) contents of the Section. The NAME
+ is used for presentation purposes, e.g., when reporting error conditions.
+ """
+ super(Section, me).__init__()
+ if dict is None: me._dict = {}
+ else: me._dict = dict
+ me._visited = me.V_WHITE
+ me.name = name
+ me.includes = set()
+ me.inferiors = set()
+ me.inherited = {}
+
+ ## Dictionary methods for UD.DictMixin. The built-in `dict' class provides
+ ## equality, and is therefore not hashable. By doing things this way, we
+ ## can retain reference equality and stay hashable, which will be useful
+ ## later.
+ def __getitem__(me, key):
+ return me._dict[key]
+ def __setitem__(me, key, value):
+ me._dict[key] = value
+ def __delitem__(me, key):
+ del me._dict[key]
+ def keys(me):
+ return me._dict.keys()
+ def __contains__(me, key):
+ return key in me._dict
+ def __iter__(me):
+ return me._dict.__iter__()
+ def iteritems(me):
+ return me._dict.iteritems()
+ def __repr__(me):
+ return 'Section(%r, %r)' % (me.name, me.inherited)
+
+ def transit(me, seen = None, path = None):
+ """
+ Visit the Section for the purposes of computing transitive inclusion.
+
+ If this completes successfully, the Section's inferiors slot is set up to
+ contain all of its (non-strict) inferiors. A section's inferiors consist
+ of itself, together with the union of the inferiors of all of its
+ included Sections.
+
+ If the Section's visited state is black, nothing happens; if it's white
+ then it will be coloured grey temporarily, and its included Sections
+ processed recursively; if it's grey to begin with then we have
+ encountered a cycle.
+
+ The SEEN dictionary and PATH list are used for detecting and reporting
+ cycles. The PATH contains a list of the currently grey Sections, in the
+ order in which they were encountered; SEEN maps Section names to their
+ indices in the PATH list.
+
+ It is possible to make this work in the presence of cycles, but it's more
+ effort than it's worth.
+ """
+
+ ## Extend the path to include us. This will be useful when reporting
+ ## cycles.
+ if seen is None: seen = {}
+ if path is None: path = []
+ path.append(me)
+
+ ## Already been here: nothing to do.
+ if me._visited == me.V_BLACK:
+ pass
+
+ ## We've found a cycle: report it to the user.
+ elif me._visited == me.V_GREY:
+ raise UserError, 'detected inclusion cycle:\n\t%s' % \
+ ' -> '.join(["`%s'" % s.name for s in path[seen[me]:]])
+
+ ## Not done this one yet: process my included Sections, and compute the
+ ## union of their inferiors.
+ else:
+ seen[me] = len(path) - 1
+ me._visited = me.V_GREY
+ me.inferiors = set([me])
+ for s in me.includes:
+ s.transit(seen, path)
+ me.inferiors.update(s.inferiors)
+ me._visited = me.V_BLACK
+
+ ## Remove myself from the path.
+ path.pop()
+
+ def inherit(me):
+ """
+ Compute the inherited properties for this Section.
+
+ A Section has an inherited property named P if any inferior has a direct
+ property named P. The value of the property is determined as follows.
+ Firstly, determine the set A of all inferiors which have a direct
+ property P. Secondly, determine a /reduced/ set containing only the
+ maximal elements of A: if R contains a pair of distinct inferiors I and J
+ such that I is an inferior of J, then R does not contain I; R contains
+ all elements A not so excluded. If all inferiors in R define the same
+ value for the property, then that is the value of the inherited property;
+ if two inferiors disagree, then the situation is erroneous.
+
+ Note that if a Section defines a direct property then it has an inherited
+ property with the same value: in this case, the reduced set is a
+ singleton.
+ """
+
+ ## First pass: for each property name, determine the reduced set of
+ ## inferiors defining that property, and the values they have for it.
+ ## Here, D maps property names to lists of `prop' records.
+ d = {}
+ for s in me.inferiors:
+
+ ## Work through the direct properties of inferior S.
+ for k, v in s.iteritems():
+
+ ## Ignore `special' properties.
+ if k.startswith('@'):
+ continue
+
+ ## Work through the current reduced set. Discard entries from
+ ## sections inferior to S. If an entry exists for a section T to
+ ## which S is inferior, then don't add S itself.
+ addp = True
+ pp = []
+ try:
+ for q in d[k]:
+ if s in q.source.inferiors:
+ addp = False
+ if q.source not in s.inferiors:
+ pp.append(q)
+ except KeyError:
+ pass
+ if addp:
+ pp.append(prop(value = v, source = s))
+ d[k] = pp
+
+ ## Second pass: check that the reduced set defines a unique value for
+ ## each inherited property.
+ for k, vv in d.iteritems():
+ c = {}
+
+ ## Build in C a dictionary mapping candidate values to lists of
+ ## inferiors asserting those values.
+ for p in vv:
+ c.setdefault(p.value, []).append(p.source)
+
+ ## Now C should have only one key. If not, C records enough
+ ## information that we can give a useful error report.
+ if len(c) != 1:
+ raise UserError, \
+ "inconsistent values for property `%s' in section `%s': %s" % \
+ (k, me.name,
+ ''.join(["\n\t`%s' via %s" %
+ (v, ', '.join(["`%s'" % s.name for s in ss]))
+ for v, ss in c.iteritems()]))
+
+ ## Insert the computed property value.
+ me.inherited[k] = c.keys()[0]
+
+ def expand(me, string, seen = None, path = None):
+ """
+ Expand placeholders in STRING and return the result.
+
+ A placeholder has the form $PROP or ${PROP} (the latter syntax identifies
+ the property name unambiguously), and is replaced by the value of the
+ (inherited) property named PROP. A token $$ is replaced with a single $.
+
+ The SEEN and PATH parameters work the same way as in the `transit'
+ method.
+ """
+
+ if seen is None: seen = {}
+ if path is None: path = []
+
+ ## Prepare stuff for the loop.
+ out = StringIO()
+ left = 0
+ n = len(string)
+
+ ## Pick out placeholders and expand them.
+ while True:
+
+ ## Find a placeholder.
+ dol = string.find('$', left)
+
+ ## None: commit the rest of the string and we're done.
+ if dol < 0:
+ out.write(string[left:])
+ break
+
+ ## Commit the portion before the placeholder.
+ out.write(string[left:dol])
+
+ ## Check for a trailing `$'. After this, we can be sure of at least
+ ## one more character.
+ if dol + 1 >= n:
+ prop = ''
+
+ ## If there's a left brace, find a right brace: the property name is
+ ## between them.
+ elif string[dol + 1] == '{':
+ ace = string.find('}', dol + 2)
+ if ace < 0:
+ raise UserError, \
+ "invalid placeholder (missing `}') in `%s'" % string
+ prop = string[dol + 2:ace]
+ left = ace + 1
+
+ ## If there's a dollar, just commit it and go round again.
+ elif string[dol + 1] == '$':
+ left = dol + 2
+ out.write('$')
+ continue
+
+ ## Otherwise take as many constituent characters as we can.
+ else:
+ left = dol + 1
+ while left < n and (string[left].isalnum() or string[left] in '-_'):
+ left += 1
+ prop = string[dol + 1:left].replace('-', '_')
+
+ ## If we came up empty, report an error.
+ if prop == '':
+ raise UserError, \
+ "invalid placeholder (empty name) in `%s'" % string
+
+ ## Extend the path: we're going to do a recursive expansion.
+ path.append(prop)
+
+ ## Report a cycle if we found one.
+ if prop in seen:
+ raise UserError, 'substitution cycle:\n\t%s' % \
+ (' -> '.join(["`%s'" % p for p in path[seen[prop]:]]))
+
+ ## Look up the raw value.
+ try:
+ value = me.inherited[prop]
+ except KeyError:
+ raise UserError, "unknown property `%s'" % prop
+
+ ## Recursively expand, and unwind the PATH and SEEN stuff.
+ seen[prop] = len(path) - 1
+ out.write(me.expand(value, seen, path))
+ path.pop()
+ del seen[prop]
+
+ ## Done: return the accumulated result.
+ return out.getvalue()
+
+def link(d):
+ """
+ Link together the Sections in D according to their inclusions.
+
+ If a Section S has an `@include' special property, then set S's `includes'
+ slot to be the set of sections named in that property's value. Then
+ compute the inferiors and inherited properties for all of the Sections.
+ """
+
+ ## Capture the global section.
+ g = d['@GLOBAL']
+
+ ## Walk through all of the sections.
+ for sect in d.itervalues():
+
+ ## If this isn't the global section, then add the global section as an
+ ## implicit inclusion.
+ if sect is not g:
+ sect.includes.add(g)
+
+ ## If there are explicit inclusions, then add them to the included set.
+ try:
+ inc = sect['@include']
+ except KeyError:
+ pass
+ else:
+ for s in inc.split():
+ try:
+ sect.includes.add(d[s])
+ except KeyError:
+ raise UserError, \
+ "unknown section `%s' included in `%s'" % (s, sect.name)
+
+ ## Compute the inferiors and inherited properties.
+ for sect in d.itervalues():
+ sect.transit()
+ for sect in d.itervalues():
+ sect.inherit()
+
+###--------------------------------------------------------------------------
+### Parsing input files.
+
+## Names of special properties. All of these begin with an `@' sign.
+SPECIALS = set(['@include'])
+
+def parse(filename, d):
+ """
+ Parse a profile file FILENAME, updating dictionary D.
+
+ Each entry in the dictionary maps a section name to the section's contents;
+ the contents are in turn represented as a dictionary mapping properties to
+ values. Inter-section references, defaults, and so on are not processed
+ here.
+ """
+
+ sect = '@GLOBAL'
+
+ with open(filename) as f:
+ n = 0
+ for line in f:
+ n += 1
+ line = line.strip()
+ if not line or line[0] in ';#':
+ continue
+ if line[0] == '[' and line[-1] == ']':
+ sect = line[1:-1]
+ continue
+
+ ## Parse an assignment.
+ eq = line.find('=')
+ colon = line.find(':')
+ if eq < 0 or 0 <= colon < eq: eq = colon
+ if eq < 0: raise UserError, '%s:%d: no assignment' % (filename, n)
+ name, value = line[:eq].strip(), line[eq + 1:].strip()
+
+ ## Check that the name is well-formed.
+ name = name.replace('-', '_')
+ if not (name and
+ (name in SPECIALS or
+ all(map(lambda ch: ch == '_' or ch.isalnum(), name)))):
+ raise UserError, "%s:%d: bad name `%s'" % (filename, n, name)
+
+ ## Store the assignment.
+ try:
+ d[sect][name] = value
+ except KeyError:
+ s = Section(sect)
+ d[sect] = s
+ s[name] = value
+
+###--------------------------------------------------------------------------
+### Main program.
+
+OP = O.OptionParser(
+ usage = '%prog FILE|DIRECTORY ... SECTION',
+ version = '%%prog, version %s' % VERSION,
+ description = '''\
+Parse the configurations FILE and DIRECTORY contents, and output the named
+SECTION as a sequence of simple assignments.
+''')
+
+def main(args):
+ try:
+
+ ## Check the arguments.
+ opts, args = OP.parse_args(args[1:])
+ if len(args) < 2:
+ OP.error('not enough positional parameters')
+ files = args[:-1]
+ sect = args[-1]
+
+ ## Read in the inputs.
+ d = { '@GLOBAL': Section('@GLOBAL') }
+ for f in files:
+
+ ## It's a directory: pick out the files contained.
+ if OS.path.isdir(f):
+ for sf in sorted(OS.listdir(f)):
+ if not all(map(lambda ch: ch in '_-' or ch.isalnum(), sf)):
+ continue
+ ff = OS.path.join(f, sf)
+ if not OS.path.isfile(ff):
+ continue
+ parse(ff, d)
+
+ ## Not a directory: just try to parse it.
+ else:
+ parse(f, d)
+
+ ## Print the contents.
+ link(d)
+ try:
+ s = d[sect]
+ except KeyError:
+ raise UserError, "unknown section `%s'" % sect
+ for k, v in s.inherited.iteritems():
+ print '%s=%s' % (k, s.expand(v))
+
+ ## Report errors for expected problems.
+ except UserError, e:
+ SYS.stderr.write('%s: %s\n' % (OP.get_prog_name(), e.args[0]))
+ SYS.exit(1)
+ except OSError, e:
+ SYS.stderr.write('%s: %s\n' % (OP.get_prog_name(), e.args[1]))
+ SYS.exit(1)
+ except IOError, e:
+ SYS.stderr.write('%s: %s: %s\n' %
+ (OP.get_prog_name(), e.filename, e.strerror))
+ SYS.exit(1)
+
+if __name__ == '__main__':
+ main(SYS.argv)
+
+###----- That's all, folks --------------------------------------------------
### Configuration variables.
PACKAGE="@PACKAGE@" VERSION="@VERSION@"
-pkgconfdir="@pkgconfdir@" pkglibdir="@pkglibdir@"
bindir="@bindir@"
case ":$PATH:" in *:"$bindir":*) ;; *) PATH=$bindir:$PATH ;; esac
-if [ -f $KEYS/keys.conf ]; then . $KEYS/keys.conf; fi
-: ${random=/dev/random}
+if [ -f $ETC/keys.conf ]; then . $ETC/keys.conf; fi
case "${KEYS_DEBUG+t}" in t) set -x ;; esac
### Cleanup handling.
cleanups=""
-cleanup () { cleanups="$cleanups $1"; }
-trap 'rc=$?; for i in $cleanups; do $i; done; exit $rc' EXIT
-trap 'exit 127' INT TERM
+cleanup () { cleanups=${cleanups+$cleanups }$1; }
+runcleanups () { for i in $cleanups; do $i; done; }
+trap 'rc=$?; runcleanups; exit $rc' EXIT
+trap 'trap "" EXIT; runcleanups; exit 127' INT TERM
###--------------------------------------------------------------------------
### Utility functions.
+reqsafe () {
+ ## Fail unless a safe directory is set.
+
+ err="$quis: (CONFIGURATION ERROR)"
+ case ${SAFE+t} in
+ t) ;;
+ *) echo >&2 "$err: no SAFE directory"; exit 1 ;;
+ esac
+ if [ ! -d "$SAFE" ]; then
+ echo >&2 "$err: SAFE path \`$SAFE' isn't a directory"
+ exit 1
+ fi
+ case "$SAFE" in
+ [!/]* | *[][[:space:]*?]*)
+ echo >&2 "$err: SAFE path \`$SAFE' contains bad characters"
+ exit 1
+ ;;
+ esac
+ ls -ld "$SAFE" | {
+ me=$(id -un)
+ read perm _ user stuff
+ case "$perm:$user" in
+ d???------:"$me") ;;
+ *)
+ echo >&2 "$err: SAFE path \`$SAFE' has bad owner or permissions"
+ exit 1
+ ;;
+ esac
+ }
+}
+
## Temporary directory.
unset tmp
-rmtmp () { cd /; rm -rf $tmp; }
+rmtmp () { case ${tmp+t} in t) cd /; rm -rf $tmp ;; esac; }
+cleanup rmtmp
mktmp () {
- ## Make and return the name of a temporary directory.
+ ## Make a temporary directory and store its name in `tmp'.
- case "${tmp+t}" in t) echo "$tmp"; return ;; esac
- mem=$(userv root claim-mem-dir </dev/null)
- tmp="$mem/keys.tmp.$$"
+ case "${tmp+t}" in t) return ;; esac
+ reqsafe
+ tmp="$SAFE/keys.tmp.$$"
rm -rf "$tmp"
mkdir -m700 "$tmp"
- echo "$tmp"
}
-###--------------------------------------------------------------------------
-### Input validation functions.
+reqtmp () {
+ ## Fail unless a temporary directory is set.
-checknumber () {
- what=$1 thing=$2
- case "$thing" in
- "" | [!1-9]* | *[!0-9]*)
- echo >&2 "$quis: bad $what \`$thing'"
- exit 1
- ;;
+ case ${tmp+t} in
+ t) ;;
+ *) echo >&2 "$quis (INTERNAL): no tmp directory set"; exit 127 ;;
esac
}
-checkword () {
- what=$1 thing=$2
- case "$thing" in
- "" | *[!-0-9a-zA-Z_!%@+=]*)
- echo >&2 "$quis: bad $what: \`$thing'"
- exit 1
- ;;
+parse_keylabel () {
+ key=$1
+ ## Parse the key label string KEY. Set `kdir' to the base path to use for
+ ## the key's storage, and `kowner' to the key owner's name.
+
+ case "$key" in
+ *:*) kowner=${key%%:*} klabel=${key#*:} ;;
+ *) kowner=$USERV_USER klabel=$key ;;
esac
+ checkword "key owner name" "$kowner"
+ checklabel "key" "$klabel"
+ kdir=$KEYS/store/$kowner/$klabel
+ knub=$KEYS/nub/$kowner/$klabel
}
-checklabel () {
- what=$1 thing=$2
+###--------------------------------------------------------------------------
+### Input validation functions.
+
+nl="
+"
+check () {
+ ckwhat=$1 ckpat=$2 thing=$3
+ ## Verify that THING matches the (anchored, basic) regular expression
+ ## CKPAT. Since matching newlines is hard to do portably, also check that
+ ## THING doesn't contain any. If the checks fail, report an error and
+ ## exit.
+
+ validp=t
case "$thing" in
- *[!-0-9a-zA-Z_!%@+=/#]* | *//* | /* | */)
- echo >&2 "$quis: bad $what label \`$thing'"
- exit 1
- ;;
+ *"$nl"*) validp=nil ;;
+ *) if ! expr >/dev/null "$thing" : "$ckpat\$"; then validp=nil; fi ;;
+ esac
+ case $validp in
+ nil) echo >&2 "$quis: bad $ckwhat \`$thing'"; exit 1 ;;
esac
}
+## Regular expressions for validating input.
+R_IDENTCHARS="A-Za-z0-9_"
+R_WORDCHARS="-$R_IDENTCHARS!%@+="
+R_IDENT="[$R_IDENTCHARS][$R_IDENTCHARS]*"
+R_WORD="[$R_WORDCHARS][$R_WORDCHARS]*"
+R_WORDSEQ="[$R_WORDCHARS[:space:]][$R_WORDCHARS[:space:]]*"
+R_NUMERIC='\(\([1-9][0-9]*\)\{0,1\}0\{0,1\}\)'
+R_LABEL="\($R_WORD\(/$R_WORD\)*\)"
+R_LINE=".*"
+
+## Various validation functions.
+checknumber () { check "$1" "$R_NUMERIC" "$2"; }
+checkident () { check "$1" "$R_IDENT" "$2"; }
+checkword () { check "$1" "$R_WORD" "$2"; }
+checklabel () { check "$1 label" "$R_LABEL" "$2"; }
+
###--------------------------------------------------------------------------
-### Crypto operations.
-###
-### We use Seccure for this, but it's interface is Very Annoying.
+### Key storage and properties.
+
+getsysprofile () {
+ profile=$1
+ ## Write the named system PROFILE to standard output.
+
+ $bindir/extract-profile $ETC/profile.d/ "$profile"
+}
+
+setprops () {
+ what=$1 prefix=$2; shift 2
+ ## Set variables based on the NAME=VALUE assignments in the arguments. The
+ ## value for property NAME is stored in the shell variable PREFIX_NAME.
+
+ for assg in "$@"; do
+ goodp=t
+ case "$assg" in
+ *\=*) name=${assg%%=*} value=${assg#*=} ;;
+ *) goodp=nil ;;
+ esac
+ case "$goodp,$name" in t,*[!0-9A-Za-z_]*=*) goodp=nil ;; esac
+ case "$goodp" in
+ nil) echo >&2 "$quis: bad $what assignment \`$assg'"; exit 1 ;;
+ esac
+ eval "$prefix$name=\$value"
+ done
+}
-run_seccure () {
- op=$1; shift
- ## run_seccure OP ARG ...
+checkprops () {
+ whatprop=$1 prefix=$2; shift 2
+ ## Check that property variables are set in accordance with the remaining
+ ## TABLE arguments. Each row of TABLE has the form
##
- ## Run a Seccure program, ensuring that its stderr is reported if it had
- ## anything very interesting to say, but suppressed if it was boring.
+ ## NAME OMIT PAT
+ ##
+ ## A table row is satisfied if there is a variable PREFIXNAME whose value
+ ## matces the (basic) regular expression PAT, or if the variable is unset
+ ## and OMIT is `t'.
+
+ for table in "$@"; do
+ case "$table" in ?*) ;; *) continue ;; esac
+ while read -r name omit pat; do
+ eval foundp=\${$prefix$name+t}
+ case "$foundp,$omit" in
+ ,t) continue ;;
+ ,nil)
+ echo >&2 "$quis: missing $whatprop \`$name' required"
+ exit 1
+ ;;
+ esac
+ eval value=\$$prefix$name
+ check "value for $whatprop \`$name'" "$pat" "$value"
+ done <<EOF
+$table
+EOF
+ done
+}
- ## We need a temporary place for the error output.
- case ${tmp+t} in
- t) ;;
+defprops () {
+ name=$1
+ ## Define a properties table NAME.
+
+ table=$(cat)
+ eval $name=\$table
+}
+
+defprops g_props <<EOF
+type nil $R_IDENT
+recovery t $R_WORDSEQ
+random t $R_WORD
+nubhash t $R_WORD
+nubidhash t $R_WORD
+nubsz t $R_NUMERIC
+EOF
+
+readprops () {
+ file=$1
+ ## Read a profile from a file. This doesn't check the form of the
+ ## filename, so it's not suitable for unchecked input. Properties are set
+ ## using `setprops' with prefix `kprop_'.
+
+ ## Parse the settings from the file.
+ exec 3<"$file"
+ while read line; do
+ case "$line" in "" | \#*) continue ;; esac
+ setprops "property" kprop_ "$line"
+ done <&3
+ exec 3>&-
+ checkprops "property" kprop_ "$g_props"
+
+ ## Fetch the key-type handling library.
+ if [ ! -f $KEYSLIB/ktype.$kprop_type ]; then
+ echo >&2 "$quis: unknown key type \`$kprop_type'"
+ exit 1
+ fi
+ . $KEYSLIB/ktype.$kprop_type
+ checkprops "property" kprop_ "$k_props"
+}
+
+readmeta () {
+ kdir=$1
+ ## Read key metadata from KDIR.
+
+ { read profile; } <"$kdir"/meta
+}
+
+makenub () {
+ ## Generate a key nub in the default way, and write it to standard output.
+ ## The properties `random', `nubsz' and `nubhash' are referred to.
+
+ dd 2>/dev/null \
+ if=/dev/${kprop_random-random} bs=1 count=${kprop_nubsz-512} |
+ openssl dgst -${kprop_nubhash-sha384} -binary |
+ openssl base64
+}
+
+nubid () {
+ ## Compute a hash of the key nub in stdin, and write it to stdout in hex.
+ ## The property `nubidhash' is used.
+
+ { echo "distorted-keys nubid"; cat -; } |
+ openssl dgst -${kprop_nubidhash-sha256}
+}
+
+subst () {
+ what=$1 templ=$2 prefix=$3 pat=$4
+ ## Substitute option values into the template TEMPL. Each occurrence of
+ ## %{VAR} is replaced by the value of the variable PREFIXVAR. Finally, an
+ ## error is reported unless the final value matches the regular expression
+ ## PAT.
+
+ out=""
+ rest=$templ
+ while :; do
+
+ ## If there are no more markers to substitute, then finish.
+ case "$rest" in *"%{"*"}"*) ;; *) out=$out$rest; break ;; esac
+
+ ## Split the template into three parts.
+ left=${rest%%\%\{*} right=${rest#*\%\{}
+ var=${right%%\}*} rest=${right#*\}}
+ case "$var" in
+ *-*) default=${var#*-} var=${var%%-*} defaultp=t ;;
+ *) defaultp=nil ;;
+ esac
+
+ ## Find the variable value.
+ checkident "template variable name" "$var"
+ eval foundp=\${$prefix$var+t}
+ case $foundp,$defaultp in
+ t,*) eval value=\$$prefix$var ;;
+ ,t) value=$default ;;
+ *)
+ echo >&2 "$quis: option \`$var' unset, used in template \`$templ'"
+ exit 1
+ ;;
+ esac
+
+ ## Do the substitution.
+ out=$out$left$value
+ done
+
+ ## Check the final result.
+ check "$what" "$pat" "$out"
+
+ ## Done.
+ echo "$out"
+}
+
+read_profile () {
+ profile=$1
+ ## Read property settings from a profile. The PROFILE name has the form
+ ## [USER:]LABEL. Properties are set using `setprops' with prefix `kprop_'.
+
+ reqtmp
+ case "$profile" in
+ :*)
+ label=${profile#:} uservp=nil
+ ;;
*)
- echo >&2 "$quis (INTERNAL): run_seccure called without tmpdir"
- exit 127
+ user=$USERV_USER label=$profile uservp=t
+ ;;
+ *:*)
+ user=${profile%%:*} label=${profile#*:} uservp=t
+ ;;
+ esac
+ checkword "profile label" "$label"
+
+ ## Fetch the profile settings from the user.
+ reqtmp
+ case $uservp in
+ t)
+ checkword "profile user" "$user"
+ userv "$user" cryptop-profile "$label" >$tmp/profile
+ ;;
+ nil)
+ $bindir/extract-profile $ETC/profile.d/ "$label" >$tmp/profile
;;
esac
- ## Run the program.
- set +e; seccure-$op "$@" 2>$tmp/seccure.out; rc=$?; set -e
- grep -v '^WARNING: Cannot obtain memory lock' $tmp/seccure.out >&2 || :
- return $rc
+ ## Read the file.
+ readprops $tmp/profile
}
-ec_public () {
- private=$1
- ## Write the public key corresponding to PRIVATE to stdout.
+###--------------------------------------------------------------------------
+### General crypto operations.
+
+c_genkey () {
+ profile=$1 kdir=$2 knub=$3 hook=$4; shift 4
+ ## Generate a key, and associate it with the named PROFILE (which is
+ ## assumed already to have been read!); store the main data in KDIR, and
+ ## the nub separately in the file KNUB; run HOOK after generation, passing
+ ## it the working key directory and nub file. Remaining arguments are
+ ## options to the key type.
+
+ ## Set options and check them.
+ setprops "option" kopt_ "$@"
+ checkprops "option" kopt_ "$k_genopts"
+
+ ## Create directory structure and start writing metadata.
+ rm -rf "$kdir.new"
+ mkdir -m755 -p "$kdir.new"
+ case "$knub" in */*) mkdir -m700 -p "${knub%/*}" ;; esac
+ cat >"$kdir.new/meta" <<EOF
+$profile
+EOF
- run_seccure key -q -cp256 -F"$private"
+ ## Generate the key.
+ umask=$(umask); umask 077; >"$knub.new"; umask $umask
+ k_generate "$kdir.new" "$knub.new"
+ $hook "$kdir.new" "$knub.new"
+
+ ## Hash the nub.
+ nubid <"$knub.new" >"$kdir.new/nubid"
+
+ ## Juggle everything into place. Doing this atomically is very difficult,
+ ## and requires more machinery than I can really justify here. If
+ ## something goes wrong halfway, it should always be possible to fix it,
+ ## either by backing out (if $kdir.new still exists) or pressing on
+ ## forwards (if not).
+ rm -rf "$kdir.old"
+ if [ -e "$kdir" ]; then mv "$kdir" "$kdir.old"; fi
+ mv "$kdir.new" "$kdir"
+ mv "$knub.new" "$knub"
+ rm -rf "$kdir.old"
}
-ec_keygen () {
- private=$1 public=$2
- ## Make a new key, write private key to PRIVATE and public key to PUBLIC.
-
- dd if=$random bs=1 count=512 2>/dev/null |
- openssl dgst -sha384 -binary |
- (umask 077 && openssl base64 >"$private")
- ec_public "$private" >"$public"
+c_encrypt () { k_encrypt "$@"; }
+c_decrypt () {
+ if k_decrypt "$@" >$tmp/plain; then cat $tmp/plain
+ else return $?
+ fi
+}
+c_sign () { k_sign "$@"; }
+c_verify () { k_verify "$@"; }
+
+## Stub implementations.
+notsupp () { op=$1; echo >&2 "$quis: operation \`$op' not supported"; }
+k_info () { :; }
+k_encrypt () { notsupp encrypt; }
+k_decrypt () { notsupp decrypt; }
+k_sign () { notsupp sign; }
+k_verify () { notsupp verify; }
+
+prepare () {
+ key=$1 op=$2
+ ## Prepare for a crypto operation OP, using the KEY. This validates the
+ ## key label, reads the profile, and checks the access-control list.
+
+ ## Find the key properties.
+ parse_keylabel "$key"
+ if [ ! -d $kdir ]; then echo >&2 "$quis: unknown key \`$key'"; exit 1; fi
+ readmeta $kdir
+ read_profile "$profile"
+
+ ## Check whether we're allowed to do this thing. This is annoyingly
+ ## fiddly.
+ eval acl=\${kprop_acl_$op-!owner}
+ verdict=forbid
+ while :; do
+
+ ## Remove leading whitespace.
+ while :; do
+ case "$acl" in
+ [[:space:]]*) acl=${acl#?} ;;
+ *) break ;;
+ esac
+ done
+
+ ## If there's nothing left, leave.
+ case "$acl" in ?*) ;; *) break ;; esac
+
+ ## Split off the leading word.
+ case "$acl" in
+ *[[:space:]]*) word=${acl%%[[:space:]]*} acl=${acl#*[[:space:]]} ;;
+ *) word=$acl acl="" ;;
+ esac
+
+ ## See what sense it has if it matches.
+ case "$word" in
+ -*) sense=forbid rest=${word#-} ;;
+ *) sense=allow rest=$word ;;
+ esac
+
+ ## See whether the calling user matches.
+ case "$rest" in
+ !owner) pat=$kowner list=$USERV_USER ;;
+ !*) echo >&2 "$quis: unknown ACL token \`$word'" ;;
+ %*) pat=${rest#%} list="$USERV_GROUP $USERV_GID" ;;
+ *) pat=$rest list="$USERV_USER $USERV_UID" ;;
+ esac
+ matchp=nil
+ for i in $list; do case "$i" in $pat) matchp=t; break ;; esac; done
+ case $matchp in t) verdict=$sense; break ;; esac
+ done
+
+ case $verdict in
+ forbid) echo >&2 "$quis: $op access to key \`$key' forbidden"; exit ;;
+ esac
}
-ec_encrypt () {
- public=$1; shift
- ## Encrypt stuff using the PUBLIC key. Use -i/-o or redirection.
+###--------------------------------------------------------------------------
+### Crypto operations for infrastructure purposes.
+
+c_sysprofile () {
+ profile=$1
+ ## Select the profile in FILE for future crypto operations.
- run_seccure encrypt -q -cp256 -m128 "$@" -- $(cat "$public")
+ unset $(set | sed -n '/^kprop_/s/=.*$//p')
+ reqtmp
+ getsysprofile "$profile" >$tmp/profile
+ readprops $tmp/profile
}
-ec_decrypt () {
- private=$1; shift
- ## Decrypt stuff using the PRIVATE key. Use -i/-o or redirection.
+c_gensyskey () {
+ profile=$1 kdir=$2 knub=$3; shift 3
+ ## Generate a system key using PROFILE; store the data in KDIR and the nub
+ ## in KNUB. Remaining arguments are options.
- run_seccure decrypt -q -cp256 -m128 -F"$private" "$@"
+ c_sysprofile "$profile"
+ c_genkey "$profile" "$kdir" "$knub" : "$@"
}
-ec_sign () {
- private=$1; shift
- ## Sign stuff using the PRIVATE key. Use -i/-o or redirection.
+c_sysprepare () {
+ kdir=$1
+ readmeta "$kdir"
+ c_sysprofile "$profile"
+}
- run_seccure sign -q -cp256 -F"$private" "$@"
+c_sysop () {
+ op=$1 kdir=$2; shift 1
+ c_sysprepare "$kdir"
+ c_$op "$@"
}
-ec_verify () {
- public=$1 signature=$2; shift
- ## Verify a SIGNATURE using the PUBLIC key; use -i or redirection for the
- ## input.
+c_sysencrypt () { c_sysop encrypt "$1" /dev/null; }
+c_sysdecrypt () { c_sysop decrypt "$1" "$2"; }
+c_syssign () { c_sysop sign "$1" "$2"; }
+c_sysverify () { c_sysop verify "$1" /dev/null; }
+
+###--------------------------------------------------------------------------
+### Recovery operations.
+
+stash () {
+ recov=$1 label=$2
+ ## Stash a copy of stdin encrypted under the recovery key RECOV, with a
+ ## given LABEL.
+ checkword "recovery key label" "$recov"
+ checklabel "secret" "$label"
+
+ rdir=$KEYS/recov/$recov/current
+ if [ ! -d $rdir/store ]; then
+ echo >&2 "$quis: unknown recovery key \`$recov'"
+ exit 1
+ fi
+ case $label in */*) mkdir -m755 -p $rdir/${label%/*} ;; esac
+ (c_sysencrypt $rdir/store >$rdir/$label.new)
+ mv $rdir/$label.new $rdir/$label.recov
+}
- run_seccure verify -q -cp256 "$@" -- $(cat "$public") "$signature"
+recover () {
+ recov=$1 label=$2
+ ## Recover a stashed secret, protected by RECOV and stored as LABEL, and
+ ## write it to stdout.
+ checkword "recovery key label" "$recov"
+ checklabel "secret" "$label"
+
+ rdir=$KEYS/recov/$recov/current
+ if [ ! -f $rdir/$label.recov ]; then
+ echo >&2 "$quis: no blob for \`$label' under recovery key \`$recov'"
+ exit 1
+ fi
+ reqsafe
+ nub=$SAFE/keys.reveal/$recov.current/nub
+ if [ ! -f $nub ]; then
+ echo >&2 "$quis: current recovery key \`$recov' not revealed"
+ exit 1;
+ fi
+ mktmp
+ c_sysdecrypt $rdir/store $nub <$rdir/$label.recov
}
###--------------------------------------------------------------------------
### Help text.
-dohelp () {
- case "$KEYS_HELP" in t) ;; *) return ;; esac
- help; exit
+defhelp () {
+ read umsg
+ usage="usage: $quis${umsg+ }$umsg"
+ help=$(cat)
+ case "$KEYS_HELP" in t) help; exit ;; esac
}
-defhelp () { read umsg; usage="usage: $quis${umsg+ }$umsg"; help=$(cat); }
help () { showhelp; }
showhelp () {
cat <<EOF
EOF
}
+usage_err () { echo >&2 "$usage"; exit 1; }
+
+###--------------------------------------------------------------------------
+### Subcommand handling.
+
+version () {
+ echo "$PACKAGE version $VERSION"
+}
+
+cmd_help () {
+ rc=0
+ version
+ case $# in
+ 0)
+ cat <<EOF
+
+$usage
+
+Options:
+ -h Show this help text.
+ -v Show the program version number.
+
+Commands installed:
+EOF
+ cd "$KEYSLIB"
+ for i in $prefix.*; do
+ if [ ! -x "$i" ]; then continue; fi
+ sed -n "/<<HELP/{n;s/^/ ${i#$prefix.} /;p;q;}" "$i"
+ done
+ ;;
+ *)
+ for i in "$@"; do
+ echo
+ if [ ! -x "$KEYSLIB/$prefix.$i" ]; then
+ echo >&2 "$quis: unrecognized command \`$i'"
+ rc=1
+ continue
+ elif ! KEYS_HELP=t "$KEYSLIB/$prefix.$i"; then
+ rc=1
+ fi
+ done
+ ;;
+ esac
+ return $rc
+}
+
+dispatch () {
+ case $# in 0) echo >&2 "$usage"; exit 1 ;; esac
+ cmd=$1; shift
+ case "$cmd" in help) cmd_help "$@"; exit ;; esac
+ if [ ! -x "$KEYSLIB/$prefix.$cmd" ]; then
+ echo >&2 "$quis: unrecognized command \`$cmd'"
+ exit 1
+ fi
+
+ unset KEYS_HELP
+ exec "$KEYSLIB/$prefix.$cmd" "$@"
+}
+
###----- That's all, folks --------------------------------------------------
--- /dev/null
+#! /bin/sh
+###
+### Unreveal a revealed recovery key
+###
+### (c) 2011 Mark Wooding
+###
+
+###----- Licensing notice ---------------------------------------------------
+###
+### This file is part of the distorted.org.uk key management suite.
+###
+### distorted-keys 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 2 of the License, or
+### (at your option) any later version.
+###
+### distorted-keys 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 distorted-keys; if not, write to the Free Software Foundation,
+### Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+set -e
+case "${KEYSLIB+t}" in t) ;; *) echo >&2 "$0: KEYSLIB unset"; exit 1 ;; esac
+. "$KEYSLIB"/keyfunc.sh
+
+defhelp <<HELP
+RECOV
+Remove the revealed nub of the recovery key RECOV.
+HELP
+
+case $# in 1) ;; *) echo >&2 "$usage"; exit 1 ;; esac
+recov=$1
+checklabel "recovery key" "$recov"
+case "$recov" in
+ */*) ;;
+ *) recov=$recov/current ;;
+esac
+
+reqsafe
+tag=$(echo $recov | tr / .)
+if [ ! -d $SAFE/keys.reveal/$tag ]; then
+ echo >&2 "$quis: recovery key \`$recov' is not revealed"
+ exit 1
+fi
+rm -rf $SAFE/keys.reveal/$tag
+
+###----- That's all, folks --------------------------------------------------
### Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
set -e
-
-quis=${0##*/}
-PACKAGE=@PACKAGE@
-VERSION=@VERSION@
-
-: ${KEYS=@pkgconfdir@}
+: ${ETC=@pkgconfdir@}
+: ${KEYS=@pkgstatedir@}
: ${KEYSLIB=@pkglibdir@}
-export KEYS KEYSLIB
+export ETC KEYS KEYSLIB
-###--------------------------------------------------------------------------
-### Help.
+. "$KEYSLIB"/keyfunc.sh
usage="usage: $quis COMMAND [ARGUMENTS ...]"
+prefix=keys
-version () {
- echo "$PACKAGE version $VERSION"
-}
-
-help () {
- rc=0
- version
- case $# in
- 0)
- cat <<EOF
-
-$usage
-
-Options:
- -h Show this help text.
- -v Show the program version number.
-
-Commands installed:
-EOF
- cd "$KEYSLIB"
- for i in *; do
- case "$i" in *.*) continue ;; esac
- if [ ! -x "$i" ]; then continue; fi
- sed -n "/<<HELP/{n;s/^/ $i /;p;q;}" "$i"
- done
- ;;
- *)
- for i in "$@"; do
- echo
- if [ ! -x "$KEYSLIB"/"$i" ]; then
- echo >&2 "$quis: unrecognized command \`$i'"
- rc=1
- continue
- elif ! KEYS_HELP=t "$KEYSLIB"/"$i"; then
- rc=1
- fi
- done
- ;;
- esac
- return $rc
-}
-
-###--------------------------------------------------------------------------
-### Command dispatch.
-
+## Parse options.
while getopts "hv" opt; do
case "$opt" in
- h) help; exit ;;
+ h) cmd_help; exit ;;
v) version; exit ;;
- *) echo >&2 "$usage"; exit 1 ;;
+ *) usage_err ;;
esac
done
-shift $((OPTIND - 1))
-
-case $# in 0) echo >&2 "$usage"; exit 1 ;; esac
-cmd=$1; shift
-case "$cmd" in help) help "$@"; exit ;; esac
-if [ ! -x "$KEYSLIB"/"$cmd" ]; then
- echo >&2 "$quis: unrecognized command \`$cmd'"
- exit 1
-fi
+shift $(( $OPTIND - 1 ))
-unset KEYS_HELP
-exec "$KEYSLIB"/"$cmd" "$@"
+## Dispatch.
+dispatch "$@"
###----- That's all, folks --------------------------------------------------
The public keys are found in $KEYS/keeper/KEEPER/I.pub;
private keys are read from KEEPER/I in the current directory.
HELP
-dohelp
## Parse the command line.
-case $# in 0) echo >&2 "$usage"; exit 1 ;; esac
+case $# in 0) usage_err ;; esac
keeper=$1; shift
checkword "keeper set label" "$keeper"
read n hunoz <$KEYS/keeper/$keeper/meta
;;
esac
case "$low" in ?*) ;; *) low=0 ;; esac
- case "$high" in ?*) ;; *) high=$((n - 1)) ;; esac
+ case "$high" in ?*) ;; *) high=$(( $n - 1 )) ;; esac
if [ 0 -gt $low -o $low -gt $high -o $high -ge $n ]; then
echo >&2 "$quis: invalid index range \`$range'"
exit 1
fi
- i=$((low + 0))
+ i=$(( $low + 0 ))
while [ $i -le $high ]; do
case $want in *:"$i":*) ;; *) want=$want$i: ;; esac
- i=$((i + 1))
+ i=$(( $i + 1 ))
done
done
## Start working on the output file. This will contain deep secrets, so
## don't leave stuff easily readable.
-tmp=$(mktmp); cleanup rmtmp
+mktmp
umask 077
exec 3>$tmp/$keeper.tex
cat >&3 <<'EOF'
\card{$i}{$secret}
EOF
esac
- i=$((i + 1))
+ i=$(( $i + 1 ))
done
## Wrap up and build the document.
. "$KEYSLIB"/keyfunc.sh
defhelp <<HELP
-KEEPER N
+[-p PROFILE] KEEPER N [OPTION=VALUE ...]
Create a new set of keeper keys.
-The private keys are stored in KEEPER/I for each 0 <= I < N in the current
+The key nubs are stored in KEEPER/I for each 0 <= I < N in the current
directory; presumably you'll do something sensible with them. A new
directory $KEYS/keeper/KEEPER is created (it is an error if it already
-exists), containing the public keys I.pub and some metadata meta.
+exists), containing the key store directories and some metadata meta.
HELP
-dohelp
## Parse the command line.
-case $# in 2) ;; *) echo >&2 "$usage"; exit 1 ;; esac
-keeper=$1 n=$2
+profile=${keeper_profile-keeper}
+while getopts "p:" opt; do
+ case "$opt" in
+ p) profile=$OPTARG ;;
+ *) usage_err ;;
+ esac
+done
+shift $(( $OPTIND - 1 ))
+case $# in 0 | 1) usage_err ;; esac
+keeper=$1 n=$2; shift 2
checkword "keeper set label" "$keeper"
checknumber "set size" "$n"
+checkword "profile label" "$profile"
## Preflight checking.
if [ -e $KEYS/keeper/$keeper ]; then
fi
## Generate the private keys, one per file, and compute the public keys.
-tmp=$(mktmp); cleanup rmtmp
+mktmp
rm -rf $keeper.new
mkdir -m700 $keeper.new
mkdir -p -m755 $KEYS/keeper/$keeper.new
echo $n >$KEYS/keeper/$keeper.new/meta
i=0
while [ $i -lt $n ]; do
- ec_keygen $keeper.new/$i $KEYS/keeper/$keeper.new/$i.pub
- i=$(( i + 1 ))
+ c_gensyskey $profile \
+ $KEYS/keeper/$keeper.new/$i \
+ $keeper.new/$i \
+ keeper="$keeper" seq="$i" tot="$n" "$@"
+ i=$(( $i + 1 ))
done
mv $keeper.new $keeper
mv $KEYS/keeper/$keeper.new $KEYS/keeper/$keeper
. "$KEYSLIB"/keyfunc.sh
defhelp <<HELP
-RECOV [KEEPER:K ...]
+[-p PROFILE] RECOV [KEEPER:K ...] [-- OPTION=VALUE ...]
Generate a new recovery secret, and split it among the available keepers.
The new secret will be called RECOV. For each KEEPER set, the private key
If there is not a recovery key called RECOV then at least one keeper set must
be specified.
HELP
-dohelp
## Parse the command line.
-case $# in 0) echo >&2 "$usage"; exit 1 ;; esac
+profile=${recov_profile-recovery}
+while getopts "p:" opt; do
+ case "$opt" in
+ p) profile=$OPTARG ;;
+ *) usage_err ;;
+ esac
+done
+shift $(( $OPTIND - 1 ))
+case $# in 0) usage_err ;; esac
recov=$1; shift
checkword "recovery key label" "$recov"
+checkword "profile label" "$profile"
+nkeep=0
for k in "$@"; do
case "$k" in
+ --) break ;;
*:*) ;;
*) echo >&2 "$quis: bad keeper spec \`$k'"; exit 1 ;;
esac
- keeper=${k%:*} t=${k#*:}
+ keeper=${k%:*} t=${k#*:} nkeep=$(( $nkeep + 1 ))
checkword "keeper set label" "$keeper"
checknumber "keeper quorum" "$t"
done
## Establish the keeper parameters.
rdir=$KEYS/recov/$recov
if [ ! -d $rdir ]; then mkdir -m755 -p $rdir; fi
-case $# in
+case $nkeep in
0)
kparam=$rdir/keepers
;;
*)
for k in "$@"; do
+ case "$k" in --) break ;; esac
keeper=${k%:*} t=${k#*:}
echo $keeper $t
done >$rdir/keepers.new
if [ ! -f $kparam ]; then echo >&2 "$quis: no keepers specified"; exit 1; fi
## Make the new key and issue the shares.
-tmp=$(mktmp); cleanup rmtmp
+mktmp
rm -rf $rdir/new
mkdir -m755 $rdir/new
cd $tmp
-ec_keygen secret $rdir/new/pub
+while :; do case "$#,$1" in
+ 0,) break ;;
+ *,*,*) ;;
+ *,--) break ;;
+ esac
+ shift
+done
+c_gensyskey $profile $rdir/new/store secret recov="$recov"
while read keeper k; do
read n hunoz <$KEYS/keeper/$keeper/meta
shamir issue $k/$n secret | {
echo "$param" >$rdir/new/$keeper.param
i=0
while read share; do
- echo $share |
- ec_encrypt $KEYS/keeper/$keeper/$i.pub -o$rdir/new/$keeper.$i.share
- i=$(( i + 1 ))
+ c_sysencrypt $KEYS/keeper/$keeper/$i >$rdir/new/$keeper.$i.share <<EOF
+$share
+EOF
+ i=$(( $i + 1 ))
done
}
done <$kparam
seq=0
else
seq=$(readlink $rdir/current)
- mem=$(userv root claim-mem-dir </dev/null)
- reveal=$mem/keys.reveal/$recov.current/secret
+ reqsafe
+ reveal=$SAFE/keys.reveal/$recov.current/secret
if [ ! -f $reveal ]; then
echo >&2 "$quis: current $recov key not revealed"
exit 1
find $rdir/current/ -type f -name '*.recov' -print | while read name; do
name=${name#$rdir/current/}
case "$name" in */*) mkdir -p -m755 $rdir/new/${name%/*} ;; esac
- ec_decrypt $reveal -i$rdir/current/$name |
- ec_encrypt $rdir/new/pub -o$rdir/new/$name
+ c_sysdecrypt $rdir/current/store $reveal <$rdir/current/$name >tmp
+ c_sysencrypt $rdir/new/store <tmp >$rdir/new/$name
+ rm tmp
done
- rm -r $mem/keys.reveal/$recov.current
+ rm -r $SAFE/keys.reveal/$recov.current
fi
## Tidy up and commit. Repointing the symlink is grim because, according to
## symlink to a directory -- and there's no way of turning this behaviour
## off. The subterfuge here is due to Colin Watson.
cd $rdir
-while [ -d $seq ]; do seq=$(( seq + 1 )); done
+while [ -d $seq ]; do seq=$(( $seq + 1 )); done
case $kparam in *.new) mv keepers.new keepers ;; esac
rm -f next
ln -s $seq next
The recovery key must be revealed. The secret is written to stdout.
HELP
-dohelp
## Parse the command line.
-case $# in 2) ;; *) echo >&2 "$usage"; exit 1 ;; esac
+case $# in 2) ;; *) usage_err ;; esac
recov=$1 label=$2
checklabel "recovery key label" "$recov"
checklabel "secret" "$label"
## Do the recovery.
-blob=$KEYS/recov/$recov/current/$label.recov
-if [ ! -f $blob ]; then
- echo >&2 "$quis: no recovery blob for secret \`$label'"
- exit 1
-fi
-mem=$(userv root claim-mem-dir </dev/null)
-reveal=$mem/keys.reveal/$recov.current/secret
-if [ ! -f $reveal ]; then
- echo >&2 "$quis: current $recov key not revealed"
- exit 1
-fi
-tmp=$(mktmp); cleanup rmtmp
-ec_decrypt $reveal -i$blob
+recover $recov $label
###----- That's all, folks --------------------------------------------------
. "$KEYSLIB"/keyfunc.sh
defhelp <<HELP
-RECOV KEEPER [KEY]
+RECOV KEEPER [NUB]
Reveal a share of a recovery key distributed among keepers.
If enough shares have been revealed, reconstruct the recovery private key.
-The key is read from KEY, or stdin if KEY is omitted or \`-'.
+The keeper nub is read from NUB, or stdin if NUB is omitted or \`-'.
HELP
-dohelp
## Parse the command line.
case $# in
2) if [ -t 0 ]; then echo >&2 "$quis: stdin is a terminal"; exit 1; fi ;;
3) ;;
- *) echo >&2 "$usage"; exit 1 ;;
+ *) usage_err ;;
esac
recov=$1 keeper=$2; shift 2
checklabel "recovery key" "$recov"
esac
checkword "keeper set label" "$keeper"
+## Check that this is a sensible thing to do.
+if [ ! -f $KEYS/keeper/$keeper/meta ]; then
+ echo >&2 "$quis: unknown keeper set \`$keeper'"
+ exit 1
+fi
+if [ ! -d $KEYS/recov/$recov ]; then
+ echo >&2 "$quis: unknown recovery key \`$recov'"
+ exit 1
+fi
+if [ ! -f $KEYS/recov/$recov/$keeper.param ]; then
+ echo >&2 "$quis: recovery key \`$recov' not kept by keeper set \`$keeper'"
+ exit 1
+fi
+
## Grab the key, because we'll need to read it several times.
-tmp=$(mktmp); cleanup rmtmp
-secret=$(cat -- "$@")
-pub=$(ec_public /dev/stdin <<EOF
-$secret
-EOF
-)
+mktmp
+cat -- "$@" >$tmp/secret
## Read the threshold from the recovery metadata.
read param <$KEYS/recov/$recov/$keeper.param
read n hunoz <$KEYS/keeper/$keeper/meta
i=0
foundp=nil
-: "$pub"
while [ $i -lt $n ]; do
- read cand <$KEYS/keeper/$keeper/$i.pub
- : "$cand"
- case "$pub" in "$cand") foundp=t; break ;; esac
- i=$(( i + 1 ))
+ c_sysprepare $KEYS/keeper/$keeper/$i
+ nubbin=$(nubid <$tmp/secret)
+ nubid=$(cat $KEYS/keeper/$keeper/$i/nubid)
+ case "$nubbin" in "$nubid") foundp=t; break ;; esac
+ i=$(( $i + 1 ))
done
case $foundp in
- nil) echo >&2 "$quis: key doesn't match keeper \`$keeper'"; exit 1 ;;
+ nil) echo >&2 "$quis: nub doesn't match keeper \`$keeper'"; exit 1 ;;
esac
## Establish the recovery staging area. See whether we've done enough
## already.
-mem=$(userv root claim-mem-dir </dev/null)
+reqsafe
tag=$(echo $recov | tr / .)
-mkdir -p -m700 $mem/keys.reveal
-reveal=$mem/keys.reveal/$tag
+mkdir -p -m700 $SAFE/keys.reveal
+reveal=$SAFE/keys.reveal/$tag
if [ ! -d $reveal ]; then mkdir -m700 $reveal; fi
cd $reveal
-if [ -f secret ]; then
- echo >&2 "$quis: secret $recov already revealed"
- exit 1
-fi
-if [ -f $keeper.$i ]; then
- echo >&2 "$quis: share $i already revealed"
+if [ -f nub ]; then
+ echo >&2 "$quis: recovery key \`$recov' already revealed"
exit 1
fi
## Decrypt the share.
umask 077
-ec_decrypt /dev/stdin \
- -i$KEYS/recov/$recov/$keeper.$i.share \
- -o$keeper.$i.new <<EOF
-$secret
-EOF
-mv $keeper.$i.new $keeper.$i
+if [ -f $keeper.$i.share ]; then
+ echo >&2 "$quis: share $i already revealed"
+else
+ c_sysdecrypt $KEYS/keeper/$keeper/$i $tmp/secret \
+ <$KEYS/recov/$recov/$keeper.$i.share \
+ >$keeper.$i.new
+ mv $keeper.$i.new $keeper.$i.share
+fi
## See if there's enough for a recovery.
n=0
-for j in $keeper.*; do if [ -f "$j" ]; then n=$(( n + 1 )); fi; done
+for j in $keeper.*.share; do if [ -f "$j" ]; then n=$(( $n + 1 )); fi; done
if [ $n -lt $t ]; then
- echo >&2 "$quis: share $i revealed; $(( t - n )) more required"
+ echo >&2 "$quis: share $i revealed; $(( $t - $n )) more required"
else
- cat $KEYS/recov/$recov/$keeper.param $keeper.* >$keeper.shares
- shamir recover <$keeper.shares >secret.new
- pubx=$(ec_public secret.new)
- puby=$(cat $KEYS/recov/$recov/pub)
- case "$pubx" in
- "$puby") ;;
+ cat $KEYS/recov/$recov/$keeper.param $keeper.*.share >$keeper.shares
+ shamir recover <$keeper.shares >nub.new
+ c_sysprepare $KEYS/recov/$recov/store
+ nubbin=$(nubid <nub.new)
+ nubid=$(cat $KEYS/recov/$recov/store/nubid)
+ case "$nubbin" in
+ "$nubid") ;;
*)
- echo >&2 "quis: recovered secret key doesn't match public key"
+ echo >&2 "$quis: recovered nub doesn't match stored hash"
exit 1
;;
esac
- mv secret.new secret
- echo >&2 "$quis: secret $recov revealed"
+ mv nub.new nub
+ rm -f $keeper.*
+ echo >&2 "$quis: recovery key \`$recov' revealed"
fi
###----- That's all, folks --------------------------------------------------
command. The secret is read from SECRET, or stdin if SECRET is omitted or
\`-'.
HELP
-dohelp
## Parse the command line.
case $# in
2) if [ -t 0 ]; then echo >&2 "$quis: stdin is a terminal"; exit 1; fi ;;
3) ;;
- *) echo >&2 "$usage"; exit 1 ;;
+ *) usage_err ;;
esac
recov=$1 label=$2; shift 2
checkword "recovery key label" "$recov"
checklabel "secret" "$label"
## Do the thing.
-tmp=$(mktmp); cleanup rmtmp
+mktmp
cat -- "$@" >$tmp/secret
-cd $KEYS/recov/$recov/current
-case $label in */*) mkdir -m755 -p ${label%/*} ;; esac
-ec_encrypt pub -i$tmp/secret -o$label.new
-mv $label.new $label.recov
+stash $recov $label <$tmp/secret
###----- That's all, folks --------------------------------------------------
--- /dev/null
+### -*-sh-*-
+###
+### Key type for GNU Privacy Guard
+###
+### (c) 2011 Mark Wooding
+###
+
+###----- Licensing notice ---------------------------------------------------
+###
+### This file is part of the distorted.org.uk key management suite.
+###
+### distorted-keys 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 2 of the License, or
+### (at your option) any later version.
+###
+### distorted-keys 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 distorted-keys; if not, write to the Free Software Foundation,
+### Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+run_gnupg () {
+ base=$1; shift
+ ## Run GnuPG with some standard options.
+
+ gpg --homedir="$base" --no-permission-warning -q --batch \
+ --always-trust \
+ "$@"
+}
+
+defprops k_props <<EOF
+main_type t $R_WORD
+main_length t $R_NUMERIC
+sub_type t $R_WORD
+sub_length t $R_NUMERIC
+s2k_cipher t $R_WORD
+s2k_digest t $R_WORD
+cipher_prefs t $R_WORDSEQ
+digest_prefs t $R_WORDSEQ
+compress_prefs t $R_WORDSEQ
+realname t $R_LINE
+comment t $R_LINE
+email t $R_LINE
+EOF
+
+: ${kprop_main_type=RSA} ${kprop_main_length=3072}
+: ${kprop_sub_type=ELG-E} ${kprop_sub_length=3072}
+: ${kprop_cipher_prefs=AES256 AES TWOFISH 3DES BLOWFISH CAST5}
+: ${kprop_digest_prefs=SHA256 SHA1 RIPEMD160}
+: ${kprop_compress_prefs=ZLIB ZIP}
+
+: ${kprop_realname=%{realname\}} ${kprop_email=%{email\}}
+: ${kprop_comment=%{comment-nil\}}
+
+k_generate () {
+ base=$1 nub=$2
+
+ makenub >"$nub"
+ prefs="$kprop_cipher_prefs $kprop_digest_prefs $kprop_compress_prefs"
+
+ case ${kprop_s2k_cipher+t} in
+ t) ;;
+ *) set -- $kprop_cipher_prefs; kprop_s2k_cipher=$1 ;;
+ esac
+ case ${kprop_s2k_digest+t} in
+ t) ;;
+ *) set -- $kprop_digest_prefs; kprop_s2k_digest=$1 ;;
+ esac
+
+ cat >"$base/gpg.conf" <<EOF
+### GnuPG configuration
+
+## Annoying copyright notice and other tedious warnings.
+no-greeting
+expert
+always-trust
+
+## Algorithm selection
+s2k-cipher-algo $kprop_s2k_cipher
+s2k-digest-algo $kprop_s2k_digest
+personal-cipher-preferences $kprop_cipher_prefs
+personal-digest-preferences $kprop_digest_prefs
+personal-compress-preferences $kprop_compress_prefs
+default-preference-list $prefs
+EOF
+
+ { cat <<EOF
+Key-Type: $kprop_main_type
+Key-Length: $kprop_main_length
+Passphrase: $(cat "$nub")
+EOF
+ case ${kprop_sub_type-nil} in
+ nil) ;;
+ *) cat <<EOF
+Subkey-Type: $kprop_sub_type
+Subkey-Length: $kprop_sub_length
+EOF
+ esac
+ real=$(subst "\`realname' value" "$kprop_realname" kopt_ "$R_LINE")
+ email=$(subst "\`email' value" "$kprop_email" kopt_ "$R_LINE")
+ cat <<EOF
+Name-Real: $real
+Name-Email: $email
+EOF
+ comment=$(subst "\`comment' value" "$kprop_comment" kopt_ "$R_LINE")
+ case "$comment" in
+ ?*) cat <<EOF
+Name-Comment: $comment
+EOF
+ ;;
+ esac
+ } | run_gnupg "$base" --gen-key
+
+ ## Commit the new key.
+ run_gnupg "$base" --fingerprint --with-colons | \
+ grep '^fpr:' | cut -d: -f10 >"$base/fpr"
+ run_gnupg "$base" --export --armor --output="$base/pub"
+}
+
+k_encrypt () {
+ base=$1
+ run_gnupg "$base" --encrypt --armor --recipient=$(cat "$base/fpr")
+}
+
+k_decrypt () {
+ base=$1 nub=$2
+ run_gnupg "$base" --passphrase-file "$nub" --decrypt
+}
+
+k_sign () {
+ base=$1 nub=$2
+ run_gnupg "$base" --passphrase-file "$nub" --detach-sign --armor
+}
+
+k_verify () {
+ base=$1 sig=$3
+ echo "$sig" >$tmp/sig
+ if run_gnupg "$base" --verify $tmp/sig - >/dev/null 2>$tmp/err
+ then :; else
+ rc=$?
+ cat >&2 $tmp/err
+ return $rc
+ fi
+}
+
+###----- That's all, folks --------------------------------------------------
--- /dev/null
+### -*-sh-*-
+###
+### Key type for B. Poettering's `Seccure' suite
+###
+### (c) 2011 Mark Wooding
+###
+
+###----- Licensing notice ---------------------------------------------------
+###
+### This file is part of the distorted.org.uk key management suite.
+###
+### distorted-keys 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 2 of the License, or
+### (at your option) any later version.
+###
+### distorted-keys 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 distorted-keys; if not, write to the Free Software Foundation,
+### Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+###--------------------------------------------------------------------------
+### Utility functions.
+
+run_seccure () {
+ op=$1; shift
+ ## run_seccure OP ARG ...
+ ##
+ ## Run a Seccure program, ensuring that its stderr is reported if it had
+ ## anything very interesting to say, but suppressed if it was boring.
+
+ set +e; seccure-$op "$@" 2>$tmp/seccure.out; rc=$?; set -e
+ grep -v '^WARNING: Cannot obtain memory lock' $tmp/seccure.out >&2 || :
+ return $rc
+}
+
+###--------------------------------------------------------------------------
+### Key type definition.
+
+defprops k_props <<EOF
+curve t $R_LABEL
+tagsz t $R_NUMERIC
+EOF
+
+: ${kprop_curve=p256}
+: ${kprop_tagsz=128}
+
+k_public () {
+ nub=$2
+ run_seccure key -q -c$kprop_curve -F"$nub"
+}
+
+k_generate () {
+ base=$1 nub=$2
+ makenub >"$nub"
+ k_public "$base" "$nub" >"$base/pub"
+}
+
+k_check () {
+ base=$1 nub=$2
+ this=$(k_public "$base" "$nub")
+ orig=$(cat "$base/pub")
+ case "$orig" in "$this") return 0 ;; *) return 1 ;; esac
+}
+
+k_encrypt () {
+ base=$1
+ run_seccure encrypt -q -c$kprop_curve -m$kprop_tagsz -- $(cat "$base/pub")
+}
+
+k_decrypt () {
+ nub=$2
+ if ! run_seccure decrypt -q -c$kprop_curve -m$kprop_tagsz -F"$nub"; then
+ echo >&2 "$quis: decryption failed"
+ return 1
+ fi
+}
+
+k_sign () {
+ nub=$2
+ run_seccure sign -q -c$kprop_curve -F"$nub" -s/dev/stdout
+}
+
+k_verify () {
+ base=$1 sig=$3
+ if run_seccure verify -q -c$kprop_curve -- \
+ $(cat "$base/pub") "$sig"
+ then :; else
+ rc=$?
+ echo >&2 "$quis: signature verification failed"
+ return $rc
+ fi
+}
+
+###----- That's all, folks --------------------------------------------------