function test() {
}

var observer = new function() {
    this.observe = function(a, b, c) {
        print('observed', a, b, c);
    }
};

function testTimer() {
    var timer = XPO('timer;1', XPI.nsITimer);
    timer.init(observer, 1000, XPI.nsITimer.TYPE_REPEATING_SLACK);
}

var runnable = new function() {
    this.run = function() {
        var hasher = XPO('@passpet.org/iterated-hash;1', XPI.IPasspetHash);
        hasher.data = 'hello';
        for (var i = 0; i < 20; i++) {
            hasher.run(-1, 1000);
        }
    }
}

function testThreads() {
    var thread = XPO('thread;1', XPI.nsIThread);
    thread.init(runnable, 1024, XPI.nsIThread.PRIORITY_NORMAL,
                XPI.nsIThread.SCOPE_LOCAL, XPI.nsIThread.STATE_UNJOINABLE);
}

function notify(label) {
    var message = label;
    if (arguments.length > 1) message += ': ';
    for (var i = 1; i < arguments.length; i++) {
        if (i > 1) message += ', ';
        message += arguments[i];
    }
    alert(message);
}

function testEntropy() {
    print('\ntestEntropy');
    var ee = XPO('@passpet.org/entropy-estimator;1', XPI.IPasspetEntropy);
    print(ee);
    print('abc', ee.getPasswordEntropyBits('abc'));
    print('secret', ee.getPasswordEntropyBits('secret'));
    print('2938ghf2', ee.getPasswordEntropyBits('2938ghf2'));
}

// Convert a string of bytes to a string of hex digits.
function strToHex(bytes) {
    if (bytes.length > 100) return '[too long]';
    var hex = '', digits = '0123456789abcdef';
    for (var i = 0; i < bytes.length; i++) {
        var b = bytes.charCodeAt(i);
        hex += digits.charAt(b >> 4) + digits.charAt(b & 0xf);
    }
    return hex;
}

function check(a, b) {
    var ahex = strToHex(a), bhex = strToHex(b);
    print('have', ahex);
    print('want', bhex);
    if (ahex == bhex) print('ok');
    else print('FAIL!');
}

function testHash() {
    print('\ntestHash');
    var hasher = XPO('@passpet.org/iterated-hash;1', XPI.IPasspetHash);

    // Test correctness.
    hasher.hash(1, 'abc');
    print('sha256(abc)', hasher.iterations);
    check(hasher.data, '\xba\x78\x16\xbf\x8f\x01\xcf\xea\x41\x41\x40\xde\x5d\xae\x22\x23\xb0\x03\x61\xa3\x96\x17\x7a\x9c\xb4\x10\xff\x61\xf2\x00\x15\xad');

    hasher.hash(1000, 'abc');
    print('sha256(abc)', hasher.iterations);
    check(hasher.data, '\xfc\x8a\x6b\x86\xa1\x3f\x71\xcd\x9a\x67\xf5\x58\xab\x6f\xd8\x2a\x3d\xd8\x91\x86\x16\x3a\x01\x7e\xd8\x05\x1a\xcf\x6d\x3f\x8f\x99');

    // Measure speed.
    hasher.data = 'xyz';
    var start = new Date().getTime();
    hasher.run(10000, 20000);
    var finish = new Date().getTime();
    print('sha256(xyz)', hasher.iterations,
          (finish - start)*1000/hasher.iterations + ' us');
}

