package net.i2p.router.crypto;
/*
 * free (adj.): unencumbered; not under the control of others
 * Written by jrandom in 2003 and released into the public domain
 * with no warranty of any kind, either expressed or implied.
 * It probably won't  make your computer catch on fire, or eat
 * your children, but it might.  Use at your own risk.
 *
 */

import java.util.HashSet;
import java.util.Set;

import junit.framework.TestCase;
import net.i2p.I2PAppContext;
import net.i2p.crypto.KeyGenerator;
import net.i2p.crypto.SessionKeyManager;
import net.i2p.crypto.TagSetHandle;
import net.i2p.data.DataHelper;
import net.i2p.data.PrivateKey;
import net.i2p.data.PublicKey;
import net.i2p.data.SessionKey;
import net.i2p.data.SessionTag;
import net.i2p.router.crypto.ElGamalAESEngine;

/**
 *
 * session key management unit tests:
 *
 */
public class SessionEncryptionTest extends TestCase{
    private I2PAppContext _context;
    
    protected void setUp(){
        _context = I2PAppContext.getGlobalContext();
    }
    
    protected void tearDown() {
        System.gc();
    }
    
    
    public void testNoSessions1() throws Exception{
        Object keys[] = KeyGenerator.getInstance().generatePKIKeypair();
        PublicKey pubKey = (PublicKey)keys[0];
        PrivateKey privKey = (PrivateKey)keys[1];
        SessionKeyManager skm = new TransientSessionKeyManager(_context);
        SessionKey curKey = skm.createSession(pubKey);
        
        byte[] msg = DataHelper.getASCII("msg 1");
        
        ElGamalAESEngine e = new ElGamalAESEngine(_context);
        byte emsg[] = e.encrypt(msg, pubKey, curKey, null, null, 64);
        byte dmsg[] = e.decrypt(emsg, privKey, skm);
        assertTrue(DataHelper.eq(dmsg, msg));
    }
    
    public void testNoSessions2() throws Exception{
        Object keys[] = KeyGenerator.getInstance().generatePKIKeypair();
        PublicKey pubKey = (PublicKey)keys[0];
        PrivateKey privKey = (PrivateKey)keys[1];
        SessionKeyManager skm = new TransientSessionKeyManager(_context);
        SessionKey curKey = skm.createSession(pubKey);
        
        byte[] msg = DataHelper.getASCII("msg 2");
        
        ElGamalAESEngine e = new ElGamalAESEngine(_context);
        byte emsg[] = e.encrypt(msg, pubKey, curKey, null, null, 64);
        byte dmsg[] = e.decrypt(emsg, privKey, skm);
        assertTrue(DataHelper.eq(dmsg, msg));
    }
    
