Class: SelfSDK::Crypto

Inherits:
Object
  • Object
show all
Defined in:
lib/crypto.rb

Instance Method Summary collapse

Constructor Details

#initialize(client, device, storage_folder, storage_key) ⇒ Crypto

Returns a new instance of Crypto.

[View source]

7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# File 'lib/crypto.rb', line 7

def initialize(client, device, storage_folder, storage_key)
  @client = client
  @device = device
  @storage_key = storage_key
  @storage_folder = "#{storage_folder}/#{@client.jwt.key_id}"

  if File.exist?()
    # 1a) if alice's account file exists load the pickle from the file
    @account = SelfCrypto::Account.from_pickle(File.read(), @storage_key)
  else
    # 1b-i) if create a new account for alice if one doesn't exist already
    @account = SelfCrypto::Account.from_seed(@client.jwt.key)

    # 1b-ii) generate some keys for alice and publish them
    @account.gen_otk(100)

    # 1b-iii) convert those keys to json
    keys = @account.otk['curve25519'].map{|k,v| {id: k, key: v}}.to_json

    # 1b-iv) post those keys to POST /v1/identities/<selfid>/devices/1/pre_keys/
    res = @client.post("/v1/apps/#{@client.jwt.id}/devices/#{@device}/pre_keys", keys)
    raise 'unable to push prekeys, please try in a few minutes' if res.code != 200

    # 1b-v) store the account to a file
    File.write(, @account.to_pickle(storage_key))
  end
end

Instance Method Details

#decrypt(message, sender, sender_device) ⇒ Object

[View source]

79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
# File 'lib/crypto.rb', line 79

def decrypt(message, sender, sender_device)
  session_file_name = session_path(sender, sender_device)

  if File.exist?(session_file_name)
    # 7a) if carol's session file exists load the pickle from the file
    session_with_bob = SelfCrypto::Session.from_pickle(File.read(session_file_name), @storage_key)
  else
    # 7b-i) if you have not previously sent or received a message to/from bob,
    #       you should extract the initial message from the group message intended
    #       for your account id.
    m = SelfCrypto::GroupMessage.new(message.to_s).get_message("#{@client.jwt.id}:#{@device}")

    # 7b-ii) use the initial message to create a session for bob or carol
    session_with_bob = @account.inbound_session(m)

    # 7b-iii) remove the session's prekey from the account
    @account.remove_one_time_keys(session_with_bob)

    current_one_time_keys = @account.otk['curve25519']

    # 7b-iv) if the number of remaining prekeys is below a certain threshold, publish new keys
    if current_one_time_keys.length < 10
      @account.gen_otk(100)

      keys = Array.new

      @account.otk['curve25519'].each do |k,v|
        keys.push({id: k, key: v}) if current_one_time_keys[k].nil?
      end

      res = @client.post("/v1/apps/#{@client.jwt.id}/devices/#{@device}/pre_keys", keys.to_json)
      raise 'unable to push prekeys, please try in a few minutes' if res.code != 200
    end

    File.write(, @account.to_pickle(@storage_key))
  end

  # 8) create a group session and set the identity of the account you're using
  gs = SelfCrypto::GroupSession.new("#{@client.jwt.id}:#{@device}")

  # 9) add all recipients and their sessions
  gs.add_participant("#{sender}:#{sender_device}", session_with_bob)

  # 10) decrypt the message ciphertext
  pt = gs.decrypt("#{sender}:#{sender_device}", message).to_s

  # 11) store the session to a file
  File.write(session_file_name, session_with_bob.to_pickle(@storage_key))

  pt
end

#encrypt(message, recipient, recipient_device) ⇒ Object

[View source]

35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
# File 'lib/crypto.rb', line 35

def encrypt(message, recipient, recipient_device)
  session_file_name = session_path(recipient, recipient_device)

  if File.exist?(session_file_name)
    # 2a) if bob's session file exists load the pickle from the file
    session_with_bob = SelfCrypto::Session.from_pickle(File.read(session_file_name), @storage_key)
  else
    # 2b-i) if you have not previously sent or recevied a message to/from bob,
    #       you must get his identity key from GET /v1/identities/bob/
    ed25519_identity_key = @client.device_public_key(recipient, recipient_device)

    # 2b-ii) get a one time key for bob
    res = @client.get("/v1/identities/#{recipient}/devices/#{recipient_device}/pre_keys")

    if res.code != 200
      b = JSON.parse(res.body)
      ::SelfSDK.logger.error "identity response : #{b['message']}"
      raise "could not get identity pre_keys"
    end

    one_time_key = JSON.parse(res.body)["key"]

    # 2b-iii) convert bobs ed25519 identity key to a curve25519 key
    curve25519_identity_key = SelfCrypto::Util.ed25519_pk_to_curve25519(ed25519_identity_key.raw_public_key)

    # 2b-iv) create the session with bob
    session_with_bob = @account.outbound_session(curve25519_identity_key, one_time_key)
  end

  # 3) create a group session and set the identity of the account youre using
  gs = SelfCrypto::GroupSession.new("#{@client.jwt.id}:#{@device}")

  # 4) add all recipients and their sessions
  gs.add_participant("#{recipient}:#{recipient_device}", session_with_bob)

  # 5) encrypt a message
  ct = gs.encrypt(message).to_s

  # 6) store the session to a file
  File.write(session_file_name, session_with_bob.to_pickle(@storage_key))

  ct
end