Router: Prep for RI sig types:

- New router.sigType config
 - Generate / regenerate router keys based on config
 - New router.keys2 file format for sig types and padding
 - Fix RouterInfo.readBytes() signature verification with sig types
 - Catch unset padding in KeysAndCert.writeBytes()
 - Catch key errors in ReadRouterJob
 - Show RI sig type on /netdb in console
 - Move some things from Router to startup classes
 - Startup classes package private
 - Buffer readin of key files
 - Remove configurability of router.info and router.keys file locations
This commit is contained in:
zzz
2014-08-23 23:48:16 +00:00
parent d7feab116f
commit 593779b54f
16 changed files with 252 additions and 87 deletions

View File

@ -415,7 +415,9 @@ public class NetDbRenderer {
// shouldnt happen
buf.append("<b>" + _("Published") + ":</b> in ").append(DataHelper.formatDuration2(0-age)).append("???<br>\n");
}
buf.append("<b>" + _("Address(es)") + ":</b> ");
buf.append("<b>").append(_("Signing Key")).append(":</b> ")
.append(info.getIdentity().getSigningPublicKey().getType().toString());
buf.append("<br>\n<b>" + _("Address(es)") + ":</b> ");
String country = _context.commSystem().getCountry(info.getIdentity().getHash());
if(country != null) {
buf.append("<img height=\"11\" width=\"16\" alt=\"").append(country.toUpperCase(Locale.US)).append('\"');

View File

@ -114,6 +114,8 @@ public class KeysAndCert extends DataStructureImpl {
_publicKey.writeBytes(out);
if (_padding != null)
out.write(_padding);
else if (_signingKey.length() < SigningPublicKey.KEYSIZE_BYTES)
throw new DataFormatException("No padding set");
_signingKey.writeTruncatedBytes(out);
_certificate.writeBytes(out);
}

View File

@ -36,6 +36,7 @@ import net.i2p.data.DataHelper;
import net.i2p.data.Hash;
import net.i2p.data.KeysAndCert;
import net.i2p.data.Signature;
import net.i2p.data.SimpleDataStructure;
import net.i2p.util.Clock;
import net.i2p.util.Log;
import net.i2p.util.OrderedProperties;
@ -525,17 +526,20 @@ public class RouterInfo extends DatabaseEntry {
public void readBytes(InputStream in, boolean verifySig) throws DataFormatException, IOException {
if (_signature != null)
throw new IllegalStateException();
_identity = new RouterIdentity();
_identity.readBytes(in);
// can't set the digest until we know the sig type
InputStream din;
MessageDigest digest;
if (verifySig) {
digest = SHA1.getInstance();
digest = _identity.getSigningPublicKey().getType().getDigestInstance();
// TODO any better way?
digest.update(_identity.toByteArray());
din = new DigestInputStream(in, digest);
} else {
digest = null;
din = in;
}
_identity = new RouterIdentity();
_identity.readBytes(din);
// avoid thrashing objects
//Date when = DataHelper.readDate(in);
//if (when == null)
@ -565,7 +569,8 @@ public class RouterInfo extends DatabaseEntry {
_signature.readBytes(in);
if (verifySig) {
SHA1Hash hash = new SHA1Hash(digest.digest());
SimpleDataStructure hash = _identity.getSigningPublicKey().getType().getHashInstance();
hash.setData(digest.digest());
_isValid = DSAEngine.getInstance().verifySignature(_signature, hash, _identity.getSigningPublicKey());
_validated = true;
if (!_isValid) {

View File

@ -18,6 +18,7 @@ import java.io.OutputStream;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import net.i2p.crypto.SigType;
import net.i2p.data.DataFormatException;
import net.i2p.data.DataStructure;
import net.i2p.data.Destination;
@ -26,6 +27,7 @@ import net.i2p.data.PrivateKey;
import net.i2p.data.PublicKey;
import net.i2p.data.SigningPrivateKey;
import net.i2p.data.SigningPublicKey;
import net.i2p.router.startup.CreateRouterInfoJob;
import net.i2p.util.Log;
import net.i2p.util.SecureDirectory;
import net.i2p.util.SecureFileOutputStream;
@ -151,8 +153,9 @@ public class KeyManager {
private void syncKeys(File keyDir) {
syncPrivateKey(keyDir);
syncPublicKey(keyDir);
syncSigningKey(keyDir);
syncVerificationKey(keyDir);
SigType type = CreateRouterInfoJob.getSigTypeConfig(getContext());
syncSigningKey(keyDir, type);
syncVerificationKey(keyDir, type);
}
private void syncPrivateKey(File keyDir) {
@ -181,27 +184,33 @@ public class KeyManager {
_publicKey = (PublicKey) readin;
}
private void syncSigningKey(File keyDir) {
/**
* @param type the SigType to expect on read-in, ignored on write
*/
private void syncSigningKey(File keyDir, SigType type) {
DataStructure ds;
File keyFile = new File(keyDir, KEYFILE_PRIVATE_SIGNING);
boolean exists = (_signingPrivateKey != null);
if (exists)
ds = _signingPrivateKey;
else
ds = new SigningPrivateKey();
ds = new SigningPrivateKey(type);
DataStructure readin = syncKey(keyFile, ds, exists);
if (readin != null && !exists)
_signingPrivateKey = (SigningPrivateKey) readin;
}
private void syncVerificationKey(File keyDir) {
/**
* @param type the SigType to expect on read-in, ignored on write
*/
private void syncVerificationKey(File keyDir, SigType type) {
DataStructure ds;
File keyFile = new File(keyDir, KEYFILE_PUBLIC_SIGNING);
boolean exists = (_signingPublicKey != null);
if (exists)
ds = _signingPublicKey;
else
ds = new SigningPublicKey();
ds = new SigningPublicKey(type);
DataStructure readin = syncKey(keyFile, ds, exists);
if (readin != null && !exists)
_signingPublicKey = (SigningPublicKey) readin;

View File

@ -98,10 +98,6 @@ public class Router implements RouterClock.ClockShiftListener {
/** this does not put an 'H' in your routerInfo **/
public final static String PROP_HIDDEN_HIDDEN = "router.isHidden";
public final static String PROP_DYNAMIC_KEYS = "router.dynamicKeys";
public final static String PROP_INFO_FILENAME = "router.info.location";
public final static String PROP_INFO_FILENAME_DEFAULT = "router.info";
public final static String PROP_KEYS_FILENAME = "router.keys.location";
public final static String PROP_KEYS_FILENAME_DEFAULT = "router.keys";
public final static String PROP_SHUTDOWN_IN_PROGRESS = "__shutdownInProgress";
public final static String DNS_CACHE_TIME = "" + (5*60);
private static final String EVENTLOG = "eventlog.txt";
@ -672,20 +668,6 @@ public class Router implements RouterClock.ClockShiftListener {
return _context.commSystem().isInBadCountry();
}
/**
* Only called at startup via LoadRouterInfoJob and RebuildRouterInfoJob.
* Not called by periodic RepublishLocalRouterInfoJob.
* We don't want to change the cert on the fly as it changes the router hash.
* RouterInfo.isHidden() checks the capability, but RouterIdentity.isHidden() checks the cert.
* There's no reason to ever add a hidden cert?
* @return the certificate for a new RouterInfo - probably a null cert.
*/
public Certificate createCertificate() {
if (_context.getBooleanProperty(PROP_HIDDEN))
return new Certificate(Certificate.CERTIFICATE_TYPE_HIDDEN, null);
return Certificate.NULL_CERT;
}
/**
* @since 0.9.3
*/

View File

@ -524,6 +524,11 @@ class PersistentDataStore extends TransientDataStore {
if (_log.shouldLog(Log.INFO))
_log.info("Unable to read the router reference in " + _routerFile.getName(), ioe);
corrupt = true;
} catch (Exception e) {
// key certificate problems, etc., don't let one bad RI kill the whole thing
if (_log.shouldLog(Log.INFO))
_log.info("Unable to read the router reference in " + _routerFile.getName(), e);
corrupt = true;
} finally {
if (fis != null) try { fis.close(); } catch (IOException ioe) {}
}

View File

@ -16,7 +16,7 @@ import net.i2p.router.tasks.ReadConfigJob;
import net.i2p.util.Log;
/** This actually boots almost everything */
public class BootCommSystemJob extends JobImpl {
class BootCommSystemJob extends JobImpl {
private Log _log;
public static final String PROP_USE_TRUSTED_LINKS = "router.trustedLinks";

View File

@ -12,7 +12,7 @@ import net.i2p.router.JobImpl;
import net.i2p.router.RouterContext;
/** start up the network database */
public class BootNetworkDbJob extends JobImpl {
class BootNetworkDbJob extends JobImpl {
public BootNetworkDbJob(RouterContext ctx) {
super(ctx);

View File

@ -12,7 +12,7 @@ import net.i2p.router.JobImpl;
import net.i2p.router.RouterContext;
/** start up the peer manager */
public class BootPeerManagerJob extends JobImpl {
class BootPeerManagerJob extends JobImpl {
public BootPeerManagerJob(RouterContext ctx) {
super(ctx);

View File

@ -15,7 +15,7 @@ import net.i2p.router.RouterContext;
/**
* For future restricted routes. Does nothing now.
*/
public class BuildTrustedLinksJob extends JobImpl {
class BuildTrustedLinksJob extends JobImpl {
private final Job _next;
public BuildTrustedLinksJob(RouterContext context, Job next) {

View File

@ -12,16 +12,21 @@ import java.io.BufferedOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.security.GeneralSecurityException;
import java.util.Properties;
import net.i2p.crypto.SigType;
import net.i2p.data.Certificate;
import net.i2p.data.DataFormatException;
import net.i2p.data.DataHelper;
import net.i2p.data.KeyCertificate;
import net.i2p.data.PrivateKey;
import net.i2p.data.PublicKey;
import net.i2p.data.router.RouterIdentity;
import net.i2p.data.router.RouterInfo;
import net.i2p.data.SigningPrivateKey;
import net.i2p.data.SigningPublicKey;
import net.i2p.data.SimpleDataStructure;
import net.i2p.router.Job;
import net.i2p.router.JobImpl;
import net.i2p.router.Router;
@ -40,7 +45,16 @@ public class CreateRouterInfoJob extends JobImpl {
private final Log _log;
private final Job _next;
public CreateRouterInfoJob(RouterContext ctx, Job next) {
public static final String INFO_FILENAME = "router.info";
static final String KEYS_FILENAME = "router.keys";
static final String KEYS2_FILENAME = "router.keys2";
private static final String PROP_ROUTER_SIGTYPE = "router.sigType";
/** TODO when changing, check isAvailable() and fallback to DSA_SHA1 */
private static final SigType DEFAULT_SIGTYPE = SigType.DSA_SHA1;
static final byte[] KEYS2_MAGIC = DataHelper.getASCII("I2Pkeys2");
static final int KEYS2_UNUSED_BYTES = 28;
CreateRouterInfoJob(RouterContext ctx, Job next) {
super(ctx);
_next = next;
_log = ctx.logManager().getLog(CreateRouterInfoJob.class);
@ -59,9 +73,24 @@ public class CreateRouterInfoJob extends JobImpl {
/**
* Writes 6 files: router.info (standard RI format),
* router,keys, and 4 individual key files under keyBackup/
* router.keys2, and 4 individual key files under keyBackup/
*
* router.keys file format: Note that this is NOT the
* router.keys2 file format: Note that this is NOT the
* same "eepPriv.dat" format used by the client code.
*<pre>
* - Magic "I2Pkeys2"
* - 2 byte crypto type, always 0000 for now
* - 2 byte sig type, see SigType
* - 28 bytes unused
* - Private key (256 bytes)
* - Signing Private key (20 bytes or see SigType)
* - Public key (256 bytes)
* - Random padding for Signing Public Key if less than 128 bytes
* - Signing Public key (128 bytes or see SigTpe)
* Total 660 bytes
*</pre>
*
* Old router.keys file format: Note that this is NOT the
* same "eepPriv.dat" format used by the client code.
*<pre>
* - Private key (256 bytes)
@ -74,6 +103,7 @@ public class CreateRouterInfoJob extends JobImpl {
* Caller must hold Router.routerInfoFileLock.
*/
RouterInfo createRouterInfo() {
SigType type = getSigTypeConfig(getContext());
RouterInfo info = new RouterInfo();
OutputStream fos1 = null;
OutputStream fos2 = null;
@ -86,21 +116,26 @@ public class CreateRouterInfoJob extends JobImpl {
// not necessary, in constructor
//info.setPeers(new HashSet());
info.setPublished(getCurrentPublishDate(getContext()));
RouterIdentity ident = new RouterIdentity();
Certificate cert = getContext().router().createCertificate();
ident.setCertificate(cert);
PublicKey pubkey = null;
PrivateKey privkey = null;
SigningPublicKey signingPubKey = null;
SigningPrivateKey signingPrivKey = null;
Object keypair[] = getContext().keyGenerator().generatePKIKeypair();
pubkey = (PublicKey)keypair[0];
privkey = (PrivateKey)keypair[1];
Object signingKeypair[] = getContext().keyGenerator().generateSigningKeypair();
signingPubKey = (SigningPublicKey)signingKeypair[0];
signingPrivKey = (SigningPrivateKey)signingKeypair[1];
PublicKey pubkey = (PublicKey)keypair[0];
PrivateKey privkey = (PrivateKey)keypair[1];
SimpleDataStructure signingKeypair[] = getContext().keyGenerator().generateSigningKeys(type);
SigningPublicKey signingPubKey = (SigningPublicKey)signingKeypair[0];
SigningPrivateKey signingPrivKey = (SigningPrivateKey)signingKeypair[1];
RouterIdentity ident = new RouterIdentity();
Certificate cert = createCertificate(getContext(), signingPubKey);
ident.setCertificate(cert);
ident.setPublicKey(pubkey);
ident.setSigningPublicKey(signingPubKey);
byte[] padding;
int padLen = SigningPublicKey.KEYSIZE_BYTES - signingPubKey.length();
if (padLen > 0) {
padding = new byte[padLen];
getContext().random().nextBytes(padding);
ident.setPadding(padding);
} else {
padding = null;
}
info.setIdentity(ident);
info.sign(signingPrivKey);
@ -108,23 +143,35 @@ public class CreateRouterInfoJob extends JobImpl {
if (!info.isValid())
throw new DataFormatException("RouterInfo we just built is invalid: " + info);
String infoFilename = getContext().getProperty(Router.PROP_INFO_FILENAME, Router.PROP_INFO_FILENAME_DEFAULT);
File ifile = new File(getContext().getRouterDir(), infoFilename);
// remove router.keys
(new File(getContext().getRouterDir(), KEYS_FILENAME)).delete();
// write router.info
File ifile = new File(getContext().getRouterDir(), INFO_FILENAME);
fos1 = new BufferedOutputStream(new SecureFileOutputStream(ifile));
info.writeBytes(fos1);
String keyFilename = getContext().getProperty(Router.PROP_KEYS_FILENAME, Router.PROP_KEYS_FILENAME_DEFAULT);
File kfile = new File(getContext().getRouterDir(), keyFilename);
// write router.keys2
File kfile = new File(getContext().getRouterDir(), KEYS2_FILENAME);
fos2 = new BufferedOutputStream(new SecureFileOutputStream(kfile));
fos2.write(KEYS2_MAGIC);
DataHelper.writeLong(fos2, 2, 0);
DataHelper.writeLong(fos2, 2, type.getCode());
fos2.write(new byte[KEYS2_UNUSED_BYTES]);
privkey.writeBytes(fos2);
signingPrivKey.writeBytes(fos2);
pubkey.writeBytes(fos2);
if (padding != null)
fos2.write(padding);
signingPubKey.writeBytes(fos2);
getContext().keyManager().setKeys(pubkey, privkey, signingPubKey, signingPrivKey);
_log.info("Router info created and stored at " + ifile.getAbsolutePath() + " with private keys stored at " + kfile.getAbsolutePath() + " [" + info + "]");
if (_log.shouldLog(Log.INFO))
_log.info("Router info created and stored at " + ifile.getAbsolutePath() + " with private keys stored at " + kfile.getAbsolutePath() + " [" + info + "]");
getContext().router().eventLog().addEvent(EventLog.REKEYED, ident.calculateHash().toBase64());
} catch (GeneralSecurityException gse) {
_log.log(Log.CRIT, "Error building the new router information", gse);
} catch (DataFormatException dfe) {
_log.log(Log.CRIT, "Error building the new router information", dfe);
} catch (IOException ioe) {
@ -136,6 +183,20 @@ public class CreateRouterInfoJob extends JobImpl {
return info;
}
/**
* The configured SigType to expect on read-in
* @since 0.9.16
*/
public static SigType getSigTypeConfig(RouterContext ctx) {
SigType cstype = CreateRouterInfoJob.DEFAULT_SIGTYPE;
String sstype = ctx.getProperty(PROP_ROUTER_SIGTYPE);
if (sstype != null) {
SigType ntype = SigType.parseSigType(sstype);
if (ntype != null && ntype.isAvailable())
cstype = ntype;
}
return cstype;
}
/**
* We probably don't want to expose the exact time at which a router published its info.
@ -146,4 +207,22 @@ public class CreateRouterInfoJob extends JobImpl {
//_log.info("Setting published date to /now/");
return context.clock().now();
}
/**
* Only called at startup via LoadRouterInfoJob and RebuildRouterInfoJob.
* Not called by periodic RepublishLocalRouterInfoJob.
* We don't want to change the cert on the fly as it changes the router hash.
* RouterInfo.isHidden() checks the capability, but RouterIdentity.isHidden() checks the cert.
* There's no reason to ever add a hidden cert?
*
* @return the certificate for a new RouterInfo - probably a null cert.
* @since 0.9.16 moved from Router
*/
static Certificate createCertificate(RouterContext ctx, SigningPublicKey spk) {
if (spk.getType() != SigType.DSA_SHA1)
return new KeyCertificate(spk);
if (ctx.getBooleanProperty(Router.PROP_HIDDEN))
return new Certificate(Certificate.CERTIFICATE_TYPE_HIDDEN, null);
return Certificate.NULL_CERT;
}
}

View File

@ -15,7 +15,9 @@ import java.io.InputStream;
import java.io.IOException;
import java.util.concurrent.atomic.AtomicBoolean;
import net.i2p.crypto.SigType;
import net.i2p.data.DataFormatException;
import net.i2p.data.DataHelper;
import net.i2p.data.PrivateKey;
import net.i2p.data.PublicKey;
import net.i2p.data.router.RouterInfo;
@ -26,7 +28,11 @@ import net.i2p.router.Router;
import net.i2p.router.RouterContext;
import net.i2p.util.Log;
public class LoadRouterInfoJob extends JobImpl {
/**
* Run once or twice at startup by StartupJob,
* and then runs BootCommSystemJob
*/
class LoadRouterInfoJob extends JobImpl {
private final Log _log;
private RouterInfo _us;
private static final AtomicBoolean _keyLengthChecked = new AtomicBoolean();
@ -45,6 +51,7 @@ public class LoadRouterInfoJob extends JobImpl {
if (_us == null) {
RebuildRouterInfoJob r = new RebuildRouterInfoJob(getContext());
r.rebuildRouterInfo(false);
// run a second time
getContext().jobQueue().addJob(this);
return;
} else {
@ -54,15 +61,19 @@ public class LoadRouterInfoJob extends JobImpl {
}
}
/**
* Loads router.info and router.keys2 or router.keys.
*
* See CreateRouterInfoJob for file formats
*/
private void loadRouterInfo() {
String routerInfoFile = getContext().getProperty(Router.PROP_INFO_FILENAME, Router.PROP_INFO_FILENAME_DEFAULT);
RouterInfo info = null;
String keyFilename = getContext().getProperty(Router.PROP_KEYS_FILENAME, Router.PROP_KEYS_FILENAME_DEFAULT);
File rif = new File(getContext().getRouterDir(), routerInfoFile);
File rif = new File(getContext().getRouterDir(), CreateRouterInfoJob.INFO_FILENAME);
boolean infoExists = rif.exists();
File rkf = new File(getContext().getRouterDir(), keyFilename);
File rkf = new File(getContext().getRouterDir(), CreateRouterInfoJob.KEYS_FILENAME);
boolean keysExist = rkf.exists();
File rkf2 = new File(getContext().getRouterDir(), CreateRouterInfoJob.KEYS2_FILENAME);
boolean keys2Exist = rkf2.exists();
InputStream fis1 = null;
InputStream fis2 = null;
@ -73,7 +84,7 @@ public class LoadRouterInfoJob extends JobImpl {
// CRIT ...sport.udp.EstablishmentManager: Error in the establisher java.lang.NullPointerException
// at net.i2p.router.transport.udp.PacketBuilder.buildSessionConfirmedPacket(PacketBuilder.java:574)
// so pretend the RI isn't there if there is no keyfile
if (infoExists && keysExist) {
if (infoExists && (keys2Exist || keysExist)) {
fis1 = new BufferedInputStream(new FileInputStream(rif));
info = new RouterInfo();
info.readBytes(fis1);
@ -85,11 +96,37 @@ public class LoadRouterInfoJob extends JobImpl {
_us = info;
}
if (keysExist) {
fis2 = new BufferedInputStream(new FileInputStream(rkf));
if (keys2Exist || keysExist) {
SigType stype;
if (keys2Exist) {
fis2 = new BufferedInputStream(new FileInputStream(rkf2));
// read keys2 headers
byte[] magic = new byte[CreateRouterInfoJob.KEYS2_MAGIC.length];
DataHelper.read(fis2, magic);
if (!DataHelper.eq(magic, CreateRouterInfoJob.KEYS2_MAGIC))
throw new IOException("Bad magic");
int ctype = (int) DataHelper.readLong(fis2, 2);
if (ctype != 0)
throw new IOException("Unsupported RI crypto type " + ctype);
int sstype = (int) DataHelper.readLong(fis2, 2);
stype = SigType.getByCode(sstype);
if (stype == null || !stype.isAvailable())
throw new IOException("Unsupported RI sig type " + stype);
DataHelper.skip(fis2, CreateRouterInfoJob.KEYS2_UNUSED_BYTES);
} else {
fis2 = new BufferedInputStream(new FileInputStream(rkf));
stype = SigType.DSA_SHA1;
}
// check if the sigtype config changed
SigType cstype = CreateRouterInfoJob.getSigTypeConfig(getContext());
boolean sigTypeChanged = stype != cstype;
PrivateKey privkey = new PrivateKey();
privkey.readBytes(fis2);
if (shouldRebuild(privkey)) {
if (sigTypeChanged || shouldRebuild(privkey)) {
if (sigTypeChanged)
_log.logAlways(Log.WARN, "Rebuilding RouterInfo with new signature type " + cstype);
_us = null;
// windows... close before deleting
if (fis1 != null) {
@ -100,13 +137,19 @@ public class LoadRouterInfoJob extends JobImpl {
fis2 = null;
rif.delete();
rkf.delete();
rkf2.delete();
return;
}
SigningPrivateKey signingPrivKey = new SigningPrivateKey();
SigningPrivateKey signingPrivKey = new SigningPrivateKey(stype);
signingPrivKey.readBytes(fis2);
PublicKey pubkey = new PublicKey();
pubkey.readBytes(fis2);
SigningPublicKey signingPubKey = new SigningPublicKey();
SigningPublicKey signingPubKey = new SigningPublicKey(stype);
int padLen = SigningPublicKey.KEYSIZE_BYTES - signingPubKey.length();
if (padLen > 0) {
// we lose the padding as keymanager doesn't store it, what to do?
DataHelper.skip(fis2, padLen);
}
signingPubKey.readBytes(fis2);
getContext().keyManager().setKeys(pubkey, privkey, signingPubKey, signingPrivKey);
@ -125,6 +168,7 @@ public class LoadRouterInfoJob extends JobImpl {
}
rif.delete();
rkf.delete();
rkf2.delete();
} catch (DataFormatException dfe) {
_log.log(Log.CRIT, "Corrupt router info or keys at " + rif.getAbsolutePath() + " / " + rkf.getAbsolutePath(), dfe);
_us = null;
@ -139,6 +183,7 @@ public class LoadRouterInfoJob extends JobImpl {
}
rif.delete();
rkf.delete();
rkf2.delete();
} finally {
if (fis1 != null) try { fis1.close(); } catch (IOException ioe) {}
if (fis2 != null) try { fis2.close(); } catch (IOException ioe) {}

View File

@ -8,14 +8,18 @@ package net.i2p.router.startup;
*
*/
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.IOException;
import java.util.Properties;
import net.i2p.crypto.SigType;
import net.i2p.data.Certificate;
import net.i2p.data.DataFormatException;
import net.i2p.data.DataHelper;
import net.i2p.data.PrivateKey;
import net.i2p.data.PublicKey;
import net.i2p.data.router.RouterIdentity;
@ -44,7 +48,7 @@ import net.i2p.util.SecureFileOutputStream;
* router.info.rebuild file is deleted
*
*/
public class RebuildRouterInfoJob extends JobImpl {
class RebuildRouterInfoJob extends JobImpl {
private final Log _log;
private final static long REBUILD_DELAY = 45*1000; // every 30 seconds
@ -57,11 +61,11 @@ public class RebuildRouterInfoJob extends JobImpl {
public String getName() { return "Rebuild Router Info"; }
public void runJob() {
throw new UnsupportedOperationException();
/****
_log.debug("Testing to rebuild router info");
String infoFile = getContext().getProperty(Router.PROP_INFO_FILENAME, Router.PROP_INFO_FILENAME_DEFAULT);
File info = new File(getContext().getRouterDir(), infoFile);
String keyFilename = getContext().getProperty(Router.PROP_KEYS_FILENAME, Router.PROP_KEYS_FILENAME_DEFAULT);
File keyFile = new File(getContext().getRouterDir(), keyFilename);
File info = new File(getContext().getRouterDir(), CreateRouterInfoJob.INFO_FILENAME);
File keyFile = new File(getContext().getRouterDir(), CreateRouterInfoJob.KEYS2_FILENAME);
if (!info.exists() || !keyFile.exists()) {
_log.info("Router info file [" + info.getAbsolutePath() + "] or private key file [" + keyFile.getAbsolutePath() + "] deleted, rebuilding");
@ -71,41 +75,72 @@ public class RebuildRouterInfoJob extends JobImpl {
}
getTiming().setStartAfter(getContext().clock().now() + REBUILD_DELAY);
getContext().jobQueue().addJob(this);
****/
}
void rebuildRouterInfo() {
rebuildRouterInfo(true);
}
/**
* @param alreadyRunning unused
*/
void rebuildRouterInfo(boolean alreadyRunning) {
_log.debug("Rebuilding the new router info");
RouterInfo info = null;
String infoFilename = getContext().getProperty(Router.PROP_INFO_FILENAME, Router.PROP_INFO_FILENAME_DEFAULT);
File infoFile = new File(getContext().getRouterDir(), infoFilename);
String keyFilename = getContext().getProperty(Router.PROP_KEYS_FILENAME, Router.PROP_KEYS_FILENAME_DEFAULT);
File keyFile = new File(getContext().getRouterDir(), keyFilename);
File infoFile = new File(getContext().getRouterDir(), CreateRouterInfoJob.INFO_FILENAME);
File keyFile = new File(getContext().getRouterDir(), CreateRouterInfoJob.KEYS_FILENAME);
File keyFile2 = new File(getContext().getRouterDir(), CreateRouterInfoJob.KEYS2_FILENAME);
if (keyFile.exists()) {
if (keyFile2.exists() || keyFile.exists()) {
// ok, no need to rebuild a brand new identity, just update what we can
RouterInfo oldinfo = getContext().router().getRouterInfo();
if (oldinfo == null) {
info = new RouterInfo();
FileInputStream fis = null;
InputStream fis = null;
try {
fis = new FileInputStream(keyFile);
SigType stype;
if (keyFile2.exists()) {
fis = new BufferedInputStream(new FileInputStream(keyFile2));
byte[] magic = new byte[CreateRouterInfoJob.KEYS2_MAGIC.length];
DataHelper.read(fis, magic);
if (!DataHelper.eq(magic, CreateRouterInfoJob.KEYS2_MAGIC))
throw new IOException("Bad magic");
int ctype = (int) DataHelper.readLong(fis, 2);
if (ctype != 0)
throw new IOException("Unsupported RI crypto type " + ctype);
int sstype = (int) DataHelper.readLong(fis, 2);
stype = SigType.getByCode(sstype);
if (stype == null || !stype.isAvailable())
throw new IOException("Unsupported RI sig type " + stype);
DataHelper.skip(fis, CreateRouterInfoJob.KEYS2_UNUSED_BYTES);
} else {
fis = new BufferedInputStream(new FileInputStream(keyFile));
stype = SigType.DSA_SHA1;
}
PrivateKey privkey = new PrivateKey();
privkey.readBytes(fis);
SigningPrivateKey signingPrivKey = new SigningPrivateKey();
SigningPrivateKey signingPrivKey = new SigningPrivateKey(stype);
signingPrivKey.readBytes(fis);
PublicKey pubkey = new PublicKey();
pubkey.readBytes(fis);
SigningPublicKey signingPubKey = new SigningPublicKey();
SigningPublicKey signingPubKey = new SigningPublicKey(stype);
byte[] padding;
int padLen = SigningPublicKey.KEYSIZE_BYTES - signingPubKey.length();
if (padLen > 0) {
padding = new byte[padLen];
DataHelper.read(fis, padding);
} else {
padding = null;
}
signingPubKey.readBytes(fis);
RouterIdentity ident = new RouterIdentity();
Certificate cert = getContext().router().createCertificate();
Certificate cert = CreateRouterInfoJob.createCertificate(getContext(), signingPubKey);
ident.setCertificate(cert);
ident.setPublicKey(pubkey);
ident.setSigningPublicKey(signingPubKey);
if (padding != null)
ident.setPadding(padding);
info.setIdentity(ident);
} catch (Exception e) {
_log.log(Log.CRIT, "Error reading in the key data from " + keyFile.getAbsolutePath(), e);
@ -160,12 +195,14 @@ public class RebuildRouterInfoJob extends JobImpl {
_log.warn("Private key file " + keyFile.getAbsolutePath() + " deleted! Rebuilding a brand new router identity!");
// this proc writes the keys and info to the file as well as builds the latest and greatest info
CreateRouterInfoJob j = new CreateRouterInfoJob(getContext(), null);
info = j.createRouterInfo();
synchronized (getContext().router().routerInfoFileLock) {
info = j.createRouterInfo();
}
}
//MessageHistory.initialize();
getContext().router().setRouterInfo(info);
_log.info("Router info rebuilt and stored at " + infoFilename + " [" + info + "]");
_log.info("Router info rebuilt and stored at " + infoFile + " [" + info + "]");
}
}

View File

@ -12,7 +12,7 @@ import net.i2p.router.JobImpl;
import net.i2p.router.RouterContext;
/** start I2CP interface */
public class StartAcceptingClientsJob extends JobImpl {
class StartAcceptingClientsJob extends JobImpl {
public StartAcceptingClientsJob(RouterContext context) {
super(context);

View File

@ -147,7 +147,7 @@ public class WorkingDir {
// Check for a router.keys file or logs dir, if either exists it's an old install,
// and only migrate the data files if told to do so
// (router.keys could be deleted later by a killkeys())
test = new File(oldDirf, "router.keys");
test = new File(oldDirf, CreateRouterInfoJob.KEYS_FILENAME);
boolean oldInstall = test.exists();
if (!oldInstall) {
test = new File(oldDirf, "logs");

View File

@ -15,8 +15,8 @@ import java.io.IOException;
import net.i2p.data.DataFormatException;
import net.i2p.data.router.RouterInfo;
import net.i2p.router.JobImpl;
import net.i2p.router.Router;
import net.i2p.router.RouterContext;
import net.i2p.router.startup.CreateRouterInfoJob;
import net.i2p.util.Log;
import net.i2p.util.SecureFileOutputStream;
@ -37,8 +37,7 @@ public class PersistRouterInfoJob extends JobImpl {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Persisting updated router info");
String infoFilename = getContext().getProperty(Router.PROP_INFO_FILENAME, Router.PROP_INFO_FILENAME_DEFAULT);
File infoFile = new File(getContext().getRouterDir(), infoFilename);
File infoFile = new File(getContext().getRouterDir(), CreateRouterInfoJob.INFO_FILENAME);
RouterInfo info = getContext().router().getRouterInfo();