From 354df6d333ffb7b69e92117406c8ce8d61ea09e0 Mon Sep 17 00:00:00 2001 From: SoniEx2 Date: Sun, 24 Jul 2022 21:42:55 -0300 Subject: Prepare for improved SAM auth and World Codes --- .../ganarchy/friendcode/sam/I2PSamControl.java | 102 ++++++++++++++++----- 1 file changed, 81 insertions(+), 21 deletions(-) (limited to 'src/main/java/ganarchy/friendcode/sam/I2PSamControl.java') diff --git a/src/main/java/ganarchy/friendcode/sam/I2PSamControl.java b/src/main/java/ganarchy/friendcode/sam/I2PSamControl.java index f470bb0..693cba3 100644 --- a/src/main/java/ganarchy/friendcode/sam/I2PSamControl.java +++ b/src/main/java/ganarchy/friendcode/sam/I2PSamControl.java @@ -10,11 +10,27 @@ import java.net.Socket; public class I2PSamControl extends I2PSamStateMachine { private final boolean zeroHop; - private String pubkey; + /** + * The session's private key. + * + * The threat model assumes the local machine (including RAM) to be trusted. + */ + private String privateKey; + /** + * The session's public key. + */ + private String publicKey; private String id; - public I2PSamControl(boolean zeroHop) { + /** + * Creates a new SAM control socket. + * + * @param zeroHop Whether to use zero-hop tunnels. + * @param privateKey The (optional) identity private key, for persistent friend codes. + */ + public I2PSamControl(boolean zeroHop, String privateKey) { this.zeroHop = zeroHop; + this.privateKey = privateKey; } @Override @@ -44,15 +60,16 @@ public class I2PSamControl extends I2PSamStateMachine { try { // try to enable auth this.enableAuth(); - // FIXME "Friend Code Type: World" vs "Friend Code Type: Session" - // generate our keys - this.sendCommand(new I2PSamCommand( - "DEST", "GENERATE", - ImmutableMap.of("SIGNATURE_TYPE", "EdDSA_SHA512_Ed25519") - )); - var dest = this.getCommand("DEST", "REPLY"); - this.pubkey = dest.parameters().get("PUB"); - var privkey = dest.parameters().get("PRIV"); + if (this.privateKey == null) { + // generate our keys + this.sendCommand(new I2PSamCommand( + "DEST", "GENERATE", + ImmutableMap.of("SIGNATURE_TYPE", "EdDSA_SHA512_Ed25519") + )); + var dest = this.getCommand("DEST", "REPLY"); + this.publicKey = dest.parameters().get("PUB"); + this.privateKey = dest.parameters().get("PRIV"); + } // setup our session int i = 0; String status; @@ -70,7 +87,7 @@ public class I2PSamControl extends I2PSamStateMachine { ).put( "ID", this.id ).put( - "DESTINATION", privkey + "DESTINATION", this.privateKey ).put( "inbound.length", this.zeroHop ? "0" : "1" ).put( @@ -88,7 +105,19 @@ public class I2PSamControl extends I2PSamStateMachine { ).build() )); } while ("DUPLICATED_ID".equals(status = this.getCommand("SESSION", "STATUS").parameters().get("RESULT"))); - return "OK".equals(status); + if ("OK".equals(status)) { + // find our public key (if we're using persistent keys) + if (this.publicKey == null) { + this.sendCommand(new I2PSamCommand("NAMING", "LOOKUP", ImmutableMap.of("NAME", "ME"))); + final var lookup = this.getCommand("NAMING", "REPLY"); + if (!"OK".equals(lookup.parameters().get("RESULT"))) { + return false; + } + this.publicKey = lookup.parameters().get("VALUE"); + } + return true; + } + return false; } catch (IOException e) { return false; } @@ -111,10 +140,17 @@ public class I2PSamControl extends I2PSamStateMachine { } /** - * Returns the session pubkey. + * Returns the session public key. */ - public String pubkey() { - return this.pubkey; + public String publicKey() { + return this.publicKey; + } + + /** + * Returs the session private key. + */ + public String privateKey() { + return this.privateKey; } /** @@ -123,15 +159,39 @@ public class I2PSamControl extends I2PSamStateMachine { * @throws IOException If an I/O error occurs. */ private void enableAuth() throws IOException { - // enable auth (if it hasn't been explicitly disabled by the user), for the user's sake + // enable auth (if it hasn't been explicitly disabled by the user), for + // the user's sake // (ugh i2p you really fucked up by not making this the default) - // btw tor does this with a filesystem path to an auth cookie (which is much more secure) but we digress + // btw tor does this with a filesystem path to an auth cookie (which is + // much more secure) but we digress. + // check if we're already using strong auth. + if (I2PSamAuthUtil.isStrongAuth()) { + return; + } + // attempt to delete old user - versions 1.0.0 and 1.0.1 of the mod are + // deprecated, unsupported, and should never be used. + this.sendCommand(new I2PSamCommand( + "AUTH", "REMOVE", + ImmutableMap.of("USER", "minecraft_friendcode") + )); + // check if removed. + var removed = "OK".equals( + this.getCommand("AUTH", "STATUS").parameters().get("RESULT") + ); + // generate secure auth pair + var auth = I2PSamAuthUtil.upgradeAuthPair(); + // add new user this.sendCommand(new I2PSamCommand( "AUTH", "ADD", - ImmutableMap.of("USER", "minecraft_friendcode", "PASSWORD", "friendcode") + ImmutableMap.of("USER", auth.user(), "PASSWORD", auth.password()) )); - // if the user already exists, don't enable auth - if ("OK".equals(this.getCommand("AUTH", "STATUS").parameters().get("RESULT"))) { + if ("OK".equals( + this.getCommand("AUTH", "STATUS").parameters().get("RESULT") + )) { + // old user existed - don't mess with auth enable. + if (removed) { + return; + } this.sendCommand(new I2PSamCommand("AUTH", "ENABLE")); // ignore the response on this one, just get it off the queue this.getCommand("AUTH", "STATUS"); -- cgit 1.4.1