Class: Noise::Connection::Base

Inherits:
Object
  • Object
show all
Defined in:
lib/noise/connection/base.rb

Direct Known Subclasses

Initiator, Responder

Constant Summary collapse

MAX_MESSAGE_LENGTH =

The Noise spec caps a handshake or transport message at 65535 bytes.

65_535

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(name, keypairs: { s: nil, e: nil, rs: nil, re: nil }) ⇒ Base

Returns a new instance of Base.



13
14
15
16
17
18
19
20
21
22
23
24
# File 'lib/noise/connection/base.rb', line 13

def initialize(name, keypairs: { s: nil, e: nil, rs: nil, re: nil })
  @protocol = Protocol.create(name)

  # parameter keypairs[:e] and keypairs[:s] are strings, so should convert Noise::Key object.
  @local_keypairs = {}
  @local_keypairs[:e] = @protocol.dh_fn.class.from_private(keypairs[:e]) if keypairs[:e]
  @local_keypairs[:s] = @protocol.dh_fn.class.from_private(keypairs[:s]) if keypairs[:s]
  @remote_keys = { rs: keypairs[:rs], re: keypairs[:re] }
  @handshake_started = false
  @handshake_finished = false
  initialize_next_message
end

Instance Attribute Details

#cipher_state_decryptObject (readonly)

Returns the value of attribute cipher_state_decrypt.



9
10
11
# File 'lib/noise/connection/base.rb', line 9

def cipher_state_decrypt
  @cipher_state_decrypt
end

#cipher_state_encryptObject (readonly)

Returns the value of attribute cipher_state_encrypt.



9
10
11
# File 'lib/noise/connection/base.rb', line 9

def cipher_state_encrypt
  @cipher_state_encrypt
end

#cipher_state_handshakeObject (readonly)

Returns the value of attribute cipher_state_handshake.



9
10
11
# File 'lib/noise/connection/base.rb', line 9

def cipher_state_handshake
  @cipher_state_handshake
end

#handshake_finishedObject (readonly)

Returns the value of attribute handshake_finished.



9
10
11
# File 'lib/noise/connection/base.rb', line 9

def handshake_finished
  @handshake_finished
end

#handshake_hashObject (readonly)

Returns the value of attribute handshake_hash.



9
10
11
# File 'lib/noise/connection/base.rb', line 9

def handshake_hash
  @handshake_hash
end

#handshake_startedObject (readonly)

Returns the value of attribute handshake_started.



9
10
11
# File 'lib/noise/connection/base.rb', line 9

def handshake_started
  @handshake_started
end

#handshake_stateObject (readonly)

Returns the value of attribute handshake_state.



9
10
11
# File 'lib/noise/connection/base.rb', line 9

def handshake_state
  @handshake_state
end

#prologueObject

Returns the value of attribute prologue.



11
12
13
# File 'lib/noise/connection/base.rb', line 11

def prologue
  @prologue
end

#protocolObject (readonly)

Returns the value of attribute protocol.



9
10
11
# File 'lib/noise/connection/base.rb', line 9

def protocol
  @protocol
end

#psksObject

Returns the value of attribute psks.



11
12
13
# File 'lib/noise/connection/base.rb', line 11

def psks
  @psks
end

#rsObject (readonly)

Returns the value of attribute rs.



9
10
11
# File 'lib/noise/connection/base.rb', line 9

def rs
  @rs
end

#sObject (readonly)

Returns the value of attribute s.



9
10
11
# File 'lib/noise/connection/base.rb', line 9

def s
  @s
end

Instance Method Details

#decrypt(data) ⇒ Object



94
95
96
97
98
# File 'lib/noise/connection/base.rb', line 94

def decrypt(data)
  raise Noise::Exceptions::NoiseHandshakeError unless @handshake_finished

  @cipher_state_decrypt.decrypt_with_ad('', data)
end

#encrypt(data) ⇒ Object



88
89
90
91
92
# File 'lib/noise/connection/base.rb', line 88

def encrypt(data)
  raise Noise::Exceptions::NoiseHandshakeError unless @handshake_finished

  @cipher_state_encrypt.encrypt_with_ad('', data)
end

#fallback(fallback_name) ⇒ Object

Restarts the handshake with a fallback pattern, carrying over the keys of the aborted one.

