# frozen_string_literal: true # # Copyright (c) 2006-2024 Hal Brodigan (postmodern.mod3 at gmail.com) # # Ronin Support 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 Support 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 Support. If not, see . # require 'ronin/support/crypto/openssl' require 'ronin/support/crypto/hmac' require 'ronin/support/crypto/cipher' require 'ronin/support/crypto/cipher/aes' require 'ronin/support/crypto/cipher/aes128' require 'ronin/support/crypto/cipher/aes256' require 'ronin/support/crypto/key/rsa' require 'ronin/support/crypto/cert' require 'ronin/support/crypto/cert_chain' require 'ronin/support/crypto/mixin' require 'ronin/support/crypto/core_ext' module Ronin module Support # # {Crypto} provides a nicer more user-friendly API ontop of `OpenSSL`. # # ## Core-Ext Methods # # * {File.md5} # * {File.sha1} # * {File.sha128} # * {File.sha256} # * {File.sha512} # * {File.sha2} # * {File.sha5} # * {File.rmd160} # * {File.hmac} # * {File.encrypt} # * {File.decrypt} # * {File.aes_encrypt} # * {File.aes_decrypt} # * {File.aes128_encrypt} # * {File.aes128_decrypt} # * {File.aes256_encrypt} # * {File.aes256_decrypt} # * {File.rsa_encrypt} # * {File.rsa_decrypt} # * {String#md5} # * {String#sha1} # * {String#sha256} # * {String#sha512} # * {String#rmd160} # * {String#hmac} # * {String#encrypt} # * {String#decrypt} # * {String#aes_encrypt} # * {String#aes_decrypt} # * {String#aes128_encrypt} # * {String#aes128_decrypt} # * {String#aes256_encrypt} # * {String#aes256_decrypt} # * {String#rsa_encrypt} # * {String#rsa_decrypt} # * {String#rot} # * {String#xor} # # @api public # # @since 1.0.0 # module Crypto # # Looks up a digest. # # @param [String, Symbol] name # The name of the digest. # # @return [OpenSSL::Digest] # The OpenSSL Digest class. # # @example # Crypto.digest(:sha256) # # => OpenSSL::Digest::SHA256 # # @see http://rubydoc.info/stdlib/openssl/OpenSSL/Digest # def self.digest(name) OpenSSL::Digest.const_get(name.upcase) end # # Creates a new HMAC. # # @param [String, nil] data # The optional data to sign. # # @param [String] key # The secret key for the HMAC. # # @param [Symbol] digest # The digest algorithm for the HMAC. # # @yield [hmac] # If a block is given, it will be passed the new HMAC object, which can # then be populated. # # @yieldparam [OpenSSL::HMAC] hmac # The new HMAC object. # # @return [OpenSSL::HMAC] # The HMAC object. # # @example # hmac = Crypto.hmac("hello world", key: 'secret') # # => # # hmac.hexdigest # # => "03376ee7ad7bbfceee98660439a4d8b125122a5a" # hmac.digest # # => "\x037n\xE7\xAD{\xBF\xCE\xEE\x98f\x049\xA4\xD8\xB1%\x12*Z" # # @example with a block: # hmac = Crypto.hmac("hello world", key: 'secret') do |hmac| # hmac << "hello" # hmac << " world" # end # # => # # # @see HMAC # def self.hmac(data=nil, key: , digest: :sha1) hmac = HMAC.new(key,digest(digest).new) if block_given? then yield hmac elsif data then hmac.update(data) end return hmac end # # The list of supported ciphers. # # @return [Array] # The list of supported cipher names. # # @see Cipher.supported # # @example # Cipher.supported # # => ["RC5", # # "aes-128-cbc", # # "aes-128-cbc-hmac-sha1", # # "aes-128-cbc-hmac-sha256", # # ...] # # @see Cipher.supported # def self.ciphers Cipher.supported end # # Creates a cipher. # # @param [String] name # The cipher name. # # @param [Hash{Symbol => Object}] kwargs # Additional keyword arguments for {Cipher#initialize}. # # @option kwargs [:encrypt, :decrypt] :direction # Specifies whether to encrypt or decrypt data. # # @option kwargs [Symbol] :hash (:sha256) # The algorithm to hash the `:password`. # # @option kwargs [String] :key # The secret key to use. # # @option kwargs [String] :password # The password for the cipher. # # @option kwargs [String] :iv # The optional Initial Vector (IV). # # @option kwargs [Integer] :padding # Sets the padding for the cipher. # # @return [OpenSSL::Cipher] # The newly created cipher. # # @raise [ArgumentError] # Either the the `key:` or `password:` keyword argument must be given. # # @example # Crypto.cipher('aes-128-cbc', direction: :encrypt, key 'secret'.md5) # # => # # # @see Cipher # def self.cipher(name,**kwargs) Cipher.new(name,**kwargs) end # # Encrypts data using the cipher. # # @param [#to_s] data # The data to encrypt. # # @param [String] cipher # The cipher name (ex: `"aes-256-cbc"`). # # @param [Hash{Symbol => Object}] kwargs # Additional keyword arguments for {cipher}. # # @option kwargs [Symbol] :hash (:sha256) # The algorithm to hash the `:password`. # # @option kwargs [String] :key # The secret key to use. # # @option kwargs [String] :password # The password for the cipher. # # @option kwargs [String] :iv # The optional Initial Vector (IV). # # @option kwargs [Integer] :padding # Sets the padding for the cipher. # # @return [String] # The encrypted data. # # @raise [ArgumentError] # Either the the `key:` or `password:` keyword argument must be given. # # @see Cipher#encrypt # def self.encrypt(data, cipher: ,**kwargs) self.cipher(cipher, direction: :encrypt, **kwargs).encrypt(data) end # # Decrypts data using the cipher. # # @param [#to_s] data # The data to decrypt. # # @param [String] cipher # The cipher name (ex: `"aes-256-cbc"`). # # @param [Hash{Symbol => Object}] kwargs # Additional keyword arguments for {cipher}. # # @option kwargs [Symbol] :hash (:sha256) # The algorithm to hash the `:password`. # # @option kwargs [String] :key # The secret key to use. # # @option kwargs [String] :password # The password for the cipher. # # @option kwargs [String] :iv # The optional Initial Vector (IV). # # @option kwargs [Integer] :padding # Sets the padding for the cipher. # # @return [String] # The decrypted data. # # @raise [ArgumentError] # Either the the `key:` or `password:` keyword argument must be given. # # @see Cipher#decrypt # def self.decrypt(data, cipher: ,**kwargs) self.cipher(cipher, direction: :decrypt, **kwargs).decrypt(data) end # # Creates a new AES cipher. # # @param [Hash{Symbol => Object}] kwargs # Additional keyword arguments for {Cipher::AES#initialize}. # # @option kwargs [Integer] :key_size # The desired key size in bits. # # @option kwargs [:cbc, :cfb, :ofb, :ctr, Symbol] :mode (:cbc) # The desired AES cipher mode. # # @option kwargs [Symbol] :hash (:sha256) # The algorithm to hash the `:password`. # # @option kwargs [String] :key # The secret key to use. # # @option kwargs [String] :password # The password for the cipher. # # @option kwargs [String] :iv # The optional Initial Vector (IV). # # @option kwargs [Integer] :padding # Sets the padding for the cipher. # # @return [Cipher::AES] # The new AES cipher. # # @example # Crypto.aes_cipher(key_size: 256, direction: :encrypt, password: 's3cr3t', hash: :sha256) # # => # # # @see Cipher::AES # def self.aes_cipher(**kwargs) Cipher::AES.new(**kwargs) end # # Encrypts data using AES. # # @param [#to_s] data # The data to encrypt. # # @param [Hash{Symbol => Object}] kwargs # Additional keyword arguments for {aes_cipher}. # # @option kwargs [Integer] :key_size # The desired key size in bits. # # @option kwargs [:cbc, :cfb, :ofb, :ctr, Symbol] mode (:cbc) # The desired AES cipher mode. # # @option kwargs [Symbol] :hash (:sha256) # The algorithm to hash the `:password`. # # @option kwargs [String] :key # The secret key to use. # # @option kwargs [String] :password # The password for the cipher. # # @option kwargs [String] :iv # The optional Initial Vector (IV). # # @option kwargs [Integer] :padding # Sets the padding for the cipher. # # @return [String] # The encrypted data. # # @raise [ArgumentError] # Either the the `key:` or `password:` keyword argument must be given. # def self.aes_encrypt(data,**kwargs) self.aes_cipher(direction: :encrypt, **kwargs).encrypt(data) end # # Decrypts data using AES. # # @param [#to_s] data # The data to encrypt. # # @param [Hash{Symbol => Object}] kwargs # Additional keyword arguments for {aes_cipher}. # # @option kwargs [Integer] :key_size # The desired key size in bits. # # @option kwargs [:cbc, :cfb, :ofb, :ctr, Symbol] mode (:cbc) # The desired AES cipher mode. # # @option kwargs [Symbol] :hash (:sha256) # The algorithm to hash the `:password`. # # @option kwargs [String] :key # The secret key to use. # # @option kwargs [String] :password # The password for the cipher. # # @option kwargs [String] :iv # The optional Initial Vector (IV). # # @option kwargs [Integer] :padding # Sets the padding for the cipher. # # @return [String] # The encrypted data. # # @raise [ArgumentError] # Either the the `key:` or `password:` keyword argument must be given. # def self.aes_decrypt(data,**kwargs) self.aes_cipher(direction: :decrypt, **kwargs).decrypt(data) end # # Creates a new AES-128 cipher. # # @param [Hash{Symbol => Object}] kwargs # Additional keyword arguments for {Cipher::AES128#initialize}. # # @option kwargs [:cbc, :cfb, :ofb, :ctr, Symbol] :mode (:cbc) # The desired AES cipher mode. # # @option kwargs [Symbol] :hash (:md5) # The algorithm to hash the `:password`. # # @option kwargs [String] :key # The secret key to use. # # @option kwargs [String] :password # The password for the cipher. # # @option kwargs [String] :iv # The optional Initial Vector (IV). # # @option kwargs [Integer] :padding # Sets the padding for the cipher. # # @return [Cipher::AES] # The new AES cipher. # # @example # Crypto.aes128_cipher(direction: :encrypt, password: 's3cr3t') # # => # # # @see Cipher::AES128 # def self.aes128_cipher(**kwargs) Cipher::AES128.new(**kwargs) end # # Encrypts data using AES-128. # # @param [#to_s] data # The data to encrypt. # # @param [Hash{Symbol => Object}] kwargs # Additional keyword arguments for {aes128_cipher}. # # @option kwargs [:cbc, :cfb, :ofb, :ctr, Symbol] mode (:cbc) # The desired AES cipher mode. # # @option kwargs [Symbol] :hash (:md5) # The algorithm to hash the `:password`. # # @option kwargs [String] :key # The secret key to use. # # @option kwargs [String] :password # The password for the cipher. # # @option kwargs [String] :iv # The optional Initial Vector (IV). # # @option kwargs [Integer] :padding # Sets the padding for the cipher. # # @return [String] # The encrypted data. # # @raise [ArgumentError] # Either the the `key:` or `password:` keyword argument must be given. # def self.aes128_encrypt(data,**kwargs) self.aes128_cipher(direction: :encrypt, **kwargs).encrypt(data) end # # Decrypts data using AES-128. # # @param [#to_s] data # The data to encrypt. # # @param [Hash{Symbol => Object}] kwargs # Additional keyword arguments for {aes128_cipher}. # # @option kwargs [:cbc, :cfb, :ofb, :ctr, Symbol] mode (:cbc) # The desired AES cipher mode. # # @option kwargs [Symbol] :hash (:md5) # The algorithm to hash the `:password`. # # @option kwargs [String] :key # The secret key to use. # # @option kwargs [String] :password # The password for the cipher. # # @option kwargs [String] :iv # The optional Initial Vector (IV). # # @option kwargs [Integer] :padding # Sets the padding for the cipher. # # @return [String] # The encrypted data. # # @raise [ArgumentError] # Either the the `key:` or `password:` keyword argument must be given. # def self.aes128_decrypt(data,**kwargs) self.aes128_cipher(direction: :decrypt, **kwargs).decrypt(data) end # # Creates a new AES-256 cipher. # # @param [Hash{Symbol => Object}] kwargs # Additional keyword arguments for {Cipher::AES256#initialize}. # # @option kwargs [:cbc, :cfb, :ofb, :ctr, Symbol] :mode (:cbc) # The desired AES cipher mode. # # @option kwargs [Symbol] :hash (:sha256) # The algorithm to hash the `:password`. # # @option kwargs [String] :key # The secret key to use. # # @option kwargs [String] :password # The password for the cipher. # # @option kwargs [String] :iv # The optional Initial Vector (IV). # # @option kwargs [Integer] :padding # Sets the padding for the cipher. # # @return [Cipher::AES] # The new AES cipher. # # @example # Crypto.aes256_cipher(direction: :encrypt, password: 's3cr3t') # # => # # # @see Cipher::AES256 # def self.aes256_cipher(**kwargs) Cipher::AES256.new(**kwargs) end # # Encrypts data using AES-256. # # @param [#to_s] data # The data to encrypt. # # @param [Hash{Symbol => Object}] kwargs # Additional keyword arguments for {aes256_cipher}. # # @option kwargs [:cbc, :cfb, :ofb, :ctr, Symbol] mode (:cbc) # The desired AES cipher mode. # # @option kwargs [Symbol] :hash (:sha256) # The algorithm to hash the `:password`. # # @option kwargs [String] :key # The secret key to use. # # @option kwargs [String] :password # The password for the cipher. # # @option kwargs [String] :iv # The optional Initial Vector (IV). # # @option kwargs [Integer] :padding # Sets the padding for the cipher. # # @return [String] # The encrypted data. # # @raise [ArgumentError] # Either the the `key:` or `password:` keyword argument must be given. # def self.aes256_encrypt(data,**kwargs) self.aes256_cipher(direction: :encrypt, **kwargs).encrypt(data) end # # Decrypts data using AES-256. # # @param [#to_s] data # The data to encrypt. # # @param [Hash{Symbol => Object}] kwargs # Additional keyword arguments for {aes256_cipher}. # # @option kwargs [:cbc, :cfb, :ofb, :ctr, Symbol] mode (:cbc) # The desired AES cipher mode. # # @option kwargs [Symbol] :hash (:sh256) # The algorithm to hash the `:password`. # # @option kwargs [String] :key # The secret key to use. # # @option kwargs [String] :password # The password for the cipher. # # @option kwargs [String] :iv # The optional Initial Vector (IV). # # @option kwargs [Integer] :padding # Sets the padding for the cipher. # # @return [String] # The encrypted data. # # @raise [ArgumentError] # Either the the `key:` or `password:` keyword argument must be given. # def self.aes256_decrypt(data,**kwargs) self.aes256_cipher(direction: :decrypt, **kwargs).decrypt(data) end # # Loads an RSA key. # # @param [String, nil] key # The PEM or DER encoded RSA key string. # # @param [String, nil] path # The path to the PEM or DER encoded RSA key file. # # @param [String, nil] password # The optional password to decrypt the encrypted RSA key. # # @return [Key::RSA] # # @raise [ArgumentError] # Either the `key:` or `key_file:` keyword argument must be given. # def self.rsa_key(key=nil, path: nil, password: nil) if path Key::RSA.load_file(path, password: password) elsif key case key when Key::RSA then key when OpenSSL::PKey::RSA then Key::RSA.new(key) when String then Key::RSA.load(key, password: password) end else raise(ArgumentError,"either key: or key_file: keyword arguments must be given") end end # # Encrypts data using a RSA key. # # @param [String] data # The data to encrypt. # # @param [String, nil] key # The PEM or DER encoded RSA key string. # # @param [String, nil] key_file # The path to the PEM or DER encoded RSA key file. # # @param [String, nil] key_password # The optional password to decrypt the encrypted RSA key. # # @param [Hash{Symbol => Object}] kwargs # Additional keyword arguments for {Key::RSA#public_encrypt}. # # @option kwargs [:pkcs1_oaep, :pkcs1, :sslv23, # nil, false] :padding (:pkcs1) # Optional padding mode. `nil` and `false` will disable padding. # # @return [String] # The encrypted data. # # @raise [ArgumentError] # Either the `key:` or `key_file:` keyword argument must be given. # def self.rsa_encrypt(data, key: nil, key_file: nil, key_password: nil, **kwargs) rsa = rsa_key(key, path: key_file, password: key_password) return rsa.public_encrypt(data,**kwargs) end # # Decrypts data using a RSA key. # # @param [String] data # The data to decrypt. # # @param [String, nil] key # The PEM or DER encoded RSA key string. # # @param [String, nil] key_file # The path to the PEM or DER encoded RSA key file. # # @param [String, nil] key_password # The optional password to decrypt the encrypted RSA key. # # @param [Hash{Symbol => Object}] kwargs # Additional keyword arguments for {Key::RSA#private_decrypt}. # # @option kwargs [:pkcs1_oaep, :pkcs1, :sslv23, # nil, false] :padding (:pkcs1) # Optional padding mode. `nil` and `false` will disable padding. # # @return [String] # The decrypted data. # # @raise [ArgumentError] # Either the `key:` or `key_file:` keyword argument must be given. # def self.rsa_decrypt(data, key: nil, key_file: nil, key_password: nil, **kwargs) rsa = rsa_key(key, path: key_file, password: key_password) return rsa.private_decrypt(data,**kwargs) end # # Rotates the characters in the given string using the given alphabet. # # @param [String] string # The String to rotate. # # @param [Integer] n # The number of characters to shift each character by. # # @param [Array>] alphabets # The alphabet(s) to use. # # @return [String] # The rotated string. # # @note # This method was added as a joke and should not be used for secure # cryptographic communications. # # @example ROT13 "encryption": # Crypto.rot("The quick brown fox jumps over 13 lazy dogs.") # # => "Gur dhvpx oebja sbk whzcf bire 46 ynml qbtf." # # @example ROT13 "decryption": # Crypto.rot("Gur dhvpx oebja sbk whzcf bire 46 ynml qbtf.", -13) # # => "The quick brown fox jumps over 13 lazy dogs." # def self.rot(string,n=13, alphabets: [('A'..'Z').to_a, ('a'..'z').to_a, ('0'..'9').to_a]) translation_table = {} alphabets.each do |alphabet| modulo = alphabet.count alphabet.each_with_index do |char,index| translation_table[char] = alphabet[(index + n) % modulo] end end new_string = String.new(encoding: string.encoding) string.each_char do |char| new_string << translation_table.fetch(char,char) end return new_string end # # XOR encodes the String. # # @param [String] string # The String to XOR. # # @param [Enumerable, Integer] key # The byte to XOR against each byte in the String. # # @return [String] # The XOR encoded String. # # @example # Crypto.xor("hello", 0x41) # # => ")$--." # # @example # Crypto.xor("hello again", [0x55, 0x41, 0xe1]) # # => "=$\x8d9.\xc14&\x80