Skip to content

Add encrypted SQLite offline store support via SQLite3 Multiple Ciphers#1

Merged
fileman merged 2 commits into
mainfrom
claude/exciting-ptolemy-rfznaz
Jun 20, 2026
Merged

Add encrypted SQLite offline store support via SQLite3 Multiple Ciphers#1
fileman merged 2 commits into
mainfrom
claude/exciting-ptolemy-rfznaz

Conversation

@fileman

@fileman fileman commented Jun 20, 2026

Copy link
Copy Markdown
Owner

Summary

This PR adds support for encrypted SQLite offline stores to the Datasync Toolkit client, addressing the gap created by SQLitePCLRaw 3.0's removal of free encryption bundles. The implementation uses SQLite3 Multiple Ciphers (SQLite3MC), a free, MIT-licensed encryption engine that is compatible with SQLCipher databases.

Key Changes

  • New Package: CommunityToolkit.Datasync.Client.EncryptedSqlite

    • Provides UseEncryptedSqlite() extension methods for configuring encrypted SQLite stores
    • Includes EncryptedSqliteFactory for creating and managing encrypted connections
    • Supports cipher configuration via EncryptedSqliteOptions (e.g., SQLCipher compatibility)
    • Implements RekeyEncryptedSqlite() for changing encryption keys via PRAGMA rekey
  • Core Implementation:

    • EncryptedSqliteDbContextOptionsExtensions: Two overloads of UseEncryptedSqlite() - one accepting a connection string with password, another accepting a pre-configured SqliteConnection
    • EncryptedSqliteFactory: Helper for creating and opening encrypted connections with optional cipher configuration
    • EncryptedSqliteOptions: Configuration class for non-default cipher schemes and legacy compatibility levels
    • SqliteBatteries: Thread-safe initialization of the SQLite3MC provider via SQLitePCL.Batteries_V2.Init()
  • Base Package Change:

    • Modified CommunityToolkit.Datasync.Client.csproj to reference Microsoft.EntityFrameworkCore.Sqlite.Core (bundle-less) instead of the full Microsoft.EntityFrameworkCore.Sqlite package, allowing consumers to choose their SQLitePCLRaw bundle
  • Comprehensive Tests:

    • EncryptedSqlite_Tests: Validates encryption round-trips, file encryption verification, wrong password rejection, and key rotation
    • EncryptedSqlite_OfflineDbContext_Tests: Verifies integration with OfflineDbContext
    • EncryptedSqliteTestBase: Reusable test infrastructure with helpers for seeding, reading, and asserting encryption
  • Documentation: New encryption.md guide covering setup, key management, and SQLCipher compatibility

Notable Implementation Details

  • Safe PRAGMA handling: Encryption keys are safely quoted using SQLite's quote() function rather than string concatenation to prevent injection
  • Lazy initialization: SQLite3MC provider is initialized exactly once per process using double-checked locking
  • Bundle isolation: The test project intentionally avoids TestCommon to prevent SQLitePCLRaw bundle conflicts
  • Connection ownership: Clear semantics for connection lifecycle - UseEncryptedSqlite(string, password) manages the connection; UseEncryptedSqlite(SqliteConnection) requires caller disposal

https://claude.ai/code/session_01NXcj1dFqpm6qVWLAmc857D

claude added 2 commits June 20, 2026 09:10
SQLitePCLRaw 3.0 (pulled in by EF Core 10) removed the free encryption
bundles (bundle_e_sqlcipher / bundle_e_sqlite3mc). The maintainer's
recommended replacement - the SQLite Encryption Extension (SEE) - requires
a paid license. This adds a free, no-paid-license way to encrypt the
offline client store using the open-source, MIT-licensed SQLite3 Multiple
Ciphers engine.

- New package CommunityToolkit.Datasync.Client.EncryptedSqlite providing
  UseEncryptedSqlite() EF Core extensions, EncryptedSqliteOptions (SQLCipher
  format compatibility), EncryptedSqliteFactory, and PRAGMA rekey support.
- Switch the base Client to Microsoft.EntityFrameworkCore.Sqlite.Core so the
  consumer chooses a single SQLitePCLRaw bundle (plain bundle_e_sqlite3 or
  the new encrypted package). A project must reference exactly one bundle.
- Self-contained test project, solution + signed-package-list wiring, and a
  new docs page (in-depth/client/encryption.md) plus nav/cross-links.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NXcj1dFqpm6qVWLAmc857D
Two EncryptedSqlite tests failed in CI (the build compiled cleanly):

- Rekey: the SQLite3 Multiple Ciphers build opens databases in WAL journal
  mode, where PRAGMA rekey is rejected ("Rekeying is not supported in WAL
  journal mode"). RekeyEncryptedSqlite now switches to a rollback journal
  (PRAGMA journal_mode = DELETE) before re-keying.
- SQLCipher-compat reopen: EncryptedSqliteOptions.Apply escaped the key via
  "SELECT quote(...)", which executes a query before PRAGMA key is set, so
  reading an already-encrypted database failed with "file is not a database".
  The key/cipher PRAGMAs are now applied as the first statements, with the
  literal escaped via a new internal SqliteLiteral.Quote helper (no pre-key
  query).

Also tidied warnings in the new files: enable nullable in the test project
(CS8632), implement the full IDisposable pattern in the test base (CA1063),
and justify-suppress CA2100 where escaped values are embedded in PRAGMA text.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NXcj1dFqpm6qVWLAmc857D
@fileman fileman merged commit b8bd37c into main Jun 20, 2026
3 checks passed
@fileman fileman deleted the claude/exciting-ptolemy-rfznaz branch June 20, 2026 10:49
@fileman fileman restored the claude/exciting-ptolemy-rfznaz branch June 20, 2026 10:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants