From fef591412ec4c5c18bb9f7fb52507fe499acce51 Mon Sep 17 00:00:00 2001 From: zzz Date: Sat, 28 Jun 2014 14:14:39 +0000 Subject: [PATCH] SAM: Cherrypick from patch in ticket #1318: - Add SIGNATURE_TYPE support to GENERATE and CREATE - Don't NPE checking dest+privkeys - Simplify HELLO checking - Don't require two params in HELLO message - Make MIN parameter optional too - Version checking fixes - Bump version to 3.1, only visible if requested - Cleanups, javadocs --- .../src/net/i2p/sam/SAMHandlerFactory.java | 59 ++++++++----------- apps/sam/java/src/net/i2p/sam/SAMUtils.java | 28 +++++++-- .../java/src/net/i2p/sam/SAMv1Handler.java | 25 +++++--- .../java/src/net/i2p/sam/SAMv3Handler.java | 22 +++++-- history.txt | 11 ++++ .../src/net/i2p/router/RouterVersion.java | 2 +- 6 files changed, 94 insertions(+), 53 deletions(-) diff --git a/apps/sam/java/src/net/i2p/sam/SAMHandlerFactory.java b/apps/sam/java/src/net/i2p/sam/SAMHandlerFactory.java index 8fb040076..4da579238 100644 --- a/apps/sam/java/src/net/i2p/sam/SAMHandlerFactory.java +++ b/apps/sam/java/src/net/i2p/sam/SAMHandlerFactory.java @@ -24,7 +24,7 @@ import net.i2p.util.VersionComparator; */ class SAMHandlerFactory { - private static final String VERSION = "3.0"; + private static final String VERSION = "3.1"; /** * Return the right SAM handler depending on the protocol version @@ -53,29 +53,22 @@ class SAMHandlerFactory { throw new SAMException("Unexpected error", e); } - // Message format: HELLO VERSION MIN=v1 MAX=v2 - if (tok.countTokens() != 4) { - throw new SAMException("Bad format in HELLO message"); + // Message format: HELLO VERSION [MIN=v1] [MAX=v2] + if (tok.countTokens() < 2) { + throw new SAMException("Must start with HELLO VERSION"); } - if (!tok.nextToken().equals("HELLO")) { - throw new SAMException("Bad domain in HELLO message"); - } - { - String opcode; - if (!(opcode = tok.nextToken()).equals("VERSION")) { - throw new SAMException("Unrecognized HELLO message opcode: '" - + opcode + "'"); - } + if (!tok.nextToken().equals("HELLO") || + !tok.nextToken().equals("VERSION")) { + throw new SAMException("Must start with HELLO VERSION"); } Properties props = SAMUtils.parseParams(tok); - if (props.isEmpty()) { - throw new SAMException("No parameters in HELLO VERSION message"); - } String minVer = props.getProperty("MIN"); if (minVer == null) { - throw new SAMException("Missing MIN parameter in HELLO VERSION message"); + //throw new SAMException("Missing MIN parameter in HELLO VERSION message"); + // MIN optional as of 0.9.14 + minVer = "1"; } String maxVer = props.getProperty("MAX"); @@ -126,18 +119,14 @@ class SAMHandlerFactory { * @return "x.y" the best version we can use, or null on failure */ private static String chooseBestVersion(String minVer, String maxVer) { + if (VersionComparator.comp(VERSION, minVer) >= 0 && + VersionComparator.comp(VERSION, maxVer) <= 0) + return VERSION; // in VersionComparator, "3" < "3.0" so // use comparisons carefully - if (VersionComparator.comp("3.0", minVer) >= 0) { - // Documentation said: - // In order to force protocol version 3.0, the values of $min and $max - // must be "3.0". - int maxcomp = VersionComparator.comp("3", maxVer); - if (maxcomp == 0 || maxVer.equals("3.0")) - return "3.0"; // spoof version - if (maxcomp < 0) - return VERSION; - } + if (VersionComparator.comp("3.0", minVer) >= 0 && + VersionComparator.comp("3", maxVer) <= 0) + return "3.0"; if (VersionComparator.comp("2.0", minVer) >= 0 && VersionComparator.comp("2", maxVer) <= 0) return "2.0"; @@ -147,21 +136,23 @@ class SAMHandlerFactory { return null; } - /* Get the major protocol version from a string */ + /* Get the major protocol version from a string, or -1 */ private static int getMajor(String ver) { - if ( (ver == null) || (ver.indexOf('.') < 0) ) + if (ver == null) return -1; + int dot = ver.indexOf("."); + if (dot == 0) + return -1; + if (dot > 0) + ver = ver.substring(0, dot); try { - String major = ver.substring(0, ver.indexOf(".")); - return Integer.parseInt(major); + return Integer.parseInt(ver); } catch (NumberFormatException e) { return -1; - } catch (ArrayIndexOutOfBoundsException e) { - return -1; } } - /* Get the minor protocol version from a string */ + /* Get the minor protocol version from a string, or -1 */ private static int getMinor(String ver) { if ( (ver == null) || (ver.indexOf('.') < 0) ) return -1; diff --git a/apps/sam/java/src/net/i2p/sam/SAMUtils.java b/apps/sam/java/src/net/i2p/sam/SAMUtils.java index 44beee5b0..15ecb1143 100644 --- a/apps/sam/java/src/net/i2p/sam/SAMUtils.java +++ b/apps/sam/java/src/net/i2p/sam/SAMUtils.java @@ -20,6 +20,7 @@ import net.i2p.I2PException; import net.i2p.client.I2PClient; import net.i2p.client.I2PClientFactory; import net.i2p.client.naming.NamingService; +import net.i2p.crypto.SigType; import net.i2p.data.Base64; import net.i2p.data.DataFormatException; import net.i2p.data.Destination; @@ -37,16 +38,30 @@ class SAMUtils { //private final static Log _log = new Log(SAMUtils.class); /** - * Generate a random destination key + * Generate a random destination key using DSA_SHA1 signature type. + * Caller must close streams. Fails silently. * - * @param priv Stream used to write the private key - * @param pub Stream used to write the public key (may be null) + * @param priv Stream used to write the destination and private keys + * @param pub Stream used to write the destination (may be null) */ public static void genRandomKey(OutputStream priv, OutputStream pub) { + genRandomKey(priv, pub, SigType.DSA_SHA1); + } + + /** + * Generate a random destination key. + * Caller must close streams. Fails silently. + * + * @param priv Stream used to write the destination and private keys + * @param pub Stream used to write the destination (may be null) + * @param sigType what signature type + * @since 0.9.14 + */ + public static void genRandomKey(OutputStream priv, OutputStream pub, SigType sigType) { //_log.debug("Generating random keys..."); try { I2PClient c = I2PClientFactory.createClient(); - Destination d = c.createDestination(priv); + Destination d = c.createDestination(priv, sigType); priv.flush(); if (pub != null) { @@ -85,7 +100,10 @@ class SAMUtils { * @return true if valid */ public static boolean checkPrivateDestination(String dest) { - ByteArrayInputStream destKeyStream = new ByteArrayInputStream(Base64.decode(dest)); + byte[] b = Base64.decode(dest); + if (b == null || b.length < 663) + return false; + ByteArrayInputStream destKeyStream = new ByteArrayInputStream(b); try { Destination d = new Destination(); d.readBytes(destKeyStream); diff --git a/apps/sam/java/src/net/i2p/sam/SAMv1Handler.java b/apps/sam/java/src/net/i2p/sam/SAMv1Handler.java index 0cb5c874c..2ffba654f 100644 --- a/apps/sam/java/src/net/i2p/sam/SAMv1Handler.java +++ b/apps/sam/java/src/net/i2p/sam/SAMv1Handler.java @@ -24,6 +24,7 @@ import java.util.concurrent.atomic.AtomicLong; import net.i2p.I2PException; import net.i2p.client.I2PClient; import net.i2p.client.I2PSessionException; +import net.i2p.crypto.SigType; import net.i2p.data.Base64; import net.i2p.data.DataFormatException; import net.i2p.data.DataHelper; @@ -320,7 +321,7 @@ class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatagramRece } - SAMStreamSession newSAMStreamSession(String destKeystream, String direction, Properties props ) + private SAMStreamSession newSAMStreamSession(String destKeystream, String direction, Properties props ) throws IOException, DataFormatException, SAMException { return new SAMStreamSession(destKeystream, direction, props, this) ; @@ -330,16 +331,23 @@ class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatagramRece protected boolean execDestMessage(String opcode, Properties props) { if (opcode.equals("GENERATE")) { - if (!props.isEmpty()) { - if (_log.shouldLog(Log.DEBUG)) - _log.debug("Properties specified in DEST GENERATE message"); - return false; + String sigTypeStr = props.getProperty("SIGNATURE_TYPE"); + SigType sigType; + if (sigTypeStr != null) { + sigType = SigType.parseSigType(sigTypeStr); + if (sigType == null) { + writeString("DEST REPLY RESULT=I2P_ERROR MESSAGE=\"SIGNATURE_TYPE " + + sigTypeStr + " unsupported\"\n"); + return false; + } + } else { + sigType = SigType.DSA_SHA1; } - ByteArrayOutputStream priv = new ByteArrayOutputStream(); - ByteArrayOutputStream pub = new ByteArrayOutputStream(); + ByteArrayOutputStream priv = new ByteArrayOutputStream(663); + ByteArrayOutputStream pub = new ByteArrayOutputStream(387); - SAMUtils.genRandomKey(priv, pub); + SAMUtils.genRandomKey(priv, pub, sigType); return writeString("DEST REPLY" + " PUB=" + Base64.encode(pub.toByteArray()) @@ -347,6 +355,7 @@ class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatagramRece + Base64.encode(priv.toByteArray()) + "\n"); } else { + writeString("DEST REPLY RESULT=I2P_ERROR MESSAGE=\"DEST GENERATE required\""); if (_log.shouldLog(Log.DEBUG)) _log.debug("Unrecognized DEST message opcode: \"" + opcode + "\""); return false; diff --git a/apps/sam/java/src/net/i2p/sam/SAMv3Handler.java b/apps/sam/java/src/net/i2p/sam/SAMv3Handler.java index 00d95594a..6a5bf7fa3 100644 --- a/apps/sam/java/src/net/i2p/sam/SAMv3Handler.java +++ b/apps/sam/java/src/net/i2p/sam/SAMv3Handler.java @@ -28,6 +28,7 @@ import java.util.StringTokenizer; import net.i2p.I2PException; import net.i2p.client.I2PClient; import net.i2p.client.I2PSessionException; +import net.i2p.crypto.SigType; import net.i2p.data.Base64; import net.i2p.data.DataFormatException; import net.i2p.data.DataHelper; @@ -487,21 +488,32 @@ class SAMv3Handler extends SAMv1Handler } props.remove("DESTINATION"); - if (dest.equals("TRANSIENT")) { if (_log.shouldLog(Log.DEBUG)) _log.debug("TRANSIENT destination requested"); - ByteArrayOutputStream priv = new ByteArrayOutputStream(640); - SAMUtils.genRandomKey(priv, null); + String sigTypeStr = props.getProperty("SIGNATURE_TYPE"); + SigType sigType; + if (sigTypeStr != null) { + sigType = SigType.parseSigType(sigTypeStr); + if (sigType == null) { + return writeString("SESSION STATUS RESULT=I2P_ERROR MESSAGE=\"SIGNATURE_TYPE " + + sigTypeStr + " unsupported\"\n"); + } + props.remove("SIGNATURE_TYPE"); + } else { + sigType = SigType.DSA_SHA1; + } + ByteArrayOutputStream priv = new ByteArrayOutputStream(663); + SAMUtils.genRandomKey(priv, null, sigType); dest = Base64.encode(priv.toByteArray()); } else { if (_log.shouldLog(Log.DEBUG)) _log.debug("Custom destination specified [" + dest + "]"); + if (!SAMUtils.checkPrivateDestination(dest)) + return writeString("SESSION STATUS RESULT=INVALID_KEY\n"); } - if (!SAMUtils.checkPrivateDestination(dest)) - return writeString("SESSION STATUS RESULT=INVALID_KEY\n"); nick = props.getProperty("ID"); if (nick == null) { diff --git a/history.txt b/history.txt index 9cc68a85f..ac6864b4b 100644 --- a/history.txt +++ b/history.txt @@ -1,3 +1,14 @@ +2014-06-28 zzz + * SAM: + - Support SIGNATURE_TYPE, bump to 3.1 (ticket #1318) + - Private key checking fixes (ticket #1318) + - Parameter parsing fixes (ticket #1325) + - Cleanups + +2014-06-24 zzz + * Streaming; Drop the preliminary channel implementations, + as they don't work and can't ever work as designed. + 2014-06-23 zzz * Streaming: - Bundle new socket messages for translation diff --git a/router/java/src/net/i2p/router/RouterVersion.java b/router/java/src/net/i2p/router/RouterVersion.java index f2522cd4e..7ca1a3ec2 100644 --- a/router/java/src/net/i2p/router/RouterVersion.java +++ b/router/java/src/net/i2p/router/RouterVersion.java @@ -18,7 +18,7 @@ public class RouterVersion { /** deprecated */ public final static String ID = "Monotone"; public final static String VERSION = CoreVersion.VERSION; - public final static long BUILD = 9; + public final static long BUILD = 10; /** for example "-test" */ public final static String EXTRA = "";