Skip to content

Instantly share code, notes, and snippets.

@stupidpupil
Created April 16, 2015 23:59
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save stupidpupil/83be3a5d0daf3db17b2b to your computer and use it in GitHub Desktop.
Save stupidpupil/83be3a5d0daf3db17b2b to your computer and use it in GitHub Desktop.
FreeOTP Tokens.xml Exporter
#!/usr/bin/env ruby
module GoogleBase32
Alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567" #RFC4648
Digits = Alphabet.chars
Mask = Digits.length - 1
Shift = Digits.length.to_s(2).match(/0*\Z/)[0].length
def self.encode_array(array)
return '' if array.empty?
raise ArgumentError if array.length >= (1<<28)
result = ''
buffer = array.first
nxt = 1
bits_left = 8
while bits_left > 0 or nxt < array.length
if bits_left < Shift
if nxt < array.length
buffer <<= 8
buffer = (buffer | (array[nxt] & 0xff))
nxt += 1
bits_left += 8
else
pad = Shift - bits_left
buffer <<= pad
bits_left += pad
end
end
index = Mask & (buffer >> (bits_left - Shift))
bits_left -= Shift
result << Digits[index]
end
return result
end
end
require 'nokogiri'
require 'json'
require 'uri'
f = File.open ARGV[0]
n = Nokogiri.parse f
n.xpath("//string").each do |string|
j = JSON.parse(string.inner_text)
next unless j.is_a? Hash and j.has_key? 'secret'
r = "otpauth://#{j['type'].downcase}/#{j['issuerAlt']}"
r << "?secret=#{GoogleBase32.encode_array j['secret']}"
r << "&period=#{j['period']}"
r << "&digits=#{j['digits']}"
r << "&algorithm=#{j['algo']}"
if not j['issuerInt'].to_s.empty?
r << "&issuer=#{j['issuerInt']}"
elsif not j['issuerExt'].to_s.empty?
r << "&issuer=#{j['issuerExt']}"
end
r << "&counter=#{j['counter'] + 1}" if j['type'] == 'HOTP'
puts r
end
f.close
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment