forked from I2P_Developers/i2p.i2p
propagate from branch 'i2p.i2p.zzz.ecdsa' (head e83bcdc842f5995d310a4295147f9326a993e010)
to branch 'i2p.i2p' (head 4983f716f8740bc7ddfae5561a562a0d42a815ae)
This commit is contained in:
@ -1396,7 +1396,8 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate a new keypair
|
* Generate a new keypair.
|
||||||
|
* Does NOT support non-default sig types.
|
||||||
* Deprecated - only used by CLI
|
* Deprecated - only used by CLI
|
||||||
*
|
*
|
||||||
* Sets the event "genkeysResult" = "ok" or "error" after the generation is complete
|
* Sets the event "genkeysResult" = "ok" or "error" after the generation is complete
|
||||||
@ -1439,7 +1440,8 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate a new keypair
|
* Generate a new keypair.
|
||||||
|
* Does NOT support non-default sig types.
|
||||||
* Deprecated - only used by CLI
|
* Deprecated - only used by CLI
|
||||||
*
|
*
|
||||||
* Sets the event "privateKey" = base64 of the privateKey stream and
|
* Sets the event "privateKey" = base64 of the privateKey stream and
|
||||||
@ -1448,7 +1450,7 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging {
|
|||||||
* @param l logger to receive events and output
|
* @param l logger to receive events and output
|
||||||
*/
|
*/
|
||||||
private static void runGenTextKeys(Logging l) {
|
private static void runGenTextKeys(Logging l) {
|
||||||
ByteArrayOutputStream privkey = new ByteArrayOutputStream(512);
|
ByteArrayOutputStream privkey = new ByteArrayOutputStream(1024);
|
||||||
ByteArrayOutputStream pubkey = new ByteArrayOutputStream(512);
|
ByteArrayOutputStream pubkey = new ByteArrayOutputStream(512);
|
||||||
makeKey(privkey, pubkey, l);
|
makeKey(privkey, pubkey, l);
|
||||||
l.log("Private key: " + Base64.encode(privkey.toByteArray()));
|
l.log("Private key: " + Base64.encode(privkey.toByteArray()));
|
||||||
@ -1694,10 +1696,11 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new destination, storing the destination and its private keys where
|
* Create a new destination, storing the destination and its private keys where
|
||||||
* instructed
|
* instructed.
|
||||||
|
* Does NOT support non-default sig types.
|
||||||
* Deprecated - only used by CLI
|
* Deprecated - only used by CLI
|
||||||
*
|
*
|
||||||
* @param writeTo location to store the private keys
|
* @param writeTo location to store the destination and private keys
|
||||||
* @param pubDest location to store the destination
|
* @param pubDest location to store the destination
|
||||||
* @param l logger to send messages to
|
* @param l logger to send messages to
|
||||||
*/
|
*/
|
||||||
|
@ -7,11 +7,13 @@ import java.util.ArrayList;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
|
|
||||||
import net.i2p.I2PAppContext;
|
import net.i2p.I2PAppContext;
|
||||||
import net.i2p.I2PException;
|
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.I2PSession;
|
import net.i2p.client.I2PSession;
|
||||||
|
import net.i2p.crypto.SigType;
|
||||||
import net.i2p.data.Base32;
|
import net.i2p.data.Base32;
|
||||||
import net.i2p.data.Destination;
|
import net.i2p.data.Destination;
|
||||||
import net.i2p.i2ptunnel.socks.I2PSOCKSTunnel;
|
import net.i2p.i2ptunnel.socks.I2PSOCKSTunnel;
|
||||||
@ -49,8 +51,8 @@ public class TunnelController implements Logging {
|
|||||||
* the prefix should be used (and, in turn, that prefix should be stripped off
|
* the prefix should be used (and, in turn, that prefix should be stripped off
|
||||||
* before being interpreted by this controller)
|
* before being interpreted by this controller)
|
||||||
*
|
*
|
||||||
* @param config original key=value mapping
|
* @param config original key=value mapping non-null
|
||||||
* @param prefix beginning of key values that are relevent to this tunnel
|
* @param prefix beginning of key values that are relevant to this tunnel
|
||||||
*/
|
*/
|
||||||
public TunnelController(Properties config, String prefix) {
|
public TunnelController(Properties config, String prefix) {
|
||||||
this(config, prefix, true);
|
this(config, prefix, true);
|
||||||
@ -58,6 +60,8 @@ public class TunnelController implements Logging {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
|
* @param config original key=value mapping non-null
|
||||||
|
* @param prefix beginning of key values that are relevant to this tunnel
|
||||||
* @param createKey for servers, whether we want to create a brand new destination
|
* @param createKey for servers, whether we want to create a brand new destination
|
||||||
* with private keys at the location specified or not (does not
|
* with private keys at the location specified or not (does not
|
||||||
* overwrite existing ones)
|
* overwrite existing ones)
|
||||||
@ -99,7 +103,16 @@ public class TunnelController implements Logging {
|
|||||||
FileOutputStream fos = null;
|
FileOutputStream fos = null;
|
||||||
try {
|
try {
|
||||||
fos = new SecureFileOutputStream(keyFile);
|
fos = new SecureFileOutputStream(keyFile);
|
||||||
Destination dest = client.createDestination(fos);
|
SigType stype = I2PClient.DEFAULT_SIGTYPE;
|
||||||
|
String st = _config.getProperty("option." + I2PClient.PROP_SIGTYPE);
|
||||||
|
if (st != null) {
|
||||||
|
SigType type = SigType.parseSigType(st);
|
||||||
|
if (type != null)
|
||||||
|
stype = type;
|
||||||
|
else
|
||||||
|
log("Unsupported sig type " + st);
|
||||||
|
}
|
||||||
|
Destination dest = client.createDestination(fos, stype);
|
||||||
String destStr = dest.toBase64();
|
String destStr = dest.toBase64();
|
||||||
log("Private key created and saved in " + keyFile.getAbsolutePath());
|
log("Private key created and saved in " + keyFile.getAbsolutePath());
|
||||||
log("You should backup this file in a secure place.");
|
log("You should backup this file in a secure place.");
|
||||||
|
@ -12,6 +12,7 @@ import net.i2p.client.I2PClient;
|
|||||||
import net.i2p.client.I2PClientFactory;
|
import net.i2p.client.I2PClientFactory;
|
||||||
import net.i2p.client.I2PSession;
|
import net.i2p.client.I2PSession;
|
||||||
import net.i2p.client.I2PSessionException;
|
import net.i2p.client.I2PSessionException;
|
||||||
|
import net.i2p.crypto.SigType;
|
||||||
import net.i2p.data.Destination;
|
import net.i2p.data.Destination;
|
||||||
import net.i2p.i2ptunnel.I2PTunnel;
|
import net.i2p.i2ptunnel.I2PTunnel;
|
||||||
import net.i2p.i2ptunnel.I2PTunnelTask;
|
import net.i2p.i2ptunnel.I2PTunnelTask;
|
||||||
@ -78,8 +79,17 @@ import net.i2p.util.EventDispatcher;
|
|||||||
I2PClient client = I2PClientFactory.createClient();
|
I2PClient client = I2PClientFactory.createClient();
|
||||||
byte[] key;
|
byte[] key;
|
||||||
try {
|
try {
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream(512);
|
ByteArrayOutputStream out = new ByteArrayOutputStream(1024);
|
||||||
client.createDestination(out);
|
SigType stype = I2PClient.DEFAULT_SIGTYPE;
|
||||||
|
String st = tunnel.getClientOptions().getProperty(I2PClient.PROP_SIGTYPE);
|
||||||
|
if (st != null) {
|
||||||
|
SigType type = SigType.parseSigType(st);
|
||||||
|
if (type != null)
|
||||||
|
stype = type;
|
||||||
|
else
|
||||||
|
l.log("Unsupported sig type " + st);
|
||||||
|
}
|
||||||
|
client.createDestination(out, stype);
|
||||||
key = out.toByteArray();
|
key = out.toByteArray();
|
||||||
} catch(Exception exc) {
|
} catch(Exception exc) {
|
||||||
throw new RuntimeException("failed to create i2p-destination", exc);
|
throw new RuntimeException("failed to create i2p-destination", exc);
|
||||||
|
@ -14,6 +14,7 @@ import java.util.Properties;
|
|||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.TreeMap;
|
import java.util.TreeMap;
|
||||||
|
|
||||||
|
import net.i2p.client.I2PClient;
|
||||||
import net.i2p.data.Base64;
|
import net.i2p.data.Base64;
|
||||||
import net.i2p.data.Destination;
|
import net.i2p.data.Destination;
|
||||||
import net.i2p.data.PrivateKeyFile;
|
import net.i2p.data.PrivateKeyFile;
|
||||||
@ -177,6 +178,11 @@ public class EditBean extends IndexBean {
|
|||||||
return getBooleanProperty(tunnel, "i2cp.encryptLeaseSet");
|
return getBooleanProperty(tunnel, "i2cp.encryptLeaseSet");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @since 0.9.12 */
|
||||||
|
public int getSigType(int tunnel) {
|
||||||
|
return getProperty(tunnel, I2PClient.PROP_SIGTYPE, 0);
|
||||||
|
}
|
||||||
|
|
||||||
/** @since 0.8.9 */
|
/** @since 0.8.9 */
|
||||||
public boolean getDCC(int tunnel) {
|
public boolean getDCC(int tunnel) {
|
||||||
return getBooleanProperty(tunnel, I2PTunnelIRCClient.PROP_DCC);
|
return getBooleanProperty(tunnel, I2PTunnelIRCClient.PROP_DCC);
|
||||||
@ -358,6 +364,11 @@ public class EditBean extends IndexBean {
|
|||||||
return Addresses.getAllAddresses();
|
return Addresses.getAllAddresses();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @since 0.9.12 */
|
||||||
|
public boolean isAdvanced() {
|
||||||
|
return _context.getBooleanProperty("routerconsole.advanced");
|
||||||
|
}
|
||||||
|
|
||||||
public String getI2CPHost(int tunnel) {
|
public String getI2CPHost(int tunnel) {
|
||||||
if (_context.isRouterContext())
|
if (_context.isRouterContext())
|
||||||
return _("internal");
|
return _("internal");
|
||||||
|
@ -24,6 +24,7 @@ import java.util.concurrent.ConcurrentHashMap;
|
|||||||
import net.i2p.I2PAppContext;
|
import net.i2p.I2PAppContext;
|
||||||
import net.i2p.app.ClientAppManager;
|
import net.i2p.app.ClientAppManager;
|
||||||
import net.i2p.app.Outproxy;
|
import net.i2p.app.Outproxy;
|
||||||
|
import net.i2p.client.I2PClient;
|
||||||
import net.i2p.data.Base32;
|
import net.i2p.data.Base32;
|
||||||
import net.i2p.data.Certificate;
|
import net.i2p.data.Certificate;
|
||||||
import net.i2p.data.Destination;
|
import net.i2p.data.Destination;
|
||||||
@ -983,6 +984,7 @@ public class IndexBean {
|
|||||||
} catch (NumberFormatException nfe) {}
|
} catch (NumberFormatException nfe) {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setCert(String val) {
|
public void setCert(String val) {
|
||||||
if (val != null) {
|
if (val != null) {
|
||||||
try {
|
try {
|
||||||
@ -990,10 +992,24 @@ public class IndexBean {
|
|||||||
} catch (NumberFormatException nfe) {}
|
} catch (NumberFormatException nfe) {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setSigner(String val) {
|
public void setSigner(String val) {
|
||||||
_certSigner = val;
|
_certSigner = val;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @since 0.9.12 */
|
||||||
|
public void setSigType(String val) {
|
||||||
|
if (val != null) {
|
||||||
|
_otherOptions.put(I2PClient.PROP_SIGTYPE, val);
|
||||||
|
if (val.equals("0"))
|
||||||
|
_certType = 0;
|
||||||
|
else
|
||||||
|
_certType = 5;
|
||||||
|
}
|
||||||
|
// TODO: Call modifyDestination??
|
||||||
|
// Otherwise this only works on a new tunnel...
|
||||||
|
}
|
||||||
|
|
||||||
/** Modify or create a destination */
|
/** Modify or create a destination */
|
||||||
private String modifyDestination() {
|
private String modifyDestination() {
|
||||||
if (_privKeyFile == null || _privKeyFile.trim().length() <= 0)
|
if (_privKeyFile == null || _privKeyFile.trim().length() <= 0)
|
||||||
@ -1234,13 +1250,14 @@ public class IndexBean {
|
|||||||
"outproxyUsername", "outproxyPassword",
|
"outproxyUsername", "outproxyPassword",
|
||||||
I2PTunnelHTTPClient.PROP_JUMP_SERVERS,
|
I2PTunnelHTTPClient.PROP_JUMP_SERVERS,
|
||||||
I2PTunnelHTTPClientBase.PROP_AUTH,
|
I2PTunnelHTTPClientBase.PROP_AUTH,
|
||||||
|
I2PClient.PROP_SIGTYPE,
|
||||||
I2PTunnelHTTPClient.PROP_SSL_OUTPROXIES
|
I2PTunnelHTTPClient.PROP_SSL_OUTPROXIES
|
||||||
};
|
};
|
||||||
private static final String _otherServerOpts[] = {
|
private static final String _otherServerOpts[] = {
|
||||||
"i2cp.reduceIdleTime", "i2cp.reduceQuantity", "i2cp.leaseSetKey", "i2cp.accessList",
|
"i2cp.reduceIdleTime", "i2cp.reduceQuantity", "i2cp.leaseSetKey", "i2cp.accessList",
|
||||||
PROP_MAX_CONNS_MIN, PROP_MAX_CONNS_HOUR, PROP_MAX_CONNS_DAY,
|
PROP_MAX_CONNS_MIN, PROP_MAX_CONNS_HOUR, PROP_MAX_CONNS_DAY,
|
||||||
PROP_MAX_TOTAL_CONNS_MIN, PROP_MAX_TOTAL_CONNS_HOUR, PROP_MAX_TOTAL_CONNS_DAY,
|
PROP_MAX_TOTAL_CONNS_MIN, PROP_MAX_TOTAL_CONNS_HOUR, PROP_MAX_TOTAL_CONNS_DAY,
|
||||||
PROP_MAX_STREAMS
|
PROP_MAX_STREAMS, I2PClient.PROP_SIGTYPE
|
||||||
};
|
};
|
||||||
private static final String _httpServerOpts[] = {
|
private static final String _httpServerOpts[] = {
|
||||||
I2PTunnelHTTPServer.OPT_POST_WINDOW,
|
I2PTunnelHTTPServer.OPT_POST_WINDOW,
|
||||||
|
@ -449,6 +449,37 @@ input.default { width: 1px; height: 1px; visibility: hidden; }
|
|||||||
</div>
|
</div>
|
||||||
<% } %>
|
<% } %>
|
||||||
|
|
||||||
|
<% if (editBean.isAdvanced()) { %>
|
||||||
|
<div id="tunnelOptionsField" class="rowItem">
|
||||||
|
<label>
|
||||||
|
<%=intl._("Signature type")%>
|
||||||
|
(<%=intl._("Experts only!")%>)
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div id="hostField" class="rowItem">
|
||||||
|
<div id="portField" class="rowItem">
|
||||||
|
<label>DSA-SHA1</label>
|
||||||
|
<input value="0" type="radio" id="startOnLoad" name="sigType" title="Default"<%=(editBean.getSigType(curTunnel)==0 ? " checked=\"checked\"" : "")%> class="tickbox" />
|
||||||
|
</div>
|
||||||
|
<div id="portField" class="rowItem">
|
||||||
|
<label>ECDSA-P256</label>
|
||||||
|
<input value="1" type="radio" id="startOnLoad" name="sigType" title="Advanced users only"<%=(editBean.getSigType(curTunnel)==1 ? " checked=\"checked\"" : "")%> class="tickbox" />
|
||||||
|
</div>
|
||||||
|
<div id="portField" class="rowItem">
|
||||||
|
<label>ECDSA-P384</label>
|
||||||
|
<input value="2" type="radio" id="startOnLoad" name="sigType" title="Advanced users only"<%=(editBean.getSigType(curTunnel)==2 ? " checked=\"checked\"" : "")%> class="tickbox" />
|
||||||
|
</div>
|
||||||
|
<div id="portField" class="rowItem">
|
||||||
|
<label>ECDSA-P521</label>
|
||||||
|
<input value="3" type="radio" id="startOnLoad" name="sigType" title="Advanced users only"<%=(editBean.getSigType(curTunnel)==3 ? " checked=\"checked\"" : "")%> class="tickbox" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="subdivider">
|
||||||
|
<hr />
|
||||||
|
</div>
|
||||||
|
<% } // isAdvanced %>>
|
||||||
|
|
||||||
<% if ("httpclient".equals(tunnelType) || "connectclient".equals(tunnelType) || "sockstunnel".equals(tunnelType) || "socksirctunnel".equals(tunnelType)) { %>
|
<% if ("httpclient".equals(tunnelType) || "connectclient".equals(tunnelType) || "sockstunnel".equals(tunnelType) || "socksirctunnel".equals(tunnelType)) { %>
|
||||||
<div id="accessField" class="rowItem">
|
<div id="accessField" class="rowItem">
|
||||||
<label><%=intl._("Local Authorization")%>:</label>
|
<label><%=intl._("Local Authorization")%>:</label>
|
||||||
|
@ -491,6 +491,7 @@ input.default { width: 1px; height: 1px; visibility: hidden; }
|
|||||||
<hr />
|
<hr />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<% /***************** %>
|
||||||
<div id="tunnelOptionsField" class="rowItem">
|
<div id="tunnelOptionsField" class="rowItem">
|
||||||
<label for="cert" accesskey="c">
|
<label for="cert" accesskey="c">
|
||||||
<%=intl._("New Certificate type")%>(<span class="accessKey">C</span>):
|
<%=intl._("New Certificate type")%>(<span class="accessKey">C</span>):
|
||||||
@ -533,10 +534,38 @@ input.default { width: 1px; height: 1px; visibility: hidden; }
|
|||||||
<button accesskey="S" class="control" type="submit" name="action" value="Modify" title="Force New Cert Now"><%=intl._("Modify")%></button>
|
<button accesskey="S" class="control" type="submit" name="action" value="Modify" title="Force New Cert Now"><%=intl._("Modify")%></button>
|
||||||
<span class="comment"><%=intl._("(Tunnel must be stopped first)")%></span>
|
<span class="comment"><%=intl._("(Tunnel must be stopped first)")%></span>
|
||||||
</div>
|
</div>
|
||||||
|
<% **********************/ %>
|
||||||
|
|
||||||
|
<% if (editBean.isAdvanced()) { %>
|
||||||
|
<div id="tunnelOptionsField" class="rowItem">
|
||||||
|
<label>
|
||||||
|
<%=intl._("Signature type")%>
|
||||||
|
(<%=intl._("Experts only! Changes B32!")%>)
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div id="hostField" class="rowItem">
|
||||||
|
<div id="portField" class="rowItem">
|
||||||
|
<label>DSA-SHA1</label>
|
||||||
|
<input value="0" type="radio" id="startOnLoad" name="sigType" title="Default"<%=(editBean.getSigType(curTunnel)==0 ? " checked=\"checked\"" : "")%> class="tickbox" />
|
||||||
|
</div>
|
||||||
|
<div id="portField" class="rowItem">
|
||||||
|
<label>ECDSA-P256</label>
|
||||||
|
<input value="1" type="radio" id="startOnLoad" name="sigType" title="Advanced users only"<%=(editBean.getSigType(curTunnel)==1 ? " checked=\"checked\"" : "")%> class="tickbox" />
|
||||||
|
</div>
|
||||||
|
<div id="portField" class="rowItem">
|
||||||
|
<label>ECDSA-P384</label>
|
||||||
|
<input value="2" type="radio" id="startOnLoad" name="sigType" title="Advanced users only"<%=(editBean.getSigType(curTunnel)==2 ? " checked=\"checked\"" : "")%> class="tickbox" />
|
||||||
|
</div>
|
||||||
|
<div id="portField" class="rowItem">
|
||||||
|
<label>ECDSA-P521</label>
|
||||||
|
<input value="3" type="radio" id="startOnLoad" name="sigType" title="Advanced users only"<%=(editBean.getSigType(curTunnel)==3 ? " checked=\"checked\"" : "")%> class="tickbox" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="subdivider">
|
<div class="subdivider">
|
||||||
<hr />
|
<hr />
|
||||||
</div>
|
</div>
|
||||||
|
<% } // isAdvanced %>>
|
||||||
|
|
||||||
<div id="customOptionsField" class="rowItem">
|
<div id="customOptionsField" class="rowItem">
|
||||||
<label for="customOptions" accesskey="u">
|
<label for="customOptions" accesskey="u">
|
||||||
|
@ -14,6 +14,7 @@ import net.i2p.client.I2PClient;
|
|||||||
import net.i2p.client.I2PClientFactory;
|
import net.i2p.client.I2PClientFactory;
|
||||||
import net.i2p.client.I2PSession;
|
import net.i2p.client.I2PSession;
|
||||||
import net.i2p.client.I2PSessionException;
|
import net.i2p.client.I2PSessionException;
|
||||||
|
import net.i2p.crypto.SigType;
|
||||||
import net.i2p.util.Log;
|
import net.i2p.util.Log;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -26,7 +27,7 @@ public class I2PSocketManagerFactory {
|
|||||||
|
|
||||||
public static final String PROP_MANAGER = "i2p.streaming.manager";
|
public static final String PROP_MANAGER = "i2p.streaming.manager";
|
||||||
public static final String DEFAULT_MANAGER = "net.i2p.client.streaming.impl.I2PSocketManagerFull";
|
public static final String DEFAULT_MANAGER = "net.i2p.client.streaming.impl.I2PSocketManagerFull";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a socket manager using a brand new destination connected to the
|
* Create a socket manager using a brand new destination connected to the
|
||||||
* I2CP router on the local machine on the default port (7654).
|
* I2CP router on the local machine on the default port (7654).
|
||||||
@ -79,9 +80,9 @@ public class I2PSocketManagerFactory {
|
|||||||
*/
|
*/
|
||||||
public static I2PSocketManager createManager(String i2cpHost, int i2cpPort, Properties opts) {
|
public static I2PSocketManager createManager(String i2cpHost, int i2cpPort, Properties opts) {
|
||||||
I2PClient client = I2PClientFactory.createClient();
|
I2PClient client = I2PClientFactory.createClient();
|
||||||
ByteArrayOutputStream keyStream = new ByteArrayOutputStream(512);
|
ByteArrayOutputStream keyStream = new ByteArrayOutputStream(1024);
|
||||||
try {
|
try {
|
||||||
client.createDestination(keyStream);
|
client.createDestination(keyStream, getSigType(opts));
|
||||||
ByteArrayInputStream in = new ByteArrayInputStream(keyStream.toByteArray());
|
ByteArrayInputStream in = new ByteArrayInputStream(keyStream.toByteArray());
|
||||||
return createManager(in, i2cpHost, i2cpPort, opts);
|
return createManager(in, i2cpHost, i2cpPort, opts);
|
||||||
} catch (IOException ioe) {
|
} catch (IOException ioe) {
|
||||||
@ -168,9 +169,9 @@ public class I2PSocketManagerFactory {
|
|||||||
int i2cpPort, Properties opts) throws I2PSessionException {
|
int i2cpPort, Properties opts) throws I2PSessionException {
|
||||||
if (myPrivateKeyStream == null) {
|
if (myPrivateKeyStream == null) {
|
||||||
I2PClient client = I2PClientFactory.createClient();
|
I2PClient client = I2PClientFactory.createClient();
|
||||||
ByteArrayOutputStream keyStream = new ByteArrayOutputStream(512);
|
ByteArrayOutputStream keyStream = new ByteArrayOutputStream(1024);
|
||||||
try {
|
try {
|
||||||
client.createDestination(keyStream);
|
client.createDestination(keyStream, getSigType(opts));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new I2PSessionException("Error creating keys", e);
|
throw new I2PSessionException("Error creating keys", e);
|
||||||
}
|
}
|
||||||
@ -257,6 +258,23 @@ public class I2PSocketManagerFactory {
|
|||||||
return i2cpPort;
|
return i2cpPort;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param opts may be null
|
||||||
|
* @since 0.9.12
|
||||||
|
*/
|
||||||
|
private static SigType getSigType(Properties opts) {
|
||||||
|
if (opts != null) {
|
||||||
|
String st = opts.getProperty(I2PClient.PROP_SIGTYPE);
|
||||||
|
if (st != null) {
|
||||||
|
SigType rv = SigType.parseSigType(st);
|
||||||
|
if (rv != null)
|
||||||
|
return rv;
|
||||||
|
getLog().error("Unsupported sig type " + st);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return I2PClient.DEFAULT_SIGTYPE;
|
||||||
|
}
|
||||||
|
|
||||||
/** @since 0.9.7 */
|
/** @since 0.9.7 */
|
||||||
private static Log getLog() {
|
private static Log getLog() {
|
||||||
return I2PAppContext.getGlobalContext().logManager().getLog(I2PSocketManagerFactory.class);
|
return I2PAppContext.getGlobalContext().logManager().getLog(I2PSocketManagerFactory.class);
|
||||||
|
@ -23,6 +23,7 @@ import java.util.Map;
|
|||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.TreeSet;
|
import java.util.TreeSet;
|
||||||
|
|
||||||
|
import net.i2p.data.Base32;
|
||||||
import net.i2p.data.DataHelper;
|
import net.i2p.data.DataHelper;
|
||||||
import net.i2p.data.Destination;
|
import net.i2p.data.Destination;
|
||||||
import net.i2p.data.Hash;
|
import net.i2p.data.Hash;
|
||||||
@ -167,6 +168,8 @@ public class NetDbRenderer {
|
|||||||
median = dist;
|
median = dist;
|
||||||
}
|
}
|
||||||
buf.append(" Dist: <b>").append(fmt.format(biLog2(dist))).append("</b><br>");
|
buf.append(" Dist: <b>").append(fmt.format(biLog2(dist))).append("</b><br>");
|
||||||
|
buf.append(Base32.encode(key.getData())).append(".b32.i2p<br>");
|
||||||
|
buf.append("Sig type: ").append(dest.getSigningPublicKey().getType()).append("<br>");
|
||||||
buf.append("Routing Key: ").append(ls.getRoutingKey().toBase64());
|
buf.append("Routing Key: ").append(ls.getRoutingKey().toBase64());
|
||||||
buf.append("<br>");
|
buf.append("<br>");
|
||||||
buf.append("Encryption Key: ").append(ls.getEncryptionKey().toBase64().substring(0, 20)).append("...<br>");
|
buf.append("Encryption Key: ").append(ls.getEncryptionKey().toBase64().substring(0, 20)).append("...<br>");
|
||||||
|
@ -5,6 +5,7 @@ import java.io.IOException;
|
|||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
import net.i2p.I2PAppContext;
|
import net.i2p.I2PAppContext;
|
||||||
|
import net.i2p.crypto.SigType;
|
||||||
import net.i2p.data.Base64;
|
import net.i2p.data.Base64;
|
||||||
import net.i2p.data.ByteArray;
|
import net.i2p.data.ByteArray;
|
||||||
import net.i2p.data.DataFormatException;
|
import net.i2p.data.DataFormatException;
|
||||||
@ -403,13 +404,14 @@ class Packet {
|
|||||||
* @throws IllegalStateException if there is data missing or otherwise b0rked
|
* @throws IllegalStateException if there is data missing or otherwise b0rked
|
||||||
*/
|
*/
|
||||||
public int writePacket(byte buffer[], int offset) throws IllegalStateException {
|
public int writePacket(byte buffer[], int offset) throws IllegalStateException {
|
||||||
return writePacket(buffer, offset, true);
|
return writePacket(buffer, offset, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param includeSig if true, include the real signature, otherwise put zeroes
|
* @param fakeSigLen if 0, include the real signature in _optionSignature;
|
||||||
* in its place.
|
* if nonzero, leave space for that many bytes
|
||||||
*/
|
*/
|
||||||
private int writePacket(byte buffer[], int offset, boolean includeSig) throws IllegalStateException {
|
private int writePacket(byte buffer[], int offset, int fakeSigLen) throws IllegalStateException {
|
||||||
int cur = offset;
|
int cur = offset;
|
||||||
DataHelper.toLong(buffer, cur, 4, (_sendStreamId >= 0 ? _sendStreamId : STREAM_ID_UNKNOWN));
|
DataHelper.toLong(buffer, cur, 4, (_sendStreamId >= 0 ? _sendStreamId : STREAM_ID_UNKNOWN));
|
||||||
cur += 4;
|
cur += 4;
|
||||||
@ -443,8 +445,14 @@ class Packet {
|
|||||||
optionSize += _optionFrom.size();
|
optionSize += _optionFrom.size();
|
||||||
if (isFlagSet(FLAG_MAX_PACKET_SIZE_INCLUDED))
|
if (isFlagSet(FLAG_MAX_PACKET_SIZE_INCLUDED))
|
||||||
optionSize += 2;
|
optionSize += 2;
|
||||||
if (isFlagSet(FLAG_SIGNATURE_INCLUDED))
|
if (isFlagSet(FLAG_SIGNATURE_INCLUDED)) {
|
||||||
optionSize += Signature.SIGNATURE_BYTES;
|
if (fakeSigLen > 0)
|
||||||
|
optionSize += fakeSigLen;
|
||||||
|
else if (_optionSignature != null)
|
||||||
|
optionSize += _optionSignature.length();
|
||||||
|
else
|
||||||
|
throw new IllegalStateException();
|
||||||
|
}
|
||||||
|
|
||||||
DataHelper.toLong(buffer, cur, 2, optionSize);
|
DataHelper.toLong(buffer, cur, 2, optionSize);
|
||||||
cur += 2;
|
cur += 2;
|
||||||
@ -461,11 +469,14 @@ class Packet {
|
|||||||
cur += 2;
|
cur += 2;
|
||||||
}
|
}
|
||||||
if (isFlagSet(FLAG_SIGNATURE_INCLUDED)) {
|
if (isFlagSet(FLAG_SIGNATURE_INCLUDED)) {
|
||||||
if (includeSig)
|
if (fakeSigLen == 0) {
|
||||||
System.arraycopy(_optionSignature.getData(), 0, buffer, cur, Signature.SIGNATURE_BYTES);
|
// we're signing (or validating)
|
||||||
else // we're signing (or validating)
|
System.arraycopy(_optionSignature.getData(), 0, buffer, cur, _optionSignature.length());
|
||||||
Arrays.fill(buffer, cur, cur+Signature.SIGNATURE_BYTES, (byte)0x0);
|
cur += _optionSignature.length();
|
||||||
cur += Signature.SIGNATURE_BYTES;
|
} else {
|
||||||
|
Arrays.fill(buffer, cur, cur + fakeSigLen, (byte)0x0);
|
||||||
|
cur += fakeSigLen;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_payload != null) {
|
if (_payload != null) {
|
||||||
@ -518,7 +529,7 @@ class Packet {
|
|||||||
if (isFlagSet(FLAG_MAX_PACKET_SIZE_INCLUDED))
|
if (isFlagSet(FLAG_MAX_PACKET_SIZE_INCLUDED))
|
||||||
size += 2;
|
size += 2;
|
||||||
if (isFlagSet(FLAG_SIGNATURE_INCLUDED))
|
if (isFlagSet(FLAG_SIGNATURE_INCLUDED))
|
||||||
size += Signature.SIGNATURE_BYTES;
|
size += _optionSignature.length();
|
||||||
|
|
||||||
size += 2; // option size
|
size += 2; // option size
|
||||||
|
|
||||||
@ -613,12 +624,37 @@ class Packet {
|
|||||||
cur += 2;
|
cur += 2;
|
||||||
}
|
}
|
||||||
if (isFlagSet(FLAG_SIGNATURE_INCLUDED)) {
|
if (isFlagSet(FLAG_SIGNATURE_INCLUDED)) {
|
||||||
Signature optionSignature = new Signature();
|
Signature optionSignature;
|
||||||
byte buf[] = new byte[Signature.SIGNATURE_BYTES];
|
Destination from = getOptionalFrom();
|
||||||
System.arraycopy(buffer, cur, buf, 0, Signature.SIGNATURE_BYTES);
|
if (from != null) {
|
||||||
|
optionSignature = new Signature(from.getSigningPublicKey().getType());
|
||||||
|
} else {
|
||||||
|
// super cheat for now, look for correct type,
|
||||||
|
// assume no more options. If we add to the options
|
||||||
|
// we will have to ask the manager.
|
||||||
|
int siglen = payloadBegin - cur;
|
||||||
|
SigType type = null;
|
||||||
|
for (SigType t : SigType.values()) {
|
||||||
|
if (t.getSigLen() == siglen) {
|
||||||
|
type = t;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (type == null) {
|
||||||
|
if (siglen < Signature.SIGNATURE_BYTES)
|
||||||
|
throw new IllegalArgumentException("unknown sig type len=" + siglen);
|
||||||
|
// Hope it's the default type with some unknown options following;
|
||||||
|
// if not the sig will fail later
|
||||||
|
type = SigType.DSA_SHA1;
|
||||||
|
siglen = Signature.SIGNATURE_BYTES;
|
||||||
|
}
|
||||||
|
optionSignature = new Signature(type);
|
||||||
|
}
|
||||||
|
byte buf[] = new byte[optionSignature.length()];
|
||||||
|
System.arraycopy(buffer, cur, buf, 0, buf.length);
|
||||||
optionSignature.setData(buf);
|
optionSignature.setData(buf);
|
||||||
setOptionalSignature(optionSignature);
|
setOptionalSignature(optionSignature);
|
||||||
cur += Signature.SIGNATURE_BYTES;
|
cur += buf.length;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -641,7 +677,7 @@ class Packet {
|
|||||||
|
|
||||||
if (buffer == null)
|
if (buffer == null)
|
||||||
buffer = new byte[size];
|
buffer = new byte[size];
|
||||||
int written = writePacket(buffer, 0, false);
|
int written = writePacket(buffer, 0, from.getSigningPublicKey().getType().getSigLen());
|
||||||
if (written != size) {
|
if (written != size) {
|
||||||
ctx.logManager().getLog(Packet.class).error("Written " + written + " size " + size + " for " + toString(), new Exception("moo"));
|
ctx.logManager().getLog(Packet.class).error("Written " + written + " size " + size + " for " + toString(), new Exception("moo"));
|
||||||
return false;
|
return false;
|
||||||
@ -672,14 +708,14 @@ class Packet {
|
|||||||
*/
|
*/
|
||||||
public int writeSignedPacket(byte buffer[], int offset, I2PAppContext ctx, SigningPrivateKey key) throws IllegalStateException {
|
public int writeSignedPacket(byte buffer[], int offset, I2PAppContext ctx, SigningPrivateKey key) throws IllegalStateException {
|
||||||
setFlag(FLAG_SIGNATURE_INCLUDED);
|
setFlag(FLAG_SIGNATURE_INCLUDED);
|
||||||
int size = writePacket(buffer, offset, false);
|
int size = writePacket(buffer, offset, key.getType().getSigLen());
|
||||||
_optionSignature = ctx.dsa().sign(buffer, offset, size, key);
|
_optionSignature = ctx.dsa().sign(buffer, offset, size, key);
|
||||||
if (false) {
|
//if (false) {
|
||||||
Log l = ctx.logManager().getLog(Packet.class);
|
// Log l = ctx.logManager().getLog(Packet.class);
|
||||||
l.error("Signing: " + toString());
|
// l.error("Signing: " + toString());
|
||||||
l.error(Base64.encode(buffer, 0, size));
|
// l.error(Base64.encode(buffer, 0, size));
|
||||||
l.error("Signature: " + Base64.encode(_optionSignature.getData()));
|
// l.error("Signature: " + Base64.encode(_optionSignature.getData()));
|
||||||
}
|
//}
|
||||||
// jump into the signed data and inject the signature where we
|
// jump into the signed data and inject the signature where we
|
||||||
// previously placed a bunch of zeroes
|
// previously placed a bunch of zeroes
|
||||||
int signatureOffset = offset
|
int signatureOffset = offset
|
||||||
@ -694,7 +730,7 @@ class Packet {
|
|||||||
+ (isFlagSet(FLAG_DELAY_REQUESTED) ? 2 : 0)
|
+ (isFlagSet(FLAG_DELAY_REQUESTED) ? 2 : 0)
|
||||||
+ (isFlagSet(FLAG_FROM_INCLUDED) ? _optionFrom.size() : 0)
|
+ (isFlagSet(FLAG_FROM_INCLUDED) ? _optionFrom.size() : 0)
|
||||||
+ (isFlagSet(FLAG_MAX_PACKET_SIZE_INCLUDED) ? 2 : 0);
|
+ (isFlagSet(FLAG_MAX_PACKET_SIZE_INCLUDED) ? 2 : 0);
|
||||||
System.arraycopy(_optionSignature.getData(), 0, buffer, signatureOffset, Signature.SIGNATURE_BYTES);
|
System.arraycopy(_optionSignature.getData(), 0, buffer, signatureOffset, _optionSignature.length());
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -740,7 +776,7 @@ class Packet {
|
|||||||
if (isFlagSet(FLAG_CLOSE)) buf.append(" CLOSE");
|
if (isFlagSet(FLAG_CLOSE)) buf.append(" CLOSE");
|
||||||
if (isFlagSet(FLAG_DELAY_REQUESTED)) buf.append(" DELAY ").append(_optionDelay);
|
if (isFlagSet(FLAG_DELAY_REQUESTED)) buf.append(" DELAY ").append(_optionDelay);
|
||||||
if (isFlagSet(FLAG_ECHO)) buf.append(" ECHO");
|
if (isFlagSet(FLAG_ECHO)) buf.append(" ECHO");
|
||||||
if (isFlagSet(FLAG_FROM_INCLUDED)) buf.append(" FROM");
|
if (isFlagSet(FLAG_FROM_INCLUDED)) buf.append(" FROM ").append(_optionFrom.size());
|
||||||
if (isFlagSet(FLAG_MAX_PACKET_SIZE_INCLUDED)) buf.append(" MS ").append(_optionMaxSize);
|
if (isFlagSet(FLAG_MAX_PACKET_SIZE_INCLUDED)) buf.append(" MS ").append(_optionMaxSize);
|
||||||
if (isFlagSet(FLAG_PROFILE_INTERACTIVE)) buf.append(" INTERACTIVE");
|
if (isFlagSet(FLAG_PROFILE_INTERACTIVE)) buf.append(" INTERACTIVE");
|
||||||
if (isFlagSet(FLAG_RESET)) buf.append(" RESET");
|
if (isFlagSet(FLAG_RESET)) buf.append(" RESET");
|
||||||
|
@ -29,6 +29,7 @@ import java.util.Locale;
|
|||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
|
|
||||||
import net.i2p.I2PAppContext;
|
import net.i2p.I2PAppContext;
|
||||||
|
import net.i2p.crypto.SigType;
|
||||||
import net.i2p.data.Base32;
|
import net.i2p.data.Base32;
|
||||||
import net.i2p.data.Base64;
|
import net.i2p.data.Base64;
|
||||||
import net.i2p.data.Certificate;
|
import net.i2p.data.Certificate;
|
||||||
@ -228,11 +229,38 @@ public class AddressBean
|
|||||||
return _("Hidden");
|
return _("Hidden");
|
||||||
case Certificate.CERTIFICATE_TYPE_SIGNED:
|
case Certificate.CERTIFICATE_TYPE_SIGNED:
|
||||||
return _("Signed");
|
return _("Signed");
|
||||||
|
case Certificate.CERTIFICATE_TYPE_KEY:
|
||||||
|
return _("Key");
|
||||||
default:
|
default:
|
||||||
return _("Type {0}", type);
|
return _("Type {0}", type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Do this the easy way
|
||||||
|
* @since 0.9.12
|
||||||
|
*/
|
||||||
|
public String getSigType() {
|
||||||
|
// (4 / 3) * (pubkey length + signing key length)
|
||||||
|
String cert = destination.substring(512);
|
||||||
|
if (cert.equals("AAAA"))
|
||||||
|
return _("DSA 1024 bit");
|
||||||
|
byte[] enc = Base64.decode(cert);
|
||||||
|
if (enc == null)
|
||||||
|
// shouldn't happen
|
||||||
|
return "invalid";
|
||||||
|
int type = enc[0] & 0xff;
|
||||||
|
if (type != Certificate.CERTIFICATE_TYPE_KEY)
|
||||||
|
return _("DSA 1024 bit");
|
||||||
|
int st = ((enc[3] & 0xff) << 8) | (enc[4] & 0xff);
|
||||||
|
if (st == 0)
|
||||||
|
return _("DSA 1024 bit");
|
||||||
|
SigType stype = SigType.getByCode(st);
|
||||||
|
if (stype == null)
|
||||||
|
return _("Type {0}", st);
|
||||||
|
return stype.toString();
|
||||||
|
}
|
||||||
|
|
||||||
/** @since 0.8.7 */
|
/** @since 0.8.7 */
|
||||||
private String getProp(String p) {
|
private String getProp(String p) {
|
||||||
if (props == null)
|
if (props == null)
|
||||||
|
@ -107,7 +107,7 @@
|
|||||||
<td><%=intl._("ElGamal 2048 bit")%></td>
|
<td><%=intl._("ElGamal 2048 bit")%></td>
|
||||||
</tr><tr class="list${book.trClass}">
|
</tr><tr class="list${book.trClass}">
|
||||||
<td><%=intl._("Signing Key")%></td>
|
<td><%=intl._("Signing Key")%></td>
|
||||||
<td><%=intl._("DSA 1024 bit")%></td>
|
<td><%=addr.getSigType()%></td>
|
||||||
</tr><tr class="list${book.trClass}">
|
</tr><tr class="list${book.trClass}">
|
||||||
<td><%=intl._("Certificate")%></td>
|
<td><%=intl._("Certificate")%></td>
|
||||||
<td><%=addr.getCert()%></td>
|
<td><%=addr.getCert()%></td>
|
||||||
|
@ -15,6 +15,7 @@ import java.io.OutputStream;
|
|||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
|
|
||||||
import net.i2p.I2PException;
|
import net.i2p.I2PException;
|
||||||
|
import net.i2p.crypto.SigType;
|
||||||
import net.i2p.data.Certificate;
|
import net.i2p.data.Certificate;
|
||||||
import net.i2p.data.Destination;
|
import net.i2p.data.Destination;
|
||||||
|
|
||||||
@ -40,6 +41,11 @@ public interface I2PClient {
|
|||||||
/** @since 0.8.1 */
|
/** @since 0.8.1 */
|
||||||
public final static String PROP_RELIABILITY_NONE = "none";
|
public final static String PROP_RELIABILITY_NONE = "none";
|
||||||
|
|
||||||
|
/** @since 0.9.12 */
|
||||||
|
public static final String PROP_SIGTYPE = "i2cp.destination.sigType";
|
||||||
|
/** @since 0.9.12 */
|
||||||
|
public static final SigType DEFAULT_SIGTYPE = SigType.DSA_SHA1;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* For router->client payloads.
|
* For router->client payloads.
|
||||||
*
|
*
|
||||||
@ -83,6 +89,18 @@ public interface I2PClient {
|
|||||||
*/
|
*/
|
||||||
public Destination createDestination(OutputStream destKeyStream) throws I2PException, IOException;
|
public Destination createDestination(OutputStream destKeyStream) throws I2PException, IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a destination with the given signature type.
|
||||||
|
* It will have a null certificate for DSA 1024/160 and KeyCertificate otherwise.
|
||||||
|
* This is not bound to the I2PClient, you must supply the data back again
|
||||||
|
* in createSession().
|
||||||
|
*
|
||||||
|
* @param destKeyStream location to write out the destination, PrivateKey, and SigningPrivateKey,
|
||||||
|
* format is specified in {@link net.i2p.data.PrivateKeyFile PrivateKeyFile}
|
||||||
|
* @since 0.9.12
|
||||||
|
*/
|
||||||
|
public Destination createDestination(OutputStream destKeyStream, SigType type) throws I2PException, IOException;
|
||||||
|
|
||||||
/** Create a new destination with the given certificate and store it, along with the private
|
/** Create a new destination with the given certificate and store it, along with the private
|
||||||
* encryption and signing keys at the specified location
|
* encryption and signing keys at the specified location
|
||||||
*
|
*
|
||||||
|
@ -12,17 +12,22 @@ package net.i2p.client;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
|
import java.security.GeneralSecurityException;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
|
|
||||||
import net.i2p.I2PAppContext;
|
import net.i2p.I2PAppContext;
|
||||||
import net.i2p.I2PException;
|
import net.i2p.I2PException;
|
||||||
import net.i2p.crypto.KeyGenerator;
|
import net.i2p.crypto.KeyGenerator;
|
||||||
|
import net.i2p.crypto.SigType;
|
||||||
import net.i2p.data.Certificate;
|
import net.i2p.data.Certificate;
|
||||||
import net.i2p.data.Destination;
|
import net.i2p.data.Destination;
|
||||||
|
import net.i2p.data.KeyCertificate;
|
||||||
import net.i2p.data.PrivateKey;
|
import net.i2p.data.PrivateKey;
|
||||||
import net.i2p.data.PublicKey;
|
import net.i2p.data.PublicKey;
|
||||||
import net.i2p.data.SigningPrivateKey;
|
import net.i2p.data.SigningPrivateKey;
|
||||||
import net.i2p.data.SigningPublicKey;
|
import net.i2p.data.SigningPublicKey;
|
||||||
|
import net.i2p.data.SimpleDataStructure;
|
||||||
|
import net.i2p.util.RandomSource;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base client implementation.
|
* Base client implementation.
|
||||||
@ -34,7 +39,7 @@ import net.i2p.data.SigningPublicKey;
|
|||||||
class I2PClientImpl implements I2PClient {
|
class I2PClientImpl implements I2PClient {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create the destination with a null payload.
|
* Create a destination with a DSA 1024/160 signature type and a null certificate.
|
||||||
* This is not bound to the I2PClient, you must supply the data back again
|
* This is not bound to the I2PClient, you must supply the data back again
|
||||||
* in createSession().
|
* in createSession().
|
||||||
*
|
*
|
||||||
@ -42,9 +47,26 @@ class I2PClientImpl implements I2PClient {
|
|||||||
* format is specified in {@link net.i2p.data.PrivateKeyFile PrivateKeyFile}
|
* format is specified in {@link net.i2p.data.PrivateKeyFile PrivateKeyFile}
|
||||||
*/
|
*/
|
||||||
public Destination createDestination(OutputStream destKeyStream) throws I2PException, IOException {
|
public Destination createDestination(OutputStream destKeyStream) throws I2PException, IOException {
|
||||||
Certificate cert = new Certificate();
|
return createDestination(destKeyStream, DEFAULT_SIGTYPE);
|
||||||
cert.setCertificateType(Certificate.CERTIFICATE_TYPE_NULL);
|
}
|
||||||
cert.setPayload(null);
|
|
||||||
|
/**
|
||||||
|
* Create a destination with the given signature type.
|
||||||
|
* It will have a null certificate for DSA 1024/160 and KeyCertificate otherwise.
|
||||||
|
* This is not bound to the I2PClient, you must supply the data back again
|
||||||
|
* in createSession().
|
||||||
|
*
|
||||||
|
* @param destKeyStream location to write out the destination, PrivateKey, and SigningPrivateKey,
|
||||||
|
* format is specified in {@link net.i2p.data.PrivateKeyFile PrivateKeyFile}
|
||||||
|
* @since 0.9.12
|
||||||
|
*/
|
||||||
|
public Destination createDestination(OutputStream destKeyStream, SigType type) throws I2PException, IOException {
|
||||||
|
Certificate cert;
|
||||||
|
if (type == SigType.DSA_SHA1) {
|
||||||
|
cert = Certificate.NULL_CERT;
|
||||||
|
} else {
|
||||||
|
cert = new KeyCertificate(type);
|
||||||
|
}
|
||||||
return createDestination(destKeyStream, cert);
|
return createDestination(destKeyStream, cert);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -52,20 +74,49 @@ class I2PClientImpl implements I2PClient {
|
|||||||
* Create the destination with the given payload and write it out along with
|
* Create the destination with the given payload and write it out along with
|
||||||
* the PrivateKey and SigningPrivateKey to the destKeyStream
|
* the PrivateKey and SigningPrivateKey to the destKeyStream
|
||||||
*
|
*
|
||||||
|
* If cert is a KeyCertificate, the signing keypair will be of the specified type.
|
||||||
|
* The KeyCertificate data must be .............................
|
||||||
|
* The padding if any will be randomized. The extra key data if any will be set in the
|
||||||
|
* key cert.
|
||||||
|
*
|
||||||
* @param destKeyStream location to write out the destination, PrivateKey, and SigningPrivateKey,
|
* @param destKeyStream location to write out the destination, PrivateKey, and SigningPrivateKey,
|
||||||
* format is specified in {@link net.i2p.data.PrivateKeyFile PrivateKeyFile}
|
* format is specified in {@link net.i2p.data.PrivateKeyFile PrivateKeyFile}
|
||||||
*/
|
*/
|
||||||
public Destination createDestination(OutputStream destKeyStream, Certificate cert) throws I2PException, IOException {
|
public Destination createDestination(OutputStream destKeyStream, Certificate cert) throws I2PException, IOException {
|
||||||
Destination d = new Destination();
|
Destination d = new Destination();
|
||||||
d.setCertificate(cert);
|
|
||||||
Object keypair[] = KeyGenerator.getInstance().generatePKIKeypair();
|
Object keypair[] = KeyGenerator.getInstance().generatePKIKeypair();
|
||||||
PublicKey publicKey = (PublicKey) keypair[0];
|
PublicKey publicKey = (PublicKey) keypair[0];
|
||||||
PrivateKey privateKey = (PrivateKey) keypair[1];
|
PrivateKey privateKey = (PrivateKey) keypair[1];
|
||||||
Object signingKeys[] = KeyGenerator.getInstance().generateSigningKeypair();
|
SimpleDataStructure signingKeys[];
|
||||||
|
if (cert.getCertificateType() == Certificate.CERTIFICATE_TYPE_KEY) {
|
||||||
|
KeyCertificate kcert = cert.toKeyCertificate();
|
||||||
|
SigType type = kcert.getSigType();
|
||||||
|
try {
|
||||||
|
signingKeys = KeyGenerator.getInstance().generateSigningKeys(type);
|
||||||
|
} catch (GeneralSecurityException gse) {
|
||||||
|
throw new I2PException("keygen fail", gse);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
signingKeys = KeyGenerator.getInstance().generateSigningKeys();
|
||||||
|
}
|
||||||
SigningPublicKey signingPubKey = (SigningPublicKey) signingKeys[0];
|
SigningPublicKey signingPubKey = (SigningPublicKey) signingKeys[0];
|
||||||
SigningPrivateKey signingPrivKey = (SigningPrivateKey) signingKeys[1];
|
SigningPrivateKey signingPrivKey = (SigningPrivateKey) signingKeys[1];
|
||||||
d.setPublicKey(publicKey);
|
d.setPublicKey(publicKey);
|
||||||
d.setSigningPublicKey(signingPubKey);
|
d.setSigningPublicKey(signingPubKey);
|
||||||
|
if (cert.getCertificateType() == Certificate.CERTIFICATE_TYPE_KEY) {
|
||||||
|
// fix up key certificate or padding
|
||||||
|
KeyCertificate kcert = cert.toKeyCertificate();
|
||||||
|
SigType type = kcert.getSigType();
|
||||||
|
int len = type.getPubkeyLen();
|
||||||
|
if (len < 128) {
|
||||||
|
byte[] pad = new byte[128 - len];
|
||||||
|
RandomSource.getInstance().nextBytes(pad);
|
||||||
|
d.setPadding(pad);
|
||||||
|
} else if (len > 128) {
|
||||||
|
System.arraycopy(signingPubKey.getData(), 128, kcert.getPayload(), KeyCertificate.HEADER_LENGTH, len - 128);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
d.setCertificate(cert);
|
||||||
|
|
||||||
d.writeBytes(destKeyStream);
|
d.writeBytes(destKeyStream);
|
||||||
privateKey.writeBytes(destKeyStream);
|
privateKey.writeBytes(destKeyStream);
|
||||||
|
@ -67,7 +67,7 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
|
|||||||
/** private key for decryption */
|
/** private key for decryption */
|
||||||
private final PrivateKey _privateKey;
|
private final PrivateKey _privateKey;
|
||||||
/** private key for signing */
|
/** private key for signing */
|
||||||
private final SigningPrivateKey _signingPrivateKey;
|
private /* final */ SigningPrivateKey _signingPrivateKey;
|
||||||
/** configuration options */
|
/** configuration options */
|
||||||
private final Properties _options;
|
private final Properties _options;
|
||||||
/** this session's Id */
|
/** this session's Id */
|
||||||
@ -390,6 +390,7 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
|
|||||||
private void readDestination(InputStream destKeyStream) throws DataFormatException, IOException {
|
private void readDestination(InputStream destKeyStream) throws DataFormatException, IOException {
|
||||||
_myDestination.readBytes(destKeyStream);
|
_myDestination.readBytes(destKeyStream);
|
||||||
_privateKey.readBytes(destKeyStream);
|
_privateKey.readBytes(destKeyStream);
|
||||||
|
_signingPrivateKey = new SigningPrivateKey(_myDestination.getSigningPublicKey().getType());
|
||||||
_signingPrivateKey.readBytes(destKeyStream);
|
_signingPrivateKey.readBytes(destKeyStream);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,6 +12,7 @@ import java.util.Properties;
|
|||||||
|
|
||||||
import net.i2p.I2PAppContext;
|
import net.i2p.I2PAppContext;
|
||||||
import net.i2p.I2PException;
|
import net.i2p.I2PException;
|
||||||
|
import net.i2p.crypto.SigType;
|
||||||
import net.i2p.data.Certificate;
|
import net.i2p.data.Certificate;
|
||||||
import net.i2p.data.Destination;
|
import net.i2p.data.Destination;
|
||||||
|
|
||||||
@ -20,14 +21,30 @@ import net.i2p.data.Destination;
|
|||||||
* just used to talk to the router.
|
* just used to talk to the router.
|
||||||
*/
|
*/
|
||||||
public class I2PSimpleClient implements I2PClient {
|
public class I2PSimpleClient implements I2PClient {
|
||||||
/** @deprecated Don't do this */
|
|
||||||
|
/**
|
||||||
|
* @deprecated Don't do this
|
||||||
|
* @throws UnsupportedOperationException always
|
||||||
|
*/
|
||||||
public Destination createDestination(OutputStream destKeyStream) throws I2PException, IOException {
|
public Destination createDestination(OutputStream destKeyStream) throws I2PException, IOException {
|
||||||
return null;
|
throw new UnsupportedOperationException();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @deprecated or this */
|
/**
|
||||||
|
* @deprecated Don't do this
|
||||||
|
* @throws UnsupportedOperationException always
|
||||||
|
* @since 0.9.12
|
||||||
|
*/
|
||||||
|
public Destination createDestination(OutputStream destKeyStream, SigType type) throws I2PException, IOException {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated Don't do this
|
||||||
|
* @throws UnsupportedOperationException always
|
||||||
|
*/
|
||||||
public Destination createDestination(OutputStream destKeyStream, Certificate cert) throws I2PException, IOException {
|
public Destination createDestination(OutputStream destKeyStream, Certificate cert) throws I2PException, IOException {
|
||||||
return null;
|
throw new UnsupportedOperationException();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -37,6 +54,7 @@ public class I2PSimpleClient implements I2PClient {
|
|||||||
public I2PSession createSession(InputStream destKeyStream, Properties options) throws I2PSessionException {
|
public I2PSession createSession(InputStream destKeyStream, Properties options) throws I2PSessionException {
|
||||||
return createSession(I2PAppContext.getGlobalContext(), options);
|
return createSession(I2PAppContext.getGlobalContext(), options);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new session (though do not connect it yet)
|
* Create a new session (though do not connect it yet)
|
||||||
*
|
*
|
||||||
|
@ -14,6 +14,7 @@ import java.util.concurrent.ConcurrentHashMap;
|
|||||||
|
|
||||||
import net.i2p.I2PAppContext;
|
import net.i2p.I2PAppContext;
|
||||||
import net.i2p.crypto.KeyGenerator;
|
import net.i2p.crypto.KeyGenerator;
|
||||||
|
import net.i2p.crypto.SigType;
|
||||||
import net.i2p.data.DataFormatException;
|
import net.i2p.data.DataFormatException;
|
||||||
import net.i2p.data.DataHelper;
|
import net.i2p.data.DataHelper;
|
||||||
import net.i2p.data.Destination;
|
import net.i2p.data.Destination;
|
||||||
@ -105,6 +106,15 @@ class RequestLeaseSetMessageHandler extends HandlerImpl {
|
|||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
leaseSet.sign(session.getPrivateKey());
|
leaseSet.sign(session.getPrivateKey());
|
||||||
|
// Workaround for unparsable serialized signing private key for revocation
|
||||||
|
// Send him a dummy DSA_SHA1 private key since it's unused anyway
|
||||||
|
// See CreateLeaseSetMessage.doReadMessage()
|
||||||
|
SigningPrivateKey spk = li.getSigningPrivateKey();
|
||||||
|
if (!_context.isRouterContext() && spk.getType() != SigType.DSA_SHA1) {
|
||||||
|
byte[] dummy = new byte[SigningPrivateKey.KEYSIZE_BYTES];
|
||||||
|
_context.random().nextBytes(dummy);
|
||||||
|
spk = new SigningPrivateKey(dummy);
|
||||||
|
}
|
||||||
session.getProducer().createLeaseSet(session, leaseSet, li.getSigningPrivateKey(), li.getPrivateKey());
|
session.getProducer().createLeaseSet(session, leaseSet, li.getSigningPrivateKey(), li.getPrivateKey());
|
||||||
session.setLeaseSet(leaseSet);
|
session.setLeaseSet(leaseSet);
|
||||||
} catch (DataFormatException dfe) {
|
} catch (DataFormatException dfe) {
|
||||||
|
@ -14,6 +14,7 @@ import java.io.IOException;
|
|||||||
import net.i2p.I2PAppContext;
|
import net.i2p.I2PAppContext;
|
||||||
import net.i2p.crypto.DSAEngine;
|
import net.i2p.crypto.DSAEngine;
|
||||||
import net.i2p.crypto.SHA256Generator;
|
import net.i2p.crypto.SHA256Generator;
|
||||||
|
import net.i2p.crypto.SigType;
|
||||||
import net.i2p.data.DataFormatException;
|
import net.i2p.data.DataFormatException;
|
||||||
import net.i2p.data.Destination;
|
import net.i2p.data.Destination;
|
||||||
import net.i2p.data.Hash;
|
import net.i2p.data.Hash;
|
||||||
@ -67,7 +68,10 @@ public final class I2PDatagramDissector {
|
|||||||
try {
|
try {
|
||||||
// read destination
|
// read destination
|
||||||
rxDest = Destination.create(dgStream);
|
rxDest = Destination.create(dgStream);
|
||||||
rxSign = new Signature();
|
SigType type = rxDest.getSigningPublicKey().getType();
|
||||||
|
if (type == null)
|
||||||
|
throw new DataFormatException("unsupported sig type");
|
||||||
|
rxSign = new Signature(type);
|
||||||
// read signature
|
// read signature
|
||||||
rxSign.readBytes(dgStream);
|
rxSign.readBytes(dgStream);
|
||||||
|
|
||||||
|
@ -89,13 +89,20 @@ public class DSAEngine {
|
|||||||
* Uses TheCrypto code for DSA-SHA1 unless configured to use the java.security libraries.
|
* Uses TheCrypto code for DSA-SHA1 unless configured to use the java.security libraries.
|
||||||
*/
|
*/
|
||||||
public boolean verifySignature(Signature signature, byte signedData[], SigningPublicKey verifyingKey) {
|
public boolean verifySignature(Signature signature, byte signedData[], SigningPublicKey verifyingKey) {
|
||||||
|
return verifySignature(signature, signedData, 0, signedData.length, verifyingKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verify using any sig type as of 0.9.12 (DSA only prior to that)
|
||||||
|
*/
|
||||||
|
public boolean verifySignature(Signature signature, byte signedData[], int offset, int size, SigningPublicKey verifyingKey) {
|
||||||
boolean rv;
|
boolean rv;
|
||||||
SigType type = signature.getType();
|
SigType type = signature.getType();
|
||||||
if (type != verifyingKey.getType())
|
if (type != verifyingKey.getType())
|
||||||
throw new IllegalArgumentException("type mismatch sig=" + signature.getType() + " key=" + verifyingKey.getType());
|
throw new IllegalArgumentException("type mismatch sig=" + signature.getType() + " key=" + verifyingKey.getType());
|
||||||
if (type != SigType.DSA_SHA1) {
|
if (type != SigType.DSA_SHA1) {
|
||||||
try {
|
try {
|
||||||
rv = altVerifySig(signature, signedData, verifyingKey);
|
rv = altVerifySig(signature, signedData, offset, size, verifyingKey);
|
||||||
if ((!rv) && _log.shouldLog(Log.WARN))
|
if ((!rv) && _log.shouldLog(Log.WARN))
|
||||||
_log.warn(type + " Sig Verify Fail");
|
_log.warn(type + " Sig Verify Fail");
|
||||||
return rv;
|
return rv;
|
||||||
@ -107,7 +114,7 @@ public class DSAEngine {
|
|||||||
}
|
}
|
||||||
if (_useJavaLibs) {
|
if (_useJavaLibs) {
|
||||||
try {
|
try {
|
||||||
rv = altVerifySigSHA1(signature, signedData, verifyingKey);
|
rv = altVerifySigSHA1(signature, signedData, offset, size, verifyingKey);
|
||||||
if ((!rv) && _log.shouldLog(Log.WARN))
|
if ((!rv) && _log.shouldLog(Log.WARN))
|
||||||
_log.warn("Lib DSA Sig Verify Fail");
|
_log.warn("Lib DSA Sig Verify Fail");
|
||||||
return rv;
|
return rv;
|
||||||
@ -117,19 +124,12 @@ public class DSAEngine {
|
|||||||
// now try TheCrypto
|
// now try TheCrypto
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
rv = verifySignature(signature, signedData, 0, signedData.length, verifyingKey);
|
rv = verifySignature(signature, calculateHash(signedData, offset, size), verifyingKey);
|
||||||
if ((!rv) && _log.shouldLog(Log.WARN))
|
if ((!rv) && _log.shouldLog(Log.WARN))
|
||||||
_log.warn("TheCrypto DSA Sig Verify Fail");
|
_log.warn("TheCrypto DSA Sig Verify Fail");
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Verify using DSA-SHA1 ONLY
|
|
||||||
*/
|
|
||||||
public boolean verifySignature(Signature signature, byte signedData[], int offset, int size, SigningPublicKey verifyingKey) {
|
|
||||||
return verifySignature(signature, calculateHash(signedData, offset, size), verifyingKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Verify using DSA-SHA1 ONLY
|
* Verify using DSA-SHA1 ONLY
|
||||||
*/
|
*/
|
||||||
@ -256,16 +256,26 @@ public class DSAEngine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sign using DSA-SHA1 or ECDSA.
|
* Sign using any key type.
|
||||||
* Uses TheCrypto code unless configured to use the java.security libraries.
|
* Uses TheCrypto code unless configured to use the java.security libraries.
|
||||||
*
|
*
|
||||||
* @return null on error
|
* @return null on error
|
||||||
*/
|
*/
|
||||||
public Signature sign(byte data[], SigningPrivateKey signingKey) {
|
public Signature sign(byte data[], SigningPrivateKey signingKey) {
|
||||||
|
return sign(data, 0, data.length, signingKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sign using any key type as of 0.9.12 (DSA-SHA1 only prior to that)
|
||||||
|
*
|
||||||
|
* @return null on error
|
||||||
|
*/
|
||||||
|
public Signature sign(byte data[], int offset, int length, SigningPrivateKey signingKey) {
|
||||||
|
if ((signingKey == null) || (data == null) || (data.length <= 0)) return null;
|
||||||
SigType type = signingKey.getType();
|
SigType type = signingKey.getType();
|
||||||
if (type != SigType.DSA_SHA1) {
|
if (type != SigType.DSA_SHA1) {
|
||||||
try {
|
try {
|
||||||
return altSign(data, signingKey);
|
return altSign(data, offset, length, signingKey);
|
||||||
} catch (GeneralSecurityException gse) {
|
} catch (GeneralSecurityException gse) {
|
||||||
if (_log.shouldLog(Log.WARN))
|
if (_log.shouldLog(Log.WARN))
|
||||||
_log.warn(type + " Sign Fail", gse);
|
_log.warn(type + " Sign Fail", gse);
|
||||||
@ -274,23 +284,13 @@ public class DSAEngine {
|
|||||||
}
|
}
|
||||||
if (_useJavaLibs) {
|
if (_useJavaLibs) {
|
||||||
try {
|
try {
|
||||||
return altSignSHA1(data, signingKey);
|
return altSignSHA1(data, offset, length, signingKey);
|
||||||
} catch (GeneralSecurityException gse) {
|
} catch (GeneralSecurityException gse) {
|
||||||
if (_log.shouldLog(Log.WARN))
|
if (_log.shouldLog(Log.WARN))
|
||||||
_log.warn("Lib Sign Fail, privkey = " + signingKey, gse);
|
_log.warn("Lib Sign Fail, privkey = " + signingKey, gse);
|
||||||
// now try TheCrypto
|
// now try TheCrypto
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return sign(data, 0, data.length, signingKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sign using DSA-SHA1 ONLY
|
|
||||||
*
|
|
||||||
* @return null on error
|
|
||||||
*/
|
|
||||||
public Signature sign(byte data[], int offset, int length, SigningPrivateKey signingKey) {
|
|
||||||
if ((signingKey == null) || (data == null) || (data.length <= 0)) return null;
|
|
||||||
SHA1Hash h = calculateHash(data, offset, length);
|
SHA1Hash h = calculateHash(data, offset, length);
|
||||||
return sign(h, signingKey);
|
return sign(h, signingKey);
|
||||||
}
|
}
|
||||||
@ -495,20 +495,20 @@ public class DSAEngine {
|
|||||||
/**
|
/**
|
||||||
* Generic verify DSA_SHA1, ECDSA, or RSA
|
* Generic verify DSA_SHA1, ECDSA, or RSA
|
||||||
* @throws GeneralSecurityException if algorithm unvailable or on other errors
|
* @throws GeneralSecurityException if algorithm unvailable or on other errors
|
||||||
* @since 0.9.9
|
* @since 0.9.9 added off/len 0.9.12
|
||||||
*/
|
*/
|
||||||
private boolean altVerifySig(Signature signature, byte[] data, SigningPublicKey verifyingKey)
|
private boolean altVerifySig(Signature signature, byte[] data, int offset, int len, SigningPublicKey verifyingKey)
|
||||||
throws GeneralSecurityException {
|
throws GeneralSecurityException {
|
||||||
SigType type = signature.getType();
|
SigType type = signature.getType();
|
||||||
if (type != verifyingKey.getType())
|
if (type != verifyingKey.getType())
|
||||||
throw new IllegalArgumentException("type mismatch sig=" + type + " key=" + verifyingKey.getType());
|
throw new IllegalArgumentException("type mismatch sig=" + type + " key=" + verifyingKey.getType());
|
||||||
if (type == SigType.DSA_SHA1)
|
if (type == SigType.DSA_SHA1)
|
||||||
return altVerifySigSHA1(signature, data, verifyingKey);
|
return altVerifySigSHA1(signature, data, offset, len, verifyingKey);
|
||||||
|
|
||||||
java.security.Signature jsig = java.security.Signature.getInstance(type.getAlgorithmName());
|
java.security.Signature jsig = java.security.Signature.getInstance(type.getAlgorithmName());
|
||||||
PublicKey pubKey = SigUtil.toJavaKey(verifyingKey);
|
PublicKey pubKey = SigUtil.toJavaKey(verifyingKey);
|
||||||
jsig.initVerify(pubKey);
|
jsig.initVerify(pubKey);
|
||||||
jsig.update(data);
|
jsig.update(data, offset, len);
|
||||||
boolean rv = jsig.verify(SigUtil.toJavaSig(signature));
|
boolean rv = jsig.verify(SigUtil.toJavaSig(signature));
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
@ -555,13 +555,14 @@ public class DSAEngine {
|
|||||||
/**
|
/**
|
||||||
* Alternate to verifySignature() using java.security libraries.
|
* Alternate to verifySignature() using java.security libraries.
|
||||||
* @throws GeneralSecurityException if algorithm unvailable or on other errors
|
* @throws GeneralSecurityException if algorithm unvailable or on other errors
|
||||||
* @since 0.8.7
|
* @since 0.8.7 added off/len 0.9.12
|
||||||
*/
|
*/
|
||||||
private boolean altVerifySigSHA1(Signature signature, byte[] data, SigningPublicKey verifyingKey) throws GeneralSecurityException {
|
private boolean altVerifySigSHA1(Signature signature, byte[] data, int offset,
|
||||||
|
int len, SigningPublicKey verifyingKey) throws GeneralSecurityException {
|
||||||
java.security.Signature jsig = java.security.Signature.getInstance("SHA1withDSA");
|
java.security.Signature jsig = java.security.Signature.getInstance("SHA1withDSA");
|
||||||
PublicKey pubKey = SigUtil.toJavaDSAKey(verifyingKey);
|
PublicKey pubKey = SigUtil.toJavaDSAKey(verifyingKey);
|
||||||
jsig.initVerify(pubKey);
|
jsig.initVerify(pubKey);
|
||||||
jsig.update(data);
|
jsig.update(data, offset, len);
|
||||||
boolean rv = jsig.verify(SigUtil.toJavaSig(signature));
|
boolean rv = jsig.verify(SigUtil.toJavaSig(signature));
|
||||||
//if (!rv) {
|
//if (!rv) {
|
||||||
// System.out.println("BAD SIG\n" + net.i2p.util.HexDump.dump(signature.getData()));
|
// System.out.println("BAD SIG\n" + net.i2p.util.HexDump.dump(signature.getData()));
|
||||||
@ -573,17 +574,18 @@ public class DSAEngine {
|
|||||||
/**
|
/**
|
||||||
* Generic sign DSA_SHA1, ECDSA, or RSA
|
* Generic sign DSA_SHA1, ECDSA, or RSA
|
||||||
* @throws GeneralSecurityException if algorithm unvailable or on other errors
|
* @throws GeneralSecurityException if algorithm unvailable or on other errors
|
||||||
* @since 0.9.9
|
* @since 0.9.9 added off/len 0.9.12
|
||||||
*/
|
*/
|
||||||
private Signature altSign(byte[] data, SigningPrivateKey privateKey) throws GeneralSecurityException {
|
private Signature altSign(byte[] data, int offset, int len,
|
||||||
|
SigningPrivateKey privateKey) throws GeneralSecurityException {
|
||||||
SigType type = privateKey.getType();
|
SigType type = privateKey.getType();
|
||||||
if (type == SigType.DSA_SHA1)
|
if (type == SigType.DSA_SHA1)
|
||||||
return altSignSHA1(data, privateKey);
|
return altSignSHA1(data, offset, len, privateKey);
|
||||||
|
|
||||||
java.security.Signature jsig = java.security.Signature.getInstance(type.getAlgorithmName());
|
java.security.Signature jsig = java.security.Signature.getInstance(type.getAlgorithmName());
|
||||||
PrivateKey privKey = SigUtil.toJavaKey(privateKey);
|
PrivateKey privKey = SigUtil.toJavaKey(privateKey);
|
||||||
jsig.initSign(privKey, _context.random());
|
jsig.initSign(privKey, _context.random());
|
||||||
jsig.update(data);
|
jsig.update(data, offset, len);
|
||||||
return SigUtil.fromJavaSig(jsig.sign(), type);
|
return SigUtil.fromJavaSig(jsig.sign(), type);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -622,13 +624,14 @@ public class DSAEngine {
|
|||||||
/**
|
/**
|
||||||
* Alternate to sign() using java.security libraries.
|
* Alternate to sign() using java.security libraries.
|
||||||
* @throws GeneralSecurityException if algorithm unvailable or on other errors
|
* @throws GeneralSecurityException if algorithm unvailable or on other errors
|
||||||
* @since 0.8.7
|
* @since 0.8.7 added off/len args 0.9.12
|
||||||
*/
|
*/
|
||||||
private Signature altSignSHA1(byte[] data, SigningPrivateKey privateKey) throws GeneralSecurityException {
|
private Signature altSignSHA1(byte[] data, int offset, int len,
|
||||||
|
SigningPrivateKey privateKey) throws GeneralSecurityException {
|
||||||
java.security.Signature jsig = java.security.Signature.getInstance("SHA1withDSA");
|
java.security.Signature jsig = java.security.Signature.getInstance("SHA1withDSA");
|
||||||
PrivateKey privKey = SigUtil.toJavaDSAKey(privateKey);
|
PrivateKey privKey = SigUtil.toJavaDSAKey(privateKey);
|
||||||
jsig.initSign(privKey, _context.random());
|
jsig.initSign(privKey, _context.random());
|
||||||
jsig.update(data);
|
jsig.update(data, offset, len);
|
||||||
return SigUtil.fromJavaSig(jsig.sign(), SigType.DSA_SHA1);
|
return SigUtil.fromJavaSig(jsig.sign(), SigType.DSA_SHA1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -530,23 +530,6 @@ public class SU3File {
|
|||||||
return buf.toString();
|
return buf.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param stype number or name
|
|
||||||
* @return null if not found
|
|
||||||
* @since 0.9.9
|
|
||||||
*/
|
|
||||||
private static SigType parseSigType(String stype) {
|
|
||||||
try {
|
|
||||||
return SigType.valueOf(stype.toUpperCase(Locale.US));
|
|
||||||
} catch (IllegalArgumentException iae) {
|
|
||||||
try {
|
|
||||||
int code = Integer.parseInt(stype);
|
|
||||||
return SigType.getByCode(code);
|
|
||||||
} catch (NumberFormatException nfe) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/**
|
/**
|
||||||
* @param stype number or name
|
* @param stype number or name
|
||||||
* @return null if not found
|
* @return null if not found
|
||||||
@ -639,7 +622,7 @@ public class SU3File {
|
|||||||
*/
|
*/
|
||||||
private static final boolean signCLI(String stype, String ctype, String inputFile, String signedFile,
|
private static final boolean signCLI(String stype, String ctype, String inputFile, String signedFile,
|
||||||
String privateKeyFile, String version, String signerName, String keypw) {
|
String privateKeyFile, String version, String signerName, String keypw) {
|
||||||
SigType type = stype == null ? SigType.getByCode(Integer.valueOf(DEFAULT_SIG_CODE)) : parseSigType(stype);
|
SigType type = stype == null ? SigType.getByCode(Integer.valueOf(DEFAULT_SIG_CODE)) : SigType.parseSigType(stype);
|
||||||
if (type == null) {
|
if (type == null) {
|
||||||
System.out.println("Signature type " + stype + " is not supported");
|
System.out.println("Signature type " + stype + " is not supported");
|
||||||
return false;
|
return false;
|
||||||
@ -731,7 +714,7 @@ public class SU3File {
|
|||||||
* @since 0.9.9
|
* @since 0.9.9
|
||||||
*/
|
*/
|
||||||
private static final boolean genKeysCLI(String stype, String publicKeyFile, String privateKeyFile, String alias) {
|
private static final boolean genKeysCLI(String stype, String publicKeyFile, String privateKeyFile, String alias) {
|
||||||
SigType type = stype == null ? SigType.getByCode(Integer.valueOf(DEFAULT_SIG_CODE)) : parseSigType(stype);
|
SigType type = stype == null ? SigType.getByCode(Integer.valueOf(DEFAULT_SIG_CODE)) : SigType.parseSigType(stype);
|
||||||
if (type == null) {
|
if (type == null) {
|
||||||
System.out.println("Signature type " + stype + " is not supported");
|
System.out.println("Signature type " + stype + " is not supported");
|
||||||
return false;
|
return false;
|
||||||
|
@ -5,6 +5,7 @@ import java.security.NoSuchAlgorithmException;
|
|||||||
import java.security.spec.AlgorithmParameterSpec;
|
import java.security.spec.AlgorithmParameterSpec;
|
||||||
import java.security.spec.InvalidParameterSpecException;
|
import java.security.spec.InvalidParameterSpecException;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import net.i2p.data.Hash;
|
import net.i2p.data.Hash;
|
||||||
@ -170,4 +171,24 @@ public enum SigType {
|
|||||||
public static SigType getByCode(int code) {
|
public static SigType getByCode(int code) {
|
||||||
return BY_CODE.get(Integer.valueOf(code));
|
return BY_CODE.get(Integer.valueOf(code));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convenience for user apps
|
||||||
|
*
|
||||||
|
* @param stype number or name
|
||||||
|
* @return null if not found
|
||||||
|
* @since 0.9.9 moved from SU3File in 0.9.12
|
||||||
|
*/
|
||||||
|
public static SigType parseSigType(String stype) {
|
||||||
|
try {
|
||||||
|
return valueOf(stype.toUpperCase(Locale.US));
|
||||||
|
} catch (IllegalArgumentException iae) {
|
||||||
|
try {
|
||||||
|
int code = Integer.parseInt(stype);
|
||||||
|
return getByCode(code);
|
||||||
|
} catch (NumberFormatException nfe) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -42,6 +42,8 @@ public class Certificate extends DataStructureImpl {
|
|||||||
public final static int CERTIFICATE_LENGTH_SIGNED_WITH_HASH = Signature.SIGNATURE_BYTES + Hash.HASH_LENGTH;
|
public final static int CERTIFICATE_LENGTH_SIGNED_WITH_HASH = Signature.SIGNATURE_BYTES + Hash.HASH_LENGTH;
|
||||||
/** Contains multiple certs */
|
/** Contains multiple certs */
|
||||||
public final static int CERTIFICATE_TYPE_MULTIPLE = 4;
|
public final static int CERTIFICATE_TYPE_MULTIPLE = 4;
|
||||||
|
/** @since 0.9.12 */
|
||||||
|
public final static int CERTIFICATE_TYPE_KEY = 5;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If null cert, return immutable static instance, else create new
|
* If null cert, return immutable static instance, else create new
|
||||||
@ -58,6 +60,13 @@ public class Certificate extends DataStructureImpl {
|
|||||||
return new Certificate(type, null);
|
return new Certificate(type, null);
|
||||||
byte[] payload = new byte[length];
|
byte[] payload = new byte[length];
|
||||||
System.arraycopy(data, off + 3, payload, 0, length);
|
System.arraycopy(data, off + 3, payload, 0, length);
|
||||||
|
if (type == CERTIFICATE_TYPE_KEY) {
|
||||||
|
try {
|
||||||
|
return new KeyCertificate(payload);
|
||||||
|
} catch (DataFormatException dfe) {
|
||||||
|
throw new IllegalArgumentException(dfe);
|
||||||
|
}
|
||||||
|
}
|
||||||
return new Certificate(type, payload);
|
return new Certificate(type, payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -77,13 +86,20 @@ public class Certificate extends DataStructureImpl {
|
|||||||
int read = DataHelper.read(in, payload);
|
int read = DataHelper.read(in, payload);
|
||||||
if (read != length)
|
if (read != length)
|
||||||
throw new DataFormatException("Not enough bytes for the payload (read: " + read + " length: " + length + ')');
|
throw new DataFormatException("Not enough bytes for the payload (read: " + read + " length: " + length + ')');
|
||||||
|
if (type == CERTIFICATE_TYPE_KEY)
|
||||||
|
return new KeyCertificate(payload);
|
||||||
return new Certificate(type, payload);
|
return new Certificate(type, payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Certificate() {
|
public Certificate() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws IllegalArgumentException if type < 0
|
||||||
|
*/
|
||||||
public Certificate(int type, byte[] payload) {
|
public Certificate(int type, byte[] payload) {
|
||||||
|
if (type < 0)
|
||||||
|
throw new IllegalArgumentException();
|
||||||
_type = type;
|
_type = type;
|
||||||
_payload = payload;
|
_payload = payload;
|
||||||
}
|
}
|
||||||
@ -93,7 +109,15 @@ public class Certificate extends DataStructureImpl {
|
|||||||
return _type;
|
return _type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws IllegalArgumentException if type < 0
|
||||||
|
* @throws IllegalStateException if already set
|
||||||
|
*/
|
||||||
public void setCertificateType(int type) {
|
public void setCertificateType(int type) {
|
||||||
|
if (type < 0)
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
if (_type != 0 && _type != type)
|
||||||
|
throw new IllegalStateException("already set");
|
||||||
_type = type;
|
_type = type;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -101,11 +125,21 @@ public class Certificate extends DataStructureImpl {
|
|||||||
return _payload;
|
return _payload;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws IllegalStateException if already set
|
||||||
|
*/
|
||||||
public void setPayload(byte[] payload) {
|
public void setPayload(byte[] payload) {
|
||||||
|
if (_payload != null)
|
||||||
|
throw new IllegalStateException("already set");
|
||||||
_payload = payload;
|
_payload = payload;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws IllegalStateException if already set
|
||||||
|
*/
|
||||||
public void readBytes(InputStream in) throws DataFormatException, IOException {
|
public void readBytes(InputStream in) throws DataFormatException, IOException {
|
||||||
|
if (_type != 0 || _payload != null)
|
||||||
|
throw new IllegalStateException("already set");
|
||||||
_type = (int) DataHelper.readLong(in, 1);
|
_type = (int) DataHelper.readLong(in, 1);
|
||||||
int length = (int) DataHelper.readLong(in, 2);
|
int length = (int) DataHelper.readLong(in, 2);
|
||||||
if (length > 0) {
|
if (length > 0) {
|
||||||
@ -149,7 +183,12 @@ public class Certificate extends DataStructureImpl {
|
|||||||
return cur - offset;
|
return cur - offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws IllegalStateException if already set
|
||||||
|
*/
|
||||||
public int readBytes(byte source[], int offset) throws DataFormatException {
|
public int readBytes(byte source[], int offset) throws DataFormatException {
|
||||||
|
if (_type != 0 || _payload != null)
|
||||||
|
throw new IllegalStateException("already set");
|
||||||
if (source == null) throw new DataFormatException("Cert is null");
|
if (source == null) throw new DataFormatException("Cert is null");
|
||||||
if (source.length < offset + 3)
|
if (source.length < offset + 3)
|
||||||
throw new DataFormatException("Cert is too small [" + source.length + " off=" + offset + "]");
|
throw new DataFormatException("Cert is too small [" + source.length + " off=" + offset + "]");
|
||||||
@ -175,6 +214,18 @@ public class Certificate extends DataStructureImpl {
|
|||||||
return 1 + 2 + (_payload != null ? _payload.length : 0);
|
return 1 + 2 + (_payload != null ? _payload.length : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Up-convert this to a KeyCertificate
|
||||||
|
*
|
||||||
|
* @throws DataFormatException if cert type != CERTIFICATE_TYPE_KEY
|
||||||
|
* @since 0.9.12
|
||||||
|
*/
|
||||||
|
public KeyCertificate toKeyCertificate() throws DataFormatException {
|
||||||
|
if (_type != CERTIFICATE_TYPE_KEY)
|
||||||
|
throw new DataFormatException("type");
|
||||||
|
return new KeyCertificate(this);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object object) {
|
public boolean equals(Object object) {
|
||||||
if (object == this) return true;
|
if (object == this) return true;
|
||||||
@ -194,6 +245,8 @@ public class Certificate extends DataStructureImpl {
|
|||||||
buf.append("[Certificate: type: ");
|
buf.append("[Certificate: type: ");
|
||||||
if (getCertificateType() == CERTIFICATE_TYPE_NULL)
|
if (getCertificateType() == CERTIFICATE_TYPE_NULL)
|
||||||
buf.append("Null certificate");
|
buf.append("Null certificate");
|
||||||
|
else if (getCertificateType() == CERTIFICATE_TYPE_KEY)
|
||||||
|
buf.append("Key certificate");
|
||||||
else if (getCertificateType() == CERTIFICATE_TYPE_HASHCASH)
|
else if (getCertificateType() == CERTIFICATE_TYPE_HASHCASH)
|
||||||
buf.append("Hashcash certificate");
|
buf.append("Hashcash certificate");
|
||||||
else if (getCertificateType() == CERTIFICATE_TYPE_HIDDEN)
|
else if (getCertificateType() == CERTIFICATE_TYPE_HIDDEN)
|
||||||
|
@ -57,6 +57,16 @@ public class Destination extends KeysAndCert {
|
|||||||
PublicKey pk = PublicKey.create(in);
|
PublicKey pk = PublicKey.create(in);
|
||||||
SigningPublicKey sk = SigningPublicKey.create(in);
|
SigningPublicKey sk = SigningPublicKey.create(in);
|
||||||
Certificate c = Certificate.create(in);
|
Certificate c = Certificate.create(in);
|
||||||
|
byte[] padding;
|
||||||
|
if (c.getCertificateType() == Certificate.CERTIFICATE_TYPE_KEY) {
|
||||||
|
// convert SPK to new SPK and padding
|
||||||
|
KeyCertificate kcert = c.toKeyCertificate();
|
||||||
|
padding = sk.getPadding(kcert);
|
||||||
|
sk = sk.toTypedKey(kcert);
|
||||||
|
c = kcert;
|
||||||
|
} else {
|
||||||
|
padding = null;
|
||||||
|
}
|
||||||
Destination rv;
|
Destination rv;
|
||||||
synchronized(_cache) {
|
synchronized(_cache) {
|
||||||
rv = _cache.get(sk);
|
rv = _cache.get(sk);
|
||||||
@ -67,7 +77,7 @@ public class Destination extends KeysAndCert {
|
|||||||
}
|
}
|
||||||
//if (STATS)
|
//if (STATS)
|
||||||
// I2PAppContext.getGlobalContext().statManager().addRateData("DestCache", 0);
|
// I2PAppContext.getGlobalContext().statManager().addRateData("DestCache", 0);
|
||||||
rv = new Destination(pk, sk, c);
|
rv = new Destination(pk, sk, c, padding);
|
||||||
_cache.put(sk, rv);
|
_cache.put(sk, rv);
|
||||||
}
|
}
|
||||||
return rv;
|
return rv;
|
||||||
@ -86,10 +96,11 @@ public class Destination extends KeysAndCert {
|
|||||||
/**
|
/**
|
||||||
* @since 0.9.9
|
* @since 0.9.9
|
||||||
*/
|
*/
|
||||||
private Destination(PublicKey pk, SigningPublicKey sk, Certificate c) {
|
private Destination(PublicKey pk, SigningPublicKey sk, Certificate c, byte[] padding) {
|
||||||
_publicKey = pk;
|
_publicKey = pk;
|
||||||
_signingKey = sk;
|
_signingKey = sk;
|
||||||
_certificate = c;
|
_certificate = c;
|
||||||
|
_padding = padding;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -100,14 +111,19 @@ public class Destination extends KeysAndCert {
|
|||||||
int cur = offset;
|
int cur = offset;
|
||||||
System.arraycopy(_publicKey.getData(), 0, target, cur, PublicKey.KEYSIZE_BYTES);
|
System.arraycopy(_publicKey.getData(), 0, target, cur, PublicKey.KEYSIZE_BYTES);
|
||||||
cur += PublicKey.KEYSIZE_BYTES;
|
cur += PublicKey.KEYSIZE_BYTES;
|
||||||
System.arraycopy(_signingKey.getData(), 0, target, cur, SigningPublicKey.KEYSIZE_BYTES);
|
if (_padding != null) {
|
||||||
cur += SigningPublicKey.KEYSIZE_BYTES;
|
System.arraycopy(_padding, 0, target, cur, _padding.length);
|
||||||
|
cur += _padding.length;
|
||||||
|
}
|
||||||
|
System.arraycopy(_signingKey.getData(), 0, target, cur, _signingKey.length());
|
||||||
|
cur += _signingKey.length();
|
||||||
cur += _certificate.writeBytes(target, cur);
|
cur += _certificate.writeBytes(target, cur);
|
||||||
return cur - offset;
|
return cur - offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @deprecated was used only by Packet.java in streaming, now unused
|
* deprecated was used only by Packet.java in streaming, now unused
|
||||||
|
* Warning - used by i2p-bote. Does NOT support alternate key types. DSA-SHA1 only.
|
||||||
*
|
*
|
||||||
* @throws IllegalStateException if data already set
|
* @throws IllegalStateException if data already set
|
||||||
*/
|
*/
|
||||||
@ -132,7 +148,16 @@ public class Destination extends KeysAndCert {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public int size() {
|
public int size() {
|
||||||
return PublicKey.KEYSIZE_BYTES + SigningPublicKey.KEYSIZE_BYTES + _certificate.size();
|
int rv = PublicKey.KEYSIZE_BYTES + _signingKey.length();
|
||||||
|
if (_certificate.getCertificateType() == Certificate.CERTIFICATE_TYPE_KEY) {
|
||||||
|
// cert data included in keys
|
||||||
|
rv += 7;
|
||||||
|
if (_padding != null)
|
||||||
|
rv += _padding.length;
|
||||||
|
} else {
|
||||||
|
rv += _certificate.size();
|
||||||
|
}
|
||||||
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
252
core/java/src/net/i2p/data/KeyCertificate.java
Normal file
252
core/java/src/net/i2p/data/KeyCertificate.java
Normal file
@ -0,0 +1,252 @@
|
|||||||
|
package net.i2p.data;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
|
||||||
|
import net.i2p.crypto.SigType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This certificate type gets its own class because it's going to be used a lot.
|
||||||
|
*
|
||||||
|
* The crypto type is assumed to be always 0x0000 (ElG) for now.
|
||||||
|
*
|
||||||
|
* @since 0.9.12
|
||||||
|
*/
|
||||||
|
public class KeyCertificate extends Certificate {
|
||||||
|
|
||||||
|
public static final int HEADER_LENGTH = 4;
|
||||||
|
|
||||||
|
public static final KeyCertificate ELG_ECDSA256_CERT;
|
||||||
|
static {
|
||||||
|
KeyCertificate kc;
|
||||||
|
try {
|
||||||
|
kc = new ECDSA256Cert();
|
||||||
|
} catch (DataFormatException dfe) {
|
||||||
|
kc = null; // won't happen
|
||||||
|
}
|
||||||
|
ELG_ECDSA256_CERT = kc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param payload 4 bytes minimum if non-null
|
||||||
|
* @throws DataFormatException
|
||||||
|
*/
|
||||||
|
public KeyCertificate(byte[] payload) throws DataFormatException {
|
||||||
|
super(CERTIFICATE_TYPE_KEY, payload);
|
||||||
|
if (payload != null && payload.length < HEADER_LENGTH)
|
||||||
|
throw new DataFormatException("data");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A KeyCertificate with crypto type 0 (ElGamal)
|
||||||
|
* and the signature type and extra data from the given public key.
|
||||||
|
*
|
||||||
|
* @param sig non-null data non-null
|
||||||
|
* @throws IllegalArgumentException
|
||||||
|
*/
|
||||||
|
public KeyCertificate(SigningPublicKey spk) {
|
||||||
|
super(CERTIFICATE_TYPE_KEY, null);
|
||||||
|
if (spk == null || spk.getData() == null)
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
SigType type = spk.getType();
|
||||||
|
int len = type.getPubkeyLen();
|
||||||
|
int extra = Math.max(0, len - 128);
|
||||||
|
_payload = new byte[HEADER_LENGTH + extra];
|
||||||
|
int code = type.getCode();
|
||||||
|
_payload[0] = (byte) (code >> 8);
|
||||||
|
_payload[1] = (byte) (code & 0xff);
|
||||||
|
// 2 and 3 always 0, it is the only crypto code for now
|
||||||
|
if (extra > 0)
|
||||||
|
System.arraycopy(spk.getData(), 128, _payload, HEADER_LENGTH, extra);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A KeyCertificate with crypto type 0 (ElGamal)
|
||||||
|
* and the signature type as specified.
|
||||||
|
* Payload is created.
|
||||||
|
* If type.getPubkeyLen() is greater than 128, caller MUST
|
||||||
|
* fill in the extra key data in the payload.
|
||||||
|
*
|
||||||
|
* @param sig non-null data non-null
|
||||||
|
* @throws IllegalArgumentException
|
||||||
|
*/
|
||||||
|
public KeyCertificate(SigType type) {
|
||||||
|
super(CERTIFICATE_TYPE_KEY, null);
|
||||||
|
int len = type.getPubkeyLen();
|
||||||
|
int extra = Math.max(0, len - 128);
|
||||||
|
_payload = new byte[HEADER_LENGTH + extra];
|
||||||
|
int code = type.getCode();
|
||||||
|
_payload[0] = (byte) (code >> 8);
|
||||||
|
_payload[1] = (byte) (code & 0xff);
|
||||||
|
// 2 and 3 always 0, it is the only crypto code for now
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Up-convert a cert to this class
|
||||||
|
*
|
||||||
|
* @param cert payload 4 bytes minimum if non-null
|
||||||
|
* @throws DataFormatException if cert type != CERTIFICATE_TYPE_KEY
|
||||||
|
*/
|
||||||
|
public KeyCertificate(Certificate cert) throws DataFormatException {
|
||||||
|
this(cert.getPayload());
|
||||||
|
if (cert.getCertificateType() != CERTIFICATE_TYPE_KEY)
|
||||||
|
throw new DataFormatException("type");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return -1 if unset
|
||||||
|
*/
|
||||||
|
public int getSigTypeCode() {
|
||||||
|
if (_payload == null)
|
||||||
|
return -1;
|
||||||
|
return ((_payload[0] & 0xff) << 8) | (_payload[1] & 0xff);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return -1 if unset
|
||||||
|
*/
|
||||||
|
public int getCryptoTypeCode() {
|
||||||
|
if (_payload == null)
|
||||||
|
return -1;
|
||||||
|
return ((_payload[2] & 0xff) << 8) | (_payload[3] & 0xff);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return null if unset or unknown
|
||||||
|
*/
|
||||||
|
public SigType getSigType() {
|
||||||
|
return SigType.getByCode(getSigTypeCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Signing Key extra data, if any, is first in the array.
|
||||||
|
* Crypto Key extra data, if any, is second in the array,
|
||||||
|
* at offset max(0, 128 - getSigType().getPubkeyLen()
|
||||||
|
*
|
||||||
|
* @return null if unset or none
|
||||||
|
*/
|
||||||
|
public byte[] getExtraKeyData() {
|
||||||
|
if (_payload == null || _payload.length <= HEADER_LENGTH)
|
||||||
|
return null;
|
||||||
|
byte[] rv = new byte[_payload.length - HEADER_LENGTH];
|
||||||
|
System.arraycopy(_payload, HEADER_LENGTH, rv, 0, rv.length);
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Signing Key extra data, if any.
|
||||||
|
*
|
||||||
|
* @return null if unset or none
|
||||||
|
* @throws UnsupportedOperationException if the sig type is unsupported
|
||||||
|
*/
|
||||||
|
public byte[] getExtraSigningKeyData() {
|
||||||
|
// we assume no crypto key data
|
||||||
|
if (_payload == null || _payload.length <= HEADER_LENGTH)
|
||||||
|
return null;
|
||||||
|
SigType type = getSigType();
|
||||||
|
if (type == null)
|
||||||
|
throw new UnsupportedOperationException("unknown sig type");
|
||||||
|
int extra = 128 - type.getPubkeyLen();
|
||||||
|
if (_payload.length == HEADER_LENGTH + extra)
|
||||||
|
return getExtraKeyData();
|
||||||
|
byte[] rv = new byte[extra];
|
||||||
|
System.arraycopy(_payload, HEADER_LENGTH, rv, 0, extra);
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
// todo
|
||||||
|
// constructor w/ crypto type
|
||||||
|
// getCryptoType()
|
||||||
|
// getCryptoDataOffset()
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public KeyCertificate toKeyCertificate() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
StringBuilder buf = new StringBuilder(64);
|
||||||
|
buf.append("[Certificate: type: Key certificate");
|
||||||
|
if (_payload == null) {
|
||||||
|
buf.append(" null payload");
|
||||||
|
} else {
|
||||||
|
buf.append("\n\tCrypto type: ").append(getCryptoTypeCode());
|
||||||
|
buf.append("\n\tSig type: ").append(getSigTypeCode())
|
||||||
|
.append(" (").append(getSigType()).append(')');
|
||||||
|
if (_payload.length > HEADER_LENGTH)
|
||||||
|
buf.append("\n\tKey data: ").append(_payload.length - HEADER_LENGTH).append(" bytes");
|
||||||
|
}
|
||||||
|
buf.append("]");
|
||||||
|
return buf.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An immutable ElG/ECDSA-256 certificate.
|
||||||
|
* @since 0.8.3
|
||||||
|
*/
|
||||||
|
private static final class ECDSA256Cert extends KeyCertificate {
|
||||||
|
private static final byte[] ECDSA256_DATA = new byte[] {
|
||||||
|
CERTIFICATE_TYPE_KEY, 0, HEADER_LENGTH, 0, (byte) (SigType.ECDSA_SHA256_P256.getCode()), 0, 0
|
||||||
|
};
|
||||||
|
private static final int ECDSA256_LENGTH = ECDSA256_DATA.length;
|
||||||
|
private static final byte[] ECDSA256_PAYLOAD = new byte[] {
|
||||||
|
0, (byte) (SigType.ECDSA_SHA256_P256.getCode()), 0, 0
|
||||||
|
};
|
||||||
|
|
||||||
|
public ECDSA256Cert() throws DataFormatException {
|
||||||
|
super(ECDSA256_PAYLOAD);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @throws RuntimeException always */
|
||||||
|
@Override
|
||||||
|
public void setCertificateType(int type) {
|
||||||
|
throw new RuntimeException("Data already set");
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @throws RuntimeException always */
|
||||||
|
@Override
|
||||||
|
public void setPayload(byte[] payload) {
|
||||||
|
throw new RuntimeException("Data already set");
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @throws RuntimeException always */
|
||||||
|
@Override
|
||||||
|
public void readBytes(InputStream in) throws DataFormatException, IOException {
|
||||||
|
throw new RuntimeException("Data already set");
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Overridden for efficiency */
|
||||||
|
@Override
|
||||||
|
public void writeBytes(OutputStream out) throws IOException {
|
||||||
|
out.write(ECDSA256_DATA);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Overridden for efficiency */
|
||||||
|
@Override
|
||||||
|
public int writeBytes(byte target[], int offset) {
|
||||||
|
System.arraycopy(ECDSA256_DATA, 0, target, offset, ECDSA256_LENGTH);
|
||||||
|
return ECDSA256_LENGTH;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @throws RuntimeException always */
|
||||||
|
@Override
|
||||||
|
public int readBytes(byte source[], int offset) throws DataFormatException {
|
||||||
|
throw new RuntimeException("Data already set");
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Overridden for efficiency */
|
||||||
|
@Override
|
||||||
|
public int size() {
|
||||||
|
return ECDSA256_LENGTH;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Overridden for efficiency */
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return 1234567;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -35,6 +35,7 @@ public class KeysAndCert extends DataStructureImpl {
|
|||||||
protected SigningPublicKey _signingKey;
|
protected SigningPublicKey _signingKey;
|
||||||
protected Certificate _certificate;
|
protected Certificate _certificate;
|
||||||
protected Hash __calculatedHash;
|
protected Hash __calculatedHash;
|
||||||
|
protected byte[] _padding;
|
||||||
|
|
||||||
public Certificate getCertificate() {
|
public Certificate getCertificate() {
|
||||||
return _certificate;
|
return _certificate;
|
||||||
@ -78,6 +79,17 @@ public class KeysAndCert extends DataStructureImpl {
|
|||||||
__calculatedHash = null;
|
__calculatedHash = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws IllegalStateException if was already set
|
||||||
|
* @since 0.9.12
|
||||||
|
*/
|
||||||
|
public void setPadding(byte[] padding) {
|
||||||
|
if (_padding != null)
|
||||||
|
throw new IllegalStateException();
|
||||||
|
_padding = padding;
|
||||||
|
__calculatedHash = null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @throws IllegalStateException if data already set
|
* @throws IllegalStateException if data already set
|
||||||
*/
|
*/
|
||||||
@ -85,8 +97,18 @@ public class KeysAndCert extends DataStructureImpl {
|
|||||||
if (_publicKey != null || _signingKey != null || _certificate != null)
|
if (_publicKey != null || _signingKey != null || _certificate != null)
|
||||||
throw new IllegalStateException();
|
throw new IllegalStateException();
|
||||||
_publicKey = PublicKey.create(in);
|
_publicKey = PublicKey.create(in);
|
||||||
_signingKey = SigningPublicKey.create(in);
|
SigningPublicKey spk = SigningPublicKey.create(in);
|
||||||
_certificate = Certificate.create(in);
|
Certificate cert = Certificate.create(in);
|
||||||
|
if (cert.getCertificateType() == Certificate.CERTIFICATE_TYPE_KEY) {
|
||||||
|
// convert SPK to new SPK and padding
|
||||||
|
KeyCertificate kcert = cert.toKeyCertificate();
|
||||||
|
_signingKey = spk.toTypedKey(kcert);
|
||||||
|
_padding = spk.getPadding(kcert);
|
||||||
|
_certificate = kcert;
|
||||||
|
} else {
|
||||||
|
_signingKey = spk;
|
||||||
|
_certificate = cert;
|
||||||
|
}
|
||||||
__calculatedHash = null;
|
__calculatedHash = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -94,7 +116,9 @@ public class KeysAndCert extends DataStructureImpl {
|
|||||||
if ((_certificate == null) || (_publicKey == null) || (_signingKey == null))
|
if ((_certificate == null) || (_publicKey == null) || (_signingKey == null))
|
||||||
throw new DataFormatException("Not enough data to format the router identity");
|
throw new DataFormatException("Not enough data to format the router identity");
|
||||||
_publicKey.writeBytes(out);
|
_publicKey.writeBytes(out);
|
||||||
_signingKey.writeBytes(out);
|
if (_padding != null)
|
||||||
|
out.write(_padding);
|
||||||
|
_signingKey.writeTruncatedBytes(out);
|
||||||
_certificate.writeBytes(out);
|
_certificate.writeBytes(out);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -106,6 +130,7 @@ public class KeysAndCert extends DataStructureImpl {
|
|||||||
return
|
return
|
||||||
DataHelper.eq(_signingKey, ident._signingKey)
|
DataHelper.eq(_signingKey, ident._signingKey)
|
||||||
&& DataHelper.eq(_publicKey, ident._publicKey)
|
&& DataHelper.eq(_publicKey, ident._publicKey)
|
||||||
|
&& DataHelper.eq(_padding, ident._padding)
|
||||||
&& DataHelper.eq(_certificate, ident._certificate);
|
&& DataHelper.eq(_certificate, ident._certificate);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -125,6 +150,8 @@ public class KeysAndCert extends DataStructureImpl {
|
|||||||
buf.append("\n\tCertificate: ").append(_certificate);
|
buf.append("\n\tCertificate: ").append(_certificate);
|
||||||
buf.append("\n\tPublicKey: ").append(_publicKey);
|
buf.append("\n\tPublicKey: ").append(_publicKey);
|
||||||
buf.append("\n\tSigningPublicKey: ").append(_signingKey);
|
buf.append("\n\tSigningPublicKey: ").append(_signingKey);
|
||||||
|
if (_padding != null)
|
||||||
|
buf.append("\n\tPadding: ").append(_padding.length).append(" bytes");
|
||||||
buf.append(']');
|
buf.append(']');
|
||||||
return buf.toString();
|
return buf.toString();
|
||||||
}
|
}
|
||||||
|
@ -275,11 +275,9 @@ public class LeaseSet extends DatabaseEntry {
|
|||||||
protected byte[] getBytes() {
|
protected byte[] getBytes() {
|
||||||
if ((_destination == null) || (_encryptionKey == null) || (_signingKey == null))
|
if ((_destination == null) || (_encryptionKey == null) || (_signingKey == null))
|
||||||
return null;
|
return null;
|
||||||
int len = PublicKey.KEYSIZE_BYTES // dest
|
int len = _destination.size()
|
||||||
+ SigningPublicKey.KEYSIZE_BYTES // dest
|
|
||||||
+ 3 // cert minimum, could be more, only used to size the BAOS
|
|
||||||
+ PublicKey.KEYSIZE_BYTES // encryptionKey
|
+ PublicKey.KEYSIZE_BYTES // encryptionKey
|
||||||
+ SigningPublicKey.KEYSIZE_BYTES // signingKey
|
+ _signingKey.length() // signingKey
|
||||||
+ 1
|
+ 1
|
||||||
+ _leases.size() * 44; // leases
|
+ _leases.size() * 44; // leases
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream(len);
|
ByteArrayOutputStream out = new ByteArrayOutputStream(len);
|
||||||
@ -310,7 +308,9 @@ public class LeaseSet extends DatabaseEntry {
|
|||||||
throw new IllegalStateException();
|
throw new IllegalStateException();
|
||||||
_destination = Destination.create(in);
|
_destination = Destination.create(in);
|
||||||
_encryptionKey = PublicKey.create(in);
|
_encryptionKey = PublicKey.create(in);
|
||||||
_signingKey = SigningPublicKey.create(in);
|
// revocation signing key must be same type as the destination signing key
|
||||||
|
_signingKey = new SigningPublicKey(_destination.getSigningPublicKey().getType());
|
||||||
|
_signingKey.readBytes(in);
|
||||||
int numLeases = (int) DataHelper.readLong(in, 1);
|
int numLeases = (int) DataHelper.readLong(in, 1);
|
||||||
if (numLeases > MAX_LEASES)
|
if (numLeases > MAX_LEASES)
|
||||||
throw new DataFormatException("Too many leases - max is " + MAX_LEASES);
|
throw new DataFormatException("Too many leases - max is " + MAX_LEASES);
|
||||||
@ -320,7 +320,8 @@ public class LeaseSet extends DatabaseEntry {
|
|||||||
lease.readBytes(in);
|
lease.readBytes(in);
|
||||||
addLease(lease);
|
addLease(lease);
|
||||||
}
|
}
|
||||||
_signature = new Signature();
|
// signature must be same type as the destination signing key
|
||||||
|
_signature = new Signature(_destination.getSigningPublicKey().getType());
|
||||||
_signature.readBytes(in);
|
_signature.readBytes(in);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -345,11 +346,9 @@ public class LeaseSet extends DatabaseEntry {
|
|||||||
* Number of bytes, NOT including signature
|
* Number of bytes, NOT including signature
|
||||||
*/
|
*/
|
||||||
public int size() {
|
public int size() {
|
||||||
return PublicKey.KEYSIZE_BYTES //destination.pubKey
|
return _destination.size()
|
||||||
+ SigningPublicKey.KEYSIZE_BYTES // destination.signPubKey
|
|
||||||
+ _destination.getCertificate().size() // destination.certificate, usually 3
|
|
||||||
+ PublicKey.KEYSIZE_BYTES // encryptionKey
|
+ PublicKey.KEYSIZE_BYTES // encryptionKey
|
||||||
+ SigningPublicKey.KEYSIZE_BYTES // signingKey
|
+ _signingKey.length() // signingKey
|
||||||
+ 1 // number of leases
|
+ 1 // number of leases
|
||||||
+ _leases.size() * (Hash.HASH_LENGTH + 4 + 8);
|
+ _leases.size() * (Hash.HASH_LENGTH + 4 + 8);
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,8 @@ import java.io.File;
|
|||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.security.GeneralSecurityException;
|
||||||
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
|
|
||||||
@ -19,7 +21,9 @@ import net.i2p.client.I2PClientFactory;
|
|||||||
import net.i2p.client.I2PSession;
|
import net.i2p.client.I2PSession;
|
||||||
import net.i2p.client.I2PSessionException;
|
import net.i2p.client.I2PSessionException;
|
||||||
import net.i2p.crypto.DSAEngine;
|
import net.i2p.crypto.DSAEngine;
|
||||||
|
import net.i2p.crypto.KeyGenerator;
|
||||||
import net.i2p.crypto.SigType;
|
import net.i2p.crypto.SigType;
|
||||||
|
import net.i2p.util.RandomSource;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This helper class reads and writes files in the
|
* This helper class reads and writes files in the
|
||||||
@ -138,11 +142,15 @@ public class PrivateKeyFile {
|
|||||||
PrivateKeyFile pkf2 = new PrivateKeyFile(args[g.getOptind() + 1]);
|
PrivateKeyFile pkf2 = new PrivateKeyFile(args[g.getOptind() + 1]);
|
||||||
pkf.setSignedCert(pkf2);
|
pkf.setSignedCert(pkf2);
|
||||||
System.out.println("New destination with signed cert is:");
|
System.out.println("New destination with signed cert is:");
|
||||||
break;
|
|
||||||
|
|
||||||
case 't':
|
case 't':
|
||||||
// TODO merge with ecdsa branch
|
// KeyCert
|
||||||
throw new UnsupportedOperationException();
|
SigType type = SigType.parseSigType(args[g.getOptind()]);
|
||||||
|
if (type == null)
|
||||||
|
throw new IllegalArgumentException("Signature type " + args[g.getOptind()] + " is not supported");
|
||||||
|
pkf.setKeyCert(type);
|
||||||
|
System.out.println("New destination with key cert is:");
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
// shouldn't happen
|
// shouldn't happen
|
||||||
@ -151,7 +159,7 @@ public class PrivateKeyFile {
|
|||||||
}
|
}
|
||||||
System.out.println(pkf);
|
System.out.println(pkf);
|
||||||
pkf.write();
|
pkf.write();
|
||||||
verifySignature(d);
|
verifySignature(pkf.getDestination());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
System.exit(1);
|
System.exit(1);
|
||||||
@ -268,6 +276,43 @@ public class PrivateKeyFile {
|
|||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Change cert type - caller must also call write().
|
||||||
|
* Side effect - creates new Destination object.
|
||||||
|
* @since 0.9.12
|
||||||
|
*/
|
||||||
|
public Certificate setKeyCert(SigType type) {
|
||||||
|
if (type == SigType.DSA_SHA1)
|
||||||
|
return setCertType(Certificate.CERTIFICATE_TYPE_NULL);
|
||||||
|
if (dest == null)
|
||||||
|
throw new IllegalArgumentException("Dest is null");
|
||||||
|
KeyCertificate c = new KeyCertificate(type);
|
||||||
|
SimpleDataStructure signingKeys[];
|
||||||
|
try {
|
||||||
|
signingKeys = KeyGenerator.getInstance().generateSigningKeys(type);
|
||||||
|
} catch (GeneralSecurityException gse) {
|
||||||
|
throw new RuntimeException("keygen fail", gse);
|
||||||
|
}
|
||||||
|
SigningPublicKey signingPubKey = (SigningPublicKey) signingKeys[0];
|
||||||
|
signingPrivKey = (SigningPrivateKey) signingKeys[1];
|
||||||
|
// dests now immutable, must create new
|
||||||
|
Destination newdest = new Destination();
|
||||||
|
newdest.setPublicKey(dest.getPublicKey());
|
||||||
|
newdest.setSigningPublicKey(signingPubKey);
|
||||||
|
// fix up key certificate or padding
|
||||||
|
int len = type.getPubkeyLen();
|
||||||
|
if (len < 128) {
|
||||||
|
byte[] pad = new byte[128 - len];
|
||||||
|
RandomSource.getInstance().nextBytes(pad);
|
||||||
|
newdest.setPadding(pad);
|
||||||
|
} else if (len > 128) {
|
||||||
|
System.arraycopy(signingPubKey.getData(), 128, c.getPayload(), KeyCertificate.HEADER_LENGTH, len - 128);
|
||||||
|
}
|
||||||
|
newdest.setCertificate(c);
|
||||||
|
dest = newdest;
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
/** change to hashcash cert - caller must also call write() */
|
/** change to hashcash cert - caller must also call write() */
|
||||||
public Certificate setHashCashCert(int effort) {
|
public Certificate setHashCashCert(int effort) {
|
||||||
Certificate c = setCertType(Certificate.CERTIFICATE_TYPE_HASHCASH);
|
Certificate c = setCertType(Certificate.CERTIFICATE_TYPE_HASHCASH);
|
||||||
@ -503,8 +548,6 @@ public class PrivateKeyFile {
|
|||||||
|
|
||||||
private static final int HASH_EFFORT = VerifiedDestination.MIN_HASHCASH_EFFORT;
|
private static final int HASH_EFFORT = VerifiedDestination.MIN_HASHCASH_EFFORT;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private final File file;
|
private final File file;
|
||||||
private final I2PClient client;
|
private final I2PClient client;
|
||||||
private Destination dest;
|
private Destination dest;
|
||||||
|
@ -554,7 +554,7 @@ public class RouterInfo extends DatabaseEntry {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
DataHelper.readProperties(din, _options);
|
DataHelper.readProperties(din, _options);
|
||||||
_signature = new Signature();
|
_signature = new Signature(_identity.getSigningPublicKey().getType());
|
||||||
_signature.readBytes(in);
|
_signature.readBytes(in);
|
||||||
|
|
||||||
if (verifySig) {
|
if (verifySig) {
|
||||||
|
@ -13,12 +13,15 @@ import net.i2p.crypto.SigType;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Defines the signature as defined by the I2P data structure spec.
|
* Defines the signature as defined by the I2P data structure spec.
|
||||||
* A signature is a 40-byte array verifying the authenticity of some data
|
* By default, a signature is a 40-byte array verifying the authenticity of some data
|
||||||
* using the DSA-SHA1 algorithm.
|
* using the DSA-SHA1 algorithm.
|
||||||
*
|
*
|
||||||
* The signature is the 20-byte R followed by the 20-byte S,
|
* The signature is the 20-byte R followed by the 20-byte S,
|
||||||
* both are unsigned integers.
|
* both are unsigned integers.
|
||||||
*
|
*
|
||||||
|
* As of release 0.9.8, signatures of arbitrary length and type are supported.
|
||||||
|
* See SigType.
|
||||||
|
*
|
||||||
* @author jrandom
|
* @author jrandom
|
||||||
*/
|
*/
|
||||||
public class Signature extends SimpleDataStructure {
|
public class Signature extends SimpleDataStructure {
|
||||||
@ -39,10 +42,15 @@ public class Signature extends SimpleDataStructure {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Unknown type not allowed as we won't know the length to read in the data.
|
||||||
|
*
|
||||||
|
* @param type non-null
|
||||||
* @since 0.9.8
|
* @since 0.9.8
|
||||||
*/
|
*/
|
||||||
public Signature(SigType type) {
|
public Signature(SigType type) {
|
||||||
super();
|
super();
|
||||||
|
if (type == null)
|
||||||
|
throw new IllegalArgumentException("unknown type");
|
||||||
_type = type;
|
_type = type;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -51,10 +59,15 @@ public class Signature extends SimpleDataStructure {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Should we allow an unknown type here?
|
||||||
|
*
|
||||||
|
* @param type non-null
|
||||||
* @since 0.9.8
|
* @since 0.9.8
|
||||||
*/
|
*/
|
||||||
public Signature(SigType type, byte data[]) {
|
public Signature(SigType type, byte data[]) {
|
||||||
super();
|
super();
|
||||||
|
if (type == null)
|
||||||
|
throw new IllegalArgumentException("unknown type");
|
||||||
_type = type;
|
_type = type;
|
||||||
setData(data);
|
setData(data);
|
||||||
}
|
}
|
||||||
@ -64,6 +77,7 @@ public class Signature extends SimpleDataStructure {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @return non-null
|
||||||
* @since 0.9.8
|
* @since 0.9.8
|
||||||
*/
|
*/
|
||||||
public SigType getType() {
|
public SigType getType() {
|
||||||
|
@ -14,10 +14,13 @@ import net.i2p.crypto.SigType;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Defines the SigningPrivateKey as defined by the I2P data structure spec.
|
* Defines the SigningPrivateKey as defined by the I2P data structure spec.
|
||||||
* A signing private key is 20 byte Integer. The private key represents only the
|
* A signing private key is by default a 20 byte Integer. The private key represents only the
|
||||||
* exponent, not the primes, which are constant and defined in the crypto spec.
|
* exponent, not the primes, which are constant and defined in the crypto spec.
|
||||||
* This key varies from the PrivateKey in its usage (signing, not decrypting)
|
* This key varies from the PrivateKey in its usage (signing, not decrypting)
|
||||||
*
|
*
|
||||||
|
* As of release 0.9.8, keys of arbitrary length and type are supported.
|
||||||
|
* See SigType.
|
||||||
|
*
|
||||||
* @author jrandom
|
* @author jrandom
|
||||||
*/
|
*/
|
||||||
public class SigningPrivateKey extends SimpleDataStructure {
|
public class SigningPrivateKey extends SimpleDataStructure {
|
||||||
|
@ -11,15 +11,19 @@ package net.i2p.data;
|
|||||||
|
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
|
||||||
import net.i2p.crypto.SigType;
|
import net.i2p.crypto.SigType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Defines the SigningPublicKey as defined by the I2P data structure spec.
|
* Defines the SigningPublicKey as defined by the I2P data structure spec.
|
||||||
* A signing public key is 128 byte Integer. The public key represents only the
|
* A signing public key is by default 128 byte Integer. The public key represents only the
|
||||||
* exponent, not the primes, which are constant and defined in the crypto spec.
|
* exponent, not the primes, which are constant and defined in the crypto spec.
|
||||||
* This key varies from the PrivateKey in its usage (verifying signatures, not encrypting)
|
* This key varies from the PrivateKey in its usage (verifying signatures, not encrypting)
|
||||||
*
|
*
|
||||||
|
* As of release 0.9.8, keys of arbitrary length and type are supported.
|
||||||
|
* See SigType.
|
||||||
|
*
|
||||||
* @author jrandom
|
* @author jrandom
|
||||||
*/
|
*/
|
||||||
public class SigningPublicKey extends SimpleDataStructure {
|
public class SigningPublicKey extends SimpleDataStructure {
|
||||||
@ -55,6 +59,7 @@ public class SigningPublicKey extends SimpleDataStructure {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @param type if null, type is unknown
|
||||||
* @since 0.9.8
|
* @since 0.9.8
|
||||||
*/
|
*/
|
||||||
public SigningPublicKey(SigType type) {
|
public SigningPublicKey(SigType type) {
|
||||||
@ -67,12 +72,16 @@ public class SigningPublicKey extends SimpleDataStructure {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @param type if null, type is unknown
|
||||||
* @since 0.9.8
|
* @since 0.9.8
|
||||||
*/
|
*/
|
||||||
public SigningPublicKey(SigType type, byte data[]) {
|
public SigningPublicKey(SigType type, byte data[]) {
|
||||||
super();
|
super();
|
||||||
_type = type;
|
_type = type;
|
||||||
setData(data);
|
if (type != null || data == null)
|
||||||
|
setData(data);
|
||||||
|
else
|
||||||
|
_data = data; // bypass length check
|
||||||
}
|
}
|
||||||
|
|
||||||
/** constructs from base64
|
/** constructs from base64
|
||||||
@ -84,17 +93,91 @@ public class SigningPublicKey extends SimpleDataStructure {
|
|||||||
fromBase64(base64Data);
|
fromBase64(base64Data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return if type unknown, the length of the data, or 128 if no data
|
||||||
|
*/
|
||||||
public int length() {
|
public int length() {
|
||||||
return _type.getPubkeyLen();
|
if (_type != null)
|
||||||
|
return _type.getPubkeyLen();
|
||||||
|
if (_data != null)
|
||||||
|
return _data.length;
|
||||||
|
return KEYSIZE_BYTES;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @return null if unknown
|
||||||
* @since 0.9.8
|
* @since 0.9.8
|
||||||
*/
|
*/
|
||||||
public SigType getType() {
|
public SigType getType() {
|
||||||
return _type;
|
return _type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Up-convert this from an untyped (type 0) SPK to a typed SPK based on the Key Cert given
|
||||||
|
*
|
||||||
|
* @throws IllegalArgumentException if this is already typed to a different type
|
||||||
|
* @since 0.9.12
|
||||||
|
*/
|
||||||
|
public SigningPublicKey toTypedKey(KeyCertificate kcert) {
|
||||||
|
if (_data == null)
|
||||||
|
throw new IllegalStateException();
|
||||||
|
SigType newType = kcert.getSigType();
|
||||||
|
if (_type == newType)
|
||||||
|
return this;
|
||||||
|
if (_type != SigType.DSA_SHA1)
|
||||||
|
throw new IllegalArgumentException("Cannot convert " + _type + " to " + newType);
|
||||||
|
int newLen = newType.getPubkeyLen();
|
||||||
|
if (newLen == SigType.DSA_SHA1.getPubkeyLen())
|
||||||
|
return new SigningPublicKey(newType, _data);
|
||||||
|
byte[] newData = new byte[newLen];
|
||||||
|
if (newLen < SigType.DSA_SHA1.getPubkeyLen()) {
|
||||||
|
// right-justified
|
||||||
|
System.arraycopy(_data, _data.length - newLen, newData, 0, newLen);
|
||||||
|
} else {
|
||||||
|
// full 128 bytes + fragment in kcert
|
||||||
|
System.arraycopy(_data, 0, newData, 0, _data.length);
|
||||||
|
System.arraycopy(kcert.getPayload(), KeyCertificate.HEADER_LENGTH, newData, _data.length, newLen - _data.length);
|
||||||
|
}
|
||||||
|
return new SigningPublicKey(newType, newData);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the portion of this (type 0) SPK that is really padding based on the Key Cert type given,
|
||||||
|
* if any
|
||||||
|
*
|
||||||
|
* @return leading padding length > 0 or null
|
||||||
|
* @throws IllegalArgumentException if this is already typed to a different type
|
||||||
|
* @since 0.9.12
|
||||||
|
*/
|
||||||
|
public byte[] getPadding(KeyCertificate kcert) {
|
||||||
|
if (_data == null)
|
||||||
|
throw new IllegalStateException();
|
||||||
|
SigType newType = kcert.getSigType();
|
||||||
|
if (_type == newType)
|
||||||
|
return null;
|
||||||
|
if (_type != SigType.DSA_SHA1)
|
||||||
|
throw new IllegalStateException("Cannot convert " + _type + " to " + newType);
|
||||||
|
int newLen = newType.getPubkeyLen();
|
||||||
|
if (newLen >= SigType.DSA_SHA1.getPubkeyLen())
|
||||||
|
return null;
|
||||||
|
int padLen = SigType.DSA_SHA1.getPubkeyLen() - newLen;
|
||||||
|
byte[] pad = new byte[padLen];
|
||||||
|
System.arraycopy(_data, 0, pad, 0, padLen);
|
||||||
|
return pad;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write the data up to a max of 128 bytes.
|
||||||
|
* If longer, the rest will be written in the KeyCertificate.
|
||||||
|
* @since 0.9.12
|
||||||
|
*/
|
||||||
|
public void writeTruncatedBytes(OutputStream out) throws DataFormatException, IOException {
|
||||||
|
if (_type.getPubkeyLen() <= KEYSIZE_BYTES)
|
||||||
|
out.write(_data);
|
||||||
|
else
|
||||||
|
out.write(_data, 0, KEYSIZE_BYTES);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @since 0.9.8
|
* @since 0.9.8
|
||||||
*/
|
*/
|
||||||
|
@ -71,6 +71,11 @@ public class CreateLeaseSetMessage extends I2CPMessageImpl {
|
|||||||
try {
|
try {
|
||||||
_sessionId = new SessionId();
|
_sessionId = new SessionId();
|
||||||
_sessionId.readBytes(in);
|
_sessionId.readBytes(in);
|
||||||
|
// Revocation is unimplemented.
|
||||||
|
// As the SPK comes before the LeaseSet, we don't know the key type.
|
||||||
|
// We could have some sort of callback or state setting so we get the
|
||||||
|
// expected type from the session. But for now, we just assume it's 20 bytes.
|
||||||
|
// Clients outside router context should throw in a dummy 20 bytes.
|
||||||
_signingPrivateKey = new SigningPrivateKey();
|
_signingPrivateKey = new SigningPrivateKey();
|
||||||
_signingPrivateKey.readBytes(in);
|
_signingPrivateKey.readBytes(in);
|
||||||
_privateKey = new PrivateKey();
|
_privateKey = new PrivateKey();
|
||||||
@ -87,7 +92,7 @@ public class CreateLeaseSetMessage extends I2CPMessageImpl {
|
|||||||
if ((_sessionId == null) || (_signingPrivateKey == null) || (_privateKey == null) || (_leaseSet == null))
|
if ((_sessionId == null) || (_signingPrivateKey == null) || (_privateKey == null) || (_leaseSet == null))
|
||||||
throw new I2CPMessageException("Unable to write out the message as there is not enough data");
|
throw new I2CPMessageException("Unable to write out the message as there is not enough data");
|
||||||
int size = 4 // sessionId
|
int size = 4 // sessionId
|
||||||
+ SigningPrivateKey.KEYSIZE_BYTES
|
+ _signingPrivateKey.length()
|
||||||
+ PrivateKey.KEYSIZE_BYTES
|
+ PrivateKey.KEYSIZE_BYTES
|
||||||
+ _leaseSet.size();
|
+ _leaseSet.size();
|
||||||
ByteArrayOutputStream os = new ByteArrayOutputStream(size);
|
ByteArrayOutputStream os = new ByteArrayOutputStream(size);
|
||||||
|
@ -190,7 +190,7 @@ public class SessionConfig extends DataStructureImpl {
|
|||||||
_destination = Destination.create(rawConfig);
|
_destination = Destination.create(rawConfig);
|
||||||
_options = DataHelper.readProperties(rawConfig);
|
_options = DataHelper.readProperties(rawConfig);
|
||||||
_creationDate = DataHelper.readDate(rawConfig);
|
_creationDate = DataHelper.readDate(rawConfig);
|
||||||
_signature = new Signature();
|
_signature = new Signature(_destination.getSigningPublicKey().getType());
|
||||||
_signature.readBytes(rawConfig);
|
_signature.readBytes(rawConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,8 +12,10 @@ import java.util.ArrayList;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
import net.i2p.data.Certificate;
|
||||||
import net.i2p.data.DatabaseEntry;
|
import net.i2p.data.DatabaseEntry;
|
||||||
import net.i2p.data.Hash;
|
import net.i2p.data.Hash;
|
||||||
|
import net.i2p.data.LeaseSet;
|
||||||
import net.i2p.data.RouterInfo;
|
import net.i2p.data.RouterInfo;
|
||||||
import net.i2p.data.TunnelId;
|
import net.i2p.data.TunnelId;
|
||||||
import net.i2p.data.i2np.DatabaseStoreMessage;
|
import net.i2p.data.i2np.DatabaseStoreMessage;
|
||||||
@ -163,6 +165,18 @@ class StoreJob extends JobImpl {
|
|||||||
if (_log.shouldLog(Log.INFO))
|
if (_log.shouldLog(Log.INFO))
|
||||||
_log.info(getJobId() + ": Error selecting closest hash that wasnt a router! " + peer + " : " + ds);
|
_log.info(getJobId() + ": Error selecting closest hash that wasnt a router! " + peer + " : " + ds);
|
||||||
_state.addSkipped(peer);
|
_state.addSkipped(peer);
|
||||||
|
} else if (_state.getData().getType() == DatabaseEntry.KEY_TYPE_LEASESET &&
|
||||||
|
((LeaseSet)_state.getData()).getDestination().getCertificate().getCertificateType() == Certificate.CERTIFICATE_TYPE_KEY &&
|
||||||
|
!supportsKeyCerts((RouterInfo)ds)) {
|
||||||
|
if (_log.shouldLog(Log.INFO))
|
||||||
|
_log.info(getJobId() + ": Skipping router that doesn't support key certs " + peer + " : " + ds);
|
||||||
|
_state.addSkipped(peer);
|
||||||
|
} else if (_state.getData().getType() == DatabaseEntry.KEY_TYPE_LEASESET &&
|
||||||
|
((LeaseSet)_state.getData()).getLeaseCount() > 6 &&
|
||||||
|
!supportsBigLeaseSets((RouterInfo)ds)) {
|
||||||
|
if (_log.shouldLog(Log.INFO))
|
||||||
|
_log.info(getJobId() + ": Skipping router that doesn't support big leasesets " + peer + " : " + ds);
|
||||||
|
_state.addSkipped(peer);
|
||||||
} else {
|
} else {
|
||||||
int peerTimeout = _facade.getPeerTimeout(peer);
|
int peerTimeout = _facade.getPeerTimeout(peer);
|
||||||
|
|
||||||
@ -487,6 +501,32 @@ class StoreJob extends JobImpl {
|
|||||||
return VersionComparator.comp(v, MIN_ENCRYPTION_VERSION) >= 0;
|
return VersionComparator.comp(v, MIN_ENCRYPTION_VERSION) >= 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final String MIN_KEYCERT_VERSION = "0.9.12";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Does he support key certs (assumed with a non-DSA key)?
|
||||||
|
* @since 0.9.12
|
||||||
|
*/
|
||||||
|
private static boolean supportsKeyCerts(RouterInfo ri) {
|
||||||
|
String v = ri.getOption("router.version");
|
||||||
|
if (v == null)
|
||||||
|
return false;
|
||||||
|
return VersionComparator.comp(v, MIN_KEYCERT_VERSION) >= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final String MIN_BIGLEASESET_VERSION = "0.9";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Does he support more than 6 leasesets?
|
||||||
|
* @since 0.9.12
|
||||||
|
*/
|
||||||
|
private static boolean supportsBigLeaseSets(RouterInfo ri) {
|
||||||
|
String v = ri.getOption("router.version");
|
||||||
|
if (v == null)
|
||||||
|
return false;
|
||||||
|
return VersionComparator.comp(v, MIN_BIGLEASESET_VERSION) >= 0;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called after sending a dbStore to a peer successfully,
|
* Called after sending a dbStore to a peer successfully,
|
||||||
* marking the store as successful
|
* marking the store as successful
|
||||||
|
Reference in New Issue
Block a user