noise-ruby 0.2.0 → 0.3.0

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