Class: PDF::Reader::Rc4

Inherits:
Object
  • Object
show all
Defined in:
lib/pdf/reader/rc4.rb

Overview

A minimal, self-contained implementation of the RC4 stream cipher, used to decrypt PDFs encrypted with the (legacy, but still spec-compliant) RC4 security handler. Vendored directly to avoid depending on the abandoned ruby-rc4 gem (last released 2012, archived since 2020, and missing license metadata in its gemspec).

Instance Method Summary collapse

Constructor Details

#initialize(key) ⇒ Rc4

Returns a new instance of Rc4.

Signature:

  • (String) -> void

Raises:

  • (ArgumentError)


12
13
14
15
16
17
18
19
20
21
22
23
24
# File 'lib/pdf/reader/rc4.rb', line 12

def initialize(key)
  raise ArgumentError, "key must not be empty" if key.empty?

  @key = key.bytes #: Array[Integer]
  @s = (0..255).to_a #: Array[Integer]
  j = 0
  256.times do |i|
    j = (j + @s.fetch(i) + @key.fetch(i % @key.length)) & 0xFF
    @s[i], @s[j] = @s.fetch(j), @s.fetch(i)
  end
  @i = 0 #: Integer
  @j = 0 #: Integer
end

Instance Method Details

#decrypt(data) ⇒ Object Also known as: encrypt

RC4 encryption and decryption are the same operation (XOR with the keystream).

Signature:

  • (String) -> String



28
29
30
31
32
33
34
35
36
37
38
39
# File 'lib/pdf/reader/rc4.rb', line 28

def decrypt(data)
  out = "".b
  data = data.dup.force_encoding(::Encoding::ASCII_8BIT)
  data.each_byte do |byte|
    @i = (@i + 1) & 0xFF
    @j = (@j + @s.fetch(@i)) & 0xFF
    @s[@i], @s[@j] = @s.fetch(@j), @s.fetch(@i)
    k = @s.fetch((@s.fetch(@i) + @s.fetch(@j)) & 0xFF)
    out << (byte ^ k)
  end
  out
end