Skip to content

Commit

Permalink
Documentation cleanups
Browse files Browse the repository at this point in the history
For #14, #13
  • Loading branch information
richfitz committed Jan 29, 2018
1 parent d1ac0b4 commit 3a20372
Show file tree
Hide file tree
Showing 4 changed files with 155 additions and 20 deletions.
8 changes: 4 additions & 4 deletions README.Rmd
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ knitr::opts_chunk$set(

**WARNING: until this package reaches version 1, the format used internally to move data around may change without warning or regard for backward compatibility - this will make it very difficult to recover your data! Once we reach version 1.0.0, we will be careful to support backward compatiblity**

Encryption wrappers, using low-level support from [`sodium`](https://github.com/jeroenooms/sodium) and [`openssl`](https://github.com/jeroenooms/openssl). This package is designed to be easy to use, rather than the most secure thing (you're using R, remember).
Encryption wrappers, using low-level support from [`sodium`](https://github.com/jeroenooms/sodium) and [`openssl`](https://github.com/jeroenooms/openssl). This package is designed to be easy to use, rather than the most secure thing (you're using R, remember - for examples of what `cyphr` can't protect against see [`jammr`](https://github.com/Ironholds/jammr), [`rpwnd`](https://github.com/hrbrmstr/rpwnd) and [`evil.R`](https://github.com/romainfrancois/evil.R).)

It provides high level functions to:
`cyphr` provides high level functions to:

* Encrypt and decrypt
* **strings**: `encrypt_string` / `decrypt_string`
Expand Down Expand Up @@ -115,7 +115,7 @@ file.remove("myfile", "myfile.encrypted", "myfile.clear")

## Wrappers around R's file functions

Encrypting files like the above risks leaving a cleartext version around. If you want to wrap the output of something like `write.csv` or `saveRDS` you really have no choice but to write out the file first, encrypt it, and delete the clear version. Making sure that this happens even if a step fails is error prone and takes a surprising number of repetitive lines of code.
Encrypting files like the above risks leaving a cleartext (i.e., unencrypted) version around. If you want to wrap the output of something like `write.csv` or `saveRDS` you really have no choice but to write out the file first, encrypt it, and delete the clear version. Making sure that this happens even if a step fails is error prone and takes a surprising number of repetitive lines of code.

Alternatively, to encrypt the output of a file producing command, just wrap it in `cyphr::encrypt`

Expand Down Expand Up @@ -190,7 +190,7 @@ The package contains support for a group of people are working on a sensitive da

A proper connection could be nice but there are at least three issues stopping this:

1. `sodium` does not support streaming encryption/decrption. It might be possible (bindings to node and swift have it). In general this would be great and allow the sort of cool things you can do with streaming large data in curl.
1. `sodium` does not support streaming encryption/decryption. It might be possible (bindings to node and swift have it). In general this would be great and allow the sort of cool things you can do with streaming large data in curl.
2. R plays pretty loose and free with creating connections when given a filename; `readRDS`/`saveRDS` will open files with decompression on in binary mode, `read.csv`/`write.csv` don't. `write.table` adds encoding information when openning the connection object. The logic around what happens is entirely within the functions themselves so is hard to capture in a general way.
3. Connection objects look like a pain to write.

Expand Down
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@

**WARNING: until this package reaches version 1, the format used internally to move data around may change without warning or regard for backward compatibility - this will make it very difficult to recover your data! Once we reach version 1.0.0, we will be careful to support backward compatiblity**

Encryption wrappers, using low-level support from [`sodium`](https://github.com/jeroenooms/sodium) and [`openssl`](https://github.com/jeroenooms/openssl). This package is designed to be easy to use, rather than the most secure thing (you're using R, remember).
Encryption wrappers, using low-level support from [`sodium`](https://github.com/jeroenooms/sodium) and [`openssl`](https://github.com/jeroenooms/openssl). This package is designed to be easy to use, rather than the most secure thing (you're using R, remember - for examples of what `cyphr` can't protect against see [`jammr`](https://github.com/Ironholds/jammr), [`rpwnd`](https://github.com/hrbrmstr/rpwnd) and [`evil.R`](https://github.com/romainfrancois/evil.R).)

It provides high level functions to:
`cyphr` provides high level functions to:

* Encrypt and decrypt
* **strings**: `encrypt_string` / `decrypt_string`
Expand Down Expand Up @@ -131,7 +131,7 @@ identical(readRDS("myfile.clear"), iris)

## Wrappers around R's file functions

Encrypting files like the above risks leaving a cleartext version around. If you want to wrap the output of something like `write.csv` or `saveRDS` you really have no choice but to write out the file first, encrypt it, and delete the clear version. Making sure that this happens even if a step fails is error prone and takes a surprising number of repetitive lines of code.
Encrypting files like the above risks leaving a cleartext (i.e., unencrypted) version around. If you want to wrap the output of something like `write.csv` or `saveRDS` you really have no choice but to write out the file first, encrypt it, and delete the clear version. Making sure that this happens even if a step fails is error prone and takes a surprising number of repetitive lines of code.

Alternatively, to encrypt the output of a file producing command, just wrap it in `cyphr::encrypt`

Expand Down Expand Up @@ -216,7 +216,7 @@ The package contains support for a group of people are working on a sensitive da

A proper connection could be nice but there are at least three issues stopping this:

1. `sodium` does not support streaming encryption/decrption. It might be possible (bindings to node and swift have it). In general this would be great and allow the sort of cool things you can do with streaming large data in curl.
1. `sodium` does not support streaming encryption/decryption. It might be possible (bindings to node and swift have it). In general this would be great and allow the sort of cool things you can do with streaming large data in curl.
2. R plays pretty loose and free with creating connections when given a filename; `readRDS`/`saveRDS` will open files with decompression on in binary mode, `read.csv`/`write.csv` don't. `write.table` adds encoding information when openning the connection object. The logic around what happens is entirely within the functions themselves so is hard to capture in a general way.
3. Connection objects look like a pain to write.

Expand Down
142 changes: 130 additions & 12 deletions vignettes/cyphr.Rmd
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,10 @@ decrypt such information.
This vignette works through the basic functionality of the package.
It does not offer much in the way of an introduction to encryption
itself; for that see the excellent vignettes in the `openssl` and
`sodium` packages. This package is a thin wrapper for those
packages.
`sodium` packages (see `vignette("crypto101")` and
`vignette("bignum")` for information about how encryption works).
This package is a wrapper around those packages in order to make
them more accessible.

# Keys and the like

Expand All @@ -61,13 +63,16 @@ To encrypt anything we need a key. There are two sorts of key

We support symmetric keys and asymmetric key pairs from the
`openssl` and `sodium` packages (which wrap around
industry-standard cryptographic libraries).
industry-standard cryptographic libraries) - this vignette will
show how to create and load keys of different types as they're
used.

The `openssl` keys have the advantage of a standard key format, and
that many people (especially on Linux and macOS) have a keypair
already. The `sodium` keys have the advantage of being a new
library, starting from a clean slate rather than carrying with it
accumulated ideas from the last 20 years of development.
already (see below if you're not sure if you do). The `sodium`
keys have the advantage of being a new library, starting from a
clean slate rather than carrying with it accumulated ideas from the
last 20 years of development.

The idea in `cyphr` is that we can abstract away some differences
in the types of keys and the functions that go with them to create
Expand Down Expand Up @@ -240,6 +245,92 @@ secret <- cyphr::encrypt_string("secret message", pair_us)
secret
```

This all skips over how Alice and Bob will exchange this secret
information. Because the secret is bytes, it's a bit odd to work
with. Alice could save the secret to disk with
``` {r }
secret <- cyphr::encrypt_string("secret message", pair_a)
writeBin(secret, "for_bob_only")
```

And then send Bob the file `for_bob_only` (over email or any other
insecure medium).

and bob could read the secret in with:
``` {r }
secret <- readBin("for_bob_only", raw(), file.size("for_bob_only"))
cyphr::decrypt_string(secret, pair_b)
```

As an alternative, you can "base64 encode" the bytes into something
that you can just email around:
``` {r }
secret_base64 <- openssl::base64_encode(secret)
secret_base64
```

This can be converted back with `openssl::base64_decode`:
``` {r }
identical(openssl::base64_decode(secret_base64), secret)
```

Or, less compactly but also suitable for email, you might just
convert the bytes into their hex representation:
``` {r }
secret_hex <- sodium::bin2hex(secret)
secret_hex
```

and the reverse with `sodium::hex2bin`:
``` {r }
identical(sodium::hex2bin(secret_hex), secret)
```

(this is somewhat less space efficient than base64 encoding.

As a final option, you can just save the secret with `saveRDS` and
read it in with `readRDS` like any other option. This will be the
best route if the secret is saved into a more complicated R object
(e.g., a list or `data.frame`).

See the other cyphr vignette (`vignette("data", package =
"cyphr")`) for a suggested workflow for exchanging secrets within a
team, and the wrapper functions below for more convenient ways of
working with encrypted data.

**Do you already have an ssh keypair?** To find out, run

```r
cyphr::keypair_openssl(NULL, NULL)
```

One of three things will happen:

1. you will be prompted for your password to decrypt your private
key, and then after entering it an object `<cyphr_keypair:
openssl>` will be returned - you're good to go!

2. you were _not_ prompted for your password, but got a
`<cyphr_keypair: openssl>` object. You should consider whether
this is appropriate and consider generating a new keypair with the
private key encrypted. If you don't then anyone who can read your
private key can decrypt any message intended for you.

3. you get an error like `Did not find default ssh public key at
~/.ssh/id_rsa.pub`. You need to create a keypair.

To create a keypair, you can use the `cyphr::ssh_keygen()` function as

```r
cyphr::ssh_keygen("~/.ssh")
```

This will create the keypair as `~/.ssh/id_rsa` and
`~/.ssh/id_rsa.pub`, which is where `cyphr` will look for your keys
by default. See `?ssh_keygen` for more information. (On Linux and
macOS you might use the `ssh-keygen` command line utility. On
windows, PuTTY` has a utility for creating keys.)

### `sodium`

With `sodium`, things are largely the same with the exception that
Expand Down Expand Up @@ -422,9 +513,10 @@ key <- cyphr::key_sodium(sodium::keygen())
x <- list(a = 1:10, b = "don't tell anyone else")
```

If you save this to disk with `saveRDS` it will be readable by
everyone. But if you encrypted the file that `saveRDS` produced it
would be protected:
If you save `x` to disk with `saveRDS` it will be readable by
everyone until it is deleted. But if you encrypted the file that
`saveRDS` produced it would be protected and only people with the
key can read it:
``` {r }
cyphr::encrypt(saveRDS(x, "secret.rds"), key)
```
Expand All @@ -443,9 +535,18 @@ object it can be decrypted and read:
cyphr::decrypt(readRDS("secret.rds"), key)
```

What happens in the call above is some moderately nasty call
rewriting. If this bothers you, you should just use `encrypt_file`
/ `decrypt_file` and make sure to clean up after yourself.
What happens in the call above is `cyphr` uses "non standard
evaluation" to rewrite the call above so that it becomes
(approximately)

1. use `cyphr::decrypt_file` to decrypt "secret.rds" as a temporary file
2. call `readRDS` on that temporary file
3. delete the temporary file (even if there is an error in the above calls)

This non-standard evaluation breaks referential integrity (so may
not be suitable for programming). You can always do this manually
with `encrypt_file` / `decrypt_file` so long as you make sure to
clean up after yourself.

The `encrypt` function inspects the call in the first argument
passed to it and works out for the function provided (`saveRDS`)
Expand Down Expand Up @@ -555,3 +656,20 @@ attacker to use in a subsequent session.
Sys.unsetenv("USER_KEY")
file.remove(c("secret.rds", "iris.csv", "iris2.csv"))
```

# Further reading

* The wikipedia page on Public Key cryptography has some nice
diagrams that explain how key and data interact
https://en.wikipedia.org/wiki/Public-key_cryptography
* The vigettes in the `openssl` (`vignette(package = "openssl")`)
and `sodium` (`vignette(package = "openssl")`) packages have
explanations of how the tools used in `cyphr` work and interface
with R.

Confused? Need help? Found a bug?

* Post an issue on the [`cyphr` issue
tracker](https://github.com/ropensci/cyphr/issues)
* Start a discussion on the [rOpenSci discussion
forum](https://discuss.ropensci.org/)
17 changes: 17 additions & 0 deletions vignettes/src/cyphr.R
Original file line number Diff line number Diff line change
Expand Up @@ -548,3 +548,20 @@ cyphr::decrypt_string(secret, key)
##+ echo = FALSE, results = "hide"
Sys.unsetenv("USER_KEY")
file.remove(c("secret.rds", "iris.csv", "iris2.csv"))

## # Further reading

## * The wikipedia page on Public Key cryptography has some nice
## diagrams that explain how key and data interact
## https://en.wikipedia.org/wiki/Public-key_cryptography
## * The vigettes in the `openssl` (`vignette(package = "openssl")`)
## and `sodium` (`vignette(package = "openssl")`) packages have
## explanations of how the tools used in `cyphr` work and interface
## with R.

## Confused? Need help? Found a bug?

## * Post an issue on the [`cyphr` issue
## tracker](https://github.com/ropensci/cyphr/issues)
## * Start a discussion on the [rOpenSci discussion
## forum](https://discuss.ropensci.org/)

0 comments on commit 3a20372

Please sign in to comment.