Author: dbkr
Date: 2006-07-18 16:37:19 +0000 (Tue, 18 Jul 2006)
New Revision: 9652

Added:
   trunk/apps/Freemail/src/freemail/InboundContact.java
Modified:
   trunk/apps/Freemail/docs/spec/spec.tex
   trunk/apps/Freemail/src/freemail/RTSFetcher.java
   trunk/apps/Freemail/src/freemail/RTSLog.java
   trunk/apps/Freemail/src/freemail/SingleAccountWatcher.java
Log:
Finish handling of RTS messages


Modified: trunk/apps/Freemail/docs/spec/spec.tex
===================================================================
--- trunk/apps/Freemail/docs/spec/spec.tex      2006-07-18 10:43:32 UTC (rev 
9651)
+++ trunk/apps/Freemail/docs/spec/spec.tex      2006-07-18 16:37:19 UTC (rev 
9652)
@@ -20,11 +20,10 @@

 A Freemail address comprises an arbitrary text string, followed by an '@' 
character. Following this is the mailsite address encoded in base 32 - that is, 
a valid Freenet uri that points to the mailsite. The URI must be base 32 
encoded in order to make the address case insensitive to maintain compatability 
with traditional email clients. The string '.freenet' is appended to the whole 
address. An example Freemail address follows:

-bob at 
KVJUWQCMJ53UI6KEGJKFI2JQIZZFKY3XG\-4YE4QRSMUYWYV2PLJ4U27TLG\-R4DI3TLNZXW6QTSJIYCYNKS\-OR4XK23MGZDS2UDRKVCTIT\-BXJJWDQYKIOFM\-WC33VONTFOZTQMJZTS5LCO\-NEWGY3WGQWECUKBIJA\-UCRJPNVQWS3DTNF\-2GKLZNGEXQ.freemail
+bob at 
JRHXORDZIQZFIVDJ\-GBDHEVLDO43TATSCGJST\-C3CXJ5NHSTL6NM2HQ\-NDONNXG632COJFD\-ALBVKJ2HS5LLNQ3E\-OLKQOFKUKNCMG5F\-GYODBJBYVSYLPOVZ\-WMV3GOBRHGOLVMJ\-ZUSY3DOY2CY\-QKRIFBECQKF.freemail

+The base 32 encoded mailsite in this case is: 
LOwDyD2TTi0FrUcw70N\-B2e1lWOZyM~k4x4n\-knooBrJ0,5Rtyu\-kl6G-PqUE4L7\-Jl8aHqYaous\-fWfpbs9ubsI\-ccv4,AQABAAE

-The base 32 encoded mailsite in this case is: USK at 
LOwDyD2TTi0FrUcw70N\-B2e1lWOZyM~k4x4n\-knooBrJ0,5Rtyu\-kl6G-PqUE4L7\-Jl8aHqYaous\-fWfpbs9ubsI\-ccv4,AQABAAE/mailsite/-1/
-
 (this is liable to change to make the addresses shorter)

 Once the mailsite address has been obtained from the Freemail address, the 
string 'mailpage' is appended to obtain the URI for the mailpage. This mailpage 
contains all information required to contact the owner. The format of a 
mailpage is a 'Props File', which is used repeatedly in Freemail as a trivial 
format for storing short pieces of information. See section \ref{PropsFile}.
@@ -47,7 +46,7 @@
 \item messagetype - This should be 'rts', to indicate that this message is an 
RTS.
 \item to - The Freenet URI that appears encoded in Bob's Freemail address. 
This is necessary in order to prevent surreptitious forwarding to support the 
enryption explained later.
 \item mailsite - Alice's mailsite URI
-\item ctsssk - A randomly generated KSK that Bob should insert to once he has 
recieved Alice's RTS message in order to acknoweldge that he is ready to 
recieve messages. This should be randomly generated and un-guessable so that 
only Bob knows which key to insert to.
+\item ctsksk - A randomly generated KSK that Bob should insert to once he has 
recieved Alice's RTS message in order to acknoweldge that he is ready to 
recieve messages. This should be randomly generated and un-guessable so that 
only Bob knows which key to insert to.
 \end{itemize}

 Following the last data item, there are two carriage-return-line-feeds, 
