# encoding: binary
# frozen_string_literal: true

module RbNaCl
  module Boxes
    class Curve25519XSalsa20Poly1305
      # RbNaCl::Box private key. Keep it safe
      #
      # This class generates and stores NaCL private keys, as well as providing a
      # reference to the public key associated with this private key, if that's
      # provided.
      #
      # Note that the documentation for NaCl refers to this as a secret key, but in
      # this library its a private key, to avoid confusing the issue with the
      # SecretBox, which does symmetric encryption.
      class PrivateKey
        include KeyComparator
        include Serializable

        extend Sodium

        sodium_type      :box
        sodium_primitive :curve25519xsalsa20poly1305

        sodium_function  :box_curve25519xsalsa20poly1305_keypair,
                         :crypto_box_curve25519xsalsa20poly1305_keypair,
                         [:pointer, :pointer]

        # The size of the key, in bytes
        BYTES = Boxes::Curve25519XSalsa20Poly1305::PRIVATEKEYBYTES

        # Initializes a new PrivateKey for key operations.
        #
        # Takes the (optionally encoded) private key bytes.  This class can then be
        # used for various key operations, including deriving the corresponding
        # PublicKey
        #
        # @param private_key [String] The private key
        #
        # @raise [TypeError] If the key is nil
        # @raise [RbNaCl::LengthError] If the key is not valid after decoding.
        #
        # @return A new PrivateKey
        def initialize(private_key)
          @private_key = Util.check_string(private_key, BYTES, "Private key")
        end

        # Generates a new keypair
        #
        # @raise [RbNaCl::CryptoError] if key generation fails, due to insufficient randomness.
        #
        # @return [RbNaCl::PrivateKey] A new private key, with the associated public key also set.
        def self.generate
          pk = Util.zeros(Boxes::Curve25519XSalsa20Poly1305::PUBLICKEYBYTES)
          sk = Util.zeros(Boxes::Curve25519XSalsa20Poly1305::PRIVATEKEYBYTES)
          box_curve25519xsalsa20poly1305_keypair(pk, sk) || raise(CryptoError, "Failed to generate a key pair")
          new(sk)
        end

        # The raw bytes of the key
        #
        # @return [String] the raw bytes.
        def to_bytes
          @private_key
        end

        # the public key associated with this private key
        #
        # @return [PublicKey] the key
        def public_key
          @public_key ||= PublicKey.new GroupElements::Curve25519.base.mult(to_bytes)
        end

        # The crypto primitive this PrivateKey is to be used for.
        #
        # @return [Symbol] The primitive
        def primitive
          self.class.primitive
        end
      end
    end
  end
end
