org.thoughtcrime.SMP.crypto.PreKeyUtil.java Source code

Java tutorial

Introduction

Here is the source code for org.thoughtcrime.SMP.crypto.PreKeyUtil.java

Source

/**
 * Copyright (C) 2013 Open 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.thoughtcrime.SMP.crypto;

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

import com.fasterxml.jackson.annotation.JsonProperty;

import org.thoughtcrime.SMP.crypto.storage.TextSecurePreKeyStore;
import org.thoughtcrime.SMP.util.JsonUtils;
import org.thoughtcrime.SMP.util.Util;
import org.whispersystems.libaxolotl.IdentityKeyPair;
import org.whispersystems.libaxolotl.InvalidKeyException;
import org.whispersystems.libaxolotl.InvalidKeyIdException;
import org.whispersystems.libaxolotl.ecc.Curve;
import org.whispersystems.libaxolotl.ecc.ECKeyPair;
import org.whispersystems.libaxolotl.state.PreKeyRecord;
import org.whispersystems.libaxolotl.state.PreKeyStore;
import org.whispersystems.libaxolotl.state.SignedPreKeyRecord;
import org.whispersystems.libaxolotl.state.SignedPreKeyStore;
import org.whispersystems.libaxolotl.util.Medium;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.LinkedList;
import java.util.List;

public class PreKeyUtil {

    public static final int BATCH_SIZE = 100;

    public static List<PreKeyRecord> generatePreKeys(Context context, MasterSecret masterSecret) {
        PreKeyStore preKeyStore = new TextSecurePreKeyStore(context, masterSecret);
        List<PreKeyRecord> records = new LinkedList<>();
        int preKeyIdOffset = getNextPreKeyId(context);

        for (int i = 0; i < BATCH_SIZE; i++) {
            int preKeyId = (preKeyIdOffset + i) % Medium.MAX_VALUE;
            ECKeyPair keyPair = Curve.generateKeyPair();
            PreKeyRecord record = new PreKeyRecord(preKeyId, keyPair);

            preKeyStore.storePreKey(preKeyId, record);
            records.add(record);
        }

        setNextPreKeyId(context, (preKeyIdOffset + BATCH_SIZE + 1) % Medium.MAX_VALUE);
        return records;
    }

    public static SignedPreKeyRecord generateSignedPreKey(Context context, MasterSecret masterSecret,
            IdentityKeyPair identityKeyPair) {
        try {
            SignedPreKeyStore signedPreKeyStore = new TextSecurePreKeyStore(context, masterSecret);
            int signedPreKeyId = getNextSignedPreKeyId(context);
            ECKeyPair keyPair = Curve.generateKeyPair();
            byte[] signature = Curve.calculateSignature(identityKeyPair.getPrivateKey(),
                    keyPair.getPublicKey().serialize());
            SignedPreKeyRecord record = new SignedPreKeyRecord(signedPreKeyId, System.currentTimeMillis(), keyPair,
                    signature);

            signedPreKeyStore.storeSignedPreKey(signedPreKeyId, record);
            setNextSignedPreKeyId(context, (signedPreKeyId + 1) % Medium.MAX_VALUE);

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

    public static PreKeyRecord generateLastResortKey(Context context, MasterSecret masterSecret) {
        PreKeyStore preKeyStore = new TextSecurePreKeyStore(context, masterSecret);

        if (preKeyStore.containsPreKey(Medium.MAX_VALUE)) {
            try {
                return preKeyStore.loadPreKey(Medium.MAX_VALUE);
            } catch (InvalidKeyIdException e) {
                Log.w("PreKeyUtil", e);
                preKeyStore.removePreKey(Medium.MAX_VALUE);
            }
        }

        ECKeyPair keyPair = Curve.generateKeyPair();
        PreKeyRecord record = new PreKeyRecord(Medium.MAX_VALUE, keyPair);

        preKeyStore.storePreKey(Medium.MAX_VALUE, record);

        return record;
    }

    private static void setNextPreKeyId(Context context, int id) {
        try {
            File nextFile = new File(getPreKeysDirectory(context), PreKeyIndex.FILE_NAME);
            FileOutputStream fout = new FileOutputStream(nextFile);
            fout.write(JsonUtils.toJson(new PreKeyIndex(id)).getBytes());
            fout.close();
        } catch (IOException e) {
            Log.w("PreKeyUtil", e);
        }
    }

    private static void setNextSignedPreKeyId(Context context, int id) {
        try {
            File nextFile = new File(getSignedPreKeysDirectory(context), SignedPreKeyIndex.FILE_NAME);
            FileOutputStream fout = new FileOutputStream(nextFile);
            fout.write(JsonUtils.toJson(new SignedPreKeyIndex(id)).getBytes());
            fout.close();
        } catch (IOException e) {
            Log.w("PreKeyUtil", e);
        }
    }

    private static int getNextPreKeyId(Context context) {
        try {
            File nextFile = new File(getPreKeysDirectory(context), PreKeyIndex.FILE_NAME);

            if (!nextFile.exists()) {
                return Util.getSecureRandom().nextInt(Medium.MAX_VALUE);
            } else {
                InputStreamReader reader = new InputStreamReader(new FileInputStream(nextFile));
                PreKeyIndex index = JsonUtils.fromJson(reader, PreKeyIndex.class);
                reader.close();
                return index.nextPreKeyId;
            }
        } catch (IOException e) {
            Log.w("PreKeyUtil", e);
            return Util.getSecureRandom().nextInt(Medium.MAX_VALUE);
        }
    }

    private static int getNextSignedPreKeyId(Context context) {
        try {
            File nextFile = new File(getSignedPreKeysDirectory(context), SignedPreKeyIndex.FILE_NAME);

            if (!nextFile.exists()) {
                return Util.getSecureRandom().nextInt(Medium.MAX_VALUE);
            } else {
                InputStreamReader reader = new InputStreamReader(new FileInputStream(nextFile));
                SignedPreKeyIndex index = JsonUtils.fromJson(reader, SignedPreKeyIndex.class);
                reader.close();
                return index.nextSignedPreKeyId;
            }
        } catch (IOException e) {
            Log.w("PreKeyUtil", e);
            return Util.getSecureRandom().nextInt(Medium.MAX_VALUE);
        }
    }

    private static File getPreKeysDirectory(Context context) {
        return getKeysDirectory(context, TextSecurePreKeyStore.PREKEY_DIRECTORY);
    }

    private static File getSignedPreKeysDirectory(Context context) {
        return getKeysDirectory(context, TextSecurePreKeyStore.SIGNED_PREKEY_DIRECTORY);
    }

    private static File getKeysDirectory(Context context, String name) {
        File directory = new File(context.getFilesDir(), name);

        if (!directory.exists())
            directory.mkdirs();

        return directory;
    }

    private static class PreKeyIndex {
        public static final String FILE_NAME = "index.dat";

        @JsonProperty
        private int nextPreKeyId;

        public PreKeyIndex() {
        }

        public PreKeyIndex(int nextPreKeyId) {
            this.nextPreKeyId = nextPreKeyId;
        }
    }

    private static class SignedPreKeyIndex {
        public static final String FILE_NAME = "index.dat";

        @JsonProperty
        private int nextSignedPreKeyId;

        public SignedPreKeyIndex() {
        }

        public SignedPreKeyIndex(int nextSignedPreKeyId) {
            this.nextSignedPreKeyId = nextSignedPreKeyId;
        }
    }

}