NSS Shared DB
NSS has not updated it's database format since 199x. Over the years the restrictions created by the current database has become more accute. Of these restrictions, the lack of the ability for applications to share certificate and key database has been one of the more severe. As more applications use NSS, the need for each one to manage a database separately becomes unnecessary overhead.
In 2001 we built some tools to work around those restrictions so certain applications could share the database if they supplied their own shared database implementation, and configured NSS to use that implementation. Today we have a process level, ACID, open source, and widely available database called SQLite. In addition, there is a strong desire to make NSS the system security service for Linux. I am proposing how we could leverage this database to give all of our applications Shared Database access.
Besides the lack of shared database, other issues with the current NSS database scheme include:
- The need for a more flexible schema which can handle storing more meta information about certificates and keys, particular finer grain information about the trust of a certificate.
- The need to match the underlying certificate and key storage with it's reflection into NSS (that is PKCS #11).
- FIPS requires integrity checks on critical keys and trust objects. We need to be able to store that information in the database.
Where we are today
At initialization time, NSS currently takes an argument which points to some directory the application uses to store its private configuration data. NSS uses 3 libdbm files in that directory:
- cert8.db - stores publicly accessible objects (certs, CRLs, S/MIME records).
- key3.db - stores the private keys.
- secmod.db - stores the PKCS #11 module configuration.
In addition:
- NSS may also use a directory called cert8.dir to store very large blobs (typically large CRLs).
- NSS may read from from previous certificate database (cert7.db, cert5.db, etc.) and build a new cert8.db if it doesn't exist.
These files are all accessed through the softoken, making libsoftokn3.so the only NSS library that needs to link with libdbm.
If the directory argument passed to NSS starts with the string 'multiaccess:', NSS does not use it as a directory path. Instead, NSS parses the string out as follows:
multiaccess:appName[:directory]
Where:
multicaccess is a keyword.
appName is a unique string for each group of applications which share the database.
directory is an optional parameter pointing to an NSS non-shared database which NSS will use to update the shared database from on loading.
NSS will find librdb.so (rdb.dll on Windows) in its path and load it. This shared library is expected to implement a superset of the standard libdbm interface. The main entry point is rdbopen, which will pass the appName, database name, and open flags. The rdb shared library will pick a location or method to store the database (it may not necessarily be a file), then handle the raw db records from NSS. The library does not do any formatting of the data.
What we want to do
We want to move key3 and cert8 into new SQL databases called key4.db and cert9.db. These databases will store the PKCS #11 objects currently stored or implied in cert8 and key3.
Optionally the databases could be combined into a single database, cert9.db, where private and public objects are stored in separate tables. Softoken will automatically identify any cert9.db which has an imbedded key store and open that key store up. By default a separate key4.db and cert9.db will be opened. In the first release there will be no way to create a combined cert/keydb.
schema
The schema for the database will be simple.
- Each row will represent an object.
- The row schema will contain the the Object ID and the list of known Attribute Types.
- Newer versions of NSS may add new attribute types on the fly as necessary (extending the schema).
- The Attribute values will be stored as binary blobs.
- Attributes that represent CK_ULONG values will be stored as 32-bit values in network byte order.
- All other objects, byte order is already specified by PKCS #11.
- Private attributes will be encrypted with a PKCS #5 PBE in the same way the pkcs8 private and secret key data is encrypted today.
- Softoken will only set those attributes appropriate for the given object. If The attribute is not appropriate it will be left blank. (Note that sqlite does not distinguish between a NULL attribute and an empty one. This will be handled by storing a special value which means 'NULL' when writing a NULL record.
- integrity will be maintained by a PBE based MAC on critical attributes.
Out of band data necessary for the proper operation of softoken, but not reflected back to NSS in general (currently this only applies to password entries), will be stored in separate tables in the database with their own schema.
Database extension is accomplished in 2 ways:
- New attributes are added to the list known attributes and to already defined PKCS #11 objects. Older database objects can be detected because they will have 'invalid' values for these attributes (example, could add CKA_TRUST_EXTENSION_OVERRIDE to trust objects to add or override existing certificate extensions).
- Add new PKCS #11 objects to hold the data (example, could add a new SSL_DATA record to store mappings to various certificates to different cipher suites and host name*)
Softoken will be able to store the following objects and attributes. In the table below, attributes marked CK_ULONG will be written to the database as a 32-bit network order unsigned integer, attributes marked 'encrypted' will be encrypted with the token's pbe key, and attributes marked 'MACed' will be MACed with the token's pbe key.
Legal Attributes and objects
While the key and certificate database format is extensible, the initial implementation has to understand a particular subset of attributes. The following list the attributes and understood, and any special coding conditions for that given attribute.
- Stored in the key database:
- CKO_PRIVATE_KEY
- CKA_CLASS - CK_ULONG
- CKA_TOKEN
- CKA_PRIVATE
- CKA_LABEL
- CKA_MODIFIABLE
- CKA_ID
- CKA_START_DATE
- CKA_END_DATE
- CKA_DERIVE
- CKA_LOCAL
- CKA_KEY_TYPE - CK_ULONG
- CKA_DECRYPT
- CKA_SIGN
- CKA_SIGN_RECOVER
- CKA_UNWRAP
- CKA_SUBJECT
- CKA_SENSITIVE,
- CKA_EXTRACTABLE
- CKA_NSS_DB - (allowed, but not required)
- CKA_MODULUS - RSA only - MACed
- CKA_PUBLIC_EXPONENT - RSA only - MACed
- CKA_PRIVATE_EXPONENT - RSA only - encrypted
- CKA_PRIME_1 - RSA only - encrypted
- CKA_PRIME_2 - RSA only - encrypted
- CKA_EXPONENT_1 - RSA only - encrypted
- CKA_EXPONENT_2 - RSA only - encrypted
- CKA_COEFFICIENT - RSA only - encrypted
- CKA_SUBPRIME - DSA only - MACed
- CKA_PRIME - DH, DSA only - MACed
- CKA_BASE - DH, DSA only - MACed
- CKA_VALUE - DH, DSA, and ECC only - encrypted
- CKA_EC_PARAMS - ECC only - MACed
- CKO_SECRET_KEY
- CKA_CLASS - CK_ULONG
- CKA_TOKEN
- CKA_PRIVATE
- CKA_LABEL
- CKA_MODIFIABLE
- CKA_ID
- CKA_START_DATE
- CKA_END_DATE
- CKA_DERIVE
- CKA_LOCAL
- CKA_KEY_TYPE - CK_ULONG
- CKA_SENSITIVE
- CKA_EXTRACTABLE
- CKA_ENCRYPT
- CKA_DECRYPT
- CKA_SIGN
- CKA_VERIFY
- CKA_WRAP
- CKA_UNWRAP
- CKA_VALUE - encrypted
- CKO_PRIVATE_KEY
- Stored in the cert database:
- CKO_PUBLIC_KEY
- CKA_CLASS - CK_ULONG
- CKA_TOKEN
- CKA_PRIVATE
- CKA_LABEL
- CKA_MODIFIABLE
- CKA_ID
- CKA_START_DATE
- CKA_END_DATE
- CKA_DERIVE
- CKA_LOCAL
- CKA_KEY_TYPE - CK_ULONG
- CKA_ENCRYPT
- CKA_VERIFY
- CKA_VERIFY_RECOVER
- CKA_WRAP
- CKA_SUBJECT
- CKA_MODULUS - RSA only - MACed
- CKA_PUBLIC_EXPONENT - RSA only - MACed
- CKA_SUBPRIME - DSA only - MACed
- CKA_PRIME - DH, DSA only - MACed
- CKA_BASE - DH, DSA only - MACed
- CKA_VALUE - DH, DSA, ECC only - MACed
- CKA_EC_PARAMS - ECC only - MACed
- CKO_CERTIFICATE
- CKA_CLASS - CK_ULONG
- CKA_TOKEN
- CKA_PRIVATE
- CKA_LABEL
- CKA_MODIFIABLE
- CKA_CERTIFICATE_TYPE - CK_ULONG
- CKA_VALUE
- CKA_SUBJECT
- CKA_ISSUER
- CKA_SERIAL_NUMBER
- CKA_NSS_OVERRIDE_EXTENSIONS (sql database only) - MACed
- CKO_NSS_TRUST
- CKA_CLASS - CK_ULONG
- CKA_TOKEN
- CKA_PRIVATE
- CKA_LABEL
- CKA_MODIFIABLE
- CKA_ISSUER - MACed
- CKA_SERIAL_NUMBER- MACed
- CKA_CERT_SHA1_HASH- MACed
- CKA_CERT_MD5_HASH - MACed
- CKA_TRUST_SERVER_AUTH - CK_ULONG - MACed
- CKA_TRUST_CLIENT_AUTH - CK_ULONG - MACed
- CKA_TRUST_EMAIL_PROTECTION - CK_ULONG - MACed
- CKA_TRUST_CODE_SIGNING - CK_ULONG - MACed
- CKA_TRUST_STEP_UP_APPROVED - MACed
- CKO_NSS_CRL
- CKA_CLASS - CK_ULONG
- CKA_TOKEN
- CKA_PRIVATE
- CKA_LABEL
- CKA_MODIFIABLE
- CKA_SUBJECT
- CKA_VALUE
- CKA_NETSCAPE_URL
- CKA_NETSCAPE_KRL
- CKO_NSS_SMIME
- CKA_CLASS - CK_ULONG
- CKA_TOKEN
- CKA_PRIVATE
- CKA_LABEL
- CKA_MODIFIABLE
- CKA_SUBJECT
- CKA_NETSCAPE_EMAIL
- CKA_NETSCAPE_SMIME_TIMESTAMP
- CKA_VALUE
- CKO_PUBLIC_KEY
Special coding for CK_ULONG
All CK_ULONG values are encoded as 4 byte values, most significant byte first.
Special coding for encrypted entries
Encrypted entries are stored in the database as pkcs5 encoded blobs.
SEQUENCE { AlgorithmID algorithm, OctetString encryptedData };
The algorithm parameter must be a valid PKCS5 or PKCS12 PBE oid, and contain the appropriate salt value. NSS understands the following PBE oids:
- SEC_OID_PKCS12_PBE_WITH_SHA1_AND_TRIPLE_DES_CBC
- SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_2KEY_TRIPLE_DES_CBC
- SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_3KEY_TRIPLE_DES_CBC
- SEC_OID_PKCS5_PBE_WITH_MD2_AND_DES_CBC
- SEC_OID_PKCS5_PBE_WITH_MD5_AND_DES_CBC
- SEC_OID_PKCS5_PBE_WITH_SHA1_AND_DES_CBC
- SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_128_BIT_RC2_CBC
- SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_40_BIT_RC2_CBC
- SEC_OID_PKCS12_PBE_WITH_SHA1_AND_128_BIT_RC2_CBC
- SEC_OID_PKCS12_PBE_WITH_SHA1_AND_40_BIT_RC2_CBC
- SEC_OID_PKCS12_PBE_WITH_SHA1_AND_128_BIT_RC4
- SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_2KEY_TRIPLE_DES_CBC
- SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_3KEY_TRIPLE_DES_CBC
- SEC_OID_PKCS12_PBE_WITH_SHA1_AND_TRIPLE_DES_CBC
- SEC_OID_PKCS5_PBE_WITH_MD2_AND_DES_CBC
- SEC_OID_PKCS5_PBE_WITH_MD5_AND_DES_CBC
- SEC_OID_PKCS5_PBE_WITH_SHA1_AND_DES_CBC:
- SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_128_BIT_RC2_CBC:
- SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_40_BIT_RC2_CBC:
- SEC_OID_PKCS12_PBE_WITH_SHA1_AND_128_BIT_RC2_CBC:
- SEC_OID_PKCS12_PBE_WITH_SHA1_AND_40_BIT_RC2_CBC:
- SEC_OID_PKCS12_PBE_WITH_SHA1_AND_128_BIT_RC4:
- SEC_OID_PKCS5_PBES2 (actual encryption and prf algorithms are stored in parameters)
- Valid Encryption Algorithms
- SEC_OID_AES_128_CBC
- SEC_OID_AES_192_CBC
- SEC_OID_AES_256_CBC
- Valid PRF Algorithms
- SEC_OID_HMAC_SHA_1
- SEC_OID_HMAC_SHA256
- SEC_OID_HMAC_SHA384
- SEC_OID_HMAC_SHA512
- Valid Encryption Algorithms
- SEC_OID_PKCS5_PBMAC1 (actual hmac and prf algorthms are stored in parameters)
- Valid HMAC and PRF Algorithms
- SEC_OID_HMAC_SHA_1
- SEC_OID_HMAC_SHA256
- SEC_OID_HMAC_SHA384
- SEC_OID_HMAC_SHA512
- Valid HMAC and PRF Algorithms
The base key used in the PBE is the token password hashed with the token's global salt stored in the password entry. <In FIPS mode this resulting key is further transformed by setting it to value 'x' and using x to raise generator 'g' to the 'x' power modulo 'p'. The final result is the token key. g and p are stored with the base user's protection in the filesystem in the same directory as the certificate and key database. g and p should be considered 'secret' values.>*
- Purpose: by choosing an appropriate g and p, we can control password attempt rates from multiple processes. By making g and p secret, we can remove our reliance on the security of the PBE - the security of the password is at least as strong as the security of g and p.
This will be implemented by checking for the g/p file in the database directory. If no g/q file exists, key processing would continue as normal. If g/p does exist it will be used in the key formation. If a new key is being created, then g/p is create/used if in FIPS mode, and not create/used if not in FIPS mode. This way passwords continue to work even after mode switching.
Special coding for MACed entries
Some specialized entries in the database are MACed to either for FIPS protection or to protect trust integrity. MACed entries are only processed if the user has logged into the token. If the user has not logged into the token, then the MAC is not used.
MACs are not processed for the legacy database.
Entries which require MACs require that the user be logged into the token to write or modify the entry. On readback, they are not checked unless the user is logged in.
Tokens which have no key database (and therefore no master password) do not have any stored MACs.
Attributes that are MACed are:
- Trust object hashes and trust values
- public key values.
Certs themselves are considered properly authenticated by virtue of their signature, or their matching hash with the trust object.
MACS are formed by concatenating the object id (encoded as a database ULONG) with the attribute type (also encoded as a ULONG) and with the atttribute data. This concatenation is fed into a SHA1 HMAC keyed with the token key described in the 'Special coding for encrypted entries'. The resulting Mac is stored in a special table in the key database called the 'metadata'. This table could store other types of meta data as well in the future. Currently the password check is also stored in the metadata table.
Integrity entries are indexed by the string
sig_[cert/key]_{ObjectID}_{Attribute}
Where cert/key indicates which logical database the actual object is stored in, {ObjectID} is the id of the object and {Attribute} is the attribute this integrity entry is for.
Entries are check when the user tries to get and attribute. Once the attributes are fetched, GetAttributes loops through the attributes looking for attributes that require integrity checks. When such an attribute is found, the integrity check entry is fetched and an HMAC is calculated for the attribute. If the attribute matches the integrity entry, processing proceeds. If it doesn't that attribute data is cleared and CKR_SIGNATURE_INVALID is returned.
MACS are PBMAC1 data structures defined in pkcs5 2.0. Since pkcs5 v1 does not have integrity checks and pkcs12 has not definition for storing purely mac data,the shared DB integrity checks uses pkcs5 v2 to store that pbe and mac data.
Database coherency issues
In our previous database, we had issues with database corruption resulting in hard to diagnose issues. In order to mitigate that, This section analyses how various forms of corruption can affect the new database design, and possible ways of repairing that corruption in the field.
Hidden Meta-data records
The most obvious corruption issues surround data records which do not have direct visibility to the application, or NSS itself (beyond softoken). In the previous design these included such records as the SubjectList records which kept track of all the certs with a given subject. In this design, we have the following meta-data records:
- g/p files
- password entries
- MAC data
g/p files only exist in FIPs mode. Loss of g/p files are unrecoverable. All private data and MAC data will become inaccessible. This means all private and secret keys will be lost. Lost MAC data would have to be regenerated. This would require a special tool which modified the sqlite database directly.
loss of password entries would be almost as devistating. If there are any keys or MAC data, it is theoretically possible to verify a password using one of those entries. From that password the password entry could be regenerated. This would require a speical tool that modified the sqlite database directly.
loss of MAC data would interfere with the ability of the database to validate certificates. NOTE: this would only affect the databases ability to validate certificates while the user is logged in. Lost MAC data would have to be regenerated by walking the database and creating fresh MACs.
Linked records
Since each record represents a PKCS #11 object, the current database design is significantly less reliant on two related records being self-consistant. There are 2 areas, however where there is linkages: CKA_IDs and CKO_NSS_SMIME.
CKA_IDs link certificates with their related private keys. This is a PKCS #11 specified linkage. CKA_IDs are generated by NSS when keys are created. NSS sets this value to a hash related to the public key of the key pair. If this value is corrupted in the private key, then NSS will no longer be able to find that private key. A tool at the PKCS #11 level could repair such damage. If the CKA_ID of a certificate is corrupted, NSS will stop recognizing the certificate as a user certificate, and it will be unable to find the key associated with it. This can also be repaired by a PKCS #11 level tool. The corrupted CKA_ID also be repaired by deleting and reimporting the certificate. Both cases could be repaired by reimporting a pkcs12 file with the certificate and key.
If the Private key is deleted, but the corresponding public key is not, then NSS may be confused and think that the private key exists for the certificate. Also, if the public key is deleted, but the private key is not, NSS may be confused and think the private key does not exist for the certificate. Both of these cases will act correctly if the token is logged in. This kind of corruption can be repaired by reimporting the pkcs12 file, or by a PKCS #11 level tool to delete or restore the public key.
CKO_NSS_SMIME object holds the email address, subject of the S/MIME certificate, and the s/mime profile. In the legacy database, multiple email addresses could hold the same profile data and certificate subject, but only one s/mime profile data and subject would be allowed for each email address. In the new database, multiple independent email records can exist for the same email address. While S/MIME will function without CKO_NSS_SMIME objects, Certificates that verify multiple email address can not be found by the email addresses (other then the 'primary' address) without CKO_NSS_SMIME objects. If S/MIME records are corrupted, the Cerificates will not be findable for other email addresses. Unlike the legacy database, these records are destroyed by S/MIME records for other certificates with the same email address. Now if you have multiple certificates with the same email address, all those certificates can be found. This corruption can be repaired with a PKCS #11 level tool.
In order to maintain binary compatibility, the following keywords will be understood and used by softoken.
multiaccess:appName[:directory] works as it does today, including using the cert8/key3 record version.
dbm:directory opens an existing non-shared libdbm version 8 database.
sql:directory1[:directory2] opens a shared database,
cert9.db (& key4.db) in directory1 if cert9.db does exist. If the database does not exist, then directory2 is searched for a libdbm cert8.db and key3.db. If directory2 is not supplied, directory1 is searched.
extern:directory open a sql-like database by loading an external module, a. la. rdb and multiaccess:. This option would not be implemented in the initial release, but the extern: keyword would be reserved for future use.
Plain directory spec. For binary compatibility, the plain directory spec as the same as dbm:directory unless overridden with the NSS_DEFAULT_DATABASE environment variable. Applications will not need to change for this release of NSS. (particularly unfriendly applications that want to tweak with the actual database file). Users can force older applications to share the database with the environment variable. The environment variable only affects non-tagged directories.
When accessing the dbm: and multiaccess: directories, external shared library will be loaded which knows how to handle these legacy databases. This allows us to move much of the current mapping code into this shared library.
Secmod.db
In the dbm: and multiaccess: cases, there will be no changes to secmod.db.
In the sql: case, a new directory with separate flat files containing text files of the format specified in the PKCS #11 working group, but not yet included in any spec. This directory will be opened, locked, used, then closed (much like the current secmod.db). The directory will live as a sub directory of the directory that holds cert9.db/key4.db. As a directory of flat files it would not use the sqlite database to access these records. The file name should become pkcs11.txt.
User App Initialization and System App Initialization
One of the goals of making a shared database version of NSS is to create a 'system crypto library' in which applications will automatically share database and configuration settings. In order for this to work, applications need to be able to open NSS databases from standard locations.
This design assumes that new NSS init functions will be defined for applications wanting to do 'standard user initialization', rather than building special knowledge into softoken or the database model. Note: This is different from the 2001 design, or and earlier prototype shared database, where the database code knew the location of the shared database.
Database Upgrade
NSS will automatically upgrade from old databases to new databases if the following conditions are met:
- The application (either explicitly or implicitly) opens an sql style database read write.
- The sql database does not exist.
- A legacy dbm database exists in the same directory of the sql database.
- The application logs into the softoken (supplies the database password for the existing legacy database). If there is no password this condition is met automatically.
This upgrade handles the initial case where applications want to get the new features of the new database, but not necessarily want to participate in any sharing scheme.
A more natural tact an application may take is move from it's own database instance to a common database instance, shared among several applications. In this case, the application will need to participate in the database upgrade, as it may be necessary to actually merge entries from several databases. In this case NSS will provide services for the application to determine if the shared database it opened had already been updated by the application itself. If not, NSS will provide the application with a function (to be defined), which will read records from the old database and merge them into the new database. NOTE: we may need a couple of functions here to allow recoding such things as passwords encoded with sdr keys which the application may have.
Layering
In order to keep clean separation between the data and database operations, we will continue to maintain an layer between the actual data handling and interpretation and the database itself. The database code will not need to understand:
- What objects are actually stored in it.
- The types of the attributes.
- The meaning of the stored data.
Softoken (not the database adapter layer) will manage canonicalizing any CK_ULONGs, encrypting or decrypting private data blobs, checking integrity and deciding what attributes an object should have and setting the appropriate defaults if necessary.
Since softoken deals with PKCS #11 templates internally, its interface to the database will be in terms of those templates.
The database layer must be multi-thread safe. If the underlying database is not thread safe, sdb_ layer must implement the appropriate locking.
s_open
The database API consists of an initialization call, which returns an SDB data structure (defined below).
CK_RV s_open(const char *directory, const char *certPrefix, const char *keyPrefix, int cert_version, int key_version, int flags, SDB **certdb, SDB **keydb, int *newInit)
The sdb_init function takes:
- directory full path to where the database lives.
- certPrefix a prefix string to add in front of the key and cert db (if keyPrefix is null), null means add no prefix.
- keyPrefix a prefix string to add in front of the key db. Null means use the same prefix as the cert db.
- cert_version current version is the current database version
- key_version is the current key database version
- flags are:
- FORCE
- READONLY
- READ/WRITE/CREATE
- certdb is the returned cert SDB structure
- keydb is the returned key SDB structure
- newInit returns 1 of s_open created new instances of cert and key (used for update).
The returned SDB structure has the following format:
typedef struct SDBStr SDB;
struct SDBStr { void *private; void *sdb_app_private; int sdb_type; int sdb_flags; int sdb_version; CK_RV (*sdb_FindObjectsInit)(SDB *sdb, const CK_ATTRIBUTE *template, int count, SDBFind **find); CK_RV (*sdb_FindObjects)(SDB *sdb, SDBFind *find, CK_OBJECT_HANDLE *ids, int arraySize, int *count); CK_RV (*sdb_FindObjectsFinal)(SDB *sdb, SDBFind *find); CK_RV (*sdb_GetAttributeValue)(SDB *sdb, CK_OBJECT_HANDLE object, CK_ATTRIBUTE *template, int count); CK_RV (*sdb_SetAttributeValue)(SDB *sdb, CK_OBJECT_HANDLE object, const CK_ATTRIBUTE *template, int count); CK_RV (*sdb_CreateObject)(SDB *sdb, CK_OBJECT_HANDLE *object, const CK_ATTRIBUTE *template, int count); CK_RV (*sdb_DestroyObject)(SDB *sdb, CK_OBJECT_HANDLE object); CK_RV (*sdb_GetPWEntry)(SDB *sdb, SDBPasswordEntry *entry); CK_RV (*sdb_PutPWEntry)(SDB *sdb, SDBPasswordEntry *entry); CK_RV (*sdb_Begin)(SDB *sdb); CK_RV (*sdb_Commit)(SDB *sdb); CK_RV (*sdb_Abort)(SDB *sdb); CK_RV (*sdb_Reset)(SDB *sdb); CK_RV (*sdb_Close)(SDB *sdb); };
where:
- private is a pointer to opaque private data specific to the Shared DB implementation.
- sdb_type is the type of database (key [aka private] or cert [aka public]).
- sdb_flags specifies how the database was opened (ReadOnly, Create, etc).
- sdb_version specifies the version of the underlying sdb structure. This allows us to handle future expansion of the sdb data structure safely.
- The rest are function pointers to database primitives described next.
sdb_FindObjectsInit
CK_RV (*sdb_FindObjectsInit)(SDB *sdb, const CK_ATTRIBUTE *template, int count, SDBFind **find);
This function is the equivalent of PKCS #11 C_FindObjectsInit(). It returns a SDBFind context with is opaque to the caller. The caller must call sdb_FindObjectsFinal with this context if sdb_FindobjectsInit succeeds.
sdb_FindObjects
CK_RV (*sdb_FindObjects)(SDB *sdb, SDBFind *find, CK_OBJECT_HANDLE *ids, int arraySize, int *count);
This function is the equivalent of PKCS #11 C_FindObjects(). It takes a SDBFind context returned by sdb_FindObjectsInit. This function has the same semantics as C_FindObjects with respect to handling how many objects are returned in a single call.
sdb_FindObjectsFinal
CK_RV (*sdb_FindObjectsFinal)(SDB *sdb, SDBFind *find);
This function is the equivalent of PKCS #11 C_FindObjectsFinal(). It frees any resources associated with SDBFIND.
sdb_GetAttributeValue
CK_RV (*sdb_GetAttributeValue)(SDB *sdb, CK_OBJECT_HANDLE object, CK_ATTRIBUTE *template, int count);
This function is the equivalent of PKCS #11 C_GetAttributeValue(). It has the same memory allocation and error code semantics of the PKCS #11 call. The attributes passed to sdb_GetAttributeValues are already transformed from their native representations in the following ways:
- CKU_LONG values are stored as 32-bit values in network byte order.
- Private attributes will be encrypted.
sdb_SetAttributeValue
CK_RV (*sdb_SetAttributeValue)(SDB *sdb, CK_OBJECT_HANDLE object, const CK_ATTRIBUTE *template, int count);
This function is the equivalent of PKCS #11 C_SetAttributeValue(). The attributes returned to sdb_SetAttributeValues are transformed from their native representations in the following ways:
- CKU_LONG values returned 32-bit values in network byte order.
- Private attributes returned encrypted.
sdb_CreateObject
CK_RV (*sdb_CreateObject)(SDB *sdb, CK_OBJECT_HANDLE *object, const CK_ATTRIBUTE *template, int count);
This function is the equivalent of PKCS #11 C_CreateObject(). The value of 'object' is chosen by the implementer of sdb_CreateObject. This value must be unique for this sdb instance. It should be no more than 30 bits long.
sdb_DestroyObject
CK_RV (*sdb_DestroyObject)(SDB *sdb, CK_OBJECT_HANDLE object);
This function is the equivalent of PKCS #11 C_Destroy object(). It removed the object from the database.
sdb_GetPWEntry
CK_RV (*sdb_GetPWEntry)(SDB *sdb, SDBPasswordEntry *entry);
Get the password entry. This only applies to the private database.
sdb_PutPWEntry
CK_RV (*sdb_PutPWEntry)(SDB *sdb, SDBPasswordEntry *entry);
Write the password entry. This only applies to the private database. Writing a password entry will overwrite the old entry.
sdb_Begin
CK_RV (*sdb_Begin)(SDB *sdb);
Begin a transaction. Any write to the database (sdb_CreateObject, sdb_DestroyObject, sdb_SetAttributeValue) must be accomplished while holding a transaction. Transactions are completed by calling sdb_Commit to commit the change, or sdb_Abort to discard the change. More than one write operation may be made while holding a transaction. Aborting the transaction will discard all writes made while in the transaction.
sdb_Commit
CK_RV (*sdb_Commit)(SDB *sdb);
Commit a transaction. Any write to the database (sdb_CreateObject, sdb_DestroyObject, sdb_SetAttributeValue) must be accomplished while holding a transaction. Transactions are completed by calling sdb_Commit to commit the change, or sdb_Abort to discard the change. More than one write operation may be made while holding a transaction.
sdb_Abort
CK_RV (*sdb_Abort)(SDB *sdb);
Abort a transaction. Any write to the database (sdb_CreateObject, sdb_DestroyObject, sdb_SetAttributeValue) must be accomplished while holding a transaction. Transactions are completed by calling sdb_Commit to commit the change, or sdb_Abort to discard the change. More than one write operation may be made while holding a transaction. Aborting the transaction will discard all writes made while in the transaction.
sdb_Close
CK_RV (*sdb_Close)(SDB *sdb);
Close the SDB and free up any resources associated with it.
sdb_Reset
CK_RV (*sdb_Reset)(SDB *sdb);
Reset zeros out the key database and resets the password.
legacy DB support
The old dbm code can be supported with the above SDB structure with the following exceptions:
- The old db code cannot be extensible (can't dynamically handle new types).
- A private interface may be needed to unwrap the private keys, or provide a handle to the password so the keys can be presented in the attribute format.
This code would live in its own shared library, called lgdbm (with the appropriate platform semantics, lgdbm.dll on windows, liblgdbm.so on unix, etc). Most of the low level cert, CRL, key handling, and translation to PKCS #11 objects and attributes that was part of softoken will moved to this legacy shared library. When access to old databased are needed, the lgdbm shared library will be loaded, and the following symbols will be dynamically found:
- legacy_Open - This has the same signature as s_open and returns SDB handles for the legacy database.
- legacy_ReadSecmodDB, legacy_ReleaseSecmodDBData, legacy_DeleteSecmodDB, legacy_AddSecmodDB - These functions provide access to the old secmod databases.
- legacy_Shutdown - This is called when NSS is through with all database support (that is when softoken shuts down).
- legacy_SetCryptFunctions - This is used to set some callbacks that the legacy db can call to decrypt and encrypt password protected records (pkcs8 formatted keys, etc.). This allows the legacy database to translate it's database records to the new format without getting direct access to the keys.
NSS will automaticall load the legacy database support under the following conditions:
- The application requests that the old databases be loaded (either implicitly or explicitly).
- The application request that new databases are loaded, but the new databases do not exist and the old databases do.