diff --git a/CHANGES b/CHANGES
index a03aa4bbe..5f84cfd40 100644
--- a/CHANGES
+++ b/CHANGES
@@ -4,6 +4,10 @@
 
  Changes between 1.0.x and 1.1.0  [xx XXX xxxx]
 
+  *) New function X509_CRL_diff to generate a delta CRL from the difference
+     of two full CRLs. Add support to "crl" utility.
+     [Steve Henson]
+
   *) New options -CRL and -CRLform for s_client and s_server for CRLs.
      [Steve Henson]
 
diff --git a/apps/crl.c b/apps/crl.c
index 8ee88af46..745469d1b 100644
--- a/apps/crl.c
+++ b/apps/crl.c
@@ -104,8 +104,8 @@ int MAIN(int argc, char **argv)
 	char *CAfile = NULL, *CApath = NULL;
 	int ret=1,i,num,badops=0,badsig=0;
 	BIO *out=NULL;
-	int informat,outformat;
-	char *infile=NULL,*outfile=NULL;
+	int informat,outformat, keyformat;
+	char *infile=NULL,*outfile=NULL, *crldiff = NULL, *keyfile = NULL;
 	int hash=0,issuer=0,lastupdate=0,nextupdate=0,noout=0,text=0;
 	int fingerprint = 0, crlnumber = 0;
 	const char **pp;
@@ -140,6 +140,7 @@ int MAIN(int argc, char **argv)
 
 	informat=FORMAT_PEM;
 	outformat=FORMAT_PEM;
+	keyformat=FORMAT_PEM;
 
 	argc--;
 	argv++;
@@ -168,6 +169,21 @@ int MAIN(int argc, char **argv)
 			if (--argc < 1) goto bad;
 			infile= *(++argv);
 			}
+		else if (strcmp(*argv,"-gendelta") == 0)
+			{
+			if (--argc < 1) goto bad;
+			crldiff= *(++argv);
+			}
+		else if (strcmp(*argv,"-key") == 0)
+			{
+			if (--argc < 1) goto bad;
+			keyfile= *(++argv);
+			}
+		else if (strcmp(*argv,"-keyform") == 0)
+			{
+			if (--argc < 1) goto bad;
+			keyformat=str2fmt(*(++argv));
+			}
 		else if (strcmp(*argv,"-out") == 0)
 			{
 			if (--argc < 1) goto bad;
@@ -277,6 +293,39 @@ bad:
 		else BIO_printf(bio_err, "verify OK\n");
 	}
 
