NetDB: Persistence for blinding cache

This commit is contained in:
zzz
2019-03-29 12:50:41 +00:00
parent ba801be24f
commit eb0920e2c7
3 changed files with 199 additions and 10 deletions

View File

@@ -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);

View File

@@ -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();

View File

@@ -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();
}
}