├── .github ├── .gitignore └── workflows │ └── R-CMD-check.yaml ├── LICENSE ├── src ├── Makevars.in ├── xor.c ├── init.c ├── diffie.c ├── Makevars.win ├── keygen.c ├── password.c ├── seal.c ├── helpers.c ├── messaging.c ├── signing.c ├── secret.c ├── stream.c └── hashing.c ├── cleanup ├── .gitignore ├── .Rbuildignore ├── sodium.Rproj ├── NEWS ├── revdep ├── problems.md └── README.md ├── DESCRIPTION ├── tools └── winlibs.R ├── man ├── password.Rd ├── helpers.Rd ├── simple.Rd ├── sig.Rd ├── keygen.Rd ├── symmetric.Rd ├── messaging.Rd ├── diffie.Rd ├── stream.Rd └── hash.Rd ├── R ├── password.R ├── simple-encrypt.R ├── utilities.R ├── keygen.R ├── signatures.R ├── diffie-hellman.R ├── auth-encrypt.R ├── data-encrypt.R ├── stream.R └── hashing.R ├── NAMESPACE ├── README.md ├── configure └── vignettes ├── intro.rmd └── crypto101.rmd /.github/.gitignore: -------------------------------------------------------------------------------- 1 | *.html 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | YEAR: 2017 2 | COPYRIGHT HOLDER: Jeroen Ooms 3 | -------------------------------------------------------------------------------- /src/Makevars.in: -------------------------------------------------------------------------------- 1 | PKG_CPPFLAGS=@cflags@ 2 | PKG_LIBS=@libs@ 3 | -------------------------------------------------------------------------------- /cleanup: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | rm -f src/Makevars autobrew configure.log 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .Rproj.user 2 | .Rhistory 3 | .RData 4 | src/*.o 5 | src/*.so 6 | src/*.dll 7 | src/Makevars 8 | revdep/*.rds 9 | windows 10 | configure.log 11 | -------------------------------------------------------------------------------- /.Rbuildignore: -------------------------------------------------------------------------------- 1 | ^.*\.Rproj$ 2 | ^\.Rproj\.user$ 3 | ^src/Makevars$ 4 | ^windows 5 | ^\.travis\.yml$ 6 | ^appveyor\.yml$ 7 | ^revdep 8 | ^README.md$ 9 | configure.log 10 | ^\.github$ 11 | -------------------------------------------------------------------------------- /src/xor.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | SEXP R_xor(SEXP x, SEXP y){ 4 | if(LENGTH(x) != LENGTH(y)) 5 | Rf_error("x and y have different lengths"); 6 | SEXP z = allocVector(RAWSXP, LENGTH(x)); 7 | for(int i = 0; i < LENGTH(x); i++){ 8 | RAW(z)[i] = RAW(x)[i] ^ RAW(y)[i]; 9 | } 10 | return z; 11 | } 12 | -------------------------------------------------------------------------------- /src/init.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | void R_init_sodium(DllInfo *info) { 6 | if (sodium_init() < 0) 7 | Rf_error("Failed to initialize libsodium."); 8 | R_registerRoutines(info, NULL, NULL, NULL, NULL); 9 | R_useDynamicSymbols(info, TRUE); 10 | } 11 | 12 | void R_unload_sodium(DllInfo *info) { 13 | 14 | } 15 | -------------------------------------------------------------------------------- /sodium.Rproj: -------------------------------------------------------------------------------- 1 | Version: 1.0 2 | 3 | RestoreWorkspace: Default 4 | SaveWorkspace: Default 5 | AlwaysSaveHistory: Default 6 | 7 | EnableCodeIndexing: Yes 8 | UseSpacesForTab: Yes 9 | NumSpacesForTab: 2 10 | Encoding: UTF-8 11 | 12 | RnwWeave: Sweave 13 | LaTeX: pdfLaTeX 14 | 15 | AutoAppendNewline: Yes 16 | StripTrailingWhitespace: Yes 17 | 18 | BuildType: Package 19 | PackageUseDevtools: Yes 20 | PackageInstallArgs: --no-multiarch --with-keep.source 21 | PackageRoxygenize: rd,collate,namespace 22 | -------------------------------------------------------------------------------- /src/diffie.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | SEXP R_diffie_hellman(SEXP key, SEXP pubkey){ 5 | if(LENGTH(key) != crypto_scalarmult_SCALARBYTES) 6 | Rf_error("Invalid key, must be exactly %d bytes", crypto_scalarmult_SCALARBYTES); 7 | if(LENGTH(pubkey) != crypto_scalarmult_BYTES) 8 | Rf_error("Invalid pubkey, must be exactly %d bytes", crypto_scalarmult_BYTES); 9 | SEXP res = allocVector(RAWSXP, crypto_scalarmult_BYTES); 10 | if(crypto_scalarmult(RAW(res), RAW(key), RAW(pubkey))) 11 | Rf_error("Failed crypto_scalarmult"); 12 | return res; 13 | } 14 | -------------------------------------------------------------------------------- /NEWS: -------------------------------------------------------------------------------- 1 | 1.4.0 2 | - Windows: use libsodium from Rtools if available 3 | 4 | 1.3.1 5 | - Fix shell script error for cross compiling 6 | 7 | 1.3.0 8 | - Windows: support arm 9 | 10 | 1.2.0 11 | - Fix crash with bin2hex() for very large input vectors 12 | 13 | 1.1 14 | - Fix autobrew for OSX Mavericks 15 | 16 | 1.0 17 | - Remove the deprecated aes128() 18 | - Windows: update libsodium to 1.0.12 19 | - OSX: autobrew script moved to separate repo 20 | 21 | 0.4 22 | - Update Homebrew URL in configure script 23 | - Workaround for random WARNING in vignette for null strings 24 | - Add Travis and README file 25 | 26 | 0.2 27 | - Add 2 vignettes 28 | -------------------------------------------------------------------------------- /src/Makevars.win: -------------------------------------------------------------------------------- 1 | PKG_CONFIG ?= $(BINPREF)pkg-config 2 | PKG_CONFIG_NAME = libsodium 3 | PKG_LIBS := $(shell $(PKG_CONFIG) --libs $(PKG_CONFIG_NAME)) 4 | 5 | ifneq ($(PKG_LIBS),) 6 | $(info using $(PKG_CONFIG_NAME) from Rtools) 7 | PKG_CPPFLAGS := $(shell $(PKG_CONFIG) --cflags $(PKG_CONFIG_NAME)) 8 | else 9 | RWINLIB = ../windows/sodium 10 | PKG_CPPFLAGS = -I$(RWINLIB)/include 11 | PKG_LIBS = -L$(RWINLIB)/lib$(R_ARCH) -L${RWINLIB}/lib -lsodium 12 | endif 13 | 14 | all: $(SHLIB) 15 | 16 | $(OBJECTS): $(RWINLIB) 17 | 18 | $(RWINLIB): 19 | "${R_HOME}/bin${R_ARCH_BIN}/Rscript.exe" "../tools/winlibs.R" 20 | 21 | clean: 22 | rm -f $(SHLIB) $(OBJECTS) 23 | -------------------------------------------------------------------------------- /revdep/problems.md: -------------------------------------------------------------------------------- 1 | # Setup 2 | 3 | ## Platform 4 | 5 | |setting |value | 6 | |:--------|:----------------------------| 7 | |version |R version 3.3.1 (2016-06-21) | 8 | |system |x86_64, darwin13.4.0 | 9 | |ui |RStudio (0.99.875) | 10 | |language |(EN) | 11 | |collate |en_US.UTF-8 | 12 | |tz |Europe/Amsterdam | 13 | |date |2016-07-06 | 14 | 15 | ## Packages 16 | 17 | |package |* |version |date |source | 18 | |:-------|:--|:-------|:----------|:----------------| 19 | |sodium |* |0.3 |2016-07-06 |local (NA/NA@NA) | 20 | 21 | # Check results 22 | 0 packages with problems 23 | 24 | 25 | -------------------------------------------------------------------------------- /src/keygen.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | SEXP R_keygen(SEXP seed){ 5 | if(LENGTH(seed) != crypto_box_SEEDBYTES) 6 | Rf_error("Invalid seed, must be exactly %d bytes", crypto_box_SEEDBYTES); 7 | unsigned char pubkey[crypto_box_PUBLICKEYBYTES]; 8 | SEXP res = allocVector(RAWSXP, crypto_box_SECRETKEYBYTES); 9 | crypto_box_seed_keypair(pubkey, RAW(res), RAW(seed)); 10 | return res; 11 | } 12 | 13 | SEXP R_pubkey(SEXP key){ 14 | if(LENGTH(key) != crypto_scalarmult_SCALARBYTES) 15 | Rf_error("Invalid key, must be exactly %d bytes", crypto_scalarmult_SCALARBYTES); 16 | SEXP res = allocVector(RAWSXP, crypto_scalarmult_BYTES); 17 | if(crypto_scalarmult_base(RAW(res), RAW(key))) 18 | Rf_error("Failed crypto_scalarmult_base"); 19 | return res; 20 | } 21 | -------------------------------------------------------------------------------- /src/password.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | SEXP R_password_hash(SEXP password){ 5 | char out[crypto_pwhash_scryptsalsa208sha256_STRBYTES]; 6 | if(crypto_pwhash_scryptsalsa208sha256_str(out, CHAR(STRING_ELT(password, 0)), LENGTH(STRING_ELT(password, 0)), 7 | crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_INTERACTIVE, crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_INTERACTIVE)) 8 | Rf_error("pwhash failed"); 9 | return mkString(out); 10 | } 11 | 12 | SEXP R_password_verify(SEXP hash, SEXP password){ 13 | if(LENGTH(STRING_ELT(hash, 0)) != crypto_pwhash_scryptsalsa208sha256_STRBYTES - 1) 14 | Rf_error("Invalid hash, must be exactly %d characters", crypto_pwhash_scryptsalsa208sha256_STRBYTES - 1); 15 | int res = crypto_pwhash_scryptsalsa208sha256_str_verify(CHAR(STRING_ELT(hash, 0)), 16 | CHAR(STRING_ELT(password, 0)), LENGTH(STRING_ELT(password, 0))); 17 | return ScalarLogical(res == 0); 18 | } 19 | -------------------------------------------------------------------------------- /DESCRIPTION: -------------------------------------------------------------------------------- 1 | Package: sodium 2 | Type: Package 3 | Title: A Modern and Easy-to-Use Crypto Library 4 | Version: 1.4.0 5 | Authors@R: person("Jeroen", "Ooms", role = c("aut", "cre"), email = "jeroenooms@gmail.com", 6 | comment = c(ORCID = "0000-0002-4035-0289")) 7 | Description: Bindings to 'libsodium' : a modern, 8 | easy-to-use software library for encryption, decryption, signatures, password 9 | hashing and more. Sodium uses curve25519, a state-of-the-art Diffie-Hellman 10 | function by Daniel Bernstein, which has become very popular after it was 11 | discovered that the NSA had backdoored Dual EC DRBG. 12 | License: MIT + file LICENSE 13 | URL: https://docs.ropensci.org/sodium/ 14 | https://github.com/r-lib/sodium 15 | BugReports: https://github.com/r-lib/sodium/issues 16 | SystemRequirements: libsodium (>= 1.0.3) 17 | VignetteBuilder: knitr 18 | Suggests: 19 | knitr, 20 | rmarkdown 21 | RoxygenNote: 7.2.3 22 | -------------------------------------------------------------------------------- /tools/winlibs.R: -------------------------------------------------------------------------------- 1 | if(!file.exists("../windows/sodium/include/sodium.h")){ 2 | unlink("../windows", recursive = TRUE) 3 | url <- if(grepl("aarch", R.version$platform)){ 4 | "https://github.com/r-windows/bundles/releases/download/libsodium-1.0.18/libsodium-1.0.18-clang-aarch64.tar.xz" 5 | } else if(grepl("clang", Sys.getenv('R_COMPILED_BY'))){ 6 | "https://github.com/r-windows/bundles/releases/download/libsodium-1.0.18/libsodium-1.0.18-clang-x86_64.tar.xz" 7 | } else if(getRversion() >= "4.2") { 8 | "https://github.com/r-windows/bundles/releases/download/libsodium-1.0.18/libsodium-1.0.18-ucrt-x86_64.tar.xz" 9 | } else { 10 | "https://github.com/rwinlib/sodium/archive/v1.0.12.tar.gz" 11 | } 12 | download.file(url, basename(url), quiet = TRUE) 13 | dir.create("../windows", showWarnings = FALSE) 14 | untar(basename(url), exdir = "../windows", tar = 'internal') 15 | unlink(basename(url)) 16 | setwd("../windows") 17 | file.rename(list.files(), 'sodium') 18 | } 19 | -------------------------------------------------------------------------------- /src/seal.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | SEXP R_seal_box(SEXP msg, SEXP pubkey){ 5 | if(LENGTH(pubkey) != crypto_box_PUBLICKEYBYTES) 6 | Rf_error("Invalid pubkey, must be exactly %d bytes", crypto_box_PUBLICKEYBYTES); 7 | R_xlen_t mlen = XLENGTH(msg); 8 | R_xlen_t clen = mlen + crypto_box_SEALBYTES; 9 | SEXP res = allocVector(RAWSXP, clen); 10 | if(crypto_box_seal(RAW(res), RAW(msg), mlen, RAW(pubkey))) 11 | Rf_error("Failed to encrypt"); 12 | return res; 13 | } 14 | 15 | SEXP R_seal_open(SEXP cipher, SEXP key){ 16 | if(LENGTH(key) != crypto_box_SECRETKEYBYTES) 17 | Rf_error("Invalid key, must be exactly %d bytes", crypto_box_SECRETKEYBYTES); 18 | R_xlen_t clen = XLENGTH(cipher); 19 | R_xlen_t mlen = clen - crypto_box_SEALBYTES; 20 | SEXP res = allocVector(RAWSXP, mlen); 21 | unsigned char pk[crypto_box_PUBLICKEYBYTES]; 22 | crypto_scalarmult_base(pk, RAW(key)); 23 | if(crypto_box_seal_open(RAW(res), RAW(cipher), clen, pk, RAW(key))) 24 | Rf_error("Failed to decrypt"); 25 | return res; 26 | } 27 | -------------------------------------------------------------------------------- /revdep/README.md: -------------------------------------------------------------------------------- 1 | # Setup 2 | 3 | ## Platform 4 | 5 | |setting |value | 6 | |:--------|:----------------------------| 7 | |version |R version 3.3.1 (2016-06-21) | 8 | |system |x86_64, darwin13.4.0 | 9 | |ui |RStudio (0.99.875) | 10 | |language |(EN) | 11 | |collate |en_US.UTF-8 | 12 | |tz |Europe/Amsterdam | 13 | |date |2016-07-06 | 14 | 15 | ## Packages 16 | 17 | |package |* |version |date |source | 18 | |:-------|:--|:-------|:----------|:----------------| 19 | |sodium |* |0.3 |2016-07-06 |local (NA/NA@NA) | 20 | 21 | # Check results 22 | 2 packages 23 | 24 | ## homomorpheR (0.1-1) 25 | Maintainer: Balasubramanian Narasimhan 26 | Bug reports: http://github.com/bnaras/homomorpheR/issues 27 | 28 | 0 errors | 0 warnings | 0 notes 29 | 30 | ## remoter (0.3-2) 31 | Maintainer: Drew Schmidt 32 | Bug reports: https://github.com/RBigData/remoter/issues 33 | 34 | 0 errors | 0 warnings | 0 notes 35 | 36 | -------------------------------------------------------------------------------- /src/helpers.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | SEXP R_sodium_bin2hex(SEXP bin){ 6 | size_t bin_len = LENGTH(bin); 7 | size_t hex_len = bin_len * 2 + 1; 8 | char *hex = malloc(hex_len); 9 | if(NULL == sodium_bin2hex(hex, hex_len, RAW(bin), bin_len)){ 10 | free(hex); 11 | Rf_error("Overflow error, failed to convert to hex"); 12 | } 13 | SEXP res = Rf_mkString(hex); 14 | free(hex); 15 | return res; 16 | } 17 | 18 | SEXP R_sodium_hex2bin(SEXP hex, SEXP ignore){ 19 | int hex_len = LENGTH(STRING_ELT(hex, 0)); 20 | int max_len = hex_len / 2; 21 | unsigned char *bin = malloc(max_len); 22 | size_t bin_len; 23 | const char * hex_end; 24 | if(sodium_hex2bin(bin, max_len, CHAR(STRING_ELT(hex, 0)), hex_len, CHAR(STRING_ELT(ignore, 0)), &bin_len, &hex_end)){ 25 | free(bin); 26 | Rf_error("Overflow error, failed to parse hex."); 27 | } 28 | SEXP res = allocVector(RAWSXP, bin_len); 29 | memcpy(RAW(res), bin, bin_len); 30 | free(bin); 31 | return res; 32 | } 33 | 34 | SEXP R_randombytes_buf(SEXP length){ 35 | size_t size = asInteger(length); 36 | SEXP res = allocVector(RAWSXP, size); 37 | randombytes_buf(RAW(res), size); 38 | return res; 39 | } 40 | 41 | -------------------------------------------------------------------------------- /man/password.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/password.R 3 | \name{Password storage} 4 | \alias{Password storage} 5 | \alias{password_store} 6 | \alias{password} 7 | \alias{password_verify} 8 | \title{Password Storage} 9 | \usage{ 10 | password_store(password) 11 | 12 | password_verify(hash, password) 13 | } 14 | \arguments{ 15 | \item{password}{a string of length one with a password} 16 | 17 | \item{hash}{a hash string of length one generated by \code{password_store}} 18 | } 19 | \description{ 20 | Wrapper that implements best practices for storing passwords based on scrypt with 21 | a random salt. 22 | } 23 | \details{ 24 | The \link{password_store} function returns an ASCII encoded string which contains 25 | the result of a memory-hard, CPU-intensive hash function along with the automatically 26 | generated salt and other parameters required to verify the password. Use 27 | \link{password_verify} to verify a password from this string. 28 | } 29 | \examples{ 30 | # Example password 31 | password <- "I like cookies" 32 | 33 | # Hash is what you store in the database 34 | hash <- password_store(password) 35 | 36 | # To verify the password when the user logs in 37 | stopifnot(password_verify(hash, password)) 38 | } 39 | \references{ 40 | \url{https://doc.libsodium.org/password_hashing/} 41 | } 42 | -------------------------------------------------------------------------------- /man/helpers.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/utilities.R 3 | \name{Sodium utilities} 4 | \alias{Sodium utilities} 5 | \alias{bin2hex} 6 | \alias{helpers} 7 | \alias{hex2bin} 8 | \alias{random} 9 | \title{Sodium Utilities} 10 | \usage{ 11 | bin2hex(bin) 12 | 13 | hex2bin(hex, ignore = ":") 14 | 15 | random(n = 1) 16 | } 17 | \arguments{ 18 | \item{bin}{raw vector with binary data to convert to hex string} 19 | 20 | \item{hex}{a string with hexadecimal characters to parse into a binary (raw) vector.} 21 | 22 | \item{ignore}{a string with characters to ignore from \code{hex}. See example.} 23 | 24 | \item{n}{number of random bytes or numbers to generate} 25 | } 26 | \description{ 27 | The functions \code{bin2hex} and \code{hex2bin} convert between binary (raw) 28 | vectors and corresponding string in hexadecimal notation. The \code{random} 29 | function generates \code{n} crypto secure random bytes. 30 | } 31 | \examples{ 32 | # Convert raw to hex string and back 33 | test <- charToRaw("test 123") 34 | x <- bin2hex(test) 35 | y <- hex2bin(x) 36 | stopifnot(identical(test, y)) 37 | stopifnot(identical(x, paste(test, collapse = ""))) 38 | 39 | # Parse text with characters 40 | x2 <- paste(test, collapse = ":") 41 | y2 <- hex2bin(x2, ignore = ":") 42 | stopifnot(identical(test, y2)) 43 | } 44 | \concept{sodium} 45 | -------------------------------------------------------------------------------- /R/password.R: -------------------------------------------------------------------------------- 1 | #' Password Storage 2 | #' 3 | #' Wrapper that implements best practices for storing passwords based on scrypt with 4 | #' a random salt. 5 | #' 6 | #' The \link{password_store} function returns an ASCII encoded string which contains 7 | #' the result of a memory-hard, CPU-intensive hash function along with the automatically 8 | #' generated salt and other parameters required to verify the password. Use 9 | #' \link{password_verify} to verify a password from this string. 10 | #' 11 | #' @export 12 | #' @rdname password 13 | #' @name Password storage 14 | #' @aliases password 15 | #' @param password a string of length one with a password 16 | #' @param hash a hash string of length one generated by \code{password_store} 17 | #' @useDynLib sodium R_password_hash 18 | #' @references \url{https://doc.libsodium.org/password_hashing/} 19 | #' @examples # Example password 20 | #' password <- "I like cookies" 21 | #' 22 | #' # Hash is what you store in the database 23 | #' hash <- password_store(password) 24 | #' 25 | #' # To verify the password when the user logs in 26 | #' stopifnot(password_verify(hash, password)) 27 | password_store <- function(password){ 28 | .Call(R_password_hash, password) 29 | } 30 | 31 | #' @export 32 | #' @rdname password 33 | #' @useDynLib sodium R_password_verify 34 | password_verify <- function(hash, password){ 35 | .Call(R_password_verify, hash, password) 36 | } 37 | -------------------------------------------------------------------------------- /man/simple.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/simple-encrypt.R 3 | \name{Simple encryption} 4 | \alias{Simple encryption} 5 | \alias{simple_encrypt} 6 | \alias{simple_decrypt} 7 | \title{Anonymous Public-key Encryption (Sealed Box)} 8 | \usage{ 9 | simple_encrypt(msg, pubkey) 10 | 11 | simple_decrypt(bin, key) 12 | } 13 | \arguments{ 14 | \item{msg}{message to be encrypted} 15 | 16 | \item{pubkey}{public key of the receiver} 17 | 18 | \item{bin}{encrypted ciphertext} 19 | 20 | \item{key}{private key of the receiver} 21 | } 22 | \description{ 23 | Create an encrypted message (sealed box) from a curve25519 public key. 24 | } 25 | \details{ 26 | Simple public key encryption allows for sending anonymous encrypted messages to 27 | a recipient given its public key. Only the recipient can decrypt these messages, 28 | using its private key. 29 | 30 | While the recipient can verify the integrity of the message, it cannot verify the 31 | identity of the sender. For sending authenticated encrypted messages, use 32 | \link{auth_encrypt} and \link{auth_decrypt}. 33 | } 34 | \examples{ 35 | # Generate keypair 36 | key <- keygen() 37 | pub <- pubkey(key) 38 | 39 | # Encrypt message with pubkey 40 | msg <- serialize(iris, NULL) 41 | ciphertext <- simple_encrypt(msg, pub) 42 | 43 | # Decrypt message with private key 44 | out <- simple_decrypt(ciphertext, key) 45 | stopifnot(identical(out, msg)) 46 | } 47 | \references{ 48 | \url{https://doc.libsodium.org/public-key_cryptography/sealed_boxes.html} 49 | } 50 | -------------------------------------------------------------------------------- /src/messaging.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | SEXP R_secure_send(SEXP msg, SEXP key, SEXP pubkey, SEXP nonce) { 5 | if(LENGTH(key) != crypto_box_SECRETKEYBYTES) 6 | Rf_error("Invalid key, must be exactly %d bytes", crypto_box_SECRETKEYBYTES); 7 | if(LENGTH(pubkey) != crypto_box_PUBLICKEYBYTES) 8 | Rf_error("Invalid pubkey, must be exactly %d bytes", crypto_box_PUBLICKEYBYTES); 9 | if(LENGTH(nonce) != crypto_box_NONCEBYTES) 10 | Rf_error("Invalid nonce, must be exactly %d bytes", crypto_box_NONCEBYTES); 11 | R_xlen_t mlen = XLENGTH(msg); 12 | R_xlen_t clen = mlen + crypto_box_MACBYTES; 13 | SEXP res = allocVector(RAWSXP, clen); 14 | if(crypto_box_easy(RAW(res), RAW(msg), XLENGTH(msg), RAW(nonce), RAW(pubkey), RAW(key))) 15 | Rf_error("Authenticated encryption failed"); 16 | return res; 17 | } 18 | 19 | SEXP R_secure_recv(SEXP cipher, SEXP key, SEXP pubkey, SEXP nonce){ 20 | if(LENGTH(key) != crypto_box_SECRETKEYBYTES) 21 | Rf_error("Invalid key, must be exactly %d bytes", crypto_box_SECRETKEYBYTES); 22 | if(LENGTH(pubkey) != crypto_box_PUBLICKEYBYTES) 23 | Rf_error("Invalid pubkey, must be exactly %d bytes", crypto_box_PUBLICKEYBYTES); 24 | if(LENGTH(nonce) != crypto_box_NONCEBYTES) 25 | Rf_error("Invalid nonce, must be exactly %d bytes", crypto_box_NONCEBYTES); 26 | R_xlen_t clen = XLENGTH(cipher); 27 | R_xlen_t mlen = clen - crypto_box_MACBYTES; 28 | SEXP res = allocVector(RAWSXP, mlen); 29 | if(crypto_box_open_easy(RAW(res), RAW(cipher), XLENGTH(cipher), RAW(nonce), RAW(pubkey), RAW(key))) 30 | Rf_error("Authenticated decryption failed"); 31 | return res; 32 | } 33 | -------------------------------------------------------------------------------- /R/simple-encrypt.R: -------------------------------------------------------------------------------- 1 | #' Anonymous Public-key Encryption (Sealed Box) 2 | #' 3 | #' Create an encrypted message (sealed box) from a curve25519 public key. 4 | #' 5 | #' Simple public key encryption allows for sending anonymous encrypted messages to 6 | #' a recipient given its public key. Only the recipient can decrypt these messages, 7 | #' using its private key. 8 | #' 9 | #' While the recipient can verify the integrity of the message, it cannot verify the 10 | #' identity of the sender. For sending authenticated encrypted messages, use 11 | #' \link{auth_encrypt} and \link{auth_decrypt}. 12 | #' 13 | #' @export 14 | #' @rdname simple 15 | #' @name Simple encryption 16 | #' @useDynLib sodium R_seal_box 17 | #' @references \url{https://doc.libsodium.org/public-key_cryptography/sealed_boxes.html} 18 | #' @param msg message to be encrypted 19 | #' @param key private key of the receiver 20 | #' @param pubkey public key of the receiver 21 | #' @param bin encrypted ciphertext 22 | #' @examples # Generate keypair 23 | #' key <- keygen() 24 | #' pub <- pubkey(key) 25 | #' 26 | #' # Encrypt message with pubkey 27 | #' msg <- serialize(iris, NULL) 28 | #' ciphertext <- simple_encrypt(msg, pub) 29 | #' 30 | #' # Decrypt message with private key 31 | #' out <- simple_decrypt(ciphertext, key) 32 | #' stopifnot(identical(out, msg)) 33 | simple_encrypt <- function(msg, pubkey){ 34 | stopifnot(is.raw(msg)) 35 | stopifnot(is.raw(pubkey)) 36 | .Call(R_seal_box, msg, pubkey) 37 | } 38 | 39 | #' @export 40 | #' @rdname simple 41 | #' @useDynLib sodium R_seal_open 42 | simple_decrypt <- function(bin, key){ 43 | stopifnot(is.raw(bin)) 44 | stopifnot(is.raw(key)) 45 | .Call(R_seal_open, bin, key) 46 | } 47 | -------------------------------------------------------------------------------- /src/signing.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | SEXP R_sig_keygen(SEXP seed){ 5 | if(LENGTH(seed) != crypto_sign_SEEDBYTES) 6 | Rf_error("Invalid seed, must be exactly %d bytes", crypto_sign_SEEDBYTES); 7 | SEXP res = allocVector(RAWSXP, crypto_sign_SECRETKEYBYTES); 8 | unsigned char pk[crypto_sign_PUBLICKEYBYTES]; 9 | if(crypto_sign_seed_keypair(pk, RAW(res), RAW(seed))) 10 | Rf_error("keygen failed"); 11 | return res; 12 | } 13 | 14 | SEXP R_sig_pubkey(SEXP key){ 15 | if(LENGTH(key) != crypto_sign_SECRETKEYBYTES) 16 | Rf_error("Invalid key: must be exactly %d bytes", crypto_sign_SECRETKEYBYTES); 17 | SEXP res = allocVector(RAWSXP, crypto_sign_PUBLICKEYBYTES); 18 | if(crypto_sign_ed25519_sk_to_pk(RAW(res), RAW(key))) 19 | Rf_error("conversion failed"); 20 | return res; 21 | } 22 | 23 | SEXP R_sig_sign(SEXP msg, SEXP key){ 24 | if(LENGTH(key) != crypto_sign_SECRETKEYBYTES) 25 | Rf_error("Invalid key: must be exactly %d bytes", crypto_sign_SECRETKEYBYTES); 26 | SEXP res = allocVector(RAWSXP, crypto_sign_BYTES); 27 | if(crypto_sign_detached(RAW(res), NULL, RAW(msg), XLENGTH(msg), RAW(key))) 28 | Rf_error("Failed to create signature"); 29 | return res; 30 | } 31 | 32 | SEXP R_sig_verify(SEXP msg, SEXP sig, SEXP pubkey){ 33 | if(LENGTH(pubkey) != crypto_sign_PUBLICKEYBYTES) 34 | Rf_error("Invalid pubkey: must be exactly %d bytes", crypto_sign_PUBLICKEYBYTES); 35 | if(LENGTH(sig) != crypto_sign_BYTES) 36 | Rf_error("Invalid sig: must be exactly %d bytes", crypto_sign_BYTES); 37 | if(crypto_sign_verify_detached(RAW(sig), RAW(msg), XLENGTH(msg), RAW(pubkey))) 38 | Rf_error("Signature verification failed"); 39 | return ScalarLogical(TRUE); 40 | } 41 | -------------------------------------------------------------------------------- /.github/workflows/R-CMD-check.yaml: -------------------------------------------------------------------------------- 1 | # Workflow derived from https://github.com/r-lib/actions/tree/v2/examples 2 | # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help 3 | on: 4 | push: 5 | pull_request: 6 | 7 | name: R-CMD-check.yaml 8 | 9 | permissions: read-all 10 | 11 | jobs: 12 | R-CMD-check: 13 | runs-on: ${{ matrix.config.os }} 14 | 15 | name: ${{ matrix.config.os }} (${{ matrix.config.r }}) 16 | 17 | strategy: 18 | fail-fast: false 19 | matrix: 20 | config: 21 | - {os: macos-13, r: 'release'} 22 | - {os: macos-14, r: 'release'} 23 | - {os: windows-latest, r: '4.1'} 24 | - {os: windows-latest, r: '4.2'} 25 | - {os: windows-latest, r: '4.3'} 26 | - {os: windows-latest, r: '4.4'} 27 | - {os: windows-latest, r: '4.5'} 28 | - {os: windows-latest, r: 'devel'} 29 | - {os: ubuntu-latest, r: 'devel', http-user-agent: 'release'} 30 | - {os: ubuntu-latest, r: 'release'} 31 | - {os: ubuntu-latest, r: 'oldrel-1'} 32 | 33 | env: 34 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 35 | R_KEEP_PKG_SOURCE: yes 36 | 37 | steps: 38 | - uses: actions/checkout@v4 39 | 40 | - uses: r-lib/actions/setup-pandoc@v2 41 | 42 | - uses: r-lib/actions/setup-r@v2 43 | with: 44 | r-version: ${{ matrix.config.r }} 45 | http-user-agent: ${{ matrix.config.http-user-agent }} 46 | use-public-rspm: true 47 | 48 | - uses: r-lib/actions/setup-r-dependencies@v2 49 | with: 50 | extra-packages: any::rcmdcheck 51 | needs: check 52 | 53 | - uses: r-lib/actions/check-r-package@v2 54 | env: 55 | PKG_BUILD_IGNORE_PKG_CONFIG_PATH: true 56 | -------------------------------------------------------------------------------- /R/utilities.R: -------------------------------------------------------------------------------- 1 | #' Sodium Utilities 2 | #' 3 | #' The functions \code{bin2hex} and \code{hex2bin} convert between binary (raw) 4 | #' vectors and corresponding string in hexadecimal notation. The \code{random} 5 | #' function generates \code{n} crypto secure random bytes. 6 | #' 7 | #' @useDynLib sodium R_sodium_bin2hex 8 | #' @export 9 | #' @rdname helpers 10 | #' @name Sodium utilities 11 | #' @aliases helpers 12 | #' @family sodium 13 | #' @param bin raw vector with binary data to convert to hex string 14 | #' @examples # Convert raw to hex string and back 15 | #' test <- charToRaw("test 123") 16 | #' x <- bin2hex(test) 17 | #' y <- hex2bin(x) 18 | #' stopifnot(identical(test, y)) 19 | #' stopifnot(identical(x, paste(test, collapse = ""))) 20 | #' 21 | #' # Parse text with characters 22 | #' x2 <- paste(test, collapse = ":") 23 | #' y2 <- hex2bin(x2, ignore = ":") 24 | #' stopifnot(identical(test, y2)) 25 | bin2hex <- function(bin){ 26 | stopifnot(is.raw(bin)) 27 | .Call(R_sodium_bin2hex, bin) 28 | } 29 | 30 | #' @useDynLib sodium R_sodium_hex2bin 31 | #' @export 32 | #' @rdname helpers 33 | #' @param hex a string with hexadecimal characters to parse into a binary (raw) vector. 34 | #' @param ignore a string with characters to ignore from \code{hex}. See example. 35 | hex2bin <- function(hex, ignore = ":"){ 36 | stopifnot(is.character(hex)) 37 | stopifnot(length(hex) == 1) 38 | stopifnot(is.character(ignore)) 39 | stopifnot(length(ignore) == 1) 40 | .Call(R_sodium_hex2bin, hex, ignore) 41 | } 42 | 43 | #' @useDynLib sodium R_randombytes_buf 44 | #' @export 45 | #' @rdname helpers 46 | #' @param n number of random bytes or numbers to generate 47 | random <- function(n = 1){ 48 | stopifnot(is.numeric(n)) 49 | .Call(R_randombytes_buf, as.integer(n)) 50 | } 51 | 52 | #' @useDynLib sodium R_xor 53 | xor <- function(x, y){ 54 | stopifnot(is.raw(x)) 55 | stopifnot(is.raw(y)) 56 | .Call(R_xor, x, y) 57 | } 58 | -------------------------------------------------------------------------------- /NAMESPACE: -------------------------------------------------------------------------------- 1 | # Generated by roxygen2: do not edit by hand 2 | 3 | export(argon2) 4 | export(auth_decrypt) 5 | export(auth_encrypt) 6 | export(bin2hex) 7 | export(chacha20) 8 | export(data_decrypt) 9 | export(data_encrypt) 10 | export(data_tag) 11 | export(diffie_hellman) 12 | export(hash) 13 | export(hex2bin) 14 | export(keygen) 15 | export(password_store) 16 | export(password_verify) 17 | export(pubkey) 18 | export(random) 19 | export(salsa20) 20 | export(scrypt) 21 | export(sha256) 22 | export(sha512) 23 | export(shorthash) 24 | export(sig_keygen) 25 | export(sig_pubkey) 26 | export(sig_sign) 27 | export(sig_verify) 28 | export(simple_decrypt) 29 | export(simple_encrypt) 30 | export(xchacha20) 31 | export(xsalsa20) 32 | useDynLib(sodium,R_auth_sha256) 33 | useDynLib(sodium,R_auth_sha512) 34 | useDynLib(sodium,R_crypto_generichash) 35 | useDynLib(sodium,R_crypto_secret_auth) 36 | useDynLib(sodium,R_crypto_secret_decrypt) 37 | useDynLib(sodium,R_crypto_secret_encrypt) 38 | useDynLib(sodium,R_crypto_secret_verify) 39 | useDynLib(sodium,R_crypto_shorthash) 40 | useDynLib(sodium,R_diffie_hellman) 41 | useDynLib(sodium,R_keygen) 42 | useDynLib(sodium,R_password_hash) 43 | useDynLib(sodium,R_password_verify) 44 | useDynLib(sodium,R_pubkey) 45 | useDynLib(sodium,R_pwhash) 46 | useDynLib(sodium,R_pwhash_argon2) 47 | useDynLib(sodium,R_randombytes_buf) 48 | useDynLib(sodium,R_seal_box) 49 | useDynLib(sodium,R_seal_open) 50 | useDynLib(sodium,R_secure_recv) 51 | useDynLib(sodium,R_secure_send) 52 | useDynLib(sodium,R_sha256) 53 | useDynLib(sodium,R_sha512) 54 | useDynLib(sodium,R_sig_keygen) 55 | useDynLib(sodium,R_sig_pubkey) 56 | useDynLib(sodium,R_sig_sign) 57 | useDynLib(sodium,R_sig_verify) 58 | useDynLib(sodium,R_sodium_bin2hex) 59 | useDynLib(sodium,R_sodium_hex2bin) 60 | useDynLib(sodium,R_stream_chacha20) 61 | useDynLib(sodium,R_stream_salsa20) 62 | useDynLib(sodium,R_stream_xchacha20) 63 | useDynLib(sodium,R_stream_xsalsa20) 64 | useDynLib(sodium,R_xor) 65 | -------------------------------------------------------------------------------- /man/sig.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/signatures.R 3 | \name{Signatures} 4 | \alias{Signatures} 5 | \alias{sig_sign} 6 | \alias{sig} 7 | \alias{sig_verify} 8 | \alias{sig_keygen} 9 | \alias{sig_pubkey} 10 | \title{Create and Verify Signatures} 11 | \usage{ 12 | sig_sign(msg, key) 13 | 14 | sig_verify(msg, sig, pubkey) 15 | 16 | sig_keygen(seed = random(32)) 17 | 18 | sig_pubkey(key) 19 | } 20 | \arguments{ 21 | \item{msg}{message to sign} 22 | 23 | \item{key}{private key to sign message with} 24 | 25 | \item{sig}{a signature generated by \code{signature_sign}} 26 | 27 | \item{pubkey}{a public key of the keypair used by the signature} 28 | 29 | \item{seed}{random data to seed the keygen} 30 | } 31 | \description{ 32 | Cryptographic signatures can be used to verify the integrity of a message using 33 | the author's public key. 34 | } 35 | \details{ 36 | A signature is an authenticated checksum that can be used to check that a message 37 | (any data) was created by a particular author and was not tampered with. The signature is 38 | created using a private key and can be verified from the corresponding public key. 39 | 40 | Signatures are used when the message itself is not confidential but integrity is 41 | important. A common use is for software repositories where maintainers include 42 | a signature of the package index. This allows client package managers to verify 43 | that the binaries were not modified by intermediate parties in the distribution 44 | process. 45 | 46 | For confidential data, use authenticated encryption (\link{auth_encrypt}) 47 | which allows for sending signed and encrypted messages in a single method. 48 | 49 | Currently sodium requires a different type of key pairfor signatures (ed25519) 50 | than for encryption (curve25519). 51 | } 52 | \examples{ 53 | # Generate keypair 54 | key <- sig_keygen() 55 | pubkey <- sig_pubkey(key) 56 | 57 | # Create signature 58 | msg <- serialize(iris, NULL) 59 | sig <- sig_sign(msg, key) 60 | sig_verify(msg, sig, pubkey) 61 | } 62 | \references{ 63 | \url{https://doc.libsodium.org/public-key_cryptography/public-key_signatures.html} 64 | } 65 | -------------------------------------------------------------------------------- /src/secret.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | SEXP R_crypto_secret_encrypt(SEXP message, SEXP key, SEXP nonce){ 6 | if(LENGTH(key) != crypto_secretbox_KEYBYTES) 7 | Rf_error("Invalid key: must be exactly %d bytes", crypto_secretbox_KEYBYTES); 8 | if(LENGTH(nonce) != crypto_secretbox_NONCEBYTES) 9 | Rf_error("Invalid nonce: must be exactly %d bytes", crypto_secretbox_NONCEBYTES); 10 | 11 | R_xlen_t mlen = XLENGTH(message); 12 | R_xlen_t clen = mlen + crypto_secretbox_MACBYTES; 13 | SEXP res = allocVector(RAWSXP, clen); 14 | 15 | if(crypto_secretbox_easy(RAW(res), RAW(message), mlen, RAW(nonce), RAW(key))) 16 | Rf_error("Failed to encrypt"); 17 | 18 | return res; 19 | } 20 | 21 | SEXP R_crypto_secret_decrypt(SEXP cipher, SEXP key, SEXP nonce){ 22 | if(LENGTH(key) != crypto_secretbox_KEYBYTES) 23 | Rf_error("Invalid key. Key must be exactly %d bytes", crypto_secretbox_KEYBYTES); 24 | if(LENGTH(nonce) != crypto_secretbox_NONCEBYTES) 25 | Rf_error("Invalid key. Key must be exactly %d bytes", crypto_secretbox_NONCEBYTES); 26 | 27 | R_xlen_t clen = XLENGTH(cipher); 28 | R_xlen_t mlen = clen - crypto_secretbox_MACBYTES; 29 | SEXP res = allocVector(RAWSXP, mlen); 30 | 31 | if(crypto_secretbox_open_easy(RAW(res), RAW(cipher), clen, RAW(nonce), RAW(key))) 32 | Rf_error("Failed to decrypt"); 33 | 34 | return res; 35 | } 36 | 37 | SEXP R_crypto_secret_auth(SEXP message, SEXP key){ 38 | if(LENGTH(key) != crypto_auth_KEYBYTES) 39 | Rf_error("Invalid key. Key must be exactly %d bytes", crypto_auth_KEYBYTES); 40 | 41 | SEXP res = allocVector(RAWSXP, crypto_auth_BYTES); 42 | 43 | if(crypto_auth(RAW(res), RAW(message), XLENGTH(message), RAW(key))) 44 | Rf_error("Authentication failed."); 45 | 46 | return res; 47 | } 48 | 49 | SEXP R_crypto_secret_verify(SEXP message, SEXP key, SEXP tag){ 50 | if(LENGTH(key) != crypto_auth_KEYBYTES) 51 | Rf_error("Invalid key. Key must be exactly %d bytes", crypto_auth_KEYBYTES); 52 | if(LENGTH(tag) != crypto_auth_BYTES) 53 | Rf_error("Invalid tag. Key must be exactly %d bytes", crypto_auth_BYTES); 54 | 55 | int res = crypto_auth_verify(RAW(tag), RAW(message), XLENGTH(message), RAW(key)); 56 | return ScalarLogical(res == 0); 57 | } 58 | -------------------------------------------------------------------------------- /src/stream.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | SEXP R_stream_chacha20(SEXP n, SEXP key, SEXP nonce){ 5 | if(LENGTH(key) != crypto_stream_chacha20_KEYBYTES) 6 | Rf_error("Invalid key, must be exactly %d bytes", crypto_stream_chacha20_KEYBYTES); 7 | if(LENGTH(nonce) != crypto_stream_chacha20_NONCEBYTES) 8 | Rf_error("Invalid nonce, must be exactly %d bytes", crypto_stream_chacha20_NONCEBYTES); 9 | unsigned long long clen = (unsigned long long) asReal(n); 10 | SEXP res = allocVector(RAWSXP, clen); 11 | crypto_stream_chacha20(RAW(res), clen, RAW(nonce), RAW(key)); 12 | return res; 13 | } 14 | 15 | SEXP R_stream_xchacha20(SEXP n, SEXP key, SEXP nonce){ 16 | if(LENGTH(key) != crypto_stream_xchacha20_KEYBYTES) 17 | Rf_error("Invalid key, must be exactly %d bytes", crypto_stream_xchacha20_KEYBYTES); 18 | if(LENGTH(nonce) != crypto_stream_xchacha20_NONCEBYTES) 19 | Rf_error("Invalid nonce, must be exactly %d bytes", crypto_stream_xchacha20_NONCEBYTES); 20 | unsigned long long clen = (unsigned long long) asReal(n); 21 | SEXP res = allocVector(RAWSXP, clen); 22 | crypto_stream_xchacha20(RAW(res), clen, RAW(nonce), RAW(key)); 23 | return res; 24 | } 25 | 26 | SEXP R_stream_salsa20(SEXP n, SEXP key, SEXP nonce){ 27 | if(LENGTH(key) != crypto_stream_salsa20_KEYBYTES) 28 | Rf_error("Invalid key, must be exactly %d bytes", crypto_stream_salsa20_KEYBYTES); 29 | if(LENGTH(nonce) != crypto_stream_salsa20_NONCEBYTES) 30 | Rf_error("Invalid nonce, must be exactly %d bytes", crypto_stream_salsa20_NONCEBYTES); 31 | unsigned long long clen = (unsigned long long) asReal(n); 32 | SEXP res = allocVector(RAWSXP, clen); 33 | crypto_stream_salsa20(RAW(res), clen, RAW(nonce), RAW(key)); 34 | return res; 35 | } 36 | 37 | SEXP R_stream_xsalsa20(SEXP n, SEXP key, SEXP nonce){ 38 | if(LENGTH(key) != crypto_stream_KEYBYTES) 39 | Rf_error("Invalid key, must be exactly %d bytes", crypto_stream_KEYBYTES); 40 | if(LENGTH(nonce) != crypto_stream_NONCEBYTES) 41 | Rf_error("Invalid nonce, must be exactly %d bytes", crypto_stream_NONCEBYTES); 42 | unsigned long long clen = (unsigned long long) asReal(n); 43 | SEXP res = allocVector(RAWSXP, clen); 44 | crypto_stream_xsalsa20(RAW(res), clen, RAW(nonce), RAW(key)); 45 | return res; 46 | } 47 | -------------------------------------------------------------------------------- /man/keygen.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/keygen.R 3 | \name{Key generation} 4 | \alias{Key generation} 5 | \alias{keygen} 6 | \alias{pubkey} 7 | \title{Keypair Generation} 8 | \usage{ 9 | keygen(seed = random(32)) 10 | 11 | pubkey(key) 12 | } 13 | \arguments{ 14 | \item{seed}{random data to seed the keygen} 15 | 16 | \item{key}{private key for which to calculate the public key} 17 | } 18 | \description{ 19 | Functions to generate a random private key and calculate the corresponding curve25519 20 | public key. 21 | } 22 | \details{ 23 | Asymmetric methods rely on public-private keypairs. The private keys are secret and 24 | should never be shared with anyone. The public key on the other hand is not confidential 25 | and should be shared with the other parties. Public keys are typically published on the 26 | users's website or posted in public directories or keyservers. 27 | 28 | The two main applications for public key cryptography are encryption and authentication. 29 | 30 | In public key encryption, data that is encrypted using a public key can only be 31 | decrypted using the corresponding private key. This allows anyone to send somebody a 32 | secure message by encrypting it with the receivers public key. The encrypted message 33 | will only be readable by the owner of the corresponding private key. Basic encryption 34 | is implemented in \link{simple_encrypt}. 35 | 36 | Authentication works the other way around. In public key authentication, the owner of the 37 | private key creates a 'signature' (an authenticated checksum) for a message in a way that 38 | allows anyone who knows the user's public key to verify that this message was indeed signed 39 | by the owner of the private key. 40 | 41 | If both sender and receiver know each other's public key, the two methods can be combined 42 | so that each message going back and forth is signed by the sender and encrypted for the 43 | receiver. This protects both against eavesdropping and MITM tampering, creating a fully 44 | secure channel. 45 | } 46 | \examples{ 47 | # Create keypair 48 | key <- keygen() 49 | pub <- pubkey(key) 50 | 51 | # Basic encryption 52 | msg <- serialize(iris, NULL) 53 | ciphertext <- simple_encrypt(msg, pub) 54 | out <- simple_decrypt(ciphertext, key) 55 | stopifnot(identical(msg, out)) 56 | } 57 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # sodium 2 | 3 | > A Modern and Easy-to-Use Crypto Library 4 | 5 | [![CRAN_Status_Badge](http://www.r-pkg.org/badges/version/sodium)](https://cran.r-project.org/package=sodium) 6 | [![CRAN RStudio mirror downloads](http://cranlogs.r-pkg.org/badges/sodium)](https://cran.r-project.org/package=sodium) 7 | 8 | Bindings to libsodium: a modern, easy-to-use software library for 9 | encryption, decryption, signatures, password hashing and more. Sodium uses 10 | curve25519, a state-of-the-art Diffie-Hellman function by Daniel Bernstein, 11 | which has become very popular after it was discovered that the NSA had 12 | backdoored Dual EC DRBG. 13 | 14 | ## Documentation 15 | 16 | About the R package: 17 | 18 | - Vignette: [Introduction to Sodium for R](https://cran.r-project.org/web/packages/sodium/vignettes/intro.html) 19 | - Vignette: [How does cryptography work](https://cran.r-project.org/web/packages/sodium/vignettes/crypto101.html) 20 | 21 | Other resources: 22 | 23 | - [The Sodium crypto library (libsodium)](https://doc.libsodium.org/) 24 | 25 | 26 | ## Hello World 27 | 28 | ```r 29 | # Generate keypair: 30 | key <- keygen() 31 | pub <- pubkey(key) 32 | 33 | # Encrypt message with pubkey 34 | msg <- serialize(iris, NULL) 35 | ciphertext <- simple_encrypt(msg, pub) 36 | 37 | # Decrypt message with private key 38 | out <- simple_decrypt(ciphertext, key) 39 | ``` 40 | 41 | 42 | 43 | ## Installation 44 | 45 | Binary packages for __OS-X__ or __Windows__ can be installed directly from CRAN: 46 | 47 | ```r 48 | install.packages("sodium") 49 | ``` 50 | 51 | Installation from source on Linux or OSX requires [`libsodium`](https://doc.libsodium.org/). On __Debian or Ubuntu__ install [libsodium-dev](https://packages.debian.org/testing/libsodium-dev): 52 | 53 | ``` 54 | sudo apt-get install -y libsodium-dev 55 | ``` 56 | 57 | On __Fedora__ we need [libsodium-devel](https://src.fedoraproject.org/rpms/libsodium): 58 | 59 | ``` 60 | sudo yum install libsodium-devel 61 | ```` 62 | 63 | On __CentOS / RHEL__ we install [libsodium-devel](https://src.fedoraproject.org/rpms/libsodium) via EPEL: 64 | 65 | ``` 66 | sudo yum install epel-release 67 | sudo yum install libsodium-devel 68 | ``` 69 | 70 | On __OS-X__ use [libsodium](https://github.com/Homebrew/homebrew-core/blob/master/Formula/libsodium.rb) from Homebrew: 71 | 72 | ``` 73 | brew install libsodium 74 | ``` 75 | -------------------------------------------------------------------------------- /man/symmetric.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/data-encrypt.R 3 | \name{Symmetric encryption} 4 | \alias{Symmetric encryption} 5 | \alias{data_encrypt} 6 | \alias{data_decrypt} 7 | \alias{data_tag} 8 | \title{Symmetric Encryption and Tagging} 9 | \usage{ 10 | data_encrypt(msg, key, nonce = random(24)) 11 | 12 | data_decrypt(bin, key, nonce = attr(bin, "nonce")) 13 | 14 | data_tag(msg, key) 15 | } 16 | \arguments{ 17 | \item{msg}{message to be encrypted} 18 | 19 | \item{key}{shared secret key used for both encryption and decryption} 20 | 21 | \item{nonce}{non-secret unique data to randomize the cipher} 22 | 23 | \item{bin}{encrypted ciphertext} 24 | } 25 | \description{ 26 | Encryption with authentication using a 256 bit shared secret. Mainly useful for 27 | encrypting local data. For secure communication use public-key encryption 28 | (\link{simple_encrypt} and \link{auth_encrypt}). 29 | } 30 | \details{ 31 | Symmetric encryption uses a secret key to encode and decode a message. This can be 32 | used to encrypt local data on disk, or as a building block for more complex methods. 33 | 34 | Because the same \code{secret} is used for both encryption and decryption, symmetric 35 | encryption by itself is impractical for communication. For exchanging secure messages 36 | with other parties, use assymetric (public-key) methods (see \link{simple_encrypt} or 37 | \link{auth_encrypt}). 38 | 39 | The \code{nonce} is not confidential but required for decryption, and should be 40 | stored or sent along with the ciphertext. The purpose of the \code{nonce} is to 41 | randomize the cipher to protect gainst re-use attacks. This way you can use one 42 | and the same secret for encrypting multiple messages. 43 | 44 | The \link{data_tag} function generates an authenticated hash that can be stored 45 | alongside the data to be able to verify the integrity of the data later on. For 46 | public key signatures see \code{sig_sign} instead. 47 | } 48 | \examples{ 49 | # 256-bit key 50 | key <- sha256(charToRaw("This is a secret passphrase")) 51 | msg <- serialize(iris, NULL) 52 | 53 | # Encrypts with random nonce 54 | cipher <- data_encrypt(msg, key) 55 | orig <- data_decrypt(cipher, key) 56 | stopifnot(identical(msg, orig)) 57 | 58 | # Tag the message with your key (HMAC) 59 | tag <- data_tag(msg, key) 60 | } 61 | \references{ 62 | \url{https://libsodium.gitbook.io/doc/public-key_cryptography/authenticated_encryption} 63 | } 64 | -------------------------------------------------------------------------------- /R/keygen.R: -------------------------------------------------------------------------------- 1 | #' Keypair Generation 2 | #' 3 | #' Functions to generate a random private key and calculate the corresponding curve25519 4 | #' public key. 5 | #' 6 | #' Asymmetric methods rely on public-private keypairs. The private keys are secret and 7 | #' should never be shared with anyone. The public key on the other hand is not confidential 8 | #' and should be shared with the other parties. Public keys are typically published on the 9 | #' users's website or posted in public directories or keyservers. 10 | #' 11 | #' The two main applications for public key cryptography are encryption and authentication. 12 | #' 13 | #' In public key encryption, data that is encrypted using a public key can only be 14 | #' decrypted using the corresponding private key. This allows anyone to send somebody a 15 | #' secure message by encrypting it with the receivers public key. The encrypted message 16 | #' will only be readable by the owner of the corresponding private key. Basic encryption 17 | #' is implemented in \link{simple_encrypt}. 18 | #' 19 | #' Authentication works the other way around. In public key authentication, the owner of the 20 | #' private key creates a 'signature' (an authenticated checksum) for a message in a way that 21 | #' allows anyone who knows the user's public key to verify that this message was indeed signed 22 | #' by the owner of the private key. 23 | #' 24 | #' If both sender and receiver know each other's public key, the two methods can be combined 25 | #' so that each message going back and forth is signed by the sender and encrypted for the 26 | #' receiver. This protects both against eavesdropping and MITM tampering, creating a fully 27 | #' secure channel. 28 | #' 29 | #' @export 30 | #' @rdname keygen 31 | #' @name Key generation 32 | #' @param key private key for which to calculate the public key 33 | #' @param seed random data to seed the keygen 34 | #' @useDynLib sodium R_keygen 35 | #' @examples # Create keypair 36 | #' key <- keygen() 37 | #' pub <- pubkey(key) 38 | #' 39 | #' # Basic encryption 40 | #' msg <- serialize(iris, NULL) 41 | #' ciphertext <- simple_encrypt(msg, pub) 42 | #' out <- simple_decrypt(ciphertext, key) 43 | #' stopifnot(identical(msg, out)) 44 | keygen <- function(seed = random(32)){ 45 | stopifnot(is.raw(seed)) 46 | .Call(R_keygen, seed) 47 | } 48 | 49 | #' @export 50 | #' @rdname keygen 51 | #' @useDynLib sodium R_pubkey 52 | pubkey <- function(key){ 53 | stopifnot(is.raw(key)) 54 | .Call(R_pubkey, key) 55 | } 56 | -------------------------------------------------------------------------------- /man/messaging.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/auth-encrypt.R 3 | \name{Authenticated encryption} 4 | \alias{Authenticated encryption} 5 | \alias{auth_encrypt} 6 | \alias{auth_decrypt} 7 | \title{Authenticated Encryption} 8 | \usage{ 9 | auth_encrypt(msg, key, pubkey, nonce = random(24)) 10 | 11 | auth_decrypt(bin, key, pubkey, nonce = attr(bin, "nonce")) 12 | } 13 | \arguments{ 14 | \item{msg}{message to be encrypted} 15 | 16 | \item{key}{your own private key} 17 | 18 | \item{pubkey}{other person's public key} 19 | 20 | \item{nonce}{non-secret unique data to randomize the cipher} 21 | 22 | \item{bin}{encrypted ciphertext generated by \code{secure_send}} 23 | } 24 | \description{ 25 | Exchange secure messages through curve25519 authenticated encryption. 26 | } 27 | \details{ 28 | Authenticated encryption implements best practices for secure messaging. 29 | It requires that both sender and receiver have a keypair and know each 30 | other's public key. Each message gets authenticated with the key of the 31 | sender and encrypted with the key of the receiver. 32 | 33 | Even though public keys are not confidential, you should not exchange them 34 | over the same insecure channel you are trying to protect. If the connection 35 | is being tampered with, the attacker could simply replace the key with another 36 | one to hijack the interaction. 37 | 38 | Most people share their public key by posting them on their website or on a 39 | public keyserver. Another alternative is having your public key signed by a 40 | mutually trusted third party. HTTPS does this using Certificate Authorities. 41 | } 42 | \examples{ 43 | # Bob's keypair: 44 | bob_key <- keygen() 45 | bob_pubkey <- pubkey(bob_key) 46 | 47 | # Alice's keypair: 48 | alice_key <- keygen() 49 | alice_pubkey <- pubkey(alice_key) 50 | 51 | # Bob sends encrypted message for Alice: 52 | msg <- charToRaw("TTIP is evil") 53 | ciphertext <- auth_encrypt(msg, bob_key, alice_pubkey) 54 | 55 | # Alice verifies and decrypts with her key 56 | out <- auth_decrypt(ciphertext, alice_key, bob_pubkey) 57 | stopifnot(identical(out, msg)) 58 | 59 | # Alice sends encrypted message for Bob 60 | msg <- charToRaw("Let's protest") 61 | ciphertext <- auth_encrypt(msg, alice_key, bob_pubkey) 62 | 63 | # Bob verifies and decrypts with his key 64 | out <- auth_decrypt(ciphertext, bob_key, alice_pubkey) 65 | stopifnot(identical(out, msg)) 66 | } 67 | \references{ 68 | \url{https://libsodium.gitbook.io/doc/public-key_cryptography/authenticated_encryption} 69 | } 70 | -------------------------------------------------------------------------------- /R/signatures.R: -------------------------------------------------------------------------------- 1 | #' Create and Verify Signatures 2 | #' 3 | #' Cryptographic signatures can be used to verify the integrity of a message using 4 | #' the author's public key. 5 | #' 6 | #' A signature is an authenticated checksum that can be used to check that a message 7 | #' (any data) was created by a particular author and was not tampered with. The signature is 8 | #' created using a private key and can be verified from the corresponding public key. 9 | #' 10 | #' Signatures are used when the message itself is not confidential but integrity is 11 | #' important. A common use is for software repositories where maintainers include 12 | #' a signature of the package index. This allows client package managers to verify 13 | #' that the binaries were not modified by intermediate parties in the distribution 14 | #' process. 15 | #' 16 | #' For confidential data, use authenticated encryption (\link{auth_encrypt}) 17 | #' which allows for sending signed and encrypted messages in a single method. 18 | #' 19 | #' Currently sodium requires a different type of key pairfor signatures (ed25519) 20 | #' than for encryption (curve25519). 21 | #' 22 | #' @rdname sig 23 | #' @name Signatures 24 | #' @aliases sig 25 | #' @export 26 | #' @useDynLib sodium R_sig_sign 27 | #' @param msg message to sign 28 | #' @param key private key to sign message with 29 | #' @param sig a signature generated by \code{signature_sign} 30 | #' @param pubkey a public key of the keypair used by the signature 31 | #' @references \url{https://doc.libsodium.org/public-key_cryptography/public-key_signatures.html} 32 | #' @examples # Generate keypair 33 | #' key <- sig_keygen() 34 | #' pubkey <- sig_pubkey(key) 35 | #' 36 | #' # Create signature 37 | #' msg <- serialize(iris, NULL) 38 | #' sig <- sig_sign(msg, key) 39 | #' sig_verify(msg, sig, pubkey) 40 | sig_sign <- function(msg, key){ 41 | stopifnot(is.raw(msg)) 42 | stopifnot(is.raw(key)) 43 | .Call(R_sig_sign, msg, key) 44 | } 45 | 46 | #' @export 47 | #' @rdname sig 48 | #' @useDynLib sodium R_sig_verify 49 | sig_verify <- function(msg, sig, pubkey){ 50 | stopifnot(is.raw(msg)) 51 | stopifnot(is.raw(sig)) 52 | stopifnot(is.raw(pubkey)) 53 | .Call(R_sig_verify, msg, sig, pubkey) 54 | } 55 | 56 | #' @export 57 | #' @rdname sig 58 | #' @useDynLib sodium R_sig_keygen 59 | #' @param seed random data to seed the keygen 60 | sig_keygen <- function(seed = random(32)){ 61 | stopifnot(is.raw(seed)) 62 | .Call(R_sig_keygen, seed) 63 | } 64 | 65 | #' @export 66 | #' @rdname sig 67 | #' @useDynLib sodium R_sig_pubkey 68 | sig_pubkey <- function(key){ 69 | stopifnot(is.raw(key)) 70 | .Call(R_sig_pubkey, key) 71 | } 72 | -------------------------------------------------------------------------------- /man/diffie.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/diffie-hellman.R 3 | \name{Diffie-Hellman} 4 | \alias{Diffie-Hellman} 5 | \alias{diffie_hellman} 6 | \alias{diffie} 7 | \title{Diffie-Hellman} 8 | \usage{ 9 | diffie_hellman(key, pubkey) 10 | } 11 | \arguments{ 12 | \item{key}{your private key} 13 | 14 | \item{pubkey}{other person's public key} 15 | } 16 | \value{ 17 | Returns a shared secret key which can be used in e.g. \link{data_encrypt}. 18 | } 19 | \description{ 20 | The Diffie-Hellman key exchange method allows two parties that have no prior knowledge 21 | of each other to jointly establish a shared secret key over an insecure channel. This 22 | key can then be used to encrypt subsequent communications using a symmetric key cipher. 23 | } 24 | \details{ 25 | Encryption methods as implemented in \link{data_encrypt} require that parties have a 26 | shared secret key. But often we wish to establish a secure channel with a party we have 27 | no prior relationship with. Diffie-hellman is a method for jointly agreeing on a shared 28 | secret without ever exchanging the secret itself. Sodium implements 29 | \href{https://en.wikipedia.org/wiki/Curve25519}{Curve25519}, a state-of-the-art Diffie-Hellman 30 | function suitable for a wide variety of applications. 31 | 32 | The method conists of two steps (see examples). First, both parties generate a random private 33 | key and derive the corresponding public key using \link{pubkey}. These public keys are not 34 | confidential and can be exchanged over an insecure channel. After the public keys are exchanged, 35 | both parties will be able to calculate the (same) shared secret by combining his/her own private 36 | key with the other person's public key using \link{diffie_hellman}. 37 | 38 | After the shared secret has been established, the private and public keys are disposed, 39 | and parties can start encrypting communications based on the shared secret using e.g. 40 | \link{data_encrypt}. Because the shared secret cannot be calculated using only the public 41 | keys, the process is safe from eavesdroppers. 42 | } 43 | \examples{ 44 | # Bob generates keypair 45 | bob_key <- keygen() 46 | bob_pubkey <- pubkey(bob_key) 47 | 48 | # Alice generates keypair 49 | alice_key <- keygen() 50 | alice_pubkey <- pubkey(alice_key) 51 | 52 | # After Bob and Alice exchange pubkey they can both derive the secret 53 | alice_secret <- diffie_hellman(alice_key, bob_pubkey) 54 | bob_secret <- diffie_hellman(bob_key, alice_pubkey) 55 | stopifnot(identical(alice_secret, bob_secret)) 56 | } 57 | \references{ 58 | \url{https://doc.libsodium.org/advanced/scalar_multiplication.html} 59 | } 60 | -------------------------------------------------------------------------------- /R/diffie-hellman.R: -------------------------------------------------------------------------------- 1 | #' Diffie-Hellman 2 | #' 3 | #' The Diffie-Hellman key exchange method allows two parties that have no prior knowledge 4 | #' of each other to jointly establish a shared secret key over an insecure channel. This 5 | #' key can then be used to encrypt subsequent communications using a symmetric key cipher. 6 | #' 7 | #' Encryption methods as implemented in \link{data_encrypt} require that parties have a 8 | #' shared secret key. But often we wish to establish a secure channel with a party we have 9 | #' no prior relationship with. Diffie-hellman is a method for jointly agreeing on a shared 10 | #' secret without ever exchanging the secret itself. Sodium implements 11 | #' \href{https://en.wikipedia.org/wiki/Curve25519}{Curve25519}, a state-of-the-art Diffie-Hellman 12 | #' function suitable for a wide variety of applications. 13 | #' 14 | #' The method conists of two steps (see examples). First, both parties generate a random private 15 | #' key and derive the corresponding public key using \link{pubkey}. These public keys are not 16 | #' confidential and can be exchanged over an insecure channel. After the public keys are exchanged, 17 | #' both parties will be able to calculate the (same) shared secret by combining his/her own private 18 | #' key with the other person's public key using \link{diffie_hellman}. 19 | #' 20 | #' After the shared secret has been established, the private and public keys are disposed, 21 | #' and parties can start encrypting communications based on the shared secret using e.g. 22 | #' \link{data_encrypt}. Because the shared secret cannot be calculated using only the public 23 | #' keys, the process is safe from eavesdroppers. 24 | #' 25 | #' @export 26 | #' @rdname diffie 27 | #' @name Diffie-Hellman 28 | #' @aliases diffie 29 | #' @useDynLib sodium R_diffie_hellman 30 | #' @references \url{https://doc.libsodium.org/advanced/scalar_multiplication.html} 31 | #' @param key your private key 32 | #' @param pubkey other person's public key 33 | #' @return Returns a shared secret key which can be used in e.g. \link{data_encrypt}. 34 | #' @examples # Bob generates keypair 35 | #' bob_key <- keygen() 36 | #' bob_pubkey <- pubkey(bob_key) 37 | #' 38 | #' # Alice generates keypair 39 | #' alice_key <- keygen() 40 | #' alice_pubkey <- pubkey(alice_key) 41 | #' 42 | #' # After Bob and Alice exchange pubkey they can both derive the secret 43 | #' alice_secret <- diffie_hellman(alice_key, bob_pubkey) 44 | #' bob_secret <- diffie_hellman(bob_key, alice_pubkey) 45 | #' stopifnot(identical(alice_secret, bob_secret)) 46 | diffie_hellman <- function(key, pubkey){ 47 | stopifnot(is.raw(key)) 48 | stopifnot(is.raw(pubkey)) 49 | .Call(R_diffie_hellman, key, pubkey) 50 | } 51 | -------------------------------------------------------------------------------- /man/stream.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/stream.R 3 | \name{Stream ciphers} 4 | \alias{Stream ciphers} 5 | \alias{chacha20} 6 | \alias{stream} 7 | \alias{xchacha20} 8 | \alias{salsa20} 9 | \alias{xsalsa20} 10 | \title{Stream Ciphers} 11 | \usage{ 12 | chacha20(size, key, nonce) 13 | 14 | xchacha20(size, key, nonce) 15 | 16 | salsa20(size, key, nonce) 17 | 18 | xsalsa20(size, key, nonce) 19 | } 20 | \arguments{ 21 | \item{size}{length of cipher stream in bytes} 22 | 23 | \item{key}{secret key used by the cipher} 24 | 25 | \item{nonce}{non-secret unique data to randomize the cipher} 26 | } 27 | \description{ 28 | Generate deterministic streams of random data based off a secret key and random nonce. 29 | } 30 | \details{ 31 | You usually don't need to call these methods directly. For local encryption 32 | use \link{data_encrypt}. For secure communication use \link{simple_encrypt} or 33 | \link{auth_encrypt}. 34 | 35 | Random streams form the basis for most cryptographic methods. Based a shared secret 36 | (the key) we generate a predictable random data stream of equal length as the message 37 | we need to encrypt. Then we \link{xor} the message data with this random stream, 38 | which effectively inverts each byte in the message with probabiliy 0.5. The message 39 | can be decrypted by re-generating exactly the same random data stream and \link{xor}'ing 40 | it back. See the examples. 41 | 42 | Each stream generator requires a \code{key} and a \code{nonce}. Both are required to re-generate 43 | the same stream for decryption. The key forms the shared secret and should only known to 44 | the trusted parties. The \code{nonce} is not secret and should be stored or sent along 45 | with the ciphertext. The purpose of the \code{nonce} is to make a random stream unique 46 | to protect gainst re-use attacks. This way you can re-use a your key to encrypt multiple 47 | messages, as long as you never re-use the same nonce. 48 | } 49 | \examples{ 50 | # Very basic encryption 51 | myfile <- file.path(R.home(), "COPYING") 52 | message <- readBin(myfile, raw(), file.info(myfile)$size) 53 | passwd <- charToRaw("My secret passphrase") 54 | 55 | # Encrypt: 56 | key <- hash(passwd) 57 | nonce8 <- random(8) 58 | stream <- chacha20(length(message), key, nonce8) 59 | ciphertext <- base::xor(stream, message) 60 | 61 | # Decrypt: 62 | stream <- chacha20(length(ciphertext), key, nonce8) 63 | out <- base::xor(ciphertext, stream) 64 | stopifnot(identical(out, message)) 65 | 66 | # Other stream ciphers 67 | stream <- salsa20(10000, key, nonce8) 68 | stream <- xsalsa20(10000, key, random(24)) 69 | stream <- xchacha20(10000, key, random(24)) 70 | 71 | } 72 | \references{ 73 | \url{https://libsodium.gitbook.io/doc/advanced/stream_ciphers/xsalsa20} 74 | } 75 | -------------------------------------------------------------------------------- /R/auth-encrypt.R: -------------------------------------------------------------------------------- 1 | #' Authenticated Encryption 2 | #' 3 | #' Exchange secure messages through curve25519 authenticated encryption. 4 | #' 5 | #' Authenticated encryption implements best practices for secure messaging. 6 | #' It requires that both sender and receiver have a keypair and know each 7 | #' other's public key. Each message gets authenticated with the key of the 8 | #' sender and encrypted with the key of the receiver. 9 | #' 10 | #' Even though public keys are not confidential, you should not exchange them 11 | #' over the same insecure channel you are trying to protect. If the connection 12 | #' is being tampered with, the attacker could simply replace the key with another 13 | #' one to hijack the interaction. 14 | #' 15 | #' Most people share their public key by posting them on their website or on a 16 | #' public keyserver. Another alternative is having your public key signed by a 17 | #' mutually trusted third party. HTTPS does this using Certificate Authorities. 18 | #' 19 | #' @export 20 | #' @useDynLib sodium R_secure_send 21 | #' @rdname messaging 22 | #' @name Authenticated encryption 23 | #' @references \url{https://libsodium.gitbook.io/doc/public-key_cryptography/authenticated_encryption} 24 | #' @examples # Bob's keypair: 25 | #' bob_key <- keygen() 26 | #' bob_pubkey <- pubkey(bob_key) 27 | #' 28 | #' # Alice's keypair: 29 | #' alice_key <- keygen() 30 | #' alice_pubkey <- pubkey(alice_key) 31 | #' 32 | #' # Bob sends encrypted message for Alice: 33 | #' msg <- charToRaw("TTIP is evil") 34 | #' ciphertext <- auth_encrypt(msg, bob_key, alice_pubkey) 35 | #' 36 | #' # Alice verifies and decrypts with her key 37 | #' out <- auth_decrypt(ciphertext, alice_key, bob_pubkey) 38 | #' stopifnot(identical(out, msg)) 39 | #' 40 | #' # Alice sends encrypted message for Bob 41 | #' msg <- charToRaw("Let's protest") 42 | #' ciphertext <- auth_encrypt(msg, alice_key, bob_pubkey) 43 | #' 44 | #' # Bob verifies and decrypts with his key 45 | #' out <- auth_decrypt(ciphertext, bob_key, alice_pubkey) 46 | #' stopifnot(identical(out, msg)) 47 | #' @param msg message to be encrypted 48 | #' @param bin encrypted ciphertext generated by \code{secure_send} 49 | #' @param key your own private key 50 | #' @param pubkey other person's public key 51 | #' @param nonce non-secret unique data to randomize the cipher 52 | auth_encrypt <- function(msg, key, pubkey, nonce = random(24)){ 53 | stopifnot(is.raw(msg)) 54 | stopifnot(is.raw(key)) 55 | stopifnot(is.raw(pubkey)) 56 | stopifnot(is.raw(nonce)) 57 | res <- .Call(R_secure_send, msg, key, pubkey, nonce) 58 | structure(res, nonce = nonce) 59 | } 60 | 61 | #' @export 62 | #' @rdname messaging 63 | #' @useDynLib sodium R_secure_recv 64 | auth_decrypt <- function(bin, key, pubkey, nonce = attr(bin, "nonce")){ 65 | stopifnot(is.raw(bin)) 66 | stopifnot(is.raw(key)) 67 | stopifnot(is.raw(pubkey)) 68 | stopifnot(is.raw(nonce)) 69 | .Call(R_secure_recv, bin, key, pubkey, nonce) 70 | } 71 | -------------------------------------------------------------------------------- /man/hash.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/hashing.R 3 | \name{Hash functions} 4 | \alias{Hash functions} 5 | \alias{hash} 6 | \alias{hashing} 7 | \alias{scrypt} 8 | \alias{argon2} 9 | \alias{shorthash} 10 | \alias{sha512} 11 | \alias{sha256} 12 | \title{Hash Functions} 13 | \usage{ 14 | hash(buf, key = NULL, size = 32) 15 | 16 | scrypt(buf, salt = raw(32), size = 32) 17 | 18 | argon2(buf, salt = raw(16), size = 32) 19 | 20 | shorthash(buf, key) 21 | 22 | sha512(buf, key = NULL) 23 | 24 | sha256(buf, key = NULL) 25 | } 26 | \arguments{ 27 | \item{buf}{data to be hashed} 28 | 29 | \item{key}{key for HMAC hashing. Optional, except for in \code{shorthash}.} 30 | 31 | \item{size}{length of the output hash. Must be between 16 and 64 (recommended is 32)} 32 | 33 | \item{salt}{non-confidential random data to seed the algorithm} 34 | } 35 | \description{ 36 | Functions to calculate cryptographic hash of a message, with optionally a key for 37 | HMAC applications. For storing passwords, use \link{password_store} instead. 38 | } 39 | \details{ 40 | The generic \code{hash} function is recommended for most applications. It uses 41 | dynamic length 42 | \href{https://libsodium.gitbook.io/doc/hashing/generic_hashing}{BLAKE2b} 43 | where output size can be any value between 16 bytes (128bit) and 64 bytes (512bit). 44 | 45 | The \link{scrypt} hash function is designed to be CPU and memory expensive to protect 46 | against brute force attacks. This algorithm is also used by the \link{password_store} 47 | function. 48 | 49 | The \link{argon2} hash function is also designed to be CPU and memory expensive to protect 50 | against brute force attacks. Argon2 is a password-hashing function that summarizes the 51 | state of the art in the design of memory-hard functions 52 | 53 | The \code{shorthash} function is a special 8 byte (64 bit) hash based on 54 | \href{https://libsodium.gitbook.io/doc/hashing/short-input_hashing}{SipHash-2-4}. 55 | The output of this function is only 64 bits (8 bytes). It is useful for in e.g. 56 | Hash tables, but it should not be considered collision-resistant. 57 | 58 | Hash functions can be used for HMAC by specifying a secret \code{key}. They key size 59 | for \code{shorthash} is 16 bytes, for \code{sha256} it is 32 bytes and for \code{sha512} 60 | it is 64 bytes. For \code{hash} the key size can be any value between 16 and 62, 61 | recommended is at least 32. 62 | } 63 | \examples{ 64 | # Basic hashing 65 | msg <- serialize(iris, NULL) 66 | hash(msg) 67 | sha256(msg) 68 | sha512(msg) 69 | scrypt(msg) 70 | 71 | # Generate keys from passphrase 72 | passphrase <- charToRaw("This is super secret") 73 | key <- hash(passphrase) 74 | shortkey <- hash(passphrase, size = 16) 75 | longkey <- hash(passphrase, size = 64) 76 | 77 | # HMAC (hashing with key) 78 | hash(msg, key = key) 79 | shorthash(msg, shortkey) 80 | sha256(msg, key = key) 81 | sha512(msg, key = longkey) 82 | } 83 | \references{ 84 | \url{https://libsodium.gitbook.io/doc/hashing/generic_hashing} 85 | } 86 | -------------------------------------------------------------------------------- /configure: -------------------------------------------------------------------------------- 1 | # Anticonf (tm) script by Jeroen Ooms (2021) 2 | # This script will query 'pkg-config' for the required cflags and ldflags. 3 | # If pkg-config is unavailable or does not find the library, try setting 4 | # INCLUDE_DIR and LIB_DIR manually via e.g: 5 | # R CMD INSTALL --configure-vars='INCLUDE_DIR=/.../include LIB_DIR=/.../lib' 6 | 7 | # Library settings 8 | PKG_CONFIG_NAME="libsodium" 9 | PKG_DEB_NAME="libsodium-dev" 10 | PKG_RPM_NAME="libsodium-devel" 11 | PKG_CSW_NAME="libsodium_dev" 12 | PKG_BREW_NAME="libsodium" 13 | PKG_TEST_HEADER="" 14 | PKG_LIBS="-lsodium" 15 | PKG_CFLAGS="" 16 | 17 | # Use pkg-config if available 18 | if [ `command -v pkg-config` ]; then 19 | PKGCONFIG_CFLAGS=`pkg-config --cflags --silence-errors ${PKG_CONFIG_NAME}` 20 | PKGCONFIG_LIBS=`pkg-config --libs ${PKG_CONFIG_NAME}` 21 | fi 22 | 23 | # Note that cflags may be empty in case of success 24 | if [ "$INCLUDE_DIR" ] || [ "$LIB_DIR" ]; then 25 | echo "Found INCLUDE_DIR and/or LIB_DIR!" 26 | PKG_CFLAGS="-I$INCLUDE_DIR $PKG_CFLAGS" 27 | PKG_LIBS="-L$LIB_DIR $PKG_LIBS" 28 | elif [ "$PKGCONFIG_CFLAGS" ] || [ "$PKGCONFIG_LIBS" ]; then 29 | echo "Found pkg-config cflags and libs!" 30 | PKG_CFLAGS=${PKGCONFIG_CFLAGS} 31 | PKG_LIBS=${PKGCONFIG_LIBS} 32 | elif [ `uname` = "Darwin" ]; then 33 | test ! "$CI" && brew --version 2>/dev/null 34 | if [ $? -eq 0 ]; then 35 | BREWDIR=`brew --prefix` 36 | PKG_CFLAGS="-I$BREWDIR/include" 37 | PKG_LIBS="-L$BREWDIR/lib $PKG_LIBS" 38 | else 39 | curl -sfL "https://autobrew.github.io/scripts/libsodium" > autobrew 40 | . ./autobrew 41 | fi 42 | fi 43 | 44 | # For debugging 45 | echo "Using PKG_CFLAGS=$PKG_CFLAGS" 46 | echo "Using PKG_LIBS=$PKG_LIBS" 47 | 48 | # Find compiler 49 | CC=`${R_HOME}/bin/R CMD config CC` 50 | CFLAGS=`${R_HOME}/bin/R CMD config CFLAGS` 51 | CPPFLAGS=`${R_HOME}/bin/R CMD config CPPFLAGS` 52 | 53 | # Test configuration 54 | echo "#include $PKG_TEST_HEADER" | ${CC} ${CPPFLAGS} ${PKG_CFLAGS} ${CFLAGS} -E -xc - >/dev/null 2>configure.log 55 | 56 | # Customize the error 57 | if [ $? -ne 0 ]; then 58 | echo "--------------------------- [ANTICONF] --------------------------------" 59 | echo "Configuration failed because $PKG_CONFIG_NAME was not found. Try installing:" 60 | echo " * deb: $PKG_DEB_NAME (Debian, Ubuntu, etc)" 61 | echo " * rpm: $PKG_RPM_NAME (Fedora, EPEL)" 62 | echo " * csw: $PKG_CSW_NAME (Solaris)" 63 | echo " * brew: $PKG_BREW_NAME (OSX)" 64 | echo "If $PKG_CONFIG_NAME is already installed, check that 'pkg-config' is in your" 65 | echo "PATH and PKG_CONFIG_PATH contains a $PKG_CONFIG_NAME.pc file. If pkg-config" 66 | echo "is unavailable you can set INCLUDE_DIR and LIB_DIR manually via:" 67 | echo "R CMD INSTALL --configure-vars='INCLUDE_DIR=... LIB_DIR=...'" 68 | echo "-------------------------- [ERROR MESSAGE] ---------------------------" 69 | cat configure.log 70 | echo "--------------------------------------------------------------------" 71 | exit 1 72 | fi 73 | 74 | 75 | # Write to Makevars 76 | sed -e "s|@cflags@|$PKG_CFLAGS|" -e "s|@libs@|$PKG_LIBS|" src/Makevars.in > src/Makevars 77 | 78 | # Success 79 | exit 0 80 | -------------------------------------------------------------------------------- /R/data-encrypt.R: -------------------------------------------------------------------------------- 1 | #' Symmetric Encryption and Tagging 2 | #' 3 | #' Encryption with authentication using a 256 bit shared secret. Mainly useful for 4 | #' encrypting local data. For secure communication use public-key encryption 5 | #' (\link{simple_encrypt} and \link{auth_encrypt}). 6 | #' 7 | #' Symmetric encryption uses a secret key to encode and decode a message. This can be 8 | #' used to encrypt local data on disk, or as a building block for more complex methods. 9 | #' 10 | #' Because the same \code{secret} is used for both encryption and decryption, symmetric 11 | #' encryption by itself is impractical for communication. For exchanging secure messages 12 | #' with other parties, use assymetric (public-key) methods (see \link{simple_encrypt} or 13 | #' \link{auth_encrypt}). 14 | #' 15 | #' The \code{nonce} is not confidential but required for decryption, and should be 16 | #' stored or sent along with the ciphertext. The purpose of the \code{nonce} is to 17 | #' randomize the cipher to protect gainst re-use attacks. This way you can use one 18 | #' and the same secret for encrypting multiple messages. 19 | #' 20 | #' The \link{data_tag} function generates an authenticated hash that can be stored 21 | #' alongside the data to be able to verify the integrity of the data later on. For 22 | #' public key signatures see \code{sig_sign} instead. 23 | #' 24 | #' @export 25 | #' @rdname symmetric 26 | #' @name Symmetric encryption 27 | #' @useDynLib sodium R_crypto_secret_encrypt 28 | #' @param msg message to be encrypted 29 | #' @param key shared secret key used for both encryption and decryption 30 | #' @param bin encrypted ciphertext 31 | #' @param nonce non-secret unique data to randomize the cipher 32 | #' @references \url{https://libsodium.gitbook.io/doc/public-key_cryptography/authenticated_encryption} 33 | #' @examples # 256-bit key 34 | #' key <- sha256(charToRaw("This is a secret passphrase")) 35 | #' msg <- serialize(iris, NULL) 36 | #' 37 | #' # Encrypts with random nonce 38 | #' cipher <- data_encrypt(msg, key) 39 | #' orig <- data_decrypt(cipher, key) 40 | #' stopifnot(identical(msg, orig)) 41 | #' 42 | #' # Tag the message with your key (HMAC) 43 | #' tag <- data_tag(msg, key) 44 | data_encrypt <- function(msg, key, nonce = random(24)){ 45 | stopifnot(is.raw(msg)) 46 | stopifnot(is.raw(key)) 47 | out <- .Call(R_crypto_secret_encrypt, msg, key, nonce) 48 | structure(out, nonce = nonce) 49 | } 50 | 51 | #' @export 52 | #' @rdname symmetric 53 | #' @useDynLib sodium R_crypto_secret_decrypt 54 | data_decrypt <- function(bin, key, nonce = attr(bin, "nonce")){ 55 | stopifnot(is.raw(bin)) 56 | stopifnot(is.raw(key)) 57 | .Call(R_crypto_secret_decrypt, bin, key, nonce) 58 | } 59 | 60 | #' @export 61 | #' @rdname symmetric 62 | #' @useDynLib sodium R_crypto_secret_auth 63 | data_tag <- function(msg, key){ 64 | stopifnot(is.raw(msg)) 65 | stopifnot(is.raw(key)) 66 | .Call(R_crypto_secret_auth, msg, key) 67 | } 68 | 69 | #' @useDynLib sodium R_crypto_secret_verify 70 | data_verify <- function(msg, key, tag){ 71 | stopifnot(is.raw(msg)) 72 | stopifnot(is.raw(tag)) 73 | stopifnot(is.raw(key)) 74 | .Call(R_crypto_secret_verify, msg, key, tag) 75 | } 76 | -------------------------------------------------------------------------------- /R/stream.R: -------------------------------------------------------------------------------- 1 | #' Stream Ciphers 2 | #' 3 | #' Generate deterministic streams of random data based off a secret key and random nonce. 4 | #' 5 | #' You usually don't need to call these methods directly. For local encryption 6 | #' use \link{data_encrypt}. For secure communication use \link{simple_encrypt} or 7 | #' \link{auth_encrypt}. 8 | #' 9 | #' Random streams form the basis for most cryptographic methods. Based a shared secret 10 | #' (the key) we generate a predictable random data stream of equal length as the message 11 | #' we need to encrypt. Then we \link{xor} the message data with this random stream, 12 | #' which effectively inverts each byte in the message with probabiliy 0.5. The message 13 | #' can be decrypted by re-generating exactly the same random data stream and \link{xor}'ing 14 | #' it back. See the examples. 15 | #' 16 | #' Each stream generator requires a \code{key} and a \code{nonce}. Both are required to re-generate 17 | #' the same stream for decryption. The key forms the shared secret and should only known to 18 | #' the trusted parties. The \code{nonce} is not secret and should be stored or sent along 19 | #' with the ciphertext. The purpose of the \code{nonce} is to make a random stream unique 20 | #' to protect gainst re-use attacks. This way you can re-use a your key to encrypt multiple 21 | #' messages, as long as you never re-use the same nonce. 22 | #' 23 | #' @export 24 | #' @useDynLib sodium R_stream_chacha20 25 | #' @rdname stream 26 | #' @aliases stream 27 | #' @name Stream ciphers 28 | #' @param size length of cipher stream in bytes 29 | #' @param key secret key used by the cipher 30 | #' @param nonce non-secret unique data to randomize the cipher 31 | #' @references \url{https://libsodium.gitbook.io/doc/advanced/stream_ciphers/xsalsa20} 32 | #' @examples # Very basic encryption 33 | #' myfile <- file.path(R.home(), "COPYING") 34 | #' message <- readBin(myfile, raw(), file.info(myfile)$size) 35 | #' passwd <- charToRaw("My secret passphrase") 36 | #' 37 | #' # Encrypt: 38 | #' key <- hash(passwd) 39 | #' nonce8 <- random(8) 40 | #' stream <- chacha20(length(message), key, nonce8) 41 | #' ciphertext <- base::xor(stream, message) 42 | #' 43 | #' # Decrypt: 44 | #' stream <- chacha20(length(ciphertext), key, nonce8) 45 | #' out <- base::xor(ciphertext, stream) 46 | #' stopifnot(identical(out, message)) 47 | #' 48 | #' # Other stream ciphers 49 | #' stream <- salsa20(10000, key, nonce8) 50 | #' stream <- xsalsa20(10000, key, random(24)) 51 | #' stream <- xchacha20(10000, key, random(24)) 52 | #' 53 | chacha20 <- function(size, key, nonce){ 54 | stopifnot(is.numeric(size)) 55 | stopifnot(is.raw(key)) 56 | stopifnot(is.raw(nonce)) 57 | .Call(R_stream_chacha20, size, key, nonce) 58 | } 59 | 60 | #' @export 61 | #' @useDynLib sodium R_stream_xchacha20 62 | #' @rdname stream 63 | xchacha20 <- function(size, key, nonce){ 64 | stopifnot(is.numeric(size)) 65 | stopifnot(is.raw(key)) 66 | stopifnot(is.raw(nonce)) 67 | .Call(R_stream_xchacha20, size, key, nonce) 68 | } 69 | 70 | #' @export 71 | #' @useDynLib sodium R_stream_salsa20 72 | #' @rdname stream 73 | salsa20 <- function(size, key, nonce){ 74 | stopifnot(is.numeric(size)) 75 | stopifnot(is.raw(key)) 76 | stopifnot(is.raw(nonce)) 77 | .Call(R_stream_salsa20, size, key, nonce) 78 | } 79 | 80 | #' @export 81 | #' @useDynLib sodium R_stream_xsalsa20 82 | #' @rdname stream 83 | xsalsa20 <- function(size, key, nonce){ 84 | stopifnot(is.numeric(size)) 85 | stopifnot(is.raw(key)) 86 | stopifnot(is.raw(nonce)) 87 | .Call(R_stream_xsalsa20, size, key, nonce) 88 | } 89 | -------------------------------------------------------------------------------- /src/hashing.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | /* SHA256 */ 5 | 6 | SEXP R_sha256(SEXP buf){ 7 | SEXP res = allocVector(RAWSXP, crypto_hash_sha256_BYTES); 8 | if(crypto_hash_sha256(RAW(res), RAW(buf), XLENGTH(buf))) 9 | Rf_error("Failed to hash"); 10 | return res; 11 | } 12 | 13 | SEXP R_auth_sha256(SEXP buf, SEXP key){ 14 | if(LENGTH(key) != crypto_auth_hmacsha256_BYTES) 15 | Rf_error("Invalid key, must be exactly %d bytes", crypto_auth_hmacsha256_BYTES); 16 | SEXP res = allocVector(RAWSXP, crypto_hash_sha256_BYTES); 17 | if(crypto_auth_hmacsha256(RAW(res), RAW(buf), XLENGTH(buf), RAW(key))) 18 | Rf_error("Failed to hash"); 19 | return res; 20 | } 21 | 22 | /* SHA512 */ 23 | 24 | SEXP R_sha512(SEXP buf){ 25 | SEXP res = allocVector(RAWSXP, crypto_hash_sha512_BYTES); 26 | if(crypto_hash_sha512(RAW(res), RAW(buf), XLENGTH(buf))) 27 | Rf_error("Failed to hash"); 28 | return res; 29 | } 30 | 31 | SEXP R_auth_sha512(SEXP buf, SEXP key){ 32 | if(LENGTH(key) != crypto_auth_hmacsha512_BYTES) 33 | Rf_error("Invalid key, must be exactly %d bytes", crypto_auth_hmacsha512_BYTES); 34 | SEXP res = allocVector(RAWSXP, crypto_hash_sha512_BYTES); 35 | if(crypto_auth_hmacsha512(RAW(res), RAW(buf), XLENGTH(buf), RAW(key))) 36 | Rf_error("Failed to hash"); 37 | return res; 38 | } 39 | 40 | /* BLAKE2b */ 41 | 42 | SEXP R_crypto_generichash(SEXP buf, SEXP size, SEXP key){ 43 | int outlen = asInteger(size); 44 | if(outlen < crypto_generichash_BYTES_MIN || outlen > crypto_generichash_BYTES_MAX) 45 | Rf_error("Invalid output length, must be in between %d and %d", crypto_generichash_BYTES_MIN, crypto_generichash_BYTES_MAX); 46 | 47 | unsigned char *keyval = NULL; 48 | int keysize = 0; 49 | if(key != R_NilValue){ 50 | keysize = LENGTH(key); 51 | keyval = RAW(key); 52 | if(keysize < crypto_generichash_KEYBYTES_MIN || keysize > crypto_generichash_KEYBYTES_MAX) 53 | Rf_error("Invalid key size, must be between %d and %d bytes", crypto_generichash_KEYBYTES_MIN, crypto_generichash_KEYBYTES_MAX); 54 | } 55 | 56 | SEXP res = allocVector(RAWSXP, outlen); 57 | if(crypto_generichash(RAW(res), outlen, RAW(buf), XLENGTH(buf), keyval, keysize)) 58 | Rf_error("Failed to hash"); 59 | return res; 60 | } 61 | 62 | /* Shorthash */ 63 | 64 | SEXP R_crypto_shorthash(SEXP buf, SEXP key){ 65 | if(LENGTH(key) != crypto_shorthash_KEYBYTES) 66 | Rf_error("Invalid key, must be exactly %d bytes", crypto_shorthash_KEYBYTES); 67 | 68 | SEXP res = allocVector(RAWSXP, crypto_shorthash_BYTES); 69 | if(crypto_shorthash(RAW(res), RAW(buf), XLENGTH(buf), RAW(key))) 70 | Rf_error("Failed to hash"); 71 | return res; 72 | } 73 | 74 | /* Password hashing */ 75 | 76 | SEXP R_pwhash(SEXP buf, SEXP salt, SEXP size){ 77 | int outlen = asInteger(size); 78 | if(LENGTH(salt) != crypto_pwhash_scryptsalsa208sha256_SALTBYTES) 79 | Rf_error("Invalid salt, must be exactly %d bytes", crypto_pwhash_scryptsalsa208sha256_SALTBYTES); 80 | SEXP res = allocVector(RAWSXP, outlen); 81 | if(crypto_pwhash_scryptsalsa208sha256(RAW(res), outlen, (char*) RAW(buf), XLENGTH(buf), RAW(salt), 82 | crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_INTERACTIVE, crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_INTERACTIVE)) 83 | Rf_error("pwhash failed"); 84 | return res; 85 | } 86 | 87 | SEXP R_pwhash_argon2(SEXP buf, SEXP salt, SEXP size){ 88 | // Libsodium version needs to be at least 1.0.9 (aka 9.2) 89 | #if (SODIUM_LIBRARY_VERSION_MAJOR > 9 || \ 90 | SODIUM_LIBRARY_VERSION_MAJOR == 9 && SODIUM_LIBRARY_VERSION_MINOR >= 2) 91 | int outlen = asInteger(size); 92 | if(LENGTH(salt) != crypto_pwhash_SALTBYTES) 93 | Rf_error("Invalid salt, must be exactly %d bytes", crypto_pwhash_SALTBYTES); 94 | SEXP res = allocVector(RAWSXP, outlen); 95 | if(crypto_pwhash(RAW(res), outlen, (char*) RAW(buf), XLENGTH(buf), RAW(salt), 96 | crypto_pwhash_OPSLIMIT_INTERACTIVE, 97 | crypto_pwhash_MEMLIMIT_INTERACTIVE, 98 | crypto_pwhash_ALG_ARGON2I13)) 99 | Rf_error("pwhash failed"); 100 | return res; 101 | #else 102 | Rf_error("Argon2 is only supported in libdsodium >= 1.0.9."); 103 | return 0; 104 | #endif 105 | } 106 | -------------------------------------------------------------------------------- /R/hashing.R: -------------------------------------------------------------------------------- 1 | #' Hash Functions 2 | #' 3 | #' Functions to calculate cryptographic hash of a message, with optionally a key for 4 | #' HMAC applications. For storing passwords, use \link{password_store} instead. 5 | #' 6 | #' The generic \code{hash} function is recommended for most applications. It uses 7 | #' dynamic length 8 | #' \href{https://libsodium.gitbook.io/doc/hashing/generic_hashing}{BLAKE2b} 9 | #' where output size can be any value between 16 bytes (128bit) and 64 bytes (512bit). 10 | #' 11 | #' The \link{scrypt} hash function is designed to be CPU and memory expensive to protect 12 | #' against brute force attacks. This algorithm is also used by the \link{password_store} 13 | #' function. 14 | #' 15 | #' The \link{argon2} hash function is also designed to be CPU and memory expensive to protect 16 | #' against brute force attacks. Argon2 is a password-hashing function that summarizes the 17 | #' state of the art in the design of memory-hard functions 18 | #' 19 | #' The \code{shorthash} function is a special 8 byte (64 bit) hash based on 20 | #' \href{https://libsodium.gitbook.io/doc/hashing/short-input_hashing}{SipHash-2-4}. 21 | #' The output of this function is only 64 bits (8 bytes). It is useful for in e.g. 22 | #' Hash tables, but it should not be considered collision-resistant. 23 | #' 24 | #' Hash functions can be used for HMAC by specifying a secret \code{key}. They key size 25 | #' for \code{shorthash} is 16 bytes, for \code{sha256} it is 32 bytes and for \code{sha512} 26 | #' it is 64 bytes. For \code{hash} the key size can be any value between 16 and 62, 27 | #' recommended is at least 32. 28 | #' 29 | #' @rdname hash 30 | #' @name Hash functions 31 | #' @aliases hashing 32 | #' @references \url{https://libsodium.gitbook.io/doc/hashing/generic_hashing} 33 | #' @useDynLib sodium R_crypto_generichash 34 | #' @param buf data to be hashed 35 | #' @param key key for HMAC hashing. Optional, except for in \code{shorthash}. 36 | #' @export 37 | #' @examples # Basic hashing 38 | #' msg <- serialize(iris, NULL) 39 | #' hash(msg) 40 | #' sha256(msg) 41 | #' sha512(msg) 42 | #' scrypt(msg) 43 | #' 44 | #' # Generate keys from passphrase 45 | #' passphrase <- charToRaw("This is super secret") 46 | #' key <- hash(passphrase) 47 | #' shortkey <- hash(passphrase, size = 16) 48 | #' longkey <- hash(passphrase, size = 64) 49 | #' 50 | #' # HMAC (hashing with key) 51 | #' hash(msg, key = key) 52 | #' shorthash(msg, shortkey) 53 | #' sha256(msg, key = key) 54 | #' sha512(msg, key = longkey) 55 | hash <- function(buf, key = NULL, size = 32){ 56 | stopifnot(is.raw(buf)) 57 | stopifnot(is.null(key) || is.raw(key)) 58 | .Call(R_crypto_generichash, buf, size, key) 59 | } 60 | 61 | #' @export 62 | #' @rdname hash 63 | #' @param salt non-confidential random data to seed the algorithm 64 | #' @useDynLib sodium R_pwhash 65 | scrypt <- function(buf, salt = raw(32), size = 32){ 66 | stopifnot(is.raw(buf)) 67 | stopifnot(is.raw(salt)) 68 | stopifnot(is.numeric(size)) 69 | .Call(R_pwhash, buf, salt, size) 70 | } 71 | 72 | #' @export 73 | #' @rdname hash 74 | #' @useDynLib sodium R_pwhash_argon2 75 | argon2 <- function(buf, salt = raw(16), size = 32){ 76 | stopifnot(is.raw(buf)) 77 | stopifnot(is.raw(salt)) 78 | stopifnot(is.numeric(size)) 79 | .Call(R_pwhash_argon2, buf, salt, size) 80 | } 81 | 82 | #' @export 83 | #' @rdname hash 84 | #' @useDynLib sodium R_crypto_shorthash 85 | shorthash <- function(buf, key){ 86 | stopifnot(is.raw(buf)) 87 | stopifnot(is.raw(key)) 88 | .Call(R_crypto_shorthash, buf, key) 89 | } 90 | 91 | #' @rdname hash 92 | #' @useDynLib sodium R_sha512 R_auth_sha512 93 | #' @export 94 | sha512 <- function(buf, key = NULL){ 95 | stopifnot(is.raw(buf)) 96 | if(length(key)){ 97 | stopifnot(is.raw(key)) 98 | .Call(R_auth_sha512, buf, key) 99 | } else { 100 | .Call(R_sha512, buf) 101 | } 102 | } 103 | 104 | #' @rdname hash 105 | #' @param size length of the output hash. Must be between 16 and 64 (recommended is 32) 106 | #' @useDynLib sodium R_sha256 R_auth_sha256 107 | #' @export 108 | sha256 <- function(buf, key = NULL){ 109 | stopifnot(is.raw(buf)) 110 | if(length(key)){ 111 | stopifnot(is.raw(key)) 112 | .Call(R_auth_sha256, buf, key) 113 | } else { 114 | .Call(R_sha256, buf) 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /vignettes/intro.rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Sodium: A Modern and Easy-to-Use Crypto Library" 3 | output: 4 | html_document: 5 | toc: false 6 | date: "`r Sys.Date()`" 7 | vignette: > 8 | %\VignetteIndexEntry{Introduction to Sodium for R} 9 | %\VignetteEngine{knitr::rmarkdown} 10 | \usepackage[utf8]{inputenc} 11 | --- 12 | 13 | ```{r, echo = FALSE, message = FALSE} 14 | knitr::opts_chunk$set(comment = "") 15 | library(sodium) 16 | ``` 17 | 18 | The sodium R package provides bindings to [libsodium](https://libsodium.gitbook.io/): a modern, easy-to-use software library for encryption, decryption, signatures, password hashing and more. 19 | 20 | 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. 21 | 22 | 23 | | | Authenticated Encryption | Encryption only | Authentication only | 24 | |-----------------------------:|:--------------------------------:|:-----------------------------------:|:--------------------------:| 25 | | __Symmetric__ *(secret key)*: | [`data_encrypt` / `data_decrypt`](#secret-key-encryption) | | [`data_tag`](#secret-key-authentication) | 26 | | __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) | 27 | | | | | | 28 | 29 | ### Using Sodium 30 | 31 | 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: 32 | 33 | ```{r} 34 | test <- hash(charToRaw("test 123")) 35 | str <- bin2hex(test) 36 | print(str) 37 | hex2bin(str) 38 | ``` 39 | 40 | ### Random data generator 41 | 42 | The `random()` function generates n bytes of unpredictable data, suitable for creating secret keys. 43 | 44 | ```{r} 45 | secret <- random(8) 46 | print(secret) 47 | ``` 48 | 49 | Implementation is platform specific, see the [docs](https://libsodium.gitbook.io/doc/generating_random_data) for details. 50 | 51 | ### Hash functions 52 | 53 | Sodium has several hash functions including `hash()`, `shorthash()`, `sha256()`, `sha512` and `scrypt()`. The generic `hash()` is usually recommended. It uses [blake2b](https://doc.libsodium.org/hashing/generic_hashing.html) with a configurable size between 16 bytes (128bit) and 64 bytes (512bit). 54 | 55 | ```{r} 56 | # Generate keys from passphrase 57 | passphrase <- charToRaw("This is super secret") 58 | hash(passphrase) 59 | hash(passphrase, size = 16) 60 | hash(passphrase, size = 64) 61 | ``` 62 | 63 | The `shorthash()` function is a special 8 byte (64 bit) hash based on [SipHash-2-4](https://doc.libsodium.org/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. 64 | 65 | 66 | ### Secret key encryption 67 | 68 | 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. 69 | 70 | 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. 71 | 72 | ```{r} 73 | key <- hash(charToRaw("This is a secret passphrase")) 74 | msg <- serialize(iris, NULL) 75 | 76 | # Encrypt with a random nonce 77 | nonce <- random(24) 78 | cipher <- data_encrypt(msg, key, nonce) 79 | 80 | # Decrypt with same key and nonce 81 | orig <- data_decrypt(cipher, key, nonce) 82 | identical(iris, unserialize(orig)) 83 | ``` 84 | 85 | 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. 86 | 87 | ### Secret key authentication 88 | 89 | Secret key authentication is called tagging in Sodium. A tag is basically a hash of the data together with a secret key. 90 | 91 | ```{r} 92 | key <- hash(charToRaw("This is a secret passphrase")) 93 | msg <- serialize(iris, NULL) 94 | mytag <- data_tag(msg, key) 95 | ``` 96 | 97 | To verify the integrity of the data at a later point in time, simply re-calculate the tag with the same key: 98 | 99 | ```{r} 100 | stopifnot(identical(mytag, data_tag(msg, key))) 101 | ``` 102 | 103 | The secret key protects against forgery of the data+tag by an intermediate party, as would be possible with a regular checksum. 104 | 105 | ### Public key encryption 106 | 107 | 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. 108 | 109 | ```{r} 110 | key <- keygen() 111 | pub <- pubkey(key) 112 | ``` 113 | 114 | 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. 115 | 116 | ```{r} 117 | # Encrypt message with pubkey 118 | msg <- serialize(iris, NULL) 119 | ciphertext <- simple_encrypt(msg, pub) 120 | 121 | # Decrypt message with private key 122 | out <- simple_decrypt(ciphertext, key) 123 | stopifnot(identical(out, msg)) 124 | ``` 125 | 126 | 127 | ### Public key authentication (signatures) 128 | 129 | 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. 130 | 131 | Currently sodium requires a different type of key-pair for signatures (ed25519) than for encryption (curve25519). 132 | 133 | ```{r} 134 | # Generate signature keypair 135 | key <- sig_keygen() 136 | pubkey <- sig_pubkey(key) 137 | 138 | # Create signature with private key 139 | msg <- serialize(iris, NULL) 140 | sig <- sig_sign(msg, key) 141 | print(sig) 142 | 143 | # Verify a signature from public key 144 | sig_verify(msg, sig, pubkey) 145 | ``` 146 | 147 | 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. 148 | 149 | ### Public key authenticated encryption 150 | 151 | 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. 152 | 153 | ```{r} 154 | # Bob's keypair: 155 | bob_key <- keygen() 156 | bob_pubkey <- pubkey(bob_key) 157 | 158 | # Alice's keypair: 159 | alice_key <- keygen() 160 | alice_pubkey <- pubkey(alice_key) 161 | 162 | # Bob sends encrypted message for Alice: 163 | msg <- charToRaw("TTIP is evil") 164 | ciphertext <- auth_encrypt(msg, bob_key, alice_pubkey) 165 | 166 | # Alice verifies and decrypts with her key 167 | out <- auth_decrypt(ciphertext, alice_key, bob_pubkey) 168 | stopifnot(identical(out, msg)) 169 | 170 | # Alice sends encrypted message for Bob 171 | msg <- charToRaw("Let's protest") 172 | ciphertext <- auth_encrypt(msg, alice_key, bob_pubkey) 173 | 174 | # Bob verifies and decrypts with his key 175 | out <- auth_decrypt(ciphertext, bob_key, alice_pubkey) 176 | stopifnot(identical(out, msg)) 177 | ``` 178 | 179 | 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. 180 | -------------------------------------------------------------------------------- /vignettes/crypto101.rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "How does cryptography work?" 3 | output: html_document 4 | date: "`r Sys.Date()`" 5 | vignette: > 6 | %\VignetteIndexEntry{How does cryptography work} 7 | %\VignetteEngine{knitr::rmarkdown} 8 | \usepackage[utf8]{inputenc} 9 | --- 10 | 11 | ```{r, echo = FALSE, message = FALSE} 12 | knitr::opts_chunk$set(comment = "") 13 | library(sodium) 14 | 15 | # hack for printable bits 16 | random <- function(n = 1){ 17 | if(n != nchar("TTIP is evil")) 18 | return(sodium::random(n)) 19 | repeat { 20 | x <- sodium::random(n) 21 | y <- base::xor(charToRaw("TTIP is evil"), x) 22 | if(all(c(x,y) != 0)) return(x) 23 | } 24 | } 25 | ``` 26 | 27 | This page attempts to give a very basic conceptual introduction to cryptographic methods. Before we start the usual disclaimer: 28 | 29 | ___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.___ 30 | 31 | Now that we got this is out of the way, let's start hacking :) 32 | 33 | ### The XOR operator 34 | 35 | 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 *inverter* because the output of a bit in `x` gets inverted if and only if the corresponding bit in `y` is true: 36 | 37 | ```{r} 38 | # XOR two (8bit) bytes 'x' and 'y' 39 | x <- as.raw(0x7a) 40 | y <- as.raw(0xe4) 41 | z <- base::xor(x, y) 42 | dput(z) 43 | 44 | # Show the bits in each byte 45 | cbind(x = rawToBits(x), y = rawToBits(y), z = rawToBits(z)) 46 | ``` 47 | 48 | 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`. 49 | 50 | ```{r} 51 | # Encrypt message using random one-time-pad 52 | msg <- charToRaw("TTIP is evil") 53 | one_time_pad <- random(length(msg)) 54 | ciphertext <- base::xor(msg, one_time_pad) 55 | 56 | # It's really encrypted 57 | rawToChar(ciphertext) 58 | 59 | # Decrypt with same pad 60 | rawToChar(base::xor(ciphertext, one_time_pad)) 61 | ``` 62 | 63 | This method is perfectly secure and forms the basis for most cryptographic 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. 64 | 65 | ### Stream ciphers 66 | 67 | 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: 68 | 69 | ```{r} 70 | password <- "My secret passphrase" 71 | key <- hash(charToRaw(password)) 72 | nonce <- random(8) 73 | chacha20(size = 20, key, nonce) 74 | ``` 75 | 76 | 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 against 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. 77 | 78 | ```{r} 79 | salsa20(size = 20, key, nonce) 80 | ``` 81 | 82 | 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. 83 | 84 | 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). 85 | 86 | 87 | ```{r} 88 | # Illustrative example. 89 | sha256_ctr <- function(size, key, nonce){ 90 | n <- ceiling(size/32) 91 | output <- raw() 92 | for(i in 1:n){ 93 | counter <- packBits(intToBits(i)) 94 | block <- sha256(c(key, nonce, counter)) 95 | output <- c(output, block) 96 | } 97 | return(output[1:size]) 98 | } 99 | ``` 100 | 101 | This allows us to generate an arbitrary length stream from a single secret key: 102 | 103 | ```{r} 104 | password <- "My secret passphrase" 105 | key <- hash(charToRaw(password)) 106 | nonce <- random(8) 107 | sha256_ctr(50, key, nonce) 108 | ``` 109 | 110 | 111 | 112 | In practice, you should never write your own ciphers. In the remainder we just use the standard Sodium ciphers: [`chacha20`](https://libsodium.gitbook.io/doc/advanced/stream_ciphers/chacha20), [`salsa20`](https://libsodium.gitbook.io/doc/advanced/stream_ciphers/salsa20) or [`xsalsa20`](https://libsodium.gitbook.io/doc/advanced/stream_ciphers/xsalsa20). 113 | 114 | ### Symmetric encryption 115 | 116 | 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`: 117 | 118 | ```{r} 119 | # Encrypt 'message' using 'password' 120 | myfile <- file.path(R.home(), "COPYING") 121 | message <- readBin(myfile, raw(), file.info(myfile)$size) 122 | passwd <- charToRaw("My secret passphrase") 123 | ``` 124 | 125 | A hash function converts the password to a key of suitable size for the stream cipher, which we use to generate a pseudo random stream of equal length to the message: 126 | 127 | ```{r} 128 | # Basic secret key encryption 129 | key <- hash(passwd) 130 | nonce8 <- random(8) 131 | stream <- chacha20(length(message), key, nonce8) 132 | ciphertext <- base::xor(stream, message) 133 | ``` 134 | 135 | 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. 136 | 137 | ```{r} 138 | # Decrypt with the same key 139 | key <- hash(charToRaw("My secret passphrase")) 140 | stream <- chacha20(length(ciphertext), key, nonce8) 141 | out <- base::xor(ciphertext, stream) 142 | 143 | # Print part of the message 144 | cat(substring(rawToChar(out), 1, 120)) 145 | ``` 146 | 147 | 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. 148 | 149 | 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. 150 | 151 | ### Public-key encryption and Diffie-Hellman 152 | 153 | Rather than using a single secret-key, asymmetric (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. 154 | 155 | 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. 156 | 157 | ```{r} 158 | # Create keypair 159 | key <- keygen() 160 | pub <- pubkey(key) 161 | 162 | # Encrypt message for receiver using his/her public key 163 | msg <- serialize(iris, NULL) 164 | ciphertext <- simple_encrypt(msg, pub) 165 | 166 | # Receiver decrypts with his/her private key 167 | out <- simple_decrypt(ciphertext, key) 168 | identical(msg, out) 169 | ``` 170 | 171 | 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 calculate the (same) shared secret key by combining their own private key with the other person's public key: 172 | 173 | ```{r} 174 | # Bob generates keypair 175 | bob_key <- keygen() 176 | bob_pubkey <- pubkey(bob_key) 177 | 178 | # Alice generates keypair 179 | alice_key <- keygen() 180 | alice_pubkey <- pubkey(alice_key) 181 | 182 | # After Bob and Alice exchange pubkey they can both derive the secret 183 | alice_secret <- diffie_hellman(alice_key, bob_pubkey) 184 | bob_secret <- diffie_hellman(bob_key, alice_pubkey) 185 | identical(alice_secret, bob_secret) 186 | ``` 187 | 188 | 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. 189 | 190 | The classical Diffie-Hellman method is based on the discrete logarithm problem with large prime numbers. Sodium uses [curve25519](https://cr.yp.to/ecdh/curve25519-20060209.pdf), a state-of-the-art D-H function by Daniel J. Bernstein designed for use with the elliptic curve Diffie–Hellman (ECDH) key agreement scheme. 191 | --------------------------------------------------------------------------------