noise-ruby 0.7.1 → 0.7.3

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
  SHA256:
3
- metadata.gz: 52f9bedd93319f9cf54acb51c1a83c64fcfd25cfdaef69204f709b59c83060c4
4
- data.tar.gz: c72fb7afa8aab1c577b305f1d57316ab34ad183b6bbf249d420b3b17d4bb81e6
3
+ metadata.gz: 9fcf6ecf59b631aacb32b66661719e57a205ccea71a487748d3bb2979fc2ee4b
4
+ data.tar.gz: 22bee13bda1fe1a9339777261eccd6d173e32679e969e78b065abe457d06884e
5
5
  SHA512:
6
- metadata.gz: be31e461a923db0af2f6ae19b42fa74ac3b70fcac8e332a281a46377ac0fa0164ce799567ba19e862fbbc40b367b06672bd095ea850bcd4bfaec2291b1b4ab16
7
- data.tar.gz: e3495a893d23263a534d7f490fdcd84a5f33c81b4e5d8437d1a268abc8111940916ecf6f832f2b13bf610d5ec9f1059129c16a9f2a0728a823b96a147482e7ac
6
+ metadata.gz: 2cbd93529da1345b646b9be626591bf81eb1fe53d211d70a75fe6fe1f56d99c7b55bdedc7102aed3d20c27080fcf4f04b0619b7bacd8f664f8301397f3f4e069
7
+ data.tar.gz: '0845488f8a48edb3f09fba247a8f7950aefd0c5a6cade1ae114e3b3e683e4f66dbc3d34267b2ee298f066d170ddd84343da3d843a5a89587ce592cac906da849'
data/README.md CHANGED
@@ -49,7 +49,53 @@ gem 'secp256k1-ruby'
49
49
 
50
50
  ## Usage
51
51
 
52
- TODO: Write usage instructions here
52
+ Followings shows handshake protocol with "Noise_NN_25519_ChaChaPoly_BLAKE2b"
53
+
54
+ ### Handshake
55
+
56
+ #### initiator
57
+
58
+ ```
59
+ initiator = Noise::Connection::Initiator.new("Noise_NN_25519_ChaChaPoly_BLAKE2b")
60
+ initiator.prologue = "test" # => "test"
61
+ initiator.start_handshake # => true
62
+ cipher = initiator.write_message("") # => "\xB6\xF7gmxi\xAB\xBCY|t\xF0\x9D\x01A\ad\x92\xBBvp\x80ZNU\f=\x83\x81^\xFD\x15"
63
+ ```
64
+
65
+ then initiator sends `cipher` to responder.
66
+
67
+ #### responder
68
+
69
+ Responder receive `cipher` from initiator.
70
+ Responder respond messages to initiator.
71
+
72
+ ```
73
+ responder = Noise::Connection::Responder.new("Noise_NN_25519_ChaChaPoly_BLAKE2b")
74
+ responder.prologue = "test" # => "test"
75
+ responder.start_handshake # => true
76
+ plain = responder.read_message(cipher) # => ""
77
+ cipher = responder.write_message("") # => "\v\xD9\x97'\xC0\xB1\xC9\xFFD\x8C\x7F\x18L\xB0\xF2\x14\xB0\x11\xC0\x90\xAAZ\xE1\x03\x17z)\xB81/5L\x16\xE3\xD1\xBE<{\xB8\xBB\xD6\xF1\x00\x10]\x99=\xD7"
78
+ ```
79
+
80
+
81
+ ### initiator
82
+
83
+ ```
84
+ plain = initiator.read_message(cipher) # => ""
85
+ ```
86
+
87
+ ### Send transport message (after handshake finished)
88
+
89
+ ```
90
+ cipher = initiator.encrypt("Hello, World!") # => "\xDA\xC7\xD7as\v\xFA\xCC,\xB3\xC7\xD0/xL\xE8I,\xD9\n\xEExh\x8F\xFA\xD6\x01\x99W"
91
+ ```
92
+
93
+ ### Receive transport message
94
+
95
+ ```
96
+ plain = responder.decrypt(cipher) # => "Hello, World!"
97
+ ```
98
+
53
99
 
54
100
  ## Development
55
101
 
@@ -1,5 +1,7 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
+ # frozen_string_literal: true
4
+
3
5
  require 'bundler/setup'
4
6
  require 'noise'
5
7
 
@@ -12,6 +12,7 @@ require 'noise/utils/string'
12
12
 
13
13
  module Noise
14
14
  autoload :Connection, 'noise/connection'
15
+ autoload :Key, 'noise/key'
15
16
  autoload :KeyPair, 'noise/key_pair'
16
17
  autoload :Protocol, 'noise/protocol'
17
18
  autoload :Pattern, 'noise/pattern'
