package net.i2p.router.message; /* * free (adj.): unencumbered; not under the control of others * Written by jrandom in 2003 and released into the public domain * with no warranty of any kind, either expressed or implied. * It probably won't make your computer catch on fire, or eat * your children, but it might. Use at your own risk. * */ import java.util.Date; import net.i2p.I2PAppContext; import net.i2p.crypto.SessionKeyManager; import net.i2p.data.Certificate; import net.i2p.data.DataFormatException; import net.i2p.data.DataHelper; import net.i2p.data.PrivateKey; import net.i2p.data.i2np.GarlicClove; import net.i2p.data.i2np.GarlicMessage; import net.i2p.util.Log; /** * Read a GarlicMessage, decrypt it, and return the resulting CloveSet. * Thread-safe, does not contain any state. * Public as it's now in the RouterContext. */ public class GarlicMessageParser { private final Log _log; private final I2PAppContext _context; /** * Huge limit just to reduce chance of trouble. Typ. usage is 3. * As of 0.9.12. Was 255. */ private static final int MAX_CLOVES = 32; public GarlicMessageParser(I2PAppContext context) { _context = context; _log = _context.logManager().getLog(GarlicMessageParser.class); } /** * @param skm use tags from this session key manager * @return null on error */ public CloveSet getGarlicCloves(GarlicMessage message, PrivateKey encryptionKey, SessionKeyManager skm) { byte encData[] = message.getData(); byte decrData[]; try { if (_log.shouldLog(Log.DEBUG)) _log.debug("Decrypting with private key " + encryptionKey); decrData = _context.elGamalAESEngine().decrypt(encData, encryptionKey, skm); } catch (DataFormatException dfe) { if (_log.shouldLog(Log.WARN)) _log.warn("Error decrypting", dfe); return null; } if (decrData == null) { // This is the usual error path and it's logged at WARN level in GarlicMessageReceiver if (_log.shouldLog(Log.INFO)) _log.info("Decryption of garlic message failed", new Exception("Decrypt fail")); return null; } else { try { return readCloveSet(decrData); } catch (DataFormatException dfe) { if (_log.shouldLog(Log.WARN)) _log.warn("Unable to read cloveSet", dfe); return null; } } } private CloveSet readCloveSet(byte data[]) throws DataFormatException { int offset = 0; int numCloves = data[offset] & 0xff; offset++; if (_log.shouldLog(Log.DEBUG)) _log.debug("# cloves to read: " + numCloves); if (numCloves <= 0 || numCloves > MAX_CLOVES) throw new DataFormatException("bad clove count " + numCloves); GarlicClove[] cloves = new GarlicClove[numCloves]; for (int i = 0; i < numCloves; i++) { //if (_log.shouldLog(Log.DEBUG)) // _log.debug("Reading clove " + i); GarlicClove clove = new GarlicClove(_context); offset += clove.readBytes(data, offset); cloves[i] = clove; //if (_log.shouldLog(Log.DEBUG)) // _log.debug("After reading clove " + i); } //Certificate cert = new Certificate(); //offset += cert.readBytes(data, offset); Certificate cert = Certificate.create(data, offset); offset += cert.size(); long msgId = DataHelper.fromLong(data, offset, 4); offset += 4; //Date expiration = DataHelper.fromDate(data, offset); long expiration = DataHelper.fromLong(data, offset, 8); CloveSet set = new CloveSet(cloves, cert, msgId, expiration); return set; } }