bnet-authenticator 0.1.6 → 0.1.8

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 0d94b65670bd3f1a4459dd364e9849c926ac646d
4
+ data.tar.gz: 78084b93e3e2a87566e2a3526211f2e5680fd1bc
5
+ SHA512:
6
+ metadata.gz: 92a2c1dd5afe2bb2b3affdf3746c7221f19e549988aa03dcd3908053d0fe90e0d444d49e4d653838ba728d849c1f09094c70e6c9f2beb151797f09efd273aeb7
7
+ data.tar.gz: f29318c63c651cc07f600c4e56233248480d7e195e059dcbc65dc9be02bb302c7c63d6c751ae2fe97508fed07bbb74ab273f1dad38767899ee1ebebcea88f4a7
data/README.md CHANGED
@@ -1,39 +1,51 @@
1
- Bnet::Authenticator
2
- ====
1
+ # Bnet::Authenticator
3
2
  Ruby implementation of the Battle.net Mobile Authenticator.
4
3
 
5
- [![Gem Version](https://badge.fury.io/rb/bnet-authenticator.png)](http://badge.fury.io/rb/bnet-authenticator)
4
+ [![Gem Version](http://img.shields.io/gem/v/bnet-authenticator.svg?style=flat)](http://badge.fury.io/rb/bnet-authenticator)
5
+ [![Dependency Status](http://img.shields.io/gemnasium/dorentus/bnet-authenticator.svg?style=flat)](https://gemnasium.com/dorentus/bnet-authenticator)
6
6
 
7
- [![Build Status](https://travis-ci.org/dorentus/bnet-authenticator.svg?branch=master)](https://travis-ci.org/dorentus/bnet-authenticator) [![Dependency Status](https://gemnasium.com/dorentus/bnet-authenticator.png)](https://gemnasium.com/dorentus/bnet-authenticator) [![Coverage Status](https://coveralls.io/repos/dorentus/bnet-authenticator/badge.png)](https://coveralls.io/r/dorentus/bnet-authenticator) [![Code Climate](https://codeclimate.com/github/dorentus/bnet-authenticator.png)](https://codeclimate.com/github/dorentus/bnet-authenticator)
7
+ [![Build Status](http://img.shields.io/travis/dorentus/bnet-authenticator.svg?style=flat)](https://travis-ci.org/dorentus/bnet-authenticator)
8
8
 
9
- Installation
10
- ====
11
- $ [sudo] gem install bnet-authenticator
9
+ [![Coverage Status](http://img.shields.io/coveralls/dorentus/bnet-authenticator.svg?style=flat)](https://coveralls.io/r/dorentus/bnet-authenticator)
12
10
 
13
- Using the library
14
- ====
15
- >> require 'bnet/authenticator'
11
+ [![Code Climate](http://img.shields.io/codeclimate/github/dorentus/bnet-authenticator.svg?style=flat)](https://codeclimate.com/github/dorentus/bnet-authenticator)
12
+ [![Coverage Status from Code Climate](http://img.shields.io/codeclimate/coverage/github/dorentus/bnet-authenticator.svg?style=flat)](https://codeclimate.com/github/dorentus/bnet-authenticator)
16
13
 
17
- Request a new authenticator
18
- ----
19
- >> authenticator = Bnet::Authenticator.request_authenticator(:US)
20
- => #<Bnet::Authenticator:0x007f83599ae848 @serial="US-1403-1677-5336", @secret="33a107e6a2927a2aa1be99cfe7b2d08c092a7a2a", @region=:US, @restorecode="4YV9XZVNMX">
14
+ <sub>FYI: Badge images provided by http://shields.io/</sub>
21
15
 
22
- Get a token
23
- ----
24
- >> authenticator.get_token
25
- => ["18338810", 1394965110]
16
+ ## Installation
17
+ ```bash
18
+ $ [sudo] gem install bnet-authenticator
19
+ ```
26
20
 
27
- Restore an authenticator from server
28
- ----
29
- >> Bnet::Authenticator.restore_authenticator('CN-1402-1943-1283', '4CKBN08QEB')
30
- => #<Bnet::Authenticator:0x007f83599cf458 @serial="CN-1402-1943-1283", @secret="4202aa2182640745d8a807e0fe7e34b30c1edb23", @region=:CN, @restorecode="4CKBN08QEB">
21
+ ## Using the library
22
+ ```irb
23
+ >> require 'bnet/authenticator'
24
+ ```
31
25
 
32
- Initialize an authenticator with given serial and secret
33
- ----
34
- >> Bnet::Authenticator.new('CN-1402-1943-1283', '4202aa2182640745d8a807e0fe7e34b30c1edb23')
35
- => #<Bnet::Authenticator:0x007f8359a17500 @serial="CN-1402-1943-1283", @secret="4202aa2182640745d8a807e0fe7e34b30c1edb23", @region=:CN, @restorecode="4CKBN08QEB">
26
+ ### Request a new authenticator
27
+ ```irb
28
+ >> authenticator = Bnet::Authenticator.request_authenticator(:US)
29
+ => {:serial=>"US-1405-0242-3258", :secret=>"778275450e5c3e092bc4fe901cd7c11241166c88", :restorecode=>"0WCRH9Z926", :region=>:US}
30
+ ```
36
31
 
37
- Using the command-line tool
38
- ====
32
+ ### Get a token
33
+ ```irb
34
+ >> authenticator.get_token
35
+ => ["38530888", 1399038930]
36
+ ```
37
+
38
+ ### Restore an authenticator from server
39
+ ```irb
40
+ >> Bnet::Authenticator.restore_authenticator('CN-1402-1943-1283', '4CKBN08QEB')
41
+ => {:serial=>"CN-1402-1943-1283", :secret=>"4202aa2182640745d8a807e0fe7e34b30c1edb23", :restorecode=>"4CKBN08QEB", :region=>:CN}
42
+ ```
43
+
44
+ ### Initialize an authenticator with given serial and secret
45
+ ```irb
46
+ >> Bnet::Authenticator.new('CN-1402-1943-1283', '4202aa2182640745d8a807e0fe7e34b30c1edb23')
47
+ => {:serial=>"CN-1402-1943-1283", :secret=>"4202aa2182640745d8a807e0fe7e34b30c1edb23", :restorecode=>"4CKBN08QEB", :region=>:CN}
48
+ ```
49
+
50
+ ## Using the command-line tool
39
51
  Run `bna` and follow instructions.
@@ -17,10 +17,11 @@ Gem::Specification.new do |s|
17
17
  s.required_ruby_version = '>= 1.9.3'
18
18
 
19
19
  if s.respond_to?(:add_development_dependency)
20
- s.add_development_dependency 'rake', '~> 10.1'
21
- s.add_development_dependency 'minitest', '~> 5.0'
22
- s.add_development_dependency 'yard', '~> 0.8'
23
- s.add_development_dependency 'coveralls', '~> 0.7'
20
+ s.add_development_dependency 'rake'
21
+ s.add_development_dependency 'minitest'
22
+ s.add_development_dependency 'yard'
23
+ s.add_development_dependency 'coveralls'
24
+ s.add_development_dependency 'codeclimate-test-reporter'
24
25
  end
25
26
 
26
27
  s.files = `git ls-files`.split("\n") - %w(.travis.yml .gitignore)
@@ -0,0 +1,34 @@
1
+ require 'digest/sha1'
2
+
3
+ module Bnet
4
+ module Attributes
5
+ class Restorecode
6
+ attr_reader :text
7
+
8
+ def initialize(serial_or_restorecode, secret = nil)
9
+ if secret.nil?
10
+ restorecode = serial_or_restorecode
11
+ raise BadInputError.new("bad restoration code #{restorecode}") unless restorecode =~ /[0-9A-Z]{10}/
12
+ @text = restorecode
13
+ else
14
+ serial = serial_or_restorecode.is_a?(Serial) ? serial_or_restorecode : Serial.new(serial_or_restorecode)
15
+ secret = Secret.new secret unless secret.is_a?(Secret)
16
+
17
+ bin = Digest::SHA1.digest(serial.normalized + secret.binary)
18
+
19
+ @text = bin.split(//).last(10).join.bytes.map do |v|
20
+ RESTORECODE_MAP[v & 0x1f]
21
+ end.pack('C*')
22
+ end
23
+ end
24
+
25
+ def binary
26
+ text.bytes.map do |c|
27
+ RESTORECODE_MAP_INVERSE[c]
28
+ end.pack('C*')
29
+ end
30
+
31
+ alias_method :to_s, :text
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,25 @@
1
+ module Bnet
2
+ module Attributes
3
+ class Secret
4
+ attr_reader :text
5
+
6
+ def initialize(secret)
7
+ secret = secret.to_s
8
+
9
+ if secret =~ /[0-9a-f]{40}/i
10
+ @text = secret
11
+ elsif secret.length == 20 # treated as binary input
12
+ @text = secret.unpack('C*').map{ |i| i.to_s(16).rjust(2, '0') }.join
13
+ else
14
+ raise BadInputError.new("bad secret #{secret}")
15
+ end
16
+ end
17
+
18
+ def binary
19
+ text.scan(/.{2}/).map{|s| s.to_i(16)}.pack('C*')
20
+ end
21
+
22
+ alias_method :to_s, :text
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,23 @@
1
+ module Bnet
2
+ module Attributes
3
+ class Serial
4
+ attr_reader :normalized
5
+
6
+ def initialize(serial)
7
+ serial = serial.to_s.upcase.gsub(/-/, '')
8
+ raise BadInputError.new("bad serial #{serial}") unless serial =~ Regexp.new("^(#{AUTHENTICATOR_HOSTS.keys.join('|')})\\d{12}$")
9
+ @normalized = serial
10
+ end
11
+
12
+ def prettified
13
+ "#{normalized[0, 2]}-" + normalized[2, 12].scan(/.{4}/).join('-')
14
+ end
15
+
16
+ def region
17
+ normalized[0, 2].upcase.to_sym
18
+ end
19
+
20
+ alias_method :to_s, :prettified
21
+ end
22
+ end
23
+ end
@@ -1,52 +1,51 @@
1
1
  require 'bnet/errors'
2
- require 'bnet/authenticator_helper'
2
+ require 'bnet/constants'
3
+
4
+ require 'bnet/attributes/serial'
5
+ require 'bnet/attributes/secret'
6
+ require 'bnet/attributes/restorecode'
7
+
8
+ require 'bnet/authenticator/core'
3
9
 
4
10
  module Bnet
5
11
 
6
12
  # The Battle.net authenticator
7
13
  class Authenticator
8
- include AuthenticatorHelper
9
14
 
10
15
  # @!attribute [r] serial
11
16
  # @return [String] serial
12
- def serial
13
- self.class.prettify_serial(@normalized_serial)
14
- end
17
+ attr_reader :serial
15
18
 
16
19
  # @!attribute [r] secret
17
- # @return [String] hexified secret
20
+ # @return [String] hexlified secret
18
21
  attr_reader :secret
19
22
 
20
23
  # @!attribute [r] restorecode
21
24
  # @return [String] restoration code
22
- def restorecode
23
- restorecode_bin = Digest::SHA1.digest(@normalized_serial + secret.as_hex_to_bin)
24
- self.class.encode_restorecode(restorecode_bin.split(//).last(10).join)
25
- end
25
+ attr_reader :restorecode
26
26
 
27
27
  # @!attribute [r] region
28
28
  # @return [Symbol] region
29
- def region
30
- self.class.extract_region(@normalized_serial)
31
- end
29
+ attr_reader :region
32
30
 
33
31
  # Create a new authenticator with given serial and secret
34
32
  # @param serial [String]
35
33
  # @param secret [String]
36
34
  def initialize(serial, secret)
37
- raise BadInputError.new("bad serial #{serial}") unless self.class.is_valid_serial?(serial)
38
- raise BadInputError.new("bad secret #{secret}") unless self.class.is_valid_secret?(secret)
39
-
40
- @normalized_serial = self.class.normalize_serial(serial)
41
- @secret = secret
35
+ serial = Bnet::Attributes::Serial.new serial
36
+ secret = Bnet::Attributes::Secret.new secret
37
+ restorecode = Bnet::Attributes::Restorecode.new serial, secret
38
+
39
+ @serial = serial.to_s
40
+ @secret = secret.to_s
41
+ @restorecode = restorecode.to_s
42
+ @region = serial.region
42
43
  end
43
44
 
44
45
  # Request a new authenticator from server
45
46
  # @param region [Symbol]
46
47
  # @return [Bnet::Authenticator]
47
48
  def self.request_authenticator(region)
48
- raise BadInputError.new("bad region #{region}") unless is_valid_region?(region)
49
-
50
49
  k = create_one_time_pad(37)
51
50
 
52
51
  payload_plain = "\1" + k + region.to_s + CLIENT_MODEL.ljust(16, "\0")[0, 16]
@@ -56,7 +55,7 @@ module Bnet
56
55
 
57
56
  decrypted = decrypt_response(response_body[8, 37], k)
58
57
 
59
- Authenticator.new(decrypted[20, 17], decrypted[0, 20].as_bin_to_hex)
58
+ Authenticator.new(decrypted[20, 17], decrypted[0, 20])
60
59
  end
61
60
 
62
61
  # Restore an authenticator from server
@@ -64,35 +63,36 @@ module Bnet
64
63
  # @param restorecode [String]
65
64
  # @return [Bnet::Authenticator]
66
65
  def self.restore_authenticator(serial, restorecode)
67
- raise BadInputError.new("bad serial #{serial}") unless is_valid_serial?(serial)
68
- raise BadInputError.new("bad restoration code #{restorecode}") unless is_valid_restorecode?(restorecode)
69
-
70
- normalized_serial = normalize_serial(serial)
71
- region = extract_region(normalized_serial)
66
+ serial = Bnet::Attributes::Serial.new serial
67
+ restorecode = Bnet::Attributes::Restorecode.new restorecode
72
68
 
73
69
  # stage 1
74
- challenge = request_for('restore (stage 1)', region, RESTORE_INIT_REQUEST_PATH, normalized_serial)
70
+ challenge = request_for('restore (stage 1)',
71
+ serial.region,
72
+ RESTORE_INIT_REQUEST_PATH,
73
+ serial.normalized)
75
74
 
76
75
  # stage 2
77
76
  key = create_one_time_pad(20)
78
77
 
79
- digest = Digest::HMAC.digest(normalized_serial + challenge,
80
- decode_restorecode(restorecode),
78
+ digest = Digest::HMAC.digest(serial.normalized + challenge,
79
+ restorecode.binary,
81
80
  Digest::SHA1)
82
81
 
83
- payload = normalized_serial + rsa_encrypt_bin(digest + key)
82
+ payload = serial.normalized + rsa_encrypt_bin(digest + key)
84
83
 
85
- response_body = request_for('restore (stage 2)', region, RESTORE_VALIDATE_REQUEST_PATH, payload)
84
+ response_body = request_for('restore (stage 2)',
85
+ serial.region,
86
+ RESTORE_VALIDATE_REQUEST_PATH,
87
+ payload)
86
88
 
87
- Authenticator.new(prettify_serial(normalized_serial), decrypt_response(response_body, key).as_bin_to_hex)
89
+ Authenticator.new(serial, decrypt_response(response_body, key))
88
90
  end
89
91
 
90
92
  # Get server's time
91
93
  # @param region [Symbol]
92
94
  # @return [Integer] server timestamp in seconds
93
95
  def self.request_server_time(region)
94
- raise BadInputError.new("bad region #{region}") unless is_valid_region?(region)
95
-
96
96
  server_time_big_endian = request_for('server time', region, TIME_REQUEST_PATH)
97
97
  server_time_big_endian.unpack('Q>')[0].to_f / 1000
98
98
  end
@@ -103,10 +103,10 @@ module Bnet
103
103
  # defaults to current time
104
104
  # @return [String, Integer] token and the next timestamp token to change
105
105
  def self.get_token(secret, timestamp = nil)
106
- raise BadInputError.new("bad seret #{secret}") unless is_valid_secret?(secret)
106
+ secret = Bnet::Attributes::Secret.new secret
107
107
 
108
108
  current = (timestamp || Time.now.getutc.to_i) / 30
109
- digest = Digest::HMAC.digest([current].pack('Q>'), secret.as_hex_to_bin, Digest::SHA1)
109
+ digest = Digest::HMAC.digest([current].pack('Q>'), secret.binary, Digest::SHA1)
110
110
  start_position = digest[19].ord & 0xf
111
111
 
112
112
  token = digest[start_position, 4].unpack('L>')[0] & 0x7fffffff
@@ -119,7 +119,7 @@ module Bnet
119
119
  # defaults to current time
120
120
  # @return [String, Integer] token and the next timestamp token to change
121
121
  def get_token(timestamp = nil)
122
- self.class.get_token(@secret, timestamp)
122
+ self.class.get_token(secret, timestamp)
123
123
  end
124
124
 
125
125
  # Hash representation of this authenticator
@@ -0,0 +1,50 @@
1
+ require 'net/http'
2
+ require 'digest/sha1'
3
+
4
+ module Bnet
5
+
6
+ class Authenticator
7
+
8
+ class << self
9
+
10
+ def create_one_time_pad(length)
11
+ (0..1.0/0.0).reduce('') do |memo, i|
12
+ break memo if memo.length >= length
13
+ memo << Digest::SHA1.hexdigest(rand().to_s)
14
+ end[0, length]
15
+ end
16
+
17
+ def decrypt_response(text, key)
18
+ text.bytes.zip(key.bytes).reduce('') do |memo, pair|
19
+ memo + (pair[0] ^ pair[1]).chr
20
+ end
21
+ end
22
+
23
+ def rsa_encrypt_bin(bin)
24
+ i = bin.unpack('C*').map{ |i| i.to_s(16).rjust(2, '0') }.join.to_i(16)
25
+ (i ** RSA_KEY % RSA_MOD).to_s(16).scan(/.{2}/).map {|s| s.to_i(16)}.pack('C*')
26
+ end
27
+
28
+ def request_for(label, region, path, body = nil)
29
+ raise BadInputError.new("bad region #{region}") unless AUTHENTICATOR_HOSTS.has_key? region
30
+
31
+ request = body.nil? ? Net::HTTP::Get.new(path) : Net::HTTP::Post.new(path)
32
+ request.content_type = 'application/octet-stream'
33
+ request.body = body unless body.nil?
34
+
35
+ response = Net::HTTP.new(AUTHENTICATOR_HOSTS[region]).start do |http|
36
+ http.request request
37
+ end
38
+
39
+ if response.code.to_i != 200
40
+ raise RequestFailedError.new("Error requesting #{label}: #{response.code}")
41
+ end
42
+
43
+ response.body
44
+ end
45
+
46
+ end
47
+
48
+ end
49
+
50
+ end
@@ -1,5 +1,5 @@
1
1
  module Bnet
2
2
  class Authenticator
3
- VERSION = "0.1.6"
3
+ VERSION = "0.1.8"
4
4
  end
5
5
  end
@@ -26,7 +26,6 @@ module Bnet
26
26
 
27
27
  token, next_timestamp = Authenticator.get_token(secret)
28
28
 
29
- puts token
30
29
  if @options.repeat
31
30
  interrupted = false
32
31
  trap("INT") { interrupted = true } # traps Ctrl-C
@@ -35,21 +34,22 @@ module Bnet
35
34
  sleep 1
36
35
 
37
36
  if Time.now.getutc.to_i < next_timestamp
38
- print_countdown(next_timestamp - Time.now.getutc.to_i)
37
+ seconds = next_timestamp - Time.now.getutc.to_i
38
+ h, c = color_of(seconds)
39
+ puts "\e[s\e[%d;%dm\e[5m%02d\e[25m\t->\t%s\t<-\e[1A\e[0m\e[u" % [h, c, seconds, token]
39
40
  next
40
41
  end
41
42
 
42
43
  token, next_timestamp = Authenticator.get_token(secret)
43
- puts token
44
44
  end
45
+ else
46
+ puts token
45
47
  end
46
48
  end
47
49
 
48
50
  private
49
51
 
50
- def print_countdown(seconds, output = $stdout)
51
- return unless output.tty?
52
-
52
+ def color_of(seconds)
53
53
  case
54
54
  when seconds > 25 then h, c = 1, 32
55
55
  when seconds > 20 then h, c = 0, 32
@@ -60,7 +60,7 @@ module Bnet
60
60
  h, c = 1, 31
61
61
  end
62
62
 
63
- output.puts "\e[%d;%dm%02d\e[1A\e[0m" % [h, c, seconds]
63
+ [h, c]
64
64
  end
65
65
 
66
66
  end
@@ -1,31 +1,34 @@
1
1
  module Bnet
2
2
 
3
3
  CLIENT_MODEL = 'bn/authenticator'
4
- RSA_MOD = 104890018807986556874007710914205443157030159668034197186125678960287470894290830530618284943118405110896322835449099433232093151168250152146023319326491587651685252774820340995950744075665455681760652136576493028733914892166700899109836291180881063097461175643998356321993663868233366705340758102567742483097
4
+ RSA_MOD = "955e4bd989f3917d2f15544a7e0504eb\
5
+ 9d7bb66b6f8a2fe470e453c779200e5e\
6
+ 3ad2e43a02d06c4adbd8d328f1a426b8\
7
+ 3658e88bfd949b2af4eaf30054673a14\
8
+ 19a250fa4cc1278d12855b5b25818d16\
9
+ 2c6e6ee2ab4a350d401d78f6ddb99711\
10
+ e72626b48bd8b5b0b7f3acf9ea3c9e00\
11
+ 05fee59e19136cdb7c83f2ab8b0a2a99".gsub(/\s+/, '').to_i(16)
5
12
  RSA_KEY = 257
6
13
  AUTHENTICATOR_HOSTS = {
7
14
  :CN => "mobile-service.battlenet.com.cn",
8
- :EU => "m.eu.mobileservice.blizzard.com",
9
- :US => "m.us.mobileservice.blizzard.com",
15
+ :EU => "mobile-service.blizzard.com",
16
+ :US => "mobile-service.blizzard.com",
10
17
  }
11
18
  ENROLLMENT_REQUEST_PATH = '/enrollment/enroll.htm'
12
19
  TIME_REQUEST_PATH = '/enrollment/time.htm'
13
20
  RESTORE_INIT_REQUEST_PATH = '/enrollment/initiatePaperRestore.htm'
14
21
  RESTORE_VALIDATE_REQUEST_PATH = '/enrollment/validatePaperRestore.htm'
15
22
 
16
- RESTORECODE_MAP = (0..32).reduce({}) do |memo, c|
17
- memo[c] = case
18
- when c < 10 then c + 48
19
- else
20
- c += 55
21
- c += 1 if c > 72 # S
22
- c += 1 if c > 75 # O
23
- c += 1 if c > 78 # L
24
- c += 1 if c > 82 # I
25
- c
26
- end
27
- memo
28
- end
23
+ RESTORECODE_MAP = {
24
+ 0=>48, 1=>49, 2=>50, 3=>51, 4=>52,
25
+ 5=>53, 6=>54, 7=>55, 8=>56, 9=>57,
26
+ 10=>65, 11=>66, 12=>67, 13=>68, 14=>69,
27
+ 15=>70, 16=>71, 17=>72, 18=>74, 19=>75,
28
+ 20=>77, 21=>78, 22=>80, 23=>81, 24=>82,
29
+ 25=>84, 26=>85, 27=>86, 28=>87, 29=>88,
30
+ 30=>89, 31=>90, 32=>91
31
+ }
29
32
  RESTORECODE_MAP_INVERSE = RESTORECODE_MAP.invert
30
33
 
31
34
  end
@@ -1,6 +1,9 @@
1
1
  require 'coveralls'
2
2
  Coveralls.wear_merged!
3
3
 
4
+ require 'codeclimate-test-reporter'
5
+ CodeClimate::TestReporter.start
6
+
4
7
  gem "minitest"
5
8
  require 'minitest/autorun'
6
9
  require 'bnet/authenticator'
@@ -18,21 +21,25 @@ class Bnet::AuthenticatorTest < Minitest::Test
18
21
 
19
22
  def test_argument_error
20
23
  assert_raises ::Bnet::BadInputError do
21
- Bnet::Authenticator.new('ABC', '')
24
+ Bnet::Authenticator.new 'ABC', ''
25
+ end
26
+
27
+ assert_raises ::Bnet::BadInputError do
28
+ Bnet::Authenticator.new DEFAULT_SERIAL, 'ASDF'
22
29
  end
23
30
 
24
31
  assert_raises ::Bnet::BadInputError do
25
- Bnet::Authenticator.request_authenticator('SG')
32
+ Bnet::Authenticator.request_authenticator 'SG'
26
33
  end
27
34
 
28
35
  assert_raises ::Bnet::BadInputError do
29
- Bnet::Authenticator.restore_authenticator('DDDD', 'EEE')
36
+ Bnet::Authenticator.restore_authenticator 'DDDD', 'EEE'
30
37
  end
31
38
  end
32
39
 
33
40
  def test_request_new_serial
34
41
  begin
35
- authenticator = Bnet::Authenticator.request_authenticator(:US)
42
+ authenticator = Bnet::Authenticator.request_authenticator :US
36
43
  assert_equal :US, authenticator.region
37
44
  refute_nil authenticator.serial
38
45
  refute_nil authenticator.secret
@@ -59,6 +66,10 @@ class Bnet::AuthenticatorTest < Minitest::Test
59
66
  end
60
67
  end
61
68
 
69
+ def test_bin
70
+ assert_equal true, system("ruby -Ilib bin/bna", :out => "/dev/null")
71
+ end
72
+
62
73
  private
63
74
 
64
75
  def is_default_authenticator(authenticator)
@@ -69,5 +80,6 @@ class Bnet::AuthenticatorTest < Minitest::Test
69
80
  assert_equal ['61459300', 1347279360], authenticator.get_token(1347279358)
70
81
  assert_equal ['61459300', 1347279360], authenticator.get_token(1347279359)
71
82
  assert_equal ['75939986', 1347279390], authenticator.get_token(1347279360)
83
+ assert_equal ['59914793', 1370448030], authenticator.get_token(1370448000)
72
84
  end
73
85
  end
metadata CHANGED
@@ -1,80 +1,85 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bnet-authenticator
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.6
5
- prerelease:
4
+ version: 0.1.8
6
5
  platform: ruby
7
6
  authors:
8
7
  - ZHANG Yi
9
8
  autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2014-04-09 00:00:00.000000000 Z
11
+ date: 2017-04-18 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
14
  name: rake
16
15
  requirement: !ruby/object:Gem::Requirement
17
- none: false
18
16
  requirements:
19
- - - ~>
17
+ - - ">="
20
18
  - !ruby/object:Gem::Version
21
- version: '10.1'
19
+ version: '0'
22
20
  type: :development
23
21
  prerelease: false
24
22
  version_requirements: !ruby/object:Gem::Requirement
25
- none: false
26
23
  requirements:
27
- - - ~>
24
+ - - ">="
28
25
  - !ruby/object:Gem::Version
29
- version: '10.1'
26
+ version: '0'
30
27
  - !ruby/object:Gem::Dependency
31
28
  name: minitest
32
29
  requirement: !ruby/object:Gem::Requirement
33
- none: false
34
30
  requirements:
35
- - - ~>
31
+ - - ">="
36
32
  - !ruby/object:Gem::Version
37
- version: '5.0'
33
+ version: '0'
38
34
  type: :development
39
35
  prerelease: false
40
36
  version_requirements: !ruby/object:Gem::Requirement
41
- none: false
42
37
  requirements:
43
- - - ~>
38
+ - - ">="
44
39
  - !ruby/object:Gem::Version
45
- version: '5.0'
40
+ version: '0'
46
41
  - !ruby/object:Gem::Dependency
47
42
  name: yard
48
43
  requirement: !ruby/object:Gem::Requirement
49
- none: false
50
44
  requirements:
51
- - - ~>
45
+ - - ">="
52
46
  - !ruby/object:Gem::Version
53
- version: '0.8'
47
+ version: '0'
54
48
  type: :development
55
49
  prerelease: false
56
50
  version_requirements: !ruby/object:Gem::Requirement
57
- none: false
58
51
  requirements:
59
- - - ~>
52
+ - - ">="
60
53
  - !ruby/object:Gem::Version
61
- version: '0.8'
54
+ version: '0'
62
55
  - !ruby/object:Gem::Dependency
63
56
  name: coveralls
64
57
  requirement: !ruby/object:Gem::Requirement
65
- none: false
66
58
  requirements:
67
- - - ~>
59
+ - - ">="
68
60
  - !ruby/object:Gem::Version
69
- version: '0.7'
61
+ version: '0'
70
62
  type: :development
71
63
  prerelease: false
72
64
  version_requirements: !ruby/object:Gem::Requirement
73
- none: false
74
65
  requirements:
75
- - - ~>
66
+ - - ">="
76
67
  - !ruby/object:Gem::Version
77
- version: '0.7'
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: codeclimate-test-reporter
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
78
83
  description: Ruby implementation of the Battle.net Mobile Authenticator
79
84
  email:
80
85
  - zhangyi.cn@gmail.com
@@ -83,16 +88,19 @@ executables:
83
88
  extensions: []
84
89
  extra_rdoc_files: []
85
90
  files:
86
- - .yardopts
91
+ - ".yardopts"
87
92
  - COPYING
88
93
  - Gemfile
89
94
  - README.md
90
95
  - Rakefile
91
96
  - bin/bna
92
97
  - bnet-authenticator.gemspec
98
+ - lib/bnet/attributes/restorecode.rb
99
+ - lib/bnet/attributes/secret.rb
100
+ - lib/bnet/attributes/serial.rb
93
101
  - lib/bnet/authenticator.rb
102
+ - lib/bnet/authenticator/core.rb
94
103
  - lib/bnet/authenticator/version.rb
95
- - lib/bnet/authenticator_helper.rb
96
104
  - lib/bnet/command.rb
97
105
  - lib/bnet/commands/help.rb
98
106
  - lib/bnet/commands/info.rb
@@ -101,33 +109,30 @@ files:
101
109
  - lib/bnet/commands/token.rb
102
110
  - lib/bnet/constants.rb
103
111
  - lib/bnet/errors.rb
104
- - lib/bnet/support.rb
105
112
  - test/test_battlenet_authenticator.rb
106
113
  homepage: https://github.com/dorentus/bnet-authenticator
107
114
  licenses:
108
115
  - MIT
116
+ metadata: {}
109
117
  post_install_message:
110
118
  rdoc_options: []
111
119
  require_paths:
112
120
  - lib
113
121
  required_ruby_version: !ruby/object:Gem::Requirement
114
- none: false
115
122
  requirements:
116
- - - ! '>='
123
+ - - ">="
117
124
  - !ruby/object:Gem::Version
118
125
  version: 1.9.3
119
126
  required_rubygems_version: !ruby/object:Gem::Requirement
120
- none: false
121
127
  requirements:
122
- - - ! '>='
128
+ - - ">="
123
129
  - !ruby/object:Gem::Version
124
130
  version: '0'
125
131
  requirements: []
126
132
  rubyforge_project:
127
- rubygems_version: 1.8.23
133
+ rubygems_version: 2.6.7
128
134
  signing_key:
129
- specification_version: 3
135
+ specification_version: 4
130
136
  summary: Battle.net Mobile Authenticator
131
137
  test_files:
132
138
  - test/test_battlenet_authenticator.rb
133
- has_rdoc:
@@ -1,95 +0,0 @@
1
- require 'bnet/support'
2
- require 'bnet/constants'
3
- require 'digest/sha1'
4
- require 'digest/hmac'
5
- require 'net/http'
6
-
7
- module Bnet
8
-
9
- module AuthenticatorHelper
10
-
11
- def self.included(base)
12
-
13
- class << base
14
-
15
- def is_valid_serial?(serial)
16
- normalized_serial = normalize_serial(serial)
17
- normalized_serial =~ Regexp.new("^(#{AUTHENTICATOR_HOSTS.keys.join('|')})\\d{12}$") && is_valid_region?(extract_region(normalized_serial))
18
- end
19
-
20
- def normalize_serial(serial)
21
- serial.upcase.gsub(/-/, '')
22
- end
23
-
24
- def extract_region(serial)
25
- serial[0, 2].upcase.to_sym
26
- end
27
-
28
- def prettify_serial(serial)
29
- "#{serial[0, 2]}-" + serial[2, 12].scan(/.{4}/).join('-')
30
- end
31
-
32
- def is_valid_secret?(secret)
33
- secret =~ /[0-9a-f]{40}/i
34
- end
35
-
36
- def is_valid_region?(region)
37
- AUTHENTICATOR_HOSTS.has_key? region
38
- end
39
-
40
- def is_valid_restorecode?(restorecode)
41
- restorecode =~ /[0-9A-Z]{10}/
42
- end
43
-
44
- def encode_restorecode(bin)
45
- bin.bytes.map do |v|
46
- RESTORECODE_MAP[v & 0x1f]
47
- end.as_bytes_to_bin
48
- end
49
-
50
- def decode_restorecode(str)
51
- str.bytes.map do |c|
52
- RESTORECODE_MAP_INVERSE[c]
53
- end.as_bytes_to_bin
54
- end
55
-
56
- def create_one_time_pad(length)
57
- (0..1.0/0.0).reduce('') do |memo, i|
58
- break memo if memo.length >= length
59
- memo << Digest::SHA1.hexdigest(rand().to_s)
60
- end[0, length]
61
- end
62
-
63
- def decrypt_response(text, key)
64
- text.bytes.zip(key.bytes).reduce('') do |memo, pair|
65
- memo + (pair[0] ^ pair[1]).chr
66
- end
67
- end
68
-
69
- def rsa_encrypt_bin(bin)
70
- (bin.as_bin_to_i ** RSA_KEY % RSA_MOD).to_bin
71
- end
72
-
73
- def request_for(label, region, path, body = nil)
74
- request = body.nil? ? Net::HTTP::Get.new(path) : Net::HTTP::Post.new(path)
75
- request.content_type = 'application/octet-stream'
76
- request.body = body unless body.nil?
77
-
78
- response = Net::HTTP.new(AUTHENTICATOR_HOSTS[region]).start do |http|
79
- http.request request
80
- end
81
-
82
- if response.code.to_i != 200
83
- raise RequestFailedError.new("Error requesting #{label}: #{response.code}")
84
- end
85
-
86
- response.body
87
- end
88
-
89
- end
90
-
91
- end
92
-
93
- end
94
-
95
- end
@@ -1,63 +0,0 @@
1
- module Bnet
2
-
3
- module Support
4
-
5
- module Array
6
-
7
- def as_bytes_to_bin
8
- pack('C*')
9
- end
10
-
11
- def as_bytes_to_hex
12
- '%02x' * length % self
13
- end
14
-
15
- def as_bytes_to_i
16
- as_bytes_to_hex.to_i(16)
17
- end
18
-
19
- end
20
-
21
- module Integer
22
-
23
- def to_bytes
24
- to_s(16).scan(/.{2}/).map {|s| s.to_i(16)}
25
- end
26
-
27
- def to_bin
28
- to_bytes.as_bytes_to_bin
29
- end
30
-
31
- end
32
-
33
- module String
34
-
35
- def as_bin_to_i
36
- bytes.to_a.as_bytes_to_i
37
- end
38
-
39
- def as_bin_to_hex
40
- bytes.to_a.as_bytes_to_hex
41
- end
42
-
43
- def as_hex_to_bin
44
- to_i(16).to_bytes.as_bytes_to_bin
45
- end
46
-
47
- end
48
-
49
- end
50
-
51
- end
52
-
53
- class Array
54
- include Bnet::Support::Array
55
- end
56
-
57
- class Integer
58
- include Bnet::Support::Integer
59
- end
60
-
61
- class String
62
- include Bnet::Support::String
63
- end