Complete key derivation support.
diff --git a/CHANGES b/CHANGES
index 2a07f91..3ba2d71 100644
--- a/CHANGES
+++ b/CHANGES
@@ -4,6 +4,10 @@
 
  Changes between 0.9.8a and 0.9.9  [xx XXX xxxx]
 
+  *) Add support for key derivation (agreement) in the API, DH method and
+     pkeyutl.
+     [Steve Henson]
+
   *) Add DSA pkey method and DH pkey methods, extend DH ASN1 method to support
      public and private key formats. As a side effect these add additional 
      command line functionality not previously available: DSA signatures can be
diff --git a/apps/pkeyutl.c b/apps/pkeyutl.c
index 522656d..a3e55f5 100644
--- a/apps/pkeyutl.c
+++ b/apps/pkeyutl.c
@@ -74,7 +74,10 @@
 
 static EVP_PKEY_CTX *init_ctx(int *pkeysize,
 				char *keyfile, int keyform, int key_type,
-				char *passargin, int pkey_op, char *engine);
+				char *passargin, int pkey_op, ENGINE *e);
+
+static int setup_peer(BIO *err, EVP_PKEY_CTX *ctx, int peerform,
+							const char *file);
 
 int MAIN(int argc, char **);
 
@@ -82,9 +85,9 @@
 {
 	BIO *in = NULL, *out = NULL;
 	char *infile = NULL, *outfile = NULL, *sigfile = NULL;
-	char *engine = NULL;
+	ENGINE *e = NULL;
 	int pkey_op = EVP_PKEY_OP_SIGN, key_type = KEY_PRIVKEY;
-	int keyform = FORMAT_PEM;
+	int keyform = FORMAT_PEM, peerform = FORMAT_PEM;
 	char badarg = 0, rev = 0;
 	char hexdump = 0, asn1parse = 0;
 	EVP_PKEY_CTX *ctx = NULL;
@@ -131,7 +134,7 @@
 				{
 				ctx = init_ctx(&keysize,
 						*(++argv), keyform, key_type,
-						passargin, pkey_op, engine);
+						passargin, pkey_op, e);
 				if (!ctx)
 					{
 					BIO_puts(bio_err,
@@ -141,11 +144,23 @@
 					}
 				}
 			}
+		else if (!strcmp(*argv,"-peerkey"))
+			{
+			if (--argc < 1)
+				badarg = 1;
+			else if (!setup_peer(bio_err, ctx, peerform, *(++argv)))
+				badarg = 1;
+			}
 		else if (!strcmp(*argv,"-passin"))
 			{
 			if (--argc < 1) badarg = 1;
 			passargin= *(++argv);
 			}
+		else if (strcmp(*argv,"-peerform") == 0)
+			{
+			if (--argc < 1) badarg = 1;
+			peerform=str2fmt(*(++argv));
+			}
 		else if (strcmp(*argv,"-keyform") == 0)
 			{
 			if (--argc < 1) badarg = 1;
@@ -157,7 +172,7 @@
 			if (--argc < 1)
 				badarg = 1;
 			else
-				engine = *(++argv);
+				e = setup_engine(bio_err, *(++argv), 0);
 			}
 #endif
 		else if(!strcmp(*argv, "-pubin"))
@@ -180,14 +195,16 @@
 			pkey_op = EVP_PKEY_OP_ENCRYPT;
 		else if(!strcmp(*argv, "-decrypt"))
 			pkey_op = EVP_PKEY_OP_DECRYPT;
+		else if(!strcmp(*argv, "-derive"))
+			pkey_op = EVP_PKEY_OP_DERIVE;
 		else if (strcmp(*argv,"-pkeyopt") == 0)
 			{
 			if (--argc < 1)
 				badarg = 1;
-			if (!ctx)
+			else if (!ctx)
 				{
 				BIO_puts(bio_err,
-					"-param command before -inkey\n");
+					"-pkeyopt command before -inkey\n");
 				badarg = 1;
 				}
 			else if (pkey_ctrl_string(ctx, *(++argv)) <= 0)
