mirror of
https://github.com/UglyToad/PdfPig.git
synced 2025-09-19 19:07:56 +08:00
finish revision 5 and 6 owner password handling #34
moves owner password check first. correctly calculates encryption key for owner password for revision 5 and 6.
This commit is contained in:
@@ -86,12 +86,7 @@
|
|||||||
: encryptionDictionary.KeyLength.GetValueOrDefault() / 8;
|
: encryptionDictionary.KeyLength.GetValueOrDefault() / 8;
|
||||||
|
|
||||||
var isUserPassword = false;
|
var isUserPassword = false;
|
||||||
if (IsUserPassword(passwordBytes, encryptionDictionary, length, documentIdBytes))
|
if (IsOwnerPassword(passwordBytes, encryptionDictionary, length, documentIdBytes, out var userPassBytes))
|
||||||
{
|
|
||||||
decryptionPasswordBytes = passwordBytes;
|
|
||||||
isUserPassword = true;
|
|
||||||
}
|
|
||||||
else if (IsOwnerPassword(passwordBytes, encryptionDictionary, length, documentIdBytes, out var userPassBytes))
|
|
||||||
{
|
{
|
||||||
if (encryptionDictionary.Revision == 5 || encryptionDictionary.Revision == 6)
|
if (encryptionDictionary.Revision == 5 || encryptionDictionary.Revision == 6)
|
||||||
{
|
{
|
||||||
@@ -102,6 +97,11 @@
|
|||||||
decryptionPasswordBytes = userPassBytes;
|
decryptionPasswordBytes = userPassBytes;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (IsUserPassword(passwordBytes, encryptionDictionary, length, documentIdBytes))
|
||||||
|
{
|
||||||
|
decryptionPasswordBytes = passwordBytes;
|
||||||
|
isUserPassword = true;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
throw new PdfDocumentEncryptedException("The document was encrypted and the provided password was neither the user or owner password.", encryptionDictionary);
|
throw new PdfDocumentEncryptedException("The document was encrypted and the provided password was neither the user or owner password.", encryptionDictionary);
|
||||||
@@ -277,12 +277,15 @@
|
|||||||
Array.Copy(encryptionDictionary.OwnerBytes, ownerHash, ownerHash.Length);
|
Array.Copy(encryptionDictionary.OwnerBytes, ownerHash, ownerHash.Length);
|
||||||
Array.Copy(encryptionDictionary.OwnerBytes, ownerHash.Length, validationSalt, 0, validationSalt.Length);
|
Array.Copy(encryptionDictionary.OwnerBytes, ownerHash.Length, validationSalt, 0, validationSalt.Length);
|
||||||
|
|
||||||
|
byte[] result;
|
||||||
if (encryptionDictionary.Revision == 6)
|
if (encryptionDictionary.Revision == 6)
|
||||||
{
|
{
|
||||||
throw new PdfDocumentEncryptedException($"Support for revision 6 encryption not implemented: {encryptionDictionary}.");
|
result = ComputeStupidIsoHash(truncatedPassword, validationSalt, encryptionDictionary.UserBytes);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result = ComputeSha256Hash(truncatedPassword, validationSalt, encryptionDictionary.UserBytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
var result = ComputeSha256Hash(truncatedPassword, validationSalt);
|
|
||||||
|
|
||||||
return result.SequenceEqual(ownerHash);
|
return result.SequenceEqual(ownerHash);
|
||||||
}
|
}
|
||||||
@@ -571,36 +574,53 @@
|
|||||||
// Truncate the UTF-8 representation of the password to 127 bytes if it is longer than 127 bytes
|
// Truncate the UTF-8 representation of the password to 127 bytes if it is longer than 127 bytes
|
||||||
password = TruncatePasswordTo127Bytes(password);
|
password = TruncatePasswordTo127Bytes(password);
|
||||||
|
|
||||||
|
byte[] intermediateKey;
|
||||||
|
byte[] encryptedFileKey;
|
||||||
// If the password is the owner password:
|
// If the password is the owner password:
|
||||||
// Compute an intermediate owner key by computing the SHA-256 hash of the UTF-8 password concatenated with the 8 bytes of owner Key Salt,
|
// Compute an intermediate owner key by computing the SHA-256 hash of the UTF-8 password concatenated with the 8 bytes of owner Key Salt,
|
||||||
// concatenated with the 48-byte U string. The 32-byte result is the key used to decrypt the 32-byte OE string using AES-256 in CBC mode
|
// concatenated with the 48-byte U string. The 32-byte result is the key used to decrypt the 32-byte OE string using AES-256 in CBC mode
|
||||||
// with no padding and an initialization vector of zero. The 32-byte result is the file encryption key.
|
// with no padding and an initialization vector of zero. The 32-byte result is the file encryption key.
|
||||||
if (!isUserPassword)
|
if (!isUserPassword)
|
||||||
{
|
{
|
||||||
throw new PdfDocumentEncryptedException($"Unsupported owner key encryption with revision: {encryptionDictionary.Revision}.");
|
var ownerKeySalt = new byte[8];
|
||||||
}
|
Array.Copy(encryptionDictionary.OwnerBytes, 40, ownerKeySalt, 0, ownerKeySalt.Length);
|
||||||
|
|
||||||
// If the password is the user password:
|
if (encryptionDictionary.Revision == 6)
|
||||||
// Compute an intermediate user key by computing the SHA-256 hash of the UTF-8 password concatenated with the 8 bytes of user Key Salt.
|
{
|
||||||
// The 32-byte result is the key used to decrypt the 32-byte UE string using AES-256 in CBC mode with no padding and an initialization vector of zero.
|
intermediateKey = ComputeStupidIsoHash(password, ownerKeySalt, encryptionDictionary.UserBytes);
|
||||||
// The 32-byte result is the file encryption key.
|
}
|
||||||
var userKeySalt = new byte[8];
|
else
|
||||||
Array.Copy(encryptionDictionary.UserBytes, 40, userKeySalt, 0, 8);
|
{
|
||||||
|
intermediateKey = ComputeSha256Hash(password, ownerKeySalt, encryptionDictionary.UserBytes);
|
||||||
|
}
|
||||||
|
|
||||||
byte[] intermediateKey;
|
encryptedFileKey = encryptionDictionary.OwnerEncryptionBytes;
|
||||||
if (encryptionDictionary.Revision == 6)
|
|
||||||
{
|
|
||||||
intermediateKey = ComputeStupidIsoHash(password, userKeySalt, null);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
intermediateKey = ComputeSha256Hash(password, userKeySalt);
|
// If the password is the user password:
|
||||||
|
// Compute an intermediate user key by computing the SHA-256 hash of the UTF-8 password concatenated with the 8 bytes of user Key Salt.
|
||||||
|
// The 32-byte result is the key used to decrypt the 32-byte UE string using AES-256 in CBC mode with no padding and an initialization vector of zero.
|
||||||
|
// The 32-byte result is the file encryption key.
|
||||||
|
var userKeySalt = new byte[8];
|
||||||
|
Array.Copy(encryptionDictionary.UserBytes, 40, userKeySalt, 0, 8);
|
||||||
|
|
||||||
|
if (encryptionDictionary.Revision == 6)
|
||||||
|
{
|
||||||
|
intermediateKey = ComputeStupidIsoHash(password, userKeySalt, null);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
intermediateKey = ComputeSha256Hash(password, userKeySalt);
|
||||||
|
}
|
||||||
|
|
||||||
|
encryptedFileKey = encryptionDictionary.UserEncryptionBytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
var iv = new byte[16];
|
var iv = new byte[16];
|
||||||
|
|
||||||
using (var rijndaelManaged = new RijndaelManaged { Key = intermediateKey, IV = iv, Mode = CipherMode.CBC, Padding = PaddingMode.None })
|
using (var rijndaelManaged = new RijndaelManaged { Key = intermediateKey, IV = iv, Mode = CipherMode.CBC, Padding = PaddingMode.None })
|
||||||
using (var memoryStream = new MemoryStream(encryptionDictionary.UserEncryptionBytes))
|
using (var memoryStream = new MemoryStream(encryptedFileKey))
|
||||||
using (var output = new MemoryStream())
|
using (var output = new MemoryStream())
|
||||||
using (var cryptoStream = new CryptoStream(memoryStream, rijndaelManaged.CreateDecryptor(intermediateKey, iv), CryptoStreamMode.Read))
|
using (var cryptoStream = new CryptoStream(memoryStream, rijndaelManaged.CreateDecryptor(intermediateKey, iv), CryptoStreamMode.Read))
|
||||||
{
|
{
|
||||||
|
Reference in New Issue
Block a user