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 {
|
||||
|
||||
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;
|
||||
|
@@ -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);
|
||||
|
@@ -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;
|
||||
|
@@ -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) {
|
||||
|
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
|
||||
* Streaming:
|
||||
- Bundle new socket messages for translation
|
||||
|
@@ -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 = "";
|
||||
|
Reference in New Issue
Block a user