chiark / gitweb /
Multiple key types, key profiles, and user key storage.
[distorted-keys] / keys.new-recov
diff --git a/keys.new-recov b/keys.new-recov
new file mode 100755 (executable)
index 0000000..c5d7fce
--- /dev/null
@@ -0,0 +1,153 @@
+#! /bin/sh
+###
+### Generate a new recovery key and split it among keepers
+###
+### (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
+[-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
+wil be split into shares, and encrypted with the KEEPER's public keys; any K
+of these shares can be used to reveal the original key.
+
+If there is already a recovery key called RECOV then it will be rolled over.
+The previous key must already be revealed.  If KEEPERs are listed, these
+replace the existing keeper sets; otherwise the same keepers as before are
+used.
+
+If there is not a recovery key called RECOV then at least one keeper set must
+be specified.
+HELP
+
+## Parse the command line.
+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#*:} 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 $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
+    kparam=$rdir/keepers.new
+    ;;
+esac
+if [ ! -f $kparam ]; then echo >&2 "$quis: no keepers specified"; exit 1; fi
+
+## Make the new key and issue the shares.
+mktmp
+rm -rf $rdir/new
+mkdir -m755 $rdir/new
+cd $tmp
+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 | {
+    read param
+    echo "$param" >$rdir/new/$keeper.param
+    i=0
+    while read share; do
+      c_sysencrypt $KEYS/keeper/$keeper/$i >$rdir/new/$keeper.$i.share <<EOF
+$share
+EOF
+      i=$(( $i + 1 ))
+    done
+  }
+done <$kparam
+rm -f secret
+
+## If there's an existing instance of this key, transfer the recovery blobs.
+if [ ! -d $rdir/current ]; then
+  seq=0
+else
+  seq=$(readlink $rdir/current)
+  reqsafe
+  reveal=$SAFE/keys.reveal/$recov.current/secret
+  if [ ! -f $reveal ]; then
+    echo >&2 "$quis: current $recov key not revealed"
+    exit 1
+  fi
+
+  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
+    c_sysdecrypt $rdir/current/store $reveal <$rdir/current/$name >tmp
+    c_sysencrypt $rdir/new/store <tmp >$rdir/new/$name
+    rm tmp
+  done
+  rm -r $SAFE/keys.reveal/$recov.current
+fi
+
+## Tidy up and commit.  Repointing the symlink is grim because, according to
+## POSIX rules, `mv foo bar' should rename `foo' to `bar/foo' is `bar' is a
+## 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
+case $kparam in *.new) mv keepers.new keepers ;; esac
+rm -f next
+ln -s $seq next
+mv new $seq
+mkdir hack; mv next hack/current; mv hack/current .; rmdir hack
+
+###----- That's all, folks --------------------------------------------------