function testAES() {
    print('\ntestAES');

    var K, p, c, aes;

    // Appendix B of FIPS 197, page 33.
    K = '\x2b\x7e\x15\x16\x28\xae\xd2\xa6\xab\xf7\x15\x88\x09\xcf\x4f\x3c';
    p = '\x32\x43\xf6\xa8\x88\x5a\x30\x8d\x31\x31\x98\xa2\xe0\x37\x07\x34';
    c = '\x39\x25\x84\x1d\x02\xdc\x09\xfb\xdc\x11\x85\x97\x19\x6a\x0b\x32';
    aes = XPO('@passpet.org/aes;1', XPI.IPasspetAES);
    aes.init(128, K);
    check(aes.encipher(p), c);
    check(aes.decipher(c), p);

    // Appendix C of FIPS 197, page 35.
    K = '\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f';
    p = '\x00\x11\x22\x33\x44\x55\x66\x77\x88\x99\xaa\xbb\xcc\xdd\xee\xff';
    c = '\x69\xc4\xe0\xd8\x6a\x7b\x04\x30\xd8\xcd\xb7\x80\x70\xb4\xc5\x5a';
    aes = XPO('@passpet.org/aes;1', XPI.IPasspetAES);
    aes.init(128, K);
    check(aes.encipher(p), c);
    check(aes.decipher(c), p);

    K = '\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17';
    p = '\x00\x11\x22\x33\x44\x55\x66\x77\x88\x99\xaa\xbb\xcc\xdd\xee\xff';
    c = '\xdd\xa9\x7c\xa4\x86\x4c\xdf\xe0\x6e\xaf\x70\xa0\xec\x0d\x71\x91';
    aes = XPO('@passpet.org/aes;1', XPI.IPasspetAES);
    aes.init(192, K);
    check(aes.encipher(p), c);
    check(aes.decipher(c), p);

    K = '\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f';
    p = '\x00\x11\x22\x33\x44\x55\x66\x77\x88\x99\xaa\xbb\xcc\xdd\xee\xff';
    c = '\x8e\xa2\xb7\xca\x51\x67\x45\xbf\xea\xfc\x49\x90\x4b\x49\x60\x89';
    aes = XPO('@passpet.org/aes;1', XPI.IPasspetAES);
    aes.init(256, K);
    check(aes.encipher(p), c);
    check(aes.decipher(c), p);
}

function testMAC() {
    print('\ntestMAC');

    var K, t, m, aes;

    // http://www.ietf.org/internet-drafts/draft-songlee-aes-cmac-03.txt

    K = '\x2b\x7e\x15\x16\x28\xae\xd2\xa6\xab\xf7\x15\x88\x09\xcf\x4f\x3c';
    aes = XPO('@passpet.org/aes;1', XPI.IPasspetAES);
    aes.init(128, K);

    t = '';
    m = '\xbb\x1d\x69\x29\xe9\x59\x37\x28\x7f\xa3\x7d\x12\x9b\x75\x67\x46';
    check(aes.getMAC(t), m);

    t = '\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96\xe9\x3d\x7e\x11\x73\x93\x17\x2a';
    m = '\x07\x0a\x16\xb4\x6b\x4d\x41\x44\xf7\x9b\xdd\x9d\xd0\x4a\x28\x7c';
    check(aes.getMAC(t), m);

    t = '\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96\xe9\x3d\x7e\x11\x73\x93\x17\x2a\xae\x2d\x8a\x57\x1e\x03\xac\x9c\x9e\xb7\x6f\xac\x45\xaf\x8e\x51\x30\xc8\x1c\x46\xa3\x5c\xe4\x11';
    m = '\xdf\xa6\x67\x47\xde\x9a\xe6\x30\x30\xca\x32\x61\x14\x97\xc8\x27';
    check(aes.getMAC(t), m);

    t = '\x6b\xc1\xbe\xe2\x2e\x40\x9f\x96\xe9\x3d\x7e\x11\x73\x93\x17\x2a\xae\x2d\x8a\x57\x1e\x03\xac\x9c\x9e\xb7\x6f\xac\x45\xaf\x8e\x51\x30\xc8\x1c\x46\xa3\x5c\xe4\x11\xe5\xfb\xc1\x19\x1a\x0a\x52\xef\xf6\x9f\x24\x45\xdf\x4f\x9b\x17\xad\x2b\x41\x7b\xe6\x6c\x37\x10';
    m = '\x51\xf0\xbe\xbf\x7e\x3b\x9d\x92\xfc\x49\x74\x17\x79\x36\x3c\xfe';
    check(aes.getMAC(t), m);
}

