ruby_home 0.1.13 → 0.1.14

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: cbb486ea0b95baaee4388d69f2875024e941d7a79b2385f327f6cfb4639cc270
4
- data.tar.gz: d451c724e41292c3899461951bcd337095404abd0c730744cd3bf863a58e9859
3
+ metadata.gz: 247444e8f829a6189260709b8e0ea216ebe541788e2c8db878a944a92ed926f2
4
+ data.tar.gz: a015fb65638df959a1d0bd5b69d6510f5c1b04d24eef96742fb0c6188153db8f
5
5
  SHA512:
6
- metadata.gz: 569b75436eaa94dd62ce2395cedf0af12b4a9d498168d80a12534e6ee2320a870e6f7967676fae986243b2f4bf6e98a456e17089183ee9c5e2768539fcb131b1
7
- data.tar.gz: 0e43c491540462de28126a06cbe84f93b15ede9ef13133998f9c1e855b7c48ba8f4a3570d4d1fa8c485f6accc2fd1e1cb2864e92ff7dca0b04f7e728363dc818
6
+ metadata.gz: 93daf94385bd9f965392cd9ed748ff3e0f23132383bff1fe510f326b255aa6fe45b33f88a43b01b98c181de54d43bec2fbe48dde02d39e3004cf956c86616cb6
7
+ data.tar.gz: 8100648de51d429ec93b6de81137cf0c9354435acbb11a47fb57a365f0a5a14f38224a6dfaf41cba61edae6dc67d762a227ae6da9af4d22fef8d18c742ebaebd
@@ -31,16 +31,5 @@ module RubyHome
31
31
  def constraints
32
32
  @constraints || {}
33
33
  end
34
-
35
- def to_hash
36
- {
37
- name: name,
38
- description: description,
39
- uuid: uuid,
40
- format: format,
41
- unit: unit,
42
- properties: properties,
43
- }
44
- end
45
34
  end
46
35
  end
@@ -36,13 +36,5 @@ module RubyHome
36
36
  CharacteristicTemplate.find_by(uuid: uuid)
37
37
  end
38
38
  end
39
-
40
- def to_hash
41
- {
42
- name: name,
43
- description: description,
44
- uuid: uuid
45
- }
46
- end
47
39
  end
48
40
  end
@@ -1,10 +1,10 @@
1
1
  module RubyHome
2
2
  module HAP
3
3
  class EVResponse
4
- def initialize(socket, body)
5
- @socket = socket
4
+ def initialize(session, body)
5
+ @session = session
6
+ @socket = session.socket
6
7
  @body = body
7
- cache[:accessory_to_controller_count] ||= 0
8
8
  end
9
9
 
10
10
  def send_response
@@ -13,14 +13,17 @@ module RubyHome
13
13
  send_header(response)
14
14
  send_body(response)
15
15
 
16
+ encrypter = session.encrypter
16
17
  encrypted_response = encrypter.encrypt(response).join
17
- cache[:accessory_to_controller_count] = encrypter.count
18
+ session.accessory_to_controller_count = encrypter.count
18
19
 
19
20
  socket << encrypted_response
20
21
  end
21
22
 
22
23
  private
23
24
 
25
+ attr_reader :body, :socket, :session
26
+
24
27
  CRLF = -"\x0d\x0a"
25
28
  STATUS_LINE = -'EVENT/1.0 200 OK'
26
29
  CONTENT_TYPE_LINE = -'Content-Type: application/hap+json'
@@ -39,26 +42,6 @@ module RubyHome
39
42
  def content_length_line
40
43
  "Content-Length: #{body.length}"
41
44
  end
42
-
43
- attr_reader :body, :socket
44
-
45
- def encrypter
46
- @_encrypter ||= RubyHome::HAP::HTTPEncryption.new(encryption_key, encrypter_params)
47
- end
48
-
49
- def encrypter_params
50
- {
51
- count: cache[:accessory_to_controller_count]
52
- }
53
- end
54
-
55
- def encryption_key
56
- cache[:accessory_to_controller_key]
57
- end
58
-
59
- def cache
60
- RubyHome.socket_store[socket]
61
- end
62
45
  end
