noise-ruby 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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))