Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
[crypto] Allow certificate chains to be long-lived data structures
At present, certificate chain validation is treated as an
instantaneous process that can be carried out using only data that is
already in memory.  This model does not allow for validation to
include non-instantaneous steps, such as downloading a cross-signing
certificate, or determining certificate revocation status via OCSP.

Redesign the internal representation of certificate chains to allow
chains to outlive the scope of the original source of certificates
(such as a TLS Certificate record).

Allow for certificates to be cached, so that each certificate needs to
be validated only once.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
  • Loading branch information
mcb30 committed May 4, 2012
1 parent 6ed905a commit 557f467
Show file tree
Hide file tree
Showing 9 changed files with 1,075 additions and 463 deletions.
383 changes: 274 additions & 109 deletions src/crypto/cms.c

Large diffs are not rendered by default.

478 changes: 362 additions & 116 deletions src/crypto/x509.c

Large diffs are not rendered by default.

53 changes: 39 additions & 14 deletions src/include/ipxe/cms.h
Expand Up @@ -13,37 +13,62 @@ FILE_LICENCE ( GPL2_OR_LATER );
#include <ipxe/asn1.h>
#include <ipxe/crypto.h>
#include <ipxe/x509.h>
#include <ipxe/refcnt.h>
#include <ipxe/uaccess.h>

/** CMS signer information */
struct cms_signer_info {
/** Issuer name */
struct asn1_cursor issuer;
/** Serial number */
struct asn1_cursor serial;
/** List of signer information blocks */
struct list_head list;

/** Certificate chain */
struct x509_chain *chain;

/** Digest algorithm */
struct digest_algorithm *digest;
/** Public-key algorithm */
struct pubkey_algorithm *pubkey;

/** Signature */
const void *signature;
void *signature;
/** Length of signature */
size_t signature_len;
};

/** A CMS signature */
struct cms_signature {
/** Raw certificate list */
struct asn1_cursor certificates;
/** Signer information
*
* We currently use only the first signer information block.
*/
struct cms_signer_info info;
/** Reference count */
struct refcnt refcnt;
/** List of all certificates */
struct x509_chain *certificates;
/** List of signer information blocks */
struct list_head info;
};

extern int cms_parse ( struct cms_signature *sig, const void *data,
size_t len );
/**
* Get reference to CMS signature
*
* @v sig CMS signature
* @ret sig CMS signature
*/
static inline __attribute__ (( always_inline )) struct cms_signature *
cms_get ( struct cms_signature *sig ) {
ref_get ( &sig->refcnt );
return sig;
}

/**
* Drop reference to CMS signature
*
* @v sig CMS signature
*/
static inline __attribute__ (( always_inline )) void
cms_put ( struct cms_signature *sig ) {
ref_put ( &sig->refcnt );
}

extern int cms_signature ( const void *data, size_t len,
struct cms_signature **sig );
extern int cms_verify ( struct cms_signature *sig, userptr_t data, size_t len,
const char *name, time_t time, struct x509_root *root );

