From 2cabf96a91fe05a95a350cb3daa6a373ddcda9de Mon Sep 17 00:00:00 2001 From: Jeroen Ooms Date: Thu, 21 Oct 2021 16:22:59 +0200 Subject: [PATCH] Cleanup --- inst/doc/crypto101.R | 114 --------------- inst/doc/crypto101.html | 287 ------------------------------------ inst/doc/crypto101.rmd | 190 ------------------------ inst/doc/intro.R | 92 ------------ inst/doc/intro.html | 317 ---------------------------------------- inst/doc/intro.rmd | 179 ----------------------- 6 files changed, 1179 deletions(-) delete mode 100644 inst/doc/crypto101.R delete mode 100644 inst/doc/crypto101.html delete mode 100644 inst/doc/crypto101.rmd delete mode 100644 inst/doc/intro.R delete mode 100644 inst/doc/intro.html delete mode 100644 inst/doc/intro.rmd diff --git a/inst/doc/crypto101.R b/inst/doc/crypto101.R deleted file mode 100644 index b1d9c38..0000000 --- a/inst/doc/crypto101.R +++ /dev/null @@ -1,114 +0,0 @@ -## ---- echo = FALSE, message = FALSE-------------------------------------- -knitr::opts_chunk$set(comment = "") -library(sodium) - -# hack for printable bits -random <- function(n = 1){ - if(n != nchar("TTIP is evil")) - return(sodium::random(n)) - repeat { - x <- sodium::random(n) - y <- base::xor(charToRaw("TTIP is evil"), x) - if(all(c(x,y) != 0)) return(x) - } -} - -## ------------------------------------------------------------------------ -# XOR two (8bit) bytes 'x' and 'y' -x <- as.raw(0x7a) -y <- as.raw(0xe4) -z <- base::xor(x, y) -dput(z) - -# Show the bits in each byte -cbind(x = rawToBits(x), y = rawToBits(y), z = rawToBits(z)) - -## ------------------------------------------------------------------------ -# Encrypt message using random one-time-pad -msg <- charToRaw("TTIP is evil") -one_time_pad <- random(length(msg)) -ciphertext <- base::xor(msg, one_time_pad) - -# It's really encrypted -rawToChar(ciphertext) - -# Decrypt with same pad -rawToChar(base::xor(ciphertext, one_time_pad)) - -## ------------------------------------------------------------------------ -password <- "My secret passphrase" -key <- hash(charToRaw(password)) -nonce <- random(8) -chacha20(size = 20, key, nonce) - -## ------------------------------------------------------------------------ -salsa20(size = 20, key, nonce) - -## ------------------------------------------------------------------------ -# Illustrative example. -sha256_ctr <- function(size, key, nonce){ - n <- ceiling(size/32) - output <- raw() - for(i in 1:n){ - counter <- packBits(intToBits(i)) - block <- sha256(c(key, nonce, counter)) - output <- c(output, block) - } - return(output[1:size]) -} - -## ------------------------------------------------------------------------ -password <- "My secret passphrase" -key <- hash(charToRaw(password)) -nonce <- random(8) -sha256_ctr(50, key, nonce) - -## ------------------------------------------------------------------------ -# Encrypt 'message' using 'password' -myfile <- file.path(R.home(), "COPYING") -message <- readBin(myfile, raw(), file.info(myfile)$size) -passwd <- charToRaw("My secret passphrase") - -## ------------------------------------------------------------------------ -# Basic secret key encryption -key <- hash(passwd) -nonce8 <- random(8) -stream <- chacha20(length(message), key, nonce8) -ciphertext <- base::xor(stream, message) - -## ------------------------------------------------------------------------ -# Decrypt with the same key -key <- hash(charToRaw("My secret passphrase")) -stream <- chacha20(length(ciphertext), key, nonce8) -out <- base::xor(ciphertext, stream) - -# Print part of the message -cat(substring(rawToChar(out), 1, 120)) - -## ------------------------------------------------------------------------ -# Create keypair -key <- keygen() -pub <- pubkey(key) - -# Encrypt message for receiver using his/her public key -msg <- serialize(iris, NULL) -ciphertext <- simple_encrypt(msg, pub) - -# Receiver decrypts with his/her private key -out <- simple_decrypt(ciphertext, key) -identical(msg, out) - -## ------------------------------------------------------------------------ -# Bob generates keypair -bob_key <- keygen() -bob_pubkey <- pubkey(bob_key) - -# Alice generates keypair -alice_key <- keygen() -alice_pubkey <- pubkey(alice_key) - -# After Bob and Alice exchange pubkey they can both derive the secret -alice_secret <- diffie_hellman(alice_key, bob_pubkey) -bob_secret <- diffie_hellman(bob_key, alice_pubkey) -identical(alice_secret, bob_secret) - diff --git a/inst/doc/crypto101.html b/inst/doc/crypto101.html deleted file mode 100644 index e80a682..0000000 --- a/inst/doc/crypto101.html +++ /dev/null @@ -1,287 +0,0 @@ - - - - - - - - - - - - - - -How does cryptography work? - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - -

This page attempts to give a very basic conceptual introduction to cryptographic methods. Before we start the usual disclaimer:

-

I am not a cryptographer. This document is only for educational purposes. Crypto is hard, you should never trust your home-grown implementation. Unless you’re a cryptographer you will probably overlook some crucial details. Developers should only use the high-level functions that have been implemented by an actual cryptographer.

-

Now that we got this is out of the way, let’s start hacking :)

-
-

The XOR operator

-

The bitwise XOR operator outputs true only when both inputs differ (one is true, the other is false). It is sometimes called an invertor because the output of a bit in x gets inverted if and only if the corresponding bit in y is true:

-
# XOR two (8bit) bytes 'x' and 'y'
-x <- as.raw(0x7a)
-y <- as.raw(0xe4)
-z <- base::xor(x, y)
-dput(z)
-
as.raw(0x9e)
-
# Show the bits in each byte
-cbind(x = rawToBits(x), y = rawToBits(y), z = rawToBits(z))
-
      x  y  z
-[1,] 00 00 00
-[2,] 01 00 01
-[3,] 00 01 01
-[4,] 01 00 01
-[5,] 01 00 01
-[6,] 01 01 00
-[7,] 01 01 00
-[8,] 00 01 01
-

In cryptography we xor a message x with secret random data y. Because each bit in y is randomly true with probability 0.5, the xor output is completely random and uncorrelated to x. This is called perfect secrecy. Only if we know y we can decipher the message x.

-
# Encrypt message using random one-time-pad
-msg <- charToRaw("TTIP is evil")
-one_time_pad <- random(length(msg))
-ciphertext <- base::xor(msg, one_time_pad)
-
-# It's really encrypted
-rawToChar(ciphertext)
-
[1] "\xe9\x8c8\u009fd\xfd\x93\xf1\xed/%"
-
# Decrypt with same pad
-rawToChar(base::xor(ciphertext, one_time_pad))
-
[1] "TTIP is evil"
-

This method is perfectly secure and forms the basis for most cryptograhpic methods. However the challenge is generating and communicating unique pseudo-random y data every time we want to encrypt something. One-time-pads as in the example are not very practical for large messages. Also we should never re-use a one-time-pad y for encrypting multiple messages, as this compromises the secrecy.

-
-
-

Stream ciphers

-

The solution to this problem are stream ciphers. A stream cipher generates a unique stream of pseudo-random data based on a secret key and a unique nonce. For a given set of parameters the stream cipher always generates the same stream of data. Sodium implements a few popular stream ciphers:

-
password <- "My secret passphrase"
-key <- hash(charToRaw(password))
-nonce <- random(8)
-chacha20(size = 20, key, nonce)
-
 [1] bb 96 2f 70 96 7d bd 2d ee 20 b5 e6 61 50 59 7b e7 be d1 9f
-

Each stream requires a key and a nonce. The key forms the shared secret and should only be known to trusted parties. The nonce is not secret and is stored or sent along with the ciphertext. The purpose of the nonce is to make a random stream unique to protect gainst re-use attacks. This way you can re-use a your key to encrypt multiple messages, as long as you never re-use the same nonce.

-
salsa20(size = 20, key, nonce)
-
 [1] 56 47 ff d1 96 14 3a f2 f2 70 30 fe ba 8a 05 1e 74 2a 4d be
-

Over the years cryptographers have come up with many more variants. Many stream ciphers are based on a block cipher such as AES: a keyed permutation of fixed length amount of data. The block ciphers get chained in a particular mode of operation which repeatedly applies the cipher’s single-block operation to securely transform amounts of data larger than a block.

-

We are not going to discuss implementation details, but you could probably come up with something yourself. For example you could use a hash function such sha256 as the block cipher and append counter which is incremented for each block (this is called CTR mode).

-
# Illustrative example.
-sha256_ctr <- function(size, key, nonce){
-  n <- ceiling(size/32)
-  output <- raw()
-  for(i in 1:n){
-    counter <- packBits(intToBits(i))
-    block <- sha256(c(key, nonce, counter))
-    output <- c(output, block)
-  }
-  return(output[1:size])
-}
-

This allows us to generate an arbitrary length stream from a single secret key:

-
password <- "My secret passphrase"
-key <- hash(charToRaw(password))
-nonce <- random(8)
-sha256_ctr(50, key, nonce)
-
 [1] e3 29 96 fb b5 bd 15 d7 55 df d7 b3 1e 12 d1 5c 0b 74 86 4a 5d 85 b3
-[24] bf aa 62 bb 6e 0b d3 bf ab 05 a2 31 9f a6 3a fd 83 1d 26 9f 7f 35 8d
-[47] eb ec eb c3
-

In practice, you should never write your own ciphers. A lot of research goes into studying the properties of block ciphers under various modes of operation. In the remainder we just use the standard Sodium ciphers: chacha20, salsa20 or xsalsa20.

-
-
-

Symmetric encryption

-

Symmetric encryption means that the same secret key is used for both encryption and decryption. All that is needed to implement symmetric encryption is xor and a stream cipher. For example to encrypt an arbitrary length message using password:

-
# Encrypt 'message' using 'password'
-myfile <- file.path(R.home(), "COPYING")
-message <- readBin(myfile, raw(), file.info(myfile)$size)
-passwd <- charToRaw("My secret passphrase")
-

A hash function converts the password to a key of suitable size for the stream cipher, which we use to generate a psuedo random stream of equal length to the message:

-
# Basic secret key encryption
-key <- hash(passwd)
-nonce8 <- random(8)
-stream <- chacha20(length(message), key, nonce8)
-ciphertext <- base::xor(stream, message)
-

