forked from I2P_Developers/i2p.i2p
NetDB: Persistence for blinding cache
This commit is contained in:
@@ -218,6 +218,7 @@ public final class Blinding {
|
||||
* PRELIMINARY - Subject to change - see proposal 149
|
||||
*
|
||||
* @param b 35+ bytes
|
||||
* @return BlindData structure, use getUnblindedPubKey() for the result
|
||||
* @throws IllegalArgumentException on bad inputs
|
||||
* @throws UnsupportedOperationException unless supported SigTypes
|
||||
* @since 0.9.40
|
||||
@@ -313,6 +314,15 @@ public final class Blinding {
|
||||
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 {
|
||||
net.i2p.data.SimpleDataStructure[] keys = KeyGenerator.getInstance().generateSigningKeys(TYPE);
|
||||
|
@@ -42,7 +42,7 @@ public class BlindData {
|
||||
_clearSPK = spk;
|
||||
_blindType = blindType;
|
||||
_secret = secret;
|
||||
_authType = 0;
|
||||
_authType = -1;
|
||||
_authKey = null;
|
||||
// defer until needed
|
||||
//calculate();
|
||||
@@ -55,6 +55,13 @@ public class BlindData {
|
||||
return _clearSPK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The type of the blinded key
|
||||
*/
|
||||
public SigType getBlindedSigType() {
|
||||
return _blindType;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The blinded key for the current day, non-null
|
||||
*/
|
||||
@@ -115,12 +122,19 @@ public class BlindData {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 0 for no client auth
|
||||
* @return -1 for no client auth
|
||||
*/
|
||||
public int getAuthType() {
|
||||
return _authType;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return null for no client auth
|
||||
*/
|
||||
public PrivateKey getAuthPrivKey() {
|
||||
return _authKey;
|
||||
}
|
||||
|
||||
private synchronized void calculate() {
|
||||
if (_context.isRouterContext()) {
|
||||
RoutingKeyGenerator gen = _context.routingKeyGenerator();
|
||||
@@ -147,16 +161,24 @@ public class BlindData {
|
||||
@Override
|
||||
public synchronized String toString() {
|
||||
calculate();
|
||||
StringBuilder buf = new StringBuilder(256);
|
||||
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);
|
||||
buf.append("\n\tSecret: ").append(_secret);
|
||||
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);
|
||||
if (_dest != null)
|
||||
else
|
||||
buf.append("\n\tDestination: unknown");
|
||||
buf.append(']');
|
||||
return buf.toString();
|
||||
|
@@ -1,14 +1,29 @@
|
||||
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.
|
||||
@@ -25,6 +40,8 @@ class BlindCache {
|
||||
// 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
|
||||
*/
|
||||
@@ -201,18 +218,158 @@ class BlindCache {
|
||||
* Refresh all the data at midnight
|
||||
*
|
||||
*/
|
||||
public void rollover() {
|
||||
public synchronized void rollover() {
|
||||
_reverseCache.clear();
|
||||
for (BlindData bd : _cache.values()) {
|
||||
_reverseCache.put(bd.getBlindedPubKey(), bd);
|
||||
}
|
||||
}
|
||||
|
||||
private void load() {
|
||||
// TODO
|
||||
/**
|
||||
* 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 void store() {
|
||||
// TODO
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user