[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

PKINIT



Hi all,
a pre-beta version of the pkinit implementation for Heimdal is enclosed. You
can have a look at it but remember it's still under development.
Unfortunatelly I'm out of my office until end of this week so I assume I'll
continue in this work (and post a version of the patch) at the end of the 
next week.

regards

--
Dan
Index: heimdal/kdc/Makefile.am
diff -u heimdal/kdc/Makefile.am:1.1.1.1 heimdal/kdc/Makefile.am:1.1.1.1.2.1
--- heimdal/kdc/Makefile.am:1.1.1.1	Tue Feb 26 15:42:03 2002
+++ heimdal/kdc/Makefile.am	Fri May 10 14:45:00 2002
@@ -33,6 +33,7 @@
 	log.c		\
 	main.c		\
 	misc.c		\
+	pkinit.c	\
 	$(krb4_sources)
 
 
Index: heimdal/kdc/Makefile.in
diff -u heimdal/kdc/Makefile.in:1.1.1.1 heimdal/kdc/Makefile.in:1.2.2.1
--- heimdal/kdc/Makefile.in:1.1.1.1	Tue Feb 26 15:42:03 2002
+++ heimdal/kdc/Makefile.in	Fri May 10 14:45:00 2002
@@ -224,6 +224,7 @@
 	log.c		\
 	main.c		\
 	misc.c		\
+	pkinit.c	\
 	$(krb4_sources)
 
 
@@ -289,11 +290,11 @@
 hpropd_LDFLAGS = 
 @KRB4_FALSE@am_kdc_OBJECTS =  config.$(OBJEXT) connect.$(OBJEXT) \
 @KRB4_FALSE@kerberos5.$(OBJEXT) log.$(OBJEXT) main.$(OBJEXT) \
-@KRB4_FALSE@misc.$(OBJEXT)
+@KRB4_FALSE@misc.$(OBJEXT) pkinit.$(OBJEXT)
 @KRB4_TRUE@am_kdc_OBJECTS =  config.$(OBJEXT) connect.$(OBJEXT) \
 @KRB4_TRUE@kerberos5.$(OBJEXT) log.$(OBJEXT) main.$(OBJEXT) \
 @KRB4_TRUE@misc.$(OBJEXT) 524.$(OBJEXT) kerberos4.$(OBJEXT) \
-@KRB4_TRUE@kaserver.$(OBJEXT)
+@KRB4_TRUE@kaserver.$(OBJEXT) pkinit.$(OBJEXT)
 kdc_OBJECTS =  $(am_kdc_OBJECTS)
 kdc_DEPENDENCIES =  $(top_builddir)/lib/hdb/libhdb.la \
 $(top_builddir)/lib/krb5/libkrb5.la $(top_builddir)/lib/asn1/libasn1.la
Index: heimdal/kdc/config.c
diff -u heimdal/kdc/config.c:1.1.1.1 heimdal/kdc/config.c:1.2.2.5
--- heimdal/kdc/config.c:1.1.1.1	Tue Feb 26 15:42:03 2002
+++ heimdal/kdc/config.c	Wed May 15 14:21:55 2002
@@ -68,6 +68,12 @@
 static struct getarg_strings addresses_str;	/* addresses to listen on */
 krb5_addresses explicit_addresses;
 
+#ifdef PKINIT
+STACK_OF(X509) *pk_certificate = NULL; /* whole certification chain */
+STACK_OF(X509) *pk_trusted_certs = NULL;
+EVP_PKEY *pk_private_key = NULL;
+#endif
+
 #ifdef KRB4
 char *v4_realm;
 int enable_v4 = -1;
@@ -297,6 +425,26 @@
 	}
     }
 
+#ifdef PKINIT
+    {
+       const char *cert_file = NULL;
+       const char *key_file = NULL;
+       const char *ca_dir = NULL;
+
+       cert_file = 
+	   krb5_config_get_string(context, cf,
+			          "kdc", "pk-certificate", NULL);
+       key_file =
+	   krb5_config_get_string(context, cf,
+			          "kdc", "pk-private-key", NULL);
+       ca_dir =
+	   krb5_config_get_string(context, cf,
+			          "kdc", "pk-ca-dir", NULL);
+       pk_load_config(context, cert_file, key_file, ca_dir, 
+	              &pk_certificate, &pk_private_key, &pk_trusted_certs); 
+    }
+#endif
+
 #ifdef KRB4
     if(enable_v4 == -1)
 	enable_v4 = krb5_config_get_bool_default(context, cf, TRUE, "kdc", 
Index: heimdal/kdc/headers.h
diff -u heimdal/kdc/headers.h:1.1.1.1 heimdal/kdc/headers.h:1.1.1.1.2.1
--- heimdal/kdc/headers.h:1.1.1.1	Tue Feb 26 15:42:03 2002
+++ heimdal/kdc/headers.h	Fri May 10 14:45:00 2002
@@ -87,6 +87,10 @@
 #include <parse_units.h>
 #ifdef HAVE_OPENSSL
 #include <openssl/des.h>
+#ifdef PKINIT
+#include <openssl/x509.h>
+#include <openssl/pem.h>
+#endif
 #else
 #include <des.h>
 #endif
Index: heimdal/kdc/kdc_locl.h
diff -u heimdal/kdc/kdc_locl.h:1.1.1.1 heimdal/kdc/kdc_locl.h:1.2.2.2
--- heimdal/kdc/kdc_locl.h:1.1.1.1	Tue Feb 26 15:42:03 2002
+++ heimdal/kdc/kdc_locl.h	Tue May 14 14:30:59 2002
@@ -70,6 +73,12 @@
 extern krb5_boolean enable_kaserver;
 #endif
 
+#ifdef PKINIT
+extern STACK_OF(X509) *pk_certificate;
+extern STACK_OF(X509) *pk_trusted_certs;
+extern EVP_PKEY *pk_private_key;
+#endif 
+
 #define _PATH_KDC_CONF		HDB_DB_DIR "/kdc.conf"
 #define DEFAULT_LOG_DEST	"0-1/FILE:" HDB_DB_DIR "/kdc.log"
 
@@ -115,6 +124,22 @@
 
 #ifdef HAVE_OPENSSL
 #define des_new_random_key des_random_key
+#endif
+
+#ifdef PKINIT
+krb5_error_code
+pk_mk_pa_reply(krb5_context context,
+               unsigned pk_nonce,
+               krb5_keyblock *reply_key,
+               X509 *user_cert,
+               PA_DATA *pa);
+
+krb5_error_code
+pk_rd_padata(krb5_context context,
+             KDC_REQ *req,
+             PA_DATA *pa,
+             unsigned *pk_nonce,
+             X509 **user_cert);
 #endif
 
 #endif /* __KDC_LOCL_H__ */
Index: heimdal/kdc/kerberos5.c
diff -u heimdal/kdc/kerberos5.c:1.1.1.1 heimdal/kdc/kerberos5.c:1.2.2.5
--- heimdal/kdc/kerberos5.c:1.1.1.1	Tue Feb 26 15:42:03 2002
+++ heimdal/kdc/kerberos5.c	Wed May 15 14:05:36 2002
@@ -60,6 +60,18 @@
     }
 }
 
+#ifdef PKINIT
+static void
+set_pk_padata(METHOD_DATA **m, PA_DATA *pa)
+{
+   ALLOC(*m);
+   (*m)->len = 1;
+   ALLOC((*m)->val);
+   (*m)->val->padata_type = pa->padata_type;
+   copy_octet_string(&pa->padata_value, &(*m)->val->padata_value);
+}   
+#endif /* PKINIT */
+
 static PA_DATA*
 find_padata(KDC_REQ *req, int *start, int type)
 {
@@ -443,6 +455,15 @@
     const char *e_text = NULL;
     krb5_crypto crypto;
     Key *ckey, *skey;
+#ifdef PKINIT
+    EncryptionKey reply_key;
+    PA_DATA pk_pa;
+    X509 *user_cert = NULL;
+    unsigned pk_nonce;
+
+    memset(&reply_key, 0, sizeof(reply_key));
+    memset(&pk_pa, 0, sizeof(pk_pa));
+#endif
 
     memset(&rep, 0, sizeof(rep));
 
@@ -497,13 +518,40 @@
 	PA_DATA *pa;
 	int found_pa = 0;
 	kdc_log(5, "Looking for pa-data -- %s", client_name);
-	while((pa = find_padata(req, &i, KRB5_PADATA_ENC_TIMESTAMP))){
+#ifdef PKINIT
+	/* XXX write better find_padata() */
+	while((pa = find_padata(req, &i, 
+               (pk_certificate == NULL) ? KRB5_PADATA_ENC_TIMESTAMP :
+	                                  KRB5_PADATA_PK_AS_REQ)))
+#else
+	while((pa = find_padata(req, &i, KRB5_PADATA_ENC_TIMESTAMP)))
+#endif
+	{
 	    krb5_data ts_data;
 	    PA_ENC_TS_ENC p;
 	    time_t patime;
 	    size_t len;
 	    EncryptedData enc_data;
 	    Key *pa_key;
+#ifdef PKINIT
+	    if (pa->padata_type == KRB5_PADATA_PK_AS_REQ) {
+	       found_pa = 1;
+	       ret = pk_rd_padata(context, req, pa, &pk_nonce, &user_cert);
+	       if (ret) {
+		  goto out;
+	       }
+	       et.flags.pre_authent = 1; 
+	       kdc_log(2, "PKINIT pre-authentication succeded -- %s", client_name);
+				       
+	       krb5_generate_random_keyblock(context, KEYTYPE_DES3, &reply_key);
+
+	       ret = pk_mk_pa_reply(context, pk_nonce, &reply_key, user_cert, &pk_pa);
+	       if (ret) {
+		  goto out;
+	       }
+	    } else
+#endif
+	    {
 	    
 	    found_pa = 1;
 	    
@@ -582,6 +630,7 @@
 	    et.flags.pre_authent = 1;
 	    kdc_log(2, "Pre-authentication succeded -- %s", client_name);
 	    break;
+	    }
 	}
 	if(found_pa == 0 && require_preauth)
 	    goto use_pa;
@@ -849,9 +898,20 @@
 	copy_HostAddresses(et.caddr, ek.caddr);
     }
 
-    set_salt_padata (&rep.padata, ckey->salt);
+#ifdef PKINIT
+    if (reply_key.keytype != 0) /* XXX better check */
+       set_pk_padata(&rep.padata, &pk_pa);
+    else
+       set_salt_padata (&rep.padata, ckey->salt);
+
+    ret = encode_reply(&rep, &et, &ek, setype, server->kvno, &skey->key,
+		       client->kvno, /* XXX */
+		       (reply_key.keytype == 0) ? &ckey->key : &reply_key,
+		       reply);
+#else
     ret = encode_reply(&rep, &et, &ek, setype, server->kvno, &skey->key,
 		       client->kvno, &ckey->key, reply);
+#endif
     free_EncTicketPart(&et);
     free_EncKDCRepPart(&ek);
     free_AS_REP(&rep);
@@ -877,6 +937,9 @@
 	free_ent(client);
     if(server)
 	free_ent(server);
+#ifdef PKINIT
+    /* XXX free reply_key, pk_pa */ 
+#endif
     return ret;
 }
 
