otp 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 9c347957dce5fa1a828f04c97e6c4ad600a06e8a
4
- data.tar.gz: 3bd57768dd62dbcf301115d62ff7957ff6a06395
3
+ metadata.gz: 2c5be10fdb528da5e72ee28f45a98265c7fad8e4
4
+ data.tar.gz: 237c2004139b3314964b3a93bb4b2ed7eacb22d6
5
5
  SHA512:
6
- metadata.gz: ff936f0e8dd58332943d6871f8df6f7eaa7f270253dbecd8ec3bf652d57dbabd23b5d162bdcee77e7ac88f6d58afcff58a99efade8b21f0e68808f89f7c29c36
7
- data.tar.gz: e1270fd1fdfcf1c68164159758c2eac6325ec5144c3974f315e59cfc38cc876f4664654cb352e5ca105f78d69ee27cfd05a2a1eaf8968c977e589af1178711e2
6
+ metadata.gz: 30734912941bd921dccc0f0cb98a2e45c2d83e251b2bd359647d2685a5655d389fca40f3514d21731fc6665e03ce8102224d513e714772e1610393c16b6f944c
7
+ data.tar.gz: d0a3a72ba084e92dc638c07bc76d14d663754c9725d175b1994e4c134c9ac86c28a1f7a20812a634a952c71d2443ee9cfdbc7b0a4116f5392b22ac668f85b260
data/README.md CHANGED
@@ -23,6 +23,11 @@ To create new TOTP secret:
23
23
  p totp.time #=> nil (UNIX time by Time or Integer; nil for the current time)
24
24
  p totp.password #=> "123456" (password for the current time)
25
25
 
26
+ # Format TOTP URI. Otpauth scheme URLs can be read by OTP::URI.parse.
27
+ totp.issuer = "My Company"
28
+ totp.accountname = "account@exaple.com"
29
+ p totp.to_uri #=> "otpauth://totp/My%20Company:account@exaple.com?secret=47JBA7ZWDDLNZJMX&issuer=My+Company"
30
+
26
31
  To verify given TOTP password:
27
32
 
28
33
  require "otp"
@@ -1,18 +1,23 @@
1
1
  require "otp/utils"
2
2
  require "otp/base32"
3
+ require "otp/uri"
3
4
 
4
5
  module OTP
5
6
  class Base
6
7
  include OTP::Utils
7
8
 
9
+ DEFAULT_DIGITS = 6
10
+ DEFAULT_ALGORITHM = "SHA1"
11
+
8
12
  attr_accessor :secret
9
13
  attr_accessor :algorithm
10
14
  attr_accessor :digits
15
+ attr_accessor :issuer, :accountname
11
16
 
12
- def initialize(secret=nil, algorithm="SHA1", digits=6)
17
+ def initialize(secret=nil, algorithm=nil, digits=nil)
13
18
  self.secret = secret
14
- self.algorithm = algorithm
15
- self.digits = digits
19
+ self.algorithm = algorithm || DEFAULT_ALGORITHM
20
+ self.digits = digits || DEFAULT_DIGITS
16
21
  end
17
22
 
18
23
  def new_secret(num_bytes=10)
@@ -40,5 +45,19 @@ module OTP
40
45
  return false if otp.nil? || otp.empty?
41
46
  return compare(password, otp)
42
47
  end
48
+
49
+ ## URI related methods
50
+
51
+ def to_uri
52
+ OTP::URI.format(self)
53
+ end
54
+
55
+ def type_specific_uri_params
56
+ raise NotImplementedError
57
+ end
58
+
59
+ def extract_type_specific_uri_param
60
+ raise NotImplementedError
61
+ end
43
62
  end
44
63
  end
@@ -12,5 +12,15 @@ module OTP
12
12
  def moving_factor
13
13
  return count
14
14
  end
15
+
16
+ def type_specific_uri_params
17
+ return {count: count}
18
+ end
19
+
20
+ def extract_type_specific_uri_params(query)
21
+ if value = query["count"]
22
+ self.count = value.to_i
23
+ end
24
+ end
15
25
  end
16
26
  end
@@ -4,14 +4,28 @@ module OTP
4
4
  class TOTP < OTP::Base
5
5
  attr_accessor :period, :time
