All Downloads are FREE. Search and download functionalities are using the official Maven repository.

de.schlichtherle.truezip.key.pbe.swing.SwingSafePbeParametersView Maven / Gradle / Ivy

Go to download

The file system driver family for ZIP and related archive file types. Add the JAR artifact of this module to the run time class path to make its file system drivers available for service location in the client API modules.

There is a newer version: 7.7.10
Show newest version
/*
 * Copyright (C) 2005-2013 Schlichtherle IT Services.
 * All rights reserved. Use is subject to license terms.
 */
package de.schlichtherle.truezip.key.pbe.swing;

import de.schlichtherle.truezip.awt.Windows;
import de.schlichtherle.truezip.crypto.param.KeyStrength;
import de.schlichtherle.truezip.key.KeyPromptingInterruptedException;
import de.schlichtherle.truezip.key.PromptingKeyProvider.Controller;
import de.schlichtherle.truezip.key.UnknownKeyException;
import de.schlichtherle.truezip.key.pbe.SafePbeParameters;
import de.schlichtherle.truezip.key.pbe.SafePbeParametersView;
import de.schlichtherle.truezip.util.ServiceLocator;
import java.awt.EventQueue;
import java.awt.Window;
import java.io.EOFException;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.UndeclaredThrowableException;
import java.net.URI;
import java.util.Map;
import java.util.ResourceBundle;
import java.util.WeakHashMap;
import javax.annotation.concurrent.ThreadSafe;
import javax.swing.JOptionPane;

/**
 * A Swing based user interface to prompt for passwords or key files.
 *
 * @author Christian Schlichtherle
 */
@ThreadSafe
public abstract class SwingSafePbeParametersView<
        S extends KeyStrength,
        P extends SafePbeParameters>
extends SafePbeParametersView