Index: heimdal/kdc/pkinit.c
diff -u /dev/null heimdal/kdc/pkinit.c:1.1.2.4
--- /dev/null	Wed May 15 15:07:04 2002
+++ heimdal/kdc/pkinit.c	Wed May 15 14:05:36 2002
@@ -0,0 +1,223 @@
+#ifdef PKINIT
+#include "kdc_locl.h"
+#include "pkinit_asn1.h"
+
+RCSID("$Id$");
+
+static krb5_error_code
+pk_check_pkauthenticator(krb5_context context,
+      			 PKAuthenticator *a,
+			 KDC_REQ *req,
+			 unsigned *pk_nonce)
+{
+   u_char buf[1024];
+   krb5_error_code ret;
+   size_t len;
+
+   memset(buf, 0, sizeof(buf));
+
+   /* XXX check the times */
+   ret = encode_KDC_REQ_BODY(buf + sizeof(buf) - 1, sizeof(buf), &req->req_body,
+	                     &len);
+   if (ret)
+      goto out;
+	
+   ret = krb5_verify_checksum(context,
+	                      NULL,
+	 		      0,
+			      buf + sizeof(buf) - len,
+			      len,
+			      &a->pachecksum);
+   if (ret)
+      goto out;
+
+out:
+   return ret;
+}
+
+static krb5_error_code
+pk_encrypt_key(krb5_context context,
+      	       krb5_keyblock *key,
+               EVP_PKEY *public_key,
+	       krb5_data *encrypted_key)
+{
+   krb5_error_code ret;
+   u_char buf[1024];
+   size_t len;
+
+   ret = encode_EncryptionKey(buf + sizeof(buf) - 1, sizeof(buf), key, &len);
+   if (ret)
+      return ret;
+
+   encrypted_key->length = EVP_PKEY_size(public_key);
+   encrypted_key->data = malloc(encrypted_key->length);
+   if (encrypted_key->data == NULL) {
+      krb5_set_error_string(context, "malloc: out of memory");
+      return ENOMEM;
+   }
+
+   ret = EVP_PKEY_encrypt(encrypted_key->data, 
+	                  buf + sizeof(buf) - len,
+			  len,
+			  public_key);
+   if (ret < 0) {
+      free(encrypted_key->data);
+      ret = -1; /* XXX */
+   }
+
+   return 0;
+}
+
+krb5_error_code
+pk_rd_padata(krb5_context context,
+             KDC_REQ *req,
+             PA_DATA *pa,
+             unsigned *pk_nonce,
+	     X509 **user_cert)
+{
+  krb5_error_code ret;
+  size_t len;
+  PA_PK_AS_REQ r;
+  AuthPack a;
+  int i;
+  
+  memset(&a, 0, sizeof(a));
+  memset(&r, 0, sizeof(r));
+  if (pa->padata_type !=  KRB5_PADATA_PK_AS_REQ)
+     return -1; /* XXX */
+
+  ret = decode_PA_PK_AS_REQ(pa->padata_value.data, pa->padata_value.length,
+		            &r, &len);
+  if (ret)
+     return ret;
+  
+  ret = pk_verify_sign(context, &r.signed_data, pk_trusted_certs, user_cert); 
+  if (ret)
+     goto end;
+
+  /* XXX use something like g_OID_equal() */
+  if (strcmp(r.signed_data.econtent_info.type, OID_PKAUTHDATA)) {
+     ret = -1; /* XXX */
+     goto end;
+  }
+  ret = decode_AuthPack(r.signed_data.econtent_info.content.data,
+	                r.signed_data.econtent_info.content.length,
+			&a, &len);
+  if (ret)
+     goto end;
+
+  ret = pk_check_pkauthenticator(context, &a.pkAuthenticator, req, pk_nonce);
+  if (ret)
+     goto end;
+
+  if (r.trusted_certifiers != NULL && sk_X509_NAME_num(r.trusted_certifiers) > 0) {
+#if 0
+     X509_NAME *name;
+     X509 *kdc_cert = sk_X509_value(pk_certificate, 0);
+     X509_NAME *kdc_issuer = X509_get_issuer_name(kdc_cert);
+     
+     ret = -1; /* ca not found */
+     /* nebude fungovat pro heirarchicke CA */
+     /* also serial_number should be compared */
+     for (i = 0; i < sk_X509_NAME_num(r.trusted_certifiers); i++) {
+	name = sk_X509_NAME_value(r.trusted_certifiers, i);
+	if (X509_NAME_cmp(name, kdc_issuer) == 0) {
+	   ret = 0;
+	   break;
+	}
+     }
+#else
+     ret = 0;
+#endif
+     if (ret)
+	goto end;
+  }
+
+end:
+  free_PA_PK_AS_REQ(&r);
+  free_AuthPack(&a);
+  return ret;
+}
+
+krb5_error_code
+pk_mk_pa_reply(krb5_context context,
+      	       unsigned pk_nonce,
+               krb5_keyblock *reply_key,
+               X509 *user_cert,
+	       PA_DATA *pa)
+{
+  PA_PK_AS_REP rep;
+  SignedData sd;
+  ReplyKeyPack kp;
+  size_t len;
+  u_char buf[8192];
+  krb5_keyblock tmp_key;
+  krb5_error_code ret;
+  krb5_crypto crypto;
+
+  memset(&sd, 0, sizeof(sd));
+  memset(&kp, 0, sizeof(kp));
+
+  /* Prepare signed Data (containg signed reply key and nonce from request) */
+  copy_EncryptionKey(reply_key, &kp.replyKey);
+  kp.nonce = pk_nonce;
+  encode_ReplyKeyPack(buf + sizeof(buf) - 1, sizeof(buf), &kp, &len);
+  free_ReplyKeyPack(&kp);
+
+  sd.econtent_info.type = strdup(OID_PKRKEYDATA);
+  krb5_data_copy(&sd.econtent_info.content, buf + sizeof(buf) - len , len);
+  ret = pk_create_sign(context, pk_certificate, pk_private_key, &sd);
+  if (ret) 
+     goto end;
+
+  /* Create KDC PA reply (containing the SignedData built above)
+     The PA reply is encrypted with a tmp key, which is encrypted with 
+     client's public key and stored within the reply.
+     The DH variant is not implemented. */
+     
+  ret = krb5_generate_random_keyblock(context, KEYTYPE_DES3, &tmp_key);
+  if (ret)
+     goto end;
+
+  rep.recipient_info.rid.issuer =
+     X509_NAME_dup(X509_get_issuer_name(user_cert));
+  rep.recipient_info.rid.serial = 
+     ASN1_INTEGER_dup(X509_get_serialNumber(user_cert)); 
+  rep.recipient_info.etype = ETYPE_ENCRYPT_RSA_PUB; /* XXX */
+  ret = pk_encrypt_key(context, &tmp_key, X509_get_pubkey(user_cert), 
+	               &rep.recipient_info.encrypted_key);
+  if (ret)
+     goto end;
+
+  rep.econtent_info.type = strdup(OID_ID_SIGNEDDATA);
+  rep.econtent_info.etype = tmp_key.keytype; /* XXX */
+
+  ret = encode_SignedData(buf + sizeof(buf) - 1, sizeof(buf), &sd, &len);
+  if (ret)
+     goto end;
+
+  ret = krb5_crypto_init(context, &tmp_key, 0, &crypto);
+  if (ret)
+     goto end;
+  ret = krb5_encrypt(context, crypto, 0,
+	             buf + sizeof(buf) - len, len,
+		     &rep.econtent_info.content);
+  krb5_crypto_destroy(context, crypto);
+  if (ret)
+     goto end;
+
+  ret = encode_PA_PK_AS_REP(buf + sizeof(buf) - 1, sizeof(buf), &rep, &len);
+  if (ret)
+     goto end;
+
+  pa->padata_type = KRB5_PADATA_PK_AS_REP;
+  krb5_data_copy(&pa->padata_value, buf + sizeof(buf) - len, len);
+
+end:
+  memset(&tmp_key, 0, sizeof(tmp_key));
+  free_SignedData(&sd);
+  free_PA_PK_AS_REP(&rep);
+
+  return ret;
+}
+#endif /* PKINIT */
Index: heimdal/kuser/kinit.c
diff -u heimdal/kuser/kinit.c:1.1.1.1 heimdal/kuser/kinit.c:1.2.2.1
--- heimdal/kuser/kinit.c:1.1.1.1	Tue Feb 26 15:42:04 2002
+++ heimdal/kuser/kinit.c	Tue May 14 14:30:59 2002
@@ -57,6 +58,11 @@
 int convert_524;
 #endif
 int fcache_version;
+#ifdef PKINIT
+char *pk_cert_file      = NULL;
+char *pk_key_file       = NULL;
+char *pk_ca_dir         = NULL;
+#endif
 
 static struct getargs args[] = {
 #ifdef KRB4
@@ -117,6 +123,17 @@
     { "anonymous",	0,   arg_flag,	&anonymous_flag,
       "request an anonymous ticket" },
 
+#ifdef PKINIT
+    {  "certificate",  'C',  arg_string, &pk_cert_file,
+       "principal's public key certificate", "filename"},
+       
+    {  "private-key",  'K',  arg_string, &pk_key_file,
+       "principal's private key", "filename" },
+
+    {  "ca-dir",       'D',  arg_string, &pk_ca_dir,
+       "directory with CA certificates", "directory" },
+#endif 
+
     { "version", 	0,   arg_flag, &version_flag },
     { "help",		0,   arg_flag, &help_flag }
 };
