Signal-Android/library/src/org/whispersystems/textsecure/storage/PreKeyRecord.java

131 lines
4.3 KiB
Java

package org.whispersystems.textsecure.storage;
import android.content.Context;
import android.util.Log;
import com.google.protobuf.ByteString;
import org.whispersystems.libaxolotl.InvalidKeyException;
import org.whispersystems.libaxolotl.InvalidMessageException;
import org.whispersystems.libaxolotl.ecc.Curve;
import org.whispersystems.libaxolotl.ecc.ECKeyPair;
import org.whispersystems.libaxolotl.ecc.ECPrivateKey;
import org.whispersystems.libaxolotl.ecc.ECPublicKey;
import org.whispersystems.textsecure.crypto.MasterCipher;
import org.whispersystems.textsecure.crypto.MasterSecret;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.channels.FileChannel;
public class PreKeyRecord extends Record {
private static final Object FILE_LOCK = new Object();
private static final int CURRENT_VERSION_MARKER = 1;
private final MasterSecret masterSecret;
private StorageProtos.PreKeyRecordStructure structure;
public PreKeyRecord(Context context, MasterSecret masterSecret, int id)
throws InvalidKeyIdException
{
super(context, PREKEY_DIRECTORY, id+"");
this.structure = StorageProtos.PreKeyRecordStructure.newBuilder().setId(id).build();
this.masterSecret = masterSecret;
loadData();
}
public PreKeyRecord(Context context, MasterSecret masterSecret,
int id, ECKeyPair keyPair)
{
super(context, PREKEY_DIRECTORY, id+"");
this.masterSecret = masterSecret;
this.structure = StorageProtos.PreKeyRecordStructure.newBuilder()
.setId(id)
.setPublicKey(ByteString.copyFrom(keyPair.getPublicKey()
.serialize()))
.setPrivateKey(ByteString.copyFrom(keyPair.getPrivateKey()
.serialize()))
.build();
}
public int getId() {
return this.structure.getId();
}
public ECKeyPair getKeyPair() {
try {
ECPublicKey publicKey = Curve.decodePoint(this.structure.getPublicKey().toByteArray(), 0);
ECPrivateKey privateKey = Curve.decodePrivatePoint(this.structure.getPrivateKey().toByteArray());
return new ECKeyPair(publicKey, privateKey);
} catch (InvalidKeyException e) {
throw new AssertionError(e);
}
}
public static boolean hasRecord(Context context, long id) {
Log.w("PreKeyRecord", "Checking: " + id);
return Record.hasRecord(context, PREKEY_DIRECTORY, id+"");
}
public static void delete(Context context, long id) {
Record.delete(context, PREKEY_DIRECTORY, id+"");
}
public void save() {
synchronized (FILE_LOCK) {
try {
RandomAccessFile file = openRandomAccessFile();
FileChannel out = file.getChannel();
out.position(0);
MasterCipher masterCipher = new MasterCipher(masterSecret);
writeInteger(CURRENT_VERSION_MARKER, out);
writeBlob(masterCipher.encryptBytes(structure.toByteArray()), out);
out.force(true);
out.truncate(out.position());
out.close();
file.close();
} catch (IOException ioe) {
Log.w("PreKeyRecord", ioe);
}
}
}
private void loadData() throws InvalidKeyIdException {
synchronized (FILE_LOCK) {
try {
MasterCipher masterCipher = new MasterCipher(masterSecret);
FileInputStream in = this.openInputStream();
int recordVersion = readInteger(in);
if (recordVersion != CURRENT_VERSION_MARKER) {
Log.w("PreKeyRecord", "Invalid version: " + recordVersion);
return;
}
this.structure =
StorageProtos.PreKeyRecordStructure.parseFrom(masterCipher.decryptBytes(readBlob(in)));
in.close();
} catch (FileNotFoundException e) {
Log.w("PreKeyRecord", e);
throw new InvalidKeyIdException(e);
} catch (IOException ioe) {
Log.w("PreKeyRecord", ioe);
throw new InvalidKeyIdException(ioe);
} catch (InvalidMessageException ime) {
Log.w("PreKeyRecord", ime);
throw new InvalidKeyIdException(ime);
}
}
}
}