Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support different encryption libraries #392

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
146 changes: 96 additions & 50 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ endif (SQLITE_ENABLE_ASSERT_HANDLER)

option(SQLITE_HAS_CODEC "Enable database encryption API. Not available in the public release of SQLite." OFF)
if (SQLITE_HAS_CODEC)
# Enable database encryption API. Requires implementations of sqlite3_key & sqlite3_key_v2.
# Enable database encryption API. Requires implementations of sqlite3_key & sqlite3_rekey.
# Eg. SQLCipher (libsqlcipher-dev) is an SQLite extension that provides 256 bit AES encryption of database files.
target_compile_definitions(SQLiteCpp PUBLIC SQLITE_HAS_CODEC)
endif (SQLITE_HAS_CODEC)
Expand Down Expand Up @@ -271,63 +271,109 @@ if (SQLITECPP_USE_GCOV)
set_target_properties(SQLiteCpp PROPERTIES COMPILE_FLAGS "-fkeep-inline-functions -fkeep-static-functions")
endif ()

## Build provided copy of SQLite3 C library ##
# Try to handle the non-standard SQLite3 implementations first

option(SQLITECPP_USE_SQLCIPHER "Use SQLCipher as SQLite3 library with encyption support." OFF)
if (SQLITECPP_USE_SQLCIPHER AND TARGET SQLite::SQLite3)
message(FATAL_ERROR "SQLite::SQLite3 target has already been defined, cannot use SQLCipher.")
elseif (SQLITECPP_USE_SQLCIPHER)
# Make PkgConfig optional since Windows doesn't usually have it installed.
find_package(PkgConfig QUIET)
if(PKG_CONFIG_FOUND)
# IMPORTED_TARGET was added in 3.6.3
if(CMAKE_VERSION VERSION_LESS 3.6.3)
pkg_check_modules(SQLCipher REQUIRED sqlcipher)
# Since we can't use IMPORTED_TARGET on this older Cmake version, manually link libs & includes
message(STATUS "Link to SQLCipher library")
add_library(SQLite::SQLite3 ALIAS ${SQLCipher_LIBRARIES})
target_link_libraries(SQLiteCpp PUBLIC ${SQLCipher_LIBRARIES})
target_include_directories(SQLiteCpp PUBLIC ${SQLCipher_INCLUDE_DIRS})
else()
pkg_check_modules(SQLCipher REQUIRED IMPORTED_TARGET sqlcipher)
message(STATUS "Link to SQLCipher library")
add_library(SQLite::SQLite3 ALIAS PkgConfig::SQLCipher)
target_link_libraries(SQLiteCpp PUBLIC PkgConfig::SQLCipher)
endif()
else()
# Since we aren't using pkgconf here, find it manually
find_library(SQLCipher_LIBRARY "sqlcipher")
find_path(SQLCipher_INCLUDE_DIR "sqlcipher/sqlite3.h"
PATH_SUFFIXES
"include"
"includes"
)
# Hides it from the GUI
mark_as_advanced(SQLCipher_LIBRARY SQLCipher_INCLUDE_DIR)
if(NOT SQLCipher_INCLUDE_DIR)
message(FATAL_ERROR "${PROJECT_NAME} requires the \"<sqlcipher/sqlite3.h>\" header to use the codec functionality but it wasn't found.")
elseif(NOT SQLCipher_LIBRARY)
message(FATAL_ERROR "${PROJECT_NAME} requires the SQLCipher library to use the codec functionality but it wasn't found.")
endif()
message(STATUS "Link to SQLCipher library")
add_library(SQLite::SQLite3 ALIAS ${SQLCipher_LIBRARY})
target_link_libraries(SQLiteCpp PUBLIC ${SQLCipher_LIBRARY})
target_include_directories(SQLiteCpp PUBLIC "${SQLCipher_INCLUDE_DIR}/sqlcipher")
endif()
# Try to extract the SQLCipher version
file(STRINGS "${SQLCipher_INCLUDE_DIR}/sqlcipher/crypto.h" _ver_line
REGEX "^#define CIPHER_VERSION_NUMBER *\"[0-9]+\\.[0-9]+\\.[0-9]+\""
LIMIT_COUNT 1)
string(REGEX MATCH "[0-9]+\\.[0-9]+\\.[0-9]+"
SQLCipher_VERSION "${_ver_line}")
unset(_ver_line)
mark_as_advanced(SQLCipher_VERSION)
# Try to extract the SQLite3 version
file(STRINGS "${SQLCipher_INCLUDE_DIR}/sqlcipher/sqlite3.h" _ver_line
REGEX "^#define SQLITE_VERSION *\"[0-9]+\\.[0-9]+\\.[0-9]+\""
LIMIT_COUNT 1)
string(REGEX MATCH "[0-9]+\\.[0-9]+\\.[0-9]+"
SQLite3_VERSION "${_ver_line}")
unset(_ver_line)
mark_as_advanced(SQLite3_VERSION)
target_compile_definitions(SQLiteCpp PRIVATE SQLITECPP_USE_SQLCIPHER)
target_compile_definitions(SQLiteCpp PUBLIC SQLITE_HAS_CODEC)
endif ()

