Skip to content

Commit 2094194

Browse files
committed
Include MAC by default if encryption supports it
DEVSIX-8622
1 parent dc76524 commit 2094194

File tree

91 files changed

+859
-297
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

91 files changed

+859
-297
lines changed

forms/src/test/java/com/itextpdf/forms/PdfEncryptionTest.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ This file is part of the iText (R) project.
2222
*/
2323
package com.itextpdf.forms;
2424

25+
import com.itextpdf.bouncycastleconnector.BouncyCastleFactoryCreator;
2526
import com.itextpdf.forms.fields.PdfButtonFormField;
2627
import com.itextpdf.forms.fields.PdfFormAnnotation;
2728
import com.itextpdf.forms.fields.PdfFormCreator;
@@ -42,6 +43,7 @@ This file is part of the iText (R) project.
4243
import com.itextpdf.test.annotations.LogMessage;
4344
import com.itextpdf.test.annotations.LogMessages;
4445

46+
import java.security.Security;
4547
import org.junit.jupiter.api.Assertions;
4648
import org.junit.jupiter.api.BeforeAll;
4749
import org.junit.jupiter.api.Test;
@@ -67,6 +69,7 @@ public class PdfEncryptionTest extends ExtendedITextTest {
6769
@BeforeAll
6870
public static void beforeClass() {
6971
createOrClearDestinationFolder(destinationFolder);
72+
Security.addProvider(BouncyCastleFactoryCreator.getFactory().getProvider());
7073
}
7174

7275
// Custom entry in Info dictionary is used because standard entried are gone into metadata in PDF 2.0

kernel/src/main/java/com/itextpdf/kernel/crypto/securityhandler/PubKeySecurityHandler.java

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -210,14 +210,11 @@ private byte[] getEncodedRecipient(int index) throws IOException, GeneralSecurit
210210
//constants permissions: PdfWriter.AllowCopy | PdfWriter.AllowPrinting | PdfWriter.AllowScreenReaders |
211211
// PdfWriter.AllowAssembly;
212212
int permission = recipient.getPermission();
213-
// NOTE! Added while porting to itext
214-
// Previous strange code was:
215-
// int revision = 3;
216-
// permission |= revision == 3 ? 0xfffff0c0 : 0xffffffc0;
217-
// revision value never changed, so code have been replaced to this:
218-
permission |= 0xfffff0c0;
219-
permission &= 0xfffffffc;
220-
permission += 1;
213+
// Force set 1 to 1, 7, 8 bits and all bits above 13.
214+
// Basically to all not used bits.
215+
// Bit 13 we do not touch. It's handled separately in PdfEncryption.
216+
// Not sure about bit 1. But we always set it to 1 so let's not change for now.
217+
permission |= 0xffffe0c1;
221218

222219
byte[] pkcs7input = new byte[24];
223220

kernel/src/main/java/com/itextpdf/kernel/crypto/securityhandler/StandardHandlerUsingAes256.java

Lines changed: 35 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,17 @@ public IDecryptor getDecryptor() {
9292
return new AesDecryptor(nextObjectKey, 0, nextObjectKeySize);
9393
}
9494

95+
/**
96+
* {@inheritDoc}
97+
*/
98+
@Override
99+
public void setPermissions(int permissions, PdfDictionary encryptionDictionary) {
100+
super.setPermissions(permissions, encryptionDictionary);
101+
102+
byte[] aes256Perms = getAes256Perms(permissions, isEncryptMetadata());
103+
encryptionDictionary.put(PdfName.Perms, new PdfLiteral(StreamUtil.createEscapedString(aes256Perms)));
104+
}
105+
95106
void setAES256DicEntries(PdfDictionary encryptionDictionary, byte[] oeKey, byte[] ueKey, byte[] aes256Perms,
96107
boolean encryptMetadata, boolean embeddedFilesOnly) {
97108
int version = 5;
@@ -191,21 +202,7 @@ private void initKeyAndFillDictionary(PdfDictionary encryptionDictionary, byte[]
191202

192203

193204
// Algorithm 10
194-
byte[] permsp = IVGenerator.getIV(16);
195-
permsp[0] = (byte) permissions;
196-
permsp[1] = (byte) (permissions >> 8);
197-
permsp[2] = (byte) (permissions >> 16);
198-
permsp[3] = (byte) (permissions >> 24);
199-
permsp[4] = (byte) (255);
200-
permsp[5] = (byte) (255);
201-
permsp[6] = (byte) (255);
202-
permsp[7] = (byte) (255);
203-
permsp[8] = encryptMetadata ? (byte) 'T' : (byte) 'F';
204-
permsp[9] = (byte) 'a';
205-
permsp[10] = (byte) 'd';
206-
permsp[11] = (byte) 'b';
207-
ac = new AESCipherCBCnoPad(true, nextObjectKey);
208-
aes256Perms = ac.processBlock(permsp, 0, permsp.length);
205+
aes256Perms = getAes256Perms(permissions, encryptMetadata);
209206

210207
this.permissions = permissions;
211208
this.encryptMetadata = encryptMetadata;
@@ -216,6 +213,28 @@ private void initKeyAndFillDictionary(PdfDictionary encryptionDictionary, byte[]
216213
}
217214
}
218215

216+
private byte[] getAes256Perms(int permissions, boolean encryptMetadata) {
217+
byte[] aes256Perms;
218+
AESCipherCBCnoPad ac;
219+
byte[] permsp = IVGenerator.getIV(16);
220+
permsp[0] = (byte) permissions;
221+
permsp[1] = (byte) (permissions >> 8);
222+
permsp[2] = (byte) (permissions >> 16);
223+
permsp[3] = (byte) (permissions >> 24);
224+
permsp[4] = (byte) (255);
225+
permsp[5] = (byte) (255);
226+
permsp[6] = (byte) (255);
227+
permsp[7] = (byte) (255);
228+
permsp[8] = encryptMetadata ? (byte) 'T' : (byte) 'F';
229+
permsp[9] = (byte) 'a';
230+
permsp[10] = (byte) 'd';
231+
permsp[11] = (byte) 'b';
232+
ac = new AESCipherCBCnoPad(true, nextObjectKey);
233+
aes256Perms = ac.processBlock(permsp, 0, permsp.length);
234+
235+
return aes256Perms;
236+
}
237+
219238
private void initKeyAndReadDictionary(PdfDictionary encryptionDictionary, byte[] password) {
220239
try {
221240
if (password == null) {
@@ -235,7 +254,7 @@ private void initKeyAndReadDictionary(PdfDictionary encryptionDictionary, byte[]
235254
byte[] perms = getIsoBytes(encryptionDictionary.getAsString(PdfName.Perms));
236255
PdfNumber pValue = (PdfNumber) encryptionDictionary.get(PdfName.P);
237256

238-
this.permissions = pValue.longValue();
257+
this.permissions = pValue.intValue();
239258

240259
byte[] hash;
241260

kernel/src/main/java/com/itextpdf/kernel/crypto/securityhandler/StandardHandlerUsingStandard40.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ private void initKeyAndReadDictionary(PdfDictionary encryptionDictionary, byte[]
171171
byte[] oValue = getIsoBytes(encryptionDictionary.getAsString(PdfName.O));
172172

173173
PdfNumber pValue = (PdfNumber) encryptionDictionary.get(PdfName.P);
174-
this.permissions = pValue.longValue();
174+
this.permissions = pValue.intValue();
175175

176176
this.documentId = documentId;
177177
keyLength = getKeyLength(encryptionDictionary);

kernel/src/main/java/com/itextpdf/kernel/crypto/securityhandler/StandardSecurityHandler.java

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,17 +33,28 @@ This file is part of the iText (R) project.
3333

3434
public abstract class StandardSecurityHandler extends SecurityHandler {
3535

36-
protected static final int PERMS_MASK_1_FOR_REVISION_2 = -64;
37-
protected static final int PERMS_MASK_1_FOR_REVISION_3_OR_GREATER = -8000;
38-
protected static final int PERMS_MASK_2 = -4;
36+
protected static final int PERMS_MASK_1_FOR_REVISION_2 = 0xffffffc0;
37+
protected static final int PERMS_MASK_1_FOR_REVISION_3_OR_GREATER = 0xffffe0c0;
38+
protected static final int PERMS_MASK_2 = 0xfffffffc;
3939

40-
protected long permissions;
40+
protected int permissions;
4141
protected boolean usedOwnerPassword = true;
4242

43-
public long getPermissions() {
43+
public int getPermissions() {
4444
return permissions;
4545
}
4646

47+
/**
48+
* Updates encryption dictionary with the security permissions provided.
49+
*
50+
* @param permissions new permissions to set
51+
* @param encryptionDictionary encryption dictionary to update
52+
*/
53+
public void setPermissions(int permissions, PdfDictionary encryptionDictionary) {
54+
this.permissions = permissions;
55+
encryptionDictionary.put(PdfName.P, new PdfNumber(permissions));
56+
}
57+
4758
public boolean isUsedOwnerPassword() {
4859
return usedOwnerPassword;
4960
}

kernel/src/main/java/com/itextpdf/kernel/events/PdfDocumentEvent.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,9 @@ public class PdfDocumentEvent extends Event {
5353
public static final String END_PAGE = "EndPdfPage";
5454

5555
/**
56-
* Dispatched before writer is flushed to a document.
56+
* Dispatched before writer is closed.
5757
*/
58-
public static final String END_WRITER_FLUSH = "EndWriterFlush";
58+
public static final String START_WRITER_CLOSING = "StartWriterClosing";
5959
/**
6060
* Dispatched after writer is flushed to a document.
6161
*/

kernel/src/main/java/com/itextpdf/kernel/exceptions/KernelExceptionMessageConstant.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,7 @@ public final class KernelExceptionMessageConstant {
283283
public static final String MAC_VALIDATION_EXCEPTION = "Unexpected exception occurred during MAC token validation.";
284284
public static final String MAC_VALIDATION_FAILED =
285285
"MAC integrity protection was compromised. Document content was modified.";
286+
public static final String MAC_VALIDATION_NO_SALT = "MAC token validation failed. Salt is not found.";
286287
public static final String MISSING_REQUIRED_FIELD_IN_FONT_DICTIONARY
287288
= "Missing required field {0} in font dictionary.";
288289
public static final String MUST_BE_A_TAGGED_DOCUMENT = "Must be a tagged document.";

kernel/src/main/java/com/itextpdf/kernel/mac/AbstractMacIntegrityProtector.java

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,9 @@ public void setKdfSalt(byte[] kdfSalt) {
122122
* introduced to the document in question, after MAC container is integrated.
123123
*/
124124
public void validateMacToken() {
125+
if (kdfSalt == null) {
126+
throw new MacValidationException(KernelExceptionMessageConstant.MAC_VALIDATION_NO_SALT);
127+
}
125128
try {
126129
byte[] macKey = generateDecryptedKey(macContainerReader.parseMacKey());
127130
long[] byteRange = macContainerReader.getByteRange();
@@ -143,12 +146,12 @@ public void validateMacToken() {
143146

144147
if (!Arrays.equals(expectedMac, actualMac) ||
145148
!Arrays.equals(expectedMessageDigest, actualMessageDigest)) {
146-
throw new PdfException(KernelExceptionMessageConstant.MAC_VALIDATION_FAILED);
149+
throw new MacValidationException(KernelExceptionMessageConstant.MAC_VALIDATION_FAILED);
147150
}
148151
} catch (PdfException e) {
149152
throw e;
150153
} catch (Exception e) {
151-
throw new PdfException(KernelExceptionMessageConstant.MAC_VALIDATION_EXCEPTION, e);
154+
throw new MacValidationException(KernelExceptionMessageConstant.MAC_VALIDATION_EXCEPTION, e);
152155
}
153156
}
154157

@@ -187,7 +190,7 @@ protected byte[] digestBytes(InputStream inputStream)
187190
return null;
188191
}
189192
final String algorithm = MacProperties.macDigestAlgorithmToString(macProperties.getMacDigestAlgorithm());
190-
MessageDigest digest = DigestAlgorithms.getMessageDigest(algorithm, null);
193+
MessageDigest digest = DigestAlgorithms.getMessageDigest(algorithm, BC_FACTORY.getProviderName());
191194
byte[] buf = new byte[8192];
192195
int rd;
193196
while ((rd = inputStream.read(buf, 0, buf.length)) > 0) {
@@ -311,7 +314,7 @@ private IDERSequence createMessageDigestSequence(byte[] messageBytes)
311314

312315
final String algorithm = MacProperties.macDigestAlgorithmToString(macProperties.getMacDigestAlgorithm());
313316
// Hash messageBytes to get messageDigest attribute
314-
MessageDigest digest = DigestAlgorithms.getMessageDigest(algorithm, null);
317+
MessageDigest digest = DigestAlgorithms.getMessageDigest(algorithm, BC_FACTORY.getProviderName());
315318
digest.update(messageBytes);
316319
byte[] messageDigest = digestBytes(messageBytes);
317320

0 commit comments

Comments
 (0)