ephemeral_calc 0.2.0 → 0.3.0

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
  SHA1:
3
- metadata.gz: d8cecf935ff387d53df0c4f3a5ab898810061627
4
- data.tar.gz: 4a303fa845749f4c4f33c7df5e0616fd08e3d018
3
+ metadata.gz: d66ed2f2feb08b61474d5e99d9906de5702b409a
4
+ data.tar.gz: 448f9eb5dbad2eb1cc99e097f384d0041a60ebcc
5
5
  SHA512:
6
- metadata.gz: 134e6ceb11fa9daeafb21b228e9a97289ed25eaa953a525a81d9058a0bf0d5a38f531ff5aedd578933018ebbba0ee0f1c5edcab4812340fc91a8764c3eb06c85
7
- data.tar.gz: c03aaa19d4f06effd6fe854cd22d4ba7be0f75612a07f4ca256f90776f8209370017da3b1698e609f856929220a0dfeee3b519fa1ed88b7e154cdb177b233aa4
6
+ metadata.gz: 0f4e472ea04511a2406a17897c2571a4337dbffef92796661d8b66de8e5a26ce4a834859b0e6352cda6e26d147535cd27cf8ab55c994e533822e9b7979eb8914
7
+ data.tar.gz: f0a98210f4e975c70ef30de5beb8c2dc6af80b2837eb9d6969f7d5afec7e938883467b38c6907d3daa7ec44733d4057d4d6d8ab049e255b6ca1c2da05bceb686
@@ -20,6 +20,8 @@ Gem::Specification.new do |spec|
20
20
  spec.extensions = %w[ext/curve25519/extconf.rb]
21
21
  spec.require_paths = ["lib"]
22
22
 
23
+ spec.add_runtime_dependency "proximity_beacon", "~> 0.1.3"
24
+
23
25
  spec.add_development_dependency "bundler", "~> 1.10"
24
26
  spec.add_development_dependency "rake", "~> 10.0"
25
27
  spec.add_development_dependency "rspec"
data/exe/ephemeral_calc CHANGED
@@ -53,11 +53,13 @@ Commands:
53
53
  access the API.
54
54
 
55
55
  register:
56
- ephemeral_calc register <beacon_public_key> <rotation_scaler> <initial_eid> <initial_clock> <namespace> <instance>
56
+ ephemeral_calc register <beacon_description> [<rotation_exp> [<namespace> [<instance]]]
57
57
 
58
- Registers an EID beacon with Google's Proximity Beacon API. All keys and byte strings should be in hex.
59
- Uses environment variables "GOOGLE_CLIENT_ID" and "GOOGLE_CLIENT_SECRET" to do the necessary OAuth in
60
- order to access the API.
58
+ Registers an EID beacon with Google's Proximity Beacon API. All byte strings should be in hex. If you
59
+ do not supply a namespace it will use the following: 'e3dd811dd3bbe49e630a'. If you do not supply an
60
+ instance it will randomly generate one. Uses environment variables "GOOGLE_CLIENT_ID" and
61
+ "GOOGLE_CLIENT_SECRET" to do the neccessary OAuth in order to access the API. Outputs YAML which
62
+ contains all the information necessary to advertise as this registered beacon.
61
63
 
62
64
  keygen:
63
65
  ephemeral_calc keygen
@@ -135,18 +137,17 @@ eos
135
137
  eid_hex = ARGV.shift
136
138
  api_key = ENV["GOOGLE_API_KEY"]
137
139
 
140
+ client = ProximityBeacon::Client.new
138
141
  if api_key
139
- client = EphemeralCalc::GoogleAPI::Client.new(nil) # don't try oauth
140
142
  response = client.getforobserved(eid_hex, api_key)
141
143
  beacons = response["beacons"]
142
144
  beacon = beacons[0] if beacons
143
145
  else
144
146
  beacon_name = "beacons/4!#{eid_hex.downcase}"
145
147
  begin
146
- beacon = oauth_client.get_resource(beacon_name)
147
- attachments = oauth_client.get_resource("#{beacon_name}/attachments")
148
- beacon.merge!(attachments)
149
- rescue EphemeralCalc::GoogleAPI::RequestError => e
148
+ beacon = client.beacons.get(beacon_name).as_json
149
+ beacon["attachments"] = client.attachments.list(beacon_name).map(&:as_json)
150
+ rescue ProximityBeacon::Client::RequestError => e
150
151
  # if we get a 404, the EID doesn't resolve
