1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117
|
# encoding: binary
# frozen_string_literal: true
require "forwardable"
# NaCl/libsodium for Ruby
module RbNaCl
# The simplest nonce strategy that could possibly work
#
# This class implements the simplest possible nonce generation strategy to
# wrap a RbNaCl::Box or RbNaCl::SecretBox. A 24-byte random nonce is used
# for the encryption and is prepended to the message. When it is time to
# open the box, the message is split into nonce and ciphertext, and then the
# box is decrypted.
#
# Thanks to the size of the nonce, the chance of a collision is negligible. For
# example, after encrypting 2^64 messages, the odds of their having been
# repeated nonce is approximately 2^-64. As an additional convenience, the
# ciphertexts may be encoded or decoded by any of the encoders implemented in
# the library.
#
# The resulting ciphertexts are 40 bytes longer than the plain text (24 byte
# nonce plus a 16 byte authenticator). This might be annoying if you're
# encrypting tweets, but for files represents a fairly small overhead.
#
# Some caveats:
#
# * If your random source is broken, so is the security of the messages. You
# have bigger problems than just this library at that point, but it's worth
# saying.
# * The confidentiality of your messages is assured with this strategy, but
# there is no protection against messages being reordered and replayed by an
# active adversary.
class SimpleBox
extend Forwardable
def_delegators :@box, :nonce_bytes, :primitive
# Create a new SimpleBox
#
# @param box [SecretBox, Box] the SecretBox or Box to use.
#
# @return [SimpleBox] Ready for use
def initialize(box)
@box = box
end
# Use a secret key to create a SimpleBox
#
# This is a convenience method. It takes a secret key and instantiates a
# SecretBox under the hood, then returns the new SimpleBox.
#
# @param secret_key [String] The secret key, 32 bytes long.
#
# @return [SimpleBox] Ready for use
def self.from_secret_key(secret_key)
new(SecretBox.new(secret_key))
end
# Use a pair of keys to create a SimpleBox
#
# This is a convenience method. It takes a pair of keys and instantiates a
# Box under the hood, then returns the new SimpleBox.
#
# @param public_key [PublicKey, String] The RbNaCl public key, as class or string
# @param private_key [PrivateKey, String] The RbNaCl private key, as class or string
#
# @return [SimpleBox] Ready for use
def self.from_keypair(public_key, private_key)
new(Box.new(public_key, private_key))
end
# Encrypts the message with a random nonce
#
# Encrypts the message with a random nonce, then returns the ciphertext with
# the nonce prepended. Optionally encodes the message using an encoder.
#
# @param message [String] The message to encrypt
#
# @return [String] The enciphered message
def box(message)
nonce = generate_nonce
cipher_text = @box.box(nonce, message)
nonce + cipher_text
end
alias encrypt box
# Decrypts the ciphertext with a random nonce
#
# Takes a ciphertext, optionally decodes it, then splits the nonce off the
# front and uses this to decrypt. Returns the message.
#
# @param enciphered_message [String] The message to decrypt.
#
# @raise [CryptoError] If the message has been tampered with.
#
# @return [String] The decoded message
def open(enciphered_message)
nonce, ciphertext = extract_nonce(enciphered_message.to_s)
@box.open(nonce, ciphertext)
end
alias decrypt open
private
def generate_nonce
Random.random_bytes(nonce_bytes)
end
def extract_nonce(bytes)
nonce = bytes.slice(0, nonce_bytes)
[nonce, bytes.slice(nonce_bytes..-1)]
end
end
# Backwards compatibility with the old RandomNonceBox name
RandomNonceBox = SimpleBox
end
|