Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add AES-128 encryption #94

Open
wants to merge 27 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
43b0840
Added a new enum value for AES-128 encryption
benjcallaghan Oct 24, 2018
68acb72
Added extra AES padding for encryption key generation
benjcallaghan Oct 24, 2018
89af79b
Renamed PrepareKey to PrepareRC4Key for consistency
benjcallaghan Oct 24, 2018
614a1e2
Created a new method to encrypt with AES-128
benjcallaghan Oct 24, 2018
268790d
Added AES encryption alongside existing RC4 encryption
benjcallaghan Oct 24, 2018
ccea8b3
Removed duplicate code that unintentionally encrypted the data twice
benjcallaghan Oct 24, 2018
d8cb127
Added AES decryption methods
benjcallaghan Oct 24, 2018
c06ef4b
Renamed methods to match their function
bcallaghan-et Oct 26, 2018
1c4c18b
Added CryptFilterDictionary keys
bcallaghan-et Oct 26, 2018
eedd02b
Added crypt filter dictionary in the case of AES-128 encryption
bcallaghan-et Oct 26, 2018
8bb64f7
Added support for V=4 and R=4 during password validation
bcallaghan-et Oct 26, 2018
a280114
Removed test files
bcallaghan-et Oct 26, 2018
55ac8c1
Fixed the names of new elements
bcallaghan-et Oct 26, 2018
b578044
Refactored code style
bcallaghan-et Oct 26, 2018
c72f978
Created a standalone CryptFilterDictionary for convenience
bcallaghan-et Oct 29, 2018
aec1814
Simplified the implementation of the crypt filter dictionary
bcallaghan-et Oct 29, 2018
83cea9a
Automatically set Stream length using actual length
bcallaghan-et Oct 29, 2018
c51820b
Disabled the debug-mode IV
bcallaghan-et Oct 31, 2018
3cdae47
Added a new error message for unsupported crypt filters
bcallaghan-et Oct 31, 2018
8f2a8aa
Added a new error message for an unsupported revision number
bcallaghan-et Oct 31, 2018
22ec785
Added support for reading documents that use V=4 and crypt filters
bcallaghan-et Oct 31, 2018
8895db7
Fixed the retrieval of the IV from encrypted data
bcallaghan-et Oct 31, 2018
85be61c
Added a guard to avoid decrypting the XRef stream
bcallaghan-et Oct 31, 2018
05ee372
Reset the solution file back to its original state
bcallaghan-et Oct 31, 2018
c4c55f3
Fixed a typo in the comments
bcallaghan-et Nov 5, 2018
72366fd
Merge remote-tracking branch 'upstream/master'
bcallaghan-et Jul 24, 2019
8850ad3
Removed incorrect #if DEBUG
bcallaghan-et Jul 24, 2019
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/PdfSharp-gdi/PdfSharp-gdi.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -902,6 +902,9 @@
<Compile Include="..\PdfSharp\Pdf.Printing\PdfFilePrinter.cs">
<Link>Pdf.Printing\PdfFilePrinter.cs</Link>
</Compile>
<Compile Include="..\PdfSharp\Pdf.Security\CryptFilterDictionary.cs">
<Link>Pdf.Security\CryptFilterDictionary.cs</Link>
</Compile>
<Compile Include="..\PdfSharp\Pdf.Security\enums\PdfDocumentSecurity.cs">
<Link>Pdf.Security\enums\PdfDocumentSecurity.cs</Link>
</Compile>
Expand Down
3 changes: 3 additions & 0 deletions src/PdfSharp-wpf/PdfSharp-wpf.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -847,6 +847,9 @@
<Compile Include="..\PdfSharp\Pdf.Printing\PdfFilePrinter.cs">
<Link>Pdf.Printing\PdfFilePrinter.cs</Link>
</Compile>
<Compile Include="..\PdfSharp\Pdf.Security\CryptFilterDictionary.cs">
<Link>Pdf.Security\CryptFilterDictionary.cs</Link>
</Compile>
<Compile Include="..\PdfSharp\Pdf.Security\enums\PdfDocumentSecurity.cs">
<Link>Pdf.Security\enums\PdfDocumentSecurity.cs</Link>
</Compile>
Expand Down
4 changes: 2 additions & 2 deletions src/PdfSharp/Pdf.IO/PdfReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -452,10 +452,10 @@ public static PdfDocument Open(Stream stream, string password, PdfDocumentOpenMo
iref.ObjectNumber);
}

// Encrypt all objects.
// Decrypt all objects.
if (xrefEncrypt != null)
{
document.SecurityHandler.EncryptDocument();
document.SecurityHandler.DecryptDocument();
}

// Fix references of trailer values and then objects and irefs are consistent.
Expand Down
11 changes: 6 additions & 5 deletions src/PdfSharp/Pdf.IO/PdfWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -427,11 +427,12 @@ public void WriteStream(PdfDictionary value, bool omitStream)
byte[] bytes = value.Stream.Value;
if (bytes.Length != 0)
{
if (_securityHandler != null)
{
bytes = (byte[])bytes.Clone();
bytes = _securityHandler.EncryptBytes(bytes);
}
// Dictionary stream has already been encrypted
// if (_securityHandler != null)
// {
// bytes = (byte[])bytes.Clone();
// bytes = _securityHandler.EncryptBytes(bytes);
// }
Write(bytes);
if (_lastCat != CharCat.NewLine)
WriteRaw('\n');
Expand Down
207 changes: 207 additions & 0 deletions src/PdfSharp/Pdf.Security/CryptFilterDictionary.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,207 @@
using System;

namespace PdfSharp.Pdf.Security
{
/// <summary>
/// The method that should be used when decrypting data.
/// </summary>
internal enum CFM
{
/// <summary>
/// Applications that encounter this value shall report that the file is encrypted with an unsupported algorithm.
/// </summary>
Unknown,

/// <summary>
/// The application shall not decrypt data but shall direct the input stream to the security handler for decryption.
/// </summary>
None,

/// <summary>
/// The application shall ask the security handler for the encryption key and shall implicitly decrypt data with
/// "Algorithm 1: Encryption of data using the RC4 or AES algorithms", using the RC4 algorithm.
/// </summary>
V2,

/// <summary>
/// (PDF 1.6) The application shall ask the security handler for the encryption key and shall implicitly decrypt data with
/// "Algorithm 1: Encryption of data using the RC4 or AES algorithms", using the AES algorithm in Cipher Block Chaining (CBC)
/// mode with a 16-byte block size and an initialization vector that shall be randomly generated and placed as the first
/// 16 bytes in the stream or string.
/// </summary>
AESV2
}

/// <summary>
/// The event that will trigger authorization.
/// </summary>
internal enum AuthEvent
{
/// <summary>
/// Authorization shall be required when a document is opened.
/// </summary>
DocOpen,

/// <summary>
/// Authorization shall be required when accessing embedded files.
/// </summary>
EFOpen
}

/// <summary>
/// Represents a crypt filter dictionary used for providing granular control of encryption.
/// </summary>
internal class CryptFilterDictionary : PdfDictionary
{
/// <summary>
/// Initializes a new CryptFilterDictionary with the required Type name.
/// </summary>
public CryptFilterDictionary()
{
Type = "CryptFilter";
}

/// <summary>
/// Initializes a new CryptFilterDictionary from an existing dictionary. Used for object type transformation.
/// </summary>
/// <param name="cryptFilterDictionary">An existing dictionary to read as a crypt filter dictionary.</param>
public CryptFilterDictionary(PdfDictionary cryptFilterDictionary) : base(cryptFilterDictionary) { }

/// <summary>
/// (Optional) If present, shall be CryptFilter for a crypt filter dictionary.
/// </summary>
public string Type
{
get { return Elements.GetName("/Type"); }
private set { Elements.SetName("/Type", value); }
}

/// <summary>
/// (Optional) The method used, if any, by the conforming reader to decrypt data. The following
/// values shall be supported:
/// • None The application shall not decrypt data but shall direct the input stream to the security
/// handler for decryption.
/// • V2 The application shall ask the security handler for the encryption key and shall implicity
/// decrypt data with "Algorithm 1: Encryption of data using the RC4 or AES algorithms", using the
/// RC4 algorithm.
/// • AESV2 (PDF 1.6) The application shall ask the security handler for the encryption key and shall
/// implicitly decrypt data with "Algorithm 1: Encryption of data using the RC4 or AES algorithms",
/// using the AES algorithm in Cipher Block Chaining (CBC) mode with a 16-byte block size and an
/// initialization vector that shall be randomly generated and placed as the first 16 bytes in the
/// stream or string.
/// When the value is V2 or AESV2, the application may ask once for this encryption key and cache the key
/// for subsequent use for streams that use the same crypt filter. Therefore, there shall be a one-to-one
/// relationship between a crypt filter name and the corresponding encryption key.
/// Only the values listed here shall be supported. Applications that encounter other values shall report
/// that the file is encrypted with an unsupported algorithm.
/// Default value: None.
/// </summary>
public CFM CFM
{
get
{
string cfmName = Elements.GetName("/CFM");
switch (cfmName)
{
case "/None":
return CFM.None;
case "/V2":
return CFM.V2;
case "/AESV2":
return CFM.AESV2;
default:
return CFM.Unknown;
}
}
set
{
string cfmName;
switch (value)
{
case CFM.None:
cfmName = "None";
break;
case CFM.V2:
cfmName = "V2";
break;
case CFM.AESV2:
cfmName = "AESV2";
break;
case CFM.Unknown:
default:
throw new ArgumentOutOfRangeException("value", "The CFM must be a valid value.");
}
Elements.SetName("/CFM", cfmName);
}
}

/// <summary>
/// (Optional) The event to be used to trigger the authorization that is required to access encryption
/// keys used by this filter. If authorization fails, the event shall fail. Valid values shall be:
/// • DocOpen: Authorization shall be required when a document is opened.
/// • EFOpen: Authorization shall be required when accessing embedded files.
/// Default value: DocOpen.
/// If this filter is used as the value of StrF or StmF in the encryption dictionary, the conforming
/// reader shall ignore this key and behave as if the value is DocOpen.
/// </summary>
public AuthEvent AuthEvent
{
get
{
string authEventName = Elements.GetName("/AuthEvent");
switch (authEventName)
{
case "/DocOpen":
return AuthEvent.DocOpen;
case "/EFOpen":
return AuthEvent.EFOpen;
default:
return AuthEvent.DocOpen;
}
}
set
{
string authEventName;
switch (value)
{
case AuthEvent.DocOpen:
authEventName = "DocOpen";
break;
case AuthEvent.EFOpen:
authEventName = "EFOpen";
break;
default:
throw new ArgumentOutOfRangeException("value", "The AuthEvent must be a valid value.");
}
Elements.SetName("/AuthEvent", authEventName);
}
}

/// <summary>
/// (Optional) The bit length of the encryption key. It shall be a multiple of 8 in the range of 40 to 128.
/// Security handlers may define their own use of the Length entry and should use it to define the bit length
/// of the encryption key. Standard security handler expresses the length in multiples of 8 (16 means 128)
/// and public-key security handler express it as is (128 means 128).
/// </summary>
public int Length
{
get { return Elements.GetInteger("/Length"); }
set
{
int valueToTest = value;
if (value < 40)
{
// Value can be in bits or bytes.
valueToTest = value * 8;
}

if (valueToTest < 40 || valueToTest > 128)
throw new ArgumentOutOfRangeException("value", "The Length must be between 40 and 128 bits inclusive.");
if (valueToTest % 8 != 0)
throw new ArgumentException("The Length must be a multiple of 8 bits.", "value");

Elements.SetInteger("/Length", value);
}
}
}
}
Loading