org.fdroid.enigtext.crypto.KeyUtil.java Source code

Java tutorial

Introduction

Here is the source code for org.fdroid.enigtext.crypto.KeyUtil.java

Source

/** 
 * Copyright (C) 2011 Whisper Systems
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.fdroid.enigtext.crypto;

import java.math.BigInteger;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;

import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
import org.bouncycastle.crypto.agreement.ECDHBasicAgreement;
import org.bouncycastle.crypto.generators.ECKeyPairGenerator;
import org.bouncycastle.crypto.params.ECDomainParameters;
import org.bouncycastle.crypto.params.ECKeyGenerationParameters;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.math.ec.ECCurve;
import org.bouncycastle.math.ec.ECFieldElement;
import org.bouncycastle.math.ec.ECPoint;
import org.fdroid.enigtext.database.keys.LocalKeyRecord;
import org.fdroid.enigtext.database.keys.RemoteKeyRecord;
import org.fdroid.enigtext.database.keys.SessionRecord;
import org.fdroid.enigtext.recipients.Recipient;

import android.content.Context;
import android.util.Log;

/**
 * Helper class for generating key pairs and calculating ECDH agreements.
 * 
 * @author Moxie Marlinspike
 */

public class KeyUtil {

    public static final BigInteger q = new BigInteger(
            "FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF", 16);
    private static final BigInteger a = new BigInteger(
            "FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC", 16);
    private static final BigInteger b = new BigInteger(
            "5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B", 16);
    private static final BigInteger n = new BigInteger(
            "FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551", 16);

    private static final ECFieldElement x = new ECFieldElement.Fp(q,
            new BigInteger("6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296", 16));
    private static final ECFieldElement y = new ECFieldElement.Fp(q,
            new BigInteger("4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5", 16));

    private static final ECCurve curve = new ECCurve.Fp(q, a, b);
    private static final ECPoint g = new ECPoint.Fp(curve, x, y, true);

    public static final ECDomainParameters domainParameters = new ECDomainParameters(curve, g, n);

    public static ECPoint decodePoint(byte[] pointBytes) {
        synchronized (curve) {
            return curve.decodePoint(pointBytes);
        }
    }

    public static byte[] encodePoint(ECPoint point) {
        synchronized (curve) {
            return point.getEncoded();
        }
    }

    public static BigInteger calculateAgreement(ECDHBasicAgreement agreement, ECPublicKeyParameters remoteKey) {
        synchronized (curve) {
            return agreement.calculateAgreement(remoteKey);
        }
    }

    public static void abortSessionFor(Context context, Recipient recipient) {
        //XXX Obviously we should probably do something more thorough here eventually.
        Log.w("KeyUtil", "Aborting session, deleting keys...");
        LocalKeyRecord.delete(context, recipient);
        RemoteKeyRecord.delete(context, recipient);
        SessionRecord.delete(context, recipient);
    }

    public static boolean isSessionFor(Context context, Recipient recipient) {
        Log.w("KeyUtil", "Checking session...");
        return (LocalKeyRecord.hasRecord(context, recipient)) && (RemoteKeyRecord.hasRecord(context, recipient))
                && (SessionRecord.hasSession(context, recipient));
    }

    public static boolean isIdentityKeyFor(Context context, MasterSecret masterSecret, Recipient recipient) {
        return isSessionFor(context, recipient)
                && new SessionRecord(context, masterSecret, recipient).getIdentityKey() != null;
    }

    public static LocalKeyRecord initializeRecordFor(Recipient recipient, Context context,
            MasterSecret masterSecret) {
        Log.w("KeyUtil", "Initializing local key pairs...");
        try {
            SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");
            int initialId = secureRandom.nextInt(4094) + 1;

            KeyPair currentPair = new KeyPair(initialId, KeyUtil.generateKeyPair(), masterSecret);
            KeyPair nextPair = new KeyPair(initialId + 1, KeyUtil.generateKeyPair(), masterSecret);
            LocalKeyRecord record = new LocalKeyRecord(context, masterSecret, recipient);

            record.setCurrentKeyPair(currentPair);
            record.setNextKeyPair(nextPair);
            record.save();

            return record;
        } catch (NoSuchAlgorithmException e) {
            throw new AssertionError(e);
        }
    }

    public static AsymmetricCipherKeyPair generateKeyPair() {
        try {
            synchronized (curve) {
                ECKeyGenerationParameters keyParamters = new ECKeyGenerationParameters(domainParameters,
                        SecureRandom.getInstance("SHA1PRNG"));
                ECKeyPairGenerator generator = new ECKeyPairGenerator();
                generator.init(keyParamters);

                AsymmetricCipherKeyPair keyPair = generator.generateKeyPair();

                return cloneKeyPairWithPointCompression(keyPair);
            }
        } catch (NoSuchAlgorithmException nsae) {
            Log.w("keyutil", nsae);
            return null;
        }
    }

    // This is dumb, but the ECPublicKeys that the generator makes by default don't have point compression
    // turned on, and there's no setter.  Great.
    private static AsymmetricCipherKeyPair cloneKeyPairWithPointCompression(AsymmetricCipherKeyPair keyPair) {
        ECPublicKeyParameters publicKey = (ECPublicKeyParameters) keyPair.getPublic();
        ECPoint q = publicKey.getQ();

        return new AsymmetricCipherKeyPair(
                new ECPublicKeyParameters(new ECPoint.Fp(q.getCurve(), q.getX(), q.getY(), true),
                        publicKey.getParameters()),
                keyPair.getPrivate());
    }

}