noise-ruby 0.3.0 → 0.5.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: b311addeddc717377b96beaecbe50300759ea5d6
4
- data.tar.gz: 2979832b1b009280a5998de859bead7cae613b00
3
+ metadata.gz: 61342d193c1bb42324605f5be2643542fac0bc2b
4
+ data.tar.gz: 44dec9620e075573cf4af4b44367506c45e71b8b
5
5
  SHA512:
6
- metadata.gz: 176002bdc3b0763255c0c2679485e38aa9d9795477dd7d789f40cebaa2154ae877fcec6593ab0409f095447cfd49f565aac8216053742bc3dce3572fc9a67253
7
- data.tar.gz: ac57838f8ab1e3a0f8910dae97cb8d28a8b9a83bec9b6e433316c062c1f2c09465c149ace734db23d0da6360bb5c1f12545f3ad858f72a87cca045ae89554ad9
6
+ metadata.gz: 4e613f8379f2309a540b1e5a9a9b2f884220fcb39387720a1b5ee978fbb0e698f013ba09435e6a2834c45c5534d413b8d8fd76159bc90a748b219417d49b3fa7
7
+ data.tar.gz: 72b2c24e768d729b67d6a54d26efb71b0acbed829a66725113ce0a953092a6e6543801bf0f936c3ac80dfa11dd3870d02b74ba556d11eff6c7efe4bfb8cca579
data/.rubocop.yml CHANGED
@@ -4,6 +4,9 @@ Metrics/BlockLength:
4
4
  Metrics/LineLength:
5
5
  Max: 120
6
6
 
7
+ Metrics/MethodLength:
8
+ Max: 30
9
+
7
10
  Style/Documentation:
8
11
  Enabled: false
9
12
 
@@ -18,4 +21,3 @@ Style/WordArray:
18
21
 
19
22
  AllCops:
20
23
  TargetRubyVersion: 2.4.1
21
-
data/Gemfile CHANGED
@@ -1,6 +1,8 @@
1
+ # frozen_string_literal: true
2
+
1
3
  source 'https://rubygems.org'
2
4
 
3
- git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
5
+ git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
4
6
 
5
7
  # Specify your gem's dependencies in noise.gemspec
6
8
  gemspec
