pdf_sec.ps 14 KB

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