selfsdk 0.0.132 → 0.0.137

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 588e5cd4c23df24315565cc2cc3716c03479606080cd57396e543c319b2563b7
4
- data.tar.gz: 1db12416f426287d711b744c4de4aa3ef99b6be93ec7bdc05cbe9c4f88267b28
3
+ metadata.gz: b4b5f44ffce4a1ad4c61c20b752e97173f2cc0c96091138683ddb56c2dca2f73
4
+ data.tar.gz: 841ad78f32bcb7434126eac7bdea3e3eb9d7abf4cc35fd41d0d243fa7f05fffa
5
5
  SHA512:
6
- metadata.gz: b77bfb0b0e30b9d20b8e9592e9cf6abc92b005eba4ae375cf48b8f3881144ce80d49cedc0376e4aeac611703a2e772f25e8d1eab7bf4d81c9f21daa2f3d01c0b
7
- data.tar.gz: '062289d4c389dfa3636e674cafaedd6bfd498ce269f7b2a3b30eab26ebd0ba319e4cd41fcd5d75615a61835cca348c3987d417179141b17894df3a0ad6d3ac77'
6
+ metadata.gz: 04f00c82b5f64dc8f23597e28fc11eee64db02deacfbeedb328d3f766048e40d3a625c3ae25b7a5a888969272883650e63ce9425d5056f2ed98f5beae1712987
7
+ data.tar.gz: 2dfa4e9f4e3796393adfda66d994ef39fa2de8f37b08b2274b4fcdaa1ccd752e7aafdec5f7eb80b1b4f336ecaeb74350a42464c785c5026eb82e8ffd3458761f
@@ -65,6 +65,22 @@ module SelfSDK
65
65
  i[:public_keys]
66
66
  end
67
67
 
68
+ def post(endpoint, body)
69
+ p HTTParty.post("#{@self_url}#{endpoint}",
70
+ headers: {
71
+ 'Content-Type' => 'application/json',
72
+ 'Authorization' => "Bearer #{@jwt.auth_token}"
73
+ },
74
+ body: body)
75
+ end
76
+
77
+ def get(endpoint)
78
+ HTTParty.get("#{@self_url}#{endpoint}", headers: {
79
+ 'Content-Type' => 'application/json',
80
+ 'Authorization' => "Bearer #{@jwt.auth_token}"
81
+ })
82
+ end
83
+
68
84
  # Lists all public keys stored on self for the given ID
69
85
  #
70
86
  # @param id [string] identity id
@@ -86,20 +102,6 @@ module SelfSDK
86
102
  body
87
103
  end
88
104
 
89
- def get(endpoint)
90
- HTTParty.get("#{@self_url}#{endpoint}", headers: {
91
- 'Content-Type' => 'application/json',
92
- 'Authorization' => "Bearer #{@jwt.auth_token}"
93
- })
94
- end
95
105
 
96
- def post(endpoint, body)
97
- HTTParty.post("#{@self_url}#{endpoint}",
98
- headers: {
99
- 'Content-Type' => 'application/json',
100
- 'Authorization' => "Bearer #{@jwt.auth_token}"
101
- },
102
- body: body)
103
- end
104
106
  end
105
107
  end
