From 6173b4b6c7acf1de7daf42a094d9f4be47b8d778 Mon Sep 17 00:00:00 2001 From: bwatters-r7 Date: Fri, 29 May 2026 15:10:35 -0500 Subject: [PATCH] Add binary conversion calls and rspec tests --- modules/encoders/riscv32le/byte_xori.rb | 2 +- modules/encoders/riscv32le/longxor.rb | 4 +- .../encoders/riscv32le/longxor_feedback.rb | 6 +- modules/encoders/riscv32le/longxor_tag.rb | 4 +- modules/encoders/riscv64le/byte_xori.rb | 2 +- modules/encoders/riscv64le/longxor.rb | 4 +- .../encoders/riscv64le/longxor_feedback.rb | 6 +- modules/encoders/riscv64le/longxor_tag.rb | 4 +- .../encoders/riscv32le/byte_xori_spec.rb | 6 + .../riscv32le/longxor_feedback_spec.rb | 6 + .../encoders/riscv32le/longxor_spec.rb | 6 + .../encoders/riscv32le/longxor_tag_spec.rb | 6 + .../encoders/riscv64le/byte_xori_spec.rb | 6 + .../riscv64le/longxor_feedback_spec.rb | 6 + .../encoders/riscv64le/longxor_spec.rb | 6 + .../encoders/riscv64le/longxor_tag_spec.rb | 6 + .../encoders/riscv_xor_encoder_examples.rb | 330 ++++++++++++++++++ 17 files changed, 394 insertions(+), 16 deletions(-) create mode 100644 spec/modules/encoders/riscv32le/byte_xori_spec.rb create mode 100644 spec/modules/encoders/riscv32le/longxor_feedback_spec.rb create mode 100644 spec/modules/encoders/riscv32le/longxor_spec.rb create mode 100644 spec/modules/encoders/riscv32le/longxor_tag_spec.rb create mode 100644 spec/modules/encoders/riscv64le/byte_xori_spec.rb create mode 100644 spec/modules/encoders/riscv64le/longxor_feedback_spec.rb create mode 100644 spec/modules/encoders/riscv64le/longxor_spec.rb create mode 100644 spec/modules/encoders/riscv64le/longxor_tag_spec.rb create mode 100644 spec/modules/encoders/riscv_xor_encoder_examples.rb diff --git a/modules/encoders/riscv32le/byte_xori.rb b/modules/encoders/riscv32le/byte_xori.rb index 79662727e2086..6d350a5ac1c7e 100644 --- a/modules/encoders/riscv32le/byte_xori.rb +++ b/modules/encoders/riscv32le/byte_xori.rb @@ -34,7 +34,7 @@ def initialize # the buffer being encoded. # def decoder_stub(state) - if state.badchars.to_s.include?("\x00") + if state.badchars.to_s.include?("\x00".b) raise EncodingError, 'The RISC-V decoder stub inherently contains null bytes (auipc, ecall)' end diff --git a/modules/encoders/riscv32le/longxor.rb b/modules/encoders/riscv32le/longxor.rb index 2219877ad2351..15dbd72b7eea1 100644 --- a/modules/encoders/riscv32le/longxor.rb +++ b/modules/encoders/riscv32le/longxor.rb @@ -32,7 +32,7 @@ def initialize # the buffer being encoded. # def decoder_stub(state) - if state.badchars.to_s.include?("\x00") + if state.badchars.to_s.include?("\x00".b) raise EncodingError, 'The RISC-V decoder stub inherently contains null bytes (auipc, ecall)' end @@ -84,7 +84,7 @@ def decoder_stub(state) ].pack('V*') state.decoder_key_offset = decoder.length - decoder + "\x00\x00\x00\x00" + decoder + "\x00\x00\x00\x00".b end private diff --git a/modules/encoders/riscv32le/longxor_feedback.rb b/modules/encoders/riscv32le/longxor_feedback.rb index 37230d3028456..38018776b7157 100644 --- a/modules/encoders/riscv32le/longxor_feedback.rb +++ b/modules/encoders/riscv32le/longxor_feedback.rb @@ -35,7 +35,7 @@ def initialize # the buffer being encoded. # def decoder_stub(state) - if state.badchars.to_s.include?("\x00") + if state.badchars.to_s.include?("\x00".b) raise EncodingError, 'The RISC-V decoder stub inherently contains null bytes (auipc, ecall)' end @@ -89,7 +89,7 @@ def decoder_stub(state) ].pack('V*') state.decoder_key_offset = decoder.length - decoder + "\x00\x00\x00\x00" + decoder + "\x00\x00\x00\x00".b end # @@ -120,7 +120,7 @@ def find_key_verify(buf, key_bytes, badchars) feedback = key_bytes_to_integer(key_bytes) buf.bytes.each_slice(4) do |bytes| - block = bytes.pack('C*').ljust(4, "\x00") + block = bytes.pack('C*').ljust(4, "\x00".b) encoded_val = block.unpack1('V') ^ feedback return false unless has_badchars?([encoded_val].pack('V'), badchars).nil? diff --git a/modules/encoders/riscv32le/longxor_tag.rb b/modules/encoders/riscv32le/longxor_tag.rb index e7f41b0ac0dc7..5e7b0e4e25592 100644 --- a/modules/encoders/riscv32le/longxor_tag.rb +++ b/modules/encoders/riscv32le/longxor_tag.rb @@ -38,7 +38,7 @@ def initialize # the buffer being encoded. # def decoder_stub(state) - if state.badchars.to_s.include?("\x00") + if state.badchars.to_s.include?("\x00".b) raise EncodingError, 'The RISC-V decoder stub inherently contains null bytes (auipc, ecall)' end @@ -92,7 +92,7 @@ def decoder_stub(state) ].pack('V*') state.decoder_key_offset = decoder.length - decoder + "\x00\x00\x00\x00" + decoder + "\x00\x00\x00\x00".b end # diff --git a/modules/encoders/riscv64le/byte_xori.rb b/modules/encoders/riscv64le/byte_xori.rb index 75082a46857d1..ccada5182f31b 100644 --- a/modules/encoders/riscv64le/byte_xori.rb +++ b/modules/encoders/riscv64le/byte_xori.rb @@ -34,7 +34,7 @@ def initialize # the buffer being encoded. # def decoder_stub(state) - if state.badchars.to_s.include?("\x00") + if state.badchars.to_s.include?("\x00".b) raise EncodingError, 'The RISC-V decoder stub inherently contains null bytes (auipc, ecall)' end diff --git a/modules/encoders/riscv64le/longxor.rb b/modules/encoders/riscv64le/longxor.rb index 21f153e7e9aea..6b914285a03a8 100644 --- a/modules/encoders/riscv64le/longxor.rb +++ b/modules/encoders/riscv64le/longxor.rb @@ -32,7 +32,7 @@ def initialize # the buffer being encoded. # def decoder_stub(state) - if state.badchars.to_s.include?("\x00") + if state.badchars.to_s.include?("\x00".b) raise EncodingError, 'The RISC-V decoder stub inherently contains null bytes (auipc, ecall)' end @@ -84,7 +84,7 @@ def decoder_stub(state) ].pack('V*') state.decoder_key_offset = decoder.length - decoder + "\x00\x00\x00\x00" + decoder + "\x00\x00\x00\x00".b end private diff --git a/modules/encoders/riscv64le/longxor_feedback.rb b/modules/encoders/riscv64le/longxor_feedback.rb index 766e5869a271c..1d186d8fcb3dd 100644 --- a/modules/encoders/riscv64le/longxor_feedback.rb +++ b/modules/encoders/riscv64le/longxor_feedback.rb @@ -35,7 +35,7 @@ def initialize # the buffer being encoded. # def decoder_stub(state) - if state.badchars.to_s.include?("\x00") + if state.badchars.to_s.include?("\x00".b) raise EncodingError, 'The RISC-V decoder stub inherently contains null bytes (auipc, ecall)' end @@ -89,7 +89,7 @@ def decoder_stub(state) ].pack('V*') state.decoder_key_offset = decoder.length - decoder + "\x00\x00\x00\x00" + decoder + "\x00\x00\x00\x00".b end # @@ -120,7 +120,7 @@ def find_key_verify(buf, key_bytes, badchars) feedback = key_bytes_to_integer(key_bytes) buf.bytes.each_slice(4) do |bytes| - block = bytes.pack('C*').ljust(4, "\x00") + block = bytes.pack('C*').ljust(4, "\x00".b) encoded_val = block.unpack1('V') ^ feedback return false unless has_badchars?([encoded_val].pack('V'), badchars).nil? diff --git a/modules/encoders/riscv64le/longxor_tag.rb b/modules/encoders/riscv64le/longxor_tag.rb index 30bca3efd6be5..9323e554ac3eb 100644 --- a/modules/encoders/riscv64le/longxor_tag.rb +++ b/modules/encoders/riscv64le/longxor_tag.rb @@ -38,7 +38,7 @@ def initialize # the buffer being encoded. # def decoder_stub(state) - if state.badchars.to_s.include?("\x00") + if state.badchars.to_s.include?("\x00".b) raise EncodingError, 'The RISC-V decoder stub inherently contains null bytes (auipc, ecall)' end @@ -92,7 +92,7 @@ def decoder_stub(state) ].pack('V*') state.decoder_key_offset = decoder.length - decoder + "\x00\x00\x00\x00" + decoder + "\x00\x00\x00\x00".b end # diff --git a/spec/modules/encoders/riscv32le/byte_xori_spec.rb b/spec/modules/encoders/riscv32le/byte_xori_spec.rb new file mode 100644 index 0000000000000..9e2d1f9c2fa76 --- /dev/null +++ b/spec/modules/encoders/riscv32le/byte_xori_spec.rb @@ -0,0 +1,6 @@ +require 'spec_helper' +require_relative '../riscv_xor_encoder_examples' + +RSpec.describe 'modules/encoders/riscv32le/byte_xori' do + it_behaves_like 'riscv byte_xori encoder', 'riscv32le/byte_xori' +end diff --git a/spec/modules/encoders/riscv32le/longxor_feedback_spec.rb b/spec/modules/encoders/riscv32le/longxor_feedback_spec.rb new file mode 100644 index 0000000000000..713c90f45450b --- /dev/null +++ b/spec/modules/encoders/riscv32le/longxor_feedback_spec.rb @@ -0,0 +1,6 @@ +require 'spec_helper' +require_relative '../riscv_xor_encoder_examples' + +RSpec.describe 'modules/encoders/riscv32le/longxor_feedback' do + it_behaves_like 'riscv longxor_feedback encoder', 'riscv32le/longxor_feedback' +end diff --git a/spec/modules/encoders/riscv32le/longxor_spec.rb b/spec/modules/encoders/riscv32le/longxor_spec.rb new file mode 100644 index 0000000000000..15920b7c0b080 --- /dev/null +++ b/spec/modules/encoders/riscv32le/longxor_spec.rb @@ -0,0 +1,6 @@ +require 'spec_helper' +require_relative '../riscv_xor_encoder_examples' + +RSpec.describe 'modules/encoders/riscv32le/longxor' do + it_behaves_like 'riscv longxor encoder', 'riscv32le/longxor' +end diff --git a/spec/modules/encoders/riscv32le/longxor_tag_spec.rb b/spec/modules/encoders/riscv32le/longxor_tag_spec.rb new file mode 100644 index 0000000000000..d05c16d8741e6 --- /dev/null +++ b/spec/modules/encoders/riscv32le/longxor_tag_spec.rb @@ -0,0 +1,6 @@ +require 'spec_helper' +require_relative '../riscv_xor_encoder_examples' + +RSpec.describe 'modules/encoders/riscv32le/longxor_tag' do + it_behaves_like 'riscv longxor_tag encoder', 'riscv32le/longxor_tag' +end diff --git a/spec/modules/encoders/riscv64le/byte_xori_spec.rb b/spec/modules/encoders/riscv64le/byte_xori_spec.rb new file mode 100644 index 0000000000000..3c9dfe9d13488 --- /dev/null +++ b/spec/modules/encoders/riscv64le/byte_xori_spec.rb @@ -0,0 +1,6 @@ +require 'spec_helper' +require_relative '../riscv_xor_encoder_examples' + +RSpec.describe 'modules/encoders/riscv64le/byte_xori' do + it_behaves_like 'riscv byte_xori encoder', 'riscv64le/byte_xori' +end diff --git a/spec/modules/encoders/riscv64le/longxor_feedback_spec.rb b/spec/modules/encoders/riscv64le/longxor_feedback_spec.rb new file mode 100644 index 0000000000000..de4db0ea2c18f --- /dev/null +++ b/spec/modules/encoders/riscv64le/longxor_feedback_spec.rb @@ -0,0 +1,6 @@ +require 'spec_helper' +require_relative '../riscv_xor_encoder_examples' + +RSpec.describe 'modules/encoders/riscv64le/longxor_feedback' do + it_behaves_like 'riscv longxor_feedback encoder', 'riscv64le/longxor_feedback' +end diff --git a/spec/modules/encoders/riscv64le/longxor_spec.rb b/spec/modules/encoders/riscv64le/longxor_spec.rb new file mode 100644 index 0000000000000..5eb2abbf8e425 --- /dev/null +++ b/spec/modules/encoders/riscv64le/longxor_spec.rb @@ -0,0 +1,6 @@ +require 'spec_helper' +require_relative '../riscv_xor_encoder_examples' + +RSpec.describe 'modules/encoders/riscv64le/longxor' do + it_behaves_like 'riscv longxor encoder', 'riscv64le/longxor' +end diff --git a/spec/modules/encoders/riscv64le/longxor_tag_spec.rb b/spec/modules/encoders/riscv64le/longxor_tag_spec.rb new file mode 100644 index 0000000000000..c6312cbc3aa06 --- /dev/null +++ b/spec/modules/encoders/riscv64le/longxor_tag_spec.rb @@ -0,0 +1,6 @@ +require 'spec_helper' +require_relative '../riscv_xor_encoder_examples' + +RSpec.describe 'modules/encoders/riscv64le/longxor_tag' do + it_behaves_like 'riscv longxor_tag encoder', 'riscv64le/longxor_tag' +end diff --git a/spec/modules/encoders/riscv_xor_encoder_examples.rb b/spec/modules/encoders/riscv_xor_encoder_examples.rb new file mode 100644 index 0000000000000..5ea14a58d652c --- /dev/null +++ b/spec/modules/encoders/riscv_xor_encoder_examples.rb @@ -0,0 +1,330 @@ +# Shared examples for RISC-V XOR encoder modules. +# Each shared example group accepts the encoder reference name as a parameter. + +def make_encoder_state(buf, badchars = ''.b, key = 0xdeadbeef) + state = Msf::EncoderState.new(key) + state.buf = buf + state.badchars = badchars + state +end + +RSpec.shared_examples 'riscv byte_xori encoder' do |ref_name| + include_context 'Msf::Simple::Framework#modules loading' + + let(:encoder) { load_and_create_module(module_type: 'encoder', reference_name: ref_name) } + let(:valid_payload) { ("\xde\xad\xbe\xef" * 4).b } + + describe '#decoder_stub' do + context 'when badchars include a null byte' do + it 'raises EncodingError' do + state = make_encoder_state(valid_payload, "\x00".b) + expect { encoder.decoder_stub(state) }.to raise_error(EncodingError) + end + end + + context 'when payload is empty' do + it 'raises EncodingError' do + state = make_encoder_state(''.b) + expect { encoder.decoder_stub(state) }.to raise_error(EncodingError) + end + end + + context 'when payload exceeds 2047 bytes' do + it 'raises EncodingError' do + state = make_encoder_state(('A' * 2048).b) + expect { encoder.decoder_stub(state) }.to raise_error(EncodingError) + end + end + + context 'with a valid payload' do + subject(:stub) { encoder.decoder_stub(make_encoder_state(valid_payload)) } + + it 'returns a 64-byte stub' do + expect(stub.bytesize).to eq(64) + end + + it 'is binary encoded' do + expect(stub.encoding).to eq(Encoding::ASCII_8BIT) + end + + it 'begins with auipc t0, 0 (0x00000297)' do + expect(stub[0, 4].unpack1('V')).to eq(0x00000297) + end + + it 'ends with jalr x0, t4, 0' do + # jalr rd=0, rs1=29(t4), imm=0 => (0<<20)|(29<<15)|(0<<7)|0b1100111 + expected_jalr = (29 << 15) | 0b1100111 + expect(stub[-4, 4].unpack1('V')).to eq(expected_jalr) + end + + it 'contains no null bytes in the first instruction' do + expect(stub[0, 4]).not_to include("\x00".b) + end + end + end + + describe '#find_key_verify' do + it 'accepts a key that produces valid xori encoding' do + key_bytes = [0x01].pack('C') + result = encoder.find_key_verify(valid_payload, key_bytes, ''.b) + expect(result).to be(true).or be(false) + end + + it 'rejects a key whose xori instruction encoding hits a bad character' do + bad_byte = "\x13".b + key_bytes = [0x00].pack('C') + # Force a state where 0x13 is a badchar — the addi/xori opcodes end in 0x13 + result = encoder.find_key_verify(valid_payload, key_bytes, bad_byte) + expect(result).to be(false) + end + end +end + +RSpec.shared_examples 'riscv longxor encoder' do |ref_name| + include_context 'Msf::Simple::Framework#modules loading' + + let(:encoder) { load_and_create_module(module_type: 'encoder', reference_name: ref_name) } + let(:valid_payload) { ("\xde\xad\xbe\xef" * 4).b } # 16 bytes, 4-dword aligned + let(:stub_insn_size) { 68 } # 17 instructions × 4 bytes + let(:stub_total_size) { 72 } # stub + 4-byte key placeholder + + describe '#decoder_stub' do + context 'when badchars include a null byte' do + it 'raises EncodingError' do + state = make_encoder_state(valid_payload, "\x00".b) + expect { encoder.decoder_stub(state) }.to raise_error(EncodingError) + end + end + + context 'when payload is empty' do + it 'raises EncodingError' do + state = make_encoder_state(''.b) + expect { encoder.decoder_stub(state) }.to raise_error(EncodingError) + end + end + + context 'when payload is not 4-byte aligned' do + it 'raises EncodingError' do + state = make_encoder_state(('A' * 5).b) + expect { encoder.decoder_stub(state) }.to raise_error(EncodingError) + end + end + + context 'when payload exceeds 2047 dwords' do + it 'raises EncodingError' do + state = make_encoder_state(('A' * 4 * 2048).b) + expect { encoder.decoder_stub(state) }.to raise_error(EncodingError) + end + end + + context 'with a valid aligned payload' do + let(:state) { make_encoder_state(valid_payload) } + + subject(:stub) { encoder.decoder_stub(state) } + + it 'returns the correct total size' do + expect(stub.bytesize).to eq(stub_total_size) + end + + it 'is binary encoded' do + expect(stub.encoding).to eq(Encoding::ASCII_8BIT) + end + + it 'begins with auipc t0, 0 (0x00000297)' do + expect(stub[0, 4].unpack1('V')).to eq(0x00000297) + end + + it 'ends with 4 null bytes (key placeholder)' do + expect(stub[-4, 4]).to eq("\x00\x00\x00\x00".b) + end + + it 'sets decoder_key_offset to the instruction section length' do + stub + expect(state.decoder_key_offset).to eq(stub_insn_size) + end + end + end +end + +RSpec.shared_examples 'riscv longxor_tag encoder' do |ref_name| + include_context 'Msf::Simple::Framework#modules loading' + + let(:encoder) { load_and_create_module(module_type: 'encoder', reference_name: ref_name) } + let(:valid_payload) { ("\xde\xad\xbe\xef" * 4).b } + let(:stub_insn_size) { 60 } # 15 instructions × 4 bytes + let(:stub_total_size) { 64 } # stub + 4-byte key placeholder + + describe '#decoder_stub' do + context 'when badchars include a null byte' do + it 'raises EncodingError' do + state = make_encoder_state(valid_payload, "\x00".b) + expect { encoder.decoder_stub(state) }.to raise_error(EncodingError) + end + end + + context 'when payload is empty' do + it 'raises EncodingError' do + state = make_encoder_state(''.b) + expect { encoder.decoder_stub(state) }.to raise_error(EncodingError) + end + end + + context 'when payload is not 4-byte aligned' do + it 'raises EncodingError' do + state = make_encoder_state(('A' * 5).b) + expect { encoder.decoder_stub(state) }.to raise_error(EncodingError) + end + end + + context 'when payload contains a zero dword' do + it 'raises EncodingError' do + payload_with_zero = ("\xde\xad\xbe\xef" + "\x00\x00\x00\x00" + "\xde\xad\xbe\xef" * 2).b + state = make_encoder_state(payload_with_zero) + expect { encoder.decoder_stub(state) }.to raise_error(EncodingError, /zero dword/) + end + end + + context 'with a valid aligned payload containing no zero dwords' do + let(:state) { make_encoder_state(valid_payload) } + + subject(:stub) { encoder.decoder_stub(state) } + + it 'returns the correct total size' do + expect(stub.bytesize).to eq(stub_total_size) + end + + it 'is binary encoded' do + expect(stub.encoding).to eq(Encoding::ASCII_8BIT) + end + + it 'begins with auipc t0, 0 (0x00000297)' do + expect(stub[0, 4].unpack1('V')).to eq(0x00000297) + end + + it 'ends with 4 null bytes (key placeholder)' do + expect(stub[-4, 4]).to eq("\x00\x00\x00\x00".b) + end + + it 'sets decoder_key_offset to the instruction section length' do + stub + expect(state.decoder_key_offset).to eq(stub_insn_size) + end + end + end + + describe '#encode_end' do + it 'appends the XOR key to the encoded buffer' do + key = 0xcafebabe + state = make_encoder_state(valid_payload, ''.b, key) + state.encoded = ''.b + encoder.encode_end(state) + expect(state.encoded).to eq([key].pack('V')) + end + + it 'appends key ^ key == 0 as the sentinel dword' do + key = 0x12345678 + state = make_encoder_state(valid_payload, ''.b, key) + state.encoded = ''.b + encoder.encode_end(state) + sentinel = state.encoded.unpack1('V') + expect(sentinel ^ key).to eq(0) + end + end +end + +RSpec.shared_examples 'riscv longxor_feedback encoder' do |ref_name| + include_context 'Msf::Simple::Framework#modules loading' + + let(:encoder) { load_and_create_module(module_type: 'encoder', reference_name: ref_name) } + let(:valid_payload) { ("\xde\xad\xbe\xef" * 4).b } + let(:stub_insn_size) { 72 } # 18 instructions × 4 bytes + let(:stub_total_size) { 76 } # stub + 4-byte key placeholder + + describe '#decoder_stub' do + context 'when badchars include a null byte' do + it 'raises EncodingError' do + state = make_encoder_state(valid_payload, "\x00".b) + expect { encoder.decoder_stub(state) }.to raise_error(EncodingError) + end + end + + context 'when payload is empty' do + it 'raises EncodingError' do + state = make_encoder_state(''.b) + expect { encoder.decoder_stub(state) }.to raise_error(EncodingError) + end + end + + context 'when payload is not 4-byte aligned' do + it 'raises EncodingError' do + state = make_encoder_state(('A' * 5).b) + expect { encoder.decoder_stub(state) }.to raise_error(EncodingError) + end + end + + context 'when payload exceeds 2047 dwords' do + it 'raises EncodingError' do + state = make_encoder_state(('A' * 4 * 2048).b) + expect { encoder.decoder_stub(state) }.to raise_error(EncodingError) + end + end + + context 'with a valid aligned payload' do + let(:state) { make_encoder_state(valid_payload) } + + subject(:stub) { encoder.decoder_stub(state) } + + it 'returns the correct total size' do + expect(stub.bytesize).to eq(stub_total_size) + end + + it 'is binary encoded' do + expect(stub.encoding).to eq(Encoding::ASCII_8BIT) + end + + it 'begins with auipc t0, 0 (0x00000297)' do + expect(stub[0, 4].unpack1('V')).to eq(0x00000297) + end + + it 'ends with 4 null bytes (key placeholder)' do + expect(stub[-4, 4]).to eq("\x00\x00\x00\x00".b) + end + + it 'sets decoder_key_offset to the instruction section length' do + stub + expect(state.decoder_key_offset).to eq(stub_insn_size) + end + end + end + + describe '#encode_begin' do + it 'seeds the feedback register with the initial key' do + state = make_encoder_state(valid_payload, ''.b, 0xdeadbeef) + encoder.encode_begin(state) + expect(encoder.instance_variable_get(:@feedback)).to eq(0xdeadbeef) + end + end + + describe '#encode_block' do + before { encoder.encode_begin(make_encoder_state(valid_payload, ''.b, 0x11111111)) } + + it 'XORs the first block with the initial key' do + state = make_encoder_state(valid_payload, ''.b, 0x11111111) + encoder.encode_begin(state) + block = "\xef\xbe\xad\xde".b + result = encoder.encode_block(state, block) + plain = block.unpack1('V') + expect(result.unpack1('V')).to eq(plain ^ 0x11111111) + end + + it 'uses the previous encoded dword as feedback for the next block' do + state = make_encoder_state(valid_payload, ''.b, 0x11111111) + encoder.encode_begin(state) + block1 = "\x01\x00\x00\x00".b + block2 = "\x02\x00\x00\x00".b + encoded1 = encoder.encode_block(state, block1) + encoded2 = encoder.encode_block(state, block2) + expect(encoded2.unpack1('V')).to eq(block2.unpack1('V') ^ encoded1.unpack1('V')) + end + end +end