tanker-identity 2.16.0.alpha.1 → 3.0.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 +4 -4
- data/README.md +3 -3
- data/lib/tanker/crypto.rb +15 -0
- data/lib/tanker/identity.rb +58 -40
- data/lib/tanker/identity/version.rb +1 -1
- metadata +5 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 538f757e41dcface55b1025098af3e75bb52303547e5665ae8ad118f80d447eb
|
4
|
+
data.tar.gz: ab2746f68ae1ecc15e8c0900ec382b49a01508270dfc5e7217503221c19cb168
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9b1cbeae3ffc99ab33c8f65602a1d40c72d31108a273e98e403652f95e5596cce27f778c33154372f77e764fbc879008c497969ab52f3cde19ece5139e97c57a
|
7
|
+
data.tar.gz: 76881380c77649b73eb2d9f3d68f28e21ecc6055da620155c20ad67e9181de55380be53b5f44b9eb60c22a6d4469cd123fa5faf53e92803f1639618544f2f010
|
data/README.md
CHANGED
@@ -10,7 +10,7 @@ Identity generation in Ruby for the [Tanker SDK](https://docs.tanker.io/latest/)
|
|
10
10
|
|
11
11
|
This gem requires Ruby v2.5 or greater (transitive requirement from [rbnacl](https://github.com/crypto-rb/rbnacl)).
|
12
12
|
|
13
|
-
|
13
|
+
Older Ruby versions are not supported.
|
14
14
|
|
15
15
|
## Installation
|
16
16
|
|
@@ -21,7 +21,7 @@ Before going further, please follow [instructions to install libsodium](https://
|
|
21
21
|
Then, add this line to your application's Gemfile:
|
22
22
|
|
23
23
|
```ruby
|
24
|
-
gem 'tanker-identity',
|
24
|
+
gem 'tanker-identity', 'X.Y.Z'
|
25
25
|
```
|
26
26
|
|
27
27
|
Finally, execute:
|
@@ -48,7 +48,7 @@ The unique ID of a user in your application.
|
|
48
48
|
<br><br>
|
49
49
|
|
50
50
|
```ruby
|
51
|
-
Tanker::Identity.create_provisional_identity(app_id, email)
|
51
|
+
Tanker::Identity.create_provisional_identity(app_id, 'email', email)
|
52
52
|
```
|
53
53
|
Create a Tanker provisional identity. It allows you to share a resource with a user who does not have an account in your application yet.
|
54
54
|
|
data/lib/tanker/crypto.rb
CHANGED
@@ -7,12 +7,27 @@ module Tanker
|
|
7
7
|
class InvalidSignature < StandardError; end
|
8
8
|
|
9
9
|
HASH_MIN_SIZE = RbNaCl::Hash::Blake2b::BYTES_MIN # 16
|
10
|
+
BLOCK_HASH_SIZE = 32
|
10
11
|
|
11
12
|
def self.generichash(input, size)
|
12
13
|
binary_input = input.dup.force_encoding(Encoding::ASCII_8BIT)
|
13
14
|
RbNaCl::Hash.blake2b(binary_input, digest_size: size)
|
14
15
|
end
|
15
16
|
|
17
|
+
def self.hash_user_id(app_id, user_id)
|
18
|
+
binary_user_id = user_id.dup.force_encoding(Encoding::ASCII_8BIT)
|
19
|
+
Crypto.generichash(binary_user_id + app_id, BLOCK_HASH_SIZE)
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.hashed_provisional_email(email)
|
23
|
+
Base64.strict_encode64(Crypto.generichash(email, BLOCK_HASH_SIZE))
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.hashed_provisional_value(value, private_signature_key)
|
27
|
+
secret_salt = Crypto.generichash(Base64.strict_decode64(private_signature_key), BLOCK_HASH_SIZE)
|
28
|
+
Base64.strict_encode64(Crypto.generichash(secret_salt + value, BLOCK_HASH_SIZE))
|
29
|
+
end
|
30
|
+
|
16
31
|
# We need this static method since a RbNaCl::SigningKey instance can't be
|
17
32
|
# directly initialized with a given private_signature_key
|
18
33
|
def self.sign_detached(message, private_signature_key)
|
data/lib/tanker/identity.rb
CHANGED
@@ -9,16 +9,10 @@ module Tanker
|
|
9
9
|
APP_SECRET_SIZE = 64
|
10
10
|
APP_PUBLIC_KEY_SIZE = 32
|
11
11
|
AUTHOR_SIZE = 32
|
12
|
-
BLOCK_HASH_SIZE = 32
|
13
12
|
USER_SECRET_SIZE = 32
|
14
13
|
|
15
14
|
private
|
16
15
|
|
17
|
-
def self.hash_user_id(app_id, user_id)
|
18
|
-
binary_user_id = user_id.dup.force_encoding(Encoding::ASCII_8BIT)
|
19
|
-
Crypto.generichash(binary_user_id + app_id, BLOCK_HASH_SIZE)
|
20
|
-
end
|
21
|
-
|
22
16
|
def self.user_secret(hashed_user_id)
|
23
17
|
random_bytes = Crypto.random_bytes(USER_SECRET_SIZE - 1)
|
24
18
|
check = Crypto.generichash(random_bytes + hashed_user_id, Crypto::HASH_MIN_SIZE)
|
@@ -27,9 +21,7 @@ module Tanker
|
|
27
21
|
|
28
22
|
def self.assert_string_values(hash)
|
29
23
|
hash.each_pair do |key, value|
|
30
|
-
unless value.is_a?(String)
|
31
|
-
raise TypeError.new("expected #{key} to be a String but was a #{value.class}")
|
32
|
-
end
|
24
|
+
raise TypeError, "expected #{key} to be a String but was a #{value.class}" unless value.is_a?(String)
|
33
25
|
end
|
34
26
|
end
|
35
27
|
|
@@ -37,7 +29,7 @@ module Tanker
|
|
37
29
|
block_nature = APP_CREATION_NATURE.chr(Encoding::ASCII_8BIT)
|
38
30
|
none_author = 0.chr(Encoding::ASCII_8BIT) * AUTHOR_SIZE
|
39
31
|
app_public_key = app_secret[-APP_PUBLIC_KEY_SIZE..-1]
|
40
|
-
Crypto.generichash(block_nature + none_author + app_public_key, BLOCK_HASH_SIZE)
|
32
|
+
Crypto.generichash(block_nature + none_author + app_public_key, Crypto::BLOCK_HASH_SIZE)
|
41
33
|
end
|
42
34
|
|
43
35
|
public
|
@@ -52,55 +44,61 @@ module Tanker
|
|
52
44
|
|
53
45
|
def self.create_identity(b64_app_id, b64_app_secret, user_id)
|
54
46
|
assert_string_values({
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
47
|
+
app_id: b64_app_id,
|
48
|
+
app_secret: b64_app_secret,
|
49
|
+
user_id: user_id
|
50
|
+
})
|
59
51
|
|
60
52
|
app_id = Base64.strict_decode64(b64_app_id)
|
61
53
|
app_secret = Base64.strict_decode64(b64_app_secret)
|
62
54
|
|
63
|
-
raise ArgumentError
|
64
|
-
raise ArgumentError
|
65
|
-
raise ArgumentError
|
55
|
+
raise ArgumentError, "Invalid app_id" if app_id.bytesize != Crypto::BLOCK_HASH_SIZE
|
56
|
+
raise ArgumentError, "Invalid app_secret" if app_secret.bytesize != APP_SECRET_SIZE
|
57
|
+
raise ArgumentError, "Invalid (app_id, app_secret) combination" if app_id != generate_app_id(app_secret)
|
66
58
|
|
67
|
-
hashed_user_id = hash_user_id(app_id, user_id)
|
59
|
+
hashed_user_id = Crypto.hash_user_id(app_id, user_id)
|
68
60
|
signature_keypair = Crypto.generate_signature_keypair
|
69
61
|
message = signature_keypair[:public_key] + hashed_user_id
|
70
62
|
signature = Crypto.sign_detached(message, app_secret)
|
71
63
|
|
72
64
|
serialize({
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
65
|
+
trustchain_id: Base64.strict_encode64(app_id),
|
66
|
+
target: 'user',
|
67
|
+
value: Base64.strict_encode64(hashed_user_id),
|
68
|
+
delegation_signature: Base64.strict_encode64(signature),
|
69
|
+
ephemeral_public_signature_key: Base64.strict_encode64(signature_keypair[:public_key]),
|
70
|
+
ephemeral_private_signature_key: Base64.strict_encode64(signature_keypair[:private_key]),
|
71
|
+
user_secret: Base64.strict_encode64(user_secret(hashed_user_id))
|
72
|
+
})
|
81
73
|
end
|
82
74
|
|
83
|
-
def self.create_provisional_identity(b64_app_id,
|
75
|
+
def self.create_provisional_identity(b64_app_id, target, value)
|
84
76
|
assert_string_values({
|
85
|
-
|
86
|
-
|
87
|
-
|
77
|
+
app_id: b64_app_id,
|
78
|
+
target: target,
|
79
|
+
value: value
|
80
|
+
})
|
81
|
+
|
82
|
+
valid_targets = %w[email phone_number]
|
83
|
+
unless valid_targets.include? target
|
84
|
+
raise ArgumentError, "expected #{target} to be one of #{valid_targets.join(', ')}"
|
85
|
+
end
|
88
86
|
|
89
87
|
app_id = Base64.strict_decode64(b64_app_id)
|
90
|
-
raise ArgumentError
|
88
|
+
raise ArgumentError, "Invalid app_id" if app_id.bytesize != Crypto::BLOCK_HASH_SIZE
|
91
89
|
|
92
90
|
encryption_keypair = Crypto.generate_encryption_keypair
|
93
91
|
signature_keypair = Crypto.generate_signature_keypair
|
94
92
|
|
95
93
|
serialize({
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
94
|
+
trustchain_id: b64_app_id,
|
95
|
+
target: target,
|
96
|
+
value: value,
|
97
|
+
public_encryption_key: Base64.strict_encode64(encryption_keypair[:public_key]),
|
98
|
+
private_encryption_key: Base64.strict_encode64(encryption_keypair[:private_key]),
|
99
|
+
public_signature_key: Base64.strict_encode64(signature_keypair[:public_key]),
|
100
|
+
private_signature_key: Base64.strict_encode64(signature_keypair[:private_key])
|
101
|
+
})
|
104
102
|
end
|
105
103
|
|
106
104
|
def self.get_public_identity(serialized_identity)
|
@@ -117,9 +115,29 @@ module Tanker
|
|
117
115
|
public_identity = {}
|
118
116
|
public_keys.each { |key| public_identity[key] = identity.fetch(key) }
|
119
117
|
|
118
|
+
if identity['target'] == 'email'
|
119
|
+
public_identity['target'] = 'hashed_email'
|
120
|
+
public_identity['value'] = Crypto.hashed_provisional_email(public_identity['value'])
|
121
|
+
elsif identity['target'] != 'user'
|
122
|
+
public_identity['target'] = 'hashed_' + public_identity['target']
|
123
|
+
public_identity['value'] = Crypto.hashed_provisional_value(public_identity['value'],
|
124
|
+
identity['private_signature_key'])
|
125
|
+
end
|
126
|
+
|
120
127
|
serialize(public_identity)
|
121
128
|
rescue KeyError # failed fetch
|
122
|
-
raise ArgumentError
|
129
|
+
raise ArgumentError, 'Not a valid Tanker identity'
|
130
|
+
end
|
131
|
+
|
132
|
+
def self.upgrade_identity(serialized_identity)
|
133
|
+
assert_string_values({ identity: serialized_identity })
|
134
|
+
|
135
|
+
identity = deserialize(serialized_identity)
|
136
|
+
if identity['target'] == 'email' && !identity.key?('private_encryption_key')
|
137
|
+
identity['target'] = 'hashed_email'
|
138
|
+
identity['value'] = Crypto.hashed_provisional_email(identity['value'])
|
139
|
+
end
|
140
|
+
serialize(identity)
|
123
141
|
end
|
124
142
|
end
|
125
143
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: tanker-identity
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 3.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tanker Team
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-07-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rbnacl
|
@@ -154,11 +154,11 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
154
154
|
version: 2.5.0
|
155
155
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
156
156
|
requirements:
|
157
|
-
- - "
|
157
|
+
- - ">="
|
158
158
|
- !ruby/object:Gem::Version
|
159
|
-
version:
|
159
|
+
version: '0'
|
160
160
|
requirements: []
|
161
|
-
rubygems_version: 3.
|
161
|
+
rubygems_version: 3.1.6
|
162
162
|
signing_key:
|
163
163
|
specification_version: 4
|
164
164
|
summary: Tanker identity management library packaged as a gem
|