151
152
  raise e unless e.code == 404
152
153
  end
@@ -161,7 +162,7 @@ eos
161
162
 
162
163
  def eidparams
163
164
  require 'yaml'
164
- eidparams = oauth_client.eidparams
165
+ eidparams = ProximityBeacon::Client.new.eidparams
165
166
  key_hex = to_hex(Base64.decode64(eidparams["serviceEcdhPublicKey"]))
166
167
  eidparams["serviceEcdhPublicKeyHex"] = key_hex
167
168
  puts eidparams.to_yaml
@@ -177,15 +178,18 @@ eos
177
178
 
178
179
  def register
179
180
  require 'yaml'
180
- beacon_public_key = from_hex(ARGV.shift)
181
- rotation_scaler = ARGV.shift
182
- initial_eid = from_hex(ARGV.shift)
183
- initial_clock = ARGV.shift
184
- namespace = from_hex(ARGV.shift)
185
- instance = from_hex(ARGV.shift)
186
- uid_bytes = namespace + instance
187
- response = oauth_client.register_eid(beacon_public_key, rotation_scaler, initial_eid, initial_clock, uid_bytes)
188
- puts response.to_yaml
181
+ name = ARGV.shift
182
+ rotation_exp = ARGV.shift
183
+ namespace = ARGV.shift
184
+ instance = ARGV.shift
185
+ registration = EphemeralCalc::Registration.new(
186
+ name: name,
187
+ rotation_exp: rotation_exp,
188
+ namespace: namespace,
189
+ instance: instance
190
+ )
191
+ registration.register
192
+ puts registration.to_yaml
189
193
  end
190
194
 
191
195
  def keygen
@@ -207,14 +211,6 @@ eos
207
211
  end
208
212
  end
209
213
 
210
- def oauth_client
211
- @oauth_client ||=
212
- begin
213
- client = EphemeralCalc::GoogleAPI::Client.new
214
- client.credentials.save
215
- client
216
- end
217
- end
218
214
  end
219
215
 
220
216
  app = App.new
@@ -0,0 +1,89 @@
1
+ module EphemeralCalc
2
+ class Registration
3
+
4
+ DEFAULT_NAMESPACE = "e3dd811dd3bbe49e630a"
5
+ DEFAULT_ROTATION_EXP = 12 # 2^12 = ~68 minutes
6
+
7
+ attr_reader :name,
8
+ :rotation_exp,
9
+ :namespace,
10
+ :instance,
11
+ :beacon_private_key,
12
+ :beacon_keypair,
13
+ :beacon_time_zero
14
+
15
+ def initialize(name:,
16
+ rotation_exp: nil,
17
+ namespace: nil,
18
+ instance: nil,
19
+ beacon_private_key: nil)
20
+ @name = name
21
+ @rotation_exp = (rotation_exp || DEFAULT_ROTATION_EXP).to_i
22
+ @namespace = namespace || DEFAULT_NAMESPACE
23
+ @instance = instance || random_instance
24
+ @beacon_private_key = beacon_private_key || EphemeralCalc::KeyPair.generate_private_key
25
+ @beacon_keypair = EphemeralCalc::KeyPair.new(@beacon_private_key)
26
+ end
27
+
28
+ def api_client
29
+ @api_client ||= ProximityBeacon::Client.new
30
+ end
31
+
32
+ def eid_params
33
+ @eid_params ||= api_client.eidparams
34
+ end
35
+
36
+ def resolver_public_key
37
+ Base64.decode64(eid_params["serviceEcdhPublicKey"])
38
+ end
39
+
40
+ def identity_key
41
+ beacon_keypair.identity_key(resolver_public_key: resolver_public_key)
42
+ end
43
+
44
+ def encryptor
45
+ @encryptor ||= EphemeralCalc::Encryptor.new(identity_key, rotation_exp)
46
+ end
47
+
48
+ def initial_eid
49
+ [encryptor.get_identifier(0)].pack("H*")
50
+ end
51
+
52
+ def register
53
+ beacon = ProximityBeacon::Beacon.new(
54
+ ephemeral_id_registration: {
55
+ beaconEcdhPublicKey: Base64.strict_encode64(beacon_keypair.public_key),
56
+ serviceEcdhPublicKey: eid_params["serviceEcdhPublicKey"],
57
+ rotationPeriodExponent: rotation_exp,
58
+ initialClockValue: encryptor.beacon_time,
59
+ initialEid: Base64.strict_encode64(initial_eid)
60
+ },
61
+ advertised_id: ProximityBeacon::AdvertisedId.new(
62
+ eddystone_ids: [namespace, instance]
63
+ ),
64
+ status: "ACTIVE",
65
+ description: self.name,
66
+ )
67
+ api_client.beacons.register(beacon)
68
+ end
69
+
70
+ def as_yaml
71
+ {
72
+ identity_key: identity_key,
73
+ rotation_exp: rotation_exp,
74
+ initial_time: encryptor.initial_time,
75
+ }
76
+ end
77
+
78
+ def to_yaml
79
+ as_yaml.to_yaml
80
+ end
81
+
82
+ private
83
+
84
+ def random_instance
85
+ SecureRandom.random_bytes(6).unpack("H*")[0]
86
+ end
87
+
88
+ end
89
+ end
@@ -1,3 +1,3 @@
1
1
  module EphemeralCalc