@@ -228,17 +245,21 @@
 /* FIXME: seed PRNG only if needed */
 	app_RAND_load_file(NULL, bio_err, 0);
 
-	if(infile)
+	if (pkey_op != EVP_PKEY_OP_DERIVE)
 		{
-		if(!(in = BIO_new_file(infile, "rb")))
+		if(infile)
 			{
-			BIO_printf(bio_err, "Error Reading Input File\n");
-			ERR_print_errors(bio_err);	
-			goto end;
+			if(!(in = BIO_new_file(infile, "rb")))
+				{
+				BIO_puts(bio_err,
+					"Error Opening Input File\n");
+				ERR_print_errors(bio_err);	
+				goto end;
+				}
 			}
+		else
+			in = BIO_new_fp(stdin, BIO_NOCLOSE);
 		}
-	else
-		in = BIO_new_fp(stdin, BIO_NOCLOSE);
 
 	if(outfile)
 		{
@@ -280,24 +301,28 @@
 	
 	buf_out = OPENSSL_malloc(keysize);
 
-	/* Read the input data */
-	buf_inlen = bio_to_mem(&buf_in, keysize * 10, in);
-	if(buf_inlen <= 0)
+	if (in)
 		{
-		BIO_printf(bio_err, "Error reading input Data\n");
-		exit(1);
-		}
-	if(rev)
-		{
-		int i;
-		unsigned char ctmp;
-		for(i = 0; i < buf_inlen/2; i++)
+		/* Read the input data */
+		buf_inlen = bio_to_mem(&buf_in, keysize * 10, in);
+		if(buf_inlen <= 0)
 			{
-			ctmp = buf_in[i];
-			buf_in[i] = buf_in[buf_inlen - 1 - i];
-			buf_in[buf_inlen - 1 - i] = ctmp;
+			BIO_printf(bio_err, "Error reading input Data\n");
+			exit(1);
+			}
+		if(rev)
+			{
+			int i;
+			unsigned char ctmp;
+			for(i = 0; i < buf_inlen/2; i++)
+				{
+				ctmp = buf_in[i];
+				buf_in[i] = buf_in[buf_inlen - 1 - i];
+				buf_in[buf_inlen - 1 - i] = ctmp;
+				}
 			}
 		}
+
 	switch(pkey_op)
 		{
 		case EVP_PKEY_OP_VERIFYRECOVER:
@@ -330,6 +355,10 @@
 			goto end;
 		break; 
 
+		case EVP_PKEY_OP_DERIVE:
+		rv  = EVP_PKEY_derive(ctx, buf_out, &buf_outlen);
+		break;
+
 		}
 
 	if(rv <= 0)
@@ -387,15 +416,15 @@
 
 static EVP_PKEY_CTX *init_ctx(int *pkeysize,
 				char *keyfile, int keyform, int key_type,
-				char *passargin, int pkey_op, char *engine)
+				char *passargin, int pkey_op, ENGINE *e)
 	{
-	ENGINE *e = NULL;
 	EVP_PKEY *pkey = NULL;
 	EVP_PKEY_CTX *ctx = NULL;
 	char *passin = NULL;
 	int rv = -1;
 	X509 *x;
-	if(((pkey_op == EVP_PKEY_OP_SIGN) || (pkey_op == EVP_PKEY_OP_DECRYPT))
+	if(((pkey_op == EVP_PKEY_OP_SIGN) || (pkey_op == EVP_PKEY_OP_DECRYPT) 
+		|| (pkey_op == EVP_PKEY_OP_DERIVE))
 		&& (key_type != KEY_PRIVKEY))
 		{
 		BIO_printf(bio_err, "A private key is needed for this operation\n");
@@ -435,7 +464,7 @@
 	if (!pkey)
 		goto end;
 
-	ctx = EVP_PKEY_CTX_new(pkey, NULL);
+	ctx = EVP_PKEY_CTX_new(pkey, e);
 
 	EVP_PKEY_free(pkey);
 
@@ -463,6 +492,10 @@
 		case EVP_PKEY_OP_DECRYPT:
 		rv = EVP_PKEY_decrypt_init(ctx);
 		break;
+
+		case EVP_PKEY_OP_DERIVE:
+		rv = EVP_PKEY_derive_init(ctx);
+		break;
 		}
 
 	if (rv <= 0)
@@ -481,3 +514,31 @@
 
 	}
 
+static int setup_peer(BIO *err, EVP_PKEY_CTX *ctx, int peerform,
+							const char *file)
+	{
+	EVP_PKEY *peer = NULL;
+	int ret;
+	if (!ctx)
+		{
+		BIO_puts(err, "-peerkey command before -inkey\n");
+		return 0;
+		}
+		
+	peer = load_pubkey(bio_err, file, peerform, 0, NULL, NULL, "Peer Key");
+
+	if (!peer)
+		{
+		BIO_printf(bio_err, "Error reading peer key %s\n", file);
+		ERR_print_errors(err);
+		return 0;
+		}
+
+	ret = EVP_PKEY_derive_set_peer(ctx, peer);
+
+	EVP_PKEY_free(peer);
+	if (ret <= 0)
+		ERR_print_errors(err);
+	return ret;
+	}
+			
diff --git a/crypto/dh/dh.h b/crypto/dh/dh.h
index b4b181c..10bf3fe 100644
--- a/crypto/dh/dh.h
+++ b/crypto/dh/dh.h
@@ -234,6 +234,7 @@
 #define DH_F_DH_PUB_ENCODE				 109
 #define DH_F_GENERATE_KEY				 103
 #define DH_F_GENERATE_PARAMETERS			 104
+#define DH_F_PKEY_DH_DERIVE				 112
 #define DH_F_PKEY_DH_KEYGEN				 113
 
 /* Reason codes. */
@@ -242,6 +243,7 @@
 #define DH_R_BN_ERROR					 106
 #define DH_R_DECODE_ERROR				 104
 #define DH_R_INVALID_PUBKEY				 102
+#define DH_R_KEYS_NOT_SET				 108
 #define DH_R_NO_PARAMETERS_SET				 107
 #define DH_R_NO_PRIVATE_VALUE				 100
 #define DH_R_PARAMETER_ENCODING_ERROR			 105
diff --git a/crypto/dh/dh_ameth.c b/crypto/dh/dh_ameth.c
index 6d31257..7a83768 100644
--- a/crypto/dh/dh_ameth.c
+++ b/crypto/dh/dh_ameth.c
@@ -397,6 +397,15 @@
 	return BN_num_bits(pkey->pkey.dh->p);
 	}
 
