Skip to content

Commit c360ae6

Browse files
committed
Require encryption keys to be passed to constructors
This is to prevent the error of turning on WAL (new default) before the key has been set. This also prevents the user from not calling the old SetKey functions at the wrong time. Working on #597
1 parent 65fe573 commit c360ae6

File tree

4 files changed

+66
-65
lines changed

4 files changed

+66
-65
lines changed

src/SQLite.cs

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
//
2-
// Copyright (c) 2009-2017 Krueger Systems, Inc.
2+
// Copyright (c) 2009-2018 Krueger Systems, Inc.
33
//
44
// Permission is hereby granted, free of charge, to any person obtaining a copy
55
// of this software and associated documentation files (the "Software"), to deal
@@ -228,8 +228,11 @@ static SQLiteConnection ()
228228
/// If you use DateTimeOffset properties, it will be always stored as ticks regardingless
229229
/// the storeDateTimeAsTicks parameter.
230230
/// </param>
231-
public SQLiteConnection (string databasePath, bool storeDateTimeAsTicks = true)
232-
: this (databasePath, SQLiteOpenFlags.ReadWrite | SQLiteOpenFlags.Create, storeDateTimeAsTicks)
231+
/// <param name="key">
232+
/// Specifies the encryption key to use on the database. Should be a string or a byte[].
233+
/// </param>
234+
public SQLiteConnection (string databasePath, bool storeDateTimeAsTicks = true, object key = null)
235+
: this (databasePath, SQLiteOpenFlags.ReadWrite | SQLiteOpenFlags.Create, storeDateTimeAsTicks, key: key)
233236
{
234237
}
235238

@@ -250,7 +253,10 @@ public SQLiteConnection (string databasePath, bool storeDateTimeAsTicks = true)
250253
/// If you use DateTimeOffset properties, it will be always stored as ticks regardingless
251254
/// the storeDateTimeAsTicks parameter.
252255
/// </param>
253-
public SQLiteConnection (string databasePath, SQLiteOpenFlags openFlags, bool storeDateTimeAsTicks = true)
256+
/// <param name="key">
257+
/// Specifies the encryption key to use on the database. Should be a string or a byte[].
258+
/// </param>
259+
public SQLiteConnection (string databasePath, SQLiteOpenFlags openFlags, bool storeDateTimeAsTicks = true, object key = null)
254260
{
255261
if (databasePath==null)
256262
throw new ArgumentException ("Must be specified", nameof(databasePath));
@@ -284,10 +290,20 @@ public SQLiteConnection (string databasePath, SQLiteOpenFlags openFlags, bool st
284290
StoreDateTimeAsTicks = storeDateTimeAsTicks;
285291

286292
BusyTimeout = TimeSpan.FromSeconds (0.1);
293+
Tracer = line => Debug.WriteLine (line);
294+
295+
if (key is string stringKey) {
296+
SetKey (stringKey);
297+
}
298+
else if (key is byte[] bytesKey) {
299+
SetKey (bytesKey);
300+
}
301+
else if (key != null) {
302+
throw new ArgumentException ("Encryption keys must be strings or byte arrays", nameof (key));
303+
}
287304
if (openFlags.HasFlag (SQLiteOpenFlags.ReadWrite)) {
288305
ExecuteScalar<string> ("PRAGMA journal_mode=WAL");
289306
}
290-
Tracer = line => Debug.WriteLine (line);
291307
}
292308

293309
/// <summary>
@@ -304,13 +320,13 @@ static string Quote (string unsafeString)
304320
}
305321

