ephemeral_calc 0.2.0 → 0.3.0

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