package com.limegroup.gnutella.updates;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.UnsupportedEncodingException;
import java.security.PublicKey;
import com.bitzi.util.Base32;
import com.limegroup.gnutella.ErrorService;
import com.limegroup.gnutella.security.SignatureVerifier;
import com.limegroup.gnutella.util.CommonUtils;
import org.apache.commons.logging.LogFactory;
import org.apache.commons.logging.Log;
/**
* Provides static methods, which accept an InputStream and use the
* LimeWire public key to verify that the contents are authentic.
*/
public class UpdateMessageVerifier {
private static final Log LOG = LogFactory.getLog(UpdateMessageVerifier.class);
private byte[] data;
private byte[] signature;
private byte[] xmlMessage;
private boolean fromDisk;
/**
* @param fromDisk true if the byte are being read from disk, false is the
* bytes are being read from the network
*/
public UpdateMessageVerifier(byte[] fromStream, boolean fromDisk) {
if(fromStream == null)
throw new IllegalArgumentException();
this.data = fromStream;
this.fromDisk = fromDisk;
}
public boolean verifySource() {
//read the input stream and parse it into signature and xmlMessage
boolean parsed = parse();
if(!parsed)
return false;
//get the public key
PublicKey pubKey = null;
FileInputStream fis = null;
ObjectInputStream ois = null;
try {
File file =
new File(CommonUtils.getUserSettingsDir(),"public.key");
fis = new FileInputStream(file);
ois = new ObjectInputStream(fis);
pubKey = (PublicKey)ois.readObject();
} catch(Throwable t) {
LOG.error("Unable to read public key", t);
return false;
} finally {
if(ois != null) {
try {
ois.close();
} catch (IOException e) {
// we can only try to close it...
}
}
if(fis != null) {
try {
fis.close();
} catch (IOException e) {
// we can only try to close it...
}
}
}
SignatureVerifier verifier =
new SignatureVerifier(xmlMessage,signature, pubKey, "DSA");
return verifier.verifySignature();
}
private boolean parse() {
int i;
int j;
i = findPipe(0);
j = findPipe(i+1);
if(i<0 || j<0) //no 2 pipes? this file cannot be the real thing,
return false;
if( (data.length - j) < 10) //xml smaller than 10? no way
return false;
//now i is at the first | delimiter and j is at the second | delimiter
byte[] temp = new byte[i];
System.arraycopy(data,0,temp,0,i);
String base32 = null;
try {
base32 = new String(temp, "UTF-8");
} catch(UnsupportedEncodingException usx) {
ErrorService.error(usx);
}
signature = Base32.decode(base32);
xmlMessage = new byte[data.length-1-j];
System.arraycopy(data,j+1,xmlMessage,0,data.length-1-j);
return true;
}
/**
* @return the index of "|" starting from startIndex, -1 if none found in
* this.data
*/
private int findPipe(int startIndex) {
byte b = (byte)-1;
boolean found = false;
int i = startIndex;
for( ; i < data.length; i++) {
if(data[i] == (byte)124) {
found = true;
break;
}
}
if(found)
return i;
return -1;
}
public byte[] getMessageBytes() throws IllegalStateException {
if(xmlMessage==null)
throw new IllegalStateException();
return xmlMessage;
}
}