+ ## 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 "$label" $ETC/profile.d/ >$tmp/profile
+ ;;
+ esac
+
+ ## Read the file.
+ readprops $tmp/profile
+}
+
+###--------------------------------------------------------------------------
+### 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
+
+ ## 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"
+}
+
+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. If OP
+ ## is `-' then allow the operation unconditionally.
+
+ ## Find the key properties.
+ parse_keylabel "$key"
+ if [ ! -d $kdir ]; then echo >&2 "$quis: unknown key \`$key'"; exit 1; fi
+ readmeta $kdir
+ read_profile $kowner "$profile"
+
+ ## Check whether we're allowed to do this thing. This is annoyingly
+ ## fiddly.
+ case $op in -) return ;; esac
+ 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 1 ;;
+ esac