@@ -456,6 +475,19 @@
 					       etype_str.num_strings);
     }
 
+#ifdef PKINIT
+    if(pk_cert_file) {
+        ret = krb5_get_init_creds_pkinit(context,
+	      				 &cred,
+					 principal,
+					 pk_cert_file,
+					 pk_key_file,
+					 pk_ca_dir,
+					 start_time,
+					 server,
+					 &opt);
+    } else
+#endif
     if(use_keytab || keytab_str) {
 	krb5_keytab kt;
 	if(keytab_str)
@@ -566,6 +602,13 @@
 
     argc -= optind;
     argv += optind;
+
+#ifdef PKINIT
+    if ((pk_cert_file && !pk_key_file) ||
+	(!pk_cert_file && pk_key_file)) 
+       krb5_err (context, 1, -1, 
+	         "Both certificate and private key must be given");
+#endif
 
     if (argv[0]) {
 	ret = krb5_parse_name (context, argv[0], &principal);
Index: heimdal/lib/asn1/Makefile.am
diff -u heimdal/lib/asn1/Makefile.am:1.1.1.1 heimdal/lib/asn1/Makefile.am:1.1.1.1.2.2
--- heimdal/lib/asn1/Makefile.am:1.1.1.1	Tue Feb 26 15:42:04 2002
+++ heimdal/lib/asn1/Makefile.am	Mon May  6 16:51:40 2002
@@ -2,6 +2,8 @@
 
 include $(top_srcdir)/Makefile.am.common
 
+INCLUDES += $(INCLUDE_des)
+
 YFLAGS = -d
 
 lib_LTLIBRARIES = libasn1.la
@@ -65,6 +67,9 @@
 	asn1_Ticket.x				\
 	asn1_TicketFlags.x			\
 	asn1_TransitedEncoding.x		\
+	asn1_PKAuthenticator.x			\
+	asn1_AuthPack.x				\
+	asn1_ReplyKeyPack.x			\
 	asn1_UNSIGNED.x
 
 
@@ -93,6 +98,7 @@
 	der_length.c				\
 	der_copy.c				\
 	timegm.c				\
+	pkinit_asn1.c				\
 	$(BUILT_SOURCES)
 
 asn1_compile_LDADD = \
@@ -107,7 +113,7 @@
 CLEANFILES = lex.c parse.c parse.h krb5_asn1.h $(BUILT_SOURCES) \
 	$(gen_files) asn1_files
 
-include_HEADERS = krb5_asn1.h asn1_err.h der.h
+include_HEADERS = krb5_asn1.h asn1_err.h der.h pkinit_asn1.h
 
 $(asn1_compile_OBJECTS): parse.h parse.c
 
Index: heimdal/lib/asn1/Makefile.in
diff -u heimdal/lib/asn1/Makefile.in:1.1.1.1 heimdal/lib/asn1/Makefile.in:1.2.2.2
--- heimdal/lib/asn1/Makefile.in:1.1.1.1	Tue Feb 26 15:42:04 2002
+++ heimdal/lib/asn1/Makefile.in	Mon May  6 16:51:40 2002
@@ -137,7 +137,7 @@
 
 SUFFIXES = .et .h .1 .3 .5 .8 .cat1 .cat3 .cat5 .cat8 .x
 
-INCLUDES = -I$(top_builddir)/include $(INCLUDES_roken)
+INCLUDES = -I$(top_builddir)/include $(INCLUDES_roken) $(INCLUDE_des)
 
 AM_CFLAGS = $(WFLAGS)
 
@@ -263,6 +263,9 @@
 	asn1_Ticket.x				\
 	asn1_TicketFlags.x			\
 	asn1_TransitedEncoding.x		\
+	asn1_PKAuthenticator.x			\
+	asn1_AuthPack.x				\
+	asn1_ReplyKeyPack.x			\
 	asn1_UNSIGNED.x
 
 
@@ -292,6 +295,7 @@
 	der_length.c				\
 	der_copy.c				\
 	timegm.c				\
+	pkinit_asn1.c				\
 	$(BUILT_SOURCES)
 
 
@@ -306,11 +310,11 @@
 
 asn1_print_LDADD = $(check_der_LDADD)
 
-CLEANFILES = lex.c parse.c parse.h krb5_asn1.h $(BUILT_SOURCES) \
+CLEANFILES = lex.c parse.c parse.h krb5_asn1.h pkinit_asn1.h $(BUILT_SOURCES) \
 	$(gen_files) asn1_files
 
 
-include_HEADERS = krb5_asn1.h asn1_err.h der.h
+include_HEADERS = krb5_asn1.h asn1_err.h der.h pkinit_asn1.h
 
 EXTRA_DIST = asn1_err.et
 subdir = lib/asn1
@@ -329,7 +333,7 @@
 X_PRE_LIBS = @X_PRE_LIBS@
 libasn1_la_DEPENDENCIES = 
 am_libasn1_la_OBJECTS =  der_get.lo der_put.lo der_free.lo der_length.lo \
-der_copy.lo timegm.lo asn1_APOptions.lo asn1_AP_REP.lo asn1_AP_REQ.lo \
+der_copy.lo timegm.lo pkinit_asn1.lo asn1_APOptions.lo asn1_AP_REP.lo asn1_AP_REQ.lo \
 asn1_AS_REP.lo asn1_AS_REQ.lo asn1_Authenticator.lo \
 asn1_AuthorizationData.lo asn1_CKSUMTYPE.lo asn1_Checksum.lo \
 asn1_ENCTYPE.lo asn1_ETYPE_INFO.lo asn1_ETYPE_INFO_ENTRY.lo \
@@ -345,6 +349,7 @@
 asn1_PA_DATA.lo asn1_PA_ENC_TS_ENC.lo asn1_Principal.lo \
 asn1_PrincipalName.lo asn1_Realm.lo asn1_TGS_REP.lo asn1_TGS_REQ.lo \
 asn1_Ticket.lo asn1_TicketFlags.lo asn1_TransitedEncoding.lo \
+asn1_PKAuthenticator.lo asn1_AuthPack.lo asn1_ReplyKeyPack.lo \
 asn1_UNSIGNED.lo asn1_err.lo
 libasn1_la_OBJECTS =  $(am_libasn1_la_OBJECTS)
 check_PROGRAMS =  check-der$(EXEEXT)
Index: heimdal/lib/asn1/k5.asn1
diff -u heimdal/lib/asn1/k5.asn1:1.1.1.1 heimdal/lib/asn1/k5.asn1:1.1.1.1.2.1
--- heimdal/lib/asn1/k5.asn1:1.1.1.1	Tue Feb 26 15:42:04 2002
+++ heimdal/lib/asn1/k5.asn1	Tue Apr 30 11:00:53 2002
@@ -443,6 +443,45 @@
 
 DOMAIN-X500-COMPRESS	INTEGER ::= 1
 
+--
+-- PKINIT
+PKAuthenticator ::= SEQUENCE {
+	cusec[0]		INTEGER,
+				-- for replay prevention as in RFC 1510bis
+	ctime[1]		KerberosTime,
+				-- for replay prevention as in RFC 1510bis
+	nonce[2]		INTEGER,
+				-- zero only if client will accept
+				-- cached DH parameters from KDC;
+				-- must be non-zero otherwise
+	pachecksum[3]		Checksum
+				-- Checksum over KDC-REQ-BODY
+				-- Defined by Kerberos spec;
+				-- must be unkeyed, e.g. sha1 or rsa-md5
+}
+
+AuthPack ::= SEQUENCE {
+	pkAuthenticator[0]	PKAuthenticator
+-- DK: DH is not supported since SubjectPublicKeyInfo is not defined
+--	clientPublicValue[1]	SubjectPublicKeyInfo OPTIONAL
+				-- if client is using Diffie-Hellman
+				-- (ephemeral-ephemeral only)
+}
+
+ReplyKeyPack ::= SEQUENCE {
+				-- not used for Diffie-Hellman
+	replyKey[0]		EncryptionKey,
+				-- from RFC 1510bis
+				-- used to encrypt main reply
+				-- ENCTYPE is at least as strong as
+				-- ENCTYPE of session key
+	nonce[1]		INTEGER
+				-- binds response to the request
+				-- must be same as the nonce
+				-- passed in the PKAuthenticator
+}
+
+
 END
 
 -- etags -r '/\([A-Za-z][-A-Za-z0-9]*\).*::=/\1/' k5.asn1
Index: heimdal/lib/asn1/pkinit_asn1.c
diff -u /dev/null heimdal/lib/asn1/pkinit_asn1.c:1.1.2.6
--- /dev/null	Wed May 15 15:07:04 2002
+++ heimdal/lib/asn1/pkinit_asn1.c	Wed May 15 14:04:02 2002
@@ -0,0 +1,477 @@
+/* Generated by hand :-) */
+/* Feel free to edit */
+
+#include "libasn1.h"
+#include "pkinit_asn1.h"
+
+#define BACK if (e) return e; p -= l; len -= l; ret += l
+#define FORW if(e) goto fail; p += l; len -= l; ret += l
+
+/* All the i2d_TYPE_bio() functions return 1 on success, 0 otherwise. 
+   Unfortunatelly this is different from simple i2d_TYPE() functions, which 
+   return number of bytes. */
+
+static int
+i2d_X509_NAME_bio(BIO *bio, X509_NAME *data)
+{
+   return ASN1_i2d_bio(i2d_X509_NAME, bio, (unsigned char *)data);
+}
+
+static int
+i2d_ASN1_INTEGER_bio(BIO *bio, ASN1_INTEGER *data)
+{
+   return ASN1_i2d_bio(i2d_ASN1_INTEGER, bio, (unsigned char *)data);
+}
+
+#if 0
+static X509_NAME *
+d2i_X509_NAME_bio(BIO *bio, X509_NAME **data)
+{
+   return (X509_NAME *) ASN1_d2i_bio((char *(*)()) X509_NAME_new,
+			             (char *(*)()) d2i_X509_NAME,
+				     bio,
+				     (unsigned char **) data);
+}
+
+static ASN1_INTEGER *
+d2i_ASN1_INTEGER_bio(BIO *bio, ASN1_INTEGER **data)
+{
+   return (ASN1_INTEGER *) ASN1_d2i_bio((char *(*)()) ASN1_INTEGER_new,
+		                        (char *(*)()) d2i_ASN1_INTEGER,
+				        bio,
+					(unsigned char **) data);
+}
+#endif
+
+
+static int
+bio_to_buffer(BIO *bio, unsigned char *p, size_t len, size_t *size)
+{
+  size_t bio_len;
+  int e;
+
+  bio_len = BIO_pending(bio);
+  if (bio_len > len)
+     return ASN1_OVERFLOW; /* XXX */
+
+  e = BIO_read(bio, p - bio_len + 1, bio_len);
+  if (e == 0)
+     return -1; /* XXX */
+
+  *size = bio_len;
+  return 0;
+}
+
+#if 0
+static int
+buffer_to_bio(const unsigned char *p, size_t len, BIO **bio)
+{
+  BIO *ret_bio = NULL;
+  int e;
+
+  ret_bio = BIO_new(BIO_s_mem());
+  if (ret_bio == NULL)
+     return -1; /* XXX */
+
+  e = BIO_write(ret_bio, (unsigned char *) p, len);
+  if (e == 0) {
+     BIO_free(ret_bio);
+     return -1; /* XXX */
+  }
+
+  *bio = ret_bio;
+  return 0;
+}
+#endif
+
+/*
+ * IssuerAndSerialNumber
+ */
+int
+encode_IssuerAndSerialNumber(unsigned char *p, size_t len, const IssuerAndSerialNumber *data, size_t *size)
+{
+   size_t ret = 0;
+   size_t l;
+   int e;
+   BIO *bio = NULL;
+
+   bio = BIO_new(BIO_s_mem());
+   if (bio == NULL)
+      return -1; /* XXX */
+
+   e = i2d_X509_NAME_bio(bio, data->issuer);
+   if (e != 1) {
+      BIO_free(bio);
+      return -1; /* XXX */
+   }
+
+   e = i2d_ASN1_INTEGER_bio(bio, data->serial);
+   if (e != 1) {
+      BIO_free(bio);
+      return -1; /* XXX */
+   }
+
+   e = bio_to_buffer(bio, p, len, &l);
+   BACK;
+
+   *size = ret;
+   return 0;
+}
+
+int 
+decode_IssuerAndSerialNumber(const unsigned char *p, size_t len, IssuerAndSerialNumber *data, size_t *size)
+{
+  size_t ret = 0;
+  size_t l;
+  int e;
+  
+  memset(data, 0, sizeof(*data));
+  {
+    unsigned char *begin;
+
+    begin = (unsigned char *)p;
+    data->issuer = d2i_X509_NAME(NULL, (unsigned char **)&p, len);
+    if (data->issuer == NULL) {
+       e = -1; /* XXX */
+       goto fail;
+    }
+    l = p - begin;
+    len -= l;
+    ret += l;
+
+    begin = (unsigned char *)p;
+    data->serial = d2i_ASN1_INTEGER(NULL, (unsigned char **)&p, len);
+    if (data->issuer == NULL) {
+       e = -1; /* XXX */
+       goto fail;
+    }
+    l = p - begin;
+    len -= l;
+    ret += l;
+  }
+
+  if (size) *size = ret;
+  return 0;
+
+fail:
+  free_IssuerAndSerialNumber(data);
+  return e;
+}
+
+void
+free_IssuerAndSerialNumber(IssuerAndSerialNumber *data)
+{
+  if (data->issuer)
+     X509_NAME_free(data->issuer);
+  if (data->serial)
+     ASN1_INTEGER_free(data->serial);
+}
+
+/*
+ * SignedData
+ */
+int
+encode_SignedData(unsigned char *p, size_t len, const SignedData *data, size_t *size)
+{
+  size_t ret = 0;
+  size_t l;
+  int e;
+
+  e = encode_general_string(p, len, &data->econtent_info.type, &l);
+  BACK;
+  e = encode_octet_string(p, len, &data->econtent_info.content, &l);
+  BACK;
+
+  {
+     unsigned num;
+     int i;
+     BIO *bio = NULL;
+
+     num = sk_X509_num(data->chain);
+     if (num < 0)
+	return -1; /* XXX */
+
+     bio = BIO_new(BIO_s_mem());
+     if (bio == NULL)
+        return -1; /* XXX */
+     
+     for (i = 0; i < num; i++) {
+	e = i2d_X509_bio(bio, sk_X509_value(data->chain, i));
+	if (e != 1) {
+           BIO_free(bio);
+	   return -1;
+	}
+     }
+     e = bio_to_buffer(bio, p, len, &l);
+     BIO_free(bio);
+     BACK;
+
+     e = encode_unsigned(p, len, &num, &l);
+     BACK;
+  }
+	
+  e = encode_IssuerAndSerialNumber(p, len, &data->signer_info.sid, &l);
+  BACK;
+  e = encode_CKSUMTYPE(p, len, &data->signer_info.digest_type, &l);
+  BACK;
+  e = encode_ENCTYPE(p, len, &data->signer_info.signature_type, &l);
+  BACK;
+  e = encode_octet_string(p, len, &data->econtent_info.content, &l);
+  BACK;
+
+  *size = ret;
+  return 0;
+}
+
+int
+decode_SignedData(const unsigned char *p, size_t len, SignedData *data, size_t *size)
+{
+  size_t ret = 0;
+  size_t l;
+  int e;
+
+  memset(data, 0, sizeof(*data));
+
+  e = decode_octet_string(p, len, &data->econtent_info.content, &l);
+  FORW;
+  e = decode_ENCTYPE(p, len, &data->signer_info.signature_type, &l);
+  FORW;
+  e = decode_CKSUMTYPE(p, len, &data->signer_info.digest_type, &l);
+  FORW;
+  e = decode_IssuerAndSerialNumber(p, len, &data->signer_info.sid, &l);
+  FORW;
+
+  {
+     unsigned num;
+     X509 *cert; 
+     int i;
+     unsigned char *begin;
+
+     e = decode_unsigned(p, len, &num, &l);
+     FORW;
+
+     data->chain = sk_X509_new_null();
+     for (i = 0; i < num; i++) {
+	 begin = (unsigned char *)p;
+	 cert = d2i_X509(NULL, (unsigned char **)&p, len);
+	 if (cert == NULL) {
+            e = -1; /* XXX */
+	    goto fail;
+	 }
+	 sk_X509_push(data->chain, cert);
+	 l = p - begin;
+	 len -= l;
+	 ret += l;
+     }
+		
+  }
+
+  e = decode_octet_string(p, len, &data->econtent_info.content, &l);
+  FORW;
+  e = decode_general_string(p, len, &data->econtent_info.type, &l);
+  FORW;
+  
+  if (size) *size = ret;
+  return 0;
+  
+fail:
+  free_SignedData(data);
+  return e;
+}
+
+void
+free_SignedData(SignedData *data)
+{
+  if (data->econtent_info.type)
+     free_general_string(&data->econtent_info.type);
+  if (data->econtent_info.content.length)
+     free_octet_string(&data->econtent_info.content);
+
+  if (data->chain)
+     sk_X509_pop_free(data->chain, X509_free);
+
+  free_IssuerAndSerialNumber(&data->signer_info.sid);
+  free_CKSUMTYPE(&data->signer_info.digest_type);
+  free_ENCTYPE(&data->signer_info.signature_type);
+  if (data->signer_info.signature.length)
+     free_octet_string(&data->signer_info.signature);
+}
+
+/*
+ * PA_PK_AS_REQ
+ */
+int
+encode_PA_PK_AS_REQ(unsigned char *p, size_t len, const PA_PK_AS_REQ *data, size_t *size)
+{
+  size_t ret = 0;
+  size_t l;
+  int e;
+
+  e = encode_SignedData(p, len, &data->signed_data, &l);
+  BACK;
+
+  {
+     unsigned num;
+     int i;
+     BIO *bio = NULL;
+
+     num = sk_X509_NAME_num(data->trusted_certifiers);
+     if (num < 0)
+	return -1; /* XXX */
+
+     bio = BIO_new(BIO_s_mem());
+     if (bio == NULL)
+	return -1; /* XXX */
+
+     for (i = 0; i < num; i++) {
+	 /* pozor na ssleay.doc:650 ! */
+	 e = i2d_X509_NAME_bio(bio, sk_X509_NAME_value(data->trusted_certifiers, i));
+	 if (e != 1) {
+            BIO_free(bio);
+	    return -1;
+	 }
+     }
+     e = bio_to_buffer(bio, p, len, &l);
+     BIO_free(bio);
+     BACK;
+
+     e = encode_unsigned(p, len, &num, &l);
+     BACK;
+  }
+
+  *size = ret;
+  return 0;
+}
+
+int
+decode_PA_PK_AS_REQ(const unsigned char *p, size_t len, PA_PK_AS_REQ *data, size_t *size)
+{
+  size_t ret = 0;
+  size_t l;
+  int e;
+
+  memset(data, 0, sizeof(*data));
+  {
+    unsigned certs_num;
+    X509_NAME *name = NULL;
+    unsigned char *begin;
+    int i;
+
+    e = decode_unsigned(p, len, &certs_num, &l);
+    FORW;
+
+    if (certs_num > 0) {
+       data->trusted_certifiers = sk_X509_NAME_new_null();
+       for (i = 0; i < certs_num; i++) {
+          begin = (unsigned char *)p;
+          name = d2i_X509_NAME(NULL, (unsigned char **)&p, len);
+          if (name == NULL) {
+	     e = -1; /* XXX */
+	     goto fail;
+          }
+          sk_X509_NAME_push(data->trusted_certifiers, name);
+          l = p - begin;
+          len -= l;
+          ret += l;
+       }
+    }
+  }
+
+  e = decode_SignedData(p, len, &data->signed_data, &l);
+  FORW;
+
+  if (size) *size = ret;
+  return 0;
+  
+fail:
+  free_PA_PK_AS_REQ(data);
+  return e;
+}
+
+void
+free_PA_PK_AS_REQ(PA_PK_AS_REQ *data)
+{
+  free_SignedData(&data->signed_data);
+  if (data->trusted_certifiers)
+     sk_X509_NAME_pop_free(data->trusted_certifiers, X509_NAME_free);
+}
+
+
+/*
+ * PA_PK_AS_REP
+ */
+int
+encode_PA_PK_AS_REP(unsigned char *p, size_t len, const PA_PK_AS_REP *data, size_t *size)
+{
+  size_t ret = 0;
+  size_t l;
+  int e;
+
+  /* recipient_info */
+  e = encode_IssuerAndSerialNumber(p, len, &data->recipient_info.rid, &l);
+  BACK;
+  e = encode_ENCTYPE(p, len, &data->recipient_info.etype, &l);
+  BACK;
+  e = encode_octet_string(p, len, &data->recipient_info.encrypted_key, &l);
+  BACK;
+
+  /* econtent_info */
+  e = encode_general_string(p, len, &data->econtent_info.type, &l);
+  BACK;
+  e = encode_ENCTYPE(p, len, &data->econtent_info.etype, &l);
+  BACK;
+  e = encode_octet_string(p, len, &data->econtent_info.content, &l);
+  BACK;
+
+  *size = ret;
+  return 0;
+}
+
+
+int
+decode_PA_PK_AS_REP(const unsigned char *p, size_t len, PA_PK_AS_REP *data, size_t *size)
+{
+   size_t ret = 0;
+   size_t l;
+   int e;
+
+   memset(data, 0, sizeof(*data));
+
+   /* econtent_info */
+   e = decode_octet_string(p, len, &data->econtent_info.content, &l);
+   FORW;
+   e = decode_ENCTYPE(p, len, &data->econtent_info.etype, &l);
+   FORW;
+   e = decode_general_string(p, len, &data->econtent_info.type, &l);
+   FORW;
+
+   /* recipient_info */
+   e = decode_octet_string(p, len, &data->recipient_info.encrypted_key, &l);
+   FORW;
+   e = decode_ENCTYPE(p, len, &data->recipient_info.etype, &l);
+   FORW;
+   e = decode_IssuerAndSerialNumber(p, len, &data->recipient_info.rid, &l);
+   FORW;
+
+   if (size) *size = ret;
+   return 0;
+
+fail:
+   free_PA_PK_AS_REP(data);
+   return e;
+}
+
+void
+free_PA_PK_AS_REP(PA_PK_AS_REP *data)
+{
+   free_IssuerAndSerialNumber(&data->recipient_info.rid);
+   free_ENCTYPE(&data->recipient_info.etype);
+   if (data->recipient_info.encrypted_key.length)
+      free_octet_string(&data->recipient_info.encrypted_key);
+
+   if (data->econtent_info.type)
+      free_general_string(&data->econtent_info.type);
+   free_ENCTYPE(&data->econtent_info.etype);
+   if (data->econtent_info.content.length)
+      free_octet_string(&data->econtent_info.content);
+}
Index: heimdal/lib/asn1/pkinit_asn1.h
diff -u /dev/null heimdal/lib/asn1/pkinit_asn1.h:1.1.2.4
--- /dev/null	Wed May 15 15:07:04 2002
+++ heimdal/lib/asn1/pkinit_asn1.h	Fri May 10 14:45:00 2002
@@ -0,0 +1,89 @@
+#ifndef __PKINIT_ASN1_H__
+#define __PKINIT_ASN1_H__
+
+#ifdef PKINIT
+#include "krb5_asn1.h"
+#include <openssl/x509.h>
+#include <openssl/bio.h>
+
+#include <openssl/pem.h>
+#include <openssl/err.h>
+/* XXX ^^^ snad jen docasne */
+
+/* For encoding OIDs see rfc2743 section 3.1, item 5. */
+
+/* pkauthdata (in client's as-req):
+   iso (1) org (3) dod (6) internet (1)
+   security (5) kerberosv5 (2) pkinit (3) pkauthdata (1) */
+#define OID_PKAUTHDATA "\x2b\x06\x01\x05\x02\x03\x01"
+
+/* id-signedData (in KDC's reply):
+   iso (1) member-body (2) us (840)
+   rsadsi (113549) pkcs (1) pkcs7 (7) signedData (2) */
+#define OID_ID_SIGNEDDATA "\x2a\x86\x48\x86\xf7\x0d\x01\x07\x02"
+
+/* pkrkeydata (in KDC's reply):
+   iso (1) org (3) dod (6) internet (1)
+   security (5) kerberosv5 (2) pkinit (3) pkrkeydata (3) */
+#define OID_PKRKEYDATA "\x2b\x06\x01\x05\x02\x03\x03"
+
+typedef struct IssuerAndSerialNumber{
+  X509_NAME *issuer; 
+  ASN1_INTEGER *serial;
+} IssuerAndSerialNumber;
+
+int encode_IssuerAndSerialNumber(unsigned char *, size_t, const IssuerAndSerialNumber *, size_t *);
+int decode_IssuerAndSerialNumber(const unsigned char *, size_t, IssuerAndSerialNumber *, size_t *);
+void free_IssuerAndSerialNumber(IssuerAndSerialNumber *);
+
+typedef struct SignedData {
+  struct {
+    general_string type;
+    octet_string content;
+  } econtent_info;
+  STACK_OF(X509) *chain;
+  struct {
+    IssuerAndSerialNumber sid;  /* SignerIdentifier */
+    CKSUMTYPE digest_type;
+    ENCTYPE signature_type; /* XXX ? X509_ALGOR */
+    octet_string signature;
+  } signer_info;
+} SignedData; 
+
+int encode_SignedData(unsigned char *, size_t, const SignedData *, size_t *);
+int decode_SignedData(const unsigned char *, size_t, SignedData *, size_t *);
+void free_SignedData(SignedData *);
+
+typedef struct PA_PK_AS_REQ {
+  SignedData signed_data;
+  STACK_OF(X509_NAME) *trusted_certifiers;
+} PA_PK_AS_REQ;
+
+int encode_PA_PK_AS_REQ(unsigned char *, size_t, const PA_PK_AS_REQ *, size_t *);
+int decode_PA_PK_AS_REQ(const unsigned char *, size_t, PA_PK_AS_REQ *, size_t *);
+void free_PA_PK_AS_REQ(PA_PK_AS_REQ *);
+
+typedef struct EnvelopedData {
+  struct {
+    IssuerAndSerialNumber rid;
+    ENCTYPE etype; /* XXX ? X509_ALGOR */
+    octet_string encrypted_key;
+  } recipient_info;
+  struct {
+    general_string type;
+    ENCTYPE etype; /* XXX ? X509_ALGOR */
+    octet_string content;
+  } econtent_info;
+} EnvelopedData;
+
+typedef EnvelopedData PA_PK_AS_REP;
+
+int encode_PA_PK_AS_REP(unsigned char *, size_t, const PA_PK_AS_REP *, size_t *);
+int decode_PA_PK_AS_REP(const unsigned char *, size_t, PA_PK_AS_REP *, size_t *);
+void free_PA_PK_AS_REP(PA_PK_AS_REP *);
+
+
+
+#endif /* PKINIT */
+
+#endif /* __PKINIT_ASN1_H__ */
Index: heimdal/lib/krb5/Makefile.am
diff -u heimdal/lib/krb5/Makefile.am:1.1.1.1 heimdal/lib/krb5/Makefile.am:1.1.1.1.2.1
--- heimdal/lib/krb5/Makefile.am:1.1.1.1	Tue Feb 26 15:42:04 2002
+++ heimdal/lib/krb5/Makefile.am	Sun May  5 19:43:04 2002
@@ -120,6 +120,7 @@
 	version.c				\
 	warn.c					\
 	write_message.c				\
+	pkinit.c				\
 	$(ERR_FILES)
 
 libkrb5_la_LDFLAGS = -version-info 18:2:1
Index: heimdal/lib/krb5/Makefile.in
diff -u heimdal/lib/krb5/Makefile.in:1.1.1.1 heimdal/lib/krb5/Makefile.in:1.2.2.1
--- heimdal/lib/krb5/Makefile.in:1.1.1.1	Tue Feb 26 15:42:04 2002
+++ heimdal/lib/krb5/Makefile.in	Sun May  5 19:43:04 2002
@@ -317,6 +317,7 @@
 	version.c				\
 	warn.c					\
 	write_message.c				\
+	pkinit.c				\
 	$(ERR_FILES)
 
 
@@ -391,7 +392,7 @@
 read_message.lo recvauth.lo replay.lo send_to_kdc.lo sendauth.lo \
 set_default_realm.lo sock_principal.lo store.lo store_emem.lo \
 store_fd.lo store_mem.lo ticket.lo time.lo transited.lo verify_init.lo \
-verify_user.lo version.lo warn.lo write_message.lo krb5_err.lo \
+verify_user.lo version.lo warn.lo write_message.lo pkinit.lo krb5_err.lo \
 heim_err.lo k524_err.lo
 libkrb5_la_OBJECTS =  $(am_libkrb5_la_OBJECTS)
 bin_PROGRAMS =  verify_krb5_conf$(EXEEXT)
Index: heimdal/lib/krb5/get_in_tkt.c
diff -u heimdal/lib/krb5/get_in_tkt.c:1.1.1.1 heimdal/lib/krb5/get_in_tkt.c:1.1.1.1.2.5
--- heimdal/lib/krb5/get_in_tkt.c:1.1.1.1	Tue Feb 26 15:42:04 2002
+++ heimdal/lib/krb5/get_in_tkt.c	Wed May 15 14:08:33 2002
@@ -427,6 +427,12 @@
 	     krb5_key_proc key_proc,
 	     krb5_const_pointer keyseed,
 	     unsigned nonce,
+#ifdef PKINIT
+	     STACK_OF(X509) *cert,
+	     EVP_PKEY *private_key,
+	     STACK_OF(X509) *trusted_certs,
+	     unsigned pk_nonce,
+#endif     
 	     AS_REQ *a)
 {
     krb5_error_code ret;
@@ -585,7 +591,27 @@
 	add_padata(context, a->padata, creds->client, 
 		   key_proc, keyseed, a->req_body.etype.val,
 		   a->req_body.etype.len, &salt);
-    } else {
+    }
+#ifdef PKINIT
+    else if (*ptypes == KRB5_PADATA_PK_AS_REQ) {
+        ALLOC(a->padata, 1);
+	if (a->padata == NULL) {
+	   ret = ENOMEM;
+	   krb5_set_error_string(context, "malloc: out of memory");
+	   goto fail;
+	}
+	a->padata->len = 0;
+	a->padata->val = NULL;
+	ret = pk_mk_padata(context,
+		           cert, private_key, trusted_certs,
+       			   &a->req_body,
+			   pk_nonce,
+			   a->padata);
+	if (ret)
+		goto fail;
+    }
+#endif /* PKINIT */ 
+    else {
 	krb5_set_error_string (context, "pre-auth type %d not supported",
 			       *ptypes);
 	ret = KRB5_PREAUTH_BAD_TYPE;
@@ -640,18 +666,23 @@
 }
 
 krb5_error_code
-krb5_get_in_cred(krb5_context context,
-		 krb5_flags options,
-		 const krb5_addresses *addrs,
-		 const krb5_enctype *etypes,
-		 const krb5_preauthtype *ptypes,
-		 const krb5_preauthdata *preauth,
-		 krb5_key_proc key_proc,
-		 krb5_const_pointer keyseed,
-		 krb5_decrypt_proc decrypt_proc,
-		 krb5_const_pointer decryptarg,
-		 krb5_creds *creds,
-		 krb5_kdc_rep *ret_as_reply)
+krb5_get_in_cred_ext(krb5_context context,
+		     krb5_flags options,
+		     const krb5_addresses *addrs,
+		     const krb5_enctype *etypes,
+		     const krb5_preauthtype *ptypes,
+		     const krb5_preauthdata *preauth,
+		     krb5_key_proc key_proc,
+		     krb5_const_pointer keyseed,
+		     krb5_decrypt_proc decrypt_proc,
+		     krb5_const_pointer decryptarg,
+#ifdef PKINIT
+		     void *p_cert,          /* STACK_OF(X509) */
+		     void *p_private_key,   /* EVP_PKEY */
+		     void *p_trusted_certs, /* STACK_OF(X509) */
+#endif
+		     krb5_creds *creds,
+		     krb5_kdc_rep *ret_as_reply)
 {
     krb5_error_code ret;
     AS_REQ a;
@@ -666,12 +697,21 @@
     krb5_enctype etype;
     krb5_preauthdata *my_preauth = NULL;
     unsigned nonce;
+#ifdef PKINIT
+    unsigned pk_nonce;
+    X509 *user_cert;
+    STACK_OF(X509) *cert = (STACK_OF(X509) *) p_cert;
+    EVP_PKEY *private_key = (EVP_PKEY *) p_private_key;
+    STACK_OF(X509) *trusted_certs = (STACK_OF(X509) *) p_trusted_certs;
+#endif
     int done;
 
     opts.i = options;
 
     krb5_generate_random_block (&nonce, sizeof(nonce));
     nonce &= 0xffffffff;
+    krb5_generate_random_block (&pk_nonce, sizeof(pk_nonce));
+    pk_nonce &= 0xffffffff;
 
     do {
 	done = 1;
@@ -685,6 +725,12 @@
 			   key_proc,
 			   keyseed,
 			   nonce,
+#ifdef PKINIT
+			   cert,
+			   private_key,
+			   trusted_certs,
+			   pk_nonce,
+#endif
 			   &a);
 	if (my_preauth) {
 	    free_ETYPE_INFO(&my_preauth->val[0].info);
@@ -757,12 +803,34 @@
 				  rep.kdc_rep.padata->len, 
 				  KRB5_PADATA_AFS3_SALT, &index);
 	}
+#ifdef PKINIT
+	if (pa == NULL && *ptypes == KRB5_PADATA_PK_AS_REQ) {
+	     index = 0;
+	     pa = krb5_find_padata(rep.kdc_rep.padata->val,
+			           rep.kdc_rep.padata->len,
+				   KRB5_PADATA_PK_AS_REP, &index);
+	}
+#endif
     }
     if(pa) {
-	salt.salttype = pa->padata_type;
-	salt.saltvalue = pa->padata_value;
+#ifdef PKINIT
+	if (pa->padata_type == KRB5_PADATA_PK_AS_REP) {
+	  user_cert = sk_X509_value(cert, 0);
+          ret = pk_rd_pa_reply(context,
+	                       user_cert,
+		       	       private_key,
+			       trusted_certs, 
+                               pk_nonce, 
+			       pa, 
+			       &key);
+	} else 
+#endif
+	{
+	  salt.salttype = pa->padata_type;
+	  salt.saltvalue = pa->padata_value;
 	
-	ret = (*key_proc)(context, etype, salt, keyseed, &key);
+	  ret = (*key_proc)(context, etype, salt, keyseed, &key);
+	}
     } else {
 	/* make a v5 salted pa-data */
 	ret = krb5_get_pw_salt (context, creds->client, &salt);
@@ -798,6 +866,40 @@
 	krb5_free_kdc_rep (context, &rep);
     return ret;
 }
