diff -urbBN --exclude=.svn ssh.orig/auth2-zk.c ssh/auth2-zk.c
--- ssh.orig/auth2-zk.c	1970-01-01 01:00:00.000000000 +0100
+++ ssh/auth2-zk.c	2005-11-29 17:45:17.000000000 +0100
@@ -0,0 +1,252 @@
+#include "includes.h"
+
+#include "xmalloc.h"
+#include "packet.h"
+#include "log.h"
+#include "key.h"
+#include "auth.h"
+#include "monitor_wrap.h"
+#include "servconf.h"
+#include "ssh2.h"
+#include "uidswap.h"
+#include "misc.h"
+#include "auth-options.h"
+#include <openssl/bn.h>
+#include "ohta-okamoto.h"
+
+/* import */
+extern ServerOptions options;
+
+oo_public_key public;	/* public key of client, put into authentication context! */
+BIGNUM **challenge;	/* challenge for client */
+BIGNUM *X;          	/* witness from client */
+
+/* return 1 if user allows given key */
+static int
+user_key_allowed2(struct passwd *pw, Key *key, char *file)
+{
+	int success = 0;
+	char line[4000];
+	int found_key = 0;
+	FILE *f;
+	u_long linenum = 0;
+	struct stat st;
+	Key *found;
+	char *fp;
+
+	file = authorized_keys_file(pw);
+
+	/* Temporarily use the user's uid. */
+	temporarily_use_uid(pw);
+
+	debug("((ZK)) trying public key file %s", file);
+
+	/* Fail quietly if file does not exist */
+	if (stat(file, &st) < 0) {
+		debug2("((ZK)) %s does not exist", file);
+		/* Restore the privileged uid. */
+		restore_uid();
+		return 0;
+	}
+
+	/* Open the file containing the authorized keys. */
+	f = fopen(file, "r");
+	if (!f) {
+		/* Restore the privileged uid. */
+		restore_uid();
+		return 0;
+	}
+
+	found_key = 0;
+	found = key_new(key->type);
+
+	while (read_keyfile_line(f, file, line, sizeof(line), &linenum) != -1) {
+		char *cp, *key_options = NULL;
+
+		/* Skip leading whitespace, empty and comment lines. */
+		for (cp = line; *cp == ' ' || *cp == '\t'; cp++)
+			;
+		if (!*cp || *cp == '\n' || *cp == '#')
+			continue;
+
+		if (key_read(found, &cp) != 1) {
+		/* no key?  check if there are options for this key */
+			int quoted = 0;
+			debug2("((ZK)) %s: check options: '%s'", __func__, cp);
+			key_options = cp;
+			for (; *cp && (quoted || (*cp != ' ' && *cp != '\t')); cp++) {
+				if (*cp == '\\' && cp[1] == '"')
+					cp++;	/* Skip both */
+				else if (*cp == '"')
+					quoted = !quoted;
+			}
+			/* Skip remaining whitespace. */
+			for (; *cp == ' ' || *cp == '\t'; cp++)
+				;
+			if (key_read(found, &cp) != 1) {
+				debug2("((ZK)) %s: advance: '%s'", __func__, cp);
+				/* still no key?  advance to next line*/
+				continue;
+			}
+			continue;
+		}
+		if (key_equal(found, key)  &&
+		    auth_parse_options(pw, key_options, file, linenum) == 1) {
+			found_key = 1;
+			debug("((ZK)) %s: matching key found: file %s, line %lu",
+			    __func__, file, linenum);
+			fp = key_fingerprint(found, SSH_FP_MD5, SSH_FP_HEX);
+			verbose("((ZK)) %s: Found matching %s key: %s", __func__,
+			    key_type(found), fp);
+			xfree(fp);
+			success = 1;
+			break;
+		}
+	}
+	restore_uid();
+	fclose(f);
+	key_free(found);
+	if (!found_key)
+		debug2("((ZK)) %s: key not found", __func__);
+
+	return success;
+}
+
+static int
+userauth_zk(Authctxt *authctxt)
+{
+	int authenticated = 0;	/* FALSE */
+	u_int round;
+	Key *received_key = key_new(KEY_OO_ZK);
+	int i;		/* counter for loops */
+
+	/* read values sent by client */
+	round = packet_get_int();
+	debug2("((ZK)) Round %d",round);
+
+ 	if (round % 2 == 0)
+ 	{
+		X = BN_new();
+		packet_get_bignum2(X);
+		if (round == 0) 
+		{
+			public.exp = BN_new();
+ 			packet_get_bignum2(public.exp);
+ 			public.n = BN_new();
+ 			packet_get_bignum2(public.n);
+ 			public.num = packet_get_int();
+			public.I    = (BIGNUM**)malloc(sizeof(BIGNUM*) * public.num);
+			for (i=0; i<public.num; ++i)
+			{
+				public.I[i] = BN_new();
+				packet_get_bignum2(public.I[i]);
+			}
+			received_key->oozk->public = &public;
+
+			/* dump public key */
+ 			debug("((ZK)) Received public key for user %s:", authctxt->user);
+ 						
+			/* check if public key is found in authorized keys file */
+			if (user_key_allowed2(authctxt->pw, received_key, options.authorized_keys_file2))
+				debug("((ZK)) Received key for user %s is trusted", authctxt->user);
+			else
+			{
+				debug("((ZK)) Received key for user %s is NOT trusted", authctxt->user);
+				debug("((ZK)) Round %d: Could not find matching public key. Aborting", round);
+				authctxt->postponed = 0;
+				authenticated = 0;
+				/* cleanup, free memory holding public key */
+				free(public.I);
+				BN_free(public.exp);
+				BN_free(public.n);
+
+				/* If there's no matching key, let's bail out */
+				return authenticated;
+			}
+		}
+
+		packet_check_eom();
+	
+		debug2("((ZK)) Received witness");
+				
+		/* check witness for correctness and send next challenge if ok, otherwise cancel */
+	
+		/* V: Issue a challenge */
+		challenge = OO_challenge(&public);
+		if (challenge == NULL)
+			fatal("((ZK)) Challenge could not be created.");
+	
+		/* dump challenge */
+		debug2("((ZK)) Sending challenge for user %s", authctxt->user);
+ 	
+		/* send challenge to client */
+		packet_start(SSH2_MSG_USERAUTH_ZK_OK);
+		packet_put_int(round);
+		for (i=0; i<public.num; ++i)
+			packet_put_bignum2(challenge[i]);
+		packet_send();
+		packet_write_wait();
+
+		/* 8 rounds; only in last round will be decided if user is authenticated successfully */
+		authctxt->postponed = 1;
+	}
+
+	if (round % 2 == 1)
+	{
+		BIGNUM *response = BN_new();
+
+		/*read response from client */
+		packet_get_bignum2(response);
+
+		/* dump response received from client */
+		debug2("((ZK)) Response received");
+	 		
+		if (OO_verify_response(challenge, response, X, &public)) {
+			debug("((ZK)) Round %d: Success", round);
+			if (round < 7)
+			{
+				debug("((ZK)) Starting next round");
+				packet_start(SSH2_MSG_USERAUTH_ZK_NEXT_ROUND);
+				packet_put_int(round);
+				packet_send();
+				packet_write_wait();
+				authctxt->postponed = 1;
+			}
+			else
+			{
+				debug("((ZK)) Authentication successfull, logging in...");
+				authenticated = 1;
+				authctxt->postponed = 0;
+
+				/* cleanup, free memory holding public key */
+				free(public.I);
+ 				BN_free(public.exp);
+ 				BN_free(public.n);
+			}
+		} 
+		else 
+		{
+			debug("((ZK)) Round %d: Failure. Aborting", round);
+			authctxt->postponed = 0;
+			authenticated = 0;
+			/* cleanup, free memory holding public key */
+			free(public.I);
+			BN_free(public.exp);
+			BN_free(public.n);
+		}
+
+		/* cleanup */
+		BN_free(X);
+		BN_free(response);
+		free(challenge);
+		
+	}
+
+	return authenticated;
+}
+
+Authmethod method_zk = {
+	"zk@cms.ac",
+	userauth_zk,
+	&options.zk_authentication
+};
diff -urbBN --exclude=.svn ssh.orig/auth2.c ssh/auth2.c
--- ssh.orig/auth2.c	2004-07-28 11:40:29.000000000 +0200
+++ ssh/auth2.c	2005-11-29 17:45:17.000000000 +0100
@@ -55,6 +55,7 @@
 #ifdef GSSAPI
 extern Authmethod method_gssapi;
 #endif
+extern Authmethod method_zk;		
 
 Authmethod *authmethods[] = {
 	&method_none,
@@ -65,6 +66,7 @@
 	&method_passwd,
 	&method_kbdint,
 	&method_hostbased,
+	&method_zk,			
 	NULL
 };
 
diff -urbBN --exclude=.svn ssh.orig/authfile.c ssh/authfile.c
--- ssh.orig/authfile.c	2004-12-11 02:48:56.000000000 +0100
+++ ssh/authfile.c	2005-11-29 17:45:17.000000000 +0100
@@ -202,6 +202,104 @@
 	return success;
 }
 