Now the ciphertext is an encrypted version of the message. Only those that know the key and the nonce can re-generate the same keystream in order to xor the ciphertext back into the original message.

-
# Decrypt with the same key
-key <- hash(charToRaw("My secret passphrase"))
-stream <- chacha20(length(ciphertext), key, nonce8)
-out <- base::xor(ciphertext, stream)
-
-# Print part of the message
-cat(substring(rawToChar(out), 1, 120))
-
            GNU GENERAL PUBLIC LICENSE
-               Version 2, June 1991
-
- Copyright (C) 1989, 1991 Free Software Foundation, Inc.
-

The Sodium functions data_encrypt and data_decrypt provide a more elaborate implementation of the above. This is what you should use in practice for secret key encryption.

-

Symmetric encryption can be used for e.g. encrypting local data. However because the same secret is used for both encryption and decryption, it is impractical for communication with other parties. For exchanging secure messages we need public key encryption.

-
-
-

Public-key encryption and Diffie-Hellman

-

Rather than using a single secret-key, assymetric (public key) encryption requires a keypair, consisting of a public key for encryption and a private-key for decryption. Data that is encrypted using a given public key can only be decrypted using the corresponding private key.

-

The public key is not confidential and can be shared on e.g. a website or keyserver. This allows anyone to send somebody a secure message by encrypting it with the receivers public key. The encrypted message will only be readable by the owner of the corresponding private key.

-
# Create keypair
-key <- keygen()
-pub <- pubkey(key)
-
-# Encrypt message for receiver using his/her public key
-msg <- serialize(iris, NULL)
-ciphertext <- simple_encrypt(msg, pub)
-
-# Receiver decrypts with his/her private key
-out <- simple_decrypt(ciphertext, key)
-identical(msg, out)
-
[1] TRUE
-

How does this work? Public key encryption makes use of Diffie-Hellman (D-H): a method which allows two parties that have no prior knowledge of each other to jointly establish a shared secret key over an insecure channel. In the most simple case, both parties generate a temporary keypair and exchange their public key over the insecure channel. Then both parties use the D-H function to calculcate the (same) shared secret key by combining their own private key with the other person’s public key:

-
# Bob generates keypair
-bob_key <- keygen()
-bob_pubkey <- pubkey(bob_key)
-
-# Alice generates keypair
-alice_key <- keygen()
-alice_pubkey <- pubkey(alice_key)
-
-# After Bob and Alice exchange pubkey they can both derive the secret
-alice_secret <- diffie_hellman(alice_key, bob_pubkey)
-bob_secret <- diffie_hellman(bob_key, alice_pubkey)
-identical(alice_secret, bob_secret)
-
[1] TRUE
-

Once the shared secret has been established, both parties can discard their temporary public/private key and use the shared secret to start encrypting communications with symmetric encryption as discussed earlier. Because the shared secret cannot be calculated using only the public keys, the process is safe from eavesdroppers.

-

The classical Diffie-Hellman method is based on the discrete logarithm problem with large prime numbers. Sodium uses curve25519, a state-of-the-art D-H function by Daniel Bernsteinan designed for use with the elliptic curve Diffie–Hellman (ECDH) key agreement scheme.

