View | Details | Raw Unified | Return to bug 47652
Collapse All | Expand All

(-)src/java/org/apache/poi/hssf/record/RecordInputStream.java (-9 / +38 lines)
Lines 19-30 Link Here
19
19
20
import java.io.ByteArrayOutputStream;
20
import java.io.ByteArrayOutputStream;
21
import java.io.InputStream;
21
import java.io.InputStream;
22
import java.util.Set;
23
import java.util.Collections;
24
import java.util.HashSet;
22
25
23
import org.apache.poi.hssf.dev.BiffViewer;
26
import org.apache.poi.hssf.dev.BiffViewer;
24
import org.apache.poi.util.LittleEndian;
27
import org.apache.poi.util.LittleEndian;
25
import org.apache.poi.util.LittleEndianInput;
28
import org.apache.poi.util.LittleEndianInput;
26
import org.apache.poi.util.LittleEndianInputStream;
29
import org.apache.poi.util.LittleEndianInputStream;
30
import org.apache.poi.util.RC4InputStream;
27
31
32
import javax.crypto.Cipher;
33
28
/**
34
/**
29
 * Title:  Record Input Stream<P>
35
 * Title:  Record Input Stream<P>
30
 * Description:  Wraps a stream and provides helper methods for the construction of records.<P>
36
 * Description:  Wraps a stream and provides helper methods for the construction of records.<P>
Lines 41-48 Link Here
41
	 */
47
	 */
42
	private static final int DATA_LEN_NEEDS_TO_BE_READ = -1;
48
	private static final int DATA_LEN_NEEDS_TO_BE_READ = -1;
43
	private static final byte[] EMPTY_BYTE_ARRAY = { };
49
	private static final byte[] EMPTY_BYTE_ARRAY = { };
44
	
50
45
	/**
51
        private RC4InputStream rc4is;
52
53
        private static final Set<Short> unencryptSid = new HashSet<Short>();
54
        static {
55
            /*
56
                [MS-XLS] — v20090708
57
                Excel Binary File Format (.xls) Structure Specification
58
             */
59
            unencryptSid.add(BOFRecord.sid);
60
            unencryptSid.add(FilePassRecord.sid);
61
            // UsrExcl
62
            // FileLock
63
            unencryptSid.add(InterfaceHdrRecord.sid);
64
            // RRDInfo
65
            // RRDHead
66
            // TODO: Additionally, the lbPlyPos field of the BoundSheet8 record MUST NOT be encrypted.
67
        }
68
69
        /**
46
	 * For use in {@link BiffViewer} which may construct {@link Record}s that don't completely
70
	 * For use in {@link BiffViewer} which may construct {@link Record}s that don't completely
47
	 * read all available data.  This exception should never be thrown otherwise.
71
	 * read all available data.  This exception should never be thrown otherwise.
48
	 */
72
	 */
Lines 77-89 Link Here
77
	private int _currentDataOffset;
101
	private int _currentDataOffset;
78
102
79
	public RecordInputStream(InputStream in) throws RecordFormatException {
103
	public RecordInputStream(InputStream in) throws RecordFormatException {
80
		if (in instanceof LittleEndianInput) {
104
                rc4is = new RC4InputStream(in);
81
			// accessing directly is an optimisation
105
		_le = new LittleEndianInputStream(rc4is);
82
			_le = (LittleEndianInput) in;
83
		} else {
84
			// less optimal, but should work OK just the same. Often occurs in junit tests.
85
			_le = new LittleEndianInputStream(in);
86
		}
87
		_nextSid = readNextSid();
106
		_nextSid = readNextSid();
88
	}
107
	}
89
	
108
	
Lines 100-105 Link Here
100
		_currentDataOffset += LittleEndian.BYTE_SIZE;
119
		_currentDataOffset += LittleEndian.BYTE_SIZE;
101
		return _le.readUByte();
120
		return _le.readUByte();
102
	}
121
	}