    /**
     *  Run     tagsIncluded    useTag  rekey
     *  1       yes (2)         no      no
     *  2       no              yes     no
     *  3       yes (2)         yes     no
     *  4       no              yes     no
     *  5       no              yes     no
     */
    public void testSessions() throws Exception{
        Object keys[] = KeyGenerator.getInstance().generatePKIKeypair();
        PublicKey pubKey = (PublicKey)keys[0];
        PrivateKey privKey = (PrivateKey)keys[1];
        SessionKeyManager skm = new TransientSessionKeyManager(_context);
        SessionKey curKey = skm.createSession(pubKey);
        
        SessionTag tag1 = new SessionTag(true);
        SessionTag tag2 = new SessionTag(true);
        SessionTag tag3 = new SessionTag(true);
        SessionTag tag4 = new SessionTag(true);
        
        HashSet<SessionTag> firstTags = new HashSet<SessionTag>();
        firstTags.add(tag1);
        firstTags.add(tag2);
        
        HashSet<SessionTag> secondTags = new HashSet<SessionTag>();
        secondTags.add(tag3);
        secondTags.add(tag4);
        
        byte[] msg1 = DataHelper.getASCII("msg 1");
        byte[] msg2 = DataHelper.getASCII("msg 2");
        byte[] msg3 = DataHelper.getASCII("msg 3");
        byte[] msg4 = DataHelper.getASCII("msg 4");
        byte[] msg5 = DataHelper.getASCII("msg 5");
        
        ElGamalAESEngine e = new ElGamalAESEngine(_context);
        byte emsg1[] = e.encrypt(msg1, pubKey, curKey, firstTags, null, 64);
        
        byte dmsg1[] = e.decrypt(emsg1, privKey, skm);
        assertTrue(DataHelper.eq(dmsg1, msg1));
        
        
        
        TagSetHandle tsh = skm.tagsDelivered(pubKey, curKey, firstTags);
        skm.tagsAcked(pubKey, curKey, tsh);
        
        curKey = skm.getCurrentKey(pubKey);
        SessionTag curTag = skm.consumeNextAvailableTag(pubKey, curKey);
        
        assertNotNull(curTag);
        
        byte emsg2[] = e.encrypt(msg2, pubKey, curKey, null, curTag, 64);
        
        byte dmsg2[] = e.decrypt(emsg2, privKey, skm);
        assertTrue(DataHelper.eq(dmsg2, msg2));
        
        
        
        
        curKey = skm.getCurrentKey(pubKey);
        curTag = skm.consumeNextAvailableTag(pubKey, curKey);
        
        assertNotNull(curTag);
        assertNotNull(curKey);
        
        byte emsg3[] = e.encrypt(msg3, pubKey, curKey, secondTags, curTag, 64);
        
        byte dmsg3[] = e.decrypt(emsg3, privKey, skm);
        assertTrue(DataHelper.eq(dmsg3, msg3));
        
        
        
        tsh = skm.tagsDelivered(pubKey, curKey, secondTags);
        skm.tagsAcked(pubKey, curKey, tsh);
        
        curKey = skm.getCurrentKey(pubKey);
        curTag = skm.consumeNextAvailableTag(pubKey, curKey);
        
        assertNotNull(curTag);
        assertNotNull(curKey);
        
        byte emsg4[] = e.encrypt(msg4, pubKey, curKey, null, curTag, 64);
        
        byte dmsg4[] = e.decrypt(emsg4, privKey, skm);
        assertTrue(DataHelper.eq(dmsg4, msg4));
        
        
        curKey = skm.getCurrentKey(pubKey);
        curTag = skm.consumeNextAvailableTag(pubKey, curKey);
        
        assertNotNull(curTag);
        assertNotNull(curKey);
        
        byte emsg5[] = e.encrypt(msg5, pubKey, curKey, null, curTag, 64);
        
        byte dmsg5[] = e.decrypt(emsg5, privKey, skm);
        assertTrue(DataHelper.eq(dmsg5, msg5));
        
        
    }
    
