]> git.lizzy.rs Git - plan9front.git/blob - sys/lib/ghostscript/pdf_sec.ps
etherbcm: handle 64-bit host addresses, use PCIWADDR() instead of PADDR()
[plan9front.git] / sys / lib / ghostscript / pdf_sec.ps
1 %   Copyright (C) 1996-1998 Geoffrey Keating. 
2 %       Copyright (C) 2001 Artifex Software, Inc.
3 % This file may be freely distributed with or without modifications,
4 % so long as modified versions are marked as such and copyright notices are
5 % not removed.
6
7 % $Id: pdf_sec.ps,v 1.15 2004/03/12 01:55:58 dan Exp $
8 % Implementation of security hooks for PDF reader.
9
10 % This file contains the procedures that have to take encryption into
11 % account when reading a PDF file. It replaces the stub version of this
12 % file that is shipped with GhostScript. It requires GhostScript 7.01
13 % or later.
14
15 % Documentation for using this file is available at
16 % http://www.ozemail.com.au/%7Egeoffk/pdfencrypt/
17
18 % Modified by Alex Cherepanov to work with GS 6.60 and higher.
19 % New versions of GS require explicit checks for /true , /false, and /null
20 % in .decpdfrun . This fix is backward-compatible.
21
22 % Modified by Raph Levien and Ralph Giles to use the new C
23 % implementations of md5 and arcfour in ghostscript 7.01, and to
24 % be compatible with PDF 1.4 128-bit encryption.
25
26 /.setlanguagelevel where { pop 2 .setlanguagelevel } if
27 .currentglobal true .setglobal
28 /pdfdict where { pop } { /pdfdict 100 dict def } ifelse
29 pdfdict begin
30
31 % Older ghostscript versions do not have .pdftoken, so we use 'token' instead.
32 /.pdftoken where { pop } { /.pdftoken /token load def } ifelse
33
34 % take a stream and arc4 decrypt it.
35 % <stream> <key> arc4decodefilter <stream>
36 /arc4decodefilter {
37   1 dict begin
38   /Key exch def
39   currentdict end /ArcfourDecode filter
40 } bind def
41
42 /aesdecodefilter {
43   1 dict begin
44   /Key exch def
45   currentdict end /AESDecode filter
46 } bind def
47
48 % <ciphertext> <key> arc4decode <plaintext>
49 /arc4decode {
50   %(key: ) print dup == (ct: ) print 1 index ==
51   1 index length 0 eq {
52     pop
53   } {
54     1 index length string 3 1 roll arc4decodefilter exch readstring pop
55   } ifelse
56 } bind def
57
58 /aesdecode {
59   1 index length 0 eq {
60     pop
61   } {
62     1 index length string 3 1 roll
63
64     % If our second argument is a dictionary, it's the full set
65     % of decoding options (including the key); pass it directly
66     % to the AESDecode filter.  Otherwise, it's just the key, so
67     % call aesdecodefilter to construct the dictionary.
68     dup type /dicttype eq { /AESDecode filter } { aesdecodefilter } ifelse
69
70     exch readstring pop
71   } ifelse
72 } bind def
73
74 /md5 {
75   16 string dup /MD5Encode filter dup 4 3 roll writestring closefile
76 } bind def
77
78 /md5_trunk {
79   md5 0 pdf_key_length getinterval
80 } bind def
81
82
83 /pdf_padding_string
84    <28bf4e5e4e758a41 64004e56fffa0108
85     2e2e00b6d0683e80 2f0ca9fe6453697a>
86 def
87
88 % Pad a key out to 32 bytes.
89 /pdf_pad_key {          % <key> pdf_pad_key <padded key>
90   dup length 32 gt { 0 32 getinterval } if
91   pdf_padding_string
92   0 32 3 index length sub getinterval
93   concatstrings
94 } bind def
95
96 /pdf_xorbytes {      % <iter-num> <key> pdf_xorbytes <xored-key>
97   dup length dup string
98   exch 1 sub 0 1 3 2 roll {
99     % <iter-num> <key> <new-key> <byte-num>
100     dup 3 index exch get 4 index xor
101     % <iter-num> <key> <new-key> <byte-num> <byte>
102     3 copy put pop pop
103   } for
104   3 1 roll pop pop
105 } bind def
106
107 % Get length of encryption key in bytes
108 /pdf_key_length {    % pdf_key_length <key_length>
109   Trailer /Encrypt oget /Length knownoget { -3 bitshift } { 5 } ifelse
110 } bind def
111
112 % Algorithm 3.2
113 /pdf_compute_encryption_key {  % <password> pdf_compute_encryption_key <key>
114   % Step 1.
115   pdf_pad_key
116
117   % Step 2, 3.
118   Trailer /Encrypt oget dup /O oget
119   % <padded-key> <encrypt> <O>
120
121   % Step 4.
122   exch /P oget 4 string exch
123   2 copy 255 and 0 exch put
124   2 copy -8 bitshift 255 and 1 exch put
125   2 copy -16 bitshift 255 and 2 exch put
126   2 copy -24 bitshift 255 and 3 exch put pop
127   % <padded-key> <O> <P>
128
129   % Step 5.
130   Trailer /ID knownoget { 0 oget } {
131     ()
132     (   **** ID key in the trailer is required for encrypted files.\n) pdfformaterror
133   } ifelse
134   3 { concatstrings } repeat 
135   % We will finish step 5 after possibly including step 6.
136
137   % The following only executed for /R equal to 3 or more
138   Trailer /Encrypt oget dup /R oget dup 3 ge {
139
140      % Step 6.  If EncryptMetadata is false, pass 0xFFFFFFFF to md5 function
141      % The PDF 1.5 Spec says that EncryptMetadata is an undocumented
142      % feature of PDF 1.4.  That implies that this piece of logic should
143      % be executed if R >= 3.  However testing with Acrobat 5.0 and 6.0 shows
144      % that this step is not executed if R equal to 3.  Thus we have a test for
145      % R being >= 4.
146      4 ge {
147        /EncryptMetadata knownoget       % Get EncryptMetadata (if present)
148        not { true } if                  % Default is true
149        not {                            % If EncryptMetadata is false
150          <ff ff ff ff> concatstrings    % Add 0xFFFFFFFF to working string
151        } if
152      } {
153        pop                              % Remove Encrypt dict
154      } ifelse
155      md5_trunk                          % Finish step 5 and 6.
156
157      % Step 7.  Executed as part of step 6
158      % Step 8.  (This step is defintely a part of PDF 1.4.)
159      50 { md5_trunk } repeat
160   } {
161      pop pop md5_trunk                  % Remove R, Encrypt dict, finish step 5
162   } ifelse
163
164   % Step 9 - Done in md5_trunk.
165 } bind def
166
167 % Algorithm 3.4
168 /pdf_gen_user_password_R2 { % <filekey> pdf_gen_user_password_R2 <U>
169
170   % Step 2.
171   pdf_padding_string exch arc4decode
172 } bind def
173
174 % Algorithm 3.5
175 /pdf_gen_user_password_R3 { % <filekey> pdf_gen_user_password_R3 <U>
176
177   % Step 2.
178   pdf_padding_string
179
180   % Step 3.
181   Trailer /ID knownoget { 0 oget } {
182     ()
183     (   **** ID key in the trailer is required for encrypted files.\n) pdfformaterror
184   } ifelse
185   concatstrings md5
186
187   % Step 4.
188   1 index arc4decode
189
190   % Step 5.
191   1 1 19 {
192     2 index pdf_xorbytes arc4decode
193   } for
194   exch pop
195
196 } bind def
197
198 /pdf_gen_user_password { % <password> pdf_gen_user_password <filekey> <U>
199   % common Step 1 of Algorithms 3.4 and 3.5.
200   pdf_compute_encryption_key dup
201
202   Trailer /Encrypt oget
203
204   /R oget dup 2 eq {
205     pop pdf_gen_user_password_R2
206   } {
207     dup 3 eq {
208       pop pdf_gen_user_password_R3
209     } {
210       dup 4 eq {        % 4 uses the algorithm as 3
211         pop pdf_gen_user_password_R3
212       } {
213         (   **** This file uses an unknown standard security handler revision: )
214         exch =string cvs concatstrings pdfformaterror printProducer
215         /pdf_check_user_password cvx /undefined signalerror
216       } ifelse
217     } ifelse
218   } ifelse
219 } bind def
220
221 % Algorithm 3.6
222 /pdf_check_user_password { % <password> pdf_check_user_password <filekey> true
223                            % <password> pdf_check_user_password false
224   pdf_gen_user_password
225
226   Trailer /Encrypt oget /U oget
227
228   0 2 index length getinterval eq {
229     true
230   } {
231     pop false
232   } ifelse
233 } bind def
234
235 % Compute an owner key, ie the result of step 4 of Algorithm 3.3
236 /pdf_owner_key % <password> pdf_owner_key <owner-key>
237 {
238   % Step 1.
239   pdf_pad_key
240
241   % Step 2.
242   md5_trunk
243
244   % 3.3 Step 3.  Only executed for /R equal to 3 or more
245   Trailer /Encrypt oget /R oget 3 ge {
246     50 { md5_trunk } repeat
247   } if
248
249   % Step 4 - Done in md5_trunk.
250 } bind def
251
252 % Algorithm 3.7
253 /pdf_check_owner_password { % <password> pdf_check_owner_password <filekey> true
254                             % <password> pdf_check_owner_password false
255   % Step 1.
256   pdf_owner_key
257
258   % Step 2.
259   Trailer /Encrypt oget dup /O oget 2 index arc4decode
260   % <encryption-key> <encrypt-dict> <decrypted-O>
261
262   % Step 3.  Only executed for /R equal to 3 or more
263   exch /R oget 3 ge {
264     1 1 19 {
265       2 index pdf_xorbytes arc4decode
266     } for
267   } if
268   exch pop
269   % <result-of-step-3>
270
271   pdf_check_user_password
272 } bind def
273
274 % Process the encryption information in the Trailer.
275 /pdf_process_Encrypt {
276   Trailer /Encrypt oget
277   /Filter oget /Standard eq not {
278     (   **** This file uses an unknown security handler.\n) pdfformaterror
279     printProducer
280     /pdf_process_Encrypt cvx /undefined signalerror
281   } if
282   () pdf_check_user_password
283   {
284     /FileKey exch def
285   } {
286     /PDFPassword where {
287        pop PDFPassword pdf_check_user_password
288        {
289          /FileKey exch def
290        } {
291          PDFPassword pdf_check_owner_password
292          {
293            /FileKey exch def
294          } {
295            (   **** Password did not work.\n) pdfformaterror
296            printProducer
297            /pdf_process_Encrypt cvx /invalidfileaccess signalerror
298          } ifelse
299        } ifelse
300     } {
301       (   **** This file requires a password for access.\n) pdfformaterror
302       printProducer
303       /pdf_process_Encrypt cvx /invalidfileaccess signalerror
304     } ifelse
305   } ifelse
306
307 %   Trailer /Encrypt oget /P oget 4 and 0 eq #? and
308 %    { (   ****This owner of this file has requested you do not print it.\n)
309 %      pdfformaterror printProducer
310 %      /pdf_process_Encrypt cvx /invalidfileaccess signalerror
311 %    }
312 %   if
313 } bind def
314
315 % Calculate the key used to decrypt an object (to pass to .decpdfrun or
316 % put into a stream dictionary).
317 /computeobjkey  % <object#> <generation#> computeobjkey <keystring>
318 {
319   Trailer /Encrypt oget /V oget 5 eq {
320     % Encrypt version 5 doesn't use object keys; everything is
321     % encrypted with the file key.
322     pop pop FileKey
323   } {
324     exch
325     FileKey length 5 add string
326     dup 0 FileKey putinterval
327     exch
328                   % stack:  gen# string obj#
329       2 copy 255 and FileKey length exch put
330       2 copy -8 bitshift 255 and FileKey length 1 add exch put
331       2 copy -16 bitshift 255 and FileKey length 2 add exch put
332     pop exch
333       2 copy 255 and FileKey length 3 add exch put
334       2 copy -8 bitshift 255 and FileKey length 4 add exch put
335     pop
336       % this step is for the AES cipher only
337       Trailer /Encrypt oget
338       dup /StmF knownoget {
339         exch /CF knownoget {
340           exch oget /CFM oget /AESV2 eq {
341             (sAlT) concatstrings
342           } if
343         } {
344           pop
345         } ifelse
346       } {
347         pop
348       } ifelse
349     md5 0 FileKey length 5 add 2 index length .min getinterval
350   } ifelse
351 } bind def
352
353 % As .pdfrun, but decrypt strings with key <key>.
354 /.decpdfrun                     % <file> <keystring> <opdict> .decpdfrun -
355  {      % Construct a procedure with the file, opdict and key bound into it.
356    2 index cvlit mark mark 5 2 roll
357     { .pdftoken not { (%%EOF) cvn cvx } if
358       dup xcheck
359        { PDFDEBUG { dup == flush } if
360          3 -1 roll pop
361          2 copy .knownget
362           { exch pop exch pop exec
363           }
364           { exch pop
365             dup /true eq
366               { pop //true
367               }
368               { dup /false eq
369                   { pop //false 
370                   }
371                   { dup /null eq
372                       { pop //null
373                       }
374                       { (   **** Unknown operator: ) 
375                         exch =string cvs concatstrings (\n) concatstrings
376                         pdfformaterror
377                       }
378                     ifelse
379                   }
380                 ifelse
381               }
382             ifelse
383           }
384          ifelse
385        }
386        { exch pop PDFDEBUG { dup ==only ( ) print flush } if
387          dup type /stringtype eq
388           {
389         % Check if we have encrypted strings  R=4 allows for
390         % selection of encryption on streams and strings
391             Trailer /Encrypt oget       % Get encryption dictionary
392             dup /R oget 4 lt            % only 4 has selectable
393              {                          % R < 4 --> encrypted strings
394                pop 1 index arc4decode   % Decrypt string
395                PDFDEBUG { (%Decrypted: ) print dup == flush } if
396              } {                        % Else R &gt;= 4
397                /StrF knownoget          % Get StrF (if present)
398                 {                       % If StrF is present ...
399                   dup /Identity eq not  % Check if StrF != Identity
400                    { /StdCF eq
401                        { Trailer /Encrypt oget /CF knownoget {
402                            /StdCF oget /CFM oget
403                            dup /AESV2 eq exch /AESV3 eq or
404                          } {
405                            //false
406                          } ifelse {     % Decrypt string
407                            1 index aesdecode
408                          } {
409                            1 index arc4decode
410                          } ifelse
411                        }
412                        { 1 index arc4decode }
413                      ifelse             % If StrF != StdCF
414                      PDFDEBUG { (%Decrypted: ) print dup //== exec flush } if
415                    }
416                    { pop }
417                   ifelse                % If StrF != identity
418                 }
419                if                       % If StrF is known
420              }
421             ifelse                      % Ifelse R < 4
422           }
423          if                             % If  = stringtype
424          exch pop
425        }
426       ifelse
427     }
428    aload pop .packtomark cvx
429    /loop cvx 2 packedarray cvx
430     { stopped /PDFsource } aload pop
431    PDFsource
432     { store { stop } if } aload pop .packtomark cvx 
433    /PDFsource 3 -1 roll store exec
434  } bind def
435
436 % Run the code to resolve an object reference.
437 /pdf_run_resolve
438 { /FileKey where                        % Check if the file is encrypted
439   { pop                                 % File is encrypted
440     2 copy computeobjkey dup 4 1 roll
441     PDFfile exch resolveopdict .decpdfrun
442     dup dup dup 5 2 roll
443         % stack: object object key object object
444     {   % Use loop to provide an exitable context.
445       xcheck exch type /dicttype eq and % Check if executable dictionary
446       not {                             % If object is not ...
447         pop pop                         % ignore object
448         exit                            % Exit 'loop' context
449       } if                              % If not possible stream
450         % Starting with PDF 1.4 (R = 3), there are some extra features
451         % which control encryption of streams.  The EncryptMetadata entry
452         % in the Encrypt dict controls the encryption of metadata streams.
453       Trailer /Encrypt oget             % Get encryption dictionary
454       dup /R oget dup 3 lt              % Only PDF 1.4 and higher has options
455       {                                 % R &lt; 3 --&gt; all streams encrypted
456         pop pop /StreamKey exch put     % Insert StreamKey in dictionary
457         exit                            % Exit 'loop' context
458       } if
459         % Check EncryptMeta.  stack: object object key Encrypt R
460       exch dup /EncryptMetadata knownoget % Get EncryptMetadata (if present)
461       not { //true } if                 % If not present default = true
462       not                               % Check if EncryptMetadata = false
463       {                                 % if false we need to check the stream type
464         3 index /Type knownoget         % Get stream type (if present)
465         not { //null } if               % If type not present use fake name
466         /Metadata eq                    % Check if the type is Metadata
467         { pop pop pop pop               % Type == Metadata --&gt; no encryption
468           exit                          % Exit 'loop' context
469         } if
470       } if
471         % PDF 1.5 encryption (R == 4) has selectable encryption handlers.  If
472         % this is not PDF 1.5 encryption (R &lt; 4) then we are done checking and
473         % we need to decrypt the stream.  stack: object object key R Encrypt
474       exch 4 lt                         % Check for less than PDF 1.5
475       { pop /StreamKey exch put         % Insert StreamKey in dictionary
476         exit                            % Exit 'loop' context
477       } if
478         % Check if the stream encryption handler (StmF) == Identity.
479       PDFDEBUG {
480         Trailer /Encrypt oget /CF knownoget {
481           /StdCF oget /CFM oget
482           (Encrypt StmF is StdCF with CFM ) print =
483         } if
484       } if
485       /StmF knownoget                   % Get StmF (if present)
486       not { /Identity } if              % If StmF not present default = Identity
487       /Identity eq                      % Check if StmF == Identity
488       { pop pop                         % Identity --&gt; no encryption
489         exit                            % Exit 'loop' context
490       } if
491         % If we get here then we need to decrypt the stream.
492       /StreamKey exch put               % Insert StreamKey into dictionary
493       exit                              % Exit 'loop' context, never loop
494     } loop                              % End of loop exitable context
495   } {                                   % Else file is not encrypted
496     PDFfile resolveopdict .pdfrun
497   } ifelse                              % Ifelse encrypted
498 } bind def
499
500 % Prefix a decryption filter to a stream if needed.
501 % Stack: readdata? dict parms file/string filternames
502 % (both before and after).
503 /pdf_decrypt_stream
504  { 3 index /StreamKey known     % Check if the file is encrypted
505    {
506       exch
507         % Stack: readdata? dict parms filternames file/string
508       3 index /StreamKey get
509       Trailer /Encrypt oget
510       dup /StmF knownoget
511        {                        % stack: key Encrypt StmF
512         exch /CF knownoget {
513           exch oget /CFM oget   % stack: key StmF-CFM
514           dup /AESV2 eq exch /AESV3 eq or
515         } { pop //false } ifelse
516          { aesdecodefilter }    % install the requested filter
517          { arc4decodefilter }
518         ifelse
519        }
520        { pop arc4decodefilter } % fallback for no StmF
521       ifelse
522       exch
523    } if
524  } bind def
525
526 end                     % pdfdict
527 .setglobal