diff options
Diffstat (limited to 'src/testcases/org/apache')
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 Binary files differnew file mode 100755 index 0000000000..a6ad86a113 --- /dev/null +++ b/src/testcases/org/apache/poi/hssf/data/password.xls 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"); + } + } +} |