lib/rbnacl/aead/base.rb
# encoding: binary # frozen_string_literal: true module RbNaCl module AEAD # Abstract base class for Authenticated Encryption with Additional Data # # This construction encrypts a message, and computes an authentication # tag for the encrypted message and some optional additional data # # RbNaCl provides wrappers for both ChaCha20-Poly1305 AEAD implementations # in libsodium: the original, and the IETF version. class Base # Number of bytes in a valid key KEYBYTES = 0 # Number of bytes in a valid nonce NPUBBYTES = 0 attr_reader :key private :key # Create a new AEAD using the IETF chacha20poly1305 construction # # Sets up AEAD with a secret key for encrypting and decrypting messages. # # @param key [String] The key to encrypt and decrypt with # # @raise [RbNaCl::LengthError] on invalid keys # # @return [RbNaCl::AEAD::Chacha20Poly1305IETF] The new AEAD construct, ready to use def initialize(key) @key = Util.check_string(key, key_bytes, "Secret key") end # Encrypts and authenticates a message with additional authenticated data # # @param nonce [String] An 8-byte string containing the nonce. # @param message [String] The message to be encrypted. # @param additional_data [String] The additional authenticated data # # @raise [RbNaCl::LengthError] If the nonce is not valid # @raise [RbNaCl::CryptoError] If the ciphertext cannot be authenticated. # # @return [String] The encrypted message with the authenticator tag appended def encrypt(nonce, message, additional_data) Util.check_length(nonce, nonce_bytes, "Nonce") ciphertext_len = Util.zeros(1) ciphertext = Util.zeros(data_len(message) + tag_bytes) success = do_encrypt(ciphertext, ciphertext_len, nonce, message, additional_data) raise CryptoError, "Encryption failed" unless success ciphertext end # Decrypts and verifies an encrypted message with additional authenticated data # # @param nonce [String] An 8-byte string containing the nonce. # @param ciphertext [String] The message to be decrypted. # @param additional_data [String] The additional authenticated data # # @raise [RbNaCl::LengthError] If the nonce is not valid # @raise [RbNaCl::CryptoError] If the ciphertext cannot be authenticated. # # @return [String] The decrypted message def decrypt(nonce, ciphertext, additional_data) Util.check_length(nonce, nonce_bytes, "Nonce") message_len = Util.zeros(1) message = Util.zeros(data_len(ciphertext) - tag_bytes) success = do_decrypt(message, message_len, nonce, ciphertext, additional_data) raise CryptoError, "Decryption failed. Ciphertext failed verification." unless success message end # The crypto primitive for this aead instance # # @return [Symbol] The primitive used def primitive self.class.primitive end # The nonce bytes for the AEAD class # # @return [Integer] The number of bytes in a valid nonce def self.nonce_bytes self::NPUBBYTES end # The nonce bytes for the AEAD instance # # @return [Integer] The number of bytes in a valid nonce def nonce_bytes self.class.nonce_bytes end # The key bytes for the AEAD class # # @return [Integer] The number of bytes in a valid key def self.key_bytes self::KEYBYTES end # The key bytes for the AEAD instance # # @return [Integer] The number of bytes in a valid key def key_bytes self.class.key_bytes end # The number bytes in the tag or authenticator from this AEAD class # # @return [Integer] number of tag bytes def self.tag_bytes self::ABYTES end # The number of bytes in the tag or authenticator for this AEAD instance # # @return [Integer] number of tag bytes def tag_bytes self.class.tag_bytes end private def data_len(data) return 0 if data.nil? data.bytesize end def do_encrypt(_ciphertext, _ciphertext_len, _nonce, _message, _additional_data) raise NotImplementedError end def do_decrypt(_message, _message_len, _nonce, _ciphertext, _additional_data) raise NotImplementedError end end end end