selfsdk 0.0.133 → 0.0.138

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: b0b0d98353e921f104b67c05456975e12135f7c9ce67b285f962cffd11b30c5e
4
- data.tar.gz: 82d0dac60ed6ec6bd31f0ea7ef9d01ab8dda4a3bdd3f9d4912073953c53676b0
3
+ metadata.gz: d9cbb2bad8f28cb417c2df600279bed7de2730b4391a735574a953e15da32eb0
4
+ data.tar.gz: caee509d09748376b2185de51e73e4cdc78b1790a2de323f90babcf94b2df1f3
5
5
  SHA512:
6
- metadata.gz: a30410ab97a325f40ee84f465eac9a15972a188916b3633c08a2d02ef4f07ef528be69446a8189c06e037a90a12b8085e4c4b9de3e686fc0f6eba8335005ad91
7
- data.tar.gz: 70091f9d604789568e96d36c86a094093bfbf872dcec0c768e674bf6b01f8f480822e18ddc09027380187337767b55d798919cc823172d829df24a38e3f53fcb
6
+ metadata.gz: 63b33c646f77f9f07e365e2c33f840eef746b7e7f564833a4598755d36da7f698cb8a01c24158a82de8477e3861a82ce5dea588a4bf2d0ba7ff92addb101601f
7
+ data.tar.gz: d858ae95e76994ac51a2622f141a8b8bd47eeaebca0ed55842ae6fa4bd025debaeb557b51201fd63755da9425341c5c85737b551718b21ad61b4860199d6a40c
@@ -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,12 +39,16 @@ 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
+ rq = opts.fetch(:request, true)
43
+ if rq
44
+ raise "You're not permitting connections from #{selfid}" unless @messaging_service.is_permitted?(selfid)
45
+ end
41
46
 
42
47
  req = SelfSDK::Messages::AuthenticationReq.new(@messaging)
43
48
  req.populate(selfid, opts)
44
49
 
45
50
  body = @client.jwt.prepare(req.body)
46
- return body unless opts.fetch(:request, true)
51
+ return body unless rq
47
52
  return req.send_message if opts.fetch(:async, false)
48
53
 
49
54
  # when a block is given the request will always be asynchronous.
@@ -86,7 +91,7 @@ module SelfSDK
86
91
  elsif @client.env == 'development'
87
92
  return "https://joinself.page.link/?link=#{callback}%3Fqr=#{body}&apn=com.joinself.app.dev"
88
93
  end
89
- "https://selfid.page.link/?link=#{callback}%3Fqr=#{body}&apn=com.joinself.app.#{@client.env}"
94
+ "https://joinself.page.link/?link=#{callback}%3Fqr=#{body}&apn=com.joinself.app.#{@client.env}"
90
95
  end
91
96
 
92
97
  # 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,12 +41,16 @@ 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
+ rq = opts.fetch(:request, true)
45
+ if rq
46
+ raise "You're not permitting connections from #{selfid}" unless @messaging_service.is_permitted?(selfid)
47
+ end
43
48
 
44
49
  req = SelfSDK::Messages::FactRequest.new(@messaging)
45
50
  req.populate(selfid, prepare_facts(facts), opts)
46
51
 
47
52
  body = @client.jwt.prepare(req.body)
48
- return body unless opts.fetch(:request, true)
53
+ return body unless rq
49
54
 
50
55
  # when a block is given the request will always be asynchronous.
51
56
  if block_given?
@@ -108,11 +113,11 @@ module SelfSDK
108
113
  body = @client.jwt.encode(request(selfid, facts, opts))
109
114
 
110
115
  if @client.env.empty?
111
- return "https://selfid.page.link/?link=#{callback}%3Fqr=#{body}&apn=net.selfid.app"
116
+ return "https://joinself.page.link/?link=#{callback}%3Fqr=#{body}&apn=com.joinself.app"
112
117
  elsif @client.env == 'development'
113
- return "https://selfid.page.link/?link=#{callback}%3Fqr=#{body}&apn=net.selfid.app.dev"
118
+ return "https://joinself.page.link/?link=#{callback}%3Fqr=#{body}&apn=com.joinself.app.dev"
114
119
  end
115
- "https://selfid.page.link/?link=#{callback}%3Fqr=#{body}&apn=net.selfid.app.#{@client.env}"
120
+ "https://joinself.page.link/?link=#{callback}%3Fqr=#{body}&apn=com.joinself.app.#{@client.env}"
116
121
  end
117
122
 
118
123
  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.133
4
+ version: 0.0.138
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