self_crypto 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,14 @@
1
+ require 'self_crypto/version'
2
+ require 'self_crypto/self_crypto'
3
+ require 'self_crypto/account'
4
+ require 'self_crypto/session'
5
+ require 'self_crypto/group_session'
6
+ require 'self_crypto/olm_message'
7
+ require 'self_crypto/message'
8
+ require 'self_crypto/group_message'
9
+ require 'self_crypto/pre_key_message'
10
+ require 'self_crypto/sas'
11
+ require 'self_crypto/utility'
12
+
13
+ module SelfCrypto
14
+ end
@@ -0,0 +1,30 @@
1
+ require 'base64'
2
+
3
+ module SelfCrypto
4
+
5
+ class Account
6
+
7
+ # @param pickle [String] pickled state
8
+ # @param password [String] password used to encrypt pickled state
9
+ # @return [Account]
10
+ def self.from_pickle(pickle, password="")
11
+ Account.new(pickle: pickle, password: password)
12
+ end
13
+
14
+ def self.from_seed(seed)
15
+ Account.new(seed: Base64.decode64(seed))
16
+ end
17
+
18
+ def gen_otk(number=1)
19
+ generate_one_time_keys(number)
20
+ end
21
+
22
+ alias_method :ik, :identity_keys
23
+ alias_method :otk, :one_time_keys
24
+ alias_method :mark_otk, :mark_keys_as_published
25
+ alias_method :max_otk, :max_number_of_one_time_keys
26
+ alias_method :update_otk, :remove_one_time_keys
27
+
28
+ end
29
+
30
+ end
@@ -0,0 +1,34 @@
1
+ require 'base64'
2
+
3
+ module SelfCrypto
4
+
5
+ class GroupMessage
6
+
7
+ # @param msg [String] base64 or bytes
8
+ def initialize(msg)
9
+ @value = msg
10
+ @data = JSON.parse(msg)
11
+ end
12
+
13
+ # @return [String] bytes
14
+ def to_bytes
15
+ Base64.decode64(value)
16
+ end
17
+
18
+ # @return [String] base64
19
+ def to_s
20
+ @value.dup
21
+ end
22
+
23
+ def get_message(identity)
24
+ h = @data['recipients'][identity]
25
+ if h['mtype'] == 0
26
+ PreKeyMessage.new(h['ciphertext'])
27
+ else
28
+ Message.new(h['ciphertext'])
29
+ end
30
+ end
31
+
32
+ end
33
+
34
+ end
@@ -0,0 +1,8 @@
1
+ require 'base64'
2
+
3
+ module SelfCrypto
4
+
5
+ class GroupSession
6
+ end
7
+
8
+ end
@@ -0,0 +1,6 @@
1
+ module SelfCrypto
2
+
3
+ class Message < OlmMessage
4
+ end
5
+
6
+ end
@@ -0,0 +1,70 @@
1
+ module SelfCrypto
2
+
3
+ module OlmError
4
+
5
+ class SUCCESS < StandardError
6
+ end
7
+
8
+ class NOT_ENOUGH_RANDOM < StandardError
9
+ end
10
+
11
+ class OUTPUT_BUFFER_TOO_SMALL < StandardError
12
+ end
13
+
14
+ class BAD_MESSAGE_VERSION < StandardError
15
+ end
16
+
17
+ class BAD_MESSAGE_FORMAT < StandardError
18
+ end
19
+
20
+ class BAD_MESSAGE_MAC < StandardError
21
+ end
22
+
23
+ class BAD_MESSAGE_KEY_ID < StandardError
24
+ end
25
+
26
+ class INVALID_BASE64 < StandardError
27
+ end
28
+
29
+ class BAD_ACCOUNT_KEY < StandardError
30
+ end
31
+
32
+ class UNKNOWN_PICKLE_VERSION < StandardError
33
+ end
34
+
35
+ class CORRUPTED_PICKLE < StandardError
36
+ end
37
+
38
+ class BAD_SESSION_KEY < StandardError
39
+ end
40
+
41
+ class UNKNOWN_MESSAGE_INDEX < StandardError
42
+ end
43
+
44
+ class BAD_LEGACY_ACCOUNT_PICKLE < StandardError
45
+ end
46
+
47
+ class BAD_SIGNATURE < StandardError
48
+ end
49
+
50
+ class OLM_INPUT_BUFFER_TOO_SMALL < StandardError
51
+ end
52
+
53
+ class UnknownError < StandardError
54
+ end
55
+
56
+ def self.from_string(str)
57
+ begin
58
+ OlmError.const_get(str)
59
+ rescue NameError
60
+ UnknownError.new str
61
+ end
62
+ end
63
+
64
+ def self.raise_from_string(str)
65
+ raise from_string(str)
66
+ end
67
+
68
+ end
69
+
70
+ end
@@ -0,0 +1,25 @@
1
+ require 'base64'
2
+
3
+ module SelfCrypto
4
+
5
+ class OlmMessage
6
+
7
+ # @param msg [String] base64 or bytes
8
+ def initialize(msg)
9
+ raise "abstract class" if self.class == OlmMessage
10
+ @value = msg
11
+ end
12
+
13
+ # @return [String] bytes
14
+ def to_bytes
15
+ Base64.decode64(value)
16
+ end
17
+
18
+ # @return [String] base64
19
+ def to_s
20
+ @value.dup
21
+ end
22
+
23
+ end
24
+
25
+ end
@@ -0,0 +1,6 @@
1
+ module SelfCrypto
2
+
3
+ class PreKeyMessage < OlmMessage
4
+ end
5
+
6
+ end
@@ -0,0 +1,28 @@
1
+ require_relative './sas_data'
2
+
3
+ class SelfCrypto::SAS
4
+ METHODS = %i[decimal emoji]
5
+
6
+ def generate(method, info)
7
+ method = method.to_sym
8
+ raise ArgumentError, "Unknown SAS method: #{method}" unless METHODS.include? method
9
+
10
+ send method, info
11
+ end
12
+
13
+ protected
14
+
15
+ def decimal(info)
16
+ bytes = generate_bytes(5, info)
17
+ bits = bytes.unpack1('B39')
18
+ grouped = bits.chars.each_slice(13).map &:join
19
+ grouped.map {|s| s.to_i(2) + 1000}
20
+ end
21
+
22
+ def emoji(info)
23
+ bytes = generate_bytes(6, info)
24
+ bits = bytes.unpack1('B42')
25
+ grouped = bits.chars.each_slice(6).map &:join
26
+ grouped.map {|s| EMOJI_TABLE[s.to_i(2)]}.join
27
+ end
28
+ end
@@ -0,0 +1,71 @@
1
+ # encoding: UTF-8
2
+ # frozen_string_literal: true
3
+
4
+ class SelfCrypto::SAS
5
+ EMOJI_TABLE = {
6
+ 0 => '🐶',
7
+ 1 => '🐱',
8
+ 2 => '🦁',
9
+ 3 => '🐎',
10
+ 4 => '🦄',
11
+ 5 => '🐷',
12
+ 6 => '🐘',
13
+ 7 => '🐰',
14
+ 8 => '🐼',
15
+ 9 => '🐓',
16
+ 10 => '🐧',
17
+ 11 => '🐢',
18
+ 12 => '🐟',
19
+ 13 => '🐙',
20
+ 14 => '🦋',
21
+ 15 => '🌷',
22
+ 16 => '🌳',
23
+ 17 => '🌵',
24
+ 18 => '🍄',
25
+ 19 => '🌏',
26
+ 20 => '🌙',
27
+ 21 => '☁',
28
+ 22 => '🔥',
29
+ 23 => '🍌',
30
+ 24 => '🍎',
31
+ 25 => '🍓',
32
+ 26 => '🌽',
33
+ 27 => '🍕',
34
+ 28 => '🎂',
35
+ 29 => '❤',
36
+ 30 => '😀',
37
+ 31 => '🤖',
38
+ 32 => '🎩',
39
+ 33 => '👓',
40
+ 34 => '🔧',
41
+ 35 => '🎅',
42
+ 36 => '👍',
43
+ 37 => '☂',
44
+ 38 => '⌛',
45
+ 39 => '⏰',
46
+ 40 => '🎁',
47
+ 41 => '💡',
48
+ 42 => '📕',
49
+ 43 => '✏',
50
+ 44 => '📎',
51
+ 45 => '✂',
52
+ 46 => '🔒',
53
+ 47 => '🔑',
54
+ 48 => '🔨',
55
+ 49 => '☎',
56
+ 50 => '🏁',
57
+ 51 => '🚂',
58
+ 52 => '🚲',
59
+ 53 => '✈',
60
+ 54 => '🚀',
61
+ 55 => '🏆',
62
+ 56 => '⚽',
63
+ 57 => '🎸',
64
+ 58 => '🎺',
65
+ 59 => '🔔',
66
+ 60 => '⚓',
67
+ 61 => '🎧',
68
+ 62 => '📁',
69
+ 63 => '📌'
70
+ }
71
+ end
@@ -0,0 +1,16 @@
1
+ module SelfCrypto
2
+
3
+ class Session
4
+
5
+ # @param pickle [String] pickled state
6
+ # @param password [String] password used to encrypt pickled state
7
+ # @return [Session]
8
+ def self.from_pickle(pickle, password="")
9
+ Session.new(pickle, password)
10
+ end
11
+
12
+ alias_method :has_received?, :has_received_message
13
+
14
+ end
15
+
16
+ end
@@ -0,0 +1,7 @@
1
+ module SelfCrypto
2
+
3
+ module Util
4
+
5
+ end
6
+
7
+ end
@@ -0,0 +1,5 @@
1
+ module SelfCrypto
2
+
3
+ VERSION="0.0.1"
4
+
5
+ end
@@ -0,0 +1,62 @@
1
+ require 'minitest/autorun'
2
+ require 'self_crypto'
3
+
4
+ class TestExchange < Minitest::Test
5
+
6
+ include SelfCrypto
7
+
8
+ # Alice -> Bob
9
+ # Alice -> Bob
10
+ #
11
+ def test_bob_no_answer
12
+
13
+ alice = Account.new
14
+ bob = Account.new
15
+
16
+ # Alice wants to send a message to Bob
17
+ alice_msg = "hi bob"
18
+
19
+ # Bob generates a one-time-key
20
+ bob.gen_otk
21
+
22
+ # Alice must have Bob's identity and one-time-key to make a session
23
+ alice_session = alice.outbound_session(bob.ik['curve25519'], bob.otk['curve25519'].values.first)
24
+
25
+ # Bob marks all one-time-keys as published
26
+ bob.mark_otk
27
+
28
+ # Alice can encrypt
29
+ encrypted = alice_session.encrypt(alice_msg)
30
+ assert_instance_of PreKeyMessage, encrypted
31
+
32
+ # Bob can create a session from this first message
33
+ bob_session = bob.inbound_session(encrypted)
34
+
35
+ # Bob can now update his list of marked otk (since he knows one has been used)
36
+ bob.update_otk(bob_session)
37
+
38
+ # Bob can decrypt Alice's message
39
+ bob_msg = bob_session.decrypt(encrypted)
40
+
41
+ assert_equal alice_msg, bob_msg
42
+
43
+ # At this point Bob has received but Alice hasn't
44
+ assert bob_session.has_received?
45
+ refute alice_session.has_received?
46
+
47
+ ###
48
+
49
+ # Alice sends another message before reply from Bob
50
+ alice_msg = "BOB!"
51
+
52
+ encrypted = alice_session.encrypt(alice_msg)
53
+ assert_instance_of PreKeyMessage, encrypted
54
+
55
+ # Bob needs to check if this is the same session or a new one
56
+ same_session = bob_session.will_receive? encrypted
57
+
58
+ assert same_session
59
+
60
+ end
61
+
62
+ end
@@ -0,0 +1,60 @@
1
+ require 'minitest/autorun'
2
+ require 'self_crypto'
3
+
4
+ class TestExchange < Minitest::Test
5
+
6
+ include SelfCrypto
7
+
8
+ # Alice -> Bob
9
+ # Alice <- Bob
10
+ def test_exchange
11
+
12
+ alice = Account.new
13
+ bob = Account.new
14
+
15
+ # Alice wants to send a message to Bob
16
+ alice_msg = "hi bob"
17
+
18
+ # Bob generates a one-time-key
19
+ bob.gen_otk
20
+
21
+ # Alice must have Bob's identity and one-time-key to make a session
22
+ alice_session = alice.outbound_session(bob.ik['curve25519'], bob.otk['curve25519'].values.first)
23
+
24
+ # Bob marks all one-time-keys as published
25
+ bob.mark_otk
26
+
27
+ # Alice can encrypt
28
+ encrypted = alice_session.encrypt(alice_msg)
29
+ assert_instance_of PreKeyMessage, encrypted
30
+
31
+ # Bob can create a session from this first message
32
+ bob_session = bob.inbound_session(encrypted)
33
+
34
+ # Bob can now update his list of marked otk (since he knows one has been used)
35
+ bob.update_otk(bob_session)
36
+
37
+ # Bob can decrypt Alice's message
38
+ bob_msg = bob_session.decrypt(encrypted)
39
+
40
+ assert_equal alice_msg, bob_msg
41
+
42
+ # At this point Bob has received but Alice hasn't
43
+ assert bob_session.has_received?
44
+ refute alice_session.has_received?
45
+
46
+ ####
47
+
48
+ # Bob can send messages back to Alice
49
+ bob_msg = "hi alice"
50
+
51
+ encrypted = bob_session.encrypt(bob_msg)
52
+ assert_instance_of Message, encrypted
53
+
54
+ alice_msg = alice_session.decrypt(encrypted)
55
+
56
+ assert_equal alice_msg, bob_msg
57
+
58
+ end
59
+
60
+ end