diff --git a/.gitignore b/.gitignore
index a3758d8db8..5c22dd338c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -20,3 +20,5 @@ signing.properties
gradle
gradlew
gradlew.bat
+library/lib/
+library/obj/
diff --git a/library/build.gradle b/library/build.gradle
index 46facf9be9..cd7eab85fc 100644
--- a/library/build.gradle
+++ b/library/build.gradle
@@ -22,7 +22,8 @@ dependencies {
compile 'com.google.protobuf:protobuf-java:2.4.1'
compile 'com.madgag:sc-light-jdk15on:1.47.0.2'
compile 'com.googlecode.libphonenumber:libphonenumber:5.3'
- compile 'org.whispersystems:gson:2.1'
+ compile 'org.whispersystems:gson:2.2.4'
+ compile fileTree(dir: 'libs', include: 'armeabi.jar')
}
android {
diff --git a/library/jni/Android.mk b/library/jni/Android.mk
new file mode 100644
index 0000000000..20211e82e7
--- /dev/null
+++ b/library/jni/Android.mk
@@ -0,0 +1,17 @@
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libcurve25519-donna
+LOCAL_SRC_FILES := curve25519-donna.c
+
+include $(BUILD_STATIC_LIBRARY)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libcurve25519
+LOCAL_SRC_FILES := curve25519-donna-jni.c
+
+LOCAL_STATIC_LIBRARIES := libcurve25519-donna
+
+include $(BUILD_SHARED_LIBRARY)
\ No newline at end of file
diff --git a/library/jni/curve25519-donna-jni.c b/library/jni/curve25519-donna-jni.c
new file mode 100644
index 0000000000..bcda8a4a5a
--- /dev/null
+++ b/library/jni/curve25519-donna-jni.c
@@ -0,0 +1,70 @@
+/**
+ * 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 .
+ */
+
+#include
+#include
+
+#include
+#include "curve25519-donna.h"
+
+JNIEXPORT jbyteArray JNICALL Java_org_whispersystems_textsecure_crypto_ecc_Curve25519_generatePrivateKey
+ (JNIEnv *env, jclass clazz, jbyteArray random)
+{
+ uint8_t* privateKey = (uint8_t*)(*env)->GetByteArrayElements(env, random, 0);
+
+ privateKey[0] &= 248;
+ privateKey[31] &= 127;
+ privateKey[31] |= 64;
+
+ (*env)->ReleaseByteArrayElements(env, random, privateKey, 0);
+
+ return random;
+}
+
+JNIEXPORT jbyteArray JNICALL Java_org_whispersystems_textsecure_crypto_ecc_Curve25519_generatePublicKey
+ (JNIEnv *env, jclass clazz, jbyteArray privateKey)
+{
+ static const uint8_t basepoint[32] = {9};
+
+ jbyteArray publicKey = (*env)->NewByteArray(env, 32);
+ uint8_t* publicKeyBytes = (uint8_t*)(*env)->GetByteArrayElements(env, publicKey, 0);
+ uint8_t* privateKeyBytes = (uint8_t*)(*env)->GetByteArrayElements(env, privateKey, 0);
+
+ curve25519_donna(publicKeyBytes, privateKeyBytes, basepoint);
+
+ (*env)->ReleaseByteArrayElements(env, publicKey, publicKeyBytes, 0);
+ (*env)->ReleaseByteArrayElements(env, privateKey, privateKeyBytes, 0);
+
+ return publicKey;
+}
+
+JNIEXPORT jbyteArray JNICALL Java_org_whispersystems_textsecure_crypto_ecc_Curve25519_calculateAgreement
+ (JNIEnv *env, jclass clazz, jbyteArray privateKey, jbyteArray publicKey)
+{
+ jbyteArray sharedKey = (*env)->NewByteArray(env, 32);
+ uint8_t* sharedKeyBytes = (uint8_t*)(*env)->GetByteArrayElements(env, sharedKey, 0);
+ uint8_t* privateKeyBytes = (uint8_t*)(*env)->GetByteArrayElements(env, privateKey, 0);
+ uint8_t* publicKeyBytes = (uint8_t*)(*env)->GetByteArrayElements(env, publicKey, 0);
+
+ curve25519_donna(sharedKeyBytes, privateKeyBytes, publicKeyBytes);
+
+ (*env)->ReleaseByteArrayElements(env, sharedKey, sharedKeyBytes, 0);
+ (*env)->ReleaseByteArrayElements(env, publicKey, publicKeyBytes, 0);
+ (*env)->ReleaseByteArrayElements(env, privateKey, privateKeyBytes, 0);
+
+ return sharedKey;
+}
diff --git a/library/jni/curve25519-donna.c b/library/jni/curve25519-donna.c
new file mode 100644
index 0000000000..bb1262e5e9
--- /dev/null
+++ b/library/jni/curve25519-donna.c
@@ -0,0 +1,734 @@
+/* Copyright 2008, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * curve25519-donna: Curve25519 elliptic curve, public key function
+ *
+ * http://code.google.com/p/curve25519-donna/
+ *
+ * Adam Langley
+ *
+ * Derived from public domain C code by Daniel J. Bernstein
+ *
+ * More information about curve25519 can be found here
+ * http://cr.yp.to/ecdh.html
+ *
+ * djb's sample implementation of curve25519 is written in a special assembly
+ * language called qhasm and uses the floating point registers.
+ *
+ * This is, almost, a clean room reimplementation from the curve25519 paper. It
+ * uses many of the tricks described therein. Only the crecip function is taken
+ * from the sample implementation.
+ */
+
+#include
+#include
+
+#ifdef _MSC_VER
+#define inline __inline
+#endif
+
+typedef uint8_t u8;
+typedef int32_t s32;
+typedef int64_t limb;
+
+/* Field element representation:
+ *
+ * Field elements are written as an array of signed, 64-bit limbs, least
+ * significant first. The value of the field element is:
+ * x[0] + 2^26·x[1] + x^51·x[2] + 2^102·x[3] + ...
+ *
+ * i.e. the limbs are 26, 25, 26, 25, ... bits wide.
+ */
+
+/* Sum two numbers: output += in */
+static void fsum(limb *output, const limb *in) {
+ unsigned i;
+ for (i = 0; i < 10; i += 2) {
+ output[0+i] = (output[0+i] + in[0+i]);
+ output[1+i] = (output[1+i] + in[1+i]);
+ }
+}
+
+/* Find the difference of two numbers: output = in - output
+ * (note the order of the arguments!)
+ */
+static void fdifference(limb *output, const limb *in) {
+ unsigned i;
+ for (i = 0; i < 10; ++i) {
+ output[i] = (in[i] - output[i]);
+ }
+}
+
+/* Multiply a number by a scalar: output = in * scalar */
+static void fscalar_product(limb *output, const limb *in, const limb scalar) {
+ unsigned i;
+ for (i = 0; i < 10; ++i) {
+ output[i] = in[i] * scalar;
+ }
+}
+
+/* Multiply two numbers: output = in2 * in
+ *
+ * output must be distinct to both inputs. The inputs are reduced coefficient
+ * form, the output is not.
+ */
+static void fproduct(limb *output, const limb *in2, const limb *in) {
+ output[0] = ((limb) ((s32) in2[0])) * ((s32) in[0]);
+ output[1] = ((limb) ((s32) in2[0])) * ((s32) in[1]) +
+ ((limb) ((s32) in2[1])) * ((s32) in[0]);
+ output[2] = 2 * ((limb) ((s32) in2[1])) * ((s32) in[1]) +
+ ((limb) ((s32) in2[0])) * ((s32) in[2]) +
+ ((limb) ((s32) in2[2])) * ((s32) in[0]);
+ output[3] = ((limb) ((s32) in2[1])) * ((s32) in[2]) +
+ ((limb) ((s32) in2[2])) * ((s32) in[1]) +
+ ((limb) ((s32) in2[0])) * ((s32) in[3]) +
+ ((limb) ((s32) in2[3])) * ((s32) in[0]);
+ output[4] = ((limb) ((s32) in2[2])) * ((s32) in[2]) +
+ 2 * (((limb) ((s32) in2[1])) * ((s32) in[3]) +
+ ((limb) ((s32) in2[3])) * ((s32) in[1])) +
+ ((limb) ((s32) in2[0])) * ((s32) in[4]) +
+ ((limb) ((s32) in2[4])) * ((s32) in[0]);
+ output[5] = ((limb) ((s32) in2[2])) * ((s32) in[3]) +
+ ((limb) ((s32) in2[3])) * ((s32) in[2]) +
+ ((limb) ((s32) in2[1])) * ((s32) in[4]) +
+ ((limb) ((s32) in2[4])) * ((s32) in[1]) +
+ ((limb) ((s32) in2[0])) * ((s32) in[5]) +
+ ((limb) ((s32) in2[5])) * ((s32) in[0]);
+ output[6] = 2 * (((limb) ((s32) in2[3])) * ((s32) in[3]) +
+ ((limb) ((s32) in2[1])) * ((s32) in[5]) +
+ ((limb) ((s32) in2[5])) * ((s32) in[1])) +
+ ((limb) ((s32) in2[2])) * ((s32) in[4]) +
+ ((limb) ((s32) in2[4])) * ((s32) in[2]) +
+ ((limb) ((s32) in2[0])) * ((s32) in[6]) +
+ ((limb) ((s32) in2[6])) * ((s32) in[0]);
+ output[7] = ((limb) ((s32) in2[3])) * ((s32) in[4]) +
+ ((limb) ((s32) in2[4])) * ((s32) in[3]) +
+ ((limb) ((s32) in2[2])) * ((s32) in[5]) +
+ ((limb) ((s32) in2[5])) * ((s32) in[2]) +
+ ((limb) ((s32) in2[1])) * ((s32) in[6]) +
+ ((limb) ((s32) in2[6])) * ((s32) in[1]) +
+ ((limb) ((s32) in2[0])) * ((s32) in[7]) +
+ ((limb) ((s32) in2[7])) * ((s32) in[0]);
+ output[8] = ((limb) ((s32) in2[4])) * ((s32) in[4]) +
+ 2 * (((limb) ((s32) in2[3])) * ((s32) in[5]) +
+ ((limb) ((s32) in2[5])) * ((s32) in[3]) +
+ ((limb) ((s32) in2[1])) * ((s32) in[7]) +
+ ((limb) ((s32) in2[7])) * ((s32) in[1])) +
+ ((limb) ((s32) in2[2])) * ((s32) in[6]) +
+ ((limb) ((s32) in2[6])) * ((s32) in[2]) +
+ ((limb) ((s32) in2[0])) * ((s32) in[8]) +
+ ((limb) ((s32) in2[8])) * ((s32) in[0]);
+ output[9] = ((limb) ((s32) in2[4])) * ((s32) in[5]) +
+ ((limb) ((s32) in2[5])) * ((s32) in[4]) +
+ ((limb) ((s32) in2[3])) * ((s32) in[6]) +
+ ((limb) ((s32) in2[6])) * ((s32) in[3]) +
+ ((limb) ((s32) in2[2])) * ((s32) in[7]) +
+ ((limb) ((s32) in2[7])) * ((s32) in[2]) +
+ ((limb) ((s32) in2[1])) * ((s32) in[8]) +
+ ((limb) ((s32) in2[8])) * ((s32) in[1]) +
+ ((limb) ((s32) in2[0])) * ((s32) in[9]) +
+ ((limb) ((s32) in2[9])) * ((s32) in[0]);
+ output[10] = 2 * (((limb) ((s32) in2[5])) * ((s32) in[5]) +
+ ((limb) ((s32) in2[3])) * ((s32) in[7]) +
+ ((limb) ((s32) in2[7])) * ((s32) in[3]) +
+ ((limb) ((s32) in2[1])) * ((s32) in[9]) +
+ ((limb) ((s32) in2[9])) * ((s32) in[1])) +
+ ((limb) ((s32) in2[4])) * ((s32) in[6]) +
+ ((limb) ((s32) in2[6])) * ((s32) in[4]) +
+ ((limb) ((s32) in2[2])) * ((s32) in[8]) +
+ ((limb) ((s32) in2[8])) * ((s32) in[2]);
+ output[11] = ((limb) ((s32) in2[5])) * ((s32) in[6]) +
+ ((limb) ((s32) in2[6])) * ((s32) in[5]) +
+ ((limb) ((s32) in2[4])) * ((s32) in[7]) +
+ ((limb) ((s32) in2[7])) * ((s32) in[4]) +
+ ((limb) ((s32) in2[3])) * ((s32) in[8]) +
+ ((limb) ((s32) in2[8])) * ((s32) in[3]) +
+ ((limb) ((s32) in2[2])) * ((s32) in[9]) +
+ ((limb) ((s32) in2[9])) * ((s32) in[2]);
+ output[12] = ((limb) ((s32) in2[6])) * ((s32) in[6]) +
+ 2 * (((limb) ((s32) in2[5])) * ((s32) in[7]) +
+ ((limb) ((s32) in2[7])) * ((s32) in[5]) +
+ ((limb) ((s32) in2[3])) * ((s32) in[9]) +
+ ((limb) ((s32) in2[9])) * ((s32) in[3])) +
+ ((limb) ((s32) in2[4])) * ((s32) in[8]) +
+ ((limb) ((s32) in2[8])) * ((s32) in[4]);
+ output[13] = ((limb) ((s32) in2[6])) * ((s32) in[7]) +
+ ((limb) ((s32) in2[7])) * ((s32) in[6]) +
+ ((limb) ((s32) in2[5])) * ((s32) in[8]) +
+ ((limb) ((s32) in2[8])) * ((s32) in[5]) +
+ ((limb) ((s32) in2[4])) * ((s32) in[9]) +
+ ((limb) ((s32) in2[9])) * ((s32) in[4]);
+ output[14] = 2 * (((limb) ((s32) in2[7])) * ((s32) in[7]) +
+ ((limb) ((s32) in2[5])) * ((s32) in[9]) +
+ ((limb) ((s32) in2[9])) * ((s32) in[5])) +
+ ((limb) ((s32) in2[6])) * ((s32) in[8]) +
+ ((limb) ((s32) in2[8])) * ((s32) in[6]);
+ output[15] = ((limb) ((s32) in2[7])) * ((s32) in[8]) +
+ ((limb) ((s32) in2[8])) * ((s32) in[7]) +
+ ((limb) ((s32) in2[6])) * ((s32) in[9]) +
+ ((limb) ((s32) in2[9])) * ((s32) in[6]);
+ output[16] = ((limb) ((s32) in2[8])) * ((s32) in[8]) +
+ 2 * (((limb) ((s32) in2[7])) * ((s32) in[9]) +
+ ((limb) ((s32) in2[9])) * ((s32) in[7]));
+ output[17] = ((limb) ((s32) in2[8])) * ((s32) in[9]) +
+ ((limb) ((s32) in2[9])) * ((s32) in[8]);
+ output[18] = 2 * ((limb) ((s32) in2[9])) * ((s32) in[9]);
+}
+
+/* Reduce a long form to a short form by taking the input mod 2^255 - 19. */
+static void freduce_degree(limb *output) {
+ /* Each of these shifts and adds ends up multiplying the value by 19. */
+ output[8] += output[18] << 4;
+ output[8] += output[18] << 1;
+ output[8] += output[18];
+ output[7] += output[17] << 4;
+ output[7] += output[17] << 1;
+ output[7] += output[17];
+ output[6] += output[16] << 4;
+ output[6] += output[16] << 1;
+ output[6] += output[16];
+ output[5] += output[15] << 4;
+ output[5] += output[15] << 1;
+ output[5] += output[15];
+ output[4] += output[14] << 4;
+ output[4] += output[14] << 1;
+ output[4] += output[14];
+ output[3] += output[13] << 4;
+ output[3] += output[13] << 1;
+ output[3] += output[13];
+ output[2] += output[12] << 4;
+ output[2] += output[12] << 1;
+ output[2] += output[12];
+ output[1] += output[11] << 4;
+ output[1] += output[11] << 1;
+ output[1] += output[11];
+ output[0] += output[10] << 4;
+ output[0] += output[10] << 1;
+ output[0] += output[10];
+}
+
+#if (-1 & 3) != 3
+#error "This code only works on a two's complement system"
+#endif
+
+/* return v / 2^26, using only shifts and adds. */
+static inline limb
+div_by_2_26(const limb v)
+{
+ /* High word of v; no shift needed*/
+ const uint32_t highword = (uint32_t) (((uint64_t) v) >> 32);
+ /* Set to all 1s if v was negative; else set to 0s. */
+ const int32_t sign = ((int32_t) highword) >> 31;
+ /* Set to 0x3ffffff if v was negative; else set to 0. */
+ const int32_t roundoff = ((uint32_t) sign) >> 6;
+ /* Should return v / (1<<26) */
+ return (v + roundoff) >> 26;
+}
+
+/* return v / (2^25), using only shifts and adds. */
+static inline limb
+div_by_2_25(const limb v)
+{
+ /* High word of v; no shift needed*/
+ const uint32_t highword = (uint32_t) (((uint64_t) v) >> 32);
+ /* Set to all 1s if v was negative; else set to 0s. */
+ const int32_t sign = ((int32_t) highword) >> 31;
+ /* Set to 0x1ffffff if v was negative; else set to 0. */
+ const int32_t roundoff = ((uint32_t) sign) >> 7;
+ /* Should return v / (1<<25) */
+ return (v + roundoff) >> 25;
+}
+
+static inline s32
+div_s32_by_2_25(const s32 v)
+{
+ const s32 roundoff = ((uint32_t)(v >> 31)) >> 7;
+ return (v + roundoff) >> 25;
+}
+
+/* Reduce all coefficients of the short form input so that |x| < 2^26.
+ *
+ * On entry: |output[i]| < 2^62
+ */
+static void freduce_coefficients(limb *output) {
+ unsigned i;
+
+ output[10] = 0;
+
+ for (i = 0; i < 10; i += 2) {
+ limb over = div_by_2_26(output[i]);
+ output[i] -= over << 26;
+ output[i+1] += over;
+
+ over = div_by_2_25(output[i+1]);
+ output[i+1] -= over << 25;
+ output[i+2] += over;
+ }
+ /* Now |output[10]| < 2 ^ 38 and all other coefficients are reduced. */
+ output[0] += output[10] << 4;
+ output[0] += output[10] << 1;
+ output[0] += output[10];
+
+ output[10] = 0;
+
+ /* Now output[1..9] are reduced, and |output[0]| < 2^26 + 19 * 2^38
+ * So |over| will be no more than 77825 */
+ {
+ limb over = div_by_2_26(output[0]);
+ output[0] -= over << 26;
+ output[1] += over;
+ }
+
+ /* Now output[0,2..9] are reduced, and |output[1]| < 2^25 + 77825
+ * So |over| will be no more than 1. */
+ {
+ /* output[1] fits in 32 bits, so we can use div_s32_by_2_25 here. */
+ s32 over32 = div_s32_by_2_25((s32) output[1]);
+ output[1] -= over32 << 25;
+ output[2] += over32;
+ }
+
+ /* Finally, output[0,1,3..9] are reduced, and output[2] is "nearly reduced":
+ * we have |output[2]| <= 2^26. This is good enough for all of our math,
+ * but it will require an extra freduce_coefficients before fcontract. */
+}
+
+/* A helpful wrapper around fproduct: output = in * in2.
+ *
+ * output must be distinct to both inputs. The output is reduced degree and
+ * reduced coefficient.
+ */
+static void
+fmul(limb *output, const limb *in, const limb *in2) {
+ limb t[19];
+ fproduct(t, in, in2);
+ freduce_degree(t);
+ freduce_coefficients(t);
+ memcpy(output, t, sizeof(limb) * 10);
+}
+
+static void fsquare_inner(limb *output, const limb *in) {
+ output[0] = ((limb) ((s32) in[0])) * ((s32) in[0]);
+ output[1] = 2 * ((limb) ((s32) in[0])) * ((s32) in[1]);
+ output[2] = 2 * (((limb) ((s32) in[1])) * ((s32) in[1]) +
+ ((limb) ((s32) in[0])) * ((s32) in[2]));
+ output[3] = 2 * (((limb) ((s32) in[1])) * ((s32) in[2]) +
+ ((limb) ((s32) in[0])) * ((s32) in[3]));
+ output[4] = ((limb) ((s32) in[2])) * ((s32) in[2]) +
+ 4 * ((limb) ((s32) in[1])) * ((s32) in[3]) +
+ 2 * ((limb) ((s32) in[0])) * ((s32) in[4]);
+ output[5] = 2 * (((limb) ((s32) in[2])) * ((s32) in[3]) +
+ ((limb) ((s32) in[1])) * ((s32) in[4]) +
+ ((limb) ((s32) in[0])) * ((s32) in[5]));
+ output[6] = 2 * (((limb) ((s32) in[3])) * ((s32) in[3]) +
+ ((limb) ((s32) in[2])) * ((s32) in[4]) +
+ ((limb) ((s32) in[0])) * ((s32) in[6]) +
+ 2 * ((limb) ((s32) in[1])) * ((s32) in[5]));
+ output[7] = 2 * (((limb) ((s32) in[3])) * ((s32) in[4]) +
+ ((limb) ((s32) in[2])) * ((s32) in[5]) +
+ ((limb) ((s32) in[1])) * ((s32) in[6]) +
+ ((limb) ((s32) in[0])) * ((s32) in[7]));
+ output[8] = ((limb) ((s32) in[4])) * ((s32) in[4]) +
+ 2 * (((limb) ((s32) in[2])) * ((s32) in[6]) +
+ ((limb) ((s32) in[0])) * ((s32) in[8]) +
+ 2 * (((limb) ((s32) in[1])) * ((s32) in[7]) +
+ ((limb) ((s32) in[3])) * ((s32) in[5])));
+ output[9] = 2 * (((limb) ((s32) in[4])) * ((s32) in[5]) +
+ ((limb) ((s32) in[3])) * ((s32) in[6]) +
+ ((limb) ((s32) in[2])) * ((s32) in[7]) +
+ ((limb) ((s32) in[1])) * ((s32) in[8]) +
+ ((limb) ((s32) in[0])) * ((s32) in[9]));
+ output[10] = 2 * (((limb) ((s32) in[5])) * ((s32) in[5]) +
+ ((limb) ((s32) in[4])) * ((s32) in[6]) +
+ ((limb) ((s32) in[2])) * ((s32) in[8]) +
+ 2 * (((limb) ((s32) in[3])) * ((s32) in[7]) +
+ ((limb) ((s32) in[1])) * ((s32) in[9])));
+ output[11] = 2 * (((limb) ((s32) in[5])) * ((s32) in[6]) +
+ ((limb) ((s32) in[4])) * ((s32) in[7]) +
+ ((limb) ((s32) in[3])) * ((s32) in[8]) +
+ ((limb) ((s32) in[2])) * ((s32) in[9]));
+ output[12] = ((limb) ((s32) in[6])) * ((s32) in[6]) +
+ 2 * (((limb) ((s32) in[4])) * ((s32) in[8]) +
+ 2 * (((limb) ((s32) in[5])) * ((s32) in[7]) +
+ ((limb) ((s32) in[3])) * ((s32) in[9])));
+ output[13] = 2 * (((limb) ((s32) in[6])) * ((s32) in[7]) +
+ ((limb) ((s32) in[5])) * ((s32) in[8]) +
+ ((limb) ((s32) in[4])) * ((s32) in[9]));
+ output[14] = 2 * (((limb) ((s32) in[7])) * ((s32) in[7]) +
+ ((limb) ((s32) in[6])) * ((s32) in[8]) +
+ 2 * ((limb) ((s32) in[5])) * ((s32) in[9]));
+ output[15] = 2 * (((limb) ((s32) in[7])) * ((s32) in[8]) +
+ ((limb) ((s32) in[6])) * ((s32) in[9]));
+ output[16] = ((limb) ((s32) in[8])) * ((s32) in[8]) +
+ 4 * ((limb) ((s32) in[7])) * ((s32) in[9]);
+ output[17] = 2 * ((limb) ((s32) in[8])) * ((s32) in[9]);
+ output[18] = 2 * ((limb) ((s32) in[9])) * ((s32) in[9]);
+}
+
+static void
+fsquare(limb *output, const limb *in) {
+ limb t[19];
+ fsquare_inner(t, in);
+ freduce_degree(t);
+ freduce_coefficients(t);
+ memcpy(output, t, sizeof(limb) * 10);
+}
+
+/* Take a little-endian, 32-byte number and expand it into polynomial form */
+static void
+fexpand(limb *output, const u8 *input) {
+#define F(n,start,shift,mask) \
+ output[n] = ((((limb) input[start + 0]) | \
+ ((limb) input[start + 1]) << 8 | \
+ ((limb) input[start + 2]) << 16 | \
+ ((limb) input[start + 3]) << 24) >> shift) & mask;
+ F(0, 0, 0, 0x3ffffff);
+ F(1, 3, 2, 0x1ffffff);
+ F(2, 6, 3, 0x3ffffff);
+ F(3, 9, 5, 0x1ffffff);
+ F(4, 12, 6, 0x3ffffff);
+ F(5, 16, 0, 0x1ffffff);
+ F(6, 19, 1, 0x3ffffff);
+ F(7, 22, 3, 0x1ffffff);
+ F(8, 25, 4, 0x3ffffff);
+ F(9, 28, 6, 0x3ffffff);
+#undef F
+}
+
+#if (-32 >> 1) != -16
+#error "This code only works when >> does sign-extension on negative numbers"
+#endif
+
+/* Take a fully reduced polynomial form number and contract it into a
+ * little-endian, 32-byte array
+ */
+static void
+fcontract(u8 *output, limb *input) {
+ int i;
+ int j;
+
+ for (j = 0; j < 2; ++j) {
+ for (i = 0; i < 9; ++i) {
+ if ((i & 1) == 1) {
+ /* This calculation is a time-invariant way to make input[i] positive
+ by borrowing from the next-larger limb.
+ */
+ const s32 mask = (s32)(input[i]) >> 31;
+ const s32 carry = -(((s32)(input[i]) & mask) >> 25);
+ input[i] = (s32)(input[i]) + (carry << 25);
+ input[i+1] = (s32)(input[i+1]) - carry;
+ } else {
+ const s32 mask = (s32)(input[i]) >> 31;
+ const s32 carry = -(((s32)(input[i]) & mask) >> 26);
+ input[i] = (s32)(input[i]) + (carry << 26);
+ input[i+1] = (s32)(input[i+1]) - carry;
+ }
+ }
+ {
+ const s32 mask = (s32)(input[9]) >> 31;
+ const s32 carry = -(((s32)(input[9]) & mask) >> 25);
+ input[9] = (s32)(input[9]) + (carry << 25);
+ input[0] = (s32)(input[0]) - (carry * 19);
+ }
+ }
+
+ /* The first borrow-propagation pass above ended with every limb
+ except (possibly) input[0] non-negative.
+
+ Since each input limb except input[0] is decreased by at most 1
+ by a borrow-propagation pass, the second borrow-propagation pass
+ could only have wrapped around to decrease input[0] again if the
+ first pass left input[0] negative *and* input[1] through input[9]
+ were all zero. In that case, input[1] is now 2^25 - 1, and this
+ last borrow-propagation step will leave input[1] non-negative.
+ */
+ {
+ const s32 mask = (s32)(input[0]) >> 31;
+ const s32 carry = -(((s32)(input[0]) & mask) >> 26);
+ input[0] = (s32)(input[0]) + (carry << 26);
+ input[1] = (s32)(input[1]) - carry;
+ }
+
+ /* Both passes through the above loop, plus the last 0-to-1 step, are
+ necessary: if input[9] is -1 and input[0] through input[8] are 0,
+ negative values will remain in the array until the end.
+ */
+
+ input[1] <<= 2;
+ input[2] <<= 3;
+ input[3] <<= 5;
+ input[4] <<= 6;
+ input[6] <<= 1;
+ input[7] <<= 3;
+ input[8] <<= 4;
+ input[9] <<= 6;
+#define F(i, s) \
+ output[s+0] |= input[i] & 0xff; \
+ output[s+1] = (input[i] >> 8) & 0xff; \
+ output[s+2] = (input[i] >> 16) & 0xff; \
+ output[s+3] = (input[i] >> 24) & 0xff;
+ output[0] = 0;
+ output[16] = 0;
+ F(0,0);
+ F(1,3);
+ F(2,6);
+ F(3,9);
+ F(4,12);
+ F(5,16);
+ F(6,19);
+ F(7,22);
+ F(8,25);
+ F(9,28);
+#undef F
+}
+
+/* Input: Q, Q', Q-Q'
+ * Output: 2Q, Q+Q'
+ *
+ * x2 z3: long form
+ * x3 z3: long form
+ * x z: short form, destroyed
+ * xprime zprime: short form, destroyed
+ * qmqp: short form, preserved
+ */
+static void fmonty(limb *x2, limb *z2, /* output 2Q */
+ limb *x3, limb *z3, /* output Q + Q' */
+ limb *x, limb *z, /* input Q */
+ limb *xprime, limb *zprime, /* input Q' */
+ const limb *qmqp /* input Q - Q' */) {
+ limb origx[10], origxprime[10], zzz[19], xx[19], zz[19], xxprime[19],
+ zzprime[19], zzzprime[19], xxxprime[19];
+
+ memcpy(origx, x, 10 * sizeof(limb));
+ fsum(x, z);
+ fdifference(z, origx); // does x - z
+
+ memcpy(origxprime, xprime, sizeof(limb) * 10);
+ fsum(xprime, zprime);
+ fdifference(zprime, origxprime);
+ fproduct(xxprime, xprime, z);
+ fproduct(zzprime, x, zprime);
+ freduce_degree(xxprime);
+ freduce_coefficients(xxprime);
+ freduce_degree(zzprime);
+ freduce_coefficients(zzprime);
+ memcpy(origxprime, xxprime, sizeof(limb) * 10);
+ fsum(xxprime, zzprime);
+ fdifference(zzprime, origxprime);
+ fsquare(xxxprime, xxprime);
+ fsquare(zzzprime, zzprime);
+ fproduct(zzprime, zzzprime, qmqp);
+ freduce_degree(zzprime);
+ freduce_coefficients(zzprime);
+ memcpy(x3, xxxprime, sizeof(limb) * 10);
+ memcpy(z3, zzprime, sizeof(limb) * 10);
+
+ fsquare(xx, x);
+ fsquare(zz, z);
+ fproduct(x2, xx, zz);
+ freduce_degree(x2);
+ freduce_coefficients(x2);
+ fdifference(zz, xx); // does zz = xx - zz
+ memset(zzz + 10, 0, sizeof(limb) * 9);
+ fscalar_product(zzz, zz, 121665);
+ /* No need to call freduce_degree here:
+ fscalar_product doesn't increase the degree of its input. */
+ freduce_coefficients(zzz);
+ fsum(zzz, xx);
+ fproduct(z2, zz, zzz);
+ freduce_degree(z2);
+ freduce_coefficients(z2);
+}
+
+/* Conditionally swap two reduced-form limb arrays if 'iswap' is 1, but leave
+ * them unchanged if 'iswap' is 0. Runs in data-invariant time to avoid
+ * side-channel attacks.
+ *
+ * NOTE that this function requires that 'iswap' be 1 or 0; other values give
+ * wrong results. Also, the two limb arrays must be in reduced-coefficient,
+ * reduced-degree form: the values in a[10..19] or b[10..19] aren't swapped,
+ * and all all values in a[0..9],b[0..9] must have magnitude less than
+ * INT32_MAX.
+ */
+static void
+swap_conditional(limb a[19], limb b[19], limb iswap) {
+ unsigned i;
+ const s32 swap = (s32) -iswap;
+
+ for (i = 0; i < 10; ++i) {
+ const s32 x = swap & ( ((s32)a[i]) ^ ((s32)b[i]) );
+ a[i] = ((s32)a[i]) ^ x;
+ b[i] = ((s32)b[i]) ^ x;
+ }
+}
+
+/* Calculates nQ where Q is the x-coordinate of a point on the curve
+ *
+ * resultx/resultz: the x coordinate of the resulting curve point (short form)
+ * n: a little endian, 32-byte number
+ * q: a point of the curve (short form)
+ */
+static void
+cmult(limb *resultx, limb *resultz, const u8 *n, const limb *q) {
+ limb a[19] = {0}, b[19] = {1}, c[19] = {1}, d[19] = {0};
+ limb *nqpqx = a, *nqpqz = b, *nqx = c, *nqz = d, *t;
+ limb e[19] = {0}, f[19] = {1}, g[19] = {0}, h[19] = {1};
+ limb *nqpqx2 = e, *nqpqz2 = f, *nqx2 = g, *nqz2 = h;
+
+ unsigned i, j;
+
+ memcpy(nqpqx, q, sizeof(limb) * 10);
+
+ for (i = 0; i < 32; ++i) {
+ u8 byte = n[31 - i];
+ for (j = 0; j < 8; ++j) {
+ const limb bit = byte >> 7;
+
+ swap_conditional(nqx, nqpqx, bit);
+ swap_conditional(nqz, nqpqz, bit);
+ fmonty(nqx2, nqz2,
+ nqpqx2, nqpqz2,
+ nqx, nqz,
+ nqpqx, nqpqz,
+ q);
+ swap_conditional(nqx2, nqpqx2, bit);
+ swap_conditional(nqz2, nqpqz2, bit);
+
+ t = nqx;
+ nqx = nqx2;
+ nqx2 = t;
+ t = nqz;
+ nqz = nqz2;
+ nqz2 = t;
+ t = nqpqx;
+ nqpqx = nqpqx2;
+ nqpqx2 = t;
+ t = nqpqz;
+ nqpqz = nqpqz2;
+ nqpqz2 = t;
+
+ byte <<= 1;
+ }
+ }
+
+ memcpy(resultx, nqx, sizeof(limb) * 10);
+ memcpy(resultz, nqz, sizeof(limb) * 10);
+}
+
+// -----------------------------------------------------------------------------
+// Shamelessly copied from djb's code
+// -----------------------------------------------------------------------------
+static void
+crecip(limb *out, const limb *z) {
+ limb z2[10];
+ limb z9[10];
+ limb z11[10];
+ limb z2_5_0[10];
+ limb z2_10_0[10];
+ limb z2_20_0[10];
+ limb z2_50_0[10];
+ limb z2_100_0[10];
+ limb t0[10];
+ limb t1[10];
+ int i;
+
+ /* 2 */ fsquare(z2,z);
+ /* 4 */ fsquare(t1,z2);
+ /* 8 */ fsquare(t0,t1);
+ /* 9 */ fmul(z9,t0,z);
+ /* 11 */ fmul(z11,z9,z2);
+ /* 22 */ fsquare(t0,z11);
+ /* 2^5 - 2^0 = 31 */ fmul(z2_5_0,t0,z9);
+
+ /* 2^6 - 2^1 */ fsquare(t0,z2_5_0);
+ /* 2^7 - 2^2 */ fsquare(t1,t0);
+ /* 2^8 - 2^3 */ fsquare(t0,t1);
+ /* 2^9 - 2^4 */ fsquare(t1,t0);
+ /* 2^10 - 2^5 */ fsquare(t0,t1);
+ /* 2^10 - 2^0 */ fmul(z2_10_0,t0,z2_5_0);
+
+ /* 2^11 - 2^1 */ fsquare(t0,z2_10_0);
+ /* 2^12 - 2^2 */ fsquare(t1,t0);
+ /* 2^20 - 2^10 */ for (i = 2;i < 10;i += 2) { fsquare(t0,t1); fsquare(t1,t0); }
+ /* 2^20 - 2^0 */ fmul(z2_20_0,t1,z2_10_0);
+
+ /* 2^21 - 2^1 */ fsquare(t0,z2_20_0);
+ /* 2^22 - 2^2 */ fsquare(t1,t0);
+ /* 2^40 - 2^20 */ for (i = 2;i < 20;i += 2) { fsquare(t0,t1); fsquare(t1,t0); }
+ /* 2^40 - 2^0 */ fmul(t0,t1,z2_20_0);
+
+ /* 2^41 - 2^1 */ fsquare(t1,t0);
+ /* 2^42 - 2^2 */ fsquare(t0,t1);
+ /* 2^50 - 2^10 */ for (i = 2;i < 10;i += 2) { fsquare(t1,t0); fsquare(t0,t1); }
+ /* 2^50 - 2^0 */ fmul(z2_50_0,t0,z2_10_0);
+
+ /* 2^51 - 2^1 */ fsquare(t0,z2_50_0);
+ /* 2^52 - 2^2 */ fsquare(t1,t0);
+ /* 2^100 - 2^50 */ for (i = 2;i < 50;i += 2) { fsquare(t0,t1); fsquare(t1,t0); }
+ /* 2^100 - 2^0 */ fmul(z2_100_0,t1,z2_50_0);
+
+ /* 2^101 - 2^1 */ fsquare(t1,z2_100_0);
+ /* 2^102 - 2^2 */ fsquare(t0,t1);
+ /* 2^200 - 2^100 */ for (i = 2;i < 100;i += 2) { fsquare(t1,t0); fsquare(t0,t1); }
+ /* 2^200 - 2^0 */ fmul(t1,t0,z2_100_0);
+
+ /* 2^201 - 2^1 */ fsquare(t0,t1);
+ /* 2^202 - 2^2 */ fsquare(t1,t0);
+ /* 2^250 - 2^50 */ for (i = 2;i < 50;i += 2) { fsquare(t0,t1); fsquare(t1,t0); }
+ /* 2^250 - 2^0 */ fmul(t0,t1,z2_50_0);
+
+ /* 2^251 - 2^1 */ fsquare(t1,t0);
+ /* 2^252 - 2^2 */ fsquare(t0,t1);
+ /* 2^253 - 2^3 */ fsquare(t1,t0);
+ /* 2^254 - 2^4 */ fsquare(t0,t1);
+ /* 2^255 - 2^5 */ fsquare(t1,t0);
+ /* 2^255 - 21 */ fmul(out,t1,z11);
+}
+
+int curve25519_donna(u8 *, const u8 *, const u8 *);
+
+int
+curve25519_donna(u8 *mypublic, const u8 *secret, const u8 *basepoint) {
+ limb bp[10], x[10], z[11], zmone[10];
+ uint8_t e[32];
+ int i;
+
+ for (i = 0; i < 32; ++i) e[i] = secret[i];
+ e[0] &= 248;
+ e[31] &= 127;
+ e[31] |= 64;
+
+ fexpand(bp, basepoint);
+ cmult(x, z, e, bp);
+ crecip(zmone, z);
+ fmul(z, x, zmone);
+ freduce_coefficients(z);
+ fcontract(mypublic, z);
+ return 0;
+}
diff --git a/library/jni/curve25519-donna.h b/library/jni/curve25519-donna.h
new file mode 100644
index 0000000000..3e4dfcee04
--- /dev/null
+++ b/library/jni/curve25519-donna.h
@@ -0,0 +1,6 @@
+#ifndef CURVE25519_DONNA_H
+#define CURVE25519_DONNA_H
+
+extern int curve25519_donna(uint8_t *, const uint8_t *, const uint8_t *);
+
+#endif
diff --git a/library/libs/armeabi.jar b/library/libs/armeabi.jar
new file mode 100644
index 0000000000..b34bd1237c
Binary files /dev/null and b/library/libs/armeabi.jar differ
diff --git a/library/libs/libphonenumber-5.3.jar b/library/libs/libphonenumber-5.3.jar
deleted file mode 100644
index 7be725cc63..0000000000
Binary files a/library/libs/libphonenumber-5.3.jar and /dev/null differ
diff --git a/library/libs/protobuf-java-2.4.1.jar b/library/libs/protobuf-java-2.4.1.jar
deleted file mode 100644
index 1373fa424d..0000000000
Binary files a/library/libs/protobuf-java-2.4.1.jar and /dev/null differ
diff --git a/library/libs/sc-light-jdk15on-1.47.0.2.jar b/library/libs/sc-light-jdk15on-1.47.0.2.jar
deleted file mode 100644
index 7cdbf856e5..0000000000
Binary files a/library/libs/sc-light-jdk15on-1.47.0.2.jar and /dev/null differ
diff --git a/library/libs/thoughtcrimegson-2.1.jar b/library/libs/thoughtcrimegson-2.1.jar
deleted file mode 100644
index 8406d51d03..0000000000
Binary files a/library/libs/thoughtcrimegson-2.1.jar and /dev/null differ
diff --git a/library/src/org/whispersystems/textsecure/crypto/IdentityKey.java b/library/src/org/whispersystems/textsecure/crypto/IdentityKey.java
index 2ea71a7fa2..742d716d58 100644
--- a/library/src/org/whispersystems/textsecure/crypto/IdentityKey.java
+++ b/library/src/org/whispersystems/textsecure/crypto/IdentityKey.java
@@ -1,5 +1,6 @@
/**
* Copyright (C) 2011 Whisper Systems
+ * 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
@@ -19,8 +20,10 @@ package org.whispersystems.textsecure.crypto;
import android.os.Parcel;
import android.os.Parcelable;
-import org.spongycastle.crypto.params.ECPublicKeyParameters;
+import org.whispersystems.textsecure.crypto.ecc.Curve;
+import org.whispersystems.textsecure.crypto.ecc.ECPublicKey;
import org.whispersystems.textsecure.util.Hex;
+import org.whispersystems.textsecure.util.Util;
/**
* A class for representing an identity key.
@@ -44,15 +47,15 @@ public class IdentityKey implements Parcelable, SerializableKey {
}
};
- public static final int SIZE = 1 + KeyUtil.POINT_SIZE;
- private static final int VERSION = 1;
-
- private ECPublicKeyParameters publicKey;
-
- public IdentityKey(ECPublicKeyParameters publicKey) {
+ public static final int SIZE = 1 + ECPublicKey.KEY_SIZE;
+ private static final int CURRENT_VESION = 1;
+
+ private ECPublicKey publicKey;
+
+ public IdentityKey(ECPublicKey publicKey) {
this.publicKey = publicKey;
}
-
+
public IdentityKey(Parcel in) throws InvalidKeyException {
int length = in.readInt();
byte[] serialized = new byte[length];
@@ -64,43 +67,42 @@ public class IdentityKey implements Parcelable, SerializableKey {
public IdentityKey(byte[] bytes, int offset) throws InvalidKeyException {
initializeFromSerialized(bytes, offset);
}
-
- public ECPublicKeyParameters getPublicKeyParameters() {
- return this.publicKey;
+
+ public ECPublicKey getPublicKey() {
+ return publicKey;
}
-
+
private void initializeFromSerialized(byte[] bytes, int offset) throws InvalidKeyException {
- int version = bytes[offset] & 0xff;
+ int version = bytes[offset] & 0xff;
- if (version > VERSION)
+ if (version > CURRENT_VESION)
throw new InvalidKeyException("Unsupported key version: " + version);
- this.publicKey = KeyUtil.decodePoint(bytes, offset+1);
+ this.publicKey = Curve.decodePoint(bytes, offset + 1);
}
public byte[] serialize() {
- byte[] encodedKey = KeyUtil.encodePoint(publicKey.getQ());
- byte[] combined = new byte[1 + encodedKey.length];
-
- combined[0] = (byte)VERSION;
- System.arraycopy(encodedKey, 0, combined, 1, encodedKey.length);
-
- return combined;
+ byte[] versionBytes = {(byte)CURRENT_VESION};
+ byte[] encodedKey = publicKey.serialize();
+
+ return Util.combine(versionBytes, encodedKey);
}
-
+
public String getFingerprint() {
- return Hex.toString(serialize());
+ return Hex.toString(publicKey.serialize());
}
@Override
public boolean equals(Object other) {
+ if (other == null) return false;
if (!(other instanceof IdentityKey)) return false;
- return publicKey.getQ().equals(((IdentityKey)other).publicKey.getQ());
+
+ return publicKey.equals(((IdentityKey) other).getPublicKey());
}
@Override
public int hashCode() {
- return publicKey.getQ().hashCode();
+ return publicKey.hashCode();
}
public int describeContents() {
diff --git a/library/src/org/whispersystems/textsecure/crypto/IdentityKeyPair.java b/library/src/org/whispersystems/textsecure/crypto/IdentityKeyPair.java
index 643e752f59..390fbe208f 100644
--- a/library/src/org/whispersystems/textsecure/crypto/IdentityKeyPair.java
+++ b/library/src/org/whispersystems/textsecure/crypto/IdentityKeyPair.java
@@ -16,7 +16,7 @@
*/
package org.whispersystems.textsecure.crypto;
-import org.spongycastle.crypto.params.ECPrivateKeyParameters;
+import org.whispersystems.textsecure.crypto.ecc.ECPrivateKey;
/**
* Holder for public and private identity key pair.
@@ -26,9 +26,9 @@ import org.spongycastle.crypto.params.ECPrivateKeyParameters;
public class IdentityKeyPair {
private final IdentityKey publicKey;
- private final ECPrivateKeyParameters privateKey;
+ private final ECPrivateKey privateKey;
- public IdentityKeyPair(IdentityKey publicKey, ECPrivateKeyParameters privateKey) {
+ public IdentityKeyPair(IdentityKey publicKey, ECPrivateKey privateKey) {
this.publicKey = publicKey;
this.privateKey = privateKey;
}
@@ -37,7 +37,7 @@ public class IdentityKeyPair {
return publicKey;
}
- public ECPrivateKeyParameters getPrivateKey() {
+ public ECPrivateKey getPrivateKey() {
return privateKey;
}
}
diff --git a/library/src/org/whispersystems/textsecure/crypto/KeyPair.java b/library/src/org/whispersystems/textsecure/crypto/KeyPair.java
index 4795e637e2..fb8c1d748e 100644
--- a/library/src/org/whispersystems/textsecure/crypto/KeyPair.java
+++ b/library/src/org/whispersystems/textsecure/crypto/KeyPair.java
@@ -1,5 +1,6 @@
/**
* Copyright (C) 2011 Whisper Systems
+ * 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
@@ -16,13 +17,13 @@
*/
package org.whispersystems.textsecure.crypto;
-import org.spongycastle.crypto.AsymmetricCipherKeyPair;
-import org.spongycastle.crypto.params.ECPrivateKeyParameters;
-import org.spongycastle.crypto.params.ECPublicKeyParameters;
-import org.whispersystems.textsecure.util.Hex;
-
import android.util.Log;
+import org.whispersystems.textsecure.crypto.ecc.ECKeyPair;
+import org.whispersystems.textsecure.crypto.ecc.ECPrivateKey;
+import org.whispersystems.textsecure.util.Hex;
+import org.whispersystems.textsecure.util.Util;
+
/**
* Represents a session's active KeyPair.
*
@@ -31,15 +32,15 @@ import android.util.Log;
public class KeyPair {
- private ECPrivateKeyParameters privateKey;
- private PublicKey publicKey;
-
+ private PublicKey publicKey;
+ private ECPrivateKey privateKey;
+
private final MasterCipher masterCipher;
- public KeyPair(int keyPairId, AsymmetricCipherKeyPair keyPair, MasterSecret masterSecret) {
+ public KeyPair(int keyPairId, ECKeyPair keyPair, MasterSecret masterSecret) {
this.masterCipher = new MasterCipher(masterSecret);
- this.publicKey = new PublicKey(keyPairId, (ECPublicKeyParameters)keyPair.getPublic());
- this.privateKey = (ECPrivateKeyParameters)keyPair.getPrivate();
+ this.publicKey = new PublicKey(keyPairId, keyPair.getPublicKey());
+ this.privateKey = keyPair.getPrivateKey();
}
public KeyPair(byte[] bytes, MasterCipher masterCipher) throws InvalidKeyException {
@@ -54,11 +55,11 @@ public class KeyPair {
public PublicKey getPublicKey() {
return publicKey;
}
-
- public AsymmetricCipherKeyPair getKeyPair() {
- return new AsymmetricCipherKeyPair(publicKey.getKey(), privateKey);
+
+ public ECPrivateKey getPrivateKey() {
+ return privateKey;
}
-
+
public byte[] toBytes() {
return serialize();
}
@@ -67,18 +68,14 @@ public class KeyPair {
this.publicKey = new PublicKey(bytes);
byte[] privateKeyBytes = new byte[bytes.length - PublicKey.KEY_SIZE];
System.arraycopy(bytes, PublicKey.KEY_SIZE, privateKeyBytes, 0, privateKeyBytes.length);
- this.privateKey = masterCipher.decryptKey(privateKeyBytes);
+ this.privateKey = masterCipher.decryptKey(this.publicKey.getType(), privateKeyBytes);
}
public byte[] serialize() {
byte[] publicKeyBytes = publicKey.serialize();
Log.w("KeyPair", "Serialized public key bytes: " + Hex.toString(publicKeyBytes));
- byte[] privateKeyBytes = masterCipher.encryptKey(privateKey);
- byte[] combined = new byte[publicKeyBytes.length + privateKeyBytes.length];
- System.arraycopy(publicKeyBytes, 0, combined, 0, publicKeyBytes.length);
- System.arraycopy(privateKeyBytes, 0, combined, publicKeyBytes.length, privateKeyBytes.length);
-
- return combined;
+ byte[] privateKeyBytes = masterCipher.encryptKey(privateKey);
+ return Util.combine(publicKeyBytes, privateKeyBytes);
}
}
diff --git a/library/src/org/whispersystems/textsecure/crypto/KeyUtil.java b/library/src/org/whispersystems/textsecure/crypto/KeyUtil.java
index a08c18d83a..e720d1b7a7 100644
--- a/library/src/org/whispersystems/textsecure/crypto/KeyUtil.java
+++ b/library/src/org/whispersystems/textsecure/crypto/KeyUtil.java
@@ -1,5 +1,5 @@
/**
- * Copyright (C) 2011 Whisper Systems
+ * 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
@@ -16,26 +16,17 @@
*/
package org.whispersystems.textsecure.crypto;
-import java.math.BigInteger;
-import java.security.NoSuchAlgorithmException;
-import java.security.SecureRandom;
+import android.content.Context;
+import android.util.Log;
-import org.spongycastle.crypto.AsymmetricCipherKeyPair;
-import org.spongycastle.crypto.agreement.ECDHBasicAgreement;
-import org.spongycastle.crypto.generators.ECKeyPairGenerator;
-import org.spongycastle.crypto.params.ECDomainParameters;
-import org.spongycastle.crypto.params.ECKeyGenerationParameters;
-import org.spongycastle.crypto.params.ECPublicKeyParameters;
-import org.spongycastle.math.ec.ECCurve;
-import org.spongycastle.math.ec.ECFieldElement;
-import org.spongycastle.math.ec.ECPoint;
+import org.whispersystems.textsecure.crypto.ecc.Curve;
import org.whispersystems.textsecure.storage.CanonicalRecipientAddress;
import org.whispersystems.textsecure.storage.LocalKeyRecord;
import org.whispersystems.textsecure.storage.RemoteKeyRecord;
import org.whispersystems.textsecure.storage.SessionRecord;
-import android.content.Context;
-import android.util.Log;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
/**
* Helper class for generating key pairs and calculating ECDH agreements.
@@ -45,52 +36,6 @@ import android.util.Log;
public class KeyUtil {
- public static final BigInteger q = new BigInteger("FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF", 16);
- private static final BigInteger a = new BigInteger("FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC", 16);
- private static final BigInteger b = new BigInteger("5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B", 16);
- private static final BigInteger n = new BigInteger("FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551", 16);
-
- private static final ECFieldElement x = new ECFieldElement.Fp(q, new BigInteger("6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296", 16));
- private static final ECFieldElement y = new ECFieldElement.Fp(q, new BigInteger("4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5", 16));
-
- private static final ECCurve curve = new ECCurve.Fp(q, a, b);
- private static final ECPoint g = new ECPoint.Fp(curve, x, y, true);
-
- public static final int POINT_SIZE = 33;
-
- public static final ECDomainParameters domainParameters = new ECDomainParameters(curve, g, n);
-
- public static byte[] encodePoint(ECPoint point) {
- synchronized (curve) {
- return point.getEncoded();
- }
- }
-
- public static ECPublicKeyParameters decodePoint(byte[] encoded, int offset)
- throws InvalidKeyException
- {
- byte[] pointBytes = new byte[POINT_SIZE];
- System.arraycopy(encoded, offset, pointBytes, 0, pointBytes.length);
-
- synchronized (curve) {
- ECPoint Q;
-
- try {
- Q = curve.decodePoint(pointBytes);
- } catch (RuntimeException re) {
- throw new InvalidKeyException(re);
- }
-
- return new ECPublicKeyParameters(Q, KeyUtil.domainParameters);
- }
- }
-
- public static BigInteger calculateAgreement(ECDHBasicAgreement agreement, ECPublicKeyParameters remoteKey) {
- synchronized (curve) {
- return agreement.calculateAgreement(remoteKey);
- }
- }
-
public static void abortSessionFor(Context context, CanonicalRecipientAddress recipient) {
//XXX Obviously we should probably do something more thorough here eventually.
Log.w("KeyUtil", "Aborting session, deleting keys...");
@@ -120,17 +65,18 @@ public class KeyUtil {
new SessionRecord(context, masterSecret, recipient).getIdentityKey() != null;
}
- public static LocalKeyRecord initializeRecordFor(CanonicalRecipientAddress recipient,
- Context context,
- MasterSecret masterSecret)
+ public static LocalKeyRecord initializeRecordFor(Context context,
+ MasterSecret masterSecret,
+ CanonicalRecipientAddress recipient,
+ int sessionVersion)
{
Log.w("KeyUtil", "Initializing local key pairs...");
try {
SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");
int initialId = secureRandom.nextInt(4094) + 1;
- KeyPair currentPair = new KeyPair(initialId, KeyUtil.generateKeyPair(), masterSecret);
- KeyPair nextPair = new KeyPair(initialId + 1, KeyUtil.generateKeyPair(), masterSecret);
+ KeyPair currentPair = new KeyPair(initialId, Curve.generateKeyPairForSession(sessionVersion), masterSecret);
+ KeyPair nextPair = new KeyPair(initialId + 1, Curve.generateKeyPairForSession(sessionVersion), masterSecret);
LocalKeyRecord record = new LocalKeyRecord(context, masterSecret, recipient);
record.setCurrentKeyPair(currentPair);
@@ -143,30 +89,4 @@ public class KeyUtil {
}
}
- public static AsymmetricCipherKeyPair generateKeyPair() {
- try {
- synchronized (curve) {
- ECKeyGenerationParameters keyParamters = new ECKeyGenerationParameters(domainParameters, SecureRandom.getInstance("SHA1PRNG"));
- ECKeyPairGenerator generator = new ECKeyPairGenerator();
- generator.init(keyParamters);
-
- AsymmetricCipherKeyPair keyPair = generator.generateKeyPair();
-
- return cloneKeyPairWithPointCompression(keyPair);
- }
- } catch (NoSuchAlgorithmException nsae) {
- Log.w("keyutil", nsae);
- return null;
- }
- }
-
- // This is dumb, but the ECPublicKeys that the generator makes by default don't have point compression
- // turned on, and there's no setter. Great.
- private static AsymmetricCipherKeyPair cloneKeyPairWithPointCompression(AsymmetricCipherKeyPair keyPair) {
- ECPublicKeyParameters publicKey = (ECPublicKeyParameters)keyPair.getPublic();
- ECPoint q = publicKey.getQ();
-
- return new AsymmetricCipherKeyPair(new ECPublicKeyParameters(new ECPoint.Fp(q.getCurve(), q.getX(), q.getY(), true), publicKey.getParameters()), keyPair.getPrivate());
- }
-
}
diff --git a/library/src/org/whispersystems/textsecure/crypto/MasterCipher.java b/library/src/org/whispersystems/textsecure/crypto/MasterCipher.java
index 8390c42a79..358563b9f8 100644
--- a/library/src/org/whispersystems/textsecure/crypto/MasterCipher.java
+++ b/library/src/org/whispersystems/textsecure/crypto/MasterCipher.java
@@ -1,5 +1,6 @@
/**
* Copyright (C) 2011 Whisper Systems
+ * 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
@@ -16,8 +17,14 @@
*/
package org.whispersystems.textsecure.crypto;
+import android.util.Log;
+
+import org.whispersystems.textsecure.crypto.ecc.Curve;
+import org.whispersystems.textsecure.crypto.ecc.ECPrivateKey;
+import org.whispersystems.textsecure.util.Base64;
+import org.whispersystems.textsecure.util.Hex;
+
import java.io.IOException;
-import java.math.BigInteger;
import java.security.GeneralSecurityException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
@@ -32,12 +39,6 @@ import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
-import org.spongycastle.crypto.params.ECPrivateKeyParameters;
-import org.whispersystems.textsecure.util.Base64;
-import org.whispersystems.textsecure.util.Hex;
-
-import android.util.Log;
-
/**
* Class that handles encryption for local storage.
*
@@ -69,13 +70,11 @@ public class MasterCipher {
throw new AssertionError(e);
}
}
-
- public byte[] encryptKey(ECPrivateKeyParameters params) {
- BigInteger d = params.getD();
- byte[] dBytes = d.toByteArray();
- return encryptBytes(dBytes);
+
+ public byte[] encryptKey(ECPrivateKey privateKey) {
+ return encryptBytes(privateKey.serialize());
}
-
+
public String encryptBody(String body) {
return encryptAndEncodeBytes(body.getBytes());
}
@@ -84,13 +83,13 @@ public class MasterCipher {
return new String(decodeAndDecryptBytes(body));
}
- public ECPrivateKeyParameters decryptKey(byte[] key) {
+ public ECPrivateKey decryptKey(int type, byte[] key)
+ throws org.whispersystems.textsecure.crypto.InvalidKeyException
+ {
try {
- BigInteger d = new BigInteger(decryptBytes(key));
- return new ECPrivateKeyParameters(d, KeyUtil.domainParameters);
+ return Curve.decodePrivatePoint(type, decryptBytes(key));
} catch (InvalidMessageException ime) {
- Log.w("bodycipher", ime);
- return null; // XXX
+ throw new org.whispersystems.textsecure.crypto.InvalidKeyException(ime);
}
}
diff --git a/library/src/org/whispersystems/textsecure/crypto/MessageCipher.java b/library/src/org/whispersystems/textsecure/crypto/MessageCipher.java
index b1b654e684..79755a54a4 100644
--- a/library/src/org/whispersystems/textsecure/crypto/MessageCipher.java
+++ b/library/src/org/whispersystems/textsecure/crypto/MessageCipher.java
@@ -1,5 +1,6 @@
/**
* Copyright (C) 2011 Whisper Systems
+ * 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
@@ -57,14 +58,12 @@ public class MessageCipher {
try {
CiphertextMessage message = new CiphertextMessage(ciphertext);
- int messageVersion = message.getCurrentVersion();
- int supportedVersion = message.getSupportedVersion();
- int negotiatedVersion = Math.min(supportedVersion, CiphertextMessage.SUPPORTED_VERSION);
- int senderKeyId = message.getSenderKeyId();
- int receiverKeyId = message.getReceiverKeyId();
- PublicKey nextRemoteKey = new PublicKey(message.getNextKeyBytes());
- int counter = message.getCounter();
- byte[] body = message.getBody();
+ int messageVersion = message.getCurrentVersion();
+ int senderKeyId = message.getSenderKeyId();
+ int receiverKeyId = message.getReceiverKeyId();
+ PublicKey nextRemoteKey = new PublicKey(message.getNextKeyBytes());
+ int counter = message.getCounter();
+ byte[] body = message.getBody();
SessionCipher sessionCipher = new SessionCipher();
SessionCipherContext sessionContext = sessionCipher.getDecryptionContext(context, masterSecret,
@@ -73,8 +72,7 @@ public class MessageCipher {
receiverKeyId,
nextRemoteKey,
counter,
- messageVersion,
- negotiatedVersion);
+ messageVersion);
message.verifyMac(sessionContext);
@@ -84,5 +82,4 @@ public class MessageCipher {
}
}
}
-
}
diff --git a/library/src/org/whispersystems/textsecure/crypto/PreKeyPair.java b/library/src/org/whispersystems/textsecure/crypto/PreKeyPair.java
index 3b03500560..e4429b584d 100644
--- a/library/src/org/whispersystems/textsecure/crypto/PreKeyPair.java
+++ b/library/src/org/whispersystems/textsecure/crypto/PreKeyPair.java
@@ -1,40 +1,53 @@
+/**
+ * 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 .
+ */
+
package org.whispersystems.textsecure.crypto;
-import org.spongycastle.crypto.AsymmetricCipherKeyPair;
-import org.spongycastle.crypto.params.ECPrivateKeyParameters;
-import org.spongycastle.crypto.params.ECPublicKeyParameters;
+import org.whispersystems.textsecure.crypto.ecc.ECKeyPair;
+import org.whispersystems.textsecure.crypto.ecc.ECPrivateKey;
import org.whispersystems.textsecure.util.Util;
public class PreKeyPair {
- private final MasterCipher masterCipher;
- private final ECPrivateKeyParameters privateKey;
- private final PreKeyPublic publicKey;
+ private final MasterCipher masterCipher;
+ private final PreKeyPublic publicKey;
+ private final ECPrivateKey privateKey;
- public PreKeyPair(MasterSecret masterSecret, AsymmetricCipherKeyPair keyPair) {
+ public PreKeyPair(MasterSecret masterSecret, ECKeyPair keyPair) {
this.masterCipher = new MasterCipher(masterSecret);
- this.publicKey = new PreKeyPublic((ECPublicKeyParameters)keyPair.getPublic());
- this.privateKey = (ECPrivateKeyParameters)keyPair.getPrivate();
+ this.publicKey = new PreKeyPublic(keyPair.getPublicKey());
+ this.privateKey = keyPair.getPrivateKey();
}
public PreKeyPair(MasterSecret masterSecret, byte[] serialized) throws InvalidKeyException {
- if (serialized.length < KeyUtil.POINT_SIZE + 1)
- throw new InvalidKeyException("Serialized length: " + serialized.length);
-
- byte[] privateKeyBytes = new byte[serialized.length - KeyUtil.POINT_SIZE];
- System.arraycopy(serialized, KeyUtil.POINT_SIZE, privateKeyBytes, 0, privateKeyBytes.length);
+ byte[] privateKeyBytes = new byte[serialized.length - PreKeyPublic.KEY_SIZE];
+ System.arraycopy(serialized, PreKeyPublic.KEY_SIZE, privateKeyBytes, 0, privateKeyBytes.length);
this.masterCipher = new MasterCipher(masterSecret);
this.publicKey = new PreKeyPublic(serialized, 0);
- this.privateKey = masterCipher.decryptKey(privateKeyBytes);
+ this.privateKey = masterCipher.decryptKey(this.publicKey.getType(), privateKeyBytes);
}
public PreKeyPublic getPublicKey() {
return publicKey;
}
- public AsymmetricCipherKeyPair getKeyPair() {
- return new AsymmetricCipherKeyPair(publicKey.getPublicKey(), privateKey);
+ public ECKeyPair getKeyPair() {
+ return new ECKeyPair(publicKey.getPublicKey(), privateKey);
}
public byte[] serialize() {
diff --git a/library/src/org/whispersystems/textsecure/crypto/PreKeyPublic.java b/library/src/org/whispersystems/textsecure/crypto/PreKeyPublic.java
index e4f4e436c5..8b889b317d 100644
--- a/library/src/org/whispersystems/textsecure/crypto/PreKeyPublic.java
+++ b/library/src/org/whispersystems/textsecure/crypto/PreKeyPublic.java
@@ -1,25 +1,49 @@
+/**
+ * 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 .
+ */
+
package org.whispersystems.textsecure.crypto;
-import org.spongycastle.crypto.params.ECPublicKeyParameters;
+import org.whispersystems.textsecure.crypto.ecc.Curve;
+import org.whispersystems.textsecure.crypto.ecc.ECPublicKey;
+import org.whispersystems.textsecure.util.Util;
public class PreKeyPublic {
- private final ECPublicKeyParameters publicKey;
+ public static final int KEY_SIZE = ECPublicKey.KEY_SIZE;
- public PreKeyPublic(ECPublicKeyParameters publicKey) {
+ private final ECPublicKey publicKey;
+
+ public PreKeyPublic(ECPublicKey publicKey) {
this.publicKey = publicKey;
}
public PreKeyPublic(byte[] serialized, int offset) throws InvalidKeyException {
- this.publicKey = KeyUtil.decodePoint(serialized, offset);
+ this.publicKey = Curve.decodePoint(serialized, offset);
}
public byte[] serialize() {
- return KeyUtil.encodePoint(publicKey.getQ());
+ return publicKey.serialize();
}
- public ECPublicKeyParameters getPublicKey() {
+ public ECPublicKey getPublicKey() {
return publicKey;
}
+ public int getType() {
+ return this.publicKey.getType();
+ }
}
diff --git a/library/src/org/whispersystems/textsecure/crypto/PreKeyUtil.java b/library/src/org/whispersystems/textsecure/crypto/PreKeyUtil.java
index fddbdca965..9c97a155db 100644
--- a/library/src/org/whispersystems/textsecure/crypto/PreKeyUtil.java
+++ b/library/src/org/whispersystems/textsecure/crypto/PreKeyUtil.java
@@ -1,9 +1,28 @@
+/**
+ * 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 .
+ */
+
package org.whispersystems.textsecure.crypto;
import android.content.Context;
import android.util.Log;
import com.google.thoughtcrimegson.Gson;
+
+import org.whispersystems.textsecure.crypto.ecc.Curve25519;
import org.whispersystems.textsecure.storage.InvalidKeyIdException;
import org.whispersystems.textsecure.storage.PreKeyRecord;
import org.whispersystems.textsecure.util.Medium;
@@ -21,7 +40,7 @@ import java.util.List;
public class PreKeyUtil {
- public static final int BATCH_SIZE = 70;
+ public static final int BATCH_SIZE = 20;
public static List generatePreKeys(Context context, MasterSecret masterSecret) {
List records = new LinkedList();
@@ -29,7 +48,7 @@ public class PreKeyUtil {
for (int i=0;i.
+ */
+
package org.whispersystems.textsecure.crypto;
import android.util.Log;
-import org.spongycastle.crypto.CipherParameters;
-import org.spongycastle.crypto.agreement.ECDHBasicAgreement;
-import org.spongycastle.crypto.params.ECPublicKeyParameters;
+import org.whispersystems.textsecure.crypto.ecc.Curve;
+import org.whispersystems.textsecure.crypto.ecc.ECPublicKey;
import org.whispersystems.textsecure.crypto.kdf.DerivedSecrets;
import org.whispersystems.textsecure.crypto.kdf.HKDF;
import org.whispersystems.textsecure.crypto.kdf.KDF;
@@ -12,7 +28,6 @@ import org.whispersystems.textsecure.crypto.kdf.NKDF;
import org.whispersystems.textsecure.crypto.protocol.CiphertextMessage;
import org.whispersystems.textsecure.util.Conversions;
-import java.math.BigInteger;
import java.util.LinkedList;
import java.util.List;
@@ -21,34 +36,34 @@ public class SharedSecretCalculator {
public static DerivedSecrets calculateSharedSecret(boolean isLowEnd, KeyPair localKeyPair,
int localKeyId,
IdentityKeyPair localIdentityKeyPair,
- ECPublicKeyParameters remoteKey,
+ ECPublicKey remoteKey,
int remoteKeyId,
IdentityKey remoteIdentityKey)
+ throws InvalidKeyException
{
- Log.w("SharedSecretCalculator", "Calculating shared secret with cradle agreement...");
- KDF kdf = new HKDF();
- List results = new LinkedList();
+ Log.w("SharedSecretCalculator", "Calculating shared secret with 3DHE agreement...");
+ KDF kdf = new HKDF();
+ List results = new LinkedList();
if (isSmaller(localKeyPair.getPublicKey().getKey(), remoteKey)) {
- results.add(calculateAgreement(localIdentityKeyPair.getPrivateKey(), remoteKey));
-
- results.add(calculateAgreement(localKeyPair.getKeyPair().getPrivate(),
- remoteIdentityKey.getPublicKeyParameters()));
+ results.add(Curve.calculateAgreement(remoteKey, localIdentityKeyPair.getPrivateKey()));
+ results.add(Curve.calculateAgreement(remoteIdentityKey.getPublicKey(),
+ localKeyPair.getPrivateKey()));
} else {
- results.add(calculateAgreement(localKeyPair.getKeyPair().getPrivate(),
- remoteIdentityKey.getPublicKeyParameters()));
-
- results.add(calculateAgreement(localIdentityKeyPair.getPrivateKey(), remoteKey));
+ results.add(Curve.calculateAgreement(remoteIdentityKey.getPublicKey(),
+ localKeyPair.getPrivateKey()));
+ results.add(Curve.calculateAgreement(remoteKey, localIdentityKeyPair.getPrivateKey()));
}
- results.add(calculateAgreement(localKeyPair.getKeyPair().getPrivate(), remoteKey));
+ results.add(Curve.calculateAgreement(remoteKey, localKeyPair.getPrivateKey()));
- return kdf.deriveSecrets(results, isLowEnd, getInfo(localKeyId,remoteKeyId));
+ return kdf.deriveSecrets(results, isLowEnd, getInfo(localKeyId, remoteKeyId));
}
public static DerivedSecrets calculateSharedSecret(int messageVersion, boolean isLowEnd,
KeyPair localKeyPair, int localKeyId,
- ECPublicKeyParameters remoteKey, int remoteKeyId)
+ ECPublicKey remoteKey, int remoteKeyId)
+ throws InvalidKeyException
{
Log.w("SharedSecretCalculator", "Calculating shared secret with standard agreement...");
KDF kdf;
@@ -58,8 +73,8 @@ public class SharedSecretCalculator {
Log.w("SharedSecretCalculator", "Using kdf: " + kdf);
- List results = new LinkedList();
- results.add(calculateAgreement(localKeyPair.getKeyPair().getPrivate(), remoteKey));
+ List results = new LinkedList();
+ results.add(Curve.calculateAgreement(remoteKey, localKeyPair.getPrivateKey()));
return kdf.deriveSecrets(results, isLowEnd, getInfo(localKeyId, remoteKeyId));
}
@@ -78,23 +93,10 @@ public class SharedSecretCalculator {
return info;
}
- private static BigInteger calculateAgreement(CipherParameters privateKey,
- ECPublicKeyParameters publicKey)
+ private static boolean isSmaller(ECPublicKey localPublic,
+ ECPublicKey remotePublic)
{
- ECDHBasicAgreement agreement = new ECDHBasicAgreement();
- agreement.init(privateKey);
-
- return KeyUtil.calculateAgreement(agreement, publicKey);
- }
-
-
- private static boolean isSmaller(ECPublicKeyParameters localPublic,
- ECPublicKeyParameters remotePublic)
- {
- BigInteger local = localPublic.getQ().getX().toBigInteger();
- BigInteger remote = remotePublic.getQ().getX().toBigInteger();
-
- return local.compareTo(remote) < 0;
+ return localPublic.compareTo(remotePublic) < 0;
}
}
diff --git a/library/src/org/whispersystems/textsecure/crypto/ecc/Curve.java b/library/src/org/whispersystems/textsecure/crypto/ecc/Curve.java
new file mode 100644
index 0000000000..763c9e4efd
--- /dev/null
+++ b/library/src/org/whispersystems/textsecure/crypto/ecc/Curve.java
@@ -0,0 +1,85 @@
+/**
+ * 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 .
+ */
+package org.whispersystems.textsecure.crypto.ecc;
+
+import org.whispersystems.textsecure.crypto.InvalidKeyException;
+import org.whispersystems.textsecure.crypto.protocol.CiphertextMessage;
+
+public class Curve {
+
+ public static final int NIST_TYPE = 0x02;
+ private static final int NIST_TYPE2 = 0x03;
+ public static final int DJB_TYPE = 0x04;
+
+ public static ECKeyPair generateKeyPairForType(int keyType) {
+ if (keyType == DJB_TYPE) {
+ return Curve25519.generateKeyPair();
+ } else if (keyType == NIST_TYPE || keyType == NIST_TYPE2) {
+ return CurveP256.generateKeyPair();
+ } else {
+ throw new AssertionError("Bad key type: " + keyType);
+ }
+ }
+
+ public static ECKeyPair generateKeyPairForSession(int messageVersion) {
+ if (messageVersion >= CiphertextMessage.CURVE25519_INTRODUCED_VERSION) {
+ return generateKeyPairForType(DJB_TYPE);
+ } else {
+ return generateKeyPairForType(NIST_TYPE);
+ }
+ }
+
+ public static ECPublicKey decodePoint(byte[] bytes, int offset)
+ throws InvalidKeyException
+ {
+ int type = bytes[offset];
+
+ if (type == DJB_TYPE) {
+ return Curve25519.decodePoint(bytes, offset);
+ } else if (type == NIST_TYPE || type == NIST_TYPE2) {
+ return CurveP256.decodePoint(bytes, offset);
+ } else {
+ throw new InvalidKeyException("Unknown key type: " + type);
+ }
+ }
+
+ public static ECPrivateKey decodePrivatePoint(int type, byte[] bytes) {
+ if (type == DJB_TYPE) {
+ return new DjbECPrivateKey(bytes);
+ } else if (type == NIST_TYPE || type == NIST_TYPE2) {
+ return CurveP256.decodePrivatePoint(bytes);
+ } else {
+ throw new AssertionError("Bad key type: " + type);
+ }
+ }
+
+ public static byte[] calculateAgreement(ECPublicKey publicKey, ECPrivateKey privateKey)
+ throws InvalidKeyException
+ {
+ if (publicKey.getType() != privateKey.getType()) {
+ throw new InvalidKeyException("Public and private keys must be of the same type!");
+ }
+
+ if (publicKey.getType() == DJB_TYPE) {
+ return Curve25519.calculateAgreement(publicKey, privateKey);
+ } else if (publicKey.getType() == NIST_TYPE || publicKey.getType() == NIST_TYPE2) {
+ return CurveP256.calculateAgreement(publicKey, privateKey);
+ } else {
+ throw new InvalidKeyException("Unknown type: " + publicKey.getType());
+ }
+ }
+}
diff --git a/library/src/org/whispersystems/textsecure/crypto/ecc/Curve25519.java b/library/src/org/whispersystems/textsecure/crypto/ecc/Curve25519.java
new file mode 100644
index 0000000000..64dc5e7b27
--- /dev/null
+++ b/library/src/org/whispersystems/textsecure/crypto/ecc/Curve25519.java
@@ -0,0 +1,75 @@
+/**
+ * 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 .
+ */
+package org.whispersystems.textsecure.crypto.ecc;
+
+import org.whispersystems.textsecure.crypto.InvalidKeyException;
+
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+
+public class Curve25519 {
+
+ static {
+ System.loadLibrary("curve25519");
+
+ try {
+ random = SecureRandom.getInstance("SHA1PRNG");
+ } catch (NoSuchAlgorithmException e) {
+ throw new AssertionError(e);
+ }
+ }
+
+ private static final SecureRandom random;
+
+ private static native byte[] calculateAgreement(byte[] ourPrivate, byte[] theirPublic);
+ private static native byte[] generatePublicKey(byte[] privateKey);
+ private static native byte[] generatePrivateKey(byte[] random);
+
+ public static ECKeyPair generateKeyPair() {
+ byte[] privateKey = generatePrivateKey();
+ byte[] publicKey = generatePublicKey(privateKey);
+
+ return new ECKeyPair(new DjbECPublicKey(publicKey), new DjbECPrivateKey(privateKey));
+ }
+
+ static byte[] calculateAgreement(ECPublicKey publicKey, ECPrivateKey privateKey) {
+ return calculateAgreement(((DjbECPrivateKey)privateKey).getPrivateKey(),
+ ((DjbECPublicKey)publicKey).getPublicKey());
+ }
+
+ static ECPublicKey decodePoint(byte[] encoded, int offset)
+ throws InvalidKeyException
+ {
+ int type = encoded[offset] & 0xFF;
+ byte[] keyBytes = new byte[32];
+ System.arraycopy(encoded, offset+1, keyBytes, 0, keyBytes.length);
+
+ if (type != Curve.DJB_TYPE) {
+ throw new InvalidKeyException("Bad key type: " + type);
+ }
+
+ return new DjbECPublicKey(keyBytes);
+ }
+
+ private static byte[] generatePrivateKey() {
+ byte[] privateKey = new byte[32];
+ random.nextBytes(privateKey);
+
+ return generatePrivateKey(privateKey);
+ }
+
+}
diff --git a/library/src/org/whispersystems/textsecure/crypto/ecc/CurveP256.java b/library/src/org/whispersystems/textsecure/crypto/ecc/CurveP256.java
new file mode 100644
index 0000000000..edc29eb169
--- /dev/null
+++ b/library/src/org/whispersystems/textsecure/crypto/ecc/CurveP256.java
@@ -0,0 +1,122 @@
+/**
+ * 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 .
+ */
+
+package org.whispersystems.textsecure.crypto.ecc;
+
+import android.util.Log;
+
+import org.spongycastle.crypto.AsymmetricCipherKeyPair;
+import org.spongycastle.crypto.agreement.ECDHBasicAgreement;
+import org.spongycastle.crypto.generators.ECKeyPairGenerator;
+import org.spongycastle.crypto.params.ECDomainParameters;
+import org.spongycastle.crypto.params.ECKeyGenerationParameters;
+import org.spongycastle.crypto.params.ECPrivateKeyParameters;
+import org.spongycastle.crypto.params.ECPublicKeyParameters;
+import org.spongycastle.math.ec.ECCurve;
+import org.spongycastle.math.ec.ECFieldElement;
+import org.spongycastle.math.ec.ECPoint;
+import org.whispersystems.textsecure.crypto.InvalidKeyException;
+
+import java.math.BigInteger;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+
+public class CurveP256 {
+
+ private static final BigInteger q = new BigInteger("FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF", 16);
+ private static final BigInteger a = new BigInteger("FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC", 16);
+ private static final BigInteger b = new BigInteger("5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B", 16);
+ private static final BigInteger n = new BigInteger("FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551", 16);
+
+ private static final ECFieldElement x = new ECFieldElement.Fp(q, new BigInteger("6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296", 16));
+ private static final ECFieldElement y = new ECFieldElement.Fp(q, new BigInteger("4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5", 16));
+
+ private static final ECCurve curve = new ECCurve.Fp(q, a, b);
+ private static final ECPoint g = new ECPoint.Fp(curve, x, y, true);
+
+ private static final ECDomainParameters domainParameters = new ECDomainParameters(curve, g, n);
+
+ public static final int P256_POINT_SIZE = 33;
+
+ static byte[] encodePoint(ECPoint point) {
+ synchronized (curve) {
+ return point.getEncoded();
+ }
+ }
+
+ static ECPublicKey decodePoint(byte[] encoded, int offset)
+ throws InvalidKeyException
+ {
+ byte[] pointBytes = new byte[P256_POINT_SIZE];
+ System.arraycopy(encoded, offset, pointBytes, 0, pointBytes.length);
+
+ synchronized (curve) {
+ ECPoint Q;
+
+ try {
+ Q = curve.decodePoint(pointBytes);
+ } catch (RuntimeException re) {
+ throw new InvalidKeyException(re);
+ }
+
+ return new NistECPublicKey(new ECPublicKeyParameters(Q, domainParameters));
+ }
+ }
+
+ static ECPrivateKey decodePrivatePoint(byte[] encoded) {
+ BigInteger d = new BigInteger(encoded);
+ return new NistECPrivateKey(new ECPrivateKeyParameters(d, domainParameters));
+ }
+
+ static byte[] calculateAgreement(ECPublicKey publicKey, ECPrivateKey privateKey) {
+ ECDHBasicAgreement agreement = new ECDHBasicAgreement();
+ agreement.init(((NistECPrivateKey)privateKey).getParameters());
+
+ synchronized (curve) {
+ return agreement.calculateAgreement(((NistECPublicKey)publicKey).getParameters()).toByteArray();
+ }
+ }
+
+ public static ECKeyPair generateKeyPair() {
+ try {
+ synchronized (curve) {
+ ECKeyGenerationParameters keyParamters = new ECKeyGenerationParameters(domainParameters, SecureRandom.getInstance("SHA1PRNG"));
+ ECKeyPairGenerator generator = new ECKeyPairGenerator();
+ generator.init(keyParamters);
+
+ AsymmetricCipherKeyPair keyPair = generator.generateKeyPair();
+ keyPair = cloneKeyPairWithPointCompression(keyPair);
+
+ return new ECKeyPair(new NistECPublicKey((ECPublicKeyParameters)keyPair.getPublic()),
+ new NistECPrivateKey((ECPrivateKeyParameters)keyPair.getPrivate()));
+ }
+ } catch (NoSuchAlgorithmException nsae) {
+ Log.w("CurveP256", nsae);
+ throw new AssertionError(nsae);
+ }
+ }
+
+ // This is dumb, but the ECPublicKeys that the generator makes by default don't have point compression
+ // turned on, and there's no setter. Great.
+ private static AsymmetricCipherKeyPair cloneKeyPairWithPointCompression(AsymmetricCipherKeyPair keyPair) {
+ ECPublicKeyParameters publicKey = (ECPublicKeyParameters)keyPair.getPublic();
+ ECPoint q = publicKey.getQ();
+
+ return new AsymmetricCipherKeyPair(new ECPublicKeyParameters(new ECPoint.Fp(q.getCurve(), q.getX(), q.getY(), true),
+ publicKey.getParameters()), keyPair.getPrivate());
+ }
+}
diff --git a/library/src/org/whispersystems/textsecure/crypto/ecc/DjbECPrivateKey.java b/library/src/org/whispersystems/textsecure/crypto/ecc/DjbECPrivateKey.java
new file mode 100644
index 0000000000..fbf37a2232
--- /dev/null
+++ b/library/src/org/whispersystems/textsecure/crypto/ecc/DjbECPrivateKey.java
@@ -0,0 +1,41 @@
+/**
+ * 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 .
+ */
+
+package org.whispersystems.textsecure.crypto.ecc;
+
+public class DjbECPrivateKey implements ECPrivateKey {
+
+ private final byte[] privateKey;
+
+ DjbECPrivateKey(byte[] privateKey) {
+ this.privateKey = privateKey;
+ }
+
+ @Override
+ public byte[] serialize() {
+ return privateKey;
+ }
+
+ @Override
+ public int getType() {
+ return Curve.DJB_TYPE;
+ }
+
+ public byte[] getPrivateKey() {
+ return privateKey;
+ }
+}
diff --git a/library/src/org/whispersystems/textsecure/crypto/ecc/DjbECPublicKey.java b/library/src/org/whispersystems/textsecure/crypto/ecc/DjbECPublicKey.java
new file mode 100644
index 0000000000..bd6020741d
--- /dev/null
+++ b/library/src/org/whispersystems/textsecure/crypto/ecc/DjbECPublicKey.java
@@ -0,0 +1,66 @@
+/**
+ * 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 .
+ */
+
+package org.whispersystems.textsecure.crypto.ecc;
+
+import org.whispersystems.textsecure.util.Util;
+
+import java.math.BigInteger;
+import java.util.Arrays;
+
+public class DjbECPublicKey implements ECPublicKey {
+
+ private final byte[] publicKey;
+
+ DjbECPublicKey(byte[] publicKey) {
+ this.publicKey = publicKey;
+ }
+
+ @Override
+ public byte[] serialize() {
+ byte[] type = {Curve.DJB_TYPE};
+ return Util.combine(type, publicKey);
+ }
+
+ @Override
+ public int getType() {
+ return Curve.DJB_TYPE;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == null) return false;
+ if (!(other instanceof DjbECPublicKey)) return false;
+
+ DjbECPublicKey that = (DjbECPublicKey)other;
+ return Arrays.equals(this.publicKey, that.publicKey);
+ }
+
+ @Override
+ public int hashCode() {
+ return Arrays.hashCode(publicKey);
+ }
+
+ @Override
+ public int compareTo(ECPublicKey another) {
+ return new BigInteger(publicKey).compareTo(new BigInteger(((DjbECPublicKey)another).publicKey));
+ }
+
+ public byte[] getPublicKey() {
+ return publicKey;
+ }
+}
diff --git a/library/src/org/whispersystems/textsecure/crypto/ecc/ECKeyPair.java b/library/src/org/whispersystems/textsecure/crypto/ecc/ECKeyPair.java
new file mode 100644
index 0000000000..2fc454c73c
--- /dev/null
+++ b/library/src/org/whispersystems/textsecure/crypto/ecc/ECKeyPair.java
@@ -0,0 +1,38 @@
+/**
+ * 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 .
+ */
+
+package org.whispersystems.textsecure.crypto.ecc;
+
+public class ECKeyPair {
+
+ private final ECPublicKey publicKey;
+ private final ECPrivateKey privateKey;
+
+ public ECKeyPair(ECPublicKey publicKey, ECPrivateKey privateKey) {
+ this.publicKey = publicKey;
+ this.privateKey = privateKey;
+ }
+
+
+ public ECPublicKey getPublicKey() {
+ return publicKey;
+ }
+
+ public ECPrivateKey getPrivateKey() {
+ return privateKey;
+ }
+}
diff --git a/library/src/org/whispersystems/textsecure/crypto/ecc/ECPrivateKey.java b/library/src/org/whispersystems/textsecure/crypto/ecc/ECPrivateKey.java
new file mode 100644
index 0000000000..4892d0e658
--- /dev/null
+++ b/library/src/org/whispersystems/textsecure/crypto/ecc/ECPrivateKey.java
@@ -0,0 +1,23 @@
+/**
+ * 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 .
+ */
+
+package org.whispersystems.textsecure.crypto.ecc;
+
+public interface ECPrivateKey {
+ public byte[] serialize();
+ public int getType();
+}
diff --git a/library/src/org/whispersystems/textsecure/crypto/ecc/ECPublicKey.java b/library/src/org/whispersystems/textsecure/crypto/ecc/ECPublicKey.java
new file mode 100644
index 0000000000..42a9ce86d5
--- /dev/null
+++ b/library/src/org/whispersystems/textsecure/crypto/ecc/ECPublicKey.java
@@ -0,0 +1,27 @@
+/**
+ * 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 .
+ */
+
+package org.whispersystems.textsecure.crypto.ecc;
+
+public interface ECPublicKey extends Comparable {
+
+ public static final int KEY_SIZE = 33;
+
+ public byte[] serialize();
+
+ public int getType();
+}
diff --git a/library/src/org/whispersystems/textsecure/crypto/ecc/NistECPrivateKey.java b/library/src/org/whispersystems/textsecure/crypto/ecc/NistECPrivateKey.java
new file mode 100644
index 0000000000..c4fa5a6879
--- /dev/null
+++ b/library/src/org/whispersystems/textsecure/crypto/ecc/NistECPrivateKey.java
@@ -0,0 +1,43 @@
+/**
+ * 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 .
+ */
+
+package org.whispersystems.textsecure.crypto.ecc;
+
+import org.spongycastle.crypto.params.ECPrivateKeyParameters;
+
+public class NistECPrivateKey implements ECPrivateKey {
+
+ private final ECPrivateKeyParameters privateKey;
+
+ public NistECPrivateKey(ECPrivateKeyParameters privateKey) {
+ this.privateKey = privateKey;
+ }
+
+ @Override
+ public byte[] serialize() {
+ return privateKey.getD().toByteArray();
+ }
+
+ @Override
+ public int getType() {
+ return Curve.NIST_TYPE;
+ }
+
+ public ECPrivateKeyParameters getParameters() {
+ return privateKey;
+ }
+}
diff --git a/library/src/org/whispersystems/textsecure/crypto/ecc/NistECPublicKey.java b/library/src/org/whispersystems/textsecure/crypto/ecc/NistECPublicKey.java
new file mode 100644
index 0000000000..1c54fec1bb
--- /dev/null
+++ b/library/src/org/whispersystems/textsecure/crypto/ecc/NistECPublicKey.java
@@ -0,0 +1,63 @@
+/**
+ * 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 .
+ */
+
+package org.whispersystems.textsecure.crypto.ecc;
+
+import org.spongycastle.crypto.params.ECPublicKeyParameters;
+
+public class NistECPublicKey implements ECPublicKey {
+
+ private final ECPublicKeyParameters publicKey;
+
+ NistECPublicKey(ECPublicKeyParameters publicKey) {
+ this.publicKey = publicKey;
+ }
+
+ @Override
+ public byte[] serialize() {
+ return CurveP256.encodePoint(publicKey.getQ());
+ }
+
+ @Override
+ public int getType() {
+ return Curve.NIST_TYPE;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == null) return false;
+ if (!(other instanceof NistECPublicKey)) return false;
+
+ NistECPublicKey that = (NistECPublicKey)other;
+ return publicKey.getQ().equals(that.publicKey.getQ());
+ }
+
+ @Override
+ public int hashCode() {
+ return publicKey.getQ().hashCode();
+ }
+
+ @Override
+ public int compareTo(ECPublicKey another) {
+ return publicKey.getQ().getX().toBigInteger()
+ .compareTo(((NistECPublicKey) another).publicKey.getQ().getX().toBigInteger());
+ }
+
+ public ECPublicKeyParameters getParameters() {
+ return publicKey;
+ }
+}
diff --git a/library/src/org/whispersystems/textsecure/crypto/kdf/DerivedSecrets.java b/library/src/org/whispersystems/textsecure/crypto/kdf/DerivedSecrets.java
index 74f8b82e67..294b1b27c1 100644
--- a/library/src/org/whispersystems/textsecure/crypto/kdf/DerivedSecrets.java
+++ b/library/src/org/whispersystems/textsecure/crypto/kdf/DerivedSecrets.java
@@ -1,3 +1,20 @@
+/**
+ * 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 .
+ */
+
package org.whispersystems.textsecure.crypto.kdf;
import javax.crypto.spec.SecretKeySpec;
diff --git a/library/src/org/whispersystems/textsecure/crypto/kdf/HKDF.java b/library/src/org/whispersystems/textsecure/crypto/kdf/HKDF.java
index 69cf3afc0f..e07e4baa4c 100644
--- a/library/src/org/whispersystems/textsecure/crypto/kdf/HKDF.java
+++ b/library/src/org/whispersystems/textsecure/crypto/kdf/HKDF.java
@@ -1,3 +1,20 @@
+/**
+ * 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 .
+ */
+
package org.whispersystems.textsecure.crypto.kdf;
import java.io.ByteArrayOutputStream;
@@ -18,7 +35,7 @@ public class HKDF extends KDF {
private static final int MAC_KEYS_OFFSET = 32;
@Override
- public DerivedSecrets deriveSecrets(List sharedSecret,
+ public DerivedSecrets deriveSecrets(List sharedSecret,
boolean isLowEnd, byte[] info)
{
byte[] inputKeyMaterial = concatenateSharedSecrets(sharedSecret);
diff --git a/library/src/org/whispersystems/textsecure/crypto/kdf/KDF.java b/library/src/org/whispersystems/textsecure/crypto/kdf/KDF.java
index b49ddc8d41..66e9b86e59 100644
--- a/library/src/org/whispersystems/textsecure/crypto/kdf/KDF.java
+++ b/library/src/org/whispersystems/textsecure/crypto/kdf/KDF.java
@@ -1,33 +1,45 @@
+/**
+ * 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 .
+ */
+
package org.whispersystems.textsecure.crypto.kdf;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
import java.math.BigInteger;
import java.util.LinkedList;
import java.util.List;
public abstract class KDF {
- public abstract DerivedSecrets deriveSecrets(List sharedSecret,
+ public abstract DerivedSecrets deriveSecrets(List sharedSecret,
boolean isLowEnd, byte[] info);
- protected byte[] concatenateSharedSecrets(List sharedSecrets) {
- int totalByteSize = 0;
- List byteValues = new LinkedList();
+ protected byte[] concatenateSharedSecrets(List sharedSecrets) {
+ try {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
- for (BigInteger sharedSecret : sharedSecrets) {
- byte[] byteValue = sharedSecret.toByteArray();
- totalByteSize += byteValue.length;
- byteValues.add(byteValue);
+ for (byte[] sharedSecret : sharedSecrets) {
+ baos.write(sharedSecret);
+ }
+
+ return baos.toByteArray();
+ } catch (IOException e) {
+ throw new AssertionError(e);
}
-
- byte[] combined = new byte[totalByteSize];
- int offset = 0;
-
- for (byte[] byteValue : byteValues) {
- System.arraycopy(byteValue, 0, combined, offset, byteValue.length);
- offset += byteValue.length;
- }
-
- return combined;
}
}
diff --git a/library/src/org/whispersystems/textsecure/crypto/kdf/NKDF.java b/library/src/org/whispersystems/textsecure/crypto/kdf/NKDF.java
index 5106af24f5..afde62a1a4 100644
--- a/library/src/org/whispersystems/textsecure/crypto/kdf/NKDF.java
+++ b/library/src/org/whispersystems/textsecure/crypto/kdf/NKDF.java
@@ -1,10 +1,26 @@
+/**
+ * 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 .
+ */
+
package org.whispersystems.textsecure.crypto.kdf;
import android.util.Log;
import org.whispersystems.textsecure.util.Conversions;
-import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.List;
@@ -14,7 +30,7 @@ import javax.crypto.spec.SecretKeySpec;
public class NKDF extends KDF {
@Override
- public DerivedSecrets deriveSecrets(List sharedSecret,
+ public DerivedSecrets deriveSecrets(List sharedSecret,
boolean isLowEnd, byte[] info)
{
SecretKeySpec cipherKey = deriveCipherSecret(isLowEnd, sharedSecret);
@@ -23,7 +39,7 @@ public class NKDF extends KDF {
return new DerivedSecrets(cipherKey, macKey);
}
- private SecretKeySpec deriveCipherSecret(boolean isLowEnd, List sharedSecret) {
+ private SecretKeySpec deriveCipherSecret(boolean isLowEnd, List sharedSecret) {
byte[] sharedSecretBytes = concatenateSharedSecrets(sharedSecret);
byte[] derivedBytes = deriveBytes(sharedSecretBytes, 16 * 2);
byte[] cipherSecret = new byte[16];
diff --git a/library/src/org/whispersystems/textsecure/crypto/protocol/CiphertextMessage.java b/library/src/org/whispersystems/textsecure/crypto/protocol/CiphertextMessage.java
index 63710d8eac..e2322e694e 100644
--- a/library/src/org/whispersystems/textsecure/crypto/protocol/CiphertextMessage.java
+++ b/library/src/org/whispersystems/textsecure/crypto/protocol/CiphertextMessage.java
@@ -9,8 +9,9 @@ import org.whispersystems.textsecure.util.Conversions;
public class CiphertextMessage {
- public static final int SUPPORTED_VERSION = 2;
- public static final int DHE3_INTRODUCED_VERSION = 2;
+ public static final int SUPPORTED_VERSION = 2;
+ public static final int DHE3_INTRODUCED_VERSION = 2;
+ public static final int CURVE25519_INTRODUCED_VERSION = 2;
static final int VERSION_LENGTH = 1;
private static final int SENDER_KEY_ID_LENGTH = 3;
diff --git a/library/src/org/whispersystems/textsecure/crypto/protocol/PreKeyBundleMessage.java b/library/src/org/whispersystems/textsecure/crypto/protocol/PreKeyBundleMessage.java
index 6f65d47ad5..741f0979a0 100644
--- a/library/src/org/whispersystems/textsecure/crypto/protocol/PreKeyBundleMessage.java
+++ b/library/src/org/whispersystems/textsecure/crypto/protocol/PreKeyBundleMessage.java
@@ -77,7 +77,9 @@ public class PreKeyBundleMessage {
messageBytes[VERSION_OFFSET] = bundledMessageBytes[VERSION_OFFSET];
System.arraycopy(identityKeyBytes, 0, messageBytes, IDENTITY_KEY_OFFSET, identityKeyBytes.length);
- System.arraycopy(bundledMessageBytes, VERSION_OFFSET+VERSION_LENGTH, messageBytes, IDENTITY_KEY_OFFSET+IDENTITY_KEY_LENGTH, bundledMessageBytes.length-VERSION_LENGTH);
+ System.arraycopy(bundledMessageBytes, VERSION_OFFSET+VERSION_LENGTH,
+ messageBytes, IDENTITY_KEY_OFFSET+IDENTITY_KEY_LENGTH,
+ bundledMessageBytes.length-VERSION_LENGTH);
}
public byte[] serialize() {
diff --git a/library/src/org/whispersystems/textsecure/push/PushServiceSocket.java b/library/src/org/whispersystems/textsecure/push/PushServiceSocket.java
index 288e0649d3..cb42d19aee 100644
--- a/library/src/org/whispersystems/textsecure/push/PushServiceSocket.java
+++ b/library/src/org/whispersystems/textsecure/push/PushServiceSocket.java
@@ -133,6 +133,7 @@ public class PushServiceSocket {
PreKeyEntity entity = new PreKeyEntity(record.getId(),
record.getKeyPair().getPublicKey(),
identityKey);
+
entities.add(entity);
}
@@ -140,7 +141,9 @@ public class PushServiceSocket {
lastResortKey.getKeyPair().getPublicKey(),
identityKey);
- makeRequest(String.format(PREKEY_PATH, ""), "PUT", PreKeyList.toJson(new PreKeyList(lastResortEntity, entities)));
+
+ makeRequest(String.format(PREKEY_PATH, ""), "PUT",
+ PreKeyList.toJson(new PreKeyList(lastResortEntity, entities)));
}
public PreKeyEntity getPreKey(PushDestination destination) throws IOException {
diff --git a/library/src/org/whispersystems/textsecure/storage/LocalKeyRecord.java b/library/src/org/whispersystems/textsecure/storage/LocalKeyRecord.java
index a92f7d0b41..042f26df2d 100644
--- a/library/src/org/whispersystems/textsecure/storage/LocalKeyRecord.java
+++ b/library/src/org/whispersystems/textsecure/storage/LocalKeyRecord.java
@@ -1,5 +1,6 @@
/**
* Copyright (C) 2011 Whisper Systems
+ * 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
@@ -21,9 +22,9 @@ import android.util.Log;
import org.whispersystems.textsecure.crypto.InvalidKeyException;
import org.whispersystems.textsecure.crypto.KeyPair;
-import org.whispersystems.textsecure.crypto.KeyUtil;
import org.whispersystems.textsecure.crypto.MasterCipher;
import org.whispersystems.textsecure.crypto.MasterSecret;
+import org.whispersystems.textsecure.crypto.ecc.Curve;
import org.whispersystems.textsecure.util.Medium;
import java.io.FileInputStream;
@@ -65,9 +66,12 @@ public class LocalKeyRecord extends Record {
public void advanceKeyIfNecessary(int keyId) {
Log.w("LocalKeyRecord", "Remote client acknowledges receiving key id: " + keyId);
if (keyId == localNextKeyPair.getId()) {
+ int keyType = this.localNextKeyPair.getPublicKey().getType();
+
this.localCurrentKeyPair = this.localNextKeyPair;
this.localNextKeyPair = new KeyPair((this.localNextKeyPair.getId()+1) % Medium.MAX_VALUE,
- KeyUtil.generateKeyPair(), masterSecret);
+ Curve.generateKeyPairForType(keyType),
+ masterSecret);
}
}
diff --git a/src/org/thoughtcrime/securesms/DatabaseUpgradeActivity.java b/src/org/thoughtcrime/securesms/DatabaseUpgradeActivity.java
index 1bb540ba99..9c4f1252ce 100644
--- a/src/org/thoughtcrime/securesms/DatabaseUpgradeActivity.java
+++ b/src/org/thoughtcrime/securesms/DatabaseUpgradeActivity.java
@@ -1,3 +1,20 @@
+/**
+ * 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 .
+ */
+
package org.thoughtcrime.securesms;
import android.app.Activity;
@@ -10,6 +27,7 @@ import android.util.Log;
import android.view.View;
import android.widget.ProgressBar;
+import org.thoughtcrime.securesms.crypto.IdentityKeyUtil;
import org.whispersystems.textsecure.crypto.MasterSecret;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.util.VersionTracker;
@@ -22,10 +40,12 @@ public class DatabaseUpgradeActivity extends Activity {
public static final int NO_MORE_KEY_EXCHANGE_PREFIX_VERSION = 46;
public static final int MMS_BODY_VERSION = 46;
public static final int TOFU_IDENTITIES_VERSION = 50;
+ public static final int CURVE25519_VERSION = 58;
private static final SortedSet UPGRADE_VERSIONS = new TreeSet() {{
add(NO_MORE_KEY_EXCHANGE_PREFIX_VERSION);
add(TOFU_IDENTITIES_VERSION);
+ add(CURVE25519_VERSION);
}};
private MasterSecret masterSecret;
@@ -33,9 +53,9 @@ public class DatabaseUpgradeActivity extends Activity {
@Override
public void onCreate(Bundle bundle) {
super.onCreate(bundle);
- this.masterSecret = (MasterSecret)getIntent().getParcelableExtra("master_secret");
+ this.masterSecret = getIntent().getParcelableExtra("master_secret");
- if (needsDatabaseUpgrade()) {
+ if (needsUpgradeTask()) {
Log.w("DatabaseUpgradeActivity", "Upgrading...");
setContentView(R.layout.database_upgrade_activity);
@@ -51,7 +71,7 @@ public class DatabaseUpgradeActivity extends Activity {
}
}
- private boolean needsDatabaseUpgrade() {
+ private boolean needsUpgradeTask() {
try {
int currentVersionCode = getPackageManager().getPackageInfo(getPackageName(), 0).versionCode;
int lastSeenVersion = VersionTracker.getLastSeenVersion(this);
@@ -102,10 +122,18 @@ public class DatabaseUpgradeActivity extends Activity {
@Override
protected Void doInBackground(Integer... params) {
+ Context context = DatabaseUpgradeActivity.this.getApplicationContext();
+
Log.w("DatabaseUpgradeActivity", "Running background upgrade..");
DatabaseFactory.getInstance(DatabaseUpgradeActivity.this)
- .onApplicationLevelUpgrade(DatabaseUpgradeActivity.this.getApplicationContext(),
- masterSecret, params[0], this);
+ .onApplicationLevelUpgrade(context, masterSecret, params[0], this);
+
+ if (params[0] < CURVE25519_VERSION) {
+ if (!IdentityKeyUtil.hasCurve25519IdentityKeys(context)) {
+ IdentityKeyUtil.generateCurve25519IdentityKeys(context, masterSecret);
+ }
+ }
+
return null;
}
diff --git a/src/org/thoughtcrime/securesms/VerifyIdentityActivity.java b/src/org/thoughtcrime/securesms/VerifyIdentityActivity.java
index 5c8763d2cb..cd7ad31dd0 100644
--- a/src/org/thoughtcrime/securesms/VerifyIdentityActivity.java
+++ b/src/org/thoughtcrime/securesms/VerifyIdentityActivity.java
@@ -1,5 +1,6 @@
/**
* Copyright (C) 2011 Whisper Systems
+ * 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
@@ -23,6 +24,9 @@ import android.widget.Toast;
import org.whispersystems.textsecure.crypto.IdentityKey;
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil;
import org.whispersystems.textsecure.crypto.MasterSecret;
+import org.whispersystems.textsecure.crypto.ecc.Curve;
+import org.whispersystems.textsecure.crypto.ecc.ECPublicKey;
+import org.whispersystems.textsecure.crypto.protocol.CiphertextMessage;
import org.whispersystems.textsecure.storage.SessionRecord;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.util.MemoryCleaner;
@@ -40,6 +44,8 @@ public class VerifyIdentityActivity extends KeyScanningActivity {
private TextView localIdentityFingerprint;
private TextView remoteIdentityFingerprint;
+ private int keyType;
+
@Override
public void onCreate(Bundle state) {
super.onCreate(state);
@@ -57,12 +63,12 @@ public class VerifyIdentityActivity extends KeyScanningActivity {
}
private void initializeLocalIdentityKey() {
- if (!IdentityKeyUtil.hasIdentityKey(this)) {
+ if (!IdentityKeyUtil.hasIdentityKey(this, keyType)) {
localIdentityFingerprint.setText(R.string.VerifyIdentityActivity_you_do_not_have_an_identity_key);
return;
}
- localIdentityFingerprint.setText(IdentityKeyUtil.getFingerprint(this));
+ localIdentityFingerprint.setText(IdentityKeyUtil.getFingerprint(this, keyType));
}
private void initializeRemoteIdentityKey() {
@@ -86,15 +92,24 @@ public class VerifyIdentityActivity extends KeyScanningActivity {
}
private void initializeResources() {
- localIdentityFingerprint = (TextView)findViewById(R.id.you_read);
- remoteIdentityFingerprint = (TextView)findViewById(R.id.friend_reads);
- recipient = (Recipient)this.getIntent().getParcelableExtra("recipient");
- masterSecret = (MasterSecret)this.getIntent().getParcelableExtra("master_secret");
+ this.localIdentityFingerprint = (TextView)findViewById(R.id.you_read);
+ this.remoteIdentityFingerprint = (TextView)findViewById(R.id.friend_reads);
+ this.recipient = this.getIntent().getParcelableExtra("recipient");
+ this.masterSecret = this.getIntent().getParcelableExtra("master_secret");
+
+ SessionRecord sessionRecord = new SessionRecord(this, masterSecret, recipient);
+ int sessionVersion = sessionRecord.getSessionVersion();
+
+ if (sessionVersion >= CiphertextMessage.CURVE25519_INTRODUCED_VERSION) {
+ this.keyType = Curve.DJB_TYPE;
+ } else {
+ this.keyType = Curve.NIST_TYPE;
+ }
}
@Override
protected void initiateDisplay() {
- if (!IdentityKeyUtil.hasIdentityKey(this)) {
+ if (!IdentityKeyUtil.hasIdentityKey(this, keyType)) {
Toast.makeText(this,
R.string.VerifyIdentityActivity_you_don_t_have_an_identity_key_exclamation,
Toast.LENGTH_LONG).show();
@@ -135,7 +150,7 @@ public class VerifyIdentityActivity extends KeyScanningActivity {
@Override
protected IdentityKey getIdentityKeyToDisplay() {
- return IdentityKeyUtil.getIdentityKey(this);
+ return IdentityKeyUtil.getIdentityKey(this, keyType);
}
@Override
diff --git a/src/org/thoughtcrime/securesms/ViewLocalIdentityActivity.java b/src/org/thoughtcrime/securesms/ViewLocalIdentityActivity.java
index 03c27cad1b..00d172c323 100644
--- a/src/org/thoughtcrime/securesms/ViewLocalIdentityActivity.java
+++ b/src/org/thoughtcrime/securesms/ViewLocalIdentityActivity.java
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2011 Whisper Systems
+ * 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
@@ -28,6 +29,8 @@ import com.actionbarsherlock.view.MenuInflater;
import com.actionbarsherlock.view.MenuItem;
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil;
import org.whispersystems.textsecure.crypto.MasterSecret;
+import org.whispersystems.textsecure.crypto.ecc.Curve;
+import org.whispersystems.textsecure.crypto.ecc.ECPublicKey;
/**
* Activity that displays the local identity key and offers the option to regenerate it.
@@ -41,7 +44,7 @@ public class ViewLocalIdentityActivity extends ViewIdentityActivity {
public void onCreate(Bundle bundle) {
this.masterSecret = getIntent().getParcelableExtra("master_secret");
- getIntent().putExtra("identity_key", IdentityKeyUtil.getIdentityKey(this));
+ getIntent().putExtra("identity_key", IdentityKeyUtil.getIdentityKey(this, Curve.DJB_TYPE));
getIntent().putExtra("title", getString(R.string.ApplicationPreferencesActivity_my) + " " +
getString(R.string.ViewIdentityActivity_identity_fingerprint));
super.onCreate(bundle);
@@ -113,7 +116,8 @@ public class ViewLocalIdentityActivity extends ViewIdentityActivity {
Toast.LENGTH_LONG).show();
getIntent().putExtra("identity_key",
- IdentityKeyUtil.getIdentityKey(ViewLocalIdentityActivity.this));
+ IdentityKeyUtil.getIdentityKey(ViewLocalIdentityActivity.this,
+ Curve.DJB_TYPE));
initialize();
}
diff --git a/src/org/thoughtcrime/securesms/crypto/AsymmetricMasterCipher.java b/src/org/thoughtcrime/securesms/crypto/AsymmetricMasterCipher.java
index 9c5f3f0be1..4c6588a664 100644
--- a/src/org/thoughtcrime/securesms/crypto/AsymmetricMasterCipher.java
+++ b/src/org/thoughtcrime/securesms/crypto/AsymmetricMasterCipher.java
@@ -1,5 +1,6 @@
/**
* Copyright (C) 2011 Whisper Systems
+ * 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
@@ -16,25 +17,25 @@
*/
package org.thoughtcrime.securesms.crypto;
+import org.whispersystems.textsecure.crypto.InvalidKeyException;
+import org.whispersystems.textsecure.crypto.InvalidMessageException;
+import org.whispersystems.textsecure.crypto.MasterCipher;
+import org.whispersystems.textsecure.crypto.MasterSecret;
+import org.whispersystems.textsecure.crypto.PublicKey;
+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;
+import org.whispersystems.textsecure.util.Base64;
+import org.whispersystems.textsecure.util.Conversions;
+import org.whispersystems.textsecure.util.Util;
+
import java.io.IOException;
-import java.math.BigInteger;
import java.security.NoSuchAlgorithmException;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
-import org.spongycastle.crypto.AsymmetricCipherKeyPair;
-import org.spongycastle.crypto.agreement.ECDHBasicAgreement;
-import org.spongycastle.crypto.params.ECPublicKeyParameters;
-import org.whispersystems.textsecure.crypto.InvalidKeyException;
-import org.whispersystems.textsecure.crypto.InvalidMessageException;
-import org.whispersystems.textsecure.crypto.KeyUtil;
-import org.whispersystems.textsecure.crypto.MasterCipher;
-import org.whispersystems.textsecure.crypto.MasterSecret;
-import org.whispersystems.textsecure.crypto.PublicKey;
-import org.whispersystems.textsecure.util.Base64;
-import org.whispersystems.textsecure.util.Conversions;
-
/**
* This class is used to asymmetricly encrypt local data. This is used in the case
* where TextSecure receives an SMS, but the user's local encryption passphrase is
@@ -58,26 +59,23 @@ import org.whispersystems.textsecure.util.Conversions;
public class AsymmetricMasterCipher {
private final AsymmetricMasterSecret asymmetricMasterSecret;
-
+
public AsymmetricMasterCipher(AsymmetricMasterSecret asymmetricMasterSecret) {
this.asymmetricMasterSecret = asymmetricMasterSecret;
}
public String decryptBody(String body) throws IOException, InvalidMessageException {
try {
- byte[] combined = Base64.decode(body);
- PublicKey theirPublicKey = new PublicKey(combined, 0);
- byte[] encryptedBodyBytes = new byte[combined.length - PublicKey.KEY_SIZE];
- System.arraycopy(combined, PublicKey.KEY_SIZE, encryptedBodyBytes, 0, encryptedBodyBytes.length);
-
- ECDHBasicAgreement agreement = new ECDHBasicAgreement();
- agreement.init(asymmetricMasterSecret.getPrivateKey());
-
- BigInteger secret = KeyUtil.calculateAgreement(agreement, theirPublicKey.getKey());
- MasterCipher masterCipher = getMasterCipherForSecret(secret);
- byte[] decryptedBodyBytes = masterCipher.decryptBytes(encryptedBodyBytes);
-
- return new String(decryptedBodyBytes);
+ byte[] combined = Base64.decode(body);
+ byte[][] parts = Util.split(combined, PublicKey.KEY_SIZE, combined.length - PublicKey.KEY_SIZE);
+ PublicKey theirPublicKey = new PublicKey(parts[0], 0);
+
+ ECPrivateKey ourPrivateKey = asymmetricMasterSecret.getPrivateKey(theirPublicKey.getType());
+ byte[] secret = Curve.calculateAgreement(theirPublicKey.getKey(), ourPrivateKey);
+ MasterCipher masterCipher = getMasterCipherForSecret(secret);
+ byte[] decryptedBody = masterCipher.decryptBytes(parts[1]);
+
+ return new String(decryptedBody);
} catch (InvalidKeyException ike) {
throw new InvalidMessageException(ike);
} catch (InvalidMessageException e) {
@@ -86,26 +84,31 @@ public class AsymmetricMasterCipher {
}
public String encryptBody(String body) {
- ECDHBasicAgreement agreement = new ECDHBasicAgreement();
- AsymmetricCipherKeyPair keyPair = KeyUtil.generateKeyPair();
-
- agreement.init(keyPair.getPrivate());
-
- BigInteger secret = KeyUtil.calculateAgreement(agreement, asymmetricMasterSecret.getPublicKey().getKey());
- MasterCipher masterCipher = getMasterCipherForSecret(secret);
- byte[] encryptedBodyBytes = masterCipher.encryptBytes(body.getBytes());
- PublicKey publicKey = new PublicKey(31337, (ECPublicKeyParameters)keyPair.getPublic());
- byte[] publicKeyBytes = publicKey.serialize();
- byte[] combined = new byte[publicKeyBytes.length + encryptedBodyBytes.length];
-
- System.arraycopy(publicKeyBytes, 0, combined, 0, publicKeyBytes.length);
- System.arraycopy(encryptedBodyBytes, 0, combined, publicKeyBytes.length, encryptedBodyBytes.length);
-
- return Base64.encodeBytes(combined);
+ try {
+ ECPublicKey theirPublic;
+
+ if (asymmetricMasterSecret.getDjbPublicKey() != null) {
+ theirPublic = asymmetricMasterSecret.getDjbPublicKey();
+ } else {
+ theirPublic = asymmetricMasterSecret.getNistPublicKey();
+ }
+
+ ECKeyPair ourKeyPair = Curve.generateKeyPairForType(theirPublic.getType());
+ byte[] secret = Curve.calculateAgreement(theirPublic, ourKeyPair.getPrivateKey());
+ MasterCipher masterCipher = getMasterCipherForSecret(secret);
+ byte[] encryptedBodyBytes = masterCipher.encryptBytes(body.getBytes());
+
+ PublicKey ourPublicKey = new PublicKey(31337, ourKeyPair.getPublicKey());
+ byte[] publicKeyBytes = ourPublicKey.serialize();
+ byte[] combined = Util.combine(publicKeyBytes, encryptedBodyBytes);
+
+ return Base64.encodeBytes(combined);
+ } catch (InvalidKeyException e) {
+ throw new AssertionError(e);
+ }
}
- private MasterCipher getMasterCipherForSecret(BigInteger secret) {
- byte[] secretBytes = secret.toByteArray();
+ private MasterCipher getMasterCipherForSecret(byte[] secretBytes) {
SecretKeySpec cipherKey = deriveCipherKey(secretBytes);
SecretKeySpec macKey = deriveMacKey(secretBytes);
MasterSecret masterSecret = new MasterSecret(cipherKey, macKey);
diff --git a/src/org/thoughtcrime/securesms/crypto/AsymmetricMasterSecret.java b/src/org/thoughtcrime/securesms/crypto/AsymmetricMasterSecret.java
index ddb7de7892..aa20e5c4f2 100644
--- a/src/org/thoughtcrime/securesms/crypto/AsymmetricMasterSecret.java
+++ b/src/org/thoughtcrime/securesms/crypto/AsymmetricMasterSecret.java
@@ -1,5 +1,6 @@
/**
* Copyright (C) 2011 Whisper Systems
+ * 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
@@ -16,8 +17,9 @@
*/
package org.thoughtcrime.securesms.crypto;
-import org.spongycastle.crypto.params.ECPrivateKeyParameters;
-import org.whispersystems.textsecure.crypto.PublicKey;
+import org.whispersystems.textsecure.crypto.ecc.Curve;
+import org.whispersystems.textsecure.crypto.ecc.ECPrivateKey;
+import org.whispersystems.textsecure.crypto.ecc.ECPublicKey;
/**
* When a user first initializes TextSecure, a few secrets
@@ -38,19 +40,35 @@ import org.whispersystems.textsecure.crypto.PublicKey;
public class AsymmetricMasterSecret {
- private final PublicKey publicKey;
- private final ECPrivateKeyParameters privateKey;
-
- public AsymmetricMasterSecret(PublicKey publicKey, ECPrivateKeyParameters privateKey) {
- this.publicKey = publicKey;
- this.privateKey = privateKey;
+ private final ECPublicKey djbPublicKey;
+ private final ECPrivateKey djbPrivateKey;
+
+ private final ECPublicKey nistPublicKey;
+ private final ECPrivateKey nistPrivateKey;
+
+ public AsymmetricMasterSecret(ECPublicKey djbPublicKey, ECPrivateKey djbPrivateKey,
+ ECPublicKey nistPublicKey, ECPrivateKey nistPrivateKey)
+ {
+ this.djbPublicKey = djbPublicKey;
+ this.djbPrivateKey = djbPrivateKey;
+ this.nistPublicKey = nistPublicKey;
+ this.nistPrivateKey = nistPrivateKey;
}
-
- public PublicKey getPublicKey() {
- return publicKey;
+
+ public ECPublicKey getDjbPublicKey() {
+ return djbPublicKey;
}
-
- public ECPrivateKeyParameters getPrivateKey() {
- return privateKey;
+
+ public ECPublicKey getNistPublicKey() {
+ return nistPublicKey;
}
+
+ public ECPrivateKey getPrivateKey(int type) {
+ if (type == Curve.DJB_TYPE) {
+ return djbPrivateKey;
+ } else {
+ return nistPrivateKey;
+ }
+ }
+
}
diff --git a/src/org/thoughtcrime/securesms/crypto/DecryptingQueue.java b/src/org/thoughtcrime/securesms/crypto/DecryptingQueue.java
index d9ded58c2a..c0b825e54f 100644
--- a/src/org/thoughtcrime/securesms/crypto/DecryptingQueue.java
+++ b/src/org/thoughtcrime/securesms/crypto/DecryptingQueue.java
@@ -1,5 +1,6 @@
/**
* Copyright (C) 2011 Whisper Systems
+ * 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
@@ -47,6 +48,8 @@ import org.whispersystems.textsecure.crypto.KeyUtil;
import org.whispersystems.textsecure.crypto.MasterSecret;
import org.whispersystems.textsecure.crypto.MessageCipher;
import org.whispersystems.textsecure.crypto.SessionCipher;
+import org.whispersystems.textsecure.crypto.ecc.Curve;
+import org.whispersystems.textsecure.crypto.ecc.ECPublicKey;
import org.whispersystems.textsecure.push.IncomingPushMessage;
import org.whispersystems.textsecure.util.Hex;
@@ -195,7 +198,7 @@ public class DecryptingQueue {
return;
}
- IdentityKeyPair identityKey = IdentityKeyUtil.getIdentityKeyPair(context, masterSecret);
+ IdentityKeyPair identityKey = IdentityKeyUtil.getIdentityKeyPair(context, masterSecret, Curve.DJB_TYPE);
MessageCipher messageCipher = new MessageCipher(context, masterSecret, identityKey);
byte[] plaintextBody = messageCipher.decrypt(recipient, message.getBody());
@@ -276,7 +279,7 @@ public class DecryptingQueue {
synchronized (SessionCipher.CIPHER_LOCK) {
Log.w("DecryptingQueue", "Decrypting: " + Hex.toString(ciphertextPduBytes));
TextTransport transportDetails = new TextTransport();
- IdentityKeyPair identityKey = IdentityKeyUtil.getIdentityKeyPair(context, masterSecret);
+ IdentityKeyPair identityKey = IdentityKeyUtil.getIdentityKeyPair(context, masterSecret, Curve.DJB_TYPE);
MessageCipher messageCipher = new MessageCipher(context, masterSecret, identityKey);
byte[] ciphertext = transportDetails.getDecodedMessage(ciphertextPduBytes);
@@ -360,7 +363,7 @@ public class DecryptingQueue {
}
SmsTransportDetails transportDetails = new SmsTransportDetails();
- IdentityKeyPair identityKey = IdentityKeyUtil.getIdentityKeyPair(context, masterSecret);
+ IdentityKeyPair identityKey = IdentityKeyUtil.getIdentityKeyPair(context, masterSecret, Curve.DJB_TYPE);
MessageCipher messageCipher = new MessageCipher(context, masterSecret, identityKey);
byte[] ciphertext = transportDetails.getDecodedMessage(body.getBytes());
byte[] paddedPlaintext = messageCipher.decrypt(recipient, ciphertext);
diff --git a/src/org/thoughtcrime/securesms/crypto/IdentityKeyUtil.java b/src/org/thoughtcrime/securesms/crypto/IdentityKeyUtil.java
index e852539ab6..3bd6d57138 100644
--- a/src/org/thoughtcrime/securesms/crypto/IdentityKeyUtil.java
+++ b/src/org/thoughtcrime/securesms/crypto/IdentityKeyUtil.java
@@ -1,5 +1,6 @@
/**
* Copyright (C) 2011 Whisper Systems
+ * 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
@@ -26,17 +27,18 @@ import org.spongycastle.asn1.ASN1Primitive;
import org.spongycastle.asn1.ASN1Sequence;
import org.spongycastle.asn1.DERInteger;
import org.spongycastle.asn1.DERSequence;
-import org.spongycastle.crypto.AsymmetricCipherKeyPair;
-import org.spongycastle.crypto.params.ECPrivateKeyParameters;
-import org.spongycastle.crypto.params.ECPublicKeyParameters;
import org.spongycastle.crypto.signers.ECDSASigner;
import org.whispersystems.textsecure.crypto.IdentityKey;
import org.whispersystems.textsecure.crypto.IdentityKeyPair;
import org.whispersystems.textsecure.crypto.InvalidKeyException;
-import org.whispersystems.textsecure.crypto.KeyUtil;
import org.whispersystems.textsecure.crypto.MasterCipher;
import org.whispersystems.textsecure.crypto.MasterSecret;
import org.whispersystems.textsecure.crypto.PublicKey;
+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.NistECPrivateKey;
+import org.whispersystems.textsecure.crypto.ecc.NistECPublicKey;
import org.whispersystems.textsecure.util.Base64;
import org.whispersystems.textsecure.util.Conversions;
import org.whispersystems.textsecure.util.Util;
@@ -54,19 +56,39 @@ import java.security.NoSuchAlgorithmException;
public class IdentityKeyUtil {
- private static final String IDENTITY_PUBLIC_KEY_PREF = "pref_identity_public";
- private static final String IDENTITY_PRIVATE_KEY_PREF = "pref_identity_private";
+ private static final String IDENTITY_PUBLIC_KEY_NIST_PREF = "pref_identity_public";
+ private static final String IDENTITY_PRIVATE_KEY_NIST_PREF = "pref_identity_private";
+
+ private static final String IDENTITY_PUBLIC_KEY_DJB_PREF = "pref_identity_public_curve25519";
+ private static final String IDENTITY_PRIVATE_KEY_DJB_PREF = "pref_identity_private_curve25519";
- public static boolean hasIdentityKey(Context context) {
+ public static boolean hasIdentityKey(Context context, int type) {
SharedPreferences preferences = context.getSharedPreferences(MasterSecretUtil.PREFERENCES_NAME, 0);
- return preferences.contains(IDENTITY_PUBLIC_KEY_PREF) && preferences.contains(IDENTITY_PRIVATE_KEY_PREF);
+
+ if (type == Curve.DJB_TYPE) {
+ return
+ preferences.contains(IDENTITY_PUBLIC_KEY_DJB_PREF) &&
+ preferences.contains(IDENTITY_PRIVATE_KEY_DJB_PREF);
+ } else if (type == Curve.NIST_TYPE) {
+ return
+ preferences.contains(IDENTITY_PUBLIC_KEY_NIST_PREF) &&
+ preferences.contains(IDENTITY_PRIVATE_KEY_NIST_PREF);
+ }
+
+ return false;
}
- public static IdentityKey getIdentityKey(Context context) {
- if (!hasIdentityKey(context)) return null;
+ public static IdentityKey getIdentityKey(Context context, int type) {
+ if (!hasIdentityKey(context, type)) return null;
try {
- byte[] publicKeyBytes = Base64.decode(retrieve(context, IDENTITY_PUBLIC_KEY_PREF));
+ String key;
+
+ if (type == Curve.DJB_TYPE) key = IDENTITY_PUBLIC_KEY_DJB_PREF;
+ else if (type == Curve.NIST_TYPE) key = IDENTITY_PUBLIC_KEY_NIST_PREF;
+ else return null;
+
+ byte[] publicKeyBytes = Base64.decode(retrieve(context, key));
return new IdentityKey(publicKeyBytes, 0);
} catch (IOException ioe) {
Log.w("IdentityKeyUtil", ioe);
@@ -77,43 +99,78 @@ public class IdentityKeyUtil {
}
}
- public static IdentityKeyPair getIdentityKeyPair(Context context, MasterSecret masterSecret) {
- if (!hasIdentityKey(context))
+ public static IdentityKeyPair getIdentityKeyPair(Context context,
+ MasterSecret masterSecret,
+ int type)
+ {
+ if (!hasIdentityKey(context, type))
return null;
try {
- MasterCipher masterCipher = new MasterCipher(masterSecret);
- IdentityKey publicKey = getIdentityKey(context);
- byte[] privateKeyBytes = Base64.decode(retrieve(context, IDENTITY_PRIVATE_KEY_PREF));
- ECPrivateKeyParameters privateKey = masterCipher.decryptKey(privateKeyBytes);
+ String key;
+
+ if (type == Curve.DJB_TYPE) key = IDENTITY_PRIVATE_KEY_DJB_PREF;
+ else if (type == Curve.NIST_TYPE) key = IDENTITY_PRIVATE_KEY_NIST_PREF;
+ else return null;
+
+ MasterCipher masterCipher = new MasterCipher(masterSecret);
+ IdentityKey publicKey = getIdentityKey(context, type);
+ ECPrivateKey privateKey = masterCipher.decryptKey(type, Base64.decode(retrieve(context, key)));
return new IdentityKeyPair(publicKey, privateKey);
} catch (IOException e) {
throw new AssertionError(e);
+ } catch (InvalidKeyException e) {
+ throw new AssertionError(e);
}
}
- public static String getFingerprint(Context context) {
- if (!hasIdentityKey(context)) return null;
+ public static String getFingerprint(Context context, int type) {
+ if (!hasIdentityKey(context, type)) return null;
- IdentityKey identityKey = getIdentityKey(context);
+ IdentityKey identityKey = getIdentityKey(context, type);
if (identityKey == null) return null;
else return identityKey.getFingerprint();
}
public static void generateIdentityKeys(Context context, MasterSecret masterSecret) {
- MasterCipher masterCipher = new MasterCipher(masterSecret);
- AsymmetricCipherKeyPair keyPair = KeyUtil.generateKeyPair();
- IdentityKey identityKey = new IdentityKey((ECPublicKeyParameters)keyPair.getPublic());
- byte[] serializedPublicKey = identityKey.serialize();
- byte[] serializedPrivateKey = masterCipher.encryptKey((ECPrivateKeyParameters)keyPair.getPrivate());
-
- save(context, IDENTITY_PUBLIC_KEY_PREF, Base64.encodeBytes(serializedPublicKey));
- save(context, IDENTITY_PRIVATE_KEY_PREF, Base64.encodeBytes(serializedPrivateKey));
+ ECKeyPair nistKeyPair = Curve.generateKeyPairForType(Curve.NIST_TYPE);
+ ECKeyPair djbKeyPair = Curve.generateKeyPairForType(Curve.DJB_TYPE);
+
+ MasterCipher masterCipher = new MasterCipher(masterSecret);
+ IdentityKey nistIdentityKey = new IdentityKey(nistKeyPair.getPublicKey());
+ IdentityKey djbIdentityKey = new IdentityKey(djbKeyPair.getPublicKey());
+
+ byte[] nistPrivateKey = masterCipher.encryptKey(nistKeyPair.getPrivateKey());
+ byte[] djbPrivateKey = masterCipher.encryptKey(djbKeyPair.getPrivateKey());
+
+ save(context, IDENTITY_PUBLIC_KEY_NIST_PREF, Base64.encodeBytes(nistIdentityKey.serialize()));
+ save(context, IDENTITY_PUBLIC_KEY_DJB_PREF, Base64.encodeBytes(djbIdentityKey.serialize()));
+
+ save(context, IDENTITY_PRIVATE_KEY_NIST_PREF, Base64.encodeBytes(nistPrivateKey));
+ save(context, IDENTITY_PRIVATE_KEY_DJB_PREF, Base64.encodeBytes(djbPrivateKey));
+ }
+
+ public static boolean hasCurve25519IdentityKeys(Context context) {
+ return
+ retrieve(context, IDENTITY_PUBLIC_KEY_DJB_PREF) != null &&
+ retrieve(context, IDENTITY_PRIVATE_KEY_DJB_PREF) != null;
+ }
+
+ public static void generateCurve25519IdentityKeys(Context context, MasterSecret masterSecret) {
+ MasterCipher masterCipher = new MasterCipher(masterSecret);
+ ECKeyPair djbKeyPair = Curve.generateKeyPairForType(Curve.DJB_TYPE);
+ IdentityKey djbIdentityKey = new IdentityKey(djbKeyPair.getPublicKey());
+ byte[] djbPrivateKey = masterCipher.encryptKey(djbKeyPair.getPrivateKey());
+
+ save(context, IDENTITY_PUBLIC_KEY_DJB_PREF, Base64.encodeBytes(djbIdentityKey.serialize()));
+ save(context, IDENTITY_PRIVATE_KEY_DJB_PREF, Base64.encodeBytes(djbPrivateKey));
}
- public static IdentityKey verifySignedKeyExchange(byte[] keyExchangeBytes) throws InvalidKeyException {
+ public static IdentityKey verifySignedKeyExchange(byte[] keyExchangeBytes)
+ throws InvalidKeyException
+ {
try {
byte[] messageBytes = new byte[1 + PublicKey.KEY_SIZE];
System.arraycopy(keyExchangeBytes, 0, messageBytes, 0, messageBytes.length);
@@ -128,8 +185,12 @@ public class IdentityKeyUtil {
byte[] messageHash = getMessageHash(messageBytes, publicKeyBytes);
IdentityKey identityKey = new IdentityKey(publicKeyBytes, 0);
ECDSASigner verifier = new ECDSASigner();
-
- verifier.init(false, identityKey.getPublicKeyParameters());
+
+ if (identityKey.getPublicKey().getType() != Curve.NIST_TYPE) {
+ throw new InvalidKeyException("Signing only support on P256 keys!");
+ }
+
+ verifier.init(false, ((NistECPublicKey)identityKey.getPublicKey()).getParameters());
ASN1Sequence sequence = (ASN1Sequence) ASN1Primitive.fromByteArray(signatureBytes);
BigInteger[] signatureIntegers = new BigInteger[]{
@@ -148,17 +209,18 @@ public class IdentityKeyUtil {
}
- public static byte[] getSignedKeyExchange(Context context, MasterSecret masterSecret, byte[] keyExchangeBytes) {
+ public static byte[] getSignedKeyExchange(Context context, MasterSecret masterSecret,
+ byte[] keyExchangeBytes)
+ {
try {
-
- MasterCipher masterCipher = new MasterCipher(masterSecret);
- byte[] publicKeyBytes = getIdentityKey(context).serialize();
- byte[] messageHash = getMessageHash(keyExchangeBytes, publicKeyBytes);
- byte[] privateKeyBytes = Base64.decode(retrieve(context, IDENTITY_PRIVATE_KEY_PREF));
- ECPrivateKeyParameters privateKey = masterCipher.decryptKey(privateKeyBytes);
- ECDSASigner signer = new ECDSASigner();
+ MasterCipher masterCipher = new MasterCipher(masterSecret);
+ byte[] publicKeyBytes = getIdentityKey(context, Curve.NIST_TYPE).serialize();
+ byte[] messageHash = getMessageHash(keyExchangeBytes, publicKeyBytes);
+ byte[] privateKeyBytes = Base64.decode(retrieve(context, IDENTITY_PRIVATE_KEY_NIST_PREF));
+ ECPrivateKey privateKey = masterCipher.decryptKey(Curve.NIST_TYPE, privateKeyBytes);
+ ECDSASigner signer = new ECDSASigner();
- signer.init(true, privateKey);
+ signer.init(true, ((NistECPrivateKey)privateKey).getParameters());
BigInteger[] messageSignatureInts = signer.generateSignature(messageHash);
DERInteger[] derMessageSignatureInts = new DERInteger[]{ new DERInteger(messageSignatureInts[0]), new DERInteger(messageSignatureInts[1]) };
@@ -167,12 +229,12 @@ public class IdentityKeyUtil {
Conversions.shortToByteArray(messageSignature, 0, messageSignatureBytes.length);
System.arraycopy(messageSignatureBytes, 0, messageSignature, 2, messageSignatureBytes.length);
-
- byte[] combined = Util.combine(keyExchangeBytes, publicKeyBytes, messageSignature);
-
- return combined;
+
+ return Util.combine(keyExchangeBytes, publicKeyBytes, messageSignature);
} catch (IOException ioe) {
throw new AssertionError(ioe);
+ } catch (InvalidKeyException e) {
+ throw new AssertionError(e);
}
}
diff --git a/src/org/thoughtcrime/securesms/crypto/KeyExchangeInitiator.java b/src/org/thoughtcrime/securesms/crypto/KeyExchangeInitiator.java
index e3a3d13537..05e0027301 100644
--- a/src/org/thoughtcrime/securesms/crypto/KeyExchangeInitiator.java
+++ b/src/org/thoughtcrime/securesms/crypto/KeyExchangeInitiator.java
@@ -1,5 +1,6 @@
/**
* Copyright (C) 2011 Whisper Systems
+ * 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
@@ -28,6 +29,7 @@ import org.thoughtcrime.securesms.sms.MessageSender;
import org.thoughtcrime.securesms.sms.OutgoingKeyExchangeMessage;
import org.whispersystems.textsecure.crypto.KeyUtil;
import org.whispersystems.textsecure.crypto.MasterSecret;
+import org.whispersystems.textsecure.crypto.protocol.CiphertextMessage;
import org.whispersystems.textsecure.storage.LocalKeyRecord;
public class KeyExchangeInitiator {
@@ -52,8 +54,8 @@ public class KeyExchangeInitiator {
}
private static void initiateKeyExchange(Context context, MasterSecret masterSecret, Recipient recipient) {
- LocalKeyRecord record = KeyUtil.initializeRecordFor(recipient, context, masterSecret);
- KeyExchangeMessage message = new KeyExchangeMessage(context, masterSecret, 1, record, 0);
+ LocalKeyRecord record = KeyUtil.initializeRecordFor(context, masterSecret, recipient, CiphertextMessage.CURVE25519_INTRODUCED_VERSION);
+ KeyExchangeMessage message = new KeyExchangeMessage(context, masterSecret, CiphertextMessage.CURVE25519_INTRODUCED_VERSION, record, 0);
OutgoingKeyExchangeMessage textMessage = new OutgoingKeyExchangeMessage(recipient, message.serialize());
Log.w("SendKeyActivity", "Sending public key: " + record.getCurrentKeyPair().getPublicKey().getFingerprint());
diff --git a/src/org/thoughtcrime/securesms/crypto/KeyExchangeProcessor.java b/src/org/thoughtcrime/securesms/crypto/KeyExchangeProcessor.java
index 360ca80b6a..78de44d75a 100644
--- a/src/org/thoughtcrime/securesms/crypto/KeyExchangeProcessor.java
+++ b/src/org/thoughtcrime/securesms/crypto/KeyExchangeProcessor.java
@@ -1,5 +1,6 @@
/**
* Copyright (C) 2011 Whisper Systems
+ * 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
@@ -71,11 +72,7 @@ public class KeyExchangeProcessor {
}
public boolean isTrusted(KeyExchangeMessage message) {
- if (!message.hasIdentityKey()) {
- return false;
- }
-
- return isTrusted(message.getIdentityKey());
+ return message.hasIdentityKey() && isTrusted(message.getIdentityKey());
}
public boolean isTrusted(PreKeyBundleMessage message) {
@@ -155,7 +152,7 @@ public class KeyExchangeProcessor {
remoteKeyRecord.setLastRemoteKey(remoteKey);
remoteKeyRecord.save();
- localKeyRecord = KeyUtil.initializeRecordFor(recipient, context, masterSecret);
+ localKeyRecord = KeyUtil.initializeRecordFor(context, masterSecret, recipient, CiphertextMessage.SUPPORTED_VERSION);
localKeyRecord.setNextKeyPair(localKeyRecord.getCurrentKeyPair());
localKeyRecord.save();
@@ -176,7 +173,7 @@ public class KeyExchangeProcessor {
message.getPublicKey().setId(initiateKeyId);
if (needsResponseFromUs()) {
- localKeyRecord = KeyUtil.initializeRecordFor(recipient, context, masterSecret);
+ localKeyRecord = KeyUtil.initializeRecordFor(context, masterSecret, recipient, message.getMessageVersion());
KeyExchangeMessage ourMessage = new KeyExchangeMessage(context, masterSecret, Math.min(CiphertextMessage.SUPPORTED_VERSION, message.getMaxVersion()), localKeyRecord, initiateKeyId);
OutgoingKeyExchangeMessage textMessage = new OutgoingKeyExchangeMessage(recipient, ourMessage.serialize());
Log.w("KeyExchangeProcessor", "Responding with key exchange message fingerprint: " + ourMessage.getPublicKey().getFingerprint());
diff --git a/src/org/thoughtcrime/securesms/crypto/MasterSecretUtil.java b/src/org/thoughtcrime/securesms/crypto/MasterSecretUtil.java
index df102813fc..146925a8c9 100644
--- a/src/org/thoughtcrime/securesms/crypto/MasterSecretUtil.java
+++ b/src/org/thoughtcrime/securesms/crypto/MasterSecretUtil.java
@@ -1,5 +1,6 @@
/**
* Copyright (C) 2011 Whisper Systems
+ * 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
@@ -21,14 +22,14 @@ import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.util.Log;
-import org.spongycastle.crypto.AsymmetricCipherKeyPair;
-import org.spongycastle.crypto.params.ECPrivateKeyParameters;
import org.whispersystems.textsecure.crypto.InvalidKeyException;
-import org.whispersystems.textsecure.crypto.KeyPair;
-import org.whispersystems.textsecure.crypto.KeyUtil;
import org.whispersystems.textsecure.crypto.MasterCipher;
import org.whispersystems.textsecure.crypto.MasterSecret;
import org.whispersystems.textsecure.crypto.PublicKey;
+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;
import org.whispersystems.textsecure.util.Base64;
import org.whispersystems.textsecure.util.Util;
@@ -57,7 +58,11 @@ public class MasterSecretUtil {
public static final String UNENCRYPTED_PASSPHRASE = "unencrypted";
public static final String PREFERENCES_NAME = "SecureSMS-Preferences";
- public static final String ASYMMETRIC_LOCAL_PUBLIC = "asymmetric_master_secret_public";
+
+ private static final String ASYMMETRIC_LOCAL_PUBLIC_NIST = "asymmetric_master_secret_public";
+ private static final String ASYMMETRIC_LOCAL_PRIVATE_NIST = "asymmetric_master_secret_private";
+ private static final String ASYMMETRIC_LOCAL_PUBLIC_DJB = "asymmetric_master_secret_curve25519_public";
+ private static final String ASYMMETRIC_LOCAL_PRIVATE_DJB = "asymmetric_master_secret_curve25519_private";
public static MasterSecret changeMasterSecretPassphrase(Context context,
MasterSecret masterSecret,
@@ -86,7 +91,9 @@ public class MasterSecretUtil {
return masterSecret;
}
- public static MasterSecret getMasterSecret(Context context, String passphrase) throws InvalidPassphraseException {
+ public static MasterSecret getMasterSecret(Context context, String passphrase)
+ throws InvalidPassphraseException
+ {
try {
byte[] encryptedAndMacdMasterSecret = retrieve(context, "master_secret");
byte[] encryptedMasterSecret = verifyMac(context, encryptedAndMacdMasterSecret, passphrase);
@@ -95,7 +102,7 @@ public class MasterSecretUtil {
byte[] macSecret = getMacSecret(combinedSecrets);
return new MasterSecret(new SecretKeySpec(encryptionSecret, "AES"),
- new SecretKeySpec(macSecret, "HmacSHA1"));
+ new SecretKeySpec(macSecret, "HmacSHA1"));
} catch (GeneralSecurityException e) {
Log.w("keyutil", e);
return null; //XXX
@@ -105,17 +112,43 @@ public class MasterSecretUtil {
}
}
- public static AsymmetricMasterSecret getAsymmetricMasterSecret(Context context, MasterSecret masterSecret) {
+ public static AsymmetricMasterSecret getAsymmetricMasterSecret(Context context,
+ MasterSecret masterSecret)
+ {
try {
- PublicKey publicKey = new PublicKey(retrieve(context, ASYMMETRIC_LOCAL_PUBLIC));
- ECPrivateKeyParameters privateKey = null;
+ byte[] nistPublicBytes = retrieve(context, ASYMMETRIC_LOCAL_PUBLIC_NIST);
+ byte[] djbPublicBytes = retrieve(context, ASYMMETRIC_LOCAL_PUBLIC_DJB);
+
+ byte[] nistPrivateBytes = retrieve(context, ASYMMETRIC_LOCAL_PRIVATE_NIST);
+ byte[] djbPrivateBytes = retrieve(context, ASYMMETRIC_LOCAL_PRIVATE_DJB);
+
+ ECPublicKey nistPublicKey = null;
+ ECPublicKey djbPublicKey = null;
+
+ ECPrivateKey nistPrivateKey = null;
+ ECPrivateKey djbPrivateKey = null;
+
+ if (nistPublicBytes != null) {
+ nistPublicKey = new PublicKey(nistPublicBytes, 0).getKey();
+ }
+
+ if (djbPublicBytes != null) {
+ djbPublicKey = Curve.decodePoint(djbPublicBytes, 0);
+ }
if (masterSecret != null) {
MasterCipher masterCipher = new MasterCipher(masterSecret);
- privateKey = masterCipher.decryptKey(retrieve(context, "asymmetric_master_secret_private"));
+
+ if (nistPrivateBytes != null) {
+ nistPrivateKey = masterCipher.decryptKey(Curve.NIST_TYPE, nistPrivateBytes);
+ }
+
+ if (djbPrivateBytes != null) {
+ djbPrivateKey = masterCipher.decryptKey(Curve.DJB_TYPE, djbPrivateBytes);
+ }
}
- return new AsymmetricMasterSecret(publicKey, privateKey);
+ return new AsymmetricMasterSecret(djbPublicKey, djbPrivateKey, nistPublicKey, nistPrivateKey);
} catch (InvalidKeyException ike) {
throw new AssertionError(ike);
} catch (IOException e) {
@@ -123,17 +156,16 @@ public class MasterSecretUtil {
}
}
- public static AsymmetricMasterSecret generateAsymmetricMasterSecret(Context context, MasterSecret masterSecret) {
- MasterCipher masterCipher = new MasterCipher(masterSecret);
- AsymmetricCipherKeyPair ackp = KeyUtil.generateKeyPair();
- KeyPair keyPair = new KeyPair(31337, ackp, masterSecret);
- PublicKey publicKey = keyPair.getPublicKey();
- ECPrivateKeyParameters privateKey = (ECPrivateKeyParameters)ackp.getPrivate();
+ public static AsymmetricMasterSecret generateAsymmetricMasterSecret(Context context,
+ MasterSecret masterSecret)
+ {
+ MasterCipher masterCipher = new MasterCipher(masterSecret);
+ ECKeyPair keyPair = Curve.generateKeyPairForType(Curve.DJB_TYPE);
- save(context, ASYMMETRIC_LOCAL_PUBLIC, publicKey.serialize());
- save(context, "asymmetric_master_secret_private", masterCipher.encryptKey(privateKey));
+ save(context, ASYMMETRIC_LOCAL_PUBLIC_DJB, keyPair.getPublicKey().serialize());
+ save(context, ASYMMETRIC_LOCAL_PRIVATE_DJB, masterCipher.encryptKey(keyPair.getPrivateKey()));
- return new AsymmetricMasterSecret(publicKey, privateKey);
+ return new AsymmetricMasterSecret(keyPair.getPublicKey(), keyPair.getPrivateKey(), null, null);
}
public static MasterSecret generateMasterSecret(Context context, String passphrase) {
@@ -154,7 +186,10 @@ public class MasterSecretUtil {
public static boolean hasAsymmericMasterSecret(Context context) {
SharedPreferences settings = context.getSharedPreferences(PREFERENCES_NAME, 0);
- return settings.contains(ASYMMETRIC_LOCAL_PUBLIC);
+
+ return
+ settings.contains(ASYMMETRIC_LOCAL_PUBLIC_NIST) ||
+ settings.contains(ASYMMETRIC_LOCAL_PUBLIC_DJB);
}
public static boolean isPassphraseInitialized(Context context) {
diff --git a/src/org/thoughtcrime/securesms/crypto/protocol/KeyExchangeMessage.java b/src/org/thoughtcrime/securesms/crypto/protocol/KeyExchangeMessage.java
index f5e81c0f12..2e02c07a81 100644
--- a/src/org/thoughtcrime/securesms/crypto/protocol/KeyExchangeMessage.java
+++ b/src/org/thoughtcrime/securesms/crypto/protocol/KeyExchangeMessage.java
@@ -1,5 +1,6 @@
/**
* Copyright (C) 2011 Whisper Systems
+ * 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
@@ -25,6 +26,8 @@ import org.whispersystems.textsecure.crypto.InvalidKeyException;
import org.whispersystems.textsecure.crypto.InvalidVersionException;
import org.whispersystems.textsecure.crypto.MasterSecret;
import org.whispersystems.textsecure.crypto.PublicKey;
+import org.whispersystems.textsecure.crypto.ecc.Curve;
+import org.whispersystems.textsecure.crypto.ecc.ECPublicKey;
import org.whispersystems.textsecure.crypto.protocol.CiphertextMessage;
import org.whispersystems.textsecure.storage.LocalKeyRecord;
import org.whispersystems.textsecure.util.Base64;
@@ -59,8 +62,6 @@ import java.io.IOException;
public class KeyExchangeMessage {
- private static final int SUPPORTED_VERSION = CiphertextMessage.SUPPORTED_VERSION;
-
private final int messageVersion;
private final int supportedVersion;
private final PublicKey publicKey;
@@ -70,7 +71,7 @@ public class KeyExchangeMessage {
public KeyExchangeMessage(Context context, MasterSecret masterSecret, int messageVersion, LocalKeyRecord record, int highIdBits) {
this.publicKey = new PublicKey(record.getCurrentKeyPair().getPublicKey());
this.messageVersion = messageVersion;
- this.supportedVersion = SUPPORTED_VERSION;
+ this.supportedVersion = CiphertextMessage.SUPPORTED_VERSION;
publicKey.setId(publicKey.getId() | (highIdBits << 12));
@@ -80,11 +81,11 @@ public class KeyExchangeMessage {
byte[] serializedBytes;
if (includeIdentityNoSignature(messageVersion, context)) {
- byte[] identityKey = IdentityKeyUtil.getIdentityKey(context).serialize();
+ byte[] identityKey = IdentityKeyUtil.getIdentityKey(context, Curve.DJB_TYPE).serialize();
serializedBytes = Util.combine(versionBytes, publicKeyBytes, identityKey);
} else if (includeIdentitySignature(messageVersion, context)) {
- byte[] prolog = Util.combine(versionBytes, publicKeyBytes);
+ byte[] prolog = Util.combine(versionBytes, publicKeyBytes);
serializedBytes = IdentityKeyUtil.getSignedKeyExchange(context, masterSecret, prolog);
} else {
@@ -102,14 +103,13 @@ public class KeyExchangeMessage {
this.supportedVersion = Conversions.lowBitsToInt(keyBytes[0]);
this.serialized = messageBody;
- if (messageVersion > SUPPORTED_VERSION)
- throw new InvalidVersionException("Key exchange with version: " + messageVersion +
- " but we only support: " + SUPPORTED_VERSION);
+ if (messageVersion > CiphertextMessage.SUPPORTED_VERSION)
+ throw new InvalidVersionException("Key exchange with version: " + messageVersion);
if (messageVersion >= 1)
keyBytes = Base64.decodeWithoutPadding(messageBody);
- this.publicKey = new PublicKey(keyBytes, 1);
+ this.publicKey = new PublicKey(keyBytes, 1);
if (keyBytes.length <= PublicKey.KEY_SIZE + 1) {
this.identityKey = null;
@@ -134,11 +134,11 @@ public class KeyExchangeMessage {
}
private static boolean includeIdentitySignature(int messageVersion, Context context) {
- return IdentityKeyUtil.hasIdentityKey(context) && (messageVersion == 1);
+ return IdentityKeyUtil.hasIdentityKey(context, Curve.NIST_TYPE) && (messageVersion == 1);
}
private static boolean includeIdentityNoSignature(int messageVersion, Context context) {
- return IdentityKeyUtil.hasIdentityKey(context) && (messageVersion >= 2);
+ return IdentityKeyUtil.hasIdentityKey(context, Curve.DJB_TYPE) && (messageVersion >= 2);
}
public PublicKey getPublicKey() {
@@ -152,6 +152,10 @@ public class KeyExchangeMessage {
public int getMaxVersion() {
return supportedVersion;
}
+
+ public int getMessageVersion() {
+ return messageVersion;
+ }
public boolean hasIdentityKey() {
return identityKey != null;
diff --git a/src/org/thoughtcrime/securesms/database/IdentityDatabase.java b/src/org/thoughtcrime/securesms/database/IdentityDatabase.java
index dc1f209d56..2602559922 100644
--- a/src/org/thoughtcrime/securesms/database/IdentityDatabase.java
+++ b/src/org/thoughtcrime/securesms/database/IdentityDatabase.java
@@ -24,13 +24,14 @@ import android.database.sqlite.SQLiteOpenHelper;
import android.net.Uri;
import android.util.Log;
+import org.thoughtcrime.securesms.recipients.Recipient;
+import org.thoughtcrime.securesms.recipients.RecipientFactory;
+import org.thoughtcrime.securesms.recipients.Recipients;
import org.whispersystems.textsecure.crypto.IdentityKey;
import org.whispersystems.textsecure.crypto.InvalidKeyException;
import org.whispersystems.textsecure.crypto.MasterCipher;
import org.whispersystems.textsecure.crypto.MasterSecret;
-import org.thoughtcrime.securesms.recipients.Recipient;
-import org.thoughtcrime.securesms.recipients.RecipientFactory;
-import org.thoughtcrime.securesms.recipients.Recipients;
+import org.whispersystems.textsecure.crypto.ecc.Curve;
import org.whispersystems.textsecure.util.Base64;
import java.io.IOException;
@@ -89,6 +90,13 @@ public class IdentityDatabase extends Database {
}
IdentityKey ourIdentity = new IdentityKey(Base64.decode(serializedIdentity), 0);
+
+ if (theirIdentity.getPublicKey().getType() == Curve.DJB_TYPE &&
+ ourIdentity.getPublicKey().getType() == Curve.NIST_TYPE)
+ {
+ return true;
+ }
+
return ourIdentity.equals(theirIdentity);
} else {
return true;
diff --git a/src/org/thoughtcrime/securesms/service/RegistrationService.java b/src/org/thoughtcrime/securesms/service/RegistrationService.java
index 07007ed9da..31fb6642d7 100644
--- a/src/org/thoughtcrime/securesms/service/RegistrationService.java
+++ b/src/org/thoughtcrime/securesms/service/RegistrationService.java
@@ -19,6 +19,8 @@ import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.textsecure.crypto.IdentityKey;
import org.whispersystems.textsecure.crypto.MasterSecret;
import org.whispersystems.textsecure.crypto.PreKeyUtil;
+import org.whispersystems.textsecure.crypto.ecc.Curve;
+import org.whispersystems.textsecure.crypto.ecc.ECPublicKey;
import org.whispersystems.textsecure.directory.Directory;
import org.whispersystems.textsecure.push.ContactTokenDetails;
import org.whispersystems.textsecure.push.PushServiceSocket;
@@ -273,7 +275,7 @@ public class RegistrationService extends Service {
throws GcmRegistrationTimeoutException, IOException
{
setState(new RegistrationState(RegistrationState.STATE_GENERATING_KEYS, number));
- IdentityKey identityKey = IdentityKeyUtil.getIdentityKey(this);
+ IdentityKey identityKey = IdentityKeyUtil.getIdentityKey(this, Curve.DJB_TYPE);
List records = waitForPreKeys(masterSecret);
PreKeyRecord lastResort = PreKeyUtil.generateLastResortKey(this, masterSecret);
socket.registerPreKeys(identityKey, lastResort, records);
diff --git a/src/org/thoughtcrime/securesms/transport/MmsTransport.java b/src/org/thoughtcrime/securesms/transport/MmsTransport.java
index 033c9a6960..f90a76e582 100644
--- a/src/org/thoughtcrime/securesms/transport/MmsTransport.java
+++ b/src/org/thoughtcrime/securesms/transport/MmsTransport.java
@@ -1,3 +1,20 @@
+/**
+ * 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 .
+ */
+
package org.thoughtcrime.securesms.transport;
import android.content.Context;
@@ -16,6 +33,8 @@ import org.thoughtcrime.securesms.mms.MmsSendHelper;
import org.thoughtcrime.securesms.mms.TextTransport;
import org.thoughtcrime.securesms.protocol.WirePrefix;
import org.thoughtcrime.securesms.recipients.Recipient;
+import org.whispersystems.textsecure.crypto.ecc.Curve;
+import org.whispersystems.textsecure.crypto.ecc.ECPublicKey;
import org.whispersystems.textsecure.crypto.protocol.CiphertextMessage;
import org.whispersystems.textsecure.util.Hex;
@@ -138,7 +157,7 @@ public class MmsTransport {
private byte[] getEncryptedPdu(MasterSecret masterSecret, String recipientString, byte[] pduBytes) {
TextTransport transportDetails = new TextTransport();
Recipient recipient = new Recipient(null, recipientString, null, null);
- IdentityKeyPair identityKey = IdentityKeyUtil.getIdentityKeyPair(context, masterSecret);
+ IdentityKeyPair identityKey = IdentityKeyUtil.getIdentityKeyPair(context, masterSecret, Curve.DJB_TYPE);
MessageCipher messageCipher = new MessageCipher(context, masterSecret, identityKey);
CiphertextMessage ciphertextMessage = messageCipher.encrypt(recipient, pduBytes);
diff --git a/src/org/thoughtcrime/securesms/transport/PushTransport.java b/src/org/thoughtcrime/securesms/transport/PushTransport.java
index 7f9351c39d..8b4e897b03 100644
--- a/src/org/thoughtcrime/securesms/transport/PushTransport.java
+++ b/src/org/thoughtcrime/securesms/transport/PushTransport.java
@@ -1,3 +1,20 @@
+/**
+ * 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 .
+ */
+
package org.thoughtcrime.securesms.transport;
import android.content.Context;
@@ -21,6 +38,7 @@ import org.whispersystems.textsecure.crypto.IdentityKeyPair;
import org.whispersystems.textsecure.crypto.KeyUtil;
import org.whispersystems.textsecure.crypto.MasterSecret;
import org.whispersystems.textsecure.crypto.MessageCipher;
+import org.whispersystems.textsecure.crypto.ecc.Curve;
import org.whispersystems.textsecure.crypto.protocol.CiphertextMessage;
import org.whispersystems.textsecure.crypto.protocol.PreKeyBundleMessage;
import org.whispersystems.textsecure.push.OutgoingPushMessage;
@@ -162,7 +180,7 @@ public class PushTransport extends BaseTransport {
private byte[] getEncryptedPrekeyBundleMessageForExistingSession(Recipient recipient,
byte[] plaintext)
{
- IdentityKeyPair identityKeyPair = IdentityKeyUtil.getIdentityKeyPair(context, masterSecret);
+ IdentityKeyPair identityKeyPair = IdentityKeyUtil.getIdentityKeyPair(context, masterSecret, Curve.DJB_TYPE);
IdentityKey identityKey = identityKeyPair.getPublicKey();
MessageCipher messageCipher = new MessageCipher(context, masterSecret, identityKeyPair);
CiphertextMessage ciphertextMessage = messageCipher.encrypt(recipient, plaintext);
@@ -177,7 +195,7 @@ public class PushTransport extends BaseTransport {
byte[] plaintext)
throws IOException
{
- IdentityKeyPair identityKeyPair = IdentityKeyUtil.getIdentityKeyPair(context, masterSecret);
+ IdentityKeyPair identityKeyPair = IdentityKeyUtil.getIdentityKeyPair(context, masterSecret, Curve.DJB_TYPE);
IdentityKey identityKey = identityKeyPair.getPublicKey();
PreKeyEntity preKey = socket.getPreKey(pushDestination);
KeyExchangeProcessor processor = new KeyExchangeProcessor(context, masterSecret, recipient);
@@ -194,7 +212,7 @@ public class PushTransport extends BaseTransport {
private byte[] getEncryptedMessageForExistingSession(Recipient recipient, byte[] plaintext)
throws IOException
{
- IdentityKeyPair identityKeyPair = IdentityKeyUtil.getIdentityKeyPair(context, masterSecret);
+ IdentityKeyPair identityKeyPair = IdentityKeyUtil.getIdentityKeyPair(context, masterSecret, Curve.DJB_TYPE);
MessageCipher messageCipher = new MessageCipher(context, masterSecret, identityKeyPair);
CiphertextMessage ciphertextMessage = messageCipher.encrypt(recipient, plaintext);
diff --git a/src/org/thoughtcrime/securesms/transport/SmsTransport.java b/src/org/thoughtcrime/securesms/transport/SmsTransport.java
index fc154c3039..768e36d176 100644
--- a/src/org/thoughtcrime/securesms/transport/SmsTransport.java
+++ b/src/org/thoughtcrime/securesms/transport/SmsTransport.java
@@ -1,3 +1,20 @@
+/**
+ * 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 .
+ */
+
package org.thoughtcrime.securesms.transport;
import android.app.PendingIntent;
@@ -19,6 +36,8 @@ import org.whispersystems.textsecure.crypto.IdentityKeyPair;
import org.whispersystems.textsecure.crypto.KeyUtil;
import org.whispersystems.textsecure.crypto.MasterSecret;
import org.whispersystems.textsecure.crypto.MessageCipher;
+import org.whispersystems.textsecure.crypto.ecc.Curve;
+import org.whispersystems.textsecure.crypto.ecc.ECPublicKey;
import org.whispersystems.textsecure.crypto.protocol.CiphertextMessage;
import org.whispersystems.textsecure.crypto.protocol.PreKeyBundleMessage;
@@ -116,7 +135,7 @@ public class SmsTransport extends BaseTransport {
private ArrayList constructSentIntents(long messageId, long type, ArrayList messages) {
ArrayList sentIntents = new ArrayList(messages.size());
- for (String message : messages) {
+ for (String ignored : messages) {
sentIntents.add(PendingIntent.getBroadcast(context, 0,
constructSentIntent(context, messageId, type),
0));
@@ -132,7 +151,7 @@ public class SmsTransport extends BaseTransport {
ArrayList deliveredIntents = new ArrayList(messages.size());
- for (String message : messages) {
+ for (String ignored : messages) {
deliveredIntents.add(PendingIntent.getBroadcast(context, 0,
constructDeliveredIntent(context, messageId, type),
0));
@@ -146,7 +165,9 @@ public class SmsTransport extends BaseTransport {
{
Recipient recipient = message.getRecipients().getPrimaryRecipient();
String body = message.getMessageBody();
- IdentityKeyPair identityKey = IdentityKeyUtil.getIdentityKeyPair(context, masterSecret);
+ IdentityKeyPair identityKey = IdentityKeyUtil.getIdentityKeyPair(context, masterSecret,
+ Curve.DJB_TYPE);
+
SmsTransportDetails transportDetails = new SmsTransportDetails();
if (KeyUtil.isNonPrekeySessionFor(context, masterSecret, recipient)) {