+ ic.operation("checking master key fingerprint") { _ =>
+ val foundfp = keyFingerprint(masterfile, mastertag,
+ conf("fingerprint-hash"));
+ val wantfp = conf("hk-master");
+ if (!fingerprintsEqual(wantfp, foundfp)) {
+ throw new KeyConfigException(
+ s"master key #$seq has wrong fingerprint: " +
+ s"expected $wantfp but found $foundfp");
+ }
+ }
+
+ /* Check the archive signature. */
+ ic.operation("verifying archive signature") { or =>
+ runCommand("catsign", "-k", masterfile.getPath, "verify", "-aqC",
+ "-k", mastertag, "-t", conf("sig-fresh"),
+ sigfile.getPath, tarfile.getPath);
+ }
+
+ /* Confirm that the configuration in the new archive is sane. */
+ checkConfigSanity(unpkdir/"tripe-keys.conf", ic);
+
+ /* Build the public keyring. */
+ ic.job(new SimpleModel("counting public keys", -1)) { jr =>
+
+ /* Delete the accumulated keyring. */
+ val pubkeys = unpkdir/"keyring.pub";
+ pubkeys.remove_!();
+
+ /* Figure out which files we need to hack. */
+ var kv = ArrayBuffer[File]();
+ reposdir.foreachFile { file => file.getName match {
+ case RX_PUBKEY(peer) if file.isreg_! => kv += file;
+ case _ => ok;
+ } }
+ kv = kv.sorted;
+ val m = new DetailedModel("collecting public keys", kv.length);
+ var i: Long = 0;
+
+ /* Work through the key files. */
+ for (k <- kv) {
+ m.detail = k.getName;
+ if (!i) jr.change(m, i);
+ else jr.step(i);
+ runCommand("key", "-k", pubkeys.getPath, "merge", k.getPath);
+ i += 1;
+ }
+
+ /* Clean up finally. */
+ (unpkdir/"keyring.pub.old").remove_!();
+ }
+
+ /* Now we just have to juggle the files about. */
+ ic.operation("committing new configuration") { _ =>
+ unpkdir.rename_!(newdir);
+ livedir.rename_!(olddir);
+ newdir.rename_!(livedir);
+ }
+
+ /* All done. */
+ invalidate(); // should move to `Live'
+ cleanup(ic);
+ }
+
+ def generateKey(tag: String, label: String, ic: Eyecandy) {
+ checkIdent(tag);
+ if (label.exists { _ == '/' })
+ throw new IllegalArgumentException(s"invalid label string `$label'");
+ if ((keysdir/label).isdir_!)
+ throw new IllegalArgumentException(s"key `$label' already exists");
+
+ cleanup(ic);
+ checkState(Live);
+ val conf = config;
+ clearTmp();
+
+ val now = datefmt synchronized { datefmt.format(new Date) };
+ val kr = tmpdir/"keyring";
+ val pub = tmpdir/s"peer-$tag.pub";
+ val param = livereposdir/"param";
+
+ keysdir.mkdirNew_!();
+
+ ic.operation("fetching key-generation parameters") { _ =>
+ runCommand("key", "-k", kr.getPath, "merge", param.getPath);
+ }
+ ic.operation("generating new key") { _ =>
+ runCommand("key", "-k", kr.getPath, "add",
+ "-a", conf("kx-genalg"), "-p", "param",
+ "-e", conf("kx-expire"), "-t", tag, "tripe");
+ }
+ ic.operation("extracting public key") { _ =>
+ runCommand("key", "-k", kr.getPath, "extract",
+ "-f", "-secret", pub.getPath, tag);
+ }
+ ic.operation("writing metadata") { _ =>
+ tmpdir/"meta" withWriter { w =>
+ w.write(s"tag = $tag\n");
+ w.write(s"time = $now\n");
+ }
+ }
+ ic.operation("installing new key") { _ =>
+ tmpdir.rename_!(keysdir/label);
+ }