+static int dh_cmp_parameters(const EVP_PKEY *a, const EVP_PKEY *b)
+	{
+	if (	BN_cmp(a->pkey.dh->p,b->pkey.dh->p) ||
+		BN_cmp(a->pkey.dh->g,b->pkey.dh->g))
+		return 0;
+	else
+		return 1;
+	}
+
 static int dh_copy_parameters(EVP_PKEY *to, const EVP_PKEY *from)
 	{
 	BIGNUM *a;
@@ -409,20 +418,18 @@
 
 	if ((a=BN_dup(from->pkey.dh->g)) == NULL)
 		return 0;
-	if (to->pkey.dsa->g != NULL)
+	if (to->pkey.dh->g != NULL)
 		BN_free(to->pkey.dh->g);
 	to->pkey.dh->g=a;
 
 	return 1;
 	}
 
-static int dh_cmp_parameters(const EVP_PKEY *a, const EVP_PKEY *b)
+static int dh_missing_parameters(const EVP_PKEY *a)
 	{
-	if (	BN_cmp(a->pkey.dh->p,b->pkey.dsa->p) ||
-		BN_cmp(a->pkey.dh->g,b->pkey.dsa->g))
-		return 0;
-	else
+	if (!a->pkey.dh->p || !a->pkey.dh->g)
 		return 1;
+	return 0;
 	}
 
 static int dh_pub_cmp(const EVP_PKEY *a, const EVP_PKEY *b)
@@ -481,7 +488,7 @@
 
 	dh_param_decode,
 	dh_param_encode,
-	0,
+	dh_missing_parameters,
 	dh_copy_parameters,
 	dh_cmp_parameters,
 	dh_param_print,
