ruby_home 0.2.0 → 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +9 -9
- data/lib/ruby_home/dns/service.rb +2 -0
- data/lib/ruby_home/errors.rb +4 -0
- data/lib/ruby_home/hap/server_handler.rb +4 -13
- data/lib/ruby_home/hap/values/uint8_value.rb +11 -1
- data/lib/ruby_home/http/controllers/application_controller.rb +10 -3
- data/lib/ruby_home/http/controllers/pair_setups_controller.rb +2 -2
- data/lib/ruby_home/http/controllers/pair_verifies_controller.rb +10 -12
- data/lib/ruby_home/http/services/verify_finish_service.rb +56 -0
- data/lib/ruby_home/http/services/verify_srp_service.rb +32 -41
- data/lib/ruby_home/version.rb +1 -1
- data/rubyhome.gemspec +1 -1
- metadata +6 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cace8911ec7e8999b154e2bbcf7794ff7e9ca890b1b53fb8167091c316565bc6
|
4
|
+
data.tar.gz: 9f623241d8dd6bbca8e504919af18bd2084a722f5c9ee33a701714cbd82f73c1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a4787d16e0be2c98a35d8428ef90ef83cd8462f2fa9189f6243367ef8eca006812a86c26d4c29afa5c3d09827401fb61909f5320b7e1d5ac2d4c5ab0d3e95f78
|
7
|
+
data.tar.gz: 138ef40f9cf8e9c0fa8820c2611d1bf70dd4398578e40139165359d93558fe7df8205676fa660426debc49c9852d7f42d770f9a4b99cb9bf4d224bec21821384
|
data/README.md
CHANGED
@@ -17,7 +17,7 @@ For OS X users, libsodium is available via homebrew and can be installed with:
|
|
17
17
|
|
18
18
|
brew install libsodium
|
19
19
|
|
20
|
-
For Debian users, libsodium is available
|
20
|
+
For Debian users, libsodium is available via apt:
|
21
21
|
|
22
22
|
sudo apt-get install libsodium-dev
|
23
23
|
|
@@ -33,13 +33,13 @@ And then execute:
|
|
33
33
|
|
34
34
|
$ bundle
|
35
35
|
|
36
|
-
Or install it yourself
|
36
|
+
Or install it yourself with:
|
37
37
|
|
38
38
|
$ gem install ruby_home
|
39
39
|
|
40
40
|
## Basic usage
|
41
41
|
|
42
|
-
Create a fan with an on/off switch
|
42
|
+
Create a fan with an on/off switch:
|
43
43
|
|
44
44
|
```ruby
|
45
45
|
require 'ruby_home'
|
@@ -60,7 +60,7 @@ RubyHome.run
|
|
60
60
|
|
61
61
|
## Configuration
|
62
62
|
|
63
|
-
The configuration options can be set by using the `configure` helper
|
63
|
+
The configuration options can be set by using the `configure` helper:
|
64
64
|
|
65
65
|
```ruby
|
66
66
|
RubyHome.configure do |c|
|
@@ -74,7 +74,7 @@ The following is the full list of available configuration options:
|
|
74
74
|
|---|---|---|---|---|
|
75
75
|
| `discovery_name` | The user-visible name of the accessory | `"RubyHome"` | `"My Home"` | String |
|
76
76
|
| `model_name` | The model name of the accessory | `"RubyHome"` | `"Device1,1"` | String |
|
77
|
-
| `password` | Used for pairing must conform to the format XXX-XX-XXX where each X is a 0-9 digit and dashes are required | Randomly generated | `"101-48-005"` | String |
|
77
|
+
| `password` | Used for pairing, must conform to the format XXX-XX-XXX where each X is a 0-9 digit and dashes are required | Randomly generated | `"101-48-005"` | String |
|
78
78
|
| `host` | The hostname or IP address of the interface to listen on | `"0.0.0.0"` | `"192.168.0.2"` | String |
|
79
79
|
| `port` | The port that should be used when starting the built-in web server | `4567` | `8080` | Integer |
|
80
80
|
|
@@ -117,9 +117,9 @@ RubyHome.run
|
|
117
117
|
|
118
118
|
## Updating a characteristics value
|
119
119
|
|
120
|
-
If you have a service with characteristics that can be changed outside of Ruby Home
|
120
|
+
If you have a service with characteristics that can be changed outside of Ruby Home, you'll want to keep Ruby Home in sync with these modifications. Otherwise, the characteristics current value won't correspond with reality. The simplest way to do this is a background job that periodically polls the devices current status and updates the corresponding characteristics value if it's changed.
|
121
121
|
|
122
|
-
Given a fan which can be switched on / off with a remote control
|
122
|
+
Given a fan which can be switched on / off with a remote control, which has a JSON API endpoint at http://example.org/fan_status.json that returns its current status `{ "on": true }` or `{ "on": false }`, we can spawn a thread that keeps polling the fans current status and if it's changed update our fan service "on" characteristic.
|
123
123
|
|
124
124
|
```ruby
|
125
125
|
require 'json'
|
@@ -150,7 +150,7 @@ RubyHome.run
|
|
150
150
|
|
151
151
|
## More examples
|
152
152
|
|
153
|
-
### Create a garage door opener
|
153
|
+
### Create a garage door opener
|
154
154
|
|
155
155
|
```ruby
|
156
156
|
require 'ruby_home'
|
@@ -171,7 +171,7 @@ end
|
|
171
171
|
RubyHome.run
|
172
172
|
```
|
173
173
|
|
174
|
-
### Create a thermostat
|
174
|
+
### Create a thermostat
|
175
175
|
|
176
176
|
```ruby
|
177
177
|
require 'ruby_home'
|
@@ -35,29 +35,20 @@ module RubyHome
|
|
35
35
|
Host: bind_address,
|
36
36
|
ServerSoftware: 'RubyHome',
|
37
37
|
Logger: server_logger,
|
38
|
-
AccessLog:
|
38
|
+
AccessLog: []
|
39
39
|
}
|
40
40
|
end
|
41
41
|
|
42
42
|
def server_logger
|
43
|
-
if ENV['DEBUG']
|
43
|
+
if ENV['DEBUG'] == 'debug'
|
44
44
|
WEBrick::Log::new(STDOUT, WEBrick::BasicLog::DEBUG)
|
45
|
+
elsif ENV['DEBUG'] == 'info'
|
46
|
+
WEBrick::Log::new(STDOUT, WEBrick::BasicLog::INFO)
|
45
47
|
else
|
46
48
|
WEBrick::Log::new("/dev/null", WEBrick::BasicLog::WARN)
|
47
49
|
end
|
48
50
|
end
|
49
51
|
|
50
|
-
def access_logger
|
51
|
-
if ENV['DEBUG']
|
52
|
-
[
|
53
|
-
[ STDOUT, WEBrick::AccessLog::COMMON_LOG_FORMAT ],
|
54
|
-
[ STDOUT, WEBrick::AccessLog::REFERER_LOG_FORMAT ]
|
55
|
-
]
|
56
|
-
else
|
57
|
-
[]
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
52
|
def bind_address
|
62
53
|
configuration.host
|
63
54
|
end
|
@@ -13,7 +13,17 @@ module RubyHome
|
|
13
13
|
end
|
14
14
|
|
15
15
|
def valid_values
|
16
|
-
template.constraints.
|
16
|
+
defined_values || range_values || raise(UnknownValueError, "Constraint contains an unrecognized list of values: #{template.constraints.inspect }")
|
17
|
+
end
|
18
|
+
|
19
|
+
def defined_values
|
20
|
+
template.constraints.dig('ValidValues')
|
21
|
+
end
|
22
|
+
|
23
|
+
def range_values
|
24
|
+
if min = template.constraints.dig('MinimumValue')
|
25
|
+
{ min.to_s => min }
|
26
|
+
end
|
17
27
|
end
|
18
28
|
end
|
19
29
|
end
|
@@ -5,10 +5,17 @@ module RubyHome
|
|
5
5
|
class ApplicationController < Sinatra::Base
|
6
6
|
disable :protection
|
7
7
|
|
8
|
-
|
9
|
-
|
10
|
-
|
8
|
+
logger = Logger.new(STDOUT)
|
9
|
+
logger.level = case ENV['DEBUG']
|
10
|
+
when 'debug'
|
11
|
+
Logger::DEBUG
|
12
|
+
when 'info'
|
13
|
+
Logger::INFO
|
14
|
+
else
|
15
|
+
Logger::WARN
|
11
16
|
end
|
17
|
+
set :logger, logger
|
18
|
+
enable :logging if ENV['DEBUG']
|
12
19
|
|
13
20
|
before do
|
14
21
|
logger.debug "Session"
|
@@ -38,9 +38,9 @@ module RubyHome
|
|
38
38
|
device_proof: unpack_request[:proof],
|
39
39
|
srp_session: session.srp_session,
|
40
40
|
public_key: unpack_request[:public_key],
|
41
|
-
)
|
41
|
+
).run
|
42
42
|
|
43
|
-
if verify_srp.
|
43
|
+
if verify_srp.success?
|
44
44
|
session.session_key = verify_srp.session_key
|
45
45
|
session.srp_session = nil
|
46
46
|
|
@@ -48,18 +48,16 @@ module RubyHome
|
|
48
48
|
end
|
49
49
|
|
50
50
|
def verify_finish_response
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
if
|
59
|
-
|
60
|
-
|
61
|
-
session.controller_to_accessory_key = shared_secret.controller_to_accessory_key
|
62
|
-
session.accessory_to_controller_key = shared_secret.accessory_to_controller_key
|
51
|
+
verify_finish = VerifyFinishService.new(
|
52
|
+
encrypted_data: unpack_request[:encrypted_data],
|
53
|
+
session_key: session.session_key,
|
54
|
+
shared_secret: session.shared_secret,
|
55
|
+
accessory_info: accessory_info
|
56
|
+
).run
|
57
|
+
|
58
|
+
if verify_finish.success?
|
59
|
+
session.controller_to_accessory_key = verify_finish.controller_to_accessory_key
|
60
|
+
session.accessory_to_controller_key = verify_finish.accessory_to_controller_key
|
63
61
|
|
64
62
|
session.session_key = nil
|
65
63
|
session.shared_secret = nil
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module RubyHome
|
2
|
+
class VerifyFinishService
|
3
|
+
def initialize(accessory_info: , encrypted_data: , session_key:, shared_secret:)
|
4
|
+
@accessory_info = accessory_info
|
5
|
+
@encrypted_data = encrypted_data
|
6
|
+
@session_key = session_key
|
7
|
+
@shared_secret = HAP::Crypto::SessionKey.new(shared_secret)
|
8
|
+
end
|
9
|
+
|
10
|
+
def run
|
11
|
+
if paired_client_exists?
|
12
|
+
RubyHome.dns_service.update
|
13
|
+
|
14
|
+
OpenStruct.new(
|
15
|
+
success?: true,
|
16
|
+
controller_to_accessory_key: shared_secret.controller_to_accessory_key,
|
17
|
+
accessory_to_controller_key: shared_secret.accessory_to_controller_key
|
18
|
+
)
|
19
|
+
else
|
20
|
+
OpenStruct.new(success?: false)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
NONCE = -'PV-Msg03'
|
27
|
+
|
28
|
+
attr_reader :accessory_info, :encrypted_data, :session_key, :shared_secret
|
29
|
+
|
30
|
+
def paired_client_exists?
|
31
|
+
accessory_info.paired_clients.any? do |paired_client|
|
32
|
+
paired_client[:identifier] == identifier
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def chacha20poly1305
|
37
|
+
HAP::Crypto::ChaCha20Poly1305.new(session_key)
|
38
|
+
end
|
39
|
+
|
40
|
+
def nonce
|
41
|
+
HexHelper.pad(NONCE)
|
42
|
+
end
|
43
|
+
|
44
|
+
def decrypted_data
|
45
|
+
chacha20poly1305.decrypt(nonce, encrypted_data)
|
46
|
+
end
|
47
|
+
|
48
|
+
def unpacked_decrypted_data
|
49
|
+
TLV.decode(decrypted_data)
|
50
|
+
end
|
51
|
+
|
52
|
+
def identifier
|
53
|
+
unpacked_decrypted_data[:identifier]
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -1,57 +1,48 @@
|
|
1
1
|
module RubyHome
|
2
2
|
class VerifySRPService
|
3
|
-
def initialize(
|
4
|
-
@
|
3
|
+
def initialize(device_proof: , public_key: , srp_session: )
|
4
|
+
@device_proof_bytes = device_proof.to_s.unpack1('H*')
|
5
|
+
@public_key_bytes = public_key.to_s.unpack1('H*')
|
5
6
|
@srp_session = srp_session
|
6
|
-
@public_key = public_key
|
7
7
|
end
|
8
8
|
|
9
|
-
def
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
srp_verifier.K
|
20
|
-
end
|
21
|
-
|
22
|
-
def server_proof
|
23
|
-
verify_session_bytes
|
9
|
+
def run
|
10
|
+
if valid_session?
|
11
|
+
OpenStruct.new(
|
12
|
+
success?: true,
|
13
|
+
session_key: session_key,
|
14
|
+
server_proof: verify_session_bytes
|
15
|
+
)
|
16
|
+
else
|
17
|
+
OpenStruct.new(success?: false)
|
18
|
+
end
|
24
19
|
end
|
25
20
|
|
26
21
|
private
|
27
22
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
def verify_session_bytes
|
33
|
-
[verify_session].pack('H*')
|
34
|
-
end
|
23
|
+
def valid_session?
|
24
|
+
!!verify_session
|
25
|
+
end
|
35
26
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
device_proof_bytes
|
40
|
-
)
|
41
|
-
end
|
27
|
+
def session_key
|
28
|
+
srp_verifier.K
|
29
|
+
end
|
42
30
|
|
43
|
-
|
44
|
-
|
45
|
-
|
31
|
+
def verify_session_bytes
|
32
|
+
[verify_session].pack('H*')
|
33
|
+
end
|
46
34
|
|
47
|
-
|
48
|
-
|
49
|
-
|
35
|
+
def verify_session
|
36
|
+
srp_verifier.verify_session(
|
37
|
+
srp_session.merge({A: public_key_bytes}),
|
38
|
+
device_proof_bytes
|
39
|
+
)
|
40
|
+
end
|
50
41
|
|
51
|
-
|
52
|
-
|
53
|
-
|
42
|
+
def srp_verifier
|
43
|
+
@_verifier ||= RubyHome::SRP::Verifier.new
|
44
|
+
end
|
54
45
|
|
55
|
-
|
46
|
+
attr_reader :public_key_bytes, :device_proof_bytes, :srp_session
|
56
47
|
end
|
57
48
|
end
|
data/lib/ruby_home/version.rb
CHANGED
data/rubyhome.gemspec
CHANGED
@@ -30,7 +30,7 @@ Gem::Specification.new do |spec|
|
|
30
30
|
spec.add_dependency 'dnssd', '~> 3.0'
|
31
31
|
spec.add_dependency 'facets', '~> 3.1'
|
32
32
|
spec.add_dependency 'hkdf', '~> 0.3.0'
|
33
|
-
spec.add_dependency 'oj', '3.7.
|
33
|
+
spec.add_dependency 'oj', '3.7.12'
|
34
34
|
spec.add_dependency 'rbnacl', '~> 6.0'
|
35
35
|
spec.add_dependency 'ruby_home-srp', '~> 1.3'
|
36
36
|
spec.add_dependency 'ruby_home-tlv', '~> 0.1.0'
|
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.2.
|
4
|
+
version: 0.2.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Karl Entwistle
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-
|
11
|
+
date: 2019-06-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bindata
|
@@ -78,14 +78,14 @@ dependencies:
|
|
78
78
|
requirements:
|
79
79
|
- - '='
|
80
80
|
- !ruby/object:Gem::Version
|
81
|
-
version: 3.7.
|
81
|
+
version: 3.7.12
|
82
82
|
type: :runtime
|
83
83
|
prerelease: false
|
84
84
|
version_requirements: !ruby/object:Gem::Requirement
|
85
85
|
requirements:
|
86
86
|
- - '='
|
87
87
|
- !ruby/object:Gem::Version
|
88
|
-
version: 3.7.
|
88
|
+
version: 3.7.12
|
89
89
|
- !ruby/object:Gem::Dependency
|
90
90
|
name: rbnacl
|
91
91
|
requirement: !ruby/object:Gem::Requirement
|
@@ -262,6 +262,7 @@ files:
|
|
262
262
|
- lib/ruby_home/device_id.rb
|
263
263
|
- lib/ruby_home/dns/service.rb
|
264
264
|
- lib/ruby_home/dns/text_record.rb
|
265
|
+
- lib/ruby_home/errors.rb
|
265
266
|
- lib/ruby_home/factories/characteristic_factory.rb
|
266
267
|
- lib/ruby_home/factories/service_factory.rb
|
267
268
|
- lib/ruby_home/factories/templates/characteristic_template.rb
|
@@ -304,6 +305,7 @@ files:
|
|
304
305
|
- lib/ruby_home/http/serializers/uuid_helper.rb
|
305
306
|
- lib/ruby_home/http/services/session_notifier.rb
|
306
307
|
- lib/ruby_home/http/services/start_srp_service.rb
|
308
|
+
- lib/ruby_home/http/services/verify_finish_service.rb
|
307
309
|
- lib/ruby_home/http/services/verify_srp_service.rb
|
308
310
|
- lib/ruby_home/identifier_cache.rb
|
309
311
|
- lib/ruby_home/password.rb
|