+	if (crldiff)
+		{
+		X509_CRL *newcrl, *delta;
+		if (!keyfile)
+			{
+			BIO_puts(bio_err, "Missing CRL signing key\n");
+			goto end;
+			}
+		newcrl = load_crl(crldiff,informat);
+		if (!newcrl)
+			goto end;
+		pkey = load_key(bio_err, keyfile, keyformat, 0, NULL, NULL,
+					"CRL signing key");
+		if (!pkey)
+			{
+			X509_CRL_free(newcrl);
+			goto end;
+			}	
+		delta = X509_CRL_diff(x, newcrl, pkey, digest, 0);
+		X509_CRL_free(newcrl);
+		EVP_PKEY_free(pkey);
+		if (delta)
+			{
+			X509_CRL_free(x);
+			x = delta;
+			}
+		else
+			{
+			BIO_puts(bio_err, "Error creating delta CRL\n");
+			goto end;
+			}
+		}
+
 	if (num)
 		{
 		for (i=1; i<=num; i++)
@@ -394,6 +443,8 @@ bad:
 	if (!i) { BIO_printf(bio_err,"unable to write CRL\n"); goto end; }
 	ret=0;
 end:
+	if (ret != 0)
+		ERR_print_errors(bio_err);
 	BIO_free_all(out);
 	BIO_free_all(bio_out);
 	bio_out=NULL;
diff --git a/crypto/asn1/x_crl.c b/crypto/asn1/x_crl.c
index 137aa2180..a4dfee847 100644
--- a/crypto/asn1/x_crl.c
+++ b/crypto/asn1/x_crl.c
@@ -356,6 +356,7 @@ ASN1_SEQUENCE_ref(X509_CRL, crl_cb, CRYPTO_LOCK_X509_CRL) = {
 } ASN1_SEQUENCE_END_ref(X509_CRL, X509_CRL)
 
 IMPLEMENT_ASN1_FUNCTIONS(X509_REVOKED)
+IMPLEMENT_ASN1_DUP_FUNCTION(X509_REVOKED)
 IMPLEMENT_ASN1_FUNCTIONS(X509_CRL_INFO)
 IMPLEMENT_ASN1_FUNCTIONS(X509_CRL)
 IMPLEMENT_ASN1_DUP_FUNCTION(X509_CRL)
diff --git a/crypto/x509/x509.h b/crypto/x509/x509.h
index c913e3c39..ee560d19b 100644
--- a/crypto/x509/x509.h
+++ b/crypto/x509/x509.h
@@ -765,6 +765,7 @@ X509 *X509_dup(X509 *x509);
 X509_ATTRIBUTE *X509_ATTRIBUTE_dup(X509_ATTRIBUTE *xa);
 X509_EXTENSION *X509_EXTENSION_dup(X509_EXTENSION *ex);
 X509_CRL *X509_CRL_dup(X509_CRL *crl);
+X509_REVOKED *X509_REVOKED_dup(X509_REVOKED *rev);
 X509_REQ *X509_REQ_dup(X509_REQ *req);
 X509_ALGOR *X509_ALGOR_dup(X509_ALGOR *xn);
 int X509_ALGOR_set0(X509_ALGOR *alg, ASN1_OBJECT *aobj, int ptype, void *pval);
@@ -965,6 +966,9 @@ int X509_CRL_sort(X509_CRL *crl);
 int X509_REVOKED_set_serialNumber(X509_REVOKED *x, ASN1_INTEGER *serial);
 int X509_REVOKED_set_revocationDate(X509_REVOKED *r, ASN1_TIME *tm);
 
+X509_CRL *X509_CRL_diff(X509_CRL *base, X509_CRL *newer,
+			EVP_PKEY *skey, const EVP_MD *md, unsigned int flags);
+
 int		X509_REQ_check_private_key(X509_REQ *x509,EVP_PKEY *pkey);
 
 int		X509_check_private_key(X509 *x509,EVP_PKEY *pkey);
@@ -1245,6 +1249,7 @@ void ERR_load_X509_strings(void);
 #define X509_F_X509_ATTRIBUTE_GET0_DATA			 139
 #define X509_F_X509_ATTRIBUTE_SET1_DATA			 138
 #define X509_F_X509_CHECK_PRIVATE_KEY			 128
+#define X509_F_X509_CRL_DIFF				 105
 #define X509_F_X509_CRL_PRINT_FP			 147
 #define X509_F_X509_EXTENSION_CREATE_BY_NID		 108
 #define X509_F_X509_EXTENSION_CREATE_BY_OBJ		 109
@@ -1277,20 +1282,27 @@ void ERR_load_X509_strings(void);
 #define X509_F_X509_VERIFY_CERT				 127
 
 /* Reason codes. */
+#define X509_R_AKID_MISMATCH				 110
 #define X509_R_BAD_X509_FILETYPE			 100
 #define X509_R_BASE64_DECODE_ERROR			 118
 #define X509_R_CANT_CHECK_DH_KEY			 114
 #define X509_R_CERT_ALREADY_IN_HASH_TABLE		 101
+#define X509_R_CRL_ALREADY_DELTA			 127
+#define X509_R_CRL_VERIFY_FAILURE			 131
 #define X509_R_ERR_ASN1_LIB				 102
+#define X509_R_IDP_MISMATCH				 128
 #define X509_R_INVALID_DIRECTORY			 113
 #define X509_R_INVALID_FIELD_NAME			 119
 #define X509_R_INVALID_TRUST				 123
+#define X509_R_ISSUER_MISMATCH				 129
 #define X509_R_KEY_TYPE_MISMATCH			 115
 #define X509_R_KEY_VALUES_MISMATCH			 116
 #define X509_R_LOADING_CERT_DIR				 103
 #define X509_R_LOADING_DEFAULTS				 104
 #define X509_R_METHOD_NOT_SUPPORTED			 124
+#define X509_R_NEWER_CRL_NOT_NEWER			 132
 #define X509_R_NO_CERT_SET_FOR_US_TO_VERIFY		 105
+#define X509_R_NO_CRL_NUMBER				 130
 #define X509_R_PUBLIC_KEY_DECODE_ERROR			 125
 #define X509_R_PUBLIC_KEY_ENCODE_ERROR			 126
 #define X509_R_SHOULD_RETRY				 106
diff --git a/crypto/x509/x509_err.c b/crypto/x509/x509_err.c
index a01402f41..1731254d4 100644
--- a/crypto/x509/x509_err.c
+++ b/crypto/x509/x509_err.c
@@ -1,6 +1,6 @@
 /* crypto/x509/x509_err.c */
 /* ====================================================================
- * Copyright (c) 1999-2006 The OpenSSL Project.  All rights reserved.
+ * Copyright (c) 1999-2012 The OpenSSL Project.  All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -85,6 +85,7 @@ static ERR_STRING_DATA X509_str_functs[]=
 {ERR_FUNC(X509_F_X509_ATTRIBUTE_GET0_DATA),	"X509_ATTRIBUTE_get0_data"},
 {ERR_FUNC(X509_F_X509_ATTRIBUTE_SET1_DATA),	"X509_ATTRIBUTE_set1_data"},
 {ERR_FUNC(X509_F_X509_CHECK_PRIVATE_KEY),	"X509_check_private_key"},
+{ERR_FUNC(X509_F_X509_CRL_DIFF),	"X509_CRL_diff"},
 {ERR_FUNC(X509_F_X509_CRL_PRINT_FP),	"X509_CRL_print_fp"},
 {ERR_FUNC(X509_F_X509_EXTENSION_CREATE_BY_NID),	"X509_EXTENSION_create_by_NID"},
 {ERR_FUNC(X509_F_X509_EXTENSION_CREATE_BY_OBJ),	"X509_EXTENSION_create_by_OBJ"},
@@ -120,20 +121,27 @@ static ERR_STRING_DATA X509_str_functs[]=
 
 static ERR_STRING_DATA X509_str_reasons[]=
 	{
+{ERR_REASON(X509_R_AKID_MISMATCH)        ,"akid mismatch"},
 {ERR_REASON(X509_R_BAD_X509_FILETYPE)    ,"bad x509 filetype"},
 {ERR_REASON(X509_R_BASE64_DECODE_ERROR)  ,"base64 decode error"},
 {ERR_REASON(X509_R_CANT_CHECK_DH_KEY)    ,"cant check dh key"},
 {ERR_REASON(X509_R_CERT_ALREADY_IN_HASH_TABLE),"cert already in hash table"},
+{ERR_REASON(X509_R_CRL_ALREADY_DELTA)    ,"crl already delta"},
+{ERR_REASON(X509_R_CRL_VERIFY_FAILURE)   ,"crl verify failure"},
 {ERR_REASON(X509_R_ERR_ASN1_LIB)         ,"err asn1 lib"},
+{ERR_REASON(X509_R_IDP_MISMATCH)         ,"idp mismatch"},
 {ERR_REASON(X509_R_INVALID_DIRECTORY)    ,"invalid directory"},
 {ERR_REASON(X509_R_INVALID_FIELD_NAME)   ,"invalid field name"},
 {ERR_REASON(X509_R_INVALID_TRUST)        ,"invalid trust"},
+{ERR_REASON(X509_R_ISSUER_MISMATCH)      ,"issuer mismatch"},
 {ERR_REASON(X509_R_KEY_TYPE_MISMATCH)    ,"key type mismatch"},
 {ERR_REASON(X509_R_KEY_VALUES_MISMATCH)  ,"key values mismatch"},
 {ERR_REASON(X509_R_LOADING_CERT_DIR)     ,"loading cert dir"},
 {ERR_REASON(X509_R_LOADING_DEFAULTS)     ,"loading defaults"},
 {ERR_REASON(X509_R_METHOD_NOT_SUPPORTED) ,"method not supported"},
+{ERR_REASON(X509_R_NEWER_CRL_NOT_NEWER)  ,"newer crl not newer"},
 {ERR_REASON(X509_R_NO_CERT_SET_FOR_US_TO_VERIFY),"no cert set for us to verify"},
+{ERR_REASON(X509_R_NO_CRL_NUMBER)        ,"no crl number"},
 {ERR_REASON(X509_R_PUBLIC_KEY_DECODE_ERROR),"public key decode error"},
 {ERR_REASON(X509_R_PUBLIC_KEY_ENCODE_ERROR),"public key encode error"},
 {ERR_REASON(X509_R_SHOULD_RETRY)         ,"should retry"},
diff --git a/crypto/x509/x509_vfy.c b/crypto/x509/x509_vfy.c
index a4e31f71e..a21fa39c3 100644
--- a/crypto/x509/x509_vfy.c
+++ b/crypto/x509/x509_vfy.c
@@ -1888,6 +1888,125 @@ int X509_get_pubkey_parameters(EVP_PKEY *pkey, STACK_OF(X509) *chain)
 	return 1;
 	}
 
+/* Make a delta CRL as the diff between two full CRLs */
+
+X509_CRL *X509_CRL_diff(X509_CRL *base, X509_CRL *newer,
+			EVP_PKEY *skey, const EVP_MD *md, unsigned int flags)
+	{
+	X509_CRL *crl = NULL;
+	int i;
+	STACK_OF(X509_REVOKED) *revs = NULL;
+	/* CRLs can't be delta already */
+	if (base->base_crl_number || newer->base_crl_number)
+			{
+			X509err(X509_F_X509_CRL_DIFF, X509_R_CRL_ALREADY_DELTA);
+			return NULL;
+			}
+	/* Base and new CRL must have a CRL number */
+	if (!base->crl_number || !newer->crl_number)
+			{
+			X509err(X509_F_X509_CRL_DIFF, X509_R_NO_CRL_NUMBER);
+			return NULL;
+			}
+	/* Issuer names must match */
+	if (X509_NAME_cmp(X509_CRL_get_issuer(base),
+				X509_CRL_get_issuer(newer)))
+			{
+			X509err(X509_F_X509_CRL_DIFF, X509_R_ISSUER_MISMATCH);
+			return NULL;
+			}
+	/* AKID and IDP must match */
+	if (!crl_extension_match(base, newer, NID_authority_key_identifier))
+			{
+			X509err(X509_F_X509_CRL_DIFF, X509_R_AKID_MISMATCH);
+			return NULL;
+			}
+	if (!crl_extension_match(base, newer, NID_issuing_distribution_point))
+			{
+			X509err(X509_F_X509_CRL_DIFF, X509_R_IDP_MISMATCH);
+			return NULL;
+			}
+	/* Newer CRL number must exceed full CRL number */
+	if (ASN1_INTEGER_cmp(newer->crl_number, base->crl_number) <= 0)
+			{
+			X509err(X509_F_X509_CRL_DIFF, X509_R_NEWER_CRL_NOT_NEWER);
+			return NULL;
+			}
+	/* CRLs must verify */
+	if (skey && (X509_CRL_verify(base, skey) <= 0 ||
+			X509_CRL_verify(newer, skey) <= 0))
+		{
+		X509err(X509_F_X509_CRL_DIFF, X509_R_CRL_VERIFY_FAILURE);
+		return NULL;
+		}
+	/* Create new CRL */
+	crl = X509_CRL_new();
+	if (!crl || !X509_CRL_set_version(crl, 1))
+		goto memerr;
+	/* Set issuer name */
+	if (!X509_CRL_set_issuer_name(crl, X509_CRL_get_issuer(newer)))
+		goto memerr;
+
+	if (!X509_CRL_set_lastUpdate(crl, X509_CRL_get_lastUpdate(newer)))
+		goto memerr;
+	if (!X509_CRL_set_nextUpdate(crl, X509_CRL_get_nextUpdate(newer)))
+		goto memerr;
+
+	/* Set base CRL number: must be critical */
+
+	if (!X509_CRL_add1_ext_i2d(crl, NID_delta_crl, base->crl_number, 1, 0))
+		goto memerr;
+
+	/* Copy extensions across from newest CRL to delta: this will set
+	 * CRL number to correct value too.
+	 */
+
+	for (i = 0; i < X509_CRL_get_ext_count(newer); i++)
+		{
+		X509_EXTENSION *ext;
+		ext = X509_CRL_get_ext(newer, i);
+		if (!X509_CRL_add_ext(crl, ext, -1))
+			goto memerr;
+		}
+
+	/* Go through revoked entries, copying as needed */
+
+	revs = X509_CRL_get_REVOKED(newer);
+
+	for (i = 0; i < sk_X509_REVOKED_num(revs); i++)
+		{
+		X509_REVOKED *rvn, *rvtmp;
+		rvn = sk_X509_REVOKED_value(revs, i);
+		/* Add only if not also in base.
+		 * TODO: need something cleverer here for some more complex
+		 * CRLs covering multiple CAs.
+		 */
+		if (!X509_CRL_get0_by_serial(base, &rvtmp, rvn->serialNumber))
+			{
+			rvtmp = X509_REVOKED_dup(rvn);
+			if (!rvtmp)
+				goto memerr;
+			if (!X509_CRL_add0_revoked(crl, rvtmp))
+				{
+				X509_REVOKED_free(rvtmp);
+				goto memerr;
+				}
+			}
+		}
+	/* TODO: optionally prune deleted entries */
+
+	if (skey && md && !X509_CRL_sign(crl, skey, md))
+		goto memerr;
+	
+	return crl;
+
+	memerr:
+	X509err(X509_F_X509_CRL_DIFF, ERR_R_MALLOC_FAILURE);
+	if (crl)
+		X509_CRL_free(crl);
+	return NULL;
+	}
+
 int X509_STORE_CTX_get_ex_new_index(long argl, void *argp, CRYPTO_EX_new *new_func,
 	     CRYPTO_EX_dup *dup_func, CRYPTO_EX_free *free_func)
 	{