63
46
  end
64
47
  end
@@ -1,19 +1,16 @@
1
1
  module RubyHome
2
2
  module HAP
3
3
  class HAPRequest < WEBrick::HTTPRequest
4
- def initialize(config, sock)
5
- @sock = sock
6
- cache[:controller_to_accessory_count] ||= 0
4
+ def parse(session)
5
+ @session = session
6
+ socket = session.socket
7
7
 
8
- super(config)
9
- end
10
-
11
- def parse(socket)
12
- if decryption_time?
8
+ if session.decryption_time?
13
9
  request_line = socket.read_nonblock(@buffer_size)
14
10
 
11
+ decrypter = session.decrypter
15
12
  decrypted_request = decrypter.decrypt(request_line).join
16
- cache[:controller_to_accessory_count] = decrypter.count
13
+ session.controller_to_accessory_count = decrypter.count
17
14
 
18
15
  super(StringIO.new(decrypted_request))
19
16
  else
@@ -21,38 +18,10 @@ module RubyHome
21
18
  end
22
19
  end
23
20
 
24
- def received_encrypted_request?
25
- cache[:controller_to_accessory_count] >= 1
26
- end
27
-
28
21
  def meta_vars
29
- super.merge({"REQUEST_SOCKET" => sock})
30
- end
31
-
32
- private
33
-
34
- attr_reader :sock
35
-
36
- def decrypter
37
- @_decrypter ||= RubyHome::HAP::HTTPDecryption.new(decryption_key, decrypter_params)
38
- end
39
-
40
- def decrypter_params
41
- {
42
- count: cache[:controller_to_accessory_count]
43
- }
44
- end
45
-
46
- def decryption_time?
47
- !!decryption_key
48
- end
49
-
50
- def decryption_key
51
- cache[:controller_to_accessory_key]
52
- end
53
-
54
- def cache
55
- RubyHome.socket_store[sock]
22
+ super.merge(
23
+ { "REQUEST_SESSION" => @session }
24
+ )
56
25
  end
57
26
  end
58
27
  end
@@ -1,54 +1,23 @@
1
1
  module RubyHome
2
2
  module HAP
3
3
  class HAPResponse < WEBrick::HTTPResponse
4
- def initialize(config, sock)
5
- @sock = sock
6
- cache[:accessory_to_controller_count] ||= 0
4
+ def send_response(session)
5
+ socket = session.socket
7
6
 
8
- super(config)
9
- end
10
-
11
- def send_response(socket)
12
- if encryption_time?
7
+ if session.encryption_time?
13
8
  response = StringIO.new
14
9
  super(response)
15
10
 
11
+ encrypter = session.encrypter
12
+
16
13
  encrypted_response = encrypter.encrypt(response.string).join
17
- cache[:accessory_to_controller_count] = encrypter.count
14
+ session.accessory_to_controller_count = encrypter.count
18
15
 
19
16
  _write_data(socket, encrypted_response)
20
17
  else
21
18
  super(socket)
22
19
  end
23
20
  end
24
-
25
- attr_writer :received_encrypted_request
26
-
27
- private
28
-
29
- attr_reader :sock
30
-
31
- def encrypter
32
- @_encrypter ||= RubyHome::HAP::HTTPEncryption.new(encryption_key, encrypter_params)
33
- end
34
-
35
- def encrypter_params
36
- {
37
- count: cache[:accessory_to_controller_count]
38
- }
39
- end
40
-
41
- def encryption_time?
42
- encryption_key && !!@received_encrypted_request
43
- end
44
-
45
- def encryption_key
46
- cache[:accessory_to_controller_key]
47
- end
48
-
49
- def cache
50
- RubyHome.socket_store[sock]
51
- end
52
21
  end
53
22
  end
54
23
  end
@@ -1,14 +1,13 @@
1
1
  module RubyHome
2
2
  module HAP
3
3
  class Server
4
- def initialize(host, port, socket_store)
4
+ def initialize(host, port)
5
5
  @port = port
6
6
  @host = host
7
- @socket_store = socket_store
8
7
  @selector = NIO::Selector.new
9
8
  end
10
9
 