-
- - - - -
- - - - - - - - diff --git a/inst/doc/crypto101.rmd b/inst/doc/crypto101.rmd deleted file mode 100644 index abde32e..0000000 --- a/inst/doc/crypto101.rmd +++ /dev/null @@ -1,190 +0,0 @@ ---- -title: "How does cryptography work?" -output: html_document -date: "`r Sys.Date()`" -vignette: > - %\VignetteIndexEntry{How does cryptography work} - %\VignetteEngine{knitr::rmarkdown} - \usepackage[utf8]{inputenc} ---- - -```{r, echo = FALSE, message = FALSE} -knitr::opts_chunk$set(comment = "") -library(sodium) - -# hack for printable bits -random <- function(n = 1){ - if(n != nchar("TTIP is evil")) - return(sodium::random(n)) - repeat { - x <- sodium::random(n) - y <- base::xor(charToRaw("TTIP is evil"), x) - if(all(c(x,y) != 0)) return(x) - } -} -``` - -This page attempts to give a very basic conceptual introduction to cryptographic methods. Before we start the usual disclaimer: - -___I am not a cryptographer. This document is only for educational purposes. Crypto is hard, you should never trust your home-grown implementation. Unless you're a cryptographer you will probably overlook some crucial details. Developers should only use the high-level functions that have been implemented by an actual cryptographer.___ - -Now that we got this is out of the way, let's start hacking :) - -### The XOR operator - -The bitwise [XOR operator](https://en.wikipedia.org/wiki/Exclusive_or#Truth_table) outputs `true` only when both inputs differ (one is `true`, the other is `false`). It is sometimes called an *invertor* because the output of a bit in `x` gets inverted if and only if the corresponding bit in `y` is true: - -```{r} -# XOR two (8bit) bytes 'x' and 'y' -x <- as.raw(0x7a) -y <- as.raw(0xe4) -z <- base::xor(x, y) -dput(z) - -# Show the bits in each byte -cbind(x = rawToBits(x), y = rawToBits(y), z = rawToBits(z)) -``` - -In cryptography we `xor` a message `x` with secret random data `y`. Because each bit in `y` is randomly `true` with probability 0.5, the `xor` output is completely random and uncorrelated to `x`. This is called *perfect secrecy*. Only if we know `y` we can decipher the message `x`. - -```{r} -# Encrypt message using random one-time-pad -msg <- charToRaw("TTIP is evil") -one_time_pad <- random(length(msg)) -ciphertext <- base::xor(msg, one_time_pad) - -# It's really encrypted -rawToChar(ciphertext) - -# Decrypt with same pad -rawToChar(base::xor(ciphertext, one_time_pad)) -``` - -This method is perfectly secure and forms the basis for most cryptograhpic methods. However the challenge is generating and communicating unique pseudo-random `y` data every time we want to encrypt something. One-time-pads as in the example are not very practical for large messages. Also we should never re-use a one-time-pad `y` for encrypting multiple messages, as this compromises the secrecy. - -### Stream ciphers - -The solution to this problem are stream ciphers. A *stream cipher* generates a unique stream of pseudo-random data based on a secret `key` and a unique `nonce`. For a given set of parameters the stream cipher always generates the same stream of data. Sodium implements a few popular stream ciphers: - -```{r} -password <- "My secret passphrase" -key <- hash(charToRaw(password)) -nonce <- random(8) -chacha20(size = 20, key, nonce) -``` - -Each stream requires a `key` and a `nonce`. The key forms the shared secret and should only be known to trusted parties. The `nonce` is not secret and is stored or sent along with the ciphertext. The purpose of the `nonce` is to make a random stream unique to protect gainst re-use attacks. This way you can re-use a your key to encrypt multiple messages, as long as you never re-use the same nonce. - -```{r} -salsa20(size = 20, key, nonce) -``` - -Over the years cryptographers have come up with many more variants. Many stream ciphers are based on a block cipher such as [AES](https://en.wikipedia.org/wiki/Advanced_Encryption_Standard): a keyed permutation of fixed length amount of data. The block ciphers get chained in a particular [mode of operation](https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation) which repeatedly applies the cipher's single-block operation to securely transform amounts of data larger than a block. - -We are not going to discuss implementation details, but you could probably come up with something yourself. For example you could use a hash function such `sha256` as the block cipher and append counter which is incremented for each block (this is called CTR mode). - - -```{r} -# Illustrative example. -sha256_ctr <- function(size, key, nonce){ - n <- ceiling(size/32) - output <- raw() - for(i in 1:n){ - counter <- packBits(intToBits(i)) - block <- sha256(c(key, nonce, counter)) - output <- c(output, block) - } - return(output[1:size]) -} -``` - -This allows us to generate an arbitrary length stream from a single secret key: - -```{r} -password <- "My secret passphrase" -key <- hash(charToRaw(password)) -nonce <- random(8) -sha256_ctr(50, key, nonce) -``` - - - -In practice, you should never write your own ciphers. A lot of [research](http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.58.1811&rep=rep1&type=pdf) goes into studying the properties of block ciphers under various modes of operation. In the remainder we just use the standard Sodium ciphers: [`chacha20`](https://download.libsodium.org/libsodium/content/advanced/chacha20.html), [`salsa20`](https://download.libsodium.org/libsodium/content/advanced/salsa20.html) or [`xsalsa20`](https://download.libsodium.org/libsodium/content/advanced/xsalsa20.html). - -### Symmetric encryption - -Symmetric encryption means that the same secret key is used for both encryption and decryption. All that is needed to implement symmetric encryption is `xor` and a stream cipher. For example to encrypt an arbitrary length `message` using `password`: - -```{r} -# Encrypt 'message' using 'password' -myfile <- file.path(R.home(), "COPYING") -message <- readBin(myfile, raw(), file.info(myfile)$size) -passwd <- charToRaw("My secret passphrase") -``` - -A hash function converts the password to a key of suitable size for the stream cipher, which we use to generate a psuedo random stream of equal length to the message: - -```{r} -# Basic secret key encryption -key <- hash(passwd) -nonce8 <- random(8) -stream <- chacha20(length(message), key, nonce8) -ciphertext <- base::xor(stream, message) -``` - -Now the `ciphertext` is an encrypted version of the message. Only those that know the `key` and the `nonce` can re-generate the same keystream in order to `xor` the ciphertext back into the original message. - -```{r} -# Decrypt with the same key -key <- hash(charToRaw("My secret passphrase")) -stream <- chacha20(length(ciphertext), key, nonce8) -out <- base::xor(ciphertext, stream) - -# Print part of the message -cat(substring(rawToChar(out), 1, 120)) -``` - -The Sodium functions `data_encrypt` and `data_decrypt` provide a more elaborate implementation of the above. This is what you should use in practice for secret key encryption. - -Symmetric encryption can be used for e.g. encrypting local data. However because the same secret is used for both encryption and decryption, it is impractical for communication with other parties. For exchanging secure messages we need public key encryption. - -### Public-key encryption and Diffie-Hellman - -Rather than using a single secret-key, assymetric (public key) encryption requires a *keypair*, consisting of a *public key* for encryption and a *private-key* for decryption. Data that is encrypted using a given public key can only be decrypted using the corresponding private key. - -The public key is not confidential and can be shared on e.g. a website or keyserver. This allows anyone to send somebody a secure message by encrypting it with the receivers public key. The encrypted message will only be readable by the owner of the corresponding private key. - -```{r} -# Create keypair -key <- keygen() -pub <- pubkey(key) - -# Encrypt message for receiver using his/her public key -msg <- serialize(iris, NULL) -ciphertext <- simple_encrypt(msg, pub) - -# Receiver decrypts with his/her private key -out <- simple_decrypt(ciphertext, key) -identical(msg, out) -``` - -How does this work? Public key encryption makes use of [Diffie-Hellman](http://mathworld.wolfram.com/Diffie-HellmanProtocol.html) (D-H): a method which allows two parties that have no prior knowledge of each other to jointly establish a shared secret key over an insecure channel. In the most simple case, both parties generate a temporary keypair and exchange their public key over the insecure channel. Then both parties use the D-H function to calculcate the (same) shared secret key by combining their own private key with the other person's public key: - -```{r} -# Bob generates keypair -bob_key <- keygen() -bob_pubkey <- pubkey(bob_key) - -# Alice generates keypair -alice_key <- keygen() -alice_pubkey <- pubkey(alice_key) - -# After Bob and Alice exchange pubkey they can both derive the secret -alice_secret <- diffie_hellman(alice_key, bob_pubkey) -bob_secret <- diffie_hellman(bob_key, alice_pubkey) -identical(alice_secret, bob_secret) -``` - -Once the shared secret has been established, both parties can discard their temporary public/private key and use the shared secret to start encrypting communications with symmetric encryption as discussed earlier. Because the shared secret cannot be calculated using only the public keys, the process is safe from eavesdroppers. - -The classical [Diffie-Hellman method](http://mathworld.wolfram.com/Diffie-HellmanProtocol.html) is based on the discrete logarithm problem with large prime numbers. Sodium uses [curve25519](http://cr.yp.to/ecdh/curve25519-20060209.pdf), a state-of-the-art D-H function by Daniel Bernsteinan designed for use with the elliptic curve Diffie–Hellman (ECDH) key agreement scheme. diff --git a/inst/doc/intro.R b/inst/doc/intro.R deleted file mode 100644 index 9c5211f..0000000 --- a/inst/doc/intro.R +++ /dev/null @@ -1,92 +0,0 @@ -## ---- echo = FALSE, message = FALSE-------------------------------------- -knitr::opts_chunk$set(comment = "") -library(sodium) - -## ------------------------------------------------------------------------ -test <- hash(charToRaw("test 123")) -str <- bin2hex(test) -print(str) -hex2bin(str) - -## ------------------------------------------------------------------------ -secret <- random(8) -print(secret) - -## ------------------------------------------------------------------------ -# Generate keys from passphrase -passphrase <- charToRaw("This is super secret") -hash(passphrase) -hash(passphrase, size = 16) -hash(passphrase, size = 64) - -## ------------------------------------------------------------------------ -key <- hash(charToRaw("This is a secret passphrase")) -msg <- serialize(iris, NULL) - -# Encrypt with a random nonce -nonce <- random(24) -cipher <- data_encrypt(msg, key, nonce) - -# Decrypt with same key and nonce -orig <- data_decrypt(cipher, key, nonce) -identical(iris, unserialize(orig)) - -## ------------------------------------------------------------------------ -key <- hash(charToRaw("This is a secret passphrase")) -msg <- serialize(iris, NULL) -mytag <- data_tag(msg, key) - -## ------------------------------------------------------------------------ -stopifnot(identical(mytag, data_tag(msg, key))) - -## ------------------------------------------------------------------------ -key <- keygen() -pub <- pubkey(key) - -## ------------------------------------------------------------------------ -# Encrypt message with pubkey -msg <- serialize(iris, NULL) -ciphertext <- simple_encrypt(msg, pub) - -# Decrypt message with private key -out <- simple_decrypt(ciphertext, key) -stopifnot(identical(out, msg)) - -## ------------------------------------------------------------------------ -# Generate signature keypair -key <- sig_keygen() -pubkey <- sig_pubkey(key) - -# Create signature with private key -msg <- serialize(iris, NULL) -sig <- sig_sign(msg, key) -print(sig) - -# Verify a signature from public key -sig_verify(msg, sig, pubkey) - -## ------------------------------------------------------------------------ -# Bob's keypair: -bob_key <- keygen() -bob_pubkey <- pubkey(bob_key) - -# Alice's keypair: -alice_key <- keygen() -alice_pubkey <- pubkey(alice_key) - -# Bob sends encrypted message for Alice: -msg <- charToRaw("TTIP is evil") -ciphertext <- auth_encrypt(msg, bob_key, alice_pubkey) - -# Alice verifies and decrypts with her key -out <- auth_decrypt(ciphertext, alice_key, bob_pubkey) -stopifnot(identical(out, msg)) - -# Alice sends encrypted message for Bob -msg <- charToRaw("Let's protest") -ciphertext <- auth_encrypt(msg, alice_key, bob_pubkey) - -# Bob verifies and decrypts with his key -out <- auth_decrypt(ciphertext, bob_key, alice_pubkey) -stopifnot(identical(out, msg)) - diff --git a/inst/doc/intro.html b/inst/doc/intro.html deleted file mode 100644 index f772c24..0000000 --- a/inst/doc/intro.html +++ /dev/null @@ -1,317 +0,0 @@ - - - - - - - - - - - - - - -Sodium: A Modern and Easy-to-Use Crypto Library - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - -

The sodium R package provides bindings to libsodium: a modern, easy-to-use software library for encryption, decryption, signatures, password hashing and more.

-

The goal of Sodium is to provide the core operations needed to build higher-level cryptographic tools. It is not intended for implementing standardized protocols such as TLS, SSH or GPG. Sodium only supports a limited set of state-of-the-art elliptic curve methods, resulting in a simple but very powerful tool-kit for building secure applications.

- ------ - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Authenticated EncryptionEncryption onlyAuthentication only
Symmetric (secret key):data_encrypt / data_decryptdata_tag
Asymmetric (public+private key):auth_encrypt / auth_decryptsimple_encrypt / simple_decryptsig_sign / sig_verify
-
-

Using Sodium

-

All Sodium functions operate on binary data, called ‘raw’ vectors in R. Use charToRaw and rawToChar to convert between strings and raw vectors. Alternatively hex2bin and bin2hex can convert between binary data to strings in hex notation:

-
test <- hash(charToRaw("test 123"))
-str <- bin2hex(test)
-print(str)
-
[1] "e8b785b02e702c0b7edc9683130db36c91e0241ba0c489ff1e20cbb4fa3920f9"
-
hex2bin(str)
-
 [1] e8 b7 85 b0 2e 70 2c 0b 7e dc 96 83 13 0d b3 6c 91 e0 24 1b a0 c4 89
