de.schlichtherle.truezip.fs.archive.zip.raes.ZipRaesDriver Maven / Gradle / Ivy
/*
* Copyright (C) 2005-2015 Schlichtherle IT Services.
* All rights reserved. Use is subject to license terms.
*/
package de.schlichtherle.truezip.fs.archive.zip.raes;
import de.schlichtherle.truezip.crypto.raes.RaesOutputStream;
import de.schlichtherle.truezip.crypto.raes.RaesParameters;
import de.schlichtherle.truezip.crypto.raes.RaesReadOnlyFile;
import de.schlichtherle.truezip.crypto.raes.param.AesCipherParameters;
import de.schlichtherle.truezip.crypto.raes.param.KeyManagerRaesParameters;
import de.schlichtherle.truezip.entry.Entry;
import de.schlichtherle.truezip.entry.Entry.Type;
import de.schlichtherle.truezip.fs.FsController;
import de.schlichtherle.truezip.fs.FsEntryName;
import de.schlichtherle.truezip.fs.FsModel;
import de.schlichtherle.truezip.fs.FsOutputOption;
import static de.schlichtherle.truezip.fs.FsOutputOption.*;
import de.schlichtherle.truezip.fs.archive.zip.JarDriver;
import de.schlichtherle.truezip.fs.archive.zip.OptionOutputSocket;
import de.schlichtherle.truezip.fs.archive.zip.ZipDriverEntry;
import de.schlichtherle.truezip.fs.archive.zip.ZipInputShop;
import de.schlichtherle.truezip.key.KeyManagerProvider;
import de.schlichtherle.truezip.rof.ReadOnlyFile;
import de.schlichtherle.truezip.socket.*;
import de.schlichtherle.truezip.util.BitField;
import java.io.CharConversionException;
import java.io.IOException;
import java.io.OutputStream;
import javax.annotation.CheckForNull;
import javax.annotation.concurrent.Immutable;
/**
* An abstract archive driver for RAES encrypted ZIP files which optionally
* authenticates the cipher data of the input archive files presented to it.
*
* Sub-classes must be thread-safe and should be immutable!
*
* @author Christian Schlichtherle
*/
@Immutable
public abstract class ZipRaesDriver extends JarDriver {
/**
* The key manager provider for accessing protected resources (cryptography).
*/
private final KeyManagerProvider keyManagerProvider;
/**
* Constructs a new RAES encrypted ZIP file driver.
*
* @param ioPoolProvider the provider for the I/O buffer pool.
* @param keyManagerProvider the key manager provider for accessing
* protected resources (cryptography).
*/
public ZipRaesDriver( final IOPoolProvider ioPoolProvider,
final KeyManagerProvider keyManagerProvider) {
super(ioPoolProvider);
if (null == (this.keyManagerProvider = keyManagerProvider))
throw new NullPointerException();
}
/**
* {@inheritDoc}
*
* Since TrueZIP 7.3, the implementation in the class {@link ZipRaesDriver}
* returns {@code true} for future use.
*
* @return {@code true}
*/
@Override
public final boolean getPreambled() {
return true;
}
/**
* Returns the provider for key managers for accessing protected resources
* (encryption).
*
* The implementation in {@link ZipRaesDriver} simply returns the value of
* the field {@link #keyManagerProvider}.
*
* @return The provider for key managers for accessing protected resources
* (encryption).
* @since TrueZIP 7.3.
*/
@Override
protected final KeyManagerProvider getKeyManagerProvider() {
return keyManagerProvider;
}
/**
* Returns the RAES parameters for the given file system model.
*
* The implementation in the class {@link ZipRaesDriver} returns
* {@code new KeyManagerRaesParameters(getKeyManagerProvider().get(AesCipherParameters.class), mountPointUri(model))}.
*
* @param model the file system model.
* @return The RAES parameters for the given file system model.
*/
protected RaesParameters raesParameters(FsModel model) {
return new KeyManagerRaesParameters(
getKeyManagerProvider().get(AesCipherParameters.class),
mountPointUri(model));
}
/**
* Returns the value of the property {@code authenticationTrigger}.
*
* If the cipher text length of an input RAES file is smaller than or equal
* to this value, then the Hash-based Message Authentication Code (HMAC)
* for the entire cipher text is computed and verified in order to
* authenticate the input RAES file.
*
* Otherwise, if the cipher text length of an input RAES file is greater
* than this value, then initially only the cipher key and the cipher text
* length get authenticated.
* In addition, whenever an entry is subsequently accessed, then it's
* CRC-32 value is checked.
*
* Consequently, if the value of this property is set to a negative value,
* then the entire cipher text gets never authenticated (CRC-32
* checking only), and if set to {@link Long#MAX_VALUE}, then the entire
* cipher text gets always authenticated (no CRC-32 checking).
*
* @return The value of the property {@code authenticationTrigger}.
*/
protected abstract long getAuthenticationTrigger();
@Override
protected final boolean check(ZipInputShop input, ZipDriverEntry entry) {
// Optimization: If the cipher text alias the encrypted ZIP file is
// smaller than the authentication trigger, then its entire cipher text
// has already been authenticated by {@link ZipRaesDriver#newInputShop}.
// Hence, checking the CRC-32 value of the entry is redundant.
return input.length() > getAuthenticationTrigger();
}
/**
* {@inheritDoc}
*
* The implementation in the class {@link ZipRaesDriver} returns the
* expression
* {@code new ZipRaesKeyController(controller, this)}.
* Overridde this method in order to return just the given
* {@code controller} if you are overriding
* {@link #raesParameters(FsModel)} and do not want to use
* a locatable key manager to resolve passwords for RAES encryption.
*/
@Override
public FsController decorate(FsController controller) {
return new ZipRaesKeyController(controller, this);
}
/**
* Returns a new {@link ZipDriverEntry}, requesting that the data gets
* {@code DEFLATED} if no template is provided.
* This feature strengthens the security level of the authentication
* process and inhibits the use of an unencrypted temporary I/O entry
* (usually a temporary file) in case the output is not copied from a file
* system entry as its input.
*
* Furthermore, the method {@link ZipDriverEntry#clearEncryption()} is
* called in order to prevent adding a redundant encryption layer for the
* individual ZIP entry because this would confuse users, increase the size
* of the resulting archive file and unecessarily heat the CPU.
*/
@Override
public ZipDriverEntry newEntry(
final String path,
final Type type,
final @CheckForNull Entry template,
final BitField mknod)
throws CharConversionException {
final ZipDriverEntry entry
= super.newEntry(path, type, template, mknod.set(COMPRESS));
// Fix for http://java.net/jira/browse/TRUEZIP-176 :
// Entry level encryption is enabled if mknod.get(ENCRYPTED) is true
// OR template is an instance of ZipEntry
// AND ((ZipEntry) template).isEncrypted() is true.
// Now switch off entry level encryption because encryption is already
// provided by the RAES wrapper file format.
entry.clearEncryption();
return entry;
}
/**
* {@inheritDoc}
*
* The implementation in {@link ZipRaesDriver} calls
* {@link #raesParameters}, with which it initializes a new
* {@link RaesReadOnlyFile}.
* Next, if the gross file length of the archive is smaller than or equal
* to the authentication trigger, the MAC authentication on the cipher
* text is performed.
* Finally, the {@link RaesReadOnlyFile} is passed on to the super
* class implementation.
*/
@Override
public final InputShop
newInputShop( final FsModel model,
final InputSocket> input)
throws IOException {
if (null == model)
throw new NullPointerException();
final ReadOnlyFile rof = input.newReadOnlyFile();
try {
final RaesReadOnlyFile rrof = RaesReadOnlyFile.getInstance(
rof, raesParameters(model));
if (rrof.length() <= getAuthenticationTrigger()) // compare rrof, not rof!
rrof.authenticate();
return newInputShop(model, rrof);
} catch (IOException ex) {
rof.close();
throw ex;
}
}
/**
* Sets {@link FsOutputOption#STORE} in {@code options} before
* forwarding the call to {@code controller}.
*/
@Override
public final OptionOutputSocket getOutputSocket(
final FsController> controller,
final FsEntryName name,
BitField options,
final @CheckForNull Entry template) {
// Leave FsOutputOption.COMPRESS untouched - the driver shall be given
// opportunity to apply its own preferences to sort out such a conflict.
options = options.set(STORE);
// The RAES file format cannot support GROWing.
options = options.clear(GROW);
return new OptionOutputSocket(
controller.getOutputSocket(name, options, template),
options);
}
@Override
@edu.umd.cs.findbugs.annotations.SuppressWarnings("OBL_UNSATISFIED_OBLIGATION")
protected OutputShop newOutputShop(
final FsModel model,
final OptionOutputSocket output,
final ZipInputShop source)
throws IOException {
if (null == model)
throw new NullPointerException();
final OutputStream out = new LazyOutputSocket(output)
.newOutputStream();
try {
final RaesOutputStream ros = RaesOutputStream.getInstance(
out, raesParameters(model));
return newOutputShop(model, ros, source);
} catch (IOException ex) {
out.close();
throw ex;
}
}
}