followed by Alice's signature. This is the SHA-256 hash of the message RSA 
encrypted with Alice's private key, included as raw bytes. The resulting 
message is then RSA encrypted with Bob's public key. If the resulting message 
is longer than a single RSA block, the message is encoded in chunks equal to 
the maximum block size and the ciphertext blocks are concatenated to form the 
final message.
@@ -63,14 +62,10 @@
 \subsection{CTS Messages}
 When Bob recieves an RTS message from Alice, he decrypts the message using his 
RSA private key. He then retrives the mailsite advertised in the RTS message. 
Having done this, he reads the signature on the end and decrypts the signature 
with the public key he just retrieved from the mailsite. He then calculates a 
SHA-256 checksum of the message and checks that his checksum is identical to 
the one he has decrypted. If it is not, he must discard the message. This 
ensures that the message is really from Alice. He must then read the 'to' field 
and ensure that its value is identical to his mailsite URI. If it is not, he 
must discard the message. This ensures that he is the intended recipient of the 
message.

-Bob then records the value of the 'commssk' key so that he can poll this SSK 
for messages periodicaly. Before doing so, he creates another propfile with the 
following values:
+Bob then records the value of the 'commssk' key so that he can poll this SSK 
for messages periodicaly.

-\begin{itemize}
-\item messagetype - This should be 'cts' to indicate that this is a 
clear-to-send message
-\end{itemize}
+Before doing so, Bob inserts some data to the value of the 'ctsksk' key in the 
RTS message. The data he inserts is irrelevant - the presence of the key is 
sufficient to prove to Alice that he has received the message. This completes 
Bob's part of the channel setup procedure.

-Bob inserts this file to the value of the 'ctsssk' key in the RTS message. 
This completes Bob's part of the channel setup procedure.
-
 This message contains no valuable information and so does not need to be 
encrypted. It also does not need to be signed since only Alice and Bob know the 
KSK to which it must be inserted, so Alice knows that Bob must have inserted 
the message. The KSK that Bob inserts this message to tells Alice what RTS it 
relates to if there is any ambiguity.

 Alice should check periodically for the insertion of this CTS message. If it 
does not arrive, Alice should re-send the RTS message with a different 
'ctsssk', in order that she can be certain which RRS message any given CTS 
message corresponds to. All other field should be the same. The client may try 
several times before declaring the message undeliverable.

Added: trunk/apps/Freemail/src/freemail/InboundContact.java
===================================================================
--- trunk/apps/Freemail/src/freemail/InboundContact.java        2006-07-18 
10:43:32 UTC (rev 9651)
+++ trunk/apps/Freemail/src/freemail/InboundContact.java        2006-07-18 
16:37:19 UTC (rev 9652)
@@ -0,0 +1,30 @@
+package freemail;
+
+import java.io.File;
+
+import freemail.FreenetURI;
+import freemail.utils.PropsFile;
+
+public class InboundContact {
+       private static final String IBCT_PROPSFILE = "props";
+       private File ibct_dir;
+       private PropsFile ibct_props;
+       
+       public InboundContact(File contact_dir, FreenetURI mailsite) {
+               this(contact_dir, mailsite.getKeyBody());
+       }
+
+       private InboundContact(File contact_dir, String keybody) {
+               this.ibct_dir = new File(contact_dir, keybody);
+               
+               if (!this.ibct_dir.exists()) {
+                       this.ibct_dir.mkdir();
+               }
+               
+               this.ibct_props = new PropsFile(new File(this.ibct_dir, 
IBCT_PROPSFILE));
+       }
+       
+       public void setProp(String key, String val) {
+               this.ibct_props.put(key, val);
+       }
+}