function testGCM() {
    var K, aes, gcm, mac;

    // Test cases from: http://csrc.nist.gov/
    // CryptoToolkit/modes/proposedmodes/gcm/gcm-revised-spec.pdf

    print('\ntestGCM');
    K = '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00';
    iv = '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00';
    p = '';
    c = '';
    mac = '\x58\xe2\xfc\xce\xfa\x7e\x30\x61\x36\x7f\x1d\x57\xa4\xe7\x45\x5a';

    aes = XPO('@passpet.org/aes;1', XPI.IPasspetAES);
    aes.init(128, K);
    gcm = XPO('@passpet.org/gcm;1', XPI.IPasspetGCM);
    gcm.initWithIV(aes, iv);
    check(gcm.encrypt(p), c + mac);
    check(gcm.decrypt(c + mac), p);

    K = '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00';
    iv = '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00';
    p = '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00';
    c = '\x03\x88\xda\xce\x60\xb6\xa3\x92\xf3\x28\xc2\xb9\x71\xb2\xfe\x78';
    mac = '\xab\x6e\x47\xd4\x2c\xec\x13\xbd\xf5\x3a\x67\xb2\x12\x57\xbd\xdf';

    aes = XPO('@passpet.org/aes;1', XPI.IPasspetAES);
    aes.init(128, K);
    gcm = XPO('@passpet.org/gcm;1', XPI.IPasspetGCM);
    gcm.initWithIV(aes, iv);
    check(gcm.encrypt(p), c + mac);
    check(gcm.decrypt(c + mac), p);

    K = '\xfe\xff\xe9\x92\x86\x65\x73\x1c\x6d\x6a\x8f\x94\x67\x30\x83\x08';
    iv = '\xca\xfe\xba\xbe\xfa\xce\xdb\xad\xde\xca\xf8\x88';
    p = '\xd9\x31\x32\x25\xf8\x84\x06\xe5\xa5\x59\x09\xc5\xaf\xf5\x26\x9a\x86\xa7\xa9\x53\x15\x34\xf7\xda\x2e\x4c\x30\x3d\x8a\x31\x8a\x72\x1c\x3c\x0c\x95\x95\x68\x09\x53\x2f\xcf\x0e\x24\x49\xa6\xb5\x25\xb1\x6a\xed\xf5\xaa\x0d\xe6\x57\xba\x63\x7b\x39\x1a\xaf\xd2\x55';
    c = '\x42\x83\x1e\xc2\x21\x77\x74\x24\x4b\x72\x21\xb7\x84\xd0\xd4\x9c\xe3\xaa\x21\x2f\x2c\x02\xa4\xe0\x35\xc1\x7e\x23\x29\xac\xa1\x2e\x21\xd5\x14\xb2\x54\x66\x93\x1c\x7d\x8f\x6a\x5a\xac\x84\xaa\x05\x1b\xa3\x0b\x39\x6a\x0a\xac\x97\x3d\x58\xe0\x91\x47\x3f\x59\x85';
    mac = '\x4d\x5c\x2a\xf3\x27\xcd\x64\xa6\x2c\xf3\x5a\xbd\x2b\xa6\xfa\xb4';

    aes = XPO('@passpet.org/aes;1', XPI.IPasspetAES);
    aes.init(128, K);
    gcm = XPO('@passpet.org/gcm;1', XPI.IPasspetGCM);
    gcm.initWithIV(aes, iv);
    check(gcm.encrypt(p), c + mac);
    check(gcm.decrypt(c + mac), p);
}

function testRemote() {
    const r = XPS('@passpet.org/remote-storage;1', XPI.IPasspetRemote);
    var index;

    var cont5 = new Cont('delete', function(result) {
        print('delete', result);
    });

    var cont4 = new Cont('read', function(result) {
        print('read', result);
        r.delete_('ping@localhost', index, 'xyz', cont5);
    });

    var cont3 = new Cont('write', function(result) {
        print('write', result);
        r.read('ping@localhost', index, 'xyz', cont4);
    });

    var cont2 = new Cont('create', function(result) {
        print('create', result);
        index = parseInt(result);
        r.write('ping@localhost', index, 'xyz', '', 'abcd', cont3);
    });
        
    var cont1 = new Cont('list', function(result) {
        print('list', result);
        r.create('ping@localhost', 1, 'xyz', cont2);
    });

    r.list('ping@localhost', cont1);
}

