Move prekey jsonifcation into the push code, add identity key.

This commit is contained in:
Moxie Marlinspike 2013-08-17 19:06:04 -07:00
parent b8f663b69c
commit 499de2d2bf
11 changed files with 134 additions and 65 deletions

View File

@ -4,6 +4,7 @@ option java_package = "org.whispersystems.textsecure.encoded";
option java_outer_classname = "PreKeyProtos"; option java_outer_classname = "PreKeyProtos";
message PreKeyEntity { message PreKeyEntity {
optional uint64 id = 1; optional uint64 id = 1;
optional bytes key = 2; optional bytes public_key = 2;
optional bytes identity_key = 3;
} }

View File

@ -65,22 +65,6 @@ public class PreKeyUtil {
} }
} }
public static PreKeyList toJson(List<PreKeyRecord> records) {
List<String> encoded = new LinkedList<String>();
for (PreKeyRecord record : records) {
PreKeyEntity entity = PreKeyEntity.newBuilder().setId(record.getId())
.setKey(ByteString.copyFrom(KeyUtil.encodePoint(record.getKeyPair().getPublicKey().getQ())))
.build();
String encodedEntity = Base64.encodeBytesWithoutPadding(entity.toByteArray());
encoded.add(encodedEntity);
}
return new PreKeyList(encoded);
}
private static long getNextPreKeyId(Context context) { private static long getNextPreKeyId(Context context) {
try { try {
File directory = getPreKeysDirectory(context); File directory = getPreKeysDirectory(context);

View File

@ -22,7 +22,7 @@ import android.util.Log;
import org.spongycastle.crypto.AsymmetricCipherKeyPair; import org.spongycastle.crypto.AsymmetricCipherKeyPair;
import org.spongycastle.crypto.agreement.ECDHBasicAgreement; import org.spongycastle.crypto.agreement.ECDHBasicAgreement;
import org.spongycastle.crypto.params.ECPublicKeyParameters; import org.spongycastle.crypto.params.ECPublicKeyParameters;
import org.whispersystems.textsecure.protocol.Message; import org.whispersystems.textsecure.crypto.protocol.Message;
import org.whispersystems.textsecure.storage.CanonicalRecipientAddress; import org.whispersystems.textsecure.storage.CanonicalRecipientAddress;
import org.whispersystems.textsecure.storage.InvalidKeyIdException; import org.whispersystems.textsecure.storage.InvalidKeyIdException;
import org.whispersystems.textsecure.storage.LocalKeyRecord; import org.whispersystems.textsecure.storage.LocalKeyRecord;

View File

@ -14,7 +14,7 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
package org.whispersystems.textsecure.protocol; package org.whispersystems.textsecure.crypto.protocol;
import android.util.Log; import android.util.Log;

View File

@ -15,9 +15,13 @@ public final class PreKeyProtos {
boolean hasId(); boolean hasId();
long getId(); long getId();
// optional bytes key = 2; // optional bytes public_key = 2;
boolean hasKey(); boolean hasPublicKey();
com.google.protobuf.ByteString getKey(); com.google.protobuf.ByteString getPublicKey();
// optional bytes identity_key = 3;
boolean hasIdentityKey();
com.google.protobuf.ByteString getIdentityKey();
} }
public static final class PreKeyEntity extends public static final class PreKeyEntity extends
com.google.protobuf.GeneratedMessage com.google.protobuf.GeneratedMessage
@ -58,19 +62,30 @@ public final class PreKeyProtos {
return id_; return id_;
} }
// optional bytes key = 2; // optional bytes public_key = 2;
public static final int KEY_FIELD_NUMBER = 2; public static final int PUBLIC_KEY_FIELD_NUMBER = 2;
private com.google.protobuf.ByteString key_; private com.google.protobuf.ByteString publicKey_;
public boolean hasKey() { public boolean hasPublicKey() {
return ((bitField0_ & 0x00000002) == 0x00000002); return ((bitField0_ & 0x00000002) == 0x00000002);
} }
public com.google.protobuf.ByteString getKey() { public com.google.protobuf.ByteString getPublicKey() {
return key_; return publicKey_;
}
// optional bytes identity_key = 3;
public static final int IDENTITY_KEY_FIELD_NUMBER = 3;
private com.google.protobuf.ByteString identityKey_;
public boolean hasIdentityKey() {
return ((bitField0_ & 0x00000004) == 0x00000004);
}
public com.google.protobuf.ByteString getIdentityKey() {
return identityKey_;
} }
private void initFields() { private void initFields() {
id_ = 0L; id_ = 0L;
key_ = com.google.protobuf.ByteString.EMPTY; publicKey_ = com.google.protobuf.ByteString.EMPTY;
identityKey_ = com.google.protobuf.ByteString.EMPTY;
} }
private byte memoizedIsInitialized = -1; private byte memoizedIsInitialized = -1;
public final boolean isInitialized() { public final boolean isInitialized() {
@ -88,7 +103,10 @@ public final class PreKeyProtos {
output.writeUInt64(1, id_); output.writeUInt64(1, id_);
} }
if (((bitField0_ & 0x00000002) == 0x00000002)) { if (((bitField0_ & 0x00000002) == 0x00000002)) {
output.writeBytes(2, key_); output.writeBytes(2, publicKey_);
}
if (((bitField0_ & 0x00000004) == 0x00000004)) {
output.writeBytes(3, identityKey_);
} }
getUnknownFields().writeTo(output); getUnknownFields().writeTo(output);
} }
@ -105,7 +123,11 @@ public final class PreKeyProtos {
} }
if (((bitField0_ & 0x00000002) == 0x00000002)) { if (((bitField0_ & 0x00000002) == 0x00000002)) {
size += com.google.protobuf.CodedOutputStream size += com.google.protobuf.CodedOutputStream
.computeBytesSize(2, key_); .computeBytesSize(2, publicKey_);
}
if (((bitField0_ & 0x00000004) == 0x00000004)) {
size += com.google.protobuf.CodedOutputStream
.computeBytesSize(3, identityKey_);
} }
size += getUnknownFields().getSerializedSize(); size += getUnknownFields().getSerializedSize();
memoizedSerializedSize = size; memoizedSerializedSize = size;
@ -233,8 +255,10 @@ public final class PreKeyProtos {
super.clear(); super.clear();
id_ = 0L; id_ = 0L;
bitField0_ = (bitField0_ & ~0x00000001); bitField0_ = (bitField0_ & ~0x00000001);
key_ = com.google.protobuf.ByteString.EMPTY; publicKey_ = com.google.protobuf.ByteString.EMPTY;
bitField0_ = (bitField0_ & ~0x00000002); bitField0_ = (bitField0_ & ~0x00000002);
identityKey_ = com.google.protobuf.ByteString.EMPTY;
bitField0_ = (bitField0_ & ~0x00000004);
return this; return this;
} }
@ -280,7 +304,11 @@ public final class PreKeyProtos {
if (((from_bitField0_ & 0x00000002) == 0x00000002)) { if (((from_bitField0_ & 0x00000002) == 0x00000002)) {
to_bitField0_ |= 0x00000002; to_bitField0_ |= 0x00000002;
} }
result.key_ = key_; result.publicKey_ = publicKey_;
if (((from_bitField0_ & 0x00000004) == 0x00000004)) {
to_bitField0_ |= 0x00000004;
}
result.identityKey_ = identityKey_;
result.bitField0_ = to_bitField0_; result.bitField0_ = to_bitField0_;
onBuilt(); onBuilt();
return result; return result;
@ -300,8 +328,11 @@ public final class PreKeyProtos {
if (other.hasId()) { if (other.hasId()) {
setId(other.getId()); setId(other.getId());
} }
if (other.hasKey()) { if (other.hasPublicKey()) {
setKey(other.getKey()); setPublicKey(other.getPublicKey());
}
if (other.hasIdentityKey()) {
setIdentityKey(other.getIdentityKey());
} }
this.mergeUnknownFields(other.getUnknownFields()); this.mergeUnknownFields(other.getUnknownFields());
return this; return this;
@ -341,7 +372,12 @@ public final class PreKeyProtos {
} }
case 18: { case 18: {
bitField0_ |= 0x00000002; bitField0_ |= 0x00000002;
key_ = input.readBytes(); publicKey_ = input.readBytes();
break;
}
case 26: {
bitField0_ |= 0x00000004;
identityKey_ = input.readBytes();
break; break;
} }
} }
@ -371,26 +407,50 @@ public final class PreKeyProtos {
return this; return this;
} }
// optional bytes key = 2; // optional bytes public_key = 2;
private com.google.protobuf.ByteString key_ = com.google.protobuf.ByteString.EMPTY; private com.google.protobuf.ByteString publicKey_ = com.google.protobuf.ByteString.EMPTY;
public boolean hasKey() { public boolean hasPublicKey() {
return ((bitField0_ & 0x00000002) == 0x00000002); return ((bitField0_ & 0x00000002) == 0x00000002);
} }
public com.google.protobuf.ByteString getKey() { public com.google.protobuf.ByteString getPublicKey() {
return key_; return publicKey_;
} }
public Builder setKey(com.google.protobuf.ByteString value) { public Builder setPublicKey(com.google.protobuf.ByteString value) {
if (value == null) { if (value == null) {
throw new NullPointerException(); throw new NullPointerException();
} }
bitField0_ |= 0x00000002; bitField0_ |= 0x00000002;
key_ = value; publicKey_ = value;
onChanged(); onChanged();
return this; return this;
} }
public Builder clearKey() { public Builder clearPublicKey() {
bitField0_ = (bitField0_ & ~0x00000002); bitField0_ = (bitField0_ & ~0x00000002);
key_ = getDefaultInstance().getKey(); publicKey_ = getDefaultInstance().getPublicKey();
onChanged();
return this;
}
// optional bytes identity_key = 3;
private com.google.protobuf.ByteString identityKey_ = com.google.protobuf.ByteString.EMPTY;
public boolean hasIdentityKey() {
return ((bitField0_ & 0x00000004) == 0x00000004);
}
public com.google.protobuf.ByteString getIdentityKey() {
return identityKey_;
}
public Builder setIdentityKey(com.google.protobuf.ByteString value) {
if (value == null) {
throw new NullPointerException();
}
bitField0_ |= 0x00000004;
identityKey_ = value;
onChanged();
return this;
}
public Builder clearIdentityKey() {
bitField0_ = (bitField0_ & ~0x00000004);
identityKey_ = getDefaultInstance().getIdentityKey();
onChanged(); onChanged();
return this; return this;
} }
@ -420,10 +480,10 @@ public final class PreKeyProtos {
descriptor; descriptor;
static { static {
java.lang.String[] descriptorData = { java.lang.String[] descriptorData = {
"\n\022PreKeyEntity.proto\022\ntextsecure\"\'\n\014PreK" + "\n\022PreKeyEntity.proto\022\ntextsecure\"D\n\014PreK" +
"eyEntity\022\n\n\002id\030\001 \001(\004\022\013\n\003key\030\002 \001(\014B5\n%org" + "eyEntity\022\n\n\002id\030\001 \001(\004\022\022\n\npublic_key\030\002 \001(\014" +
".whispersystems.textsecure.encodedB\014PreK" + "\022\024\n\014identity_key\030\003 \001(\014B5\n%org.whispersys" +
"eyProtos" "tems.textsecure.encodedB\014PreKeyProtos"
}; };
com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner = com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner =
new com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner() { new com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner() {
@ -435,7 +495,7 @@ public final class PreKeyProtos {
internal_static_textsecure_PreKeyEntity_fieldAccessorTable = new internal_static_textsecure_PreKeyEntity_fieldAccessorTable = new
com.google.protobuf.GeneratedMessage.FieldAccessorTable( com.google.protobuf.GeneratedMessage.FieldAccessorTable(
internal_static_textsecure_PreKeyEntity_descriptor, internal_static_textsecure_PreKeyEntity_descriptor,
new java.lang.String[] { "Id", "Key", }, new java.lang.String[] { "Id", "PublicKey", "IdentityKey", },
org.whispersystems.textsecure.encoded.PreKeyProtos.PreKeyEntity.class, org.whispersystems.textsecure.encoded.PreKeyProtos.PreKeyEntity.class,
org.whispersystems.textsecure.encoded.PreKeyProtos.PreKeyEntity.Builder.class); org.whispersystems.textsecure.encoded.PreKeyProtos.PreKeyEntity.Builder.class);
return null; return null;

View File

@ -1,15 +1,18 @@
package org.whispersystems.textsecure.push; package org.whispersystems.textsecure.push;
import android.content.Context; import android.content.Context;
import android.util.Base64;
import android.util.Log; import android.util.Log;
import android.util.Pair; import android.util.Pair;
import com.google.protobuf.ByteString;
import com.google.thoughtcrimegson.Gson; import com.google.thoughtcrimegson.Gson;
import org.whispersystems.textsecure.R; import org.whispersystems.textsecure.R;
import org.whispersystems.textsecure.Release; import org.whispersystems.textsecure.Release;
import org.whispersystems.textsecure.crypto.IdentityKey;
import org.whispersystems.textsecure.directory.DirectoryDescriptor; import org.whispersystems.textsecure.directory.DirectoryDescriptor;
import org.whispersystems.textsecure.directory.NumberFilter; import org.whispersystems.textsecure.encoded.PreKeyProtos.PreKeyEntity;
import org.whispersystems.textsecure.storage.PreKeyRecord;
import org.whispersystems.textsecure.util.Base64;
import org.whispersystems.textsecure.util.Util; import org.whispersystems.textsecure.util.Util;
import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.HttpsURLConnection;
@ -31,7 +34,6 @@ import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException; import java.security.cert.CertificateException;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.zip.GZIPInputStream;
public class PushServiceSocket { public class PushServiceSocket {
@ -57,16 +59,16 @@ public class PushServiceSocket {
this.trustManagerFactory = initializeTrustManagerFactory(context); this.trustManagerFactory = initializeTrustManagerFactory(context);
} }
public void createAccount(boolean voice) throws IOException, RateLimitException { public void createAccount(boolean voice) throws IOException {
String path = voice ? CREATE_ACCOUNT_VOICE_PATH : CREATE_ACCOUNT_SMS_PATH; String path = voice ? CREATE_ACCOUNT_VOICE_PATH : CREATE_ACCOUNT_SMS_PATH;
makeRequest(String.format(path, localNumber), "POST", null); makeRequest(String.format(path, localNumber), "POST", null);
} }
public void verifyAccount(String verificationCode) throws IOException, RateLimitException { public void verifyAccount(String verificationCode) throws IOException {
makeRequest(String.format(VERIFY_ACCOUNT_PATH, verificationCode), "PUT", null); makeRequest(String.format(VERIFY_ACCOUNT_PATH, verificationCode), "PUT", null);
} }
public void registerGcmId(String gcmRegistrationId) throws IOException, RateLimitException { public void registerGcmId(String gcmRegistrationId) throws IOException {
GcmRegistrationId registration = new GcmRegistrationId(gcmRegistrationId); GcmRegistrationId registration = new GcmRegistrationId(gcmRegistrationId);
makeRequest(REGISTER_GCM_PATH, "PUT", new Gson().toJson(registration)); makeRequest(REGISTER_GCM_PATH, "PUT", new Gson().toJson(registration));
} }
@ -106,8 +108,23 @@ public class PushServiceSocket {
throw new IOException("Got send failure: " + response.getFailure().get(0)); throw new IOException("Got send failure: " + response.getFailure().get(0));
} }
public void registerPreKeys(PreKeyList keys) throws IOException { public void registerPreKeys(IdentityKey identityKey, List<PreKeyRecord> records)
makeRequest(PREKEY_PATH, "PUT", new Gson().toJson(keys)); throws IOException
{
List<String> encoded = new LinkedList<String>();
for (PreKeyRecord record : records) {
PreKeyEntity entity = PreKeyEntity.newBuilder().setId(record.getId())
.setPublicKey(ByteString.copyFrom(record.getEncodedPublicKey()))
.setIdentityKey(ByteString.copyFrom(identityKey.serialize()))
.build();
String encodedEntity = Base64.encodeBytesWithoutPadding(entity.toByteArray());
encoded.add(encodedEntity);
}
makeRequest(PREKEY_PATH, "PUT", new Gson().toJson(new PreKeyList(encoded)));
} }
@ -317,7 +334,7 @@ public class PushServiceSocket {
private String getAuthorizationHeader() { private String getAuthorizationHeader() {
try { try {
return "Basic " + new String(Base64.encode((localNumber + ":" + password).getBytes("UTF-8"), Base64.NO_WRAP)); return "Basic " + Base64.encodeBytes((localNumber + ":" + password).getBytes("UTF-8"));
} catch (UnsupportedEncodingException e) { } catch (UnsupportedEncodingException e) {
throw new AssertionError(e); throw new AssertionError(e);
} }

View File

@ -4,6 +4,7 @@ import android.content.Context;
import android.util.Log; import android.util.Log;
import org.whispersystems.textsecure.crypto.InvalidKeyException; import org.whispersystems.textsecure.crypto.InvalidKeyException;
import org.whispersystems.textsecure.crypto.KeyUtil;
import org.whispersystems.textsecure.crypto.MasterSecret; import org.whispersystems.textsecure.crypto.MasterSecret;
import org.whispersystems.textsecure.crypto.PreKeyPair; import org.whispersystems.textsecure.crypto.PreKeyPair;
@ -51,6 +52,10 @@ public class PreKeyRecord extends Record {
return keyPair; return keyPair;
} }
public byte[] getEncodedPublicKey() {
return KeyUtil.encodePoint(keyPair.getPublicKey().getQ());
}
public static boolean hasRecord(Context context, long id) { public static boolean hasRecord(Context context, long id) {
Log.w("PreKeyRecord", "Checking: " + id); Log.w("PreKeyRecord", "Checking: " + id);
return Record.hasRecord(context, PREKEY_DIRECTORY, id+""); return Record.hasRecord(context, PREKEY_DIRECTORY, id+"");

View File

@ -22,7 +22,6 @@ import android.content.SharedPreferences.Editor;
import android.util.Log; import android.util.Log;
import org.spongycastle.asn1.ASN1Encoding; import org.spongycastle.asn1.ASN1Encoding;
import org.spongycastle.asn1.ASN1Object;
import org.spongycastle.asn1.ASN1Primitive; import org.spongycastle.asn1.ASN1Primitive;
import org.spongycastle.asn1.ASN1Sequence; import org.spongycastle.asn1.ASN1Sequence;
import org.spongycastle.asn1.DERInteger; import org.spongycastle.asn1.DERInteger;

View File

@ -24,7 +24,7 @@ import org.whispersystems.textsecure.crypto.InvalidKeyException;
import org.whispersystems.textsecure.crypto.MasterSecret; import org.whispersystems.textsecure.crypto.MasterSecret;
import org.whispersystems.textsecure.crypto.PublicKey; import org.whispersystems.textsecure.crypto.PublicKey;
import org.whispersystems.textsecure.storage.LocalKeyRecord; import org.whispersystems.textsecure.storage.LocalKeyRecord;
import org.whispersystems.textsecure.protocol.Message; import org.whispersystems.textsecure.crypto.protocol.Message;
import org.whispersystems.textsecure.util.Base64; import org.whispersystems.textsecure.util.Base64;
import org.whispersystems.textsecure.util.Conversions; import org.whispersystems.textsecure.util.Conversions;

View File

@ -26,7 +26,7 @@ import org.whispersystems.textsecure.crypto.MasterSecret;
import org.whispersystems.textsecure.storage.LocalKeyRecord; import org.whispersystems.textsecure.storage.LocalKeyRecord;
import org.whispersystems.textsecure.storage.RemoteKeyRecord; import org.whispersystems.textsecure.storage.RemoteKeyRecord;
import org.whispersystems.textsecure.storage.SessionRecord; import org.whispersystems.textsecure.storage.SessionRecord;
import org.whispersystems.textsecure.protocol.Message; import org.whispersystems.textsecure.crypto.protocol.Message;
import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.service.KeyCachingService; import org.thoughtcrime.securesms.service.KeyCachingService;
import org.thoughtcrime.securesms.sms.MessageSender; import org.thoughtcrime.securesms.sms.MessageSender;

View File

@ -13,6 +13,8 @@ import android.util.Pair;
import com.google.android.gcm.GCMRegistrar; import com.google.android.gcm.GCMRegistrar;
import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil;
import org.whispersystems.textsecure.crypto.IdentityKey;
import org.whispersystems.textsecure.crypto.MasterSecret; import org.whispersystems.textsecure.crypto.MasterSecret;
import org.whispersystems.textsecure.crypto.PreKeyUtil; import org.whispersystems.textsecure.crypto.PreKeyUtil;
import org.whispersystems.textsecure.storage.PreKeyRecord; import org.whispersystems.textsecure.storage.PreKeyRecord;
@ -268,8 +270,9 @@ public class RegistrationService extends Service {
throws GcmRegistrationTimeoutException, IOException throws GcmRegistrationTimeoutException, IOException
{ {
setState(new RegistrationState(RegistrationState.STATE_GENERATING_KEYS, number)); setState(new RegistrationState(RegistrationState.STATE_GENERATING_KEYS, number));
List<PreKeyRecord> records = waitForPreKeys(masterSecret); IdentityKey identityKey = IdentityKeyUtil.getIdentityKey(this);
socket.registerPreKeys(PreKeyUtil.toJson(records)); List<PreKeyRecord> records = waitForPreKeys(masterSecret);
socket.registerPreKeys(identityKey, records);
setState(new RegistrationState(RegistrationState.STATE_GCM_REGISTERING, number)); setState(new RegistrationState(RegistrationState.STATE_GCM_REGISTERING, number));
GCMRegistrar.register(this, GcmIntentService.GCM_SENDER_ID); GCMRegistrar.register(this, GcmIntentService.GCM_SENDER_ID);