Modified: trunk/apps/Freemail/src/freemail/RTSFetcher.java
===================================================================
--- trunk/apps/Freemail/src/freemail/RTSFetcher.java    2006-07-18 10:43:32 UTC 
(rev 9651)
+++ trunk/apps/Freemail/src/freemail/RTSFetcher.java    2006-07-18 16:37:19 UTC 
(rev 9652)
@@ -20,6 +20,8 @@
 import java.util.TimeZone;
 import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
+import java.math.BigInteger;
+import java.net.MalformedURLException;

 import org.bouncycastle.util.encoders.Hex;
 import org.bouncycastle.crypto.params.RSAKeyParameters;
@@ -34,10 +36,12 @@
        private File contact_dir;
        private final SimpleDateFormat sdf;
        private static final int POLL_AHEAD = 3;
-       private static int PASSES_PER_DAY = 3;
-       private static int MAX_DAYS_BACK = 30;
-       private static String LOGFILE = "rtslog";
-       private static int RTS_MAX_SIZE = 2 * 1024 * 1024;
+       private static final int PASSES_PER_DAY = 3;
+       private static final int MAX_DAYS_BACK = 30;
+       private static final String LOGFILE = "rtslog";
+       private static final int RTS_MAX_SIZE = 2 * 1024 * 1024;
+       private static final String RTS_UNPROC_PREFIX = "unprocessed_rts";
+       private static final int RTS_MAX_ATTEMPTS = 15;
        private File accdir;
        private PropsFile accprops;

@@ -49,8 +53,43 @@
                this.accprops = AccountManager.getAccountFile(this.accdir);
        }