@@ -0,0 +1,102 @@
1
+ require 'self_crypto'
2
+
3
+ module SelfSDK
4
+ class Crypto
5
+ def initialize(client, device, storage_folder, storage_key)
6
+ @client = client
7
+ @device = device
8
+ @storage_key = storage_key
9
+ @storage_folder = storage_folder
10
+
11
+ if File.exist?('account.pickle')
12
+ # 1a) if alice's account file exists load the pickle from the file
13
+ @account = SelfCrypto::Account.from_pickle(File.read('account.pickle'), @storage_key)
14
+ else
15
+ # 1b-i) if create a new account for alice if one doesn't exist already
16
+ @account = SelfCrypto::Account.from_seed(@client.jwt.key)
17
+
18
+ # 1b-ii) generate some keys for alice and publish them
19
+ @account.gen_otk(100)
20
+
21
+ # 1b-iii) convert those keys to json
22
+ keys = @account.otk['curve25519'].map{|k,v| {id: k, key: v}}.to_json
23
+
24
+ # 1b-iv) post those keys to POST /v1/identities/<selfid>/devices/1/pre_keys/
25
+ @client.post("/v1/apps/#{@client.jwt.id}/devices/#{@device}/pre_keys", keys)
26
+
27
+ # 1b-v) store the account to a file
28
+ File.write('account.pickle', @account.to_pickle(storage_key))
29
+ end
30
+ end
31
+
32
+ def encrypt(message, recipient, recipient_device)
33
+ session_file_name = "#{recipient}:#{recipient_device}-session.pickle"
34
+
35
+ if File.exist?(session_file_name)
36
+ # 2a) if bob's session file exists load the pickle from the file
37
+ session_with_bob = SelfCrypto::Session.from_pickle(File.read(session_file_name), @storage_key)
38
+ else
39
+ # 2b-i) if you have not previously sent or recevied a message to/from bob,
40
+ # you must get his identity key from GET /v1/identities/bob/
41
+ ed25519_identity_key = @client.public_keys(recipient).first[:key]
42
+
43
+ # 2b-ii) get a one time key for bob
44
+ res = @client.get("/v1/identities/#{recipient}/devices/#{recipient_device}/pre_keys")
45
+
46
+ if res.code != 200
47
+ Selfid.logger.error "identity response : #{res.body[:message]}"
48
+ raise "could not get identity pre_keys"
49
+ end
50
+
51
+ one_time_key = JSON.parse(res.body)["key"]
52
+
53
+ # 2b-iii) convert bobs ed25519 identity key to a curve25519 key
54
+ curve25519_identity_key = SelfCrypto::Util.ed25519_pk_to_curve25519(ed25519_identity_key)
55
+
56
+ # 2b-iv) create the session with bob
57
+ session_with_bob = @account.outbound_session(curve25519_identity_key, one_time_key)
58
+
59
+ # 2b-v) store the session to a file
60
+ File.write(session_file_name, session_with_bob.to_pickle(@storage_key))
61
+ end
62
+
63
+ # 3) create a group session and set the identity of the account youre using
64
+ gs = SelfCrypto::GroupSession.new("#{@client.jwt.id}:#{@device}")
65
+
66
+ # 4) add all recipients and their sessions
67
+ gs.add_participant("#{recipient}:#{recipient_device}", session_with_bob)
68
+
69
+ # 5) encrypt a message
70
+ gs.encrypt(message).to_s
71
+ end
72
+
73
+ def decrypt(message, sender, sender_device)
74
+ session_file_name = "#{sender}:#{sender_device}-session.pickle"
75
+
76
+ if File.exist?(session_file_name)
77
+ # 7a) if carol's session file exists load the pickle from the file
78
+ session_with_bob = SelfCrypto::Session.from_pickle(File.read(session_file_name), @storage_key)
79
+ else
80
+ # 7b-i) if you have not previously sent or received a message to/from bob,
81
+ # you should extract the initial message from the group message intended
82
+ # for your account id.
83
+ m = SelfCrypto::GroupMessage.new(message.to_s).get_message("#{@client.jwt.id}:#{@device}")
84
+
85
+ # 7b-ii) use the initial message to create a session for bob or carol
86
+ session_with_bob = @account.inbound_session(m)
87
+
88
+ # 7b-iii) store the session to a file
89
+ File.write(session_file_name, session_with_bob.to_pickle(@storage_key))
90
+ end
91
+
92
+ # 8) create a group session and set the identity of the account you're using
93
+ gs = SelfCrypto::GroupSession.new("#{@client.jwt.id}:#{@device}")
94
+
95
+ # 9) add all recipients and their sessions
96
+ gs.add_participant("#{sender}:#{sender_device}", session_with_bob)
97
+
98
+ # 10) decrypt the message ciphertext
99
+ gs.decrypt("#{sender}:#{sender_device}", message).to_s
100
+ end
101
+ end
102
+ end
@@ -44,7 +44,7 @@ module SelfSDK
44
44
  sender: "#{@jwt.id}:#{@messaging.device_id}",
45
45
  id: @id,
46
46
  recipient: "#{@to}:#{@to_device}",
47
- ciphertext: @jwt.prepare(body))
47
+ ciphertext: encrypt_message(@jwt.prepare(body), @to, @to_device))
48
48
  end
49
49
 
50
50
  end
@@ -26,6 +26,10 @@ module SelfSDK
26
26
  res
27
27
  end
28
28
 
29
+ def encrypt_message(message, recipient, recipient_device)
30
+ @messaging.encryption_client.encrypt(message, recipient, recipient_device)
31
+ end
32
+
29
33
  def unauthorized?
30
34
  status == "unauthorized"
31
35
  end
@@ -93,19 +93,20 @@ module SelfSDK
93
93
  end
94
94
  @to_device = devices.first
95
95
 
96
- recipient = if @intermediary.nil?
97
- "#{@to}:#{@to_device}"
98
- else
99
- "#{@intermediary}:#{@to_device}"
100
- end
96
+ if @intermediary.nil?
97
+ recipient = "#{@to}:#{@to_device}"
98
+ ciphertext = encrypt_message(@jwt.prepare(body), @to, @to_device)
99
+ else
100
+ recipient = "#{@intermediary}:#{@to_device}"
101
+ ciphertext = encrypt_message(@jwt.prepare(body), @intermediary, @to_device)
102
+ end
101
103
 
102
104
  Msgproto::Message.new(
103
105
  type: Msgproto::MsgType::MSG,
104
106
  id: @id,
105
107
  sender: "#{@jwt.id}:#{@messaging.device_id}",
106
108
  recipient: recipient,
107
- ciphertext: @jwt.prepare(body),
108
- )
109
+ ciphertext: ciphertext )
109
110
  end
110
111
  end
111
112
  end
@@ -11,7 +11,8 @@ module SelfSDK
11
11
  body = if input.is_a? String
12
12
  input
13
13
  else
14
- input.ciphertext
14
+ issuer = input.recipient.split(":")
15
+ messaging.encryption_client.decrypt(input.ciphertext, issuer.first, issuer.last)
15
16
  end
16
17
 
17
18
  jwt = JSON.parse(body, symbolize_names: true)
@@ -4,6 +4,7 @@ require 'json'
4
4
  require 'monitor'
5
5
  require 'faye/websocket'
6
6
  require 'fileutils'
7
+ require_relative 'crypto'
7
8
  require_relative 'messages/message'
8
9
  require_relative 'proto/auth_pb'
9
10
  require_relative 'proto/message_pb'
@@ -18,16 +19,18 @@ module SelfSDK
18
19
  DEFAULT_STORAGE_DIR="./.self_storage"
19
20
  ON_DEMAND_CLOSE_CODE=3999
20
21
 
21
- attr_accessor :client, :jwt, :device_id, :ack_timeout, :timeout, :type_observer, :uuid_observer
22
+ attr_accessor :client, :jwt, :device_id, :ack_timeout, :timeout, :type_observer, :uuid_observer, :encryption_client
22
23
 
23
24
  # RestClient initializer
24
25
  #
25
26
  # @param url [string] self-messaging url
26
27
  # @params client [Object] SelfSDK::Client object
27
28
  # @option opts [string] :storage_dir the folder where encryption sessions and settings will be stored
29
+ # @params storage_key [String] seed to encrypt messaging
30
+ # @params storage_folder [String] folder to perist messaging encryption
28
31
  # @option opts [Bool] :auto_reconnect Automatically reconnects to websocket if connection is lost (defaults to true).
29
32
  # @option opts [String] :device_id The device id to be used by the app defaults to "1".
30
- def initialize(url, client, options = {})
33
+ def initialize(url, client, storage_key, storage_folder, options = {})
31
34
  @mon = Monitor.new
32
35
  @url = url
33
36
  @messages = {}
@@ -45,6 +48,9 @@ module SelfSDK
45
48
  @offset = read_offset
46
49
 
47
50
  FileUtils.mkdir_p @storage_dir unless File.exist? @storage_dir
51
+ unless options.include? :no_crypto
52
+ @encryption_client = Crypto.new(@client, @device_id, storage_folder, storage_key)
53
+ end
48
54
 
49
55
  if options.include? :ws
50
56
  @ws = options[:ws]
@@ -367,6 +373,8 @@ module SelfSDK
367
373
 
368
374
  def process_incomming_message(input)
369
375
  message = SelfSDK::Messages.parse(input, self)
376
+ @offset = input.offset
377
+ write_offset(@offset)
370
378
 
371
379
  if @messages.include? message.id
372
380
  message.validate! @messages[message.id][:original_message]
@@ -378,8 +386,6 @@ module SelfSDK
378
386
  notify_observer(message)
379
387
  end
380
388
 
381
- @offset = input.offset
382
- write_offset(@offset)
383
389
  rescue StandardError => e
384
390
  p "Error processing incoming message #{input.to_json}"
385
391
  SelfSDK.logger.info e
@@ -434,6 +440,7 @@ module SelfSDK
434
440
 
435
441
  def write_offset(offset)
436
442
  File.open(@offset_file, 'wb') do |f|
443
+ f.flock(File::LOCK_EX)
437
444
  f.write([offset].pack('q'))
438
445
  end
439
446
  end
@@ -53,6 +53,7 @@ module SelfSDK
53
53
  unless messaging_url.nil?
54
54
  @messaging_client = MessagingClient.new(messaging_url,
55
55
  @client,
56
+ storage_key,
56
57
  storage_dir: opts.fetch(:storage_dir, MessagingClient::DEFAULT_STORAGE_DIR),
57
58
  auto_reconnect: opts.fetch(:auto_reconnect, MessagingClient::DEFAULT_AUTO_RECONNECT),
58
59
  device_id: opts.fetch(:device_id, MessagingClient::DEFAULT_DEVICE))
@@ -61,12 +62,12 @@ module SelfSDK
61
62
 
62
63
  # Provides access to SelfSDK::Services::Facts service
63
64
  def facts
64
- @facts ||= SelfSDK::Services::Facts.new(@messaging_client, @client)
65
+ @facts ||= SelfSDK::Services::Facts.new(messaging, @client)
65
66
  end
66
67
 
67
68
  # Provides access to SelfSDK::Services::Authentication service
68
69
  def authentication
69
- @authentication ||= SelfSDK::Services::Authentication.new(@messaging_client, @client)
70
+ @authentication ||= SelfSDK::Services::Authentication.new(messaging, @client)
70
71
  end
71
72
 
72
73
  # Provides access to SelfSDK::Services::Identity service
@@ -15,7 +15,8 @@ module SelfSDK
15
15
  #
16
16
  # @return [SelfSDK::Services::Authentication] authentication service.
17
17
  def initialize(messaging, client)
18
- @messaging = messaging
18
+ @messaging = messaging.client
19
+ @messaging_service = messaging
19
20
  @client = client
20
21
  end
21
22
 
@@ -38,6 +39,7 @@ module SelfSDK
38
39
  # @return [String, String] conversation id or encoded body.
39
40
  def request(selfid, opts = {}, &block)
40
41
  SelfSDK.logger.info "authenticating #{selfid}"
42
+ raise "You're not permitting connections from #{selfid}" unless @messaging_service.is_permitted?(selfid)
41
43
 
42
44
  req = SelfSDK::Messages::AuthenticationReq.new(@messaging)
43
45
  req.populate(selfid, opts)
@@ -82,11 +84,11 @@ module SelfSDK
82
84
  body = @client.jwt.encode(request(selfid, opts))
83
85
 
84
86
  if @client.env.empty?
85
- return "https://selfid.page.link/?link=#{callback}%3Fqr=#{body}&apn=net.selfid.app"
87
+ return "https://joinself.page.link/?link=#{callback}%3Fqr=#{body}&apn=com.joinself.app"
86
88
  elsif @client.env == 'development'
