File: aes_v3_security_handler_spec.rb

package info (click to toggle)
ruby-pdf-reader 2.15.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 33,512 kB
  • sloc: ruby: 11,959; sh: 46; makefile: 11
file content (145 lines) | stat: -rw-r--r-- 4,889 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
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
118
119
120
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
# typed: false
# coding: utf-8

require 'openssl'

describe PDF::Reader::AesV3SecurityHandler do
  let(:key) { "a" * 32 } # exactly 32 bytes for AES-256
  let(:handler) { PDF::Reader::AesV3SecurityHandler.new(key) }
  let(:reference) { PDF::Reader::Reference.new(1, 0) }

  describe "#initialize" do
    context "with valid 32-byte key" do
      it "creates handler successfully" do
        expect { PDF::Reader::AesV3SecurityHandler.new("a" * 32) }.not_to raise_error
      end
    end

    context "with invalid key lengths" do
      it "raises MalformedPDFError for short key" do
        expect {
          PDF::Reader::AesV3SecurityHandler.new("short")
        }.to raise_error(
          PDF::Reader::MalformedPDFError, "AES-256 key must be exactly 32 bytes, got 5"
        )
      end

      it "raises MalformedPDFError for long key" do
        expect {
          PDF::Reader::AesV3SecurityHandler.new("a" * 40)
        }.to raise_error(
          PDF::Reader::MalformedPDFError, "AES-256 key must be exactly 32 bytes, got 40"
        )
      end

      it "raises MalformedPDFError for empty key" do
        expect {
          PDF::Reader::AesV3SecurityHandler.new("")
        }.to raise_error(
          PDF::Reader::MalformedPDFError, "AES-256 key must be exactly 32 bytes, got 0"
        )
      end
    end
  end

  describe "#decrypt" do
    context "with ciphertext that is not a multiple of 16 bytes" do
      it "raises MalformedPDFError" do
        invalid_buf = "short"
        expect {
          handler.decrypt(invalid_buf, reference)
        }.to raise_error(PDF::Reader::MalformedPDFError, "Ciphertext not a multiple of 16")
      end
    end

    context "with exactly 16 bytes (IV only)" do
      it "returns empty string" do
        iv_only = "0123456789abcdef"
        result = handler.decrypt(iv_only, reference)
        expect(result).to eq("")
      end
    end

    context "with valid padded ciphertext" do
      it "decrypts successfully with PKCS#7 padding" do
        plaintext = "Hello, World!"
        buf = encrypt_test_data(plaintext, key, padding: true)

        result = handler.decrypt(buf, reference)
        expect(result).to eq(plaintext)
      end
    end

    context "with ciphertext without padding and valid key" do
      it "should decrypt successfully with no padding" do
        plaintext = "1234567890123456" # exactly 16 bytes
        buf = encrypt_test_data(plaintext, key, padding: false)

        result = handler.decrypt(buf, reference)
        expect(result).to eq(plaintext)
      end
    end

    context "with ciphertext without padding and invalid key" do
      it "returns incorrect data when key is wrong" do
        wrong_key = "b" * 32 # different 32-byte key
        wrong_handler = PDF::Reader::AesV3SecurityHandler.new(wrong_key)

        plaintext = "1234567890123456"
        buf = encrypt_test_data(plaintext, key, padding: false)

        # Try to decrypt with wrong key - should return garbage, not raise error
        result = wrong_handler.decrypt(buf, reference)
        expect(result).not_to eq(plaintext)
        expect(result).to be_a(String)
      end
    end

    context "with padded ciphertext and invalid key" do
      it "returns incorrect data when key is wrong" do
        wrong_key = "b" * 32 # different 32-byte key
        wrong_handler = PDF::Reader::AesV3SecurityHandler.new(wrong_key)

        plaintext = "Hello, World!"
        buf = encrypt_test_data(plaintext, key, padding: true)

        # Try to decrypt with wrong key - should return garbage, not raise error
        result = wrong_handler.decrypt(buf, reference)
        expect(result).not_to eq(plaintext)
        expect(result).to be_a(String)
      end
    end

    context "with malformed ciphertext" do
      it "returns a string with garbage content" do
        # Create invalid ciphertext with proper 32-byte key handler
        iv = "0123456789abcdef"
        corrupted_data = "corrupted_data16" # exactly 16 bytes
        buf = iv + corrupted_data

        result = handler.decrypt(buf, reference)
        expect(result).to be_a(String)
      end
    end
  end

  private

  # Helper method to create encrypted test data for AES-V3
  # @param plaintext [String] the text to encrypt
  # @param encryption_key [String] the encryption key to use (32 bytes for AES-256)
  # @param padding [Boolean] whether to use PKCS#7 padding
  # @return [String] IV + ciphertext ready for decrypt method
  def encrypt_test_data(plaintext, encryption_key, padding: true)
    # AES-V3 uses the key directly without object reference modification
    cipher = OpenSSL::Cipher.new("AES-256-CBC")
    cipher.encrypt
    cipher.padding = 0 unless padding
    cipher.key = encryption_key.dup
    iv = cipher.random_iv
    ciphertext = cipher.update(plaintext) + cipher.final

    # Return IV + ciphertext for the handler
    iv + ciphertext
  end
end