upstream: Add experimental support for PQC XMSS keys (Extended

Hash-Based Signatures) The code is not compiled in by default (see WITH_XMSS
in Makefile.inc) Joint work with stefan-lukas_gazdag at genua.eu See
https://tools.ietf.org/html/draft-irtf-cfrg-xmss-hash-based-signatures-12 ok
djm@

OpenBSD-Commit-ID: ef3eccb96762a5d6f135d7daeef608df7776a7ac
diff --git a/ssh-add.c b/ssh-add.c
index 2afd483..adcc459 100644
--- a/ssh-add.c
+++ b/ssh-add.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ssh-add.c,v 1.134 2017/08/29 09:42:29 dlg Exp $ */
+/* $OpenBSD: ssh-add.c,v 1.135 2018/02/23 15:58:37 markus Exp $ */
 /*
  * Author: Tatu Ylonen <ylo@cs.hut.fi>
  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -78,6 +78,7 @@
 #endif
 #endif /* WITH_OPENSSL */
 	_PATH_SSH_CLIENT_ID_ED25519,
+	_PATH_SSH_CLIENT_ID_XMSS,
 	NULL
 };
 
@@ -89,6 +90,10 @@
 /* User has to confirm key use */
 static int confirm = 0;
 
+/* Maximum number of signatures (XMSS) */
+static u_int maxsign = 0;
+static u_int minleft = 0;
+
 /* we keep a cache of one passphrase */
 static char *pass = NULL;
 static void
@@ -190,7 +195,10 @@
 	char *comment = NULL;
 	char msg[1024], *certpath = NULL;
 	int r, fd, ret = -1;
+	size_t i;
+	u_int32_t left;
 	struct sshbuf *keyblob;
+	struct ssh_identitylist *idlist;
 
 	if (strcmp(filename, "-") == 0) {
 		fd = STDIN_FILENO;
@@ -268,8 +276,40 @@
 		comment = xstrdup(filename);
 	sshbuf_free(keyblob);
 
+	/* For XMSS */
+	if ((r = sshkey_set_filename(private, filename)) != 0) {
+		fprintf(stderr, "Could not add filename to private key: %s (%s)\n",
+		    filename, comment);
+		goto out;
+	}
+	if (maxsign && minleft &&
+	    (r = ssh_fetch_identitylist(agent_fd, &idlist)) == 0) {
+		for (i = 0; i < idlist->nkeys; i++) {
+			if (!sshkey_equal_public(idlist->keys[i], private))
+				continue;
+			left = sshkey_signatures_left(idlist->keys[i]);
+			if (left < minleft) {
+				fprintf(stderr,
+				    "Only %d signatures left.\n", left);
+				break;
+			}
+			fprintf(stderr, "Skipping update: ");
+			if (left == minleft) {
+				fprintf(stderr,
+				   "required signatures left (%d).\n", left);
+			} else {
+				fprintf(stderr,
+				   "more signatures left (%d) than"
+				    " required (%d).\n", left, minleft);
+			}
+			ssh_free_identitylist(idlist);
+			goto out;
+		}
+		ssh_free_identitylist(idlist);
+	}
+
 	if ((r = ssh_add_identity_constrained(agent_fd, private, comment,
-	    lifetime, confirm)) == 0) {
+	    lifetime, confirm, maxsign)) == 0) {
 		fprintf(stderr, "Identity added: %s (%s)\n", filename, comment);
 		ret = 0;
 		if (lifetime != 0)
@@ -317,7 +357,7 @@
 	sshkey_free(cert);
 
 	if ((r = ssh_add_identity_constrained(agent_fd, private, comment,
-	    lifetime, confirm)) != 0) {
+	    lifetime, confirm, maxsign)) != 0) {
 		error("Certificate %s (%s) add failed: %s", certpath,
 		    private->cert->key_id, ssh_err(r));
 		goto out;
@@ -368,6 +408,7 @@
 	char *fp;
 	int r;
 	struct ssh_identitylist *idlist;
+	u_int32_t left;
 	size_t i;
 
 	if ((r = ssh_fetch_identitylist(agent_fd, &idlist)) != 0) {
@@ -392,7 +433,12 @@
 				    ssh_err(r));
 				continue;
 			}
-			fprintf(stdout, " %s\n", idlist->comments[i]);
+			fprintf(stdout, " %s", idlist->comments[i]);
+			left = sshkey_signatures_left(idlist->keys[i]);
+			if (left > 0)
+				fprintf(stdout,
+				    " [signatures left %d]", left);
+			fprintf(stdout, "\n");
 		}
 	}
 	ssh_free_identitylist(idlist);
@@ -454,6 +500,8 @@
 	fprintf(stderr, "  -L          List public key parameters of all identities.\n");
 	fprintf(stderr, "  -k          Load only keys and not certificates.\n");
 	fprintf(stderr, "  -c          Require confirmation to sign using identities\n");
+	fprintf(stderr, "  -m minleft  Maxsign is only changed if less than minleft are left (for XMSS)\n");
+	fprintf(stderr, "  -M maxsign  Maximum number of signatures allowed (for XMSS)\n");
 	fprintf(stderr, "  -t life     Set lifetime (in seconds) when adding identities.\n");
 	fprintf(stderr, "  -d          Delete identity.\n");
 	fprintf(stderr, "  -D          Delete all identities.\n");
@@ -500,7 +548,7 @@
 		exit(2);
 	}
 
-	while ((ch = getopt(argc, argv, "klLcdDxXE:e:qs:t:")) != -1) {
+	while ((ch = getopt(argc, argv, "klLcdDxXE:e:M:m:qs:t:")) != -1) {
 		switch (ch) {
 		case 'E':
 			fingerprint_hash = ssh_digest_alg_by_name(optarg);
@@ -525,6 +573,22 @@
 		case 'c':
 			confirm = 1;
 			break;
+		case 'm':
+			minleft = (int)strtonum(optarg, 1, UINT_MAX, NULL);
+			if (minleft == 0) {
+				usage();
+				ret = 1;
+				goto done;
+			}
+			break;
+		case 'M':
+			maxsign = (int)strtonum(optarg, 1, UINT_MAX, NULL);
+			if (maxsign == 0) {
+				usage();
+				ret = 1;
+				goto done;
+			}
+			break;
 		case 'd':
 			deleting = 1;
 			break;