]> source.dussan.org Git - poi.git/commitdiff
Bugzilla 52690 - added a getter for length of encrypted data in Ecma and Agile decryptors
authorYegor Kozlov <yegor@apache.org>
Sun, 26 Feb 2012 08:49:36 +0000 (08:49 +0000)
committerYegor Kozlov <yegor@apache.org>
Sun, 26 Feb 2012 08:49:36 +0000 (08:49 +0000)
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1293784 13f79535-47bb-0310-9956-ffa450edef68

src/documentation/content/xdocs/status.xml
src/java/org/apache/poi/poifs/crypt/AgileDecryptor.java
src/java/org/apache/poi/poifs/crypt/Decryptor.java
src/java/org/apache/poi/poifs/crypt/EcmaDecryptor.java
src/testcases/org/apache/poi/poifs/crypt/TestDecryptor.java

index 721c503758648b844b86a783f158a3e65329ca1c..c0dda11212d35f27732459f63f41df6e7ef50711 100644 (file)
@@ -34,6 +34,7 @@
 
     <changes>
         <release version="3.8-beta6" date="2012-??-??">
+           <action dev="poi-developers" type="fix">52690 - added a getter for length of encrypted data in Ecma and Agile decryptors</action>
            <action dev="poi-developers" type="fix">52255 - support adding TIFF,EPS and WPG pictures in OOXML documents </action>
            <action dev="poi-developers" type="fix">52078 - avoid OutOfMemoryError when rendering groupped pictures in HSLF </action>
            <action dev="poi-developers" type="fix">52745 - fixed XSSFRichtextString.append to preserve leading / trailing spaces </action>
index 17ef47bb83eb08786ca9799e008925e33f59c5da..88e5f887f425a618f70005af9b9f6bae5cb26cb2 100644 (file)
@@ -19,24 +19,18 @@ package org.apache.poi.poifs.crypt;
 import java.util.Arrays;
 import java.io.IOException;
 import java.io.InputStream;
-import java.io.FilterInputStream;
-import java.io.ByteArrayInputStream;
 import java.security.MessageDigest;
 import java.security.GeneralSecurityException;
 import java.security.NoSuchAlgorithmException;
-import org.apache.poi.poifs.filesystem.POIFSFileSystem;
 import org.apache.poi.poifs.filesystem.DirectoryNode;
 import org.apache.poi.EncryptedDocumentException;
 
 import javax.crypto.Cipher;
-import javax.crypto.CipherInputStream;
 import javax.crypto.SecretKey;
 import javax.crypto.spec.SecretKeySpec;
 import javax.crypto.spec.IvParameterSpec;
 
-import org.apache.poi.poifs.filesystem.DirectoryNode;
 import org.apache.poi.poifs.filesystem.DocumentInputStream;
-import org.apache.poi.poifs.filesystem.POIFSFileSystem;
 import org.apache.poi.util.LittleEndian;
 
 /**
@@ -46,6 +40,7 @@ public class AgileDecryptor extends Decryptor {
 
     private final EncryptionInfo _info;
     private SecretKey _secretKey;
+    private long _length = -1;
 
     private static final byte[] kVerifierInputBlock;
     private static final byte[] kHashedVerifierBlock;
@@ -104,8 +99,13 @@ public class AgileDecryptor extends Decryptor {
 
     public InputStream getDataStream(DirectoryNode dir) throws IOException, GeneralSecurityException {
         DocumentInputStream dis = dir.createDocumentInputStream("EncryptedPackage");
-        long size = dis.readLong();
-        return new ChunkedCipherInputStream(dis, size);
+        _length = dis.readLong();
+        return new ChunkedCipherInputStream(dis, _length);
+    }
+
+    public long getLength(){
+        if(_length == -1) throw new IllegalStateException("EcmaDecryptor.getDataStream() was not called");
+        return _length;
     }
 
     protected AgileDecryptor(EncryptionInfo info) {
@@ -182,7 +182,7 @@ public class AgileDecryptor extends Decryptor {
         private byte[] nextChunk() throws GeneralSecurityException, IOException {
             int index = (int)(_pos >> 12);
             byte[] blockKey = new byte[4];
-            LittleEndian.putInt(blockKey, index);
+            LittleEndian.putInt(blockKey, 0, index);
             byte[] iv = generateIv(_info.getHeader().getAlgorithm(),
                                    _info.getHeader().getKeySalt(), blockKey);
             _cipher.init(Cipher.DECRYPT_MODE, _secretKey, new IvParameterSpec(iv));
index fb5b11f7b32f68e76827014019c6510cae0a1e3f..f7ed16f87541a888b5293c30c0c54c604cf309df 100644 (file)
@@ -31,12 +31,40 @@ import org.apache.poi.util.LittleEndian;
 public abstract class Decryptor {
     public static final String DEFAULT_PASSWORD="VelvetSweatshop";
 
+    /**
+     * Return a stream with decrypted data.
+     * <p>
+     * Use {@link #getLength()} to get the size of that data that can be safely read from the stream.
+     * Just reading to the end of the input stream is not sufficient because there are
+     * normally padding bytes that must be discarded
+     * </p>
+     *
+     * @param dir the node to read from
+     * @return decrypted stream
+     */
     public abstract InputStream getDataStream(DirectoryNode dir)
         throws IOException, GeneralSecurityException;
 
     public abstract boolean verifyPassword(String password)
         throws GeneralSecurityException;
 
