forked from I2P_Developers/i2p.i2p
propagate from branch 'i2p.i2p' (head b8f5da367cf5d16bc1d91aa2097830c350c9ef8a)
to branch 'i2p.i2p.str4d.eddsa' (head 6fcc5b5019abb36251e28fe0f7723fd1a046a8e9)
This commit is contained in:
@ -16,7 +16,7 @@ package net.i2p;
|
||||
public class CoreVersion {
|
||||
/** deprecated */
|
||||
public final static String ID = "Monotone";
|
||||
public final static String VERSION = "0.9.13";
|
||||
public final static String VERSION = "0.9.14.1";
|
||||
|
||||
public static void main(String args[]) {
|
||||
System.out.println("I2P Core version: " + VERSION);
|
||||
|
45
core/java/src/net/i2p/client/DomainSocketFactory.java
Normal file
45
core/java/src/net/i2p/client/DomainSocketFactory.java
Normal file
@ -0,0 +1,45 @@
|
||||
package net.i2p.client;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.ServerSocket;
|
||||
import java.net.Socket;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
|
||||
/**
|
||||
* Bridge to Unix domain socket (or similar).
|
||||
* <p/>
|
||||
* This is a stub that does nothing.
|
||||
* This class is replaced in the Android build.
|
||||
*
|
||||
* @author str4d
|
||||
* @since 0.9.14
|
||||
*/
|
||||
public class DomainSocketFactory {
|
||||
public static String I2CP_SOCKET_ADDRESS = "net.i2p.client.i2cp";
|
||||
|
||||
/**
|
||||
* @throws UnsupportedOperationException always
|
||||
*/
|
||||
public DomainSocketFactory(I2PAppContext context) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
* Override in Android.
|
||||
* @throws IOException
|
||||
* @throws UnsupportedOperationException always
|
||||
*/
|
||||
public Socket createSocket(String name) throws IOException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
* Override in Android.
|
||||
* @throws IOException
|
||||
* @throws UnsupportedOperationException always
|
||||
*/
|
||||
public ServerSocket createServerSocket(String name) throws IOException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
@ -52,6 +52,7 @@ import net.i2p.util.LHMCache;
|
||||
import net.i2p.util.Log;
|
||||
import net.i2p.util.OrderedProperties;
|
||||
import net.i2p.util.SimpleTimer2;
|
||||
import net.i2p.util.SystemVersion;
|
||||
import net.i2p.util.VersionComparator;
|
||||
|
||||
/**
|
||||
@ -157,6 +158,12 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
|
||||
protected static final String PROP_USER = "i2cp.username";
|
||||
protected static final String PROP_PW = "i2cp.password";
|
||||
|
||||
/**
|
||||
* Use Unix domain socket (or similar) to connect to a router
|
||||
* @since 0.9.14
|
||||
*/
|
||||
protected static final String PROP_DOMAIN_SOCKET = "i2cp.domainSocket";
|
||||
|
||||
private static final long VERIFY_USAGE_TIME = 60*1000;
|
||||
|
||||
private static final long MAX_SEND_WAIT = 10*1000;
|
||||
@ -279,6 +286,10 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
|
||||
if (_context.isRouterContext())
|
||||
// just for logging
|
||||
return "[internal connection]";
|
||||
else if (SystemVersion.isAndroid() &&
|
||||
Boolean.parseBoolean(_options.getProperty(PROP_DOMAIN_SOCKET)))
|
||||
// just for logging
|
||||
return "[Domain socket connection]";
|
||||
return _options.getProperty(I2PClient.PROP_TCP_HOST, "127.0.0.1");
|
||||
}
|
||||
|
||||
@ -287,7 +298,9 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
|
||||
* @since 0.9.7 was in loadConfig()
|
||||
*/
|
||||
private int getPort() {
|
||||
if (_context.isRouterContext())
|
||||
if (_context.isRouterContext() ||
|
||||
(SystemVersion.isAndroid() &&
|
||||
Boolean.parseBoolean(_options.getProperty(PROP_DOMAIN_SOCKET))))
|
||||
// just for logging
|
||||
return 0;
|
||||
String portNum = _options.getProperty(I2PClient.PROP_TCP_PORT, LISTEN_PORT + "");
|
||||
@ -447,7 +460,7 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
|
||||
try {
|
||||
// protect w/ closeSocket()
|
||||
synchronized(_stateLock) {
|
||||
// If we are in the router JVM, connect using the interal queue
|
||||
// If we are in the router JVM, connect using the internal queue
|
||||
if (_context.isRouterContext()) {
|
||||
// _socket and _writer remain null
|
||||
InternalClientManager mgr = _context.internalClientManager();
|
||||
@ -457,7 +470,11 @@ abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2CPMessa
|
||||
_queue = mgr.connect();
|
||||
_reader = new QueuedI2CPMessageReader(_queue, this);
|
||||
} else {
|
||||
if (Boolean.parseBoolean(_options.getProperty(PROP_ENABLE_SSL))) {
|
||||
if (SystemVersion.isAndroid() &&
|
||||
Boolean.parseBoolean(_options.getProperty(PROP_DOMAIN_SOCKET))) {
|
||||
final DomainSocketFactory fact = new DomainSocketFactory(_context);
|
||||
_socket = fact.createSocket(DomainSocketFactory.I2CP_SOCKET_ADDRESS);
|
||||
} else if (Boolean.parseBoolean(_options.getProperty(PROP_ENABLE_SSL))) {
|
||||
try {
|
||||
I2PSSLSocketFactory fact = new I2PSSLSocketFactory(_context, false, "certificates/i2cp");
|
||||
_socket = fact.createSocket(_hostname, _portNum);
|
||||
|
@ -19,6 +19,7 @@ import net.i2p.data.DataFormatException;
|
||||
import net.i2p.data.Destination;
|
||||
import net.i2p.data.Hash;
|
||||
import net.i2p.data.Signature;
|
||||
import net.i2p.data.SigningPublicKey;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/**
|
||||
@ -34,16 +35,11 @@ public final class I2PDatagramDissector {
|
||||
private final DSAEngine dsaEng = DSAEngine.getInstance();
|
||||
private final SHA256Generator hashGen = SHA256Generator.getInstance();
|
||||
|
||||
private Hash rxHash;
|
||||
|
||||
private byte[] rxHash;
|
||||
private Signature rxSign;
|
||||
|
||||
private Destination rxDest;
|
||||
|
||||
private final byte[] rxPayload = new byte[DGRAM_BUFSIZE];
|
||||
|
||||
private int rxPayloadLen;
|
||||
|
||||
private boolean valid;
|
||||
|
||||
/**
|
||||
@ -56,6 +52,17 @@ public final class I2PDatagramDissector {
|
||||
* Load an I2P repliable datagram into the dissector.
|
||||
* Does NOT verify the signature.
|
||||
*
|
||||
* Format is:
|
||||
* <ol>
|
||||
* <li>Destination (387+ bytes)
|
||||
* <li>Signature (40+ bytes, type and length as implied by signing key type in the Destination)
|
||||
* <li>Payload
|
||||
* </ol>
|
||||
*
|
||||
* For DSA_SHA1 Destinations, the signature is of the SHA-256 Hash of the payload.
|
||||
*
|
||||
* As of 0.9.14, for non-DSA_SHA1 Destinations, the signature is of the payload itself.
|
||||
*
|
||||
* @param dgram non-null I2P repliable datagram to be loaded
|
||||
*
|
||||
* @throws DataFormatException If there's an error in the datagram format
|
||||
@ -79,14 +86,21 @@ public final class I2PDatagramDissector {
|
||||
rxPayloadLen = dgStream.read(rxPayload);
|
||||
|
||||
// calculate the hash of the payload
|
||||
this.rxHash = hashGen.calculateHash(rxPayload, 0, rxPayloadLen);
|
||||
assert this.hashGen.calculateHash(this.extractPayload()).equals(this.rxHash);
|
||||
if (type == SigType.DSA_SHA1) {
|
||||
if (rxHash == null)
|
||||
rxHash = new byte[Hash.HASH_LENGTH];
|
||||
// non-caching
|
||||
hashGen.calculateHash(rxPayload, 0, rxPayloadLen, rxHash, 0);
|
||||
//assert this.hashGen.calculateHash(this.extractPayload()).equals(this.rxHash);
|
||||
} else {
|
||||
rxHash = null;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Log log = I2PAppContext.getGlobalContext().logManager().getLog(I2PDatagramDissector.class);
|
||||
log.error("Caught IOException - INCONSISTENT STATE!", e);
|
||||
} catch(AssertionError e) {
|
||||
Log log = I2PAppContext.getGlobalContext().logManager().getLog(I2PDatagramDissector.class);
|
||||
log.error("Assertion failed!", e);
|
||||
//} catch(AssertionError e) {
|
||||
// Log log = I2PAppContext.getGlobalContext().logManager().getLog(I2PDatagramDissector.class);
|
||||
// log.error("Assertion failed!", e);
|
||||
}
|
||||
|
||||
//_log.debug("Datagram payload size: " + rxPayloadLen + "; content:\n"
|
||||
@ -125,14 +139,16 @@ public final class I2PDatagramDissector {
|
||||
* Extract the hash of the payload of an I2P repliable datagram (previously
|
||||
* loaded with the loadI2PDatagram() method), verifying the datagram
|
||||
* signature.
|
||||
*
|
||||
* As of 0.9.14, for signature types other than DSA_SHA1, this returns null.
|
||||
*
|
||||
* @return The hash of the payload of the I2P repliable datagram
|
||||
* @throws I2PInvalidDatagramException if the signature verification fails
|
||||
*/
|
||||
public Hash getHash() throws I2PInvalidDatagramException {
|
||||
// make sure it has a valid signature
|
||||
this.verifySignature();
|
||||
|
||||
return this.extractHash();
|
||||
return extractHash();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -178,10 +194,18 @@ public final class I2PDatagramDissector {
|
||||
* Extract the hash of the payload of an I2P repliable datagram (previously
|
||||
* loaded with the loadI2PDatagram() method), without verifying the datagram
|
||||
* signature.
|
||||
*
|
||||
* As of 0.9.14, for signature types other than DSA_SHA1, this returns null.
|
||||
*
|
||||
* @return The hash of the payload of the I2P repliable datagram
|
||||
*/
|
||||
public Hash extractHash() {
|
||||
return this.rxHash;
|
||||
if (rxHash == null)
|
||||
return null;
|
||||
// make a copy as we will reuse rxHash
|
||||
byte[] hash = new byte[Hash.HASH_LENGTH];
|
||||
System.arraycopy(rxHash, 0, hash, 0, Hash.HASH_LENGTH);
|
||||
return new Hash(hash);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -194,12 +218,21 @@ public final class I2PDatagramDissector {
|
||||
if(this.valid)
|
||||
return;
|
||||
|
||||
if (rxSign == null || rxSign.getData() == null || rxDest == null || rxDest.getSigningPublicKey() == null)
|
||||
if (rxSign == null || rxSign.getData() == null || rxDest == null)
|
||||
throw new I2PInvalidDatagramException("Datagram not yet read");
|
||||
|
||||
|
||||
// now validate
|
||||
if (!this.dsaEng.verifySignature(rxSign, rxHash.getData(), rxDest.getSigningPublicKey()))
|
||||
throw new I2PInvalidDatagramException("Incorrect I2P repliable datagram signature");
|
||||
SigningPublicKey spk = rxDest.getSigningPublicKey();
|
||||
SigType type = spk.getType();
|
||||
if (type == null)
|
||||
throw new I2PInvalidDatagramException("unsupported sig type");
|
||||
if (type == SigType.DSA_SHA1) {
|
||||
if (!this.dsaEng.verifySignature(rxSign, rxHash, spk))
|
||||
throw new I2PInvalidDatagramException("Incorrect I2P repliable datagram signature");
|
||||
} else {
|
||||
if (!this.dsaEng.verifySignature(rxSign, rxPayload, 0, rxPayloadLen, spk))
|
||||
throw new I2PInvalidDatagramException("Incorrect I2P repliable datagram signature");
|
||||
}
|
||||
|
||||
// set validated
|
||||
this.valid = true;
|
||||
|
@ -16,8 +16,12 @@ import net.i2p.client.I2PSession;
|
||||
import net.i2p.crypto.DSAEngine;
|
||||
import net.i2p.crypto.SHA256Generator;
|
||||
import net.i2p.data.DataFormatException;
|
||||
import net.i2p.data.Hash;
|
||||
import net.i2p.data.Signature;
|
||||
import net.i2p.data.SigningPrivateKey;
|
||||
import net.i2p.crypto.SigType;
|
||||
import net.i2p.util.Log;
|
||||
import net.i2p.util.SimpleByteCache;
|
||||
|
||||
/**
|
||||
* Class for creating I2P repliable datagrams. Note that objects of this class
|
||||
@ -44,9 +48,9 @@ public final class I2PDatagramMaker {
|
||||
* @param session I2PSession used to send I2PDatagrams through
|
||||
*/
|
||||
public I2PDatagramMaker(I2PSession session) {
|
||||
this();
|
||||
this.setI2PDatagramMaker(session);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new I2PDatagramMaker that is null.
|
||||
* Use setI2PDatagramMaker to set the parameters.
|
||||
@ -59,22 +63,53 @@ public final class I2PDatagramMaker {
|
||||
sxPrivKey = session.getPrivateKey();
|
||||
sxDestBytes = session.getMyDestination().toByteArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a repliable I2P datagram containing the specified payload.
|
||||
*
|
||||
* Format is:
|
||||
* <ol>
|
||||
* <li>Destination (387+ bytes)
|
||||
* <li>Signature (40+ bytes, type and length as implied by signing key type in the Destination)
|
||||
* <li>Payload
|
||||
* </ol>
|
||||
*
|
||||
* Maximum datagram size is 32768, so maximum payload size is 32341, or less for
|
||||
* non-DSA_SHA1 destinations. Practical maximum is a few KB less due to
|
||||
* ElGamal/AES overhead. 10 KB or less is recommended for best results.
|
||||
*
|
||||
* For DSA_SHA1 Destinations, the signature is of the SHA-256 Hash of the payload.
|
||||
*
|
||||
* As of 0.9.14, for non-DSA_SHA1 Destinations, the signature is of the payload itself.
|
||||
*
|
||||
* @param payload non-null Bytes to be contained in the I2P datagram.
|
||||
* @return null on error
|
||||
* @throws IllegalArgumentException if payload is too big
|
||||
* @throws IllegalStateException if Destination signature type unsupported
|
||||
*/
|
||||
public byte[] makeI2PDatagram(byte[] payload) {
|
||||
sxDGram.reset();
|
||||
|
||||
try {
|
||||
sxDGram.write(sxDestBytes);
|
||||
SigType type = sxPrivKey.getType();
|
||||
if (type == null)
|
||||
throw new IllegalStateException("Unsupported sig type");
|
||||
|
||||
dsaEng.sign(hashGen.calculateHash(payload).toByteArray(),
|
||||
sxPrivKey).writeBytes(sxDGram);
|
||||
|
||||
Signature sig;
|
||||
if (type == SigType.DSA_SHA1) {
|
||||
byte[] hash = SimpleByteCache.acquire(Hash.HASH_LENGTH);
|
||||
// non-caching
|
||||
hashGen.calculateHash(payload, 0, payload.length, hash, 0);
|
||||
sig = dsaEng.sign(hash, sxPrivKey);
|
||||
SimpleByteCache.release(hash);
|
||||
} else {
|
||||
sig = dsaEng.sign(payload, sxPrivKey);
|
||||
}
|
||||
sig.writeBytes(sxDGram);
|
||||
sxDGram.write(payload);
|
||||
|
||||
if (sxDGram.size() > DGRAM_BUFSIZE)
|
||||
throw new IllegalArgumentException("Too big");
|
||||
return sxDGram.toByteArray();
|
||||
} catch (IOException e) {
|
||||
Log log = I2PAppContext.getGlobalContext().logManager().getLog(I2PDatagramMaker.class);
|
||||
|
@ -10,6 +10,9 @@ in turn, use the {@link net.i2p.client.datagram.I2PDatagramMaker} to build a
|
||||
message that can be parsed. </p>
|
||||
|
||||
<p>The datagram format implemented here includes
|
||||
the sender's {@link net.i2p.data.Destination}, the payload, and a hash of the
|
||||
payload (signed by the sender's {@link net.i2p.data.SigningPrivateKey}).</p>
|
||||
the sender's {@link net.i2p.data.Destination}, the payload, and a signature
|
||||
(signed by the sender's {@link net.i2p.data.SigningPrivateKey}).
|
||||
For DSA_SHA1 destinations, the signature is of the SHA-256 Hash of the payload.
|
||||
For other destination types, the signature is of the payload itself.
|
||||
</p>
|
||||
</body></html>
|
||||
|
@ -1,127 +0,0 @@
|
||||
/*
|
||||
* By zzz 2008, released into the public domain
|
||||
* with no warranty of any kind, either expressed or implied.
|
||||
*/
|
||||
package net.i2p.client.naming;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.util.Locale;
|
||||
import java.util.Properties;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.data.Destination;
|
||||
|
||||
/**
|
||||
* An interface to an external naming service program, with in-memory caching.
|
||||
* This can be used as a simple and flexible way to experiment with
|
||||
* alternative naming systems.
|
||||
*
|
||||
* The external command takes a hostname argument and must return (only) the
|
||||
* 516-byte Base64 destination, or hostname=dest, on stdout.
|
||||
* A trailing \n or \r\n is acceptable.
|
||||
* The command must exit 0 on success. Nonzero on failure is optional.
|
||||
*
|
||||
* The external command can do local and/or remote (via i2p or not) lookups.
|
||||
* No timeouts are implemented here - the author of the external program
|
||||
* must ensure that the program returns in a reasonable amount of time -
|
||||
* (15 sec max suggested)
|
||||
*
|
||||
* Can be used from MetaNamingService, (e.g. after HostsTxtNamingService),
|
||||
* or as the sole naming service.
|
||||
* Supports caching, b32, and b64.
|
||||
*
|
||||
* Sample chained config to put in configadvanced.jsp (restart required):
|
||||
*
|
||||
* i2p.naming.impl=net.i2p.client.naming.MetaNamingService
|
||||
* i2p.nameservicelist=net.i2p.client.naming.HostsTxtNamingService,net.i2p.client.naming.ExecNamingService
|
||||
* i2p.naming.exec.command=/usr/local/bin/i2presolve
|
||||
*
|
||||
* Sample unchained config to put in configadvanced.jsp (restart required):
|
||||
*
|
||||
* i2p.naming.impl=net.i2p.client.naming.ExecNamingService
|
||||
* i2p.naming.exec.command=/usr/local/bin/i2presolve
|
||||
*
|
||||
*/
|
||||
public class ExecNamingService extends DummyNamingService {
|
||||
|
||||
private final static String PROP_EXEC_CMD = "i2p.naming.exec.command";
|
||||
private final static String DEFAULT_EXEC_CMD = "/usr/local/bin/i2presolve";
|
||||
private final static String PROP_SHELL_CMD = "i2p.naming.exec.shell";
|
||||
private final static String DEFAULT_SHELL_CMD = "/bin/bash";
|
||||
|
||||
/**
|
||||
* The naming service should only be constructed and accessed through the
|
||||
* application context. This constructor should only be used by the
|
||||
* appropriate application context itself.
|
||||
*
|
||||
*/
|
||||
public ExecNamingService(I2PAppContext context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Destination lookup(String hostname, Properties lookupOptions, Properties storedOptions) {
|
||||
Destination d = super.lookup(hostname, null, null);
|
||||
if (d != null)
|
||||
return d;
|
||||
// Base32 failed?
|
||||
if (hostname.length() == BASE32_HASH_LENGTH + 8 && hostname.toLowerCase(Locale.US).endsWith(".b32.i2p"))
|
||||
return null;
|
||||
|
||||
hostname = hostname.toLowerCase(Locale.US);
|
||||
|
||||
// lookup
|
||||
String key = fetchAddr(hostname);
|
||||
if (key != null) {
|
||||
_log.error("Success: " + hostname);
|
||||
d = lookupBase64(key);
|
||||
putCache(hostname, d);
|
||||
return d;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// FIXME allow larger Dests for non-null Certs
|
||||
private static final int MAX_RESPONSE = DEST_SIZE + 68 + 10; // allow for hostname= and some trailing stuff
|
||||
private String fetchAddr(String hostname) {
|
||||
String[] commandArr = new String[3];
|
||||
commandArr[0] = _context.getProperty(PROP_SHELL_CMD, DEFAULT_SHELL_CMD);
|
||||
commandArr[1] = "-c";
|
||||
String command = _context.getProperty(PROP_EXEC_CMD, DEFAULT_EXEC_CMD) + " " + hostname;
|
||||
commandArr[2] = command;
|
||||
|
||||
try {
|
||||
Process get = Runtime.getRuntime().exec(commandArr);
|
||||
get.waitFor();
|
||||
int exitValue = get.exitValue();
|
||||
if (exitValue != 0) {
|
||||
_log.error("Exit " + exitValue + " from " + commandArr[0] + " " + commandArr[1] + " \"" + command + "\"");
|
||||
return null;
|
||||
}
|
||||
InputStream is = get.getInputStream();
|
||||
byte[] input = new byte[MAX_RESPONSE];
|
||||
int count = is.read(input);
|
||||
is.close();
|
||||
if (count < DEST_SIZE) {
|
||||
_log.error("Short response: " + command);
|
||||
return null;
|
||||
}
|
||||
String key = new String(input);
|
||||
if (key.startsWith(hostname + "=")) // strip hostname=
|
||||
key = key.substring(hostname.length() + 1);
|
||||
key = key.substring(0, DEST_SIZE); // catch IndexOutOfBounds exception below
|
||||
if (!key.endsWith("AA")) {
|
||||
_log.error("Invalid key: " + command);
|
||||
return null;
|
||||
}
|
||||
if (key.replaceAll("[a-zA-Z0-9~-]", "").length() != 0) {
|
||||
_log.error("Invalid chars: " + command);
|
||||
return null;
|
||||
}
|
||||
return key;
|
||||
} catch (Throwable t) {
|
||||
_log.error("Error fetching the addr", t);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
@ -18,6 +18,7 @@ import net.i2p.I2PAppContext;
|
||||
import net.i2p.data.Base64;
|
||||
import net.i2p.util.Log;
|
||||
import net.i2p.util.SecureFileOutputStream;
|
||||
import net.i2p.util.SystemVersion;
|
||||
|
||||
/**
|
||||
* Java X.509 certificate utilities, consolidated from various places.
|
||||
@ -65,11 +66,18 @@ public class CertUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a value out of the subject distinguished name
|
||||
* Get a value out of the subject distinguished name.
|
||||
*
|
||||
* Warning - unsupported in Android (no javax.naming), returns null.
|
||||
*
|
||||
* @param type e.g. "CN"
|
||||
* @return value or null if not found
|
||||
*/
|
||||
public static String getSubjectValue(X509Certificate cert, String type) {
|
||||
if (SystemVersion.isAndroid()) {
|
||||
error("Don't call this in Android", new UnsupportedOperationException("I did it"));
|
||||
return null;
|
||||
}
|
||||
type = type.toUpperCase(Locale.US);
|
||||
X500Principal p = cert.getSubjectX500Principal();
|
||||
String subj = p.getName();
|
||||
|
@ -14,9 +14,11 @@ import java.security.PublicKey;
|
||||
import java.security.cert.CertificateFactory;
|
||||
import java.security.cert.X509Certificate;
|
||||
|
||||
import net.i2p.util.SystemVersion;
|
||||
|
||||
/**
|
||||
* Dumb storage in a directory for testing.
|
||||
* No sanitization of filenames, unsafe.
|
||||
* Simple storage of each cert in a separate file in a directory.
|
||||
* Limited sanitization of filenames.
|
||||
*
|
||||
* @since 0.9.9
|
||||
*/
|
||||
@ -30,7 +32,9 @@ class DirKeyRing implements KeyRing {
|
||||
|
||||
/**
|
||||
* Cert must be in the file (escaped keyName).crt,
|
||||
* and have a CN == keyName
|
||||
* and have a CN == keyName.
|
||||
*
|
||||
* CN check unsupported on Android.
|
||||
*/
|
||||
public PublicKey getKey(String keyName, String scope, SigType type)
|
||||
throws GeneralSecurityException, IOException {
|
||||
@ -49,14 +53,21 @@ class DirKeyRing implements KeyRing {
|
||||
CertificateFactory cf = CertificateFactory.getInstance("X.509");
|
||||
X509Certificate cert = (X509Certificate)cf.generateCertificate(fis);
|
||||
cert.checkValidity();
|
||||
String cn = CertUtil.getSubjectValue(cert, "CN");
|
||||
if (!keyName.equals(cn))
|
||||
throw new GeneralSecurityException("CN mismatch: " + cn);
|
||||
if (!SystemVersion.isAndroid()) {
|
||||
// getSubjectValue() unsupported on Android.
|
||||
// Any cert problems will be caught in non-Android testing.
|
||||
String cn = CertUtil.getSubjectValue(cert, "CN");
|
||||
if (!keyName.equals(cn))
|
||||
throw new GeneralSecurityException("CN mismatch: " + cn);
|
||||
}
|
||||
return cert.getPublicKey();
|
||||
} finally {
|
||||
try { if (fis != null) fis.close(); } catch (IOException foo) {}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unimplemented, unused.
|
||||
*/
|
||||
public void setKey(String keyName, String scope, PublicKey key) {}
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ public class SessionKeyManager {
|
||||
|
||||
/**
|
||||
* A dummy SessionKeyManager for testing or for passing to
|
||||
* ElGamalAESEngine.encrypt()
|
||||
* ElGamalAESEngine.decrypt()
|
||||
*
|
||||
* @since 0.9.14
|
||||
*/
|
||||
@ -37,7 +37,7 @@ public class SessionKeyManager {
|
||||
|
||||
/**
|
||||
* A dummy SessionKeyManager for testing or for passing to
|
||||
* ElGamalAESEngine.encrypt()
|
||||
* ElGamalAESEngine.decrypt()
|
||||
*
|
||||
* @param context unused
|
||||
* @since public since 0.9.14; protected before that
|
||||
|
@ -212,9 +212,9 @@ riCe6OlAEiNpcc6mMyIYYWFICbrDFTrDR3wXqwc/Jkcx6L5VVWoagpSzbo3yGhc=
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 0.9.8
|
||||
* @since 0.9.8, public since 0.9.14.1
|
||||
*/
|
||||
Map<SigningPublicKey, String> getKeys() {
|
||||
public Map<SigningPublicKey, String> getKeys() {
|
||||
return Collections.unmodifiableMap(_trustedKeys);
|
||||
}
|
||||
|
||||
|
@ -478,12 +478,14 @@ public class DataHelper {
|
||||
String val = (String) entry.getValue();
|
||||
if (name.contains("#") ||
|
||||
name.contains("=") ||
|
||||
name.contains("\r") ||
|
||||
name.contains("\n") ||
|
||||
name.startsWith(";") ||
|
||||
val.contains("#") ||
|
||||
val.contains("\r") ||
|
||||
val.contains("\n")) {
|
||||
if (iae == null)
|
||||
iae = new IllegalArgumentException("Invalid character (one of \"#;=\\n\") in key or value: \"" +
|
||||
iae = new IllegalArgumentException("Invalid character (one of \"#;=\\r\\n\") in key or value: \"" +
|
||||
name + "\" = \"" + val + '\"');
|
||||
continue;
|
||||
}
|
||||
@ -1622,11 +1624,13 @@ public class DataHelper {
|
||||
if (orig == null) return "";
|
||||
String t1 = orig.replace('<', ' ');
|
||||
String rv = t1.replace('>', ' ');
|
||||
rv = rv.replace('\"', ' ');
|
||||
rv = rv.replace('\'', ' ');
|
||||
return rv;
|
||||
}
|
||||
|
||||
private static final String escapeChars[] = {"&", "\"", "<", ">"};
|
||||
private static final String escapeCodes[] = {"&", """, "<", ">"};
|
||||
private static final String escapeChars[] = {"&", "\"", "<", ">", "\"", "'"};
|
||||
private static final String escapeCodes[] = {"&", """, "<", ">", """, "'"};
|
||||
|
||||
/**
|
||||
* Escape a string for inclusion in HTML
|
||||
|
@ -29,6 +29,9 @@ public class Payload extends DataStructureImpl {
|
||||
private byte[] _encryptedData;
|
||||
private byte[] _unencryptedData;
|
||||
|
||||
/** So we don't OOM on I2CP protocol errors. Actual max is smaller. */
|
||||
private static final int MAX_LENGTH = 64*1024;
|
||||
|
||||
public Payload() {
|
||||
}
|
||||
|
||||
@ -51,8 +54,11 @@ public class Payload extends DataStructureImpl {
|
||||
*
|
||||
* Deprecated.
|
||||
* Unless you are doing encryption, use setEncryptedData() instead.
|
||||
* @throws IllegalArgumentException if bigger than 64KB
|
||||
*/
|
||||
public void setUnencryptedData(byte[] data) {
|
||||
if (data.length > MAX_LENGTH)
|
||||
throw new IllegalArgumentException();
|
||||
_unencryptedData = data;
|
||||
}
|
||||
|
||||
@ -61,8 +67,13 @@ public class Payload extends DataStructureImpl {
|
||||
return _encryptedData;
|
||||
}
|
||||
|
||||
/** the real data */
|
||||
/**
|
||||
* the real data
|
||||
* @throws IllegalArgumentException if bigger than 64KB
|
||||
*/
|
||||
public void setEncryptedData(byte[] data) {
|
||||
if (data.length > MAX_LENGTH)
|
||||
throw new IllegalArgumentException();
|
||||
_encryptedData = data;
|
||||
}
|
||||
|
||||
@ -77,7 +88,7 @@ public class Payload extends DataStructureImpl {
|
||||
|
||||
public void readBytes(InputStream in) throws DataFormatException, IOException {
|
||||
int size = (int) DataHelper.readLong(in, 4);
|
||||
if (size < 0) throw new DataFormatException("payload size out of range (" + size + ")");
|
||||
if (size < 0 || size > MAX_LENGTH) throw new DataFormatException("payload size out of range (" + size + ")");
|
||||
_encryptedData = new byte[size];
|
||||
int read = read(in, _encryptedData);
|
||||
if (read != size) throw new DataFormatException("Incorrect number of bytes read in the payload structure");
|
||||
|
Reference in New Issue
Block a user