option(SQLITECPP_USE_SQLITE3MULTIPLECIPHERS "Use SQLite3MultipleCiphers as SQLite3 library with encyption support." OFF)
if (SQLITECPP_USE_SQLITE3MULTIPLECIPHERS AND TARGET SQLite::SQLite3)
message(FATAL_ERROR "SQLite::SQLite3 target has already been defined, cannot use SQLite3MultipleCiphers.")
elseif (SQLITECPP_USE_SQLITE3MULTIPLECIPHERS)
find_package (SQLite3MultipleCiphers REQUIRED)
message(STATUS "Link to SQLite3MultipleCiphers library")
target_link_libraries(SQLiteCpp PUBLIC SQLite::SQLite3MultipleCiphers)
target_compile_definitions(SQLiteCpp PRIVATE SQLITECPP_USE_SQLITE3MULTIPLECIPHERS)
target_compile_definitions(SQLiteCpp PUBLIC SQLITE_HAS_CODEC)
endif ()

option(SQLITECPP_INTERNAL_SQLITE "Add the internal SQLite3 source to the project." ON)
if (SQLITECPP_INTERNAL_SQLITE)
if (SQLITECPP_INTERNAL_SQLITE AND TARGET SQLite::SQLite3)
message(FATAL_ERROR "SQLite::SQLite3 target has already been defined, cannot use internal SQLite3.")
elseif (SQLITECPP_INTERNAL_SQLITE)
# Build the provided copy of the SQLite3 C library
message(STATUS "Compile sqlite3 from source in subdirectory")
option(SQLITE_ENABLE_RTREE "Enable RTree extension when building internal sqlite3 library." OFF)
option(SQLITE_ENABLE_DBSTAT_VTAB "Enable DBSTAT read-only eponymous virtual table extension when building internal sqlite3 library." OFF)
# build the SQLite3 C library (for ease of use/compatibility) versus Linux sqlite3-dev package
add_subdirectory(sqlite3)
target_link_libraries(SQLiteCpp PUBLIC SQLite::SQLite3)
else (SQLITECPP_INTERNAL_SQLITE)
# When using the SQLite codec, we need to link against the sqlcipher lib & include <sqlcipher/sqlite3.h>
# So this gets the lib & header, and links/includes everything
if(SQLITE_HAS_CODEC)
# Make PkgConfig optional since Windows doesn't usually have it installed.
find_package(PkgConfig QUIET)
if(PKG_CONFIG_FOUND)
# IMPORTED_TARGET was added in 3.6.3
if(CMAKE_VERSION VERSION_LESS 3.6.3)
pkg_check_modules(sqlcipher REQUIRED sqlcipher)
# Only used in Database.cpp so PRIVATE to hide from end-user
# Since we can't use IMPORTED_TARGET on this older Cmake version, manually link libs & includes
target_link_libraries(SQLiteCpp PRIVATE ${sqlcipher_LIBRARIES})
target_include_directories(SQLiteCpp PRIVATE ${sqlcipher_INCLUDE_DIRS})
else()
pkg_check_modules(sqlcipher REQUIRED IMPORTED_TARGET sqlcipher)
# Only used in Database.cpp so PRIVATE to hide from end-user
target_link_libraries(SQLiteCpp PRIVATE PkgConfig::sqlcipher)
endif()
else()
# Since we aren't using pkgconf here, find it manually
find_library(sqlcipher_LIBRARY "sqlcipher")
find_path(sqlcipher_INCLUDE_DIR "sqlcipher/sqlite3.h"
PATH_SUFFIXES
"include"
"includes"
)
# Hides it from the GUI
mark_as_advanced(sqlcipher_LIBRARY sqlcipher_INCLUDE_DIR)
if(NOT sqlcipher_INCLUDE_DIR)
message(FATAL_ERROR "${PROJECT_NAME} requires the \"<sqlcipher/sqlite3.h>\" header to use the codec functionality but it wasn't found.")
elseif(NOT sqlcipher_LIBRARY)
message(FATAL_ERROR "${PROJECT_NAME} requires the sqlcipher library to use the codec functionality but it wasn't found.")
endif()
# Only used in Database.cpp so PRIVATE to hide from end-user
target_include_directories(SQLiteCpp PRIVATE "${sqlcipher_INCLUDE_DIR}/sqlcipher")
target_link_libraries(SQLiteCpp PRIVATE ${sqlcipher_LIBRARY})
endif()
else()
find_package (SQLite3 REQUIRED)
message(STATUS "Link to sqlite3 system library")
target_link_libraries(SQLiteCpp PUBLIC SQLite::SQLite3)
if(SQLite3_VERSION VERSION_LESS "3.19")
set_target_properties(SQLiteCpp PROPERTIES COMPILE_FLAGS "-DSQLITECPP_HAS_MEM_STRUCT")
endif()
endif()
endif (SQLITECPP_INTERNAL_SQLITE)
target_compile_definitions(SQLiteCpp PRIVATE SQLITECPP_INTERNAL_SQLITE)
#target_compile_definitions(SQLiteCpp PUBLIC SQLITE_HAS_CODEC)
endif ()