+static int
+key_save_private_oo_zk(Key *key, const char *filename, const char *passphrase,
+    const char *comment)
+{
+	Buffer buffer, encrypted;
+	u_char buf[100], *cp;
+	int fd, i, cipher_num;
+	CipherContext ciphercontext;
+	Cipher *cipher;
+	u_int32_t rnd;
+
+	/*
+	 * If the passphrase is empty, use SSH_CIPHER_NONE to ease converting
+	 * to another cipher; otherwise use SSH_AUTHFILE_CIPHER.
+	 */
+	cipher_num = (strcmp(passphrase, "") == 0) ?
+	    SSH_CIPHER_NONE : SSH_AUTHFILE_CIPHER;
+	if ((cipher = cipher_by_number(cipher_num)) == NULL)
+		fatal("save_private_key_oo_zk: bad cipher");
+
+	/* This buffer is used to built the secret part of the private key. */
+	buffer_init(&buffer);
+
+	/* Put checkbytes for checking passphrase validity. */
+	rnd = arc4random();
+	buf[0] = rnd & 0xff;
+	buf[1] = (rnd >> 8) & 0xff;
+	buf[2] = buf[0];
+	buf[3] = buf[1];
+	buffer_append(&buffer, buf, 4);
+
+	/*
+	 * Store the private key (n, exp and num will not be stored because they
+	 * will be stored in plain text, and storing them also in encrypted
+	 * format would just give known plaintext).
+	 */
+	for (i=0; i<key->oozk->private->num; i++)
+		buffer_put_bignum2(&buffer, key->oozk->private->S[i]);
+
+	/* Pad the part to be encrypted until its size is a multiple of 8. */
+	while (buffer_len(&buffer) % 8 != 0)
+		buffer_put_char(&buffer, 0);
+
+	/* This buffer will be used to contain the data in the file. */
+	buffer_init(&encrypted);
+
+	/* First store keyfile id string. */
+	for (i = 0; authfile_id_string[i]; i++)
+		buffer_put_char(&encrypted, authfile_id_string[i]);
+	buffer_put_char(&encrypted, 0);
+
+	/* Store cipher type. */
+	buffer_put_char(&encrypted, cipher_num);
+	buffer_put_int(&encrypted, 0);	/* For future extension */
+
+	/* Store public key.  This will be in plain text. */
+	buffer_put_int(&encrypted, key->oozk->public->num);
+	buffer_put_bignum2(&encrypted, key->oozk->public->exp);
+	buffer_put_bignum2(&encrypted, key->oozk->public->n);
+	for (i=0; i<key->oozk->public->num; i++)
+		buffer_put_bignum2(&encrypted, key->oozk->public->I[i]);
+	buffer_put_cstring(&encrypted, comment);
+
+	/* Allocate space for the private part of the key in the buffer. */
+	cp = buffer_append_space(&encrypted, buffer_len(&buffer));
+
+	cipher_set_key_string(&ciphercontext, cipher, passphrase,
+	    CIPHER_ENCRYPT);
+	cipher_crypt(&ciphercontext, cp,
+	    buffer_ptr(&buffer), buffer_len(&buffer));
+	cipher_cleanup(&ciphercontext);
+	memset(&ciphercontext, 0, sizeof(ciphercontext));
+
+	/* Destroy temporary data. */
+	memset(buf, 0, sizeof(buf));
+	buffer_free(&buffer);
+
+	fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0600);
+	if (fd < 0) {
+		error("open %s failed: %s.", filename, strerror(errno));
+		buffer_free(&encrypted);
+		return 0;
+	}
+	if (write(fd, buffer_ptr(&encrypted), buffer_len(&encrypted)) !=
+	    buffer_len(&encrypted)) {
+		error("write to key file %s failed: %s", filename,
+		    strerror(errno));
+		buffer_free(&encrypted);
+		close(fd);
+		unlink(filename);
+		return 0;
+	}
+	close(fd);
+	buffer_free(&encrypted);
+	return 1;
+}
+
+
 int
 key_save_private(Key *key, const char *filename, const char *passphrase,
     const char *comment)
@@ -216,6 +314,10 @@
 		return key_save_private_pem(key, filename, passphrase,
 		    comment);
 		break;
+	case KEY_OO_ZK:
+		return key_save_private_oo_zk(key, filename, passphrase,
+		    comment);
+		break;
 	default:
 		break;
 	}
@@ -449,6 +551,158 @@
 	return NULL;
 }
 
+/*
+ * Loads the zk private key from the file.  Returns 0 if an error is encountered
+ * (file does not exist or is not readable, or passphrase is bad). This
+ * initializes the private key.
+ * Assumes we are called under uid of the owner of the file.
+ */
+
+static Key *
+key_load_private_oo_zk(int fd, const char *filename, const char *passphrase,
+    char **commentp)
+{
+	int i, j, check1, check2, cipher_type;
+	size_t len;
+	Buffer buffer, decrypted;
+	u_char *cp;
+	CipherContext ciphercontext;
+	Cipher *cipher;
+	Key *prv = NULL;
+	struct stat st;
+
+	if (fstat(fd, &st) < 0) {
+		debug2("((ZK)) fstat for key file %.200s failed: %.100s",
+		      filename, strerror(errno));
+		close(fd);
+		return NULL;
+	}
+	if (st.st_size > 1*1024*1024) {
+		debug2("((ZK)) key file %.200s too large", filename);
+		close(fd);
+		return (NULL);
+	}
+	len = (size_t)st.st_size;		/* truncated */
+
+	buffer_init(&buffer);
+	cp = buffer_append_space(&buffer, len);
+
+	if (read(fd, cp, (size_t) len) != (size_t) len) {
+		debug3("((ZK)) Read from key file %.200s failed: %.100s", filename,
+		      strerror(errno));
+		buffer_free(&buffer);
+		close(fd);
+		return NULL;
+	}
+
+	/* Check that it is at least big enough to contain the ID string. */
+	if (len < sizeof(authfile_id_string)) {
+		debug3("((ZK)) Not a OO_ZK key file %.200s.", filename);
+		buffer_free(&buffer);
+		close(fd);
+		return NULL;
+	}
+	/*
+	* Make sure it begins with the id string.  Consume the id string
+	* from the buffer.
+	*/
+	for (i = 0; i < sizeof(authfile_id_string); i++)
+		if (buffer_get_char(&buffer) != authfile_id_string[i]) {
+			debug3("((ZK)) Not a RSA1 key file %.200s.", filename);
+			buffer_free(&buffer);
+			close(fd);
+			return NULL;
+		}
+
+	/* Read cipher type. */
+	cipher_type = buffer_get_char(&buffer);
+	(void) buffer_get_int(&buffer);	/* Reserved data. */
+
+	/* Read the public key from the buffer. */
+	prv = key_new_private(KEY_OO_ZK);
+	
+	prv->oozk->public->num = buffer_get_int(&buffer);
+	prv->oozk->public->exp = BN_new();
+	prv->oozk->public->n = BN_new();
+	prv->oozk->public->I = (BIGNUM**)malloc(sizeof(BIGNUM*) * prv->oozk->public->num);
+	buffer_get_bignum2(&buffer, prv->oozk->public->exp);
+	buffer_get_bignum2(&buffer, prv->oozk->public->n);
+	
+	/* allocate memory for private key data structure */
+	prv->oozk->private->num = prv->oozk->public->num;
+	prv->oozk->private->exp = BN_new();
+	prv->oozk->private->n = BN_new();
+	prv->oozk->private->S = (BIGNUM**)malloc(sizeof(BIGNUM*) * prv->oozk->private->num);
+	
+	BN_copy(prv->oozk->private->n, prv->oozk->public->n);
+	BN_copy(prv->oozk->private->exp, prv->oozk->public->exp);
+	/* end of private key initialization */
+	
+	for (j=0;j<prv->oozk->public->num;j++) {
+		prv->oozk->public->I[j]=BN_new();
+		prv->oozk->private->S[j]=BN_new();
+		buffer_get_bignum2(&buffer, prv->oozk->public->I[j]);
+	}
+	debug2("((ZK)) %s: Finished loading public key", __func__);
+	
+	if (commentp)
+		*commentp = buffer_get_string(&buffer, NULL);
+	else
+		xfree(buffer_get_string(&buffer, NULL));
+
+	/* Check that it is a supported cipher. */
+	cipher = cipher_by_number(cipher_type);
+	if (cipher == NULL) {
+		debug2("((ZK)) Unsupported cipher %d used in key file %.200s.",
+			cipher_type, filename);
+		buffer_free(&buffer);
+		goto fail;
+	}
+	/* Initialize space for decrypted data. */
+	buffer_init(&decrypted);
+	cp = buffer_append_space(&decrypted, buffer_len(&buffer));
+
+	/* Rest of the buffer is encrypted.  Decrypt it using the passphrase. */
+	cipher_set_key_string(&ciphercontext, cipher, passphrase,
+				CIPHER_DECRYPT);
+	cipher_crypt(&ciphercontext, cp,
+			buffer_ptr(&buffer), buffer_len(&buffer));
+	cipher_cleanup(&ciphercontext);
+	memset(&ciphercontext, 0, sizeof(ciphercontext));
+	buffer_free(&buffer);
+
+	debug3("((ZK)) %s: Checking if decryption is correct", __func__);
+	check1 = buffer_get_char(&decrypted);
+	check2 = buffer_get_char(&decrypted);
+	if (check1 != buffer_get_char(&decrypted) ||
+				check2 != buffer_get_char(&decrypted)) {
+		if (strcmp(passphrase, "") == 0)
+			debug("((ZK)) Bad passphrase supplied for key file %.200s.",
+				filename);
+		/* Bad passphrase. */	
+		buffer_free(&decrypted);
+		goto fail;
+	}
+	/* Read the rest of the private key. */
+	for (j=0;j<prv->oozk->private->num;j++) {
+		buffer_get_bignum2(&decrypted, prv->oozk->private->S[j]);
+	}
+	
+	buffer_free(&decrypted);
+
+	close(fd);
+	return prv;
+
+fail:
+	if (commentp) {
+		xfree(*commentp);
+	}
+	close(fd);
+	key_free(prv);
+	return NULL;
+}
+
+
 Key *
 key_load_private_pem(int fd, int type, const char *passphrase,
     char **commentp)