diff --git a/crypto/dh/dh_err.c b/crypto/dh/dh_err.c
index 64acfde..2fdfc55 100644
--- a/crypto/dh/dh_err.c
+++ b/crypto/dh/dh_err.c
@@ -82,6 +82,7 @@
 {ERR_FUNC(DH_F_DH_PUB_ENCODE),	"DH_PUB_ENCODE"},
 {ERR_FUNC(DH_F_GENERATE_KEY),	"GENERATE_KEY"},
 {ERR_FUNC(DH_F_GENERATE_PARAMETERS),	"GENERATE_PARAMETERS"},
+{ERR_FUNC(DH_F_PKEY_DH_DERIVE),	"PKEY_DH_DERIVE"},
 {ERR_FUNC(DH_F_PKEY_DH_KEYGEN),	"PKEY_DH_KEYGEN"},
 {0,NULL}
 	};
@@ -93,6 +94,7 @@
 {ERR_REASON(DH_R_BN_ERROR)               ,"bn error"},
 {ERR_REASON(DH_R_DECODE_ERROR)           ,"decode error"},
 {ERR_REASON(DH_R_INVALID_PUBKEY)         ,"invalid public key"},
+{ERR_REASON(DH_R_KEYS_NOT_SET)           ,"keys not set"},
 {ERR_REASON(DH_R_NO_PARAMETERS_SET)      ,"no parameters set"},
 {ERR_REASON(DH_R_NO_PRIVATE_VALUE)       ,"no private value"},
 {ERR_REASON(DH_R_PARAMETER_ENCODING_ERROR),"parameter encoding error"},
diff --git a/crypto/dh/dh_pmeth.c b/crypto/dh/dh_pmeth.c
index d2e6aaf..aaf3280 100644
--- a/crypto/dh/dh_pmeth.c
+++ b/crypto/dh/dh_pmeth.c
@@ -115,11 +115,16 @@
 		dctx->generator = p1;
 		return 1;
 
+		case EVP_PKEY_CTRL_PEER_KEY:
+		/* Default behaviour is OK */
+		return 1;
+
 		default:
 		return -2;
 
 		}
 	}
+
 			
 static int pkey_dh_ctrl_str(EVP_PKEY_CTX *ctx,
 			const char *type, const char *value)
@@ -182,6 +187,22 @@
 	return DH_generate_key(pkey->pkey.dh);
 	}
 
+static int pkey_dh_derive(EVP_PKEY_CTX *ctx, unsigned char *key, int *keylen)
+	{
+	int ret;
+	if (!ctx->pkey || !ctx->peerkey)
+		{
+		DHerr(DH_F_PKEY_DH_DERIVE, DH_R_KEYS_NOT_SET);
+		return 0;
+		}
+	ret = DH_compute_key(key, ctx->peerkey->pkey.dh->pub_key,
+							ctx->pkey->pkey.dh);
+	if (ret < 0)
+		return ret;
+	*keylen = ret;
+	return 1;
+	}
+
 const EVP_PKEY_METHOD dh_pkey_meth = 
 	{
 	EVP_PKEY_DH,
@@ -209,7 +230,8 @@
 
 	0,0,
 
-	0,0,
+	0,
+	pkey_dh_derive,
 
 	pkey_dh_ctrl,
 	pkey_dh_ctrl_str
diff --git a/crypto/evp/evp.h b/crypto/evp/evp.h
index d607816..bfffdd5 100644
--- a/crypto/evp/evp.h
+++ b/crypto/evp/evp.h
@@ -933,6 +933,7 @@
 					EVP_PKEY_CTRL_MD, 0, (void *)md)
 
 #define EVP_PKEY_CTRL_MD		1
+#define EVP_PKEY_CTRL_PEER_KEY		2
 
 #define EVP_PKEY_ALG_CTRL		0x1000
 
@@ -972,6 +973,10 @@
 			unsigned char *out, int *outlen,
 			const unsigned char *in, int inlen);
 
