forked from I2P_Developers/i2p.i2p
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:
@ -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('\"');
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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) {}
|
||||
}
|
||||
|
@ -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";
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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) {}
|
||||
|
@ -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 + "]");
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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");
|
||||
|
@ -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();
|
||||
|
||||
|
Reference in New Issue
Block a user