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

@ -5,5 +5,6 @@ option java_outer_classname = "PreKeyProtos";
message PreKeyEntity {
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) {
try {
File directory = getPreKeysDirectory(context);

View File

@ -22,7 +22,7 @@ import android.util.Log;
import org.spongycastle.crypto.AsymmetricCipherKeyPair;
import org.spongycastle.crypto.agreement.ECDHBasicAgreement;
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.InvalidKeyIdException;
import org.whispersystems.textsecure.storage.LocalKeyRecord;

View File

@ -14,7 +14,7 @@
* 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.whispersystems.textsecure.protocol;
package org.whispersystems.textsecure.crypto.protocol;
import android.util.Log;

View File

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

View File

@ -1,15 +1,18 @@
package org.whispersystems.textsecure.push;
import android.content.Context;
import android.util.Base64;
import android.util.Log;
import android.util.Pair;
import com.google.protobuf.ByteString;
import com.google.thoughtcrimegson.Gson;
import org.whispersystems.textsecure.R;
import org.whispersystems.textsecure.Release;
import org.whispersystems.textsecure.crypto.IdentityKey;
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 javax.net.ssl.HttpsURLConnection;
@ -31,7 +34,6 @@ import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.util.LinkedList;
import java.util.List;
import java.util.zip.GZIPInputStream;
public class PushServiceSocket {
@ -57,16 +59,16 @@ public class PushServiceSocket {
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;
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);
}
public void registerGcmId(String gcmRegistrationId) throws IOException, RateLimitException {
public void registerGcmId(String gcmRegistrationId) throws IOException {
GcmRegistrationId registration = new GcmRegistrationId(gcmRegistrationId);
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));
}
public void registerPreKeys(PreKeyList keys) throws IOException {
makeRequest(PREKEY_PATH, "PUT", new Gson().toJson(keys));
public void registerPreKeys(IdentityKey identityKey, List<PreKeyRecord> records)
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() {
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) {
throw new AssertionError(e);
}

View File

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

View File

@ -22,7 +22,6 @@ import android.content.SharedPreferences.Editor;
import android.util.Log;
import org.spongycastle.asn1.ASN1Encoding;
import org.spongycastle.asn1.ASN1Object;
import org.spongycastle.asn1.ASN1Primitive;
import org.spongycastle.asn1.ASN1Sequence;
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.PublicKey;
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.Conversions;

View File

@ -26,7 +26,7 @@ import org.whispersystems.textsecure.crypto.MasterSecret;
import org.whispersystems.textsecure.storage.LocalKeyRecord;
import org.whispersystems.textsecure.storage.RemoteKeyRecord;
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.service.KeyCachingService;
import org.thoughtcrime.securesms.sms.MessageSender;

View File

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