/** * Not thread-safe. Blocking. Only used for external sockets. ClientWriterRunner thread is the * only caller. Others must use doSend(). */ void writeMessage(I2CPMessage msg) { // long before = _context.clock().now(); try { // We don't need synchronization here, ClientWriterRunner is the only writer. // synchronized (_out) { msg.writeMessage(_out); _out.flush(); // } // if (_log.shouldLog(Log.DEBUG)) // _log.debug("after writeMessage("+ msg.getClass().getName() + "): " // + (_context.clock().now()-before) + "ms"); } catch (I2CPMessageException ime) { _log.error("Error sending I2CP message to client", ime); stopRunning(); } catch (EOFException eofe) { // only warn if client went away if (_log.shouldLog(Log.WARN)) _log.warn("Error sending I2CP message - client went away", eofe); stopRunning(); } catch (IOException ioe) { if (_log.shouldLog(Log.ERROR)) _log.error("IO Error sending I2CP message to client", ioe); stopRunning(); } catch (Throwable t) { _log.log(Log.CRIT, "Unhandled exception sending I2CP message to client", t); stopRunning(); // } finally { // long after = _context.clock().now(); // long lag = after - before; // if (lag > 300) { // if (_log.shouldLog(Log.WARN)) // _log.warn("synchronization on the i2cp message send took too long (" + lag // + "ms): " + msg); // } } }
private static void log(I2PAppContext ctx, int level, String msg, Throwable t) { if (level >= Log.WARN && !ctx.isRouterContext()) { System.out.println(msg); if (t != null) t.printStackTrace(); } Log l = ctx.logManager().getLog(KeyStoreUtil.class); l.log(level, msg, t); }
/** * @param hash either a Hash or a SHA1Hash * @since 0.8.3 */ private boolean verifySig( Signature signature, SimpleDataStructure hash, SigningPublicKey verifyingKey) { long start = _context.clock().now(); try { byte[] sigbytes = signature.getData(); byte rbytes[] = new byte[20]; byte sbytes[] = new byte[20]; // System.arraycopy(sigbytes, 0, rbytes, 0, 20); // System.arraycopy(sigbytes, 20, sbytes, 0, 20); for (int x = 0; x < 40; x++) { if (x < 20) { rbytes[x] = sigbytes[x]; } else { sbytes[x - 20] = sigbytes[x]; } } BigInteger s = new NativeBigInteger(1, sbytes); BigInteger r = new NativeBigInteger(1, rbytes); BigInteger y = new NativeBigInteger(1, verifyingKey.getData()); BigInteger w = null; try { w = s.modInverse(CryptoConstants.dsaq); } catch (ArithmeticException ae) { _log.warn("modInverse() error", ae); return false; } byte data[] = hash.getData(); NativeBigInteger bi = new NativeBigInteger(1, data); BigInteger u1 = bi.multiply(w).mod(CryptoConstants.dsaq); BigInteger u2 = r.multiply(w).mod(CryptoConstants.dsaq); BigInteger modval = CryptoConstants.dsag.modPow(u1, CryptoConstants.dsap); BigInteger modmulval = modval.multiply(y.modPow(u2, CryptoConstants.dsap)); BigInteger v = (modmulval).mod(CryptoConstants.dsap).mod(CryptoConstants.dsaq); boolean ok = v.compareTo(r) == 0; long diff = _context.clock().now() - start; if (diff > 1000) { if (_log.shouldLog(Log.WARN)) _log.warn("Took too long to verify the signature (" + diff + "ms)"); } return ok; } catch (Exception e) { _log.log(Log.CRIT, "Error verifying the signature", e); return false; } }
/** * @param logLevel e.g. Log.WARN * @since 0.8.2 */ void disconnectClient(String reason, int logLevel) { if (_log.shouldLog(logLevel)) _log.log(logLevel, "Disconnecting the client - " + reason + " config: " + _config); DisconnectMessage msg = new DisconnectMessage(); msg.setReason(reason); try { doSend(msg); } catch (I2CPMessageException ime) { if (_log.shouldLog(Log.WARN)) _log.warn("Error writing out the disconnect message", ime); } // give it a little time to get sent out... // even better would be to have stopRunning() flush it? try { Thread.sleep(50); } catch (InterruptedException ie) { } stopRunning(); }
/** * Uses the given {@link net.i2p.data.SigningPrivateKey} to sign the given input file along with * its version string using DSA. The output will be a signed update file where the first 40 bytes * are the resulting DSA signature, the next 16 bytes are the input file's version string encoded * in UTF-8 (padded with trailing <code>0h</code> characters if necessary), and the remaining * bytes are the raw bytes of the input file. * * @param inputFile The file to be signed. * @param signedFile The signed update file to write. * @param signingPrivateKey An instance of <code>SigningPrivateKey</code> to sign <code>inputFile * </code> with. * @param version The version string of the input file. If this is longer than 16 characters it * will be truncated. * @return An instance of {@link net.i2p.data.Signature}, or <code>null</code> if there was an * error. */ public Signature sign( String inputFile, String signedFile, SigningPrivateKey signingPrivateKey, String version) { byte[] versionHeader = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; byte[] versionRawBytes = null; if (version.length() > VERSION_BYTES) version = version.substring(0, VERSION_BYTES); try { versionRawBytes = version.getBytes("UTF-8"); } catch (UnsupportedEncodingException e) { throw new RuntimeException("wtf, your JVM doesnt support utf-8? " + e.getMessage()); } System.arraycopy(versionRawBytes, 0, versionHeader, 0, versionRawBytes.length); FileInputStream fileInputStream = null; Signature signature = null; SequenceInputStream bytesToSignInputStream = null; ByteArrayInputStream versionHeaderInputStream = null; try { fileInputStream = new FileInputStream(inputFile); versionHeaderInputStream = new ByteArrayInputStream(versionHeader); bytesToSignInputStream = new SequenceInputStream(versionHeaderInputStream, fileInputStream); signature = _context.dsa().sign(bytesToSignInputStream, signingPrivateKey); } catch (Exception e) { if (_log.shouldLog(Log.ERROR)) _log.error("Error signing", e); return null; } finally { if (bytesToSignInputStream != null) try { bytesToSignInputStream.close(); } catch (IOException ioe) { } fileInputStream = null; } FileOutputStream fileOutputStream = null; try { fileOutputStream = new FileOutputStream(signedFile); fileOutputStream.write(signature.getData()); fileOutputStream.write(versionHeader); fileInputStream = new FileInputStream(inputFile); byte[] buffer = new byte[1024]; int bytesRead = 0; while ((bytesRead = fileInputStream.read(buffer)) != -1) fileOutputStream.write(buffer, 0, bytesRead); fileOutputStream.close(); } catch (IOException ioe) { if (_log.shouldLog(Log.WARN)) _log.log(Log.WARN, "Error writing signed file " + signedFile, ioe); return null; } finally { if (fileInputStream != null) try { fileInputStream.close(); } catch (IOException ioe) { } if (fileOutputStream != null) try { fileOutputStream.close(); } catch (IOException ioe) { } } return signature; }
/** * Writes 6 files: router.info (standard RI format), router.keys.dat, and 4 individual key files * under keyBackup/ * * <p>router.keys.dat file format: This is the same "eepPriv.dat" format used by the client code, * as documented in PrivateKeyFile. * * <p>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) * - Signing Private key (20 bytes) * - Public key (256 bytes) * - Signing Public key (128 bytes) * Total 660 bytes * </pre> * * Caller must hold Router.routerInfoFileLock. */ RouterInfo createRouterInfo() { SigType type = getSigTypeConfig(getContext()); RouterInfo info = new RouterInfo(); OutputStream fos1 = null; try { info.setAddresses(getContext().commSystem().createAddresses()); // 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 = 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); Properties stats = getContext().statPublisher().publishStatistics(ident.getHash()); info.setOptions(stats); info.sign(signingPrivKey); if (!info.isValid()) throw new DataFormatException("RouterInfo we just built is invalid: " + info); // 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); // write router.keys.dat File kfile = new File(getContext().getRouterDir(), KEYS2_FILENAME); PrivateKeyFile pkf = new PrivateKeyFile(kfile, pubkey, signingPubKey, cert, privkey, signingPrivKey, padding); pkf.write(); getContext().keyManager().setKeys(pubkey, privkey, signingPubKey, signingPrivKey); 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) { _log.log(Log.CRIT, "Error writing out the new router information", ioe); } finally { if (fos1 != null) try { fos1.close(); } catch (IOException ioe) { } } return info; }
/** * calculate and update the job timings if it was lagged too much or took too long to run, spit * out a warning (and if its really excessive, kill the router) */ void updateStats(Job job, long doStart, long origStartAfter, long duration) { if (_context.router() == null) return; String key = job.getName(); long lag = doStart - origStartAfter; // how long were we ready and waiting? MessageHistory hist = _context.messageHistory(); long uptime = _context.router().getUptime(); if (lag < 0) lag = 0; if (duration < 0) duration = 0; JobStats stats = _jobStats.get(key); if (stats == null) { stats = new JobStats(key); _jobStats.put(key, stats); // yes, if two runners finish the same job at the same time, this could // create an extra object. but, who cares, its pushed out of the map // immediately anyway. } stats.jobRan(duration, lag); String dieMsg = null; if (lag > _lagWarning) { dieMsg = "Lag too long for job " + job.getName() + " [" + lag + "ms and a run time of " + duration + "ms]"; } else if (duration > _runWarning) { dieMsg = "Job run too long for job " + job.getName() + " [" + lag + "ms lag and run time of " + duration + "ms]"; } if (dieMsg != null) { if (_log.shouldLog(Log.WARN)) _log.warn(dieMsg); if (hist != null) hist.messageProcessingError(-1, JobQueue.class.getName(), dieMsg); } if ((lag > _lagFatal) && (uptime > _warmupTime)) { // this is fscking bad - the network at this size shouldn't have this much real contention // so we're going to DIE DIE DIE if (_log.shouldLog(Log.WARN)) _log.log( Log.WARN, "The router is either incredibly overloaded or (more likely) there's an error.", new Exception("ttttooooo mmmuuuccccchhhh llllaaagggg")); // try { Thread.sleep(5000); } catch (InterruptedException ie) {} // Router.getInstance().shutdown(); return; } if ((uptime > _warmupTime) && (duration > _runFatal)) { // slow CPUs can get hosed with ElGamal, but 10s is too much. if (_log.shouldLog(Log.WARN)) _log.log( Log.WARN, "The router is incredibly overloaded - either you have a 386, or (more likely) there's an error. ", new Exception("ttttooooo sssllloooowww")); // try { Thread.sleep(5000); } catch (InterruptedException ie) {} // Router.getInstance().shutdown(); return; } }