Module: Fontisan::Type1::Decryptor
- Defined in:
- lib/fontisan/type1/decryptor.rb
Overview
Decryption utilities for Type 1 fonts
[‘Decryptor`](lib/fontisan/type1/decryptor.rb) handles the two types of encryption used in Adobe Type 1 fonts:
-
**eexec encryption**: Protects the font’s private dictionary and CharStrings with key 55665
-
**CharString encryption**: Individual CharStrings within the eexec portion with key 4330
Both use the same cipher algorithm but with different keys. The algorithm uses cipher feedback, where the ciphertext is used to modify the key for subsequent bytes.
Algorithm (from Adobe Type 1 Font Format):
plain = (cipher XOR (R >> 8)) AND 0xFF
R = ((cipher + R) * 52845 + 22719) AND 0xFFFF
Constant Summary collapse
- DEFAULT_LEN_IV =
Default lenIV value (number of random bytes at start of encrypted CharString)
4- EEXEC_KEY =
eexec encryption key
55665- CHARSTRING_KEY =
CharString encryption key
4330- CIPHER_MULT =
Cipher update constants
52845- CIPHER_ADD =
22719- CIPHER_MASK =
0xFFFF
Class Method Summary collapse
-
.charstring_decrypt(data, len_iv: DEFAULT_LEN_IV) ⇒ String
Decrypt Type 1 CharString.
-
.charstring_encrypt(data, len_iv: DEFAULT_LEN_IV) ⇒ String
Encrypt data using CharString cipher.
-
.decrypt(data, key) ⇒ String
Decrypt using Type 1 cipher.
-
.eexec_decrypt(data) ⇒ String
Decrypt eexec encrypted data.
-
.eexec_encrypt(data) ⇒ String
Encrypt data using eexec cipher.
-
.encrypt(data, key) ⇒ String
Encrypt using Type 1 cipher.
Class Method Details
.charstring_decrypt(data, len_iv: DEFAULT_LEN_IV) ⇒ String
Decrypt Type 1 CharString
CharStrings are encrypted within the eexec portion using a different key (4330). The lenIV parameter specifies how many random bytes precede the actual CharString data (default is 4).
88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 |
# File 'lib/fontisan/type1/decryptor.rb', line 88 def self.charstring_decrypt(data, len_iv: DEFAULT_LEN_IV) return "" if data.nil? || data.empty? decrypted = decrypt(data, CHARSTRING_KEY) # Skip lenIV bytes (random bytes added during encryption) if len_iv.positive? if len_iv >= decrypted.length # lenIV is larger than data - return empty return "" end decrypted = decrypted.byteslice(len_iv..-1) end decrypted end |
.charstring_encrypt(data, len_iv: DEFAULT_LEN_IV) ⇒ String
Encrypt data using CharString cipher
111 112 113 114 115 116 117 118 119 120 121 122 123 124 |
# File 'lib/fontisan/type1/decryptor.rb', line 111 def self.charstring_encrypt(data, len_iv: DEFAULT_LEN_IV) return "" if data.nil? || data.empty? # Ensure binary encoding before concatenation data = data.b if data.respond_to?(:b) # Prepend lenIV random bytes if len_iv.positive? random_bytes = Array.new(len_iv) { rand(256) }.pack("C*") data = random_bytes + data end encrypt(data, CHARSTRING_KEY) end |
.decrypt(data, key) ⇒ String
Decrypt using Type 1 cipher
The cipher processes data byte by byte, maintaining state across iterations using cipher feedback.
Algorithm:
plain = (cipher XOR (R >> 8)) AND 0xFF
R = ((cipher + R) * 52845 + 22719) AND 0xFFFF
138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 |
# File 'lib/fontisan/type1/decryptor.rb', line 138 def self.decrypt(data, key) result = String.new(capacity: data.length) r = key data.each_byte do |cipher| # plain = (cipher XOR (R >> 8)) AND 0xFF plain = (cipher ^ (r >> 8)) & 0xFF result << plain # R = ((cipher + R) * 52845 + 22719) AND 0xFFFF r = ((cipher + r) * CIPHER_MULT + CIPHER_ADD) & CIPHER_MASK end result end |
.eexec_decrypt(data) ⇒ String
Decrypt eexec encrypted data
The eexec cipher is used to encrypt the private dictionary and CharStrings portion of a Type 1 font.
57 58 59 60 61 |
# File 'lib/fontisan/type1/decryptor.rb', line 57 def self.eexec_decrypt(data) return "" if data.nil? || data.empty? decrypt(data, EEXEC_KEY) end |
.eexec_encrypt(data) ⇒ String
Encrypt data using eexec cipher
67 68 69 70 71 |
# File 'lib/fontisan/type1/decryptor.rb', line 67 def self.eexec_encrypt(data) return "" if data.nil? || data.empty? encrypt(data, EEXEC_KEY) end |
.encrypt(data, key) ⇒ String
Encrypt using Type 1 cipher
The encryption algorithm is symmetric with decryption, producing the ciphertext that decrypts to the original plaintext.
Algorithm:
cipher = (plain XOR (R >> 8)) AND 0xFF
R = ((cipher + R) * 52845 + 22719) AND 0xFFFF
166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 |
# File 'lib/fontisan/type1/decryptor.rb', line 166 def self.encrypt(data, key) result = String.new(capacity: data.length) r = key data.each_byte do |plain| # cipher = (plain XOR (R >> 8)) AND 0xFF cipher = (plain ^ (r >> 8)) & 0xFF result << cipher # R = ((cipher + R) * 52845 + 22719) AND 0xFFFF r = ((cipher + r) * CIPHER_MULT + CIPHER_ADD) & CIPHER_MASK end result end |