@@ -1,94 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Noise
4
- class Connection
5
- module Status
6
- STATIC = 1
7
- REMOTE_STATIC = 2
8
- EPHEMERAL = 3
9
- REMOTE_EPHEMERAL = 4
10
- end
11
-
12
- attr_accessor :protocol, :handshake_started, :handshake_finished, :fn
13
-
14
- def initialize(name)
15
- @protocol = Protocol.create(name)
16
- @handshake_started = false
17
- @handshake_finished = false
18
- @fn = nil
19
- @write_message_proc = ->(payload) { write_message(payload) }
20
- @read_message_proc = ->(payload) { read_message(payload) }
21
- end
22
-
23
- def psks=(psks)
24
- @protocol.psks = psks
25
- end
26
-
27
- def prologue=(prologue)
28
- @protocol.prologue = prologue
29
- end
30
-
31
- def set_as_initiator!
32
- @protocol.initiator = true
33
- @fn = @write_message_proc
34
- end
35
-
36
- def set_as_responder!
37
- @protocol.initiator = false
38
- @fn = @read_message_proc
39
- end
40
-
41
- def set_keypair_from_private(keypair, private_key)
42
- @protocol.keypairs[keypair.to_sym] = @protocol.dh_fn.class.from_private(private_key)
43
- end
44
-
45
- def set_keypair_from_public(keypair, public_key)
46
- @protocol.keypairs[keypair.to_sym] = @protocol.dh_fn.class.from_public(public_key)
47
- end
48
-
49
- def start_handshake
50
- @protocol.validate
51
- @protocol.initialise_handshake_state
52
- @handshake_started = true
53
- end
54
-
55
- def write_message(payload = '')
56
- # Call NoiseConnection.start_handshake first
57
- raise Noise::Exceptions::NoiseHandshakeError unless @handshake_started
58
- raise Noise::Exceptions::NoiseHandshakeError if @fn != @write_message_proc
59
- # Handshake finished. NoiseConnection.encrypt should be used now
60
- raise Noise::Exceptions::NoiseHandshakeError if @handshake_finished
61
- @fn = @read_message_proc
62
- buffer = +''
63
- result = @protocol.handshake_state.write_message(payload, buffer)
64
- @handshake_finished = true if result
65
- buffer
66
- end
67
-
68
- def read_message(data)
69
- # Call NoiseConnection.start_handshake first
70
- raise Noise::Exceptions::NoiseHandshakeError unless @handshake_started
71
- raise Noise::Exceptions::NoiseHandshakeError if @fn != @read_message_proc
72
- # Handshake finished. NoiseConnection.encrypt should be used now
73
- raise Noise::Exceptions::NoiseHandshakeError if @handshake_finished
74
-
75
- @fn = @write_message_proc
76
- buffer = +''
77
- result = @protocol.handshake_state.read_message(data, buffer)
78
- @handshake_finished = true if result
79
- buffer
80
- end
81
-
82
- def encrypt(data)
83
- raise Noise::Exceptions::NoiseHandshakeError unless @handshake_finished
84
- # raise Noise::Exceptions::NoiseInvalidMessage
85
- @protocol.cipher_state_encrypt.encrypt_with_ad('', data)
86
- end
87
-
88
- def decrypt(data)
89
- raise Noise::Exceptions::NoiseHandshakeError unless @handshake_finished
90
- # raise Noise::Exceptions::NoiseInvalidMessage
91
- @protocol.cipher_state_decrypt.decrypt_with_ad('', data)
92
- end
4
+ module Connection
5
+ autoload :Base, 'noise/connection/base'
6
+ autoload :Initiator, 'noise/connection/initiator'
7
+ autoload :Responder, 'noise/connection/responder'
93
8
  end
94
9
  end
