blob: 55944a524622583e45514e25f3cdc66d625069b7 [file] [log] [blame]
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
package org.apache.poi.hslf.record;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.security.MessageDigest;
import java.util.List;
import org.apache.commons.codec.binary.Base64;
import org.apache.poi.POIDataSamples;
import org.apache.poi.hpsf.DocumentSummaryInformation;
import org.apache.poi.hpsf.PropertySet;
import org.apache.poi.hpsf.PropertySetFactory;
import org.apache.poi.hpsf.SummaryInformation;
import org.apache.poi.hslf.usermodel.HSLFPictureData;
import org.apache.poi.hslf.usermodel.HSLFSlide;
import org.apache.poi.hslf.usermodel.HSLFSlideShow;
import org.apache.poi.hslf.usermodel.HSLFSlideShowImpl;
import org.apache.poi.hslf.usermodel.HSLFTextParagraph;
import org.apache.poi.hssf.record.crypto.Biff8EncryptionKey;
import org.apache.poi.poifs.crypt.CryptoFunctions;
import org.apache.poi.poifs.crypt.EncryptionInfo;
import org.apache.poi.poifs.crypt.HashAlgorithm;
import org.apache.poi.poifs.crypt.cryptoapi.CryptoAPIDecryptor;
import org.apache.poi.poifs.crypt.cryptoapi.CryptoAPIEncryptionHeader;
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
/**
* Tests that DocumentEncryption works properly.
*/
public class TestDocumentEncryption {
private static final POIDataSamples slTests = POIDataSamples.getSlideShowInstance();
@ParameterizedTest
@ValueSource(strings = {
"Password_Protected-56-hello.ppt",
"Password_Protected-hello.ppt",
"Password_Protected-np-hello.ppt"
})
void cryptoAPIDecryptionOther(String pptFile) throws Exception {
Biff8EncryptionKey.setCurrentUserPassword("hello");
try {
try (POIFSFileSystem fs = new POIFSFileSystem(slTests.getFile(pptFile), true);
HSLFSlideShow ppt = new HSLFSlideShow(fs)) {
assertTrue(ppt.getSlides().size() > 0);
}
} finally {
Biff8EncryptionKey.setCurrentUserPassword(null);
}
}
@Test
void cryptoAPIChangeKeySize() throws Exception {
String pptFile = "cryptoapi-proc2356.ppt";
Biff8EncryptionKey.setCurrentUserPassword("crypto");
try (POIFSFileSystem fs = new POIFSFileSystem(slTests.getFile(pptFile), true);
HSLFSlideShowImpl hss = new HSLFSlideShowImpl(fs)) {
// need to cache data (i.e. read all data) before changing the key size
List<HSLFPictureData> picsExpected = hss.getPictureData();
hss.getDocumentSummaryInformation();
DocumentEncryptionAtom documentEncryptionAtom = hss.getDocumentEncryptionAtom();
assertNotNull(documentEncryptionAtom);
EncryptionInfo ei = documentEncryptionAtom.getEncryptionInfo();
((CryptoAPIEncryptionHeader) ei.getHeader()).setKeySize(0x78);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
hss.write(bos);
try (POIFSFileSystem fs2 = new POIFSFileSystem(new ByteArrayInputStream(bos.toByteArray()));
HSLFSlideShowImpl hss2 = new HSLFSlideShowImpl(fs2)) {
List<HSLFPictureData> picsActual = hss2.getPictureData();
assertEquals(picsExpected.size(), picsActual.size());
for (int i = 0; i < picsExpected.size(); i++) {
assertArrayEquals(picsExpected.get(i).getRawData(), picsActual.get(i).getRawData());
}
}
} finally {
Biff8EncryptionKey.setCurrentUserPassword(null);
}
}
@Test
void cryptoAPIEncryption() throws Exception {
/* documents with multiple edits need to be normalized for encryption */
String pptFile = "57272_corrupted_usereditatom.ppt";
ByteArrayOutputStream encrypted = new ByteArrayOutputStream();
ByteArrayOutputStream expected = new ByteArrayOutputStream();
ByteArrayOutputStream actual = new ByteArrayOutputStream();
try {
try (POIFSFileSystem fs = new POIFSFileSystem(slTests.getFile(pptFile), true);
HSLFSlideShowImpl hss = new HSLFSlideShowImpl(fs)) {
hss.normalizeRecords();
// normalized ppt
hss.write(expected);
// encrypted
Biff8EncryptionKey.setCurrentUserPassword("hello");
hss.write(encrypted);
}
// decrypted
ByteArrayInputStream bis = new ByteArrayInputStream(encrypted.toByteArray());
try (POIFSFileSystem fs = new POIFSFileSystem(bis);
HSLFSlideShowImpl hss = new HSLFSlideShowImpl(fs)) {
Biff8EncryptionKey.setCurrentUserPassword(null);
hss.write(actual);
}
} finally {
Biff8EncryptionKey.setCurrentUserPassword(null);
}
assertArrayEquals(expected.toByteArray(), actual.toByteArray());
}
@Test
void cryptoAPIDecryption() throws Exception {
// taken from a msdn blog:
// http://blogs.msdn.com/b/openspecification/archive/2009/05/08/dominic-salemno.aspx
Biff8EncryptionKey.setCurrentUserPassword("crypto");
try (POIFSFileSystem fs = new POIFSFileSystem(slTests.getFile("cryptoapi-proc2356.ppt"));
HSLFSlideShow ss = new HSLFSlideShow(fs)) {
HSLFSlide slide = ss.getSlides().get(0);
String rawText = HSLFTextParagraph.getRawText(slide.getTextParagraphs().get(0));
assertEquals("Dominic Salemno", rawText);
String[][] picCmp = {
{"0", "nKsDTKqxTCR8LFkVVWlP9GSTvZ0="},
{"95163", "SuNOR+9V1UVYZIoeD65l3VTaLoc="},
{"100864", "Ql3IGrr4bNq07ZTp5iPg7b+pva8="},
{"714114", "8pdst9NjBGSfWezSZE8+aVhIRe0="},
{"723752", "go6xqW7lvkCtlOO5tYLiMfb4oxw="},
{"770128", "gZUM8YqRNL5kGNfyyYvEEernvCc="},
{"957958", "CNU2iiqUFAnk3TDXsXV1ihH9eRM="},
};
MessageDigest md = CryptoFunctions.getMessageDigest(HashAlgorithm.sha1);
List<HSLFPictureData> pd = ss.getSlideShowImpl().getPictureData();
int i = 0;
for (HSLFPictureData p : pd) {
byte[] hash = md.digest(p.getData());
assertEquals(Integer.parseInt(picCmp[i][0]), p.getOffset());
assertEquals(picCmp[i][1], Base64.encodeBase64String(hash));
i++;
}
DocumentEncryptionAtom dea = ss.getSlideShowImpl().getDocumentEncryptionAtom();
assertNotNull(dea);
CryptoAPIDecryptor dec = (CryptoAPIDecryptor) dea.getEncryptionInfo().getDecryptor();
try (POIFSFileSystem fs2 = dec.getSummaryEntries(fs.getRoot(), "EncryptedSummary")) {
PropertySet ps = PropertySetFactory.create(fs2.getRoot(), SummaryInformation.DEFAULT_STREAM_NAME);
assertNotNull(ps);
assertTrue(ps.isSummaryInformation());
assertEquals("RC4 CryptoAPI Encryption", ps.getProperties()[1].getValue());
ps = PropertySetFactory.create(fs2.getRoot(), DocumentSummaryInformation.DEFAULT_STREAM_NAME);
assertNotNull(ps);
assertTrue(ps.isDocumentSummaryInformation());
assertEquals("On-screen Show (4:3)", ps.getProperties()[1].getValue());
}
} finally {
Biff8EncryptionKey.setCurrentUserPassword(null);
}
}
}