Skip to content

Instantly share code, notes, and snippets.

@safiire
Last active April 1, 2019 03:47
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save safiire/09ca8c0e0e2f0d49f6e2a4cc62362139 to your computer and use it in GitHub Desktop.
Save safiire/09ca8c0e0e2f0d49f6e2a4cc62362139 to your computer and use it in GitHub Desktop.
A script to calculate an HOTP code
#!/usr/bin/env ruby
require 'base32'
require 'openssl'
# Script to calculate HOTP so I don't have to use my phone
class HOTP
def initialize(original_secret, counter = 0)
secret = Base32.decode(original_secret)
counter_bytestring = counter_to_bytestring(counter)
hmac = OpenSSL::HMAC.digest('SHA1', secret, counter_bytestring)
token_base = hmac_to_token_base(hmac)
@token = token_base % (10**6)
end
def password
md = sprintf("%06d", @token).match(/(\d{3})(\d{3})/)
"#{md[1]} #{md[2]}"
end
private
def hmac_to_token_base(hmac)
offset = hmac[-1].ord & 0xf
[24, 16, 8, 0].each_with_index.reduce(0) do |sum, (bitshift, index)|
mask = bitshift == 24 ? 0x7f : 0xff
sum |= (hmac[offset + index].ord & mask) << bitshift
end
end
def counter_to_bytestring(counter, wordsize = 8)
shifts = (wordsize.pred).downto(0).each.map{ |position| position * 8 }
shifts.map do |shift|
mask = 0xff << shift
((counter & mask) >> shift).chr
end.join
end
end
secret, counter, code_length = ARGV
counter = counter.to_i
code_length = code_length.to_i
hotp = HOTP.new(secret, counter)
puts hotp.password
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment