diff --git a/core/java/src/net/i2p/data/Certificate.java b/core/java/src/net/i2p/data/Certificate.java index 855d474ca..798c29680 100644 --- a/core/java/src/net/i2p/data/Certificate.java +++ b/core/java/src/net/i2p/data/Certificate.java @@ -26,8 +26,10 @@ import java.io.OutputStream; * @author jrandom */ public class Certificate extends DataStructureImpl { - private int _type; - private byte[] _payload; + public final static Certificate NULL_CERT = new NullCert(); + + protected int _type; + protected byte[] _payload; /** Specifies a null certificate type with no payload */ public final static int CERTIFICATE_TYPE_NULL = 0; @@ -41,6 +43,25 @@ public class Certificate extends DataStructureImpl { /** Contains multiple certs */ public final static int CERTIFICATE_TYPE_MULTIPLE = 4; + /** + * If null cert, return immutable static instance, else create new + * @since 0.8.3 + */ + public static Certificate create(InputStream in) throws DataFormatException, IOException { + int type = (int) DataHelper.readLong(in, 1); + int length = (int) DataHelper.readLong(in, 2); + if (type == 0 && length == 0) + return NULL_CERT; + // from here down roughly the same as readBytes() below + if (length == 0) + return new Certificate(type, null); + byte[] payload = new byte[length]; + int read = DataHelper.read(in, payload); + if (read != length) + throw new DataFormatException("Not enough bytes for the payload (read: " + read + " length: " + length + ')'); + return new Certificate(type, payload); + } + public Certificate() { } @@ -140,10 +161,12 @@ public class Certificate extends DataStructureImpl { Certificate cert = (Certificate) object; return _type == cert.getCertificateType() && DataHelper.eq(_payload, cert.getPayload()); } + @Override public int hashCode() { return _type + DataHelper.hashCode(_payload); } + @Override public String toString() { StringBuilder buf = new StringBuilder(64); @@ -177,4 +200,67 @@ public class Certificate extends DataStructureImpl { buf.append("]"); return buf.toString(); } + + /** + * An immutable null certificate. + * @since 0.8.3 + */ + private static final class NullCert extends Certificate { + private static final int NULL_LENGTH = 1 + 2; + private static final byte[] NULL_DATA = new byte[NULL_LENGTH]; + + public NullCert() { + // zero already + //_type = CERTIFICATE_TYPE_NULL; + } + + /** @throws RuntimeException always */ + @Override + public void setCertificateType(int type) { + throw new RuntimeException("Data already set"); + } + + /** @throws RuntimeException always */ + @Override + public void setPayload(byte[] payload) { + throw new RuntimeException("Data already set"); + } + + /** @throws RuntimeException always */ + @Override + public void readBytes(InputStream in) throws DataFormatException, IOException { + throw new RuntimeException("Data already set"); + } + + /** Overridden for efficiency */ + @Override + public void writeBytes(OutputStream out) throws IOException { + out.write(NULL_DATA); + } + + /** Overridden for efficiency */ + @Override + public int writeBytes(byte target[], int offset) { + System.arraycopy(NULL_DATA, 0, target, offset, NULL_LENGTH); + return offset + NULL_LENGTH; + } + + /** @throws RuntimeException always */ + @Override + public int readBytes(byte source[], int offset) throws DataFormatException { + throw new RuntimeException("Data already set"); + } + + /** Overridden for efficiency */ + @Override + public int size() { + return NULL_LENGTH; + } + + /** Overridden for efficiency */ + @Override + public int hashCode() { + return 99999; + } + } } diff --git a/core/java/src/net/i2p/data/KeysAndCert.java b/core/java/src/net/i2p/data/KeysAndCert.java index 046d428a3..d27d4bcf4 100644 --- a/core/java/src/net/i2p/data/KeysAndCert.java +++ b/core/java/src/net/i2p/data/KeysAndCert.java @@ -65,8 +65,9 @@ public class KeysAndCert extends DataStructureImpl { _publicKey.readBytes(in); _signingKey = new SigningPublicKey(); _signingKey.readBytes(in); - _certificate = new Certificate(); - _certificate.readBytes(in); + //_certificate = new Certificate(); + //_certificate.readBytes(in); + _certificate = Certificate.create(in); __calculatedHash = null; } diff --git a/router/java/src/net/i2p/data/i2np/GarlicClove.java b/router/java/src/net/i2p/data/i2np/GarlicClove.java index 1380dfd60..1067fb640 100644 --- a/router/java/src/net/i2p/data/i2np/GarlicClove.java +++ b/router/java/src/net/i2p/data/i2np/GarlicClove.java @@ -68,8 +68,9 @@ public class GarlicClove extends DataStructureImpl { _expiration = DataHelper.readDate(in); if (_log.shouldLog(Log.DEBUG)) _log.debug("CloveID read: " + _cloveId + " expiration read: " + _expiration); - _certificate = new Certificate(); - _certificate.readBytes(in); + //_certificate = new Certificate(); + //_certificate.readBytes(in); + _certificate = Certificate.create(in); if (_log.shouldLog(Log.DEBUG)) _log.debug("Read cert: " + _certificate); } diff --git a/router/java/src/net/i2p/router/message/OutboundClientMessageJobHelper.java b/router/java/src/net/i2p/router/message/OutboundClientMessageJobHelper.java index 9d045c510..9025fd22b 100644 --- a/router/java/src/net/i2p/router/message/OutboundClientMessageJobHelper.java +++ b/router/java/src/net/i2p/router/message/OutboundClientMessageJobHelper.java @@ -115,7 +115,7 @@ class OutboundClientMessageJobHelper { instructions.setRouter(null); instructions.setTunnelId(null); - config.setCertificate(new Certificate(Certificate.CERTIFICATE_TYPE_NULL, null)); + config.setCertificate(Certificate.NULL_CERT); config.setDeliveryInstructions(instructions); config.setId(ctx.random().nextLong(I2NPMessage.MAX_ID_VALUE)); config.setExpiration(expiration); // +2*Router.CLOCK_FUDGE_FACTOR); @@ -165,7 +165,7 @@ class OutboundClientMessageJobHelper { if (log.shouldLog(Log.DEBUG)) log.debug("Delivery status message key: " + replyToken + " arrival: " + msg.getArrival()); - ackClove.setCertificate(new Certificate(Certificate.CERTIFICATE_TYPE_NULL, null)); + ackClove.setCertificate(Certificate.NULL_CERT); ackClove.setDeliveryInstructions(ackInstructions); ackClove.setExpiration(expiration); ackClove.setId(ctx.random().nextLong(I2NPMessage.MAX_ID_VALUE)); @@ -196,7 +196,7 @@ class OutboundClientMessageJobHelper { instructions.setDelaySeconds(0); instructions.setEncrypted(false); - clove.setCertificate(new Certificate(Certificate.CERTIFICATE_TYPE_NULL, null)); + clove.setCertificate(Certificate.NULL_CERT); clove.setDeliveryInstructions(instructions); clove.setExpiration(expiration); clove.setId(ctx.random().nextLong(I2NPMessage.MAX_ID_VALUE)); @@ -222,7 +222,7 @@ class OutboundClientMessageJobHelper { instructions.setDelaySeconds(0); instructions.setEncrypted(false); - clove.setCertificate(new Certificate(Certificate.CERTIFICATE_TYPE_NULL, null)); + clove.setCertificate(Certificate.NULL_CERT); clove.setDeliveryInstructions(instructions); clove.setExpiration(expiration); clove.setId(ctx.random().nextLong(I2NPMessage.MAX_ID_VALUE)); diff --git a/router/java/src/net/i2p/router/message/OutboundClientMessageOneShotJob.java b/router/java/src/net/i2p/router/message/OutboundClientMessageOneShotJob.java index 45622d117..ce1f36159 100644 --- a/router/java/src/net/i2p/router/message/OutboundClientMessageOneShotJob.java +++ b/router/java/src/net/i2p/router/message/OutboundClientMessageOneShotJob.java @@ -872,7 +872,7 @@ public class OutboundClientMessageOneShotJob extends JobImpl { instructions.setDelaySeconds(0); instructions.setEncrypted(false); - clove.setCertificate(new Certificate(Certificate.CERTIFICATE_TYPE_NULL, null)); + clove.setCertificate(Certificate.NULL_CERT); clove.setDeliveryInstructions(instructions); clove.setExpiration(OVERALL_TIMEOUT_MS_DEFAULT+getContext().clock().now()); clove.setId(getContext().random().nextLong(I2NPMessage.MAX_ID_VALUE)); diff --git a/router/java/src/net/i2p/router/networkdb/kademlia/MessageWrapper.java b/router/java/src/net/i2p/router/networkdb/kademlia/MessageWrapper.java index 1f521455e..b53269e52 100644 --- a/router/java/src/net/i2p/router/networkdb/kademlia/MessageWrapper.java +++ b/router/java/src/net/i2p/router/networkdb/kademlia/MessageWrapper.java @@ -46,7 +46,7 @@ class MessageWrapper { instructions.setDeliveryMode(DeliveryInstructions.DELIVERY_MODE_LOCAL); PayloadGarlicConfig payload = new PayloadGarlicConfig(); - payload.setCertificate(new Certificate(Certificate.CERTIFICATE_TYPE_NULL, null)); + payload.setCertificate(Certificate.NULL_CERT); payload.setId(ctx.random().nextLong(I2NPMessage.MAX_ID_VALUE)); payload.setPayload(m); payload.setRecipient(to);