Changeset ad810de


Ignore:
Timestamp:
Mar 13, 2017 1:48:36 PM (4 years ago)
Author:
zzz <zzz@…>
Branches:
master
Children:
e7cfb2d
Parents:
b57d7c6
Message:

i2ptunnel: Add subsession support to servers, no UI yet
Update subsession javadocs

Files:
8 edited

Legend:

Unmodified
Added
Removed
  • apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelClientBase.java

    rb57d7c6 rad810de  
    314314
    315315    /**
    316      *  Add a subsession to a shared client if necessary.
    317      *
     316     *  Add a DSA_SHA1 subsession to the shared client if necessary.
     317     *
     318     *  @return subsession, or null if none was added
    318319     *  @since 0.9.20
    319320     */
  • apps/i2ptunnel/java/src/net/i2p/i2ptunnel/I2PTunnelServer.java

    rb57d7c6 rad810de  
    1919import java.net.UnknownHostException;
    2020import java.security.GeneralSecurityException;
     21import java.util.List;
    2122import java.util.Map;
    2223import java.util.Properties;
     
    3031
    3132import net.i2p.I2PException;
     33import net.i2p.client.I2PClient;
    3234import net.i2p.client.I2PSession;
    3335import net.i2p.client.I2PSessionException;
     
    3638import net.i2p.client.streaming.I2PSocketManager;
    3739import net.i2p.client.streaming.I2PSocketManagerFactory;
     40import net.i2p.crypto.SigType;
    3841import net.i2p.data.Base64;
    3942import net.i2p.data.Hash;
     
    6871    public static final String PROP_USE_SSL = "useSSL";
    6972    public static final String PROP_UNIQUE_LOCAL = "enableUniqueLocal";
     73    /** @since 0.9.30 */
     74    public static final String PROP_ALT_PKF = "altPrivKeyFile";
    7075    /** apparently unused */
    7176    protected static volatile long __serverId = 0;
     
    218223            rv.setName("I2PTunnel Server");
    219224            getTunnel().addSession(rv.getSession());
     225            String alt = props.getProperty(PROP_ALT_PKF);
     226            if (alt != null)
     227                addSubsession(rv, alt);
    220228            return rv;
    221229        } catch (I2PSessionException ise) {
     
    226234    }
    227235
     236    /**
     237     *  Add a non-DSA_SHA1 subsession to the DSA_SHA1 server if necessary.
     238     *
     239     *  @return subsession, or null if none was added
     240     *  @since 0.9.30
     241     */
     242    private I2PSession addSubsession(I2PSocketManager sMgr, String alt) {
     243        File altFile = TunnelController.filenameToFile(alt);
     244        if (alt == null)
     245            return null;
     246        I2PSession sess = sMgr.getSession();
     247        if (sess.getMyDestination().getSigType() != SigType.DSA_SHA1)
     248            return null;
     249        Properties props = new Properties();
     250        props.putAll(getTunnel().getClientOptions());
     251        // fixme get actual sig type
     252        String name = props.getProperty("inbound.nickname");
     253        if (name != null)
     254            props.setProperty("inbound.nickname", name + " (EdDSA)");
     255        name = props.getProperty("outbound.nickname");
     256        if (name != null)
     257            props.setProperty("outbound.nickname", name + " (EdDSA)");
     258        props.setProperty(I2PClient.PROP_SIGTYPE, "EdDSA_SHA512_Ed25519");
     259        FileInputStream privData = null;
     260        try {
     261            privData = new FileInputStream(altFile);
     262            return sMgr.addSubsession(privData, props);
     263        } catch (IOException ioe) {
     264            _log.error("Failed to add subssession", ioe);
     265            return null;
     266        } catch (I2PSessionException ise) {
     267            _log.error("Failed to add subssession", ise);
     268            return null;
     269        } finally {
     270            if (privData != null) try { privData.close(); } catch (IOException ioe) {}
     271        }
     272    }
     273
    228274
    229275    /**
     
    239285            try {
    240286                sockMgr.getSession().connect();
     287                // Now connect the subsessions, if any
     288                List<I2PSession> subs = sockMgr.getSubsessions();
     289                if (!subs.isEmpty()) {
     290                    for (I2PSession sub : subs) {
     291                        try {
     292                            sub.connect();
     293                            if (_log.shouldInfo())
     294                                _log.info("Connected subsession " + sub);
     295                        } catch (I2PSessionException ise) {
     296                            // not fatal?
     297                            String msg = "Unable to connect subsession " + sub;
     298                            this.l.log(msg);
     299                            _log.error(msg, ise);
     300                        }
     301                    }
     302                }
    241303            } catch (I2PSessionException ise) {
    242304                // try to make this error sensible as it will happen...
  • apps/i2ptunnel/java/src/net/i2p/i2ptunnel/TunnelController.java

    rb57d7c6 rad810de  
    44import java.io.FileOutputStream;
    55import java.io.IOException;
     6import java.security.GeneralSecurityException;
    67import java.util.ArrayList;
    78import java.util.Collection;
     
    1718import net.i2p.client.I2PClientFactory;
    1819import net.i2p.client.I2PSession;
     20import net.i2p.client.I2PSessionException;
     21import net.i2p.crypto.KeyGenerator;
    1922import net.i2p.crypto.SigType;
    2023import net.i2p.data.Destination;
     24import net.i2p.data.KeyCertificate;
     25import net.i2p.data.PrivateKey;
     26import net.i2p.data.PrivateKeyFile;
     27import net.i2p.data.PublicKey;
     28import net.i2p.data.SigningPrivateKey;
     29import net.i2p.data.SigningPublicKey;
     30import net.i2p.data.SimpleDataStructure;
    2131import net.i2p.i2ptunnel.socks.I2PSOCKSTunnel;
    2232import net.i2p.util.FileUtil;
    2333import net.i2p.util.I2PAppThread;
    2434import net.i2p.util.Log;
     35import net.i2p.util.RandomSource;
    2536import net.i2p.util.SecureFile;
    2637import net.i2p.util.SecureFileOutputStream;
     
    8495    private static final String OPT_LOW_TAGS = PFX_OPTION + "crypto.lowTagThreshold";
    8596    private static final String OPT_SIG_TYPE = PFX_OPTION + I2PClient.PROP_SIGTYPE;
     97    /** @since 0.9.30 */
     98    private static final String OPT_ALT_PKF = PFX_OPTION + I2PTunnelServer.PROP_ALT_PKF;
    8699
    87100    /** all of these @since 0.9.14 */
     
    107120    public static final SigType PREFERRED_SIGTYPE;
    108121    static {
    109         if (SystemVersion.isARM() || SystemVersion.isGNU() || SystemVersion.isAndroid()) {
     122        if (SystemVersion.isGNU() || SystemVersion.isAndroid()) {
    110123            if (SigType.ECDSA_SHA256_P256.isAvailable())
    111124                PREFERRED_SIGTYPE = SigType.ECDSA_SHA256_P256;
     
    147160        _messages = new ArrayList<String>(4);
    148161        boolean keyOK = true;
    149         if (createKey && (getType().endsWith("server") || getPersistentClientKey()))
     162        if (createKey && (getType().endsWith("server") || getPersistentClientKey())) {
    150163            keyOK = createPrivateKey();
     164            if (keyOK && getType().endsWith("server") && !getType().equals(TYPE_STREAMR_SERVER)) {
     165                // check rv?
     166                createAltPrivateKey();
     167            }
     168        }
    151169        _state = keyOK && getStartOnLoad() ? TunnelState.START_ON_LOAD : TunnelState.STOPPED;
    152170    }
     
    187205            log("Private key created and saved in " + keyFile.getAbsolutePath());
    188206            log("You should backup this file in a secure place.");
    189             log("New destination: " + destStr);
     207            log("New alternate destination: " + destStr);
    190208            String b32 = dest.toBase32();
    191209            log("Base32: " + b32);
     
    213231        }
    214232        return true;
     233    }
     234   
     235    /**
     236     * Creates alternate Destination with the same encryption keys as the primary Destination,
     237     * but a different signing key.
     238     *
     239     * Must have already called createPrivateKey() successfully.
     240     * Does nothing unless option OPT_ALT_PKF is set with the privkey file name.
     241     * Does nothing if the file already exists.
     242     *
     243     * @return success
     244     * @since 0.9.30
     245     */
     246    private boolean createAltPrivateKey() {
     247        if (PREFERRED_SIGTYPE == SigType.DSA_SHA1)
     248            return false;
     249        File keyFile = getPrivateKeyFile();
     250        if (keyFile == null)
     251            return false;
     252        if (!keyFile.exists())
     253            return false;
     254        File altFile = getAlternatePrivateKeyFile();
     255        if (altFile == null)
     256            return false;
     257        if (altFile.exists())
     258            return true;
     259        PrivateKeyFile pkf = new PrivateKeyFile(keyFile);
     260        FileOutputStream out = null;
     261        try {
     262            Destination dest = pkf.getDestination();
     263            if (dest == null)
     264                return false;
     265            if (dest.getSigType() != SigType.DSA_SHA1)
     266                return false;
     267            PublicKey pub = dest.getPublicKey();
     268            PrivateKey priv = pkf.getPrivKey();
     269            SimpleDataStructure[] signingKeys = KeyGenerator.getInstance().generateSigningKeys(PREFERRED_SIGTYPE);
     270            SigningPublicKey signingPubKey = (SigningPublicKey) signingKeys[0];
     271            SigningPrivateKey signingPrivKey = (SigningPrivateKey) signingKeys[1];
     272            KeyCertificate cert = new KeyCertificate(signingPubKey);
     273            Destination d = new Destination();
     274            d.setPublicKey(pub);
     275            d.setSigningPublicKey(signingPubKey);
     276            d.setCertificate(cert);
     277            int len = signingPubKey.length();
     278            if (len < 128) {
     279                byte[] pad = new byte[128 - len];
     280                RandomSource.getInstance().nextBytes(pad);
     281                d.setPadding(pad);
     282            } else if (len > 128) {
     283                // copy of excess data handled in KeyCertificate constructor
     284            }
     285       
     286            out = new SecureFileOutputStream(altFile);
     287            d.writeBytes(out);
     288            priv.writeBytes(out);
     289            signingPrivKey.writeBytes(out);
     290            try { out.close(); } catch (IOException ioe) {}
     291
     292            String destStr = d.toBase64();
     293            log("Alternate private key created and saved in " + altFile.getAbsolutePath());
     294            log("You should backup this file in a secure place.");
     295            log("New destination: " + destStr);
     296            String b32 = d.toBase32();
     297            log("Base32: " + b32);
     298            File backupDir = new SecureFile(I2PAppContext.getGlobalContext().getConfigDir(), KEY_BACKUP_DIR);
     299            if (backupDir.isDirectory() || backupDir.mkdir()) {
     300                String name = b32 + '-' + I2PAppContext.getGlobalContext().clock().now() + ".dat";
     301                File backup = new File(backupDir, name);
     302                if (FileUtil.copy(altFile, backup, false, true)) {
     303                    SecureFileOutputStream.setPerms(backup);
     304                    log("Alternate private key backup saved to " + backup.getAbsolutePath());
     305                }
     306            }
     307            return true;
     308        } catch (GeneralSecurityException e) {
     309            log("Error creating keys " + e);
     310            return false;
     311        } catch (I2PSessionException e) {
     312            log("Error creating keys " + e);
     313            return false;
     314        } catch (I2PException e) {
     315            log("Error creating keys " + e);
     316            return false;
     317        } catch (IOException e) {
     318            log("Error creating keys " + e);
     319            return false;
     320        } catch (RuntimeException e) {
     321            log("Error creating keys " + e);
     322            return false;
     323        } finally {
     324            if (out != null) try { out.close(); } catch (IOException ioe) {}
     325        }
    215326    }
    216327   
     
    798909     */
    799910    public File getPrivateKeyFile() {
    800         String f = getPrivKeyFile();
     911        return filenameToFile(getPrivKeyFile());
     912    }
     913
     914    /**
     915     *  Does not necessarily exist.
     916     *  @return absolute path or null if unset
     917     *  @since 0.9.30
     918     */
     919    public File getAlternatePrivateKeyFile() {
     920        return filenameToFile(_config.getProperty(OPT_ALT_PKF));
     921    }
     922
     923    /**
     924     *  Does not necessarily exist.
     925     *  @param f relative or absolute path, may be null
     926     *  @return absolute path or null
     927     *  @since 0.9.30
     928     */
     929    static File filenameToFile(String f) {
    801930        if (f == null)
    802931            return null;
  • apps/ministreaming/java/src/net/i2p/client/streaming/I2PSocketManager.java

    rb57d7c6 rad810de  
    3939   
    4040    /**
     41     *  For a server, you must call connect() on the returned object.
     42     *  Connecting the primary session does NOT connect any subsessions.
     43     *  If the primary session is not connected, connecting a subsession will connect the primary session first.
     44     *
    4145     *  @return a new subsession, non-null
    4246     *  @param privateKeyStream null for transient, if non-null must have same encryption keys as primary session
  • apps/streaming/java/src/net/i2p/client/streaming/impl/I2PSocketManagerFull.java

    rb57d7c6 rad810de  
    237237   
    238238    /**
     239     *  For a server, you must call connect() on the returned object.
     240     *  Connecting the primary session does NOT connect any subsessions.
     241     *  If the primary session is not connected, connecting a subsession will connect the primary session first.
     242     *
    239243     *  @return a new subsession, non-null
    240244     *  @param privateKeyStream null for transient, if non-null must have same encryption keys as primary session
  • core/java/src/net/i2p/client/I2PSession.java

    rb57d7c6 rad810de  
    273273
    274274    /**
    275      * Actually connect the session and start receiving/sending messages
    276      *
     275     * Actually connect the session and start receiving/sending messages.
     276     * Connecting a primary session will not automatically connect subsessions.
     277     * Connecting a subsession will automatically connect the primary session
     278     * if not previously connected.
    277279     */
    278280    public void connect() throws I2PSessionException;
  • core/java/src/net/i2p/client/impl/I2PSessionImpl.java

    rb57d7c6 rad810de  
    548548     * Disconnect / destroy from another thread may be called simultaneously and
    549549     * will (should?) interrupt the connect.
     550     *
     551     * Connecting a primary session will not automatically connect subsessions.
     552     * Connecting a subsession will automatically connect the primary session
     553     * if not previously connected.
    550554     *
    551555     * @throws I2PSessionException if there is a configuration error or the router is
  • core/java/src/net/i2p/client/impl/SubSession.java

    rb57d7c6 rad810de  
    9393     * Disconnect / destroy from another thread may be called simultaneously and
    9494     * will (should?) interrupt the connect.
     95     *
     96     * Connecting a subsession will automatically connect the primary session
     97     * if not previously connected.
    9598     *
    9699     * @throws I2PSessionException if there is a configuration error or the router is
Note: See TracChangeset for help on using the changeset viewer.