@@ -0,0 +1,112 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Noise
4
+ module Connection
5
+ class Base
6
+ attr_reader :protocol, :handshake_started, :handshake_finished, :handshake_hash
7
+ attr_reader :cipher_state_encrypt, :cipher_state_decrypt, :cipher_state_handshake
8
+ attr_accessor :psks, :prologue
9
+
10
+ def initialize(name, keypairs: { s: nil, e: nil, rs: nil, re: nil })
11
+ @protocol = Protocol.create(name)
12
+
13
+ @keypairs = keypairs
14
+ # parameter keypairs[:e] and keypairs[:s] are strings, so should convert Noise::Key object.
15
+ @keypairs[:e] = @protocol.dh_fn.class.from_private(@keypairs[:e]) if @keypairs[:e]
16
+ @keypairs[:s] = @protocol.dh_fn.class.from_private(@keypairs[:s]) if @keypairs[:s]
17
+
18
+ @handshake_started = false
19
+ @handshake_finished = false
20
+ @next_message = nil
21
+ end
22
+
23
+ def start_handshake
24
+ validate
25
+ initialise_handshake_state
26
+ @handshake_started = true
27
+ end
28
+
29
+ def initialise_handshake_state
30
+ @handshake_state = Noise::State::HandshakeState.new(
31
+ self,
32
+ protocol,
33
+ initiator?,
34
+ @prologue,
35
+ @keypairs
36
+ )
37
+ @symmetric_state = @handshake_state.symmetric_state
38
+ @cipher_state_handshake = @symmetric_state.cipher_state
39
+ end
40
+
41
+ def write_message(payload = '')
42
+ # Call NoiseConnection.start_handshake first
43
+ raise Noise::Exceptions::NoiseHandshakeError unless @handshake_started
44
+ raise Noise::Exceptions::NoiseHandshakeError if @next_message != :write
45
+ # Handshake finished. NoiseConnection.encrypt should be used now
46
+ raise Noise::Exceptions::NoiseHandshakeError if @handshake_finished
47
+ @next_message = :read
48
+ buffer = +''
49
+ result = @handshake_state.write_message(payload, buffer)
50
+ @handshake_finished = true if result
51
+ buffer
52
+ end
53
+
54
+ def read_message(data)
55
+ # Call NoiseConnection.start_handshake first
56
+ raise Noise::Exceptions::NoiseHandshakeError unless @handshake_started
57
+ raise Noise::Exceptions::NoiseHandshakeError if @next_message != :read
58
+ # Handshake finished. NoiseConnection.encrypt should be used now
59
+ raise Noise::Exceptions::NoiseHandshakeError if @handshake_finished
60
+
61
+ @next_message = :write
62
+ buffer = +''
63
+ result = @handshake_state.read_message(data, buffer)
64
+ @handshake_finished = true if result
65
+ buffer
66
+ end
67
+
68
+ def encrypt(data)
69
+ raise Noise::Exceptions::NoiseHandshakeError unless @handshake_finished
70
+ # raise Noise::Exceptions::NoiseInvalidMessage
71
+ @cipher_state_encrypt.encrypt_with_ad('', data)
72
+ end
73
+
74
+ def decrypt(data)
75
+ raise Noise::Exceptions::NoiseHandshakeError unless @handshake_finished
76
+ # raise Noise::Exceptions::NoiseInvalidMessage
77
+ @cipher_state_decrypt.decrypt_with_ad('', data)
78
+ end
79
+
80
+ def validate_psk!
81
+ # Invalid psk length! Has to be 32 bytes long
82
+ raise Noise::Exceptions::NoisePSKError if @psks.any? { |psk| psk.bytesize != 32 }
83
+ # Bad number of PSKs provided to this protocol! {} are required,
84
+ # given {}'.format(self.pattern.psk_count, len(self.psks)))
85
+ raise Noise::Exceptions::NoisePSKError if @protocol.pattern.psk_count != @psks.count
86
+ end
87
+
88
+ def valid_keypairs?
89
+ @protocol.pattern.required_keypairs(initiator?).any? { |keypair| !@keypairs[keypair] }
90
+ end
91
+
92
+ def validate
93
+ validate_psk! if psk_handshake?
94
+
95
+ # 'Keypair {} has to be set for chosen handshake pattern'.format(keypair)
96
+ raise Noise::Exceptions::NoiseValidationError if valid_keypairs?
97
+ true
98
+ end
99
+
100
+ def psk_handshake?
101
+ @protocol.is_psk_handshake
102
+ end
103
+
104
+ def handshake_done(_c1, _c2)
105
+ @handshake_hash = @symmetric_state.handshake_hash
106
+ @handshake_state = nil
107
+ @symmetric_state = nil
108
+ @cipher_state_handshake = nil
109
+ end
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Noise
4
+ module Connection
5
+ class Initiator < Base
6
+ def initialize(name, keypairs: { s: nil, e: nil, rs: nil, re: nil })
7
+ super
8
+ @next_message = :write
9
+ end
10
+
11
+ def initiator?
12
+ true
13
+ end
14
+
15
+ def handshake_done(c1, c2)
16
+ super
17
+ @cipher_state_encrypt = c1
18
+ @cipher_state_decrypt = @protocol.pattern.one_way ? nil : c2
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Noise
4
+ module Connection
5
+ class Responder < Base
6
+ def initialize(name, keypairs: { s: nil, e: nil, rs: nil, re: nil })
7
+ super
8
+ @next_message = :read
9
+ end
10
+
11
+ def initiator?
12
+ false
13
+ end
14
+
15
+ def handshake_done(c1, c2)
16
+ super
17
+ @cipher_state_decrypt = c1
18
+ @cipher_state_encrypt = @protocol.pattern.one_way ? nil : c2
19
+ end
20
+ end
21
+ end
22
+ end
@@ -8,14 +8,14 @@ module Noise
8
8
  @cipher = RbNaCl::AEAD::ChaCha20Poly1305IETF.new(String.new(k).force_encoding('ASCII-8BIT'))
9
9
  @cipher.encrypt(nonce_to_bytes(n), plaintext, ad)
10
10
  rescue ::RbNaCl::CryptoError => e
11
- raise Noise::Exceptions::EncryptError.new(e)
11
+ raise Noise::Exceptions::EncryptError, e
12
12
  end
13
13
 
14
14
  def decrypt(k, n, ad, ciphertext)
15
15
  @cipher = RbNaCl::AEAD::ChaCha20Poly1305IETF.new(String.new(k).force_encoding('ASCII-8BIT'))
16
16
  @cipher.decrypt(nonce_to_bytes(n), ciphertext, ad)
17
17
  rescue ::RbNaCl::CryptoError => e
18
- raise Noise::Exceptions::DecryptError.new(e)
18
+ raise Noise::Exceptions::DecryptError, e
19
19
  end
20
20
 
21
21
  def nonce_to_bytes(n)
@@ -9,7 +9,7 @@ module Noise
9
9
  private_key = 1 + SecureRandom.random_number(RbNaCl::GroupElement::STANDARD_GROUP_ORDER - 1)
10
10
  scalar_as_string = ECDSA::Format::IntegerOctetString.encode(private_key, 32)
11
11
  public_key = RbNaCl::GroupElements::Curve25519.base.mult(scalar_as_string)
12
- [ECDSA::Format::IntegerOctetString.encode(private_key, 32), public_key.to_bytes]
12
+ Noise::Key.new(ECDSA::Format::IntegerOctetString.encode(private_key, 32), public_key.to_bytes)
13
13
  end