# Use the system library as fallback solution
if (NOT TARGET SQLite::SQLite3)
find_package (SQLite3 REQUIRED)
message(STATUS "Link to sqlite3 system library")
target_link_libraries(SQLiteCpp PUBLIC SQLite::SQLite3)
else (NOT TARGET SQLite::SQLite3)
message(STATUS "SQLite::SQLite3 target has already been defined, using it.")
target_link_libraries(SQLiteCpp PUBLIC SQLite::SQLite3)
endif (NOT TARGET SQLite::SQLite3)

# Check the version of the found SQLite3 implementation
if(SQLite3_VERSION AND SQLite3_VERSION VERSION_LESS "3.19")
set_target_properties(SQLiteCpp PROPERTIES COMPILE_FLAGS "-DSQLITECPP_HAS_MEM_STRUCT")
endif()

## disable the optional support for std::filesystem (C++17)
option(SQLITECPP_DISABLE_STD_FILESYSTEM "Disable the use of std::filesystem in SQLiteCpp." OFF)
Expand Down
187 changes: 187 additions & 0 deletions cmake/FindSQLite3MultipleCiphers.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
# Based on the FindSQLite3.cmake from the CMake project file to add support for
# SQLite3MultipleCiphers (https://github.com/utelle/SQLite3MultipleCiphers)
# to the SQLiteCpp (https://github.com/SRombauts/SQLiteCpp) project.
#
# Created in 2022 Jorrit Wronski (https://github.com/jowr)
#
# Distributed under the MIT License (MIT) (See accompanying file LICENSE.txt
# or copy at http://opensource.org/licenses/MIT)

#[=======================================================================[.rst:
FindSQLite3MultipleCiphers
--------------------------

Find the SQLite3MultipleCiphers library that implements SQLite3 with encryption.

IMPORTED targets
^^^^^^^^^^^^^^^^

This module defines the following `IMPORTED` targets:

``SQLite::SQLite3MultipleCiphers``
``SQLite::SQLite3``

Result variables
^^^^^^^^^^^^^^^^

This module will set the following variables if found:

``SQLite3MultipleCiphers_INCLUDE_DIRS``
where to find sqlite3mc.h, etc.
``SQLite3MultipleCiphers_LIBRARIES``
the libraries to link against to use SQLite3MultipleCiphers.
``SQLite3MultipleCiphers_VERSION``
SQLite3 version of the SQLite3MultipleCiphers library found
``SQLite3MultipleCiphers_FOUND``
TRUE if found

It also sets alias variables for SQLite3 for campatibility reasons:

``SQLite3_INCLUDE_DIRS``
where to find sqlite3.h, etc.
``SQLite3_LIBRARIES``
the libraries to link against to use SQLite3.
``SQLite3_VERSION``
version of the SQLite3 library found
``SQLite3_FOUND``
TRUE if found
``SQLite::SQLite3``
the ordinary SQLite3 target for linking

