package com.limegroup.gnutella.messages;
import java.io.File;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.Signature;
import java.security.SignatureException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import com.limegroup.gnutella.security.SignatureVerifier;
import com.limegroup.gnutella.util.CommonUtils;
import com.limegroup.gnutella.util.ProcessingQueue;
/** A class that verifies secure messages sequentially. */
public class SecureMessageVerifier {
private static final Log LOG = LogFactory.getLog(SecureMessageVerifier.class);
private final ProcessingQueue QUEUE = new ProcessingQueue("SecureMessageVerifier");
/** The public key. */
private PublicKey pubKey;
/** Queues this SecureMessage for verification. The callback will be notified of success or failure. */
public void verify(SecureMessage sm, SecureMessageCallback smc) {
QUEUE.add(new Verifier(sm, smc));
}
/** Initializes the public key if one isn't set. */
private void initializePublicKey() {
if(pubKey == null)
pubKey = createPublicKey();
}
/** Creates the public key. */
protected PublicKey createPublicKey() {
return SignatureVerifier.readKey(getKeyFile(), "DSA");
}
/** Gets the file holding the key. */
protected File getKeyFile() {
return new File(CommonUtils.getUserSettingsDir(), "secureMessage.key");
}
/** Does the verification. */
private void verifyMessage(SecureMessage message, SecureMessageCallback callback) {
if(pubKey == null) {
LOG.warn("Cannot verify message without a public key.");
message.setSecureStatus(SecureMessage.INSECURE);
callback.handleSecureMessage(message, false);
return;
}
byte[] signature = message.getSecureSignature();
if(signature == null) {
LOG.warn("Cannot verify message without a signature.");
message.setSecureStatus(SecureMessage.INSECURE);
callback.handleSecureMessage(message, false);
return;
}
try {
Signature verifier = Signature.getInstance("SHA1withDSA");
verifier.initVerify(pubKey);
message.updateSignatureWithSecuredBytes(verifier);
if(verifier.verify(signature)) {
message.setSecureStatus(SecureMessage.SECURE);
callback.handleSecureMessage(message, true);
return;
}
// fallthrough on not secure & failures to set failed.
} catch (NoSuchAlgorithmException nsax) {
LOG.error("No alg.", nsax);
} catch (InvalidKeyException ikx) {
LOG.error("Invalid key", ikx);
} catch (SignatureException sx) {
LOG.error("Bad sig", sx);
} catch (ClassCastException ccx) {
LOG.error("bad cast", ccx);
}
message.setSecureStatus(SecureMessage.FAILED);
callback.handleSecureMessage(message, false);
}
/** Simple runnable to insert into the ProcessingQueue. */
private class Verifier implements Runnable {
private final SecureMessage message;
private final SecureMessageCallback callback;
Verifier(SecureMessage message, SecureMessageCallback callback) {
this.message = message;
this.callback = callback;
}
public void run() {
initializePublicKey();
verifyMessage(message, callback);
}
}
}