You are an expert in writing code in many different languages. Your goal is to perform code completion for the following code, keeping in mind the rest of the code and the file meta data.
    Metadata details:
    filename: base.rb
    path of file: ./repos/rbnacl/lib/rbnacl/aead
    
    the code of the file until where you have to start completion: # 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 dec
    
      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