tanker-identity 2.16.0.alpha.12 → 3.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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: a265a7977b72e9861c66ac154cd518d9b9c1a6e398d1799fe11317328958cd08
|
4
|
+
data.tar.gz: 74c5ff896b3bed87e459db40dea13b8a3ef6b7acf46a4c3f21a1e7ea3dddd685
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b2462e9797f55597cbdb99a1c83e8f4e0e003a7920ec0d997910beafed5efd354409b4efc88db25db4ad7fbb7891be4602096fb8b1435133f5988898c14df921
|
7
|
+
data.tar.gz: 383149e026c2747b334b0e85d9ffd2b36f83a7fd980530cb0521deeaa17b8aa64847e8d56bb5ae954eaea53deb7f4a6877f3dd47716ab1360a5e3293e092b49f
|
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.1.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-08-02 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.0.3
|
162
162
|
signing_key:
|
163
163
|
specification_version: 4
|
164
164
|
summary: Tanker identity management library packaged as a gem
|