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 +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
|