2
- VERSION = "0.2.0"
2
+ VERSION = "0.3.0"
3
3
  end
@@ -1,11 +1,10 @@
1
+ require "proximity_beacon"
2
+
1
3
  require "ephemeral_calc/version"
2
4
  require "ephemeral_calc/curve25519"
3
5
  require "ephemeral_calc/encryptor"
4
6
  require "ephemeral_calc/key_pair"
5
- require "ephemeral_calc/google_api/oauth"
6
- require "ephemeral_calc/google_api/credentials"
7
- require "ephemeral_calc/google_api/request"
8
- require "ephemeral_calc/google_api/client"
7
+ require "ephemeral_calc/registration"
9
8
 
10
9
  module EphemeralCalc
11
10
  # Your code goes here...
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ephemeral_calc
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Radius Networks
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2016-08-12 00:00:00.000000000 Z
11
+ date: 2016-08-15 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: proximity_beacon
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 0.1.3
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 0.1.3
13
27
  - !ruby/object:Gem::Dependency
14
28
  name: bundler
15
29
  requirement: !ruby/object:Gem::Requirement
@@ -106,11 +120,8 @@ files:
106
120
  - ext/curve25519/extconf.rb
107
121
  - lib/ephemeral_calc.rb
108
122
  - lib/ephemeral_calc/encryptor.rb
109
- - lib/ephemeral_calc/google_api/client.rb
110
- - lib/ephemeral_calc/google_api/credentials.rb
111
- - lib/ephemeral_calc/google_api/oauth.rb
112
- - lib/ephemeral_calc/google_api/request.rb
113
123
  - lib/ephemeral_calc/key_pair.rb
124
+ - lib/ephemeral_calc/registration.rb
114
125
  - lib/ephemeral_calc/version.rb
115
126
  homepage: https://github.com/RadiusNetworks/ephemeral_calc
116
127
  licenses: []