6
6
 
7
+ DEFAULT_PERIOD = 30
8
+
7
9
  def initialize(*args)
8
10
  super
9
- self.period = 30
11
+ self.period = DEFAULT_PERIOD
10
12
  self.time = nil
11
13
  end
12
14
 
13
15
  def moving_factor
14
16
  return (time || Time.now).to_i / period
15
17
  end
18
+
19
+ def type_specific_uri_params
20
+ params = {}
21
+ params["period"] = period if period != DEFAULT_PERIOD
22
+ return params
23
+ end
24
+
25
+ def extract_type_specific_uri_params(query)
26
+ if value = query["period"]
27
+ self.period = value.to_i
28
+ end
29
+ end
16
30
  end
17
31
  end
@@ -0,0 +1,73 @@
1
+ require "uri"
2
+
3
+ module OTP
4
+ module URI
5
+ module_function
6
+
7
+ SCHEME = "othauth"
8
+
9
+ def parse(uri_string)
10
+ uri = ::URI.parse(uri_string)
11
+ raise "URI scheme not match: #{uri.scheme}" unless uri.scheme != SCHEME
12
+ otp = otp_class(uri).new
13
+ if %r{/(?:([^:]*): *)?(.+)} =~ uri.path
14
+ otp.issuer = $1
15
+ otp.accountname = $2
16
+ end
17
+ query = Hash[::URI.decode_www_form(uri.query)]
18
+ otp.secret = query["secret"]
19
+ if value = query["algorithm"]
20
+ otp.algorithm = value
21
+ end
22
+ if value = query["issuer"]
23
+ otp.issuer = value
24
+ end
25
+ if value = query["digits"]
26
+ otp.digits = value.to_i
27
+ end
28
+ otp.extract_type_specific_uri_params(query)
29
+ return otp
30
+ end
31
+
32
+ def format(otp)
33
+ raise "secret must be set" if otp.secret.nil?
34
+ raise "accountname must be set" if otp.accountname.nil?
35
+ typename = otp.class.name.split("::")[-1].downcase
36
+ label = otp.issuer ? "#{otp.issuer}:#{otp.accountname}" : otp.accountname
37
+ params = pickup_params(otp)
38
+ return "otpauth://%s/%s?%s" % [
39
+ ::URI.encode(typename),
40
+ ::URI.encode(label),
41
+ ::URI.encode_www_form(params)
42
+ ]
43
+ end
44
+
45
+ def otp_class(uri)
46
+ case uri.host.upcase
47
+ when "HOTP"
48
+ OTP::HOTP
49
+ when "TOTP"
50
+ OTP::TOTP
51
+ else
52
+ raise "unknown OTP type: #{uri.host}"
53
+ end
54
+ end
55
+
56
+ def pickup_params(otp)
57
+ param_spec = [
58
+ [:secret, nil],
59
+ [:issuer, nil],
60
+ [:algorithm, OTP::Base::DEFAULT_ALGORITHM],
61
+ [:digits, OTP::Base::DEFAULT_DIGITS],
62
+ ]
63
+ params = param_spec.reduce({}) do |h, (name, default)|
64
+ value = otp.send(name)
65
+ if value && value != default
66
+ h[name] = value
67
+ end
68
+ h
69
+ end
70
+ return params.merge(otp.type_specific_uri_params)
71
+ end
72
+ end
73
+ end
@@ -1,4 +1,4 @@
1
1
  module OTP
2
- VERSION = "0.0.2"
2
+ VERSION = "0.0.3"
3
3
  end
4
4
 
@@ -2,18 +2,18 @@ require "test/unit"
2
2
  require "otp/base32"
3
3
 
4
4
  class TestBase32 < Test::Unit::TestCase
5
- def assert_encode(plain, encoded)
6
- assert_equal(::OTP::Base32.encode(plain), encoded)
5
+ def assert_encode(encoded, plain)
6
+ assert_equal(encoded, ::OTP::Base32.encode(plain))
7
7
  end
8
8
 
9
- def assert_decode(encoded, plain)
9
+ def assert_decode(plain, encoded)
10
10
  plain &&= plain.dup.force_encoding("binary")
