noise-ruby 0.1.0

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.
Files changed (43) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +12 -0
  3. data/.rspec +2 -0
  4. data/.rubocop.yml +15 -0
  5. data/.ruby-version +1 -0
  6. data/.travis.yml +5 -0
  7. data/CODE_OF_CONDUCT.md +74 -0
  8. data/Gemfile +6 -0
  9. data/README.md +39 -0
  10. data/Rakefile +6 -0
  11. data/bin/console +14 -0
  12. data/bin/setup +8 -0
  13. data/lib/noise.rb +19 -0
  14. data/lib/noise/connection.rb +96 -0
  15. data/lib/noise/exceptions.rb +10 -0
  16. data/lib/noise/exceptions/max_nonce_error.rb +8 -0
  17. data/lib/noise/exceptions/noise_handshake_error.rb +8 -0
  18. data/lib/noise/exceptions/noise_validation_error.rb +8 -0
  19. data/lib/noise/exceptions/protocol_name_error.rb +8 -0
  20. data/lib/noise/functions.rb +9 -0
  21. data/lib/noise/functions/cipher.rb +10 -0
  22. data/lib/noise/functions/cipher/aes_gcm.rb +21 -0
  23. data/lib/noise/functions/cipher/cha_cha_poly.rb +23 -0
  24. data/lib/noise/functions/dh.rb +11 -0
  25. data/lib/noise/functions/dh/dh25519.rb +34 -0
  26. data/lib/noise/functions/dh/dh448.rb +25 -0
  27. data/lib/noise/functions/dh/secp256k1.rb +28 -0
  28. data/lib/noise/functions/hash.rb +32 -0
  29. data/lib/noise/functions/hash/blake2b.rb +23 -0
  30. data/lib/noise/functions/hash/blake2s.rb +23 -0
  31. data/lib/noise/functions/hash/sha256.rb +23 -0
  32. data/lib/noise/functions/hash/sha512.rb +23 -0
  33. data/lib/noise/pattern.rb +223 -0
  34. data/lib/noise/protocol.rb +107 -0
  35. data/lib/noise/state.rb +9 -0
  36. data/lib/noise/state/cipher_state.rb +54 -0
  37. data/lib/noise/state/handshake_state.rb +141 -0
  38. data/lib/noise/state/symmetric_state.rb +86 -0
  39. data/lib/noise/utils/hash.rb +9 -0
  40. data/lib/noise/utils/string.rb +10 -0
  41. data/lib/noise/version.rb +5 -0
  42. data/noise.gemspec +29 -0
  43. metadata +168 -0
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Noise
4
+ module Functions
5
+ module DH
6
+ autoload :DH448, 'noise/functions/dh/dh448'
7
+ autoload :DH25519, 'noise/functions/dh/dh25519'
8
+ autoload :Secp256k1, 'noise/functions/dh/secp256k1'
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,34 @@
1
+ module Noise
2
+ module Functions
3
+ module DH
4
+ class DH25519
5
+ DHLEN = 32
6
+ def generate_keypair
7
+ private_key = RbNaCl::Signatures::Ed25519::SigningKey.generate
8
+ public_key = private_key.verify_key
9
+ [private_key.to_bytes, public_key.to_bytes]
10
+ end
11
+
12
+ def dh(private_key, public_key)
13
+ point = RbNaCl::GroupElement.new(public_key).mult(private_key)
14
+ point.to_bytes
15
+ end
16
+
17
+ def dhlen
18
+ DHLEN
19
+ end
20
+
21
+ def self.from_private(private_key)
22
+ private_key = RbNaCl::GroupElements::Curve25519.new(private_key)
23
+ public_key = RbNaCl::GroupElements::Curve25519.base.mult(private_key)
24
+ [private_key.to_bytes, public_key.to_bytes]
25
+ end
26
+
27
+ def self.from_public(public_key)
28
+ public_key = RbNaCl::Signatures::Ed25519::VerifyKey.new(public_key)
29
+ [nil, public_key.to_bytes]
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,25 @@
1
+ module Noise
2
+ module Functions
3
+ module DH
4
+ class DH448
5
+ DHLEN = 56
6
+ def generate_keypair
7
+ throw NotImplementedError
8
+ end
9
+
10
+ def dh(key_pair, public_key)
11
+ throw NotImplementedError
12
+ end
13
+
14
+ def dhlen
15
+ DHLEN
16
+ end
17
+
18
+ def self.from_private(private_key)
19
+ end
20
+ def self.from_public(private_key)
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Noise
4
+ module Functions
5
+ module DH
6
+ class Secp256k1
7
+ def generate_keypair
8
+ group = ECDSA::Group::Secp256k1
9
+ private_key = 1 + SecureRandom.random_number(group.order - 1)
10
+ public_key = group.generator.multiply_by_scalar(private_key)
11
+ [private_key, public_key]
12
+ end
13
+
14
+ def dh(private_key, public_key)
15
+ public_key.multiply_by_scalar(private_key)
16
+ end
17
+
18
+ def dhlen
19
+ 64
20
+ end
21
+ def self.from_private(private_key)
22
+ end
23
+ def self.from_public(private_key)
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Noise
4
+ module Functions
5
+ module Hash
6
+ autoload :Blake2b, 'noise/functions/hash/blake2b'
7
+ autoload :Blake2s, 'noise/functions/hash/blake2s'
8
+ autoload :Sha256, 'noise/functions/hash/sha256'
9
+ autoload :Sha512, 'noise/functions/hash/sha512'
10
+
11
+ def self.hmac_hash(key, data, digest)
12
+ # TODO: support for blake2b, blake2s
13
+ OpenSSL::HMAC.digest(OpenSSL::Digest.new(digest), key, data)
14
+ end
15
+
16
+ def self.create_hkdf_fn(digest)
17
+ ->(chaining_key, input_key_material, num_output) {
18
+ hkdf(chaining_key, input_key_material, num_output, digest)
19
+ }
20
+ end
21
+
22
+ def self.hkdf(chaining_key, input_key_material, num_outputs, digest)
23
+ temp_key = hmac_hash(chaining_key, input_key_material, digest)
24
+ output1 = hmac_hash(temp_key, "\x01", digest)
25
+ output2 = hmac_hash(temp_key, output1 + "\x02", digest)
26
+ return [output1, output2] if num_outputs == 2
27
+ output3 = hmac_hash(temp_key, output2 + "\x03", digest)
28
+ [output1, output2, output3]
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Noise
4
+ module Functions
5
+ module Hash
6
+ class Blake2b
7
+ HASHLEN = 64
8
+ BLOCKLEN = 128
9
+ def hash(data)
10
+ RbNaCl::Hash.blake2b(data)
11
+ end
12
+
13
+ def hashlen
14
+ HASHLEN
15
+ end
16
+
17
+ def blocklen
18
+ BLOCKLEN
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Noise
4
+ module Functions
5
+ module Hash
6
+ class Blake2s
7
+ HASHLEN = 32
8
+ BLOCKLEN = 64
9
+ def hash(data)
10
+ throw NotImplementedError
11
+ end
12
+
13
+ def hashlen
14
+ HASHLEN
15
+ end
16
+
17
+ def blocklen
18
+ BLOCKLEN
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Noise
4
+ module Functions
5
+ module Hash
6
+ class Sha256
7
+ HASHLEN = 32
8
+ BLOCKLEN = 64
9
+ def hash(data)
10
+ RbNaCl::Hash.sha256(data)
11
+ end
12
+
13
+ def hashlen
14
+ HASHLEN
15
+ end
16
+
17
+ def blocklen
18
+ BLOCKLEN
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Noise
4
+ module Functions
5
+ module Hash
6
+ class Sha512
7
+ HASHLEN = 64
8
+ BLOCKLEN = 128
9
+ def hash(data)
10
+ RbNaCl::Hash.sha512(data)
11
+ end
12
+
13
+ def hashlen
14
+ HASHLEN
15
+ end
16
+
17
+ def blocklen
18
+ BLOCKLEN
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,223 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Noise
4
+ module Token
5
+ E = 'e'
6
+ S = 's'
7
+ EE = 'ee'
8
+ ES = 'es'
9
+ SE = 'se'
10
+ SS = 'ss'
11
+ PSK = 'psk'
12
+ end
13
+
14
+ class Pattern
15
+ attr_reader :one_way, :tokens
16
+
17
+ def self.create(name)
18
+ class_name = "Noise::Pattern#{name}"
19
+ klass = Object.const_get(class_name)
20
+ klass.new
21
+ end
22
+
23
+ def initialize
24
+ @pre_messages = [[], []]
25
+ @tokens = []
26
+ @name = ''
27
+ @one_way = false
28
+ @psk_count = 0
29
+ end
30
+
31
+ # initiator [Boolean]
32
+ def required_keypairs(initiator)
33
+ required = []
34
+ if initiator
35
+ required << :s if ['K', 'X', 'I'].include?(@name[0])
36
+ required << :rs if @one_way || @name[1] == 'K'
37
+ else
38
+ required << :rs if @name[0] == 'K'
39
+ required << :s if @one_way || ['K', 'X'].include?(@name[1])
40
+ end
41
+ required
42
+ end
43
+
44
+ def initiator_pre_messages
45
+ @pre_messages[0].dup
46
+ end
47
+
48
+ def responder_pre_messages
49
+ @pre_messages[1].dup
50
+ end
51
+ end
52
+
53
+ class OneWayPattern < Pattern
54
+ def initialize
55
+ super
56
+ @one_way = true
57
+ end
58
+ end
59
+
60
+ class PatternN < OneWayPattern
61
+ def initialize
62
+ super
63
+ @name = 'N'
64
+ @pre_messages = [[], [Token::S]]
65
+ @tokens = [[Token::E, Token::ES]]
66
+ end
67
+ end
68
+
69
+ class PatternK < OneWayPattern
70
+ def initialize
71
+ super
72
+ @name = 'K'
73
+ @pre_messages = [[Token::S], [Token::S]]
74
+ @tokens = [[Token::E, Token::ES, Token::SS]]
75
+ end
76
+ end
77
+
78
+ class PatternX < OneWayPattern
79
+ def initialize
80
+ super
81
+ @name = 'X'
82
+ @pre_messages = [[], [Token::S]]
83
+ @tokens = [[Token::E, Token::ES, Token::S, Token::SS]]
84
+ end
85
+ end
86
+
87
+ class PatternNN < Pattern
88
+ def initialize
89
+ super
90
+ @name = 'NN'
91
+ @pre_messages = []
92
+ @tokens = [[Token::E], [Token::E, Token::EE]]
93
+ end
94
+ end
95
+
96
+ class PatternKN < Pattern
97
+ def initialize
98
+ super
99
+ @name = 'KN'
100
+ @pre_messages = [[Token::S], []]
101
+ @tokens = [[Token::E], [Token::E, Token::EE, Token::SE]]
102
+ end
103
+ end
104
+
105
+ class PatternNK < Pattern
106
+ def initialize
107
+ super
108
+ @name = 'NK'
109
+ @pre_messages = [[], [Token::S]]
110
+ @tokens = [[Token::E, Token::ES], [Token::E, Token::EE]]
111
+ end
112
+ end
113
+
114
+ class PatternKK < Pattern
115
+ def initialize
116
+ super
117
+ @name = 'KK'
118
+ @pre_messages = [[Token::S], [Token::S]]
119
+ @tokens = [[Token::E, Token::ES, Token::SS], [Token::E, Token::EE, Token::SE]]
120
+ end
121
+ end
122
+
123
+ class PatternNX < Pattern
124
+ def initialize
125
+ super
126
+ @name = 'NX'
127
+ @tokens = [[Token::E], [Token::E, Token::EE, Token::S, Token::ES]]
128
+ end
129
+ end
130
+
131
+ class PatternKX < Pattern
132
+ def initialize
133
+ super
134
+ @name = 'KX'
135
+ @pre_messages = [[Token::S], []]
136
+ @tokens = [[Token::E], [Token::E, Token::EE, Token::SE, Token::S, Token::ES]]
137
+ end
138
+ end
139
+
140
+ class PatternXN < Pattern
141
+ def initialize
142
+ super
143
+ @name = 'XN'
144
+ @tokens = [[Token::E], [Token::E, Token::EE], [Token::S, Token::SE]]
145
+ end
146
+ end
147
+
148
+ class PatternIN < Pattern
149
+ def initialize
150
+ super
151
+ @name = 'IN'
152
+ @tokens = [[Token::E, Token::S], [Token::E, Token::EE, Token::SE]]
153
+ end
154
+ end
155
+
156
+ class PatternXK < Pattern
157
+ def initialize
158
+ super
159
+ @name = 'XK'
160
+ @pre_messages = [[], [Token::S]]
161
+ @tokens = [[Token::E, Token::ES], [Token::E, Token::EE], [Token::S, Token::SE]]
162
+ end
163
+ end
164
+
165
+ class PatternIK < Pattern
166
+ def initialize
167
+ super
168
+ @name = 'IK'
169
+ @pre_messages = [[], [Token::S]]
170
+ @tokens = [[Token::E, Token::ES, Token::S, Token::SS], [Token::E, Token::EE, Token::SE]]
171
+ end
172
+ end
173
+
174
+ class PatternXX < Pattern
175
+ def initialize
176
+ super
177
+ @name = 'XX'
178
+ @tokens = [[Token::E], [Token::E, Token::EE, Token::S, Token::ES], [Token::S, Token::SE]]
179
+ end
180
+ end
181
+
182
+ class PatternIX < Pattern
183
+ def initialize
184
+ super
185
+ @name = 'IX'
186
+ @tokens = [[Token::E, Token::S], [Token::E, Token::EE, Token::SE, Token::S, Token::ES]]
187
+ end
188
+ end
189
+ end
190
+ #
191
+ # def has_pre_messages(self):
192
+ # return any(map(lambda x: len(x) > 0, self.pre_messages))
193
+ #
194
+ # def get_initiator_pre_messages(self) -> list:
195
+ # return self.pre_messages[0].copy()
196
+ #
197
+ # def get_responder_pre_messages(self) -> list:
198
+ # return self.pre_messages[1].copy()
199
+ #
200
+ # def apply_pattern_modifiers(self, modifiers: List[str]) -> None:
201
+ # # Applies given pattern modifiers to self.tokens of the Pattern instance.
202
+ # for modifier in modifiers:
203
+ # if modifier.startswith('psk'):
204
+ # try:
205
+ # index = int(modifier.replace('psk', '', 1))
206
+ # except ValueError:
207
+ # raise ValueError('Improper psk modifier {}'.format(modifier))
208
+ #
209
+ # if index // 2 > len(self.tokens):
210
+ # raise ValueError('Modifier {} cannot be applied - pattern has not enough messages'.format(modifier))
211
+ #
212
+ # # Add TOKEN_PSK in the correct place in the correct message
213
+ # if index == 0: # if 0, insert at the beginning of first message
214
+ # self.tokens[0].insert(0, TOKEN_PSK)
215
+ # else: # if bigger than zero, append at the end of first, second etc.
216
+ # self.tokens[index - 1].append(TOKEN_PSK)
217
+ # self.psk_count += 1
218
+ #
219
+ # elif modifier == 'fallback':
220
+ # raise NotImplementedError # TODO implement
221
+ #
222
+ # else:
223
+ # raise ValueError('Unknown pattern modifier {}'.format(modifier))