forked from I2P_Developers/i2p.i2p
propagate from branch 'i2p.i2p.zab.2464' (head 47c50de0eaf4a41d0c0b2df3505ff3b885163791)
to branch 'i2p.i2p' (head a34db176d9f6313db1b8fd16926c8c2ca7e12e09)
This commit is contained in:
@ -1247,7 +1247,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
|
|||||||
header = getErrorPage("dnfp", ERR_DESTINATION_UNKNOWN);
|
header = getErrorPage("dnfp", ERR_DESTINATION_UNKNOWN);
|
||||||
} else if(ahelperPresent) {
|
} else if(ahelperPresent) {
|
||||||
header = getErrorPage("dnfb", ERR_DESTINATION_UNKNOWN);
|
header = getErrorPage("dnfb", ERR_DESTINATION_UNKNOWN);
|
||||||
} else if(destination.length() == 60 && destination.toLowerCase(Locale.US).endsWith(".b32.i2p")) {
|
} else if(destination.length() >= 60 && destination.toLowerCase(Locale.US).endsWith(".b32.i2p")) {
|
||||||
header = getErrorPage("nols", ERR_DESTINATION_UNKNOWN);
|
header = getErrorPage("nols", ERR_DESTINATION_UNKNOWN);
|
||||||
extraMessage = _t("Destination lease set not found");
|
extraMessage = _t("Destination lease set not found");
|
||||||
} else {
|
} else {
|
||||||
|
@ -171,7 +171,7 @@ class SAMUtils {
|
|||||||
String msg;
|
String msg;
|
||||||
if (s.length() >= 516)
|
if (s.length() >= 516)
|
||||||
msg = "Bad Base64 dest: ";
|
msg = "Bad Base64 dest: ";
|
||||||
else if (s.length() == 60 && s.endsWith(".b32.i2p"))
|
else if (s.length() >= 60 && s.endsWith(".b32.i2p"))
|
||||||
msg = "Lease set not found: ";
|
msg = "Lease set not found: ";
|
||||||
else
|
else
|
||||||
msg = "Host name not found: ";
|
msg = "Host name not found: ";
|
||||||
|
@ -218,6 +218,7 @@ public final class Blinding {
|
|||||||
* PRELIMINARY - Subject to change - see proposal 149
|
* PRELIMINARY - Subject to change - see proposal 149
|
||||||
*
|
*
|
||||||
* @param b 35+ bytes
|
* @param b 35+ bytes
|
||||||
|
* @return BlindData structure, use getUnblindedPubKey() for the result
|
||||||
* @throws IllegalArgumentException on bad inputs
|
* @throws IllegalArgumentException on bad inputs
|
||||||
* @throws UnsupportedOperationException unless supported SigTypes
|
* @throws UnsupportedOperationException unless supported SigTypes
|
||||||
* @since 0.9.40
|
* @since 0.9.40
|
||||||
@ -313,6 +314,15 @@ public final class Blinding {
|
|||||||
return Base32.encode(b) + ".b32.i2p";
|
return Base32.encode(b) + ".b32.i2p";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void main(String args[]) throws Exception {
|
||||||
|
if (args.length != 1) {
|
||||||
|
System.out.println("Usage: blinding {56 chars}.b32.i2p");
|
||||||
|
System.exit(1);
|
||||||
|
}
|
||||||
|
System.out.println("Blinded B32: " + args[0]);
|
||||||
|
System.out.println(decode(I2PAppContext.getGlobalContext(), args[0]).toString());
|
||||||
|
}
|
||||||
|
|
||||||
/******
|
/******
|
||||||
public static void main(String args[]) throws Exception {
|
public static void main(String args[]) throws Exception {
|
||||||
net.i2p.data.SimpleDataStructure[] keys = KeyGenerator.getInstance().generateSigningKeys(TYPE);
|
net.i2p.data.SimpleDataStructure[] keys = KeyGenerator.getInstance().generateSigningKeys(TYPE);
|
||||||
|
@ -249,7 +249,8 @@ public final class SelfSignedGenerator {
|
|||||||
cert.verify(cpub);
|
cert.verify(cpub);
|
||||||
if (!cpub.equals(jpub)) {
|
if (!cpub.equals(jpub)) {
|
||||||
boolean ok = false;
|
boolean ok = false;
|
||||||
if (cpub.getClass().getName().equals("sun.security.x509.X509Key")) {
|
if ((jpub instanceof EdDSAPublicKey) &&
|
||||||
|
cpub.getClass().getName().equals("sun.security.x509.X509Key")) {
|
||||||
// X509Certificate will sometimes contain an X509Key rather than the EdDSAPublicKey itself; the contained
|
// X509Certificate will sometimes contain an X509Key rather than the EdDSAPublicKey itself; the contained
|
||||||
// key is valid but needs to be instanced as an EdDSAPublicKey before it can be used.
|
// key is valid but needs to be instanced as an EdDSAPublicKey before it can be used.
|
||||||
try {
|
try {
|
||||||
|
69
core/java/src/net/i2p/crypto/SigContext.java
Normal file
69
core/java/src/net/i2p/crypto/SigContext.java
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
package net.i2p.crypto;
|
||||||
|
|
||||||
|
import java.security.spec.AlgorithmParameterSpec;
|
||||||
|
|
||||||
|
import net.i2p.data.DataHelper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines the context for signing with personalized hashes.
|
||||||
|
* See proposal 148.
|
||||||
|
*
|
||||||
|
* @since 0.9.40
|
||||||
|
*/
|
||||||
|
public enum SigContext {
|
||||||
|
|
||||||
|
SC_NONE (null),
|
||||||
|
SC_DATAGRAM ("sign_datagramI2P"),
|
||||||
|
SC_I2CP ("I2CP_SessionConf"),
|
||||||
|
SC_NETDB ("network_database"),
|
||||||
|
SC_NTCP ("NTCP_1_handshake"),
|
||||||
|
SC_SSU ("SSUHandshakeSign"),
|
||||||
|
SC_STREAMING("streaming_i2psig"),
|
||||||
|
SC_SU3 ("i2pSU3FileFormat"),
|
||||||
|
SC_TEST ("test1234test5678"),
|
||||||
|
|
||||||
|
;
|
||||||
|
|
||||||
|
private final SigContextSpec spec;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The 16 bytes for this type, or null for none
|
||||||
|
*/
|
||||||
|
SigContext(String p) {
|
||||||
|
spec = new SigContextSpec(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The AlgorithmParameterSpec.
|
||||||
|
* Pass this as an argument in setParameter()
|
||||||
|
* to the Blake sign/verify engines.
|
||||||
|
*/
|
||||||
|
public SigContextSpec getSpec() { return spec; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The AlgorithmParameterSpec.
|
||||||
|
* Pass this as an argument in setParameter()
|
||||||
|
* to the Blake sign/verify engines.
|
||||||
|
*/
|
||||||
|
public static class SigContextSpec implements AlgorithmParameterSpec {
|
||||||
|
private final byte[] b;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The 16 bytes for this type, or null for none
|
||||||
|
*/
|
||||||
|
public SigContextSpec(String p) {
|
||||||
|
if (p != null) {
|
||||||
|
b = DataHelper.getASCII(p);
|
||||||
|
if (b.length != 16)
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
} else {
|
||||||
|
b = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The 16 bytes for this type, or null for none
|
||||||
|
*/
|
||||||
|
public byte[] getData() { return b; }
|
||||||
|
}
|
||||||
|
}
|
@ -115,7 +115,7 @@ public class Base32 {
|
|||||||
private static void decode(InputStream in, OutputStream out) throws IOException {
|
private static void decode(InputStream in, OutputStream out) throws IOException {
|
||||||
byte decoded[] = decode(DataHelper.getUTF8(read(in)));
|
byte decoded[] = decode(DataHelper.getUTF8(read(in)));
|
||||||
if (decoded == null) {
|
if (decoded == null) {
|
||||||
System.out.println("FAIL");
|
System.err.println("FAIL");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
out.write(decoded);
|
out.write(decoded);
|
||||||
|
@ -25,6 +25,7 @@ public class BlindData {
|
|||||||
private long _routingKeyGenMod;
|
private long _routingKeyGenMod;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @param secret may be null or zero-length
|
||||||
* @throws IllegalArgumentException on various errors
|
* @throws IllegalArgumentException on various errors
|
||||||
*/
|
*/
|
||||||
public BlindData(I2PAppContext ctx, Destination dest, SigType blindType, String secret) {
|
public BlindData(I2PAppContext ctx, Destination dest, SigType blindType, String secret) {
|
||||||
@ -33,6 +34,7 @@ public class BlindData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @param secret may be null or zero-length
|
||||||
* @throws IllegalArgumentException on various errors
|
* @throws IllegalArgumentException on various errors
|
||||||
*/
|
*/
|
||||||
public BlindData(I2PAppContext ctx, SigningPublicKey spk, SigType blindType, String secret) {
|
public BlindData(I2PAppContext ctx, SigningPublicKey spk, SigType blindType, String secret) {
|
||||||
@ -40,20 +42,41 @@ public class BlindData {
|
|||||||
_clearSPK = spk;
|
_clearSPK = spk;
|
||||||
_blindType = blindType;
|
_blindType = blindType;
|
||||||
_secret = secret;
|
_secret = secret;
|
||||||
_authType = 0;
|
_authType = -1;
|
||||||
_authKey = null;
|
_authKey = null;
|
||||||
// defer until needed
|
// defer until needed
|
||||||
//calculate();
|
//calculate();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return The blinded key for the current day
|
* @return The unblinded SPK, non-null
|
||||||
|
*/
|
||||||
|
public SigningPublicKey getUnblindedPubKey() {
|
||||||
|
return _clearSPK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The type of the blinded key
|
||||||
|
*/
|
||||||
|
public SigType getBlindedSigType() {
|
||||||
|
return _blindType;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The blinded key for the current day, non-null
|
||||||
*/
|
*/
|
||||||
public synchronized SigningPublicKey getBlindedPubKey() {
|
public synchronized SigningPublicKey getBlindedPubKey() {
|
||||||
calculate();
|
calculate();
|
||||||
return _blindSPK;
|
return _blindSPK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The hash of the destination if known, or null
|
||||||
|
*/
|
||||||
|
public synchronized Hash getDestHash() {
|
||||||
|
return _dest != null ? _dest.getHash() : null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return The hash of the blinded key for the current day
|
* @return The hash of the blinded key for the current day
|
||||||
*/
|
*/
|
||||||
@ -99,12 +122,19 @@ public class BlindData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return 0 for no client auth
|
* @return -1 for no client auth
|
||||||
*/
|
*/
|
||||||
public int getAuthType() {
|
public int getAuthType() {
|
||||||
return _authType;
|
return _authType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return null for no client auth
|
||||||
|
*/
|
||||||
|
public PrivateKey getAuthPrivKey() {
|
||||||
|
return _authKey;
|
||||||
|
}
|
||||||
|
|
||||||
private synchronized void calculate() {
|
private synchronized void calculate() {
|
||||||
if (_context.isRouterContext()) {
|
if (_context.isRouterContext()) {
|
||||||
RoutingKeyGenerator gen = _context.routingKeyGenerator();
|
RoutingKeyGenerator gen = _context.routingKeyGenerator();
|
||||||
@ -127,4 +157,30 @@ public class BlindData {
|
|||||||
System.arraycopy(_blindSPK.getData(), 0, hashData, 2, _blindSPK.length());
|
System.arraycopy(_blindSPK.getData(), 0, hashData, 2, _blindSPK.length());
|
||||||
_blindHash = _context.sha().calculateHash(hashData);
|
_blindHash = _context.sha().calculateHash(hashData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized String toString() {
|
||||||
|
calculate();
|
||||||
|
StringBuilder buf = new StringBuilder(1024);
|
||||||
|
buf.append("[BlindData: ");
|
||||||
|
buf.append("\n\tSigningPublicKey: ").append(_clearSPK);
|
||||||
|
buf.append("\n\tAlpha : ").append(_alpha);
|
||||||
|
buf.append("\n\tBlindedPublicKey: ").append(_blindSPK);
|
||||||
|
buf.append("\n\tBlinded Hash : ").append(_blindHash);
|
||||||
|
if (_secret != null)
|
||||||
|
buf.append("\n\tSecret : \"").append(_secret).append('"');
|
||||||
|
buf.append("\n\tAuth Type : ");
|
||||||
|
if (_authType >= 0)
|
||||||
|
buf.append(_authType);
|
||||||
|
else
|
||||||
|
buf.append("none");
|
||||||
|
if (_authKey != null)
|
||||||
|
buf.append("\n\tAuth Key : ").append(_authKey);
|
||||||
|
if (_dest != null)
|
||||||
|
buf.append("\n\tDestination: ").append(_dest);
|
||||||
|
else
|
||||||
|
buf.append("\n\tDestination: unknown");
|
||||||
|
buf.append(']');
|
||||||
|
return buf.toString();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -59,8 +59,9 @@ public abstract class DatabaseEntry extends DataStructureImpl {
|
|||||||
public final static int KEY_TYPE_SERVICE_LIST = 11;
|
public final static int KEY_TYPE_SERVICE_LIST = 11;
|
||||||
|
|
||||||
protected volatile Signature _signature;
|
protected volatile Signature _signature;
|
||||||
protected volatile Hash _currentRoutingKey;
|
// synch: this
|
||||||
protected volatile long _routingKeyGenMod;
|
private Hash _currentRoutingKey;
|
||||||
|
private long _routingKeyGenMod;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A common interface to the timestamp of the two subclasses.
|
* A common interface to the timestamp of the two subclasses.
|
||||||
@ -153,11 +154,13 @@ public abstract class DatabaseEntry extends DataStructureImpl {
|
|||||||
throw new IllegalStateException("Not in router context");
|
throw new IllegalStateException("Not in router context");
|
||||||
RoutingKeyGenerator gen = ctx.routingKeyGenerator();
|
RoutingKeyGenerator gen = ctx.routingKeyGenerator();
|
||||||
long mod = gen.getLastChanged();
|
long mod = gen.getLastChanged();
|
||||||
if (mod != _routingKeyGenMod) {
|
synchronized(this) {
|
||||||
_currentRoutingKey = gen.getRoutingKey(getHash());
|
if (mod != _routingKeyGenMod) {
|
||||||
_routingKeyGenMod = mod;
|
_currentRoutingKey = gen.getRoutingKey(getHash());
|
||||||
|
_routingKeyGenMod = mod;
|
||||||
|
}
|
||||||
|
return _currentRoutingKey;
|
||||||
}
|
}
|
||||||
return _currentRoutingKey;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -33,6 +33,8 @@ public class EncryptedLeaseSet extends LeaseSet2 {
|
|||||||
private LeaseSet2 _decryptedLS2;
|
private LeaseSet2 _decryptedLS2;
|
||||||
private Hash __calculatedHash;
|
private Hash __calculatedHash;
|
||||||
private SigningPrivateKey _alpha;
|
private SigningPrivateKey _alpha;
|
||||||
|
// to decrypt with if we don't have full dest
|
||||||
|
private SigningPublicKey _unblindedSPK;
|
||||||
private String _secret;
|
private String _secret;
|
||||||
private final Log _log;
|
private final Log _log;
|
||||||
|
|
||||||
@ -120,11 +122,31 @@ public class EncryptedLeaseSet extends LeaseSet2 {
|
|||||||
_destination = dest;
|
_destination = dest;
|
||||||
}
|
}
|
||||||
SigningPublicKey spk = dest.getSigningPublicKey();
|
SigningPublicKey spk = dest.getSigningPublicKey();
|
||||||
|
setSigningKey(spk);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Overridden to set the blinded key
|
||||||
|
*
|
||||||
|
* @param spk unblinded key non-null, must be EdDSA_SHA512_Ed25519 or RedDSA_SHA512_Ed25519
|
||||||
|
* @throws IllegalStateException if already signed
|
||||||
|
* @throws IllegalArgumentException if not EdDSA
|
||||||
|
* @since 0.9.40
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void setSigningKey(SigningPublicKey spk) {
|
||||||
|
// TODO already-set checks
|
||||||
SigType type = spk.getType();
|
SigType type = spk.getType();
|
||||||
if (type != SigType.EdDSA_SHA512_Ed25519 &&
|
if (type != SigType.EdDSA_SHA512_Ed25519 &&
|
||||||
type != SigType.RedDSA_SHA512_Ed25519)
|
type != SigType.RedDSA_SHA512_Ed25519)
|
||||||
throw new IllegalArgumentException();
|
throw new IllegalArgumentException();
|
||||||
SigningPublicKey bpk = blind();
|
if (_unblindedSPK != null) {
|
||||||
|
if (!_unblindedSPK.equals(spk))
|
||||||
|
throw new IllegalArgumentException("unblinded pubkey mismatch");
|
||||||
|
} else {
|
||||||
|
_unblindedSPK = spk;
|
||||||
|
}
|
||||||
|
SigningPublicKey bpk = blind(spk);
|
||||||
if (_signingKey == null)
|
if (_signingKey == null)
|
||||||
_signingKey = bpk;
|
_signingKey = bpk;
|
||||||
else if (!_signingKey.equals(bpk))
|
else if (!_signingKey.equals(bpk))
|
||||||
@ -139,13 +161,12 @@ public class EncryptedLeaseSet extends LeaseSet2 {
|
|||||||
*
|
*
|
||||||
* @since 0.9.39
|
* @since 0.9.39
|
||||||
*/
|
*/
|
||||||
private SigningPublicKey blind() {
|
private SigningPublicKey blind(SigningPublicKey spk) {
|
||||||
SigningPublicKey spk = _destination.getSigningPublicKey();
|
|
||||||
I2PAppContext ctx = I2PAppContext.getGlobalContext();
|
I2PAppContext ctx = I2PAppContext.getGlobalContext();
|
||||||
if (_published <= 0)
|
if (_published <= 0)
|
||||||
_alpha = Blinding.generateAlpha(ctx, _destination.getSigningPublicKey(), _secret);
|
_alpha = Blinding.generateAlpha(ctx, spk, _secret);
|
||||||
else
|
else
|
||||||
_alpha = Blinding.generateAlpha(ctx, _destination.getSigningPublicKey(), _secret, _published);
|
_alpha = Blinding.generateAlpha(ctx, spk, _secret, _published);
|
||||||
SigningPublicKey rv = Blinding.blind(spk, _alpha);
|
SigningPublicKey rv = Blinding.blind(spk, _alpha);
|
||||||
if (_log.shouldDebug())
|
if (_log.shouldDebug())
|
||||||
_log.debug("Blind:" +
|
_log.debug("Blind:" +
|
||||||
@ -476,14 +497,13 @@ public class EncryptedLeaseSet extends LeaseSet2 {
|
|||||||
* @since 0.9.39
|
* @since 0.9.39
|
||||||
*/
|
*/
|
||||||
private byte[] getSubcredential(I2PAppContext ctx) {
|
private byte[] getSubcredential(I2PAppContext ctx) {
|
||||||
if (_destination == null)
|
if (_unblindedSPK == null)
|
||||||
throw new IllegalStateException("no known destination to decrypt with");
|
throw new IllegalStateException("no known SPK to decrypt with");
|
||||||
SigningPublicKey destspk = _destination.getSigningPublicKey();
|
int spklen = _unblindedSPK.length();
|
||||||
int spklen = destspk.length();
|
|
||||||
byte[] in = new byte[spklen + 4];
|
byte[] in = new byte[spklen + 4];
|
||||||
// SHA256("credential" || spk || sigtypein || sigtypeout)
|
// SHA256("credential" || spk || sigtypein || sigtypeout)
|
||||||
System.arraycopy(destspk.getData(), 0, in, 0, spklen);
|
System.arraycopy(_unblindedSPK.getData(), 0, in, 0, spklen);
|
||||||
DataHelper.toLong(in, spklen, 2, destspk.getType().getCode());
|
DataHelper.toLong(in, spklen, 2, _unblindedSPK.getType().getCode());
|
||||||
DataHelper.toLong(in, spklen + 2, 2, SigType.RedDSA_SHA512_Ed25519.getCode());
|
DataHelper.toLong(in, spklen + 2, 2, SigType.RedDSA_SHA512_Ed25519.getCode());
|
||||||
byte[] credential = hash(ctx, CREDENTIAL, in);
|
byte[] credential = hash(ctx, CREDENTIAL, in);
|
||||||
byte[] spk = _signingKey.getData();
|
byte[] spk = _signingKey.getData();
|
||||||
@ -572,8 +592,9 @@ public class EncryptedLeaseSet extends LeaseSet2 {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
_log.info("ELS2 outer sig verify success");
|
_log.info("ELS2 outer sig verify success");
|
||||||
if (_destination == null) {
|
if (_unblindedSPK == null) {
|
||||||
_log.warn("ELS2 no dest to decrypt with");
|
if (_log.shouldWarn())
|
||||||
|
_log.warn("ELS2 no dest/SPK to decrypt with", new Exception("I did it"));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
|
@ -10,9 +10,11 @@ package net.i2p.data;
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import javax.security.auth.Destroyable;
|
||||||
|
|
||||||
import net.i2p.crypto.EncType;
|
import net.i2p.crypto.EncType;
|
||||||
import net.i2p.crypto.KeyGenerator;
|
import net.i2p.crypto.KeyGenerator;
|
||||||
|
import net.i2p.util.SimpleByteCache;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Defines the PrivateKey as defined by the I2P data structure spec.
|
* Defines the PrivateKey as defined by the I2P data structure spec.
|
||||||
@ -24,7 +26,7 @@ import net.i2p.crypto.KeyGenerator;
|
|||||||
*
|
*
|
||||||
* @author jrandom
|
* @author jrandom
|
||||||
*/
|
*/
|
||||||
public class PrivateKey extends SimpleDataStructure {
|
public class PrivateKey extends SimpleDataStructure implements Destroyable {
|
||||||
private static final EncType DEF_TYPE = EncType.ELGAMAL_2048;
|
private static final EncType DEF_TYPE = EncType.ELGAMAL_2048;
|
||||||
public final static int KEYSIZE_BYTES = DEF_TYPE.getPrivkeyLen();
|
public final static int KEYSIZE_BYTES = DEF_TYPE.getPrivkeyLen();
|
||||||
|
|
||||||
@ -89,13 +91,36 @@ public class PrivateKey extends SimpleDataStructure {
|
|||||||
return KeyGenerator.getPublicKey(this);
|
return KeyGenerator.getPublicKey(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* javax.security.auth.Destroyable interface
|
||||||
|
*
|
||||||
|
* @since 0.9.40
|
||||||
|
*/
|
||||||
|
public void destroy() {
|
||||||
|
byte[] data = _data;
|
||||||
|
if (data != null) {
|
||||||
|
_data = null;
|
||||||
|
Arrays.fill(data, (byte) 0);
|
||||||
|
SimpleByteCache.release(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* javax.security.auth.Destroyable interface
|
||||||
|
*
|
||||||
|
* @since 0.9.40
|
||||||
|
*/
|
||||||
|
public boolean isDestroyed() {
|
||||||
|
return _data == null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @since 0.9.38
|
* @since 0.9.38
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
StringBuilder buf = new StringBuilder(64);
|
StringBuilder buf = new StringBuilder(64);
|
||||||
buf.append("[PrivateKey ").append(_type).append(": ");
|
buf.append("[PrivateKey ").append(_type).append(' ');
|
||||||
int length = length();
|
int length = length();
|
||||||
if (_data == null) {
|
if (_data == null) {
|
||||||
buf.append("null");
|
buf.append("null");
|
||||||
|
@ -10,10 +10,12 @@ package net.i2p.data;
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import javax.security.auth.Destroyable;
|
||||||
|
|
||||||
import net.i2p.crypto.Blinding;
|
import net.i2p.crypto.Blinding;
|
||||||
import net.i2p.crypto.KeyGenerator;
|
import net.i2p.crypto.KeyGenerator;
|
||||||
import net.i2p.crypto.SigType;
|
import net.i2p.crypto.SigType;
|
||||||
|
import net.i2p.util.SimpleByteCache;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Defines the SigningPrivateKey as defined by the I2P data structure spec.
|
* Defines the SigningPrivateKey as defined by the I2P data structure spec.
|
||||||
@ -26,7 +28,7 @@ import net.i2p.crypto.SigType;
|
|||||||
*
|
*
|
||||||
* @author jrandom
|
* @author jrandom
|
||||||
*/
|
*/
|
||||||
public class SigningPrivateKey extends SimpleDataStructure {
|
public class SigningPrivateKey extends SimpleDataStructure implements Destroyable {
|
||||||
private static final SigType DEF_TYPE = SigType.DSA_SHA1;
|
private static final SigType DEF_TYPE = SigType.DSA_SHA1;
|
||||||
public final static int KEYSIZE_BYTES = DEF_TYPE.getPrivkeyLen();
|
public final static int KEYSIZE_BYTES = DEF_TYPE.getPrivkeyLen();
|
||||||
|
|
||||||
@ -115,13 +117,36 @@ public class SigningPrivateKey extends SimpleDataStructure {
|
|||||||
return b == 0;
|
return b == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* javax.security.auth.Destroyable interface
|
||||||
|
*
|
||||||
|
* @since 0.9.40
|
||||||
|
*/
|
||||||
|
public void destroy() {
|
||||||
|
byte[] data = _data;
|
||||||
|
if (data != null) {
|
||||||
|
_data = null;
|
||||||
|
Arrays.fill(data, (byte) 0);
|
||||||
|
SimpleByteCache.release(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* javax.security.auth.Destroyable interface
|
||||||
|
*
|
||||||
|
* @since 0.9.40
|
||||||
|
*/
|
||||||
|
public boolean isDestroyed() {
|
||||||
|
return _data == null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @since 0.9.8
|
* @since 0.9.8
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
StringBuilder buf = new StringBuilder(64);
|
StringBuilder buf = new StringBuilder(64);
|
||||||
buf.append("[SigningPrivateKey ").append(_type).append(": ");
|
buf.append("[SigningPrivateKey ").append(_type).append(' ');
|
||||||
int length = length();
|
int length = length();
|
||||||
if (_data == null) {
|
if (_data == null) {
|
||||||
buf.append("null");
|
buf.append("null");
|
||||||
|
@ -9,11 +9,10 @@ Build-Depends: debhelper (>= 7.0.50~)
|
|||||||
,ant (>= 1.8)
|
,ant (>= 1.8)
|
||||||
,debconf
|
,debconf
|
||||||
,openjdk-7-jdk
|
,openjdk-7-jdk
|
||||||
# Ant requires java 6 tools.jar:
|
|
||||||
# Unable to locate tools.jar. Expected to find it in /usr/lib/jvm/java-6-openjdk-amd64/lib/tools.jar
|
|
||||||
,openjdk-6-jdk
|
|
||||||
,libtomcat8-java
|
,libtomcat8-java
|
||||||
,dh-apparmor
|
,dh-apparmor
|
||||||
|
,dh-systemd
|
||||||
|
,bash-completion
|
||||||
,gettext
|
,gettext
|
||||||
,libgetopt-java
|
,libgetopt-java
|
||||||
,libjson-simple-java (<< 3)
|
,libjson-simple-java (<< 3)
|
||||||
@ -33,7 +32,7 @@ Depends: ${java:Depends}, ${shlibs:Depends},
|
|||||||
libjbigi-jni,
|
libjbigi-jni,
|
||||||
lsb-base (>= 3.0-6),
|
lsb-base (>= 3.0-6),
|
||||||
service-wrapper
|
service-wrapper
|
||||||
Description: Anonymous network (I2P)
|
Description: Invisible Internet Project (I2P) - anonymous network
|
||||||
I2P is an anonymizing network, offering a simple layer that identity-sensitive
|
I2P is an anonymizing network, offering a simple layer that identity-sensitive
|
||||||
applications can use to securely communicate. All data is wrapped with several
|
applications can use to securely communicate. All data is wrapped with several
|
||||||
layers of encryption, and the network is both distributed and dynamic, with no
|
layers of encryption, and the network is both distributed and dynamic, with no
|
||||||
@ -48,7 +47,7 @@ Section: java
|
|||||||
Priority: optional
|
Priority: optional
|
||||||
Depends: ${shlibs:Depends}, i2p-router
|
Depends: ${shlibs:Depends}, i2p-router
|
||||||
Homepage: https://geti2p.net/
|
Homepage: https://geti2p.net/
|
||||||
Description: I2P libjbigi library
|
Description: Invisible Internet Project (I2P) - libjbigi library
|
||||||
This Package contains the libjbigi JNI library (and on x86 platforms, jcpuid).
|
This Package contains the libjbigi JNI library (and on x86 platforms, jcpuid).
|
||||||
.
|
.
|
||||||
libjbigi is a math library that is part of the I2P installation. Use of this
|
libjbigi is a math library that is part of the I2P installation. Use of this
|
||||||
@ -62,7 +61,7 @@ Section: doc
|
|||||||
Priority: extra
|
Priority: extra
|
||||||
Depends: ${misc:Depends}
|
Depends: ${misc:Depends}
|
||||||
Suggests: i2p, default-jdk-doc
|
Suggests: i2p, default-jdk-doc
|
||||||
Description: I2P developer documentation
|
Description: Invisible Internet Project (I2P) - developer documentation
|
||||||
I2P is an anonymizing network, offering a simple layer that identity-sensitive
|
I2P is an anonymizing network, offering a simple layer that identity-sensitive
|
||||||
applications can use to securely communicate. All data is wrapped with several
|
applications can use to securely communicate. All data is wrapped with several
|
||||||
layers of encryption, and the network is both distributed and dynamic, with no
|
layers of encryption, and the network is both distributed and dynamic, with no
|
||||||
@ -88,7 +87,7 @@ Recommends: libjbigi-jni, fonts-dejavu
|
|||||||
Suggests: apparmor
|
Suggests: apparmor
|
||||||
,privoxy
|
,privoxy
|
||||||
,syndie
|
,syndie
|
||||||
Description: Router for I2P
|
Description: Invisible Internet Project (I2P) - Router
|
||||||
I2P is an anonymizing network, offering a simple layer that identity-sensitive
|
I2P is an anonymizing network, offering a simple layer that identity-sensitive
|
||||||
applications can use to securely communicate. All data is wrapped with several
|
applications can use to securely communicate. All data is wrapped with several
|
||||||
layers of encryption, and the network is both distributed and dynamic, with no
|
layers of encryption, and the network is both distributed and dynamic, with no
|
||||||
|
@ -9,11 +9,9 @@ Build-Depends: debhelper (>= 7.0.50~)
|
|||||||
,ant (>= 1.8)
|
,ant (>= 1.8)
|
||||||
,debconf
|
,debconf
|
||||||
,openjdk-7-jdk
|
,openjdk-7-jdk
|
||||||
# Ant requires java 6 tools.jar:
|
|
||||||
# Unable to locate tools.jar. Expected to find it in /usr/lib/jvm/java-6-openjdk-amd64/lib/tools.jar
|
|
||||||
,openjdk-6-jdk
|
|
||||||
,glassfish-javaee
|
,glassfish-javaee
|
||||||
,dh-apparmor
|
,dh-apparmor
|
||||||
|
,dh-systemd
|
||||||
,bash-completion
|
,bash-completion
|
||||||
,gettext
|
,gettext
|
||||||
,libgetopt-java
|
,libgetopt-java
|
||||||
|
@ -9,9 +9,6 @@ Build-Depends: debhelper (>= 7.0.50~)
|
|||||||
,ant (>= 1.8)
|
,ant (>= 1.8)
|
||||||
,debconf
|
,debconf
|
||||||
,openjdk-7-jdk
|
,openjdk-7-jdk
|
||||||
# Ant requires java 6 tools.jar:
|
|
||||||
# Unable to locate tools.jar. Expected to find it in /usr/lib/jvm/java-6-openjdk-amd64/lib/tools.jar
|
|
||||||
,openjdk-6-jdk
|
|
||||||
,glassfish-javaee
|
,glassfish-javaee
|
||||||
,dh-apparmor
|
,dh-apparmor
|
||||||
,dh-systemd
|
,dh-systemd
|
||||||
|
54
debian/po/sv.po
vendored
54
debian/po/sv.po
vendored
@ -1,7 +1,7 @@
|
|||||||
# SOME DESCRIPTIVE TITLE.
|
# SOME DESCRIPTIVE TITLE.
|
||||||
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
||||||
# This file is distributed under the same license as the PACKAGE package.
|
# This file is distributed under the same license as the PACKAGE package.
|
||||||
#
|
#
|
||||||
# Translators:
|
# Translators:
|
||||||
# Anders Nilsson <anders@devode.se>, 2015
|
# Anders Nilsson <anders@devode.se>, 2015
|
||||||
# cacapo <handelsehorisont@gmail.com>, 2015
|
# cacapo <handelsehorisont@gmail.com>, 2015
|
||||||
@ -11,22 +11,24 @@
|
|||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: I2P\n"
|
"Project-Id-Version: I2P\n"
|
||||||
"Report-Msgid-Bugs-To: https://trac.i2p2.de/\n"
|
"Report-Msgid-Bugs-To: i2p@packages.debian.org\n"
|
||||||
"POT-Creation-Date: 2015-02-18 22:14+0000\n"
|
"POT-Creation-Date: 2017-11-12 14:01+0000\n"
|
||||||
"PO-Revision-Date: 2017-06-30 21:32+0000\n"
|
"PO-Revision-Date: 2019-03-26 06:10+0100\n"
|
||||||
"Last-Translator: Jony\n"
|
"Last-Translator: Jonatan Nyberg <jonatan.nyberg.karl@gmail.com>\n"
|
||||||
"Language-Team: Swedish (Sweden) (http://www.transifex.com/otf/I2P/language/sv_SE/)\n"
|
"Language-Team: Swedish (Sweden) (http://www.transifex.com/otf/I2P/language/"
|
||||||
|
"sv_SE/)\n"
|
||||||
|
"Language: sv_SE\n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"Language: sv_SE\n"
|
|
||||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||||
|
"X-Generator: Poedit 2.2.1\n"
|
||||||
|
|
||||||
#. Type: boolean
|
#. Type: boolean
|
||||||
#. Description
|
#. Description
|
||||||
#: ../i2p.templates:2001
|
#: ../i2p.templates:2001
|
||||||
msgid "Should the I2P router be started at boot?"
|
msgid "Should the I2P router be started at boot?"
|
||||||
msgstr "Ska I2P routern startas vid systemstart?"
|
msgstr "Ska I2P-routern startas vid systemstart?"
|
||||||
|
|
||||||
#. Type: boolean
|
#. Type: boolean
|
||||||
#. Description
|
#. Description
|
||||||
@ -34,7 +36,9 @@ msgstr "Ska I2P routern startas vid systemstart?"
|
|||||||
msgid ""
|
msgid ""
|
||||||
"The I2P router can be run as a daemon that starts automatically when your "
|
"The I2P router can be run as a daemon that starts automatically when your "
|
||||||
"computer boots up. This is the recommended configuration."
|
"computer boots up. This is the recommended configuration."
|
||||||
msgstr "I2P routern kan köras som en tjänst (demon) som automatiskt startas när datorn startas. Detta är den rekommenderade konfigurationen. "
|
msgstr ""
|
||||||
|
"I2P-routern kan köras som en tjänst (demon) som automatiskt startas när "
|
||||||
|
"datorn startas. Detta är den rekommenderade konfigurationen."
|
||||||
|
|
||||||
#. Type: string
|
#. Type: string
|
||||||
#. Description
|
#. Description
|
||||||
@ -46,11 +50,15 @@ msgstr "I2P-demon användare:"
|
|||||||
#. Description
|
#. Description
|
||||||
#: ../i2p.templates:3001
|
#: ../i2p.templates:3001
|
||||||
msgid ""
|
msgid ""
|
||||||
"By default I2P is configured to run under the account i2psvc when running as"
|
"By default I2P is configured to run under the account i2psvc when running as "
|
||||||
" a daemon. To use an **existing** I2P profile you may enter a different "
|
"a daemon. To use an **existing** I2P profile you may enter a different "
|
||||||
"account name here. For example, if your previous I2P installation is at "
|
"account name here. For example, if your previous I2P installation is at /"
|
||||||
"/home/user/i2p, you may enter 'user' here."
|
"home/user/i2p, you may enter 'user' here."
|
||||||
msgstr "Som standard är I2P inställt för att köras under användaren i2psvc när det körs som tjänst. För att använda ett **existerande** I2P profil, ange en annan användare här. Exempelvis, om din tidigare I2P installation är /home/user/i2p så ange 'user' här."
|
msgstr ""
|
||||||
|
"Som standard är I2P konfigurerad för att köras under användaren i2psvc när "
|
||||||
|
"det körs som tjänst. Om du vill använda en **befintlig** I2P-profil kan du "
|
||||||
|
"ange ett annat kontonamn här. Till exempel, om din tidigare I2P-installation "
|
||||||
|
"är på /home/user/I2P, kan du ange \"användare\" här."
|
||||||
|
|
||||||
#. Type: string
|
#. Type: string
|
||||||
#. Description
|
#. Description
|
||||||
@ -58,7 +66,9 @@ msgstr "Som standard är I2P inställt för att köras under användaren i2psvc
|
|||||||
msgid ""
|
msgid ""
|
||||||
"Very important: If a user other than the default of 'i2psvc' is entered "
|
"Very important: If a user other than the default of 'i2psvc' is entered "
|
||||||
"here, the chosen username *MUST* already exist."
|
"here, the chosen username *MUST* already exist."
|
||||||
msgstr "OBS! Viktigt: Om en annan användare än standard 'i2psvc' skrivs in här. *MÅSTE* det användarnamnet redan existera."
|
msgstr ""
|
||||||
|
"Mycket viktigt: om en annan användare än standard 'i2psvc' anges här, "
|
||||||
|
"*MÅSTE* det valda användarnamnet *redan finnas."
|
||||||
|
|
||||||
#. Type: string
|
#. Type: string
|
||||||
#. Description
|
#. Description
|
||||||
@ -70,7 +80,7 @@ msgstr "Minne som kan tilldelas I2P:"
|
|||||||
#. Description
|
#. Description
|
||||||
#: ../i2p.templates:4001
|
#: ../i2p.templates:4001
|
||||||
msgid "By default, I2P will only be allowed to use up to 128MB of RAM."
|
msgid "By default, I2P will only be allowed to use up to 128MB of RAM."
|
||||||
msgstr "Som standard kommer I2P bara att använda up till 128MB RAM."
|
msgstr "Som standard får I2P endast använda upp till 128 MB RAM."
|
||||||
|
|
||||||
#. Type: string
|
#. Type: string
|
||||||
#. Description
|
#. Description
|
||||||
@ -78,13 +88,15 @@ msgstr "Som standard kommer I2P bara att använda up till 128MB RAM."
|
|||||||
msgid ""
|
msgid ""
|
||||||
"High bandwidth routers, as well as routers with a lot of active torrents / "
|
"High bandwidth routers, as well as routers with a lot of active torrents / "
|
||||||
"plugins, may need to have this value increased."
|
"plugins, may need to have this value increased."
|
||||||
msgstr "För routrar med hög bandbredd eller routrar med hög aktivitet kan detta behöva ökas"
|
msgstr ""
|
||||||
|
"För routrar med hög bandbredd samt routrar med en hel del aktiva torrenter / "
|
||||||
|
"insticksmoduler, kan detta värde behöva ökas."
|
||||||
|
|
||||||
#. Type: boolean
|
#. Type: boolean
|
||||||
#. Description
|
#. Description
|
||||||
#: ../i2p.templates:5001
|
#: ../i2p.templates:5001
|
||||||
msgid "Run I2P daemon confined with AppArmor"
|
msgid "Should the I2P daemon be confined with AppArmor?"
|
||||||
msgstr "Kör I2P begränsad av AppArmor"
|
msgstr "Ska I2P-demonen vara begränsad med AppArmor?"
|
||||||
|
|
||||||
#. Type: boolean
|
#. Type: boolean
|
||||||
#. Description
|
#. Description
|
||||||
@ -92,4 +104,6 @@ msgstr "Kör I2P begränsad av AppArmor"
|
|||||||
msgid ""
|
msgid ""
|
||||||
"With this option enabled I2P will be sandboxed with AppArmor, restricting "
|
"With this option enabled I2P will be sandboxed with AppArmor, restricting "
|
||||||
"which files and directories may be accessed by I2P."
|
"which files and directories may be accessed by I2P."
|
||||||
msgstr "Med det här valet aktiverat kommer I2P att köras i sandlåda med AppArmor, som begränsar vilka filer och mappar som kan kommas åt av I2P."
|
msgstr ""
|
||||||
|
"Med det här alternativet aktiverat kommer I2P att köras i sandlåda med "
|
||||||
|
"AppArmor, som begränsar vilka filer och mappar som kan kommas åt av I2P."
|
||||||
|
10
history.txt
10
history.txt
@ -1,3 +1,13 @@
|
|||||||
|
2019-03-27 zzz
|
||||||
|
* NetDB: Cache blinding data for lookups and decryption (proposal #123)
|
||||||
|
|
||||||
|
2019-03-23 zzz
|
||||||
|
* Data: Preliminary work on new b32 format (proposal #149)
|
||||||
|
* SelfSignedGenerator:
|
||||||
|
- Fix generation with Ed25519ph keys (ticket #2465)
|
||||||
|
- Increase serial number from 63 to 71 bits
|
||||||
|
* SusiDNS: Add import feature (ticket #2447)
|
||||||
|
|
||||||
2019-03-22 zzz
|
2019-03-22 zzz
|
||||||
* i2ptunnel: Escape {} in URLs (ticket #2130)
|
* i2ptunnel: Escape {} in URLs (ticket #2130)
|
||||||
|
|
||||||
|
@ -13,10 +13,12 @@ import java.io.Writer;
|
|||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
import net.i2p.data.BlindData;
|
||||||
import net.i2p.data.DatabaseEntry;
|
import net.i2p.data.DatabaseEntry;
|
||||||
import net.i2p.data.Destination;
|
import net.i2p.data.Destination;
|
||||||
import net.i2p.data.Hash;
|
import net.i2p.data.Hash;
|
||||||
import net.i2p.data.LeaseSet;
|
import net.i2p.data.LeaseSet;
|
||||||
|
import net.i2p.data.SigningPublicKey;
|
||||||
import net.i2p.data.router.RouterInfo;
|
import net.i2p.data.router.RouterInfo;
|
||||||
import net.i2p.router.networkdb.reseed.ReseedChecker;
|
import net.i2p.router.networkdb.reseed.ReseedChecker;
|
||||||
|
|
||||||
@ -161,4 +163,19 @@ public abstract class NetworkDatabaseFacade implements Service {
|
|||||||
* @since 0.9.16
|
* @since 0.9.16
|
||||||
*/
|
*/
|
||||||
public boolean isNegativeCachedForever(Hash key) { return false; }
|
public boolean isNegativeCachedForever(Hash key) { return false; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param spk unblinded key
|
||||||
|
* @return BlindData or null
|
||||||
|
* @since 0.9.40
|
||||||
|
*/
|
||||||
|
public BlindData getBlindData(SigningPublicKey spk) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param bd new BlindData to put in the cache
|
||||||
|
* @since 0.9.40
|
||||||
|
*/
|
||||||
|
public void setBlindData(BlindData bd) {}
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,7 @@ public class RouterVersion {
|
|||||||
/** deprecated */
|
/** deprecated */
|
||||||
public final static String ID = "Monotone";
|
public final static String ID = "Monotone";
|
||||||
public final static String VERSION = CoreVersion.VERSION;
|
public final static String VERSION = CoreVersion.VERSION;
|
||||||
public final static long BUILD = 1;
|
public final static long BUILD = 2;
|
||||||
|
|
||||||
/** for example "-test" */
|
/** for example "-test" */
|
||||||
public final static String EXTRA = "";
|
public final static String EXTRA = "";
|
||||||
|
@ -608,7 +608,7 @@ class ClientConnectionRunner {
|
|||||||
* called after a new leaseSet is granted by the client, the NetworkDb has been
|
* called after a new leaseSet is granted by the client, the NetworkDb has been
|
||||||
* updated. This takes care of all the LeaseRequestState stuff (including firing any jobs)
|
* updated. This takes care of all the LeaseRequestState stuff (including firing any jobs)
|
||||||
*
|
*
|
||||||
* @param ls, if encrypted, the encrypted LS, not the decrypted one
|
* @param ls if encrypted, the encrypted LS, not the decrypted one
|
||||||
*/
|
*/
|
||||||
void leaseSetCreated(LeaseSet ls) {
|
void leaseSetCreated(LeaseSet ls) {
|
||||||
Hash h = ls.getDestination().calculateHash();
|
Hash h = ls.getDestination().calculateHash();
|
||||||
|
@ -9,9 +9,12 @@ import java.util.Locale;
|
|||||||
import net.i2p.crypto.Blinding;
|
import net.i2p.crypto.Blinding;
|
||||||
import net.i2p.data.Base32;
|
import net.i2p.data.Base32;
|
||||||
import net.i2p.data.BlindData;
|
import net.i2p.data.BlindData;
|
||||||
|
import net.i2p.data.DatabaseEntry;
|
||||||
import net.i2p.data.Destination;
|
import net.i2p.data.Destination;
|
||||||
|
import net.i2p.data.EncryptedLeaseSet;
|
||||||
import net.i2p.data.Hash;
|
import net.i2p.data.Hash;
|
||||||
import net.i2p.data.LeaseSet;
|
import net.i2p.data.LeaseSet;
|
||||||
|
import net.i2p.data.SigningPublicKey;
|
||||||
import net.i2p.data.i2cp.DestReplyMessage;
|
import net.i2p.data.i2cp.DestReplyMessage;
|
||||||
import net.i2p.data.i2cp.HostReplyMessage;
|
import net.i2p.data.i2cp.HostReplyMessage;
|
||||||
import net.i2p.data.i2cp.I2CPMessage;
|
import net.i2p.data.i2cp.I2CPMessage;
|
||||||
@ -34,6 +37,7 @@ class LookupDestJob extends JobImpl {
|
|||||||
private final String _name;
|
private final String _name;
|
||||||
private final SessionId _sessID;
|
private final SessionId _sessID;
|
||||||
private final Hash _fromLocalDest;
|
private final Hash _fromLocalDest;
|
||||||
|
private final BlindData _blindData;
|
||||||
|
|
||||||
private static final long DEFAULT_TIMEOUT = 15*1000;
|
private static final long DEFAULT_TIMEOUT = 15*1000;
|
||||||
|
|
||||||
@ -69,6 +73,7 @@ class LookupDestJob extends JobImpl {
|
|||||||
_timeout = timeout;
|
_timeout = timeout;
|
||||||
_sessID = sessID;
|
_sessID = sessID;
|
||||||
_fromLocalDest = fromLocalDest;
|
_fromLocalDest = fromLocalDest;
|
||||||
|
BlindData bd = null;
|
||||||
if (name != null && name.length() >= 60) {
|
if (name != null && name.length() >= 60) {
|
||||||
// convert a b32 lookup to a hash lookup
|
// convert a b32 lookup to a hash lookup
|
||||||
String nlc = name.toLowerCase(Locale.US);
|
String nlc = name.toLowerCase(Locale.US);
|
||||||
@ -82,8 +87,16 @@ class LookupDestJob extends JobImpl {
|
|||||||
name = null;
|
name = null;
|
||||||
} else if (b.length >= 35) {
|
} else if (b.length >= 35) {
|
||||||
// encrypted LS2
|
// encrypted LS2
|
||||||
|
// lookup the blinded hash
|
||||||
try {
|
try {
|
||||||
BlindData bd = Blinding.decode(context, b);
|
bd = Blinding.decode(context, b);
|
||||||
|
SigningPublicKey spk = bd.getUnblindedPubKey();
|
||||||
|
BlindData bd2 = getContext().netDb().getBlindData(spk);
|
||||||
|
if (bd2 != null) {
|
||||||
|
bd = bd2;
|
||||||
|
} else {
|
||||||
|
getContext().netDb().setBlindData(bd);
|
||||||
|
}
|
||||||
h = bd.getBlindedHash();
|
h = bd.getBlindedHash();
|
||||||
if (_log.shouldDebug())
|
if (_log.shouldDebug())
|
||||||
_log.debug("Converting name lookup " + name + " to blinded " + h);
|
_log.debug("Converting name lookup " + name + " to blinded " + h);
|
||||||
@ -91,7 +104,9 @@ class LookupDestJob extends JobImpl {
|
|||||||
} catch (RuntimeException re) {
|
} catch (RuntimeException re) {
|
||||||
if (_log.shouldWarn())
|
if (_log.shouldWarn())
|
||||||
_log.debug("Failed blinding conversion of " + name, re);
|
_log.debug("Failed blinding conversion of " + name, re);
|
||||||
// lookup as a name, which will probably fail
|
// Do NOT lookup as a name, naming service will call us again and infinite loop
|
||||||
|
name = null;
|
||||||
|
// h and name both null, runJob will fail immediately
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -99,6 +114,7 @@ class LookupDestJob extends JobImpl {
|
|||||||
}
|
}
|
||||||
_hash = h;
|
_hash = h;
|
||||||
_name = name;
|
_name = name;
|
||||||
|
_blindData = bd;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getName() { return _name != null ?
|
public String getName() { return _name != null ?
|
||||||
@ -107,6 +123,15 @@ class LookupDestJob extends JobImpl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void runJob() {
|
public void runJob() {
|
||||||
|
if (_blindData != null) {
|
||||||
|
Destination d = _blindData.getDestination();
|
||||||
|
if (d != null) {
|
||||||
|
if (_log.shouldDebug())
|
||||||
|
_log.debug("Found cached b33 lookup " + _name + " to " + d);
|
||||||
|
returnDest(d);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (_name != null) {
|
if (_name != null) {
|
||||||
// inline, ignore timeout
|
// inline, ignore timeout
|
||||||
Destination d = getContext().namingService().lookup(_name);
|
Destination d = getContext().namingService().lookup(_name);
|
||||||
@ -119,9 +144,13 @@ class LookupDestJob extends JobImpl {
|
|||||||
_log.debug("Failed name lookup " + _name);
|
_log.debug("Failed name lookup " + _name);
|
||||||
returnFail();
|
returnFail();
|
||||||
}
|
}
|
||||||
} else {
|
} else if (_hash != null) {
|
||||||
DoneJob done = new DoneJob(getContext());
|
DoneJob done = new DoneJob(getContext());
|
||||||
|
// TODO tell router this is an encrypted lookup, skip 38 or earlier ffs?
|
||||||
getContext().netDb().lookupDestination(_hash, done, _timeout, _fromLocalDest);
|
getContext().netDb().lookupDestination(_hash, done, _timeout, _fromLocalDest);
|
||||||
|
} else {
|
||||||
|
// blinding decode fail
|
||||||
|
returnFail();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -132,6 +161,18 @@ class LookupDestJob extends JobImpl {
|
|||||||
public String getName() { return "LeaseSet Lookup Reply to Client"; }
|
public String getName() { return "LeaseSet Lookup Reply to Client"; }
|
||||||
public void runJob() {
|
public void runJob() {
|
||||||
Destination dest = getContext().netDb().lookupDestinationLocally(_hash);
|
Destination dest = getContext().netDb().lookupDestinationLocally(_hash);
|
||||||
|
if (dest == null && _blindData != null) {
|
||||||
|
// TODO store and lookup original hash instead
|
||||||
|
LeaseSet ls = getContext().netDb().lookupLeaseSetLocally(_hash);
|
||||||
|
if (ls != null && ls.getType() == DatabaseEntry.KEY_TYPE_ENCRYPTED_LS2) {
|
||||||
|
// already decrypted
|
||||||
|
EncryptedLeaseSet encls = (EncryptedLeaseSet) ls;
|
||||||
|
LeaseSet decls = encls.getDecryptedLeaseSet();
|
||||||
|
if (decls != null) {
|
||||||
|
dest = decls.getDestination();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
if (dest != null) {
|
if (dest != null) {
|
||||||
if (_log.shouldDebug())
|
if (_log.shouldDebug())
|
||||||
_log.debug("Found hash lookup " + _hash + " to " + dest);
|
_log.debug("Found hash lookup " + _hash + " to " + dest);
|
||||||
@ -163,8 +204,10 @@ class LookupDestJob extends JobImpl {
|
|||||||
I2CPMessage msg;
|
I2CPMessage msg;
|
||||||
if (_reqID >= 0)
|
if (_reqID >= 0)
|
||||||
msg = new HostReplyMessage(_sessID, HostReplyMessage.RESULT_FAILURE, _reqID);
|
msg = new HostReplyMessage(_sessID, HostReplyMessage.RESULT_FAILURE, _reqID);
|
||||||
else
|
else if (_hash != null)
|
||||||
msg = new DestReplyMessage(_hash);
|
msg = new DestReplyMessage(_hash);
|
||||||
|
else
|
||||||
|
return; // shouldn't happen
|
||||||
try {
|
try {
|
||||||
_runner.doSend(msg);
|
_runner.doSend(msg);
|
||||||
} catch (I2CPMessageException ime) {}
|
} catch (I2CPMessageException ime) {}
|
||||||
|
@ -76,7 +76,7 @@ public class SendMessageDirectJob extends JobImpl {
|
|||||||
_targetHash = toPeer;
|
_targetHash = toPeer;
|
||||||
if (timeoutMs < 10*1000) {
|
if (timeoutMs < 10*1000) {
|
||||||
if (_log.shouldLog(Log.WARN))
|
if (_log.shouldLog(Log.WARN))
|
||||||
_log.warn("Very little time given [" + timeoutMs + "], resetting to 5s", new Exception("stingy caller!"));
|
_log.warn("Very little time given [" + timeoutMs + "], resetting to 10s", new Exception("stingy caller!"));
|
||||||
_expiration = ctx.clock().now() + 10*1000;
|
_expiration = ctx.clock().now() + 10*1000;
|
||||||
} else {
|
} else {
|
||||||
_expiration = timeoutMs + ctx.clock().now();
|
_expiration = timeoutMs + ctx.clock().now();
|
||||||
|
@ -0,0 +1,375 @@
|
|||||||
|
package net.i2p.router.networkdb.kademlia;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.BufferedWriter;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStreamWriter;
|
||||||
|
import java.io.PrintWriter;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
import net.i2p.crypto.Blinding;
|
||||||
|
import net.i2p.crypto.EncType;
|
||||||
|
import net.i2p.crypto.SigType;
|
||||||
|
import net.i2p.data.Base64;
|
||||||
|
import net.i2p.data.BlindData;
|
||||||
|
import net.i2p.data.DataFormatException;
|
||||||
|
import net.i2p.data.DataHelper;
|
||||||
|
import net.i2p.data.Destination;
|
||||||
|
import net.i2p.data.Hash;
|
||||||
|
import net.i2p.data.PrivateKey;
|
||||||
|
import net.i2p.data.SigningPublicKey;
|
||||||
|
import net.i2p.router.RouterContext;
|
||||||
|
import net.i2p.util.Log;
|
||||||
|
import net.i2p.util.SecureFileOutputStream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cache of blinding data. See proposal 123.
|
||||||
|
*
|
||||||
|
* @since 0.9.40
|
||||||
|
*/
|
||||||
|
class BlindCache {
|
||||||
|
|
||||||
|
private final RouterContext _context;
|
||||||
|
// unblinded key
|
||||||
|
private final ConcurrentHashMap<SigningPublicKey, BlindData> _cache;
|
||||||
|
// blinded key
|
||||||
|
private final ConcurrentHashMap<SigningPublicKey, BlindData> _reverseCache;
|
||||||
|
// dest hash
|
||||||
|
private final ConcurrentHashMap<Hash, BlindData> _hashCache;
|
||||||
|
|
||||||
|
private static final String PERSIST_FILE = "router.blindcache.dat";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Caller MUST call startup() to load persistent cache from disk
|
||||||
|
*/
|
||||||
|
public BlindCache(RouterContext ctx) {
|
||||||
|
_context = ctx;
|
||||||
|
_cache = new ConcurrentHashMap<SigningPublicKey, BlindData>(32);
|
||||||
|
_reverseCache = new ConcurrentHashMap<SigningPublicKey, BlindData>(32);
|
||||||
|
_hashCache = new ConcurrentHashMap<Hash, BlindData>(32);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* May be restarted by calling startup() again.
|
||||||
|
*/
|
||||||
|
public synchronized void shutdown() {
|
||||||
|
store();
|
||||||
|
_cache.clear();
|
||||||
|
_reverseCache.clear();
|
||||||
|
_hashCache.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void startup() {
|
||||||
|
load();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The hash to lookup for a dest.
|
||||||
|
* If known to be blinded, returns the current blinded hash.
|
||||||
|
* If not known to be blinded, returns the standard dest hash.
|
||||||
|
*
|
||||||
|
* @param dest may or may not be blinded
|
||||||
|
* @return the unblinded or blinded hash
|
||||||
|
*/
|
||||||
|
public Hash getHash(Destination dest) {
|
||||||
|
Hash rv = getBlindedHash(dest);
|
||||||
|
if (rv != null)
|
||||||
|
return rv;
|
||||||
|
return dest.getHash();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The hash to lookup for a dest hash.
|
||||||
|
* If known to be blinded, returns the current blinded hash.
|
||||||
|
* If not known to be blinded, returns h.
|
||||||
|
*
|
||||||
|
* @param h may or may not be blinded
|
||||||
|
* @return the blinded hash or h
|
||||||
|
*/
|
||||||
|
public Hash getHash(Hash h) {
|
||||||
|
BlindData bd = _hashCache.get(h);
|
||||||
|
if (bd != null)
|
||||||
|
return bd.getBlindedHash();
|
||||||
|
return h;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The hash to lookup for a dest.
|
||||||
|
* If known to be blinded, returns the current blinded hash.
|
||||||
|
* If not known to be blinded, returns null.
|
||||||
|
*
|
||||||
|
* @param dest may or may not be blinded
|
||||||
|
* @return the blinded hash or null if not blinded
|
||||||
|
*/
|
||||||
|
public Hash getBlindedHash(Destination dest) {
|
||||||
|
BlindData bd = _cache.get(dest.getSigningPublicKey());
|
||||||
|
if (bd != null)
|
||||||
|
return bd.getBlindedHash();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The hash to lookup for a SPK known to be blinded.
|
||||||
|
* Default blinded type assumed.
|
||||||
|
* Secret assumed null.
|
||||||
|
*
|
||||||
|
* @param spk known to be blinded
|
||||||
|
* @return the blinded hash
|
||||||
|
* @throws IllegalArgumentException on various errors
|
||||||
|
*/
|
||||||
|
public Hash getBlindedHash(SigningPublicKey spk) {
|
||||||
|
BlindData bd = _cache.get(spk);
|
||||||
|
if (bd == null)
|
||||||
|
bd = new BlindData(_context, spk, Blinding.getDefaultBlindedType(spk.getType()), null);
|
||||||
|
addToCache(bd);
|
||||||
|
return bd.getBlindedHash();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mark a destination as known to be blinded
|
||||||
|
*
|
||||||
|
* @param dest known to be blinded
|
||||||
|
* @param blindedType null for default
|
||||||
|
* @param secret may be null
|
||||||
|
* @throws IllegalArgumentException on various errors
|
||||||
|
*/
|
||||||
|
public void setBlinded(Destination dest, SigType blindedType, String secret) {
|
||||||
|
SigningPublicKey spk = dest.getSigningPublicKey();
|
||||||
|
BlindData bd = _cache.get(spk);
|
||||||
|
if (bd != null) {
|
||||||
|
bd.setDestination(dest);
|
||||||
|
} else {
|
||||||
|
if (blindedType == null)
|
||||||
|
blindedType = Blinding.getDefaultBlindedType(spk.getType());
|
||||||
|
bd = new BlindData(_context, dest, blindedType, secret);
|
||||||
|
bd.setDestination(dest);
|
||||||
|
addToCache(bd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the destination to the cache entry.
|
||||||
|
* Must previously be in cache.
|
||||||
|
*
|
||||||
|
* @param dest known to be blinded
|
||||||
|
* @throws IllegalArgumentException on various errors
|
||||||
|
*/
|
||||||
|
public void setBlinded(Destination dest) {
|
||||||
|
SigningPublicKey spk = dest.getSigningPublicKey();
|
||||||
|
BlindData bd = _cache.get(spk);
|
||||||
|
if (bd != null) {
|
||||||
|
bd.setDestination(dest);
|
||||||
|
_hashCache.putIfAbsent(dest.getHash(), bd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addToCache(BlindData bd) {
|
||||||
|
_cache.put(bd.getUnblindedPubKey(), bd);
|
||||||
|
_reverseCache.put(bd.getBlindedPubKey(), bd);
|
||||||
|
Destination dest = bd.getDestination();
|
||||||
|
if (dest != null)
|
||||||
|
_hashCache.put(dest.getHash(), bd);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The cached data or null
|
||||||
|
*/
|
||||||
|
public BlindData getData(Destination dest) {
|
||||||
|
BlindData rv = getData(dest.getSigningPublicKey());
|
||||||
|
if (rv != null) {
|
||||||
|
Destination d = rv.getDestination();
|
||||||
|
if (d == null)
|
||||||
|
rv.setDestination(dest);
|
||||||
|
else if (!dest.equals(d))
|
||||||
|
rv = null; // mismatch ???
|
||||||
|
}
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The cached data or null
|
||||||
|
*
|
||||||
|
* @param spk the unblinded public key
|
||||||
|
*/
|
||||||
|
public BlindData getData(SigningPublicKey spk) {
|
||||||
|
SigType type = spk.getType();
|
||||||
|
if (type != SigType.EdDSA_SHA512_Ed25519 &&
|
||||||
|
type != SigType.RedDSA_SHA512_Ed25519)
|
||||||
|
return null;
|
||||||
|
return _cache.get(spk);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The cached data or null
|
||||||
|
*
|
||||||
|
* @param spk the blinded public key
|
||||||
|
*/
|
||||||
|
public BlindData getReverseData(SigningPublicKey spk) {
|
||||||
|
SigType type = spk.getType();
|
||||||
|
if (type != SigType.RedDSA_SHA512_Ed25519)
|
||||||
|
return null;
|
||||||
|
return _reverseCache.get(spk);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Refresh all the data at midnight
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public synchronized void rollover() {
|
||||||
|
_reverseCache.clear();
|
||||||
|
for (BlindData bd : _cache.values()) {
|
||||||
|
_reverseCache.put(bd.getBlindedPubKey(), bd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load from file.
|
||||||
|
* Format:
|
||||||
|
* sigtype,bsigtype,b64 pubkey,[b64 secret],[b64 dest]
|
||||||
|
*/
|
||||||
|
private synchronized void load() {
|
||||||
|
File file = new File(_context.getConfigDir(), PERSIST_FILE);
|
||||||
|
if (!file.exists())
|
||||||
|
return;
|
||||||
|
Log log = _context.logManager().getLog(BlindCache.class);
|
||||||
|
int count = 0;
|
||||||
|
BufferedReader br = null;
|
||||||
|
try {
|
||||||
|
br = new BufferedReader(new InputStreamReader(
|
||||||
|
new FileInputStream(file), "ISO-8859-1"));
|
||||||
|
String line = null;
|
||||||
|
while ( (line = br.readLine()) != null) {
|
||||||
|
if (line.startsWith("#"))
|
||||||
|
continue;
|
||||||
|
try {
|
||||||
|
addToCache(fromPersistentString(line));
|
||||||
|
count++;
|
||||||
|
} catch (IllegalArgumentException iae) {
|
||||||
|
if (log.shouldLog(Log.WARN))
|
||||||
|
log.warn("Error reading cache entry", iae);
|
||||||
|
} catch (DataFormatException dfe) {
|
||||||
|
if (log.shouldLog(Log.WARN))
|
||||||
|
log.warn("Error reading cache entry", dfe);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (IOException ioe) {
|
||||||
|
if (log.shouldLog(Log.WARN) && file.exists())
|
||||||
|
log.warn("Error reading the blinding cache file", ioe);
|
||||||
|
} finally {
|
||||||
|
if (br != null) try { br.close(); } catch (IOException ioe) {}
|
||||||
|
}
|
||||||
|
if (log.shouldLog(Log.INFO))
|
||||||
|
log.info("Loaded " + count + " entries from " + file);
|
||||||
|
}
|
||||||
|
|
||||||
|
private synchronized void store() {
|
||||||
|
if (_cache.isEmpty())
|
||||||
|
return;
|
||||||
|
Log log = _context.logManager().getLog(BlindCache.class);
|
||||||
|
int count = 0;
|
||||||
|
File file = new File(_context.getConfigDir(), PERSIST_FILE);
|
||||||
|
PrintWriter out = null;
|
||||||
|
try {
|
||||||
|
out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(new SecureFileOutputStream(file), "ISO-8859-1")));
|
||||||
|
out.println("# Blinding cache entries. Format is: sigtype,bsigtype,authtype,time,key,[secret],[privkey],[dest]");
|
||||||
|
for (BlindData bd : _cache.values()) {
|
||||||
|
out.println(toPersistentString(bd));
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
if (out.checkError())
|
||||||
|
throw new IOException("Failed write to " + file);
|
||||||
|
} catch (IOException ioe) {
|
||||||
|
if (log.shouldLog(Log.WARN))
|
||||||
|
log.warn("Error writing the blinding cache File", ioe);
|
||||||
|
} finally {
|
||||||
|
if (out != null) out.close();
|
||||||
|
}
|
||||||
|
if (log.shouldLog(Log.INFO))
|
||||||
|
log.info("Stored " + count + " entries to " + file);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Format:
|
||||||
|
* sigtype,bsigtype,authtype,timestamp,b64 pubkey,[b64 secret],[b64 auth privkey],[b64 dest]
|
||||||
|
*/
|
||||||
|
private BlindData fromPersistentString(String line) throws DataFormatException {
|
||||||
|
String[] ss = DataHelper.split(line, ",", 8);
|
||||||
|
if (ss.length != 8)
|
||||||
|
throw new DataFormatException("bad format");
|
||||||
|
int ist1, ist2, auth;
|
||||||
|
long time;
|
||||||
|
try {
|
||||||
|
ist1 = Integer.parseInt(ss[0]);
|
||||||
|
ist2 = Integer.parseInt(ss[1]);
|
||||||
|
auth = Integer.parseInt(ss[2]);
|
||||||
|
time = Long.parseLong(ss[3]);
|
||||||
|
} catch (NumberFormatException nfe) {
|
||||||
|
throw new DataFormatException("bad codes", nfe);
|
||||||
|
}
|
||||||
|
SigType st1 = SigType.getByCode(ist1);
|
||||||
|
SigType st2 = SigType.getByCode(ist2);
|
||||||
|
if (st1 == null || !st1.isAvailable() || st2 == null || !st2.isAvailable())
|
||||||
|
throw new DataFormatException("bad codes");
|
||||||
|
SigningPublicKey spk = new SigningPublicKey(st1);
|
||||||
|
spk.fromBase64(ss[4]);
|
||||||
|
String secret;
|
||||||
|
if (ss[5].length() > 0) {
|
||||||
|
byte[] b = Base64.decode(ss[5]);
|
||||||
|
if (b == null)
|
||||||
|
throw new DataFormatException("bad secret");
|
||||||
|
secret = DataHelper.getUTF8(b);
|
||||||
|
} else {
|
||||||
|
secret = null;
|
||||||
|
}
|
||||||
|
PrivateKey privkey;
|
||||||
|
if (ss[6].length() > 0) {
|
||||||
|
byte[] b = Base64.decode(ss[6]);
|
||||||
|
if (b == null)
|
||||||
|
throw new DataFormatException("bad privkey");
|
||||||
|
privkey = new PrivateKey(EncType.ECIES_X25519, b);
|
||||||
|
} else {
|
||||||
|
privkey = null;
|
||||||
|
}
|
||||||
|
BlindData rv;
|
||||||
|
// TODO pass privkey
|
||||||
|
if (ss[7].length() > 0) {
|
||||||
|
Destination dest = new Destination(ss[7]);
|
||||||
|
if (!spk.equals(dest.getSigningPublicKey()))
|
||||||
|
throw new DataFormatException("spk mismatch");
|
||||||
|
rv = new BlindData(_context, dest, st2, secret);
|
||||||
|
} else {
|
||||||
|
rv = new BlindData(_context, spk, st2, secret);
|
||||||
|
}
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Format:
|
||||||
|
* sigtype,bsigtype,authtype,timestamp,b64 pubkey,[b64 secret],[b64 auth privkey],[b64 dest]
|
||||||
|
*/
|
||||||
|
private static String toPersistentString(BlindData bd) {
|
||||||
|
StringBuilder buf = new StringBuilder(1024);
|
||||||
|
SigningPublicKey spk = bd.getUnblindedPubKey();
|
||||||
|
buf.append(spk.getType().getCode()).append(',');
|
||||||
|
buf.append(bd.getBlindedSigType().getCode()).append(',');
|
||||||
|
buf.append(bd.getAuthType()).append(',');
|
||||||
|
// timestamp todo
|
||||||
|
buf.append('0').append(',');
|
||||||
|
buf.append(spk.toBase64()).append(',');
|
||||||
|
String secret = bd.getSecret();
|
||||||
|
if (secret != null && secret.length() > 0)
|
||||||
|
buf.append(Base64.encode(secret));
|
||||||
|
buf.append(',');
|
||||||
|
PrivateKey pk = bd.getAuthPrivKey();
|
||||||
|
if (pk != null)
|
||||||
|
buf.append(pk.toBase64());
|
||||||
|
buf.append(',');
|
||||||
|
Destination dest = bd.getDestination();
|
||||||
|
if (dest != null)
|
||||||
|
buf.append(dest.toBase64());
|
||||||
|
return buf.toString();
|
||||||
|
}
|
||||||
|
}
|
@ -21,15 +21,18 @@ import java.util.Set;
|
|||||||
|
|
||||||
import net.i2p.crypto.SigAlgo;
|
import net.i2p.crypto.SigAlgo;
|
||||||
import net.i2p.crypto.SigType;
|
import net.i2p.crypto.SigType;
|
||||||
|
import net.i2p.data.BlindData;
|
||||||
import net.i2p.data.Certificate;
|
import net.i2p.data.Certificate;
|
||||||
import net.i2p.data.DatabaseEntry;
|
import net.i2p.data.DatabaseEntry;
|
||||||
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;
|
||||||
|
import net.i2p.data.EncryptedLeaseSet;
|
||||||
import net.i2p.data.Hash;
|
import net.i2p.data.Hash;
|
||||||
import net.i2p.data.KeyCertificate;
|
import net.i2p.data.KeyCertificate;
|
||||||
import net.i2p.data.LeaseSet;
|
import net.i2p.data.LeaseSet;
|
||||||
import net.i2p.data.LeaseSet2;
|
import net.i2p.data.LeaseSet2;
|
||||||
|
import net.i2p.data.SigningPublicKey;
|
||||||
import net.i2p.data.i2np.DatabaseLookupMessage;
|
import net.i2p.data.i2np.DatabaseLookupMessage;
|
||||||
import net.i2p.data.i2np.DatabaseStoreMessage;
|
import net.i2p.data.i2np.DatabaseStoreMessage;
|
||||||
import net.i2p.data.router.RouterAddress;
|
import net.i2p.data.router.RouterAddress;
|
||||||
@ -73,6 +76,7 @@ public abstract class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacad
|
|||||||
private volatile long _lastRIPublishTime;
|
private volatile long _lastRIPublishTime;
|
||||||
private NegativeLookupCache _negativeCache;
|
private NegativeLookupCache _negativeCache;
|
||||||
protected final int _networkID;
|
protected final int _networkID;
|
||||||
|
private final BlindCache _blindCache;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Map of Hash to RepublishLeaseSetJob for leases we'realready managing.
|
* Map of Hash to RepublishLeaseSetJob for leases we'realready managing.
|
||||||
@ -171,6 +175,7 @@ public abstract class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacad
|
|||||||
_publishingLeaseSets = new HashMap<Hash, RepublishLeaseSetJob>(8);
|
_publishingLeaseSets = new HashMap<Hash, RepublishLeaseSetJob>(8);
|
||||||
_activeRequests = new HashMap<Hash, SearchJob>(8);
|
_activeRequests = new HashMap<Hash, SearchJob>(8);
|
||||||
_reseedChecker = new ReseedChecker(context);
|
_reseedChecker = new ReseedChecker(context);
|
||||||
|
_blindCache = new BlindCache(context);
|
||||||
context.statManager().createRateStat("netDb.lookupDeferred", "how many lookups are deferred?", "NetworkDatabase", new long[] { 60*60*1000 });
|
context.statManager().createRateStat("netDb.lookupDeferred", "how many lookups are deferred?", "NetworkDatabase", new long[] { 60*60*1000 });
|
||||||
context.statManager().createRateStat("netDb.exploreKeySet", "how many keys are queued for exploration?", "NetworkDatabase", new long[] { 60*60*1000 });
|
context.statManager().createRateStat("netDb.exploreKeySet", "how many keys are queued for exploration?", "NetworkDatabase", new long[] { 60*60*1000 });
|
||||||
context.statManager().createRateStat("netDb.negativeCache", "Aborted lookup, already cached", "NetworkDatabase", new long[] { 60*60*1000l });
|
context.statManager().createRateStat("netDb.negativeCache", "Aborted lookup, already cached", "NetworkDatabase", new long[] { 60*60*1000l });
|
||||||
@ -246,7 +251,9 @@ public abstract class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacad
|
|||||||
//_ds = null;
|
//_ds = null;
|
||||||
_exploreKeys.clear(); // hope this doesn't cause an explosion, it shouldn't.
|
_exploreKeys.clear(); // hope this doesn't cause an explosion, it shouldn't.
|
||||||
// _exploreKeys = null;
|
// _exploreKeys = null;
|
||||||
_negativeCache.clear();
|
if (_negativeCache != null)
|
||||||
|
_negativeCache.clear();
|
||||||
|
_blindCache.shutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void restart() {
|
public synchronized void restart() {
|
||||||
@ -257,6 +264,7 @@ public abstract class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacad
|
|||||||
}
|
}
|
||||||
_ds.restart();
|
_ds.restart();
|
||||||
_exploreKeys.clear();
|
_exploreKeys.clear();
|
||||||
|
_blindCache.startup();
|
||||||
|
|
||||||
_initialized = true;
|
_initialized = true;
|
||||||
|
|
||||||
@ -287,6 +295,7 @@ public abstract class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacad
|
|||||||
// _exploreKeys = new HashSet(64);
|
// _exploreKeys = new HashSet(64);
|
||||||
_dbDir = dbDir;
|
_dbDir = dbDir;
|
||||||
_negativeCache = new NegativeLookupCache(_context);
|
_negativeCache = new NegativeLookupCache(_context);
|
||||||
|
_blindCache.startup();
|
||||||
|
|
||||||
createHandlers();
|
createHandlers();
|
||||||
|
|
||||||
@ -463,6 +472,27 @@ public abstract class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacad
|
|||||||
return _kb.size();
|
return _kb.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param spk unblinded key
|
||||||
|
* @return BlindData or null
|
||||||
|
* @since 0.9.40
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public BlindData getBlindData(SigningPublicKey spk) {
|
||||||
|
return _blindCache.getData(spk);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param bd new BlindData to put in the cache
|
||||||
|
* @since 0.9.40
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void setBlindData(BlindData bd) {
|
||||||
|
if (_log.shouldWarn())
|
||||||
|
_log.warn("Adding to blind cache: " + bd);
|
||||||
|
_blindCache.addToCache(bd);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return RouterInfo, LeaseSet, or null, validated
|
* @return RouterInfo, LeaseSet, or null, validated
|
||||||
* @since 0.8.3
|
* @since 0.8.3
|
||||||
@ -476,10 +506,12 @@ public abstract class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacad
|
|||||||
int type = rv.getType();
|
int type = rv.getType();
|
||||||
if (DatabaseEntry.isLeaseSet(type)) {
|
if (DatabaseEntry.isLeaseSet(type)) {
|
||||||
LeaseSet ls = (LeaseSet)rv;
|
LeaseSet ls = (LeaseSet)rv;
|
||||||
if (ls.isCurrent(Router.CLOCK_FUDGE_FACTOR))
|
if (ls.isCurrent(Router.CLOCK_FUDGE_FACTOR)) {
|
||||||
return rv;
|
return rv;
|
||||||
else
|
} else {
|
||||||
|
key = _blindCache.getHash(key);
|
||||||
fail(key);
|
fail(key);
|
||||||
|
}
|
||||||
} else if (type == DatabaseEntry.KEY_TYPE_ROUTERINFO) {
|
} else if (type == DatabaseEntry.KEY_TYPE_ROUTERINFO) {
|
||||||
try {
|
try {
|
||||||
if (validate((RouterInfo)rv) == null)
|
if (validate((RouterInfo)rv) == null)
|
||||||
@ -533,6 +565,7 @@ public abstract class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacad
|
|||||||
} else {
|
} else {
|
||||||
//if (_log.shouldLog(Log.DEBUG))
|
//if (_log.shouldLog(Log.DEBUG))
|
||||||
// _log.debug("leaseSet not found locally, running search");
|
// _log.debug("leaseSet not found locally, running search");
|
||||||
|
key = _blindCache.getHash(key);
|
||||||
search(key, onFindJob, onFailedLookupJob, timeoutMs, true, fromLocalDest);
|
search(key, onFindJob, onFailedLookupJob, timeoutMs, true, fromLocalDest);
|
||||||
}
|
}
|
||||||
//if (_log.shouldLog(Log.DEBUG))
|
//if (_log.shouldLog(Log.DEBUG))
|
||||||
@ -549,6 +582,7 @@ public abstract class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacad
|
|||||||
*/
|
*/
|
||||||
public void lookupLeaseSetRemotely(Hash key, Hash fromLocalDest) {
|
public void lookupLeaseSetRemotely(Hash key, Hash fromLocalDest) {
|
||||||
if (!_initialized) return;
|
if (!_initialized) return;
|
||||||
|
key = _blindCache.getHash(key);
|
||||||
search(key, null, null, 20*1000, true, fromLocalDest);
|
search(key, null, null, 20*1000, true, fromLocalDest);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -564,6 +598,7 @@ public abstract class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacad
|
|||||||
if (ls.isCurrent(Router.CLOCK_FUDGE_FACTOR)) {
|
if (ls.isCurrent(Router.CLOCK_FUDGE_FACTOR)) {
|
||||||
return ls;
|
return ls;
|
||||||
} else {
|
} else {
|
||||||
|
key = _blindCache.getHash(key);
|
||||||
fail(key);
|
fail(key);
|
||||||
// this was an interesting key, so either refetch it or simply explore with it
|
// this was an interesting key, so either refetch it or simply explore with it
|
||||||
_exploreKeys.add(key);
|
_exploreKeys.add(key);
|
||||||
@ -599,6 +634,7 @@ public abstract class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacad
|
|||||||
_log.info("Negative cached, not searching dest: " + key);
|
_log.info("Negative cached, not searching dest: " + key);
|
||||||
_context.jobQueue().addJob(onFinishedJob);
|
_context.jobQueue().addJob(onFinishedJob);
|
||||||
} else {
|
} else {
|
||||||
|
key = _blindCache.getHash(key);
|
||||||
search(key, onFinishedJob, onFinishedJob, timeoutMs, true, fromLocalDest);
|
search(key, onFinishedJob, onFinishedJob, timeoutMs, true, fromLocalDest);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -892,12 +928,47 @@ public abstract class KademliaNetworkDatabaseFacade extends NetworkDatabaseFacad
|
|||||||
if (rv != null && !leaseSet.getDestination().equals(rv.getDestination()))
|
if (rv != null && !leaseSet.getDestination().equals(rv.getDestination()))
|
||||||
throw new IllegalArgumentException("LS Hash collision");
|
throw new IllegalArgumentException("LS Hash collision");
|
||||||
|
|
||||||
|
EncryptedLeaseSet encls = null;
|
||||||
|
if (leaseSet.getType() == DatabaseEntry.KEY_TYPE_ENCRYPTED_LS2) {
|
||||||
|
// set dest or key before validate() calls verifySignature() which
|
||||||
|
// will do the decryption
|
||||||
|
BlindData bd = _blindCache.getReverseData(leaseSet.getSigningKey());
|
||||||
|
if (bd != null) {
|
||||||
|
if (_log.shouldWarn())
|
||||||
|
_log.warn("Found blind data for encls: " + bd);
|
||||||
|
encls = (EncryptedLeaseSet) leaseSet;
|
||||||
|
Destination dest = bd.getDestination();
|
||||||
|
if (dest != null) {
|
||||||
|
encls.setDestination(dest);
|
||||||
|
} else {
|
||||||
|
encls.setSigningKey(bd.getUnblindedPubKey());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (_log.shouldWarn())
|
||||||
|
_log.warn("No blind data found for encls: " + encls);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
String err = validate(key, leaseSet);
|
String err = validate(key, leaseSet);
|
||||||
if (err != null)
|
if (err != null)
|
||||||
throw new IllegalArgumentException("Invalid store attempt - " + err);
|
throw new IllegalArgumentException("Invalid store attempt - " + err);
|
||||||
|
|
||||||
_ds.put(key, leaseSet);
|
_ds.put(key, leaseSet);
|
||||||
|
|
||||||
|
if (encls != null) {
|
||||||
|
// we now have decrypted it, store it as well
|
||||||
|
LeaseSet decls = encls.getDecryptedLeaseSet();
|
||||||
|
if (decls != null) {
|
||||||
|
if (_log.shouldWarn())
|
||||||
|
_log.warn("Successfully decrypted encls: " + decls);
|
||||||
|
// recursion
|
||||||
|
Destination dest = decls.getDestination();
|
||||||
|
store(dest.getHash(), decls);
|
||||||
|
_blindCache.setBlinded(dest);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Iterate through the old failure / success count, copying over the old
|
// Iterate through the old failure / success count, copying over the old
|
||||||
// values (if any tunnels overlap between leaseSets). no need to be
|
// values (if any tunnels overlap between leaseSets). no need to be
|
||||||
// ueberthreadsafe fascists here, since these values are just heuristics
|
// ueberthreadsafe fascists here, since these values are just heuristics
|
||||||
|
Reference in New Issue
Block a user