14
14
 
15
15
  def dh(private_key, public_key)
@@ -22,11 +22,7 @@ module Noise
22
22
 
23
23
  def self.from_private(private_key)
24
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]
25
+ Noise::Key.new(private_key, public_key.to_bytes)
30
26
  end
31
27
  end
32
28
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Noise
2
4
  module Functions
3
5
  module DH
@@ -7,18 +9,13 @@ module Noise
7
9
  throw NotImplementedError
8
10
  end
9
11
 
10
- def dh(key_pair, public_key)
12
+ def dh(_key_pair, _public_key)
11
13
  throw NotImplementedError
12
14
  end
13
15
 
14
16
  def dhlen
15
17
  DHLEN
16
18
  end
17
-
18
- def self.from_private(private_key)
19
- end
20
- def self.from_public(private_key)
21
- end
22
19
  end
23
20
  end
24
21
  end
@@ -5,7 +5,6 @@ begin
5
5
  rescue LoadError
6
6
  end
7
7
 
8
-
9
8
  module Noise
10
9
  module Functions
11
10
  module DH
@@ -14,17 +13,17 @@ module Noise
14
13
  group = ECDSA::Group::Secp256k1
15
14
  private_key = 1 + SecureRandom.random_number(group.order - 1)
16
15
  public_key = group.generator.multiply_by_scalar(private_key)
17
- [
16
+ Noise::Key.new(
18
17
  ECDSA::Format::IntegerOctetString.encode(private_key, 32),
19
18
  ECDSA::Format::PointOctetString.encode(public_key, compression: true)
20
- ]
19
+ )
21
20
  end
22
21
 
23
22
  def dh(private_key, public_key)
24
23
  key = ::Secp256k1::PublicKey.new(pubkey: public_key, raw: true)
25
24
  key.ecdh(private_key)
26
25
  rescue ::Secp256k1::AssertError => _
27
- raise Noise::Exceptions::InvalidPublicKeyError.new(public_key)
26
+ raise Noise::Exceptions::InvalidPublicKeyError, public_key
28
27
  end
29
28
 
30
29
  def dhlen
@@ -35,11 +34,7 @@ module Noise
35
34
  group = ECDSA::Group::Secp256k1
36
35
  scalar = ECDSA::Format::IntegerOctetString.decode(private_key)
37
36
  point = group.generator.multiply_by_scalar(scalar)
38
- [private_key, ECDSA::Format::PointOctetString.encode(point, compression: true)]
39
- end
40
-
41
- def self.from_public(public_key)
42
- [nil, public_key]
37
+ Noise::Key.new(private_key, ECDSA::Format::PointOctetString.encode(point, compression: true))
43
38
  end
44
39
  end
45
40
  end
@@ -27,46 +27,46 @@ module Noise
27
27
  end
28
28
 
29
29
  class Blake2sDigester
30
- IV = [0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A,0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19]
30
+ IV = [0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A, 0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19].freeze
31
31
  SIGMA = [
32
- [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 ],
33
- [ 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 ],
34
- [ 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 ],
35
- [ 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 ],
36
- [ 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 ],
37
- [ 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 ],
38
- [ 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 ],
39
- [ 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 ],
40
- [ 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 ],
41
- [ 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0 ]
42
- ]
32
+ [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
33
+ [14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3],
34
+ [11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4],
35
+ [7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8],
36
+ [9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13],
37
+ [2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9],
38
+ [12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11],
39
+ [13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10],
40
+ [6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5],
41
+ [10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0]
42
+ ].freeze
43
43
 
44
44
  def initialize(key: '')
45
45
  @key = key
46
- @ctx = init(Blake2s::HASHLEN, @key.unpack("C*"))
46
+ @ctx = init(Blake2s::HASHLEN, @key.unpack('C*'))
47
47
  end
48
48
 
49
49
  def update(data)
50
- update_internal(@ctx, data.unpack("C*"))
50
+ update_internal(@ctx, data.unpack('C*'))
51
51
  self
52
52
  end
53
53
 
54
54
  def digest
55
55
  out = []
56
56
  final(@ctx, out)
57
- out.pack("C*")
57
+ out.pack('C*')
58
58
  end
59
59
 
60
60
  # @return context
61
61
  def init(out_len, key)
62
- raise ArgumentError if out_len == 0 || out_len > 32
62
+ raise ArgumentError if out_len.zero? || out_len > 32
63
63
  h = IV.dup
64
64
  h[0] ^= 0x01010000 ^ (key.size << 8) ^ out_len
65
65
  t = 0
66
66
  c = 0
67
67
  b = Array.new(Blake2s::BLOCKLEN).fill(0, key.size)
68
68
  ctx = Context.new(b, h, t, c, out_len)
69
- if key.size > 0
69
+ if key.size.positive?
70
70
  update_internal(ctx, key)
71
71
  ctx.c = 64
72
72
  end
@@ -101,12 +101,12 @@ module Noise
101
101
  private
102
102
 
103
103
  def to_int32(x)
104
- x = x & 0xFFFFFFFF
104
+ x &= 0xFFFFFFFF
105
105
  x < 0x80000000 ? x : x - 2**32
106
106
  end
107
107
 
