Class: Noise::Connection::Base
- Inherits:
-
Object
- Object
- Noise::Connection::Base
- Defined in:
- lib/noise/connection/base.rb
Constant Summary collapse
- MAX_MESSAGE_LENGTH =
The Noise spec caps a handshake or transport message at 65535 bytes. A transport message is the ciphertext, so the plaintext a caller may hand to encrypt is shorter by the authentication tag that ENCRYPT() appends.
65_535- MAX_PLAINTEXT_LENGTH =
MAX_MESSAGE_LENGTH - Noise::State::CipherState::TAG_LENGTH
Instance Attribute Summary collapse
-
#cipher_state_decrypt ⇒ Object
readonly
Returns the value of attribute cipher_state_decrypt.
-
#cipher_state_encrypt ⇒ Object
readonly
Returns the value of attribute cipher_state_encrypt.
-
#cipher_state_handshake ⇒ Object
readonly
Returns the value of attribute cipher_state_handshake.
-
#handshake_finished ⇒ Object
readonly
Returns the value of attribute handshake_finished.
-
#handshake_hash ⇒ Object
readonly
Returns the value of attribute handshake_hash.
-
#handshake_started ⇒ Object
readonly
Returns the value of attribute handshake_started.
-
#handshake_state ⇒ Object
readonly
Returns the value of attribute handshake_state.
-
#prologue ⇒ Object
Returns the value of attribute prologue.
-
#protocol ⇒ Object
readonly
Returns the value of attribute protocol.
-
#psks ⇒ Object
Returns the value of attribute psks.
-
#rs ⇒ Object
readonly
Returns the value of attribute rs.
-
#s ⇒ Object
readonly
Returns the value of attribute s.
Instance Method Summary collapse
- #decrypt(data) ⇒ Object
-
#decryption_nonce ⇒ Integer
The nonce the next #decrypt call uses.
-
#decryption_nonce=(nonce) ⇒ Object
Sets the nonce of the next #decrypt call.
- #encrypt(data) ⇒ Object
-
#encryption_nonce ⇒ Integer
The nonce the next #encrypt call uses.
-
#encryption_nonce=(nonce) ⇒ Object
Sets the nonce of the next #encrypt call.
-
#fallback(fallback_name) ⇒ Object
Restarts the handshake with a fallback pattern, carrying over the keys of the aborted one.
- #handshake_done(_c1, _c2) ⇒ Object
- #initialise_handshake_state ⇒ Object
-
#initialize(name, keypairs: { s: nil, e: nil, rs: nil, re: nil }) ⇒ Base
constructor
A new instance of Base.
- #missing_keypairs? ⇒ Boolean
- #read_message(data) ⇒ Object
-
#rekey_decryption ⇒ void
Replaces the key used by #decrypt with REKEY(k).
-
#rekey_encryption ⇒ void
Replaces the key used by #encrypt with REKEY(k), so that the old key cannot decrypt the messages that follow.
- #start_handshake ⇒ Object
- #validate ⇒ Object
- #validate_psk! ⇒ Object
- #write_message(payload = '') ⇒ Object
Constructor Details
#initialize(name, keypairs: { s: nil, e: nil, rs: nil, re: nil }) ⇒ Base
Returns a new instance of Base.
26 27 28 29 30 31 32 33 34 35 36 37 |
# File 'lib/noise/connection/base.rb', line 26 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 end |
Instance Attribute Details
#cipher_state_decrypt ⇒ Object (readonly)
Returns the value of attribute cipher_state_decrypt.
12 13 14 |
# File 'lib/noise/connection/base.rb', line 12 def cipher_state_decrypt @cipher_state_decrypt end |
#cipher_state_encrypt ⇒ Object (readonly)
Returns the value of attribute cipher_state_encrypt.
12 13 14 |
# File 'lib/noise/connection/base.rb', line 12 def cipher_state_encrypt @cipher_state_encrypt end |
#cipher_state_handshake ⇒ Object (readonly)
Returns the value of attribute cipher_state_handshake.
12 13 14 |
# File 'lib/noise/connection/base.rb', line 12 def cipher_state_handshake @cipher_state_handshake end |
#handshake_finished ⇒ Object (readonly)
Returns the value of attribute handshake_finished.
12 13 14 |
# File 'lib/noise/connection/base.rb', line 12 def handshake_finished @handshake_finished end |
#handshake_hash ⇒ Object (readonly)
Returns the value of attribute handshake_hash.
12 13 14 |
# File 'lib/noise/connection/base.rb', line 12 def handshake_hash @handshake_hash end |
#handshake_started ⇒ Object (readonly)
Returns the value of attribute handshake_started.
12 13 14 |
# File 'lib/noise/connection/base.rb', line 12 def handshake_started @handshake_started end |
#handshake_state ⇒ Object (readonly)
Returns the value of attribute handshake_state.
12 13 14 |
# File 'lib/noise/connection/base.rb', line 12 def handshake_state @handshake_state end |
#prologue ⇒ Object
Returns the value of attribute prologue.
14 15 16 |
# File 'lib/noise/connection/base.rb', line 14 def prologue @prologue end |
#protocol ⇒ Object (readonly)
Returns the value of attribute protocol.
12 13 14 |
# File 'lib/noise/connection/base.rb', line 12 def protocol @protocol end |
#psks ⇒ Object
Returns the value of attribute psks.
14 15 16 |
# File 'lib/noise/connection/base.rb', line 14 def psks @psks end |
#rs ⇒ Object (readonly)
Returns the value of attribute rs.
12 13 14 |
# File 'lib/noise/connection/base.rb', line 12 def rs @rs end |
#s ⇒ Object (readonly)
Returns the value of attribute s.
12 13 14 |
# File 'lib/noise/connection/base.rb', line 12 def s @s end |
Instance Method Details
#decrypt(data) ⇒ Object
116 117 118 119 120 121 122 123 124 125 126 |
# File 'lib/noise/connection/base.rb', line 116 def decrypt(data) cipher_state = transport_cipher_state(:decrypt) # Rejected before the cipher state is used, so an over-long message leaves n untouched # and the connection usable, exactly as a failed decryption does. if data.bytesize > MAX_MESSAGE_LENGTH raise Noise::Exceptions::MessageTooLongError, "Message is #{data.bytesize} bytes, which exceeds the maximum of #{MAX_MESSAGE_LENGTH}." end cipher_state.decrypt_with_ad('', data) end |
#decryption_nonce ⇒ Integer
Returns the nonce the next #decrypt call uses.
134 135 136 |
# File 'lib/noise/connection/base.rb', line 134 def decryption_nonce transport_cipher_state(:decrypt).n end |
#decryption_nonce=(nonce) ⇒ Object
Sets the nonce of the next #decrypt call. This is how the Noise spec handles transport messages that arrive out of order: the receiver sets n to the nonce of the message it is about to decrypt, and restores the previous value if the message fails to authenticate.
151 152 153 |
# File 'lib/noise/connection/base.rb', line 151 def decryption_nonce=(nonce) transport_cipher_state(:decrypt).nonce = nonce end |
#encrypt(data) ⇒ Object
106 107 108 109 110 111 112 113 114 |
# File 'lib/noise/connection/base.rb', line 106 def encrypt(data) cipher_state = transport_cipher_state(:encrypt) if data.bytesize > MAX_PLAINTEXT_LENGTH raise Noise::Exceptions::MessageTooLongError, "Plaintext is #{data.bytesize} bytes, which exceeds the maximum of #{MAX_PLAINTEXT_LENGTH}." end cipher_state.encrypt_with_ad('', data) end |
#encryption_nonce ⇒ Integer
Returns the nonce the next #encrypt call uses.
129 130 131 |
# File 'lib/noise/connection/base.rb', line 129 def encryption_nonce transport_cipher_state(:encrypt).n end |
#encryption_nonce=(nonce) ⇒ Object
Sets the nonce of the next #encrypt call. Needed when the transport layer numbers the messages itself instead of relying on the sender and the receiver counting in step.
142 143 144 |
# File 'lib/noise/connection/base.rb', line 142 def encryption_nonce=(nonce) transport_cipher_state(:encrypt).nonce = nonce 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.
50 51 52 53 54 55 56 57 |
# File 'lib/noise/connection/base.rb', line 50 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
198 199 200 201 202 203 204 205 |
# File 'lib/noise/connection/base.rb', line 198 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_state ⇒ Object
59 60 61 62 63 64 65 66 67 68 69 |
# File 'lib/noise/connection/base.rb', line 59 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
185 186 187 188 |
# File 'lib/noise/connection/base.rb', line 185 def missing_keypairs? keypairs = @local_keypairs.merge(@remote_keys) @protocol.pattern.required_keypairs(initiator?).any? { |keypair| !keypairs[keypair] } end |
#read_message(data) ⇒ Object
89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 |
# File 'lib/noise/connection/base.rb', line 89 def (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 if data.bytesize > MAX_MESSAGE_LENGTH raise Noise::Exceptions::MessageTooLongError, "Message is #{data.bytesize} bytes, which exceeds the maximum of #{MAX_MESSAGE_LENGTH}." end @next_message = :write buffer = +'' @handshake_finished = @handshake_state.(data, buffer) buffer end |
#rekey_decryption ⇒ void
This method returns an undefined value.
Replaces the key used by #decrypt with REKEY(k). See #rekey_encryption.
168 169 170 171 |
# File 'lib/noise/connection/base.rb', line 168 def rekey_decryption transport_cipher_state(:decrypt).rekey nil end |
#rekey_encryption ⇒ void
This method returns an undefined value.
Replaces the key used by #encrypt with REKEY(k), so that the old key cannot decrypt the messages that follow. Both parties must rekey the matching direction at the same point of the message stream, which is up to the application protocol to agree on.
160 161 162 163 |
# File 'lib/noise/connection/base.rb', line 160 def rekey_encryption transport_cipher_state(:encrypt).rekey nil end |
#start_handshake ⇒ Object
39 40 41 42 43 |
# File 'lib/noise/connection/base.rb', line 39 def start_handshake validate initialise_handshake_state @handshake_started = true end |
#validate ⇒ Object
190 191 192 193 194 195 196 |
# File 'lib/noise/connection/base.rb', line 190 def validate validate_psk! if @protocol.psk? raise Noise::Exceptions::NoiseValidationError if missing_keypairs? true end |
#validate_psk! ⇒ Object
173 174 175 176 177 178 179 180 181 182 183 |
# File 'lib/noise/connection/base.rb', line 173 def validate_psk! raise Noise::Exceptions::NoisePSKError, 'psks are not set.' if @psks.nil? # Invalid psk length! Has to be 32 bytes long raise Noise::Exceptions::NoisePSKError, 'psks have to be 32 bytes long.' if @psks.any? { |psk| psk.bytesize != 32 } return if @protocol.pattern.psk_count == @psks.count raise Noise::Exceptions::NoisePSKError, "This protocol needs #{@protocol.pattern.psk_count} psks, got #{@psks.count}." end |
#write_message(payload = '') ⇒ Object
71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 |
# File 'lib/noise/connection/base.rb', line 71 def (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 length = @handshake_state.(payload.bytesize) if length > MAX_MESSAGE_LENGTH raise Noise::Exceptions::MessageTooLongError, "Message would be #{length} bytes, which exceeds the maximum of #{MAX_MESSAGE_LENGTH}." end @next_message = :read buffer = +'' @handshake_finished = @handshake_state.(payload, buffer) buffer end |