Search code examples
rusttaurirusqlite

Check if SQLCipher database is unlocked and ready


I'm currently connecting my local Tauri project with an SQLite DB via Rusqlite. The database is based on SQLCipher and I can create it without problems. But how to I use it? I want to unlock it and check whether it can be used or not (check integrity, etc...). But I am having problems with Rusqlite, as the docs are not very helpful in my opinion.

I have a command that is supposed to unlock and check the database:

// CRATE 1
pub fn get_connection_pool(pin: String) -> Result<Pool<SqliteConnectionManager>> {
    let db_dir = get_database_file(); // TODO: Use tauri app_handle to manage directories
    let manager = SqliteConnectionManager::file(db_dir.clone());

    let pool = Pool::new(manager).expect("Could not create Connection Pool Manager");
    let conn = pool.get().expect("Could not retrieve Connection from Pool");
    conn.pragma_update(None, "KEY", pin).expect("Could not execute pragma key");
    conn.pragma_update(None, "FOREIGN_KEYS", "ON")
        .expect("Could not execute PRAGMA foreign_keys=ON");

    info!("Retrieved database and prepared pool");

    Ok(pool)
}


// CRATE 2
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct UnlockDatabaseData {
    pub pin: String,
}

#[tauri::command]
pub async fn unlock_database<R: Runtime>(
    app: tauri::AppHandle<R>,
    data: CommandValue<UnlockDatabaseData>,
) -> CommandResult<bool> {
    let db = get_connection_pool(data.values.pin);

    match db {
        Ok(database) => {
            let conn = database
                .get()
                .expect("Could not get database connection from pool");

            conn.execute("SELECT * FROM campaigns LIMIT 1", [])
                .expect("Could not perform integrity check on database");

            let state = AppState::new(database);
            app.manage(state);
            Ok(true)
        }
        Err(err) => Err(CommandError::Error(err)),
    }
}

The code above throws an error on this line:

conn.execute("SELECT * FROM campaigns LIMIT 1", [])
            .expect("Could not perform integrity check on database");

Saying: Could not perform integrity check on database: SqliteFailure(Error { code: NotADatabase, extended_code: 26 }, Some("file is not a database")) This means the database is not unlocked as I see it. But as you can see I am using PRAGMA Key when opening the connection.

Also the command still returns true to the frontend instead of an error.

Is there a better way to check for integrity and unlock status? (I know there are pragmas for both, though they did not seem to work as expected)


Solution

  • No, there is no better way.

    According to the documentation, there is no better way than to query the database:

    The only way to determine if the key is correct is to try to read from the database file. An incorrect key will result in a read error.