otp 0.0.10 → 0.0.11
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/Rakefile +6 -1
- data/lib/otp/base.rb +1 -1
- data/lib/otp/base32.rb +29 -48
- data/lib/otp/utils.rb +9 -9
- data/lib/otp/version.rb +1 -1
- data/test/test_base.rb +14 -3
- data/test/test_base32.rb +30 -38
- data/test/test_uri.rb +15 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f29c79a8ef901714a1cbc348dfbd36c1f47e4e8e
|
4
|
+
data.tar.gz: 6e51d1a7b778f71092637fa5e3b698fc32f892a7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e83401db9e5f49f49f86667091054f27834d273b505bb5918fe8c2c485292d692c420bd2096eed767093c754cf4c978429d233253937555830944211e727ed3b
|
7
|
+
data.tar.gz: 9e0fdec72d1b9701a53d3253b38479060ea6c04af63efc0bd33e0f1b38df5b753a002f74b8245f804305842158c2476c467a72e896491f545d586fc983114ab1
|
data/Rakefile
CHANGED
data/lib/otp/base.rb
CHANGED
@@ -42,7 +42,7 @@ module OTP
|
|
42
42
|
raise ArgumentError, "last must be greater than or equal to 0" if last < 0
|
43
43
|
raise ArgumentError, "post must be greater than or equal to 0" if post < 0
|
44
44
|
return false if given_pw.nil? || given_pw.empty?
|
45
|
-
return (-last..post).any?{|i|
|
45
|
+
return (-last..post).any?{|i| otp_compare(password(i), given_pw) }
|
46
46
|
end
|
47
47
|
|
48
48
|
def to_uri
|
data/lib/otp/base32.rb
CHANGED
@@ -10,68 +10,49 @@ module OTP
|
|
10
10
|
"I"=>8, "J"=>9, "K"=>10, "L"=>11, "M"=>12, "N"=>13, "O"=>14, "P"=>15,
|
11
11
|
"Q"=>16, "R"=>17, "S"=>18, "T"=>19, "U"=>20, "V"=>21, "W"=>22, "X"=>23,
|
12
12
|
"Y"=>24, "Z"=>25, "2"=>26, "3"=>27, "4"=>28, "5"=>29, "6"=>30, "7"=>31,
|
13
|
-
"
|
14
|
-
|
15
|
-
|
16
|
-
DECODE_LENGTH = {
|
17
|
-
1 => 1, # 5 bits -> 1 byte (irregular)
|
18
|
-
2 => 1, # 10 bits -> 1 byte
|
19
|
-
3 => 2, # 15 bits -> 2 bytes (irregular)
|
20
|
-
4 => 2, # 20 bits -> 2 bytes
|
21
|
-
5 => 3, # 25 bits -> 3 bytes
|
22
|
-
6 => 4, # 30 bits -> 4 bytes (irregular)
|
23
|
-
7 => 4, # 35 bits -> 4 bytes
|
24
|
-
8 => 5, # 40 bits -> 5 bytes
|
13
|
+
"0"=>14, "1"=>11, "8"=>1, # mistyped chars
|
14
|
+
" "=>-2, "-"=>-2, "\n"=>-2, "\r"=>-2, "\t"=>-2, # separators
|
15
|
+
"="=>-1, # padding
|
25
16
|
}
|
26
17
|
|
27
18
|
module_function
|
28
19
|
|
29
|
-
def encode(bytes
|
20
|
+
def encode(bytes)
|
30
21
|
return nil unless bytes
|
31
|
-
pad = padding ? "=" : ""
|
32
|
-
ret = ""
|
33
22
|
bytes = bytes.dup.force_encoding("binary")
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
23
|
+
ret = ""
|
24
|
+
offset = buffer = buffered = 0
|
25
|
+
while offset < bytes.length
|
26
|
+
buffer = ((buffer << 8) | bytes[offset].ord)
|
27
|
+
buffered += 8
|
28
|
+
offset += 1
|
29
|
+
while buffered >= 5
|
30
|
+
ret << ENCODE_CHARS[buffer >> (buffered - 5)]
|
31
|
+
buffered -= 5
|
32
|
+
buffer &= (0xff >> (8 - buffered))
|
44
33
|
end
|
45
34
|
end
|
35
|
+
if buffered > 0
|
36
|
+
ret << ENCODE_CHARS[buffer << (5 - buffered)]
|
37
|
+
end
|
46
38
|
return ret
|
47
39
|
end
|
48
40
|
|
49
41
|
def decode(chars)
|
50
42
|
return nil unless chars
|
51
|
-
ret = ""
|
43
|
+
ret = "".force_encoding("binary")
|
52
44
|
chars = chars.upcase
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
elsif d < 0
|
65
|
-
n <<= 5 * (8-i)
|
66
|
-
off = chars.length
|
67
|
-
break
|
68
|
-
else
|
69
|
-
n <<= 5
|
70
|
-
n |= d
|
71
|
-
l = DECODE_LENGTH[i+1]
|
72
|
-
end
|
73
|
-
end
|
74
|
-
ret << (1..l).map{|i| (n >> 40 - i * 8) & 0xff }.pack("c*")
|
45
|
+
buffer = buffered = 0
|
46
|
+
chars.each_char do |c|
|
47
|
+
d = DECODE_MAP[c]
|
48
|
+
raise ArgumentError, "invalid char: #{c}" if d.nil?
|
49
|
+
next if d == -2
|
50
|
+
break if d == -1
|
51
|
+
buffer = (buffer << 5) | d
|
52
|
+
buffered += 5
|
53
|
+
next if buffered < 8
|
54
|
+
ret << ((buffer >> (buffered - 8)) & 0xff)
|
55
|
+
buffered -= 8
|
75
56
|
end
|
76
57
|
return ret
|
77
58
|
end
|
data/lib/otp/utils.rb
CHANGED
@@ -5,35 +5,35 @@ module OTP
|
|
5
5
|
private
|
6
6
|
|
7
7
|
def otp(algorithm, secret, moving_factor, digits)
|
8
|
-
message =
|
9
|
-
digest =
|
10
|
-
num =
|
11
|
-
return
|
8
|
+
message = otp_pack_int64(moving_factor)
|
9
|
+
digest = otp_hmac(algorithm, secret, message)
|
10
|
+
num = otp_pickup(digest)
|
11
|
+
return otp_truncate(num, digits)
|
12
12
|
end
|
13
13
|
|
14
|
-
def
|
14
|
+
def otp_pack_int64(i)
|
15
15
|
return [i >> 32 & 0xffffffff, i & 0xffffffff].pack("NN")
|
16
16
|
end
|
17
17
|
|
18
|
-
def
|
18
|
+
def otp_hmac(algorithm, secret, text)
|
19
19
|
mac = OpenSSL::HMAC.new(secret, algorithm)
|
20
20
|
mac << text
|
21
21
|
return mac.digest
|
22
22
|
end
|
23
23
|
|
24
|
-
def
|
24
|
+
def otp_pickup(digest)
|
25
25
|
offset = digest[-1].ord & 0xf
|
26
26
|
binary = digest[offset, 4]
|
27
27
|
return binary.unpack("N")[0] & 0x7fffffff
|
28
28
|
end
|
29
29
|
|
30
|
-
def
|
30
|
+
def otp_truncate(num, digits)
|
31
31
|
pw = (num % (10 ** digits)).to_s
|
32
32
|
pw.prepend("0") while pw.length < digits
|
33
33
|
return pw
|
34
34
|
end
|
35
35
|
|
36
|
-
def
|
36
|
+
def otp_compare(a, b)
|
37
37
|
return a.to_i == b.to_i
|
38
38
|
end
|
39
39
|
end
|
data/lib/otp/version.rb
CHANGED
data/test/test_base.rb
CHANGED
@@ -24,8 +24,8 @@ class TestBase < Test::Unit::TestCase
|
|
24
24
|
assert_equal("", otp.secret)
|
25
25
|
assert_equal("", otp.raw_secret)
|
26
26
|
|
27
|
-
otp.secret = "MZXW6YTBOI
|
28
|
-
assert_equal("MZXW6YTBOI
|
27
|
+
otp.secret = "MZXW6YTBOI"
|
28
|
+
assert_equal("MZXW6YTBOI", otp.secret)
|
29
29
|
assert_equal("foobar", otp.raw_secret)
|
30
30
|
|
31
31
|
otp.secret = "MZXW6YTBOI"
|
@@ -45,10 +45,21 @@ class TestBase < Test::Unit::TestCase
|
|
45
45
|
assert_equal("", otp.raw_secret)
|
46
46
|
|
47
47
|
otp.raw_secret = "foobarbaz"
|
48
|
-
assert_equal("MZXW6YTBOJRGC6Q
|
48
|
+
assert_equal("MZXW6YTBOJRGC6Q", otp.secret)
|
49
49
|
assert_equal("foobarbaz", otp.raw_secret)
|
50
50
|
end
|
51
51
|
|
52
|
+
def test_verify
|
53
|
+
otp = OTP::Base.new
|
54
|
+
e = assert_raise(ArgumentError){ otp.verify("0", last:-1) }
|
55
|
+
assert_match(/last must be greater than or equal to 0/, e.message)
|
56
|
+
e = assert_raise(ArgumentError){ otp.verify("0", post:-1) }
|
57
|
+
assert_match(/post must be greater than or equal to 0/, e.message)
|
58
|
+
assert_equal(false, otp.verify(nil))
|
59
|
+
assert_equal(false, otp.verify(""))
|
60
|
+
assert_raise(NotImplementedError){ otp.verify("0") }
|
61
|
+
end
|
62
|
+
|
52
63
|
def test_moving_factor
|
53
64
|
base = OTP::Base.new
|
54
65
|
hotp = OTP::HOTP.new
|
data/test/test_base32.rb
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
require_relative "helper"
|
2
2
|
|
3
3
|
class TestBase32 < Test::Unit::TestCase
|
4
|
-
def assert_encode(encoded, plain
|
5
|
-
assert_equal(encoded, ::OTP::Base32.encode(plain
|
4
|
+
def assert_encode(encoded, plain)
|
5
|
+
assert_equal(encoded, ::OTP::Base32.encode(plain))
|
6
6
|
end
|
7
7
|
|
8
8
|
def assert_decode(plain, encoded)
|
@@ -10,58 +10,50 @@ class TestBase32 < Test::Unit::TestCase
|
|
10
10
|
assert_equal(plain, ::OTP::Base32.decode(encoded))
|
11
11
|
end
|
12
12
|
|
13
|
-
def assert_encode_decode(plain, encoded
|
13
|
+
def assert_encode_decode(plain, encoded)
|
14
14
|
assert_decode(plain, encoded)
|
15
|
-
assert_encode(encoded, plain
|
15
|
+
assert_encode(encoded, plain)
|
16
16
|
end
|
17
17
|
|
18
18
|
def test_base32
|
19
19
|
assert_encode_decode(nil, nil)
|
20
20
|
assert_encode_decode("", "")
|
21
|
-
assert_encode_decode("f", "MY
|
22
|
-
assert_encode_decode("fo", "MZXQ
|
23
|
-
assert_encode_decode("foo", "MZXW6
|
24
|
-
assert_encode_decode("foob", "MZXW6YQ
|
21
|
+
assert_encode_decode("f", "MY")
|
22
|
+
assert_encode_decode("fo", "MZXQ")
|
23
|
+
assert_encode_decode("foo", "MZXW6")
|
24
|
+
assert_encode_decode("foob", "MZXW6YQ")
|
25
25
|
assert_encode_decode("fooba", "MZXW6YTB")
|
26
|
-
assert_encode_decode("foobar", "MZXW6YTBOI
|
26
|
+
assert_encode_decode("foobar", "MZXW6YTBOI")
|
27
27
|
assert_encode_decode("\u{3042}\u{3044}\u{3046}\u{3048}\u{304a}", "4OAYFY4BQTRYDBXDQGEOHAMK")
|
28
28
|
end
|
29
29
|
|
30
|
-
def
|
31
|
-
|
32
|
-
|
33
|
-
assert_encode_decode("f", "MY", padding: false)
|
34
|
-
assert_encode_decode("fo", "MZXQ", padding: false)
|
35
|
-
assert_encode_decode("foo", "MZXW6", padding: false)
|
36
|
-
assert_encode_decode("foob", "MZXW6YQ", padding: false)
|
37
|
-
assert_encode_decode("fooba", "MZXW6YTB", padding: false)
|
38
|
-
assert_encode_decode("foobar", "MZXW6YTBOI", padding: false)
|
39
|
-
assert_encode_decode("\u{3042}\u{3044}\u{3046}\u{3048}\u{304a}", "4OAYFY4BQTRYDBXDQGEOHAMK", padding: false)
|
30
|
+
def test_decode_with_mistyped
|
31
|
+
assert_decode(OTP::Base32.decode("AOLB"), "A018")
|
32
|
+
assert_decode(OTP::Base32.decode("aolb"), "A018")
|
40
33
|
end
|
41
34
|
|
42
|
-
def
|
43
|
-
assert_decode("f", "
|
44
|
-
assert_decode("fo", "
|
45
|
-
assert_decode("foo", "
|
46
|
-
assert_decode("
|
47
|
-
assert_decode("
|
48
|
-
assert_decode("
|
49
|
-
assert_decode("foo", "mzxw6")
|
50
|
-
assert_decode("foob", "mzxw6yq")
|
35
|
+
def test_decode_include_space
|
36
|
+
assert_decode("f", "M Y")
|
37
|
+
assert_decode("fo", "MZ XQ")
|
38
|
+
assert_decode("foo", "M\nZ\rX\tW-6")
|
39
|
+
assert_decode("f", "m y")
|
40
|
+
assert_decode("fo", "mz xq")
|
41
|
+
assert_decode("foo", "m\nz\rx\tw-6")
|
51
42
|
end
|
52
43
|
|
53
44
|
def test_unspecified
|
54
|
-
assert_raise(ArgumentError){ OTP::Base32.decode("
|
45
|
+
e = assert_raise(ArgumentError){ OTP::Base32.decode("MY9") }
|
46
|
+
assert_equal("invalid char: 9", e.message)
|
55
47
|
assert_decode("", "=")
|
56
|
-
assert_decode("
|
57
|
-
assert_decode("
|
58
|
-
assert_decode("foo
|
59
|
-
assert_decode("
|
60
|
-
assert_decode("
|
61
|
-
assert_decode("foo
|
62
|
-
assert_decode("
|
63
|
-
assert_decode("
|
64
|
-
assert_decode("foo
|
48
|
+
assert_decode("", "M=======")
|
49
|
+
assert_decode("f", "MZX=====")
|
50
|
+
assert_decode("foo", "MZXW6Y==")
|
51
|
+
assert_decode("", "M=======G")
|
52
|
+
assert_decode("f", "MZX=====G")
|
53
|
+
assert_decode("foo", "MZXW6Y==G")
|
54
|
+
assert_decode("", "M")
|
55
|
+
assert_decode("f", "MZX")
|
56
|
+
assert_decode("foo", "MZXW6Y")
|
65
57
|
assert_decode("fooba", "MZXW6YTB===")
|
66
58
|
assert_decode("f", "MY=AAAAA")
|
67
59
|
assert_decode("fo", "MZXQ=AAA")
|
data/test/test_uri.rb
CHANGED
@@ -17,6 +17,11 @@ class TestURI < Test::Unit::TestCase
|
|
17
17
|
assert_equal("account@example.com", otp.accountname)
|
18
18
|
assert_equal("My Company", otp.issuer)
|
19
19
|
|
20
|
+
uri = "otpauth://totp/My%20Company%3A%20%20account@example.com?secret=GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ"
|
21
|
+
otp = OTP::URI.parse(uri)
|
22
|
+
assert_equal("account@example.com", otp.accountname)
|
23
|
+
assert_equal("My Company", otp.issuer)
|
24
|
+
|
20
25
|
uri = "otpauth://totp/My%20Company:%20%20account@example.com?secret=GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ&issuer=Foo"
|
21
26
|
otp = OTP::URI.parse(uri)
|
22
27
|
assert_equal("account@example.com", otp.accountname)
|
@@ -113,6 +118,16 @@ class TestURI < Test::Unit::TestCase
|
|
113
118
|
assert_equal(otp.password, hotp.password)
|
114
119
|
end
|
115
120
|
|
121
|
+
def test_format_invalid
|
122
|
+
totp = OTP::TOTP.new
|
123
|
+
e = assert_raise(RuntimeError){ totp.to_uri }
|
124
|
+
assert_match(/secret must be set/, e.message)
|
125
|
+
|
126
|
+
totp.new_secret
|
127
|
+
e = assert_raise(RuntimeError){ totp.to_uri }
|
128
|
+
assert_match(/accountname must be set/, e.message)
|
129
|
+
end
|
130
|
+
|
116
131
|
def test_parse_invalid
|
117
132
|
e = assert_raise(RuntimeError){ OTP::URI.parse("http://www.netlab.jp") }
|
118
133
|
assert_match(/URI scheme not match/, e.message)
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
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.11
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Yuuzou Gotou
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-06-
|
11
|
+
date: 2015-06-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|