122
103
	public int read(byte[] b, int off, int len) {
123
	public int read(byte[] b, int off, int len) {
104
		int limit = Math.min(len, remaining());
124
		int limit = Math.min(len, remaining());
105
		if (limit == 0) {
125
		if (limit == 0) {
Lines 143-148 Link Here
143
			}
163
			}
144
			return INVALID_SID_VALUE;
164
			return INVALID_SID_VALUE;
145
		}
165
		}
166
                rc4is.setDecrypt(false);
146
		int result = _le.readUShort();
167
		int result = _le.readUShort();
147
		if (result == INVALID_SID_VALUE) {
168
		if (result == INVALID_SID_VALUE) {
148
			throw new RecordFormatException("Found invalid sid (" + result + ")");
169
			throw new RecordFormatException("Found invalid sid (" + result + ")");
Lines 169-174 Link Here
169
			throw new RecordFormatException("The content of an excel record cannot exceed "
190
			throw new RecordFormatException("The content of an excel record cannot exceed "
170
					+ MAX_RECORD_DATA_SIZE + " bytes");
191
					+ MAX_RECORD_DATA_SIZE + " bytes");
171
		}
192
		}
193
194
                if (!unencryptSid.contains((short) _currentSid)) {
195
                       rc4is.setDecrypt(true);
196
                }
172
	}
197
	}
173
198
174
	private void checkRecordPosition(int requiredByteCount) {
199
	private void checkRecordPosition(int requiredByteCount) {
Lines 394-397 Link Here
394
		//    and before the formatting run data)
419
		//    and before the formatting run data)
395
		return _nextSid == ContinueRecord.sid;
420
		return _nextSid == ContinueRecord.sid;
396
	}
421
	}
422
423
        public void setRC4Header(RC4Header rc4Header) {
424
                rc4is.setHeader(rc4Header);
425
        }
397
}
426
}
(-)src/java/org/apache/poi/hssf/record/RecordFactoryInputStream.java (+4 lines)
Lines 148-153 Link Here
148
                return record;
148
                return record;
149
            }
149
            }
150
150
151
            if (record instanceof FilePassRecord) {
152
                recStream.setRC4Header(((FilePassRecord) record).getRC4Header());
153
            }
