123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459 |
- % Copyright (C) 1996-1998 Geoffrey Keating.
- % Copyright (C) 2001 Artifex Software, Inc.
- % This file may be freely distributed with or without modifications,
- % so long as modified versions are marked as such and copyright notices are
- % not removed.
- % $Id: pdf_sec.ps,v 1.15 2004/03/12 01:55:58 dan Exp $
- % Implementation of security hooks for PDF reader.
- % This file contains the procedures that have to take encryption into
- % account when reading a PDF file. It replaces the stub version of this
- % file that is shipped with GhostScript. It requires GhostScript 7.01
- % or later.
- % Documentation for using this file is available at
- % http://www.ozemail.com.au/%7Egeoffk/pdfencrypt/
- % Modified by Alex Cherepanov to work with GS 6.60 and higher.
- % New versions of GS require explicit checks for /true , /false, and /null
- % in .decpdfrun . This fix is backward-compatible.
- % Modified by Raph Levien and Ralph Giles to use the new C
- % implementations of md5 and arcfour in ghostscript 7.01, and to
- % be compatible with PDF 1.4 128-bit encryption.
- /.setlanguagelevel where { pop 2 .setlanguagelevel } if
- .currentglobal true .setglobal
- /pdfdict where { pop } { /pdfdict 100 dict def } ifelse
- pdfdict begin
- % Older ghostscript versions do not have .pdftoken, so we use 'token' instead.
- /.pdftoken where { pop } { /.pdftoken /token load def } ifelse
- % take a stream and arc4 decrypt it.
- % <stream> <key> arc4decodefilter <stream>
- /arc4decodefilter {
- 1 dict begin
- /Key exch def
- currentdict end /ArcfourDecode filter
- } bind def
- % <ciphertext> <key> arc4decode <plaintext>
- /arc4decode {
- %(key: ) print dup == (ct: ) print 1 index ==
- 1 index length 0 eq {
- pop
- } {
- 1 index length string 3 1 roll arc4decodefilter exch readstring pop
- } ifelse
- } bind def
- /md5 {
- 16 string dup /MD5Encode filter dup 4 3 roll writestring closefile
- } bind def
- /md5_trunk {
- md5 0 pdf_key_length getinterval
- } bind def
- /pdf_padding_string
- <28bf4e5e4e758a41 64004e56fffa0108
- 2e2e00b6d0683e80 2f0ca9fe6453697a>
- def
- % Pad a key out to 32 bytes.
- /pdf_pad_key { % <key> pdf_pad_key <padded key>
- dup length 32 gt { 0 32 getinterval } if
- pdf_padding_string
- 0 32 3 index length sub getinterval
- concatstrings
- } bind def
- /pdf_xorbytes { % <iter-num> <key> pdf_xorbytes <xored-key>
- dup length dup string
- exch 1 sub 0 1 3 2 roll {
- % <iter-num> <key> <new-key> <byte-num>
- dup 3 index exch get 4 index xor
- % <iter-num> <key> <new-key> <byte-num> <byte>
- 3 copy put pop pop
- } for
- 3 1 roll pop pop
- } bind def
- % Get length of encryption key in bytes
- /pdf_key_length { % pdf_key_length <key_length>
- Trailer /Encrypt oget /Length knownoget { -3 bitshift } { 5 } ifelse
- } bind def
- % Algorithm 3.2
- /pdf_compute_encryption_key { % <password> pdf_compute_encryption_key <key>
- % Step 1.
- pdf_pad_key
- % Step 2, 3.
- Trailer /Encrypt oget dup /O oget
- % <padded-key> <encrypt> <O>
- % Step 4.
- exch /P oget 4 string exch
- 2 copy 255 and 0 exch put
- 2 copy -8 bitshift 255 and 1 exch put
- 2 copy -16 bitshift 255 and 2 exch put
- 2 copy -24 bitshift 255 and 3 exch put pop
- % <padded-key> <O> <P>
- % Step 5.
- Trailer /ID knownoget { 0 oget } {
- ()
- ( **** ID key in the trailer is required for encrypted files.\n) pdfformaterror
- } ifelse
- 3 { concatstrings } repeat
- % We will finish step 5 after possibly including step 6.
- % The following only executed for /R equal to 3 or more
- Trailer /Encrypt oget dup /R oget dup 3 ge {
- % Step 6. If EncryptMetadata is false, pass 0xFFFFFFFF to md5 function
- % The PDF 1.5 Spec says that EncryptMetadata is an undocumented
- % feature of PDF 1.4. That implies that this piece of logic should
- % be executed if R >= 3. However testing with Acrobat 5.0 and 6.0 shows
- % that this step is not executed if R equal to 3. Thus we have a test for
- % R being >= 4.
- 4 ge {
- /EncryptMetadata knownoget % Get EncryptMetadata (if present)
- not { true } if % Default is true
- not { % If EncryptMetadata is false
- <ff ff ff ff> concatstrings % Add 0xFFFFFFFF to working string
- } if
- } {
- pop % Remove Encrypt dict
- } ifelse
- md5_trunk % Finish step 5 and 6.
- % Step 7. Executed as part of step 6
- % Step 8. (This step is defintely a part of PDF 1.4.)
- 50 { md5_trunk } repeat
- } {
- pop pop md5_trunk % Remove R, Encrypt dict, finish step 5
- } ifelse
- % Step 9 - Done in md5_trunk.
- } bind def
- % Algorithm 3.4
- /pdf_gen_user_password_R2 { % <filekey> pdf_gen_user_password_R2 <U>
- % Step 2.
- pdf_padding_string exch arc4decode
- } bind def
- % Algorithm 3.5
- /pdf_gen_user_password_R3 { % <filekey> pdf_gen_user_password_R3 <U>
- % Step 2.
- pdf_padding_string
- % Step 3.
- Trailer /ID knownoget { 0 oget } {
- ()
- ( **** ID key in the trailer is required for encrypted files.\n) pdfformaterror
- } ifelse
- concatstrings md5
- % Step 4.
- 1 index arc4decode
- % Step 5.
- 1 1 19 {
- 2 index pdf_xorbytes arc4decode
- } for
- exch pop
- } bind def
- /pdf_gen_user_password { % <password> pdf_gen_user_password <filekey> <U>
- % common Step 1 of Algorithms 3.4 and 3.5.
- pdf_compute_encryption_key dup
- Trailer /Encrypt oget
- /R oget dup 2 eq {
- pop pdf_gen_user_password_R2
- } {
- dup 3 eq {
- pop pdf_gen_user_password_R3
- } {
- dup 4 eq { % 4 uses the algorithm as 3
- pop pdf_gen_user_password_R3
- } {
- ( **** This file uses an unknown standard security handler revision: )
- exch =string cvs concatstrings pdfformaterror printProducer
- /pdf_check_user_password cvx /undefined signalerror
- } ifelse
- } ifelse
- } ifelse
- } bind def
- % Algorithm 3.6
- /pdf_check_user_password { % <password> pdf_check_user_password <filekey> true
- % <password> pdf_check_user_password false
- pdf_gen_user_password
- Trailer /Encrypt oget /U oget
- 0 2 index length getinterval eq {
- true
- } {
- pop false
- } ifelse
- } bind def
- % Compute an owner key, ie the result of step 4 of Algorithm 3.3
- /pdf_owner_key % <password> pdf_owner_key <owner-key>
- {
- % Step 1.
- pdf_pad_key
- % Step 2.
- md5_trunk
- % 3.3 Step 3. Only executed for /R equal to 3 or more
- Trailer /Encrypt oget /R oget 3 ge {
- 50 { md5_trunk } repeat
- } if
- % Step 4 - Done in md5_trunk.
- } bind def
- % Algorithm 3.7
- /pdf_check_owner_password { % <password> pdf_check_owner_password <filekey> true
- % <password> pdf_check_owner_password false
- % Step 1.
- pdf_owner_key
- % Step 2.
- Trailer /Encrypt oget dup /O oget 2 index arc4decode
- % <encryption-key> <encrypt-dict> <decrypted-O>
- % Step 3. Only executed for /R equal to 3 or more
- exch /R oget 3 ge {
- 1 1 19 {
- 2 index pdf_xorbytes arc4decode
- } for
- } if
- exch pop
- % <result-of-step-3>
- pdf_check_user_password
- } bind def
- % Process the encryption information in the Trailer.
- /pdf_process_Encrypt {
- Trailer /Encrypt oget
- /Filter oget /Standard eq not {
- ( **** This file uses an unknown security handler.\n) pdfformaterror
- printProducer
- /pdf_process_Encrypt cvx /undefined signalerror
- } if
- () pdf_check_user_password
- {
- /FileKey exch def
- } {
- /PDFPassword where {
- pop PDFPassword pdf_check_user_password
- {
- /FileKey exch def
- } {
- PDFPassword pdf_check_owner_password
- {
- /FileKey exch def
- } {
- ( **** Password did not work.\n) pdfformaterror
- printProducer
- /pdf_process_Encrypt cvx /invalidfileaccess signalerror
- } ifelse
- } ifelse
- } {
- ( **** This file requires a password for access.\n) pdfformaterror
- printProducer
- /pdf_process_Encrypt cvx /invalidfileaccess signalerror
- } ifelse
- } ifelse
- % Trailer /Encrypt oget /P oget 4 and 0 eq #? and
- % { ( ****This owner of this file has requested you do not print it.\n)
- % pdfformaterror printProducer
- % /pdf_process_Encrypt cvx /invalidfileaccess signalerror
- % }
- % if
- } bind def
- % Calculate the key used to decrypt an object (to pass to .decpdfrun or
- % put into a stream dictionary).
- /computeobjkey % <object#> <generation#> computeobjkey <keystring>
- {
- exch
- FileKey length 5 add string
- dup 0 FileKey putinterval
- exch
- % stack: gen# string obj#
- 2 copy 255 and FileKey length exch put
- 2 copy -8 bitshift 255 and FileKey length 1 add exch put
- 2 copy -16 bitshift 255 and FileKey length 2 add exch put
- pop exch
- 2 copy 255 and FileKey length 3 add exch put
- 2 copy -8 bitshift 255 and FileKey length 4 add exch put
- pop md5 0 FileKey length 5 add 2 index length .min getinterval
- } bind def
- % As .pdfrun, but decrypt strings with key <key>.
- /.decpdfrun % <file> <keystring> <opdict> .decpdfrun -
- { % Construct a procedure with the file, opdict and key bound into it.
- 2 index cvlit mark mark 5 2 roll
- { .pdftoken not { (%%EOF) cvn cvx } if
- dup xcheck
- { PDFDEBUG { dup == flush } if
- 3 -1 roll pop
- 2 copy .knownget
- { exch pop exch pop exec
- }
- { exch pop
- dup /true eq
- { pop //true
- }
- { dup /false eq
- { pop //false
- }
- { dup /null eq
- { pop //null
- }
- { ( **** Unknown operator: )
- exch =string cvs concatstrings (\n) concatstrings
- pdfformaterror
- }
- ifelse
- }
- ifelse
- }
- ifelse
- }
- ifelse
- }
- { exch pop PDFDEBUG { dup ==only ( ) print flush } if
- dup type /stringtype eq
- {
- % Check if we have encrypted strings R=4 allows for
- % selection of encryption on streams and strings
- Trailer /Encrypt oget % Get encryption dictionary
- dup /R oget 4 lt % only 4 has selectable
- { % R < 4 --> encrypted strings
- pop 1 index arc4decode % Decrypt string
- PDFDEBUG { (%Decrypted: ) print dup == flush } if
- } { % Else R = 4
- /StrF knownoget % Get StrF (if present)
- { % If StrF is present ...
- /Identity eq not % Check if StrF != Identity
- { 1 index arc4decode % Decrypt string
- PDFDEBUG { (%Decrypted: ) print dup == flush } if
- }
- if % If StrF != identity
- }
- if % If StrF is known
- }
- ifelse % Ifelse R < 4
- }
- if % If = stringtype
- exch pop
- }
- ifelse
- }
- aload pop .packtomark cvx
- /loop cvx 2 packedarray cvx
- { stopped /PDFsource } aload pop
- PDFsource
- { store { stop } if } aload pop .packtomark cvx
- /PDFsource 3 -1 roll store exec
- } bind def
- % Run the code to resolve an object reference.
- /pdf_run_resolve
- { /FileKey where % Check if the file is encrypted
- { pop % File is encrypted
- 2 copy computeobjkey dup 4 1 roll
- PDFfile exch resolveopdict .decpdfrun
- dup dup dup 5 2 roll
- % stack: object object key object object
- { % Use loop to provide an exitable context.
- xcheck exch type /dicttype eq and % Check if executable dictionary
- not { % If object is not ...
- pop pop % ignore object
- exit % Exit 'loop' context
- } if % If not possible stream
- % Starting with PDF 1.4 (R = 3), there are some extra features
- % which control encryption of streams. The EncryptMetadata entry
- % in the Encrypt dict controls the encryption of metadata streams.
- Trailer /Encrypt oget % Get encryption dictionary
- dup /R oget dup 3 lt % Only PDF 1.4 and higher has options
- { % R < 3 --> all streams encrypted
- pop pop /StreamKey exch put % Insert StreamKey in dictionary
- exit % Exit 'loop' context
- } if
- % Check EncryptMeta. stack: object object key Encrypt R
- exch dup /EncryptMetadata knownoget % Get EncryptMetadata (if present)
- not { true } if % If not present default = true
- not % Check if EncryptMetadata = false
- { % if false we need to check the stream type
- 3 index /Type knownoget % Get stream type (if present)
- not { //null } if % If type not present use fake name
- /Metadata eq % Check if the type is Metadata
- { pop pop pop pop % Type == Metadata --> no encryption
- exit % Exit 'loop' context
- } if
- } if
- % PDF 1.5 encryption (R == 4) has selectable encryption handlers. If
- % this is not PDF 1.5 encryption (R < 4) then we are done checking and
- % we need to decrypt the stream. stack: object object key R Encrypt
- exch 4 lt % Check for less than PDF 1.5
- { pop /StreamKey exch put % Insert StreamKey in dictionary
- exit % Exit 'loop' context
- } if
- % Check if the stream encryption handler (StmF) == Identity.
- /StmF knownoget % Get StmF (if present)
- not { /Identity } if % If StmF not present default = Identity
- /Identity eq % Check if StmF == Identity
- { pop pop % Identity --> no encryption
- exit % Exit 'loop' context
- } if
- % If we get here then we need to decrypt the stream.
- /StreamKey exch put % Insert StreamKey into dictionary
- exit % Exit 'loop' context, never loop
- } loop % End of loop exitable context
- } { % Else file is not encrypted
- PDFfile resolveopdict .pdfrun
- } ifelse % Ifelse encrypted
- } bind def
- % Prefix a decryption filter to a stream if needed.
- % Stack: readdata? dict parms file/string filternames
- % (both before and after).
- /pdf_decrypt_stream
- { 3 index /StreamKey known % Check if the file is encrypted
- {
- exch
- % Stack: readdata? dict parms filternames file/string
- 3 index /Length oget
- dup 0 eq {
- % Handle Length=0 case specially to avoid SubFileDecode semantics
- pop pop ()
- } {
- () /SubFileDecode filter
- } ifelse
- 3 index /StreamKey get arc4decodefilter
- exch
- } if
- } bind def
- end % pdfdict
- .setglobal
|