-[24] ff 1e 20 cb b4 fa 39 20 f9
-
-
-

Random data generator

-

The random() function generates n bytes of unpredictable data, suitable for creating secret keys.

-
secret <- random(8)
-print(secret)
-
[1] 90 70 7a fd 1d 39 bc 75
-

Implementation is platform specific, see the docs for details.

-
-
-

Hash functions

-

Sodium has several hash functions including hash(), shorthash(), sha256(), sha512 and scrypt(). The generic hash() is usually recommended. It uses blake2b with a configurable size between 16 bytes (128bit) and 64 bytes (512bit).

-
# Generate keys from passphrase
-passphrase <- charToRaw("This is super secret")
-hash(passphrase)
-
 [1] 98 5c 9b b6 f6 92 d5 26 10 80 99 25 3e a5 a6 66 67 13 fd 88 10 b6 12
-[24] 74 86 c8 e9 5c 44 07 45 f5
-
hash(passphrase, size = 16)
-
 [1] eb 6c df 04 18 40 16 28 c1 b0 2e 76 f3 e6 bd 89
-
hash(passphrase, size = 64)
-
 [1] d0 89 68 30 26 1d 1b 85 76 dc ad 20 c9 58 0a fb b1 d0 62 ba 10 d6 80
-[24] f6 cb c6 ae 2d 42 57 ee a0 65 fd b0 e8 90 02 ae b3 e0 4f 88 df ba ea
-[47] 26 bb 47 3f 29 5a a4 06 cd b8 05 78 83 31 66 dc 7b 24
-

The shorthash() function is a special 8 byte (64 bit) hash based on SipHash-2-4. The output of this function is only 64 bits (8 bytes). It is useful for in e.g. Hash tables, but it should not be considered collision-resistant.

-
-
-

Secret key encryption

-

Symmetric encryption uses the same secret key for both encryption and decryption. It is mainly useful for encrypting local data, or as a building block for more complex methods.

-

Most encryption methods require a nonce: a piece of non-secret unique data that is used to randomize the cipher. This allows for safely using the same key for encrypting multiple messages. The nonce should be stored or shared along with the ciphertext.

-
key <- hash(charToRaw("This is a secret passphrase"))
-msg <- serialize(iris, NULL)
-
-# Encrypt with a random nonce
-nonce <- random(24)
-cipher <- data_encrypt(msg, key, nonce)
-
-# Decrypt with same key and nonce
-orig <- data_decrypt(cipher, key, nonce)
-identical(iris, unserialize(orig))
-
[1] TRUE
-

Because the secret has to be known by all parties, symmetric encryption by itself is often impractical for communication with third parties. For this we need asymmetric (public key) methods.

-
-
-

Secret key authentication

-

Secret key authentication is called tagging in Sodium. A tag is basically a hash of the data together with a secret key.

-
key <- hash(charToRaw("This is a secret passphrase"))
-msg <- serialize(iris, NULL)
-mytag <- data_tag(msg, key)
-

To verify the integrity of the data at a later point in time, simply re-calculate the tag with the same key:

-
stopifnot(identical(mytag, data_tag(msg, key)))
-

The secret key protects against forgery of the data+tag by an intermediate party, as would be possible with a regular checksum.

-
-
-

Public key encryption

-

Where symmetric methods use the same secret key for encryption and decryption, asymmetric methods use a key-pair consisting of a public key and private key. The private key is secret and only known by its owner. The public key on the other hand can be shared with anyone. Public keys are often published on the user’s website or posted in public directories or keyservers.

-
key <- keygen()
-pub <- pubkey(key)
-

In public key encryption, data encrypted with a public key can only be decrypted using the corresponding private key. This allows anyone to send somebody a secure message by encrypting it with the receivers public key. The encrypted message will only be readable by the owner of the corresponding private key.