87
- return "https://selfid.page.link/?link=#{callback}%3Fqr=#{body}&apn=net.selfid.app.dev"
89
+ return "https://joinself.page.link/?link=#{callback}%3Fqr=#{body}&apn=com.joinself.app.dev"
88
90
  end
89
- "https://selfid.page.link/?link=#{callback}%3Fqr=#{body}&apn=net.selfid.app.#{@client.env}"
91
+ "https://joinself.page.link/?link=#{callback}%3Fqr=#{body}&apn=com.joinself.app.#{@client.env}"
90
92
  end
91
93
 
92
94
  # Adds an observer for an authentication response
@@ -17,7 +17,8 @@ module SelfSDK
17
17
  #
18
18
  # @return [SelfSDK::Services::Facts] facts service.
19
19
  def initialize(messaging, client)
20
- @messaging = messaging
20
+ @messaging = messaging.client
21
+ @messaging_service = messaging
21
22
  @client = client
22
23
  end
23
24
 
@@ -40,6 +41,7 @@ module SelfSDK
40
41
  # @return [Object] SelfSDK:::Messages::FactRequest
41
42
  def request(selfid, facts, opts = {}, &block)
42
43
  SelfSDK.logger.info "authenticating #{selfid}"
44
+ raise "You're not permitting connections from #{selfid}" unless @messaging_service.is_permitted?(selfid)
43
45
 
44
46
  req = SelfSDK::Messages::FactRequest.new(@messaging)
45
47
  req.populate(selfid, prepare_facts(facts), opts)
@@ -108,11 +110,11 @@ module SelfSDK
108
110
  body = @client.jwt.encode(request(selfid, facts, opts))
109
111
 
110
112
  if @client.env.empty?
111
- return "https://selfid.page.link/?link=#{callback}%3Fqr=#{body}&apn=net.selfid.app"
113
+ return "https://joinself.page.link/?link=#{callback}%3Fqr=#{body}&apn=com.joinself.app"
112
114
  elsif @client.env == 'development'
113
- return "https://selfid.page.link/?link=#{callback}%3Fqr=#{body}&apn=net.selfid.app.dev"
115
+ return "https://joinself.page.link/?link=#{callback}%3Fqr=#{body}&apn=com.joinself.app.dev"
114
116
  end
115
- "https://selfid.page.link/?link=#{callback}%3Fqr=#{body}&apn=net.selfid.app.#{@client.env}"
117
+ "https://joinself.page.link/?link=#{callback}%3Fqr=#{body}&apn=com.joinself.app.#{@client.env}"
116
118
  end
117
119
 
118
120
  private
@@ -44,6 +44,15 @@ module SelfSDK
44
44
  acl.list
45
45
  end
46
46
 
47
+ # Checks if you're permitting messages from a specific self identifier
48
+ # @return [Boolean] yes|no
49
+ def is_permitted?(id)
50
+ conns = allowed_connections
51
+ return true if conns.include? "*"
52
+ return true if conns.include? id
53
+ return false
54
+ end
55
+
47
56
  # Revokes incoming messages from the given identity.
48
57
  #
49
58
  # @param [String] selfid to be denied
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: selfsdk
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.132
4
+ version: 0.0.137
5
5
  platform: ruby
6
6
  authors:
7
7
  - Aldgate Ventures
@@ -10,6 +10,20 @@ bindir: bin
10
10
  cert_chain: []
11
11
  date: 2011-09-29 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: self_crypto
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
13
27
  - !ruby/object:Gem::Dependency
14
28
  name: async
15
29
  requirement: !ruby/object:Gem::Requirement
@@ -299,6 +313,7 @@ files:
299
313
  - lib/acl.rb
300
314
  - lib/authenticated.rb
301
315
  - lib/client.rb
316
+ - lib/crypto.rb
302
317
  - lib/jwt_service.rb
303
318
  - lib/log.rb
304
319
  - lib/messages/attestation.rb