@@ -550,6 +804,10 @@
 		    commentp);
 		/* closes fd */
 		break;
+	case KEY_OO_ZK:
+		return key_load_private_oo_zk(fd, filename, passphrase,
+		commentp);
+		break;
 	case KEY_DSA:
 	case KEY_RSA:
 	case KEY_UNSPEC:
@@ -631,6 +889,51 @@
 	return 0;
 }
 
+static int
+key_try_load_public_oo_zk(Key *key, const char *filename, char **commentp)
+{
+	int i, fd, cipher_type;
+	size_t len;
+	Buffer buffer;
+	u_char *cp;
+	struct stat st;
+
+	if (stat(filename, &st) < 0) {
+		error("((ZK)) %s: fstat for key file %.200s failed: %.100s",
+		      __func__, filename, strerror(errno));
+		return 0;
+	}
+	if (st.st_size > 1*1024*1024) {
+		error("((ZK)) %s: key file %.200s too large", __func__, filename);
+		close(fd);
+		return (0);
+	}
+	len = (size_t)st.st_size;		/* truncated */
+
+	buffer_init(&buffer);
+	cp = buffer_append_space(&buffer, len);
+
+	/* Read cipher type. */
+	cipher_type = buffer_get_char(&buffer);
+	(void) buffer_get_int(&buffer);	/* Reserved data. */
+
+	/* Read the keylic key from the buffer. */
+	key->oozk->public->num = buffer_get_int(&buffer);
+	key->oozk->public->exp = BN_new();
+	key->oozk->public->n = BN_new();
+	key->oozk->public->I = (BIGNUM**)malloc(sizeof(BIGNUM*) * key->oozk->public->num);
+	buffer_get_bignum2(&buffer, key->oozk->public->exp);
+	buffer_get_bignum2(&buffer, key->oozk->public->n);
+	
+	for (i=0;i<key->oozk->public->num;i++) {
+		key->oozk->public->I[i]=BN_new();
+		buffer_get_bignum2(&buffer, key->oozk->public->I[i]);
+	}
+	debug2("((ZK)) %s: Finished loading public key", __func__);
+	
+	return 1;
+}
+
 /* load public key from ssh v1 private or any pubkey file */
 Key *
 key_load_public(const char *filename, char **commentp)
@@ -638,6 +941,15 @@
 	Key *pub;
 	char file[MAXPATHLEN];
 
+	/* try oo-zk public key */
+	pub = key_new(KEY_OO_ZK);
+	if (key_try_load_public_oo_zk(pub, filename, commentp) == 1)
+	{
+		debug2("((ZK)) %s: Returning KEY_OO_ZK", __func__);
+		return pub;
+	}
+	key_free(pub);
+
 	/* try rsa1 private key */
 	pub = key_load_public_type(KEY_RSA1, filename, commentp);
 	if (pub != NULL)
diff -urbBN --exclude=.svn ssh.orig/key.c ssh/key.c
--- ssh.orig/key.c	2004-10-30 01:57:05.000000000 +0200
+++ ssh/key.c	2005-11-29 17:45:17.000000000 +0100
@@ -55,6 +55,7 @@
 	k->flags = 0;
 	k->dsa = NULL;
 	k->rsa = NULL;
+	k->oozk = NULL;
 	switch (k->type) {
 	case KEY_RSA1:
 	case KEY_RSA:
@@ -79,6 +80,25 @@
 			fatal("key_new: BN_new failed");
 		k->dsa = dsa;
 		break;
+	case KEY_OO_ZK:
+		k->oozk = (OOZK *)malloc(sizeof(OOZK));
+		if (k->oozk == NULL)
+			fatal("((ZK)) key_new: Could not allocate memory for OOZK");
+		k->oozk->private = (oo_private_key *)malloc(sizeof(oo_private_key));
+		if (k->oozk->private == NULL)
+			fatal("((ZK)) key_new: Could not allocate memory for OOZK private");
+		k->oozk->private->S = NULL;
+		k->oozk->private->num = 0;
+		k->oozk->private->exp = NULL;
+		k->oozk->private->n = NULL;
+		k->oozk->public = (oo_public_key *)malloc(sizeof(oo_public_key));
+		if (k->oozk->public == NULL)
+			fatal("((ZK)) key_new: Could not allocate memory for OOZK public");
+		k->oozk->public->I = NULL;
+		k->oozk->public->num = 0;
+		k->oozk->public->exp = NULL;
+		k->oozk->public->n = NULL;
+		break;
 	case KEY_UNSPEC:
 		break;
 	default:
@@ -112,6 +132,25 @@
 		if ((k->dsa->priv_key = BN_new()) == NULL)
 			fatal("key_new_private: BN_new failed");
 		break;
+	case KEY_OO_ZK:
+		k->oozk = (OOZK *)malloc(sizeof(OOZK));
+		if (k->oozk == NULL)
+			fatal("((ZK)) key_new_private: Could not allocate memory for OOZK");
+		k->oozk->private = (oo_private_key *)malloc(sizeof(oo_private_key));
+		if (k->oozk->private == NULL)
+			fatal("((ZK)) key_new_private: Could not allocate memory for OOZK private");
+		k->oozk->private->S = NULL;
+		k->oozk->private->num = 0;
+		k->oozk->private->exp = NULL;
+		k->oozk->private->n = NULL;
+		k->oozk->public = (oo_public_key *)malloc(sizeof(oo_public_key));
+		if (k->oozk->public == NULL)
+			fatal("((ZK)) key_new_private: Could not allocate memory for OOZK public");
+		k->oozk->public->I = NULL;
+		k->oozk->public->num = 0;
+		k->oozk->public->exp = NULL;
+		k->oozk->public->n = NULL;
+		break;
 	case KEY_UNSPEC:
 		break;
 	default:
@@ -123,6 +162,7 @@
 void
 key_free(Key *k)
 {
+	int i;
 	switch (k->type) {
 	case KEY_RSA1:
 	case KEY_RSA:
@@ -135,6 +175,28 @@
 			DSA_free(k->dsa);
 		k->dsa = NULL;
 		break;
+	case KEY_OO_ZK:
+		if (k->oozk != NULL) {
+			if (k->oozk->public != NULL) { 
+				if (k->oozk->public->I != NULL)
+					debug3("((ZK)) %s: there are %u identities\n", __func__, k->oozk->public->num);
+					for (i=0;i<k->oozk->public->num;i++)
+						if (k->oozk->public->I[i] != NULL)
+							BN_free(k->oozk->public->I[i]);
+				xfree(k->oozk->public);
+			}
+			if (k->oozk->private != NULL) {
+				if (k->oozk->private->S != NULL)
+					for (i=0;i<k->oozk->private->num;i++)
+						if (k->oozk->private->S[i] != NULL)
+							BN_free(k->oozk->private->S[i]);
+				xfree(k->oozk->private);
+			}
+
+			xfree(k->oozk);
+			k->oozk = NULL;
+		}
+		break;
 	case KEY_UNSPEC:
 		break;
 	default:
@@ -147,6 +209,8 @@
 int
 key_equal(const Key *a, const Key *b)
 {
+	int i;
+
 	if (a == NULL || b == NULL || a->type != b->type)
 		return 0;
 	switch (a->type) {
@@ -163,6 +227,22 @@
 		    BN_cmp(a->dsa->g, b->dsa->g) == 0 &&
 		    BN_cmp(a->dsa->pub_key, b->dsa->pub_key) == 0;
 		break;
+	case KEY_OO_ZK:
+
+		if (a->oozk->public->num == b->oozk->public->num &&
+		    BN_cmp(a->oozk->public->exp, b->oozk->public->exp) == 0 &&
+		    BN_cmp(a->oozk->public->n, b->oozk->public->n) == 0) {
+			for (i=0; i<a->oozk->public->num; i++) {
+				if (BN_cmp(a->oozk->public->I[i], b->oozk->public->I[i]) != 0) {
+					debug2("((ZK)) %s: returning 0 (different identities)", __func__);
+					return 0;
+				}
+			}
+			return 1;
+		}	
+		debug2("((ZK)) %s: returning 0 (different num, exp or n)", __func__);
+		return 0;
+		break;
 	default:
 		fatal("key_equal: bad key type %d", a->type);
 		break;
@@ -178,8 +258,10 @@
 	EVP_MD_CTX ctx;
 	u_char *blob = NULL;
 	u_char *retval = NULL;
+	u_char *temp = NULL;
 	u_int len = 0;
-	int nlen, elen;
+	int nlen, elen, ilen, explen; 
+	int i;
 
 	*dgst_raw_length = 0;
 
@@ -207,6 +289,22 @@
 	case KEY_RSA:
 		key_to_blob(k, &blob, &len);
 		break;
+	case KEY_OO_ZK:
+		ilen = BN_num_bytes(k->oozk->public->I[0]) * k->oozk->public->num;
+		nlen = BN_num_bytes(k->oozk->public->n);
+		explen =BN_num_bytes(k->oozk->public->exp);
+		len = ilen + nlen + explen;
+		blob = xmalloc(len);
+		temp = blob;
+		for (i=0;i<k->oozk->public->num; i++)
+		{
+			BN_bn2bin(k->oozk->public->I[i], temp);
+			temp += BN_num_bytes(k->oozk->public->I[0]);
+		}
+		BN_bn2bin(k->oozk->public->n, temp);
+		BN_bn2bin(k->oozk->public->exp, temp + nlen);
+		break;
+
 	case KEY_UNSPEC:
 		return retval;
 		break;
@@ -386,6 +484,7 @@
 	char *cp, *space;
 	int len, n, type;
 	u_int bits;
+	int num, i;
 	u_char *blob;
 
 	cp = *cpp;
@@ -407,6 +506,51 @@
 			return -1;
 		success = 1;
 		break;
+	case KEY_OO_ZK:
+		
+		/* Get number of bits. */
+		if (*cp < '0' || *cp > '9')
+			return -1;	/* Bad bit count... */
+		for (bits = 0; *cp >= '0' && *cp <= '9'; cp++)
+			bits = 10 * bits + *cp - '0';
+		if (bits == 0)
+			return -1;
+		
+ 		*cpp = cp;
+		
+		/*Get number of identities */
+		
+		/* Skip any leading whitespace. */
+		for (; *cp == ' ' || *cp == '\t'; cp++);
+		
+		if (*cp < '0' || *cp > '9')
+			return -1;	/* Bad number of identities */
+		for (num = 0; *cp >= '0' && *cp <= '9'; cp++)
+			num = 10 * num + *cp - '0';
+		if (num < 0 || num > 99 )
+			return -1;	/* not a valid value */
+		ret->oozk->public->num = num;
+				
+		*cpp = cp; 	/* we hope its a whitespace */
+				
+		/* Get exponent L, modulus n and public identities I[i] */
+		ret->oozk->public->exp = BN_new();
+		if (!read_bignum(cpp, ret->oozk->public->exp))
+			return -1;
+		
+		ret->oozk->public->n = BN_new();
+		if (!read_bignum(cpp, ret->oozk->public->n))
+			return -1;
+		
+		ret->oozk->public->I = (BIGNUM**)malloc(sizeof(BIGNUM*) * ret->oozk->public->num);
+		for (i=0;i<ret->oozk->public->num;i++) {
+			ret->oozk->public->I[i] = BN_new();
+			if (!read_bignum(cpp, ret->oozk->public->I[i]))
+				return -1;	
+		}
+
+		success = 1;
+		break;
 	case KEY_UNSPEC:
 	case KEY_RSA:
 	case KEY_DSA:
@@ -494,7 +638,7 @@
 int
 key_write(const Key *key, FILE *f)
 {
-	int n, success = 0;
+	int n, i, success = 0;
 	u_int len, bits = 0;
 	u_char *blob;
 	char *uu;
@@ -520,6 +664,22 @@
 		}
 		xfree(blob);
 		xfree(uu);
+	} else if (key->type == KEY_OO_ZK && key->oozk != NULL) {
+		
+		bits = BN_num_bits(key->oozk->public->n);
+		fprintf(f, "%u", bits);
+		
+		if (fprintf(f, " %d", key->oozk->public->num) && write_bignum(f, key->oozk->public->exp) && write_bignum(f, key->oozk->public->n))
+			success = 1;
+		else 
+			error("key_write: failed for OO_ZK key");
+		
+		for (i=0;i<key->oozk->public->num;i++) {
+			if (!write_bignum(f, key->oozk->public->I[i])) {
+				success = 0;
+				return success;
+			}
+		}
 	}
 	return success;
 }
@@ -537,6 +697,9 @@
 	case KEY_DSA:
 		return "DSA";
 		break;
+	case KEY_OO_ZK:
+		return "OO-ZK";
+		break;
 	}
 	return "unknown";
 }
@@ -605,6 +768,15 @@
 	case KEY_RSA1:
 		k->rsa = rsa_generate_private_key(bits);
 		break;
+	case KEY_OO_ZK:
+		debug("((ZK)) Generating OOZK key pair");
+
+		k = key_new(KEY_OO_ZK);
+
+		if (!OO_genkey(k->oozk->private, k->oozk->public, bits))
+			fatal("((ZK)) %s: Error generating OOZK key pair", __func__);
+
+		break;
 	default:
 		fatal("key_generate: unknown type %d", type);
 	}
