Authenticating Berkeley DB SQL Keystore-Based User

Berkeley DB SQL provides keystore-based user authentication to allow the user to work easily with encryption and user authentication together.

In keystore-based user authentication, encryption becomes mandatory if you enable user authentication. You can then work just with the user authentication API and without the knowledge of an encryption key. You can do this by storing the encryption key into a keystore file.

The encryption key stored in the keystore file is encrypted. The keystore file, name ending with".ks", is placed under the same directory as the database environment. Each authenticated user has one entry in this keystore file. The entry contains the user's name and the encryption key. When sqlite3_user_authenticate() is called, if the encryption key is not applied to the database connection yet, Berkeley DB SQL will find the user's entry in the keystore file, compute the database encryption key with the user's password, and then apply the encryption key to the database connection.

To provide security for the Berkeley DB SQL API, turn on keystore-based user authentication. You can enable keystore-based user authentication by adding the - DBDBSQL_USER_AUTHENTICATION_KEYSTORE compile option. For further details, read the sections below.

Interface

Keystore user authentication APIs work the same way as non-keystore user authentication. For more information, see Authenticating Berkeley DB SQL User (without Keystore).

Bootstrap

A no-authentication-required database becomes an authentication-required database after the first user is added to the Berkeley DB database. This is called user authentication bootstrap. In bootstrap, you must set the isAdmin parameter of the sqlite3_user_add() to true. After bootstrap, the first added user is logged into the database connection. There are several cases related to the encryption key when doing keystore-based user authentication bootstrap:
  • If the database file does not exist yet and the user does not provide his/her encryption key, a random generated key will be applied to the database and be stored into the keystore file. The call sequence is:

    sqlite3_open_v2(); 
    sqlite3_user_add();
    

    Note:

    It is recommended to back up the keystore file, especially when using a generated random key.
  • If the database file does not exist yet and the user provides the encryption key, the encryption key provided will be applied to the database and be stored into the keystore file. The call sequence is:

    sqlite3_open_v2(); 
    sqlite3_key_v2(); 
    sqlite3_user_add();
    
  • If the database file already exists, the database is encrypted, and the user provides the correct encryption key, the encryption key provided will be stored into the keystore file. The call sequence is:

    sqlite3_open_v2(); 
    sqlite3_key_v2(); 
    sqlite3_user_add();
    
The bootstrap fails if:
  • The database file already exists, the database is encrypted, and the user provides an incorrect encryption key.

  • The database file already exists but the database is not encrypted.

User Login

As the encryption key is already in the keystore file, you only need to provide the username/password details to work with the encrypted database. Berkeley DB SQL will compute the encryption key from the keystore file and apply it to the database connection. The call sequence is:

sqlite3_open_v2(); 
sqlite3_user_authenticate(); 
/* Database is now usable */

You can also provide the encryption key before the sqlite3_user_authenticate() call. In this case, Berkeley DB SQL will not visit the keystore file for the encryption key. The call sequence is:

sqlite3_open_v2(); 
sqlite3_key_v2(); 
sqlite3_user_authenticate(); 
/* Database is now usable */

Transaction

Berkeley DB user authentication APIs sqlite3_user_add()/sqlite3_user_change()/sqlite3_user_delete() work in their own transaction. Calling these APIs inside a transaction results in an error.

The Lock File

Berkeley DB keystore user authentication uses a locking file to ensure it behaves correctly in a multi-threaded environment. In rare cases, if a system or application crash occurs while updating the keystore file, the locking file may not be cleaned and the next sqlite3_user_authenticate() call will be rejected. In this case, you need to clean the .lck file under the database environment.