11
- attr_reader :port, :host, :socket_store
10
+ attr_reader :port, :host
12
11
 
13
12
  def run
14
13
  puts "Listening on #{host}:#{port}"
@@ -24,10 +23,10 @@ module RubyHome
24
23
 
25
24
  private
26
25
 
26
+ SESSIONS = {}
27
+
27
28
  def accept
28
29
  socket = @server.accept
29
- _, port, host = socket.peeraddr
30
-
31
30
  monitor = @selector.register(socket, :r)
32
31
  monitor.value = proc { read(socket) }
33
32
  end
@@ -36,17 +35,20 @@ module RubyHome
36
35
  @_upstream ||= HTTP::Application.run
37
36
  end
38
37
 
38
+ def webrick_config
39
+ @_webrick_config ||= WEBrick::Config::HTTP
40
+ end
41
+
39
42
  def read(socket)
40
43
  return close(socket) if socket.eof?
41
44
 
42
- socket_store[socket] ||= {}
45
+ session = SESSIONS[socket] ||= Session.new(socket)
43
46
 
44
- request = HAPRequest.new(WEBrick::Config::HTTP, socket)
45
- response = HAPResponse.new(WEBrick::Config::HTTP, socket)
47
+ request = HAPRequest.new(webrick_config)
48
+ response = HAPResponse.new(webrick_config)
46
49
 
47
- request.parse(socket)
50
+ request.parse(session)
48
51
 
49
- response.received_encrypted_request = request.received_encrypted_request?
50
52
  response.request_method = request.request_method
51
53
  response.request_uri = request.request_uri
52
54
  response.request_http_version = request.http_version
@@ -58,7 +60,7 @@ module RubyHome
58
60
  if request.keep_alive? && response.keep_alive?
59
61
  request.fixup()
60
62
  end
61
- response.send_response(socket)
63
+ response.send_response(session)
62
64
  end
63
65
 
64
66
  return close(socket) unless request.keep_alive?
@@ -72,7 +74,6 @@ module RubyHome
72
74
 
73
75
  def close(socket)
74
76
  @selector.deregister(socket)
75
- socket_store.delete(socket)
76
77
  socket.close
77
78
  end
78
79
  end
@@ -0,0 +1,69 @@
1
+ module RubyHome
2
+ module HAP
3
+ class Session
4
+ def initialize(socket)
5
+ @socket = socket
6
+ @accessory_to_controller_count = 0
7
+ @controller_to_accessory_count = 0
8
+ end
9
+
10
+ attr_reader :socket
11
+ attr_accessor :controller_to_accessory_key
12
+ attr_accessor :accessory_to_controller_key
13
+ attr_writer :accessory_to_controller_count
14
+ attr_writer :controller_to_accessory_count
15
+ attr_accessor :shared_secret
16
+ attr_accessor :session_key
17
+ attr_accessor :srp_session
18
+
19
+ def encryption_time?
20
+ accessory_to_controller_key? && received_encrypted_request?
21
+ end
22
+
23
+ def received_encrypted_request?
24
+ controller_to_accessory_count > 0
25
+ end
26
+
27
+ def encrypter
28
+ HTTPEncryption.new(accessory_to_controller_key, encrypter_params)
29
+ end
30
+
31
+ def decryption_time?
32
+ controller_to_accessory_key.present?
33
+ end
34
+
35
+ def decrypter
36
+ HTTPDecryption.new(controller_to_accessory_key, decrypter_params)
37
+ end
38
+
39
+ def sufficient_privileges?
40
+ controller_to_accessory_key? && accessory_to_controller_key?
41
+ end
42
+
43
+ def active?
44
+ !socket.closed?
45
+ end
46
+
47
+ private
48
+
49
+ attr_reader :accessory_to_controller_count,
50
+ :controller_to_accessory_count
51
+
52
+ def accessory_to_controller_key?
53
+ accessory_to_controller_key.present?
54
+ end
55
+
56
+ def controller_to_accessory_key?
57
+ controller_to_accessory_key.present?
58
+ end
59
+
60
+ def encrypter_params
61
+ { count: accessory_to_controller_count }
62
+ end
63
+
64
+ def decrypter_params
65
+ { count: controller_to_accessory_count }
66
+ end
67
+ end
68
+ end
69
+ end
@@ -6,7 +6,7 @@ module RubyHome
6
6
  get '/' do