154
151
            if (record instanceof EOFRecord) {
155
            if (record instanceof EOFRecord) {
152
                bofDepth--;
156
                bofDepth--;
153
                if (bofDepth < 1) {
157
                if (bofDepth < 1) {
(-)src/java/org/apache/poi/hssf/record/FilePassRecord.java (-11 / +48 lines)
Lines 33-81 Link Here
33
public final class FilePassRecord
33
public final class FilePassRecord
34
    extends StandardRecord
34
    extends StandardRecord
35
{
35
{
36
    public final static short sid = 0x2F;
36
    public static final short sid = 0x2F;
37
    private int             field_1_encryptedpassword;
37
    private int wEncryptionType;
38
38
39
    private static final int ENCRYPTION_XOR = 0;
40
    private static final int ENCRYPTION_OTHER = 1;
41
42
    private static final int ENCRYPTION_OTHER_RC4 = 1;
43
    private static final int ENCRYPTION_OTHER_CAPI_2 = 2;
44
    private static final int ENCRYPTION_OTHER_CAPI_3 = 3;
45
46
    private RC4Header header;
47
39
    public FilePassRecord()
48
    public FilePassRecord()
40
    {
49
    {
41
    }
50
    }
42
51
43
    public FilePassRecord(RecordInputStream in)
52
    public FilePassRecord(RecordInputStream in)
44
    {
53
    {
45
        field_1_encryptedpassword = in.readInt();
54
        wEncryptionType = in.readShort();
55
56
        switch (wEncryptionType) {
57
            case ENCRYPTION_XOR:
58
                throw new RecordFormatException("HSSF does not currently support XOR obfuscation");
59
            case ENCRYPTION_OTHER:
60
                int encryptionInfo = in.readShort();
61
62
                switch (encryptionInfo) {
63
                    case ENCRYPTION_OTHER_RC4:
64
                        header = new RC4Header(in);
65
66
                        return;
67
                    case ENCRYPTION_OTHER_CAPI_2:
68
                    case ENCRYPTION_OTHER_CAPI_3:
69
                        throw new RecordFormatException("HSSF does not currently support CryptoAPI encryption");
70
                    default:
71
                        throw new RecordFormatException("Unknown encryption info "+wEncryptionType);
72
                }
73
            default:
74
                throw new RecordFormatException("Unknown encryption type "+wEncryptionType);
75
        }
46
        
76
        
47
        //Whilst i have read in the password, HSSF currently has no plans to support/decrypt the remainder
77
        //Whilst i have read in the password, HSSF currently has no plans to support/decrypt the remainder
48
        //of this workbook
78
        //of this workbook
49
        throw new RecordFormatException("HSSF does not currently support encrypted workbooks");
79
//        throw new RecordFormatException("HSSF does not currently support encrypted workbooks");
50
    }
80
    }
51
81
52
    public String toString()
82
    @Override
53
    {
83
    public String toString() {
54
        StringBuffer buffer = new StringBuffer();
84
        StringBuilder buffer = new StringBuilder();
55
85
56
        buffer.append("[FILEPASS]\n");
86
        buffer.append("[FILEPASS]\n");
57
        buffer.append("    .password        = ").append(field_1_encryptedpassword)
87
        buffer.append("    .type        = ").append(wEncryptionType)
58
            .append("\n");
88
            .append('\n');
59
        buffer.append("[/FILEPASS]\n");
89
        buffer.append("[/FILEPASS]\n");
60
        return buffer.toString();
90
        return buffer.toString();
61
    }
91
    }
62
92
93
    @Override
63
    public void serialize(LittleEndianOutput out) {
94
    public void serialize(LittleEndianOutput out) {
64
        out.writeInt(( short ) field_1_encryptedpassword);
65
    }
95
    }
66
96
97
    @Override
67
    protected int getDataSize() {
98
    protected int getDataSize() {
68
        return 4;
99
        return 4;
69
    }
100
    }
70
101
102
    @Override
71
    public short getSid()
103
    public short getSid()
72
    {
104
    {
73
        return sid;
105
        return sid;
74
    }
106
    }
75
107
108
    @Override
76
    public Object clone() {
109
    public Object clone() {
77
      FilePassRecord rec = new FilePassRecord();
110
      FilePassRecord rec = new FilePassRecord();
78
      rec.field_1_encryptedpassword = field_1_encryptedpassword;
111
      rec.wEncryptionType = wEncryptionType;
79
      return rec;
112
      return rec;
80
    }
113
    }
114
115
    public RC4Header getRC4Header() {
116
        return header;
117
    }
81
}
118
}
(-)src/testcases/org/apache/poi/hssf/extractor/TestExcelExtractor.java (+10 lines)
Lines 24-29 Link Here
24
import junit.framework.TestCase;
24
import junit.framework.TestCase;
25
25
26
import org.apache.poi.hssf.HSSFTestDataSamples;
26
import org.apache.poi.hssf.HSSFTestDataSamples;
27
import org.apache.poi.hssf.record.RC4Header;
27
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
28
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
28
import org.apache.poi.poifs.filesystem.DirectoryNode;
29
import org.apache.poi.poifs.filesystem.DirectoryNode;
29
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
30
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
Lines 289-292 Link Here
289
			assertTrue("Unable to find expected word in text\n" + text, text.indexOf("test phrase") >= 0);
290
			assertTrue("Unable to find expected word in text\n" + text, text.indexOf("test phrase") >= 0);
290
		}
291
		}
291
	}
292
	}
293
294
         public void testPassword() {
295
             RC4Header.setPassword("password");
296
             ExcelExtractor extractor = createExtractor("password.xls");
297
             String text = extractor.getText();
298
             RC4Header.setPassword(null);
299
 
300
             assertTrue(text.contains("ZIP"));
301
         }
292
}
302
}

Return to bug 47652