@@ -1,82 +0,0 @@
1
- require 'json'
2
- require 'uri'
3
- require 'net/http'
4
- require 'base64'
5
-
6
- module EphemeralCalc
7
- module GoogleAPI
8
- class Client
9
-
10
- PROXIMITY_BEACON_ROOT = "https://proximitybeacon.googleapis.com/v1beta1/"
11
- EIDPARAMS_URI = URI(PROXIMITY_BEACON_ROOT + "eidparams")
12
- BEACON_REGISTER_URI = URI(PROXIMITY_BEACON_ROOT + "beacons:register")
13
-
14
- attr_accessor :credentials
15
-
16
- def initialize(credentials = OAuth.new.get_credentials)
17
- self.credentials = credentials
18
- end
19
-
20
- def eidparams
21
- response = Request.get(EIDPARAMS_URI, credentials)
22
- # response = request(:get, EIDPARAMS_URI, credentials.access_token)
23
- return JSON.parse(response.body)
24
- end
25
-
26
- def register_eid(beacon_public_key, rotation_exp, initial_eid, initial_clock, uid_bytes)
27
- service_public_key_base64 = get_eidparams["serviceEcdhPublicKey"]
28
- response = Request.post(BEACON_REGISTER_URI, credentials) {|request|
29
- request.add_field "Content-Type", "application/json"
30
- request.body = {
31
- ephemeralIdRegistration: {
32
- beaconEcdhPublicKey: Base64.strict_encode64(beacon_public_key),
33
- serviceEcdhPublicKey: service_public_key_base64,
34
- rotationPeriodExponent: rotation_exp,
35
- initialClockValue: initial_clock,
36
- initialEid: Base64.strict_encode64(initial_eid)
37
- },
38
- advertisedId: {
39
- type: "EDDYSTONE",
40
- id: Base64.strict_encode64(uid_bytes)
41
- },
42
- status: "ACTIVE",
43
- description: "EphemeralCalc Registered EID"
44
- }.to_json
45
- }
46
- JSON.parse(response.body)
47
- end
48
-
49
- def get_resource(resource_name)
50
- uri = URI(PROXIMITY_BEACON_ROOT + resource_name)
51
- response = Request.get(uri, credentials)
52
- JSON.parse(response.body)
53
- end
54
-
55
- def getforobserved(eids, api_key = ENV["GOOGLE_API_KEY"])
56
- uri = URI("#{PROXIMITY_BEACON_ROOT}beaconinfo:getforobserved?key=#{api_key}")
57
- response = Request.post(uri) {|request|
58
- observations = Array(eids).map {|eid|
59
- {advertisedId: {type: "EDDYSTONE_EID", id: base64_eid(eid)}}
60
- }
61
- request.body = {
62
- observations: observations,
63
- namespacedTypes: "*",
64
- }.to_json
65
- request.add_field "Content-Type", "application/json"
66
- }
67
- JSON.parse(response.body)
68
- end
69
-
70
- private
71
-
72
- def base64_eid(eid)
73
- if eid.size == 16
74
- Base64.strict_encode64([eid].pack("H*"))
75
- else
76
- Base64.strict_encode64(eid)
77
- end
78
- end
79
-
80
- end
81
- end
82
- end
@@ -1,39 +0,0 @@
1
- require 'yaml'
2
-
3
- module EphemeralCalc
4
- module GoogleAPI
5
- class Credentials
6
-
7
- DEFAULT_FILE_STORE = File.expand_path("~/.ephemeral_calc_google_credentials.yaml")
8
- attr_accessor :access_token, :refresh_token, :expires_at
9
-
10
- def self.from_file(file = DEFAULT_FILE_STORE)
11
- return nil unless File.exist?(file)
12
- self.new YAML.load_file(file)
13
- end
14
-
15
- def initialize(opts = {})
16
- # convert string keys to symbols
17
- opts = opts.inject({}){|memo,(k,v)| memo[k.to_sym] = v; memo}
18
-
19
- self.access_token = opts[:access_token]
20
- self.refresh_token = opts[:refresh_token]
21
- self.expires_at = opts[:expires_at] || (Time.now + opts[:expires_in].to_i)
22
- end
23
-
24
- def save(file = DEFAULT_FILE_STORE)
25
- data = {
26
- access_token: access_token,
27
- refresh_token: refresh_token,
28
- expires_at: expires_at
29
- }.to_yaml
30
- File.open(file, 'w') {|f| f.write(data) }
31
- end
32
-
33
- def expired?
34
- self.expires_at < Time.now
35
- end
36
-
37
- end
38
- end
39
- end
@@ -1,87 +0,0 @@
1
- require 'json'
2
- require 'uri'
3
- require 'net/http'
4
-
5
- module EphemeralCalc
6
- module GoogleAPI
7
- class OAuth
8
-
9
- SCOPES = [
10
- "https://www.googleapis.com/auth/userlocation.beacon.registry",
11
- "https://www.googleapis.com/auth/cloud-platform",
12
- ]
13
-
14
- REDIRECT_URI = "urn:ietf:wg:oauth:2.0:oob"
15
- TOKEN_URI = URI("https://www.googleapis.com/oauth2/v4/token")
16
-
17
- attr_accessor :client_id, :secret
18
-
19
- def initialize(client_id = ENV["GOOGLE_CLIENT_ID"], secret = ENV["GOOGLE_CLIENT_SECRET"])
20
- if client_id.nil? || secret.nil?
21
- raise ArgumentError, "No Google Client ID or Secret was set. These can set in the environment variables \"GOOGLE_CLIENT_ID\" and \"GOOGLE_CLIENT_SECRET\" respectively. Credentials must be created for your project at \"https://console.developers.google.com/apis/credentials\"."
22
- end
23
- self.client_id = client_id
24
- self.secret = secret
25
- end
26
-
27
- def get_code
28
- puts("Performing OAuth with Google...")
29
- if RUBY_PLATFORM =~ /darwin/
30
- system("open \"#{url}\"")
31
- else
32
- puts("Open this URL in your browser: \"#{url}\"\n\n")
33
- end
34
- printf "Copy and paste code from web browser here: "
35
- _code = STDIN.gets.chomp
36
- end
37
-
38
- def get_credentials(old_credentials = Credentials.from_file)
39
- return old_credentials if old_credentials && !old_credentials.expired?
40
- response = Request.post(TOKEN_URI) {|request|
41
- request.body = hash_to_params( token_request_params(old_credentials) )
42
- }
43
- json = JSON.parse(response.body)
44
- credentials = Credentials.new(json)
45
- if old_credentials
46
- credentials.refresh_token = old_credentials.refresh_token
47
- end
48
- return credentials
49
- end
50
-
51
- def url
52
- params = hash_to_params(
53
- scope: SCOPES.join("%20"),
54
- redirect_uri: REDIRECT_URI,
55
- response_type: "code",
56
- client_id: client_id,
57
- )
58
- "https://accounts.google.com/o/oauth2/v2/auth?#{params}"
59
- end
60
-
61
- def token_request_params(old_credentials)
62
- if old_credentials == nil
63
- {
64
- code: get_code,
65
- client_id: client_id,
66
- client_secret: secret,
67
- redirect_uri: REDIRECT_URI,
68
- grant_type: "authorization_code",
69
- }
70
- else
71
- # this is a refresh of the old credentials
72
- {
73
- refresh_token: old_credentials.refresh_token,
74
- client_id: client_id,
75
- client_secret: secret,
76
- grant_type: "refresh_token",
77
- }
78
- end
79
- end
80
-
81
- def hash_to_params(hash)
82
- hash.map {|k,v| "#{k}=#{v}"}.join("&")
83
- end
84
-
85
- end
86
- end
87
- end
@@ -1,69 +0,0 @@
1
- require 'forwardable'
2
- require 'uri'
3
- require 'net/http'
4
- require 'base64'
5
-
6
- module EphemeralCalc
7
- module GoogleAPI
8
- class Request
9
- extend Forwardable
10
-
11
- attr_accessor :method, :uri, :credentials
12
- def_delegators :request, :add_field, :body=
13
-
14
- def self.get(uri, credentials = nil)
15
- result = self.new(:get, uri, credentials)
16
- result.perform {|r| yield r if block_given? }
17
- end
18
-
19
- def self.post(uri, credentials = nil)
20
- result = self.new(:post, uri, credentials)
21
- result.perform {|r| yield r if block_given? }
22
- end
23
-
24
- def initialize(method, uri, credentials = nil)
25
- self.method = method
26
- self.uri = uri
27
- self.credentials = credentials
28
- yield self if block_given?
29
- end
30
-
31
- def perform
32
- http_opts = {use_ssl: true}
33
- response = Net::HTTP.start(uri.host, uri.port, http_opts) do |http|
34
- add_field "Authorization", "Bearer #{credentials.access_token}" if credentials
35
- add_field "Accept", "application/json"
36
- yield self if block_given?
37
- http.request request
38
- end
39
- if (200..299).include?(response.code.to_i)
40
- return response
41
- else
42
- raise RequestError.new(response.code.to_i), "Error #{response.code} (#{response.msg}) - #{uri}\n#{response.body}"
43
- end
44
- end
45
-
46
- private
47
-
48
- def request
49
- @request ||=
50
- begin
51
- case method
52
- when :get
53
- Net::HTTP::Get.new uri.request_uri
54
- when :post
55
- Net::HTTP::Post.new uri.request_uri
56
- end
57
- end
58
- end
59
- end
60
-
61
- class RequestError < StandardError
62
- attr_accessor :code
63
- def initialize(code)
64
- self.code = code
65
- end
66
- end
67
-
68
- end
69
- end