self_crypto 0.0.1

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.
@@ -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