initial support for delta CRL generations by diffing two full CRLs
authorDr. Stephen Henson <steve@openssl.org>
Tue, 4 Dec 2012 18:35:36 +0000 (18:35 +0000)
committerDr. Stephen Henson <steve@openssl.org>
Thu, 17 Jan 2013 18:51:50 +0000 (18:51 +0000)
CHANGES
apps/crl.c
crypto/asn1/x_crl.c
crypto/x509/x509.h
crypto/x509/x509_err.c
crypto/x509/x509_vfy.c

diff --git a/CHANGES b/CHANGES
index f6fc95e224a40a627f1c5a2c59c1b2797234110b..34dc69b0f06213aff8068bc4d1c8cc6d7bfa59c5 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -4,6 +4,10 @@
 
  Changes between 1.0.1 and 1.0.2 [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 functions to set lookup_crls function and to retrieve
      X509_STORE from X509_STORE_CTX.
      [Steve Henson]
index 888cf7ff8d35bf59fdfe648f0903da1eaa59f133..50e7d95a6f635b4b3394024ee5a716b4dc75bbe9 100644 (file)
@@ -105,8 +105,8 @@ int MAIN(int argc, char **argv)
        char *CAfile = NULL, *CApath = NULL;
        int ret=1,i,num,badops=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;
@@ -141,6 +141,7 @@ int MAIN(int argc, char **argv)
 
        informat=FORMAT_PEM;
        outformat=FORMAT_PEM;
+       keyformat=FORMAT_PEM;
 
        argc--;
        argv++;
@@ -169,6 +170,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;
@@ -276,6 +292,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++)
@@ -390,6 +439,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;
index c51c690ba9d3f160a516180c98766b39dcc3dc26..e1ea9f3154e127268112d2f691ee5f4d65c08184 100644 (file)
@@ -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)
index c913e3c39d2862bea4713aa41022d1217c907eec..ee560d19bf587debf1f8643a43b43aa142a0dff5 100644 (file)
@@ -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
index a01402f41673083bf39174650020bab54ac43571..1731254d48abdcfee9b5e76f6002c406a0b5c376 100644 (file)
@@ -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"},
index 0e109a01d04eac1a2e167a4af5408f627d28f005..afb60163f26a7d805807a4c6d4572702193fd6a2 100644 (file)
@@ -1945,6 +1945,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)
        {