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, protocol, 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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
# File 'lib/noise/state/handshake_state.rb', line 23

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

  get_local_keypair = ->(token) { instance_variable_get('@' + token).public_key }
  get_remote_keypair = ->(token) { instance_variable_get('@r' + token) }

  if initiator
    initiator_keypair_getter = get_local_keypair
    responder_keypair_getter = get_remote_keypair
  else
    initiator_keypair_getter = get_remote_keypair
    responder_keypair_getter = get_local_keypair
  end

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

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

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

  @protocol.pattern.responder_pre_messages&.map do |token|
    keypair = responder_keypair_getter.call(token)
    @symmetric_state.mix_hash(keypair)
  end
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



66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
# File 'lib/noise/state/handshake_state.rb', line 66

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 'e'
      l += @protocol.dh_fn.dhlen
      has_key = true if @protocol.psk_handshake?
    when 's'
      l += @protocol.dh_fn.dhlen
      l += 16 if has_key
    when 'ee', 'es', 'se', 'ss', 'psk'
      has_key = true
    end
    l
  end
  len += payload_size
  len += 16 if has_key
  len
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



121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
# File 'lib/noise/state/handshake_state.rb', line 121

def read_message(message, payload_buffer)
  pattern = @message_patterns.shift
  dh_fn = @protocol.dh_fn
  len = dh_fn.dhlen
  pattern.each do |token|
    case token
    when 'e'
      @re = message[0...len] if @re.nil?
      message = message[len..-1]
      @symmetric_state.mix_hash(@re)
      @symmetric_state.mix_key(@re) if @protocol.psk_handshake?
    when 's'
      offset = @connection.cipher_state_handshake.key? ? 16 : 0
      temp = message[0...len + offset]
      message = message[(len + offset)..-1]
      @rs = @symmetric_state.decrypt_and_hash(temp)
    when 'ee'
      @symmetric_state.mix_key(dh_fn.dh(@e.private_key, @re))
    when 'es'
      private_key, public_key = @initiator ? [@e.private_key, @rs] : [@s.private_key, @re]
      @symmetric_state.mix_key(dh_fn.dh(private_key, public_key))
    when 'se'
      private_key, public_key = @initiator ? [@s.private_key, @re] : [@e.private_key, @rs]
      @symmetric_state.mix_key(dh_fn.dh(private_key, public_key))
    when 'ss'
      @symmetric_state.mix_key(dh_fn.dh(@s.private_key, @rs))
    when 'psk'
      @symmetric_state.mix_key_and_hash(@connection.psks.shift)
    end
  end
  payload_buffer << @symmetric_state.decrypt_and_hash(message)
  @symmetric_state.split if @message_patterns.empty?
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



88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
# File 'lib/noise/state/handshake_state.rb', line 88

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

  pattern.each do |token|
    case token
    when 'e'
      @e = dh_fn.generate_keypair if @e.nil?
      message_buffer << @e.public_key
      @symmetric_state.mix_hash(@e.public_key)
      @symmetric_state.mix_key(@e.public_key) if @protocol.psk_handshake?
    when 's'
      message_buffer << @symmetric_state.encrypt_and_hash(@s.public_key)
    when 'ee'
      @symmetric_state.mix_key(dh_fn.dh(@e.private_key, @re))
    when 'es'
      private_key, public_key = @initiator ? [@e.private_key, @rs] : [@s.private_key, @re]
      @symmetric_state.mix_key(dh_fn.dh(private_key, public_key))
    when 'se'
      private_key, public_key = @initiator ? [@s.private_key, @re] : [@e.private_key, @rs]
      @symmetric_state.mix_key(dh_fn.dh(private_key, public_key))
    when 'ss'
      @symmetric_state.mix_key(dh_fn.dh(@s.private_key, @rs))
    when 'psk'
      @symmetric_state.mix_key_and_hash(@connection.psks.shift)
    end
  end
  message_buffer << @symmetric_state.encrypt_and_hash(payload)
  @symmetric_state.split if @message_patterns.empty?
end