{ private static final ResourceBundle resources = ResourceBundle.getBundle(SwingSafePbeParametersView.class.getName()); static final URI INITIAL_RESOURCE = URI.create(""); // NOI18N /** * This is the number of bytes to load from the beginning of a key file. * A valid key file for encryption must contain at least this number of * bytes! */ // Must be a multiple of 2 and must be long enough so that // GZIPOutputStream most likely produces more than 2 * 256 / 8 bytes // output. public static final int KEY_FILE_LEN = 512; private static final Map readKeyPanels = new WeakHashMap(); private static final ServiceLocator serviceLocator = new ServiceLocator(SwingSafePbeParametersView.class.getClassLoader()); /** * The last resource ID used when prompting. * Initialized to the empty string. */ static volatile URI lastResource = INITIAL_RESOURCE; private volatile UnknownKeyFeedback unknownKeyFeedback; private volatile InvalidKeyFeedback invalidKeyFeedback; /** * Reads the encryption key as a byte sequence from the given pathname * into a new buffer of exactly {@code KEY_FILE_LEN} bytes and returns it. * * @throws EOFException If the file is not at least {@code KEY_FILE_LEN} * bytes long. * @throws IOException on any other I/O related issue. */ static byte[] readKeyFile(File file) throws IOException { final byte[] buf = new byte[KEY_FILE_LEN]; final RandomAccessFile raf = new RandomAccessFile(file, "r"); try { raf.readFully(buf); } finally { raf.close(); } return buf; } UnknownKeyFeedback getUnknownKeyFeedback() { final UnknownKeyFeedback uckf = this.unknownKeyFeedback; return null != uckf ? uckf : (this.unknownKeyFeedback = serviceLocator.getService( UnknownKeyFeedback.class, BasicUnknownKeyFeedback.class)); } void setUnkownKeyFeedback(final UnknownKeyFeedback uckf) { this.unknownKeyFeedback = uckf; } InvalidKeyFeedback getInvalidKeyFeedback() { final InvalidKeyFeedback ickf = this.invalidKeyFeedback; return null != ickf ? ickf : (this.invalidKeyFeedback = serviceLocator.getService( InvalidKeyFeedback.class, BasicInvalidKeyFeedback.class)); } void setInvalidKeyFeedback(final InvalidKeyFeedback ickf) { this.invalidKeyFeedback = ickf; } @Override public void promptWriteKey( final Controller

controller) throws UnknownKeyException { class PromptWriteKey implements Runnable { @Override public void run() { promptWriteKeyEDT(controller); } } // PromptWriteKey multiplexOnEDT(new PromptWriteKey()); // synchronized on class instance! } /** * This method is only called by the AWT Event Dispatch Thread, * so it doesn't need to be thread safe. */ private void promptWriteKeyEDT( final Controller

controller) { assert EventQueue.isDispatchThread(); final URI resource = controller.getResource(); assert null != resource; P param = controller.getKey(); if (null == param) param = newPbeParameters(); final KeyStrengthPanel keyStrengthPanel = new KeyStrengthPanel( param.getKeyStrengthValues()); keyStrengthPanel.setKeyStrength(param.getKeyStrength()); final WriteKeyPanel keyPanel = new WriteKeyPanel(); keyPanel.setExtraDataUI(keyStrengthPanel); final Window parent = Windows.getParentWindow(); while (!Thread.interrupted()) { // test and clear status! // Setting this inside the loop has the side effect of // de-highlighting the resource ID in the panel if the // loop iteration has to be repeated due to an invalid // user input. keyPanel.setResource(resource); keyPanel.setFeedback(keyPanel.getError() != null ? getInvalidKeyFeedback() : getUnknownKeyFeedback()); final int result = JOptionPane.showConfirmDialog( parent, keyPanel, resources.getString("writeKeyDialog.title"), JOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE); /*if (Thread.interrupted()) // test and clear status! break;*/ if (result != JOptionPane.OK_OPTION) break; // reuse old key if (keyPanel.updateParam(param)) { // valid input? param.setKeyStrength(keyStrengthPanel.getKeyStrength()); controller.setKey(param); break; } // Continue looping until valid input. assert keyPanel.getError() != null; } } @Override public void promptReadKey( final Controller

controller, final boolean invalid) throws UnknownKeyException { class PromptReadKey implements Runnable { @Override public void run() { promptReadKeyEDT(controller, invalid); } } // PromptReadKey multiplexOnEDT(new PromptReadKey()); // synchronized on class instance! } /** * This method is only called by the AWT Event Dispatch Thread, * so it doesn't need to be thread safe. */ private void promptReadKeyEDT( final Controller

controller, final boolean invalid) { assert EventQueue.isDispatchThread(); final URI resource = controller.getResource(); assert null != resource; final P param = newPbeParameters(); final ReadKeyPanel keyPanel; if (invalid) { final ReadKeyPanel panel = readKeyPanels.get(resource); if (panel != null) { keyPanel = panel; } else { keyPanel = new ReadKeyPanel(); } keyPanel.setError(resources.getString("invalidKey")); } else { keyPanel = new ReadKeyPanel(); } readKeyPanels.put(resource, keyPanel); final Window parent = Windows.getParentWindow(); while (!Thread.interrupted()) { // test and clear status! // Setting this inside the loop has the side effect of // de-highlighting the resource ID in the panel if the // loop iteration has to be repeated due to an invalid // user input. keyPanel.setResource(resource); keyPanel.setFeedback(null != keyPanel.getError() ? getInvalidKeyFeedback() : getUnknownKeyFeedback()); final int result = JOptionPane.showConfirmDialog( parent, keyPanel, resources.getString("readKeyDialog.title"), JOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE); /*if (Thread.interrupted()) // test and clear status! break;*/ if (result != JOptionPane.OK_OPTION) { controller.setKey(null); break; } if (keyPanel.updateParam(param)) { // valid input? controller.setKey(param); controller.setChangeRequested(keyPanel.isChangeKeySelected()); break; } // Continue looping until valid input. assert keyPanel.getError() != null; } } /** * Invokes the given {@code task} on the AWT Event Dispatching Thread * (EDT) and waits until it's finished. *

* In multithreaded environments, although technically possible, * do not allow multiple threads to prompt for a key concurrently, * because this would only confuse users. * By explicitly locking the class object rather than the instance, * we enforce this even if multiple implementations and instances * are used. *

* If the current thread is interrupted, an * {@link UndeclaredThrowableException} is thrown with a * {@link KeyPromptingInterruptedException} as its cause. *

* If a {@link Throwable} is thrown by the EDT, then it's wrapped in an * {@link UndeclaredThrowableException} and re-thrown by this thread. */ private static void multiplexOnEDT(final Runnable task) throws UnknownKeyException { if (EventQueue.isDispatchThread()) { task.run(); } else { synchronized (SwingSafePbeParametersView.class) { try { EventQueue.invokeAndWait(task); } catch (InterruptedException interrupt) { throw new KeyPromptingInterruptedException(interrupt); } catch (InvocationTargetException failure) { throw new UnknownKeyException(failure); } } } } }