#]=======================================================================]

find_path(SQLite3MultipleCiphers_INCLUDE_DIR NAMES sqlite3mc.h
HINTS
${SQLITE3MULTIPLECIPHERS_DIR}
ENV SQLITE3MULTIPLECIPHERS_DIR
PATH_SUFFIXES src
)
mark_as_advanced(SQLite3MultipleCiphers_INCLUDE_DIR)

#if("amd64" IN_LIST CMAKE_CPU_ARCHITECTURES) # I am targeting 64-bit x86.
if(CMAKE_SIZEOF_VOID_P MATCHES "8")
set(SQLite3MultipleCiphers_LIB_SUFFIX "_x64")
else()
set(SQLite3MultipleCiphers_LIB_SUFFIX "")
endif()

find_library(SQLite3MultipleCiphers_LIBRARY_RELEASE sqlite3mc${SQLite3MultipleCiphers_LIB_SUFFIX}
HINTS
${SQLITE3MULTIPLECIPHERS_DIR}
ENV SQLITE3MULTIPLECIPHERS_DIR
PATH_SUFFIXES
bin/vc13/lib/release
bin/vc14/lib/release
bin/vc15/lib/release
bin/vc16/lib/release
bin/vc17/lib/release
bin/gcc/lib/release
bin/clang/lib/release
bin/lib/release
REQUIRED
)
mark_as_advanced(SQLite3MultipleCiphers_LIBRARY_RELEASE)

find_library(SQLite3MultipleCiphers_LIBRARY_DEBUG sqlite3mc${SQLite3MultipleCiphers_LIB_SUFFIX}
HINTS
${SQLITE3MULTIPLECIPHERS_DIR}
ENV SQLITE3MULTIPLECIPHERS_DIR
PATH_SUFFIXES
bin/vc13/lib/debug
bin/vc14/lib/debug
bin/vc15/lib/debug
bin/vc16/lib/debug
bin/vc17/lib/debug
bin/gcc/lib/debug
bin/clang/lib/debug
bin/lib/debug
)
mark_as_advanced(SQLite3MultipleCiphers_LIBRARY_DEBUG)

find_library(SQLite3MultipleCiphers_LIBRARY_MINSIZEREL sqlite3mc${SQLite3MultipleCiphers_LIB_SUFFIX}
HINTS
${SQLITE3MULTIPLECIPHERS_DIR}
ENV SQLITE3MULTIPLECIPHERS_DIR
PATH_SUFFIXES
bin/vc13/lib/minsizerel
bin/vc14/lib/minsizerel
bin/vc15/lib/minsizerel
bin/vc16/lib/minsizerel
bin/vc17/lib/minsizerel
bin/gcc/lib/minsizerel
bin/clang/lib/minsizerel
bin/lib/minsizerel
)
mark_as_advanced(SQLite3MultipleCiphers_LIBRARY_MINSIZEREL)

find_library(SQLite3MultipleCiphers_LIBRARY_RELWITHDEBINFO sqlite3mc${SQLite3MultipleCiphers_LIB_SUFFIX}
HINTS
${SQLITE3MULTIPLECIPHERS_DIR}
ENV SQLITE3MULTIPLECIPHERS_DIR
PATH_SUFFIXES
bin/vc13/lib/relwithdebinfo
bin/vc14/lib/relwithdebinfo
bin/vc15/lib/relwithdebinfo
bin/vc16/lib/relwithdebinfo
bin/vc17/lib/relwithdebinfo
bin/gcc/lib/relwithdebinfo
bin/clang/lib/relwithdebinfo
bin/lib/relwithdebinfo
)
mark_as_advanced(SQLite3MultipleCiphers_LIBRARY_RELWITHDEBINFO)

# Extract version information from the header file
if(SQLite3MultipleCiphers_INCLUDE_DIR)
file(STRINGS ${SQLite3MultipleCiphers_INCLUDE_DIR}/sqlite3mc_version.h _ver_line
REGEX "^#define SQLITE3MC_VERSION_STRING *\"[0-9]+\\.[0-9]+\\.[0-9]+\""
LIMIT_COUNT 1)
string(REGEX MATCH "[0-9]+\\.[0-9]+\\.[0-9]+"
SQLite3MultipleCiphers_VERSION "${_ver_line}")
unset(_ver_line)
mark_as_advanced(SQLite3MultipleCiphers_VERSION)
endif()

