Class: Origami::Encryption::RC4

Inherits:
Object
  • Object
show all
Defined in:
lib/origami/encryption.rb

Overview

Class wrapper for the RC4 algorithm.

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(key) ⇒ RC4

Creates and initialises a new RC4 generator using given key



538
539
540
# File 'lib/origami/encryption.rb', line 538

def initialize(key)
  @key = key
end

Class Method Details

.decrypt(key, data) ⇒ Object

Decrypts data using the given key



531
532
533
# File 'lib/origami/encryption.rb', line 531

def self.decrypt(key, data)
  RC4.new(key).decrypt(data)
end

.encrypt(key, data) ⇒ Object

Encrypts data using the given key



524
525
526
# File 'lib/origami/encryption.rb', line 524

def self.encrypt(key, data)
  RC4.new(key).encrypt(data)
end

Instance Method Details

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

Encrypt/decrypt data with the RC4 encryption algorithm RC4 is part of the PDF specification, so we need to support it



546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
# File 'lib/origami/encryption.rb', line 546

def cipher(data)
  return +'' if data.empty?

  # Enable RC4 cipher in OpenSSL security level
  # This is needed for PDF compatibility as per PDF 1.7 specification
  begin
    # Try to lower security level temporarily to enable legacy ciphers
    original_security_level = begin
      OpenSSL::SSL.send(:security_level)
    rescue
      nil
    end
    if original_security_level
      begin
        OpenSSL::SSL.send(:security_level=, 0)
      rescue
        nil
      end
    end

    # Now try to use RC4
    rc4 = OpenSSL::Cipher.new('rc4').encrypt
    rc4.key_len = @key.length
    rc4.key = @key
    result = rc4.update(data) + rc4.final

    # Restore security level
    if original_security_level
      begin
        OpenSSL::SSL.send(:security_level=, original_security_level)
      rescue
        nil
      end
    end

    return result
  rescue
    # If we can't use OpenSSL's RC4 for any reason, silently fall back to pure Ruby implementation
  end

  # Pure Ruby RC4 implementation as fallback
  s = (0..255).to_a
  key_bytes = @key.bytes
  key_length = key_bytes.length
  j = 0

  # Key-scheduling algorithm (KSA)
  256.times do |i|
    j = (j + s[i] + key_bytes[i % key_length]) % 256
    s[i], s[j] = s[j], s[i]
  end

  # Pseudo-random generation algorithm (PRGA)
  result = +""
  i = j = 0
  data.each_byte do |byte|
    i = (i + 1) % 256
    j = (j + s[i]) % 256
    s[i], s[j] = s[j], s[i]
    k = s[(s[i] + s[j]) % 256]
    result << (byte ^ k).chr
  end

  result
end