+
+krb5_error_code
+krb5_get_in_cred(krb5_context context,
+                 krb5_flags options,
+                 const krb5_addresses *addrs,
+                 const krb5_enctype *etypes,
+                 const krb5_preauthtype *ptypes,
+                 const krb5_preauthdata *preauth,
+                 krb5_key_proc key_proc,
+                 krb5_const_pointer keyseed,
+                 krb5_decrypt_proc decrypt_proc,
+                 krb5_const_pointer decryptarg,
+                 krb5_creds *creds,
+                 krb5_kdc_rep *ret_as_reply)
+{
+   return krb5_get_in_cred_ext(context,
+                               options,
+			       addrs,
+			       etypes,
+			       ptypes,
+			       preauth,
+			       key_proc,
+			       keyseed,
+			       decrypt_proc,
+			       decryptarg,
+#ifdef PKINIT
+			       NULL,
+			       NULL,
+			       NULL,
+#endif
+			       creds,
+			       ret_as_reply);
+}
+
 
 krb5_error_code
 krb5_get_in_tkt(krb5_context context,
Index: heimdal/lib/krb5/init_creds_pw.c
diff -u heimdal/lib/krb5/init_creds_pw.c:1.1.1.1 heimdal/lib/krb5/init_creds_pw.c:1.1.1.1.2.2
--- heimdal/lib/krb5/init_creds_pw.c:1.1.1.1	Tue Feb 26 15:42:04 2002
+++ heimdal/lib/krb5/init_creds_pw.c	Wed May 15 14:08:33 2002
@@ -542,3 +542,80 @@
     krb5_free_creds_contents (context, &this_cred);
     return ret;
 }
