otp 0.0.2 → 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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