7
7
  content_type 'application/hap+json'
8
8
 
9
- if cache[:controller_to_accessory_key] && cache[:accessory_to_controller_key]
9
+ if session.sufficient_privileges?
10
10
  AccessorySerializer.new(identifier_cache.accessories).serialized_json
11
11
  else
12
12
  status 401
@@ -9,8 +9,8 @@ module RubyHome
9
9
  end
10
10
 
11
11
  before do
12
- logger.debug "Cache"
13
- logger.debug cache.inspect
12
+ logger.debug "Session"
13
+ logger.debug session.inspect
14
14
  end
15
15
 
16
16
  protected
@@ -31,16 +31,8 @@ module RubyHome
31
31
  Accessory.all
32
32
  end
33
33
 
34
- def socket
35
- env["REQUEST_SOCKET"]
36
- end
37
-
38
- def clear_cache
39
- RubyHome.socket_store.delete(socket)
40
- end
41
-
42
- def cache
43
- RubyHome.socket_store[socket]
34
+ def session
35
+ env["REQUEST_SESSION"]
44
36
  end
45
37
 
46
38
  def tlv(object)
@@ -41,7 +41,7 @@ module RubyHome
41
41
 
42
42
  def subscribe_characteristics(characteristic_params)
43
43
  find_characteristic(**characteristic_params.symbolize_keys.slice(:aid, :iid)) do |characteristic|
44
- notifier = SocketNotifier.new(socket, characteristic)
44
+ notifier = SessionNotifier.new(session, characteristic)
45
45
 
46
46
  unless characteristic.listeners.include?(notifier)
47
47
  characteristic.subscribe(notifier)
@@ -56,7 +56,7 @@ module RubyHome
56
56
  end
57
57
 
58
58
  def require_session
59
- unless cache[:controller_to_accessory_key] && cache[:accessory_to_controller_key]
59
+ unless session.sufficient_privileges?
60
60
  halt 401, JSON.generate({"status" => -70401})
61
61
  end
62
62
  end
@@ -19,8 +19,6 @@ module RubyHome
19
19
  private
20
20
 
21
21
  def pairing_failed
22
- clear_cache
23
-
24
22
  tlv state: 4, error: 2
25
23
  end
26
24
 
@@ -30,7 +28,7 @@ module RubyHome
30
28
  password: accessory_info.password
31
29
  )
32
30
 
33
- cache[:srp_session] = start_srp.proof
31
+ session.srp_session = start_srp.proof
34
32
 
35
33
  tlv salt: start_srp.salt_bytes, public_key: start_srp.public_key_bytes, state: 2
36
34
  end
@@ -38,13 +36,13 @@ module RubyHome
38
36
  def verify
39
37
  verify_srp = VerifySRPService.new(
40
38
  device_proof: unpack_request[:proof],
41
- srp_session: cache[:srp_session],
39
+ srp_session: session.srp_session,
42
40
  public_key: unpack_request[:public_key],
43
41
  )
44
42
 
45
43
  if verify_srp.valid?
46
- cache[:session_key] = verify_srp.session_key
47
- cache.delete(:srp_session)
44
+ session.session_key = verify_srp.session_key
45
+ session.srp_session = nil
48
46
 
49
47
  tlv state: 4, proof: verify_srp.server_proof
50
48
  else
@@ -56,7 +54,7 @@ module RubyHome
56
54
  encrypted_data = unpack_request[:encrypted_data]
57
55
 
58
56
  hkdf = HAP::Crypto::HKDF.new(info: 'Pair-Setup-Encrypt-Info', salt: 'Pair-Setup-Encrypt-Salt')
59
- key = hkdf.encrypt(cache[:session_key])
57
+ key = hkdf.encrypt(session.session_key)
60
58
 
61
59
  chacha20poly1305ietf = HAP::Crypto::ChaCha20Poly1305.new(key)
62
60
 
@@ -69,7 +67,7 @@ module RubyHome
69
67
  iosdeviceltpk = unpacked_decrypted_data[:public_key]
