ruby_home 0.1.13 → 0.1.14

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 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