diff --git a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnel.java b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnel.java index aa2499ea65..bbfe9378d0 100644 --- a/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnel.java +++ b/apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnel.java @@ -66,10 +66,14 @@ import net.i2p.client.I2PSession; import net.i2p.client.I2PSessionException; import net.i2p.client.I2PSimpleClient; import net.i2p.client.naming.NamingService; +import net.i2p.crypto.Blinding; +import net.i2p.crypto.EncType; import net.i2p.data.Base64; +import net.i2p.data.BlindData; import net.i2p.data.DataFormatException; import net.i2p.data.DataHelper; import net.i2p.data.Destination; +import net.i2p.data.PrivateKey; import net.i2p.i2ptunnel.socks.I2PSOCKSIRCTunnel; import net.i2p.i2ptunnel.socks.I2PSOCKSTunnel; import net.i2p.i2ptunnel.streamr.StreamrConsumer; @@ -448,6 +452,8 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging { runOwnDest(args, l); } else if (cmdname.equals("auth")) { runAuth(args, l); + } else if (cmdname.equals("blinding")) { + runBlinding(args, l); } else { l.log("Unknown command [" + cmdname + "]"); } @@ -464,6 +470,7 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging { l.log("Command list:\n" + // alphabetical please... " auth \n" + + " blinding [-d|p] [-k key] [-s password] [-e expires] xxx.b32.i2p\n" + " client [, []\n" + " clientoptions [-acx] [key=value ]*\n" + " close [forced|destroy] |all\n" + @@ -1694,6 +1701,110 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging { } } + /** + * Send a BlindingInfo message, just for testing + * @since 0.9.43 + */ + private void runBlinding(String[] argv, Logging l) { + // blinding [-d|p] [-k key] [-s password] [-e expires] xxx.b32.i2p // -d for DH; -p for PSK; expires in days + Getopt g = new Getopt("blinding", argv, "dpk:s:e:"); + boolean error = false; + boolean dh = false; + boolean psk = false; + String key = null; + String pw = null; + long expires = 0; + int c; + while ((c = g.getopt()) != -1) { + switch (c) { + case 'd': + dh = true; + break; + + case 'p': + psk = true; + break; + + case 'k': + key = g.getOptarg(); + break; + + case 's': + pw = g.getOptarg(); + break; + + case 'e': + expires = Long.parseLong(g.getOptarg()); + break; + + case '?': + case ':': + default: + error = true; + } + } + int remaining = argv.length - g.getOptind(); + if (error || remaining != 1 || (dh && psk) || ((dh || psk) && key == null)) { + System.out.println("Usage: blinding [-d|p] [-k key] [-s password] [-e expires] xxx.b32.i2p // -d for DH; -p for PSK; expires in days"); + return; + } + String name = argv[g.getOptind()]; + BlindData bd; + try { + int auth = BlindData.AUTH_NONE; + PrivateKey pk = null; + if (key != null) { + byte[] data = Base64.decode(key); + if (data == null || data.length != 32) { + System.out.println("Invalid private key"); + return; + } + pk = new PrivateKey(EncType.ECIES_X25519, data); + auth = dh ? BlindData.AUTH_DH : BlindData.AUTH_PSK; + } + // basic check and decode + bd = Blinding.decode(_context, name); + // fill in key and secret + bd = new BlindData(_context, bd.getUnblindedPubKey(), bd.getBlindedSigType(), pw, auth, pk); + long now = _context.clock().now(); + bd.setDate(now); + if (expires > 0) { + expires *= 24*60*60*1000L; + expires += now; + bd.setExpiration(expires); + } + } catch (IllegalArgumentException iae) { + System.out.println("Invalid b32 " + name + " - " + iae); + return; + } + boolean ssl = Boolean.parseBoolean(_clientOptions.getProperty("i2cp.SSL")); + String user = _clientOptions.getProperty("i2cp.username"); + String ipw = _clientOptions.getProperty("i2cp.password"); + I2PClient client = new I2PSimpleClient(); + Properties opts = new Properties(); + opts.put(I2PClient.PROP_TCP_HOST, host); + opts.put(I2PClient.PROP_TCP_PORT, port); + opts.put("i2cp.SSL", Boolean.toString(ssl)); + if (user != null) + opts.put("i2cp.username", user); + if (ipw != null) + opts.put("i2cp.password", ipw); + I2PSession session = null; + try { + session = client.createSession(null, opts); + session.connect(); + System.out.println("Sending: " + bd); + session.sendBlindingInfo(bd); + try { Thread.sleep(1000); } catch (InterruptedException ie) {} + } catch (I2PSessionException ise) { + System.out.println("Send blinding info failed: " + ise); + } finally { + if (session != null) { + try { session.destroySession(); } catch (I2PSessionException ise) {} + } + } + } + /** * Helper method to actually close the given task number (optionally forcing * closure) diff --git a/core/java/src/net/i2p/data/i2cp/BlindingInfoMessage.java b/core/java/src/net/i2p/data/i2cp/BlindingInfoMessage.java index b400a6df32..950dc393a0 100644 --- a/core/java/src/net/i2p/data/i2cp/BlindingInfoMessage.java +++ b/core/java/src/net/i2p/data/i2cp/BlindingInfoMessage.java @@ -337,6 +337,8 @@ public class BlindingInfoMessage extends I2CPMessageImpl { try { _sessionId.writeBytes(os); byte flags = (byte) _authType; + if (_privkey != null) + flags |= FLAG_AUTH; if (_secret != null) flags |= FLAG_SECRET; os.write(flags); @@ -349,10 +351,13 @@ public class BlindingInfoMessage extends I2CPMessageImpl { DataHelper.writeString(os, _host); } else if (_endpointType == TYPE_DEST) { _dest.writeBytes(os); - } else { + } else { // TYPE_KEY + DataHelper.writeLong(os, 2, _pubkey.getType().getCode()); + os.write(_pubkey.getData()); + } + if (_privkey != null) { DataHelper.writeLong(os, 2, _privkey.getType().getCode()); os.write(_privkey.getData()); - DataHelper.writeString(os, _host); } if (_secret != null) DataHelper.writeString(os, _secret);