noise-ruby 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +12 -0
- data/.rspec +2 -0
- data/.rubocop.yml +15 -0
- data/.ruby-version +1 -0
- data/.travis.yml +5 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +6 -0
- data/README.md +39 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/lib/noise.rb +19 -0
- data/lib/noise/connection.rb +96 -0
- data/lib/noise/exceptions.rb +10 -0
- data/lib/noise/exceptions/max_nonce_error.rb +8 -0
- data/lib/noise/exceptions/noise_handshake_error.rb +8 -0
- data/lib/noise/exceptions/noise_validation_error.rb +8 -0
- data/lib/noise/exceptions/protocol_name_error.rb +8 -0
- data/lib/noise/functions.rb +9 -0
- data/lib/noise/functions/cipher.rb +10 -0
- data/lib/noise/functions/cipher/aes_gcm.rb +21 -0
- data/lib/noise/functions/cipher/cha_cha_poly.rb +23 -0
- data/lib/noise/functions/dh.rb +11 -0
- data/lib/noise/functions/dh/dh25519.rb +34 -0
- data/lib/noise/functions/dh/dh448.rb +25 -0
- data/lib/noise/functions/dh/secp256k1.rb +28 -0
- data/lib/noise/functions/hash.rb +32 -0
- data/lib/noise/functions/hash/blake2b.rb +23 -0
- data/lib/noise/functions/hash/blake2s.rb +23 -0
- data/lib/noise/functions/hash/sha256.rb +23 -0
- data/lib/noise/functions/hash/sha512.rb +23 -0
- data/lib/noise/pattern.rb +223 -0
- data/lib/noise/protocol.rb +107 -0
- data/lib/noise/state.rb +9 -0
- data/lib/noise/state/cipher_state.rb +54 -0
- data/lib/noise/state/handshake_state.rb +141 -0
- data/lib/noise/state/symmetric_state.rb +86 -0
- data/lib/noise/utils/hash.rb +9 -0
- data/lib/noise/utils/string.rb +10 -0
- data/lib/noise/version.rb +5 -0
- data/noise.gemspec +29 -0
- metadata +168 -0
@@ -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))
|