@@ -616,6 +788,7 @@
 key_from_private(const Key *k)
 {
 	Key *n = NULL;
+	int i;
 	switch (k->type) {
 	case KEY_DSA:
 		n = key_new(k->type);
@@ -630,6 +803,17 @@
 		BN_copy(n->rsa->n, k->rsa->n);
 		BN_copy(n->rsa->e, k->rsa->e);
 		break;
+	case KEY_OO_ZK:
+		n = key_new(k->type);
+		n->oozk->public->I = (BIGNUM**)malloc(sizeof(BIGNUM*) * k->oozk->public->num);
+		for (i=0;i<k->oozk->public->num;i++) {
+			n->oozk->public->I[i] = BN_new();
+			BN_copy(n->oozk->public->I[i], k->oozk->public->I[i]);
+		}
+		n->oozk->public->n = BN_dup(k->oozk->public->n);
+		n->oozk->public->exp = BN_dup(k->oozk->public->exp);
+		n->oozk->public->num = k->oozk->public->num;
+		break;
 	default:
 		fatal("key_from_private: unknown type %d", k->type);
 		break;
@@ -650,6 +834,9 @@
 		return KEY_RSA;
 	} else if (strcmp(name, "ssh-dss") == 0) {
 		return KEY_DSA;
+	} else if (strcmp(name, "oo-zk") == 0) {
+		debug("((ZK)) Key type is OO_ZK");
+		return KEY_OO_ZK;
 	}
 	debug2("key_type_from_name: unknown key type '%s'", name);
 	return KEY_UNSPEC;
diff -urbBN --exclude=.svn ssh.orig/key.h ssh/key.h
--- ssh.orig/key.h	2003-11-10 17:23:41.000000000 +0100
+++ ssh/key.h	2005-11-29 17:45:17.000000000 +0100
@@ -28,12 +28,14 @@
 
 #include <openssl/rsa.h>
 #include <openssl/dsa.h>
+#include "ohta-okamoto.h"	
 
 typedef struct Key Key;
 enum types {
 	KEY_RSA1,
 	KEY_RSA,
 	KEY_DSA,
+	KEY_OO_ZK,		
 	KEY_UNSPEC
 };
 enum fp_type {
@@ -48,11 +50,17 @@
 /* key is stored in external hardware */
 #define KEY_FLAG_EXT		0x0001
 
+typedef struct _oo_zk {
+	oo_private_key *private;
+	oo_public_key  *public;
+} OOZK;
+
 struct Key {
 	int	 type;
 	int	 flags;
 	RSA	*rsa;
 	DSA	*dsa;
+	OOZK	*oozk;
 };
 
 Key		*key_new(int);
diff -urbBN --exclude=.svn ssh.orig/lib/Makefile ssh/lib/Makefile
--- ssh.orig/lib/Makefile	2004-12-22 03:13:19.000000000 +0100
+++ ssh/lib/Makefile	2005-11-29 17:48:14.000000000 +0100
@@ -11,7 +11,7 @@
 	key.c dispatch.c kex.c mac.c uidswap.c uuencode.c misc.c \
 	ssh-dss.c ssh-rsa.c dh.c kexdh.c kexgex.c \
 	kexdhc.c kexgexc.c scard.c msg.c progressmeter.c dns.c \
-	monitor_fdpass.c
+	monitor_fdpass.c ohta-okamoto.c
 
 DEBUGLIBS= no
 NOPROFILE= yes
diff -urbBN --exclude=.svn ssh.orig/log.c ssh/log.c
--- ssh.orig/log.c	2003-09-23 22:17:11.000000000 +0200
+++ ssh/log.c	2005-11-29 17:45:17.000000000 +0100
@@ -250,7 +250,7 @@
 	}
 }
 
-#define MSGBUFSIZ 1024
+#define MSGBUFSIZ 4400
 
 void
 do_log(LogLevel level, const char *fmt, va_list args)
