otp 0.0.7 → 0.0.8
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/.gitignore +1 -0
- data/README.md +17 -10
- data/lib/otp/base32.rb +32 -14
- data/lib/otp/version.rb +1 -1
- data/test/test_base32.rb +35 -4
- 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: 0c73f2c47bcd8a1d4b36391d5b8ffd77fee13670
|
4
|
+
data.tar.gz: 29c50f0d362b6abd5d018c69301f919dfffc748c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 234d1455ead02f326578c8a2470586eb056c8c91ace926476ff384e53843ef1a58481d015e8dc05442b6830834703cb1b5205344cdca6e9aa7ad7e1df9204b0b
|
7
|
+
data.tar.gz: cd3df6b91c9fb94f138646913aa6a3efdaa8988e0c3a96fb3b0dd58a3a9d32065425c932226cafad893d22fec14565997b2b4a92f76a0d30995498a4597820e5
|
data/.gitignore
CHANGED
data/README.md
CHANGED
@@ -4,13 +4,8 @@
|
|
4
4
|
[](https://rubygems.org/gems/otp)
|
5
5
|
[](https://github.com/gotoyuzo/otp/blob/master/LICENSE.txt)
|
6
6
|
|
7
|
-
This library provides an implementation of
|
8
|
-
HMAC-Based One-Time Password Algorithm (HOTP; RFC4226) and
|
7
|
+
This library provides an implementation of HMAC-Based One-Time Password Algorithm (HOTP; RFC4226) and
|
9
8
|
Time-Based One-Time Password Algorithm (HOTP; RFC6238).
|
10
|
-
The Algorithm details can be referred at the following URLs.
|
11
|
-
|
12
|
-
* HOTP: http://tools.ietf.org/html/rfc4226
|
13
|
-
* TOTP: http://tools.ietf.org/html/rfc6238
|
14
9
|
|
15
10
|
## Usage
|
16
11
|
|
@@ -43,8 +38,20 @@ To verify given TOTP password:
|
|
43
38
|
totp.secret = "YVMR2G7N4OAXGKFC"
|
44
39
|
p totp.verify("123456") #=> true/false (verify given passowrd)
|
45
40
|
|
46
|
-
|
47
|
-
|
41
|
+
You can use the last and post option parameters to verify several generations, including before and after the current password.
|
42
|
+
|
43
|
+
# verify passwords from last 2 generation to post 1 generation
|
44
|
+
p totp.verify("123456", last: 2, post: 1)
|
45
|
+
|
46
|
+
## Related Information
|
47
|
+
|
48
|
+
TOTP and HOTP algorithm details can be referred at the following URLs.
|
49
|
+
|
50
|
+
* HOTP: An HMAC-Based One-Time Password Algorithm - http://tools.ietf.org/html/rfc4226
|
51
|
+
* TOTP: Time-Based One-Time Password Algorithm - http://tools.ietf.org/html/rfc6238
|
52
|
+
|
53
|
+
In the OTP URI format, the value of "secret" is encoded with BASE32 algorithm.
|
54
|
+
The Format details are described in the document of Google Authenticator.
|
48
55
|
|
49
|
-
*
|
50
|
-
* Google Authenticator Key URI format
|
56
|
+
* The Base16, Base32, and Base64 Data Encodings - http://tools.ietf.org/html/rfc4648
|
57
|
+
* Google Authenticator Key URI format - https://github.com/google/google-authenticator/wiki/Key-Uri-Format
|
data/lib/otp/base32.rb
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
module OTP
|
2
2
|
module Base32
|
3
|
-
|
3
|
+
ENCODE_CHARS = %w(
|
4
4
|
A B C D E F G H I J K L M N O P
|
5
5
|
Q R S T U V W X Y Z 2 3 4 5 6 7
|
6
6
|
)
|
7
7
|
|
8
|
-
|
8
|
+
DECODE_MAP = {
|
9
9
|
?A=>0, ?B=>1, ?C=>2, ?D=>3, ?E=>4, ?F=>5, ?G=>6, ?H=>7,
|
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,
|
@@ -13,23 +13,35 @@ module OTP
|
|
13
13
|
?==>-1,
|
14
14
|
}
|
15
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
|
25
|
+
}
|
26
|
+
|
16
27
|
module_function
|
17
28
|
|
18
|
-
def encode(bytes)
|
29
|
+
def encode(bytes, padding: true)
|
19
30
|
return nil unless bytes
|
31
|
+
pad = padding ? ?= : ""
|
20
32
|
ret = ""
|
21
33
|
bytes = bytes.dup.force_encoding("binary")
|
22
34
|
off = 0
|
23
35
|
while off < bytes.length
|
24
36
|
n = 0
|
25
37
|
bits = bytes[off, 5]
|
26
|
-
|
38
|
+
off += 5
|
39
|
+
l = (bits.length * 8.0 / 5.0).ceil
|
27
40
|
bits << "\0" while bits.length < 5
|
28
41
|
bits.each_byte{|b| n = (n << 8) | b }
|
29
|
-
8.
|
30
|
-
ret << ((
|
42
|
+
(1..8).each do |i|
|
43
|
+
ret << ((i > l) ? pad : ENCODE_CHARS[(n >> (8-i)*5) & 0x1f])
|
31
44
|
end
|
32
|
-
off += 5
|
33
45
|
end
|
34
46
|
return ret
|
35
47
|
end
|
@@ -43,18 +55,24 @@ module OTP
|
|
43
55
|
while off < chars.length
|
44
56
|
n = l = 0
|
45
57
|
bits = chars[off, 8]
|
58
|
+
off += 8
|
46
59
|
bits << "=" while bits.length < 8
|
47
60
|
bits.each_char.with_index do |c, i|
|
48
|
-
d =
|
49
|
-
|
50
|
-
|
51
|
-
|
61
|
+
d = DECODE_MAP[c]
|
62
|
+
if d.nil?
|
63
|
+
raise ArgumentError, "invalid char: #{c}"
|
64
|
+
elsif d < 0
|
65
|
+
n <<= 5 * (8-i)
|
66
|
+
off = chars.length
|
67
|
+
break
|
68
|
+
else
|
69
|
+
n <<= 5
|
52
70
|
n |= d
|
53
|
-
l = i * 5 / 8
|
71
|
+
l = ((i+1) * 5.0 / 8.0).floor
|
72
|
+
l = DECODE_LENGTH[i+1]
|
54
73
|
end
|
55
74
|
end
|
56
|
-
ret << (
|
57
|
-
off += 8
|
75
|
+
ret << (1..l).map{|i| (n >> 40 - i * 8) & 0xff }.pack("c*")
|
58
76
|
end
|
59
77
|
return ret
|
60
78
|
end
|
data/lib/otp/version.rb
CHANGED
data/test/test_base32.rb
CHANGED
@@ -2,8 +2,8 @@ require "test/unit"
|
|
2
2
|
require "otp/base32"
|
3
3
|
|
4
4
|
class TestBase32 < Test::Unit::TestCase
|
5
|
-
def assert_encode(encoded, plain)
|
6
|
-
assert_equal(encoded, ::OTP::Base32.encode(plain))
|
5
|
+
def assert_encode(encoded, plain, padding: true)
|
6
|
+
assert_equal(encoded, ::OTP::Base32.encode(plain, padding: padding))
|
7
7
|
end
|
8
8
|
|
9
9
|
def assert_decode(plain, encoded)
|
@@ -11,9 +11,9 @@ class TestBase32 < Test::Unit::TestCase
|
|
11
11
|
assert_equal(plain, ::OTP::Base32.decode(encoded))
|
12
12
|
end
|
13
13
|
|
14
|
-
def assert_encode_decode(plain, encoded)
|
14
|
+
def assert_encode_decode(plain, encoded, padding: true)
|
15
15
|
assert_decode(plain, encoded)
|
16
|
-
assert_encode(encoded, plain)
|
16
|
+
assert_encode(encoded, plain, padding: padding)
|
17
17
|
end
|
18
18
|
|
19
19
|
def test_base32
|
@@ -28,6 +28,18 @@ class TestBase32 < Test::Unit::TestCase
|
|
28
28
|
assert_encode_decode("\u{3042}\u{3044}\u{3046}\u{3048}\u{304a}", "4OAYFY4BQTRYDBXDQGEOHAMK")
|
29
29
|
end
|
30
30
|
|
31
|
+
def test_base32_without_padding
|
32
|
+
assert_encode_decode(nil, nil, padding: false)
|
33
|
+
assert_encode_decode("", "", padding: false)
|
34
|
+
assert_encode_decode("f", "MY", padding: false)
|
35
|
+
assert_encode_decode("fo", "MZXQ", padding: false)
|
36
|
+
assert_encode_decode("foo", "MZXW6", padding: false)
|
37
|
+
assert_encode_decode("foob", "MZXW6YQ", padding: false)
|
38
|
+
assert_encode_decode("fooba", "MZXW6YTB", padding: false)
|
39
|
+
assert_encode_decode("foobar", "MZXW6YTBOI", padding: false)
|
40
|
+
assert_encode_decode("\u{3042}\u{3044}\u{3046}\u{3048}\u{304a}", "4OAYFY4BQTRYDBXDQGEOHAMK", padding: false)
|
41
|
+
end
|
42
|
+
|
31
43
|
def test_truncated_decode
|
32
44
|
assert_decode("f", "MY")
|
33
45
|
assert_decode("fo", "MZXQ")
|
@@ -38,4 +50,23 @@ class TestBase32 < Test::Unit::TestCase
|
|
38
50
|
assert_decode("foo", "mzxw6")
|
39
51
|
assert_decode("foob", "mzxw6yq")
|
40
52
|
end
|
53
|
+
|
54
|
+
def test_unspecified
|
55
|
+
assert_raise(ArgumentError){ OTP::Base32.decode("MY0") }
|
56
|
+
assert_decode("", "=")
|
57
|
+
assert_decode("`", "M=======")
|
58
|
+
assert_decode("fn", "MZX=====")
|
59
|
+
assert_decode("foo`", "MZXW6Y==")
|
60
|
+
assert_decode("`", "M=======G")
|
61
|
+
assert_decode("fn", "MZX=====G")
|
62
|
+
assert_decode("foo`", "MZXW6Y==G")
|
63
|
+
assert_decode("`", "M")
|
64
|
+
assert_decode("fn", "MZX")
|
65
|
+
assert_decode("foo`", "MZXW6Y")
|
66
|
+
assert_decode("fooba", "MZXW6YTB===")
|
67
|
+
assert_decode("f", "MY=AAAAA")
|
68
|
+
assert_decode("fo", "MZXQ=AAA")
|
69
|
+
assert_decode("foo", "MZXW6=AA")
|
70
|
+
assert_decode("foo", "MZXW6=00")
|
71
|
+
end
|
41
72
|
end
|
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.8
|
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-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|