-       public void fetch() {
+       public void poll() {
+               this.fetch();
+               this.handle_unprocessed();
+       }
+       
+       private void handle_unprocessed() {
+               File[] files = this.contact_dir.listFiles();
+               
                int i;
+               for (i = 0; i < files.length; i++) {
+                       if (files[i].getName().startsWith(RTS_UNPROC_PREFIX)) {
+                               if (this.handle_rts(files[i])) {
+                                       files[i].delete();
+                               } else {
+                                       String[] parts = 
files[i].getName().split(":", 2);
+                                       
+                                       int tries;
+                                       if (parts.length < 2) {
+                                               tries = 0;
+                                       } else {
+                                               tries = 
Integer.parseInt(parts[1]);
+                                       }
+                                       tries++;
+                                       if (tries > RTS_MAX_ATTEMPTS) {
+                                               System.out.println("Maximum 
attempts at handling RTS reached - deleting RTS");
+                                               files[i].delete();
+                                       } else {
+                                               File newname = new 
File(this.contact_dir, RTS_UNPROC_PREFIX + ":" + tries);
+                                               files[i].renameTo(newname);
+                                       }
+                               }
+                       }
+               }
+       }
+       
+       private void fetch() {
+               int i;
                RTSLog log = new RTSLog(new File(this.contact_dir, LOGFILE));
                for (i = 1 - MAX_DAYS_BACK; i <= 0; i++) {
                        String datestr = 
DateStringFactory.getOffsetKeyString(i);
@@ -88,11 +127,14 @@

                        if (result != null) {
                                System.out.println(keybase+i+": got RTS!");
-                               // if we didn't successfully handle (or fatally 
fail to handle) the RTS message, don't increment the id, so we'll get it again 
in a bit
-                               if (this.handle_rts(result)) {
+                               
+                               File rts_dest = new File(this.contact_dir, 
RTS_UNPROC_PREFIX + "-" + log.getAndIncUnprocNextId()+":0");
+                               
+                               // stick this message in the RTS 'inbox'
+                               if (result.renameTo(rts_dest)) {
+                                       // provided that worked, we can move on 
to the next RTS message
                                        log.incNextId(date);
                                }
-                               result.delete();
                        } else {
                                System.out.println(keybase+i+": no RTS.");
                        }
@@ -122,8 +164,8 @@
                        return true;
                }

-               File rtsfile;
-               byte[] sig;
+               File rtsfile = null;
+               byte[] their_encrypted_sig;
                int messagebytes = 0;
                try {
                        rtsfile = File.createTempFile("rtstmp", "tmp", 
Freemail.getTempDir());
@@ -148,20 +190,22 @@
                                // that's not right, we shouldn't have reached 
the end of the file, just the blank line before the signature

                                System.out.println("Couldn't find signature on 
RTS message - ignoring!");
+                               rtsfile.delete();
                                return true;
                        }

-                       sig = new byte[bis.available()];
+                       their_encrypted_sig = new byte[bis.available()];

                        int read = 0;
                        while (true) {
-                               read = bis.read(sig, 0, bis.available());
+                               read = bis.read(their_encrypted_sig, 0, 
bis.available());
                                if (read == 0) break;
                        }
                        bis.close();
                } catch (IOException ioe) {
                        System.out.println("IO error whilst handling RTS 
message. "+ioe.getMessage());
                        ioe.printStackTrace();
+                       if (rtsfile != null) rtsfile.delete();
                        return false;
                }

@@ -171,6 +215,7 @@
                        validate_rts(rtsprops);
                } catch (Exception e) {
                        System.out.println("RTS message does not contain vital 
information: "+e.getMessage()+" - discarding");
+                       rtsfile.delete();
                        return true;
                }

@@ -182,22 +227,99 @@
                        md = MessageDigest.getInstance("MD5");
                } catch (NoSuchAlgorithmException alge) {
                        System.out.println("No MD5 implementation available - 
sorry, Freemail cannot work!");
+                       rtsfile.delete();
                        return false;
                }
                md.update(plaintext, 0, messagebytes);
                byte[] our_hash = md.digest();

                HighLevelFCPClient fcpcli = new HighLevelFCPClient();
-               fcpcli.fetch(their_mailsite);

-               // TODO: finish.
+               File msfile = fcpcli.fetch(their_mailsite);
+               if (msfile == null) {
+                       // oh well, try again in a bit
+                       return false;
+               }

-               // verify the message is for us
+               PropsFile mailsite = new PropsFile(msfile);
+               String their_exponent = mailsite.get("asymkey.pubexponent");
+               String their_modulus = mailsite.get("asymkey.modulus");

+               if (their_exponent == null || their_modulus == null) {
+                       System.out.println("Mailsite fetched successfully but 
missing vital information! Discarding this RTS.");
+                       msfile.delete();
+                       rtsfile.delete();
+                       return true;
+               }
+               
+               RSAKeyParameters their_pubkey = new RSAKeyParameters(false, new 
BigInteger(their_modulus, 10), new BigInteger(their_exponent, 10));
+               AsymmetricBlockCipher deccipher = new RSAEngine();
+               deccipher.init(false, their_pubkey);
+               
+               byte[] their_hash;
+               try {
+                       their_hash = 
deccipher.processBlock(their_encrypted_sig, 0, their_encrypted_sig.length);
+               } catch (InvalidCipherTextException icte) {
+                       System.out.println("It was not possible to decrypt the 
signature of this RTS message. Discarding the RTS message.");
+                       msfile.delete();
+                       rtsfile.delete();
+                       return true;
+               }
+               
+               // finally we can now check that our hash and their hash
+               // match!
+               if (their_hash.length != our_hash.length) {
+                       System.out.println("The signature of the RTS message is 
not valid. Discarding the RTS message.");
+                       msfile.delete();
+                       rtsfile.delete();
+                       return true;
+               }
+               int i;
+               for (i = 0; i < their_hash.length; i++) {
+                       if (their_hash[i] != our_hash[i]) {
+                               System.out.println("The signature of the RTS 
message is not valid. Discarding the RTS message.");
+                               msfile.delete();
+                               rtsfile.delete();
+                               return true;
+                       }
+               }
+               // the signature is valid! Hooray!
+               // Now verify the message is for us
+               String our_mailsite_keybody;
+               try {
+                       our_mailsite_keybody = new 
FreenetURI(this.accprops.get("mailsite.pubkey")).getKeyBody();
+               } catch (MalformedURLException mfue) {
+                       System.out.println("Local mailsite URI is invalid! 
Corrupt account file?");
+                       msfile.delete();
+                       rtsfile.delete();
+                       return false;
+               }
+               if (!rtsprops.get("to").equals(our_mailsite_keybody)) {
+                       System.out.println("Recieved an RTS message that was 
not intended for the recipient. Discarding.");
+                       msfile.delete();
+                       rtsfile.delete();
+                       return true;
+               }
+               
                // create the inbound contact
+               FreenetURI their_mailsite_furi;
+               try {
+                       their_mailsite_furi = new FreenetURI(their_mailsite);
+               } catch (MalformedURLException mfue) {
+                       System.out.println("Mailsite in the RTS message is not 
a valid Freenet URI. Discarding RTS message.");
+                       msfile.delete();
+                       rtsfile.delete();
+                       return true;
+               }
+               InboundContact ibct = new InboundContact(this.contact_dir, 
their_mailsite_furi);

-               // move the props file to the right place
+               ibct.setProp("commssk", rtsprops.get("commssk"));
+               ibct.setProp("ackssk", rtsprops.get("ackssk"));
+               ibct.setProp("ctsksk", rtsprops.get("ctsksk"));

+               msfile.delete();
+               rtsfile.delete();
+               
                return true;
        }

@@ -229,7 +351,7 @@
                if (rts.get("commssk") == null) {
                        missing.append("commssk");
                }
-               if (rts.get("ackssk") == null) {
+               if (rts.get("ackksk") == null) {
                        missing.append("ackssk");
                }
                if (rts.get("messagetype") == null) {
@@ -241,7 +363,7 @@
                if (rts.get("mailsite") == null) {
                        missing.append("mailsite");
                }
-               if (rts.get("ctsssk") == null) {
+               if (rts.get("ctsksk") == null) {
                        missing.append("ctsssk");
                }


Modified: trunk/apps/Freemail/src/freemail/RTSLog.java
===================================================================
--- trunk/apps/Freemail/src/freemail/RTSLog.java        2006-07-18 10:43:32 UTC 
(rev 9651)
+++ trunk/apps/Freemail/src/freemail/RTSLog.java        2006-07-18 16:37:19 UTC 
(rev 9652)
@@ -12,6 +12,7 @@
        PropsFile logfile;
        private static String NEXTID = "nextid-";
        private static String PASSES = "passes-";
+       private static String UNPROC_NEXTID = "unproc-nextid";

        public RTSLog(File f) {
                this.logfile = new PropsFile(f);
@@ -27,7 +28,10 @@
        }

        public void incPasses(String day) {
-               this.logfile.put(PASSES+day, 
Integer.toString(this.getPasses(PASSES+day) + 1));
+               int passes = this.getPasses(day);
+               passes++;
+               
+               this.logfile.put(PASSES+day, Integer.toString(passes));
        }

        public void pruneBefore(Date keepafter) {
@@ -68,4 +72,18 @@
        public void incNextId(String day) {
                this.logfile.put(NEXTID+day, 
Integer.toString(this.getNextId(day) + 1));
        }
+       
+       public int getAndIncUnprocNextId() {
+               String nid = this.logfile.get(UNPROC_NEXTID);
+               int retval;
+               if (nid == null) {
+                       retval = 1;
+               } else {
+                       retval = Integer.parseInt(nid);
+               }
+               
+               this.logfile.put(UNPROC_NEXTID, Integer.toString(retval + 1));
+               
+               return retval;
+       }
 }

Modified: trunk/apps/Freemail/src/freemail/SingleAccountWatcher.java
===================================================================
--- trunk/apps/Freemail/src/freemail/SingleAccountWatcher.java  2006-07-18 
10:43:32 UTC (rev 9651)
+++ trunk/apps/Freemail/src/freemail/SingleAccountWatcher.java  2006-07-18 
16:37:19 UTC (rev 9652)
@@ -49,7 +49,7 @@
                                nf.fetch();
                        }

-                       this.rtsf.fetch();
+                       this.rtsf.poll();

                        //mf.fetch_from_all();



Reply via email to