    /**
     *  Run tagsIncluded    useTag  rekey
     *  1   yes (2)         no      no
     *  2   no              yes     no
     *  3   yes (2)         yes     yes
     *  4   no              yes     no
     *  5   no              yes     no
     */
    public void testRekeying() throws Exception{
        Object keys[] = KeyGenerator.getInstance().generatePKIKeypair();
        PublicKey pubKey = (PublicKey)keys[0];
        PrivateKey privKey = (PrivateKey)keys[1];
        SessionKeyManager skm = new TransientSessionKeyManager(_context);
        SessionKey curKey = skm.createSession(pubKey);
        SessionKey nextKey = KeyGenerator.getInstance().generateSessionKey();
        
        SessionTag tag1 = new SessionTag(true);
        SessionTag tag2 = new SessionTag(true);
        SessionTag tag3 = new SessionTag(true);
        SessionTag tag4 = new SessionTag(true);
        
        HashSet<SessionTag> firstTags = new HashSet<SessionTag>();
        firstTags.add(tag1);
        firstTags.add(tag2);
        
        HashSet<SessionTag> secondTags = new HashSet<SessionTag>();
        secondTags.add(tag3);
        secondTags.add(tag4);
        
        byte[] msg1 = DataHelper.getASCII("msg 1");
        byte[] msg2 = DataHelper.getASCII("msg 2");
        byte[] msg3 = DataHelper.getASCII("msg 3");
        byte[] msg4 = DataHelper.getASCII("msg 4");
        byte[] msg5 = DataHelper.getASCII("msg 5");
        
        ElGamalAESEngine e = new ElGamalAESEngine(_context);
        byte emsg1[] = e.encrypt(msg1, pubKey, curKey, firstTags, null, 64);
        
        byte dmsg1[] = e.decrypt(emsg1, privKey, skm);
        assertTrue(DataHelper.eq(dmsg1, msg1));
        
        
        
        TagSetHandle tsh = skm.tagsDelivered(pubKey, curKey, firstTags);
        skm.tagsAcked(pubKey, curKey, tsh);
        
        curKey = skm.getCurrentKey(pubKey);
        SessionTag curTag = skm.consumeNextAvailableTag(pubKey, curKey);
        
        assertNotNull(curTag);
        
        byte emsg2[] = e.encrypt(msg2, pubKey, curKey, null, curTag, 64);
        
        byte dmsg2[] = e.decrypt(emsg2, privKey, skm);
        assertTrue(DataHelper.eq(dmsg2, msg2));
        
        
        
        curKey = skm.getCurrentKey(pubKey);
        curTag = skm.consumeNextAvailableTag(pubKey, curKey);
        
        assertNotNull(curTag);
        assertNotNull(curKey);
        
        byte emsg3[] = e.encrypt(msg3, pubKey, curKey, secondTags, curTag, nextKey, 64);
        
        byte dmsg3[] = e.decrypt(emsg3, privKey, skm);
        assertTrue(DataHelper.eq(dmsg3, msg3));
        
        
        
        tsh = skm.tagsDelivered(pubKey, nextKey, secondTags); // note nextKey not curKey
        skm.tagsAcked(pubKey, nextKey, tsh);
        
        curKey = skm.getCurrentKey(pubKey);
        curTag = skm.consumeNextAvailableTag(pubKey, curKey);
        
        assertNotNull(curTag);
        assertNotNull(curKey);
        
        byte emsg4[] = e.encrypt(msg4, pubKey, curKey, null, curTag, 64);
        
        byte dmsg4[] = e.decrypt(emsg4, privKey, skm);
        assertTrue(DataHelper.eq(dmsg4, msg4));
        
        
        
        curKey = skm.getCurrentKey(pubKey);
        curTag = skm.consumeNextAvailableTag(pubKey, curKey);
        
        assertNotNull(curTag);
        assertNotNull(curKey);
        
        byte emsg5[] = e.encrypt(msg5, pubKey, curKey, null, curTag, 64);
        
        byte dmsg5[] = e.decrypt(emsg5, privKey, skm);
        assertTrue(DataHelper.eq(dmsg5, msg5));
        
        
        
    }
    
    
    /**
     *  20 tags every 10 messages, rekey every 50
     */
    public void testLongSession() throws Exception{
        Object keys[] = KeyGenerator.getInstance().generatePKIKeypair();
        PublicKey pubKey = (PublicKey)keys[0];
        PrivateKey privKey = (PrivateKey)keys[1];
        SessionKeyManager skm = new TransientSessionKeyManager(_context);
        SessionKey curKey = skm.createSession(pubKey);
        
        ElGamalAESEngine e = new ElGamalAESEngine(_context);
        for (int i = 0; i < 1000; i++) {
            Set<SessionTag> tags = null;
            SessionKey nextKey = null;
            curKey = skm.getCurrentKey(pubKey);
            SessionTag curTag = skm.consumeNextAvailableTag(pubKey, curKey);
            
            int availTags = skm.getAvailableTags(pubKey, curKey);
            if ((availTags < 1)) {
                tags = generateNewTags(50);
            } 
            if (i % 50 == 0)
                nextKey = KeyGenerator.getInstance().generateSessionKey();
            
            byte[] msg = DataHelper.getASCII("msg " + i);
            
            byte emsg[] = e.encrypt(msg, pubKey, curKey, tags, curTag, nextKey, 64);
            
            byte dmsg[] = e.decrypt(emsg, privKey, skm);
            assertTrue(DataHelper.eq(dmsg, msg));
            
            if ( (tags != null) && (tags.size() > 0) ) {
                if (nextKey == null) {
                    TagSetHandle tsh = skm.tagsDelivered(pubKey, curKey, tags);
                    skm.tagsAcked(pubKey, curKey, tsh);
                } else {
                    TagSetHandle tsh = skm.tagsDelivered(pubKey, nextKey, tags);
                    skm.tagsAcked(pubKey, nextKey, tsh);
                }
            }
        }
    }
    
    private Set<SessionTag> generateNewTags(int numTags) {
        Set<SessionTag> tags = new HashSet<SessionTag>(numTags);
        for (int i = 0; i < numTags; i++)
            tags.add(new SessionTag(true));
        return tags;
    }
}