+    /**
+     * Returns the length of the encytpted data that can be safely read with
+     * {@link #getDataStream(org.apache.poi.poifs.filesystem.DirectoryNode)}.
+     * Just reading to the end of the input stream is not sufficient because there are
+     * normally padding bytes that must be discarded
+     *
+     * <p>
+     *    The length variable is initialized in {@link #getDataStream(org.apache.poi.poifs.filesystem.DirectoryNode)},
+     *    an attempt to call getLength() prior to getDataStream() will result in IllegalStateException.
+     * </p>
+     *
+     * @return length of the encrypted data
+     * @throws IllegalStateException if {@link #getDataStream(org.apache.poi.poifs.filesystem.DirectoryNode)}
+     * was not called
+     */
+    public abstract long getLength();
+
     public static Decryptor getInstance(EncryptionInfo info) {
         int major = info.getVersionMajor();
         int minor = info.getVersionMinor();
@@ -82,7 +110,7 @@ public abstract class Decryptor {
 
         for (int i = 0; i < info.getVerifier().getSpinCount(); i++) {
             sha1.reset();
-            LittleEndian.putInt(iterator, i);
+            LittleEndian.putInt(iterator, 0, i);
             sha1.update(iterator);
             hash = sha1.digest(hash);
         }
index 441a4335feb3bb13924f150bb138cbf8499bec43..641ee8cb47a8a19665cb140d9c0009ce2c8494f1 100644 (file)
@@ -18,7 +18,6 @@ package org.apache.poi.poifs.crypt;
 
 import java.io.IOException;
 import java.io.InputStream;
-import java.io.UnsupportedEncodingException;
 import java.security.GeneralSecurityException;
 import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
@@ -31,7 +30,6 @@ import javax.crypto.spec.SecretKeySpec;
 
 import org.apache.poi.poifs.filesystem.DirectoryNode;
 import org.apache.poi.poifs.filesystem.DocumentInputStream;
-import org.apache.poi.poifs.filesystem.POIFSFileSystem;
 import org.apache.poi.util.LittleEndian;
 
 /**
@@ -41,6 +39,7 @@ import org.apache.poi.util.LittleEndian;
 public class EcmaDecryptor extends Decryptor {
     private final EncryptionInfo info;
     private byte[] passwordHash;
+    private long _length = -1;
 
     public EcmaDecryptor(EncryptionInfo info) {
         this.info = info;
@@ -51,7 +50,7 @@ public class EcmaDecryptor extends Decryptor {
 
         sha1.update(passwordHash);
         byte[] blockValue = new byte[4];
-        LittleEndian.putInt(blockValue, block);
+        LittleEndian.putInt(blockValue, 0, block);
         byte[] finalHash = sha1.digest(blockValue);
 
         int requiredKeyLength = info.getHeader().getKeySize()/8;
@@ -125,8 +124,13 @@ public class EcmaDecryptor extends Decryptor {
     public InputStream getDataStream(DirectoryNode dir) throws IOException, GeneralSecurityException {
         DocumentInputStream dis = dir.createDocumentInputStream("EncryptedPackage");
 
-        long size = dis.readLong();
+        _length = dis.readLong();
 
         return new CipherInputStream(dis, getCipher());
     }
+
+    public long getLength(){
+        if(_length == -1) throw new IllegalStateException("EcmaDecryptor.getDataStream() was not called");
+        return _length;
+    }
 }
index 27385719c841496ace8829906dc11379e4db6e22..dcef8d31cd464a7e7eb5258c3488260eb2c4e002 100644 (file)
@@ -20,7 +20,9 @@ import junit.framework.TestCase;
 import org.apache.poi.POIDataSamples;
 import org.apache.poi.poifs.filesystem.POIFSFileSystem;
 
+import java.io.ByteArrayInputStream;
 import java.io.IOException;
+import java.io.InputStream;
 import java.security.GeneralSecurityException;
 import java.util.zip.ZipEntry;
 import java.util.zip.ZipInputStream;
@@ -80,4 +82,36 @@ public class TestDecryptor extends TestCase {
             }
         }
     }
+    public void testDataLength() throws Exception {
+        POIFSFileSystem fs = new POIFSFileSystem(POIDataSamples.getPOIFSInstance().openResourceAsStream("protected_agile.docx"));
+
+        EncryptionInfo info = new EncryptionInfo(fs);
+
+        Decryptor d = Decryptor.getInstance(info);
+
+        d.verifyPassword(Decryptor.DEFAULT_PASSWORD);
+
+        InputStream is = d.getDataStream(fs);
+
+        long len = d.getLength();
+        assertEquals(12810, len);
+
+        byte[] buf = new byte[(int)len];
+
+        is.read(buf);
+
+        ZipInputStream zin = new ZipInputStream(new ByteArrayInputStream(buf));
+
+        while (true) {
+            ZipEntry entry = zin.getNextEntry();
+            if (entry==null) {
+                break;
+            }
+
+            while (zin.available()>0) {
+                zin.skip(zin.available());
+            }
+        }
+    }
+
 }