apsw-sqlite3mc 3.48.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. apsw-sqlite3mc-3.48.0.0/LICENSE +28 -0
  2. apsw-sqlite3mc-3.48.0.0/MANIFEST.in +38 -0
  3. apsw-sqlite3mc-3.48.0.0/PKG-INFO +249 -0
  4. apsw-sqlite3mc-3.48.0.0/README.rst +222 -0
  5. apsw-sqlite3mc-3.48.0.0/apsw/__init__.pyi +4749 -0
  6. apsw-sqlite3mc-3.48.0.0/apsw/__main__.py +9 -0
  7. apsw-sqlite3mc-3.48.0.0/apsw/bestpractice.py +129 -0
  8. apsw-sqlite3mc-3.48.0.0/apsw/ext.py +2533 -0
  9. apsw-sqlite3mc-3.48.0.0/apsw/fts5.py +3216 -0
  10. apsw-sqlite3mc-3.48.0.0/apsw/fts5aux.py +290 -0
  11. apsw-sqlite3mc-3.48.0.0/apsw/fts5query.py +1075 -0
  12. apsw-sqlite3mc-3.48.0.0/apsw/fts_test_strings +87 -0
  13. apsw-sqlite3mc-3.48.0.0/apsw/ftstests.py +2993 -0
  14. apsw-sqlite3mc-3.48.0.0/apsw/mcall.py +154 -0
  15. apsw-sqlite3mc-3.48.0.0/apsw/mctests.py +323 -0
  16. apsw-sqlite3mc-3.48.0.0/apsw/py.typed +0 -0
  17. apsw-sqlite3mc-3.48.0.0/apsw/shell.py +3654 -0
  18. apsw-sqlite3mc-3.48.0.0/apsw/speedtest.py +623 -0
  19. apsw-sqlite3mc-3.48.0.0/apsw/tests.py +11455 -0
  20. apsw-sqlite3mc-3.48.0.0/apsw/trace.py +401 -0
  21. apsw-sqlite3mc-3.48.0.0/apsw/unicode.py +1763 -0
  22. apsw-sqlite3mc-3.48.0.0/apsw_sqlite3mc.egg-info/PKG-INFO +249 -0
  23. apsw-sqlite3mc-3.48.0.0/apsw_sqlite3mc.egg-info/SOURCES.txt +64 -0
  24. apsw-sqlite3mc-3.48.0.0/apsw_sqlite3mc.egg-info/dependency_links.txt +1 -0
  25. apsw-sqlite3mc-3.48.0.0/apsw_sqlite3mc.egg-info/entry_points.txt +2 -0
  26. apsw-sqlite3mc-3.48.0.0/apsw_sqlite3mc.egg-info/top_level.txt +1 -0
  27. apsw-sqlite3mc-3.48.0.0/checksums +37 -0
  28. apsw-sqlite3mc-3.48.0.0/pyproject.toml +16 -0
  29. apsw-sqlite3mc-3.48.0.0/setup.apsw +9 -0
  30. apsw-sqlite3mc-3.48.0.0/setup.cfg +4 -0
  31. apsw-sqlite3mc-3.48.0.0/setup.py +991 -0
  32. apsw-sqlite3mc-3.48.0.0/sqlite3/configure/Makefile.in +1051 -0
  33. apsw-sqlite3mc-3.48.0.0/sqlite3/configure/README +11 -0
  34. apsw-sqlite3mc-3.48.0.0/sqlite3/configure/compile +348 -0
  35. apsw-sqlite3mc-3.48.0.0/sqlite3/configure/config.guess +1754 -0
  36. apsw-sqlite3mc-3.48.0.0/sqlite3/configure/config.sub +1890 -0
  37. apsw-sqlite3mc-3.48.0.0/sqlite3/configure/configure +17030 -0
  38. apsw-sqlite3mc-3.48.0.0/sqlite3/configure/install-sh +541 -0
  39. apsw-sqlite3mc-3.48.0.0/sqlite3/configure/ltmain.sh +11436 -0
  40. apsw-sqlite3mc-3.48.0.0/sqlite3/configure/missing +215 -0
  41. apsw-sqlite3mc-3.48.0.0/sqlite3/configure/sqlite3.c +3 -0
  42. apsw-sqlite3mc-3.48.0.0/sqlite3/configure/sqlite3.pc.in +13 -0
  43. apsw-sqlite3mc-3.48.0.0/sqlite3/sqlite3.c +358632 -0
  44. apsw-sqlite3mc-3.48.0.0/src/_unicodedb.c +154121 -0
  45. apsw-sqlite3mc-3.48.0.0/src/apsw.c +2087 -0
  46. apsw-sqlite3mc-3.48.0.0/src/apsw.docstrings +4136 -0
  47. apsw-sqlite3mc-3.48.0.0/src/apswversion.h +1 -0
  48. apsw-sqlite3mc-3.48.0.0/src/argparse.c +496 -0
  49. apsw-sqlite3mc-3.48.0.0/src/backup.c +395 -0
  50. apsw-sqlite3mc-3.48.0.0/src/blob.c +703 -0
  51. apsw-sqlite3mc-3.48.0.0/src/connection.c +5894 -0
  52. apsw-sqlite3mc-3.48.0.0/src/constants.c +1219 -0
  53. apsw-sqlite3mc-3.48.0.0/src/cursor.c +1854 -0
  54. apsw-sqlite3mc-3.48.0.0/src/exceptions.c +338 -0
  55. apsw-sqlite3mc-3.48.0.0/src/faultinject.c +162 -0
  56. apsw-sqlite3mc-3.48.0.0/src/faultinject.h +2699 -0
  57. apsw-sqlite3mc-3.48.0.0/src/fts.c +1537 -0
  58. apsw-sqlite3mc-3.48.0.0/src/pyutil.c +310 -0
  59. apsw-sqlite3mc-3.48.0.0/src/statementcache.c +487 -0
  60. apsw-sqlite3mc-3.48.0.0/src/stringconstants.c +182 -0
  61. apsw-sqlite3mc-3.48.0.0/src/testextension.c +50 -0
  62. apsw-sqlite3mc-3.48.0.0/src/traceback.c +77 -0
  63. apsw-sqlite3mc-3.48.0.0/src/unicode.c +2700 -0
  64. apsw-sqlite3mc-3.48.0.0/src/util.c +464 -0
  65. apsw-sqlite3mc-3.48.0.0/src/vfs.c +3290 -0
  66. apsw-sqlite3mc-3.48.0.0/src/vtable.c +2845 -0