data/README.md CHANGED
@@ -6,7 +6,8 @@ A Ruby implementation of the Noise Protocol framework(http://noiseprotocol.org/)
6
6
 
7
7
  Secp256k1 cipher function is supported.
8
8
  This is required for Lightning Network, layer-2 protocol for bitcoin.
9
- see
9
+
10
+ see https://github.com/lightningnetwork/lightning-rfc/blob/master/08-transport.md
10
11
 
11
12
  ## Future Works
12
13
 
@@ -19,6 +20,15 @@ The followings are not supported yet.
19
20
 
20
21
  ## Installation
21
22
 
23
+ This library requires [libsecp256k1](https://github.com/bitcoin-core/secp256k1).
24
+
25
+ $ git clone https://github.com/bitcoin-core/secp256k1
26
+ $ cd secp256k1
27
+ $ ./autogen.sh
28
+ $ ./configure --enable-experimental --enable-module-ecdh --enable-module-recovery --enable-benchmark=false
29
+ $ make
30
+ $ sudo make install
31
+
22
32
  Add this line to your application's Gemfile:
23
33
 
24
34
  ```
data/Rakefile CHANGED
@@ -1,6 +1,8 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'bundler/gem_tasks'
2
4
  require 'rspec/core/rake_task'
3
5
 
4
6
  RSpec::Core::RakeTask.new(:spec)
5
7
 
6
- task :default => :spec
8
+ task default: :spec
@@ -1,12 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Noise
4
- module KeyPair
5
- STATIC = 's'
6
- EPHEMERAL = 'e'
7
- REMOTE_STATIC = 'rs'
8
- REMOTE_EPHEMERAL = 're'
9
- end
10
4
  class Connection
11
5
  module Status
12
6
  STATIC = 1
@@ -22,8 +16,8 @@ module Noise
22
16
  @handshake_started = false
23
17
  @handshake_finished = false
24
18
  @fn = nil
25
- @write_message_proc = lambda {|payload| write_message(payload)}
26
- @read_message_proc = lambda {|payload| read_message(payload)}
19
+ @write_message_proc = ->(payload) { write_message(payload) }
20
+ @read_message_proc = ->(payload) { read_message(payload) }
27
21
  end
28
22
 
29
23
  def psks=(psks)
@@ -15,7 +15,7 @@ module Noise
15
15
  end
16
16
 
17
17
  def nonce_to_bytes(n)
18
- "\00" * 4 + sprintf('%16x', n).htb.reverse
18
+ "\00" * 4 + format('%16x', n).htb.reverse
19
19
  end
20
20
  end
21
21
  end
@@ -15,15 +15,12 @@ module Noise
15
15
  end
16
16
 
17
17
  def dh(private_key, public_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)
18
+ key = ::Secp256k1::PublicKey.new(pubkey: public_key, raw: true)
19
+ key.ecdh(private_key)
23
20
  end
24
21
 
25
22
  def dhlen
26
- 64
23
+ 33
27
24
  end
28
25
 
29
26
  def self.from_private(private_key)
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Noise
4
+ module KeyPair
5
+ STATIC = 's'
6
+ EPHEMERAL = 'e'
7
+ REMOTE_STATIC = 'rs'
8
+ REMOTE_EPHEMERAL = 're'
9
+ end
10
+ end
data/lib/noise/pattern.rb CHANGED
@@ -12,7 +12,7 @@ module Noise
12
12
  end
13
13
 
14
14
  class Pattern
15
- attr_reader :one_way, :tokens, :modifiers
15
+ attr_reader :one_way, :tokens, :modifiers, :psk_count
16
16
 
17
17
  def self.create(name)
18
18
  pattern_set = name.scan(/([A-Z]+)([^A-Z]*)/)&.first
@@ -35,15 +35,9 @@ module Noise
35
35
  def apply_pattern_modifiers
36
36
  @modifiers.each do |modifier|
37
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
38
+ index = modifier.gsub(/psk/, '').to_i
39
+ raise Noise::Exceptions::PSKValueError if index / 2 > @tokens.size
40
+ if index.zero?
47
41
  @tokens[0].insert(0, Token::PSK)
48
42
  else
49
43
  @tokens[index - 1] << Token::PSK
@@ -17,7 +17,8 @@ module Noise
17
17
 
18
18
  DH = {
19
19
  '25519': Noise::Functions::DH::ED25519,
20
- '448': Noise::Functions::DH::ED448
20
+ '448': Noise::Functions::DH::ED448,
21
+ 'secp256k1': Noise::Functions::DH::Secp256k1
21
22
  }.stringify_keys.freeze
22
23
 
23
24
  HASH = {
@@ -36,16 +37,20 @@ module Noise
36
37
  def initialize(name, pattern_name, cipher_name, hash_name, dh_name)
37
38
  @name = name
38
39
  @pattern = Noise::Pattern.create(pattern_name)
39
- @keypairs = { s: nil, e: nil, rs: nil, re: nil }
40
- @cipher_fn = CIPHER[cipher_name]&.new
41
- @hash_fn = HASH[hash_name]&.new
42
- @dh_fn = DH[dh_name]&.new
40
+ @keypairs = { s: [], e: [], rs: [], re: [] }
43
41
  @hkdf_fn = Noise::Functions::Hash.create_hkdf_fn(hash_name)
44
- @psks = nil
42
+ @psks = []
45
43
  @is_psk_handshake = @pattern.modifiers.any? { |m| m.start_with?('psk') }
46
44
 
47
45
  @pattern.apply_pattern_modifiers
48
46
 
47
+ initialize_fn!(cipher_name, hash_name, dh_name)
48
+ end
49
+
50
+ def initialize_fn!(cipher_name, hash_name, dh_name)
51
+ @cipher_fn = CIPHER[cipher_name]&.new
52
+ @hash_fn = HASH[hash_name]&.new
53
+ @dh_fn = DH[dh_name]&.new
49
54
  raise Noise::Exceptions::ProtocolNameError unless @cipher_fn && @hash_fn && @dh_fn
50
55
  end
51
56
 
@@ -66,38 +71,36 @@ module Noise
66
71
  @dh_fn = nil
67
72
  @hash_fn = nil
68
73
  @keypair_fn = nil
74
+ end
75
+
76
+ def validate_psk!
77
+ # Invalid psk length! Has to be 32 bytes long
78
+ raise Noise::Exceptions::NoisePSKError if @psks.any? { |psk| psk.bytesize != 32 }
79
+ # Bad number of PSKs provided to this protocol! {} are required,
80
+ # given {}'.format(self.pattern.psk_count, len(self.psks)))
81
+ raise Noise::Exceptions::NoisePSKError if @pattern.psk_count != @psks.count
82
+ end
69
83
 
84
+ def valid_keypairs?
85
+ @pattern.required_keypairs(@initiator).any? { |keypair| !@keypairs[keypair] }
70
86
  end
71
87
 
72
88
  def validate
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
89
+ validate_psk! if psk_handshake?
83
90
 
84
91
  # You need to set role with NoiseConnection.set_as_initiator
85
92
  # or NoiseConnection.set_as_responder
86
93
  raise Noise::Exceptions::NoiseValidationError if @initiator.nil?
87
94
 
88
95
  # 'Keypair {} has to be set for chosen handshake pattern'.format(keypair)
89
- # require 'pp'
90
- # pp @pattern
91
- # pp @initiator
92
- # pp @pattern.required_keypairs(@initiator)
93
- # pp @keypairs
94
- raise Noise::Exceptions::NoiseValidationError if @pattern.required_keypairs(@initiator).any? { |keypair| !@keypairs[keypair] }
96
+ raise Noise::Exceptions::NoiseValidationError if valid_keypairs?
95
97
 
96
98
  if @keypairs[:e] || @keypairs[:re]
97
99
  # warnings
98
100
  # One of ephemeral keypairs is already set.
99
101
  # This is OK for testing, but should NEVER happen in production!
100
102
  end
103
+ true
101
104
  end
102
105
 
103
106
  def initialise_handshake_state
@@ -105,10 +108,7 @@ module Noise
105
108
  self,
106
109
  @initiator,
107
110
  @prologue,
108
- @keypairs[:s],
109
- @keypairs[:e],
110
- @keypairs[:rs],
111
- @keypairs[:re]
111
+ @keypairs
112
112
  )
113
113
  @symmetric_state = @handshake_state.symmetric_state
114
114
  end
@@ -14,17 +14,17 @@ module Noise
14
14
 
15
15
  attr_reader :message_patterns, :symmetric_state
16
16
 
17
- def initialize(protocol, initiator, prologue, s, e, rs, re)
17
+ def initialize(protocol, initiator, prologue, keypairs)
18
18
  # @protocol = handshake_pattern.to_protocol
19
19
  @protocol = protocol
20
20
  @symmetric_state = SymmetricState.new
21
21
  @symmetric_state.initialize_symmetric(@protocol)
22
22
  @symmetric_state.mix_hash(prologue)
23
23
  @initiator = initiator
24
- @s = [s].flatten
25
- @e = [e].flatten
26
- @rs = [rs].flatten
27
- @re = [re].flatten
24
+ @s = keypairs[:s]
25
+ @e = keypairs[:e]
26
+ @rs = keypairs[:rs]
27
+ @re = keypairs[:re]
28
28
 
29
29
  # TODO : Calls MixHash() once for each public key listed in the pre-messages from handshake_pattern, with the
30
30
  # specified public key as input (see Section 7 for an explanation of pre-messages). If both initiator and
@@ -60,7 +60,7 @@ module Noise
60
60
  pattern.each do |token|
61
61
  case token
62
62
  when 'e'
63
- @e = dh_fn.generate_keypair if @e.compact.empty?
63
+ @e = dh_fn.generate_keypair if @e.empty?
64
64
  message_buffer << @e[1]
65
65
  @symmetric_state.mix_hash(@e[1])
66
66
  @symmetric_state.mix_key(@e[1]) if @protocol.psk_handshake?
@@ -104,7 +104,7 @@ module Noise
104
104
  pattern.each do |token|
105
105
  case token
106
106
  when 'e'
107
- @re = @protocol.dh_fn.class.from_public(message[0...len]) if @re.compact.empty?
107
+ @re = @protocol.dh_fn.class.from_public(message[0...len]) if @re.empty?
108
108
  message = message[len..-1]
109
109
  @symmetric_state.mix_hash(@re[1])
110
110
  @symmetric_state.mix_key(@re[1]) if @protocol.psk_handshake?
@@ -11,14 +11,17 @@ module Noise
11
11
  attr_reader :h, :ck
12
12
  attr_reader :cipher_state
13
13
 
14
+ def initialize_h(protocol)
15
+ if protocol.name.length <= protocol.hash_fn.hashlen
16
+ protocol.name.ljust(protocol.hash_fn.hashlen, "\x00")
17
+ else
18
+ protocol.hash_fn.hash(protocol.name)
19
+ end
20
+ end
21
+
14
22
  def initialize_symmetric(protocol)
15
23
  @protocol = protocol
16
- @ck = @h =
17
- if @protocol.name.length <= @protocol.hash_fn.hashlen
18
- @protocol.name.ljust(@protocol.hash_fn.hashlen, "\x00")
19
- else
20
- @protocol.hash_fn.hash(@protocol.name)
21
- end
24
+ @ck = @h = initialize_h(protocol)
22
25
 
23
26
  @cipher_state = CipherState.new(cipher: @protocol.cipher_fn)
24
27
  @cipher_state.initialize_key(nil)
@@ -59,22 +62,25 @@ module Noise
59
62
  plaintext
60
63
  end
61
64
 
65
+ def create_cipher_state(k)
66
+ k = truncate(k)
67
+ c = CipherState.new(cipher: @protocol.cipher_fn)
68
+ c.initialize_key(k)
69
+ c
70
+ end
71
+
62
72
  def split
63
73
  temp_k1, temp_k2 = @protocol.hkdf_fn.call(@ck, '', 2)
64
- temp_k1 = truncate(temp_k1)
65
- temp_k2 = truncate(temp_k2)
66
- c1 = CipherState.new(cipher: @protocol.cipher_fn)
67
- c2 = CipherState.new(cipher: @protocol.cipher_fn)
68
- c1.initialize_key(temp_k1)
69
- c2.initialize_key(temp_k2)
74
+ c1 = create_cipher_state(temp_k1)
75
+ c2 = create_cipher_state(temp_k2)
70
76
  @protocol.cipher_state_encrypt = @protocol.initiator ? c1 : c2
71
77
  @protocol.cipher_state_decrypt = @protocol.initiator ? c2 : c1
72
78
  @protocol.handshake_done
73
79
  [c1, c2]
74
80
  end
75
81
 
76
- def truncate(temp_k)
77
- @protocol.hash_fn.hashlen == 64 ? temp_k[0, 32] : temp_k
82
+ def truncate(k)
83
+ @protocol.hash_fn.hashlen == 64 ? k[0, 32] : k
78
84
  end
79
85
  end
80
86
  end
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.3.0'
4
+ VERSION = '0.5.0'
5
5
  end
data/lib/noise.rb CHANGED
@@ -5,6 +5,7 @@ require 'noise/version'
5
5
  require 'ecdsa'
6
6
  require 'rbnacl'
7
7
  require 'ruby_hmac'
8
+ require 'secp256k1'
8
9
  require 'securerandom'
9
10
 
10
11
  require 'noise/utils/hash'
@@ -12,6 +13,7 @@ require 'noise/utils/string'
12
13
 
13
14
  module Noise
14
15
  autoload :Connection, 'noise/connection'
16
+ autoload :KeyPair, 'noise/key_pair'
15
17
  autoload :Protocol, 'noise/protocol'
16
18
  autoload :Pattern, 'noise/pattern'
17
19
  autoload :Exceptions, 'noise/exceptions'
data/noise.gemspec CHANGED
@@ -26,6 +26,7 @@ Gem::Specification.new do |spec|
26
26
  spec.add_development_dependency 'rspec', '~> 3.0'
27
27
 
28
28
  spec.add_runtime_dependency 'aead'
29
+ spec.add_runtime_dependency 'bitcoin-secp256k1'
29
30
  spec.add_runtime_dependency 'ecdsa'
30
31
  spec.add_runtime_dependency 'rbnacl'
31
32
  spec.add_runtime_dependency 'ruby-hmac'
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.3.0
4
+ version: 0.5.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-08 00:00:00.000000000 Z
11
+ date: 2017-12-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -66,6 +66,20 @@ dependencies:
66
66
  - - ">="
67
67
  - !ruby/object:Gem::Version
68
68
  version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: bitcoin-secp256k1
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
69
83
  - !ruby/object:Gem::Dependency
70
84
  name: ecdsa
71
85
  requirement: !ruby/object:Gem::Requirement
@@ -148,6 +162,7 @@ files:
148
162
  - lib/noise/functions/hash/blake2s.rb
149
163
  - lib/noise/functions/hash/sha256.rb
150
164
  - lib/noise/functions/hash/sha512.rb
165
+ - lib/noise/key_pair.rb
151
166
  - lib/noise/pattern.rb
152
167
  - lib/noise/protocol.rb
153
168
  - lib/noise/state.rb