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 +4 -4
- data/README.md +5 -0
- data/lib/otp/base.rb +22 -3
- data/lib/otp/hotp.rb +10 -0
- data/lib/otp/totp.rb +15 -1
- data/lib/otp/uri.rb +73 -0
- data/lib/otp/version.rb +1 -1
- data/test/test_base32.rb +6 -6
- data/test/test_hotp.rb +1 -1
- data/test/test_totp.rb +1 -1
- data/test/test_uri.rb +49 -0
- metadata +4 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2c5be10fdb528da5e72ee28f45a98265c7fad8e4
|
4
|
+
data.tar.gz: 237c2004139b3314964b3a93bb4b2ed7eacb22d6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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"
|
data/lib/otp/base.rb
CHANGED
@@ -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=
|
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
|
data/lib/otp/hotp.rb
CHANGED
@@ -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
|
data/lib/otp/totp.rb
CHANGED
@@ -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 =
|
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
|
data/lib/otp/uri.rb
ADDED
@@ -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
|
data/lib/otp/version.rb
CHANGED
data/test/test_base32.rb
CHANGED
@@ -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(
|
6
|
-
assert_equal(::OTP::Base32.encode(plain)
|
5
|
+
def assert_encode(encoded, plain)
|
6
|
+
assert_equal(encoded, ::OTP::Base32.encode(plain))
|
7
7
|
end
|
8
8
|
|
9
|
-
def assert_decode(
|
9
|
+
def assert_decode(plain, encoded)
|
10
10
|
plain &&= plain.dup.force_encoding("binary")
|
11
|
-
assert_equal(::OTP::Base32.decode(encoded)
|
11
|
+
assert_equal(plain, ::OTP::Base32.decode(encoded))
|
12
12
|
end
|
13
13
|
|
14
14
|
def assert_encode_decode(plain, encoded)
|
15
|
-
|
16
|
-
|
15
|
+
assert_decode(plain, encoded)
|
16
|
+
assert_encode(encoded, plain)
|
17
17
|
end
|
18
18
|
|
19
19
|
def test_base32
|
data/test/test_hotp.rb
CHANGED
data/test/test_totp.rb
CHANGED
data/test/test_uri.rb
ADDED
@@ -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.
|
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
|