class OneTime

Attributes

encryption[RW]
generation_time[R]
length[RW]
password[R]
secret[R]
step[RW]

Public Class Methods

new(secret, length: 6, step: 30, encryption: 'SHA512') click to toggle source
# File lib/onetime.rb, line 19
def initialize(secret, length: 6, step: 30, encryption: 'SHA512')

  @secret = secret
  @length = length
  @step = step
  @encryption = encryption
  self.generate

end

Public Instance Methods

call_service(url, user, payload, content_type: 'application/json', accept: '*/*') click to toggle source
# File lib/onetime.rb, line 97
def call_service(url, user, payload, content_type: 'application/json', accept: '*/*')

  begin

    self.generate if self.password.nil?

    # Base64 encode authentication for the header
    auth = Base64.strict_encode64("#{user}:#{self.password}".encode("ASCII"))

    # Prep uri and headers
    uri = URI.parse(url)
    headers = {
        'Authorization' => "Basic #{auth}",
        'Content-Type' => content_type,
        'Accept' => accept
      }

    # Create request
    request = Net::HTTP.new(uri.host, uri.port)
    request.use_ssl = true

    # Send request
    return request.post(uri.path, payload.to_json, headers)
  rescue Exception => e
    puts e.message
    # puts e.backtrace.join("\n")
    return nil
  end

end
generate(time: Time.now, length: self.length, step: self.step, encryption: self.encryption, reset: false) click to toggle source
# File lib/onetime.rb, line 29
def generate(time: Time.now,
              length: self.length,
              step: self.step,
              encryption: self.encryption,
              reset: false)

  begin

    # If reset is true nil the generation time
    @generation_time = time.clone if reset == true || @generation_time.nil?

    # Binary key from time / step
    rounded_step = (time.to_f - @generation_time.to_f).floor / step
    key = ['%0.16x' % (rounded_step).to_s(16).hex].pack('H*')

    # Convert secret's non base32 char to base32 and back to string
    data = Base32.decode(Base32.encode(self.secret))

    # Get initial HMAC hash
    hash = OpenSSL::HMAC.digest(encryption.downcase, data, key)

    # Dynamic truncation:
    # Get the last for bit of the last byte of the hash
    offset = hash[hash.length - 1].ord & 0xf

    # Get the for bytes pointed by the offset
    hash = hash[offset .. offset + 3].bytes

    # Get new hash
    truncated_hash = ((hash[0] & 0x7f) << 24) |
                     ((hash[1] & 0xff) << 16) |
                     ((hash[2] & 0xff) <<  8) |
                     (hash[3] & 0xff)

    # Get actual password
    @password = "%0.#{length}d" % (truncated_hash % (10 ** length));

    return self.password

  rescue Exception => e
    puts e.message
    # puts e.backtrace.join("\n")
    return nil
  end

end
verify(password, time: Time.now, length: self.length, step: self.step, encryption: self.encryption) click to toggle source
# File lib/onetime.rb, line 76
def verify(password, time: Time.now,
                    length: self.length,
                    step: self.step,
                    encryption: self.encryption)

  # verify a password correctness for a given time
  begin

    return self.generate(time: time,
                        length: length,
                        step: step,
                        encryption: encryption) == password

  rescue Exception => e
    puts e.message
    # puts e.backtrace.join("\n")
    return false
  end

end