aboutsummaryrefslogtreecommitdiffstats
path: root/src/testcases/org/apache
diff options
context:
space:
mode:
Diffstat (limited to 'src/testcases/org/apache')
-rwxr-xr-xsrc/testcases/org/apache/poi/hssf/data/password.xlsbin0 -> 22528 bytes
-rw-r--r--src/testcases/org/apache/poi/hssf/extractor/TestExcelExtractor.java148
-rwxr-xr-xsrc/testcases/org/apache/poi/hssf/record/AllRecordTests.java6
-rw-r--r--src/testcases/org/apache/poi/hssf/record/TestRecordFactory.java10
-rw-r--r--src/testcases/org/apache/poi/hssf/record/crypto/AllHSSFEncryptionTests.java38
-rw-r--r--src/testcases/org/apache/poi/hssf/record/crypto/TestBiff8DecryptingStream.java230
-rw-r--r--src/testcases/org/apache/poi/hssf/record/crypto/TestBiff8EncryptionKey.java102
-rw-r--r--src/testcases/org/apache/poi/hssf/record/crypto/TestRC4.java76
8 files changed, 534 insertions, 76 deletions
diff --git a/src/testcases/org/apache/poi/hssf/data/password.xls b/src/testcases/org/apache/poi/hssf/data/password.xls
new file mode 100755
index 0000000000..a6ad86a113
--- /dev/null
+++ b/src/testcases/org/apache/poi/hssf/data/password.xls
Binary files differ
diff --git a/src/testcases/org/apache/poi/hssf/extractor/TestExcelExtractor.java b/src/testcases/org/apache/poi/hssf/extractor/TestExcelExtractor.java
index 25909918b3..5d2518acec 100644
--- a/src/testcases/org/apache/poi/hssf/extractor/TestExcelExtractor.java
+++ b/src/testcases/org/apache/poi/hssf/extractor/TestExcelExtractor.java
@@ -24,108 +24,109 @@ import java.io.InputStream;
import junit.framework.TestCase;
import org.apache.poi.hssf.HSSFTestDataSamples;
+import org.apache.poi.hssf.record.crypto.Biff8EncryptionKey;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.poifs.filesystem.DirectoryNode;
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
/**
- *
+ *
*/
public final class TestExcelExtractor extends TestCase {
private static ExcelExtractor createExtractor(String sampleFileName) {
-
+
InputStream is = HSSFTestDataSamples.openSampleFileStream(sampleFileName);
-
+
try {
return new ExcelExtractor(new POIFSFileSystem(is));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
-
-
+
+
public void testSimple() {
-
+
ExcelExtractor extractor = createExtractor("Simple.xls");
-
+
assertEquals("Sheet1\nreplaceMe\nSheet2\nSheet3\n", extractor.getText());
-
+
// Now turn off sheet names
extractor.setIncludeSheetNames(false);
assertEquals("replaceMe\n", extractor.getText());
}
-
+
public void testNumericFormula() {
-
+
ExcelExtractor extractor = createExtractor("sumifformula.xls");
-
+
assertEquals(
"Sheet1\n" +
"1000.0\t1.0\t5.0\n" +
- "2000.0\t2.0\n" +
+ "2000.0\t2.0\n" +
"3000.0\t3.0\n" +
- "4000.0\t4.0\n" +
+ "4000.0\t4.0\n" +
"5000.0\t5.0\n" +
- "Sheet2\nSheet3\n",
+ "Sheet2\nSheet3\n",
extractor.getText()
);
-
+
extractor.setFormulasNotResults(true);
-
+
assertEquals(
"Sheet1\n" +
"1000.0\t1.0\tSUMIF(A1:A5,\">4000\",B1:B5)\n" +
- "2000.0\t2.0\n" +
+ "2000.0\t2.0\n" +
"3000.0\t3.0\n" +
- "4000.0\t4.0\n" +
+ "4000.0\t4.0\n" +
"5000.0\t5.0\n" +
- "Sheet2\nSheet3\n",
+ "Sheet2\nSheet3\n",
extractor.getText()
);
}
-
+
public void testwithContinueRecords() {
-
+
ExcelExtractor extractor = createExtractor("StringContinueRecords.xls");
-
+
extractor.getText();
-
+
// Has masses of text
// Until we fixed bug #41064, this would've
// failed by now
assertTrue(extractor.getText().length() > 40960);
}
-
+
public void testStringConcat() {
-
+
ExcelExtractor extractor = createExtractor("SimpleWithFormula.xls");
-
+
// Comes out as NaN if treated as a number
// And as XYZ if treated as a string
assertEquals("Sheet1\nreplaceme\nreplaceme\nreplacemereplaceme\nSheet2\nSheet3\n", extractor.getText());
-
+
extractor.setFormulasNotResults(true);
-
+
assertEquals("Sheet1\nreplaceme\nreplaceme\nCONCATENATE(A1,A2)\nSheet2\nSheet3\n", extractor.getText());
}
-
+
public void testStringFormula() {
-
+
ExcelExtractor extractor = createExtractor("StringFormulas.xls");
-
+
// Comes out as NaN if treated as a number
// And as XYZ if treated as a string
assertEquals("Sheet1\nXYZ\nSheet2\nSheet3\n", extractor.getText());
-
+
extractor.setFormulasNotResults(true);
-
+
assertEquals("Sheet1\nUPPER(\"xyz\")\nSheet2\nSheet3\n", extractor.getText());
}
-
-
+
+
public void testEventExtractor() throws Exception {
EventBasedExcelExtractor extractor;
-
+
// First up, a simple file with string
// based formulas in it
extractor = new EventBasedExcelExtractor(
@@ -134,17 +135,17 @@ public final class TestExcelExtractor extends TestCase {
)
);
extractor.setIncludeSheetNames(true);
-
+
String text = extractor.getText();
assertEquals("Sheet1\nreplaceme\nreplaceme\nreplacemereplaceme\nSheet2\nSheet3\n", text);
-
+
extractor.setIncludeSheetNames(false);
extractor.setFormulasNotResults(true);
-
+
text = extractor.getText();
assertEquals("replaceme\nreplaceme\nCONCATENATE(A1,A2)\n", text);
-
+
// Now, a slightly longer file with numeric formulas
extractor = new EventBasedExcelExtractor(
new POIFSFileSystem(
@@ -157,14 +158,14 @@ public final class TestExcelExtractor extends TestCase {
text = extractor.getText();
assertEquals(
"1000.0\t1.0\tSUMIF(A1:A5,\">4000\",B1:B5)\n" +
- "2000.0\t2.0\n" +
+ "2000.0\t2.0\n" +
"3000.0\t3.0\n" +
- "4000.0\t4.0\n" +
+ "4000.0\t4.0\n" +
"5000.0\t5.0\n",
text
);
}
-
+
public void testWithComments() {
ExcelExtractor extractor = createExtractor("SimpleWithComments.xls");
extractor.setIncludeSheetNames(false);
@@ -172,34 +173,34 @@ public final class TestExcelExtractor extends TestCase {
// Check without comments
assertEquals(
"1.0\tone\n" +
- "2.0\ttwo\n" +
- "3.0\tthree\n",
+ "2.0\ttwo\n" +
+ "3.0\tthree\n",
extractor.getText()
);
-
+
// Now with
extractor.setIncludeCellComments(true);
assertEquals(
"1.0\tone Comment by Yegor Kozlov: Yegor Kozlov: first cell\n" +
- "2.0\ttwo Comment by Yegor Kozlov: Yegor Kozlov: second cell\n" +
- "3.0\tthree Comment by Yegor Kozlov: Yegor Kozlov: third cell\n",
+ "2.0\ttwo Comment by Yegor Kozlov: Yegor Kozlov: second cell\n" +
+ "3.0\tthree Comment by Yegor Kozlov: Yegor Kozlov: third cell\n",
extractor.getText()
);
}
-
+
public void testWithBlank() {
ExcelExtractor extractor = createExtractor("MissingBits.xls");
String def = extractor.getText();
extractor.setIncludeBlankCells(true);
String padded = extractor.getText();
-
+
assertTrue(def.startsWith(
"Sheet1\n" +
"&[TAB]\t\n" +
"Hello\n" +
"11.0\t23.0\n"
));
-
+
assertTrue(padded.startsWith(
"Sheet1\n" +
"&[TAB]\t\n" +
@@ -207,8 +208,8 @@ public final class TestExcelExtractor extends TestCase {
"11.0\t\t\t23.0\n"
));
}
-
-
+
+
/**
* Embded in a non-excel file
*/
@@ -219,22 +220,22 @@ public final class TestExcelExtractor extends TestCase {
POIFSFileSystem fs = new POIFSFileSystem(
new FileInputStream(filename)
);
-
+
DirectoryNode objPool = (DirectoryNode) fs.getRoot().getEntry("ObjectPool");
DirectoryNode dirA = (DirectoryNode) objPool.getEntry("_1269427460");
DirectoryNode dirB = (DirectoryNode) objPool.getEntry("_1269427461");
HSSFWorkbook wbA = new HSSFWorkbook(dirA, fs, true);
HSSFWorkbook wbB = new HSSFWorkbook(dirB, fs, true);
-
+
ExcelExtractor exA = new ExcelExtractor(wbA);
ExcelExtractor exB = new ExcelExtractor(wbB);
-
- assertEquals("Sheet1\nTest excel file\nThis is the first file\nSheet2\nSheet3\n",
+
+ assertEquals("Sheet1\nTest excel file\nThis is the first file\nSheet2\nSheet3\n",
exA.getText());
assertEquals("Sample Excel", exA.getSummaryInformation().getTitle());
-
- assertEquals("Sheet1\nAnother excel file\nThis is the second file\nSheet2\nSheet3\n",
+
+ assertEquals("Sheet1\nAnother excel file\nThis is the second file\nSheet2\nSheet3\n",
exB.getText());
assertEquals("Sample Excel 2", exB.getSummaryInformation().getTitle());
}
@@ -249,37 +250,37 @@ public final class TestExcelExtractor extends TestCase {
POIFSFileSystem fs = new POIFSFileSystem(
new FileInputStream(filename)
);
-
+
DirectoryNode dirA = (DirectoryNode) fs.getRoot().getEntry("MBD0000A3B5");
DirectoryNode dirB = (DirectoryNode) fs.getRoot().getEntry("MBD0000A3B4");
-
+
HSSFWorkbook wbA = new HSSFWorkbook(dirA, fs, true);
HSSFWorkbook wbB = new HSSFWorkbook(dirB, fs, true);
-
+
ExcelExtractor exA = new ExcelExtractor(wbA);
ExcelExtractor exB = new ExcelExtractor(wbB);
-
- assertEquals("Sheet1\nTest excel file\nThis is the first file\nSheet2\nSheet3\n",
+
+ assertEquals("Sheet1\nTest excel file\nThis is the first file\nSheet2\nSheet3\n",
exA.getText());
assertEquals("Sample Excel", exA.getSummaryInformation().getTitle());
-
- assertEquals("Sheet1\nAnother excel file\nThis is the second file\nSheet2\nSheet3\n",
+
+ assertEquals("Sheet1\nAnother excel file\nThis is the second file\nSheet2\nSheet3\n",
exB.getText());
assertEquals("Sample Excel 2", exB.getSummaryInformation().getTitle());
-
+
// And the base file too
ExcelExtractor ex = new ExcelExtractor(fs);
assertEquals("Sheet1\nI have lots of embeded files in me\nSheet2\nSheet3\n",
ex.getText());
assertEquals("Excel With Embeded", ex.getSummaryInformation().getTitle());
}
-
+
/**
* Test that we get text from headers and footers
*/
public void test45538() {
String[] files = {
- "45538_classic_Footer.xls", "45538_form_Footer.xls",
+ "45538_classic_Footer.xls", "45538_form_Footer.xls",
"45538_classic_Header.xls", "45538_form_Header.xls"
};
for(int i=0; i<files.length; i++) {
@@ -289,4 +290,13 @@ public final class TestExcelExtractor extends TestCase {
assertTrue("Unable to find expected word in text\n" + text, text.indexOf("test phrase") >= 0);
}
}
+
+ public void testPassword() {
+ Biff8EncryptionKey.setCurrentUserPassword("password");
+ ExcelExtractor extractor = createExtractor("password.xls");
+ String text = extractor.getText();
+ Biff8EncryptionKey.setCurrentUserPassword(null);
+
+ assertTrue(text.contains("ZIP"));
+ }
}
diff --git a/src/testcases/org/apache/poi/hssf/record/AllRecordTests.java b/src/testcases/org/apache/poi/hssf/record/AllRecordTests.java
index afd4893137..5aec73999a 100755
--- a/src/testcases/org/apache/poi/hssf/record/AllRecordTests.java
+++ b/src/testcases/org/apache/poi/hssf/record/AllRecordTests.java
@@ -24,20 +24,22 @@ import org.apache.poi.hssf.record.aggregates.AllRecordAggregateTests;
import org.apache.poi.hssf.record.cf.TestCellRange;
import org.apache.poi.hssf.record.chart.AllChartRecordTests;
import org.apache.poi.hssf.record.constant.TestConstantValueParser;
+import org.apache.poi.hssf.record.crypto.AllHSSFEncryptionTests;
import org.apache.poi.hssf.record.formula.AllFormulaTests;
import org.apache.poi.hssf.record.pivot.AllPivotRecordTests;
/**
* Collects all tests for package <tt>org.apache.poi.hssf.record</tt> and sub-packages.
- *
+ *
* @author Josh Micich
*/
public final class AllRecordTests {
-
+
public static Test suite() {
TestSuite result = new TestSuite(AllRecordTests.class.getName());
result.addTest(AllChartRecordTests.suite());
+ result.addTest(AllHSSFEncryptionTests.suite());
result.addTest(AllFormulaTests.suite());
result.addTest(AllPivotRecordTests.suite());
result.addTest(AllRecordAggregateTests.suite());
diff --git a/src/testcases/org/apache/poi/hssf/record/TestRecordFactory.java b/src/testcases/org/apache/poi/hssf/record/TestRecordFactory.java
index cd01ca0419..4dface4251 100644
--- a/src/testcases/org/apache/poi/hssf/record/TestRecordFactory.java
+++ b/src/testcases/org/apache/poi/hssf/record/TestRecordFactory.java
@@ -155,7 +155,7 @@ public final class TestRecordFactory extends TestCase {
*/
public void testMixedContinue() throws Exception {
/**
- * Adapted from a real test sample file 39512.xls (Offset 0x4854).
+ * Adapted from a real test sample file 39512.xls (Offset 0x4854).
* See Bug 39512 for details.
*/
String dump =
@@ -208,6 +208,7 @@ public final class TestRecordFactory extends TestCase {
public void testNonZeroPadding_bug46987() {
Record[] recs = {
new BOFRecord(),
+ new WriteAccessRecord(), // need *something* between BOF and EOF
EOFRecord.instance,
BOFRecord.createSheetBOF(),
EOFRecord.instance,
@@ -229,7 +230,7 @@ public final class TestRecordFactory extends TestCase {
baos.write(0x00);
}
-
+
POIFSFileSystem fs = new POIFSFileSystem();
InputStream is;
try {
@@ -237,7 +238,7 @@ public final class TestRecordFactory extends TestCase {
is = fs.getRoot().createDocumentInputStream("dummy");
} catch (IOException e) {
throw new RuntimeException(e);
- }
+ }
List<Record> outRecs;
try {
@@ -248,7 +249,6 @@ public final class TestRecordFactory extends TestCase {
}
throw e;
}
- assertEquals(4, outRecs.size());
-
+ assertEquals(5, outRecs.size());
}
}
diff --git a/src/testcases/org/apache/poi/hssf/record/crypto/AllHSSFEncryptionTests.java b/src/testcases/org/apache/poi/hssf/record/crypto/AllHSSFEncryptionTests.java
new file mode 100644
index 0000000000..4d56858c95
--- /dev/null
+++ b/src/testcases/org/apache/poi/hssf/record/crypto/AllHSSFEncryptionTests.java
@@ -0,0 +1,38 @@
+/* ====================================================================
+ 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.hssf.record.crypto;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+/**
+ * Collects all tests for package <tt>org.apache.poi.hssf.record.crypto</tt>.
+ *
+ * @author Josh Micich
+ */
+public final class AllHSSFEncryptionTests {
+
+ public static Test suite() {
+ TestSuite result = new TestSuite(AllHSSFEncryptionTests.class.getName());
+
+ result.addTestSuite(TestBiff8DecryptingStream.class);
+ result.addTestSuite(TestRC4.class);
+ result.addTestSuite(TestBiff8EncryptionKey.class);
+ return result;
+ }
+}
diff --git a/src/testcases/org/apache/poi/hssf/record/crypto/TestBiff8DecryptingStream.java b/src/testcases/org/apache/poi/hssf/record/crypto/TestBiff8DecryptingStream.java
new file mode 100644
index 0000000000..00c860ad0e
--- /dev/null
+++ b/src/testcases/org/apache/poi/hssf/record/crypto/TestBiff8DecryptingStream.java
@@ -0,0 +1,230 @@
+/* ====================================================================
+ 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.hssf.record.crypto;
+
+import java.io.InputStream;
+import java.util.Arrays;
+
+import junit.framework.AssertionFailedError;
+import junit.framework.ComparisonFailure;
+import junit.framework.TestCase;
+
+import org.apache.poi.util.HexDump;
+import org.apache.poi.util.HexRead;
+
+/**
+ * Tests for {@link Biff8DecryptingStream}
+ *
+ * @author Josh Micich
+ */
+public final class TestBiff8DecryptingStream extends TestCase {
+
+ /**
+ * A mock {@link InputStream} that keeps track of position and also produces
+ * slightly interesting data. Each successive data byte value is one greater
+ * than the previous.
+ */
+ private static final class MockStream extends InputStream {
+ private int _val;
+ private int _position;
+
+ public MockStream(int initialValue) {
+ _val = initialValue & 0xFF;
+ }
+ public int read() {
+ _position++;
+ return _val++ & 0xFF;
+ }
+ public int getPosition() {
+ return _position;
+ }
+ }
+
+ private static final class StreamTester {
+ private static final boolean ONLY_LOG_ERRORS = true;
+
+ private final MockStream _ms;
+ private final Biff8DecryptingStream _bds;
+ private boolean _errorsOccurred;
+
+ /**
+ * @param expectedFirstInt expected value of the first int read from the decrypted stream
+ */
+ public StreamTester(MockStream ms, String keyDigestHex, int expectedFirstInt) {
+ _ms = ms;
+ byte[] keyDigest = HexRead.readFromString(keyDigestHex);
+ _bds = new Biff8DecryptingStream(_ms, 0, new Biff8EncryptionKey(keyDigest));
+ assertEquals(expectedFirstInt, _bds.readInt());
+ _errorsOccurred = false;
+ }
+
+ public Biff8DecryptingStream getBDS() {
+ return _bds;
+ }
+
+ /**
+ * Used to 'skip over' the uninteresting middle bits of the key blocks.
+ * Also confirms that read position of the underlying stream is aligned.
+ */
+ public void rollForward(int fromPosition, int toPosition) {
+ assertEquals(fromPosition, _ms.getPosition());
+ for (int i = fromPosition; i < toPosition; i++) {
+ _bds.readByte();
+ }
+ assertEquals(toPosition, _ms.getPosition());
+ }
+
+ public void confirmByte(int expVal) {
+ cmp(HexDump.byteToHex(expVal), HexDump.byteToHex(_bds.readUByte()));
+ }
+
+ public void confirmShort(int expVal) {
+ cmp(HexDump.shortToHex(expVal), HexDump.shortToHex(_bds.readUShort()));
+ }
+
+ public void confirmInt(int expVal) {
+ cmp(HexDump.intToHex(expVal), HexDump.intToHex(_bds.readInt()));
+ }
+
+ public void confirmLong(long expVal) {
+ cmp(HexDump.longToHex(expVal), HexDump.longToHex(_bds.readLong()));
+ }
+
+ private void cmp(char[] exp, char[] act) {
+ if (Arrays.equals(exp, act)) {
+ return;
+ }
+ _errorsOccurred = true;
+ if (ONLY_LOG_ERRORS) {
+ logErr(3, "Value mismatch " + new String(exp) + " - " + new String(act));
+ return;
+ }
+ throw new ComparisonFailure("Value mismatch", new String(exp), new String(act));
+ }
+
+ public void confirmData(String expHexData) {
+
+ byte[] expData = HexRead.readFromString(expHexData);
+ byte[] actData = new byte[expData.length];
+ _bds.readFully(actData);
+ if (Arrays.equals(expData, actData)) {
+ return;
+ }
+ _errorsOccurred = true;
+ if (ONLY_LOG_ERRORS) {
+ logErr(2, "Data mismatch " + HexDump.toHex(expData) + " - "
+ + HexDump.toHex(actData));
+ return;
+ }
+ throw new ComparisonFailure("Data mismatch", HexDump.toHex(expData), HexDump.toHex(actData));
+ }
+
+ private static void logErr(int stackFrameCount, String msg) {
+ StackTraceElement ste = new Exception().getStackTrace()[stackFrameCount];
+ System.err.print("(" + ste.getFileName() + ":" + ste.getLineNumber() + ") ");
+ System.err.println(msg);
+ }
+
+ public void assertNoErrors() {
+ assertFalse("Some values decrypted incorrectly", _errorsOccurred);
+ }
+ }
+
+ /**
+ * Tests reading of 64,32,16 and 8 bit integers aligned with key changing boundaries
+ */
+ public void testReadsAlignedWithBoundary() {
+ StreamTester st = createStreamTester(0x50, "BA AD F0 0D 00", 0x96C66829);
+
+ st.rollForward(0x0004, 0x03FF);
+ st.confirmByte(0x3E);
+ st.confirmByte(0x28);
+ st.rollForward(0x0401, 0x07FE);
+ st.confirmShort(0x76CC);
+ st.confirmShort(0xD83E);
+ st.rollForward(0x0802, 0x0BFC);
+ st.confirmInt(0x25F280EB);
+ st.confirmInt(0xB549E99B);
+ st.rollForward(0x0C04, 0x0FF8);
+ st.confirmLong(0x6AA2D5F6B975D10CL);
+ st.confirmLong(0x34248ADF7ED4F029L);
+ st.assertNoErrors();
+ }
+
+ /**
+ * Tests reading of 64,32 and 16 bit integers <i>across</i> key changing boundaries
+ */
+ public void testReadsSpanningBoundary() {
+ StreamTester st = createStreamTester(0x50, "BA AD F0 0D 00", 0x96C66829);
+
+ st.rollForward(0x0004, 0x03FC);
+ st.confirmLong(0x885243283E2A5EEFL);
+ st.rollForward(0x0404, 0x07FE);
+ st.confirmInt(0xD83E76CC);
+ st.rollForward(0x0802, 0x0BFF);
+ st.confirmShort(0x9B25);
+ st.assertNoErrors();
+ }
+
+ /**
+ * Checks that the BIFF header fields (sid, size) get read without applying decryption,
+ * and that the RC4 stream stays aligned during these calls
+ */
+ public void testReadHeaderUShort() {
+ StreamTester st = createStreamTester(0x50, "BA AD F0 0D 00", 0x96C66829);
+
+ st.rollForward(0x0004, 0x03FF);
+
+ Biff8DecryptingStream bds = st.getBDS();
+ int hval = bds.readDataSize(); // unencrypted
+ int nextInt = bds.readInt();
+ if (nextInt == 0x8F534029) {
+ throw new AssertionFailedError(
+ "Indentified bug in key alignment after call to readHeaderUShort()");
+ }
+ assertEquals(0x16885243, nextInt);
+ if (hval == 0x283E) {
+ throw new AssertionFailedError("readHeaderUShort() incorrectly decrypted result");
+ }
+ assertEquals(0x504F, hval);
+
+ // confirm next key change
+ st.rollForward(0x0405, 0x07FC);
+ st.confirmInt(0x76CC1223);
+ st.confirmInt(0x4842D83E);
+ st.assertNoErrors();
+ }
+
+ /**
+ * Tests reading of byte sequences <i>across</i> and <i>aligned with</i> key changing boundaries
+ */
+ public void testReadByteArrays() {
+ StreamTester st = createStreamTester(0x50, "BA AD F0 0D 00", 0x96C66829);
+
+ st.rollForward(0x0004, 0x2FFC);
+ st.confirmData("66 A1 20 B1 04 A3 35 F5"); // 4 bytes on either side of boundary
+ st.rollForward(0x3004, 0x33F8);
+ st.confirmData("F8 97 59 36"); // last 4 bytes in block
+ st.confirmData("01 C2 4E 55"); // first 4 bytes in next block
+ st.assertNoErrors();
+ }
+
+ private static StreamTester createStreamTester(int mockStreamStartVal, String keyDigestHex, int expectedFirstInt) {
+ return new StreamTester(new MockStream(mockStreamStartVal), keyDigestHex, expectedFirstInt);
+ }
+}
diff --git a/src/testcases/org/apache/poi/hssf/record/crypto/TestBiff8EncryptionKey.java b/src/testcases/org/apache/poi/hssf/record/crypto/TestBiff8EncryptionKey.java
new file mode 100644
index 0000000000..7c6ad42a74
--- /dev/null
+++ b/src/testcases/org/apache/poi/hssf/record/crypto/TestBiff8EncryptionKey.java
@@ -0,0 +1,102 @@
+/* ====================================================================
+ 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.hssf.record.crypto;
+
+import java.util.Arrays;
+
+import org.apache.poi.util.HexDump;
+import org.apache.poi.util.HexRead;
+
+import junit.framework.ComparisonFailure;
+import junit.framework.TestCase;
+
+/**
+ * Tests for {@link Biff8EncryptionKey}
+ *
+ * @author Josh Micich
+ */
+public final class TestBiff8EncryptionKey extends TestCase {
+
+ private static byte[] fromHex(String hexString) {
+ return HexRead.readFromString(hexString);
+ }
+ public void testCreateKeyDigest() {
+ byte[] docIdData = fromHex("17 F6 D1 6B 09 B1 5F 7B 4C 9D 03 B4 81 B5 B4 4A");
+ byte[] keyDigest = Biff8EncryptionKey.createKeyDigest("MoneyForNothing", docIdData);
+ byte[] expResult = fromHex("C2 D9 56 B2 6B");
+ if (!Arrays.equals(expResult, keyDigest)) {
+ throw new ComparisonFailure("keyDigest mismatch", HexDump.toHex(expResult), HexDump.toHex(keyDigest));
+ }
+ }
+
+
+ public void testValidateWithDefaultPassword() {
+
+ String docIdSuffixA = "F 35 52 38 0D 75 4A E6 85 C2 FD 78 CE 3D D1 B6"; // valid prefix is 'D'
+ String saltHashA = "30 38 BE 5E 93 C5 7E B4 5F 52 CD A1 C6 8F B6 2A";
+ String saltDataA = "D4 04 43 EC B7 A7 6F 6A D2 68 C7 DF CF A8 80 68";
+
+ String docIdB = "39 D7 80 41 DA E4 74 2C 8C 84 F9 4D 39 9A 19 2D";
+ String saltDataSuffixB = "3 EA 8D 52 11 11 37 D2 BD 55 4C 01 0A 47 6E EB"; // valid prefix is 'C'
+ String saltHashB = "96 19 F5 D0 F1 63 08 F1 3E 09 40 1E 87 F0 4E 16";
+
+ confirmValid(true, "D" + docIdSuffixA, saltDataA, saltHashA);
+ confirmValid(true, docIdB, "C" + saltDataSuffixB, saltHashB);
+ confirmValid(false, "E" + docIdSuffixA, saltDataA, saltHashA);
+ confirmValid(false, docIdB, "B" + saltDataSuffixB, saltHashB);
+ }
+
+ public void testValidateWithSuppliedPassword() {
+
+ String docId = "DF 35 52 38 0D 75 4A E6 85 C2 FD 78 CE 3D D1 B6";
+ String saltData = "D4 04 43 EC B7 A7 6F 6A D2 68 C7 DF CF A8 80 68";
+ String saltHashA = "8D C2 63 CC E1 1D E0 05 20 16 96 AF 48 59 94 64"; // for password '5ecret'
+ String saltHashB = "31 0B 0D A4 69 55 8E 27 A1 03 AD C9 AE F8 09 04"; // for password '5ecret'
+
+ confirmValid(true, docId, saltData, saltHashA, "5ecret");
+ confirmValid(false, docId, saltData, saltHashA, "Secret");
+ confirmValid(true, docId, saltData, saltHashB, "Secret");
+ confirmValid(false, docId, saltData, saltHashB, "secret");
+ }
+
+
+ private static void confirmValid(boolean expectedResult,
+ String docIdHex, String saltDataHex, String saltHashHex) {
+ confirmValid(expectedResult, docIdHex, saltDataHex, saltHashHex, null);
+ }
+ private static void confirmValid(boolean expectedResult,
+ String docIdHex, String saltDataHex, String saltHashHex, String password) {
+ byte[] docId = fromHex(docIdHex);
+ byte[] saltData = fromHex(saltDataHex);
+ byte[] saltHash = fromHex(saltHashHex);
+
+
+ Biff8EncryptionKey key;
+ if (password == null) {
+ key = Biff8EncryptionKey.create(docId);
+ } else {
+ key = Biff8EncryptionKey.create(password, docId);
+ }
+ boolean actResult = key.validate(saltData, saltHash);
+ if (expectedResult) {
+ assertTrue("validate failed", actResult);
+ } else {
+ assertFalse("validate succeeded unexpectedly", actResult);
+ }
+ }
+}
diff --git a/src/testcases/org/apache/poi/hssf/record/crypto/TestRC4.java b/src/testcases/org/apache/poi/hssf/record/crypto/TestRC4.java
new file mode 100644
index 0000000000..35b6da4fd7
--- /dev/null
+++ b/src/testcases/org/apache/poi/hssf/record/crypto/TestRC4.java
@@ -0,0 +1,76 @@
+/* ====================================================================
+ 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.hssf.record.crypto;
+
+import java.security.GeneralSecurityException;
+import java.security.InvalidKeyException;
+import java.util.Arrays;
+
+import javax.crypto.Cipher;
+import javax.crypto.spec.SecretKeySpec;
+
+import junit.framework.ComparisonFailure;
+import junit.framework.TestCase;
+
+import org.apache.poi.util.HexDump;
+import org.apache.poi.util.HexRead;
+
+/**
+ * Tests for {@link RC4}
+ *
+ * @author Josh Micich
+ */
+public class TestRC4 extends TestCase {
+ public void testSimple() {
+ confirmRC4("Key", "Plaintext", "BBF316E8D940AF0AD3");
+ confirmRC4("Wiki", "pedia", "1021BF0420");
+ confirmRC4("Secret", "Attack at dawn", "45A01F645FC35B383552544B9BF5");
+
+ }
+
+ private static void confirmRC4(String k, String origText, String expEncrHex) {
+ byte[] actEncr = origText.getBytes();
+ new RC4(k.getBytes()).encrypt(actEncr);
+ byte[] expEncr = HexRead.readFromString(expEncrHex);
+
+ if (!Arrays.equals(expEncr, actEncr)) {
+ throw new ComparisonFailure("Data mismatch", HexDump.toHex(expEncr), HexDump.toHex(actEncr));
+ }
+
+
+ Cipher cipher;
+ try {
+ cipher = Cipher.getInstance("RC4");
+ } catch (GeneralSecurityException e) {
+ throw new RuntimeException(e);
+ }
+ String k2 = k+k; // Sun has minimum of 5 bytes for key
+ SecretKeySpec skeySpec = new SecretKeySpec(k2.getBytes(), "RC4");
+
+ try {
+ cipher.init(Cipher.DECRYPT_MODE, skeySpec);
+ } catch (InvalidKeyException e) {
+ throw new RuntimeException(e);
+ }
+ byte[] origData = origText.getBytes();
+ byte[] altEncr = cipher.update(origData);
+ if (!Arrays.equals(expEncr, altEncr)) {
+ throw new RuntimeException("Mismatch from jdk provider");
+ }
+ }
+}