diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnel.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnel.java index 2b269e6665e9f59c2d128994228591683be3afed..0b06c1ef91ce1c039081b96cb19290d96056e33e 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnel.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnel.java @@ -1223,7 +1223,8 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging { } /** - * Generate a new keypair + * Generate a new keypair. + * Does NOT support non-default sig types. * Deprecated - only used by CLI * * Sets the event "genkeysResult" = "ok" or "error" after the generation is complete @@ -1266,7 +1267,8 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging { } /** - * Generate a new keypair + * Generate a new keypair. + * Does NOT support non-default sig types. * Deprecated - only used by CLI * * Sets the event "privateKey" = base64 of the privateKey stream and @@ -1275,7 +1277,7 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging { * @param l logger to receive events and output */ private static void runGenTextKeys(Logging l) { - ByteArrayOutputStream privkey = new ByteArrayOutputStream(512); + ByteArrayOutputStream privkey = new ByteArrayOutputStream(1024); ByteArrayOutputStream pubkey = new ByteArrayOutputStream(512); makeKey(privkey, pubkey, l); l.log("Private key: " + Base64.encode(privkey.toByteArray())); @@ -1527,10 +1529,11 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging { /** * Create a new destination, storing the destination and its private keys where - * instructed + * instructed. + * Does NOT support non-default sig types. * Deprecated - only used by CLI * - * @param writeTo location to store the private keys + * @param writeTo location to store the destination and private keys * @param pubDest location to store the destination * @param l logger to send messages to */ diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelController.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelController.java index 1b1fdefcfabf14576c874702e68ae93e5f56d407..5936b6936fcd01aaeeec76f8f1b799c034357c49 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelController.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelController.java @@ -7,11 +7,13 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Properties; + import net.i2p.I2PAppContext; import net.i2p.I2PException; import net.i2p.client.I2PClient; import net.i2p.client.I2PClientFactory; import net.i2p.client.I2PSession; +import net.i2p.crypto.SigType; import net.i2p.data.Base32; import net.i2p.data.Destination; import net.i2p.i2ptunnel.socks.I2PSOCKSTunnel; @@ -49,8 +51,8 @@ public class TunnelController implements Logging { * the prefix should be used (and, in turn, that prefix should be stripped off * before being interpreted by this controller) * - * @param config original key=value mapping - * @param prefix beginning of key values that are relevent to this tunnel + * @param config original key=value mapping non-null + * @param prefix beginning of key values that are relevant to this tunnel */ public TunnelController(Properties config, String prefix) { this(config, prefix, true); @@ -58,6 +60,8 @@ public class TunnelController implements Logging { /** * + * @param config original key=value mapping non-null + * @param prefix beginning of key values that are relevant to this tunnel * @param createKey for servers, whether we want to create a brand new destination * with private keys at the location specified or not (does not * overwrite existing ones) @@ -99,7 +103,16 @@ public class TunnelController implements Logging { FileOutputStream fos = null; try { fos = new SecureFileOutputStream(keyFile); - Destination dest = client.createDestination(fos); + SigType stype = I2PClient.DEFAULT_SIGTYPE; + String st = _config.getProperty("option." + I2PClient.PROP_SIGTYPE); + if (st != null) { + SigType type = SigType.parseSigType(st); + if (type != null) + stype = type; + else + log("Unsupported sig type " + st); + } + Destination dest = client.createDestination(fos, stype); String destStr = dest.toBase64(); log("Private key created and saved in " + keyFile.getAbsolutePath()); log("You should backup this file in a secure place."); diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/udpTunnel/I2PTunnelUDPClientBase.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/udpTunnel/I2PTunnelUDPClientBase.java index 425cfb9ca2c089e7419c59a5594379f346aec81d..f1e8abaabeb88adb3eb4931c1b151eb4626c8fb1 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/udpTunnel/I2PTunnelUDPClientBase.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/udpTunnel/I2PTunnelUDPClientBase.java @@ -12,6 +12,7 @@ import net.i2p.client.I2PClient; import net.i2p.client.I2PClientFactory; import net.i2p.client.I2PSession; import net.i2p.client.I2PSessionException; +import net.i2p.crypto.SigType; import net.i2p.data.Destination; import net.i2p.i2ptunnel.I2PTunnel; import net.i2p.i2ptunnel.I2PTunnelTask; @@ -78,8 +79,17 @@ import net.i2p.util.EventDispatcher; I2PClient client = I2PClientFactory.createClient(); byte[] key; try { - ByteArrayOutputStream out = new ByteArrayOutputStream(512); - client.createDestination(out); + ByteArrayOutputStream out = new ByteArrayOutputStream(1024); + SigType stype = I2PClient.DEFAULT_SIGTYPE; + String st = tunnel.getClientOptions().getProperty(I2PClient.PROP_SIGTYPE); + if (st != null) { + SigType type = SigType.parseSigType(st); + if (type != null) + stype = type; + else + l.log("Unsupported sig type " + st); + } + client.createDestination(out, stype); key = out.toByteArray(); } catch(Exception exc) { throw new RuntimeException("failed to create i2p-destination", exc); diff --git a/apps/ministreaming/java/src/net/i2p/client/streaming/I2PSocketManagerFactory.java b/apps/ministreaming/java/src/net/i2p/client/streaming/I2PSocketManagerFactory.java index ebfb0c707f0fb86ff4fa4c6edf7d8b808588d975..493a0d272622465d4b0f0004b5100612ca643ece 100644 --- a/apps/ministreaming/java/src/net/i2p/client/streaming/I2PSocketManagerFactory.java +++ b/apps/ministreaming/java/src/net/i2p/client/streaming/I2PSocketManagerFactory.java @@ -14,6 +14,7 @@ import net.i2p.client.I2PClient; import net.i2p.client.I2PClientFactory; import net.i2p.client.I2PSession; import net.i2p.client.I2PSessionException; +import net.i2p.crypto.SigType; import net.i2p.util.Log; /** @@ -26,7 +27,7 @@ public class I2PSocketManagerFactory { public static final String PROP_MANAGER = "i2p.streaming.manager"; public static final String DEFAULT_MANAGER = "net.i2p.client.streaming.I2PSocketManagerFull"; - + /** * Create a socket manager using a brand new destination connected to the * I2CP router on the local machine on the default port (7654). @@ -79,9 +80,9 @@ public class I2PSocketManagerFactory { */ public static I2PSocketManager createManager(String i2cpHost, int i2cpPort, Properties opts) { I2PClient client = I2PClientFactory.createClient(); - ByteArrayOutputStream keyStream = new ByteArrayOutputStream(512); + ByteArrayOutputStream keyStream = new ByteArrayOutputStream(1024); try { - client.createDestination(keyStream); + client.createDestination(keyStream, getSigType(opts)); ByteArrayInputStream in = new ByteArrayInputStream(keyStream.toByteArray()); return createManager(in, i2cpHost, i2cpPort, opts); } catch (IOException ioe) { @@ -168,9 +169,9 @@ public class I2PSocketManagerFactory { int i2cpPort, Properties opts) throws I2PSessionException { if (myPrivateKeyStream == null) { I2PClient client = I2PClientFactory.createClient(); - ByteArrayOutputStream keyStream = new ByteArrayOutputStream(512); + ByteArrayOutputStream keyStream = new ByteArrayOutputStream(1024); try { - client.createDestination(keyStream); + client.createDestination(keyStream, getSigType(opts)); } catch (Exception e) { throw new I2PSessionException("Error creating keys", e); } @@ -257,6 +258,23 @@ public class I2PSocketManagerFactory { return i2cpPort; } + /** + * @param opts may be null + * @since 0.9.11 + */ + private static SigType getSigType(Properties opts) { + if (opts != null) { + String st = opts.getProperty(I2PClient.PROP_SIGTYPE); + if (st != null) { + SigType rv = SigType.parseSigType(st); + if (rv != null) + return rv; + getLog().error("Unsupported sig type " + st); + } + } + return I2PClient.DEFAULT_SIGTYPE; + } + /** @since 0.9.7 */ private static Log getLog() { return I2PAppContext.getGlobalContext().logManager().getLog(I2PSocketManagerFactory.class); diff --git a/apps/streaming/java/src/net/i2p/client/streaming/Packet.java b/apps/streaming/java/src/net/i2p/client/streaming/Packet.java index 1ecdc0771b6cb01f8e3cac49f1e35cfd440fd267..411e66105951a43d6ce2e4f3519d78f6de9ebcde 100644 --- a/apps/streaming/java/src/net/i2p/client/streaming/Packet.java +++ b/apps/streaming/java/src/net/i2p/client/streaming/Packet.java @@ -397,13 +397,14 @@ class Packet { * @throws IllegalStateException if there is data missing or otherwise b0rked */ public int writePacket(byte buffer[], int offset) throws IllegalStateException { - return writePacket(buffer, offset, true); + return writePacket(buffer, offset, 0); } + /** - * @param includeSig if true, include the real signature, otherwise put zeroes - * in its place. + * @param fakeSigLen if 0, include the real signature in _optionSignature; + * if nonzero, leave space for that many bytes */ - private int writePacket(byte buffer[], int offset, boolean includeSig) throws IllegalStateException { + private int writePacket(byte buffer[], int offset, int fakeSigLen) throws IllegalStateException { int cur = offset; DataHelper.toLong(buffer, cur, 4, (_sendStreamId >= 0 ? _sendStreamId : STREAM_ID_UNKNOWN)); cur += 4; @@ -438,7 +439,12 @@ class Packet { if (isFlagSet(FLAG_MAX_PACKET_SIZE_INCLUDED)) optionSize += 2; if (isFlagSet(FLAG_SIGNATURE_INCLUDED)) { - optionSize += _optionSignature.length(); + if (fakeSigLen > 0) + optionSize += fakeSigLen; + else if (_optionSignature != null) + optionSize += _optionSignature.length(); + else + throw new IllegalStateException(); } DataHelper.toLong(buffer, cur, 2, optionSize); @@ -456,11 +462,14 @@ class Packet { cur += 2; } if (isFlagSet(FLAG_SIGNATURE_INCLUDED)) { - if (includeSig) + if (fakeSigLen == 0) { + // we're signing (or validating) System.arraycopy(_optionSignature.getData(), 0, buffer, cur, _optionSignature.length()); - else // we're signing (or validating) - Arrays.fill(buffer, cur, cur + _optionSignature.length(), (byte)0x0); - cur += _optionSignature.length(); + cur += _optionSignature.length(); + } else { + Arrays.fill(buffer, cur, cur + fakeSigLen, (byte)0x0); + cur += fakeSigLen; + } } if (_payload != null) { @@ -661,7 +670,7 @@ class Packet { if (buffer == null) buffer = new byte[size]; - int written = writePacket(buffer, 0, false); + int written = writePacket(buffer, 0, from.getSigningPublicKey().getType().getSigLen()); if (written != size) { ctx.logManager().getLog(Packet.class).error("Written " + written + " size " + size + " for " + toString(), new Exception("moo")); return false; @@ -692,7 +701,7 @@ class Packet { */ public int writeSignedPacket(byte buffer[], int offset, I2PAppContext ctx, SigningPrivateKey key) throws IllegalStateException { setFlag(FLAG_SIGNATURE_INCLUDED); - int size = writePacket(buffer, offset, false); + int size = writePacket(buffer, offset, key.getType().getSigLen()); _optionSignature = ctx.dsa().sign(buffer, offset, size, key); //if (false) { // Log l = ctx.logManager().getLog(Packet.class); @@ -760,7 +769,7 @@ class Packet { if (isFlagSet(FLAG_CLOSE)) buf.append(" CLOSE"); if (isFlagSet(FLAG_DELAY_REQUESTED)) buf.append(" DELAY ").append(_optionDelay); if (isFlagSet(FLAG_ECHO)) buf.append(" ECHO"); - if (isFlagSet(FLAG_FROM_INCLUDED)) buf.append(" FROM"); + if (isFlagSet(FLAG_FROM_INCLUDED)) buf.append(" FROM ").append(_optionFrom.size()); if (isFlagSet(FLAG_MAX_PACKET_SIZE_INCLUDED)) buf.append(" MS ").append(_optionMaxSize); if (isFlagSet(FLAG_PROFILE_INTERACTIVE)) buf.append(" INTERACTIVE"); if (isFlagSet(FLAG_RESET)) buf.append(" RESET"); diff --git a/core/java/src/net/i2p/client/I2PClient.java b/core/java/src/net/i2p/client/I2PClient.java index 84bcf50d2af68c8215124d89f52f7d6f1935f76f..bbfd6811aeaae96e992a09f04fd9287ca396a91f 100644 --- a/core/java/src/net/i2p/client/I2PClient.java +++ b/core/java/src/net/i2p/client/I2PClient.java @@ -41,6 +41,11 @@ public interface I2PClient { /** @since 0.8.1 */ public final static String PROP_RELIABILITY_NONE = "none"; + /** @since 0.9.11 */ + public static final String PROP_SIGTYPE = "i2cp.destination.sigType"; + /** @since 0.9.11 */ + public static final SigType DEFAULT_SIGTYPE = SigType.DSA_SHA1; + /** * For router->client payloads. * diff --git a/core/java/src/net/i2p/client/I2PClientImpl.java b/core/java/src/net/i2p/client/I2PClientImpl.java index 7fac17e10d7532508ddaf2a91c7f1ca145022c3d..e29cdc342658ea1eaebc72112a0aca3122b7fc9e 100644 --- a/core/java/src/net/i2p/client/I2PClientImpl.java +++ b/core/java/src/net/i2p/client/I2PClientImpl.java @@ -47,7 +47,7 @@ class I2PClientImpl implements I2PClient { * format is specified in {@link net.i2p.data.PrivateKeyFile PrivateKeyFile} */ public Destination createDestination(OutputStream destKeyStream) throws I2PException, IOException { - return createDestination(destKeyStream, SigType.DSA_SHA1); + return createDestination(destKeyStream, DEFAULT_SIGTYPE); } /** diff --git a/core/java/src/net/i2p/crypto/DSAEngine.java b/core/java/src/net/i2p/crypto/DSAEngine.java index dce8843e624b80004ccc8dde4123dc9ae3db1135..16c63e8ea9e7b46efcfa49b75515423c02fbb879 100644 --- a/core/java/src/net/i2p/crypto/DSAEngine.java +++ b/core/java/src/net/i2p/crypto/DSAEngine.java @@ -89,13 +89,20 @@ public class DSAEngine { * Uses TheCrypto code for DSA-SHA1 unless configured to use the java.security libraries. */ public boolean verifySignature(Signature signature, byte signedData[], SigningPublicKey verifyingKey) { + return verifySignature(signature, signedData, 0, signedData.length, verifyingKey); + } + + /** + * Verify using any sig type as of 0.9.11 (DSA only prior to that) + */ + public boolean verifySignature(Signature signature, byte signedData[], int offset, int size, SigningPublicKey verifyingKey) { boolean rv; SigType type = signature.getType(); if (type != verifyingKey.getType()) throw new IllegalArgumentException("type mismatch sig=" + signature.getType() + " key=" + verifyingKey.getType()); if (type != SigType.DSA_SHA1) { try { - rv = altVerifySig(signature, signedData, verifyingKey); + rv = altVerifySig(signature, signedData, offset, size, verifyingKey); if ((!rv) && _log.shouldLog(Log.WARN)) _log.warn(type + " Sig Verify Fail"); return rv; @@ -107,7 +114,7 @@ public class DSAEngine { } if (_useJavaLibs) { try { - rv = altVerifySigSHA1(signature, signedData, verifyingKey); + rv = altVerifySigSHA1(signature, signedData, offset, size, verifyingKey); if ((!rv) && _log.shouldLog(Log.WARN)) _log.warn("Lib DSA Sig Verify Fail"); return rv; @@ -117,19 +124,12 @@ public class DSAEngine { // now try TheCrypto } } - rv = verifySignature(signature, signedData, 0, signedData.length, verifyingKey); + rv = verifySignature(signature, calculateHash(signedData, offset, size), verifyingKey); if ((!rv) && _log.shouldLog(Log.WARN)) _log.warn("TheCrypto DSA Sig Verify Fail"); return rv; } - /** - * Verify using DSA-SHA1 ONLY - */ - public boolean verifySignature(Signature signature, byte signedData[], int offset, int size, SigningPublicKey verifyingKey) { - return verifySignature(signature, calculateHash(signedData, offset, size), verifyingKey); - } - /** * Verify using DSA-SHA1 ONLY */ @@ -256,16 +256,26 @@ public class DSAEngine { } /** - * Sign using DSA-SHA1 or ECDSA. + * Sign using any key type. * Uses TheCrypto code unless configured to use the java.security libraries. * * @return null on error */ public Signature sign(byte data[], SigningPrivateKey signingKey) { + return sign(data, 0, data.length, signingKey); + } + + /** + * Sign using any key type as of 0.9.11 (DSA-SHA1 only prior to that) + * + * @return null on error + */ + public Signature sign(byte data[], int offset, int length, SigningPrivateKey signingKey) { + if ((signingKey == null) || (data == null) || (data.length <= 0)) return null; SigType type = signingKey.getType(); if (type != SigType.DSA_SHA1) { try { - return altSign(data, signingKey); + return altSign(data, offset, length, signingKey); } catch (GeneralSecurityException gse) { if (_log.shouldLog(Log.WARN)) _log.warn(type + " Sign Fail", gse); @@ -274,23 +284,13 @@ public class DSAEngine { } if (_useJavaLibs) { try { - return altSignSHA1(data, signingKey); + return altSignSHA1(data, offset, length, signingKey); } catch (GeneralSecurityException gse) { if (_log.shouldLog(Log.WARN)) _log.warn("Lib Sign Fail, privkey = " + signingKey, gse); // now try TheCrypto } } - return sign(data, 0, data.length, signingKey); - } - - /** - * Sign using DSA-SHA1 ONLY - * - * @return null on error - */ - public Signature sign(byte data[], int offset, int length, SigningPrivateKey signingKey) { - if ((signingKey == null) || (data == null) || (data.length <= 0)) return null; SHA1Hash h = calculateHash(data, offset, length); return sign(h, signingKey); } @@ -495,20 +495,20 @@ public class DSAEngine { /** * Generic verify DSA_SHA1, ECDSA, or RSA * @throws GeneralSecurityException if algorithm unvailable or on other errors - * @since 0.9.9 + * @since 0.9.9 added off/len 0.9.11 */ - private boolean altVerifySig(Signature signature, byte[] data, SigningPublicKey verifyingKey) + private boolean altVerifySig(Signature signature, byte[] data, int offset, int len, SigningPublicKey verifyingKey) throws GeneralSecurityException { SigType type = signature.getType(); if (type != verifyingKey.getType()) throw new IllegalArgumentException("type mismatch sig=" + type + " key=" + verifyingKey.getType()); if (type == SigType.DSA_SHA1) - return altVerifySigSHA1(signature, data, verifyingKey); + return altVerifySigSHA1(signature, data, offset, len, verifyingKey); java.security.Signature jsig = java.security.Signature.getInstance(type.getAlgorithmName()); PublicKey pubKey = SigUtil.toJavaKey(verifyingKey); jsig.initVerify(pubKey); - jsig.update(data); + jsig.update(data, offset, len); boolean rv = jsig.verify(SigUtil.toJavaSig(signature)); return rv; } @@ -555,13 +555,14 @@ public class DSAEngine { /** * Alternate to verifySignature() using java.security libraries. * @throws GeneralSecurityException if algorithm unvailable or on other errors - * @since 0.8.7 + * @since 0.8.7 added off/len 0.9.11 */ - private boolean altVerifySigSHA1(Signature signature, byte[] data, SigningPublicKey verifyingKey) throws GeneralSecurityException { + private boolean altVerifySigSHA1(Signature signature, byte[] data, int offset, + int len, SigningPublicKey verifyingKey) throws GeneralSecurityException { java.security.Signature jsig = java.security.Signature.getInstance("SHA1withDSA"); PublicKey pubKey = SigUtil.toJavaDSAKey(verifyingKey); jsig.initVerify(pubKey); - jsig.update(data); + jsig.update(data, offset, len); boolean rv = jsig.verify(SigUtil.toJavaSig(signature)); //if (!rv) { // System.out.println("BAD SIG\n" + net.i2p.util.HexDump.dump(signature.getData())); @@ -573,17 +574,18 @@ public class DSAEngine { /** * Generic sign DSA_SHA1, ECDSA, or RSA * @throws GeneralSecurityException if algorithm unvailable or on other errors - * @since 0.9.9 + * @since 0.9.9 added off/len 0.9.11 */ - private Signature altSign(byte[] data, SigningPrivateKey privateKey) throws GeneralSecurityException { + private Signature altSign(byte[] data, int offset, int len, + SigningPrivateKey privateKey) throws GeneralSecurityException { SigType type = privateKey.getType(); if (type == SigType.DSA_SHA1) - return altSignSHA1(data, privateKey); + return altSignSHA1(data, offset, len, privateKey); java.security.Signature jsig = java.security.Signature.getInstance(type.getAlgorithmName()); PrivateKey privKey = SigUtil.toJavaKey(privateKey); jsig.initSign(privKey, _context.random()); - jsig.update(data); + jsig.update(data, offset, len); return SigUtil.fromJavaSig(jsig.sign(), type); } @@ -622,13 +624,14 @@ public class DSAEngine { /** * Alternate to sign() using java.security libraries. * @throws GeneralSecurityException if algorithm unvailable or on other errors - * @since 0.8.7 + * @since 0.8.7 added off/len args 0.9.11 */ - private Signature altSignSHA1(byte[] data, SigningPrivateKey privateKey) throws GeneralSecurityException { + private Signature altSignSHA1(byte[] data, int offset, int len, + SigningPrivateKey privateKey) throws GeneralSecurityException { java.security.Signature jsig = java.security.Signature.getInstance("SHA1withDSA"); PrivateKey privKey = SigUtil.toJavaDSAKey(privateKey); jsig.initSign(privKey, _context.random()); - jsig.update(data); + jsig.update(data, offset, len); return SigUtil.fromJavaSig(jsig.sign(), SigType.DSA_SHA1); } diff --git a/core/java/src/net/i2p/data/Destination.java b/core/java/src/net/i2p/data/Destination.java index 4d8affc870570310a5a0e0302902b7cb296aa3a1..7268be640c71062b4bbf8e243c5de3a218f595ed 100644 --- a/core/java/src/net/i2p/data/Destination.java +++ b/core/java/src/net/i2p/data/Destination.java @@ -57,6 +57,16 @@ public class Destination extends KeysAndCert { PublicKey pk = PublicKey.create(in); SigningPublicKey sk = SigningPublicKey.create(in); Certificate c = Certificate.create(in); + byte[] padding; + if (c.getCertificateType() == Certificate.CERTIFICATE_TYPE_KEY) { + // convert SPK to new SPK and padding + KeyCertificate kcert = c.toKeyCertificate(); + padding = sk.getPadding(kcert); + sk = sk.toTypedKey(kcert); + c = kcert; + } else { + padding = null; + } Destination rv; synchronized(_cache) { rv = _cache.get(sk); @@ -67,7 +77,7 @@ public class Destination extends KeysAndCert { } //if (STATS) // I2PAppContext.getGlobalContext().statManager().addRateData("DestCache", 0); - rv = new Destination(pk, sk, c); + rv = new Destination(pk, sk, c, padding); _cache.put(sk, rv); } return rv; @@ -86,10 +96,11 @@ public class Destination extends KeysAndCert { /** * @since 0.9.9 */ - private Destination(PublicKey pk, SigningPublicKey sk, Certificate c) { + private Destination(PublicKey pk, SigningPublicKey sk, Certificate c, byte[] padding) { _publicKey = pk; _signingKey = sk; _certificate = c; + _padding = padding; } /** @@ -138,7 +149,16 @@ public class Destination extends KeysAndCert { ****/ public int size() { - return PublicKey.KEYSIZE_BYTES + _signingKey.length() + _certificate.size(); + int rv = PublicKey.KEYSIZE_BYTES + _signingKey.length(); + if (_certificate.getCertificateType() == Certificate.CERTIFICATE_TYPE_KEY) { + // cert data included in keys + rv += 7; + if (_padding != null) + rv += _padding.length; + } else { + rv += _certificate.size(); + } + return rv; } /**