Expand Down
3 changes: 3 additions & 0 deletions src/include/ipxe/tls.h
Expand Up @@ -235,6 +235,9 @@ struct tls_session {
/** Public-key algorithm used for Certificate Verify (if sent) */
struct pubkey_algorithm *verify_pubkey;

/** Server certificate chain */
struct x509_chain *chain;

/** TX sequence number */
uint64_t tx_seq;
/** TX pending transmissions */
Expand Down
156 changes: 132 additions & 24 deletions src/include/ipxe/x509.h
Expand Up @@ -13,6 +13,8 @@ FILE_LICENCE ( GPL2_OR_LATER );
#include <stddef.h>
#include <time.h>
#include <ipxe/asn1.h>
#include <ipxe/refcnt.h>
#include <ipxe/list.h>

/** An X.509 bit string */
struct x509_bit_string {
Expand Down Expand Up @@ -50,14 +52,6 @@ struct x509_validity {
struct x509_time not_after;
};

/** An X.509 string */
struct x509_string {
/** String (not NUL-terminated) */
const void *data;
/** Length of name */
size_t len;
};

/** An X.509 certificate public key */
struct x509_public_key {
/** Raw public key */
Expand All @@ -71,7 +65,7 @@ struct x509_subject {
/** Raw subject */
struct asn1_cursor raw;
/** Common name */
struct x509_string name;
char *name;
/** Public key information */
struct x509_public_key public_key;
};
Expand All @@ -92,6 +86,13 @@ struct x509_basic_constraints {
unsigned int path_len;
};

/** Unlimited path length
*
* We use -2U, since this quantity represents one *fewer* than the
* maximum number of remaining certificates in a chain.
*/
#define X509_PATH_LEN_UNLIMITED -2U

/** An X.509 certificate key usage */
struct x509_key_usage {
/** Key usage extension is present */
Expand Down Expand Up @@ -131,7 +132,7 @@ enum x509_extended_key_usage_bits {
/** X.509 certificate OCSP responder */
struct x509_ocsp_responder {
/** URI */
struct x509_string uri;
char *uri;
};

/** X.509 certificate authority information access */
Expand All @@ -154,6 +155,16 @@ struct x509_extensions {

/** An X.509 certificate */
struct x509_certificate {
/** Reference count */
struct refcnt refcnt;
/** List of certificates in cache */
struct list_head list;

/** Certificate has been validated */
int valid;
/** Maximum number of subsequent certificates in chain */
unsigned int path_remaining;

/** Raw certificate */
struct asn1_cursor raw;
/** Version */
Expand All @@ -176,6 +187,80 @@ struct x509_certificate {
struct x509_extensions extensions;
};

/**
* Get reference to X.509 certificate
*
* @v cert X.509 certificate
* @ret cert X.509 certificate
*/
static inline __attribute__ (( always_inline )) struct x509_certificate *
x509_get ( struct x509_certificate *cert ) {
ref_get ( &cert->refcnt );
return cert;
}

/**
* Drop reference to X.509 certificate
*
* @v cert X.509 certificate
*/
static inline __attribute__ (( always_inline )) void
x509_put ( struct x509_certificate *cert ) {
ref_put ( &cert->refcnt );
}

/** A link in an X.509 certificate chain */
struct x509_link {
/** List of links */
struct list_head list;
/** Certificate */
struct x509_certificate *cert;
};

/** An X.509 certificate chain */
struct x509_chain {
/** Reference count */
struct refcnt refcnt;
/** List of links */
struct list_head links;
};

/**
* Get reference to X.509 certificate chain
*
* @v chain X.509 certificate chain
* @ret chain X.509 certificate chain
*/
static inline __attribute__ (( always_inline )) struct x509_chain *
x509_chain_get ( struct x509_chain *chain ) {
ref_get ( &chain->refcnt );
return chain;
}

/**
* Drop reference to X.509 certificate chain
*
* @v chain X.509 certificate chain
*/
static inline __attribute__ (( always_inline )) void
x509_chain_put ( struct x509_chain *chain ) {
ref_put ( &chain->refcnt );
}

/**
* Get first certificate in X.509 certificate chain
*
* @v chain X.509 certificate chain
* @ret cert X.509 certificate, or NULL
*/
static inline __attribute__ (( always_inline )) struct x509_certificate *
x509_first ( struct x509_chain *chain ) {
struct x509_link *link;

link = list_first_entry ( &chain->links, struct x509_link, list );
return ( link ? link->cert : NULL );
}

/** An X.509 extension */
struct x509_extension {
/** Name */
Expand Down Expand Up @@ -228,22 +313,45 @@ struct x509_root {
const void *fingerprints;
};

extern int x509_parse ( struct x509_certificate *cert,
const void *data, size_t len );
extern int x509_validate_issuer ( struct x509_certificate *cert,
struct x509_certificate *issuer );
extern int x509_certificate ( const void *data, size_t len,
struct x509_certificate **cert );

extern struct x509_chain * x509_alloc_chain ( void );
extern int x509_append ( struct x509_chain *chain,
struct x509_certificate *cert );
extern int x509_validate_chain ( struct x509_chain *chain, time_t time,
struct x509_root *root );

/* Functions exposed only for unit testing */
extern int x509_check_issuer ( struct x509_certificate *cert,
struct x509_certificate *issuer );
extern void x509_fingerprint ( struct x509_certificate *cert,
struct digest_algorithm *digest,
void *fingerprint );
extern int x509_validate_root ( struct x509_certificate *cert,
struct x509_root *root );
extern int x509_validate_time ( struct x509_certificate *cert, time_t time );
extern int x509_validate_chain ( int ( * parse_next )
( struct x509_certificate *cert,
const struct x509_certificate *previous,
void *context ),
void *context, time_t time,
struct x509_root *root,
struct x509_certificate *first );
extern int x509_check_root ( struct x509_certificate *cert,
struct x509_root *root );
extern int x509_check_time ( struct x509_certificate *cert, time_t time );

/**
* Invalidate X.509 certificate
*
* @v cert X.509 certificate
*/
static inline void x509_invalidate ( struct x509_certificate *cert ) {
cert->valid = 0;
cert->path_remaining = 0;
}

/**
* Invalidate X.509 certificate chain
*
* @v chain X.509 certificate chain
*/
static inline void x509_invalidate_chain ( struct x509_chain *chain ) {
struct x509_link *link;

list_for_each_entry ( link, &chain->links, list )
x509_invalidate ( link->cert );
}

#endif /* _IPXE_X509_H */

0 comments on commit 557f467

Please sign in to comment.