diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/IndexBean.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/IndexBean.java index a532551f22..8bcf0050d8 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/IndexBean.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/web/IndexBean.java @@ -19,6 +19,7 @@ import net.i2p.I2PAppContext; import net.i2p.I2PException; import net.i2p.app.ClientAppManager; import net.i2p.app.Outproxy; +import net.i2p.crypto.Blinding; import net.i2p.data.Certificate; import net.i2p.data.DataHelper; import net.i2p.data.Destination; @@ -552,6 +553,26 @@ public class IndexBean { return d.toBase32(); return ""; } + + /** + * Works even if tunnel is not running. + * @return "{56 chars}.b32.i2p" or "" if not blinded + * @since 0.9.40 + */ + public String getEncryptedBase32(int tunnel) { + Destination d = getDestination(tunnel); + if (d != null) { + int mode = _helper.getEncryptMode(tunnel); + if (mode > 1) { + try { + String secret = _helper.getBlindedPassword(tunnel); + boolean requireSecret = secret != null && secret.length() > 0; + return Blinding.encode(_context, d.getSigningPublicKey(), requireSecret, false); + } catch (RuntimeException re) {} + } + } + return ""; + } /** * Works even if tunnel is not running. diff --git a/apps/i2ptunnel/jsp/editServer.jsi b/apps/i2ptunnel/jsp/editServer.jsi index 991595d63b..5fcecaf6f6 100644 --- a/apps/i2ptunnel/jsp/editServer.jsi +++ b/apps/i2ptunnel/jsp/editServer.jsi @@ -505,10 +505,10 @@ int curSigType = editBean.getSigType(curTunnel, tunnelType); if (curSigType == 7 || curSigType == 11) { %> - <% if (editBean.isAdvanced()) { // TODO, unimplemented @@ -516,11 +516,11 @@ + <%=intl._t("Blinded with lookup password and shared key")%> + <%=intl._t("Blinded with lookup password and per-user key")%> <% } // isAdvanced() } // curSigType @@ -553,7 +553,7 @@ %> - <%=intl._t("Blinded Password")%>: + <%=intl._t("Optional lookup password")%>: " value="<%=editBean.getBlindedPassword(curTunnel)%>" class="freetext password" /> diff --git a/apps/i2ptunnel/jsp/index.jsp b/apps/i2ptunnel/jsp/index.jsp index 0b1b6df439..48f31c8dec 100644 --- a/apps/i2ptunnel/jsp/index.jsp +++ b/apps/i2ptunnel/jsp/index.jsp @@ -197,15 +197,29 @@ + <% + String encName = indexBean.getEncryptedBase32(curServer); + if (encName != null && encName.length() > 0) { + %> + + + <%=intl._t("Encrypted")%>: + <%=encName%> + + + <% + } // encName + %> + - Description: + <%=intl._t("Description")%>: <%=indexBean.getTunnelDescription(curServer)%> <% - } + } // for loop %> diff --git a/core/java/src/net/i2p/crypto/Blinding.java b/core/java/src/net/i2p/crypto/Blinding.java index bb91214eba..1752f9897f 100644 --- a/core/java/src/net/i2p/crypto/Blinding.java +++ b/core/java/src/net/i2p/crypto/Blinding.java @@ -33,6 +33,10 @@ public final class Blinding { private static final String INFO = "i2pblinding1"; private static final byte[] INFO_ALPHA = DataHelper.getASCII("I2PGenerateAlpha"); + private static final byte FLAG_TWOBYTE = 0x01; + private static final byte FLAG_SECRET = 0x02; + private static final byte FLAG_AUTH = 0x04; + // following copied from RouterKeyGenerator private static final String FORMAT = "yyyyMMdd"; private static final int LENGTH = FORMAT.length(); @@ -233,9 +237,9 @@ public final class Blinding { int flag = b[0] & 0xff; if ((flag & 0xf8) != 0) throw new IllegalArgumentException("Corrupt b32 or unsupported options"); - if ((flag & 0x01) != 0) + if ((flag & FLAG_TWOBYTE) != 0) throw new IllegalArgumentException("Two byte sig types unsupported"); - if ((flag & 0x04) != 0) + if ((flag & FLAG_AUTH) != 0) throw new IllegalArgumentException("Per-client auth unsupported"); // TODO two-byte sigtypes int st1 = b[1] & 0xff; @@ -258,13 +262,16 @@ public final class Blinding { System.arraycopy(b, 3, spkData, 0, spkLen); SigningPublicKey spk = new SigningPublicKey(sigt1, spkData); String secret; - if ((flag & 0x02) != 0) { - if (4 + spkLen > b.length) - throw new IllegalArgumentException("No secret data"); - int secLen = b[3 + spkLen] & 0xff; - if (4 + spkLen + secLen != b.length) - throw new IllegalArgumentException("Bad b32 length"); - secret = DataHelper.getUTF8(b, 4 + spkLen, secLen); + if ((flag & FLAG_SECRET) != 0) { + if (4 + spkLen > b.length) { + //throw new IllegalArgumentException("No secret data"); + secret = null; + } else { + int secLen = b[3 + spkLen] & 0xff; + if (4 + spkLen + secLen != b.length) + throw new IllegalArgumentException("Bad b32 length"); + secret = DataHelper.getUTF8(b, 4 + spkLen, secLen); + } } else if (3 + spkLen != b.length) { throw new IllegalArgumentException("b32 too long"); } else { @@ -274,6 +281,33 @@ public final class Blinding { return rv; } + /** + * Encode a public key as a new-format b32 address. + * PRELIMINARY - Subject to change - see proposal 149 + * + * @return (56 chars).b32.i2p + * @throws IllegalArgumentException on bad inputs + * @throws UnsupportedOperationException unless supported SigTypes + * @since 0.9.40 + */ + public static String encode(I2PAppContext ctx, SigningPublicKey key) throws RuntimeException { + return encode(ctx, key, false, false, null); + } + + /** + * Encode a public key as a new-format b32 address. + * PRELIMINARY - Subject to change - see proposal 149 + * + * @return (56 chars).b32.i2p + * @throws IllegalArgumentException on bad inputs + * @throws UnsupportedOperationException unless supported SigTypes + * @since 0.9.40 + */ + public static String encode(I2PAppContext ctx, SigningPublicKey key, + boolean requireSecret, boolean requireAuth) throws RuntimeException { + return encode(ctx, key, requireSecret, requireAuth, null); + } + /** * Encode a public key as a new-format b32 address. * PRELIMINARY - Subject to change - see proposal 149 @@ -284,7 +318,9 @@ public final class Blinding { * @throws UnsupportedOperationException unless supported SigTypes * @since 0.9.40 */ - public static String encode(I2PAppContext ctx, SigningPublicKey key, String secret) throws RuntimeException { + public static String encode(I2PAppContext ctx, SigningPublicKey key, + boolean requireSecret, boolean requireAuth, + String secret) throws RuntimeException { SigType type = key.getType(); if (type != TYPE && type != TYPER) throw new UnsupportedOperationException(); @@ -303,8 +339,10 @@ public final class Blinding { crc.update(b, 3, b.length - 3); long check = crc.getValue(); // TODO two-byte sigtypes - if (slen > 0) - b[0] = 0x02; + if (slen > 0 || requireSecret) + b[0] = FLAG_SECRET; + if (requireAuth) + b[0] |= FLAG_AUTH; b[1] = (byte) (type.getCode() & 0xff); b[2] = (byte) (TYPER.getCode() & 0xff); b[0] ^= (byte) check;