11
- assert_equal(::OTP::Base32.decode(encoded), plain)
11
+ assert_equal(plain, ::OTP::Base32.decode(encoded))
12
12
  end
13
13
 
14
14
  def assert_encode_decode(plain, encoded)
15
- assert_encode(plain, encoded)
16
- assert_decode(encoded, plain)
15
+ assert_decode(plain, encoded)
16
+ assert_encode(encoded, plain)
17
17
  end
18
18
 
19
19
  def test_base32
@@ -4,7 +4,7 @@ require "otp"
4
4
  class TestHTOP < Test::Unit::TestCase
5
5
  def assert_hotp(hotp, count, pass)
6
6
  hotp.count = count
7
- assert_equal(hotp.password, pass)
7
+ assert_equal(pass, hotp.password)
8
8
  assert(hotp.verify(pass))
9
9
  assert(!hotp.verify(pass.chop))
10
10
  end
@@ -4,7 +4,7 @@ require "otp"
4
4
  class TestTOTP < Test::Unit::TestCase
5
5
  def assert_totp(totp, time, pass)
6
6
  totp.time = time
7
- assert_equal(totp.password, pass)
7
+ assert_equal(pass, totp.password)
8
8
  assert(totp.verify(pass))
9
9
  assert(!totp.verify(pass.chop))
10
10
  end
@@ -0,0 +1,49 @@
1
+ require "test/unit"
2
+ require "otp"
3
+
4
+ class TestURI < Test::Unit::TestCase
5
+ def test_totp
6
+ secret = OTP::Base32.encode("12345678901234567890")
7
+ totp = OTP::TOTP.new
8
+ totp.secret = secret
9
+ totp.algorithm = "SHA256"
10
+ totp.digits = 8
11
+ totp.period = 60
12
+ totp.issuer = "My Company"
13
+ totp.accountname = "account@example.com"
14
+ uri = totp.to_uri
15
+
16
+ otp = OTP::URI.parse(uri)
17
+ assert_equal(OTP::TOTP, otp.class)
18
+ assert_equal(secret, otp.secret)
19
+ assert_equal("SHA256", otp.algorithm)
20
+ assert_equal(8, otp.digits)
21
+ assert_equal(60, otp.period)
22
+ assert_equal("account@example.com", otp.accountname)
23
+ assert_equal("My Company", otp.issuer)
24
+ totp.time = otp.time = Time.now
25
+ assert_equal(otp.password, totp.password)
26
+ end
27
+
28
+ def test_hotp
29
+ secret = OTP::Base32.encode("12345678901234567890")
30
+ hotp = OTP::HOTP.new
31
+ hotp.secret = secret
32
+ hotp.algorithm = "SHA256"
33
+ hotp.digits = 8
34
+ hotp.count = 1234
35
+ hotp.issuer = "My Company"
36
+ hotp.accountname = "account@example.com"
37
+ uri = hotp.to_uri
38
+
39
+ otp = OTP::URI.parse(uri)
40
+ assert_equal(OTP::HOTP, otp.class)
41
+ assert_equal(secret, otp.secret)
42
+ assert_equal("SHA256", otp.algorithm)
43
+ assert_equal(8, otp.digits)
44
+ assert_equal(1234, otp.count)
45
+ assert_equal("account@example.com", otp.accountname)
46
+ assert_equal("My Company", otp.issuer)
47
+ assert_equal(otp.password, hotp.password)
48
+ end
49
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: otp
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yuuzou Gotou
@@ -55,12 +55,14 @@ files:
55
55
  - lib/otp/base32.rb
56
56
  - lib/otp/hotp.rb
57
57
  - lib/otp/totp.rb
58
+ - lib/otp/uri.rb
58
59
  - lib/otp/utils.rb
59
60
  - lib/otp/version.rb
60
61
  - otp.gemspec
61
62
  - test/test_base32.rb
62
63
  - test/test_hotp.rb
63
64
  - test/test_totp.rb
65
+ - test/test_uri.rb
64
66
  homepage: ''
65
67
  licenses:
66
68
  - MIT
@@ -89,3 +91,4 @@ test_files:
89
91
  - test/test_base32.rb
90
92
  - test/test_hotp.rb
91
93
  - test/test_totp.rb
94
+ - test/test_uri.rb