| /* ==================================================================== |
| 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; |
| |
| import java.io.ByteArrayOutputStream; |
| import java.io.IOException; |
| import java.util.Map; |
| import java.util.function.Supplier; |
| |
| import org.apache.poi.EncryptedDocumentException; |
| import org.apache.poi.poifs.crypt.EncryptionInfo; |
| import org.apache.poi.poifs.crypt.EncryptionMode; |
| import org.apache.poi.poifs.crypt.binaryrc4.BinaryRC4EncryptionHeader; |
| import org.apache.poi.poifs.crypt.binaryrc4.BinaryRC4EncryptionVerifier; |
| import org.apache.poi.poifs.crypt.cryptoapi.CryptoAPIEncryptionHeader; |
| import org.apache.poi.poifs.crypt.cryptoapi.CryptoAPIEncryptionVerifier; |
| import org.apache.poi.poifs.crypt.xor.XOREncryptionHeader; |
| import org.apache.poi.poifs.crypt.xor.XOREncryptionVerifier; |
| import org.apache.poi.util.GenericRecordUtil; |
| import org.apache.poi.util.LittleEndianByteArrayOutputStream; |
| import org.apache.poi.util.LittleEndianOutput; |
| import org.apache.poi.util.LittleEndianOutputStream; |
| |
| /** |
| * File Pass Record (0x002F) <p> |
| * |
| * Indicates that the record after this record are encrypted. |
| */ |
| public final class FilePassRecord extends StandardRecord { |
| public static final short sid = 0x002F; |
| private static final int ENCRYPTION_XOR = 0; |
| private static final int ENCRYPTION_OTHER = 1; |
| |
| private final int encryptionType; |
| private EncryptionInfo encryptionInfo; |
| |
| private FilePassRecord(FilePassRecord other) { |
| super(other); |
| encryptionType = other.encryptionType; |
| encryptionInfo = other.encryptionInfo.copy(); |
| } |
| |
| public FilePassRecord(EncryptionMode encryptionMode) { |
| encryptionType = (encryptionMode == EncryptionMode.xor) ? ENCRYPTION_XOR : ENCRYPTION_OTHER; |
| encryptionInfo = new EncryptionInfo(encryptionMode); |
| } |
| |
| public FilePassRecord(RecordInputStream in) { |
| encryptionType = in.readUShort(); |
| |
| EncryptionMode preferredMode; |
| switch (encryptionType) { |
| case ENCRYPTION_XOR: |
| preferredMode = EncryptionMode.xor; |
| break; |
| case ENCRYPTION_OTHER: |
| preferredMode = EncryptionMode.cryptoAPI; |
| break; |
| default: |
| throw new EncryptedDocumentException("invalid encryption type"); |
| } |
| |
| try { |
| encryptionInfo = new EncryptionInfo(in, preferredMode); |
| } catch (IOException e) { |
| throw new EncryptedDocumentException(e); |
| } |
| } |
| |
| @SuppressWarnings("resource") |
| @Override |
| public void serialize(LittleEndianOutput out) { |
| out.writeShort(encryptionType); |
| |
| byte[] data = new byte[1024]; |
| try (LittleEndianByteArrayOutputStream bos = |
| new LittleEndianByteArrayOutputStream(data, 0)) { // NOSONAR |
| |
| switch (encryptionInfo.getEncryptionMode()) { |
| case xor: |
| ((XOREncryptionHeader)encryptionInfo.getHeader()).write(bos); |
| ((XOREncryptionVerifier)encryptionInfo.getVerifier()).write(bos); |
| break; |
| case binaryRC4: |
| out.writeShort(encryptionInfo.getVersionMajor()); |
| out.writeShort(encryptionInfo.getVersionMinor()); |
| ((BinaryRC4EncryptionHeader)encryptionInfo.getHeader()).write(bos); |
| ((BinaryRC4EncryptionVerifier)encryptionInfo.getVerifier()).write(bos); |
| break; |
| case cryptoAPI: |
| out.writeShort(encryptionInfo.getVersionMajor()); |
| out.writeShort(encryptionInfo.getVersionMinor()); |
| out.writeInt(encryptionInfo.getEncryptionFlags()); |
| ((CryptoAPIEncryptionHeader)encryptionInfo.getHeader()).write(bos); |
| ((CryptoAPIEncryptionVerifier)encryptionInfo.getVerifier()).write(bos); |
| break; |
| default: |
| throw new EncryptedDocumentException("not supported"); |
| } |
| |
| out.write(data, 0, bos.getWriteIndex()); |
| } catch (IOException ioe) { |
| // should never happen in practice |
| throw new IllegalStateException(ioe); |
| } |
| } |
| |
| @Override |
| protected int getDataSize() { |
| ByteArrayOutputStream bos = new ByteArrayOutputStream(); |
| LittleEndianOutputStream leos = new LittleEndianOutputStream(bos); |
| serialize(leos); |
| return bos.size(); |
| } |
| |
| public EncryptionInfo getEncryptionInfo() { |
| return encryptionInfo; |
| } |
| |
| @Override |
| public short getSid() { |
| return sid; |
| } |
| |
| @Override |
| public FilePassRecord copy() { |
| return new FilePassRecord(this); |
| } |
| |
| @Override |
| public HSSFRecordTypes getGenericRecordType() { |
| return HSSFRecordTypes.FILE_PASS; |
| } |
| |
| @Override |
| public Map<String, Supplier<?>> getGenericProperties() { |
| return GenericRecordUtil.getGenericProperties( |
| "type", () -> encryptionType, |
| "encryptionInfo", this::getEncryptionInfo |
| ); |
| } |
| } |