Title: | A Modern and Easy-to-Use Crypto Library |
---|---|
Description: | Bindings to 'libsodium' <https://doc.libsodium.org/>: a modern, easy-to-use software library for encryption, decryption, signatures, password hashing and more. Sodium uses curve25519, a state-of-the-art Diffie-Hellman function by Daniel Bernstein, which has become very popular after it was discovered that the NSA had backdoored Dual EC DRBG. |
Authors: | Jeroen Ooms [aut, cre] |
Maintainer: | Jeroen Ooms <[email protected]> |
License: | MIT + file LICENSE |
Version: | 1.4.0 |
Built: | 2024-12-14 23:20:44 UTC |
Source: | https://github.com/r-lib/sodium |
Exchange secure messages through curve25519 authenticated encryption.
auth_encrypt(msg, key, pubkey, nonce = random(24)) auth_decrypt(bin, key, pubkey, nonce = attr(bin, "nonce"))
auth_encrypt(msg, key, pubkey, nonce = random(24)) auth_decrypt(bin, key, pubkey, nonce = attr(bin, "nonce"))
msg |
message to be encrypted |
key |
your own private key |
pubkey |
other person's public key |
nonce |
non-secret unique data to randomize the cipher |
bin |
encrypted ciphertext generated by |
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.
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.
Most people share their public key by posting them on their website or on a public keyserver. Another alternative is having your public key signed by a mutually trusted third party. HTTPS does this using Certificate Authorities.
https://libsodium.gitbook.io/doc/public-key_cryptography/authenticated_encryption
# Bob's keypair: bob_key <- keygen() bob_pubkey <- pubkey(bob_key) # Alice's keypair: alice_key <- keygen() alice_pubkey <- pubkey(alice_key) # Bob sends encrypted message for Alice: msg <- charToRaw("TTIP is evil") ciphertext <- auth_encrypt(msg, bob_key, alice_pubkey) # Alice verifies and decrypts with her key out <- auth_decrypt(ciphertext, alice_key, bob_pubkey) stopifnot(identical(out, msg)) # Alice sends encrypted message for Bob msg <- charToRaw("Let's protest") ciphertext <- auth_encrypt(msg, alice_key, bob_pubkey) # Bob verifies and decrypts with his key out <- auth_decrypt(ciphertext, bob_key, alice_pubkey) stopifnot(identical(out, msg))
# Bob's keypair: bob_key <- keygen() bob_pubkey <- pubkey(bob_key) # Alice's keypair: alice_key <- keygen() alice_pubkey <- pubkey(alice_key) # Bob sends encrypted message for Alice: msg <- charToRaw("TTIP is evil") ciphertext <- auth_encrypt(msg, bob_key, alice_pubkey) # Alice verifies and decrypts with her key out <- auth_decrypt(ciphertext, alice_key, bob_pubkey) stopifnot(identical(out, msg)) # Alice sends encrypted message for Bob msg <- charToRaw("Let's protest") ciphertext <- auth_encrypt(msg, alice_key, bob_pubkey) # Bob verifies and decrypts with his key out <- auth_decrypt(ciphertext, bob_key, alice_pubkey) stopifnot(identical(out, msg))
The Diffie-Hellman key exchange method allows two parties that have no prior knowledge of each other to jointly establish a shared secret key over an insecure channel. This key can then be used to encrypt subsequent communications using a symmetric key cipher.
diffie_hellman(key, pubkey)
diffie_hellman(key, pubkey)
key |
your private key |
pubkey |
other person's public key |
Encryption methods as implemented in data_encrypt require that parties have a shared secret key. But often we wish to establish a secure channel with a party we have no prior relationship with. Diffie-hellman is a method for jointly agreeing on a shared secret without ever exchanging the secret itself. Sodium implements Curve25519, a state-of-the-art Diffie-Hellman function suitable for a wide variety of applications.
The method conists of two steps (see examples). First, both parties generate a random private key and derive the corresponding public key using pubkey. These public keys are not confidential and can be exchanged over an insecure channel. After the public keys are exchanged, both parties will be able to calculate the (same) shared secret by combining his/her own private key with the other person's public key using diffie_hellman.
After the shared secret has been established, the private and public keys are disposed, and parties can start encrypting communications based on the shared secret using e.g. data_encrypt. Because the shared secret cannot be calculated using only the public keys, the process is safe from eavesdroppers.
Returns a shared secret key which can be used in e.g. data_encrypt.
https://doc.libsodium.org/advanced/scalar_multiplication.html
# Bob generates keypair bob_key <- keygen() bob_pubkey <- pubkey(bob_key) # Alice generates keypair alice_key <- keygen() alice_pubkey <- pubkey(alice_key) # After Bob and Alice exchange pubkey they can both derive the secret alice_secret <- diffie_hellman(alice_key, bob_pubkey) bob_secret <- diffie_hellman(bob_key, alice_pubkey) stopifnot(identical(alice_secret, bob_secret))
# Bob generates keypair bob_key <- keygen() bob_pubkey <- pubkey(bob_key) # Alice generates keypair alice_key <- keygen() alice_pubkey <- pubkey(alice_key) # After Bob and Alice exchange pubkey they can both derive the secret alice_secret <- diffie_hellman(alice_key, bob_pubkey) bob_secret <- diffie_hellman(bob_key, alice_pubkey) stopifnot(identical(alice_secret, bob_secret))
Functions to calculate cryptographic hash of a message, with optionally a key for HMAC applications. For storing passwords, use password_store instead.
hash(buf, key = NULL, size = 32) scrypt(buf, salt = raw(32), size = 32) argon2(buf, salt = raw(16), size = 32) shorthash(buf, key) sha512(buf, key = NULL) sha256(buf, key = NULL)
hash(buf, key = NULL, size = 32) scrypt(buf, salt = raw(32), size = 32) argon2(buf, salt = raw(16), size = 32) shorthash(buf, key) sha512(buf, key = NULL) sha256(buf, key = NULL)
buf |
data to be hashed |
key |
key for HMAC hashing. Optional, except for in |
size |
length of the output hash. Must be between 16 and 64 (recommended is 32) |
salt |
non-confidential random data to seed the algorithm |
The generic hash
function is recommended for most applications. It uses
dynamic length
BLAKE2b
where output size can be any value between 16 bytes (128bit) and 64 bytes (512bit).
The scrypt hash function is designed to be CPU and memory expensive to protect against brute force attacks. This algorithm is also used by the password_store function.
The argon2 hash function is also designed to be CPU and memory expensive to protect against brute force attacks. Argon2 is a password-hashing function that summarizes the state of the art in the design of memory-hard functions
The shorthash
function is a special 8 byte (64 bit) hash based on
SipHash-2-4.
The output of this function is only 64 bits (8 bytes). It is useful for in e.g.
Hash tables, but it should not be considered collision-resistant.
Hash functions can be used for HMAC by specifying a secret key
. They key size
for shorthash
is 16 bytes, for sha256
it is 32 bytes and for sha512
it is 64 bytes. For hash
the key size can be any value between 16 and 62,
recommended is at least 32.
https://libsodium.gitbook.io/doc/hashing/generic_hashing
# Basic hashing msg <- serialize(iris, NULL) hash(msg) sha256(msg) sha512(msg) scrypt(msg) # Generate keys from passphrase passphrase <- charToRaw("This is super secret") key <- hash(passphrase) shortkey <- hash(passphrase, size = 16) longkey <- hash(passphrase, size = 64) # HMAC (hashing with key) hash(msg, key = key) shorthash(msg, shortkey) sha256(msg, key = key) sha512(msg, key = longkey)
# Basic hashing msg <- serialize(iris, NULL) hash(msg) sha256(msg) sha512(msg) scrypt(msg) # Generate keys from passphrase passphrase <- charToRaw("This is super secret") key <- hash(passphrase) shortkey <- hash(passphrase, size = 16) longkey <- hash(passphrase, size = 64) # HMAC (hashing with key) hash(msg, key = key) shorthash(msg, shortkey) sha256(msg, key = key) sha512(msg, key = longkey)
Functions to generate a random private key and calculate the corresponding curve25519 public key.
keygen(seed = random(32)) pubkey(key)
keygen(seed = random(32)) pubkey(key)
seed |
random data to seed the keygen |
key |
private key for which to calculate the public key |
Asymmetric methods rely on public-private keypairs. The private keys are secret and should never be shared with anyone. The public key on the other hand is not confidential and should be shared with the other parties. Public keys are typically published on the users's website or posted in public directories or keyservers.
The two main applications for public key cryptography are encryption and authentication.
In public key encryption, data that is encrypted using 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. Basic encryption is implemented in simple_encrypt.
Authentication works the other way around. In public key authentication, the owner of the private key creates a 'signature' (an authenticated checksum) for a message in a way that allows anyone who knows the user's public key to verify that this message was indeed signed by the owner of the private key.
If both sender and receiver know each other's public key, the two methods can be combined so that each message going back and forth is signed by the sender and encrypted for the receiver. This protects both against eavesdropping and MITM tampering, creating a fully secure channel.
# Create keypair key <- keygen() pub <- pubkey(key) # Basic encryption msg <- serialize(iris, NULL) ciphertext <- simple_encrypt(msg, pub) out <- simple_decrypt(ciphertext, key) stopifnot(identical(msg, out))
# Create keypair key <- keygen() pub <- pubkey(key) # Basic encryption msg <- serialize(iris, NULL) ciphertext <- simple_encrypt(msg, pub) out <- simple_decrypt(ciphertext, key) stopifnot(identical(msg, out))
Wrapper that implements best practices for storing passwords based on scrypt with a random salt.
password_store(password) password_verify(hash, password)
password_store(password) password_verify(hash, password)
password |
a string of length one with a password |
hash |
a hash string of length one generated by |
The password_store function returns an ASCII encoded string which contains the result of a memory-hard, CPU-intensive hash function along with the automatically generated salt and other parameters required to verify the password. Use password_verify to verify a password from this string.
https://doc.libsodium.org/password_hashing/
# Example password password <- "I like cookies" # Hash is what you store in the database hash <- password_store(password) # To verify the password when the user logs in stopifnot(password_verify(hash, password))
# Example password password <- "I like cookies" # Hash is what you store in the database hash <- password_store(password) # To verify the password when the user logs in stopifnot(password_verify(hash, password))
Cryptographic signatures can be used to verify the integrity of a message using the author's public key.
sig_sign(msg, key) sig_verify(msg, sig, pubkey) sig_keygen(seed = random(32)) sig_pubkey(key)
sig_sign(msg, key) sig_verify(msg, sig, pubkey) sig_keygen(seed = random(32)) sig_pubkey(key)
msg |
message to sign |
key |
private key to sign message with |
sig |
a signature generated by |
pubkey |
a public key of the keypair used by the signature |
seed |
random data to seed the keygen |
A signature is an authenticated checksum that can be used to check that a message (any data) was created by a particular author and was not tampered with. The signature is created using a private key and can be verified from the corresponding public key.
Signatures are used when the message itself is not confidential but integrity is important. A common use is for software repositories where maintainers include a signature of the package index. This allows client package managers to verify that the binaries were not modified by intermediate parties in the distribution process.
For confidential data, use authenticated encryption (auth_encrypt) which allows for sending signed and encrypted messages in a single method.
Currently sodium requires a different type of key pairfor signatures (ed25519) than for encryption (curve25519).
https://doc.libsodium.org/public-key_cryptography/public-key_signatures.html
# Generate keypair key <- sig_keygen() pubkey <- sig_pubkey(key) # Create signature msg <- serialize(iris, NULL) sig <- sig_sign(msg, key) sig_verify(msg, sig, pubkey)
# Generate keypair key <- sig_keygen() pubkey <- sig_pubkey(key) # Create signature msg <- serialize(iris, NULL) sig <- sig_sign(msg, key) sig_verify(msg, sig, pubkey)
Create an encrypted message (sealed box) from a curve25519 public key.
simple_encrypt(msg, pubkey) simple_decrypt(bin, key)
simple_encrypt(msg, pubkey) simple_decrypt(bin, key)
msg |
message to be encrypted |
pubkey |
public key of the receiver |
bin |
encrypted ciphertext |
key |
private key of the receiver |
Simple public key encryption allows for sending anonymous encrypted messages to a recipient given its public key. Only the recipient can decrypt these messages, using its private key.
While the recipient can verify the integrity of the message, it cannot verify the identity of the sender. For sending authenticated encrypted messages, use auth_encrypt and auth_decrypt.
https://doc.libsodium.org/public-key_cryptography/sealed_boxes.html
# Generate keypair key <- keygen() pub <- pubkey(key) # Encrypt message with pubkey msg <- serialize(iris, NULL) ciphertext <- simple_encrypt(msg, pub) # Decrypt message with private key out <- simple_decrypt(ciphertext, key) stopifnot(identical(out, msg))
# Generate keypair key <- keygen() pub <- pubkey(key) # Encrypt message with pubkey msg <- serialize(iris, NULL) ciphertext <- simple_encrypt(msg, pub) # Decrypt message with private key out <- simple_decrypt(ciphertext, key) stopifnot(identical(out, msg))
The functions bin2hex
and hex2bin
convert between binary (raw)
vectors and corresponding string in hexadecimal notation. The random
function generates n
crypto secure random bytes.
bin2hex(bin) hex2bin(hex, ignore = ":") random(n = 1)
bin2hex(bin) hex2bin(hex, ignore = ":") random(n = 1)
bin |
raw vector with binary data to convert to hex string |
hex |
a string with hexadecimal characters to parse into a binary (raw) vector. |
ignore |
a string with characters to ignore from |
n |
number of random bytes or numbers to generate |
# Convert raw to hex string and back test <- charToRaw("test 123") x <- bin2hex(test) y <- hex2bin(x) stopifnot(identical(test, y)) stopifnot(identical(x, paste(test, collapse = ""))) # Parse text with characters x2 <- paste(test, collapse = ":") y2 <- hex2bin(x2, ignore = ":") stopifnot(identical(test, y2))
# Convert raw to hex string and back test <- charToRaw("test 123") x <- bin2hex(test) y <- hex2bin(x) stopifnot(identical(test, y)) stopifnot(identical(x, paste(test, collapse = ""))) # Parse text with characters x2 <- paste(test, collapse = ":") y2 <- hex2bin(x2, ignore = ":") stopifnot(identical(test, y2))
Generate deterministic streams of random data based off a secret key and random nonce.
chacha20(size, key, nonce) xchacha20(size, key, nonce) salsa20(size, key, nonce) xsalsa20(size, key, nonce)
chacha20(size, key, nonce) xchacha20(size, key, nonce) salsa20(size, key, nonce) xsalsa20(size, key, nonce)
size |
length of cipher stream in bytes |
key |
secret key used by the cipher |
nonce |
non-secret unique data to randomize the cipher |
You usually don't need to call these methods directly. For local encryption use data_encrypt. For secure communication use simple_encrypt or auth_encrypt.
Random streams form the basis for most cryptographic methods. Based a shared secret (the key) we generate a predictable random data stream of equal length as the message we need to encrypt. Then we xor the message data with this random stream, which effectively inverts each byte in the message with probabiliy 0.5. The message can be decrypted by re-generating exactly the same random data stream and xor'ing it back. See the examples.
Each stream generator requires a key
and a nonce
. Both are required to re-generate
the same stream for decryption. The key forms the shared secret and should only known to
the trusted parties. The nonce
is not secret and should be stored or sent along
with the ciphertext. The purpose of the nonce
is to make a random stream unique
to protect gainst re-use attacks. This way you can re-use a your key to encrypt multiple
messages, as long as you never re-use the same nonce.
https://libsodium.gitbook.io/doc/advanced/stream_ciphers/xsalsa20
# Very basic encryption myfile <- file.path(R.home(), "COPYING") message <- readBin(myfile, raw(), file.info(myfile)$size) passwd <- charToRaw("My secret passphrase") # Encrypt: key <- hash(passwd) nonce8 <- random(8) stream <- chacha20(length(message), key, nonce8) ciphertext <- base::xor(stream, message) # Decrypt: stream <- chacha20(length(ciphertext), key, nonce8) out <- base::xor(ciphertext, stream) stopifnot(identical(out, message)) # Other stream ciphers stream <- salsa20(10000, key, nonce8) stream <- xsalsa20(10000, key, random(24)) stream <- xchacha20(10000, key, random(24))
# Very basic encryption myfile <- file.path(R.home(), "COPYING") message <- readBin(myfile, raw(), file.info(myfile)$size) passwd <- charToRaw("My secret passphrase") # Encrypt: key <- hash(passwd) nonce8 <- random(8) stream <- chacha20(length(message), key, nonce8) ciphertext <- base::xor(stream, message) # Decrypt: stream <- chacha20(length(ciphertext), key, nonce8) out <- base::xor(ciphertext, stream) stopifnot(identical(out, message)) # Other stream ciphers stream <- salsa20(10000, key, nonce8) stream <- xsalsa20(10000, key, random(24)) stream <- xchacha20(10000, key, random(24))
Encryption with authentication using a 256 bit shared secret. Mainly useful for encrypting local data. For secure communication use public-key encryption (simple_encrypt and auth_encrypt).
data_encrypt(msg, key, nonce = random(24)) data_decrypt(bin, key, nonce = attr(bin, "nonce")) data_tag(msg, key)
data_encrypt(msg, key, nonce = random(24)) data_decrypt(bin, key, nonce = attr(bin, "nonce")) data_tag(msg, key)
msg |
message to be encrypted |
key |
shared secret key used for both encryption and decryption |
nonce |
non-secret unique data to randomize the cipher |
bin |
encrypted ciphertext |
Symmetric encryption uses a secret key to encode and decode a message. This can be used to encrypt local data on disk, or as a building block for more complex methods.
Because the same secret
is used for both encryption and decryption, symmetric
encryption by itself is impractical for communication. For exchanging secure messages
with other parties, use assymetric (public-key) methods (see simple_encrypt or
auth_encrypt).
The nonce
is not confidential but required for decryption, and should be
stored or sent along with the ciphertext. The purpose of the nonce
is to
randomize the cipher to protect gainst re-use attacks. This way you can use one
and the same secret for encrypting multiple messages.
The data_tag function generates an authenticated hash that can be stored
alongside the data to be able to verify the integrity of the data later on. For
public key signatures see sig_sign
instead.
https://libsodium.gitbook.io/doc/public-key_cryptography/authenticated_encryption
# 256-bit key key <- sha256(charToRaw("This is a secret passphrase")) msg <- serialize(iris, NULL) # Encrypts with random nonce cipher <- data_encrypt(msg, key) orig <- data_decrypt(cipher, key) stopifnot(identical(msg, orig)) # Tag the message with your key (HMAC) tag <- data_tag(msg, key)
# 256-bit key key <- sha256(charToRaw("This is a secret passphrase")) msg <- serialize(iris, NULL) # Encrypts with random nonce cipher <- data_encrypt(msg, key) orig <- data_decrypt(cipher, key) stopifnot(identical(msg, orig)) # Tag the message with your key (HMAC) tag <- data_tag(msg, key)