### Install libsodium on CentOS Source: https://github.com/rubycrypto/rbnacl/wiki/Installing-libsodium Enable EPEL repository and install libsodium using yum on CentOS. ```bash yum install libsodium ``` -------------------------------- ### Install libsodium on OS X Source: https://github.com/rubycrypto/rbnacl/wiki/Installing-libsodium Use Homebrew to install the libsodium library on macOS. ```bash brew install libsodium ``` -------------------------------- ### Install libsodium on Debian Source: https://github.com/rubycrypto/rbnacl/wiki/Installing-libsodium Install the libsodium18 package on Debian systems. ```bash apt-get install libsodium18 ``` -------------------------------- ### Generate Base Point and Public Key Source: https://context7.com/rubycrypto/rbnacl/llms.txt Demonstrates how to get the standard base point and manually compute a public key from private key bytes using RbNaCl. ```ruby base = RbNaCl::GroupElement.base private_key_bytes = RbNaCl::Random.random_bytes(32) public_key_bytes = base.mult(private_key_bytes) ``` -------------------------------- ### Blake2b Hashing Operations Source: https://context7.com/rubycrypto/rbnacl/llms.txt Examples for keyed hashing, domain separation with salt/personalization, and incremental hashing. ```ruby key = RbNaCl::Random.random_bytes(32) keyed_hash = RbNaCl::Hash.blake2b(message, key: key) #=> "\x..." (64 bytes) # Blake2b with salt and personalization (domain separation) salt = RbNaCl::Random.random_bytes(16) personal = "myapp-v1".ljust(16, "\x00") # 16 bytes, padded personalized = RbNaCl::Hash.blake2b( message, salt: salt, personal: personal ) # === Incremental Hashing with Blake2b === hasher = RbNaCl::Hash::Blake2b.new(digest_size: 32) hasher.update("First chunk") hasher.update("Second chunk") final_digest = hasher.digest #=> "\x..." (32 bytes) # Hex encoding helper RbNaCl::Util.bin2hex(sha256_digest) #=> "b10e4d0e5b5c3a1f..." (hex string) ``` -------------------------------- ### Install libsodium on Ubuntu (18.04 LTS) Source: https://github.com/rubycrypto/rbnacl/wiki/Installing-libsodium Install the libsodium23 package on Ubuntu Bionic (18.04 LTS). ```bash apt-get install libsodium23 ``` -------------------------------- ### Initialize Public-Key SimpleBox Source: https://github.com/rubycrypto/rbnacl/wiki/SimpleBox Creates a SimpleBox for asymmetric encryption using a keypair. ```ruby # We'll first generate a random private key for ourselves (even though we're just the sender) alice_private_key = RbNaCl::PrivateKey.generate # Next we need to send this to the recipient over a secure channel. Some pseudocode: send_public_key_to_bob(alice_private_key.public_key) # We'll also need to get Bob's public key over a secure channel somehow. Pseudocode: bob_public_key = get_recipient_public_key # Alice can now make a Simplebox for sending messages to Bob box = RbNaCl::SimpleBox.from_keypair(bob_public_key, alice_private_key) ``` ```ruby # We'll first generate a random private key for ourselves bob_private_key = RbNaCl::PrivateKey.generate # First we need to get Alice's public key over a secure channel alice_public_key = get_sender_public_key # Next we need to send Alice our public key over a secure channel send_public_key_to_alice(bob_private_key.public_key) # Once we've done that, Bob too can make a box for receiving messages box = RbNaCl::SimpleBox.from_keypair(alice_public_key, bob_private_key) ``` -------------------------------- ### Generate Keys and Initialize Box in Ruby Source: https://github.com/rubycrypto/rbnacl/wiki/Public-Key-Encryption Demonstrates generating a private key, deriving the public key, and initializing RbNaCl::Box instances for both sender and receiver. The shared key is derived upon the first message exchange. ```ruby # generate a private key private_key = RbNaCl::PrivateKey.generate # send the public key to someone (or everyone) public_key = private_key.public_key # initialize the box box = RbNaCl::Box.new(someones_public_key, private_key) # the other person does this with your key other_box = RbNaCl::Box.new(public_key, someones_private_key) # on sending or receiving the first message, the shared key is derived. ``` -------------------------------- ### Encrypt and Decrypt with SimpleBox Source: https://context7.com/rubycrypto/rbnacl/llms.txt Demonstrates symmetric and public-key encryption using SimpleBox, which handles nonce generation automatically. ```ruby require 'rbnacl' # === Secret-Key Encryption with SimpleBox === # Generate a random 32-byte secret key key = RbNaCl::Random.random_bytes(RbNaCl::SecretBox.key_bytes) #=> "\xAB\xCD..." (32 random bytes) # Create a SimpleBox for symmetric encryption box = RbNaCl::SimpleBox.from_secret_key(key) # Encrypt a message (nonce is automatically generated and prepended) plaintext = "Hello, World!" ciphertext = box.encrypt(plaintext) #=> "\x..." (random bytes: 24-byte nonce + encrypted message + 16-byte authenticator) # Decrypt the message decrypted = box.decrypt(ciphertext) #=> "Hello, World!" # === Public-Key Encryption with SimpleBox === # Alice generates her keypair alice_private = RbNaCl::PrivateKey.generate alice_public = alice_private.public_key # Bob generates his keypair bob_private = RbNaCl::PrivateKey.generate bob_public = bob_private.public_key # Alice creates a box to send messages to Bob alice_box = RbNaCl::SimpleBox.from_keypair(bob_public, alice_private) # Bob creates a box to receive messages from Alice bob_box = RbNaCl::SimpleBox.from_keypair(alice_public, bob_private) # Alice encrypts a message for Bob secret_message = "Meet me at midnight" encrypted = alice_box.encrypt(secret_message) # Bob decrypts the message from Alice bob_box.decrypt(encrypted) #=> "Meet me at midnight" # Tampering detection - raises RbNaCl::CryptoError begin tampered = encrypted.dup tampered[30] = (tampered[30].ord ^ 0xFF).chr # flip some bits bob_box.decrypt(tampered) rescue RbNaCl::CryptoError => e puts "Tampered message detected: #{e.message}" end ``` -------------------------------- ### Initialize Secret-Key SimpleBox Source: https://github.com/rubycrypto/rbnacl/wiki/SimpleBox Creates a SimpleBox for symmetric encryption using a generated secret key. ```ruby key = RbNaCl::Random.random_bytes(RbNaCl::SecretBox.key_bytes) ``` ```ruby box = RbNaCl::SimpleBox.from_secret_key(key) ``` -------------------------------- ### Initialize ChaCha20-Poly1305 AEAD Source: https://context7.com/rubycrypto/rbnacl/llms.txt Initializes the ChaCha20-Poly1305 IETF cipher for authenticated encryption with additional data. ```ruby require 'rbnacl' # Generate a 32-byte key key = RbNaCl::Random.random_bytes(32) # Create the AEAD cipher (IETF variant - preferred) cipher = RbNaCl::AEAD::ChaCha20Poly1305IETF.new(key) ``` -------------------------------- ### Derive keys with Argon2 Source: https://github.com/rubycrypto/rbnacl/wiki/Password-Hashing Uses the Argon2 algorithm to derive key material, requiring libsodium 1.0.9 or higher. ```ruby # Get the password somehow password = ... # Generate a random salt (libsodium enforces 16-byte salts) salt = RbNaCl::Random.random_bytes(RbNaCl::PasswordHash::Argon2::SALTBYTES) # Argon2 CPU and memory cost parameters opslimit = 5 memlimit = 7_256_678 # Size of digest to compute in bytes (default 64) digest_size = 64 output_code_example = RbNaCl::PasswordHash.argon2( password, salt, opslimit, memlimit, digest_size ) ``` -------------------------------- ### Derive keys with scrypt Source: https://github.com/rubycrypto/rbnacl/wiki/Password-Hashing Uses the scrypt algorithm to derive key material from a password and salt with specified CPU and memory costs. ```ruby # Get the password somehow password = ... # Generate a random salt (libsodium enforces 32-byte salts) salt = RbNaCl::Random.random_bytes(RbNaCl::PasswordHash::SCrypt::SALTBYTES) # scrypt CPU and memory cost parameters opslimit = 2**20 memlimit = 2**24 # Size of digest to compute in bytes (default 64) digest_size = 64 output_key_material = RbNaCl::PasswordHash.scrypt( password, salt, opslimit, memlimit, digest_size ) ``` -------------------------------- ### Password Hashing with Argon2 and scrypt Source: https://context7.com/rubycrypto/rbnacl/llms.txt Derive keys from passwords or generate storage hashes using Argon2 and scrypt. Argon2 is recommended for new applications. ```ruby require 'rbnacl' password = "correct horse battery staple" # === Argon2 (Recommended) === # Generate a random salt (16 bytes required for Argon2) argon2_salt = RbNaCl::Random.random_bytes(RbNaCl::PasswordHash::Argon2::SALTBYTES) # Cost parameters (tune based on your security requirements and hardware) opslimit = 4 # Number of iterations memlimit = 2**24 # Memory in bytes (16 MB) digest_size = 32 # Output key length in bytes # Derive a key from password (KDF mode) derived_key = RbNaCl::PasswordHash.argon2( password, argon2_salt, opslimit, memlimit, digest_size ) #=> "\x..." (32 bytes - can be used as encryption key) # Password storage mode (generates encoded string with parameters) password_hash = RbNaCl::PasswordHash.argon2_str(password) #=> "$argon2id$v=19$m=65536,t=2,p=1$..." (encoded hash string) # Verify password against stored hash is_valid = RbNaCl::PasswordHash.argon2_valid?(password, password_hash) #=> true wrong_password = "incorrect horse battery staple" RbNaCl::PasswordHash.argon2_valid?(wrong_password, password_hash) #=> false # === scrypt === # Generate a random salt (32 bytes required for scrypt) scrypt_salt = RbNaCl::Random.random_bytes(RbNaCl::PasswordHash::SCrypt::SALTBYTES) # scrypt cost parameters scrypt_opslimit = 2**20 # CPU cost scrypt_memlimit = 2**24 # Memory cost (16 MB) # Derive a key from password scrypt_key = RbNaCl::PasswordHash.scrypt( password, scrypt_salt, scrypt_opslimit, scrypt_memlimit, 32 # output length ) #=> "\x..." (32 bytes) # Use derived key for encryption box = RbNaCl::SimpleBox.from_secret_key(derived_key) encrypted = box.encrypt("Secret data protected by password") ``` -------------------------------- ### Encrypt and Decrypt Messages with RbNaCl::Box Source: https://github.com/rubycrypto/rbnacl/wiki/Public-Key-Encryption Shows how to encrypt a message using a nonce and the RbNaCl::Box instance, and then decrypt it using the corresponding other party's box. A nonce must be unique for each encryption with the same key pair. ```ruby # encrypt a message using the box. # First, make a nonce. One simple strategy is to use 24 random bytes. # The nonce isn't secret, and can be sent with the ciphertext. # crypto_box provides the #nonce_bytes method for its nonce length. nonce = RbNaCl::Random.random_bytes(box.nonce_bytes) message = "..." ciphertext = box.encrypt(nonce, message) #=> "..." # string of random looking bytes, 16 bytes longer than message. # The extra 16-bytes are the authenticator # decrypt a message # NB: Same nonce used here. decrypted_message = other_box.decrypt(nonce, ciphertext) #=> "..." ``` -------------------------------- ### Hash and verify passwords with Argon2 Source: https://github.com/rubycrypto/rbnacl/wiki/Password-Hashing Generates a password digest string and verifies a password against that digest. ```ruby digest = RbNaCl::PasswordHash.argon2_str(password) => "$argon2i$v=19$m=32768,t=4,p=1$aRJ9T7ZVYTVIpkJCbCy9bw$hWiOb6QwZ591nV8tXAfqSLuPD8yqI+ZN/B7ajmU33Zc" ... RbNaCl::PasswordHash.argon2_valid?(password, digest) => true ``` -------------------------------- ### Encrypt Data with SimpleBox Source: https://github.com/rubycrypto/rbnacl/wiki/SimpleBox Encrypts plaintext using an initialized SimpleBox instance. ```ruby ciphertext = box.encrypt(plaintext) ``` -------------------------------- ### Compare Strings in Constant Time (Ruby) Source: https://github.com/rubycrypto/rbnacl/wiki/Utilities Use RbNaCl::Util.verify32 for securely comparing two 32-byte strings in constant time to prevent timing attacks. See also verify16 for 16-byte strings. ```ruby # constant time comparisons. RbNaCl::Util.verify32(string_one, string_two) #=> true/false. See also verify16 ``` -------------------------------- ### Encrypt and Decrypt with RbNaCl SecretBox Source: https://github.com/rubycrypto/rbnacl/wiki/Secret-Key-Encryption Demonstrates authenticated encryption and decryption using RbNaCl's SecretBox (XSalsa20Poly1305). Ensure a unique nonce is used for each encryption with the same key to maintain security. Tampered ciphertexts will raise a CryptoError. ```ruby # Generate a random secret key (or perhaps use scrypt or Argon2) key = RbNaCl::Random.random_bytes(RbNaCl::SecretBox.key_bytes) # Initialize the box secret_box = RbNaCl::SecretBox.new(key) # First, make a nonce: A single-use value never repeated under the same key # The nonce isn't secret, and can be sent with the ciphertext. # The cipher instance has a nonce_bytes method for determining how many bytes should be in a nonce nonce = RbNaCl::Random.random_bytes(secret_box.nonce_bytes) # Encrypt a message with SecretBox message = "..." ciphertext = secret_box.encrypt(nonce, message) #=> "..." # string of random looking bytes, 16 bytes longer than message. # The extra 16-bytes are the authenticator # Decrypt a message, passing in the same nonce we used to encrypt decrypted_message = secret_box.decrypt(nonce, ciphertext) #=> "..." # But if the ciphertext has been tampered with: secret_box.open(nonce, corrupted_ciphertext) #=> RbNaCl::CryptoError exception is raised. # Chosen ciphertext attacks are prevented by authentication and constant-time comparisons ``` -------------------------------- ### Calculate public key from private key Source: https://github.com/rubycrypto/rbnacl/wiki/Scalar-Multiplication Derives a public key by multiplying the standard group element by the private key bytes. ```ruby public_key_bytes = RbNaCl::GroupElement.base.mult(private_key_bytes) ``` -------------------------------- ### Compute Cryptographic Hashes in Ruby Source: https://context7.com/rubycrypto/rbnacl/llms.txt Generate fixed-length digests using SHA-256, SHA-512, or Blake2b. Blake2b is recommended for new applications. ```ruby require 'rbnacl' message = "Data to hash" # === SHA-256 (32 bytes output) === sha256_digest = RbNaCl::Hash.sha256(message) #=> "\x..." (32 bytes) puts RbNaCl::Util.bin2hex(sha256_digest) #=> "b10e4d..." (64 hex characters) # === SHA-512 (64 bytes output) === sha512_digest = RbNaCl::Hash.sha512(message) #=> "\x..." (64 bytes) # === Blake2b (default 64 bytes, configurable 1-64 bytes) === # Default output (64 bytes) blake2b_digest = RbNaCl::Hash.blake2b(message) #=> "\x..." (64 bytes) # Custom output length blake2b_32 = RbNaCl::Hash.blake2b(message, digest_size: 32) #=> "\x..." (32 bytes) ``` -------------------------------- ### Generate and Use Ed25519 Signing Key Source: https://github.com/rubycrypto/rbnacl/wiki/Digital-Signatures Generates a new signing key, serializes it, signs a message, and obtains the corresponding verify key. Use this for the signer's perspective. ```ruby # Generate a new random signing key signing_key = RbNaCl::SigningKey.generate # Serialize key to bytestring - load using RbNaCl::SigningKey.new(bytes) signing_key.to_s # Sign a message with the signing key signature = signing_key.sign(message) # Obtain the verify key for a given signing key verify_key = signing_key.verify_key # Convert the verify key to a string to send it to a third party verify_key.to_s ``` -------------------------------- ### Utility Functions for Security Source: https://context7.com/rubycrypto/rbnacl/llms.txt Perform constant-time comparisons, hex encoding/decoding, and length validation. ```ruby require 'rbnacl' # === Constant-Time Comparison (Timing Attack Prevention) === # Compare 16-byte strings a16 = "\x00" * 16 b16 = "\x00" * 16 RbNaCl::Util.verify16(a16, b16) #=> true # Compare 32-byte strings hash1 = RbNaCl::Hash.sha256("message") hash2 = RbNaCl::Hash.sha256("message") RbNaCl::Util.verify32(hash1, hash2) #=> true # Compare 64-byte strings hash64a = RbNaCl::Hash.sha512("data") hash64b = RbNaCl::Hash.sha512("data") RbNaCl::Util.verify64(hash64a, hash64b) #=> true # Strict versions raise ArgumentError on wrong length RbNaCl::Util.verify32!(hash1, hash2) # raises if not 32 bytes each # === Hex Encoding/Decoding === binary_data = "\xDE\xAD\xBE\xEF" hex_string = RbNaCl::Util.bin2hex(binary_data) #=> "deadbeef" restored = RbNaCl::Util.hex2bin(hex_string) #=> "\xDE\xAD\xBE\xEF" # === Zero Padding === RbNaCl::Util.zeros(16) #=> "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" # Check string length (raises LengthError if wrong) key = RbNaCl::Random.random_bytes(32) RbNaCl::Util.check_length(key, 32, "Encryption key") #=> true ``` -------------------------------- ### Diffie-Hellman Key Exchange Source: https://context7.com/rubycrypto/rbnacl/llms.txt Illustrates a Diffie-Hellman key exchange between two parties, Alice and Bob, to establish a shared secret. Ensure the derived key is used for encryption, not the raw shared secret. ```ruby # Alice generates her keypair alice_private = RbNaCl::Random.random_bytes(32) alice_public = RbNaCl::GroupElement.base.mult(alice_private) # Bob generates his keypair bob_private = RbNaCl::Random.random_bytes(32) bob_public = RbNaCl::GroupElement.base.mult(bob_private) # Alice computes shared secret using Bob's public key alice_shared = RbNaCl::GroupElement.new(bob_public).mult(alice_private) # Bob computes the same shared secret using Alice's public key bob_shared = RbNaCl::GroupElement.new(alice_public).mult(bob_private) # Both parties now have the same shared secret alice_shared == bob_shared #=> true # IMPORTANT: Do not use shared_secret directly as a key! # Pass it through a KDF (key derivation function) first derived_key = RbNaCl::Hash.blake2b(alice_shared, digest_size: 32) ``` -------------------------------- ### Implement Ed25519 Digital Signatures in Ruby Source: https://context7.com/rubycrypto/rbnacl/llms.txt Generate signing keys, sign messages, and verify signatures using the Ed25519 scheme. Includes handling for both detached signatures and attached signed messages. ```ruby require 'rbnacl' # === Key Generation === # Generate a new signing key (private key) signing_key = RbNaCl::SigningKey.generate # Derive the verify key (public key) from the signing key verify_key = signing_key.verify_key # Serialize keys for storage signing_key_bytes = signing_key.to_s # 32 bytes (private) verify_key_bytes = verify_key.to_s # 32 bytes (public) # Reconstruct keys from bytes restored_signing = RbNaCl::SigningKey.new(signing_key_bytes) restored_verify = RbNaCl::VerifyKey.new(verify_key_bytes) # === Signing Messages === message = "I agree to the terms and conditions" # Create a detached signature (signature separate from message) signature = signing_key.sign(message) #=> "\x..." (64 bytes) # Create a signed message (signature prepended to message) signed_message = signing_key.sign_attached(message) #=> "\x..." (64 bytes signature + message) # === Verifying Signatures === # Verify detached signature - raises BadSignatureError if invalid begin verify_key.verify(signature, message) puts "Signature is valid!" rescue RbNaCl::BadSignatureError puts "Invalid signature - message may be forged" end # Verify and extract message from signed message begin original = verify_key.verify_attached(signed_message) puts "Verified message: #{original}" rescue RbNaCl::BadSignatureError puts "Invalid signed message" end # === Tampering Detection === # Modified message fails verification begin tampered_message = "I agree to the terms and CONDITIONS" verify_key.verify(signature, tampered_message) rescue RbNaCl::BadSignatureError puts "Signature verification failed - message was modified" end # Modified signature fails verification begin bad_signature = signature.dup bad_signature[0] = "\x00" verify_key.verify(bad_signature, message) rescue RbNaCl::BadSignatureError puts "Invalid signature detected" end ``` -------------------------------- ### Encrypt and Decrypt with AEAD ChaCha20Poly1305 Source: https://github.com/rubycrypto/rbnacl/wiki/Secret-Key-Encryption Shows how to perform authenticated encryption with additional data (AEAD) using RbNaCl's ChaCha20Poly1305. A unique nonce must be used for each encryption with the same key. The additional data is authenticated but sent in the clear. Tampered ciphertexts or incorrect additional data will raise a CryptoError during decryption. ```ruby # generate a random secret key (or perhaps use scrypt or PBKDF2) key = RbNaCl::Random.random_bytes(RbNaCl::SecretBox.key_bytes) # Initialize a ChaCha20Poly1305 cipher object cipher = RbNaCl::AEAD::ChaCha20Poly1305IETF.new(key) # First, make a nonce: A single-use value never repeated under the same key # The nonce isn't secret, and can be sent with the ciphertext. # The cipher instance has a nonce_bytes method for determining how many bytes should be in a nonce nonce = RbNaCl::Random.random_bytes(cipher.nonce_bytes) # Encrypt a message with ChaCha20Poly1305 message = "..." # Message to be encrypted ad = "" # Additional data sent *in the clear* to be authenticated ciphertext = cipher.encrypt(nonce, message, ad) #=> "..." # string of random looking bytes, 16 bytes longer than message. # The extra 16-bytes are the authenticator # Decrypt a message, passing in the same nonce and additional data we used to encrypt decrypted_message = cipher.decrypt(nonce, ciphertext, ad) #=> "..." # But if the ciphertext has been tampered with: cipher.decrypt(nonce, ad, corrupted_ciphertext) #=> RbNaCl::CryptoError exception is raised. # Chosen ciphertext attacks are prevented by authentication and constant-time comparisons ``` -------------------------------- ### Authenticated Encryption with SecretBox Source: https://context7.com/rubycrypto/rbnacl/llms.txt Performs symmetric encryption where the user must manually manage unique nonces for each operation. ```ruby require 'rbnacl' # Generate a random 32-byte secret key key = RbNaCl::Random.random_bytes(RbNaCl::SecretBox.key_bytes) # Create a SecretBox instance secret_box = RbNaCl::SecretBox.new(key) # Generate a unique nonce (24 bytes) - MUST be unique per encryption nonce = RbNaCl::Random.random_bytes(secret_box.nonce_bytes) #=> "\x..." (24 random bytes) # Encrypt a message message = "Top secret information" ciphertext = secret_box.encrypt(nonce, message) #=> "\x..." (message + 16-byte authenticator) # Store or transmit both nonce and ciphertext (nonce is not secret) puts "Nonce size: #{nonce.bytesize} bytes" #=> 24 bytes puts "Ciphertext size: #{ciphertext.bytesize}" #=> message.bytesize + 16 # Decrypt using the same nonce decrypted = secret_box.decrypt(nonce, ciphertext) #=> "Top secret information" # Alternative method names ciphertext2 = secret_box.box(nonce, message) # alias for encrypt plaintext2 = secret_box.open(nonce, ciphertext2) # alias for decrypt # Tampered ciphertext raises CryptoError begin corrupted = ciphertext.dup corrupted[0] = "\x00" secret_box.decrypt(nonce, corrupted) rescue RbNaCl::CryptoError puts "Message authentication failed - tampering detected" end ``` -------------------------------- ### Box Public-Key Encryption and Decryption Source: https://context7.com/rubycrypto/rbnacl/llms.txt Enables secure, authenticated communication between two parties using public/private key pairs. Each message is authenticated as coming from the expected sender. Ensure you use the correct public key of the recipient and your own private key to encrypt. ```ruby require 'rbnacl' # === Key Generation === # Alice generates her keypair alice_private = RbNaCl::PrivateKey.generate alice_public = alice_private.public_key # Bob generates his keypair bob_private = RbNaCl::PrivateKey.generate bob_public = bob_private.public_key # Keys can be serialized to bytes for storage/transmission alice_public_bytes = alice_public.to_s # 32 bytes bob_private_bytes = bob_private.to_s # 32 bytes # Keys can be reconstructed from bytes restored_public = RbNaCl::PublicKey.new(alice_public_bytes) restored_private = RbNaCl::PrivateKey.new(bob_private_bytes) # === Creating Boxes === # Alice creates a box for communicating with Bob alice_to_bob = RbNaCl::Box.new(bob_public, alice_private) # Bob creates a box for communicating with Alice bob_to_alice = RbNaCl::Box.new(alice_public, bob_private) # === Message Exchange === # Alice sends a message to Bob nonce = RbNaCl::Random.random_bytes(alice_to_bob.nonce_bytes) # 24 bytes message = "Hello Bob, this is Alice!" ciphertext = alice_to_bob.encrypt(nonce, message) # Bob decrypts the message from Alice plaintext = bob_to_alice.decrypt(nonce, ciphertext) #=> "Hello Bob, this is Alice!" # Bob responds to Alice response_nonce = RbNaCl::Random.random_bytes(bob_to_alice.nonce_bytes) response = "Hi Alice, got your message!" response_cipher = bob_to_alice.encrypt(response_nonce, response) # Alice decrypts Bob's response alice_to_bob.decrypt(response_nonce, response_cipher) #=> "Hi Alice, got your message!" # Mutual authentication - wrong sender's key fails decryption eve_private = RbNaCl::PrivateKey.generate eve_box = RbNaCl::Box.new(bob_public, eve_private) begin bob_to_alice.decrypt(nonce, eve_box.encrypt(nonce, "Fake message")) rescue RbNaCl::CryptoError puts "Authentication failed - message not from expected sender" end ``` -------------------------------- ### Handle Tampered Ciphertext with RbNaCl::Box Source: https://github.com/rubycrypto/rbnacl/wiki/Public-Key-Encryption Illustrates how RbNaCl::Box's `open` method raises a RbNaCl::CryptoError if the ciphertext has been tampered with, ensuring data integrity. This prevents chosen ciphertext attacks. ```ruby # But if the ciphertext has been tampered with: other_box.open(nonce, corrupted_ciphertext) #=> RbNaCl::CryptoError exception is raised. # Chosen ciphertext attacks are prevented by authentication and constant-time comparisons ``` -------------------------------- ### Secure Random Number Generation Source: https://context7.com/rubycrypto/rbnacl/llms.txt Generate cryptographically secure random bytes for keys, nonces, and salts. ```ruby require 'rbnacl' # Generate random bytes random_32 = RbNaCl::Random.random_bytes(32) #=> "\x..." (32 cryptographically secure random bytes) # Common use cases encryption_key = RbNaCl::Random.random_bytes(RbNaCl::SecretBox.key_bytes) # 32 bytes nonce = RbNaCl::Random.random_bytes(24) # for SecretBox/Box salt = RbNaCl::Random.random_bytes(16) # for Argon2 # Generate a random private key private_key = RbNaCl::PrivateKey.generate # uses Random internally signing_key = RbNaCl::SigningKey.generate # uses Random internally ``` -------------------------------- ### Obtain the standard group element Source: https://github.com/rubycrypto/rbnacl/wiki/Scalar-Multiplication Retrieves the reference base point used for all scalar multiplication operations in NaCl. ```ruby RbNaCl::GroupElement.base ``` -------------------------------- ### Decrypt Data with SimpleBox Source: https://github.com/rubycrypto/rbnacl/wiki/SimpleBox Decrypts ciphertext using an initialized SimpleBox instance. Raises RbNaCl::CryptoError if the data has been tampered with. ```ruby plaintext = box.decrypt(ciphertext) ``` -------------------------------- ### Compute message digests with RbNaCl Source: https://github.com/rubycrypto/rbnacl/wiki/Hash-Functions Use these methods to generate SHA-256 or Blake2b digests from a given message string. ```ruby # compute the SHA-256 digest of the message. RbNaCl::Hash.sha256(message) #=> "..." # a string of bytes. # compute the Blake2b digest of a message RbNaCl::Hash.blake2b(message) #=> "..." # a string of bytes. ``` -------------------------------- ### Verify Ed25519 Signature Source: https://github.com/rubycrypto/rbnacl/wiki/Digital-Signatures Creates a VerifyKey object from a public key and verifies a message's signature. This will raise RbNaCl::BadSignatureError if the signature check fails. Use this for the verifier's perspective. ```ruby # Create a VerifyKey object from a public key verify_key = RbNaCl::VerifyKey.new(verify_key) # Check the validity of a message's signature # Will raise RbNaCl::BadSignatureError if the signature check fails verify_key.verify(signature, message) ``` -------------------------------- ### AEAD Encryption and Decryption with Additional Data Source: https://context7.com/rubycrypto/rbnacl/llms.txt Encrypts and decrypts messages using RbNaCl::AEAD::XChaCha20Poly1305IETF, supporting additional authenticated data. Ensure the same additional data is used for decryption. ```ruby require 'rbnacl' key = RbNaCl::SecretBox.key cipher = RbNaCl::AEAD::XChaCha20Poly1305IETF.new(key) # Generate a unique nonce (12 bytes for IETF variant) nonce = RbNaCl::Random.random_bytes(cipher.nonce_bytes) #=> "\x..." (12 random bytes) # Message to encrypt message = "Confidential payload data" # Additional data - authenticated but NOT encrypted (sent in the clear) # Useful for headers, metadata, routing info, etc. additional_data = "user_id=12345;timestamp=1699900000" # Encrypt with additional data ciphertext = cipher.encrypt(nonce, message, additional_data) #=> "\x..." (encrypted message + 16-byte auth tag) # Decrypt with the same additional data decrypted = cipher.decrypt(nonce, ciphertext, additional_data) #=> "Confidential payload data" # If additional data is modified, decryption fails begin tampered_ad = "user_id=99999;timestamp=1699900000" cipher.decrypt(nonce, ciphertext, tampered_ad) rescue RbNaCl::CryptoError puts "Authentication failed - additional data was modified" end # XChaCha20-Poly1305 variant with larger 24-byte nonce (safer for random nonces) xchacha = RbNaCl::AEAD::XChaCha20Poly1305IETF.new(key) xnonce = RbNaCl::Random.random_bytes(xchacha.nonce_bytes) #=> 24 bytes xciphertext = xchacha.encrypt(xnonce, message, additional_data) ``` -------------------------------- ### Compute Diffie-Hellman shared secret Source: https://github.com/rubycrypto/rbnacl/wiki/Scalar-Multiplication Computes a shared secret by multiplying a received public key by a local private key. ```ruby shared_secret = RbNaCl::GroupElement.new(bob_public_key).mult(alice_private_key) ``` ```ruby shared_secret = RbNaCl::GroupElement.new(alice_public_key).mult(bob_private_key) ``` -------------------------------- ### Generate Random Bytes with RbNaCl Source: https://github.com/rubycrypto/rbnacl/wiki/Random-Number-Generation Use RbNaCl.random_bytes to generate a specified number of cryptographically secure random bytes. This method relies on the OS's secure random number generator. ```ruby RbNaCl::Random.random_bytes(32) ``` -------------------------------- ### Perform HMAC Message Authentication in Ruby Source: https://context7.com/rubycrypto/rbnacl/llms.txt Authenticate messages using HMAC-SHA256, SHA512, or SHA512/256. Supports both class-level authentication and instance-based streaming APIs. ```ruby require 'rbnacl' # === HMAC-SHA256 === # Generate a random key (32 bytes recommended, but any length works per RFC 2104) key = RbNaCl::Random.random_bytes(32) # Create authenticator for a message using class method message = "Important data to authenticate" authenticator = RbNaCl::HMAC::SHA256.auth(key, message) #=> "\x..." (32 bytes) # Verify the authenticator - raises BadAuthenticatorError if invalid begin RbNaCl::HMAC::SHA256.verify(key, authenticator, message) puts "Message is authentic" rescue RbNaCl::BadAuthenticatorError puts "Message authentication failed" end # === Instance-based API (supports incremental/streaming) === hmac = RbNaCl::HMAC::SHA256.new(key) # Update with message chunks (streaming) hmac.update("First part of ") hmac.update("the message") # Get authenticator puts hmac.hexdigest #=> "a1b2c3..." (64 hex chars) puts hmac.digest #=> "\x..." (32 raw bytes) # Verify using instance hmac2 = RbNaCl::HMAC::SHA256.new(key) hmac2.verify(authenticator, message) # === HMAC-SHA512 === key512 = RbNaCl::Random.random_bytes(64) auth512 = RbNaCl::HMAC::SHA512.auth(key512, message) #=> "\x..." (64 bytes) # === HMAC-SHA512/256 (SHA512 truncated to 256 bits) === auth512_256 = RbNaCl::HMAC::SHA512256.auth(key, message) #=> "\x..." (32 bytes) # Tampered message fails verification begin RbNaCl::HMAC::SHA256.verify(key, authenticator, "Modified data") rescue RbNaCl::BadAuthenticatorError puts "Authentication failed - data was modified" end ``` -------------------------------- ### SealedBox Anonymous Encryption Source: https://context7.com/rubycrypto/rbnacl/llms.txt Allows anonymous encryption to a recipient's public key. The sender cannot decrypt their own message, and the recipient cannot verify the sender's identity. Use `SealedBox.from_public_key` for sending and `SealedBox.from_private_key` for receiving. ```ruby require 'rbnacl' # Recipient generates a keypair recipient_private = RbNaCl::PrivateKey.generate recipient_public = recipient_private.public_key # === Sender (Anonymous) === # Anyone can encrypt to the recipient's public key sender_box = RbNaCl::SealedBox.from_public_key(recipient_public) # Encrypt a message anonymously anonymous_message = "This is an anonymous tip" sealed_ciphertext = sender_box.encrypt(anonymous_message) #=> "\x..." (48 bytes overhead: ephemeral public key + auth tag) # Sender CANNOT decrypt their own message begin sender_box.decrypt(sealed_ciphertext) rescue RbNaCl::CryptoError => e puts "Sender cannot decrypt: #{e.message}" end # === Recipient === # Only the recipient with the private key can decrypt recipient_box = RbNaCl::SealedBox.from_private_key(recipient_private) decrypted = recipient_box.decrypt(sealed_ciphertext) #=> "This is an anonymous tip" # Alternative: create box with both keys for encrypt/decrypt capability full_box = RbNaCl::SealedBox.new(recipient_public, recipient_private) ciphertext = full_box.encrypt("Secret message") full_box.decrypt(ciphertext) #=> "Secret message" ``` -------------------------------- ### Curve25519 Scalar Multiplication Source: https://context7.com/rubycrypto/rbnacl/llms.txt Access low-level elliptic curve operations for Diffie-Hellman key exchange. ```ruby require 'rbnacl' ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.