-
Notifications
You must be signed in to change notification settings - Fork 3.4k
Description
Bug description
The behaviour of SQLite itself changed beginning with version 3.48.0. Since then simple queries like SELECT 1; touch the underlying database file, although the database schema is not affected - this was not the case for prior versions of SQLite.
Unfortunately, Microsoft.Data.SQLite uses such a SQL statement to quote the password, before setting the encryption key with PRAGMA key - see
efcore/src/Microsoft.Data.Sqlite.Core/SqliteConnectionInternal.cs
Lines 111 to 117 in 6e10434
| var quotedPassword = ExecuteScalar( | |
| "SELECT quote($password);", | |
| connectionOptions.Password, | |
| connectionOptions.DefaultTimeout); | |
| ExecuteNonQuery( | |
| "PRAGMA key = " + quotedPassword + ";", | |
| connectionOptions.DefaultTimeout); |
This SQL statement accesses the SQLite database file internally, before the encryption key is set, and therefore throws the error message File is not a database.
The code should be changed - either by quoting the password without using SQL or by calling sqlite3_key instead of using PRAGMA key.
Source code and a database file exposing the issue can be found here:
Source: https://github.com/utelle/SQLite3MultipleCiphers-NuGet/blob/main/Tests/Tests/Tests.cs
Database: https://github.com/utelle/SQLite3MultipleCiphers-NuGet/blob/main/Tests/Tests/sqlcipher-4.0-testkey.db
Error messages: https://github.com/utelle/SQLite3MultipleCiphers-NuGet/actions/runs/13732334999/job/38411482080?pr=9
Your code
// Part of the test application (see https://github.com/utelle/SQLite3MultipleCiphers-NuGet/blob/main/Tests/Tests/Tests.cs)
public void Database_is_SQLCipher4()
{
// Test to access database encrypted with SQLCipher version 4
// Result: 78536 1 1 one one 1 2 one two
using var connection = new SqliteConnection("Data Source=file:sqlcipher-4.0-testkey.db?cipher=sqlcipher&legacy=4;Password=testkey");
var value = connection.ExecuteScalar<int>("select count(*) from t1");
Assert.Equal(78536, value);
}Stack traces
Failed Tests.Database_is_SQLCipher4 [46 ms]
Error Message:
Microsoft.Data.Sqlite.SqliteException : SQLite Error 26: 'file is not a database'.
Stack Trace:
at Microsoft.Data.Sqlite.SqliteException.ThrowExceptionForRC(Int32 rc, sqlite3 db)
at Microsoft.Data.Sqlite.SqliteConnectionInternal.RetryWhileBusy(Func`1 action, Action reset, Int32 timeout, Stopwatch timer)
at Microsoft.Data.Sqlite.SqliteConnectionInternal.RetryWhileBusy(Func`1 action, Int32 timeout, Stopwatch timer)
at Microsoft.Data.Sqlite.SqliteConnectionInternal.ExecuteScalar(String sql, String p1, Int32 timeout)
at Microsoft.Data.Sqlite.SqliteConnectionInternal..ctor(SqliteConnectionStringBuilder connectionOptions, SqliteConnectionPool pool)
at Microsoft.Data.Sqlite.SqliteConnectionPool.GetConnection()
at Microsoft.Data.Sqlite.SqliteConnectionFactory.GetConnection(SqliteConnection outerConnection)
at Microsoft.Data.Sqlite.SqliteConnection.Open()
at Dapper.SqlMapper.ExecuteScalarImpl[T](IDbConnection cnn, CommandDefinition& command) in /_/Dapper/SqlMapper.cs:line 3022
at Dapper.SqlMapper.ExecuteScalar[T](IDbConnection cnn, String sql, Object param, IDbTransaction transaction, Nullable`1 commandTimeout, Nullable`1 commandType) in /_/Dapper/SqlMapper.cs:line 597
at Tests.Database_is_SQLCipher4() in D:\a\SQLite3MultipleCiphers-NuGet\SQLite3MultipleCiphers-NuGet\Tests\Tests\Tests.cs:line 44
at System.RuntimeMethodHandle.InvokeMethod(Object target, Void** arguments, Signature sig, Boolean isConstructor)
at System.Reflection.MethodBaseInvoker.InvokeWithNoArgs(Object obj, BindingFlags invokeAttr)
Microsoft.Data.Sqlite version
8.0.13
Target framework
.NET 8.0
Operating system
Microsoft Windows Server 2022