// Copyright 2000-2008 the Contributors, as shown in the revision logs. // Licensed under the Apache Public Source License 2.0 ("the License"). // You may not use this file except in compliance with the License. package org.ibex.mail; import org.ibex.crypto.*; import org.ibex.js.*; import org.ibex.util.*; import org.ibex.mail.protocol.*; import java.util.*; import java.net.*; import java.io.*; // http://cr.yp.to/proto/verp.txt public class VERP { private static final Random random = new Random(); public static Address verpSign(Address a, byte[] secret) { return verpSign(a, secret, System.currentTimeMillis()); } // format: // VERP-----@domain.com // = 64-bit hexadecimal integer // = 16-bit hexadecimal integer // = original username (ie local-part) // = base64(sha1(VERP----)) /** converts an Address into a VERP-signed address with signature time "when" and extra payload "extra" */ public static Address verpSign(Address a, byte[] secret, long when) { if (a.user==null || "".equals(a.user)) { Log.warn(VERP.class, "note: not VERP-signing the null address: "+a); return a; } int salt = Math.abs(random.nextInt()) & 0xffff; StringBuffer ret = new StringBuffer(); ret.append("VERP--"); ret.append(Long.toString(when, 16)); ret.append('-'); ret.append(Integer.toString(salt, 16)); ret.append('-'); byte[] b = (ret.toString()+a.user).getBytes(); SHA1 sha1 = new SHA1(); sha1.update(b, 0, b.length); sha1.update(secret, 0, secret.length); b = new byte[sha1.getDigestSize()]; sha1.doFinal(b, 0); ret.append(new String(Encode.toBase64(b))); ret.append('-'); ret.append(a.user); // FIXME: encode a.host in here return new Address(ret.toString(), SMTP.localHostIsMXFor, a.description); } // FIXME: return unverpified address /** returns the field passed as "extra" when signing, or null if signature invalid */ public static String verpVerify(Address a, byte[] secret, long noEarlierThan) { String s = a.user; int i = 0; i = s.indexOf("--", i); if (i==-1) return null; i += 2; int first = i; i = s.indexOf('-', i); if (i==-1) return null; i++; int second = i; i = s.indexOf('-', i); if (i==-1) return null; i++; int third = i; i = s.indexOf('-', i); if (i==-1) return null; i++; int fourth = i; String verify = s.substring(0,third)+s.substring(fourth); Log.error("VERP", "verify=\""+verify+"\""); byte[] b = verify.getBytes(); SHA1 sha1 = new SHA1(); sha1.update(b, 0, b.length); sha1.update(secret, 0, secret.length); b = new byte[sha1.getDigestSize()]; sha1.doFinal(b, 0); if (!new String(Encode.toBase64(b)).equals(s.substring(third,fourth-1))) { Log.error("VERP", "decrypt failed\n"+new String(Encode.toBase64(b))+"\n"+s.substring(third,fourth-1)); return null; } // FIXME: check the host on the address long when = Long.parseLong(s.substring(first, second-1), 16); Log.error("VERP", "when="+when); if (when < noEarlierThan) return null; return s.substring(fourth); } }