tanker-identity 2.16.0 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: eaf24a9b2ca81ada5f0aff245b7ea9f0ca23a2d1a88ed8c3a0d218a6dc5e6117
4
- data.tar.gz: 44760ddbaeb52ad9f0abea1e59b59a40798a4eccd357a7f86d0320a66c288266
3
+ metadata.gz: 538f757e41dcface55b1025098af3e75bb52303547e5665ae8ad118f80d447eb
4
+ data.tar.gz: ab2746f68ae1ecc15e8c0900ec382b49a01508270dfc5e7217503221c19cb168
5
5
  SHA512:
6
- metadata.gz: b89085b6da9ca29614a65461cdfa266170742b24bb1039304719a6afc60691b6b7b84dd9258924e4940852c59012fbce9d03e2d964b570c78dbfe81a105bde89
7
- data.tar.gz: 0f7e2d82bbca7cae450f0b041dd860ba1bbae351f3ac15b55f4e0d6e6ef014ae4f193ddbcf0f454ca58b44c347d5777d66bb84176863df3419edd9a671a03bd3
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
- If still on an older version of Ruby, use `tanker-identity` with the [`603b35f8` commit ref](https://github.com/TankerHQ/identity-ruby/tree/603b35f8e1ca889c4862e8f9c1e54632a38b32b6).
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', git: 'https://github.com/TankerHQ/identity-ruby' #, ref: '<commit>'
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)
@@ -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
- app_id: b64_app_id,
56
- app_secret: b64_app_secret,
57
- user_id: user_id
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.new("Invalid app_id") if app_id.bytesize != BLOCK_HASH_SIZE
64
- raise ArgumentError.new("Invalid app_secret") if app_secret.bytesize != APP_SECRET_SIZE
65
- raise ArgumentError.new("Invalid (app_id, app_secret) combination") if app_id != generate_app_id(app_secret)
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
- trustchain_id: Base64.strict_encode64(app_id),
74
- target: 'user',
75
- value: Base64.strict_encode64(hashed_user_id),
76
- delegation_signature: Base64.strict_encode64(signature),
77
- ephemeral_public_signature_key: Base64.strict_encode64(signature_keypair[:public_key]),
78
- ephemeral_private_signature_key: Base64.strict_encode64(signature_keypair[:private_key]),
79
- user_secret: Base64.strict_encode64(user_secret(hashed_user_id))
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, email)
75
+ def self.create_provisional_identity(b64_app_id, target, value)
84
76
  assert_string_values({
85
- app_id: b64_app_id,
86
- email: email
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.new("Invalid app_id") if app_id.bytesize != BLOCK_HASH_SIZE
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
- trustchain_id: b64_app_id,
97
- target: 'email',
98
- value: email,
99
- public_encryption_key: Base64.strict_encode64(encryption_keypair[:public_key]),
100
- private_encryption_key: Base64.strict_encode64(encryption_keypair[:private_key]),
101
- public_signature_key: Base64.strict_encode64(signature_keypair[:public_key]),
102
- private_signature_key: Base64.strict_encode64(signature_keypair[:private_key])
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.new('Not a valid Tanker identity')
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
@@ -1,6 +1,6 @@
1
1
  module Tanker
2
2
  module Identity
3
- VERSION = '2.16.0'
3
+ VERSION = '3.0.0'
4
4
  end
5
5
 
6
6
  def self.const_missing(name)
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: 2.16.0
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-06-21 00:00:00.000000000 Z
11
+ date: 2021-07-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rbnacl