-
# Encrypt message with pubkey
-msg <- serialize(iris, NULL)
-ciphertext <- simple_encrypt(msg, pub)
-
-# Decrypt message with private key
-out <- simple_decrypt(ciphertext, key)
-stopifnot(identical(out, msg))
-
-
-

Public key authentication (signatures)

-

Public key authentication works the other way around. First, the owner of the private key creates a ‘signature’ (an authenticated checksum) for a message in a way that allows anyone who knows his/her public key to verify the integrity of the message and identity of the sender.

-

Currently sodium requires a different type of key-pair for signatures (ed25519) than for encryption (curve25519).

-
# Generate signature keypair
-key <- sig_keygen()
-pubkey <- sig_pubkey(key)
-
-# Create signature with private key
-msg <- serialize(iris, NULL)
-sig <- sig_sign(msg, key)
-print(sig)
-
 [1] 75 b0 62 9b 52 e0 2a 00 21 f2 f1 d8 61 09 a7 88 25 82 5c 4a a6 c7 c5
-[24] 4e 8f 0c c6 ff 6d 57 26 71 94 b7 6a 15 c9 a3 b6 c9 d6 80 6a 04 93 7a
-[47] 7e 81 b6 95 b7 01 20 20 77 f0 77 ea d4 7f 52 f2 67 05
-
# Verify a signature from public key
-sig_verify(msg, sig, pubkey)
-
[1] TRUE
-

Signatures are useful when the message itself is not confidential but integrity is important. A common use is for software repositories where to include an index file with checksums for all packages, signed by the repository maintainer. This allows client package managers to verify that the binaries were not manipulated by intermediate parties during the distribution process.

-
-
-

Public key authenticated encryption

-

Authenticated encryption implements best practices for secure messaging. It requires that both sender and receiver have a keypair and know each other’s public key. Each message gets authenticated with the key of the sender and encrypted with the key of the receiver.

-
# Bob's keypair:
-bob_key <- keygen()
-bob_pubkey <- pubkey(bob_key)
-
-# Alice's keypair:
-alice_key <- keygen()
-alice_pubkey <- pubkey(alice_key)
-
-# Bob sends encrypted message for Alice:
-msg <- charToRaw("TTIP is evil")
-ciphertext <- auth_encrypt(msg, bob_key, alice_pubkey)
-
-# Alice verifies and decrypts with her key
-out <- auth_decrypt(ciphertext, alice_key, bob_pubkey)
-stopifnot(identical(out, msg))
-
-# Alice sends encrypted message for Bob
-msg <- charToRaw("Let's protest")
-ciphertext <- auth_encrypt(msg, alice_key, bob_pubkey)
-
-# Bob verifies and decrypts with his key
-out <- auth_decrypt(ciphertext, bob_key, alice_pubkey)
-stopifnot(identical(out, msg))
-

Note that even though public keys are not confidential, you should not exchange them over the same insecure channel you are trying to protect. If the connection is being tampered with, the attacker could simply replace the key with another one to hijack the interaction.

