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
@@ -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: []
|