Skip to content

Commit

Permalink
Merge branch 'develop'
Browse files Browse the repository at this point in the history
  • Loading branch information
bpvgoncalves committed Aug 31, 2024
2 parents 51d3b9e + 49f4d7f commit 5717bff
Show file tree
Hide file tree
Showing 17 changed files with 7,791 additions and 5,978 deletions.
4 changes: 2 additions & 2 deletions DESCRIPTION
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Package: RSQLCipher
Title: SQLCipher Interface for R
Version: 0.2.1
Version: 0.3.0
Authors@R: c(
person("Bruno", "Gonçalves", , "[email protected]", role = "aut",
comment = c(ORCID = "0000-0002-0797-7717")),
Expand Down Expand Up @@ -76,7 +76,7 @@ Config/autostyle/strict: false
Config/testthat/edition: 3
Encoding: UTF-8
Roxygen: list(markdown = TRUE)
RoxygenNote: 7.3.1
RoxygenNote: 7.3.2
Collate:
'SQLiteConnection.R'
'SQLKeywords_SQLiteConnection.R'
Expand Down
9 changes: 9 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
# RSQLCipher 0.3.0

## Features
- Added support for different types of keys: password and long hex key (key+salt).

## Other
- Update bundled SQLCipher to version 4.6.1 (SQLite 3.46.1).
- Patch vendor code to avoid compiler errors on void* arithmetic.

# RSQLCipher 0.2.1

## Other
Expand Down
38 changes: 20 additions & 18 deletions R/databaseKeyAdd.R
Original file line number Diff line number Diff line change
@@ -1,16 +1,24 @@
#' databaseKeyAdd
#' Database Key Management - Add Key
#'
#' Creates an encrypted copy of an existing database, using the provided key.
#' *THIS FUNCTION MAY TAKE LONG FOR BIG DATABASE FILES*
#'
#' @param conn Connection to an existing (plain) database to be encrypted.
#' @param key New key to encrypt the database, a character string of size 64, containing 32
#' hex-encoded characters to be used as new key for the database.
#' @param file Optional, path to the new encrypted database. A temporary file will be generated if
#' not provided.
#' @param key A character string containing one of the following:
#' (i) a character string to be used as password. PBKDF2 is applied to generate
#' a key from the entered string, or
#' (ii) a character string of size 64, containing 32 hex encoded characters to
#' be used directly as key for database encryption, or
#' (iii) a character string of size 96, containing 32 hex encoded characters to
#' be used directly as key for database encryption and 16 hex encoded characters
#' to be used as salt.
#' @param file Optional, path to the new encrypted database. A temporary file
#' will be generated if not provided.
#'
#' @usage NULL
#' @returns A named list with True/False and the file name of the encrypted database
#' @returns A named list with True/False and the file name of the encrypted
#' database
#'
#' @export
#' @examples
#' key <- "0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF"
Expand All @@ -28,20 +36,15 @@
#'
databaseKeyAdd <- function(conn, key, file = tempfile()) {

if (!is.hex(key)) {
warning("Cannot set the database key. The 'key' provided has invalid type.")
return(invisible(list(result = FALSE, file = file)))
}

key <- gsub(" ", "", key, fixed = TRUE) # eliminate potential spaces to eval len
if (nchar(key) != 64) {
warning("Cannot set the database key. The 'key' provided has invalid length.")
type <- key_type(key)
if (is.na(type)) {
warning("Cannot set the database key. The 'key' provided is not valid.")
return(invisible(list(result = FALSE, file = file)))
} else if (type == "key") {
key <- paste0("x'", key, "'")
}

tryCatch({

key <- paste0("x'", key, "'")
dbExecute(conn,
"ATTACH DATABASE :f AS encrypted KEY :k;",
params = list(f = file, k = key))
Expand All @@ -56,8 +59,7 @@ databaseKeyAdd <- function(conn, key, file = tempfile()) {

},
condition = function(e) {
warning("Couldn't set key for database: ", conditionMessage(e), "\n",
"Use `key` = NULL to turn off this warning.",
warning("Couldn't set key for database: ", conditionMessage(e),
call. = FALSE)
return(invisible(list(result = FALSE, file = file)))
})
Expand Down
67 changes: 45 additions & 22 deletions R/databaseKeyChange.R
Original file line number Diff line number Diff line change
@@ -1,13 +1,20 @@
#' databaseKeyChange
#' Database Key Management - Change Key
#'
#' Changes the key for an encrypted database.
#' IMPORTANT: Currently it is not possible to change keys from different types,
#' i.e., if the original key is a password the new key MUST be a password and
#' if the original key is an hex key, the new key MUST be a hex key as well.
#'
#' @param conn database Connection
#' @param old_key Old database key, a character string of size 64, containing
#' 32 hex encoded characters currently used as key for the database.
#' encryption.
#' @param new_key New database key, a character string of size 64, containing
#' 32 hex encoded characters to be used as new key for the database.
#' @param old_key The string currently used as key.
#' @param new_key A string to be used as new key, be one of:
#' (i) a character string to be used as password. PBKDF2 is applied to generate
#' a key from the entered string, or
#' (ii) a character string of size 64, containing 32 hex encoded characters to
#' be used directly as key for database encryption, or
#' (iii) a character string of size 96, containing 32 hex encoded characters to
#' be used directly as key for database encryption and 16 hex encoded characters
#' to be used as salt.
#'
#' @usage NULL
#' @returns True if successful or False otherwise#'
Expand All @@ -25,30 +32,46 @@
#'
databaseKeyChange <- function(conn, old_key = NULL, new_key = NULL) {

if (!is.hex(old_key)) {
warning("Cannot change database key. The 'old_key' provided has invalid type.")
old_type <- key_type(old_key)
if (is.na(old_type)) {
warning("Cannot change database key. The 'old_key' provided is not valid.")
return(invisible(FALSE))
}

if (!is.hex(new_key)) {
warning("Cannot change database key. The 'new_key' provided has invalid type.")
new_type <- key_type(new_key)
if (is.na(new_type)) {
warning("Cannot change database key. The 'new_key' provided is not valid.")
return(invisible(FALSE))
}

old_key <- gsub(" ", "", old_key, fixed = TRUE) # eliminate potential spaces to eval len
if (nchar(old_key) != 64) {
warning("Cannot change database key. The 'old_key' provided has invalid length.")
return(invisible(FALSE))
}
# if (old_type != new_type) {
# warning("Cannot change database key.
# The 'old_key' and 'new_key' have different types.")
# return(invisible(FALSE))
# }

new_key <- gsub(" ", "", new_key, fixed = TRUE) # eliminate potential spaces to eval len
if (nchar(new_key) != 64) {
warning("Cannot change database key. The 'new_key' provided has invalid length.")
return(invisible(FALSE))
}
# old_key <- gsub(" ", "", old_key, fixed = TRUE) # eliminate potential spaces to eval len
# if (nchar(old_key) != 64) {
# warning("Cannot change database key. The 'old_key' provided has invalid length.")
# return(invisible(FALSE))
# }
#
# new_key <- gsub(" ", "", new_key, fixed = TRUE) # eliminate potential spaces to eval len
# if (nchar(new_key) != 64) {
# warning("Cannot change database key. The 'new_key' provided has invalid length.")
# return(invisible(FALSE))
# }

dbExecute(conn, sprintf("PRAGMA key = \"x'%s'\";", old_key))
dbExecute(conn, sprintf("PRAGMA rekey = \"x'%s'\";", new_key))
if (old_type == "key") {
dbExecute(conn, sprintf("PRAGMA key = \"x'%s'\";", old_key))
} else {
dbExecute(conn, sprintf("PRAGMA key = '%s';", old_key))
}
if (new_type == "key") {
dbExecute(conn, sprintf("PRAGMA rekey = \"x'%s'\";", new_key))
} else {
dbExecute(conn, sprintf("PRAGMA rekey = '%s';", new_key))
}
invisible(TRUE)
}

Expand Down
6 changes: 3 additions & 3 deletions R/databaseKeyRemove.R
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
#' databaseKeyRemove
#' Database Key Management - Remove Key
#'
#' Creates an unencrypted/plain copy of an existing encrypted database.
#' *THIS FUNCTION MAY TAKE LONG FOR BIG DATABASE FILES*
#'
#' @param conn Connection to an existing encrypted database to be decrypted.
#' @param file Optional, path to the new decrypted database. A temporary file will be generated if
#' not provided.
#' @param file Optional, path to the new decrypted database. A temporary file
#' will be generated if not provided.
#'
#' @usage NULL
#' @returns A named list with True/False and the file name of the plain database
Expand Down
45 changes: 26 additions & 19 deletions R/dbConnect_SQLiteDriver.R
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,15 @@
#' @param extended_types When `TRUE` columns of type `DATE`, `DATETIME` /
#' `TIMESTAMP`, and `TIME` are mapped to corresponding R-classes, c.f. below
#' for details. Defaults to `FALSE`.
#' @param key When different from NULL (no encryption) a character string of
#' size 64, containing 32 hex encoded characters to be used as key for database
#' encryption.
#' @param key When different from `NULL` (no encryption, default), a character
#' string containing one of the following:
#' (i) a character string to be used as password. PBKDF2 is applied to generate
#' a key from the entered string, or
#' (ii) a character string of size 64, containing 32 hex encoded characters to
#' be used directly as key for database encryption, or
#' (iii) a character string of size 96, containing 32 hex encoded characters to
#' be used directly as key for database encryption and 16 hex encoded characters
#' to be used as salt.
#'
#' @return `dbConnect()` returns an object of class [SQLiteConnection-class].
#'
Expand Down Expand Up @@ -124,23 +130,24 @@ dbConnect_SQLiteDriver <- function(drv, dbname = "", ..., loadable.extensions =

## experimental PRAGMAs
if (!is.null(key)) {
if (is.hex(key)) {
key <- gsub(" ", "", key, fixed = TRUE) # eliminate potential spaces to eval len
if (nchar(key) == 64) {
tryCatch(
dbExecute(conn, sprintf("PRAGMA key = \"x'%s'\";", key)),
error = function(e) {
warning("Couldn't set key for database: ", conditionMessage(e), "\n",
"Use `key` = NULL to turn off this warning.",
call. = FALSE
)
}
)
} else {
warning("Cannot use database encryption. The 'key' provided has invalid length.")
}

type <- key_type(key)
if (is.na(type)) {
warning("The 'key' provided is not valid. Continuing with plain database.")
} else {
warning("Cannot use database encryption. The 'key' provided has invalid type.")
tryCatch({
if (type == "key") {
dbExecute(conn, sprintf("PRAGMA key = \"x'%s'\";", key))
} else {
dbExecute(conn, sprintf("PRAGMA key = '%s';", key))
}
},
error = function(e) {
warning("Couldn't set key for database: ", conditionMessage(e), "\n",
"Use `key` = NULL to turn off this warning.",
call. = FALSE)
}
)
}
}

Expand Down
16 changes: 16 additions & 0 deletions R/utils.R
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,19 @@ is.hex <- function(x) {
return((nchar(x) %% 2 == 0) && (!grepl("[^0-9A-Fa-f]", x)))
}
}

key_type <- function(x) {

if (is.null(x)) return(NA)

if (is.na(x)) return(NA)

x_new <- gsub(" ", "", x, fixed = TRUE)
if (is.hex(x) & (nchar(x_new) == 64 | nchar(x_new) == 96)) {
return("key")
} else if (is.character(x)){
return("pass")
} else {
return(NA)
}
}
12 changes: 9 additions & 3 deletions man/SQLCipher.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

19 changes: 13 additions & 6 deletions man/databaseKeyAdd.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

19 changes: 13 additions & 6 deletions man/databaseKeyChange.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions man/databaseKeyRemove.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 5717bff

Please sign in to comment.