add org.ibex.mail.VERP
[org.ibex.mail.git] / src / org / ibex / mail / VERP.java
1 // Copyright 2000-2008 the Contributors, as shown in the revision logs.
2 // Licensed under the Apache Public Source License 2.0 ("the License").
3 // You may not use this file except in compliance with the License.
4
5 package org.ibex.mail;
6 import org.ibex.crypto.*;
7 import org.ibex.js.*;
8 import org.ibex.util.*;
9 import org.ibex.mail.protocol.*;
10 import java.util.*;
11 import java.net.*;
12 import java.io.*;
13
14 // http://cr.yp.to/proto/verp.txt
15 public class VERP {
16
17     private static final Random random = new Random();
18
19     public static Address verpSign(Address a, byte[] secret) {
20         return verpSign(a, secret, System.currentTimeMillis());
21     }
22
23     // format:
24     //   VERP--<when>-<salt>-<sig>-<user>@domain.com
25     //     <when> = 64-bit hexadecimal integer
26     //     <salt> = 16-bit hexadecimal integer
27     //     <user> = original username (ie local-part)
28     //     <sig>  = base64(sha1(VERP--<when>-<salt>-<user><secret>))
29     /** converts an Address into a VERP-signed address with signature time "when" and extra payload "extra" */
30     public static Address verpSign(Address a, byte[] secret, long when) {
31         if (a.user==null || "".equals(a.user)) {
32             Log.warn(VERP.class, "note: not VERP-signing the null address: "+a);
33             return a;
34         }
35
36         int salt = Math.abs(random.nextInt()) & 0xffff;
37
38         StringBuffer ret = new StringBuffer();
39         ret.append("VERP--");
40         ret.append(Long.toString(when, 16));
41         ret.append('-');
42         ret.append(Integer.toString(salt, 16));
43         ret.append('-');
44
45         byte[] b = (ret.toString()+a.user).getBytes();
46         SHA1 sha1 = new SHA1();
47         sha1.update(b, 0, b.length);
48         sha1.update(secret, 0, secret.length);
49         b = new byte[sha1.getDigestSize()];
50         sha1.doFinal(b, 0);
51
52         ret.append(new String(Encode.toBase64(b)));
53         ret.append('-');
54         ret.append(a.user);
55         // FIXME: encode a.host in here
56         return new Address(ret.toString(), SMTP.localHostIsMXFor, a.description);
57     }
58
59     // FIXME: return unverpified address
60     /** returns the field passed as "extra" when signing, or null if signature invalid */
61     public static String verpVerify(Address a, byte[] secret, long noEarlierThan) {
62         String s = a.user;
63         int i = 0;
64         i = s.indexOf("--", i); if (i==-1) return null; i += 2;
65         int first = i;
66         i = s.indexOf('-', i);  if (i==-1) return null; i++;
67         int second = i;
68         i = s.indexOf('-', i);  if (i==-1) return null; i++;
69         int third = i;
70         i = s.indexOf('-', i);  if (i==-1) return null; i++;
71         int fourth = i;
72         String verify = s.substring(0,third)+s.substring(fourth);
73         Log.error("VERP", "verify=\""+verify+"\"");
74
75         byte[] b = verify.getBytes();
76         SHA1 sha1 = new SHA1();
77         sha1.update(b, 0, b.length);
78         sha1.update(secret, 0, secret.length);
79         b = new byte[sha1.getDigestSize()];
80         sha1.doFinal(b, 0);
81         if (!new String(Encode.toBase64(b)).equals(s.substring(third,fourth-1))) {
82             Log.error("VERP", "decrypt failed\n"+new String(Encode.toBase64(b))+"\n"+s.substring(third,fourth-1));
83             return null;
84         }
85         // FIXME: check the host on the address
86         long when = Long.parseLong(s.substring(first, second-1), 16);
87         Log.error("VERP", "when="+when);
88         if (when < noEarlierThan) return null;
89         return s.substring(fourth);
90     }
91
92 }