From 0f103554fb8b3fae6e6b576f2c159b277bddfe40 Mon Sep 17 00:00:00 2001 From: Eliot Jones Date: Sun, 23 Jun 2019 13:12:47 +0100 Subject: [PATCH] handle non-standard crypt dictionary type and use hex bytes for password #34 using an online tool to encrypt a simple document with aes-128 seems to add the dictionary type cryptalgorithm rather than cryptfilter. i couldn't find any references to cryptalgorithm in the spec or pdfbox but it seems to work ok when treated as equivalent to cryptfilter. there are situations where the string derived from a hex token has a different length to the underlying bytes, for example if the hex token contains the '\0' byte, the encryption algorithm needs to use the raw bytes rather than the 'stringified' bytes. this change passes raw bytes for hex tokens for both the user and owner password keys. --- .../Encryption/CryptHandler.cs | 2 +- .../Encryption/EncryptionDictionary.cs | 16 +++------- .../Encryption/EncryptionDictionaryFactory.cs | 32 ++++++++++++++++--- .../Tokens/NameToken.Constants.cs | 1 + 4 files changed, 34 insertions(+), 17 deletions(-) diff --git a/src/UglyToad.PdfPig/Encryption/CryptHandler.cs b/src/UglyToad.PdfPig/Encryption/CryptHandler.cs index bc8af778..d3ecd60e 100644 --- a/src/UglyToad.PdfPig/Encryption/CryptHandler.cs +++ b/src/UglyToad.PdfPig/Encryption/CryptHandler.cs @@ -52,7 +52,7 @@ 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) + if (cryptDictionaryToken.TryGet(NameToken.Type, out NameToken typeName) && typeName != NameToken.CryptFilter && typeName != NameToken.CryptAlgorithm) { throw new PdfDocumentEncryptedException($"Invalid crypt dictionary type {typeName} for crypt filter {name}: {cryptDictionaryToken}."); } diff --git a/src/UglyToad.PdfPig/Encryption/EncryptionDictionary.cs b/src/UglyToad.PdfPig/Encryption/EncryptionDictionary.cs index 7fa7daff..eb539c6b 100644 --- a/src/UglyToad.PdfPig/Encryption/EncryptionDictionary.cs +++ b/src/UglyToad.PdfPig/Encryption/EncryptionDictionary.cs @@ -3,7 +3,6 @@ using System; using Exceptions; using Tokens; - using Util; using Util.JetBrains.Annotations; internal class EncryptionDictionary @@ -16,10 +15,6 @@ public int Revision { get; } - public string OwnerPasswordCheck { get; } - - public string UserPasswordCheck { get; } - public byte[] OwnerBytes { get; } public byte[] UserBytes { get; } @@ -47,8 +42,8 @@ public EncryptionDictionary(string filter, EncryptionAlgorithmCode encryptionAlgorithmCode, int? keyLength, int revision, - string ownerPasswordCheck, - string userPasswordCheck, + byte[] ownerBytes, + byte[] userBytes, byte[] ownerEncryptionBytes, byte[] userEncryptionBytes, UserAccessPermissions userAccessPermissions, @@ -59,16 +54,13 @@ EncryptionAlgorithmCode = encryptionAlgorithmCode; KeyLength = keyLength; Revision = revision; - OwnerPasswordCheck = ownerPasswordCheck; - UserPasswordCheck = userPasswordCheck; + OwnerBytes = ownerBytes; + UserBytes = userBytes; OwnerEncryptionBytes = ownerEncryptionBytes; UserEncryptionBytes = userEncryptionBytes; UserAccessPermissions = userAccessPermissions; Dictionary = dictionary; EncryptMetadata = encryptMetadata; - - OwnerBytes = OtherEncodings.StringAsLatin1Bytes(ownerPasswordCheck); - UserBytes = OtherEncodings.StringAsLatin1Bytes(userPasswordCheck); } public bool TryGetCryptHandler(out CryptHandler cryptHandler) diff --git a/src/UglyToad.PdfPig/Encryption/EncryptionDictionaryFactory.cs b/src/UglyToad.PdfPig/Encryption/EncryptionDictionaryFactory.cs index 3f64f50c..735e1d4f 100644 --- a/src/UglyToad.PdfPig/Encryption/EncryptionDictionaryFactory.cs +++ b/src/UglyToad.PdfPig/Encryption/EncryptionDictionaryFactory.cs @@ -1,6 +1,7 @@ namespace UglyToad.PdfPig.Encryption { using System; + using System.Linq; using Tokenization.Scanner; using Tokens; using Util; @@ -36,9 +37,32 @@ revision = revisionToken.Int; } - encryptionDictionary.TryGetOptionalStringDirect(NameToken.O, tokenScanner, out var ownerString); - encryptionDictionary.TryGetOptionalStringDirect(NameToken.U, tokenScanner, out var userString); - + byte[] ownerBytes = null; + if (encryptionDictionary.TryGet(NameToken.O, out IToken ownerToken)) + { + if (ownerToken is StringToken ownerString) + { + ownerBytes = OtherEncodings.StringAsLatin1Bytes(ownerString.Data); + } + else if (ownerToken is HexToken ownerHex) + { + ownerBytes = ownerHex.Bytes.ToArray(); + } + } + + byte[] userBytes = null; + if (encryptionDictionary.TryGet(NameToken.U, out IToken userToken)) + { + if (userToken is StringToken userString) + { + userBytes = OtherEncodings.StringAsLatin1Bytes(userString.Data); + } + else if (userToken is HexToken userHex) + { + userBytes = userHex.Bytes.ToArray(); + } + } + var access = default(UserAccessPermissions); if (encryptionDictionary.TryGetOptionalTokenDirect(NameToken.P, tokenScanner, out NumericToken accessToken)) @@ -58,7 +82,7 @@ encryptionDictionary.TryGetOptionalTokenDirect(NameToken.EncryptMetaData, tokenScanner, out BooleanToken encryptMetadata); - return new EncryptionDictionary(filter.Data, code, length, revision, ownerString, userString, + return new EncryptionDictionary(filter.Data, code, length, revision, ownerBytes, userBytes, ownerEncryptionBytes, userEncryptionBytes, access, diff --git a/src/UglyToad.PdfPig/Tokens/NameToken.Constants.cs b/src/UglyToad.PdfPig/Tokens/NameToken.Constants.cs index 4d87064b..23177074 100644 --- a/src/UglyToad.PdfPig/Tokens/NameToken.Constants.cs +++ b/src/UglyToad.PdfPig/Tokens/NameToken.Constants.cs @@ -127,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 CryptAlgorithm = new NameToken("CryptAlgorithm"); public static readonly NameToken CryptFilter = new NameToken("CryptFilter"); public static readonly NameToken Cs = new NameToken("CS"); // D