The roles swap here: the party that wrote the aborted message now reads, and the one that failed to read it now writes. Both sides are already in that state, so @next_message is deliberately left as it is rather than reset through initialize_next_message.



37
38
39
40
41
42
43
44
# File 'lib/noise/connection/base.rb', line 37

def fallback(fallback_name)
  @protocol = Protocol.create(fallback_name)
  @handshake_started = false
  @handshake_finished = false
  @local_keypairs = { e: @handshake_state.e, s: @handshake_state.s }
  @remote_keys = { re: @handshake_state.re, rs: @handshake_state.rs }
  start_handshake
end

#handshake_done(_c1, _c2) ⇒ Object



119
120
121
122
123
124
125
126
# File 'lib/noise/connection/base.rb', line 119

def handshake_done(_c1, _c2)
  @handshake_hash = @symmetric_state.handshake_hash
  @s = @handshake_state.s
  @rs = @handshake_state.rs
  @handshake_state = nil
  @symmetric_state = nil
  @cipher_state_handshake = nil
end

#initialise_handshake_stateObject



46
47
48
49
50
51
52
53
54
55
56
# File 'lib/noise/connection/base.rb', line 46

def initialise_handshake_state
  @handshake_state = Noise::State::HandshakeState.new(
    self,
    initiator?,
    @prologue,
    @local_keypairs,
    @remote_keys
  )
  @symmetric_state = @handshake_state.symmetric_state
  @cipher_state_handshake = @symmetric_state.cipher_state
end

#missing_keypairs?Boolean

Returns:

  • (Boolean)


106
107
108
109
# File 'lib/noise/connection/base.rb', line 106

def missing_keypairs?
  keypairs = @local_keypairs.merge(@remote_keys)
  @protocol.pattern.required_keypairs(initiator?).any? { |keypair| !keypairs[keypair] }
end

#read_message(data) ⇒ Object



74
75
76
77
78
79
80
81
82
83
84
85
86
# File 'lib/noise/connection/base.rb', line 74

def read_message(data)
  # Call NoiseConnection.start_handshake first
  raise Noise::Exceptions::NoiseHandshakeError unless @handshake_started
  raise Noise::Exceptions::NoiseHandshakeError if @next_message != :read
  raise Noise::Exceptions::NoiseHandshakeError if @handshake_finished
  raise Noise::Exceptions::NoiseHandshakeError, 'Message exceeds the maximum length.' if
    data.bytesize > MAX_MESSAGE_LENGTH

  @next_message = :write
  buffer = +''
  @handshake_finished = @handshake_state.read_message(data, buffer)
  buffer
end

#start_handshakeObject



26
27
28
29
30
# File 'lib/noise/connection/base.rb', line 26

def start_handshake
  validate
  initialise_handshake_state
  @handshake_started = true
end

#validateObject



111
112
113
114
115
116
117
# File 'lib/noise/connection/base.rb', line 111

def validate
  validate_psk! if @protocol.psk?

  raise Noise::Exceptions::NoiseValidationError if missing_keypairs?

  true
end

#validate_psk!Object



100
101
102
103
104
# File 'lib/noise/connection/base.rb', line 100

def validate_psk!
  # Invalid psk length! Has to be 32 bytes long
  raise Noise::Exceptions::NoisePSKError if @psks.any? { |psk| psk.bytesize != 32 }
  raise Noise::Exceptions::NoisePSKError if @protocol.pattern.psk_count != @psks.count
end

#write_message(payload = '') ⇒ Object



58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
# File 'lib/noise/connection/base.rb', line 58

def write_message(payload = '')
  # Call NoiseConnection.start_handshake first
  raise Noise::Exceptions::NoiseHandshakeError unless @handshake_started
  raise Noise::Exceptions::NoiseHandshakeError if @next_message != :write
  raise Noise::Exceptions::NoiseHandshakeError if @handshake_finished

  if @handshake_state.expected_message_length(payload.bytesize) > MAX_MESSAGE_LENGTH
    raise Noise::Exceptions::NoiseHandshakeError, 'Message exceeds the maximum length.'
  end

  @next_message = :read
  buffer = +''
  @handshake_finished = @handshake_state.write_message(payload, buffer)
  buffer
end