> I'll dig out the code. It was largely based around the PKCS#11
> functionality but with an OpenSSL flavour. That is you have a load of
> objects each of which is a set of attributes. You can then lookup based
> on exact matches of each attribute.

This is "query by example."  It has some benefits, but can be a real
pain to implement.

> One problem was that it returned matching entries as STACK_OF. That
> would work for small numbers of matches but would be awkward for huge
> databases with large numbers of matches for which some kind of "get
> first n matching" and "get next n matching" (with n >=1) might be more
> appropriate.
 
One classic approach is to have all lookup functions return a
list of unique keys.  The caller then requests each object individually
via a lookup that guarantees uniqueness.  Uniqueness is easy to guarantee
on any hashed or relational store - make it the primary key!

A good candidate for this is the issuer-and-serial-number hash.  (The
full 20 bytes, not a 4 byte variant.)  Even if you base64-encode it so
the keys will be 7-bit safe, you're only talking about 28 bytes per key.

> I'd got one of the databases partly going which was a simple memory
> based database. 

If I have an API, I could probably knock out a simple Berkerley DB 1.x
store for certs and private keys pretty quickly, complete with dynamic
library binding.  This is good enough for a single user, with multiple
users you would want to go to 2.x with transaction handling.

The schema I had in mind was:

certs:

key:
  char iands[28];          // base64 encoded SHA-1 hash of issuer DN + SN

data: 
  struct {
        char version[2];       // { 0, 0 }
        char state;            // V)alid, E)xpired, R)evoked, other?
        char dateRevoked[16];  // YYYYMMDDHHMMSS, GMT
//  maybe a list of hashes, for efficienciency?
        char cert[0];          // ASN.1 encoded cert
  };

the date is split out, not time_t, because this is Y2038 safe and
eliminates byte ordering issues.

private keys:

key:
  char kid[28];           // base-64 encoded SHA-1 hash of public key

data:
   struct {
     char version[2];     // { 0, 0 }
     char cert[0];        // ASN.1 encoded key, normally PKCS8
   }

The Cert API could be:
 
  struct CertDB {
         int (*close)(CertDB *);

         int (*store)(CertDB *, const X509 *x);

         int (*revoke)(CertDB *, const char *iands);

         int (*remove)(CertDB *, const char *iands);
     
         X509 (*lookup)(CertDB *, const char *iands);
         char (*getStatus)(CertDB *, const char *iands);
         char (*getDateRevoked)(CertDB *, const char *iands);

         int (*findBySubject)(CertDB *, const X509_NAME *, char **results);
         int (*findByIssuer)(CertDB *, const X509_NAME *, char **results);
         int (*findBySubjectHash)(CertDB *, const char *, char **results);
         int (*findByIssuerHash)(CertDB *, const char *, char **results);
         int (*findByKeyID)(CertDB *, const char *, char **results);

     int (*listRevoked)(CertDB *, char **results);

         int (*expire)(CertDB *);
  };
  typedef struct CertDB CertDB;

  CertDB * certdb_open(parameters to be determined...)

The API is designed to handle the normal cases, oddball cases require
two calls.  E.g., "store" assumes that a cert is pending(?), valid or 
expired (depending on the date), but not revoked.  If you're adding a
revoked cert, you'll need to make a second call.

Also, all calls are through a semi-opaque object that actually
defines the interface each of the plug-ins must follow.  New functions
could call old libraries and just return an "ENOSYS" (unimplemented
function) error code.

Store() determines the state of the cert and stores it.  It may
also cache some of the fields used in a "findByX()" call.  One 
interesting possibility is refusing to accept a cert unless it's
either self-signed or the issuer is already in the database.
There would need to be a way to override this, though.  (Or it
could be an option to "open()".)

Revoke() sets the 'revoked' flag and revocation date.  Should it
also propagate revocation to any certs it was used to sign?

Remove() actually removes the object from the database (or if you're
paranoid, marks it deleted but leaves the data in the table.)

Lookup() retrieves a single object given it's unique key.  getStatus()
and getDateRevoked() return the corresponding fields.

FindByX() returns a count of the number of matching entries, and 
allocates and populates a list.  Perhaps it should also contain a
"max items" field?

ListRevoked() is similar, but it lists all certificates which are
'revoked' but not yet expired.

Expire() walks through the database and changes entries from
'pending' to 'valid', or 'valid' to 'expired'.

The KeyDB API is similar

  struct KeyDB {
         int (*close)(KeyDB *);

         int (*store)(KeyDB *, const char *kid, const PKCS8 *pkcs8);

         int (*remove)(KeyDB *, const char *kid);
     
         PKCS8 (*lookup)(KeyDB *, const char *kid);

  };
  typedef struct KeyDB KeyDB;

  KeyDB * keydb_open(parameters to be determined...)
______________________________________________________________________
OpenSSL Project                                 http://www.openssl.org
Development Mailing List                       [EMAIL PROTECTED]
Automated List Manager                           [EMAIL PROTECTED]

Reply via email to