Changeset 0e2a422


Ignore:
Timestamp:
Jan 20, 2009 5:16:24 PM (12 years ago)
Author:
zzz <zzz@…>
Branches:
master
Children:
ab92206
Parents:
8d891b99
Message:
File:
1 edited

Legend:

Unmodified
Added
Removed
  • core/java/src/net/i2p/data/LeaseSet.java

    r8d891b99 r0e2a422  
    1010 */
    1111
     12import java.io.ByteArrayInputStream;
    1213import java.io.ByteArrayOutputStream;
    1314import java.io.IOException;
     
    1819import java.util.List;
    1920
     21import net.i2p.I2PAppContext;
    2022import net.i2p.crypto.DSAEngine;
    2123import net.i2p.util.Clock;
    2224import net.i2p.util.Log;
     25import net.i2p.util.RandomSource;
    2326
    2427/**
    2528 * Defines the set of leases a destination currently has.
     29 *
     30 * Support encryption and decryption with a supplied key.
     31 * Only the gateways and tunnel IDs in the individual
     32 * leases are encrypted.
     33 *
     34 * Encrypted leases are not indicated as such.
     35 * The only way to tell a lease is encrypted is to
     36 * determine that the listed gateways do not exist.
     37 * Routers wishing to decrypt a leaseset must have the
     38 * desthash and key in their keyring.
     39 * This is required for the local router as well, since
     40 * the encryption is done on the client side of I2CP, the
     41 * router must decrypt it back again for local usage
     42 * (but not for transmission to the floodfills)
     43 *
     44 * Decrypted leases are only available through the getLease()
     45 * method, so that storage and network transmission via
     46 * writeBytes() will output the original encrypted
     47 * leases and the original leaseset signature.
    2648 *
    2749 * @author jrandom
     
    4163    private long _firstExpiration;
    4264    private long _lastExpiration;
     65    private List _decryptedLeases;
     66    private boolean _decrypted;
     67    private boolean _checked;
    4368
    4469    /** This seems like plenty  */
     
    5681        _firstExpiration = Long.MAX_VALUE;
    5782        _lastExpiration = 0;
     83        _decrypted = false;
     84        _checked = false;
    5885    }
    5986
     
    105132
    106133    public int getLeaseCount() {
    107         return _leases.size();
     134        if (isEncrypted())
     135            return _leases.size() - 1;
     136        else
     137            return _leases.size();
    108138    }
    109139
    110140    public Lease getLease(int index) {
    111         return (Lease) _leases.get(index);
     141        if (isEncrypted())
     142            return (Lease) _decryptedLeases.get(index);
     143        else
     144            return (Lease) _leases.get(index);
    112145    }
    113146
     
    336369        return buf.toString();
    337370    }
     371
     372    private static final int DATA_LEN = Hash.HASH_LENGTH + 4;
     373    private static final int IV_LEN = 16;
     374
     375    /**
     376     *  Encrypt the gateway and tunnel ID of each lease, leaving the expire dates unchanged.
     377     *  This adds an extra dummy lease, because AES data must be padded to 16 bytes.
     378     *  The fact that it is encrypted is not stored anywhere.
     379     *  Must be called after all the leases are in place, but before sign().
     380     */
     381    public void encrypt(SessionKey key) {
     382        if (_log.shouldLog(Log.WARN))
     383            _log.warn("encrypting lease: " + _destination.calculateHash());
     384        try {
     385            encryp(key);
     386        } catch (DataFormatException dfe) {
     387            _log.error("Error encrypting lease: " + _destination.calculateHash());
     388        } catch (IOException ioe) {
     389            _log.error("Error encrypting lease: " + _destination.calculateHash());
     390        }
     391    }
     392
     393    /**
     394     *  - Put the {Gateway Hash, TunnelID} pairs for all the leases in a buffer
     395     *  - Pad with random data to a multiple of 16 bytes
     396     *  - Use the first part of the dest's public key as an IV
     397     *  - Encrypt
     398     *  - Pad with random data to a multiple of 36 bytes
     399     *  - Add an extra lease
     400     *  - Replace the Hash and TunnelID in each Lease
     401     */
     402    private void encryp(SessionKey key) throws DataFormatException, IOException {
     403        int size = _leases.size();
     404        if (size < 1 || size > MAX_LEASES-1)
     405            throw new IllegalArgumentException("Bad number of leases for encryption");
     406        int datalen = ((DATA_LEN * size / 16) + 1) * 16;
     407        ByteArrayOutputStream baos = new ByteArrayOutputStream(datalen);
     408        for (int i = 0; i < size; i++) {
     409            ((Lease)_leases.get(i)).getGateway().writeBytes(baos);
     410            ((Lease)_leases.get(i)).getTunnelId().writeBytes(baos);
     411        }
     412        // pad out to multiple of 16 with random data before encryption
     413        int padlen = datalen - (DATA_LEN * size);
     414        byte[] pad = new byte[padlen];
     415        RandomSource.getInstance().nextBytes(pad);
     416        baos.write(pad);
     417        byte[] iv = new byte[IV_LEN];
     418        System.arraycopy(_destination.getPublicKey().getData(), 0, iv, 0, IV_LEN);
     419        byte[] enc = new byte[DATA_LEN * (size + 1)];
     420        I2PAppContext.getGlobalContext().aes().encrypt(baos.toByteArray(), 0, enc, 0, key, iv, datalen);
     421        // pad out to multiple of 36 with random data after encryption
     422        // (even for 4 leases, where 36*4 is a multiple of 16, we add another, just to be consistent)
     423        padlen = enc.length - datalen;
     424        pad = new byte[padlen];
     425        RandomSource.getInstance().nextBytes(pad);
     426        System.arraycopy(pad, 0, enc, datalen, padlen);
     427        // add the padded lease...
     428        Lease padLease = new Lease();
     429        padLease.setEndDate(((Lease)_leases.get(0)).getEndDate());
     430        _leases.add(padLease);
     431        // ...and replace all the gateways and tunnel ids
     432        ByteArrayInputStream bais = new ByteArrayInputStream(enc);
     433        for (int i = 0; i < size+1; i++) {
     434            Hash h = new Hash();
     435            h.readBytes(bais);
     436            ((Lease)_leases.get(i)).setGateway(h);
     437            TunnelId t = new TunnelId();
     438            t.readBytes(bais);
     439            ((Lease)_leases.get(i)).setTunnelId(t);
     440        }
     441    }
     442
     443    /**
     444     *  Decrypt the leases, except for the last one which is partially padding.
     445     *  Store the new decrypted leases in a backing store,
     446     *  and keep the original leases so that verify() still works and the
     447     *  encrypted leaseset can be sent on to others (via writeBytes())
     448     */
     449    private void decrypt(SessionKey key) throws DataFormatException, IOException {
     450        if (_log.shouldLog(Log.WARN))
     451            _log.warn("decrypting lease: " + _destination.calculateHash());
     452        int size = _leases.size();
     453        if (size < 2)
     454            throw new DataFormatException("Bad number of leases for decryption");
     455        int datalen = DATA_LEN * size;
     456        ByteArrayOutputStream baos = new ByteArrayOutputStream(datalen);
     457        for (int i = 0; i < size; i++) {
     458            ((Lease)_leases.get(i)).getGateway().writeBytes(baos);
     459            ((Lease)_leases.get(i)).getTunnelId().writeBytes(baos);
     460        }
     461        byte[] iv = new byte[IV_LEN];
     462        System.arraycopy(_destination.getPublicKey().getData(), 0, iv, 0, IV_LEN);
     463        int enclen = ((DATA_LEN * (size - 1) / 16) + 1) * 16;
     464        byte[] enc = new byte[enclen];
     465        System.arraycopy(baos.toByteArray(), 0, enc, 0, enclen);
     466        byte[] dec = new byte[enclen];
     467        I2PAppContext.getGlobalContext().aes().decrypt(enc, 0, dec, 0, key, iv, enclen);
     468        ByteArrayInputStream bais = new ByteArrayInputStream(dec);
     469        _decryptedLeases = new ArrayList(size - 1);
     470        for (int i = 0; i < size-1; i++) {
     471            Lease l = new Lease();
     472            Hash h = new Hash();
     473            h.readBytes(bais);
     474            l.setGateway(h);
     475            TunnelId t = new TunnelId();
     476            t.readBytes(bais);
     477            l.setTunnelId(t);
     478            l.setEndDate(((Lease)_leases.get(i)).getEndDate());
     479            _decryptedLeases.add(l);
     480        }
     481    }
     482
     483    /**
     484     * @return true if it was encrypted, and we decrypted it successfully.
     485     * Decrypts on first call.
     486     */
     487    private synchronized boolean isEncrypted() {
     488        if (_decrypted)
     489           return true;
     490        if (_checked || _destination == null)
     491           return false;
     492        SessionKey key = I2PAppContext.getGlobalContext().keyRing().get(_destination.calculateHash());
     493        if (key != null) {
     494            try {
     495                decrypt(key);
     496                _decrypted = true;
     497            } catch (DataFormatException dfe) {
     498                _log.error("Error decrypting lease: " + _destination.calculateHash() + dfe);
     499            } catch (IOException ioe) {
     500                _log.error("Error decrypting lease: " + _destination.calculateHash() + ioe);
     501            }
     502        }
     503        _checked = true;
     504        return _decrypted;
     505    }
    338506}
Note: See TracChangeset for help on using the changeset viewer.