noise-ruby 0.2.0 → 0.3.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c16d9319142dc02bcf5e1524c141aaa99a923942
4
- data.tar.gz: b926ad7e994d5387fe48fab254b8f5c0ccb124fe
3
+ metadata.gz: b311addeddc717377b96beaecbe50300759ea5d6
4
+ data.tar.gz: 2979832b1b009280a5998de859bead7cae613b00
5
5
  SHA512:
6
- metadata.gz: d1cafc8d1343cba342d73451a6817001faf4e3298fbc239e6e4efb8d1454087f0826602a73c3c503c9d498eafcfe0aca79fd91bfe3eb2ba401bec13d1b12e9fd
7
- data.tar.gz: 40dfacc90d63bb5807dc8ce7b13d01beac299f2a62ef405f98971bf5381e6ff6bf9a2c637eedab26a9d451c95c1715c19991c8ed5014aee0ce28cc426944c3f0
6
+ metadata.gz: 176002bdc3b0763255c0c2679485e38aa9d9795477dd7d789f40cebaa2154ae877fcec6593ab0409f095447cfd49f565aac8216053742bc3dce3572fc9a67253
7
+ data.tar.gz: ac57838f8ab1e3a0f8910dae97cb8d28a8b9a83bec9b6e433316c062c1f2c09465c149ace734db23d0da6360bb5c1f12545f3ad858f72a87cca045ae89554ad9
data/README.md CHANGED
@@ -2,6 +2,12 @@
2
2
 