306322
/// <summary>
307-
/// Sets the key used to encrypt/decrypt the database.
323+
/// Sets the key used to encrypt/decrypt the database with "pragma key = ...".
308324
/// This must be the first thing you call before doing anything else with this connection
309325
/// if your database is encrypted.
310326
/// This only has an effect if you are using the SQLCipher nuget package.
311327
/// </summary>
312328
/// <param name="key">Ecryption key plain text that is converted to the real encryption key using PBKDF2 key derivation</param>
313-
public void SetKey (string key)
329+
void SetKey (string key)
314330
{
315331
if (key == null) throw new ArgumentNullException (nameof (key));
316332
var q = Quote (key);
@@ -324,7 +340,7 @@ public void SetKey (string key)
324340
/// This only has an effect if you are using the SQLCipher nuget package.
325341
/// </summary>
326342
/// <param name="key">256-bit (32 byte) ecryption key data</param>
327-
public void SetKey (byte[] key)
343+
void SetKey (byte[] key)
328344
{
329345
if (key == null) throw new ArgumentNullException (nameof (key));
330346
if (key.Length != 32) throw new ArgumentException ("Key must be 32 bytes (256-bit)", nameof(key));
@@ -2021,6 +2037,7 @@ public class SQLiteConnectionString
20212037
public string ConnectionString { get; private set; }
20222038
public string DatabasePath { get; private set; }
20232039
public bool StoreDateTimeAsTicks { get; private set; }
2040+
public object Key { get; private set; }
20242041

20252042
#if NETFX_CORE
20262043
static readonly string MetroStyleDataPath = Windows.Storage.ApplicationData.Current.LocalFolder.Path;
@@ -2038,10 +2055,11 @@ public static bool IsInMemoryPath(string databasePath)
20382055

20392056
#endif
20402057

2041-
public SQLiteConnectionString (string databasePath, bool storeDateTimeAsTicks)
2058+
public SQLiteConnectionString (string databasePath, bool storeDateTimeAsTicks, object key)
20422059
{
20432060
ConnectionString = databasePath;
20442061
StoreDateTimeAsTicks = storeDateTimeAsTicks;
2062+
Key = key;
20452063

20462064
#if NETFX_CORE
20472065
DatabasePath = IsInMemoryPath(databasePath)

src/SQLiteAsync.cs

Lines changed: 13 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ public partial class SQLiteAsyncConnection
3737
{
3838
SQLiteConnectionString _connectionString;
3939
SQLiteConnectionWithLock _fullMutexReadConnection;
40-
bool isFullMutex;
40+
readonly bool isFullMutex;
4141
SQLiteOpenFlags _openFlags;
4242

4343
/// <summary>
@@ -54,8 +54,11 @@ public partial class SQLiteAsyncConnection
5454
/// If you use DateTimeOffset properties, it will be always stored as ticks regardingless
5555
/// the storeDateTimeAsTicks parameter.
5656
/// </param>
57-
public SQLiteAsyncConnection (string databasePath, bool storeDateTimeAsTicks = true)
58-
: this (databasePath, SQLiteOpenFlags.FullMutex | SQLiteOpenFlags.ReadWrite | SQLiteOpenFlags.Create, storeDateTimeAsTicks)
57+
/// <param name="key">
58+
/// Specifies the encryption key to use on the database. Should be a string or a byte[].
59+
/// </param>
60+
public SQLiteAsyncConnection (string databasePath, bool storeDateTimeAsTicks = true, object key = null)
61+
: this (databasePath, SQLiteOpenFlags.FullMutex | SQLiteOpenFlags.ReadWrite | SQLiteOpenFlags.Create, storeDateTimeAsTicks, key: key)
5962
{
6063
}
6164

@@ -76,12 +79,15 @@ public SQLiteAsyncConnection (string databasePath, bool storeDateTimeAsTicks = t
7679
/// If you use DateTimeOffset properties, it will be always stored as ticks regardingless
7780
/// the storeDateTimeAsTicks parameter.
7881
/// </param>
79-
public SQLiteAsyncConnection (string databasePath, SQLiteOpenFlags openFlags, bool storeDateTimeAsTicks = true)
82+
/// <param name="key">
83+
/// Specifies the encryption key to use on the database. Should be a string or a byte[].
84+
/// </param>
85+
public SQLiteAsyncConnection (string databasePath, SQLiteOpenFlags openFlags, bool storeDateTimeAsTicks = true, object key = null)
8086
{
8187
_openFlags = openFlags;
8288
isFullMutex = _openFlags.HasFlag (SQLiteOpenFlags.FullMutex);
83-
_connectionString = new SQLiteConnectionString (databasePath, storeDateTimeAsTicks);
84-
if(isFullMutex)
89+
_connectionString = new SQLiteConnectionString (databasePath, storeDateTimeAsTicks, key);
90+
if (isFullMutex)
8591
_fullMutexReadConnection = new SQLiteConnectionWithLock (_connectionString, openFlags) { SkipLock = true };
8692
}
8793

@@ -203,39 +209,6 @@ Task<T> WriteAsync<T> (Func<SQLiteConnectionWithLock, T> write)
203209
}, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
204210
}
205211

206-
/// <summary>
207-
/// Sets the key used to encrypt/decrypt the database.
208-
/// This must be the first thing you call before doing anything else with this connection
209-
/// if your database is encrypted.
210-
/// This only has an effect if you are using the SQLCipher nuget package.
211-
/// </summary>
212-
/// <param name="key">Ecryption key plain text that is converted to the real encryption key using PBKDF2 key derivation</param>
213-
public Task SetKeyAsync (string key)
214-
{
215-
if (key == null) throw new ArgumentNullException (nameof (key));
216-
return WriteAsync<object> (conn => {
217-
conn.SetKey (key);
218-
return null;
219-
});
220-
}
221-
222-
/// <summary>
223-
/// Sets the key used to encrypt/decrypt the database.
224-
/// This must be the first thing you call before doing anything else with this connection
225-
/// if your database is encrypted.
226-
/// This only has an effect if you are using the SQLCipher nuget package.
227-
/// </summary>
228-
/// <param name="key">256-bit (32 byte) ecryption key data</param>
229-
public Task SetKeyAsync (byte[] key)
230-
{
231-
if (key == null) throw new ArgumentNullException (nameof (key));
232-
if (key.Length != 32) throw new ArgumentException ("Key must be 32 bytes (256-bit)", nameof (key));
233-
return WriteAsync<object> (conn => {
234-
conn.SetKey (key);
235-
return null;
236-
});
237-
}
238-
239212
/// <summary>
240213
/// Enable or disable extension loading.
241214
/// </summary>
@@ -1411,7 +1384,7 @@ public class SQLiteConnectionWithLock : SQLiteConnection
14111384
/// <param name="connectionString">Connection string containing the DatabasePath.</param>
14121385
/// <param name="openFlags">Open flags.</param>
14131386
public SQLiteConnectionWithLock (SQLiteConnectionString connectionString, SQLiteOpenFlags openFlags)
1414-
: base (connectionString.DatabasePath, openFlags, connectionString.StoreDateTimeAsTicks)
1387+
: base (connectionString.DatabasePath, openFlags, connectionString.StoreDateTimeAsTicks, key: connectionString.Key)
14151388
{
14161389
}
14171390

tests/SQLCipherTest.cs

Lines changed: 24 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -31,20 +31,16 @@ public void SetStringKey ()
3131

3232
var key = "SecretPassword";
3333

34-
using (var db = new TestDb ()) {
34+
using (var db = new TestDb (key: key)) {
3535
path = db.DatabasePath;
3636

37-
db.SetKey (key);
38-
3937
db.CreateTable<TestTable> ();
4038
db.Insert (new TestTable { Value = "Hello" });
4139
}
4240

43-
using (var db = new TestDb (path)) {
41+
using (var db = new TestDb (path, key: key)) {
4442
path = db.DatabasePath;
4543

46-
db.SetKey (key);
47-
4844
var r = db.Table<TestTable> ().First ();
4945

5046
Assert.AreEqual ("Hello", r.Value);
@@ -60,32 +56,46 @@ public void SetBytesKey ()
6056
var key = new byte[32];
6157
rand.NextBytes (key);
6258

63-
using (var db = new TestDb ()) {
59+
using (var db = new TestDb (key: key)) {
6460
path = db.DatabasePath;
6561

66-
db.SetKey (key);
67-
6862
db.CreateTable<TestTable> ();
6963
db.Insert (new TestTable { Value = "Hello" });
7064
}
7165

72-
using (var db = new TestDb (path)) {
66+
using (var db = new TestDb (path, key: key)) {
7367
path = db.DatabasePath;
7468

75-
db.SetKey (key);
76-
7769
var r = db.Table<TestTable> ().First ();
7870

7971
Assert.AreEqual ("Hello", r.Value);
8072
}
8173
}
8274

75+
[Test]
76+
public void SetEmptyStringKey ()
77+
{
78+
using (var db = new TestDb (key: "")) {
79+
}
80+
}
81+
82+
[Test]
83+
public void SetBadTypeKey ()
84+
{
85+
try {
86+
using (var db = new TestDb (key: 42)) {
87+
}
88+
Assert.Fail ("Should have thrown");
89+
}
90+
catch (ArgumentException) {
91+
}
92+
}
93+
8394
[Test]
8495
public void SetBadBytesKey ()
8596
{
8697
try {
87-
using (var db = new TestDb ()) {
88-
db.SetKey (new byte[] { 1, 2, 3, 4 });
98+
using (var db = new TestDb (key: new byte[] { 1, 2, 3, 4 })) {
8999
}
90100
Assert.Fail ("Should have thrown");
91101
}

tests/TestDb.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,12 +53,12 @@ public enum OrderLineStatus {
5353

5454
public class TestDb : SQLiteConnection
5555
{
56-
public TestDb (bool storeDateTimeAsTicks = true) : base (TestPath.GetTempFileName (), storeDateTimeAsTicks)
56+
public TestDb (bool storeDateTimeAsTicks = true, object key = null) : base (TestPath.GetTempFileName (), storeDateTimeAsTicks, key: key)
5757
{
5858
Trace = true;
5959
}
6060

61-
public TestDb (string path, bool storeDateTimeAsTicks = true) : base (path, storeDateTimeAsTicks)
61+
public TestDb (string path, bool storeDateTimeAsTicks = true, object key = null) : base (path, storeDateTimeAsTicks, key: key)
6262
{
6363
Trace = true;
6464
}

0 commit comments

Comments
 (0)