70
68
 
71
69
  hkdf = HAP::Crypto::HKDF.new(info: 'Pair-Setup-Controller-Sign-Info', salt: 'Pair-Setup-Controller-Sign-Salt')
72
- iosdevicex = hkdf.encrypt(cache[:session_key])
70
+ iosdevicex = hkdf.encrypt(session.session_key)
73
71
 
74
72
  iosdeviceinfo = [
75
73
  iosdevicex.unpack1('H*'),
@@ -80,7 +78,7 @@ module RubyHome
80
78
 
81
79
  if verify_key.verify(iosdevicesignature, [iosdeviceinfo].pack('H*'))
82
80
  hkdf = HAP::Crypto::HKDF.new(info: 'Pair-Setup-Accessory-Sign-Info', salt: 'Pair-Setup-Accessory-Sign-Salt')
83
- accessory_x = hkdf.encrypt(cache[:session_key])
81
+ accessory_x = hkdf.encrypt(session.session_key)
84
82
 
85
83
  signing_key = accessory_info.signing_key
86
84
  accessoryltpk = signing_key.verify_key.to_bytes
@@ -23,7 +23,7 @@ module RubyHome
23
23
  public_key = secret_key.public_key.to_bytes
24
24
  client_public_key = RbNaCl::PublicKey.new(unpack_request[:public_key])
25
25
  shared_secret = RbNaCl::GroupElement.new(client_public_key).mult(secret_key).to_bytes
26
- cache[:shared_secret] = shared_secret
26
+ session.shared_secret = shared_secret
27
27
 
28
28
  accessoryinfo = [
29
29
  public_key.unpack1('H*'),
@@ -38,7 +38,7 @@ module RubyHome
38
38
 
39
39
  hkdf = HAP::Crypto::HKDF.new(info: 'Pair-Verify-Encrypt-Info', salt: 'Pair-Verify-Encrypt-Salt')
40
40
  session_key = hkdf.encrypt(shared_secret)
41
- cache[:session_key] = session_key
41
+ session.session_key = session_key
42
42
 
43
43
  chacha20poly1305ietf = HAP::Crypto::ChaCha20Poly1305.new(session_key)
44
44
  nonce = HexHelper.pad('PV-Msg02')
@@ -50,18 +50,19 @@ module RubyHome
50
50
  def verify_finish_response
51
51
  encrypted_data = unpack_request[:encrypted_data]
52
52
 
53
- chacha20poly1305ietf = HAP::Crypto::ChaCha20Poly1305.new(cache[:session_key])
53
+ chacha20poly1305ietf = HAP::Crypto::ChaCha20Poly1305.new(session.session_key)
54
54
  nonce = HexHelper.pad('PV-Msg03')
55
55
  decrypted_data = chacha20poly1305ietf.decrypt(nonce, encrypted_data)
56
56
  unpacked_decrypted_data = HAP::TLV.read(decrypted_data)
57
57
 
58
58
  if accessory_info.paired_clients.any? {|h| h[:identifier] == unpacked_decrypted_data[:identifier]}
59
- shared_secret = HAP::Crypto::SessionKey.new(cache[:shared_secret])
60
- cache[:controller_to_accessory_key] = shared_secret.controller_to_accessory_key
61
- cache[:accessory_to_controller_key] = shared_secret.accessory_to_controller_key
59
+ shared_secret = HAP::Crypto::SessionKey.new(session.shared_secret)
62
60
 
63
- cache.delete(:session_key)
64
- cache.delete(:shared_secret)
61
+ session.controller_to_accessory_key = shared_secret.controller_to_accessory_key
62
+ session.accessory_to_controller_key = shared_secret.accessory_to_controller_key
63
+
64
+ session.session_key = nil
65
+ session.shared_secret = nil
65
66
 
66
67
  tlv state: 4
67
68
  else
@@ -1,13 +1,13 @@
1
1
  module RubyHome
2
2
  module HTTP
3
- class SocketNotifier
4
- def initialize(socket, characteristic)
5
- @socket = socket
3
+ class SessionNotifier
4
+ def initialize(session, characteristic)
5
+ @session = session
6
6
  @characteristic = characteristic
7
7
  end
8
8
 
9
9
  def after_update(_)
10
- if socket_still_active?
10
+ if session.active?
11
11
  send_ev_response
12
12
  else
13
13
  characteristic.unsubscribe(self)
@@ -16,20 +16,16 @@ module RubyHome
16
16
 
17
17
  def ==(other)
18
18
  self.class == other.class &&
19
- self.socket == other.socket &&
19
+ self.session == other.session &&
20
20
  self.characteristic == other.characteristic
21
21
  end
22
22
 
23
- attr_reader :socket, :characteristic
23
+ attr_reader :session, :characteristic
24
24
 
25
25
  private
26
26
 
27
- def socket_still_active?
28
- RubyHome.socket_store.include?(socket)
29
- end
30
-
31
27
  def send_ev_response
32
- HAP::EVResponse.new(socket, serialized_characteristic).send_response
28
+ HAP::EVResponse.new(session, serialized_characteristic).send_response
33
29
  end
34
30
 
35
31
  def serialized_characteristic
@@ -13,10 +13,6 @@ module RubyHome
13
13
  end
14
14
  end
15
15
 
16
- def self.reload
17
- @@_instance = nil
18
- end
19
-
20
16
  def self.create(**attributes)
21
17
  new(**attributes).save
22
18
  end
@@ -7,7 +7,9 @@ module RubyHome
7
7
 
8
8
  module ClassMethods
9
9
  def persisted
10
- new(read) if read
10
+ if yaml = read
11
+ new(yaml)
12
+ end
11
13
  end
12
14
 
13
15
  def create(**options)
@@ -19,10 +21,20 @@ module RubyHome
19
21
  end
20
22
 
21
23
  def read
22
- return false unless File.exists?(source)
24
+ return false unless file_exists?
23
25
 
24
26
  YAML.load_file(source)
25
27
  end
28
+
29
+ def truncate
30
+ return false unless file_exists?
31
+
32
+ File.truncate(source, 0)
33
+ end
34
+
35
+ def file_exists?
36
+ File.exists?(source)
37
+ end
26
38
  end
27
39
 
28
40
  def reload
@@ -1,3 +1,3 @@
1
1
  module RubyHome
2
- VERSION = '0.1.13'
2
+ VERSION = '0.1.14'
3
3
  end
data/lib/ruby_home.rb CHANGED
@@ -46,11 +46,7 @@ module RubyHome
46
46
  end
47
47
 
48
48
  def hap_server
49
- @_hap_server ||= HAP::Server.new('0.0.0.0', 4567, socket_store)
50
- end
51
-
52
- def socket_store
53
- @_socket_store ||= {}
49
+ @_hap_server ||= HAP::Server.new('0.0.0.0', 4567)
54
50
  end
55
51
 
56
52
  def greet
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby_home
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.13
4
+ version: 0.1.14
5
5
  platform: ruby
6
6
  authors:
7
7
  - Karl Entwistle
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-11-18 00:00:00.000000000 Z
11
+ date: 2018-11-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -295,6 +295,7 @@ files:
295
295
  - lib/ruby_home/hap/http_encryption.rb
296
296
  - lib/ruby_home/hap/server.rb
297
297
  - lib/ruby_home/hap/service.rb
298
+ - lib/ruby_home/hap/session.rb
298
299
  - lib/ruby_home/hap/tlv.rb
299
300
  - lib/ruby_home/hap/values/base_value.rb
300
301
  - lib/ruby_home/hap/values/bool_value.rb
@@ -319,7 +320,7 @@ files:
319
320
  - lib/ruby_home/http/serializers/object_serializer.rb
320
321
  - lib/ruby_home/http/serializers/service_serializer.rb
321
322
  - lib/ruby_home/http/serializers/uuid_helper.rb
322
- - lib/ruby_home/http/services/socket_notifier.rb
323
+ - lib/ruby_home/http/services/session_notifier.rb
323
324
  - lib/ruby_home/http/services/start_srp_service.rb
324
325
  - lib/ruby_home/http/services/verify_srp_service.rb
325
326
  - lib/ruby_home/identifier_cache.rb