3
3
  A Ruby implementation of the Noise Protocol framework(http://noiseprotocol.org/).
4
4
 
5
+ ## Secp256k1
6
+
7
+ Secp256k1 cipher function is supported.
8
+ This is required for Lightning Network, layer-2 protocol for bitcoin.
9
+ see
10
+
5
11
  ## Future Works
6
12
 
7
13
  The followings are not supported yet.
@@ -10,7 +16,6 @@ The followings are not supported yet.
10
16
  - Curve448
11
17
  - Hash Functions
12
18
  - Blake2s
13
- - PSK Mode
14
19
 
15
20
  ## Installation
16
21
 
@@ -22,8 +22,12 @@ module Noise
22
22
  @handshake_started = false
23
23
  @handshake_finished = false
24
24
  @fn = nil
25
- @write_message_proc = ->(payload) { write_message(payload) }
26
- @read_message_proc = ->(payload) { read_message(payload) }
25
+ @write_message_proc = lambda {|payload| write_message(payload)}
26
+ @read_message_proc = lambda {|payload| read_message(payload)}
27
+ end
28
+
29
+ def psks=(psks)
30
+ @protocol.psks = psks
27
31
  end
28
32
 
29
33
  def prologue=(prologue)
@@ -6,5 +6,7 @@ module Noise
6
6
  autoload :ProtocolNameError, 'noise/exceptions/protocol_name_error'
7
7
  autoload :NoiseHandshakeError, 'noise/exceptions/noise_handshake_error'
8
8
  autoload :NoiseValidationError, 'noise/exceptions/noise_validation_error'
9
+ autoload :NoisePSKError, 'noise/exceptions/noise_psk_error'
10
+ autoload :PSKValueError, 'noise/exceptions/psk_value_error'
9
11
  end
10
12
  end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Noise
4
+ module Exceptions
5
+ class NoisePSKError < RuntimeError
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Noise
4
+ module Exceptions
5
+ class PSKValueError < RuntimeError
6
+ end
7
+ end
8
+ end
@@ -3,8 +3,8 @@
3
3
  module Noise
4
4
  module Functions
5
5
  module DH
6
- autoload :DH448, 'noise/functions/dh/dh448'
7
- autoload :DH25519, 'noise/functions/dh/dh25519'
6
+ autoload :ED448, 'noise/functions/dh/ed448'
7
+ autoload :ED25519, 'noise/functions/dh/ed25519'
8
8
  autoload :Secp256k1, 'noise/functions/dh/secp256k1'
9
9
  end
10
10
  end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Noise
4
+ module Functions
5
+ module DH
6
+ class ED25519
7
+ DHLEN = 32
8
+ def generate_keypair
9
+ private_key = 1 + SecureRandom.random_number(RbNaCl::GroupElement::STANDARD_GROUP_ORDER - 1)
10
+ scalar_as_string = ECDSA::Format::IntegerOctetString.encode(private_key, 32)
11
+ public_key = RbNaCl::GroupElements::Curve25519.base.mult(scalar_as_string)
12
+ [ECDSA::Format::IntegerOctetString.encode(private_key, 32), public_key.to_bytes]
13
+ end
14
+
15
+ def dh(private_key, public_key)
16
+ RbNaCl::GroupElement.new(public_key).mult(private_key).to_bytes
17
+ end
18
+
19
+ def dhlen
20
+ DHLEN
21
+ end
22
+
23
+ def self.from_private(private_key)
24
+ public_key = RbNaCl::GroupElements::Curve25519.base.mult(private_key)
25
+ [private_key, public_key.to_bytes]
26
+ end
27
+
28
+ def self.from_public(public_key)
29
+ [nil, public_key]
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -1,7 +1,7 @@
1
1
  module Noise
2
2
  module Functions
3
3
  module DH
4
- class DH448
4
+ class ED448
5
5
  DHLEN = 56
6
6
  def generate_keypair
7
7
  throw NotImplementedError
@@ -8,19 +8,33 @@ module Noise
8
8
  group = ECDSA::Group::Secp256k1
9
9
  private_key = 1 + SecureRandom.random_number(group.order - 1)
10
10
  public_key = group.generator.multiply_by_scalar(private_key)
11
- [private_key, public_key]
11
+ [
12
+ ECDSA::Format::IntegerOctetString.encode(private_key, 32),
13
+ ECDSA::Format::PointOctetString.encode(public_key, compression: true)
14
+ ]
12
15
  end
13
16
 
14
17
  def dh(private_key, public_key)
15
- public_key.multiply_by_scalar(private_key)
18
+ group = ECDSA::Group::Secp256k1
19
+ point = ECDSA::Format::PointOctetString.decode(public_key, group)
20
+ scalar = ECDSA::Format::IntegerOctetString.decode(private_key)
21
+ point = point.multiply_by_scalar(scalar)
22
+ ECDSA::Format::PointOctetString.encode(point, compression: true)
16
23
  end
17
24
 
18
25
  def dhlen
19
26
  64
20
27
  end
28
+
21
29
  def self.from_private(private_key)
30
+ group = ECDSA::Group::Secp256k1
31
+ scalar = ECDSA::Format::IntegerOctetString.decode(private_key)
32
+ point = group.generator.multiply_by_scalar(scalar)
33
+ [private_key, ECDSA::Format::PointOctetString.encode(point, compression: true)]
22
34
  end
23
- def self.from_public(private_key)
35
+
36
+ def self.from_public(public_key)
37
+ [nil, public_key]
24
38
  end
25
39
  end
26
40
  end
data/lib/noise/pattern.rb CHANGED
@@ -12,20 +12,49 @@ module Noise
12
12
  end
13
13
 
14
14
  class Pattern
15
- attr_reader :one_way, :tokens
15
+ attr_reader :one_way, :tokens, :modifiers
16
16
 
17
17
  def self.create(name)
18
- class_name = "Noise::Pattern#{name}"
18
+ pattern_set = name.scan(/([A-Z]+)([^A-Z]*)/)&.first
19
+ pattern = pattern_set&.first
20
+ modifiers = pattern_set[1].split('+')
21
+ class_name = "Noise::Pattern#{pattern}"
19
22
  klass = Object.const_get(class_name)
20
- klass.new
23
+ klass.new(modifiers)
21
24
  end
22
25
 
23
- def initialize
26
+ def initialize(modifiers)
24
27
  @pre_messages = [[], []]
25
28
  @tokens = []
26
29
  @name = ''
27
30
  @one_way = false
28
31
  @psk_count = 0
32
+ @modifiers = modifiers
33
+ end
34
+
35
+ def apply_pattern_modifiers
36
+ @modifiers.each do |modifier|
37
+ if modifier.start_with?('psk')
38
+ begin
39
+ index = modifier.gsub(/psk/, '').to_i
40
+ rescue
41
+ raise Noise::Exceptions::PSKValueError
42
+ end
43
+ # if index * 2 > @tokens.size
44
+ # raise PSKValueError
45
+ # end
46
+ if index == 0
47
+ @tokens[0].insert(0, Token::PSK)
48
+ else
49
+ @tokens[index - 1] << Token::PSK
50
+ end
51
+ @psk_count += 1
52
+ elsif modifier == 'fallback'
53
+ raise NotImplementedError
54
+ else
55
+ raise Noise::Exceptions::PSKValueError
56
+ end
57
+ end
29
58
  end
30
59
 
31
60
  # initiator [Boolean]
@@ -57,15 +86,15 @@ module Noise
57
86
  end
58
87
 
59
88
  class OneWayPattern < Pattern
60
- def initialize
61
- super
89
+ def initialize(modifiers)
90
+ super(modifiers)
62
91
  @one_way = true
63
92
  end
64
93
  end
65
94
 
66
95
  class PatternN < OneWayPattern
67
- def initialize
68
- super
96
+ def initialize(modifiers)
97
+ super(modifiers)
69
98
  @name = 'N'
70
99
  @pre_messages = [[], [Token::S]]
71
100
  @tokens = [[Token::E, Token::ES]]
@@ -73,8 +102,8 @@ module Noise
73
102
  end
74
103
 
75
104
  class PatternK < OneWayPattern
76
- def initialize
77
- super
105
+ def initialize(modifiers)
106
+ super(modifiers)
78
107
  @name = 'K'
79
108
  @pre_messages = [[Token::S], [Token::S]]
80
109
  @tokens = [[Token::E, Token::ES, Token::SS]]
@@ -82,8 +111,8 @@ module Noise
82
111
  end
83
112
 
84
113
  class PatternX < OneWayPattern
85
- def initialize
86
- super
114
+ def initialize(modifiers)
115
+ super(modifiers)
87
116
  @name = 'X'
88
117
  @pre_messages = [[], [Token::S]]
89
118
  @tokens = [[Token::E, Token::ES, Token::S, Token::SS]]
@@ -91,8 +120,8 @@ module Noise
91
120
  end
92
121
 
93
122
  class PatternNN < Pattern
94
- def initialize
95
- super
123
+ def initialize(modifiers)
124
+ super(modifiers)
96
125
  @name = 'NN'
97
126
  @pre_messages = []
98
127
  @tokens = [[Token::E], [Token::E, Token::EE]]
@@ -100,8 +129,8 @@ module Noise
100
129
  end
101
130
 
102
131
  class PatternKN < Pattern
103
- def initialize
104
- super
132
+ def initialize(modifiers)
133
+ super(modifiers)
105
134
  @name = 'KN'
106
135
  @pre_messages = [[Token::S], []]
107
136
  @tokens = [[Token::E], [Token::E, Token::EE, Token::SE]]
@@ -109,8 +138,8 @@ module Noise
109
138
  end
110
139
 
111
140
  class PatternNK < Pattern
112
- def initialize
113
- super
141
+ def initialize(modifiers)
142
+ super(modifiers)
114
143
  @name = 'NK'
115
144
  @pre_messages = [[], [Token::S]]
116
145
  @tokens = [[Token::E, Token::ES], [Token::E, Token::EE]]
@@ -118,8 +147,8 @@ module Noise
118
147
  end
119
148
 
120
149
  class PatternKK < Pattern
121
- def initialize
122
- super
150
+ def initialize(modifiers)
151
+ super(modifiers)
123
152
  @name = 'KK'
124
153
  @pre_messages = [[Token::S], [Token::S]]
125
154
  @tokens = [[Token::E, Token::ES, Token::SS], [Token::E, Token::EE, Token::SE]]
@@ -127,16 +156,16 @@ module Noise
127
156
  end
128
157
 
129
158
  class PatternNX < Pattern
130
- def initialize
131
- super
159
+ def initialize(modifiers)
160
+ super(modifiers)
132
161
  @name = 'NX'
133
162
  @tokens = [[Token::E], [Token::E, Token::EE, Token::S, Token::ES]]
134
163
  end
135
164
  end
136
165
 
137
166
  class PatternKX < Pattern
138
- def initialize
139
- super
167
+ def initialize(modifiers)
168
+ super(modifiers)
140
169
  @name = 'KX'
141
170
  @pre_messages = [[Token::S], []]
142
171
  @tokens = [[Token::E], [Token::E, Token::EE, Token::SE, Token::S, Token::ES]]
@@ -144,24 +173,24 @@ module Noise
144
173
  end
145
174
 
146
175
  class PatternXN < Pattern
147
- def initialize
148
- super
176
+ def initialize(modifiers)
177
+ super(modifiers)
149
178
  @name = 'XN'
150
179
  @tokens = [[Token::E], [Token::E, Token::EE], [Token::S, Token::SE]]
151
180
  end
152
181
  end
153
182
 
154
183
  class PatternIN < Pattern
155
- def initialize
156
- super
184
+ def initialize(modifiers)
185
+ super(modifiers)
157
186
  @name = 'IN'
158
187
  @tokens = [[Token::E, Token::S], [Token::E, Token::EE, Token::SE]]
159
188
  end
160
189
  end
161
190
 
162
191
  class PatternXK < Pattern
163
- def initialize
164
- super
192
+ def initialize(modifiers)
193
+ super(modifiers)
165
194
  @name = 'XK'
166
195
  @pre_messages = [[], [Token::S]]
167
196
  @tokens = [[Token::E, Token::ES], [Token::E, Token::EE], [Token::S, Token::SE]]
@@ -169,8 +198,8 @@ module Noise
169
198
  end
170
199
 
171
200
  class PatternIK < Pattern
172
- def initialize
173
- super
201
+ def initialize(modifiers)
202
+ super(modifiers)
174
203
  @name = 'IK'
175
204
  @pre_messages = [[], [Token::S]]
176
205
  @tokens = [[Token::E, Token::ES, Token::S, Token::SS], [Token::E, Token::EE, Token::SE]]
@@ -178,16 +207,16 @@ module Noise
178
207
  end
179
208
 
180
209
  class PatternXX < Pattern
181
- def initialize
182
- super
210
+ def initialize(modifiers)
211
+ super(modifiers)
183
212
  @name = 'XX'
184
213
  @tokens = [[Token::E], [Token::E, Token::EE, Token::S, Token::ES], [Token::S, Token::SE]]
185
214
  end
186
215
  end
187
216
 
188
217
  class PatternIX < Pattern
189
- def initialize
190
- super
218
+ def initialize(modifiers)
219
+ super(modifiers)
191
220
  @name = 'IX'
192
221
  @tokens = [[Token::E, Token::S], [Token::E, Token::EE, Token::SE, Token::S, Token::ES]]
193
222
  end
@@ -2,11 +2,13 @@
2
2
 
3
3
  module Noise
4
4
  class Protocol
5
- attr_accessor :prologue, :initiator, :cipher_state_encrypt, :cipher_state_decrypt
5
+ attr_accessor :prologue, :initiator
6
+ attr_accessor :cipher_state_encrypt, :cipher_state_decrypt
7
+ attr_accessor :cipher_state_handshake
8
+ attr_accessor :psks
6
9
  attr_reader :name, :cipher_fn, :hash_fn, :dh_fn, :hkdf_fn, :pattern
7
10
  attr_reader :handshake_state, :keypairs, :keypair_fn
8
11
  attr_reader :handshake_hash
9
- attr_accessor :cipher_state_handshake
10
12
 
11
13
  CIPHER = {
12
14
  'AESGCM': Noise::Functions::Cipher::AesGcm,
@@ -14,8 +16,8 @@ module Noise
14
16
  }.stringify_keys.freeze
15
17
 
16
18
  DH = {
17
- '25519': Noise::Functions::DH::DH25519,
18
- '448': Noise::Functions::DH::DH448
19
+ '25519': Noise::Functions::DH::ED25519,
20
+ '448': Noise::Functions::DH::ED448
19
21
  }.stringify_keys.freeze
20
22
 
21
23
  HASH = {
@@ -33,12 +35,17 @@ module Noise
33
35
 
34
36
  def initialize(name, pattern_name, cipher_name, hash_name, dh_name)
35
37
  @name = name
36
- @pattern = Noise::Pattern.create(pattern_name[0..1])
38
+ @pattern = Noise::Pattern.create(pattern_name)
37
39
  @keypairs = { s: nil, e: nil, rs: nil, re: nil }
38
40
  @cipher_fn = CIPHER[cipher_name]&.new
39
41
  @hash_fn = HASH[hash_name]&.new
40
42
  @dh_fn = DH[dh_name]&.new
41
43
  @hkdf_fn = Noise::Functions::Hash.create_hkdf_fn(hash_name)
44
+ @psks = nil
45
+ @is_psk_handshake = @pattern.modifiers.any? { |m| m.start_with?('psk') }
46
+
47
+ @pattern.apply_pattern_modifiers
48
+
42
49
  raise Noise::Exceptions::ProtocolNameError unless @cipher_fn && @hash_fn && @dh_fn
43
50
  end
44
51
 
@@ -63,14 +70,16 @@ module Noise
63
70
  end
64
71
 
65
72
  def validate
66
- # TODO : support PSK
67
- # if @psk_handshake
68
- # if @psks.inclueds? {|psk| psk.size != 32}
69
- # raise NoisePSKError
70
- # else
71
- # raise NoisePSKError
72
- # end
73
- # end
73
+ if psk_handshake?
74
+ if @psks.any? {|psk| psk.bytesize != 32}
75
+ raise NoisePSKError # Invalid psk length! Has to be 32 bytes long
76
+ end
77
+ if @pattern.psk_count != @psks.count
78
+ # Bad number of PSKs provided to this protocol! {} are required,
79
+ # given {}'.format(self.pattern.psk_count, len(self.psks)))
80
+ raise NoisePSKError
81
+ end
82
+ end
74
83
 
75
84
  # You need to set role with NoiseConnection.set_as_initiator
76
85
  # or NoiseConnection.set_as_responder
@@ -103,5 +112,9 @@ module Noise
103
112
  )
104
113
  @symmetric_state = @handshake_state.symmetric_state
105
114
  end
115
+
116
+ def psk_handshake?
117
+ @is_psk_handshake
118
+ end
106
119
  end
107
120
  end
@@ -63,6 +63,7 @@ module Noise
63
63
  @e = dh_fn.generate_keypair if @e.compact.empty?
64
64
  message_buffer << @e[1]
65
65
  @symmetric_state.mix_hash(@e[1])
66
+ @symmetric_state.mix_key(@e[1]) if @protocol.psk_handshake?
66
67
  next
67
68
  when 's'
68
69
  message_buffer << @symmetric_state.encrypt_and_hash(@s[1])
@@ -87,6 +88,9 @@ module Noise
87
88
  when 'ss'
88
89
  @symmetric_state.mix_key(dh_fn.dh(@s[0], @rs[1]))
89
90
  next
91
+ when 'psk'
92
+ @symmetric_state.mix_key_and_hash(@protocol.psks.shift)
93
+ next
90
94
  end
91
95
  end
92
96
  message_buffer << @symmetric_state.encrypt_and_hash(payload)
@@ -103,6 +107,7 @@ module Noise
103
107
  @re = @protocol.dh_fn.class.from_public(message[0...len]) if @re.compact.empty?
104
108
  message = message[len..-1]
105
109
  @symmetric_state.mix_hash(@re[1])
110
+ @symmetric_state.mix_key(@re[1]) if @protocol.psk_handshake?
106
111
  next
107
112
  when 's'
108
113
  offset = @protocol.cipher_state_handshake.key? ? 16 : 0
@@ -130,6 +135,9 @@ module Noise
130
135
  when 'ss'
131
136
  @symmetric_state.mix_key(dh_fn.dh(@s[0], @rs[1]))
132
137
  next
138
+ when 'psk'
139
+ @symmetric_state.mix_key_and_hash(@protocol.psks.shift)
140
+ next
133
141
  end
134
142
  end
135
143
  payload_buffer << @symmetric_state.decrypt_and_hash(message)
data/lib/noise/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Noise
4
- VERSION = '0.2.0'
4
+ VERSION = '0.3.0'
5
5
  end
data/noise.gemspec CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  lib = File.expand_path('../lib', __FILE__)
2
4
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
5
  require 'noise/version'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: noise-ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Hajime Yamaguchi
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2017-12-06 00:00:00.000000000 Z
11
+ date: 2017-12-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -131,15 +131,17 @@ files:
131
131
  - lib/noise/exceptions.rb
132
132
  - lib/noise/exceptions/max_nonce_error.rb
133
133
  - lib/noise/exceptions/noise_handshake_error.rb
134
+ - lib/noise/exceptions/noise_psk_error.rb
134
135
  - lib/noise/exceptions/noise_validation_error.rb
135
136
  - lib/noise/exceptions/protocol_name_error.rb
137
+ - lib/noise/exceptions/psk_value_error.rb
136
138
  - lib/noise/functions.rb
137
139
  - lib/noise/functions/cipher.rb
138
140
  - lib/noise/functions/cipher/aes_gcm.rb
139
141
  - lib/noise/functions/cipher/cha_cha_poly.rb
140
142
  - lib/noise/functions/dh.rb
141
- - lib/noise/functions/dh/dh25519.rb
142
- - lib/noise/functions/dh/dh448.rb
143
+ - lib/noise/functions/dh/ed25519.rb
144
+ - lib/noise/functions/dh/ed448.rb
143
145
  - lib/noise/functions/dh/secp256k1.rb
144
146
  - lib/noise/functions/hash.rb
145
147
  - lib/noise/functions/hash/blake2b.rb
@@ -1,36 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Noise
4
- module Functions
5
- module DH
6
- class DH25519
7
- DHLEN = 32
8
- def generate_keypair
9
- private_key = RbNaCl::Signatures::Ed25519::SigningKey.generate
10
- public_key = private_key.verify_key
11
- [private_key.to_bytes, public_key.to_bytes]
12
- end
13
-
14
- def dh(private_key, public_key)
15
- point = RbNaCl::GroupElement.new(public_key).mult(private_key)
16
- point.to_bytes
17
- end
18
-
19
- def dhlen
20
- DHLEN
21
- end
22
-
23
- def self.from_private(private_key)
24
- private_key = RbNaCl::GroupElements::Curve25519.new(private_key)
25
- public_key = RbNaCl::GroupElements::Curve25519.base.mult(private_key)
26
- [private_key.to_bytes, public_key.to_bytes]
27
- end
28
-
29
- def self.from_public(public_key)
30
- public_key = RbNaCl::Signatures::Ed25519::VerifyKey.new(public_key)
31
- [nil, public_key.to_bytes]
32
- end
33
- end
34
- end
35
- end
36
- end