2013-08-18 01:37:18 +00:00
|
|
|
package org.whispersystems.textsecure.storage;
|
2013-08-15 15:25:30 +00:00
|
|
|
|
|
|
|
import android.content.Context;
|
|
|
|
import android.util.Log;
|
|
|
|
|
2013-11-28 01:50:38 +00:00
|
|
|
import com.google.protobuf.ByteString;
|
|
|
|
|
2013-08-18 01:37:18 +00:00
|
|
|
import org.whispersystems.textsecure.crypto.InvalidKeyException;
|
2013-11-28 01:50:38 +00:00
|
|
|
import org.whispersystems.textsecure.crypto.InvalidMessageException;
|
|
|
|
import org.whispersystems.textsecure.crypto.MasterCipher;
|
2013-08-18 01:37:18 +00:00
|
|
|
import org.whispersystems.textsecure.crypto.MasterSecret;
|
2013-11-28 01:50:38 +00:00
|
|
|
import org.whispersystems.textsecure.crypto.ecc.Curve;
|
|
|
|
import org.whispersystems.textsecure.crypto.ecc.ECKeyPair;
|
|
|
|
import org.whispersystems.textsecure.crypto.ecc.ECPrivateKey;
|
|
|
|
import org.whispersystems.textsecure.crypto.ecc.ECPublicKey;
|
2013-08-15 15:25:30 +00:00
|
|
|
|
|
|
|
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;
|
2013-11-28 01:50:38 +00:00
|
|
|
private StorageProtos.PreKeyRecordStructure structure;
|
2013-08-15 15:25:30 +00:00
|
|
|
|
2013-08-19 17:07:07 +00:00
|
|
|
public PreKeyRecord(Context context, MasterSecret masterSecret, int id)
|
2013-08-15 15:25:30 +00:00
|
|
|
throws InvalidKeyIdException
|
|
|
|
{
|
|
|
|
super(context, PREKEY_DIRECTORY, id+"");
|
|
|
|
|
2013-11-28 01:50:38 +00:00
|
|
|
this.structure = StorageProtos.PreKeyRecordStructure.newBuilder().setId(id).build();
|
2013-08-15 15:25:30 +00:00
|
|
|
this.masterSecret = masterSecret;
|
|
|
|
|
|
|
|
loadData();
|
|
|
|
}
|
|
|
|
|
|
|
|
public PreKeyRecord(Context context, MasterSecret masterSecret,
|
2013-11-28 01:50:38 +00:00
|
|
|
int id, ECKeyPair keyPair)
|
2013-08-15 15:25:30 +00:00
|
|
|
{
|
|
|
|
super(context, PREKEY_DIRECTORY, id+"");
|
|
|
|
this.masterSecret = masterSecret;
|
2013-11-28 01:50:38 +00:00
|
|
|
this.structure = StorageProtos.PreKeyRecordStructure.newBuilder()
|
|
|
|
.setId(id)
|
|
|
|
.setPublicKey(ByteString.copyFrom(keyPair.getPublicKey()
|
|
|
|
.serialize()))
|
|
|
|
.setPrivateKey(ByteString.copyFrom(keyPair.getPrivateKey()
|
|
|
|
.serialize()))
|
|
|
|
.build();
|
2013-08-15 15:25:30 +00:00
|
|
|
}
|
|
|
|
|
2013-08-19 17:07:07 +00:00
|
|
|
public int getId() {
|
2013-11-28 01:50:38 +00:00
|
|
|
return this.structure.getId();
|
2013-08-15 15:25:30 +00:00
|
|
|
}
|
|
|
|
|
2013-11-28 01:50:38 +00:00
|
|
|
public ECKeyPair getKeyPair() {
|
|
|
|
try {
|
|
|
|
ECPublicKey publicKey = Curve.decodePoint(this.structure.getPublicKey().toByteArray(), 0);
|
|
|
|
ECPrivateKey privateKey = Curve.decodePrivatePoint(publicKey.getType(), this.structure.getPrivateKey().toByteArray());
|
|
|
|
|
|
|
|
return new ECKeyPair(publicKey, privateKey);
|
|
|
|
} catch (InvalidKeyException e) {
|
|
|
|
throw new AssertionError(e);
|
|
|
|
}
|
2013-08-15 15:25:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
2013-11-28 01:50:38 +00:00
|
|
|
MasterCipher masterCipher = new MasterCipher(masterSecret);
|
|
|
|
|
2013-08-15 15:25:30 +00:00
|
|
|
writeInteger(CURRENT_VERSION_MARKER, out);
|
2013-11-28 01:50:38 +00:00
|
|
|
writeBlob(masterCipher.encryptBytes(structure.toByteArray()), out);
|
2013-08-15 15:25:30 +00:00
|
|
|
|
|
|
|
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 {
|
2013-11-28 01:50:38 +00:00
|
|
|
MasterCipher masterCipher = new MasterCipher(masterSecret);
|
|
|
|
FileInputStream in = this.openInputStream();
|
|
|
|
int recordVersion = readInteger(in);
|
2013-08-15 15:25:30 +00:00
|
|
|
|
|
|
|
if (recordVersion != CURRENT_VERSION_MARKER) {
|
|
|
|
Log.w("PreKeyRecord", "Invalid version: " + recordVersion);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-11-28 01:50:38 +00:00
|
|
|
this.structure =
|
|
|
|
StorageProtos.PreKeyRecordStructure.parseFrom(masterCipher.decryptBytes(readBlob(in)));
|
|
|
|
|
2013-08-15 15:25:30 +00:00
|
|
|
in.close();
|
|
|
|
} catch (FileNotFoundException e) {
|
|
|
|
Log.w("PreKeyRecord", e);
|
|
|
|
throw new InvalidKeyIdException(e);
|
|
|
|
} catch (IOException ioe) {
|
|
|
|
Log.w("PreKeyRecord", ioe);
|
|
|
|
throw new InvalidKeyIdException(ioe);
|
2013-11-28 01:50:38 +00:00
|
|
|
} catch (InvalidMessageException ime) {
|
|
|
|
Log.w("PreKeyRecord", ime);
|
|
|
|
throw new InvalidKeyIdException(ime);
|
2013-08-15 15:25:30 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|