forked from I2P_Developers/i2p.i2p
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
This commit is contained in:
@@ -24,7 +24,7 @@ import net.i2p.util.VersionComparator;
|
|||||||
*/
|
*/
|
||||||
class SAMHandlerFactory {
|
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
|
* Return the right SAM handler depending on the protocol version
|
||||||
@@ -53,29 +53,22 @@ class SAMHandlerFactory {
|
|||||||
throw new SAMException("Unexpected error", e);
|
throw new SAMException("Unexpected error", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Message format: HELLO VERSION MIN=v1 MAX=v2
|
// Message format: HELLO VERSION [MIN=v1] [MAX=v2]
|
||||||
if (tok.countTokens() != 4) {
|
if (tok.countTokens() < 2) {
|
||||||
throw new SAMException("Bad format in HELLO message");
|
throw new SAMException("Must start with HELLO VERSION");
|
||||||
}
|
}
|
||||||
if (!tok.nextToken().equals("HELLO")) {
|
if (!tok.nextToken().equals("HELLO") ||
|
||||||
throw new SAMException("Bad domain in HELLO message");
|
!tok.nextToken().equals("VERSION")) {
|
||||||
}
|
throw new SAMException("Must start with HELLO VERSION");
|
||||||
{
|
|
||||||
String opcode;
|
|
||||||
if (!(opcode = tok.nextToken()).equals("VERSION")) {
|
|
||||||
throw new SAMException("Unrecognized HELLO message opcode: '"
|
|
||||||
+ opcode + "'");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Properties props = SAMUtils.parseParams(tok);
|
Properties props = SAMUtils.parseParams(tok);
|
||||||
if (props.isEmpty()) {
|
|
||||||
throw new SAMException("No parameters in HELLO VERSION message");
|
|
||||||
}
|
|
||||||
|
|
||||||
String minVer = props.getProperty("MIN");
|
String minVer = props.getProperty("MIN");
|
||||||
if (minVer == null) {
|
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");
|
String maxVer = props.getProperty("MAX");
|
||||||
@@ -126,18 +119,14 @@ class SAMHandlerFactory {
|
|||||||
* @return "x.y" the best version we can use, or null on failure
|
* @return "x.y" the best version we can use, or null on failure
|
||||||
*/
|
*/
|
||||||
private static String chooseBestVersion(String minVer, String maxVer) {
|
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
|
// in VersionComparator, "3" < "3.0" so
|
||||||
// use comparisons carefully
|
// use comparisons carefully
|
||||||
if (VersionComparator.comp("3.0", minVer) >= 0) {
|
if (VersionComparator.comp("3.0", minVer) >= 0 &&
|
||||||
// Documentation said:
|
VersionComparator.comp("3", maxVer) <= 0)
|
||||||
// In order to force protocol version 3.0, the values of $min and $max
|
return "3.0";
|
||||||
// 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("2.0", minVer) >= 0 &&
|
if (VersionComparator.comp("2.0", minVer) >= 0 &&
|
||||||
VersionComparator.comp("2", maxVer) <= 0)
|
VersionComparator.comp("2", maxVer) <= 0)
|
||||||
return "2.0";
|
return "2.0";
|
||||||
@@ -147,21 +136,23 @@ class SAMHandlerFactory {
|
|||||||
return null;
|
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) {
|
private static int getMajor(String ver) {
|
||||||
if ( (ver == null) || (ver.indexOf('.') < 0) )
|
if (ver == null)
|
||||||
return -1;
|
return -1;
|
||||||
|
int dot = ver.indexOf(".");
|
||||||
|
if (dot == 0)
|
||||||
|
return -1;
|
||||||
|
if (dot > 0)
|
||||||
|
ver = ver.substring(0, dot);
|
||||||
try {
|
try {
|
||||||
String major = ver.substring(0, ver.indexOf("."));
|
return Integer.parseInt(ver);
|
||||||
return Integer.parseInt(major);
|
|
||||||
} catch (NumberFormatException e) {
|
} catch (NumberFormatException e) {
|
||||||
return -1;
|
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) {
|
private static int getMinor(String ver) {
|
||||||
if ( (ver == null) || (ver.indexOf('.') < 0) )
|
if ( (ver == null) || (ver.indexOf('.') < 0) )
|
||||||
return -1;
|
return -1;
|
||||||
|
@@ -20,6 +20,7 @@ import net.i2p.I2PException;
|
|||||||
import net.i2p.client.I2PClient;
|
import net.i2p.client.I2PClient;
|
||||||
import net.i2p.client.I2PClientFactory;
|
import net.i2p.client.I2PClientFactory;
|
||||||
import net.i2p.client.naming.NamingService;
|
import net.i2p.client.naming.NamingService;
|
||||||
|
import net.i2p.crypto.SigType;
|
||||||
import net.i2p.data.Base64;
|
import net.i2p.data.Base64;
|
||||||
import net.i2p.data.DataFormatException;
|
import net.i2p.data.DataFormatException;
|
||||||
import net.i2p.data.Destination;
|
import net.i2p.data.Destination;
|
||||||
@@ -37,16 +38,30 @@ class SAMUtils {
|
|||||||
//private final static Log _log = new Log(SAMUtils.class);
|
//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 priv Stream used to write the destination and private keys
|
||||||
* @param pub Stream used to write the public key (may be null)
|
* @param pub Stream used to write the destination (may be null)
|
||||||
*/
|
*/
|
||||||
public static void genRandomKey(OutputStream priv, OutputStream pub) {
|
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...");
|
//_log.debug("Generating random keys...");
|
||||||
try {
|
try {
|
||||||
I2PClient c = I2PClientFactory.createClient();
|
I2PClient c = I2PClientFactory.createClient();
|
||||||
Destination d = c.createDestination(priv);
|
Destination d = c.createDestination(priv, sigType);
|
||||||
priv.flush();
|
priv.flush();
|
||||||
|
|
||||||
if (pub != null) {
|
if (pub != null) {
|
||||||
@@ -85,7 +100,10 @@ class SAMUtils {
|
|||||||
* @return true if valid
|
* @return true if valid
|
||||||
*/
|
*/
|
||||||
public static boolean checkPrivateDestination(String dest) {
|
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 {
|
try {
|
||||||
Destination d = new Destination();
|
Destination d = new Destination();
|
||||||
d.readBytes(destKeyStream);
|
d.readBytes(destKeyStream);
|
||||||
|
@@ -24,6 +24,7 @@ import java.util.concurrent.atomic.AtomicLong;
|
|||||||
import net.i2p.I2PException;
|
import net.i2p.I2PException;
|
||||||
import net.i2p.client.I2PClient;
|
import net.i2p.client.I2PClient;
|
||||||
import net.i2p.client.I2PSessionException;
|
import net.i2p.client.I2PSessionException;
|
||||||
|
import net.i2p.crypto.SigType;
|
||||||
import net.i2p.data.Base64;
|
import net.i2p.data.Base64;
|
||||||
import net.i2p.data.DataFormatException;
|
import net.i2p.data.DataFormatException;
|
||||||
import net.i2p.data.DataHelper;
|
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
|
throws IOException, DataFormatException, SAMException
|
||||||
{
|
{
|
||||||
return new SAMStreamSession(destKeystream, direction, props, this) ;
|
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) {
|
protected boolean execDestMessage(String opcode, Properties props) {
|
||||||
|
|
||||||
if (opcode.equals("GENERATE")) {
|
if (opcode.equals("GENERATE")) {
|
||||||
if (!props.isEmpty()) {
|
String sigTypeStr = props.getProperty("SIGNATURE_TYPE");
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
SigType sigType;
|
||||||
_log.debug("Properties specified in DEST GENERATE message");
|
if (sigTypeStr != null) {
|
||||||
return false;
|
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 priv = new ByteArrayOutputStream(663);
|
||||||
ByteArrayOutputStream pub = new ByteArrayOutputStream();
|
ByteArrayOutputStream pub = new ByteArrayOutputStream(387);
|
||||||
|
|
||||||
SAMUtils.genRandomKey(priv, pub);
|
SAMUtils.genRandomKey(priv, pub, sigType);
|
||||||
return writeString("DEST REPLY"
|
return writeString("DEST REPLY"
|
||||||
+ " PUB="
|
+ " PUB="
|
||||||
+ Base64.encode(pub.toByteArray())
|
+ Base64.encode(pub.toByteArray())
|
||||||
@@ -347,6 +355,7 @@ class SAMv1Handler extends SAMHandler implements SAMRawReceiver, SAMDatagramRece
|
|||||||
+ Base64.encode(priv.toByteArray())
|
+ Base64.encode(priv.toByteArray())
|
||||||
+ "\n");
|
+ "\n");
|
||||||
} else {
|
} else {
|
||||||
|
writeString("DEST REPLY RESULT=I2P_ERROR MESSAGE=\"DEST GENERATE required\"");
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
if (_log.shouldLog(Log.DEBUG))
|
||||||
_log.debug("Unrecognized DEST message opcode: \"" + opcode + "\"");
|
_log.debug("Unrecognized DEST message opcode: \"" + opcode + "\"");
|
||||||
return false;
|
return false;
|
||||||
|
@@ -28,6 +28,7 @@ import java.util.StringTokenizer;
|
|||||||
import net.i2p.I2PException;
|
import net.i2p.I2PException;
|
||||||
import net.i2p.client.I2PClient;
|
import net.i2p.client.I2PClient;
|
||||||
import net.i2p.client.I2PSessionException;
|
import net.i2p.client.I2PSessionException;
|
||||||
|
import net.i2p.crypto.SigType;
|
||||||
import net.i2p.data.Base64;
|
import net.i2p.data.Base64;
|
||||||
import net.i2p.data.DataFormatException;
|
import net.i2p.data.DataFormatException;
|
||||||
import net.i2p.data.DataHelper;
|
import net.i2p.data.DataHelper;
|
||||||
@@ -487,21 +488,32 @@ class SAMv3Handler extends SAMv1Handler
|
|||||||
}
|
}
|
||||||
props.remove("DESTINATION");
|
props.remove("DESTINATION");
|
||||||
|
|
||||||
|
|
||||||
if (dest.equals("TRANSIENT")) {
|
if (dest.equals("TRANSIENT")) {
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
if (_log.shouldLog(Log.DEBUG))
|
||||||
_log.debug("TRANSIENT destination requested");
|
_log.debug("TRANSIENT destination requested");
|
||||||
ByteArrayOutputStream priv = new ByteArrayOutputStream(640);
|
String sigTypeStr = props.getProperty("SIGNATURE_TYPE");
|
||||||
SAMUtils.genRandomKey(priv, null);
|
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());
|
dest = Base64.encode(priv.toByteArray());
|
||||||
} else {
|
} else {
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
if (_log.shouldLog(Log.DEBUG))
|
||||||
_log.debug("Custom destination specified [" + dest + "]");
|
_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");
|
nick = props.getProperty("ID");
|
||||||
if (nick == null) {
|
if (nick == null) {
|
||||||
|
11
history.txt
11
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
|
2014-06-23 zzz
|
||||||
* Streaming:
|
* Streaming:
|
||||||
- Bundle new socket messages for translation
|
- Bundle new socket messages for translation
|
||||||
|
@@ -18,7 +18,7 @@ public class RouterVersion {
|
|||||||
/** deprecated */
|
/** deprecated */
|
||||||
public final static String ID = "Monotone";
|
public final static String ID = "Monotone";
|
||||||
public final static String VERSION = CoreVersion.VERSION;
|
public final static String VERSION = CoreVersion.VERSION;
|
||||||
public final static long BUILD = 9;
|
public final static long BUILD = 10;
|
||||||
|
|
||||||
/** for example "-test" */
|
/** for example "-test" */
|
||||||
public final static String EXTRA = "";
|
public final static String EXTRA = "";
|
||||||
|
Reference in New Issue
Block a user