module Ronin::Support::Crypto

{Crypto} provides a nicer more user-friendly API ontop of ‘OpenSSL`.

## Core-Ext Methods

@api public

@since 1.0.0

Public Class Methods

Cert(cert) click to toggle source

Coerces a value into a {Cert} object.

@param [String, OpenSSL::X509::Certificate, Cert] cert

The certificate String or `OpenSSL::X509::Certificate` value.

@return [Cert]

The coerced certificate.

@raise [ArgumentError]

The certificate value was not a String or a `OpenSSL::X509::Certificate` object.

@api semipublic

# File lib/ronin/support/crypto/cert.rb, line 554
def self.Cert(cert)
  case cert
  when String then Cert.parse(cert)
  when Cert   then cert
  when OpenSSL::X509::Certificate
    new_cert = Cert.allocate
    new_cert.send(:initialize_copy,cert)
    new_cert
  else
    raise(ArgumentError,"value must be either a String or a OpenSSL::X509::Certificate object: #{cert.inspect}")
  end
end
Key(key) click to toggle source

Coerces a value into a {Key} object.

@param [String, OpenSSL::PKey::RSA, OpenSSL::PKey::DSA, OpenSSL::PKey::DH , OpenSSL::PKey::EC] key

The key String or `OpenSSL::PKey::PKey` value.

@return [RSA, DSA, DH, EC]

The coerced certificate.

@raise [ArgumentError]

The key value was not a String or a `OpenSSL::PKey::PKey` object.

@raise [NotImplementedError]

A new `OpenSSL::PKey::PKey` value that was not `OpenSSL::PKey::RSA`,
`OpenSSL::PKey::DSA`, `OpenSSL::PKey::DH`, or `OpenSSL::PKey::EC`
was given.

@api semipublic

@since 1.1.0

# File lib/ronin/support/crypto/key.rb, line 130
def self.Key(key)
  case key
  when String then Key.parse(key)
  when OpenSSL::PKey::PKey
    key_class = case key
                when OpenSSL::PKey::RSA then Key::RSA
                when OpenSSL::PKey::DSA then Key::DSA
                when OpenSSL::PKey::DH  then Key::DH
                when OpenSSL::PKey::EC  then Key::EC
                else
                  raise(NotImplementedError,"#{key.inspect} is not supported")
                end

    new_key = key_class.allocate
    new_key.send(:initialize_copy,key)
    new_key
  else
    raise(ArgumentError,"value must be either a String or a OpenSSL::PKey::PKey object: #{key.inspect}")
  end
end
aes128_cipher(**kwargs) click to toggle source

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')
# => #<Ronin::Support::Crypto::Cipher::AES128:0x00007f8bde789648 @key_size=128, @mode=:cbc>

@see Cipher::AES128

# File lib/ronin/support/crypto.rb, line 445
def self.aes128_cipher(**kwargs)
  Cipher::AES128.new(**kwargs)
end
aes128_decrypt(data,**kwargs) click to toggle source

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.
# File lib/ronin/support/crypto.rb, line 519
def self.aes128_decrypt(data,**kwargs)
  self.aes128_cipher(direction: :decrypt, **kwargs).decrypt(data)
end
aes128_encrypt(data,**kwargs) click to toggle source

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.
# File lib/ronin/support/crypto.rb, line 482
def self.aes128_encrypt(data,**kwargs)
  self.aes128_cipher(direction: :encrypt, **kwargs).encrypt(data)
end
aes256_cipher(**kwargs) click to toggle source

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')
# => #<Ronin::Support::Crypto::Cipher::AES256:0x00007f8bde789648 @key_size=256, @mode=:cbc>

@see Cipher::AES256

# File lib/ronin/support/crypto.rb, line 556
def self.aes256_cipher(**kwargs)
  Cipher::AES256.new(**kwargs)
end
aes256_decrypt(data,**kwargs) click to toggle source

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.
# File lib/ronin/support/crypto.rb, line 630
def self.aes256_decrypt(data,**kwargs)
  self.aes256_cipher(direction: :decrypt, **kwargs).decrypt(data)
end
aes256_encrypt(data,**kwargs) click to toggle source

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.
# File lib/ronin/support/crypto.rb, line 593
def self.aes256_encrypt(data,**kwargs)
  self.aes256_cipher(direction: :encrypt, **kwargs).encrypt(data)
end
aes_cipher(**kwargs) click to toggle source

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)
# => #<Ronin::Support::Crypto::Cipher::AES:0x00007f2b84dfa6b8 @key_size=256, @mode=:cbc>

@see Cipher::AES

# File lib/ronin/support/crypto.rb, line 328
def self.aes_cipher(**kwargs)
  Cipher::AES.new(**kwargs)
end
aes_decrypt(data,**kwargs) click to toggle source

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.
# File lib/ronin/support/crypto.rb, line 408
def self.aes_decrypt(data,**kwargs)
  self.aes_cipher(direction: :decrypt, **kwargs).decrypt(data)
end
aes_encrypt(data,**kwargs) click to toggle source

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.
# File lib/ronin/support/crypto.rb, line 368
def self.aes_encrypt(data,**kwargs)
  self.aes_cipher(direction: :encrypt, **kwargs).encrypt(data)
end
cipher(name,**kwargs) click to toggle source

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)
# => #<OpenSSL::Cipher:0x0000000170d108>

@see Cipher

# File lib/ronin/support/crypto.rb, line 210
def self.cipher(name,**kwargs)
  Cipher.new(name,**kwargs)
end
ciphers() click to toggle source

The list of supported ciphers.

@return [Array<String>]

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

# File lib/ronin/support/crypto.rb, line 167
def self.ciphers
  Cipher.supported
end
decrypt(data, cipher: ,**kwargs) click to toggle source

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

# File lib/ronin/support/crypto.rb, line 288
def self.decrypt(data, cipher: ,**kwargs)
  self.cipher(cipher, direction: :decrypt, **kwargs).decrypt(data)
end
digest(name) click to toggle source

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 rubydoc.info/stdlib/openssl/OpenSSL/Digest

# File lib/ronin/support/crypto.rb, line 96
def self.digest(name)
  OpenSSL::Digest.const_get(name.upcase)
end
encrypt(data, cipher: ,**kwargs) click to toggle source

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

# File lib/ronin/support/crypto.rb, line 249
def self.encrypt(data, cipher: ,**kwargs)
  self.cipher(cipher, direction: :encrypt, **kwargs).encrypt(data)
end
hmac(data=nil, key: , digest: :sha1) { |hmac| ... } click to toggle source

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')
# => #<Ronin::Support::Crypto::HMAC: 03376ee7ad7bbfceee98660439a4d8b125122a5a>
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
# => #<Ronin::Support::Crypto::HMAC: 03376ee7ad7bbfceee98660439a4d8b125122a5a>

@see HMAC

# File lib/ronin/support/crypto.rb, line 139
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
rot(string,n=13, alphabets: [('A'..'Z').to_a, ('a'..'z').to_a, ('0'..'9').to_a]) click to toggle source

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<Array<String>>] 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."
# File lib/ronin/support/crypto.rb, line 760
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
rsa_decrypt(data, key: nil, key_file: nil, key_password: nil, **kwargs) click to toggle source

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.
# File lib/ronin/support/crypto.rb, line 727
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
rsa_encrypt(data, key: nil, key_file: nil, key_password: nil, **kwargs) click to toggle source

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.
# File lib/ronin/support/crypto.rb, line 693
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
rsa_key(key=nil, path: nil, password: nil) click to toggle source

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.
# File lib/ronin/support/crypto.rb, line 651
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
xor(string,key) click to toggle source

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</"
# File lib/ronin/support/crypto.rb, line 800
def self.xor(string,key)
  key = case key
        when Integer then [key]
        when String  then key.bytes
        else              key
        end

  key    = key.cycle
  result = String.new(encoding: string.encoding)

  string.bytes.each do |b|
    result << (b ^ key.next).chr
  end

  return result
end