From a19122478dc77cad88383a7f2ca56220c9ad202f Mon Sep 17 00:00:00 2001 From: Eliot Jones Date: Sat, 8 Jun 2019 14:14:51 +0100 Subject: [PATCH] begin adding support for in-document security handlers to support aes 128/256 encryption #34 --- .../Encryption/CryptDictionary.cs | 67 ++++++++++ .../Encryption/CryptHandler.cs | 101 ++++++++++++++ .../Encryption/EncryptionAlgorithmCode.cs | 29 ++++ .../Encryption/EncryptionDictionary.cs | 125 +++--------------- .../Encryption/EncryptionDictionaryFactory.cs | 55 ++++++++ .../Encryption/EncryptionHandler.cs | 78 ++++++++--- .../Encryption/UserAccessPermissions.cs | 51 +++++++ .../Tokens/NameToken.Constants.cs | 5 + 8 files changed, 382 insertions(+), 129 deletions(-) create mode 100644 src/UglyToad.PdfPig/Encryption/CryptDictionary.cs create mode 100644 src/UglyToad.PdfPig/Encryption/CryptHandler.cs create mode 100644 src/UglyToad.PdfPig/Encryption/EncryptionAlgorithmCode.cs create mode 100644 src/UglyToad.PdfPig/Encryption/EncryptionDictionaryFactory.cs create mode 100644 src/UglyToad.PdfPig/Encryption/UserAccessPermissions.cs diff --git a/src/UglyToad.PdfPig/Encryption/CryptDictionary.cs b/src/UglyToad.PdfPig/Encryption/CryptDictionary.cs new file mode 100644 index 00000000..0cedd1f1 --- /dev/null +++ b/src/UglyToad.PdfPig/Encryption/CryptDictionary.cs @@ -0,0 +1,67 @@ +namespace UglyToad.PdfPig.Encryption +{ + internal class CryptDictionary + { + public static CryptDictionary Identity { get; } = new CryptDictionary(); + + public Method Name { get; } + + public TriggerEvent Event { get; } + + public int Length { get; } + + public bool IsIdentity { get; } + + public CryptDictionary(Method name, TriggerEvent @event, int length) + { + Name = name; + Event = @event; + Length = length; + IsIdentity = false; + } + + private CryptDictionary() + { + Name = Method.None; + IsIdentity = true; + } + + /// + /// The method used by the consumer application to decrypt data. + /// + public enum Method + { + /// + /// The application does not decrypt data but directs the input stream + /// to the security handler for decryption. + /// + None, + /// + /// The application asks the security handler for the encryption key + /// and implicitly decrypts data using the RC4 algorithm. + /// + V2, + /// + /// (PDF 1.6) The application asks the security handler for the encryption key and implicitly decrypts data using the AES algorithm in Cipher Block Chaining (CBC) mode + /// with a 16-byte block size and an initialization vector that is randomly generated and placed as the first 16 bytes in the stream or string. + /// + AesV2 + } + + /// + /// The event to be used to trigger the authorization that is required + /// to access encryption keys used by this filter. + /// + public enum TriggerEvent + { + /// + /// Authorization is required when a document is opened. + /// + DocumentOpen, + /// + /// Authorization is required when accessing embedded files. + /// + EmbeddedFileOpen + } + } +} diff --git a/src/UglyToad.PdfPig/Encryption/CryptHandler.cs b/src/UglyToad.PdfPig/Encryption/CryptHandler.cs new file mode 100644 index 00000000..18147f51 --- /dev/null +++ b/src/UglyToad.PdfPig/Encryption/CryptHandler.cs @@ -0,0 +1,101 @@ +namespace UglyToad.PdfPig.Encryption +{ + using System; + using Exceptions; + using Tokens; + + internal class CryptHandler + { + private readonly DictionaryToken cryptDictionary; + + public CryptDictionary StreamDictionary { get; } + + public CryptDictionary StringDictionary { get; } + + public CryptHandler(DictionaryToken cryptDictionary, + NameToken streamName, NameToken stringName) + { + if (streamName == null) + { + throw new ArgumentNullException(nameof(streamName)); + } + + if (stringName == null) + { + throw new ArgumentNullException(nameof(stringName)); + } + + this.cryptDictionary = cryptDictionary ?? throw new ArgumentNullException(nameof(cryptDictionary)); + StreamDictionary = ParseCryptDictionary(cryptDictionary, streamName); + StringDictionary = ParseCryptDictionary(cryptDictionary, stringName); + } + + public CryptDictionary GetNamedCryptDictionary(NameToken name) + { + if (name == null) + { + throw new ArgumentNullException(nameof(name)); + } + + return ParseCryptDictionary(cryptDictionary, name); + } + + private static CryptDictionary ParseCryptDictionary(DictionaryToken cryptDictionary, NameToken name) + { + if (name == NameToken.Identity) + { + return CryptDictionary.Identity; + } + + if (!cryptDictionary.TryGet(name, out DictionaryToken cryptDictionaryToken)) + { + throw new PdfDocumentEncryptedException($"Could not find named crypt filter {name} for decryption in crypt dictionary: {cryptDictionaryToken}."); + } + + if (cryptDictionaryToken.TryGet(NameToken.Type, out NameToken typeName) && typeName != NameToken.CryptFilter) + { + throw new PdfDocumentEncryptedException($"Invalid crypt dictionary type {typeName} for crypt filter {name}: {cryptDictionaryToken}."); + } + + var cfmName = cryptDictionaryToken.TryGet(NameToken.Cfm, out NameToken cfm) ? cfm : NameToken.None; + + CryptDictionary.Method method; + if (cfmName == NameToken.None) + { + method = CryptDictionary.Method.None; + } + else if (cfmName == NameToken.V2) + { + method = CryptDictionary.Method.V2; + } + else if (cfmName == NameToken.Aesv2) + { + method = CryptDictionary.Method.AesV2; + } + else + { + throw new PdfDocumentEncryptedException($"Unrecognized CFM option for crypt filter {cfm}: {cryptDictionaryToken}."); + } + + var eventName = cryptDictionaryToken.TryGet(NameToken.AuthEvent, out NameToken auth) ? auth : NameToken.DocOpen; + + CryptDictionary.TriggerEvent @event; + if (eventName == NameToken.DocOpen) + { + @event = CryptDictionary.TriggerEvent.DocumentOpen; + } + else if (eventName == NameToken.EfOpen) + { + @event = CryptDictionary.TriggerEvent.EmbeddedFileOpen; + } + else + { + throw new PdfDocumentEncryptedException($"Unrecognized AuthEvent option for crypt filter {eventName}: {cryptDictionaryToken}."); + } + + var length = cryptDictionaryToken.TryGet(NameToken.Length, out NumericToken lengthNumeric) ? lengthNumeric.Int : 0; + + return new CryptDictionary(method, @event, length); + } + } +} diff --git a/src/UglyToad.PdfPig/Encryption/EncryptionAlgorithmCode.cs b/src/UglyToad.PdfPig/Encryption/EncryptionAlgorithmCode.cs new file mode 100644 index 00000000..d53e4ff5 --- /dev/null +++ b/src/UglyToad.PdfPig/Encryption/EncryptionAlgorithmCode.cs @@ -0,0 +1,29 @@ +namespace UglyToad.PdfPig.Encryption +{ + /// + /// A code specifying the algorithm to be used in encrypting and decrypting the document. + /// + internal enum EncryptionAlgorithmCode + { + /// + /// An algorithm that is undocumented and no longer supported. + /// + Unrecognized = 0, + /// + /// RC4 or AES encryption using a key of 40 bits. + /// + Rc4OrAes40BitKey = 1, + /// + /// RC4 or AES encryption using a key of more than 40 bits. + /// + Rc4OrAesGreaterThan40BitKey = 2, + /// + /// An unpublished algorithm that permits encryption key lengths ranging from 40 to 128 bits. + /// + UnpublishedAlgorithm40To128BitKey = 3, + /// + /// The security handler defines the use of encryption and decryption in the document. + /// + SecurityHandlerInDocument + } +} \ No newline at end of file diff --git a/src/UglyToad.PdfPig/Encryption/EncryptionDictionary.cs b/src/UglyToad.PdfPig/Encryption/EncryptionDictionary.cs index 8ae32e55..37ea691d 100644 --- a/src/UglyToad.PdfPig/Encryption/EncryptionDictionary.cs +++ b/src/UglyToad.PdfPig/Encryption/EncryptionDictionary.cs @@ -1,7 +1,7 @@ namespace UglyToad.PdfPig.Encryption { using System; - using Tokenization.Scanner; + using Exceptions; using Tokens; using Util; @@ -53,129 +53,40 @@ OwnerBytes = OtherEncodings.StringAsLatin1Bytes(ownerPasswordCheck); UserBytes = OtherEncodings.StringAsLatin1Bytes(userPasswordCheck); } - } - internal static class EncryptionDictionaryFactory - { - public static EncryptionDictionary Read(DictionaryToken encryptionDictionary, IPdfTokenScanner tokenScanner) + public bool TryGetCryptHandler(out CryptHandler cryptHandler) { - if (encryptionDictionary == null) - { - throw new ArgumentNullException(nameof(encryptionDictionary)); - } - - var filter = encryptionDictionary.Get(NameToken.Filter, tokenScanner); + cryptHandler = null; - var code = EncryptionAlgorithmCode.Unrecognized; - - if (encryptionDictionary.TryGetOptionalTokenDirect(NameToken.V, tokenScanner, out NumericToken vNum)) + if (EncryptionAlgorithmCode != EncryptionAlgorithmCode.SecurityHandlerInDocument) { - code = (EncryptionAlgorithmCode) vNum.Int; + return false; } - var length = default(int?); - - if (encryptionDictionary.TryGetOptionalTokenDirect(NameToken.Length, tokenScanner, out NumericToken lengthToken)) + if (!Dictionary.TryGet(NameToken.Cf, out DictionaryToken cryptFilterDictionary)) { - length = lengthToken.Int; + return false; } - var revision = default(int); - if (encryptionDictionary.TryGetOptionalTokenDirect(NameToken.R, tokenScanner, out NumericToken revisionToken)) + var namedFilters = cryptFilterDictionary; + + var streamFilterName = Dictionary.TryGet(NameToken.StmF, out NameToken streamFilterToken) ? streamFilterToken : NameToken.Identity; + var stringFilterName = Dictionary.TryGet(NameToken.StrF, out NameToken stringFilterToken) ? stringFilterToken : NameToken.Identity; + + if (streamFilterName != NameToken.Identity && !namedFilters.TryGet(streamFilterName, out _)) { - revision = revisionToken.Int; + throw new PdfDocumentEncryptedException($"Stream filter {streamFilterName} not found in crypt dictionary: {cryptFilterDictionary}."); } - encryptionDictionary.TryGetOptionalStringDirect(NameToken.O, tokenScanner, out var ownerString); - encryptionDictionary.TryGetOptionalStringDirect(NameToken.U, tokenScanner, out var userString); - - var access = default(UserAccessPermissions); - - if (encryptionDictionary.TryGetOptionalTokenDirect(NameToken.P, tokenScanner, out NumericToken accessToken)) + if (stringFilterName != NameToken.Identity && !namedFilters.TryGet(stringFilterName, out _)) { - access = (UserAccessPermissions) accessToken.Int; + throw new PdfDocumentEncryptedException($"String filter {stringFilterName} not found in crypt dictionary: {cryptFilterDictionary}."); } - encryptionDictionary.TryGetOptionalTokenDirect(NameToken.EncryptMetaData, tokenScanner, out BooleanToken encryptMetadata); + cryptHandler = new CryptHandler(namedFilters, streamFilterName, stringFilterName); - return new EncryptionDictionary(filter.Data, code, length, revision, ownerString, userString, access, encryptionDictionary, - encryptMetadata?.Data ?? false); + return true; } } - - /// - /// A code specifying the algorithm to be used in encrypting and decrypting the document. - /// - internal enum EncryptionAlgorithmCode - { - /// - /// An algorithm that is undocumented and no longer supported. - /// - Unrecognized = 0, - /// - /// RC4 or AES encryption using a key of 40 bits. - /// - Rc4OrAes40BitKey = 1, - /// - /// RC4 or AES encryption using a key of more than 40 bits. - /// - Rc4OrAesGreaterThan40BitKey = 2, - /// - /// An unpublished algorithm that permits encryption key lengths ranging from 40 to 128 bits. - /// - UnpublishedAlgorithm40To128BitKey = 3, - /// - /// The security handler defines the use of encryption and decryption in the document. - /// - SecurityHandlerInDocument - } - - [Flags] - internal enum UserAccessPermissions - { - /// - /// (Revision 2) Print the document. - /// (Revision 3 or greater) Print the document (possibly not at the highest quality level, see ). - /// - Print = 1 << 2, - /// - /// Modify the contents of the document by operations other than those - /// controlled by , and . - /// - Modify = 1 << 3, - /// - /// (Revision 2) Copy or otherwise extract text and graphics from the document, including extracting text and graphics - /// (in support of accessibility to users with disabilities or for other purposes). - /// (Revision 3 or greater) Copy or otherwise extract text and graphics from the document by operations other - /// than that controlled by . - /// - CopyTextAndGraphics = 1 << 4, - /// - /// Add or modify text annotations, fill in interactive form fields, and, if is also set, - /// create or modify interactive form fields (including signature fields). - /// - AddOrModifyTextAnnotationsAndFillFormFields = 1 << 5, - /// - /// (Revision 3 or greater) Fill in existing interactive form fields (including signature fields), - /// even if is clear. - /// - FillExistingFormFields = 1 << 8, - /// - /// (Revision 3 or greater) Extract text and graphics (in support of accessibility to users with disabilities or for other purposes). - /// - ExtractTextAndGraphics = 1 << 9, - /// - /// (Revision 3 or greater) Assemble the document (insert, rotate, or delete pages and create bookmarks or thumbnail images), - /// even if is clear. - /// - AssembleDocument = 1 << 10, - /// - /// (Revision 3 or greater) Print the document to a representation from which a faithful digital copy of the PDF content could be generated. - /// When this is clear (and is set), printing is limited to a low-level representation of the appearance, - /// possibly of degraded quality. - /// - PrintHighQuality = 1 << 12 - } - } diff --git a/src/UglyToad.PdfPig/Encryption/EncryptionDictionaryFactory.cs b/src/UglyToad.PdfPig/Encryption/EncryptionDictionaryFactory.cs new file mode 100644 index 00000000..0065eb9b --- /dev/null +++ b/src/UglyToad.PdfPig/Encryption/EncryptionDictionaryFactory.cs @@ -0,0 +1,55 @@ +namespace UglyToad.PdfPig.Encryption +{ + using System; + using Tokenization.Scanner; + using Tokens; + using Util; + + internal static class EncryptionDictionaryFactory + { + public static EncryptionDictionary Read(DictionaryToken encryptionDictionary, IPdfTokenScanner tokenScanner) + { + if (encryptionDictionary == null) + { + throw new ArgumentNullException(nameof(encryptionDictionary)); + } + + var filter = encryptionDictionary.Get(NameToken.Filter, tokenScanner); + + var code = EncryptionAlgorithmCode.Unrecognized; + + if (encryptionDictionary.TryGetOptionalTokenDirect(NameToken.V, tokenScanner, out NumericToken vNum)) + { + code = (EncryptionAlgorithmCode) vNum.Int; + } + + var length = default(int?); + + if (encryptionDictionary.TryGetOptionalTokenDirect(NameToken.Length, tokenScanner, out NumericToken lengthToken)) + { + length = lengthToken.Int; + } + + var revision = default(int); + if (encryptionDictionary.TryGetOptionalTokenDirect(NameToken.R, tokenScanner, out NumericToken revisionToken)) + { + revision = revisionToken.Int; + } + + encryptionDictionary.TryGetOptionalStringDirect(NameToken.O, tokenScanner, out var ownerString); + encryptionDictionary.TryGetOptionalStringDirect(NameToken.U, tokenScanner, out var userString); + + var access = default(UserAccessPermissions); + + if (encryptionDictionary.TryGetOptionalTokenDirect(NameToken.P, tokenScanner, out NumericToken accessToken)) + { + access = (UserAccessPermissions) accessToken.Int; + } + + encryptionDictionary.TryGetOptionalTokenDirect(NameToken.EncryptMetaData, tokenScanner, out BooleanToken encryptMetadata); + + return new EncryptionDictionary(filter.Data, code, length, revision, ownerString, userString, access, encryptionDictionary, + encryptMetadata?.Data ?? true); + } + } +} \ No newline at end of file diff --git a/src/UglyToad.PdfPig/Encryption/EncryptionHandler.cs b/src/UglyToad.PdfPig/Encryption/EncryptionHandler.cs index e29952c6..180586d9 100644 --- a/src/UglyToad.PdfPig/Encryption/EncryptionHandler.cs +++ b/src/UglyToad.PdfPig/Encryption/EncryptionHandler.cs @@ -29,7 +29,10 @@ [CanBeNull] private readonly EncryptionDictionary encryptionDictionary; - + + [CanBeNull] + private readonly CryptHandler cryptHandler; + private readonly byte[] encryptionKey; private readonly bool useAes; @@ -49,9 +52,18 @@ return; } + useAes = false; + if (encryptionDictionary.EncryptionAlgorithmCode == EncryptionAlgorithmCode.SecurityHandlerInDocument) { - throw new PdfDocumentEncryptedException("Document encrypted with unsupported algorithm.", encryptionDictionary); + if (!encryptionDictionary.TryGetCryptHandler(out var cryptHandlerLocal)) + { + throw new PdfDocumentEncryptedException("Document encrypted with security handler in document but no crypt dictionary found.", encryptionDictionary); + } + + cryptHandler = cryptHandlerLocal; + + useAes = cryptHandlerLocal?.StreamDictionary?.Name == CryptDictionary.Method.AesV2; } var charset = OtherEncodings.Iso88591; @@ -93,19 +105,15 @@ throw new PdfDocumentEncryptedException("The document was encrypted and the provided password was neither the user or owner password.", encryptionDictionary); } - encryptionKey = CalculateKeyRevisions2To4(decryptionPasswordBytes, encryptionDictionary.OwnerBytes, (int)encryptionDictionary.UserAccessPermissions, - encryptionDictionary.StandardSecurityHandlerRevision, + encryptionKey = CalculateKeyRevisions2To4(decryptionPasswordBytes, encryptionDictionary, length, documentIdBytes); - - useAes = false; } private static bool IsUserPassword(byte[] passwordBytes, EncryptionDictionary encryptionDictionary, int length, byte[] documentIdBytes) { // 1. Create an encryption key based on the user password string. - var calculatedEncryptionKey = CalculateKeyRevisions2To4(passwordBytes, encryptionDictionary.OwnerBytes, (int)encryptionDictionary.UserAccessPermissions, - encryptionDictionary.StandardSecurityHandlerRevision, length, documentIdBytes); + var calculatedEncryptionKey = CalculateKeyRevisions2To4(passwordBytes, encryptionDictionary, length, documentIdBytes); byte[] output; @@ -245,6 +253,13 @@ { case StreamToken stream: { + if (cryptHandler?.StreamDictionary?.IsIdentity == true + || cryptHandler?.StreamDictionary?.Name == CryptDictionary.Method.None) + { + // TODO: No idea if this is right. + return token; + } + if (stream.StreamDictionary.TryGet(NameToken.Type, out NameToken typeName)) { if (NameToken.Xref.Equals(typeName)) @@ -270,6 +285,13 @@ } case StringToken stringToken: { + if (cryptHandler?.StringDictionary?.IsIdentity == true + || cryptHandler?.StringDictionary?.Name == CryptDictionary.Method.None) + { + // TODO: No idea if this is right. + return token; + } + var data = OtherEncodings.StringAsLatin1Bytes(stringToken.Data); var decrypted = DecryptData(data, reference); @@ -386,22 +408,23 @@ } } - private static byte[] CalculateKeyRevisions2To4(byte[] password, byte[] ownerKey, - int permissions, int revision, int length, byte[] documentId) + private static byte[] CalculateKeyRevisions2To4(byte[] password, EncryptionDictionary encryptionDictionary, int length, byte[] documentId) { // 1. Pad or truncate the password string to exactly 32 bytes. var passwordFull = GetPaddedPassword(password); + var revision = encryptionDictionary.StandardSecurityHandlerRevision; + using (var md5 = MD5.Create()) { // 2. Initialize the MD5 hash function and pass the result of step 1 as input to this function. UpdateMd5(md5, passwordFull); // 3. Pass the value of the encryption dictionary's owner key entry to the MD5 hash function. - UpdateMd5(md5, ownerKey); + UpdateMd5(md5, encryptionDictionary.OwnerBytes); // 4. Treat the value of the P entry as an unsigned 4-byte integer. - var unsigned = (uint)permissions; + var unsigned = (uint)encryptionDictionary.UserAccessPermissions; // 4. Pass these bytes to the MD5 hash function, low-order byte first. UpdateMd5(md5, new[] { (byte)(unsigned) }); @@ -414,7 +437,7 @@ // 6. (Revision 4 or greater) If document metadata is not being encrypted, pass 4 bytes // with the value 0xFFFFFFFF to the MD5 hash function. - if (revision >= 4) + if (revision >= 4 && !encryptionDictionary.EncryptMetadata) { UpdateMd5(md5, new byte[] { 0xFF, 0xFF, 0xFF, 0xFF }); } @@ -427,22 +450,33 @@ { var n = length; + md5.TransformFinalBlock(EmptyArray.Instance, 0, 0); + var input = md5.Hash; - - for (var i = 0; i < 50; i++) + using (var newMd5 = MD5.Create()) { - UpdateMd5(md5, input.Take(n).ToArray()); - input = md5.Hash; + for (var i = 0; i < 50; i++) + { + input = newMd5.ComputeHash(input.Take(n).ToArray()); + } } + + var result = new byte[length]; + + Array.Copy(input, result, length); + + return result; } + else + { + md5.TransformFinalBlock(EmptyArray.Instance, 0, 0); - md5.TransformFinalBlock(EmptyArray.Instance, 0, 0); + var result = new byte[length]; - var result = new byte[length]; + Array.Copy(md5.Hash, result, length); - Array.Copy(md5.Hash, result, length); - - return result; + return result; + } } } diff --git a/src/UglyToad.PdfPig/Encryption/UserAccessPermissions.cs b/src/UglyToad.PdfPig/Encryption/UserAccessPermissions.cs new file mode 100644 index 00000000..a8ba2b4a --- /dev/null +++ b/src/UglyToad.PdfPig/Encryption/UserAccessPermissions.cs @@ -0,0 +1,51 @@ +namespace UglyToad.PdfPig.Encryption +{ + using System; + + [Flags] + internal enum UserAccessPermissions + { + /// + /// (Revision 2) Print the document. + /// (Revision 3 or greater) Print the document (possibly not at the highest quality level, see ). + /// + Print = 1 << 2, + /// + /// Modify the contents of the document by operations other than those + /// controlled by , and . + /// + Modify = 1 << 3, + /// + /// (Revision 2) Copy or otherwise extract text and graphics from the document, including extracting text and graphics + /// (in support of accessibility to users with disabilities or for other purposes). + /// (Revision 3 or greater) Copy or otherwise extract text and graphics from the document by operations other + /// than that controlled by . + /// + CopyTextAndGraphics = 1 << 4, + /// + /// Add or modify text annotations, fill in interactive form fields, and, if is also set, + /// create or modify interactive form fields (including signature fields). + /// + AddOrModifyTextAnnotationsAndFillFormFields = 1 << 5, + /// + /// (Revision 3 or greater) Fill in existing interactive form fields (including signature fields), + /// even if is clear. + /// + FillExistingFormFields = 1 << 8, + /// + /// (Revision 3 or greater) Extract text and graphics (in support of accessibility to users with disabilities or for other purposes). + /// + ExtractTextAndGraphics = 1 << 9, + /// + /// (Revision 3 or greater) Assemble the document (insert, rotate, or delete pages and create bookmarks or thumbnail images), + /// even if is clear. + /// + AssembleDocument = 1 << 10, + /// + /// (Revision 3 or greater) Print the document to a representation from which a faithful digital copy of the PDF content could be generated. + /// When this is clear (and is set), printing is limited to a low-level representation of the appearance, + /// possibly of degraded quality. + /// + PrintHighQuality = 1 << 12 + } +} \ No newline at end of file diff --git a/src/UglyToad.PdfPig/Tokens/NameToken.Constants.cs b/src/UglyToad.PdfPig/Tokens/NameToken.Constants.cs index 0e298b24..4d87064b 100644 --- a/src/UglyToad.PdfPig/Tokens/NameToken.Constants.cs +++ b/src/UglyToad.PdfPig/Tokens/NameToken.Constants.cs @@ -39,6 +39,7 @@ public static readonly NameToken Ascii85Decode = new NameToken("ASCII85Decode"); public static readonly NameToken Ascii85DecodeAbbreviation = new NameToken("A85"); public static readonly NameToken Attached = new NameToken("Attached"); + public static readonly NameToken AuthEvent = new NameToken("AuthEvent"); public static readonly NameToken Author = new NameToken("Author"); public static readonly NameToken AvgWidth = new NameToken("AvgWidth"); #endregion @@ -126,6 +127,7 @@ public static readonly NameToken Creator = new NameToken("Creator"); public static readonly NameToken CropBox = new NameToken("CropBox"); public static readonly NameToken Crypt = new NameToken("Crypt"); + public static readonly NameToken CryptFilter = new NameToken("CryptFilter"); public static readonly NameToken Cs = new NameToken("CS"); // D public static readonly NameToken D = new NameToken("D"); @@ -164,6 +166,7 @@ public static readonly NameToken Dl = new NameToken("DL"); public static readonly NameToken Dm = new NameToken("Dm"); public static readonly NameToken Doc = new NameToken("Doc"); + public static readonly NameToken DocOpen = new NameToken("DocOpen"); public static readonly NameToken DocChecksum = new NameToken("DocChecksum"); public static readonly NameToken DocTimeStamp = new NameToken("DocTimeStamp"); public static readonly NameToken Docmdp = new NameToken("DocMDP"); @@ -181,6 +184,7 @@ public static readonly NameToken E = new NameToken("E"); public static readonly NameToken EarlyChange = new NameToken("EarlyChange"); public static readonly NameToken Ef = new NameToken("EF"); + public static readonly NameToken EfOpen = new NameToken("EFOpen"); public static readonly NameToken EmbeddedFdfs = new NameToken("EmbeddedFDFs"); public static readonly NameToken EmbeddedFiles = new NameToken("EmbeddedFiles"); public static readonly NameToken Empty = new NameToken(""); @@ -527,6 +531,7 @@ public static readonly NameToken UserUnit = new NameToken("UserUnit"); // V public static readonly NameToken V = new NameToken("V"); + public static readonly NameToken V2 = new NameToken("V2"); public static readonly NameToken VerisignPpkvs = new NameToken("VeriSign.PPKVS"); public static readonly NameToken Version = new NameToken("Version"); public static readonly NameToken Vertices = new NameToken("Vertices");