+
+#ifdef PKINIT
+krb5_error_code
+krb5_get_init_creds_pkinit(krb5_context context,
+      			   krb5_creds *creds,
+			   krb5_principal client,
+			   char *pk_cert_file,
+			   char *pk_key_file,
+			   char *pk_ca_dir,
+			   krb5_deltat start_time,
+			   const char *in_tkt_service,
+			   krb5_get_init_creds_opt *options)
+{
+   krb5_error_code ret;
+   krb5_kdc_flags flags;
+   krb5_addresses *addrs = NULL;
+   krb5_enctype *etypes = NULL;
+   krb5_preauthtype *pre_auth_types = NULL;
+   krb5_preauthtype pk_pre_auth_types[2] = {KRB5_PADATA_PK_AS_REQ, KRB5_PADATA_NONE};
+   krb5_creds this_cred;
+   krb5_keytab_key_proc_args *a;
+   STACK_OF(X509) *user_cert = NULL;
+   EVP_PKEY *user_key = NULL;
+   STACK_OF(X509) *trusted_certs = NULL;
+
+   ret = get_init_creds_common(context, creds, client, start_time,
+	 		       in_tkt_service, options,
+			       &addrs, &etypes, &this_cred, &pre_auth_types,
+			       &flags);
+   if(ret)
+       goto out;
+
+   a = malloc (sizeof(*a));
+   if (a == NULL) {
+       krb5_set_error_string(context, "malloc: out of memory");
+       ret = ENOMEM;
+       goto out;
+   }
+   a->principal = this_cred.client;
+
+   ret = pk_load_config(context, pk_cert_file, pk_key_file, pk_ca_dir,
+	                &user_cert, &user_key, &trusted_certs);
+   if (ret)
+      goto out;
+
+   ret = krb5_get_in_cred_ext (context,
+	 		       flags.i,
+			       addrs,
+			       etypes,
+			       pk_pre_auth_types,
+			       NULL,
+			       NULL,
+			       NULL,
+			       NULL,
+			       NULL,
+			       user_cert,
+			       user_key,
+			       trusted_certs,
+			       &this_cred,
+			       NULL);
+   if (ret)
+      goto out;
+   free (pre_auth_types);
+   free (etypes);
+   if (creds)
+       *creds = this_cred;
+   else
+       krb5_free_creds_contents (context, &this_cred);
+   return 0;
+
+out:
+   free (pre_auth_types);
+   free (etypes);
+   krb5_free_creds_contents (context, &this_cred);
+   return ret;
+}
+#endif /* PKINIT */
Index: heimdal/lib/krb5/krb5-protos.h
diff -u heimdal/lib/krb5/krb5-protos.h:1.1.1.1 heimdal/lib/krb5/krb5-protos.h:1.1.1.1.2.3
--- heimdal/lib/krb5/krb5-protos.h:1.1.1.1	Tue Feb 26 15:42:04 2002
+++ heimdal/lib/krb5/krb5-protos.h	Wed May 15 14:08:33 2002
@@ -1380,6 +1380,24 @@
 	krb5_kdc_rep *ret_as_reply));
 
 krb5_error_code
