diff --git a/apps/routerconsole/java/src/net/i2p/router/web/NetDbRenderer.java b/apps/routerconsole/java/src/net/i2p/router/web/NetDbRenderer.java index d525cdccd6cc57dc980ba7bc157fca58561aebce..b669f1447c17cbfef7f0f629b552dc621870f865 100644 --- a/apps/routerconsole/java/src/net/i2p/router/web/NetDbRenderer.java +++ b/apps/routerconsole/java/src/net/i2p/router/web/NetDbRenderer.java @@ -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('\"'); diff --git a/core/java/src/net/i2p/data/KeysAndCert.java b/core/java/src/net/i2p/data/KeysAndCert.java index b0a8a845b7932ed3d7f6f2551fb7031eec4016eb..e5b56bc868bac7cce38bbbb239a9deee2cd6a576 100644 --- a/core/java/src/net/i2p/data/KeysAndCert.java +++ b/core/java/src/net/i2p/data/KeysAndCert.java @@ -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); } diff --git a/router/java/src/net/i2p/data/router/RouterInfo.java b/router/java/src/net/i2p/data/router/RouterInfo.java index e314e150fd6cef72098f4aabc53b218ce1a06d0a..71a1157fab27a95db9e8b2884b5a8c66e82c7b55 100644 --- a/router/java/src/net/i2p/data/router/RouterInfo.java +++ b/router/java/src/net/i2p/data/router/RouterInfo.java @@ -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) { diff --git a/router/java/src/net/i2p/router/KeyManager.java b/router/java/src/net/i2p/router/KeyManager.java index ce4992f8bd54deef53e963b2b4a4a6173e71dcd6..246f1accf44bea1d1f318849d43877e470925948 100644 --- a/router/java/src/net/i2p/router/KeyManager.java +++ b/router/java/src/net/i2p/router/KeyManager.java @@ -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; diff --git a/router/java/src/net/i2p/router/Router.java b/router/java/src/net/i2p/router/Router.java index 8ec1e4367ecf45c5d8f87581c35fd93b92542c79..1726d2ded32e356b5ea5fd3fd19c68e0ae5efff2 100644 --- a/router/java/src/net/i2p/router/Router.java +++ b/router/java/src/net/i2p/router/Router.java @@ -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"; @@ -671,20 +667,6 @@ public class Router implements RouterClock.ClockShiftListener { return Boolean.parseBoolean(h); 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 diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/PersistentDataStore.java b/router/java/src/net/i2p/router/networkdb/kademlia/PersistentDataStore.java index f94940f73480d2adf53d13638ad25ba3dac2ee4e..2a7713cffd7618a3397585927cc31332cb558f43 100644 --- a/router/java/src/net/i2p/router/networkdb/kademlia/PersistentDataStore.java +++ b/router/java/src/net/i2p/router/networkdb/kademlia/PersistentDataStore.java @@ -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) {} } diff --git a/router/java/src/net/i2p/router/startup/BootCommSystemJob.java b/router/java/src/net/i2p/router/startup/BootCommSystemJob.java index 3af4164b5eb8e0f680fca7525dfd7da05e721e4e..305869badae5c22bd6754d0709d67aa015e02b1e 100644 --- a/router/java/src/net/i2p/router/startup/BootCommSystemJob.java +++ b/router/java/src/net/i2p/router/startup/BootCommSystemJob.java @@ -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"; diff --git a/router/java/src/net/i2p/router/startup/BootNetworkDbJob.java b/router/java/src/net/i2p/router/startup/BootNetworkDbJob.java index bf8d36a7729b4c8c16d2319325207115da1fa25c..e512f9ea381819409c6ead9b27d038f9dcf19d6a 100644 --- a/router/java/src/net/i2p/router/startup/BootNetworkDbJob.java +++ b/router/java/src/net/i2p/router/startup/BootNetworkDbJob.java @@ -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); diff --git a/router/java/src/net/i2p/router/startup/BootPeerManagerJob.java b/router/java/src/net/i2p/router/startup/BootPeerManagerJob.java index 7ac5254f0e936484681751c3503ab67ebc38865f..33f4010236e1f76b76e85a10f44b6f30c3bf7686 100644 --- a/router/java/src/net/i2p/router/startup/BootPeerManagerJob.java +++ b/router/java/src/net/i2p/router/startup/BootPeerManagerJob.java @@ -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); diff --git a/router/java/src/net/i2p/router/startup/BuildTrustedLinksJob.java b/router/java/src/net/i2p/router/startup/BuildTrustedLinksJob.java index 3b88a2d9cd179e485ae098a2bfc3145e3b625693..566204068b865e436b978109d5e480dfca2687f4 100644 --- a/router/java/src/net/i2p/router/startup/BuildTrustedLinksJob.java +++ b/router/java/src/net/i2p/router/startup/BuildTrustedLinksJob.java @@ -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) { diff --git a/router/java/src/net/i2p/router/startup/CreateRouterInfoJob.java b/router/java/src/net/i2p/router/startup/CreateRouterInfoJob.java index 12bb04d3e6c1ad14fa0bd3fe86e02cb50bbc58e8..5f4d61759040e5643128eae91710b880c7deef91 100644 --- a/router/java/src/net/i2p/router/startup/CreateRouterInfoJob.java +++ b/router/java/src/net/i2p/router/startup/CreateRouterInfoJob.java @@ -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.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> * - * router.keys file format: Note that this is NOT the + * 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())); + Object keypair[] = getContext().keyGenerator().generatePKIKeypair(); + 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 = getContext().router().createCertificate(); + Certificate cert = createCertificate(getContext(), signingPubKey); 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]; 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; + } } diff --git a/router/java/src/net/i2p/router/startup/LoadRouterInfoJob.java b/router/java/src/net/i2p/router/startup/LoadRouterInfoJob.java index 016b77bc3387c3a075845fa0438592cd614155af..4b5ffdf4fb70a20c5fb35aa95b60bd9bf02a25c9 100644 --- a/router/java/src/net/i2p/router/startup/LoadRouterInfoJob.java +++ b/router/java/src/net/i2p/router/startup/LoadRouterInfoJob.java @@ -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) {} diff --git a/router/java/src/net/i2p/router/startup/RebuildRouterInfoJob.java b/router/java/src/net/i2p/router/startup/RebuildRouterInfoJob.java index f531971495178bcdbd92e905a93a66d6739b8ff4..59f822bdaadbd28a577f4ade95cdf53798b6b83b 100644 --- a/router/java/src/net/i2p/router/startup/RebuildRouterInfoJob.java +++ b/router/java/src/net/i2p/router/startup/RebuildRouterInfoJob.java @@ -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 + "]"); } } diff --git a/router/java/src/net/i2p/router/startup/StartAcceptingClientsJob.java b/router/java/src/net/i2p/router/startup/StartAcceptingClientsJob.java index 50d840d4322be182f20990c1d1d584e909cb49c7..672bf702aab4eb6b79ed5da0d54dbd21784d6f01 100644 --- a/router/java/src/net/i2p/router/startup/StartAcceptingClientsJob.java +++ b/router/java/src/net/i2p/router/startup/StartAcceptingClientsJob.java @@ -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); diff --git a/router/java/src/net/i2p/router/startup/WorkingDir.java b/router/java/src/net/i2p/router/startup/WorkingDir.java index 3763f2e7a798e1f0993ecccbf8dd1b293805b643..15a3e7fb44cd2173d32f87b5197f553b3c726bda 100644 --- a/router/java/src/net/i2p/router/startup/WorkingDir.java +++ b/router/java/src/net/i2p/router/startup/WorkingDir.java @@ -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"); diff --git a/router/java/src/net/i2p/router/tasks/PersistRouterInfoJob.java b/router/java/src/net/i2p/router/tasks/PersistRouterInfoJob.java index 7a8868985ca6216cae537961b3717014e72e4f7d..6a14a51346d510e4da9935e71744b4061d1af620 100644 --- a/router/java/src/net/i2p/router/tasks/PersistRouterInfoJob.java +++ b/router/java/src/net/i2p/router/tasks/PersistRouterInfoJob.java @@ -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();