diff -urbBN --exclude=.svn ssh.orig/misc/ssh_config ssh/misc/ssh_config
--- ssh.orig/misc/ssh_config	1970-01-01 01:00:00.000000000 +0100
+++ ssh/misc/ssh_config	2005-11-29 17:45:17.000000000 +0100
@@ -0,0 +1,40 @@
+#	$OpenBSD: ssh_config,v 1.20 2005/01/28 09:45:53 dtucker Exp $
+
+# This is the ssh client system-wide configuration file.  See
+# ssh_config(5) for more information.  This file provides defaults for
+# users, and the values can be changed in per-user configuration files
+# or on the command line.
+
+# Configuration data is parsed as follows:
+#  1. command line options
+#  2. user-specific file
+#  3. system-wide file
+# Any configuration value is only changed the first time it is set.
+# Thus, host-specific definitions should be at the beginning of the
+# configuration file, and defaults at the end.
+
+# Site-wide defaults for some commonly used options.  For a comprehensive
+# list of available options, their meanings and defaults, please see the
+# ssh_config(5) man page.
+
+# Host *
+#   ForwardAgent no
+#   ForwardX11 no
+#   RhostsRSAAuthentication no
+   RSAAuthentication no
+   ZeroKnowledgeAuthentication yes
+   PasswordAuthentication no
+#   HostbasedAuthentication no
+#   BatchMode no
+#   CheckHostIP yes
+#   AddressFamily any
+#   ConnectTimeout 0
+#   StrictHostKeyChecking ask
+#   IdentityFile ~/.ssh/identity
+#   IdentityFile ~/.ssh/id_rsa
+#   IdentityFile ~/.ssh/id_dsa
+   Port 2222
+   Protocol 2
+#   Cipher 3des
+#   Ciphers aes128-cbc,3des-cbc,blowfish-cbc,cast128-cbc,arcfour,aes192-cbc,aes256-cbc
+#   EscapeChar ~
diff -urbBN --exclude=.svn ssh.orig/misc/ssh_configure.txt ssh/misc/ssh_configure.txt
--- ssh.orig/misc/ssh_configure.txt	1970-01-01 01:00:00.000000000 +0100
+++ ssh/misc/ssh_configure.txt	2005-11-29 17:45:17.000000000 +0100
@@ -0,0 +1,42 @@
+./configure --prefix=/opt --sysconfdir=/etc/ssh --with-pam --with-md5-passwords 
+
+PAM must be enabled in sshd_config: UsePAM directive
+
+--with-lastlog=FILE
+lastlog  formats and prints the contents of the last login
+log, /usr/adm/lastlog.  The  login-name,  port,  and  last
+login time will be printed.
+
+--with-utmpx
+The utmpx and wtmpx files are extended database files that have superseded the obsolete utmp and wtmp database files.
+The utmpx database contains user access and accounting information for commands such as who(1), write(1), and login(1). 
+The wtmpx database contains the history of user access and accounting information for the utmpx database.
+
+--with-default-path=PATH 
+Allows to specify a default $PATH for sessions started by sshd. This replaces the standard path entirely.
+
+--with-pid-dir=PATH
+Specifies the directory in which the ssh.pid file is created.
+
+--with-ssl-dir=DIR
+Specify where OpenSSL libraries are installed.
+
+--with-4in6
+Check for IPv4 in IPv6 mapped addresses and convert them to real (AF_INET) IPv4 addresses. Works around some quirks on Linux.
+
+=====
+
+# apt-get install libssl-dev
+# apt-get install libpam0g-dev
+# apt-get install zlib1g-dev
+
+=====
+
+/ssh/sbin/sshd -t -f /ssh/conf/sshd_config
+
+Server: 192.168.202.129
+Client: 192.168.202.130
+
+=====
+
+./configure --prefix=/home/andi/serverssh --sysconfdir=/home/andi/serverssh/conf --with-pam --with-md5-passwords --with-pid-dir=/home/andi/serverssh/pids --with-4in6 --with-lastlog=/home/andi/serverssh/lastlog
diff -urbBN --exclude=.svn ssh.orig/misc/sshd_config ssh/misc/sshd_config
--- ssh.orig/misc/sshd_config	1970-01-01 01:00:00.000000000 +0100
+++ ssh/misc/sshd_config	2005-11-29 17:45:17.000000000 +0100
@@ -0,0 +1,109 @@
+#	$OpenBSD: sshd_config,v 1.70 2004/12/23 23:11:00 djm Exp $
+
+# This is the sshd server system-wide configuration file.  See
+# sshd_config(5) for more information.
+
+# This sshd was compiled with PATH=/usr/bin:/bin:/usr/sbin:/sbin:/home/andi/zkssh/bin
+
+# The strategy used for options in the default sshd_config shipped with
+# OpenSSH is to specify options with their default value where
+# possible, but leave them commented.  Uncommented options change a
+# default value.
+
+Port 2222
+Protocol 2
+#AddressFamily any
+#ListenAddress 0.0.0.0
+#ListenAddress ::
+
+# HostKey for protocol version 1
+#HostKey /home/andi/zkssh/conf/ssh_host_key
+# HostKeys for protocol version 2
+#HostKey /home/andi/zkssh/conf/ssh_host_rsa_key
+#HostKey /home/andi/zkssh/conf/ssh_host_dsa_key
+
+# Lifetime and size of ephemeral version 1 server key
+#KeyRegenerationInterval 1h
+#ServerKeyBits 768
+
+# Logging
+#obsoletes QuietMode and FascistLogging
+#SyslogFacility AUTH
+#LogLevel INFO
+
+# Authentication:
+
+#LoginGraceTime 2m
+#PermitRootLogin yes
+#StrictModes yes
+#MaxAuthTries 6
+
+#RSAAuthentication yes
+#PubkeyAuthentication yes
+#AuthorizedKeysFile	.ssh/authorized_keys
+
+ZeroKnowledgeAuthentication yes
+
+# For this to work you will also need host keys in /home/andi/zkssh/conf/ssh_known_hosts
+#RhostsRSAAuthentication no
+# similar for protocol version 2
+#HostbasedAuthentication no
+# Change to yes if you don't trust ~/.ssh/known_hosts for
+# RhostsRSAAuthentication and HostbasedAuthentication
+#IgnoreUserKnownHosts no
+# Don't read the user's ~/.rhosts and ~/.shosts files
+#IgnoreRhosts yes
+
+# To disable tunneled clear text passwords, change to no here!
+PasswordAuthentication no
+#PermitEmptyPasswords no
+
+# Change to no to disable s/key passwords
+#ChallengeResponseAuthentication yes
+
+# Kerberos options
+#KerberosAuthentication no
+#KerberosOrLocalPasswd yes
+#KerberosTicketCleanup yes
+#KerberosGetAFSToken no
+
+# GSSAPI options
+#GSSAPIAuthentication no
+#GSSAPICleanupCredentials yes
+
+# Set this to 'yes' to enable PAM authentication, account processing, 
+# and session processing. If this is enabled, PAM authentication will 
+# be allowed through the ChallengeResponseAuthentication mechanism. 
+# Depending on your PAM configuration, this may bypass the setting of 
+# PasswordAuthentication, PermitEmptyPasswords, and 
+# "PermitRootLogin without-password". If you just want the PAM account and 
+# session checks to run without PAM authentication, then enable this but set 
+# ChallengeResponseAuthentication=no
+#UsePAM no
+
+#AllowTcpForwarding yes
+#GatewayPorts no
+#X11Forwarding no
+#X11DisplayOffset 10
+#X11UseLocalhost yes
+#PrintMotd yes
+#PrintLastLog yes
+#TCPKeepAlive yes
+#UseLogin no
+
+# OO-ZK can't handle Privilege Seperation
+UsePrivilegeSeparation no
+
+#PermitUserEnvironment no
+#Compression yes
+#ClientAliveInterval 0
+#ClientAliveCountMax 3
+#UseDNS yes
+#PidFile /home/andi/zkssh/pids/sshd.pid
+#MaxStartups 10
+
+# no default banner path
+#Banner /some/path
+
+# override default of no subsystems
+Subsystem	sftp	/home/andi/zkssh/libexec/sftp-server
diff -urbBN --exclude=.svn ssh.orig/ohta-okamoto.c ssh/ohta-okamoto.c
--- ssh.orig/ohta-okamoto.c	1970-01-01 01:00:00.000000000 +0100
+++ ssh/ohta-okamoto.c	2005-11-29 17:45:17.000000000 +0100
@@ -0,0 +1,319 @@
+/*
+ * Copyright (c) 2005 Ulrich Zehl.  All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/* $Id: ohta-okamoto.c 72 2005-11-25 12:17:28Z uli $ */
+
+
+#include <stdio.h>
+#include <math.h>
+
+#include "ohta-okamoto.h"
+
+int
+_OO_get_modulus(BIGNUM*, unsigned int, BN_CTX*);
+
+BIGNUM *
+_OO_multiply_up(BIGNUM**, BIGNUM**, unsigned int, const BIGNUM*, const BIGNUM*);
+
+
+/**
+ *  Generates a private/public key pair for use in the parallel version
+ *  of the Ohta-Okamoto zero-knowledge identification scheme.
+ *  It is influenced by the definitions for OO_EXPONENT, OO_NUMKEYS and
+ *  OO_ROUNDS in the header file. For a more detailled explanation of what
+ *  those parameters mean, see the docs.
+ */
+int
+OO_genkey(oo_private_key *private, oo_public_key *public, unsigned int bits)
+{
+	BN_CTX *ctx;
+	BIGNUM *n;
+	unsigned int i;
+
+	ctx = BN_CTX_new();
+
+	n = BN_new();
+	if (n == NULL)
+		goto err;
+
+	if(!_OO_get_modulus(n, bits, ctx))
+		goto err;
+
+	/* Set modulus and exponent for both private and public key */
+	private->n   = BN_dup(n);
+	public->n    = n;
+	private->num = OO_NUMKEYS;
+	public->num  = OO_NUMKEYS;
+	if (private->n == NULL)
+		goto err;
+	private->exp = BN_new();
+	if (private->exp == NULL)
+		goto err;
+	if (!BN_set_word(private->exp, OO_EXPONENT))
+		goto err;
+	public->exp  = BN_dup(private->exp);
+	if (public->exp == NULL)
+		goto err;
+
+	/* Prepare the individual key entries */
+	private->S   = (BIGNUM**)malloc(sizeof(BIGNUM*) * private->num);
+	public->I    = (BIGNUM**)malloc(sizeof(BIGNUM*) * public->num);
+	if (private->S == NULL || public->I == NULL)
+		goto err;
+
+	/* Generate the individual key entries */
+	for (i=0; i < public->num; i++) {
+		private->S[i] = BN_new();
+		public->I[i]  = BN_new();
+		if (private->S[i] == NULL || public->I[i] == NULL)
+			goto err;
+		if (!BN_rand(private->S[i], bits-10, -1, 0))
+			goto err;
+		/* The public key I is simply I = S^L */
+		if (!BN_mod_exp(public->I[i], private->S[i], private->exp, private->n, ctx))
+			goto err;
+	}
+
+	return OO_FOK;
+
+err:
+	BN_CTX_free(ctx);
+	BN_free(private->n);
+	BN_free(public->n);
+	BN_free(private->exp);
+	BN_free(public->exp);
+	free(private->S);
+	free(public->I);
+	return OO_FFAIL;
+}
+
+
+/**
+ * Generates the witness X = R^L (mod n) used in the first step of every
+ * round of the Ohta-Okamoto scheme and places the result in the respective
+ * variables.
+ */
+int
+OO_witness(BIGNUM *R, BIGNUM *X, const oo_private_key *private)
+{
+	BN_CTX *ctx;
+	ctx = BN_CTX_new();
+
+	/* Neither the first nor the last bit need be zero */
+	if (!BN_rand(X, BN_num_bits(private->n), -1, 0))
+		goto err;
+	if (!BN_mod(R, X, private->n, ctx))
+		goto err;
+	if (!BN_mod_exp(X, R, private->exp, private->n, ctx))
+		goto err;
+
+	return OO_FOK;
+
+err:
+	BN_CTX_free(ctx);
+	return OO_FFAIL;
+}
+
+
+/**
+ *  Generates and returns the challenge for one round of Ohta-Okamoto. It
+ *  consists of an array of BIGNUMs that contains the (randomly chosen)
+ *  exponents (elements of Zn/Z).
+ *  On error, it returns NULL.
+ */
+BIGNUM**
+OO_challenge(oo_public_key *public)
+{
+	BIGNUM **challenge;
+	unsigned int i;
+	unsigned int bits;
+
+	bits = BN_num_bits(public->exp)-1;
+	challenge = (BIGNUM**)malloc(sizeof(BIGNUM*)*public->num);
+	if (challenge == NULL)
+		goto err;
+
+	for (i=0; i < public->num; i++) {
+		challenge[i] = BN_new();
+		if (challenge[i] == NULL)
+			goto err;
+		if(!BN_rand(challenge[i], bits, -1, 0))
+			goto err;
+	}
+
+	return challenge;
+
+err:
+	free(challenge);
+	return NULL;
+}
+
+
+/**
+ *  Prepares and returns the prover's response to the verifier's challenge
+ *  for one round of the protocol. It needs the secret key and the secret
+ *  information about the witness (i.e. its L-th root).
+ */
+BIGNUM *
+OO_response(BIGNUM **challenge, BIGNUM *R, oo_private_key *private)
+{
+	return _OO_multiply_up(private->S, challenge, private->num, R, private->n);
+}
+
+
+/**
+ *  Computes the left and right hand side of the verification equation for
+ *  one round and compares them, returning 1 if the equation holds (and thus 
+ *  the proof correct and complete), and 0 otherwise
+ */
+int
+OO_verify_response(BIGNUM **challenge, BIGNUM *response, BIGNUM *X, oo_public_key *public)
+{
+	BN_CTX *ctx;
+	BIGNUM *left;
+	BIGNUM *right;
+
+	left  = BN_new();
+	right = BN_new();
+	ctx   = BN_CTX_new();
+
+	/* Computing the left hand side is easy */
+	BN_mod_exp(left, response, public->exp, public->n, ctx);
+
+	/*
+	 * Computing the right hand side takes a bit more work as we need to
+	 * multiply and exponentiate a number of times.
+	 */
+	right = _OO_multiply_up(public->I, challenge, public->num, X, public->n);
+
+/*	printf("left:\t%s\nright:\t%s\n", BN_bn2dec(left), BN_bn2dec(right)); */
+
+	if (!BN_cmp(left, right))
+		return 1;
+	else
+		return 0;
+}
+
+
+/**
+ *  Generates a suitable RSA-like composite modulus for subsequent 
+ *  operations. The two primes that make up this number are not stored
+ *  because they are not needed by the Ohta-Okamoto scheme.
+ */
+int
+_OO_get_modulus(BIGNUM *n, unsigned int bits, BN_CTX *ctx)
+{
+	BIGNUM *p;
+	BIGNUM *q;
+
+	q = BN_new();
+	p = BN_new();
+	if (q == NULL || p == NULL)
+		goto err;
+
+	if (BN_generate_prime(p, bits/2, 1, NULL, NULL, NULL, NULL) == NULL)
+		goto err;
+/*	printf("Generated prime p: %s\n", BN_bn2dec(p));  */
+
+	if (BN_generate_prime(q, bits/2, 1, NULL, NULL, NULL, NULL) == NULL)
+		goto err;
+/* 	printf("Generated prime q: %s\n", BN_bn2dec(q)); */
+
+	if (!BN_mul(n, p, q, ctx))
+		goto err;
+/*	printf("Generated n: %s\n", BN_bn2dec(n)); */
+
+	/* p and q are not needed any longer */
+	BN_clear_free(p);
+	BN_clear_free(q);
+
+	return OO_FOK;
+
+err:
+	BN_free(q);
+	BN_free(p);
+	return OO_FFAIL;
+}
+
+
+/**
+ *  Computes and returns the product of all exponenations and the fixed
+ *  factor needed for the prover's response and the verifier's verification
+ *  in each round. It offers improved speed by using Mongomery's
+ *  multiplication method (unfortunately, the general exponentiation cannot
+ *  be sped up as easily).
+ */
+BIGNUM *
+_OO_multiply_up(BIGNUM **base, BIGNUM **exp, unsigned int len, const BIGNUM *l, const BIGNUM *n)
+{
+	BN_CTX *ctx;
+	BN_MONT_CTX *mctx;
+	BIGNUM *t1;
+	BIGNUM *t2;
+	BIGNUM *res;
+	unsigned int i;
+
+	ctx  = BN_CTX_new();
+	mctx = BN_MONT_CTX_new();
+	if (ctx == NULL || mctx == NULL)
+		goto err;
+	t1  = BN_new();
+	t2  = BN_new();
+	res = BN_new();
+	if (t1 == NULL || t2 == NULL || res == NULL)
+		goto err;
+
+	if (!BN_MONT_CTX_set(mctx, n, ctx))
+		goto err;
+
+	if (!BN_to_montgomery(res, l, mctx, ctx))
+		goto err;
+
+	for (i=0; i < len; i++) {
+		if (!BN_is_zero(exp[i])) {
+			if(!BN_mod_exp(t1, base[i], exp[i], n, ctx))
+				goto err;
+			if(!BN_to_montgomery(t2, t1, mctx, ctx))
+				goto err;
+			if(!BN_mod_mul_montgomery(t1, res, t2, mctx, ctx))
+				goto err;
+			if(BN_copy(res, t1) == NULL)
+				goto err;
+		}
+	}
+
+	if (!BN_from_montgomery(t1, res, mctx, ctx))
+		goto err;
+	BN_clear_free(t2);
+	BN_clear_free(res);
+
+	return t1;
+
+err:
+	BN_CTX_free(ctx);
+	BN_MONT_CTX_free(mctx);
+	BN_free(t1);
+	BN_free(t2);
+	BN_free(res);
+	return NULL;
+}
diff -urbBN --exclude=.svn ssh.orig/ohta-okamoto.h ssh/ohta-okamoto.h
--- ssh.orig/ohta-okamoto.h	1970-01-01 01:00:00.000000000 +0100
+++ ssh/ohta-okamoto.h	2005-11-29 17:45:17.000000000 +0100
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2005 Ulrich Zehl.  All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/* $Id: ohta-okamoto.h 73 2005-11-25 12:23:02Z uli $ */
+
+#ifndef OHTA_OKAMOTO_H
+#define OHTA_OKAMOTO_H
+
+#include <openssl/bn.h>
+#include <openssl/err.h>
+
+
+#define OO_EXPONENT  4
+#define	OO_NUMKEYS  10
+#define	OO_ROUNDS    4
+
+#define OO_FOK   1
+#define OO_FFAIL 0
+
+
+typedef struct _oo_priv {
+	BIGNUM **S;
+	BIGNUM  *n;
+	BIGNUM  *exp;
+	unsigned int num;
+} oo_private_key;
+
+typedef struct _oo_pub {
+	BIGNUM **I;
+	BIGNUM	*n;
+	BIGNUM	*exp;
+	unsigned int num;
+} oo_public_key;
+
+
+int
+OO_genkey(oo_private_key*, oo_public_key*, unsigned int);
+
+int
+OO_witness(BIGNUM*, BIGNUM*, const oo_private_key*);
+
+BIGNUM**
+OO_challenge(oo_public_key*);
+
+int
+OO_verify_response(BIGNUM**, BIGNUM*, BIGNUM*, oo_public_key*);
+
+BIGNUM *
+OO_response(BIGNUM **, BIGNUM*, oo_private_key*);
+
+#endif
+
diff -urbBN --exclude=.svn ssh.orig/pathnames.h ssh/pathnames.h
--- ssh.orig/pathnames.h	2004-07-11 19:48:47.000000000 +0200
+++ ssh/pathnames.h	2005-11-29 17:45:17.000000000 +0100
@@ -67,6 +67,7 @@
 #define _PATH_SSH_CLIENT_IDENTITY	".ssh/identity"
 #define _PATH_SSH_CLIENT_ID_DSA		".ssh/id_dsa"
 #define _PATH_SSH_CLIENT_ID_RSA		".ssh/id_rsa"