+krb5_get_in_cred_ext __P((
+	krb5_context context,
+	krb5_flags options,
+	const krb5_addresses *addrs,
+	const krb5_enctype *etypes,
+	const krb5_preauthtype *ptypes,
+	const krb5_preauthdata *preauth,
+	krb5_key_proc key_proc,
+	krb5_const_pointer keyseed,
+	krb5_decrypt_proc decrypt_proc,
+	krb5_const_pointer decryptarg,
+	void *p_cert,
+	void *p_private_key,
+	void *p_trusted_certs,
+	krb5_creds *creds,
+	krb5_kdc_rep *ret_as_reply));
+
+krb5_error_code
 krb5_get_in_tkt __P((
 	krb5_context context,
 	krb5_flags options,
@@ -2842,6 +2860,26 @@
 
 krb5_error_code
 krb5_xfree __P((void *ptr));
+
+krb5_error_code
+pk_mk_padata __P((
+	krb5_context context,
+	void *p_chain,
+	void *p_private_key,
+	void *p_trusted_certs,
+	KDC_REQ_BODY *req_body,
+	unsigned nonce,
+	METHOD_DATA *md));
+
+krb5_error_code
+pk_rd_pa_reply __P((
+	krb5_context context,
+	void *p_cert,
+	void *p_priv_key,
+	void *p_trusted_certs,
+	unsigned nonce,
+	PA_DATA *pa,
+	krb5_keyblock **key));
 
 krb5_error_code
 principalname2krb5_principal __P((
Index: heimdal/lib/krb5/krb5_locl.h
diff -u heimdal/lib/krb5/krb5_locl.h:1.1.1.1 heimdal/lib/krb5/krb5_locl.h:1.1.1.1.2.1
--- heimdal/lib/krb5/krb5_locl.h:1.1.1.1	Tue Feb 26 15:42:04 2002
+++ heimdal/lib/krb5/krb5_locl.h	Mon May  6 16:00:49 2002
@@ -115,6 +115,12 @@
 #include <openssl/md5.h>
 #include <openssl/sha.h>
 #include <openssl/rc4.h>
+#ifdef PKINIT
+#include <openssl/stack.h>
+#include <openssl/x509.h>
+#include <openssl/x509_vfy.h>
+#include <openssl/evp.h>
+#endif
 #else
 #include <des.h>
 #include <md4.h>
Index: heimdal/lib/krb5/pkinit.c
diff -u /dev/null heimdal/lib/krb5/pkinit.c:1.1.2.6
--- /dev/null	Wed May 15 15:07:04 2002
+++ heimdal/lib/krb5/pkinit.c	Wed May 15 14:08:33 2002
@@ -0,0 +1,548 @@
+#include "krb5_locl.h"
+
+RCSID("$Id$");
+
+#ifdef PKINIT
+#include "pkinit_asn1.h"
+#ifdef HAVE_DIRENT_H
+#include <dirent.h>
+#endif
+
+static krb5_error_code
+build_auth_pack(krb5_context context,
+                unsigned nonce,
+		KDC_REQ_BODY *body,
+		AuthPack *a)
+{
+  u_char buf[1024];
+  size_t len;
+  krb5_error_code ret;
+  int32_t sec, usec;
+
+  memset(a, 0, sizeof(*a));
+
+  /* fill in PKAuthenticator */
+  krb5_us_timeofday (context, &sec, &usec);
+  a->pkAuthenticator.cusec = usec;
+  a->pkAuthenticator.ctime = sec;
+
+  a->pkAuthenticator.nonce = nonce;
+
+  ret = encode_KDC_REQ_BODY(buf + sizeof(buf) - 1, sizeof(buf), body, &len);
+  if (ret)
+     return ret;
+
+  ret = krb5_create_checksum(context,
+		             NULL,
+			     0,
+			     CKSUMTYPE_SHA1, /* default type from context ?? */
+			     buf + sizeof(buf) - len,
+			     len,
+			     &a->pkAuthenticator.pachecksum);
+  return ret;
+}
+
+krb5_error_code
+pk_load_config(krb5_context context,
+               char *cert_file,
+               char *key_file,
+	       char *ca_dir,
+	       STACK_OF(X509) **user_cert,
+	       EVP_PKEY **user_key,
+	       STACK_OF(X509) **trusted_certs)
+{
+   X509 *cert = NULL;
+   FILE *fp = NULL;
+   DIR *dir = NULL;
+   char dirname[MAXPATHLEN];
+   char filename[MAXPATHLEN];
+   struct dirent *file;
+   STACK_OF(X509) *tmp_certificate = NULL;
+   EVP_PKEY *tmp_key = NULL;
+   STACK_OF(X509) *tmp_certs = NULL;
+   krb5_error_code ret;
+
+   fp = fopen(cert_file, "r");
+   if (fp == NULL) {
+      /* krb5_set_error_string() ??? */
+      return -1; /* XXX */
+   }
+   tmp_certificate = sk_X509_new_null();
+   while (1) {
+      /* see http://www.openssl.org/docs/crypto/pem.html section BUGS */
+      cert = PEM_read_X509(fp, NULL, NULL, NULL);
+      if (cert == NULL) {
+	 if (ERR_GET_REASON(ERR_peek_error()) == PEM_R_NO_START_LINE) {
+	    /* End of file reached. no error */
+	    ERR_clear_error();
+	    break;
+	 }
+	 ret = -1; /* XXX */
+	 goto fail;
+      }
+      sk_X509_push(tmp_certificate, cert);
+   }
+   fclose(fp);
+   fp = NULL;
+
+   fp = fopen(key_file, "r");
+   if (fp == NULL) {
+   }
+   tmp_key = PEM_read_PrivateKey(fp, NULL, NULL, NULL);
+   if (tmp_key == NULL) {
+      /* XXX Pozor, pokud je klic zasifrovany vyvola se interni callback,
+	 ktery ceka na zadani hesla */
+      ret = -1; /* XXX */
+      goto fail;
+   }
+   fclose(fp);
+   fp = NULL;
+
+   dir = opendir(ca_dir);
+   if (dir == NULL) {
+      ret = -1; /* XXX */
+      goto fail;
+   }
+   memset(dirname, 0, sizeof(dirname));
+   strncpy(dirname, ca_dir, sizeof(dirname) - 1);
+   if (dirname[strlen(dirname) - 1] != '/')
+      dirname[strlen(dirname)] = '/';
+
+   tmp_certs = sk_X509_new_null();
+   while ((file = readdir(dir))) {
+      /* suppose the certificate filenames constist of hashed subject name
+	 followed by suffix ".0" */
+      if (strlen(file->d_name) == 10 &&
+	  file->d_name[8] == '.' &&
+	  file->d_name[9] == '0') {
+	 snprintf(filename, sizeof(filename), "%s%s", dirname, file->d_name);
+	 fp = fopen(filename, "r");
+	 if (fp == NULL) {
+	    ret = -1;
+	     goto fail;
+	 }
+	 cert = PEM_read_X509(fp, NULL, NULL, NULL);
+	 if (cert == NULL) {
+	    ret = -1;
+	    goto fail;
+	 }
+	 fclose(fp);
+	 fp = NULL;
+	 sk_X509_push(tmp_certs, cert);
+      }
+   }
+   closedir(dir);
+
+   *user_cert = tmp_certificate;
+   *user_key = tmp_key;
+   *trusted_certs = tmp_certs;
+
+   return 0;
+
+fail:
+   if (dir)
+      closedir(dir);
+   if (fp)
+      fclose(fp);
+   if (tmp_certificate)
+      sk_X509_pop_free(tmp_certificate, X509_free);
+   if (tmp_key)
+      EVP_PKEY_free(tmp_key);
+   if (tmp_certs)
+      sk_X509_pop_free(tmp_certs, X509_free);
+
+   return ret;
+}
+
+krb5_boolean
+pk_peer_compare(krb5_context context,
+                IssuerAndSerialNumber *peer1,
+		X509 *peer2)
+{
+#if 0
+   if (ASN1_INTEGER_cmp(peer1->serial, 
+	                X509_get_serialNumber(peer2)) != 0)
+      return FALSE;
+   if (X509_NAME_cmp(peer1->issuer,
+	             X509_get_issuer_name(peer2)) != 0)
+      return FALSE;
+#endif
+
+   return TRUE;
+}
+
+/* the user's certificate is supposed to be first in the chain */
+krb5_error_code
+pk_create_sign(krb5_context context,
+               STACK_OF(X509) *cert_chain,
+               EVP_PKEY *private_key,
+               SignedData *sd)
+{
+  X509 *user_cert;
+  EVP_MD_CTX md;
+  unsigned char *buf = NULL;
+  unsigned len;
+  int ret;
+
+  if (cert_chain == NULL || private_key == NULL) {
+     return -1; /* XXX error code */
+  }
+
+  if (sk_X509_num(cert_chain) == 0) {
+     return -1; /* XXX error code */
+  }
+
+  user_cert = sk_X509_value(cert_chain, 0);
+  sd->chain = sk_dup(cert_chain);
+
+  sd->signer_info.sid.issuer = X509_NAME_dup(X509_get_issuer_name(user_cert));
+  sd->signer_info.sid.serial = ASN1_INTEGER_dup(X509_get_serialNumber(user_cert));
+  /* sd->signer_info.sid.serial = ASN1_INTEGER_get(serial); */
+
+  sd->signer_info.digest_type = CKSUMTYPE_SHA1;
+  sd->signer_info.signature_type = ETYPE_ENCRYPT_RSA_PRIV; 
+
+  /* suppose that econtent_info is already filled in by the caller */
+  buf = malloc(EVP_PKEY_size(private_key));
+  if (buf == NULL) {
+     krb5_set_error_string(context, "malloc: out of memory");
+     ret = ENOMEM;
+     goto fail;
+  }
+  EVP_SignInit(&md, EVP_sha1());
+  EVP_SignUpdate(&md,
+	         sd->econtent_info.content.data, 
+                 sd->econtent_info.content.length);
+  ret = EVP_SignFinal(&md, buf, &len, private_key);
+  if (ret == 0) {
+     /* krb5_set_error_string -- check Openssl error */
+     return -1; /* XXX */
+     goto fail;
+  }
+
+  ret = krb5_data_copy(&sd->signer_info.signature, buf, len);
+  if (ret)
+     goto fail;
+
+  free(buf);
+
+  return 0;
+
+fail:
+  if (buf)
+     free(buf);
+  free_SignedData(sd);
+  
+  return ret;
+}
+
+krb5_error_code 
+pk_mk_padata(krb5_context context,
+             void *p_chain,         /* STACK_OF(X509)* */
+             void *p_private_key,   /* EVP_PKEY* */
+             void *p_trusted_certs, /* STACK_OF(X509)* */
+             KDC_REQ_BODY *req_body,
+             unsigned nonce,
+	     METHOD_DATA *md)
+{
+  STACK_OF(X509) *chain = (STACK_OF(X509) *) p_chain;
+  EVP_PKEY *private_key = (EVP_PKEY *) p_private_key;
+  STACK_OF(X509) *trusted_certs = (STACK_OF(X509) *) p_trusted_certs ;
+  AuthPack ap;
+  PA_PK_AS_REQ req;
+  PA_DATA *pa;
+  SignedData *sd;
+  size_t len;
+  u_char buf[8096];
+  krb5_error_code problem;
+  int num, i;
+  
+  memset(&req, sizeof(req), 0);
+  memset(&ap, sizeof(ap), 0);
+
+  /* first fill in SignedData, ie. create AuthPack and sign it with client's
+     private key. */
+  problem = build_auth_pack(context, nonce, req_body, &ap);
+  if (problem)
+     return problem;
+  encode_AuthPack(buf + sizeof(buf) - 1, sizeof(buf), &ap, &len);
+  free_AuthPack(&ap);
+
+  sd = &req.signed_data;
+  krb5_data_copy(&sd->econtent_info.content, buf + sizeof(buf) - len , len);
+  sd->econtent_info.type = strdup(OID_PKAUTHDATA);
+  problem = pk_create_sign(context, chain, private_key, sd);
+  if (problem)
+     goto end;
+  
+  num = sk_X509_num(trusted_certs);
+  if (num > 0) {
+     req.trusted_certifiers = sk_X509_NAME_new_null();
+     if (req.trusted_certifiers == NULL) {
+        problem = ENOMEM; /* XXX */
+        goto end;
+     }
+     
+     for (i = 0; i < num; i++) {
+	X509_NAME *name;
+
+        name = X509_NAME_dup(X509_get_issuer_name(sk_X509_value(trusted_certs, i)));
+        sk_X509_NAME_push(req.trusted_certifiers, name); 
+     }
+  }
+
+  /* Add Signed Data to PADATA */
+
+  pa = realloc (md->val, (md->len + 1) * sizeof(*md->val));
+  if (pa == NULL) {
+     krb5_set_error_string(context, "malloc: out of memory");
+     problem = ENOMEM;
+     goto end;
+  }
+  md->val = pa;
+
+  problem = encode_PA_PK_AS_REQ(buf + sizeof(buf) - 1, sizeof(buf), &req, &len); 
+  if (problem)
+     goto end;
+  pa = &md->val[md->len]; /* XXX ??? &md */
+  pa->padata_type = KRB5_PADATA_PK_AS_REQ;
+  krb5_data_copy(&pa->padata_value,
+		 buf + sizeof(buf) - len,
+		 len);
+  ++md->len;
+
+end:
+  free_PA_PK_AS_REQ(&req);
+
+  return problem;
+}
+
+krb5_error_code
+pk_verify_sign(krb5_context context,
+      	       SignedData *sd,
+	       STACK_OF(X509) *trusted_certs,
+    	       X509 **signer)
+{
+  X509_STORE *cert_store = NULL;
+  X509_STORE_CTX *store_ctx = NULL;
+  EVP_PKEY *public_key = NULL;
+  X509 *cert = NULL;
+  krb5_error_code ret;
+  EVP_MD_CTX md;
+  int i;
+
+  ret = -1; /* cert not found */
+  for (i = 0; i < sk_X509_num(sd->chain); i++) {
+     cert = sk_X509_value(sd->chain, i);
+     if (pk_peer_compare(context, &sd->signer_info.sid, cert) == TRUE) {
+	ret = 0;
+	break;
+     }
+  }
+  if (ret)
+     return ret;
+
+  cert_store = X509_STORE_new();
+  if (cert_store == NULL) {
+     return -1; /* XXX */
+  }
+
+  /* ERR_load_crypto_strings(); */ /* ??? */
+
+  store_ctx = X509_STORE_CTX_new();
+  if (store_ctx == NULL) {
+     ret = -1; /* XXX */
+     goto end;
+  }
+
+  X509_STORE_CTX_init(store_ctx, cert_store, cert, sd->chain);
+  X509_STORE_CTX_trusted_stack(store_ctx, trusted_certs);
+  ret = X509_verify_cert(store_ctx);
+
+  public_key = X509_get_pubkey(cert);
+
+  EVP_VerifyInit(&md, EVP_sha1());
+  EVP_VerifyUpdate(&md, 
+	           sd->econtent_info.content.data,
+		   sd->econtent_info.content.length);
+  ret = EVP_VerifyFinal(&md,
+	                sd->signer_info.signature.data,
+		        sd->signer_info.signature.length,
+			public_key);  
+
+  if (signer && cert)
+     *signer = X509_dup(cert);
+
+end:
+  if (store_ctx)
+     X509_STORE_CTX_free(store_ctx);
+
+  return ret;
+}
+
+static krb5_error_code
+pk_decrypt_key(krb5_context context,
+	       krb5_data *encrypted_key,
+	       EVP_PKEY *priv_key,
+	       krb5_keyblock *key)
+{
+  int ret;
+  unsigned char *buf;
+  size_t len;
+
+  buf = malloc(EVP_PKEY_size(priv_key));
+  if (buf == NULL) {
+     krb5_set_error_string(context, "malloc: out of memory");
+     return ENOMEM;
+  }
+  ret = EVP_PKEY_decrypt(buf,
+	                 encrypted_key->data,
+			 encrypted_key->length,
+			 priv_key);
+  if (ret <= 0) {
+     free(buf);
+     return -1; /* XXX */
+  }
+
+  ret = decode_EncryptionKey(buf, EVP_PKEY_size(priv_key), key, &len);
+  free(buf);
+  return ret;
+}
+
+static krb5_error_code
+get_reply_key(krb5_context context,
+	      krb5_data *raw,
+	      unsigned nonce,
+	      krb5_keyblock **key)
+{
+  ReplyKeyPack key_pack;
+  size_t len;
+  krb5_error_code problem;
+
+  memset(&key_pack, 0, sizeof(key_pack));
+
+  problem = decode_ReplyKeyPack(raw->data,
+				raw->length,
+				&key_pack,
+				&len);
+  if (problem)
+     goto end;
+
+#if 0
+  if (key_pack.nonce != nonce) {
+     problem = -1; /* XXX */
+     goto end;
+  }
+#endif
+
+  *key = malloc (sizeof (**key));
+  if (*key == NULL) {
+     krb5_set_error_string(context, "malloc: out of memory");
+     return ENOMEM;
+  }
+
+  problem = krb5_copy_keyblock(context, &key_pack.replyKey, key);
+
+end:
+  free_ReplyKeyPack(&key_pack);
+  if (problem)
+     free(*key);
+  return problem;
+}
+
+
+krb5_error_code
+pk_rd_pa_reply(krb5_context context, 
+	       void *p_cert,          /* X509* */
+	       void *p_priv_key,      /* EVP_PKEY* */
+	       void *p_trusted_certs, /* STACK_OF(X509)* */
+	       unsigned nonce,
+	       PA_DATA *pa,
+	       krb5_keyblock **key)
+{
+  X509 *cert = (X509 *) p_cert;
+  EVP_PKEY *priv_key = (EVP_PKEY *) p_priv_key;
+  STACK_OF(X509) *trusted_certs = (STACK_OF(X509) *) p_trusted_certs;
+  krb5_error_code problem;
+  PA_PK_AS_REP rep;
+  SignedData sd;
+  size_t len;
+  krb5_keyblock tmp_key;
+  krb5_crypto crypto;
+  krb5_data plain;
+
+  memset(&rep, 0, sizeof(rep));
+  memset(&sd, 0, sizeof(sd));
+  memset(&tmp_key, 0, sizeof(tmp_key));
+  krb5_data_zero(&plain);
+
+  problem = decode_PA_PK_AS_REP(pa->padata_value.data, pa->padata_value.length, 
+		            &rep, &len);
+  if (problem)
+     return problem;
+
+  /* check the message is really for us */
+  problem = pk_peer_compare(context, &rep.recipient_info.rid, cert);
+  if (!problem) {
+     problem = -1; /* XXX */
+     goto end;
+  }
+
+  if (rep.recipient_info.etype != ETYPE_ENCRYPT_RSA_PUB) {
+     problem = KRB5_WRONG_ETYPE; /* XXX */
+     goto end;
+  }
+
+  problem = pk_decrypt_key(context,
+	                   &rep.recipient_info.encrypted_key,
+	                   priv_key, 
+		           &tmp_key);
+  if (problem)
+     goto end;
+
+  /* XXX use something like g_OID_equal() */
+  if (strcmp(rep.econtent_info.type, OID_ID_SIGNEDDATA)) {
+     problem = KRB5KRB_AP_ERR_MSG_TYPE;
+     goto end;
+  }
+
+  /* decrypt signed data with tmp_key) */
+  problem = krb5_crypto_init(context, &tmp_key, 0, &crypto);
+  if (problem)
+     goto end;
+  problem = krb5_decrypt(context, crypto,
+	                 0, 
+		         rep.econtent_info.content.data,
+		         rep.econtent_info.content.length,
+		         &plain);
+  krb5_crypto_destroy(context, crypto);
+  if (problem)
+     goto end;
+
+  problem = decode_SignedData(plain.data, plain.length, &sd, &len);
+  if (problem)
+     goto end;
+
+  problem = pk_verify_sign(context, &sd, trusted_certs, NULL);
+  if (problem)
+     goto end;
+
+  /* XXX use something like g_OID_equal() */
+  if (strcmp(sd.econtent_info.type, OID_PKRKEYDATA)) {
+     problem = KRB5KRB_AP_ERR_MSG_TYPE; /* XXX */
+     goto end;
+  }
+
+  problem = get_reply_key(context, &sd.econtent_info.content, nonce, key);
+  if (problem)
+     goto end;
+
+end:
+  krb5_free_keyblock_contents(context, &tmp_key);
+  krb5_data_free(&plain);
+  free_PA_PK_AS_REP(&rep);
+  free_SignedData(&sd);
+
+  return problem;
+}
+#endif /* PKINIT */