ephemeral_calc 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +11 -0
- data/.rspec +2 -0
- data/.travis.yml +4 -0
- data/CODE_OF_CONDUCT.md +13 -0
- data/Gemfile +4 -0
- data/README.md +60 -0
- data/Rakefile +11 -0
- data/bin/console +14 -0
- data/bin/rspec +16 -0
- data/bin/setup +12 -0
- data/ephemeral_calc.gemspec +28 -0
- data/exe/ephemeral_calc +227 -0
- data/ext/curve25519/curve25519-donna.c +860 -0
- data/ext/curve25519/curve25519_module.c +32 -0
- data/ext/curve25519/extconf.rb +11 -0
- data/lib/ephemeral_calc/encryptor.rb +94 -0
- data/lib/ephemeral_calc/google_api/client.rb +82 -0
- data/lib/ephemeral_calc/google_api/credentials.rb +39 -0
- data/lib/ephemeral_calc/google_api/oauth.rb +87 -0
- data/lib/ephemeral_calc/google_api/request.rb +69 -0
- data/lib/ephemeral_calc/key_pair.rb +67 -0
- data/lib/ephemeral_calc/version.rb +3 -0
- data/lib/ephemeral_calc.rb +12 -0
- metadata +138 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: d8cecf935ff387d53df0c4f3a5ab898810061627
|
4
|
+
data.tar.gz: 4a303fa845749f4c4f33c7df5e0616fd08e3d018
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 134e6ceb11fa9daeafb21b228e9a97289ed25eaa953a525a81d9058a0bf0d5a38f531ff5aedd578933018ebbba0ee0f1c5edcab4812340fc91a8764c3eb06c85
|
7
|
+
data.tar.gz: c03aaa19d4f06effd6fe854cd22d4ba7be0f75612a07f4ca256f90776f8209370017da3b1698e609f856929220a0dfeee3b519fa1ed88b7e154cdb177b233aa4
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/CODE_OF_CONDUCT.md
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
# Contributor Code of Conduct
|
2
|
+
|
3
|
+
As contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities.
|
4
|
+
|
5
|
+
We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, or religion.
|
6
|
+
|
7
|
+
Examples of unacceptable behavior by participants include the use of sexual language or imagery, derogatory comments or personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct.
|
8
|
+
|
9
|
+
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed from the project team.
|
10
|
+
|
11
|
+
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers.
|
12
|
+
|
13
|
+
This Code of Conduct is adapted from the [Contributor Covenant](http://contributor-covenant.org), version 1.0.0, available at [http://contributor-covenant.org/version/1/0/0/](http://contributor-covenant.org/version/1/0/0/)
|
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
# EphemeralCalc
|
2
|
+
|
3
|
+
A Ruby gem that allows you to calculate ephemeral identifiers for Eddystone-EID beacons.
|
4
|
+
|
5
|
+
# Example Usage
|
6
|
+
|
7
|
+
```ruby
|
8
|
+
# insert real resolver key here
|
9
|
+
resolver_public_key = "61136adab1bf302f1c49e860196aeefd8fbaa41518b4c226372d6cc469c47278"
|
10
|
+
# generate a new key pair for the beacon
|
11
|
+
beacon_keys = EphemeralCalc::KeyPair.new
|
12
|
+
# calculate the identity key
|
13
|
+
identity_key = beacon_keys.identity_key(resolver_public_key: resolver_public_key)
|
14
|
+
# set up the encryptor
|
15
|
+
rotation_exponent = 10
|
16
|
+
encryptor = EphemeralCalc::Encryptor.new identity_key, rotation_exponent
|
17
|
+
# get the initial eid
|
18
|
+
initial_eid = encryptor.get_identifier(0)
|
19
|
+
|
20
|
+
# yield eid values as it rotates
|
21
|
+
encryptor.each_identifier do |eid|
|
22
|
+
puts "Quantum: #{encryptor.quantum}, EID: #{eid}"
|
23
|
+
end
|
24
|
+
```
|
25
|
+
|
26
|
+
# Command Line Usage
|
27
|
+
The `ephemeral_calc` executable is included to allow you to generate keypairs, and do general eid calculation
|
28
|
+
without having to write any code.
|
29
|
+
|
30
|
+
```shell
|
31
|
+
# display CLI help
|
32
|
+
ephemeral_calc
|
33
|
+
|
34
|
+
# set up EphemeralCalc for Google OAuth
|
35
|
+
export GOOGLE_CLIENT_ID="xxxx"
|
36
|
+
export GOOGLE_CLIENT_SECRET="yyyy"
|
37
|
+
|
38
|
+
# get EID registration parameters from Google's API using OAuth
|
39
|
+
ephemeral_calc eidparams
|
40
|
+
|
41
|
+
# resolve an EID using a Google project's API key
|
42
|
+
API_KEY=xxxx ephemeral_calc resolve 0123456789ABCDEF
|
43
|
+
|
44
|
+
# resolve EID using OAuth
|
45
|
+
ephemeral_calc resolve 0123456789ABCDEF
|
46
|
+
|
47
|
+
# list first 5 EIDs given identity key, rotation scaler
|
48
|
+
ephemeral_calc list 553C66DA1485E9F78849DE00F2CB178E 12 0 5
|
49
|
+
```
|
50
|
+
|
51
|
+
## Development
|
52
|
+
|
53
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
54
|
+
|
55
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
56
|
+
|
57
|
+
## Contributing
|
58
|
+
|
59
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/radiusnetworks/ephemeral_calc. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](contributor-covenant.org) code of conduct.
|
60
|
+
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "eid_utils"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start
|
data/bin/rspec
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# This file was generated by Bundler.
|
4
|
+
#
|
5
|
+
# The application 'rspec' is installed as part of a gem, and
|
6
|
+
# this file is here to facilitate running it.
|
7
|
+
#
|
8
|
+
|
9
|
+
require 'pathname'
|
10
|
+
ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
|
11
|
+
Pathname.new(__FILE__).realpath)
|
12
|
+
|
13
|
+
require 'rubygems'
|
14
|
+
require 'bundler/setup'
|
15
|
+
|
16
|
+
load Gem.bin_path('rspec-core', 'rspec')
|
data/bin/setup
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'ephemeral_calc/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "ephemeral_calc"
|
8
|
+
spec.version = EphemeralCalc::VERSION
|
9
|
+
|
10
|
+
spec.authors = ["Radius Networks"]
|
11
|
+
spec.email = ["support@radiusnetworks.com"]
|
12
|
+
|
13
|
+
spec.summary = %q{Tools to calculate Eddystone ephemeral identifiers}
|
14
|
+
spec.description = %q{Tools to calculate Eddystone ephemeral identifiers}
|
15
|
+
spec.homepage = "https://github.com/RadiusNetworks/ephemeral_calc"
|
16
|
+
|
17
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
18
|
+
spec.bindir = "exe"
|
19
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
20
|
+
spec.extensions = %w[ext/curve25519/extconf.rb]
|
21
|
+
spec.require_paths = ["lib"]
|
22
|
+
|
23
|
+
spec.add_development_dependency "bundler", "~> 1.10"
|
24
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
25
|
+
spec.add_development_dependency "rspec"
|
26
|
+
spec.add_development_dependency "webmock"
|
27
|
+
spec.add_development_dependency "rake-compiler"
|
28
|
+
end
|
data/exe/ephemeral_calc
ADDED
@@ -0,0 +1,227 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
|
4
|
+
require 'ephemeral_calc'
|
5
|
+
|
6
|
+
class App
|
7
|
+
|
8
|
+
def help
|
9
|
+
help_text = <<eos
|
10
|
+
Usage:
|
11
|
+
ephemeral_calc <command> <arguments>
|
12
|
+
|
13
|
+
Commands:
|
14
|
+
|
15
|
+
beacon:
|
16
|
+
ephemeral_calc beacon <ik> <scaler> <beacon_initial_time_seconds> <service_initial_time_seconds>
|
17
|
+
|
18
|
+
Given the identity key, the beacon rotation exponent, and the times of
|
19
|
+
registration for the beacon and the service, this computes the eid that the
|
20
|
+
beacon is currently broadcasting.
|
21
|
+
|
22
|
+
track:
|
23
|
+
ephemeral_calc track <ik> <scaler> <beacon_initial_time_seconds> <service_initial_time_seconds>
|
24
|
+
|
25
|
+
Given the identity key, the beacon rotation exponent, and the times of
|
26
|
+
registration for the beacon and the service, this displays the current eid and
|
27
|
+
outputs new eids as they are calculated, until the user presses <ctrl-c>.
|
28
|
+
|
29
|
+
eid:
|
30
|
+
ephemeral_calc eid <identity_key_hex> <rotation_scaler> <beacon_time_seconds>
|
31
|
+
|
32
|
+
Generates an EID from the given 16-bytes identity key, the scaler and the
|
33
|
+
beacon time in seconds.
|
34
|
+
|
35
|
+
list:
|
36
|
+
ephemeral_calc list <identity_key_hex> <rotation_scaler> <beacon_time_seconds> <count>
|
37
|
+
|
38
|
+
Lists <count> EIDs, starting at <beacon_time_seconds>
|
39
|
+
|
40
|
+
resolve:
|
41
|
+
ephemeral_calc resolve <eid_hex>
|
42
|
+
|
43
|
+
Resolves the given EID to a beacon contained in Google's Proximity Beacon API. If you supply an
|
44
|
+
api key (via the environment variable "GOOGLE_API_KEY") it will use that and call the
|
45
|
+
"getforobserved" API endpoint. Otherwise it will attempt to use OAuth for authentication and
|
46
|
+
use the admin api endpoint to get the beacon details.
|
47
|
+
|
48
|
+
eidparams:
|
49
|
+
ephemeral_calc eidparams
|
50
|
+
|
51
|
+
Gets the EID registration parameters from Google's Proximity Beacon API. Uses environment
|
52
|
+
variables "GOOGLE_CLIENT_ID" and "GOOGLE_CLIENT_SECRET" to do the necessary OAuth in order to
|
53
|
+
access the API.
|
54
|
+
|
55
|
+
register:
|
56
|
+
ephemeral_calc register <beacon_public_key> <rotation_scaler> <initial_eid> <initial_clock> <namespace> <instance>
|
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.
|
61
|
+
|
62
|
+
keygen:
|
63
|
+
ephemeral_calc keygen
|
64
|
+
|
65
|
+
Generates a curve25519 keypair
|
66
|
+
eos
|
67
|
+
puts help_text
|
68
|
+
end
|
69
|
+
|
70
|
+
COMMANDS = [:beacon, :track, :eid, :list, :resolve, :keygen, :eidparams, :register]
|
71
|
+
|
72
|
+
def beacon(track = false)
|
73
|
+
identity_key = ARGV.shift
|
74
|
+
rotation_scalar = ARGV.shift.to_i
|
75
|
+
beacon_initial_time = ARGV.shift.to_i
|
76
|
+
service_initial_time = ARGV.shift.to_i
|
77
|
+
beacon_time_zero = Time.at( service_initial_time - beacon_initial_time)
|
78
|
+
encryptor = EphemeralCalc::Encryptor.new identity_key, rotation_scalar, beacon_time_zero
|
79
|
+
beacon_time = encryptor.beacon_time
|
80
|
+
current_eid = encryptor.get_identifier
|
81
|
+
previous_eid = encryptor.get_identifier(beacon_time - 2**rotation_scalar)
|
82
|
+
next_eid = encryptor.get_identifier(beacon_time + 2**rotation_scalar)
|
83
|
+
|
84
|
+
if !track
|
85
|
+
puts "Previous EID: #{previous_eid}" if previous_eid
|
86
|
+
puts " Current EID: #{current_eid}"
|
87
|
+
puts " Next EID: #{next_eid}"
|
88
|
+
else
|
89
|
+
puts "Press <ctrl-c> to stop"
|
90
|
+
begin
|
91
|
+
encryptor.each_identifier do |eid|
|
92
|
+
puts "Quantum: #{encryptor.quantum}, EID: #{eid}"
|
93
|
+
end
|
94
|
+
rescue SignalException => e
|
95
|
+
# ignore CTRL-C, just exit
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def track
|
101
|
+
beacon(true)
|
102
|
+
end
|
103
|
+
|
104
|
+
def eid
|
105
|
+
identity_key = ARGV.shift
|
106
|
+
rotation_scalar = ARGV.shift.to_i
|
107
|
+
beacon_time = (ARGV.shift || 0).to_i
|
108
|
+
encryptor = EphemeralCalc::Encryptor.new identity_key, rotation_scalar
|
109
|
+
quantum = beacon_time / 2**rotation_scalar
|
110
|
+
eid = encryptor.get_identifier(beacon_time)
|
111
|
+
puts "Identity Key: #{identity_key}"
|
112
|
+
puts " Beacon Time: #{beacon_time}"
|
113
|
+
puts " Quantum: #{quantum}"
|
114
|
+
puts "Ephemeral ID: #{eid}"
|
115
|
+
end
|
116
|
+
|
117
|
+
def list
|
118
|
+
identity_key = ARGV.shift
|
119
|
+
rotation_scalar = ARGV.shift.to_i
|
120
|
+
beacon_time = ARGV.shift.to_i
|
121
|
+
count = ARGV.shift.to_i
|
122
|
+
encryptor = EphemeralCalc::Encryptor.new identity_key, rotation_scalar
|
123
|
+
start_quantum = beacon_time / 2**rotation_scalar
|
124
|
+
end_quantum = start_quantum + count - 1
|
125
|
+
start_quantum.upto(end_quantum) do |quantum|
|
126
|
+
start_time = quantum * 2**rotation_scalar
|
127
|
+
end_time = start_time + 2**rotation_scalar
|
128
|
+
eid = encryptor.get_identifier(start_time)
|
129
|
+
puts "EID: #{eid}, Quantum: #{quantum} (#{seconds_display(start_time)} - #{seconds_display(end_time)})"
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
def resolve
|
134
|
+
require 'yaml'
|
135
|
+
eid_hex = ARGV.shift
|
136
|
+
api_key = ENV["GOOGLE_API_KEY"]
|
137
|
+
|
138
|
+
if api_key
|
139
|
+
client = EphemeralCalc::GoogleAPI::Client.new(nil) # don't try oauth
|
140
|
+
response = client.getforobserved(eid_hex, api_key)
|
141
|
+
beacons = response["beacons"]
|
142
|
+
beacon = beacons[0] if beacons
|
143
|
+
else
|
144
|
+
beacon_name = "beacons/4!#{eid_hex.downcase}"
|
145
|
+
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
|
150
|
+
# if we get a 404, the EID doesn't resolve
|
151
|
+
raise e unless e.code == 404
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
if beacon == nil
|
156
|
+
puts "Does not resolve!"
|
157
|
+
else
|
158
|
+
puts beacon.to_yaml
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
def eidparams
|
163
|
+
require 'yaml'
|
164
|
+
eidparams = oauth_client.eidparams
|
165
|
+
key_hex = to_hex(Base64.decode64(eidparams["serviceEcdhPublicKey"]))
|
166
|
+
eidparams["serviceEcdhPublicKeyHex"] = key_hex
|
167
|
+
puts eidparams.to_yaml
|
168
|
+
end
|
169
|
+
|
170
|
+
def to_hex(bytes)
|
171
|
+
bytes.unpack("H*")[0].upcase
|
172
|
+
end
|
173
|
+
|
174
|
+
def from_hex(hex)
|
175
|
+
[hex].pack("H*")
|
176
|
+
end
|
177
|
+
|
178
|
+
def register
|
179
|
+
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
|
189
|
+
end
|
190
|
+
|
191
|
+
def keygen
|
192
|
+
key_pair = EphemeralCalc::KeyPair.new
|
193
|
+
puts "Private Key: #{to_hex(key_pair.private_key)}"
|
194
|
+
puts " Public Key: #{to_hex(key_pair.public_key)}"
|
195
|
+
end
|
196
|
+
|
197
|
+
def seconds_display(seconds)
|
198
|
+
case
|
199
|
+
when seconds >= 86400
|
200
|
+
"#{seconds / 86400}d " + seconds_display(seconds % 86400)
|
201
|
+
when seconds >= 3600
|
202
|
+
"#{seconds / 3600}h " + seconds_display(seconds % 3600)
|
203
|
+
when seconds >= 60
|
204
|
+
"#{seconds / 60}m " + seconds_display(seconds % 60)
|
205
|
+
else
|
206
|
+
"#{seconds}s"
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
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
|
+
end
|
219
|
+
|
220
|
+
app = App.new
|
221
|
+
command = ARGV.shift
|
222
|
+
|
223
|
+
if command && App::COMMANDS.include?(command.to_sym)
|
224
|
+
app.send(command)
|
225
|
+
else
|
226
|
+
app.help
|
227
|
+
end
|