+int EVP_PKEY_derive_init(EVP_PKEY_CTX *ctx);
+int EVP_PKEY_derive_set_peer(EVP_PKEY_CTX *ctx, EVP_PKEY *peer);
+int EVP_PKEY_derive(EVP_PKEY_CTX *ctx, unsigned char *key, int *keylen);
+
 typedef int EVP_PKEY_gen_cb(EVP_PKEY_CTX *ctx);
 
 int EVP_PKEY_paramgen_init(EVP_PKEY_CTX *ctx);
@@ -1018,6 +1023,7 @@
 #define EVP_F_EVP_PKEY_DECRYPT_OLD			 151
 #define EVP_F_EVP_PKEY_DERIVE				 153
 #define EVP_F_EVP_PKEY_DERIVE_INIT			 154
+#define EVP_F_EVP_PKEY_DERIVE_SET_PEER			 155
 #define EVP_F_EVP_PKEY_ENCRYPT				 105
 #define EVP_F_EVP_PKEY_ENCRYPT_INIT			 139
 #define EVP_F_EVP_PKEY_ENCRYPT_OLD			 152
@@ -1061,6 +1067,7 @@
 #define EVP_R_DATA_NOT_MULTIPLE_OF_BLOCK_LENGTH		 138
 #define EVP_R_DECODE_ERROR				 114
 #define EVP_R_DIFFERENT_KEY_TYPES			 101
+#define EVP_R_DIFFERENT_PARAMETERS			 153
 #define EVP_R_ENCODE_ERROR				 115
 #define EVP_R_EVP_PBE_CIPHERINIT_ERROR			 119
 #define EVP_R_EXPECTING_AN_RSA_KEY			 127
@@ -1080,6 +1087,7 @@
 #define EVP_R_NO_CIPHER_SET				 131
 #define EVP_R_NO_DIGEST_SET				 139
 #define EVP_R_NO_DSA_PARAMETERS				 116
+#define EVP_R_NO_KEY_SET				 154
 #define EVP_R_NO_OPERATION_SET				 149
 #define EVP_R_NO_SIGN_FUNCTION_CONFIGURED		 104
 #define EVP_R_NO_VERIFY_FUNCTION_CONFIGURED		 105
diff --git a/crypto/evp/evp_err.c b/crypto/evp/evp_err.c
index 50d5a71..225564d 100644
--- a/crypto/evp/evp_err.c
+++ b/crypto/evp/evp_err.c
@@ -97,6 +97,7 @@
 {ERR_FUNC(EVP_F_EVP_PKEY_DECRYPT_OLD),	"EVP_PKEY_decrypt_old"},
 {ERR_FUNC(EVP_F_EVP_PKEY_DERIVE),	"EVP_PKEY_DERIVE"},
 {ERR_FUNC(EVP_F_EVP_PKEY_DERIVE_INIT),	"EVP_PKEY_DERIVE_INIT"},
+{ERR_FUNC(EVP_F_EVP_PKEY_DERIVE_SET_PEER),	"EVP_PKEY_DERIVE_SET_PEER"},
 {ERR_FUNC(EVP_F_EVP_PKEY_ENCRYPT),	"EVP_PKEY_encrypt"},
 {ERR_FUNC(EVP_F_EVP_PKEY_ENCRYPT_INIT),	"EVP_PKEY_encrypt_init"},
 {ERR_FUNC(EVP_F_EVP_PKEY_ENCRYPT_OLD),	"EVP_PKEY_encrypt_old"},
@@ -143,6 +144,7 @@
 {ERR_REASON(EVP_R_DATA_NOT_MULTIPLE_OF_BLOCK_LENGTH),"data not multiple of block length"},
 {ERR_REASON(EVP_R_DECODE_ERROR)          ,"decode error"},
 {ERR_REASON(EVP_R_DIFFERENT_KEY_TYPES)   ,"different key types"},
+{ERR_REASON(EVP_R_DIFFERENT_PARAMETERS)  ,"different parameters"},
 {ERR_REASON(EVP_R_ENCODE_ERROR)          ,"encode error"},
 {ERR_REASON(EVP_R_EVP_PBE_CIPHERINIT_ERROR),"evp pbe cipherinit error"},
 {ERR_REASON(EVP_R_EXPECTING_AN_RSA_KEY)  ,"expecting an rsa key"},