108
- def rshift(x, y, range=32)
109
- (x + (x > 0 ? 0 : 2 ** range)) >> y
108
+ def rshift(x, y, range = 32)
109
+ (x + (x.positive? ? 0 : 2**range)) >> y
110
110
  end
111
111
 
112
112
  def rotr32(x, y)
@@ -151,11 +151,11 @@ module Noise
151
151
  end
152
152
 
153
153
  10.times do |i|
154
- mix_g(v, 0, 4, 8, 12, m[SIGMA[i][ 0]], m[SIGMA[i][ 1]])
155
- mix_g(v, 1, 5, 9, 13, m[SIGMA[i][ 2]], m[SIGMA[i][ 3]])
156
- mix_g(v, 2, 6, 10, 14, m[SIGMA[i][ 4]], m[SIGMA[i][ 5]])
157
- mix_g(v, 3, 7, 11, 15, m[SIGMA[i][ 6]], m[SIGMA[i][ 7]])
158
- mix_g(v, 0, 5, 10, 15, m[SIGMA[i][ 8]], m[SIGMA[i][ 9]])
154
+ mix_g(v, 0, 4, 8, 12, m[SIGMA[i][0]], m[SIGMA[i][1]])
155
+ mix_g(v, 1, 5, 9, 13, m[SIGMA[i][2]], m[SIGMA[i][3]])
156
+ mix_g(v, 2, 6, 10, 14, m[SIGMA[i][4]], m[SIGMA[i][5]])
157
+ mix_g(v, 3, 7, 11, 15, m[SIGMA[i][6]], m[SIGMA[i][7]])
158
+ mix_g(v, 0, 5, 10, 15, m[SIGMA[i][8]], m[SIGMA[i][9]])
159
159
  mix_g(v, 1, 6, 11, 12, m[SIGMA[i][10]], m[SIGMA[i][11]])
160
160
  mix_g(v, 2, 7, 8, 13, m[SIGMA[i][12]], m[SIGMA[i][13]])
161
161
  mix_g(v, 3, 4, 9, 14, m[SIGMA[i][14]], m[SIGMA[i][15]])
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Noise
4
+ class Key
5
+ attr_reader :private_key, :public_key
6
+
7
+ def initialize(private_key, public_key)
8
+ @private_key = private_key
9
+ @public_key = public_key
10
+ end
11
+ end
12
+ end
@@ -2,14 +2,9 @@
2
2
 
3
3
  module Noise
4
4
  class Protocol
5
- attr_accessor :prologue, :initiator
6
- attr_accessor :cipher_state_encrypt, :cipher_state_decrypt
7
- attr_accessor :cipher_state_handshake
8
- attr_accessor :psks
9
- attr_reader :name, :cipher_fn, :hash_fn, :dh_fn, :hkdf_fn, :pattern
10
- attr_reader :handshake_state, :keypairs, :keypair_fn
11
- attr_reader :handshake_hash
12
- attr_accessor :ck
5
+ attr_accessor :is_psk_handshake
6
+ attr_accessor :cipher_fn, :hash_fn, :dh_fn, :hkdf_fn
7
+ attr_reader :name, :pattern
13
8
 
