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