Class: Noise::State::HandshakeState

Inherits:
Object
  • Object
show all
Defined in:
lib/noise/state/handshake_state.rb

Overview

A HandshakeState object contains a SymmetricState plus the following variables, any of which may be empty. Empty is a special value which indicates the variable has not yet been initialized.

s: The local static key pair e: The local ephemeral key pair rs: The remote party’s static public key re: The remote party’s ephemeral public key

A HandshakeState also has variables to track its role, and the remaining portion of the handshake pattern:

initiator: A boolean indicating the initiator or responder role.

message_patterns: A sequence of message patterns.

Each message pattern is a sequence of tokens from the set ("e", "s", "ee", "es", "se", "ss").

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(connection, initiator, prologue, local_keypairs, remote_keys) ⇒ HandshakeState

Returns a new instance of HandshakeState.



23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
# File 'lib/noise/state/handshake_state.rb', line 23

def initialize(connection, initiator, prologue, local_keypairs, remote_keys)
  @connection = connection
  @protocol = connection.protocol
  @symmetric_state = SymmetricState.initialize_symmetric(@protocol, connection, prologue: prologue)
  @initiator = initiator
  @s = local_keypairs[:s]
  @e = local_keypairs[:e]
  @rs = remote_keys[:rs]
  @re = remote_keys[:re]

  initiator_keypair_getter, responder_keypair_getter = get_keypair_getter(initiator)

  # Sets message_patterns to the message patterns from handshake_pattern
  @message_patterns = @protocol.pattern.tokens.dup

  process_initiator_pre_messages(initiator_keypair_getter)
  process_fallback(initiator_keypair_getter)
  process_responder_pre_messages(responder_keypair_getter)
end

Instance Attribute Details

#eObject (readonly)

Returns the value of attribute e.



21
22
23
# File 'lib/noise/state/handshake_state.rb', line 21

def e
  @e
end

#message_patternsObject (readonly)

Returns the value of attribute message_patterns.



20
21
22
# File 'lib/noise/state/handshake_state.rb', line 20

def message_patterns
  @message_patterns
end

#reObject (readonly)

Returns the value of attribute re.



21
22
23
# File 'lib/noise/state/handshake_state.rb', line 21

def re
  @re
end

#rsObject (readonly)

Returns the value of attribute rs.



21
22
23
# File 'lib/noise/state/handshake_state.rb', line 21

def rs
  @rs
end

#sObject (readonly)

Returns the value of attribute s.



21
22
23
# File 'lib/noise/state/handshake_state.rb', line 21

def s
  @s
end

#symmetric_stateObject (readonly)

Returns the value of attribute symmetric_state.



20
21
22
# File 'lib/noise/state/handshake_state.rb', line 20

def symmetric_state
  @symmetric_state
end

Instance Method Details

#expected_message_length(payload_size) ⇒ Object



81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
# File 'lib/noise/state/handshake_state.rb', line 81

def expected_message_length(payload_size)
  has_key = @symmetric_state.cipher_state.key?
  pattern = @message_patterns.first
  len = pattern.inject(0) do |l, token|
    case token
    when Noise::Token::E
      l += @protocol.dh_fn.dhlen
      has_key = true if @protocol.psk?
    when Noise::Token::S
      l += @protocol.dh_fn.dhlen
      l += 16 if has_key
    else
      has_key = true
    end
    l
  end
  len += payload_size
  len += 16 if has_key
  len
end

#get_keypair_getter(initiator) ⇒ Object



43
44
45
46
47
48
49
# File 'lib/noise/state/handshake_state.rb', line 43

def get_keypair_getter(initiator)
  if initiator
    [local_keypair_getter, remote_keypair_getter]
  else
    [remote_keypair_getter, local_keypair_getter]
  end
end

#local_keypair_getterObject



51
52
53
# File 'lib/noise/state/handshake_state.rb', line 51

def local_keypair_getter
  ->(token) { instance_variable_get('@' + token.to_s).public_key }
end

#process_fallback(initiator_keypair_getter) ⇒ Object



66
67
68
69
70
71
72
# File 'lib/noise/state/handshake_state.rb', line 66

def process_fallback(initiator_keypair_getter)
  return unless @protocol.pattern.fallback

  message = @message_patterns.delete_at(0).first
  public_key = initiator_keypair_getter.call(message)
  @symmetric_state.mix_hash(public_key)
end

#process_initiator_pre_messages(keypair_getter) ⇒ Object



59
60
61
62
63
64
# File 'lib/noise/state/handshake_state.rb', line 59

def process_initiator_pre_messages(keypair_getter)
  @protocol.pattern.initiator_pre_messages&.map do |token|
    keypair = keypair_getter.call(token)
    @symmetric_state.mix_hash(keypair)
  end
end

#process_responder_pre_messages(keypair_getter) ⇒ Object



74
75
76
77
78
79
# File 'lib/noise/state/handshake_state.rb', line 74

def process_responder_pre_messages(keypair_getter)
  @protocol.pattern.responder_pre_messages&.map do |token|
    keypair = keypair_getter.call(token)
    @symmetric_state.mix_hash(keypair)
  end
end

#read_message(message, payload_buffer) ⇒ Object

Takes a byte sequence containing a Noise handshake message, and a payload_buffer to write the message’s plaintext payload into



126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
# File 'lib/noise/state/handshake_state.rb', line 126

def read_message(message, payload_buffer)
  pattern = @message_patterns.shift
  pattern.each do |token|
    case token
    when Noise::Token::E
      message, re = extract_key(message, false)
      @re ||= re
      mix_e(@re)
    when Noise::Token::S
      message, @rs = extract_key(message, true)
    when Noise::Token::EE, Noise::Token::ES, Noise::Token::SE, Noise::Token::SS
      token.mix(@symmetric_state, @protocol.dh_fn, @initiator, self)
    when Noise::Token::PSK
      mix_psk
    end
  end
  payload_buffer << @symmetric_state.decrypt_and_hash(message)
  @symmetric_state.split if @message_patterns.empty?
end

#remote_keypair_getterObject



55
56
57
# File 'lib/noise/state/handshake_state.rb', line 55

def remote_keypair_getter
  ->(token) { instance_variable_get('@r' + token.to_s) }
end

#write_message(payload, message_buffer) ⇒ Object

Takes a payload byte sequence which may be zero-length, and a message_buffer to write the output into



103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
# File 'lib/noise/state/handshake_state.rb', line 103

def write_message(payload, message_buffer)
  pattern = @message_patterns.shift

  pattern.each do |token|
    case token
    when Noise::Token::E
      @e ||= @protocol.dh_fn.generate_keypair
      message_buffer << @e.public_key
      mix_e(@e.public_key)
    when Noise::Token::S
      message_buffer << @symmetric_state.encrypt_and_hash(@s.public_key)
    when Noise::Token::EE, Noise::Token::ES, Noise::Token::SE, Noise::Token::SS
      token.mix(@symmetric_state, @protocol.dh_fn, @initiator, self)
    when Noise::Token::PSK
      mix_psk
    end
  end
  message_buffer << @symmetric_state.encrypt_and_hash(payload)
  @symmetric_state.split if @message_patterns.empty?
end