#include(${CMAKE_CURRENT_LIST_DIR}/FindPackageHandleStandardArgs.cmake)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(SQLite3MultipleCiphers
REQUIRED_VARS SQLite3MultipleCiphers_INCLUDE_DIR SQLite3MultipleCiphers_LIBRARY_RELEASE SQLite3MultipleCiphers_LIBRARY_DEBUG
VERSION_VAR SQLite3MultipleCiphers_VERSION)

# Create the imported target
if(SQLite3MultipleCiphers_FOUND)
set(SQLite3MultipleCiphers_INCLUDE_DIRS ${SQLite3MultipleCiphers_INCLUDE_DIR})
set(SQLite3MultipleCiphers_LIBRARIES
debug ${SQLite3MultipleCiphers_LIBRARY_DEBUG}
optimized ${SQLite3MultipleCiphers_LIBRARY_RELEASE})
if(NOT TARGET SQLite::SQLite3MultipleCiphers)
add_library(SQLite::SQLite3MultipleCiphers UNKNOWN IMPORTED)
set_target_properties(SQLite::SQLite3MultipleCiphers PROPERTIES
IMPORTED_LOCATION_DEBUG "${SQLite3MultipleCiphers_LIBRARY_DEBUG}"
IMPORTED_LOCATION_RELEASE "${SQLite3MultipleCiphers_LIBRARY_RELEASE}"
IMPORTED_LOCATION_MINSIZEREL "${SQLite3MultipleCiphers_LIBRARY_MINSIZEREL}"
IMPORTED_LOCATION_RELWITHDEBINFO "${SQLite3MultipleCiphers_LIBRARY_RELWITHDEBINFO}"
INTERFACE_INCLUDE_DIRECTORIES "${SQLite3MultipleCiphers_INCLUDE_DIR}")
endif()
endif()

# define the alias variables
if(NOT SQLite3_INCLUDE_DIRS AND NOT SQLite3_LIBRARIES AND NOT SQLite3_VERSION AND NOT SQLite3_FOUND AND NOT TARGET SQLite::SQLite3)
set(SQLite3_INCLUDE_DIRS ${SQLite3MultipleCiphers_INCLUDE_DIRS})
mark_as_advanced(SQLite3_INCLUDE_DIRS)
set(SQLite3_LIBRARIES ${SQLite3MultipleCiphers_LIBRARIES})
mark_as_advanced(SQLite3_LIBRARIES)
#set(SQLite3_VERSION ${SQLite3MultipleCiphers_VERSION})
#mark_as_advanced(SQLite3_VERSION)
file(STRINGS ${SQLite3MultipleCiphers_INCLUDE_DIR}/sqlite3.h _ver_line
REGEX "^#define SQLITE_VERSION *\"[0-9]+\\.[0-9]+\\.[0-9]+\""
LIMIT_COUNT 1)
string(REGEX MATCH "[0-9]+\\.[0-9]+\\.[0-9]+"
SQLite3_VERSION "${_ver_line}")
unset(_ver_line)
mark_as_advanced(SQLite3_VERSION)
set(SQLite3_FOUND ${SQLite3MultipleCiphers_FOUND})
mark_as_advanced(SQLite3_FOUND)
add_library(SQLite::SQLite3 ALIAS SQLite::SQLite3MultipleCiphers)
else()
message(FATAL_ERROR "SQLite3 has already been defined, please check you options. The current settings are very likely to produce conflicts.")
endif()
17 changes: 17 additions & 0 deletions include/SQLiteCpp/Database.h
Original file line number Diff line number Diff line change
Expand Up @@ -522,6 +522,23 @@ class SQLITECPP_API Database
*/
void loadExtension(const char* apExtensionName, const char* apEntryPointName);

/**
* @brief Set the cipher for the current sqlite database instance.
*
* This function is needed because some encryption providers require the user
* to select the cipher that they wish to use. This should happen immediately
* after opening an encrypted database:
* Open encrypted database
* -> call db.cipher("aes256cbc")
* -> call db.key("secret")
* -> database ready
*
* @param[in] aCipher Cipher to decode/encode the database
*
* @throw SQLite::Exception in case of error
*/
void cipher(const std::string& aCipher) const;

/**
* @brief Set the key for the current sqlite database instance.
*
Expand Down
Loading