diff --git a/router/java/src/net/i2p/router/StatisticsManager.java b/router/java/src/net/i2p/router/StatisticsManager.java index 50b4eb0bde20c4337164bbab2e7c4c9cb72431bc..b1ed027426e0137a2d9cf07ae4b12890631735d5 100644 --- a/router/java/src/net/i2p/router/StatisticsManager.java +++ b/router/java/src/net/i2p/router/StatisticsManager.java @@ -188,24 +188,29 @@ public class StatisticsManager { if (family != null) { stats.setProperty(FamilyKeyCrypto.OPT_NAME, family); String sig = null; + String key = null; RouterInfo oldRI = _context.router().getRouterInfo(); if (oldRI != null) { // don't do it if family changed if (family.equals(oldRI.getOption(FamilyKeyCrypto.OPT_NAME))) { - // copy over the signature + // copy over the pubkey and signature + key = oldRI.getOption(FamilyKeyCrypto.OPT_KEY); + if (key != null) + stats.setProperty(FamilyKeyCrypto.OPT_KEY, key); sig = oldRI.getOption(FamilyKeyCrypto.OPT_SIG); if (sig != null) stats.setProperty(FamilyKeyCrypto.OPT_SIG, sig); } } - if (sig == null) { + if (sig == null || key == null) { FamilyKeyCrypto fkc = _context.router().getFamilyKeyCrypto(); if (fkc != null) { try { - sig = fkc.sign(family, h); - stats.setProperty(FamilyKeyCrypto.OPT_SIG, sig); + stats.putAll(fkc.sign(family, h)); } catch (GeneralSecurityException gse) { _log.error("Failed to sign router family", gse); + stats.remove(FamilyKeyCrypto.OPT_KEY); + stats.remove(FamilyKeyCrypto.OPT_SIG); } } } diff --git a/router/java/src/net/i2p/router/crypto/FamilyKeyCrypto.java b/router/java/src/net/i2p/router/crypto/FamilyKeyCrypto.java index 8af392f1c698523c06e311e4761408d227e12ca7..2bb6038c546789247e01d8fad04a582772fbbef0 100644 --- a/router/java/src/net/i2p/router/crypto/FamilyKeyCrypto.java +++ b/router/java/src/net/i2p/router/crypto/FamilyKeyCrypto.java @@ -54,7 +54,7 @@ public class FamilyKeyCrypto { private static final String KEYSTORE_PREFIX = "family-"; private static final String KEYSTORE_SUFFIX = ".ks"; private static final int DEFAULT_KEY_VALID_DAYS = 3652; // 10 years - // Note that we can't use RSA here, as the b64 sig would exceed the 256 char limit for a Mapping + // Note that we can't use RSA here, as the b64 sig would exceed the 255 char limit for a Mapping // Note that we can't use EdDSA here, as keystore doesn't know how, and encoding/decoding is unimplemented private static final String DEFAULT_KEY_ALGORITHM = SigType.ECDSA_SHA256_P256.isAvailable() ? "EC" : "DSA"; private static final int DEFAULT_KEY_SIZE = SigType.ECDSA_SHA256_P256.isAvailable() ? 256 : 1024; @@ -62,6 +62,7 @@ public class FamilyKeyCrypto { private static final String CERT_DIR = "certificates/family"; public static final String OPT_NAME = "family"; public static final String OPT_SIG = "family.sig"; + public static final String OPT_KEY = "family.key"; /** @@ -109,16 +110,20 @@ public class FamilyKeyCrypto { * * @param family non-null, must match that we were initialized with or will throw GSE * @param h non-null - * @return non-null base 64 signature string to be added to the RI + * @return non-null options to be added to the RI * @throws GeneralSecurityException on null hash, null or changed family, or signing error */ - public String sign(String family, Hash h) throws GeneralSecurityException { - if (_privkey == null) - throw new GeneralSecurityException("family name set, must restart router"); + public Map<String, String> sign(String family, Hash h) throws GeneralSecurityException { + if (_privkey == null) { + _log.logAlways(Log.WARN, "family name now set, must restart router to generate or load keys"); + throw new GeneralSecurityException("family name now set, must restart router to generate or load keys"); + } if (h == null) throw new GeneralSecurityException("null router hash"); - if (!_fname.equals(family)) - throw new GeneralSecurityException("family name changed, must restart router"); + if (!_fname.equals(family)) { + _log.logAlways(Log.WARN, "family name changed, must restart router to generate or load new keys"); + throw new GeneralSecurityException("family name changed, must restart router to generate or load new keys"); + } byte[] nb = DataHelper.getUTF8(_fname); int len = nb.length + Hash.HASH_LENGTH; byte[] b = new byte[len]; @@ -127,7 +132,11 @@ public class FamilyKeyCrypto { Signature sig = _context.dsa().sign(b, _privkey); if (sig == null) throw new GeneralSecurityException("sig failed"); - return sig.toBase64(); + Map<String, String> rv = new HashMap<String, String>(3); + rv.put(OPT_NAME, family); + rv.put(OPT_KEY, _pubkey.getType().getCode() + ";" + _pubkey.toBase64()); + rv.put(OPT_SIG, sig.toBase64()); + return rv; } /** @@ -162,12 +171,46 @@ public class FamilyKeyCrypto { return false; spk = loadCert(name); if (spk == null) { - _negativeCache.add(h); - if (_log.shouldInfo()) - _log.info("No cert for " + h + ' ' + name); - return false; + // look for a b64 key in the RI + String skey = ri.getOption(OPT_KEY); + if (skey != null) { + int semi = skey.indexOf(";"); + if (semi > 0) { + try { + int code = Integer.parseInt(skey.substring(0, semi)); + SigType type = SigType.getByCode(code); + if (type != null) { + byte[] bkey = Base64.decode(skey.substring(semi + 1)); + if (bkey != null) { + spk = new SigningPublicKey(type, bkey); + } + } + } catch (NumberFormatException e) { + if (_log.shouldInfo()) + _log.info("Bad b64 family key: " + ri, e); + } catch (IllegalArgumentException e) { + if (_log.shouldInfo()) + _log.info("Bad b64 family key: " + ri, e); + } catch (ArrayIndexOutOfBoundsException e) { + if (_log.shouldInfo()) + _log.info("Bad b64 family key: " + ri, e); + } + } + } + if (spk == null) { + _negativeCache.add(h); + if (_log.shouldInfo()) + _log.info("No cert or valid key for " + h + ' ' + name); + return false; + } } } + if (!spk.getType().isAvailable()) { + _negativeCache.add(h); + if (_log.shouldInfo()) + _log.info("Unsupported crypto for sig for " + h); + return false; + } byte[] bsig = Base64.decode(ssig); if (bsig == null) { _negativeCache.add(h);