-
- - - - -
- - - - - - - - diff --git a/inst/doc/intro.rmd b/inst/doc/intro.rmd deleted file mode 100644 index 4d65f36..0000000 --- a/inst/doc/intro.rmd +++ /dev/null @@ -1,179 +0,0 @@ ---- -title: "Sodium: A Modern and Easy-to-Use Crypto Library" -output: - html_document: - toc: false -date: "`r Sys.Date()`" -vignette: > - %\VignetteIndexEntry{Introduction to Sodium for R} - %\VignetteEngine{knitr::rmarkdown} - \usepackage[utf8]{inputenc} ---- - -```{r, echo = FALSE, message = FALSE} -knitr::opts_chunk$set(comment = "") -library(sodium) -``` - -The sodium R package provides bindings to [libsodium](https://download.libsodium.org/doc/): a modern, easy-to-use software library for encryption, decryption, signatures, password hashing and more. - -The goal of Sodium is to provide the core operations needed to build higher-level cryptographic tools. It is not intended for implementing standardized protocols such as TLS, SSH or GPG. Sodium only supports a limited set of state-of-the-art elliptic curve methods, resulting in a simple but very powerful tool-kit for building secure applications. - - -| | Authenticated Encryption | Encryption only | Authentication only | -|-----------------------------:|:--------------------------------:|:-----------------------------------:|:--------------------------:| -| __Symmetric__ *(secret key)*: | [`data_encrypt` / `data_decrypt`](#secret-key-encryption) | | [`data_tag`](#secret-key-authentication) | -| __Asymmetric__ *(public+private key)*: | [`auth_encrypt` / `auth_decrypt`](#public-key-authenticated-encryption) | [`simple_encrypt` / `simple_decrypt`](#public-key-encryption) | [`sig_sign` / `sig_verify`](#public-key-authentication-signatures) | -| | | | | - -### Using Sodium - -All Sodium functions operate on binary data, called 'raw' vectors in R. Use `charToRaw` and `rawToChar` to convert between strings and raw vectors. Alternatively `hex2bin` and `bin2hex` can convert between binary data to strings in hex notation: - -```{r} -test <- hash(charToRaw("test 123")) -str <- bin2hex(test) -print(str) -hex2bin(str) -``` - -### Random data generator - -The `random()` function generates n bytes of unpredictable data, suitable for creating secret keys. - -```{r} -secret <- random(8) -print(secret) -``` - -Implementation is platform specific, see the [docs](https://download.libsodium.org/doc/generating_random_data/index.html) for details. - -### Hash functions - -Sodium has several hash functions including `hash()`, `shorthash()`, `sha256()`, `sha512` and `scrypt()`. The generic `hash()` is usually recommended. It uses [blake2b](https://download.libsodium.org/doc/hashing/generic_hashing.html) with a configurable size between 16 bytes (128bit) and 64 bytes (512bit). - -```{r} -# Generate keys from passphrase -passphrase <- charToRaw("This is super secret") -hash(passphrase) -hash(passphrase, size = 16) -hash(passphrase, size = 64) -``` - -The `shorthash()` function is a special 8 byte (64 bit) hash based on [SipHash-2-4](https://download.libsodium.org/doc/hashing/short-input_hashing.html). The output of this function is only 64 bits (8 bytes). It is useful for in e.g. Hash tables, but it should not be considered collision-resistant. - - -### Secret key encryption - -Symmetric encryption uses the same secret key for both encryption and decryption. It is mainly useful for encrypting local data, or as a building block for more complex methods. - -Most encryption methods require a `nonce`: a piece of non-secret unique data that is used to randomize the cipher. This allows for safely using the same `key` for encrypting multiple messages. The nonce should be stored or shared along with the ciphertext. - -```{r} -key <- hash(charToRaw("This is a secret passphrase")) -msg <- serialize(iris, NULL) - -# Encrypt with a random nonce -nonce <- random(24) -cipher <- data_encrypt(msg, key, nonce) - -# Decrypt with same key and nonce -orig <- data_decrypt(cipher, key, nonce) -identical(iris, unserialize(orig)) -``` - -Because the secret has to be known by all parties, symmetric encryption by itself is often impractical for communication with third parties. For this we need asymmetric (public key) methods. - -### Secret key authentication - -Secret key authentication is called tagging in Sodium. A tag is basically a hash of the data together with a secret key. - -```{r} -key <- hash(charToRaw("This is a secret passphrase")) -msg <- serialize(iris, NULL) -mytag <- data_tag(msg, key) -``` - -To verify the integrity of the data at a later point in time, simply re-calculate the tag with the same key: - -```{r} -stopifnot(identical(mytag, data_tag(msg, key))) -``` - -The secret key protects against forgery of the data+tag by an intermediate party, as would be possible with a regular checksum. - -### Public key encryption - -Where symmetric methods use the same secret key for encryption and decryption, asymmetric methods use a key-pair consisting of a public key and private key. The private key is secret and only known by its owner. The public key on the other hand can be shared with anyone. Public keys are often published on the user's website or posted in public directories or keyservers. - -```{r} -key <- keygen() -pub <- pubkey(key) -``` - -In public key encryption, data encrypted with a public key can only be decrypted using the corresponding private key. This allows anyone to send somebody a secure message by encrypting it with the receivers public key. The encrypted message will only be readable by the owner of the corresponding private key. - -```{r} -# Encrypt message with pubkey -msg <- serialize(iris, NULL) -ciphertext <- simple_encrypt(msg, pub) - -# Decrypt message with private key -out <- simple_decrypt(ciphertext, key) -stopifnot(identical(out, msg)) -``` - - -### Public key authentication (signatures) - -Public key authentication works the other way around. First, the owner of the private key creates a 'signature' (an authenticated checksum) for a message in a way that allows anyone who knows his/her public key to verify the integrity of the message and identity of the sender. - -Currently sodium requires a different type of key-pair for signatures (ed25519) than for encryption (curve25519). - -```{r} -# Generate signature keypair -key <- sig_keygen() -pubkey <- sig_pubkey(key) - -# Create signature with private key -msg <- serialize(iris, NULL) -sig <- sig_sign(msg, key) -print(sig) - -# Verify a signature from public key -sig_verify(msg, sig, pubkey) -``` - -Signatures are useful when the message itself is not confidential but integrity is important. A common use is for software repositories where to include an index file with checksums for all packages, signed by the repository maintainer. This allows client package managers to verify that the binaries were not manipulated by intermediate parties during the distribution process. - -### Public key authenticated encryption - -Authenticated encryption implements best practices for secure messaging. It requires that both sender and receiver have a keypair and know each other's public key. Each message gets authenticated with the key of the sender and encrypted with the key of the receiver. - -```{r} -# Bob's keypair: -bob_key <- keygen() -bob_pubkey <- pubkey(bob_key) - -# Alice's keypair: -alice_key <- keygen() -alice_pubkey <- pubkey(alice_key) - -# Bob sends encrypted message for Alice: -msg <- charToRaw("TTIP is evil") -ciphertext <- auth_encrypt(msg, bob_key, alice_pubkey) - -# Alice verifies and decrypts with her key -out <- auth_decrypt(ciphertext, alice_key, bob_pubkey) -stopifnot(identical(out, msg)) - -# Alice sends encrypted message for Bob -msg <- charToRaw("Let's protest") -ciphertext <- auth_encrypt(msg, alice_key, bob_pubkey) - -# Bob verifies and decrypts with his key -out <- auth_decrypt(ciphertext, bob_key, alice_pubkey) -stopifnot(identical(out, msg)) -``` - -Note that even though public keys are not confidential, you should not exchange them over the same insecure channel you are trying to protect. If the connection is being tampered with, the attacker could simply replace the key with another one to hijack the interaction.