@@ -0,0 +1,28 @@
1
+ Copyright (c) 2004-2025 Roger Binns <rogerb@rogerbinns.com>
2
+
3
+ All code and documentation is provided under this license:
4
+
5
+ This software is provided 'as-is', without any express or implied
6
+ warranty. In no event will the authors be held liable for any damages
7
+ arising from the use of this software.
8
+
9
+ Permission is granted to anyone to use this software for any purpose,
10
+ including commercial applications, and to alter it and redistribute it
11
+ freely, subject to the following restrictions:
12
+
13
+ 1. The origin of this software must not be misrepresented; you must not
14
+ claim that you wrote the original software. If you use this software
15
+ in a product, an acknowledgment in the product documentation would be
16
+ appreciated but is not required.
17
+
18
+ 2. Altered source versions must be plainly marked as such, and must not be
19
+ misrepresented as being the original software.
20
+
21
+ 3. This notice may not be removed or altered from any source
22
+ distribution.
23
+
24
+ Alternatively you may strike the license above and use it under any
25
+ OSI approved open source license such as those listed at
26
+ https://opensource.org/licenses/alphabetical
27
+
28
+ SPDX-License-Identifier: any-OSI
@@ -0,0 +1,38 @@
1
+ # The C source
2
+ include src/*.c
3
+ include src/faultinject.h
4
+ include src/apswversion.h
5
+ include src/apsw.docstrings
6
+ include sqlite3/sqlite3.c
7
+
8
+ # other files
9
+ include apsw/__init__.pyi
10
+ include apsw/py.typed
11
+ include apsw/shell.py
12
+ include apsw/speedtest.py
13
+ include apsw/trace.py
14
+ include apsw/tests.py
15
+ include apsw/mctests.py
16
+ include apsw/ext.py
17
+
18
+ # data
19
+ include apsw/fts_test_strings
20
+
21
+ # top level
22
+ include LICENSE
23
+ include checksums
24
+ include setup.py
25
+
26
+ # Long description comes from this
27
+ include README.rst
28
+
29
+ include setup.apsw
30
+
31
+ # configure
32
+ include sqlite3/configure/*
33
+ # remove generated detritus
34
+ exclude sqlite3/configure/config.log
35
+ exclude sqlite3/configure/config.status
36
+ exclude sqlite3/configure/libtool
37
+ exclude sqlite3/configure/Makefile
38
+ exclude sqlite3/configure/sqlite3.pc
@@ -0,0 +1,249 @@
1
+ Metadata-Version: 2.1
2
+ Name: apsw-sqlite3mc
3
+ Version: 3.48.0.0
4
+ Summary: SQLite3 Multiple Ciphers combined with Another Python SQLite Wrapper
5
+ Home-page: https://github.com/utelle/apsw-sqlite3mc
6
+ Author: Ulrich Telle
7
+ Author-email: github@telle-online.de
8
+ License: OSI Approved
9
+ Project-URL: Documentation (APSW), https://rogerbinns.github.io/apsw/
10
+ Project-URL: Documentation (MultipleCiphers), https://utelle.github.io/SQLite3MultipleCiphers/
11
+ Project-URL: Documentation (SQLite), https://sqlite.org/lang.html
12
+ Project-URL: Support/Discussions, https://github.com/utelle/apsw-sqlite3mc?tab=readme-ov-file#supportdiscussions
13
+ Project-URL: Issue Tracker, https://github.com/utelle/apsw-sqlite3mc/issues
14
+ Project-URL: Code, https://github.com/utelle/apsw-sqlite3mc
15
+ Keywords: database,sqlite,encryption
16
+ Platform: any
17
+ Classifier: Development Status :: 5 - Production/Stable
18
+ Classifier: Intended Audience :: Developers
19
+ Classifier: License :: OSI Approved
20
+ Classifier: Programming Language :: C
21
+ Classifier: Programming Language :: Python :: 3
22
+ Classifier: Topic :: Database :: Front-Ends
23
+ Classifier: Topic :: Security :: Cryptography
24
+ Requires-Python: >=3.9
25
+ Description-Content-Type: text/x-rst
26
+ License-File: LICENSE
27
+
28
+ .. image:: https://raw.githubusercontent.com/utelle/apsw-sqlite3mc/sqlite3mc/apsw-sqlite3mc-logo.jpg
29
+ :width: 512 px
30
+ :alt: APSW SQLite Multiple Cipher logo - links to documentation
31
+ :target: https://utelle.github.io/SQLite3MultipleCiphers/
32
+
33
+ .. contents:: Contents
34
+
35
+ About
36
+ -----
37
+
38
+ This project packages 3 things together
39
+
40
+ `APSW <https://rogerbinns.github.io/apsw/>`__
41
+
42
+ Another Python SQLite wrapper, providing complete access to SQLite3
43
+ from Python.
44
+
45
+ `SQLite 3 <https://www.sqlite.org/>`__
46
+
47
+ Small, fast, self-contained, high-reliability, full-featured, SQL
48
+ database engine. SQLite is configured with `secure delete
49
+ <https://www.sqlite.org/pragma.html#pragma_secure_delete>`__ turned
50
+ on, and to use `memory for temporary storage
51
+ <https://www.sqlite.org/tempfiles.html#the_sqlite_temp_store_compile_time_parameter_and_pragma>`__.
52
+
53
+ `SQLite3 Multiple Ciphers <https://utelle.github.io/SQLite3MultipleCiphers/>`__
54
+
55
+ Extends SQLite 3 to allow reading and writing encrypted databases.
56
+
57
+ The distribution is entirely self contained, and does not use or alter
58
+ any existing SQLite you may already have on your system.
59
+
60
+ Installation
61
+ ------------
62
+
63
+ Available from `PyPi <https://pypi.org/project/apsw-sqlite3mc/>`__.
64
+ Binaries are included for most platforms, and pip will build from
65
+ source for the rest.::
66
+
67
+ pip install apsw-sqlite3mc
68
+
69
+ Usage
70
+ -----
71
+
72
+ Use as you would regular `APSW
73
+ <https://rogerbinns.github.io/apsw/>`__. You can check the version of
74
+ SQLite3 Multiple Ciphers with ``apsw.mc_version``.
75
+
76
+ For encrypted databases you need to use the relevant `pragmas
77
+ <https://utelle.github.io/SQLite3MultipleCiphers/docs/configuration/config_sql_pragmas/>`__
78
+ to set a passphrase based key, or a binary bytes based key::
79
+
80
+ connection.pragma("key", "my secret passphrase")
81
+ connection.pragma("hexkey", b"\xfe\x23\x9e\x77".hex())
82
+
83
+ Setting the key on a new database is the only change needed to your code.
84
+
85
+ .. code-block:: pycon
86
+
87
+ >>> import apsw
88
+ >>> print(apsw.mc_version)
89
+ SQLite3 Multiple Ciphers 2.0.2
90
+ >>> con = apsw.Connection("database.sqlite3")
91
+ >>> con.pragma("key", "my secret passphrase")
92
+ ok
93
+
94
+ **Note**: The ``ok`` means the pragma was understood. It does not mean
95
+ the key is correct or has been applied to an empty database. See the
96
+ next section on best practice to check and apply the key.
97
+
98
+ **Note**: ``key`` only sets the key for following reads and writes. If
99
+ the database already has content, and you want to encrypt it then use
100
+ ``rekey`` which will modify the database to apply the supplied key.
101
+
102
+ Alternately you can use `URI parameters
103
+ <https://utelle.github.io/SQLite3MultipleCiphers/docs/configuration/config_uri/>`__.
104
+ You need to correctly encode the filename and parameters, and tell
105
+ SQLite that you are using a URI name:
106
+
107
+ .. code-block:: python
108
+
109
+ import urllib.parse
110
+ import apsw
111
+
112
+ uri_filename = urllib.parse.quote("my db filename.sqlite3")
113
+ uri_parameters = urllib.parse.urlencode(
114
+ {
115
+ "cipher": "aes256cbc",
116
+ "kdf_iter": 8192,
117
+ "key": "it's a secret",
118
+ }
119
+ )
120
+ con = apsw.Connection(
121
+ f"file:{uri_filename}?{uri_parameters}",
122
+ flags=apsw.SQLITE_OPEN_URI
123
+ | apsw.SQLITE_OPEN_CREATE
124
+ | apsw.SQLITE_OPEN_READWRITE,
125
+ )
126
+
127
+ Best practice
128
+ -------------
129
+
130
+ SQLite has various quirks in how it operates. For example database
131
+ files are not populated until the first write. SQLite3MultipleCiphers
132
+ can't check keys are correct until the first access, and the database
133
+ is populated. You shouldn't set or change keys while in a
134
+ transaction. In order to ensure files are populated, and the keys and
135
+ cipher configuration provided are correct, use the following method with
136
+ example usage shown at the end.
137
+
138
+ .. code-block:: python
139
+
140
+ import apsw
141
+
142
+ def apply_encryption(db, **kwargs):
143
+ """You must include an argument for keying, and optional cipher configurations"""
144
+
145
+ if db.in_transaction:
146
+ raise Exception("Won't update encryption while in a transaction")
147
+
148
+ # the order of pragmas matters
149
+ def pragma_order(item):
150
+ # pragmas are case insensitive
151
+ pragma = item[0].lower()
152
+ # cipher must be first
153
+ if pragma == "cipher":
154
+ return 1
155
+ # old default settings reset configuration next
156
+ if pragma == "legacy":
157
+ return 2
158
+ # then anything with legacy in the name
159
+ if "legacy" in pragma:
160
+ return 3
161
+ # all except keys
162
+ if pragma not in {"key", "hexkey", "rekey", "hexrekey"}:
163
+ return 3
164
+ # keys are last
165
+ return 100
166
+
167
+ # check only ome key present
168
+ if 1 != sum(1 if pragma_order(item) == 100 else 0 for item in kwargs.items()):
169
+ raise ValueError("Exactly one key must be provided")
170
+
171
+ for pragma, value in sorted(kwargs.items(), key=pragma_order):
172
+ # if the pragma was understood and in range we get the value
173
+ # back, while key related ones return 'ok'
174
+ expected = "ok" if pragma_order((pragma, value)) == 100 else str(value)
175
+ if db.pragma(pragma, value) != expected:
176
+ raise ValueError(f"Failed to configure {pragma=}")
177
+
178
+ # Try to read from the database. If the database is encrypted and
179
+ # the cipher/key information is wrong you will get NotADBError
180
+ # because the file looks like random noise
181
+ db.pragma("user_version")
182
+
183
+ try:
184
+ # try to set the user_version to the value it already has
185
+ # which has a side effect of populating an empty database
186
+ with db:
187
+ # done inside a transaction to avoid race conditions
188
+ db.pragma("user_version", db.pragma("user_version"))
189
+ except apsw.ReadOnlyError:
190
+ # can't make changes - that is ok
191
+ pass
192
+
193
+
194
+ con = apsw.Connection("database.sqlite3")
195
+
196
+ apply_encryption(con, key="my secret key")
197
+
198
+ # you can also do more sophisticated operations. Here we change the cipher,
199
+ # kdf rounds, and the key
200
+ apply_encryption(con, rekey="new key", cipher="ascon128", kdf_iter=1000)
201
+
202
+
203
+ Verification
204
+ ------------
205
+
206
+ You can verify your database is encrypted with a hex viewer. Regular database files
207
+ start with `SQLite format 3` while encrypted database files are random.
208
+
209
+ .. code-block:: console
210
+
211
+ $ hexdump -C database.sqlite3 | head
212
+ 00000000 e1 3e f0 7c 5e 66 4c 20 19 85 9d de 04 d9 e8 e7 |.>.|^fL ........|
213
+ 00000010 10 00 01 01 20 40 20 20 29 2e cb 95 ef 4e 4e 67 |.... @ )....NNg|
214
+ 00000020 22 a1 5a 8f 18 1a fa a1 cf b3 a8 ba b1 80 07 b5 |".Z.............|
215
+ 00000030 2f 68 4d 8a 13 26 fd 6a 0c 99 5a a4 2c a7 f3 a7 |/hM..&.j..Z.,...|
216
+ 00000040 d9 ae ef 24 dd 1c d1 9c cc 91 4b e8 58 00 96 62 |...$......K.X..b|
217
+ 00000050 b2 aa 51 bf 57 8e 9a a9 d7 6d b2 75 58 84 f6 7d |..Q.W....m.uX..}|
218
+ 00000060 c9 fd a9 57 88 05 ca 60 7f db d1 73 40 ad 98 59 |...W...`...s@..Y|
219
+ 00000070 c2 a0 4c 76 f5 88 31 d3 d7 6f 9e ef f6 c1 c4 88 |..Lv..1..o......|
220
+ 00000080 92 ed 8a 3e 00 ce 35 ef 4b 0d 38 33 9a 61 88 8a |...>..5.K.83.a..|
221
+ 00000090 34 37 72 70 4b 33 f3 1d a2 4b 86 5f c5 59 02 c6 |47rpK3...K._.Y..|
222
+
223
+ $ hexdump -C regular.db | head
224
+ 00000000 53 51 4c 69 74 65 20 66 6f 72 6d 61 74 20 33 00 |SQLite format 3.|
225
+ 00000010 10 00 02 02 00 40 20 20 00 00 00 95 00 09 22 e6 |.....@ ......".|
226
+ 00000020 00 08 eb 8f 00 00 ff 8c 00 00 03 d5 00 00 00 04 |................|
227
+ 00000030 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 |................|
228
+ 00000040 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
229
+ 00000050 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 95 |................|
230
+ 00000060 00 2e 7a 70 0d 09 30 00 09 08 c9 00 0f a9 0e d5 |..zp..0.........|
231
+ 00000070 0e 70 0d f7 0d 8c 08 c9 0c 67 0b 2f 09 71 08 db |.p.......g./.q..|
232
+ 00000080 08 db 08 db 03 ae 03 55 03 55 03 55 03 55 03 55 |.......U.U.U.U.U|
233
+ 00000090 03 55 03 55 03 55 03 55 03 55 03 55 03 55 03 55 |.U.U.U.U.U.U.U.U|
234
+
235
+ Support/Discussions
236
+ -------------------
237
+
238
+ For SQLite questions, support, and issues, use the `SQLite
239
+ Forum <https://sqlite.org/forum/forum>`__.`
240
+
241
+ For APSW questions, support, and issues, see `your
242
+ choices <https://rogerbinns.github.io/apsw/about.html#mailing-lists-contacts>`__.
243
+
244
+ For SQLite3MultipleCiphers questions, support, and issues see `the
245
+ project page <https://github.com/utelle/SQLite3MultipleCiphers>`__.
246
+
247
+ For APSW together with SQLite3MultipleCiphers questions, support, and
248
+ issues see `the project page
249
+ <https://github.com/utelle/apsw-sqlite3mc>`__.
@@ -0,0 +1,222 @@
1
+ .. image:: https://raw.githubusercontent.com/utelle/apsw-sqlite3mc/sqlite3mc/apsw-sqlite3mc-logo.jpg
2
+ :width: 512 px
3
+ :alt: APSW SQLite Multiple Cipher logo - links to documentation
4
+ :target: https://utelle.github.io/SQLite3MultipleCiphers/
5
+
6
+ .. contents:: Contents
7
+
8
+ About
9
+ -----
10
+
11
+ This project packages 3 things together
12
+
13
+ `APSW <https://rogerbinns.github.io/apsw/>`__
14
+
15
+ Another Python SQLite wrapper, providing complete access to SQLite3
16
+ from Python.
17
+
18
+ `SQLite 3 <https://www.sqlite.org/>`__
19
+
20
+ Small, fast, self-contained, high-reliability, full-featured, SQL
21
+ database engine. SQLite is configured with `secure delete
22
+ <https://www.sqlite.org/pragma.html#pragma_secure_delete>`__ turned
23
+ on, and to use `memory for temporary storage
24
+ <https://www.sqlite.org/tempfiles.html#the_sqlite_temp_store_compile_time_parameter_and_pragma>`__.
25
+
26
+ `SQLite3 Multiple Ciphers <https://utelle.github.io/SQLite3MultipleCiphers/>`__
27
+
28
+ Extends SQLite 3 to allow reading and writing encrypted databases.
29
+
30
+ The distribution is entirely self contained, and does not use or alter
31
+ any existing SQLite you may already have on your system.
32
+
33
+ Installation
34
+ ------------
35
+
36
+ Available from `PyPi <https://pypi.org/project/apsw-sqlite3mc/>`__.
37
+ Binaries are included for most platforms, and pip will build from
38
+ source for the rest.::
39
+
40
+ pip install apsw-sqlite3mc
41
+
42
+ Usage
43
+ -----
44
+
45
+ Use as you would regular `APSW
46
+ <https://rogerbinns.github.io/apsw/>`__. You can check the version of
47
+ SQLite3 Multiple Ciphers with ``apsw.mc_version``.
48
+
49
+ For encrypted databases you need to use the relevant `pragmas
50
+ <https://utelle.github.io/SQLite3MultipleCiphers/docs/configuration/config_sql_pragmas/>`__
51
+ to set a passphrase based key, or a binary bytes based key::
52
+
53
+ connection.pragma("key", "my secret passphrase")
54
+ connection.pragma("hexkey", b"\xfe\x23\x9e\x77".hex())
55
+
56
+ Setting the key on a new database is the only change needed to your code.
57
+
58
+ .. code-block:: pycon
59
+
60
+ >>> import apsw
61
+ >>> print(apsw.mc_version)
62
+ SQLite3 Multiple Ciphers 2.0.2
63
+ >>> con = apsw.Connection("database.sqlite3")
64
+ >>> con.pragma("key", "my secret passphrase")
65
+ ok
66
+
67
+ **Note**: The ``ok`` means the pragma was understood. It does not mean
68
+ the key is correct or has been applied to an empty database. See the
69
+ next section on best practice to check and apply the key.
70
+
71
+ **Note**: ``key`` only sets the key for following reads and writes. If
72
+ the database already has content, and you want to encrypt it then use
73
+ ``rekey`` which will modify the database to apply the supplied key.
74
+
75
+ Alternately you can use `URI parameters
76
+ <https://utelle.github.io/SQLite3MultipleCiphers/docs/configuration/config_uri/>`__.
77
+ You need to correctly encode the filename and parameters, and tell
78
+ SQLite that you are using a URI name:
79
+
80
+ .. code-block:: python
81
+
82
+ import urllib.parse
83
+ import apsw
84
+
85
+ uri_filename = urllib.parse.quote("my db filename.sqlite3")
86
+ uri_parameters = urllib.parse.urlencode(
87
+ {
88
+ "cipher": "aes256cbc",
89
+ "kdf_iter": 8192,
90
+ "key": "it's a secret",
91
+ }
92
+ )
93
+ con = apsw.Connection(
94
+ f"file:{uri_filename}?{uri_parameters}",
95
+ flags=apsw.SQLITE_OPEN_URI
96
+ | apsw.SQLITE_OPEN_CREATE
97
+ | apsw.SQLITE_OPEN_READWRITE,
98
+ )
99
+
100
+ Best practice
101
+ -------------
102
+
103
+ SQLite has various quirks in how it operates. For example database
104
+ files are not populated until the first write. SQLite3MultipleCiphers
105
+ can't check keys are correct until the first access, and the database
106
+ is populated. You shouldn't set or change keys while in a
107
+ transaction. In order to ensure files are populated, and the keys and
108
+ cipher configuration provided are correct, use the following method with
109
+ example usage shown at the end.
110
+
111
+ .. code-block:: python
112
+
113
+ import apsw
114
+
115
+ def apply_encryption(db, **kwargs):
116
+ """You must include an argument for keying, and optional cipher configurations"""
117
+
118
+ if db.in_transaction:
119
+ raise Exception("Won't update encryption while in a transaction")
120
+
121
+ # the order of pragmas matters
122
+ def pragma_order(item):
123
+ # pragmas are case insensitive
124
+ pragma = item[0].lower()
125
+ # cipher must be first
126
+ if pragma == "cipher":
127
+ return 1
128
+ # old default settings reset configuration next
129
+ if pragma == "legacy":
130
+ return 2
131
+ # then anything with legacy in the name
132
+ if "legacy" in pragma:
133
+ return 3
134
+ # all except keys
135
+ if pragma not in {"key", "hexkey", "rekey", "hexrekey"}:
136
+ return 3
137
+ # keys are last
138
+ return 100
139
+
140
+ # check only ome key present
141
+ if 1 != sum(1 if pragma_order(item) == 100 else 0 for item in kwargs.items()):
142
+ raise ValueError("Exactly one key must be provided")
143
+
144
+ for pragma, value in sorted(kwargs.items(), key=pragma_order):
145
+ # if the pragma was understood and in range we get the value
146
+ # back, while key related ones return 'ok'
147
+ expected = "ok" if pragma_order((pragma, value)) == 100 else str(value)
148
+ if db.pragma(pragma, value) != expected:
149
+ raise ValueError(f"Failed to configure {pragma=}")
150
+
151
+ # Try to read from the database. If the database is encrypted and
152
+ # the cipher/key information is wrong you will get NotADBError
153
+ # because the file looks like random noise
154
+ db.pragma("user_version")
155
+
156
+ try:
157
+ # try to set the user_version to the value it already has
158
+ # which has a side effect of populating an empty database
159
+ with db:
160
+ # done inside a transaction to avoid race conditions
161
+ db.pragma("user_version", db.pragma("user_version"))
162
+ except apsw.ReadOnlyError:
163
+ # can't make changes - that is ok
164
+ pass
165
+
166
+
167
+ con = apsw.Connection("database.sqlite3")
168
+
169
+ apply_encryption(con, key="my secret key")
170
+
171
+ # you can also do more sophisticated operations. Here we change the cipher,
172
+ # kdf rounds, and the key
173
+ apply_encryption(con, rekey="new key", cipher="ascon128", kdf_iter=1000)
174
+
175
+
176
+ Verification
177
+ ------------
178
+
179
+ You can verify your database is encrypted with a hex viewer. Regular database files
180
+ start with `SQLite format 3` while encrypted database files are random.
181
+
182
+ .. code-block:: console
183
+
184
+ $ hexdump -C database.sqlite3 | head
185
+ 00000000 e1 3e f0 7c 5e 66 4c 20 19 85 9d de 04 d9 e8 e7 |.>.|^fL ........|
186
+ 00000010 10 00 01 01 20 40 20 20 29 2e cb 95 ef 4e 4e 67 |.... @ )....NNg|
187
+ 00000020 22 a1 5a 8f 18 1a fa a1 cf b3 a8 ba b1 80 07 b5 |".Z.............|
188
+ 00000030 2f 68 4d 8a 13 26 fd 6a 0c 99 5a a4 2c a7 f3 a7 |/hM..&.j..Z.,...|
189
+ 00000040 d9 ae ef 24 dd 1c d1 9c cc 91 4b e8 58 00 96 62 |...$......K.X..b|
190
+ 00000050 b2 aa 51 bf 57 8e 9a a9 d7 6d b2 75 58 84 f6 7d |..Q.W....m.uX..}|
191
+ 00000060 c9 fd a9 57 88 05 ca 60 7f db d1 73 40 ad 98 59 |...W...`...s@..Y|
192
+ 00000070 c2 a0 4c 76 f5 88 31 d3 d7 6f 9e ef f6 c1 c4 88 |..Lv..1..o......|
193
+ 00000080 92 ed 8a 3e 00 ce 35 ef 4b 0d 38 33 9a 61 88 8a |...>..5.K.83.a..|
194
+ 00000090 34 37 72 70 4b 33 f3 1d a2 4b 86 5f c5 59 02 c6 |47rpK3...K._.Y..|
195
+
196
+ $ hexdump -C regular.db | head
197
+ 00000000 53 51 4c 69 74 65 20 66 6f 72 6d 61 74 20 33 00 |SQLite format 3.|
198
+ 00000010 10 00 02 02 00 40 20 20 00 00 00 95 00 09 22 e6 |.....@ ......".|
199
+ 00000020 00 08 eb 8f 00 00 ff 8c 00 00 03 d5 00 00 00 04 |................|
200
+ 00000030 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 |................|
201
+ 00000040 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
202
+ 00000050 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 95 |................|
203
+ 00000060 00 2e 7a 70 0d 09 30 00 09 08 c9 00 0f a9 0e d5 |..zp..0.........|
204
+ 00000070 0e 70 0d f7 0d 8c 08 c9 0c 67 0b 2f 09 71 08 db |.p.......g./.q..|
205
+ 00000080 08 db 08 db 03 ae 03 55 03 55 03 55 03 55 03 55 |.......U.U.U.U.U|
206
+ 00000090 03 55 03 55 03 55 03 55 03 55 03 55 03 55 03 55 |.U.U.U.U.U.U.U.U|
207
+
208
+ Support/Discussions
209
+ -------------------
210
+
211
+ For SQLite questions, support, and issues, use the `SQLite
212
+ Forum <https://sqlite.org/forum/forum>`__.`
213
+
214
+ For APSW questions, support, and issues, see `your
215
+ choices <https://rogerbinns.github.io/apsw/about.html#mailing-lists-contacts>`__.
216
+
217
+ For SQLite3MultipleCiphers questions, support, and issues see `the
218
+ project page <https://github.com/utelle/SQLite3MultipleCiphers>`__.
219
+
220
+ For APSW together with SQLite3MultipleCiphers questions, support, and
221
+ issues see `the project page
222
+ <https://github.com/utelle/apsw-sqlite3mc>`__.