+#define _PATH_SSH_CLIENT_ID_OO_ZK	".ssh/id_oo_zk"
 
 /*
  * Configuration file in user\'s home directory.  This file need not be
diff -urbBN --exclude=.svn ssh.orig/readconf.c ssh/readconf.c
--- ssh.orig/readconf.c	2005-03-04 09:48:06.000000000 +0100
+++ ssh/readconf.c	2005-11-29 17:45:17.000000000 +0100
@@ -100,7 +100,7 @@
 	oUsePrivilegedPort, oLogLevel, oCiphers, oProtocol, oMacs,
 	oGlobalKnownHostsFile2, oUserKnownHostsFile2, oPubkeyAuthentication,
 	oKbdInteractiveAuthentication, oKbdInteractiveDevices, oHostKeyAlias,
-	oDynamicForward, oPreferredAuthentications, oHostbasedAuthentication,
+	oDynamicForward, oPreferredAuthentications, oZeroKnowledgeAuthentication, oHostbasedAuthentication,
 	oHostKeyAlgorithms, oBindAddress, oSmartcardDevice,
 	oClearAllForwardings, oNoHostAuthenticationForLocalhost,
 	oEnableSSHKeysign, oRekeyLimit, oVerifyHostKeyDNS, oConnectTimeout,
@@ -130,6 +130,7 @@
 	{ "pubkeyauthentication", oPubkeyAuthentication },
 	{ "dsaauthentication", oPubkeyAuthentication },		    /* alias */
 	{ "rhostsrsaauthentication", oRhostsRSAAuthentication },
+	{ "zeroknowledgeauthentication", oZeroKnowledgeAuthentication },	
 	{ "hostbasedauthentication", oHostbasedAuthentication },
 	{ "challengeresponseauthentication", oChallengeResponseAuthentication },
 	{ "skeyauthentication", oChallengeResponseAuthentication }, /* alias */
@@ -390,6 +391,10 @@
 		intptr = &options->rhosts_rsa_authentication;
 		goto parse_flag;
 
+	case oZeroKnowledgeAuthentication:
+		intptr = &options->zk_authentication;
+		goto parse_flag;
+
 	case oHostbasedAuthentication:
 		intptr = &options->hostbased_authentication;
 		goto parse_flag;
@@ -492,8 +497,12 @@
 			if (*intptr >= SSH_MAX_IDENTITY_FILES)
 				fatal("%.200s line %d: Too many identity files specified (max %d).",
 				    filename, linenum, SSH_MAX_IDENTITY_FILES);
+			else
+				debug2("((ZK)) %s: %.200s line %d: %d identity files specified (max %d).",
+				       __func__, filename, linenum, *intptr+1, SSH_MAX_IDENTITY_FILES);
 			charptr =  &options->identity_files[*intptr];
 			*charptr = xstrdup(arg);
+			debug2("((ZK)) %s: Recognized identity file %s", __func__, *charptr);
 			*intptr = *intptr + 1;
 		}
 		break;
@@ -894,6 +903,7 @@
 	options->kbd_interactive_authentication = -1;
 	options->kbd_interactive_devices = NULL;
 	options->rhosts_rsa_authentication = -1;
+	options->zk_authentication = -1;
 	options->hostbased_authentication = -1;
 	options->batch_mode = -1;
 	options->check_host_ip = -1;
@@ -979,6 +989,8 @@
 		options->kbd_interactive_authentication = 1;
 	if (options->rhosts_rsa_authentication == -1)
 		options->rhosts_rsa_authentication = 0;
+	if (options->zk_authentication == -1)	
+		options->zk_authentication = 1;
 	if (options->hostbased_authentication == -1)
 		options->hostbased_authentication = 0;
 	if (options->batch_mode == -1)
@@ -1029,6 +1041,12 @@
 			    xmalloc(len);
 			snprintf(options->identity_files[options->num_identity_files++],
 			    len, "~/%.100s", _PATH_SSH_CLIENT_ID_DSA);
+			
+			len = 2 + strlen(_PATH_SSH_CLIENT_ID_OO_ZK) + 1;
+			options->identity_files[options->num_identity_files] =
+					xmalloc(len);
+			snprintf(options->identity_files[options->num_identity_files++],
+				 len, "~/%.100s", _PATH_SSH_CLIENT_ID_OO_ZK);
 		}
 	}
 	if (options->escape_char == -1)
diff -urbBN --exclude=.svn ssh.orig/readconf.h ssh/readconf.h
--- ssh.orig/readconf.h	2005-03-01 11:40:27.000000000 +0100
+++ ssh/readconf.h	2005-11-29 17:45:17.000000000 +0100
@@ -41,6 +41,7 @@
 						 * authentication. */
 	int     rsa_authentication;	/* Try RSA authentication. */
 	int     pubkey_authentication;	/* Try ssh2 pubkey authentication. */
+	int	zk_authentication;	/* Try zero-knowledge authentication. ag */	
 	int     hostbased_authentication;	/* ssh2's rhosts_rsa */
 	int     challenge_response_authentication;
 					/* Try S/Key or TIS, authentication. */
diff -urbBN --exclude=.svn ssh.orig/servconf.c ssh/servconf.c
--- ssh.orig/servconf.c	2005-03-01 11:09:52.000000000 +0100
+++ ssh/servconf.c	2005-11-29 17:45:17.000000000 +0100
@@ -58,6 +58,7 @@
 	options->log_facility = SYSLOG_FACILITY_NOT_SET;
 	options->log_level = SYSLOG_LEVEL_NOT_SET;
 	options->rhosts_rsa_authentication = -1;
+	options->zk_authentication = -1;	
 	options->hostbased_authentication = -1;
 	options->hostbased_uses_name_from_packet_only = -1;
 	options->rsa_authentication = -1;
@@ -158,6 +159,8 @@
 		options->log_level = SYSLOG_LEVEL_INFO;
 	if (options->rhosts_rsa_authentication == -1)
 		options->rhosts_rsa_authentication = 0;
+	if (options->zk_authentication == -1)
+		options->zk_authentication = 1;
 	if (options->hostbased_authentication == -1)
 		options->hostbased_authentication = 0;
 	if (options->hostbased_uses_name_from_packet_only == -1)
@@ -230,7 +233,7 @@
 	sBadOption,		/* == unknown option */
 	sPort, sHostKeyFile, sServerKeyBits, sLoginGraceTime, sKeyRegenerationTime,
 	sPermitRootLogin, sLogFacility, sLogLevel,
-	sRhostsRSAAuthentication, sRSAAuthentication,
+	sRhostsRSAAuthentication, sRSAAuthentication, sZeroKnowledgeAuthentication,
 	sKerberosAuthentication, sKerberosOrLocalPasswd, sKerberosTicketCleanup,
 	sKerberosGetAFSToken,
 	sKerberosTgtPassing, sChallengeResponseAuthentication,
@@ -267,6 +270,7 @@
 	{ "permitrootlogin", sPermitRootLogin },
 	{ "syslogfacility", sLogFacility },
 	{ "loglevel", sLogLevel },
+	{ "zeroknowledgeauthentication", sZeroKnowledgeAuthentication },
 	{ "rhostsauthentication", sDeprecated },
 	{ "rhostsrsaauthentication", sRhostsRSAAuthentication },
 	{ "hostbasedauthentication", sHostbasedAuthentication },
@@ -582,6 +586,10 @@
 		intptr = &options->rhosts_rsa_authentication;
 		goto parse_flag;
 
+	case sZeroKnowledgeAuthentication:
+		intptr = &options->zk_authentication;
+		goto parse_flag;
+
 	case sHostbasedAuthentication:
 		intptr = &options->hostbased_authentication;
 		goto parse_flag;
diff -urbBN --exclude=.svn ssh.orig/servconf.h ssh/servconf.h
--- ssh.orig/servconf.h	2004-12-24 00:11:00.000000000 +0100
+++ ssh/servconf.h	2005-11-29 17:45:17.000000000 +0100
@@ -72,6 +72,7 @@
 	LogLevel log_level;	/* Level for system logging. */
 	int     rhosts_rsa_authentication;	/* If true, permit rhosts RSA
 						 * authentication. */
+	int	zk_authentication;		/* If true, permit ssh2 zero-knowledge auth */
 	int     hostbased_authentication;	/* If true, permit ssh2 hostbased auth */
 	int     hostbased_uses_name_from_packet_only; /* experimental */
 	int     rsa_authentication;	/* If true, permit RSA authentication. */
diff -urbBN --exclude=.svn ssh.orig/ssh-keygen.c ssh/ssh-keygen.c
--- ssh.orig/ssh-keygen.c	2005-03-02 02:27:41.000000000 +0100
+++ ssh/ssh-keygen.c	2005-11-29 17:45:17.000000000 +0100
@@ -112,6 +112,9 @@
 		case KEY_RSA:
 			name = _PATH_SSH_CLIENT_ID_RSA;
 			break;
+		case KEY_OO_ZK:
+			name = _PATH_SSH_CLIENT_ID_OO_ZK;
+			break;
 		default:
 			fprintf(stderr, "bad key type");
 			exit(1);
diff -urbBN --exclude=.svn ssh.orig/ssh2.h ssh/ssh2.h
--- ssh.orig/ssh2.h	2003-05-14 02:52:59.000000000 +0200
+++ ssh/ssh2.h	2005-11-29 17:45:17.000000000 +0100
@@ -112,6 +112,9 @@
 #define SSH2_MSG_USERAUTH_INFO_REQUEST			60
 #define SSH2_MSG_USERAUTH_INFO_RESPONSE			61
 
+#define SSH2_MSG_USERAUTH_ZK_OK				60
+#define SSH2_MSG_USERAUTH_ZK_NEXT_ROUND			60
+
 /* connection protocol: generic */
 
 #define SSH2_MSG_GLOBAL_REQUEST				80
diff -urbBN --exclude=.svn ssh.orig/sshconnect2.c ssh/sshconnect2.c
--- ssh.orig/sshconnect2.c	2004-06-13 14:53:24.000000000 +0200
+++ ssh/sshconnect2.c	2005-11-29 17:45:17.000000000 +0100
@@ -47,6 +47,8 @@
 #include "canohost.h"
 #include "msg.h"
 #include "pathnames.h"
+#include <openssl/bn.h>
+#include "ohta-okamoto.h"
 
 #ifdef GSSAPI
 #include "ssh-gss.h"
