Author: nextgens Date: 2007-04-14 15:49:50 +0000 (Sat, 14 Apr 2007) New Revision: 12693
Modified: trunk/freenet/src/freenet/node/fcp/ClientPut.java trunk/freenet/src/freenet/node/fcp/ClientPutBase.java trunk/freenet/src/freenet/node/fcp/ClientPutMessage.java Log: Some experimental untested code: FileHash on ClientPuts Modified: trunk/freenet/src/freenet/node/fcp/ClientPut.java =================================================================== --- trunk/freenet/src/freenet/node/fcp/ClientPut.java 2007-04-14 15:47:02 UTC (rev 12692) +++ trunk/freenet/src/freenet/node/fcp/ClientPut.java 2007-04-14 15:49:50 UTC (rev 12693) @@ -3,9 +3,11 @@ * http://www.gnu.org/ for further details of the GPL. */ package freenet.node.fcp; +import java.io.BufferedInputStream; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; +import java.security.MessageDigest; import freenet.client.ClientMetadata; import freenet.client.DefaultMIMETypes; @@ -16,7 +18,10 @@ import freenet.client.MetadataUnresolvedException; import freenet.client.async.ClientGetter; import freenet.client.async.ClientPutter; +import freenet.crypt.SHA256; import freenet.keys.FreenetURI; +import freenet.support.Base64; +import freenet.support.IllegalBase64Exception; import freenet.support.Logger; import freenet.support.SimpleFieldSet; import freenet.support.SimpleReadOnlyArrayBucket; @@ -25,6 +30,10 @@ import freenet.support.io.FileBucket; import freenet.support.io.SerializableToFieldSetBucketUtil; +/** + * + * TODO: move hash stuffs into ClientPutBase ... and enforce hash verification at a lower level + */ public class ClientPut extends ClientPutBase { final ClientPutter putter; @@ -41,6 +50,10 @@ private final String targetFilename; private boolean logMINOR; + // FIXME: should be in ClientPutBase ... but we don't want to break insert resuming, do we ? + protected final String salt; + protected final byte[] saltedHash; + /** * Creates a new persistent insert. * @@ -94,6 +107,10 @@ if(!(origFilename.exists() && origFilename.canRead())) throw new FileNotFoundException(); } + // We don't want to break insert resuming: don't enforce it + this.salt = null; + this.saltedHash = null; + logMINOR = Logger.shouldLog(Logger.MINOR, this); this.targetFilename = targetFilename; this.uploadFrom = uploadFromType; @@ -144,12 +161,25 @@ super(message.uri, message.identifier, message.verbosity, handler, message.priorityClass, message.persistenceType, message.clientToken, message.global, message.getCHKOnly, message.dontCompress, message.maxRetries, message.earlyEncode); + if((message.fileHash != null) && (message.fileHash.length() > 0)) { + try { + this.salt = handler.getConnectionIdentifier() + message.clientToken; + this.saltedHash = Base64.decode(message.fileHash); + } catch (IllegalBase64Exception e) { + throw new MessageInvalidException(ProtocolErrorMessage.INVALID_FIELD, "Can't base64 decode " + ClientPutBase.FILE_HASH, identifier, global); + } + } else { + this.salt = null; + this.saltedHash = null; + } + if(message.uploadFromType == ClientPutMessage.UPLOAD_FROM_DISK) { if(!handler.server.core.allowUploadFrom(message.origFilename)) throw new MessageInvalidException(ProtocolErrorMessage.ACCESS_DENIED, "Not allowed to upload from "+message.origFilename, identifier, global); else if(!handler.allowDDAFrom(message.origFilename, false)) - throw new MessageInvalidException(ProtocolErrorMessage.DIRECT_DISK_ACCESS_DENIED, "Not allowed to upload from "+message.origFilename+". Have you done a testDDA previously ?", identifier, global); + throw new MessageInvalidException(ProtocolErrorMessage.DIRECT_DISK_ACCESS_DENIED, "Not allowed to upload from "+message.origFilename+". Have you done a testDDA previously ?", identifier, global); } + this.targetFilename = message.targetFilename; logMINOR = Logger.shouldLog(Logger.MINOR, this); this.uploadFrom = message.uploadFromType; @@ -240,12 +270,34 @@ targetFilename = fs.get("TargetFilename"); + this.salt = fs.get(ClientPutBase.SALT); + String hash = fs.get(ClientPutBase.FILE_HASH); + this.saltedHash = (hash == null ? null : hash.getBytes("UTF-8")); + if(uploadFrom == ClientPutMessage.UPLOAD_FROM_DISK) { origFilename = new File(fs.get("Filename")); if(logMINOR) Logger.minor(this, "Uploading from disk: "+origFilename+" for "+this); data = new FileBucket(origFilename, true, false, false, false, false); targetURI = null; + + if(salt != null) { + if (logMINOR) Logger.minor(this, "Found a hash : let's verify it"); + MessageDigest sha = SHA256.getMessageDigest(); + sha.reset(); + sha.update(salt.getBytes("UTF-8")); + BufferedInputStream bis = new BufferedInputStream(data.getInputStream()); + byte[] buf = new byte[4096]; + while(bis.read(buf) > 0) + sha.update(buf); + + byte[] foundHash = sha.digest(); + SHA256.returnMessageDigest(sha); + + if(!foundHash.equals(saltedHash)) + throw new PersistenceParseException("The hash doesn't match !"); + } else // should probably be a higher level + Logger.normal(this, "Hash not found!"); } else if(uploadFrom == ClientPutMessage.UPLOAD_FROM_DIRECT) { origFilename = null; if(logMINOR) @@ -355,6 +407,13 @@ if(targetFilename != null) fs.putSingle("TargetFilename", targetFilename); fs.putSingle("EarlyEncode", Boolean.toString(earlyEncode)); + + if(salt != null) { + fs.putSingle(ClientPutBase.SALT, salt); + fs.putSingle(ClientPutBase.FILE_HASH, new String(saltedHash)); + // FIXME: shall it be base64 encoded like on FCP ? + } + return fs; } Modified: trunk/freenet/src/freenet/node/fcp/ClientPutBase.java =================================================================== --- trunk/freenet/src/freenet/node/fcp/ClientPutBase.java 2007-04-14 15:47:02 UTC (rev 12692) +++ trunk/freenet/src/freenet/node/fcp/ClientPutBase.java 2007-04-14 15:49:50 UTC (rev 12693) @@ -48,6 +48,9 @@ /** Whether to force an early generation of the CHK */ protected final boolean earlyEncode; + public final static String SALT = "Salt"; + public final static String FILE_HASH = "FileHash"; + public ClientPutBase(FreenetURI uri, String identifier, int verbosity, FCPConnectionHandler handler, short priorityClass, short persistenceType, String clientToken, boolean global, boolean getCHKOnly, boolean dontCompress, int maxRetries, boolean earlyEncode) { Modified: trunk/freenet/src/freenet/node/fcp/ClientPutMessage.java =================================================================== --- trunk/freenet/src/freenet/node/fcp/ClientPutMessage.java 2007-04-14 15:47:02 UTC (rev 12692) +++ trunk/freenet/src/freenet/node/fcp/ClientPutMessage.java 2007-04-14 15:49:50 UTC (rev 12693) @@ -31,6 +31,7 @@ * or * UploadFrom=disk // upload a file from disk * Filename=/home/toad/something.html + * FileHash=021349568329403123 * Data * * Neither IgnoreDS nor DSOnly make sense for inserts. @@ -49,6 +50,12 @@ final short priorityClass; final short persistenceType; final short uploadFromType; + /** The hash of the file you want the node to deal with. + * it is MANDATORY to do DDA operations and should be computed like that: + * + * Base64Encode(SHA256( Handler.connectionIdentifer + ClientPutMessage.identifier + content)) + */ + final String fileHash; final boolean dontCompress; final String clientToken; final File origFilename; @@ -118,6 +125,8 @@ throw new MessageInvalidException(ProtocolErrorMessage.ERROR_PARSING_NUMBER, "Error parsing PriorityClass field: "+e.getMessage(), identifier, global); } } + // We do *NOT* check that FileHash is valid here for backward compatibility... and to make the override work + this.fileHash = fs.get(ClientPutBase.SALT); String uploadFrom = fs.get("UploadFrom"); if((uploadFrom == null) || uploadFrom.equalsIgnoreCase("direct")) { uploadFromType = UPLOAD_FROM_DIRECT;
