bnet-authenticator 0.1.6 → 0.1.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.
@@ -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