From: adam Date: Mon, 22 Jun 2009 00:10:08 +0000 (+0000) Subject: add org.ibex.mail.VERP X-Git-Url: http://git.megacz.com/?p=org.ibex.mail.git;a=commitdiff_plain;h=5de1642861c256fd64b5e82826a9e24dd7c8688e add org.ibex.mail.VERP darcs-hash:20090622001008-5007d-055e271a158b0865faa77c128107d66f867e880e.gz --- diff --git a/src/org/ibex/mail/Script.java b/src/org/ibex/mail/Script.java index 3a829c6..9459c1f 100644 --- a/src/org/ibex/mail/Script.java +++ b/src/org/ibex/mail/Script.java @@ -139,6 +139,8 @@ public class Script extends JS.Obj implements Target { case "mail.clamav.check": return METHOD; case "mail.procmail": /* FEATURE */ return null; case "mail.vacation": /* FEATURE */ return null; + case "mail.verp": return getSub("mail.verp"); + case "mail.verp.check": return METHOD; case "mail.dcc": return getSub("mail.dcc"); case "mail.dcc.check": return METHOD; case "mail.bounce": return METHOD; @@ -252,6 +254,10 @@ public class Script extends JS.Obj implements Target { new Stream(p.getInputStream()).transcribe(ret); return JSU.S(ret.toString()); } + if (name.equals("mail.verp.check")) { + String ret = VERP.verpVerify(Address.parse(JSU.toString(a)), "SECRET".getBytes(), 0); + return ret==null ? null : JSU.S(ret); + } if (name.equals("mail.dcc.check")) { Process p = Runtime.getRuntime().exec(new String[] { "dccproc", "-H" }); ((Message)args[0]).getStream().transcribe(new Stream(p.getOutputStream()), true); diff --git a/src/org/ibex/mail/VERP.java b/src/org/ibex/mail/VERP.java new file mode 100644 index 0000000..19b47db --- /dev/null +++ b/src/org/ibex/mail/VERP.java @@ -0,0 +1,92 @@ +// 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); + } + +}