Title: | High Level Encryption Wrappers |
---|---|
Description: | Encryption wrappers, using low-level support from 'sodium' and 'openssl'. 'cyphr' tries to smooth over some pain points when using encryption within applications and data analysis by wrapping around differences in function names and arguments in different encryption providing packages. It also provides high-level wrappers for input/output functions for seamlessly adding encryption to existing analyses. |
Authors: | Rich FitzJohn [aut, cre], Jai Ranganathan [ctb] |
Maintainer: | Rich FitzJohn <[email protected]> |
License: | MIT + file LICENSE |
Version: | 1.1.5 |
Built: | 2024-10-28 06:06:40 UTC |
Source: | https://github.com/ropensci/cyphr |
Encryption wrappers, using low-level support from sodium and openssl.
It is strongly recommended that you read both vignettes before
attempting to use cyphr
.
introduction;
in R: vignette("cyphr", package = "cyphr")
data vignette;
in R: vignette("data", package = "cyphr")
Rich FitzJohn ([email protected])
Useful links:
Report bugs at https://github.com/ropensci/cyphr/issues
Encrypted data administration; functions for setting up, adding users, etc.
data_admin_init(path_data, path_user = NULL, quiet = FALSE) data_admin_authorise( path_data = NULL, hash = NULL, path_user = NULL, yes = FALSE, quiet = FALSE ) data_admin_list_requests(path_data = NULL) data_admin_list_keys(path_data = NULL)
data_admin_init(path_data, path_user = NULL, quiet = FALSE) data_admin_authorise( path_data = NULL, hash = NULL, path_user = NULL, yes = FALSE, quiet = FALSE ) data_admin_list_requests(path_data = NULL) data_admin_list_keys(path_data = NULL)
path_data |
Path to the data set. We will store a bunch of things in a hidden directory within this path. By default in most functions we will search down the tree until we find the .cyphr directory |
path_user |
Path to the directory with your ssh key. Usually this can be omitted. |
quiet |
Suppress printing of informative messages. |
hash |
A vector of hashes to add. If provided, each hash can be the binary or string representation of the hash to add. Or omit to add each request. |
yes |
Skip the confirmation prompt? If any request is declined then the function will throw an error on exit. |
data_admin_init
initialises the system; it will create a
data key if it does not exist and authorise you. If it already
exists and you do not have access it will throw an error.
data_admin_authorise
authorises a key by creating a key to
the data that the user can use in conjunction with their personal
key.
data_admin_list_requests
lists current requests.
data_admin_list_keys
lists known keys that can access the
data. Note that this is not secure; keys not listed here
may still be able to access the data (if a key was authorised and
moved elsewhere for example). Conversely, if the user has deleted
or changed their key they will not be able to access the data
despite the key being listed here.
data_request_access()
for requesting access
to the data, and and data_key
for using the data
itself. But for a much more thorough overview, see the vignette
(vignette("data", package = "cyphr")
).
# The workflow here does not really lend itself to an example, # please see the vignette instead. # First we need a set of user ssh keys. In a non example # environment your personal ssh keys will probably work well, but # hopefully they are password protected so cannot be used in # examples. The password = FALSE argument is only for testing, # and should not be used for data that you care about. path_ssh_key <- tempfile() cyphr::ssh_keygen(path_ssh_key, password = FALSE) # Initialise the data directory, using this key path. Ordinarily # the path_user argument would not be needed because we would be # using your user ssh keys: path_data <- tempfile() dir.create(path_data, FALSE, TRUE) cyphr::data_admin_init(path_data, path_user = path_ssh_key) # Now you can get the data key key <- cyphr::data_key(path_data, path_user = path_ssh_key) # And encrypt things with it cyphr::encrypt_string("hello", key) # See the vignette for more details. This is not the best medium # to explore this. # Cleanup unlink(path_ssh_key, recursive = TRUE) unlink(path_data, recursive = TRUE)
# The workflow here does not really lend itself to an example, # please see the vignette instead. # First we need a set of user ssh keys. In a non example # environment your personal ssh keys will probably work well, but # hopefully they are password protected so cannot be used in # examples. The password = FALSE argument is only for testing, # and should not be used for data that you care about. path_ssh_key <- tempfile() cyphr::ssh_keygen(path_ssh_key, password = FALSE) # Initialise the data directory, using this key path. Ordinarily # the path_user argument would not be needed because we would be # using your user ssh keys: path_data <- tempfile() dir.create(path_data, FALSE, TRUE) cyphr::data_admin_init(path_data, path_user = path_ssh_key) # Now you can get the data key key <- cyphr::data_key(path_data, path_user = path_ssh_key) # And encrypt things with it cyphr::encrypt_string("hello", key) # See the vignette for more details. This is not the best medium # to explore this. # Cleanup unlink(path_ssh_key, recursive = TRUE) unlink(path_data, recursive = TRUE)
User commands
data_request_access(path_data = NULL, path_user = NULL, quiet = FALSE) data_key( path_data = NULL, path_user = NULL, test = TRUE, quiet = FALSE, cache = TRUE )
data_request_access(path_data = NULL, path_user = NULL, quiet = FALSE) data_key( path_data = NULL, path_user = NULL, test = TRUE, quiet = FALSE, cache = TRUE )
path_data |
Path to the data. If not given, then we look recursively down below the working directory for a ".cyphr" directory, and use that as the data directory. |
path_user |
Path to the directory with your user key.
Usually this can be omitted. This argument is passed in as both
|
quiet |
Suppress printing of informative messages. |
test |
Test that the encryption is working? (Recommended) |
cache |
Cache the key within the session. This will be
useful if you are using ssh keys that have passwords, as if the
key is found within the cache, then you will not have to
re-enter your password. Using |
# The workflow here does not really lend itself to an example, # please see the vignette. # Suppose that Alice has created a data directory: path_alice <- tempfile() cyphr::ssh_keygen(path_alice, password = FALSE) path_data <- tempfile() dir.create(path_data, FALSE, TRUE) cyphr::data_admin_init(path_data, path_user = path_alice) # If Bob can also write to the data directory (e.g., it is a # shared git repo, on a shared drive, etc), then he can request # access path_bob <- tempfile() cyphr::ssh_keygen(path_bob, password = FALSE) hash <- cyphr::data_request_access(path_data, path_user = path_bob) # Alice can authorise Bob cyphr::data_admin_authorise(path_data, path_user = path_alice, yes = TRUE) # After which Bob can get the data key cyphr::data_key(path_data, path_user = path_bob) # See the vignette for more details. This is not the best medium # to explore this. # Cleanup unlink(path_alice, recursive = TRUE) unlink(path_bob, recursive = TRUE) unlink(path_data, recursive = TRUE)
# The workflow here does not really lend itself to an example, # please see the vignette. # Suppose that Alice has created a data directory: path_alice <- tempfile() cyphr::ssh_keygen(path_alice, password = FALSE) path_data <- tempfile() dir.create(path_data, FALSE, TRUE) cyphr::data_admin_init(path_data, path_user = path_alice) # If Bob can also write to the data directory (e.g., it is a # shared git repo, on a shared drive, etc), then he can request # access path_bob <- tempfile() cyphr::ssh_keygen(path_bob, password = FALSE) hash <- cyphr::data_request_access(path_data, path_user = path_bob) # Alice can authorise Bob cyphr::data_admin_authorise(path_data, path_user = path_alice, yes = TRUE) # After which Bob can get the data key cyphr::data_key(path_data, path_user = path_bob) # See the vignette for more details. This is not the best medium # to explore this. # Cleanup unlink(path_alice, recursive = TRUE) unlink(path_bob, recursive = TRUE) unlink(path_data, recursive = TRUE)
Wrapper functions for encryption. These functions wrap
expressions that produce or consume a file and arrange to encrypt
(for producing functions) or decrypt (for consuming functions).
The forms with a trailing underscore (encrypt_
,
decrypt_
) do not use any non-standard evaluation and may be
more useful for programming.
encrypt(expr, key, file_arg = NULL, envir = parent.frame()) decrypt(expr, key, file_arg = NULL, envir = parent.frame()) encrypt_(expr, key, file_arg = NULL, envir = parent.frame()) decrypt_(expr, key, file_arg = NULL, envir = parent.frame())
encrypt(expr, key, file_arg = NULL, envir = parent.frame()) decrypt(expr, key, file_arg = NULL, envir = parent.frame()) encrypt_(expr, key, file_arg = NULL, envir = parent.frame()) decrypt_(expr, key, file_arg = NULL, envir = parent.frame())
expr |
A single expression representing a function call that would be called for the side effect of creating or reading a file. |
key |
A |
file_arg |
Optional hint indicating which argument to
|
envir |
Environment in which |
These functions will not work for all functions. For example
pdf
/dev.off
will create a file but we can't wrap
those up (yet!). Functions that modify a file (e.g.,
appending) also will not work and may cause data loss.
# To do anything we first need a key: key <- cyphr::key_sodium(sodium::keygen()) # Encrypted write.csv - note how any number of arguments to # write.csv will be passed along path <- tempfile(fileext = ".csv") cyphr::encrypt(write.csv(iris, path, row.names = FALSE), key) # The new file now exists, but you would not be able to read it # with read.csv because it is now binary data. file.exists(path) # Wrap the read.csv call with cyphr::decrypt() dat <- cyphr::decrypt(read.csv(path, stringsAsFactors = FALSE), key) head(dat) file.remove(path) # If you have a function that is not supported you can specify the # filename argument directly. For example, with "write.dcf" the # filename argument is called "file"; we can pass that along path <- tempfile() cyphr::encrypt(write.dcf(list(a = 1), path), key, file_arg = "file") # Similarly for decryption: cyphr::decrypt(read.dcf(path), key, file_arg = "file")
# To do anything we first need a key: key <- cyphr::key_sodium(sodium::keygen()) # Encrypted write.csv - note how any number of arguments to # write.csv will be passed along path <- tempfile(fileext = ".csv") cyphr::encrypt(write.csv(iris, path, row.names = FALSE), key) # The new file now exists, but you would not be able to read it # with read.csv because it is now binary data. file.exists(path) # Wrap the read.csv call with cyphr::decrypt() dat <- cyphr::decrypt(read.csv(path, stringsAsFactors = FALSE), key) head(dat) file.remove(path) # If you have a function that is not supported you can specify the # filename argument directly. For example, with "write.dcf" the # filename argument is called "file"; we can pass that along path <- tempfile() cyphr::encrypt(write.dcf(list(a = 1), path), key, file_arg = "file") # Similarly for decryption: cyphr::decrypt(read.dcf(path), key, file_arg = "file")
Encrypt and decrypt raw data, objects, strings and files. The
core functions here are encrypt_data
and
decrypt_data
which take raw data and decrypt it, writing
either to file or returning a raw vector. The other functions
encrypt and decrypt arbitrary R objects (encrypt_object
,
decrypt_object
), strings (encrypt_string
,
decrypt_string
) and files (encrypt_file
,
decrypt_file
).
encrypt_data(data, key, dest = NULL) encrypt_object(object, key, dest = NULL, rds_version = NULL) encrypt_string(string, key, dest = NULL) encrypt_file(path, key, dest = NULL) decrypt_data(data, key, dest = NULL) decrypt_object(data, key) decrypt_string(data, key) decrypt_file(path, key, dest = NULL)
encrypt_data(data, key, dest = NULL) encrypt_object(object, key, dest = NULL, rds_version = NULL) encrypt_string(string, key, dest = NULL) encrypt_file(path, key, dest = NULL) decrypt_data(data, key, dest = NULL) decrypt_object(data, key) decrypt_string(data, key) decrypt_file(path, key, dest = NULL)
data |
(for |
key |
A |
dest |
The destination filename for the encrypted or
decrypted data, or |
object |
(for |
rds_version |
RDS serialisation version to use (see
serialize. The default in R version 3.3 and below is version
2 - in the R 3.4 series version 3 was introduced and is becoming
the default. Version 3 format serialisation is not understood
by older versions so if you need to exchange data with older R
versions, you will need to use |
string |
(for |
path |
(for |
key <- key_sodium(sodium::keygen()) # Some super secret data we want to encrypt: x <- runif(10) # Convert the data into a raw vector: data <- serialize(x, NULL) data # Encrypt the data; without the key above we will never be able to # decrypt this. data_enc <- encrypt_data(data, key) data_enc # Our random numbers: unserialize(decrypt_data(data_enc, key)) # Same as the never-encrypted version: x # This can be achieved more easily using `encrypt_object`: data_enc <- encrypt_object(x, key) identical(decrypt_object(data_enc, key), x) # Encrypt strings easily: str_enc <- encrypt_string("secret message", key) str_enc decrypt_string(str_enc, key)
key <- key_sodium(sodium::keygen()) # Some super secret data we want to encrypt: x <- runif(10) # Convert the data into a raw vector: data <- serialize(x, NULL) data # Encrypt the data; without the key above we will never be able to # decrypt this. data_enc <- encrypt_data(data, key) data_enc # Our random numbers: unserialize(decrypt_data(data_enc, key)) # Same as the never-encrypted version: x # This can be achieved more easily using `encrypt_object`: data_enc <- encrypt_object(x, key) identical(decrypt_object(data_enc, key), x) # Encrypt strings easily: str_enc <- encrypt_string("secret message", key) str_enc decrypt_string(str_enc, key)
Wrap an openssl symmetric (aes) key. This can be used with the
functions encrypt_data()
and
decrypt_data()
, along with the higher level wrappers
encrypt()
and decrypt()
. With a symmetric
key, everybody uses the same key for encryption and decryption.
key_openssl(key, mode = "cbc")
key_openssl(key, mode = "cbc")
key |
An openssl aes key (i.e., an object of class |
mode |
The encryption mode to use. Options are |
# Create a new key key <- cyphr::key_openssl(openssl::aes_keygen()) key # With this key encrypt a string secret <- cyphr::encrypt_string("my secret string", key) # And decrypt it again: cyphr::decrypt_string(secret, key)
# Create a new key key <- cyphr::key_openssl(openssl::aes_keygen()) key # With this key encrypt a string secret <- cyphr::encrypt_string("my secret string", key) # And decrypt it again: cyphr::decrypt_string(secret, key)
Wrap a sodium symmetric key. This can be used with the functions
encrypt_data()
and decrypt_data()
, along
with the higher level wrappers encrypt()
and
decrypt()
. With a symmetric key, everybody uses the
same key for encryption and decryption.
key_sodium(key)
key_sodium(key)
key |
A sodium key (i.e., generated with |
# Create a new key key <- cyphr::key_sodium(sodium::keygen()) key # With this key encrypt a string secret <- cyphr::encrypt_string("my secret string", key) # And decrypt it again: cyphr::decrypt_string(secret, key)
# Create a new key key <- cyphr::key_sodium(sodium::keygen()) key # With this key encrypt a string secret <- cyphr::encrypt_string("my secret string", key) # And decrypt it again: cyphr::decrypt_string(secret, key)
Wrap a pair of openssl keys. You should pass your private key and the public key of the person that you are communicating with.
keypair_openssl( pub, key, envelope = TRUE, password = NULL, authenticated = TRUE )
keypair_openssl( pub, key, envelope = TRUE, password = NULL, authenticated = TRUE )
pub |
An openssl public key. Usually this will be the path
to the key, in which case it may either the path to a public key
or be the path to a directory containing a file
|
key |
An openssl private key. Usually this will be the path
to the key, in which case it may either the path to a private
key or be the path to a directory containing a file. You may
specify |
envelope |
A logical indicating if "envelope" encryption
functions should be used. If so, then we use
|
password |
A password for the private key. If |
authenticated |
Logical, indicating if the result should be
signed with your public key. If |
keypair_sodium()
for a similar function using
sodium keypairs
# Note this uses password = FALSE for use in examples only, but # this should not be done for any data you actually care about. # Note that the vignette contains much more information than this # short example and should be referred to before using these # functions. # Generate two keypairs, one for Alice, and one for Bob path_alice <- tempfile() path_bob <- tempfile() cyphr::ssh_keygen(path_alice, password = FALSE) cyphr::ssh_keygen(path_bob, password = FALSE) # Alice wants to send Bob a message so she creates a key pair with # her private key and bob's public key (she does not have bob's # private key). pair_alice <- cyphr::keypair_openssl(pub = path_bob, key = path_alice) # She can then encrypt a secret message: secret <- cyphr::encrypt_string("hi bob", pair_alice) secret # Bob wants to read the message so he creates a key pair using # Alice's public key and his private key: pair_bob <- cyphr::keypair_openssl(pub = path_alice, key = path_bob) cyphr::decrypt_string(secret, pair_bob) # Clean up unlink(path_alice, recursive = TRUE) unlink(path_bob, recursive = TRUE)
# Note this uses password = FALSE for use in examples only, but # this should not be done for any data you actually care about. # Note that the vignette contains much more information than this # short example and should be referred to before using these # functions. # Generate two keypairs, one for Alice, and one for Bob path_alice <- tempfile() path_bob <- tempfile() cyphr::ssh_keygen(path_alice, password = FALSE) cyphr::ssh_keygen(path_bob, password = FALSE) # Alice wants to send Bob a message so she creates a key pair with # her private key and bob's public key (she does not have bob's # private key). pair_alice <- cyphr::keypair_openssl(pub = path_bob, key = path_alice) # She can then encrypt a secret message: secret <- cyphr::encrypt_string("hi bob", pair_alice) secret # Bob wants to read the message so he creates a key pair using # Alice's public key and his private key: pair_bob <- cyphr::keypair_openssl(pub = path_alice, key = path_bob) cyphr::decrypt_string(secret, pair_bob) # Clean up unlink(path_alice, recursive = TRUE) unlink(path_bob, recursive = TRUE)
Wrap a pair of sodium keys for asymmetric encryption. You should pass your private key and the public key of the person that you are communicating with.
keypair_sodium(pub, key, authenticated = TRUE)
keypair_sodium(pub, key, authenticated = TRUE)
pub |
A sodium public key. This is either a raw vector of
length 32 or a path to file containing the contents of the key
(written by |
key |
A sodium private key. This is either a raw vector of
length 32 or a path to file containing the contents of the key
(written by |
authenticated |
Logical, indicating if authenticated
encryption (via |
NOTE: the order here (pub, key) is very important; if the wrong order is used you cannot decrypt things. Unfortunately because sodium keys are just byte sequences there is nothing to distinguish the public and private keys so this is a pretty easy mistake to make.
keypair_openssl()
for a similar function using
openssl keypairs
# Generate two keypairs, one for Alice, and one for Bob key_alice <- sodium::keygen() pub_alice <- sodium::pubkey(key_alice) key_bob <- sodium::keygen() pub_bob <- sodium::pubkey(key_bob) # Alice wants to send Bob a message so she creates a key pair with # her private key and bob's public key (she does not have bob's # private key). pair_alice <- cyphr::keypair_sodium(pub = pub_bob, key = key_alice) # She can then encrypt a secret message: secret <- cyphr::encrypt_string("hi bob", pair_alice) secret # Bob wants to read the message so he creates a key pair using # Alice's public key and his private key: pair_bob <- cyphr::keypair_sodium(pub = pub_alice, key = key_bob) cyphr::decrypt_string(secret, pair_bob)
# Generate two keypairs, one for Alice, and one for Bob key_alice <- sodium::keygen() pub_alice <- sodium::pubkey(key_alice) key_bob <- sodium::keygen() pub_bob <- sodium::pubkey(key_bob) # Alice wants to send Bob a message so she creates a key pair with # her private key and bob's public key (she does not have bob's # private key). pair_alice <- cyphr::keypair_sodium(pub = pub_bob, key = key_alice) # She can then encrypt a secret message: secret <- cyphr::encrypt_string("hi bob", pair_alice) secret # Bob wants to read the message so he creates a key pair using # Alice's public key and his private key: pair_bob <- cyphr::keypair_sodium(pub = pub_alice, key = key_bob) cyphr::decrypt_string(secret, pair_bob)
Add information about argument rewriting so that they can be used with encrypt and decrypt.
rewrite_register(package, name, arg, fn = NULL)
rewrite_register(package, name, arg, fn = NULL)
package |
The name of the package with the function to support (as a scalar character). If your function has no package (e.g., a function you are working on outside of a package, use "" as the name). |
name |
The name of the function to support. |
arg |
The name of the argument in the target function that
refers to the file that should be encrypted or decrypted. This
is the value you would pass through to |
fn |
Optional (and should be rare) argument used to work
around functions that pass all their arguments through to a
second function as dots. This is how |
If your package uses cyphr, it might be useful to add this as
an .onLoad()
hook.
# The saveRDS function is already supported. But if we wanted to # support it we could look at the arguments for the function: args(saveRDS) # The 'file' argument is the one that refers to the filename, so # we'd write: cyphr::rewrite_register("base", "saveRDS", "file") # It's non-API but you can see what is supported in the package by # looking at ls(cyphr:::db)
# The saveRDS function is already supported. But if we wanted to # support it we could look at the arguments for the function: args(saveRDS) # The 'file' argument is the one that refers to the filename, so # we'd write: cyphr::rewrite_register("base", "saveRDS", "file") # It's non-API but you can see what is supported in the package by # looking at ls(cyphr:::db)
Refresh the session key, invalidating all keys created by
key_openssl()
, keypair_openssl()
,
key_sodium()
and keypair_sodium()
.
session_key_refresh()
session_key_refresh()
Running this function will invalidate all keys loaded with the above functions. It should not be needed very often.
# Be careful - if you run this then all keys loaded from file will # no longer work until reloaded if (FALSE) { cyphr::session_key_refresh() }
# Be careful - if you run this then all keys loaded from file will # no longer work until reloaded if (FALSE) { cyphr::session_key_refresh() }
Create openssl key pairs in the manner of ssh-keygen
(1).
In general this should not be used (generate keys yourself with
ssh-keygen
at the command line. However this is useful for
testing and demonstration so I have included it to make that
easier. Once a keypair has been generated it can be used with
keypair_openssl()
.
ssh_keygen(path = tempfile(), password = TRUE, use_shell = FALSE)
ssh_keygen(path = tempfile(), password = TRUE, use_shell = FALSE)
path |
A directory in which to create a keypair. If the path does not exist it will be created. |
password |
The password for the key. The default will prompt
interactively (but without echoing the password). Other valid
options are |
use_shell |
Try to use |
The path
, invisibly. This is useful in the case
where path
is tempfile()
.
# Generate a new key in a temporary directory: path <- cyphr::ssh_keygen(password = FALSE) dir(path) # will contain id_rsa and id_rsa.pub # This key can now be used via keypair_openssl: key <- cyphr::keypair_openssl(path, path) secret <- cyphr::encrypt_string("hello", key) cyphr::decrypt_string(secret, key) # Cleanup unlink(path, recursive = TRUE)
# Generate a new key in a temporary directory: path <- cyphr::ssh_keygen(password = FALSE) dir(path) # will contain id_rsa and id_rsa.pub # This key can now be used via keypair_openssl: key <- cyphr::keypair_openssl(path, path) secret <- cyphr::encrypt_string("hello", key) cyphr::decrypt_string(secret, key) # Cleanup unlink(path, recursive = TRUE)