14
9
  CIPHER = {
15
10
  'AESGCM': Noise::Functions::Cipher::AesGcm,
@@ -38,9 +33,7 @@ module Noise
38
33
  def initialize(name, pattern_name, cipher_name, hash_name, dh_name)
39
34
  @name = name
40
35
  @pattern = Noise::Pattern.create(pattern_name)
41
- @keypairs = { s: [], e: [], rs: [], re: [] }
42
36
  @hkdf_fn = Noise::Functions::Hash.create_hkdf_fn(hash_name)
43
- @psks = []
44
37
  @is_psk_handshake = @pattern.modifiers.any? { |m| m.start_with?('psk') }
45
38
 
46
39
  @pattern.apply_pattern_modifiers
@@ -55,68 +48,6 @@ module Noise
55
48
  raise Noise::Exceptions::ProtocolNameError unless @cipher_fn && @hash_fn && @dh_fn
56
49
  end
57
50
 
58
- def handshake_done
59
- if @pattern.one_way
60
- if @initiator
61
- @cipher_state_decrypt = nil
62
- else
63
- @cipher_state_encrypt = nil
64
- end
65
- end
66
- @handshake_hash = @symmetric_state.handshake_hash
67
- @keypairs = @handshake_state.keypairs
68
-
69
- @handshake_state = nil
70
- @ck = @symmetric_state.ck
71
- @symmetric_state = nil
72
- @cipher_state_handshake = nil
73
- @prologue = nil
74
- @initiator = nil
75
- @dh_fn = nil
76
- @hash_fn = nil
77
- @keypair_fn = nil
78
- end
79
-
80
- def validate_psk!
81
- # Invalid psk length! Has to be 32 bytes long
82
- raise Noise::Exceptions::NoisePSKError if @psks.any? { |psk| psk.bytesize != 32 }
83
- # Bad number of PSKs provided to this protocol! {} are required,
84
- # given {}'.format(self.pattern.psk_count, len(self.psks)))
85
- raise Noise::Exceptions::NoisePSKError if @pattern.psk_count != @psks.count
86
- end
87
-
88
- def valid_keypairs?
89
- @pattern.required_keypairs(@initiator).any? { |keypair| !@keypairs[keypair] }
90
- end
91
-
92
- def validate
93
- validate_psk! if psk_handshake?
94
-
95
- # You need to set role with NoiseConnection.set_as_initiator
96
- # or NoiseConnection.set_as_responder
97
- raise Noise::Exceptions::NoiseValidationError if @initiator.nil?
98
-
99
- # 'Keypair {} has to be set for chosen handshake pattern'.format(keypair)
100
- raise Noise::Exceptions::NoiseValidationError if valid_keypairs?
101
-
102
- if @keypairs[:e] || @keypairs[:re]
103
- # warnings
104
- # One of ephemeral keypairs is already set.
105
- # This is OK for testing, but should NEVER happen in production!
106
- end
107
- true
108
- end
109
-
110
- def initialise_handshake_state
111
- @handshake_state = Noise::State::HandshakeState.new(
112
- self,
113
- @initiator,
114
- @prologue,
115
- @keypairs
116
- )
117
- @symmetric_state = @handshake_state.symmetric_state
118
- end
119
-
120
51
  def psk_handshake?
121
52
  @is_psk_handshake
122
53
  end
@@ -17,11 +17,13 @@ module Noise
17
17
  @cipher = cipher
18
18
  end
19
19
 
20
+ # @param [String] 32 bytes key
20
21
  def initialize_key(key)
21
22
  @k = key
22
23
  @n = 0
23
24
  end
24
25
 
26
+ # @return [Boolean] true if k is non-empty, false otherwise.
25
27
  def key?
26
28
  !@k.nil?
27
29
  end
@@ -30,6 +32,7 @@ module Noise
30
32
  @n = nonce
31
33
  end
32
34
 
35
+ # @return [String] ENCRYPT(k, n++, ad, plaintext) if k is non-empty, otherwise returns plaintext.
33
36
  def encrypt_with_ad(ad, plaintext)
34
37
  return plaintext unless key?
35
38
  raise Noise::Exceptions::MaxNonceError if @n == MAX_NONCE
@@ -38,6 +41,7 @@ module Noise
38
41
  ciphertext
39
42
  end
40
43
 
44
+ # @return DECRYPT(k, n++, ad, ciphertext) if k is non-empty, otherwise returns ciphertext.
41
45
  def decrypt_with_ad(ad, ciphertext)
42
46
  return ciphertext unless key?
43
47
  raise Noise::Exceptions::MaxNonceError if @n == MAX_NONCE
@@ -10,15 +10,20 @@ module Noise
10
10
  # rs: The remote party's static public key
11
11
  # re: The remote party's ephemeral public key
12
12
  #
13
+ # A HandshakeState also has variables to track its role, and the remaining portion of the handshake pattern:
14
+ #
15
+ # initiator: A boolean indicating the initiator or responder role.
16
+ #
17
+ # message_patterns: A sequence of message patterns.
18
+ # Each message pattern is a sequence of tokens from the set ("e", "s", "ee", "es", "se", "ss").
13
19
  class HandshakeState
14
-
15
20
  attr_reader :message_patterns, :symmetric_state
16
21
 
17
- def initialize(protocol, initiator, prologue, keypairs)
18
- # @protocol = handshake_pattern.to_protocol
22
+ def initialize(connection, protocol, initiator, prologue, keypairs)
23
+ @connection = connection
19
24
  @protocol = protocol
20
25
  @symmetric_state = SymmetricState.new
21
- @symmetric_state.initialize_symmetric(@protocol)
26
+ @symmetric_state.initialize_symmetric(@protocol, connection)
22
27
  @symmetric_state.mix_hash(prologue)
23
28
  @initiator = initiator
24
29
  @s = keypairs[:s]
@@ -29,7 +34,7 @@ module Noise
29
34
  # TODO : Calls MixHash() once for each public key listed in the pre-messages from handshake_pattern, with the
30
35
  # specified public key as input (see Section 7 for an explanation of pre-messages). If both initiator and
31
36
  # responder have pre-messages, the initiator's public keys are hashed first.
32
- get_local_keypair = ->(token) { instance_variable_get('@' + token) }
37
+ get_local_keypair = ->(token) { instance_variable_get('@' + token).public_key }
33
38
  get_remote_keypair = ->(token) { instance_variable_get('@r' + token) }
34
39
 
35
40
  if initiator
@@ -42,17 +47,18 @@ module Noise
42
47
 
43
48
  @protocol.pattern.initiator_pre_messages&.map do |message|
44
49
  keypair = initiator_keypair_getter.call(message)
45
- @symmetric_state.mix_hash(keypair[1])
50
+ @symmetric_state.mix_hash(keypair)
46
51
  end
47
52
 
48
53
  @protocol.pattern.responder_pre_messages&.map do |message|
49
54
  keypair = responder_keypair_getter.call(message)
50
- @symmetric_state.mix_hash(keypair[1])
55
+ @symmetric_state.mix_hash(keypair)
51
56
  end
52
57
  # Sets message_patterns to the message patterns from handshake_pattern
53
58
  @message_patterns = @protocol.pattern.tokens.dup
54
59
  end
55
60
 
61
+ # Takes a payload byte sequence which may be zero-length, and a message_buffer to write the output into
56
62
  def write_message(payload, message_buffer)
57
63
  pattern = @message_patterns.shift
58
64
  dh_fn = @protocol.dh_fn
@@ -60,37 +66,32 @@ module Noise
60
66
  pattern.each do |token|
61
67
  case token
62
68
  when 'e'
63
- @e = dh_fn.generate_keypair if @e.empty?
64
- message_buffer << @e[1]
65
- @symmetric_state.mix_hash(@e[1])
66
- @symmetric_state.mix_key(@e[1]) if @protocol.psk_handshake?
67
- next
69
+ @e = dh_fn.generate_keypair if @e.nil?
70
+ message_buffer << @e.public_key
71
+ @symmetric_state.mix_hash(@e.public_key)
72
+ @symmetric_state.mix_key(@e.public_key) if @protocol.psk_handshake?
68
73
  when 's'
69
- message_buffer << @symmetric_state.encrypt_and_hash(@s[1])
70
- next
74
+ message_buffer << @symmetric_state.encrypt_and_hash(@s.public_key)
71
75
  when 'ee'
72
- @symmetric_state.mix_key(dh_fn.dh(@e[0], @re[1]))
73
- next
76
+ @symmetric_state.mix_key(dh_fn.dh(@e.private_key, @re))
74
77
  when 'es'
75
- private_key, public_key = @initiator ? [@e[0], @rs[1]] : [@s[0], @re[1]]
78
+ private_key, public_key = @initiator ? [@e.private_key, @rs] : [@s.private_key, @re]
76
79
  @symmetric_state.mix_key(dh_fn.dh(private_key, public_key))
77
- next
78
80
  when 'se'
79
- private_key, public_key = @initiator ? [@s[0], @re[1]] : [@e[0], @rs[1]]
81
+ private_key, public_key = @initiator ? [@s.private_key, @re] : [@e.private_key, @rs]
80
82
  @symmetric_state.mix_key(dh_fn.dh(private_key, public_key))
81
- next
82
83
  when 'ss'
83
- @symmetric_state.mix_key(dh_fn.dh(@s[0], @rs[1]))
84
- next
84
+ @symmetric_state.mix_key(dh_fn.dh(@s.private_key, @rs))
85
85
  when 'psk'
86
- @symmetric_state.mix_key_and_hash(@protocol.psks.shift)
87
- next
86
+ @symmetric_state.mix_key_and_hash(@connection.psks.shift)
88
87
  end
89
88
  end
90
89
  message_buffer << @symmetric_state.encrypt_and_hash(payload)
91
90
  @symmetric_state.split if @message_patterns.empty?
92
91
  end
93
92
 
93
+ # Takes a byte sequence containing a Noise handshake message,
94
+ # and a payload_buffer to write the message's plaintext payload into
94
95
  def read_message(message, payload_buffer)
95
96
  pattern = @message_patterns.shift
96
97
  dh_fn = @protocol.dh_fn
@@ -98,44 +99,32 @@ module Noise
98
99
  pattern.each do |token|
99
100
  case token
100
101
  when 'e'
101
- @re = @protocol.dh_fn.class.from_public(message[0...len]) if @re.empty?
102
+ @re = message[0...len] if @re.nil?
102
103
  message = message[len..-1]
103
- @symmetric_state.mix_hash(@re[1])
104
- @symmetric_state.mix_key(@re[1]) if @protocol.psk_handshake?
105
- next
104
+ @symmetric_state.mix_hash(@re)
105
+ @symmetric_state.mix_key(@re) if @protocol.psk_handshake?
106
106
  when 's'
107
- offset = @protocol.cipher_state_handshake.key? ? 16 : 0
107
+ offset = @connection.cipher_state_handshake.key? ? 16 : 0
108
108
  temp = message[0...len + offset]
109
109
  message = message[(len + offset)..-1]
110
- @rs = @protocol.dh_fn.class.from_public(@symmetric_state.decrypt_and_hash(temp))
111
- next
110
+ @rs = @symmetric_state.decrypt_and_hash(temp)
112
111
  when 'ee'
113
- @symmetric_state.mix_key(dh_fn.dh(@e[0], @re[1]))
114
- next
112
+ @symmetric_state.mix_key(dh_fn.dh(@e.private_key, @re))
115
113
  when 'es'
116
- private_key, public_key = @initiator ? [@e[0], @rs[1]] : [@s[0], @re[1]]
114
+ private_key, public_key = @initiator ? [@e.private_key, @rs] : [@s.private_key, @re]
117
115
  @symmetric_state.mix_key(dh_fn.dh(private_key, public_key))
118
- next
119
116
  when 'se'
120
- private_key, public_key = @initiator ? [@s[0], @re[1]] : [@e[0], @rs[1]]
117
+ private_key, public_key = @initiator ? [@s.private_key, @re] : [@e.private_key, @rs]
121
118
  @symmetric_state.mix_key(dh_fn.dh(private_key, public_key))
122
- next
123
119
  when 'ss'
124
- @symmetric_state.mix_key(dh_fn.dh(@s[0], @rs[1]))
125
- next
120
+ @symmetric_state.mix_key(dh_fn.dh(@s.private_key, @rs))
126
121
  when 'psk'
127
- @symmetric_state.mix_key_and_hash(@protocol.psks.shift)
128
- next
122
+ @symmetric_state.mix_key_and_hash(@connection.psks.shift)
129
123
  end
130
124
  end
131
125
  payload_buffer << @symmetric_state.decrypt_and_hash(message)
132
126
  @symmetric_state.split if @message_patterns.empty?
133
127
  end
134
-
135
- # no need for ephemeral keys after handshake has completed.
136
- def keypairs
137
- { s: @s, rs: @rs }
138
- end
139
128
  end
140
129
  end
141
130
  end
@@ -11,21 +11,13 @@ 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
-
22
- def initialize_symmetric(protocol)
14
+ def initialize_symmetric(protocol, connection)
23
15
  @protocol = protocol
16
+ @connection = connection
24
17
  @ck = @h = initialize_h(protocol)
25
18
 
26
19
  @cipher_state = CipherState.new(cipher: @protocol.cipher_fn)
27
20
  @cipher_state.initialize_key(nil)
28
- @protocol.cipher_state_handshake = @cipher_state
29
21
  end
30
22
 
31
23
  def mix_key(input_key_meterial)
@@ -34,11 +26,12 @@ module Noise
34
26
  @cipher_state.initialize_key(temp_k)
35
27
  end
36
28
 
37
- # data [String] binary string
29
+ # @param [String] data binary string to be hashed
38
30
  def mix_hash(data)
39
31
  @h = @protocol.hash_fn.hash(@h + data)
40
32
  end
41
33
 
34
+ # This function is used for handling pre-shared symmetric keys.
42
35
  def mix_key_and_hash(input_key_meterial)
43
36
  @ck, temp_h, temp_k = @protocol.hkdf_fn.call(@ck, input_key_meterial, 3)
44
37
  mix_hash(temp_h)
@@ -46,42 +39,58 @@ module Noise
46
39
  @cipher_state.initialize_key(temp_k)
47
40
  end
48
41
 
42
+ # Returns h. This function should only be called at the end of a handshake,
43
+ # i.e. after the Split() function has been called.
44
+ # This function is used for channel binding
45
+ # @returns [String] h
49
46
  def handshake_hash
50
47
  @h
51
48
  end
52
49
 
50
+ # Note that if k is empty, the EncryptWithAd() call will set ciphertext equal to plaintext.
53
51
  def encrypt_and_hash(plaintext)
54
52
  ciphertext = @cipher_state.encrypt_with_ad(@h, plaintext)
55
53
  mix_hash(ciphertext)
56
54
  ciphertext
57
55
  end
58
56
 
57
+ # Note that if k is empty, the DecryptWithAd() call will set plaintext equal to ciphertext.
59
58
  def decrypt_and_hash(ciphertext)
60
59
  plaintext = @cipher_state.decrypt_with_ad(@h, ciphertext)
61
60
  mix_hash(ciphertext)
62
61
  plaintext
63
62
  end
64
63
 
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
-
64
+ # @return [CipherState, CipherState] a pair of CipherState objects for encrypting transport messages.
72
65
  def split
73
66
  temp_k1, temp_k2 = @protocol.hkdf_fn.call(@ck, '', 2)
74
67
  c1 = create_cipher_state(temp_k1)
75
68
  c2 = create_cipher_state(temp_k2)
76
- @protocol.cipher_state_encrypt = @protocol.initiator ? c1 : c2
77
- @protocol.cipher_state_decrypt = @protocol.initiator ? c2 : c1
78
- @protocol.handshake_done
69
+ @connection.handshake_done(c1, c2)
79
70
  [c1, c2]
80
71
  end
81
72
 
73
+ private
74
+
75
+ def initialize_h(protocol)
76
+ if protocol.name.length <= protocol.hash_fn.hashlen
77
+ protocol.name.ljust(protocol.hash_fn.hashlen, "\x00")
78
+ else
79
+ protocol.hash_fn.hash(protocol.name)
80
+ end
81
+ end
82
+
83
+ # truncates temp_k to 32 bytes if HASHLEN is 64
82
84
  def truncate(k)
83
85
  @protocol.hash_fn.hashlen == 64 ? k[0, 32] : k
84
86
  end
87
+
88
+ def create_cipher_state(k)
89
+ k = truncate(k)
90
+ c = CipherState.new(cipher: @protocol.cipher_fn)
91
+ c.initialize_key(k)
92
+ c
93
+ end
85
94
  end
86
95
  end
87
96
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Noise
4
- VERSION = '0.7.1'
4
+ VERSION = '0.7.3'
5
5
  end
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.7.1
4
+ version: 0.7.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Hajime Yamaguchi
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-04-19 00:00:00.000000000 Z
11
+ date: 2019-04-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -128,6 +128,9 @@ files:
128
128
  - bin/setup
129
129
  - lib/noise.rb
130
130
  - lib/noise/connection.rb
131
+ - lib/noise/connection/base.rb
132
+ - lib/noise/connection/initiator.rb
133
+ - lib/noise/connection/responder.rb
131
134
  - lib/noise/exceptions.rb
132
135
  - lib/noise/exceptions/decrypt_error.rb
133
136
  - lib/noise/exceptions/encrypt_error.rb
@@ -151,6 +154,7 @@ files:
151
154
  - lib/noise/functions/hash/blake2s.rb
152
155
  - lib/noise/functions/hash/sha256.rb
153
156
  - lib/noise/functions/hash/sha512.rb
157
+ - lib/noise/key.rb
154
158
  - lib/noise/key_pair.rb
155
159
  - lib/noise/pattern.rb
156
160
  - lib/noise/protocol.rb