ephemeral_calc 0.2.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 +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
@@ -0,0 +1,32 @@
|
|
1
|
+
#include "ruby.h"
|
2
|
+
|
3
|
+
VALUE curve25519_module = Qnil;
|
4
|
+
|
5
|
+
int curve25519_donna(uint8_t *mypublic, const uint8_t *secret, const uint8_t *basepoint);
|
6
|
+
|
7
|
+
VALUE method_mult(VALUE klass, VALUE a, VALUE b)
|
8
|
+
{
|
9
|
+
VALUE result;
|
10
|
+
if (TYPE(a) != T_STRING || RSTRING_LEN(a) != 32 ||
|
11
|
+
TYPE(b) != T_STRING || RSTRING_LEN(b) != 32)
|
12
|
+
{
|
13
|
+
rb_raise(rb_eArgError, "Both arguments must be 32 byte strings");
|
14
|
+
}
|
15
|
+
result = rb_str_buf_new(32);
|
16
|
+
rb_str_set_len(result, 32);
|
17
|
+
curve25519_donna(
|
18
|
+
(uint8_t*)RSTRING_PTR(result),
|
19
|
+
(uint8_t*)RSTRING_PTR(a),
|
20
|
+
(uint8_t*)RSTRING_PTR(b)
|
21
|
+
);
|
22
|
+
return result;
|
23
|
+
}
|
24
|
+
|
25
|
+
void Init_curve25519()
|
26
|
+
{
|
27
|
+
VALUE ephemeral_calc_module = rb_const_get(rb_cObject, rb_intern("EphemeralCalc"));
|
28
|
+
curve25519_module = rb_define_module_under(ephemeral_calc_module, "Curve25519");
|
29
|
+
rb_define_singleton_method(curve25519_module, "mult", method_mult, 2);
|
30
|
+
uint8_t basepoint[32] = {9};
|
31
|
+
rb_define_const(curve25519_module, "BASEPOINT", rb_str_new( (char*)basepoint, 32) );
|
32
|
+
}
|
@@ -0,0 +1,94 @@
|
|
1
|
+
require 'openssl'
|
2
|
+
|
3
|
+
module EphemeralCalc
|
4
|
+
class Encryptor
|
5
|
+
|
6
|
+
attr_accessor :identity_key
|
7
|
+
attr_accessor :rotation_scalar
|
8
|
+
attr_accessor :initial_time
|
9
|
+
|
10
|
+
def initialize(identity_key, rotation_scalar, initial_time = Time.now)
|
11
|
+
if identity_key.size == 32
|
12
|
+
# 32 characters means this is a 16-byte hex string
|
13
|
+
self.identity_key = [identity_key].pack("H*")
|
14
|
+
else
|
15
|
+
self.identity_key = identity_key
|
16
|
+
end
|
17
|
+
self.rotation_scalar = rotation_scalar
|
18
|
+
self.initial_time = initial_time.to_i
|
19
|
+
end
|
20
|
+
|
21
|
+
def beacon_time
|
22
|
+
Time.now.to_i - self.initial_time
|
23
|
+
end
|
24
|
+
|
25
|
+
def quantum
|
26
|
+
beacon_time / (2**rotation_scalar)
|
27
|
+
end
|
28
|
+
|
29
|
+
# Output is an 8-byte encrypted identifier as a hex string
|
30
|
+
# e.g. "0102030405060708"
|
31
|
+
def get_identifier(beacon_time = nil)
|
32
|
+
beacon_time ||= self.beacon_time
|
33
|
+
return nil if beacon_time < 0
|
34
|
+
temporary_key = do_aes_encryption(self.identity_key, key_generation_data_block(beacon_time))
|
35
|
+
encrypted_data = do_aes_encryption(temporary_key, data_block(beacon_time)).bytes.to_a
|
36
|
+
# the identifier is the first 8 bytes of the encrypted output
|
37
|
+
identifier_array = encrypted_data[0,8]
|
38
|
+
identifier_array.map{|b| sprintf("%02X",b)}.join
|
39
|
+
end
|
40
|
+
|
41
|
+
# yields the current EID and each subsuquent EID, until the block returns :stop
|
42
|
+
def each_identifier
|
43
|
+
last_quantum = nil
|
44
|
+
loop do
|
45
|
+
if quantum != last_quantum
|
46
|
+
last_quantum = quantum
|
47
|
+
break if :stop == yield( get_identifier )
|
48
|
+
end
|
49
|
+
sleep 1
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
def seconds_counter_32_bit(time_in_seconds)
|
56
|
+
time_in_seconds ||= Time.now.to_i
|
57
|
+
time_in_seconds.to_i & 0xffffffff
|
58
|
+
end
|
59
|
+
|
60
|
+
def key_generation_data_block(time_in_seconds=nil)
|
61
|
+
time_b2 = (seconds_counter_32_bit(time_in_seconds) >> 16) & 0xff
|
62
|
+
time_b3 = (seconds_counter_32_bit(time_in_seconds) >> 24) & 0xff
|
63
|
+
[
|
64
|
+
0, 0, 0, 0,
|
65
|
+
0, 0, 0, 0,
|
66
|
+
0, 0, 0, 0xff,
|
67
|
+
0, 0, time_b3, time_b2
|
68
|
+
].pack('c*')
|
69
|
+
end
|
70
|
+
|
71
|
+
# this is the 16 byte block to encrypt as an array of 16 numbers
|
72
|
+
def data_block(time_in_seconds=nil)
|
73
|
+
time = seconds_counter_32_bit(time_in_seconds) & (0xffffffff - (2**rotation_scalar-1))
|
74
|
+
time_b0 = time & 0xff
|
75
|
+
time_b1 = (time >> 8) & 0xff
|
76
|
+
time_b2 = (time >> 16) & 0xff
|
77
|
+
time_b3 = (time >> 24) & 0xff
|
78
|
+
[
|
79
|
+
0, 0, 0, 0,
|
80
|
+
0, 0, 0, 0,
|
81
|
+
0, 0, 0, rotation_scalar,
|
82
|
+
time_b3, time_b2, time_b1, time_b0
|
83
|
+
].pack('c*')
|
84
|
+
end
|
85
|
+
|
86
|
+
def do_aes_encryption(key, data)
|
87
|
+
aes = OpenSSL::Cipher::Cipher.new("AES-128-ECB")
|
88
|
+
aes.encrypt
|
89
|
+
aes.key = key
|
90
|
+
#aes.iv = "what do I put here?"
|
91
|
+
aes.update(data) + aes.final
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,82 @@
|
|
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
|
@@ -0,0 +1,39 @@
|
|
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
|
@@ -0,0 +1,87 @@
|
|
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
|
@@ -0,0 +1,69 @@
|
|
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
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'openssl'
|
2
|
+
require 'securerandom'
|
3
|
+
|
4
|
+
module EphemeralCalc
|
5
|
+
class KeyPair
|
6
|
+
attr_reader :private_key
|
7
|
+
|
8
|
+
def initialize(private_key = nil)
|
9
|
+
self.private_key = private_key || KeyPair.generate_private_key
|
10
|
+
end
|
11
|
+
|
12
|
+
def private_key=(new_key)
|
13
|
+
@private_key = convert_key(new_key)
|
14
|
+
end
|
15
|
+
|
16
|
+
def public_key
|
17
|
+
@public_key ||= begin
|
18
|
+
Curve25519.mult(self.private_key, Curve25519::BASEPOINT)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def shared_secret(other_public_key)
|
23
|
+
Curve25519.mult(self.private_key, other_public_key)
|
24
|
+
end
|
25
|
+
|
26
|
+
# opts must contain the key :resolver_public_key or :beacon_public_key
|
27
|
+
def identity_key(opts)
|
28
|
+
if opts[:resolver_public_key]
|
29
|
+
resolver_public_key = convert_key(opts[:resolver_public_key])
|
30
|
+
beacon_public_key = self.public_key
|
31
|
+
secret = shared_secret(resolver_public_key)
|
32
|
+
elsif opts[:beacon_public_key]
|
33
|
+
resolver_public_key = self.public_key
|
34
|
+
beacon_public_key = convert_key(opts[:beacon_public_key])
|
35
|
+
secret = shared_secret(beacon_public_key)
|
36
|
+
else
|
37
|
+
raise ArgumentError, "Must pass a resolver_public_key or a beacon_public_key"
|
38
|
+
end
|
39
|
+
salt = resolver_public_key + beacon_public_key
|
40
|
+
hkdf(secret, salt)[0..15]
|
41
|
+
end
|
42
|
+
|
43
|
+
def convert_key(key_string)
|
44
|
+
if key_string.size == 64
|
45
|
+
[key_string].pack("H*")
|
46
|
+
else
|
47
|
+
key_string
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def self.generate_private_key
|
52
|
+
# reference: https://code.google.com/archive/p/curve25519-donna/
|
53
|
+
# See section on "generating a private key"
|
54
|
+
key = SecureRandom.random_bytes(32).bytes
|
55
|
+
key[0] &= 248
|
56
|
+
key[31] &= 127
|
57
|
+
key[31] |= 64
|
58
|
+
return key.pack("C*")
|
59
|
+
end
|
60
|
+
|
61
|
+
def hkdf(secret, salt)
|
62
|
+
digest = OpenSSL::Digest.new("SHA256")
|
63
|
+
prk = OpenSSL::HMAC.digest(digest, salt, secret)
|
64
|
+
OpenSSL::HMAC.digest(digest, prk, "\x01")
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require "ephemeral_calc/version"
|
2
|
+
require "ephemeral_calc/curve25519"
|
3
|
+
require "ephemeral_calc/encryptor"
|
4
|
+
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"
|
9
|
+
|
10
|
+
module EphemeralCalc
|
11
|
+
# Your code goes here...
|
12
|
+
end
|
metadata
ADDED
@@ -0,0 +1,138 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ephemeral_calc
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.2.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Radius Networks
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-08-12 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.10'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.10'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: webmock
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rake-compiler
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
description: Tools to calculate Eddystone ephemeral identifiers
|
84
|
+
email:
|
85
|
+
- support@radiusnetworks.com
|
86
|
+
executables:
|
87
|
+
- ephemeral_calc
|
88
|
+
extensions:
|
89
|
+
- ext/curve25519/extconf.rb
|
90
|
+
extra_rdoc_files: []
|
91
|
+
files:
|
92
|
+
- ".gitignore"
|
93
|
+
- ".rspec"
|
94
|
+
- ".travis.yml"
|
95
|
+
- CODE_OF_CONDUCT.md
|
96
|
+
- Gemfile
|
97
|
+
- README.md
|
98
|
+
- Rakefile
|
99
|
+
- bin/console
|
100
|
+
- bin/rspec
|
101
|
+
- bin/setup
|
102
|
+
- ephemeral_calc.gemspec
|
103
|
+
- exe/ephemeral_calc
|
104
|
+
- ext/curve25519/curve25519-donna.c
|
105
|
+
- ext/curve25519/curve25519_module.c
|
106
|
+
- ext/curve25519/extconf.rb
|
107
|
+
- lib/ephemeral_calc.rb
|
108
|
+
- 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
|
+
- lib/ephemeral_calc/key_pair.rb
|
114
|
+
- lib/ephemeral_calc/version.rb
|
115
|
+
homepage: https://github.com/RadiusNetworks/ephemeral_calc
|
116
|
+
licenses: []
|
117
|
+
metadata: {}
|
118
|
+
post_install_message:
|
119
|
+
rdoc_options: []
|
120
|
+
require_paths:
|
121
|
+
- lib
|
122
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
123
|
+
requirements:
|
124
|
+
- - ">="
|
125
|
+
- !ruby/object:Gem::Version
|
126
|
+
version: '0'
|
127
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - ">="
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '0'
|
132
|
+
requirements: []
|
133
|
+
rubyforge_project:
|
134
|
+
rubygems_version: 2.4.6
|
135
|
+
signing_key:
|
136
|
+
specification_version: 4
|
137
|
+
summary: Tools to calculate Eddystone ephemeral identifiers
|
138
|
+
test_files: []
|