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.
16 17 18 19 20 21 22 23 24 25 26 27 |
# File 'lib/noise/connection/base.rb', line 16 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
101 102 103 104 105 106 107 108 109 110 111 |
# File 'lib/noise/connection/base.rb', line 101 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.
119 120 121 |
# File 'lib/noise/connection/base.rb', line 119 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.
136 137 138 |
# File 'lib/noise/connection/base.rb', line 136 def decryption_nonce=(nonce) transport_cipher_state(:decrypt).nonce = nonce end |
#encrypt(data) ⇒ Object
91 92 93 94 95 96 97 98 99 |
# File 'lib/noise/connection/base.rb', line 91 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.
114 115 116 |
# File 'lib/noise/connection/base.rb', line 114 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.
127 128 129 |
# File 'lib/noise/connection/base.rb', line 127 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.
40 41 42 43 44 45 46 47 |
# File 'lib/noise/connection/base.rb', line 40 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
183 184 185 186 187 188 189 190 |
# File 'lib/noise/connection/base.rb', line 183 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
49 50 51 52 53 54 55 56 57 58 59 |
# File 'lib/noise/connection/base.rb', line 49 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
170 171 172 173 |
# File 'lib/noise/connection/base.rb', line 170 def missing_keypairs? keypairs = @local_keypairs.merge(@remote_keys) @protocol.pattern.required_keypairs(initiator?).any? { |keypair| !keypairs[keypair] } end |
#read_message(data) ⇒ Object
77 78 79 80 81 82 83 84 85 86 87 88 89 |
# File 'lib/noise/connection/base.rb', line 77 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 raise Noise::Exceptions::NoiseHandshakeError, 'Message exceeds the maximum length.' if data.bytesize > MAX_MESSAGE_LENGTH @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.
153 154 155 156 |
# File 'lib/noise/connection/base.rb', line 153 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.
145 146 147 148 |
# File 'lib/noise/connection/base.rb', line 145 def rekey_encryption transport_cipher_state(:encrypt).rekey nil end |
#start_handshake ⇒ Object
29 30 31 32 33 |
# File 'lib/noise/connection/base.rb', line 29 def start_handshake validate initialise_handshake_state @handshake_started = true end |
#validate ⇒ Object
175 176 177 178 179 180 181 |
# File 'lib/noise/connection/base.rb', line 175 def validate validate_psk! if @protocol.psk? raise Noise::Exceptions::NoiseValidationError if missing_keypairs? true end |
#validate_psk! ⇒ Object
158 159 160 161 162 163 164 165 166 167 168 |
# File 'lib/noise/connection/base.rb', line 158 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
61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 |
# File 'lib/noise/connection/base.rb', line 61 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 if @handshake_state.(payload.bytesize) > MAX_MESSAGE_LENGTH raise Noise::Exceptions::NoiseHandshakeError, 'Message exceeds the maximum length.' end @next_message = :read buffer = +'' @handshake_finished = @handshake_state.(payload, buffer) buffer end |