ronin-rb/ronin-db-activerecord

View on GitHub
lib/ronin/db/password.rb

Summary

Maintainability
A
0 mins
Test Coverage
# frozen_string_literal: true
#
# ronin-db-activerecord - ActiveRecord backend for the Ronin Database.
#
# Copyright (c) 2022-2023 Hal Brodigan (postmodern.mod3 at gmail.com)
#
# ronin-db-activerecord is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# ronin-db-activerecord is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with ronin-db-activerecord.  If not, see <https://www.gnu.org/licenses/>.
#

require 'ronin/db/model'
require 'ronin/db/model/importable'

require 'active_record'
require 'digest'

module Ronin
  module DB
    #
    # Represents a password used by {Service services} or {URL websites}.
    #
    class Password < ActiveRecord::Base

      include Model
      include Model::Importable

      # @!attribute [rw] id
      #   The primary key of the password.
      #
      #   @return [Integer]
      attribute :id, :integer

      # @!attribute [rw] plain_text
      #   The clear-text of the password.
      #
      #   @return [String]
      attribute :plain_text, :string # length:   256,
      validates :plain_text, presence: true, uniqueness: true

      # @!attribute [rw] credentials
      #   The credentials which use the password.
      #
      #   @return [Array<Credential>]
      has_many :credentials, dependent: :destroy

      # @!attribute [rw] user_names
      #   The user names which use the password.
      #
      #   @return [Array<UserName>]
      has_many :user_names, through: :credentials

      #
      # Looks up the password.
      #
      # @param [#to_s] password
      #   The password to lookup.
      #
      # @return [Password, nil]
      #   The found password.
      #
      # @api public
      #
      def self.lookup(password)
        find_by(plain_text: password.to_s)
      end

      #
      # Parses a password.
      #
      # @param [#to_s] password
      #   The password to import.
      #
      # @return [Password]
      #   The imported password.
      #
      # @api public
      #
      def self.import(password)
        create(plain_text: password.to_s)
      end

      #
      # Hashes the password.
      #
      # @param [Symbol, String] algorithm
      #   The digest algorithm to use.
      #
      # @param [String, nil] prepend_salt
      #   The salt data to prepend to the password.
      #
      # @param [String, nil] append_salt
      #   The salt data to append to the password.
      #
      # @return [String]
      #   The hex-digest of the hashed password.
      #
      # @raise [ArgumentError]
      #   Unknown Digest algorithm.
      #
      # @example
      #   pass = Password.new(plain_text: 'secret')
      #
      #   pass.digest(:sha1)
      #   # => "e5e9fa1ba31ecd1ae84f75caaa474f3a663f05f4"
      #
      #   pass.digest(:sha1, prepend_salt: "A\x90\x00")
      #   # => "e2817656a48c49f24839ccf9295b389d8f985904"
      #
      #   pass.digest(:sha1, append_salt: "BBBB")
      #   # => "aa6ca21e446d425fc044bbb26e950a788444a5b8"
      #
      # @api public
      #
      def digest(algorithm, prepend_salt: nil, append_salt: nil)
        digest_class = begin
                         Digest.const_get(algorithm.upcase)
                       rescue LoadError
                         raise(ArgumentError,"Unknown Digest algorithm #{algorithm}")
                       end

        hash = digest_class.new
        hash << prepend_salt.to_s if prepend_salt
        hash << self.plain_text
        hash << append_salt.to_s if append_salt

        return hash.hexdigest
      end

      #
      # The number of credentials which use this password.
      #
      # @return [Integer]
      #   The number of credentials that use the password.
      #
      # @api public
      #
      def count
        self.credentials.count
      end

      #
      # Converts the password into a String.
      #
      # @return [String]
      #   The clear-text of the password.
      #
      # @api public
      #
      def to_s
        self.plain_text
      end

    end
  end
end

require 'ronin/db/credential'