@@ -69,6 +71,12 @@
 
 Kex *xxx_kex = NULL;
 
+/* zk key pair of user */
+oo_private_key private;
+oo_public_key public;
+/* random value in zk userauth method */
+BIGNUM *R;
+
 static int
 verify_host_key_callback(Key *hostkey)
 {
@@ -191,6 +199,8 @@
 void	input_userauth_info_req(int, u_int32_t, void *);
 void	input_userauth_pk_ok(int, u_int32_t, void *);
 void	input_userauth_passwd_changereq(int, u_int32_t, void *);
+void	input_userauth_zk_ok(int, u_int32_t, void *);	
+void 	input_userauth_zk_next_round(int type, u_int32_t seq, void *ctxt); 
 
 int	userauth_none(Authctxt *);
 int	userauth_pubkey(Authctxt *);
@@ -198,6 +208,7 @@
 int	userauth_kbdint(Authctxt *);
 int	userauth_hostbased(Authctxt *);
 int	userauth_kerberos(Authctxt *);
+int	userauth_zk(Authctxt *);
 
 #ifdef GSSAPI
 int	userauth_gssapi(Authctxt *authctxt);
@@ -226,6 +237,10 @@
 		&options.gss_authentication,
 		NULL},
 #endif
+	{"zk@cms.ac",
+		userauth_zk,
+		&options.zk_authentication,		/* will become: &options.zk_authentication, */
+		NULL},
 	{"hostbased",
 		userauth_hostbased,
 		&options.hostbased_authentication,
@@ -705,6 +720,172 @@
 #endif /* GSSAPI */
 
 int
+userauth_zk(Authctxt *authctxt)
+{
+
+	int i;	/* counter in loops */
+	int tries = 0;	/* for counting NULL keys */
+
+	/* big numbers */
+	R = BN_new();	/* random value */
+	BIGNUM *X = BN_new();	/* witness */
+
+	Identity *id;
+
+	for (i=0; i<options.num_identity_files; ++i)
+	{
+		debug3("((ZK)) %s: Identity file %d: %s", __func__, i+1, options.identity_files[i]);
+	}
+
+	debug("((ZK)) %s: Trying to load private key", __func__);
+	i = 0;
+	while ((id = TAILQ_FIRST(&authctxt->keys))) {
+		debug3("((ZK)) %s: Try %d for key %d", __func__, id->tried, ++i);
+		/* move key to the end of the queue */
+		TAILQ_REMOVE(&authctxt->keys, id, next);
+		TAILQ_INSERT_TAIL(&authctxt->keys, id, next);
+		if (id->key == NULL)
+		{
+			debug3("((ZK)) %s: Key is NULL", __func__);
+			if (++tries == 3)
+				return 0;
+			continue;
+		}
+		/* Public key method messes up count of "id->tried"; fix this here */
+		if (id->key != NULL && id->key->type == KEY_OO_ZK)
+			id->tried = 0;
+		if (id->tried++)
+			return (0);
+		debug3("((ZK)) %s: Reading key %d", __func__, i);
+		
+		debug("((ZK)) %s: Trying private key: %s", __func__, id->filename);
+		if (id->key == NULL || id->key->type == KEY_OO_ZK) {
+			id->key = load_identity_file(id->filename);
+			if (id->key != NULL) {
+				id->isprivate = 1;
+				break;
+			}
+		}
+	}
+	
+	memcpy(&private, id->key->oozk->private, sizeof(oo_private_key));
+	memcpy(&public, id->key->oozk->public, sizeof(oo_public_key));
+
+	debug("((ZK)) ROUND 0");
+	/* P: Issue a witness */
+	OO_witness(R, X, &private);
+	debug2("((ZK)) Sending witness to server");
+	
+	/* assemble userauth request for zk */
+	packet_start(SSH2_MSG_USERAUTH_REQUEST);
+	packet_put_cstring(authctxt->server_user);
+	packet_put_cstring(authctxt->service);
+	packet_put_cstring(authctxt->method->name);
+	packet_put_int(0);	/* set round counter to 0 */
+	packet_put_bignum2(X);	/* add witness */
+ 	
+	/* add public key */
+ 	packet_put_bignum2(public.exp);		/* exponent (L) */
+ 	packet_put_bignum2(public.n);		/* modulus */
+ 	packet_put_int(public.num);		/* number of identities */
+ 	for (i=0; i<public.num; ++i)
+ 		packet_put_bignum2(public.I[i]);	/* identity */
+	packet_send();
+
+	/* register callback for USERAUTH_ZK_OK message */
+	dispatch_set(SSH2_MSG_USERAUTH_ZK_OK, &input_userauth_zk_ok);
+
+	/* cleanup */
+	BN_free(X);
+
+	return (1);	/* we sent a packet */
+}
+
+void
+input_userauth_zk_ok(int type, u_int32_t seq, void *ctxt)
+{
+
+	Authctxt *authctxt = ctxt;
+	if (authctxt == NULL)
+		fatal("input_userauth_zk_ok: no authentication context");
+
+	int i;			/* counter for loops */
+	u_int round;
+	BIGNUM *response;
+	BIGNUM **challenge = (BIGNUM**)malloc(sizeof(BIGNUM*)*public.num);
+	if (challenge == NULL)
+		fatal("((ZK)) Could not allocate memory for received challenge.");
+	
+	round = packet_get_int();
+	debug("((ZK)) ROUND %d",round+1);
+	for (i=0; i<public.num; ++i)
+	{
+		challenge[i] = BN_new();
+		packet_get_bignum2(challenge[i]);
+	}
+	debug2("((ZK)) Read challenge (from server); answering challenge");
+
+	/* P: Respond to the challenge w.r.t. the witness) */
+	response = OO_response(challenge, R, &private);
+	
+	debug2("((ZK)) sending response");
+	
+	/* send responses to server's challenges */
+	packet_start(SSH2_MSG_USERAUTH_REQUEST);
+	packet_put_cstring(authctxt->server_user);
+	packet_put_cstring(authctxt->service);
+	packet_put_cstring(authctxt->method->name);
+	packet_put_int(round+1);
+	packet_put_bignum2(response);
+	packet_send();
+
+	/* register callback for USERAUTH_ZK_NEXT_ROUND message */
+	dispatch_set(SSH2_MSG_USERAUTH_ZK_NEXT_ROUND, &input_userauth_zk_next_round);
+
+	/* cleanup */
+	BN_free(response);
+	free(challenge);
+}
+
+void
+input_userauth_zk_next_round(int type, u_int32_t seq, void *ctxt)
+{
+	BIGNUM *X;
+
+	Authctxt *authctxt = ctxt;
+	if (authctxt == NULL)
+		fatal("input_userauth_zk_ok: no authentication context");
+
+	u_int round;
+
+	R = BN_new();	/* random value */
+	X = BN_new();	/* witness */
+	round = packet_get_int();
+	debug("((ZK)) ROUND %d",round+1);
+	/* P: Issue a witness */
+	OO_witness(R, X, &private);
+	debug2("((ZK)) Sending witness to server");
+	
+	/* assemble userauth request for zk */
+	packet_start(SSH2_MSG_USERAUTH_REQUEST);
+	packet_put_cstring(authctxt->server_user);
+	packet_put_cstring(authctxt->service);
+	packet_put_cstring(authctxt->method->name);
+	packet_put_int(round+1);	/* set round counter */
+	packet_put_bignum2(X);		/* add witness */
+ 	packet_send();
+
+	/* register callback for USERAUTH_ZK_OK message */
+	dispatch_set(SSH2_MSG_USERAUTH_ZK_OK, &input_userauth_zk_ok);
+
+	/* cleanup */
+	BN_free(X);
+
+	return;	/* we sent a packet */
+}
+
+
+int
 userauth_none(Authctxt *authctxt)
 {
 	/* initial userauth request */
@@ -967,9 +1148,12 @@
 		return NULL;
 	}
 	private = key_load_private_type(KEY_UNSPEC, filename, "", NULL);
+	if (private == NULL)
+		private = key_load_private_type(KEY_OO_ZK, filename, "", NULL);
 	if (private == NULL) {
-		if (options.batch_mode)
+		if (options.batch_mode) {
 			return NULL;
+		}
 		snprintf(prompt, sizeof prompt,
 		    "Enter passphrase for key '%.100s': ", filename);
 		for (i = 0; i < options.number_of_password_prompts; i++) {
@@ -977,6 +1161,8 @@
 			if (strcmp(passphrase, "") != 0) {
 				private = key_load_private_type(KEY_UNSPEC, filename,
 				    passphrase, NULL);
+				if (private == NULL)
+					private = key_load_private_type(KEY_OO_ZK, filename, passphrase, NULL);
 				quit = 0;
 			} else {
 				debug2("no passphrase given, try next key");
diff -urbBN --exclude=.svn ssh.orig/sshd/Makefile ssh/sshd/Makefile
--- ssh.orig/sshd/Makefile	2004-08-23 16:26:39.000000000 +0200
+++ ssh/sshd/Makefile	2005-11-29 17:45:58.000000000 +0100
@@ -14,7 +14,7 @@
 	auth.c auth1.c auth2.c auth-options.c session.c \
 	auth-chall.c auth2-chall.c groupaccess.c \
 	auth-skey.c auth-bsdauth.c auth2-hostbased.c auth2-kbdint.c \
-	auth2-none.c auth2-passwd.c auth2-pubkey.c \
+	auth2-none.c auth2-passwd.c auth2-pubkey.c auth2-zk.c \
 	monitor_mm.c monitor.c monitor_wrap.c \
 	kexdhs.c kexgexs.c
 