@@ -162,6 +164,7 @@
 {ERR_REASON(EVP_R_NO_CIPHER_SET)         ,"no cipher set"},
 {ERR_REASON(EVP_R_NO_DIGEST_SET)         ,"no digest set"},
 {ERR_REASON(EVP_R_NO_DSA_PARAMETERS)     ,"no dsa parameters"},
+{ERR_REASON(EVP_R_NO_KEY_SET)            ,"no key set"},
 {ERR_REASON(EVP_R_NO_OPERATION_SET)      ,"no operation set"},
 {ERR_REASON(EVP_R_NO_SIGN_FUNCTION_CONFIGURED),"no sign function configured"},
 {ERR_REASON(EVP_R_NO_VERIFY_FUNCTION_CONFIGURED),"no verify function configured"},
diff --git a/crypto/evp/pmeth_fn.c b/crypto/evp/pmeth_fn.c
index 0ad8098..3d09ba2 100644
--- a/crypto/evp/pmeth_fn.c
+++ b/crypto/evp/pmeth_fn.c
@@ -262,6 +262,66 @@
 	return ret;
 	}
 
+int EVP_PKEY_derive_set_peer(EVP_PKEY_CTX *ctx, EVP_PKEY *peer)
+	{
+	int ret;
+	if (!ctx || !ctx->pmeth || !ctx->pmeth->derive || !ctx->pmeth->ctrl)
+		{
+		EVPerr(EVP_F_EVP_PKEY_DERIVE_SET_PEER,
+			EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE);
+		return -2;
+		}
+	if (ctx->operation != EVP_PKEY_OP_DERIVE)
+		{
+		EVPerr(EVP_F_EVP_PKEY_DERIVE_SET_PEER,
+					EVP_R_OPERATON_NOT_INITIALIZED);
+		return -1;
+		}
+
+	ret = ctx->pmeth->ctrl(ctx, EVP_PKEY_CTRL_PEER_KEY, 0, peer);
+
+	if (ret <= 0)
+		return ret;
+
+	if (ret == 2)
+		return 1;
+
+	if (!ctx->pkey)
+		{
+		EVPerr(EVP_F_EVP_PKEY_DERIVE_SET_PEER, EVP_R_NO_KEY_SET);
+		return -1;
+		}
+
+	if (ctx->pkey->type != peer->type)
+		{
+		EVPerr(EVP_F_EVP_PKEY_DERIVE_SET_PEER,
+						EVP_R_DIFFERENT_KEY_TYPES);
+		return -1;
+		}
+
+	if (!EVP_PKEY_missing_parameters(peer) &&
+		!EVP_PKEY_cmp_parameters(ctx->pkey, peer))
+		{
+		EVPerr(EVP_F_EVP_PKEY_DERIVE_SET_PEER,
+						EVP_R_DIFFERENT_PARAMETERS);
+		return -1;
+		}
+
+	ctx->peerkey = peer;
+
+	ret = ctx->pmeth->ctrl(ctx, EVP_PKEY_CTRL_PEER_KEY, 1, peer);
+
+	if (ret <= 0)
+		{
+		ctx->peerkey = NULL;
+		return ret;
+		}
+
+	CRYPTO_add(&peer->references,1,CRYPTO_LOCK_EVP_PKEY);
+	return 1;
+	}
+
+
 int EVP_PKEY_derive(EVP_PKEY_CTX *ctx, unsigned char *key, int *pkeylen)
 	{
 	if (!ctx || !ctx->pmeth || !ctx->pmeth->derive)
diff --git a/crypto/evp/pmeth_lib.c b/crypto/evp/pmeth_lib.c
index 95b1e4e..97fcf26 100644
--- a/crypto/evp/pmeth_lib.c
+++ b/crypto/evp/pmeth_lib.c
@@ -153,6 +153,8 @@
 		ctx->pmeth->cleanup(ctx);
 	if (ctx->pkey)
 		EVP_PKEY_free(ctx->pkey);
+	if (ctx->peerkey)
+		EVP_PKEY_free(ctx->peerkey);
 	OPENSSL_free(ctx);
 	}