Compare commits

..

1 Commits

Author SHA1 Message Date
Moxie Marlinspike
bc0acb2705 Remove identifiers from ScannableFingerprint
Although it helps to eliminate confusion when people inadvertantly
scan the wrong code, people might share the QR code image publicly
and inadvertantly publish their identifier.

// FREEBIE
2016-11-12 15:00:54 -08:00
35 changed files with 17376 additions and 557 deletions

View File

@ -43,7 +43,7 @@ State is kept in the following places:
On Android:
```groovy
```
dependencies {
compile 'org.whispersystems:signal-protocol-android:(latest version number)'
}
@ -51,7 +51,7 @@ dependencies {
For pure Java apps:
```xml
```
<dependency>
<groupId>org.whispersystems</groupId>
<artifactId>signal-protocol-java</artifactId>
@ -64,19 +64,17 @@ For pure Java apps:
At install time, a libsignal client needs to generate its identity keys, registration id, and
prekeys.
```java
IdentityKeyPair identityKeyPair = KeyHelper.generateIdentityKeyPair();
int registrationId = KeyHelper.generateRegistrationId();
List<PreKeyRecord> preKeys = KeyHelper.generatePreKeys(startId, 100);
SignedPreKeyRecord signedPreKey = KeyHelper.generateSignedPreKey(identityKeyPair, 5);
IdentityKeyPair identityKeyPair = KeyHelper.generateIdentityKeyPair();
int registrationId = KeyHelper.generateRegistrationId();
List<PreKeyRecord> preKeys = KeyHelper.generatePreKeys(startId, 100);
SignedPreKeyRecord signedPreKey = KeyHelper.generateSignedPreKey(identityKeyPair, 5);
// Store identityKeyPair somewhere durable and safe.
// Store registrationId somewhere durable and safe.
// Store identityKeyPair somewhere durable and safe.
// Store registrationId somewhere durable and safe.
// Store preKeys in PreKeyStore.
// Store signed prekey in SignedPreKeyStore.
// Store preKeys in PreKeyStore.
// Store signed prekey in SignedPreKeyStore.
```
## Building a session
A libsignal client needs to implement four interfaces: IdentityKeyStore, PreKeyStore,
@ -85,25 +83,23 @@ prekeys, signed prekeys, and session state.
Once those are implemented, building a session is fairly straightforward:
```java
SessionStore sessionStore = new MySessionStore();
PreKeyStore preKeyStore = new MyPreKeyStore();
SignedPreKeyStore signedPreKeyStore = new MySignedPreKeyStore();
IdentityKeyStore identityStore = new MyIdentityKeyStore();
SessionStore sessionStore = new MySessionStore();
PreKeyStore preKeyStore = new MyPreKeyStore();
SignedPreKeyStore signedPreKeyStore = new MySignedPreKeyStore();
IdentityKeyStore identityStore = new MyIdentityKeyStore();
// Instantiate a SessionBuilder for a remote recipientId + deviceId tuple.
SessionBuilder sessionBuilder = new SessionBuilder(sessionStore, preKeyStore, signedPreKeyStore,
identityStore, recipientId, deviceId);
// Instantiate a SessionBuilder for a remote recipientId + deviceId tuple.
SessionBuilder sessionBuilder = new SessionBuilder(sessionStore, preKeyStore, signedPreKeyStore,
identityStore, recipientId, deviceId);
// Build a session with a PreKey retrieved from the server.
sessionBuilder.process(retrievedPreKey);
// Build a session with a PreKey retrieved from the server.
sessionBuilder.process(retrievedPreKey);
SessionCipher sessionCipher = new SessionCipher(sessionStore, recipientId, deviceId);
CiphertextMessage message = sessionCipher.encrypt("Hello world!".getBytes("UTF-8"));
SessionCipher sessionCipher = new SessionCipher(sessionStore, recipientId, deviceId);
CiphertextMessage message = sessionCipher.encrypt("Hello world!".getBytes("UTF-8"));
deliver(message.serialize());
deliver(message.serialize());
```
# Legal things
## Cryptography Notice
@ -116,7 +112,7 @@ The form and manner of this distribution makes it eligible for export under the
## License
Copyright 2013-2019 Open Whisper Systems
Copyright 2013-2016 Open Whisper Systems
Licensed under the GPLv3: http://www.gnu.org/licenses/gpl-3.0.html

View File

@ -1,16 +1,10 @@
buildscript {
repositories {
google()
mavenCentral()
jcenter {
content {
includeVersion 'org.jetbrains.trove4j', 'trove4j', '20160824'
}
}
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.5.1'
classpath 'com.android.tools.build:gradle:2.2.0'
}
}
@ -23,35 +17,29 @@ version = version_number
group = group_info
android {
compileSdkVersion 28
buildToolsVersion '28.0.3'
defaultConfig {
minSdkVersion 19
targetSdkVersion 28
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_7
targetCompatibility JavaVersion.VERSION_1_7
}
compileSdkVersion 21
buildToolsVersion "21.1.2"
sourceSets {
androidTest {
java.srcDirs = ['src/androidTest/java', project(':tests').file('src/test/java')]
}
}
libraryVariants.all { variant ->
variant.outputs.each { output ->
def outputFile = output.outputFile
if (outputFile != null && outputFile.name.endsWith('.aar')) {
def fileName = "${archivesBaseName}-${version}.aar"
output.outputFile = new File(outputFile.parent, fileName)
}
}
}
}
repositories {
google()
mavenCentral()
mavenLocal()
jcenter {
content {
includeVersion 'org.jetbrains.trove4j', 'trove4j', '20160824'
}
}
}
dependencies {
@ -61,25 +49,8 @@ dependencies {
}
}
def isReleaseBuild() {
return version.contains("SNAPSHOT") == false
}
def getReleaseRepositoryUrl() {
return hasProperty('sonatypeRepo') ? sonatypeRepo
: "https://oss.sonatype.org/service/local/staging/deploy/maven2/"
}
def getRepositoryUsername() {
return hasProperty('whisperSonatypeUsername') ? whisperSonatypeUsername : ""
}
def getRepositoryPassword() {
return hasProperty('whisperSonatypePassword') ? whisperSonatypePassword : ""
}
signing {
required { isReleaseBuild() && gradle.taskGraph.hasTask("uploadArchives") }
required { has("release") && gradle.taskGraph.hasTask("uploadArchives") }
sign configurations.archives
}
@ -88,8 +59,8 @@ uploadArchives {
repositories.mavenDeployer {
beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) }
repository(url: getReleaseRepositoryUrl()) {
authentication(userName: getRepositoryUsername(), password: getRepositoryPassword())
repository(url: sonatypeRepo) {
authentication(userName: whisperSonatypeUsername, password: whisperSonatypePassword)
}
pom.project {

View File

@ -1,7 +1,7 @@
subprojects {
ext.version_number = "2.8.1"
ext.version_number = "2.3.0"
ext.group_info = "org.whispersystems"
ext.curve25519_version = "0.5.0"
ext.curve25519_version = "0.3.0"
if (JavaVersion.current().isJava8Compatible()) {
allprojects {

Binary file not shown.

View File

@ -1,7 +1,6 @@
# Note: Check https://gradle.org/release-checksums/ before updating wrapper or distribution
#Mon Oct 17 15:38:50 PDT 2016
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionSha256Sum=027fdd265d277bae65a0d349b6b8da02135b0b8e14ba891e26281fa877fe37a2
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip

130
gradlew vendored
View File

@ -1,20 +1,4 @@
#!/usr/bin/env sh
#
# Copyright 2015 the original author or authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
#!/usr/bin/env bash
##############################################################################
##
@ -22,6 +6,47 @@
##
##############################################################################
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn ( ) {
echo "$*"
}
die ( ) {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
esac
# For Cygwin, ensure paths are in UNIX format before anything is touched.
if $cygwin ; then
[ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
fi
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
@ -36,49 +61,9 @@ while [ -h "$PRG" ] ; do
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
cd "`dirname \"$PRG\"`/" >&-
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn () {
echo "$*"
}
die () {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
cd "$SAVED" >&-
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
@ -105,7 +90,7 @@ location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
@ -125,11 +110,10 @@ if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin or MSYS, switch paths to Windows format before running java
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
@ -170,19 +154,11 @@ if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
esac
fi
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
function splitJvmOpts() {
JVM_OPTS=("$@")
}
APP_ARGS=$(save "$@")
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
cd "$(dirname "$0")"
fi
exec "$JAVACMD" "$@"
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"

30
gradlew.bat vendored
View File

@ -1,19 +1,3 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@ -24,14 +8,14 @@
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
@ -62,9 +46,10 @@ echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windows variants
@rem Get command-line arguments, handling Windowz variants
if not "%OS%" == "Windows_NT" goto win9xME_args
if "%@eval[2+2]" == "4" goto 4NT_args
:win9xME_args
@rem Slurp the command line arguments.
@ -75,6 +60,11 @@ set _SKIP=2
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
goto execute
:4NT_args
@rem Get arguments from the 4NT Shell from JP Software
set CMD_LINE_ARGS=%$
:execute
@rem Setup the command line

View File

@ -1,16 +1,4 @@
buildscript {
repositories {
google()
mavenCentral()
}
dependencies {
classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.10'
}
}
apply plugin: 'java'
apply plugin: 'com.google.protobuf'
apply plugin: 'maven'
apply plugin: 'signing'
@ -34,30 +22,11 @@ sourceSets {
dependencies {
compile "org.whispersystems:curve25519-java:${curve25519_version}"
compile 'com.google.protobuf:protobuf-javalite:3.10.0'
compile 'com.google.protobuf:protobuf-java:2.5.0'
testCompile ('junit:junit:3.8.2')
}
protobuf {
protoc {
artifact = 'com.google.protobuf:protoc:3.10.0'
}
generateProtoTasks {
all().each { task ->
task.builtins {
java {
option "lite"
}
}
}
}
}
sourceSets {
main.proto.srcDir '../protobuf'
}
test {
testLogging {
@ -68,25 +37,8 @@ test {
include 'org/whispersystems/**'
}
def isReleaseBuild() {
return version.contains("SNAPSHOT") == false
}
def getReleaseRepositoryUrl() {
return hasProperty('sonatypeRepo') ? sonatypeRepo
: "https://oss.sonatype.org/service/local/staging/deploy/maven2/"
}
def getRepositoryUsername() {
return hasProperty('whisperSonatypeUsername') ? whisperSonatypeUsername : ""
}
def getRepositoryPassword() {
return hasProperty('whisperSonatypePassword') ? whisperSonatypePassword : ""
}
signing {
required { isReleaseBuild() && gradle.taskGraph.hasTask("uploadArchives") }
required { has("release") && gradle.taskGraph.hasTask("uploadArchives") }
sign configurations.archives
}
@ -95,8 +47,8 @@ uploadArchives {
repositories.mavenDeployer {
beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) }
repository(url: getReleaseRepositoryUrl()) {
authentication(userName: getRepositoryUsername(), password: getRepositoryPassword())
repository(url: sonatypeRepo) {
authentication(userName: whisperSonatypeUsername, password: whisperSonatypePassword)
}
pom.project {

View File

@ -34,6 +34,7 @@ import org.whispersystems.libsignal.util.guava.Optional;
* <ol>
* <li>A {@link org.whispersystems.libsignal.state.PreKeyBundle} retrieved from a server.</li>
* <li>A {@link PreKeySignalMessage} received from a client.</li>
* <li>A {@link org.whispersystems.libsignal.protocol.KeyExchangeMessage} sent to or received from a client.</li>
* </ol>
*
* Sessions are constructed per recipientId + deviceId tuple. Remote logical users are identified
@ -100,14 +101,13 @@ public class SessionBuilder {
{
IdentityKey theirIdentityKey = message.getIdentityKey();
if (!identityKeyStore.isTrustedIdentity(remoteAddress, theirIdentityKey, IdentityKeyStore.Direction.RECEIVING)) {
if (!identityKeyStore.isTrustedIdentity(remoteAddress, theirIdentityKey)) {
throw new UntrustedIdentityException(remoteAddress.getName(), theirIdentityKey);
}
Optional<Integer> unsignedPreKeyId = processV3(sessionRecord, message);
identityKeyStore.saveIdentity(remoteAddress, theirIdentityKey);
return unsignedPreKeyId;
}
@ -144,7 +144,7 @@ public class SessionBuilder {
sessionRecord.getSessionState().setRemoteRegistrationId(message.getRegistrationId());
sessionRecord.getSessionState().setAliceBaseKey(message.getBaseKey().serialize());
if (message.getPreKeyId().isPresent()) {
if (message.getPreKeyId().isPresent() && message.getPreKeyId().get() != Medium.MAX_VALUE) {
return message.getPreKeyId();
} else {
return Optional.absent();
@ -164,7 +164,7 @@ public class SessionBuilder {
*/
public void process(PreKeyBundle preKey) throws InvalidKeyException, UntrustedIdentityException {
synchronized (SessionCipher.SESSION_LOCK) {
if (!identityKeyStore.isTrustedIdentity(remoteAddress, preKey.getIdentityKey(), IdentityKeyStore.Direction.SENDING)) {
if (!identityKeyStore.isTrustedIdentity(remoteAddress, preKey.getIdentityKey())) {
throw new UntrustedIdentityException(remoteAddress.getName(), preKey.getIdentityKey());
}
@ -205,8 +205,8 @@ public class SessionBuilder {
sessionRecord.getSessionState().setRemoteRegistrationId(preKey.getRegistrationId());
sessionRecord.getSessionState().setAliceBaseKey(ourBaseKey.getPublicKey().serialize());
identityKeyStore.saveIdentity(remoteAddress, preKey.getIdentityKey());
sessionStore.storeSession(remoteAddress, sessionRecord);
identityKeyStore.saveIdentity(remoteAddress, preKey.getIdentityKey());
}
}

View File

@ -54,7 +54,6 @@ public class SessionCipher {
public static final Object SESSION_LOCK = new Object();
private final SessionStore sessionStore;
private final IdentityKeyStore identityKeyStore;
private final SessionBuilder sessionBuilder;
private final PreKeyStore preKeyStore;
private final SignalProtocolAddress remoteAddress;
@ -71,12 +70,11 @@ public class SessionCipher {
SignedPreKeyStore signedPreKeyStore, IdentityKeyStore identityKeyStore,
SignalProtocolAddress remoteAddress)
{
this.sessionStore = sessionStore;
this.preKeyStore = preKeyStore;
this.identityKeyStore = identityKeyStore;
this.remoteAddress = remoteAddress;
this.sessionBuilder = new SessionBuilder(sessionStore, preKeyStore, signedPreKeyStore,
identityKeyStore, remoteAddress);
this.sessionStore = sessionStore;
this.preKeyStore = preKeyStore;
this.remoteAddress = remoteAddress;
this.sessionBuilder = new SessionBuilder(sessionStore, preKeyStore, signedPreKeyStore,
identityKeyStore, remoteAddress);
}
public SessionCipher(SignalProtocolStore store, SignalProtocolAddress remoteAddress) {
@ -89,7 +87,7 @@ public class SessionCipher {
* @param paddedMessage The plaintext message bytes, optionally padded to a constant multiple.
* @return A ciphertext message encrypted to the recipient+device tuple.
*/
public CiphertextMessage encrypt(byte[] paddedMessage) throws UntrustedIdentityException {
public CiphertextMessage encrypt(byte[] paddedMessage) {
synchronized (SESSION_LOCK) {
SessionRecord sessionRecord = sessionStore.loadSession(remoteAddress);
SessionState sessionState = sessionRecord.getSessionState();
@ -99,7 +97,7 @@ public class SessionCipher {
int previousCounter = sessionState.getPreviousCounter();
int sessionVersion = sessionState.getSessionVersion();
byte[] ciphertextBody = getCiphertext(messageKeys, paddedMessage);
byte[] ciphertextBody = getCiphertext(sessionVersion, messageKeys, paddedMessage);
CiphertextMessage ciphertextMessage = new SignalMessage(sessionVersion, messageKeys.getMacKey(),
senderEphemeral, chainKey.getIndex(),
previousCounter, ciphertextBody,
@ -117,12 +115,6 @@ public class SessionCipher {
}
sessionState.setSenderChainKey(chainKey.getNextChainKey());
if (!identityKeyStore.isTrustedIdentity(remoteAddress, sessionState.getRemoteIdentityKey(), IdentityKeyStore.Direction.SENDING)) {
throw new UntrustedIdentityException(remoteAddress.getName(), sessionState.getRemoteIdentityKey());
}
identityKeyStore.saveIdentity(remoteAddress, sessionState.getRemoteIdentityKey());
sessionStore.storeSession(remoteAddress, sessionRecord);
return ciphertextMessage;
}
@ -206,7 +198,7 @@ public class SessionCipher {
*/
public byte[] decrypt(SignalMessage ciphertext)
throws InvalidMessageException, DuplicateMessageException, LegacyMessageException,
NoSessionException, UntrustedIdentityException
NoSessionException
{
return decrypt(ciphertext, new NullDecryptionCallback());
}
@ -231,7 +223,7 @@ public class SessionCipher {
*/
public byte[] decrypt(SignalMessage ciphertext, DecryptionCallback callback)
throws InvalidMessageException, DuplicateMessageException, LegacyMessageException,
NoSessionException, UntrustedIdentityException
NoSessionException
{
synchronized (SESSION_LOCK) {
@ -242,12 +234,6 @@ public class SessionCipher {
SessionRecord sessionRecord = sessionStore.loadSession(remoteAddress);
byte[] plaintext = decrypt(sessionRecord, ciphertext);
if (!identityKeyStore.isTrustedIdentity(remoteAddress, sessionRecord.getSessionState().getRemoteIdentityKey(), IdentityKeyStore.Direction.RECEIVING)) {
throw new UntrustedIdentityException(remoteAddress.getName(), sessionRecord.getSessionState().getRemoteIdentityKey());
}
identityKeyStore.saveIdentity(remoteAddress, sessionRecord.getSessionState().getRemoteIdentityKey());
callback.handlePlaintext(plaintext);
sessionStore.storeSession(remoteAddress, sessionRecord);
@ -304,17 +290,19 @@ public class SessionCipher {
sessionState.getSessionVersion()));
}
int messageVersion = ciphertextMessage.getMessageVersion();
ECPublicKey theirEphemeral = ciphertextMessage.getSenderRatchetKey();
int counter = ciphertextMessage.getCounter();
ChainKey chainKey = getOrCreateChainKey(sessionState, theirEphemeral);
MessageKeys messageKeys = getOrCreateMessageKeys(sessionState, theirEphemeral,
chainKey, counter);
ciphertextMessage.verifyMac(sessionState.getRemoteIdentityKey(),
ciphertextMessage.verifyMac(messageVersion,
sessionState.getRemoteIdentityKey(),
sessionState.getLocalIdentityKey(),
messageKeys.getMacKey());
byte[] plaintext = getPlaintext(messageKeys, ciphertextMessage.getBody());
byte[] plaintext = getPlaintext(messageVersion, messageKeys, ciphertextMessage.getBody());
sessionState.clearUnacknowledgedPreKeyMessage();
@ -392,26 +380,58 @@ public class SessionCipher {
return chainKey.getMessageKeys();
}
private byte[] getCiphertext(MessageKeys messageKeys, byte[] plaintext) {
private byte[] getCiphertext(int version, MessageKeys messageKeys, byte[] plaintext) {
try {
Cipher cipher = getCipher(Cipher.ENCRYPT_MODE, messageKeys.getCipherKey(), messageKeys.getIv());
Cipher cipher;
if (version >= 3) {
cipher = getCipher(Cipher.ENCRYPT_MODE, messageKeys.getCipherKey(), messageKeys.getIv());
} else {
cipher = getCipher(Cipher.ENCRYPT_MODE, messageKeys.getCipherKey(), messageKeys.getCounter());
}
return cipher.doFinal(plaintext);
} catch (IllegalBlockSizeException | BadPaddingException e) {
throw new AssertionError(e);
}
}
private byte[] getPlaintext(MessageKeys messageKeys, byte[] cipherText)
private byte[] getPlaintext(int version, MessageKeys messageKeys, byte[] cipherText)
throws InvalidMessageException
{
try {
Cipher cipher = getCipher(Cipher.DECRYPT_MODE, messageKeys.getCipherKey(), messageKeys.getIv());
Cipher cipher;
if (version >= 3) {
cipher = getCipher(Cipher.DECRYPT_MODE, messageKeys.getCipherKey(), messageKeys.getIv());
} else {
cipher = getCipher(Cipher.DECRYPT_MODE, messageKeys.getCipherKey(), messageKeys.getCounter());
}
return cipher.doFinal(cipherText);
} catch (IllegalBlockSizeException | BadPaddingException e) {
throw new InvalidMessageException(e);
}
}
private Cipher getCipher(int mode, SecretKeySpec key, int counter) {
try {
Cipher cipher = Cipher.getInstance("AES/CTR/NoPadding");
byte[] ivBytes = new byte[16];
ByteUtil.intToByteArray(ivBytes, 0, counter);
IvParameterSpec iv = new IvParameterSpec(ivBytes);
cipher.init(mode, key, iv);
return cipher;
} catch (NoSuchAlgorithmException | NoSuchPaddingException | java.security.InvalidKeyException |
InvalidAlgorithmParameterException e)
{
throw new AssertionError(e);
}
}
private Cipher getCipher(int mode, SecretKeySpec key, IvParameterSpec iv) {
try {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");

View File

@ -48,7 +48,7 @@ public class DeviceConsistencyCodeGenerator {
private static class SignatureComparator extends ByteArrayComparator implements Comparator<DeviceConsistencySignature> {
@Override
public int compare(DeviceConsistencySignature first, DeviceConsistencySignature second) {
return compare(first.getVrfOutput(), second.getVrfOutput());
return compare(first.getSignature(), second.getSignature());
}
}
}

View File

@ -30,18 +30,10 @@ public class Curve {
public static ECPublicKey decodePoint(byte[] bytes, int offset)
throws InvalidKeyException
{
if (bytes == null || bytes.length - offset < 1) {
throw new InvalidKeyException("No key type identifier");
}
int type = bytes[offset] & 0xFF;
switch (type) {
case Curve.DJB_TYPE:
if (bytes.length - offset < 33) {
throw new InvalidKeyException("Bad key length: " + bytes.length);
}
byte[] keyBytes = new byte[32];
System.arraycopy(bytes, offset+1, keyBytes, 0, keyBytes.length);
return new DjbECPublicKey(keyBytes);
@ -57,14 +49,6 @@ public class Curve {
public static byte[] calculateAgreement(ECPublicKey publicKey, ECPrivateKey privateKey)
throws InvalidKeyException
{
if (publicKey == null) {
throw new InvalidKeyException("public value is null");
}
if (privateKey == null) {
throw new InvalidKeyException("private value is null");
}
if (publicKey.getType() != privateKey.getType()) {
throw new InvalidKeyException("Public and private keys must be of the same type!");
}
@ -81,10 +65,6 @@ public class Curve {
public static boolean verifySignature(ECPublicKey signingKey, byte[] message, byte[] signature)
throws InvalidKeyException
{
if (signingKey == null || message == null || signature == null) {
throw new InvalidKeyException("Values must not be null");
}
if (signingKey.getType() == DJB_TYPE) {
return Curve25519.getInstance(BEST)
.verifySignature(((DjbECPublicKey) signingKey).getPublicKey(), message, signature);
@ -96,10 +76,6 @@ public class Curve {
public static byte[] calculateSignature(ECPrivateKey signingKey, byte[] message)
throws InvalidKeyException
{
if (signingKey == null || message == null) {
throw new InvalidKeyException("Values must not be null");
}
if (signingKey.getType() == DJB_TYPE) {
return Curve25519.getInstance(BEST)
.calculateSignature(((DjbECPrivateKey) signingKey).getPrivateKey(), message);
@ -111,10 +87,6 @@ public class Curve {
public static byte[] calculateVrfSignature(ECPrivateKey signingKey, byte[] message)
throws InvalidKeyException
{
if (signingKey == null || message == null) {
throw new InvalidKeyException("Values must not be null");
}
if (signingKey.getType() == DJB_TYPE) {
return Curve25519.getInstance(BEST)
.calculateVrfSignature(((DjbECPrivateKey)signingKey).getPrivateKey(), message);
@ -126,10 +98,6 @@ public class Curve {
public static byte[] verifyVrfSignature(ECPublicKey signingKey, byte[] message, byte[] signature)
throws InvalidKeyException, VrfSignatureVerificationFailedException
{
if (signingKey == null || message == null || signature == null) {
throw new InvalidKeyException("Values must not be null");
}
if (signingKey.getType() == DJB_TYPE) {
return Curve25519.getInstance(BEST)
.verifyVrfSignature(((DjbECPublicKey) signingKey).getPublicKey(), message, signature);

View File

@ -25,7 +25,7 @@ public class Fingerprint {
}
/**
* @return A scannable fingerprint that can be scanned and compared locally.
* @return A scannable fingerprint that can be scanned anc compared locally.
*/
public ScannableFingerprint getScannableFingerprint() {
return scannableFingerprint;

View File

@ -10,15 +10,9 @@ import org.whispersystems.libsignal.IdentityKey;
import java.util.List;
public interface FingerprintGenerator {
public Fingerprint createFor(int version,
byte[] localStableIdentifier,
IdentityKey localIdentityKey,
byte[] remoteStableIdentifier,
IdentityKey remoteIdentityKey);
public Fingerprint createFor(String localStableIdentifier, IdentityKey localIdentityKey,
String remoteStableIdentifier, IdentityKey remoteIdentityKey);
public Fingerprint createFor(int version,
byte[] localStableIdentifier,
List<IdentityKey> localIdentityKey,
byte[] remoteStableIdentifier,
List<IdentityKey> remoteIdentityKey);
public Fingerprint createFor(String localStableIdentifier, List<IdentityKey> localIdentityKey,
String remoteStableIdentifier, List<IdentityKey> remoteIdentityKey);
}

View File

@ -41,9 +41,8 @@ public class NumericFingerprintGenerator implements FingerprintGenerator {
}
/**
* Generate a scannable and displayable fingerprint.
* Generate a scannable and displayble fingerprint.
*
* @param version The version of fingerprint you are generating.
* @param localStableIdentifier The client's "stable" identifier.
* @param localIdentityKey The client's identity key.
* @param remoteStableIdentifier The remote party's "stable" identifier.
@ -51,14 +50,10 @@ public class NumericFingerprintGenerator implements FingerprintGenerator {
* @return A unique fingerprint for this conversation.
*/
@Override
public Fingerprint createFor(int version,
byte[] localStableIdentifier,
final IdentityKey localIdentityKey,
byte[] remoteStableIdentifier,
final IdentityKey remoteIdentityKey)
public Fingerprint createFor(String localStableIdentifier, final IdentityKey localIdentityKey,
String remoteStableIdentifier, final IdentityKey remoteIdentityKey)
{
return createFor(version,
localStableIdentifier,
return createFor(localStableIdentifier,
new LinkedList<IdentityKey>() {{
add(localIdentityKey);
}},
@ -69,24 +64,20 @@ public class NumericFingerprintGenerator implements FingerprintGenerator {
}
/**
* Generate a scannable and displayable fingerprint for logical identities that have multiple
* Generate a scannable and displayble fingerprint for logical identities that have multiple
* physical keys.
*
* Do not trust the output of this unless you've been through the device consistency process
* for the provided localIdentityKeys.
*
* @param version The version of fingerprint you are generating.
* @param localStableIdentifier The client's "stable" identifier.
* @param localIdentityKeys The client's collection of physical identity keys.
* @param remoteStableIdentifier The remote party's "stable" identifier.
* @param remoteIdentityKeys The remote party's collection of physical identity key.
* @return A unique fingerprint for this conversation.
*/
public Fingerprint createFor(int version,
byte[] localStableIdentifier,
List<IdentityKey> localIdentityKeys,
byte[] remoteStableIdentifier,
List<IdentityKey> remoteIdentityKeys)
public Fingerprint createFor(String localStableIdentifier, List<IdentityKey> localIdentityKeys,
String remoteStableIdentifier, List<IdentityKey> remoteIdentityKeys)
{
byte[] localFingerprint = getFingerprint(iterations, localStableIdentifier, localIdentityKeys);
byte[] remoteFingerprint = getFingerprint(iterations, remoteStableIdentifier, remoteIdentityKeys);
@ -94,19 +85,18 @@ public class NumericFingerprintGenerator implements FingerprintGenerator {
DisplayableFingerprint displayableFingerprint = new DisplayableFingerprint(localFingerprint,
remoteFingerprint);
ScannableFingerprint scannableFingerprint = new ScannableFingerprint(version,
localFingerprint,
ScannableFingerprint scannableFingerprint = new ScannableFingerprint(localFingerprint,
remoteFingerprint);
return new Fingerprint(displayableFingerprint, scannableFingerprint);
}
private byte[] getFingerprint(int iterations, byte[] stableIdentifier, List<IdentityKey> unsortedIdentityKeys) {
private byte[] getFingerprint(int iterations, String stableIdentifier, List<IdentityKey> unsortedIdentityKeys) {
try {
MessageDigest digest = MessageDigest.getInstance("SHA-512");
byte[] publicKey = getLogicalKeyBytes(unsortedIdentityKeys);
byte[] hash = ByteUtil.combine(ByteUtil.shortToByteArray(FINGERPRINT_VERSION),
publicKey, stableIdentifier);
publicKey, stableIdentifier.getBytes());
for (int i=0;i<iterations;i++) {
digest.update(hash);

View File

@ -8,33 +8,33 @@ package org.whispersystems.libsignal.fingerprint;
import com.google.protobuf.ByteString;
import com.google.protobuf.InvalidProtocolBufferException;
import org.whispersystems.libsignal.fingerprint.FingerprintProtos.CombinedFingerprints;
import org.whispersystems.libsignal.fingerprint.FingerprintProtos.LogicalFingerprint;
import org.whispersystems.libsignal.fingerprint.FingerprintProtos.LogicalFingerprints;
import org.whispersystems.libsignal.util.ByteUtil;
import java.security.MessageDigest;
public class ScannableFingerprint {
private final int version;
private final CombinedFingerprints fingerprints;
private static final int VERSION = 1;
ScannableFingerprint(int version, byte[] localFingerprintData, byte[] remoteFingerprintData)
private final LogicalFingerprints fingerprints;
ScannableFingerprint(byte[] localFingerprintData, byte[] remoteFingerprintData)
{
LogicalFingerprint localFingerprint = LogicalFingerprint.newBuilder()
.setContent(ByteString.copyFrom(ByteUtil.trim(localFingerprintData, 32)))
.setData(ByteString.copyFrom(ByteUtil.trim(localFingerprintData, 32)))
.build();
LogicalFingerprint remoteFingerprint = LogicalFingerprint.newBuilder()
.setContent(ByteString.copyFrom(ByteUtil.trim(remoteFingerprintData, 32)))
.setData(ByteString.copyFrom(ByteUtil.trim(remoteFingerprintData, 32)))
.build();
this.version = version;
this.fingerprints = CombinedFingerprints.newBuilder()
.setVersion(version)
.setLocalFingerprint(localFingerprint)
.setRemoteFingerprint(remoteFingerprint)
.build();
this.fingerprints = LogicalFingerprints.newBuilder()
.setVersion(VERSION)
.setLocalFingerprint(localFingerprint)
.setRemoteFingerprint(remoteFingerprint)
.build();
}
/**
@ -48,7 +48,7 @@ public class ScannableFingerprint {
* Compare a scanned QR code with what we expect.
*
* @param scannedFingerprintData The scanned data
* @return True if matching, otherwise false.
* @return True if matching, otehrwise false.
* @throws FingerprintVersionMismatchException if the scanned fingerprint is the wrong version.
*/
public boolean compareTo(byte[] scannedFingerprintData)
@ -56,16 +56,16 @@ public class ScannableFingerprint {
FingerprintParsingException
{
try {
CombinedFingerprints scanned = CombinedFingerprints.parseFrom(scannedFingerprintData);
LogicalFingerprints scanned = LogicalFingerprints.parseFrom(scannedFingerprintData);
if (!scanned.hasRemoteFingerprint() || !scanned.hasLocalFingerprint() ||
!scanned.hasVersion() || scanned.getVersion() != version)
!scanned.hasVersion() || scanned.getVersion() != VERSION)
{
throw new FingerprintVersionMismatchException(scanned.getVersion(), version);
throw new FingerprintVersionMismatchException(scanned.getVersion(), VERSION);
}
return MessageDigest.isEqual(fingerprints.getLocalFingerprint().getContent().toByteArray(), scanned.getRemoteFingerprint().getContent().toByteArray()) &&
MessageDigest.isEqual(fingerprints.getRemoteFingerprint().getContent().toByteArray(), scanned.getLocalFingerprint().getContent().toByteArray());
return MessageDigest.isEqual(fingerprints.getLocalFingerprint().getData().toByteArray(), scanned.getRemoteFingerprint().getData().toByteArray()) &&
MessageDigest.isEqual(fingerprints.getRemoteFingerprint().getData().toByteArray(), scanned.getLocalFingerprint().getData().toByteArray());
} catch (InvalidProtocolBufferException e) {
throw new FingerprintParsingException(e);
}

View File

@ -20,7 +20,7 @@ import static org.whispersystems.libsignal.state.StorageProtos.SenderKeyRecordSt
* A durable representation of a set of SenderKeyStates for a specific
* SenderKeyName.
*
* @author Moxie Marlinspike
* @author Moxie Marlisnpike
*/
public class SenderKeyRecord {

View File

@ -7,6 +7,7 @@ package org.whispersystems.libsignal.protocol;
public interface CiphertextMessage {
public static final int UNSUPPORTED_VERSION = 1;
public static final int CURRENT_VERSION = 3;
public static final int WHISPER_TYPE = 2;

View File

@ -41,7 +41,7 @@ public class SignalMessage implements CiphertextMessage {
byte[] message = messageParts[1];
byte[] mac = messageParts[2];
if (ByteUtil.highBitsToInt(version) < CURRENT_VERSION) {
if (ByteUtil.highBitsToInt(version) <= CiphertextMessage.UNSUPPORTED_VERSION) {
throw new LegacyMessageException("Legacy message: " + ByteUtil.highBitsToInt(version));
}
@ -82,7 +82,8 @@ public class SignalMessage implements CiphertextMessage {
.setCiphertext(ByteString.copyFrom(ciphertext))
.build().toByteArray();
byte[] mac = getMac(senderIdentityKey, receiverIdentityKey, macKey, ByteUtil.combine(version, message));
byte[] mac = getMac(messageVersion, senderIdentityKey, receiverIdentityKey, macKey,
ByteUtil.combine(version, message));
this.serialized = ByteUtil.combine(version, message, mac);
this.senderRatchetKey = senderRatchetKey;
@ -108,11 +109,12 @@ public class SignalMessage implements CiphertextMessage {
return ciphertext;
}
public void verifyMac(IdentityKey senderIdentityKey, IdentityKey receiverIdentityKey, SecretKeySpec macKey)
public void verifyMac(int messageVersion, IdentityKey senderIdentityKey,
IdentityKey receiverIdentityKey, SecretKeySpec macKey)
throws InvalidMessageException
{
byte[][] parts = ByteUtil.split(serialized, serialized.length - MAC_LENGTH, MAC_LENGTH);
byte[] ourMac = getMac(senderIdentityKey, receiverIdentityKey, macKey, parts[0]);
byte[] ourMac = getMac(messageVersion, senderIdentityKey, receiverIdentityKey, macKey, parts[0]);
byte[] theirMac = parts[1];
if (!MessageDigest.isEqual(ourMac, theirMac)) {
@ -120,7 +122,8 @@ public class SignalMessage implements CiphertextMessage {
}
}
private byte[] getMac(IdentityKey senderIdentityKey,
private byte[] getMac(int messageVersion,
IdentityKey senderIdentityKey,
IdentityKey receiverIdentityKey,
SecretKeySpec macKey, byte[] serialized)
{
@ -128,8 +131,10 @@ public class SignalMessage implements CiphertextMessage {
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(macKey);
mac.update(senderIdentityKey.getPublicKey().serialize());
mac.update(receiverIdentityKey.getPublicKey().serialize());
if (messageVersion >= 3) {
mac.update(senderIdentityKey.getPublicKey().serialize());
mac.update(receiverIdentityKey.getPublicKey().serialize());
}
byte[] fullMac = mac.doFinal(serialized);
return ByteUtil.trim(fullMac, MAC_LENGTH);
@ -150,7 +155,7 @@ public class SignalMessage implements CiphertextMessage {
public static boolean isLegacy(byte[] message) {
return message != null && message.length >= 1 &&
ByteUtil.highBitsToInt(message[0]) != CiphertextMessage.CURRENT_VERSION;
ByteUtil.highBitsToInt(message[0]) <= CiphertextMessage.UNSUPPORTED_VERSION;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -16,10 +16,6 @@ import org.whispersystems.libsignal.SignalProtocolAddress;
*/
public interface IdentityKeyStore {
public enum Direction {
SENDING, RECEIVING
}
/**
* Get the local client's identity key pair.
*
@ -44,39 +40,24 @@ public interface IdentityKeyStore {
*
* @param address The address of the remote client.
* @param identityKey The remote client's identity key.
* @return True if the identity key replaces a previous identity, false if not
*/
public boolean saveIdentity(SignalProtocolAddress address, IdentityKey identityKey);
public void saveIdentity(SignalProtocolAddress address, IdentityKey identityKey);
/**
* Verify a remote client's identity key.
* <p>
* Determine whether a remote client's identity is trusted. Convention is
* that the Signal Protocol is 'trust on first use.' This means that
* that the TextSecure protocol is 'trust on first use.' This means that
* an identity key is considered 'trusted' if there is no entry for the recipient
* in the local store, or if it matches the saved key for a recipient in the local
* store. Only if it mismatches an entry in the local store is it considered
* 'untrusted.'
*
* Clients may wish to make a distinction as to how keys are trusted based on the
* direction of travel. For instance, clients may wish to accept all 'incoming' identity
* key changes, while only blocking identity key changes when sending a message.
*
* @param address The address of the remote client.
* @param identityKey The identity key to verify.
* @param direction The direction (sending or receiving) this identity is being used for.
* @return true if trusted, false if untrusted.
*/
public boolean isTrustedIdentity(SignalProtocolAddress address, IdentityKey identityKey, Direction direction);
/**
* Return the saved public identity key for a remote client
*
* @param address The address of the remote client
* @return The public identity key, or null if absent
*/
public IdentityKey getIdentity(SignalProtocolAddress address);
public boolean isTrustedIdentity(SignalProtocolAddress address, IdentityKey identityKey);
}

View File

@ -74,9 +74,6 @@ public class SessionRecord {
return previousStates;
}
public void removePreviousSessionStates() {
previousStates.clear();
}
public boolean isFresh() {
return fresh;

File diff suppressed because it is too large Load Diff

View File

@ -36,25 +36,13 @@ public class InMemoryIdentityKeyStore implements IdentityKeyStore {
}
@Override
public boolean saveIdentity(SignalProtocolAddress address, IdentityKey identityKey) {
IdentityKey existing = trustedKeys.get(address);
if (!identityKey.equals(existing)) {
trustedKeys.put(address, identityKey);
return true;
} else {
return false;
}
public void saveIdentity(SignalProtocolAddress address, IdentityKey identityKey) {
trustedKeys.put(address, identityKey);
}
@Override
public boolean isTrustedIdentity(SignalProtocolAddress address, IdentityKey identityKey, Direction direction) {
public boolean isTrustedIdentity(SignalProtocolAddress address, IdentityKey identityKey) {
IdentityKey trusted = trustedKeys.get(address);
return (trusted == null || trusted.equals(identityKey));
}
@Override
public IdentityKey getIdentity(SignalProtocolAddress address) {
return trustedKeys.get(address);
}
}

View File

@ -39,18 +39,13 @@ public class InMemorySignalProtocolStore implements SignalProtocolStore {
}
@Override
public boolean saveIdentity(SignalProtocolAddress address, IdentityKey identityKey) {
return identityKeyStore.saveIdentity(address, identityKey);
public void saveIdentity(SignalProtocolAddress address, IdentityKey identityKey) {
identityKeyStore.saveIdentity(address, identityKey);
}
@Override
public boolean isTrustedIdentity(SignalProtocolAddress address, IdentityKey identityKey, Direction direction) {
return identityKeyStore.isTrustedIdentity(address, identityKey, direction);
}
@Override
public IdentityKey getIdentity(SignalProtocolAddress address) {
return identityKeyStore.getIdentity(address);
public boolean isTrustedIdentity(SignalProtocolAddress address, IdentityKey identityKey) {
return identityKeyStore.isTrustedIdentity(address, identityKey);
}
@Override

View File

@ -92,6 +92,17 @@ public class KeyHelper {
return results;
}
/**
* Generate the last resort PreKey. Clients should do this only once, at install
* time, and durably store it for the length of the install.
*
* @return the generated last resort PreKeyRecord.
*/
public static PreKeyRecord generateLastResortPreKey() {
ECKeyPair keyPair = Curve.generateKeyPair();
return new PreKeyRecord(Medium.MAX_VALUE, keyPair);
}
/**
* Generate a signed PreKey
*

View File

@ -1,16 +1,14 @@
syntax = "proto2";
package textsecure;
option java_package = "org.whispersystems.libsignal.fingerprint";
option java_package = "org.whispersystems.libsignal.fingerprint";
option java_outer_classname = "FingerprintProtos";
message LogicalFingerprint {
optional bytes content = 1;
optional bytes data = 1;
// optional bytes identifier = 2;
}
message CombinedFingerprints {
message LogicalFingerprints {
optional uint32 version = 1;
optional LogicalFingerprint localFingerprint = 2;
optional LogicalFingerprint remoteFingerprint = 3;

View File

@ -1,8 +1,6 @@
syntax = "proto2";
package textsecure;
option java_package = "org.whispersystems.libsignal.state";
option java_package = "org.whispersystems.libsignal.state";
option java_outer_classname = "StorageProtos";
message SessionStructure {

3
protobuf/Makefile Normal file
View File

@ -0,0 +1,3 @@
all:
protoc --java_out=../java/src/main/java/ WhisperTextProtocol.proto LocalStorageProtocol.proto FingerprintProtocol.proto

View File

@ -1,8 +1,6 @@
syntax = "proto2";
package textsecure;
option java_package = "org.whispersystems.libsignal.protocol";
option java_package = "org.whispersystems.libsignal.protocol";
option java_outer_classname = "SignalProtos";
message SignalMessage {

View File

@ -357,7 +357,7 @@ public class SessionBuilderTest extends TestCase {
private void runInteraction(SignalProtocolStore aliceStore, SignalProtocolStore bobStore)
throws DuplicateMessageException, LegacyMessageException, InvalidMessageException, NoSessionException, UntrustedIdentityException
throws DuplicateMessageException, LegacyMessageException, InvalidMessageException, NoSessionException
{
SessionCipher aliceSessionCipher = new SessionCipher(aliceStore, BOB_ADDRESS);
SessionCipher bobSessionCipher = new SessionCipher(bobStore, ALICE_ADDRESS);

View File

@ -28,7 +28,7 @@ public class SessionCipherTest extends TestCase {
public void testBasicSessionV3()
throws InvalidKeyException, DuplicateMessageException,
LegacyMessageException, InvalidMessageException, NoSuchAlgorithmException, NoSessionException, UntrustedIdentityException
LegacyMessageException, InvalidMessageException, NoSuchAlgorithmException, NoSessionException
{
SessionRecord aliceSessionRecord = new SessionRecord();
SessionRecord bobSessionRecord = new SessionRecord();
@ -70,7 +70,7 @@ public class SessionCipherTest extends TestCase {
}
private void runInteraction(SessionRecord aliceSessionRecord, SessionRecord bobSessionRecord)
throws DuplicateMessageException, LegacyMessageException, InvalidMessageException, NoSuchAlgorithmException, NoSessionException, UntrustedIdentityException {
throws DuplicateMessageException, LegacyMessageException, InvalidMessageException, NoSuchAlgorithmException, NoSessionException {
SignalProtocolStore aliceStore = new TestInMemorySignalProtocolStore();
SignalProtocolStore bobStore = new TestInMemorySignalProtocolStore();

View File

@ -133,48 +133,4 @@ public class Curve25519Test extends TestCase {
}
}
}
public void testDecodeSize() throws InvalidKeyException {
ECKeyPair keyPair = Curve.generateKeyPair();
byte[] serializedPublic = keyPair.getPublicKey().serialize();
ECPublicKey justRight = Curve.decodePoint(serializedPublic, 0);
try {
ECPublicKey tooSmall = Curve.decodePoint(serializedPublic, 1);
throw new AssertionError("Shouldn't decode");
} catch (InvalidKeyException e) {
// good
}
try {
ECPublicKey empty = Curve.decodePoint(new byte[0], 0);
throw new AssertionError("Shouldn't parse");
} catch (InvalidKeyException e) {
// good
}
try {
byte[] badKeyType = new byte[33];
System.arraycopy(serializedPublic, 0, badKeyType, 0, serializedPublic.length);
badKeyType[0] = 0x01;
Curve.decodePoint(badKeyType, 0);
throw new AssertionError("Should be bad key type");
} catch (InvalidKeyException e) {
// good
}
byte[] extraSpace = new byte[serializedPublic.length + 1];
System.arraycopy(serializedPublic, 0, extraSpace, 0, serializedPublic.length);
ECPublicKey extra = Curve.decodePoint(extraSpace, 0);
byte[] offsetSpace = new byte[serializedPublic.length + 1];
System.arraycopy(serializedPublic, 0, offsetSpace, 1, serializedPublic.length);
ECPublicKey offset = Curve.decodePoint(offsetSpace, 1);
assertTrue(Arrays.equals(serializedPublic, justRight.serialize()));
assertTrue(Arrays.equals(extra.serialize(), serializedPublic));
assertTrue(Arrays.equals(offset.serialize(), serializedPublic));
}
}

View File

@ -1,72 +1,42 @@
package org.whispersystems.libsignal.fingerprint;
import android.util.Log;
import junit.framework.TestCase;
import org.whispersystems.libsignal.IdentityKey;
import org.whispersystems.libsignal.ecc.Curve;
import org.whispersystems.libsignal.ecc.ECKeyPair;
import org.whispersystems.libsignal.util.Hex;
import java.util.Arrays;
public class NumericFingerprintGeneratorTest extends TestCase {
private static final byte[] ALICE_IDENTITY = {(byte) 0x05, (byte) 0x06, (byte) 0x86, (byte) 0x3b, (byte) 0xc6, (byte) 0x6d, (byte) 0x02, (byte) 0xb4, (byte) 0x0d, (byte) 0x27, (byte) 0xb8, (byte) 0xd4, (byte) 0x9c, (byte) 0xa7, (byte) 0xc0, (byte) 0x9e, (byte) 0x92, (byte) 0x39, (byte) 0x23, (byte) 0x6f, (byte) 0x9d, (byte) 0x7d, (byte) 0x25, (byte) 0xd6, (byte) 0xfc, (byte) 0xca, (byte) 0x5c, (byte) 0xe1, (byte) 0x3c, (byte) 0x70, (byte) 0x64, (byte) 0xd8, (byte) 0x68};
private static final byte[] BOB_IDENTITY = {(byte) 0x05, (byte) 0xf7, (byte) 0x81, (byte) 0xb6, (byte) 0xfb, (byte) 0x32, (byte) 0xfe, (byte) 0xd9, (byte) 0xba, (byte) 0x1c, (byte) 0xf2, (byte) 0xde, (byte) 0x97, (byte) 0x8d, (byte) 0x4d, (byte) 0x5d, (byte) 0xa2, (byte) 0x8d, (byte) 0xc3, (byte) 0x40, (byte) 0x46, (byte) 0xae, (byte) 0x81, (byte) 0x44, (byte) 0x02, (byte) 0xb5, (byte) 0xc0, (byte) 0xdb, (byte) 0xd9, (byte) 0x6f, (byte) 0xda, (byte) 0x90, (byte) 0x7b};
private static final String TAG = NumericFingerprintGeneratorTest.class.getSimpleName();
private static final int VERSION_1 = 1;
private static final String DISPLAYABLE_FINGERPRINT_V1 = "300354477692869396892869876765458257569162576843440918079131";
private static final byte[] ALICE_SCANNABLE_FINGERPRINT_V1 = new byte[]{(byte)0x08, (byte)0x01, (byte)0x12, (byte)0x22, (byte)0x0a, (byte)0x20, (byte)0x1e, (byte)0x30, (byte)0x1a, (byte)0x03, (byte)0x53, (byte)0xdc, (byte)0xe3, (byte)0xdb, (byte)0xe7, (byte)0x68, (byte)0x4c, (byte)0xb8, (byte)0x33, (byte)0x6e, (byte)0x85, (byte)0x13, (byte)0x6c, (byte)0xdc, (byte)0x0e, (byte)0xe9, (byte)0x62, (byte)0x19, (byte)0x49, (byte)0x4a, (byte)0xda, (byte)0x30, (byte)0x5d, (byte)0x62, (byte)0xa7, (byte)0xbd, (byte)0x61, (byte)0xdf, (byte)0x1a, (byte)0x22, (byte)0x0a, (byte)0x20, (byte)0xd6, (byte)0x2c, (byte)0xbf, (byte)0x73, (byte)0xa1, (byte)0x15, (byte)0x92, (byte)0x01, (byte)0x5b, (byte)0x6b, (byte)0x9f, (byte)0x16, (byte)0x82, (byte)0xac, (byte)0x30, (byte)0x6f, (byte)0xea, (byte)0x3a, (byte)0xaf, (byte)0x38, (byte)0x85, (byte)0xb8, (byte)0x4d, (byte)0x12, (byte)0xbc, (byte)0xa6, (byte)0x31, (byte)0xe9, (byte)0xd4, (byte)0xfb, (byte)0x3a, (byte)0x4d};
private static final byte[] BOB_SCANNABLE_FINGERPRINT_V1 = new byte[]{(byte)0x08, (byte)0x01, (byte)0x12, (byte)0x22, (byte)0x0a, (byte)0x20, (byte)0xd6, (byte)0x2c, (byte)0xbf, (byte)0x73, (byte)0xa1, (byte)0x15, (byte)0x92, (byte)0x01, (byte)0x5b, (byte)0x6b, (byte)0x9f, (byte)0x16, (byte)0x82, (byte)0xac, (byte)0x30, (byte)0x6f, (byte)0xea, (byte)0x3a, (byte)0xaf, (byte)0x38, (byte)0x85, (byte)0xb8, (byte)0x4d, (byte)0x12, (byte)0xbc, (byte)0xa6, (byte)0x31, (byte)0xe9, (byte)0xd4, (byte)0xfb, (byte)0x3a, (byte)0x4d, (byte)0x1a, (byte)0x22, (byte)0x0a, (byte)0x20, (byte)0x1e, (byte)0x30, (byte)0x1a, (byte)0x03, (byte)0x53, (byte)0xdc, (byte)0xe3, (byte)0xdb, (byte)0xe7, (byte)0x68, (byte)0x4c, (byte)0xb8, (byte)0x33, (byte)0x6e, (byte)0x85, (byte)0x13, (byte)0x6c, (byte)0xdc, (byte)0x0e, (byte)0xe9, (byte)0x62, (byte)0x19, (byte)0x49, (byte)0x4a, (byte)0xda, (byte)0x30, (byte)0x5d, (byte)0x62, (byte)0xa7, (byte)0xbd, (byte)0x61, (byte)0xdf};
private static final byte[] ALICE_IDENTITY = {(byte) 0x05, (byte) 0x06, (byte) 0x86, (byte) 0x3b, (byte) 0xc6, (byte) 0x6d, (byte) 0x02, (byte) 0xb4, (byte) 0x0d, (byte) 0x27, (byte) 0xb8, (byte) 0xd4, (byte) 0x9c, (byte) 0xa7, (byte) 0xc0, (byte) 0x9e, (byte) 0x92, (byte) 0x39, (byte) 0x23, (byte) 0x6f, (byte) 0x9d, (byte) 0x7d, (byte) 0x25, (byte) 0xd6, (byte) 0xfc, (byte) 0xca, (byte) 0x5c, (byte) 0xe1, (byte) 0x3c, (byte) 0x70, (byte) 0x64, (byte) 0xd8, (byte) 0x68};
private static final byte[] BOB_IDENTITY = {(byte) 0x05, (byte) 0xf7, (byte) 0x81, (byte) 0xb6, (byte) 0xfb, (byte) 0x32, (byte) 0xfe, (byte) 0xd9, (byte) 0xba, (byte) 0x1c, (byte) 0xf2, (byte) 0xde, (byte) 0x97, (byte) 0x8d, (byte) 0x4d, (byte) 0x5d, (byte) 0xa2, (byte) 0x8d, (byte) 0xc3, (byte) 0x40, (byte) 0x46, (byte) 0xae, (byte) 0x81, (byte) 0x44, (byte) 0x02, (byte) 0xb5, (byte) 0xc0, (byte) 0xdb, (byte) 0xd9, (byte) 0x6f, (byte) 0xda, (byte) 0x90, (byte) 0x7b};
private static final String DISPLAYABLE_FINGERPRINT = "300354477692869396892869876765458257569162576843440918079131";
private static final byte[] ALICE_SCANNABLE_FINGERPRINT = new byte[]{(byte) 0x08, (byte) 0x00, (byte) 0x12, (byte) 0x31, (byte) 0x0a, (byte) 0x21, (byte) 0x05, (byte) 0x06, (byte) 0x86, (byte) 0x3b, (byte) 0xc6, (byte) 0x6d, (byte) 0x02, (byte) 0xb4, (byte) 0x0d, (byte) 0x27, (byte) 0xb8, (byte) 0xd4, (byte) 0x9c, (byte) 0xa7, (byte) 0xc0, (byte) 0x9e, (byte) 0x92, (byte) 0x39, (byte) 0x23, (byte) 0x6f, (byte) 0x9d, (byte) 0x7d, (byte) 0x25, (byte) 0xd6, (byte) 0xfc, (byte) 0xca, (byte) 0x5c, (byte) 0xe1, (byte) 0x3c, (byte) 0x70, (byte) 0x64, (byte) 0xd8, (byte) 0x68, (byte) 0x12, (byte) 0x0c, (byte) 0x2b, (byte) 0x31, (byte) 0x34, (byte) 0x31, (byte) 0x35, (byte) 0x32, (byte) 0x32, (byte) 0x32, (byte) 0x32, (byte) 0x32, (byte) 0x32, (byte) 0x32, (byte) 0x1a, (byte) 0x31, (byte) 0x0a, (byte) 0x21, (byte) 0x05, (byte) 0xf7, (byte) 0x81, (byte) 0xb6, (byte) 0xfb, (byte) 0x32, (byte) 0xfe, (byte) 0xd9, (byte) 0xba, (byte) 0x1c, (byte) 0xf2, (byte) 0xde, (byte) 0x97, (byte) 0x8d, (byte) 0x4d, (byte) 0x5d, (byte) 0xa2, (byte) 0x8d, (byte) 0xc3, (byte) 0x40, (byte) 0x46, (byte) 0xae, (byte) 0x81, (byte) 0x44, (byte) 0x02, (byte) 0xb5, (byte) 0xc0, (byte) 0xdb, (byte) 0xd9, (byte) 0x6f, (byte) 0xda, (byte) 0x90, (byte) 0x7b, (byte) 0x12, (byte) 0x0c, (byte) 0x2b, (byte) 0x31, (byte) 0x34, (byte) 0x31, (byte) 0x35, (byte) 0x33, (byte) 0x33, (byte) 0x33, (byte) 0x33, (byte) 0x33, (byte) 0x33, (byte) 0x33};
private static final byte[] BOB_SCANNABLE_FINGERPRINT = new byte[]{(byte) 0x08, (byte) 0x00, (byte) 0x12, (byte) 0x31, (byte) 0x0a, (byte) 0x21, (byte) 0x05, (byte) 0xf7, (byte) 0x81, (byte) 0xb6, (byte) 0xfb, (byte) 0x32, (byte) 0xfe, (byte) 0xd9, (byte) 0xba, (byte) 0x1c, (byte) 0xf2, (byte) 0xde, (byte) 0x97, (byte) 0x8d, (byte) 0x4d, (byte) 0x5d, (byte) 0xa2, (byte) 0x8d, (byte) 0xc3, (byte) 0x40, (byte) 0x46, (byte) 0xae, (byte) 0x81, (byte) 0x44, (byte) 0x02, (byte) 0xb5, (byte) 0xc0, (byte) 0xdb, (byte) 0xd9, (byte) 0x6f, (byte) 0xda, (byte) 0x90, (byte) 0x7b, (byte) 0x12, (byte) 0x0c, (byte) 0x2b, (byte) 0x31, (byte) 0x34, (byte) 0x31, (byte) 0x35, (byte) 0x33, (byte) 0x33, (byte) 0x33, (byte) 0x33, (byte) 0x33, (byte) 0x33, (byte) 0x33, (byte) 0x1a, (byte) 0x31, (byte) 0x0a, (byte) 0x21, (byte) 0x05, (byte) 0x06, (byte) 0x86, (byte) 0x3b, (byte) 0xc6, (byte) 0x6d, (byte) 0x02, (byte) 0xb4, (byte) 0x0d, (byte) 0x27, (byte) 0xb8, (byte) 0xd4, (byte) 0x9c, (byte) 0xa7, (byte) 0xc0, (byte) 0x9e, (byte) 0x92, (byte) 0x39, (byte) 0x23, (byte) 0x6f, (byte) 0x9d, (byte) 0x7d, (byte) 0x25, (byte) 0xd6, (byte) 0xfc, (byte) 0xca, (byte) 0x5c, (byte) 0xe1, (byte) 0x3c, (byte) 0x70, (byte) 0x64, (byte) 0xd8, (byte) 0x68, (byte) 0x12, (byte) 0x0c, (byte) 0x2b, (byte) 0x31, (byte) 0x34, (byte) 0x31, (byte) 0x35, (byte) 0x32, (byte) 0x32, (byte) 0x32, (byte) 0x32, (byte) 0x32, (byte) 0x32, (byte) 0x32};
private static final int VERSION_2 = 2;
private static final String DISPLAYABLE_FINGERPRINT_V2 = DISPLAYABLE_FINGERPRINT_V1;
private static final byte[] ALICE_SCANNABLE_FINGERPRINT_V2 = new byte[]{(byte)0x08, (byte)0x02, (byte)0x12, (byte)0x22, (byte)0x0a, (byte)0x20, (byte)0x1e, (byte)0x30, (byte)0x1a, (byte)0x03, (byte)0x53, (byte)0xdc, (byte)0xe3, (byte)0xdb, (byte)0xe7, (byte)0x68, (byte)0x4c, (byte)0xb8, (byte)0x33, (byte)0x6e, (byte)0x85, (byte)0x13, (byte)0x6c, (byte)0xdc, (byte)0x0e, (byte)0xe9, (byte)0x62, (byte)0x19, (byte)0x49, (byte)0x4a, (byte)0xda, (byte)0x30, (byte)0x5d, (byte)0x62, (byte)0xa7, (byte)0xbd, (byte)0x61, (byte)0xdf, (byte)0x1a, (byte)0x22, (byte)0x0a, (byte)0x20, (byte)0xd6, (byte)0x2c, (byte)0xbf, (byte)0x73, (byte)0xa1, (byte)0x15, (byte)0x92, (byte)0x01, (byte)0x5b, (byte)0x6b, (byte)0x9f, (byte)0x16, (byte)0x82, (byte)0xac, (byte)0x30, (byte)0x6f, (byte)0xea, (byte)0x3a, (byte)0xaf, (byte)0x38, (byte)0x85, (byte)0xb8, (byte)0x4d, (byte)0x12, (byte)0xbc, (byte)0xa6, (byte)0x31, (byte)0xe9, (byte)0xd4, (byte)0xfb, (byte)0x3a, (byte)0x4d};
private static final byte[] BOB_SCANNABLE_FINGERPRINT_V2 = new byte[]{(byte)0x08, (byte)0x02, (byte)0x12, (byte)0x22, (byte)0x0a, (byte)0x20, (byte)0xd6, (byte)0x2c, (byte)0xbf, (byte)0x73, (byte)0xa1, (byte)0x15, (byte)0x92, (byte)0x01, (byte)0x5b, (byte)0x6b, (byte)0x9f, (byte)0x16, (byte)0x82, (byte)0xac, (byte)0x30, (byte)0x6f, (byte)0xea, (byte)0x3a, (byte)0xaf, (byte)0x38, (byte)0x85, (byte)0xb8, (byte)0x4d, (byte)0x12, (byte)0xbc, (byte)0xa6, (byte)0x31, (byte)0xe9, (byte)0xd4, (byte)0xfb, (byte)0x3a, (byte)0x4d, (byte)0x1a, (byte)0x22, (byte)0x0a, (byte)0x20, (byte)0x1e, (byte)0x30, (byte)0x1a, (byte)0x03, (byte)0x53, (byte)0xdc, (byte)0xe3, (byte)0xdb, (byte)0xe7, (byte)0x68, (byte)0x4c, (byte)0xb8, (byte)0x33, (byte)0x6e, (byte)0x85, (byte)0x13, (byte)0x6c, (byte)0xdc, (byte)0x0e, (byte)0xe9, (byte)0x62, (byte)0x19, (byte)0x49, (byte)0x4a, (byte)0xda, (byte)0x30, (byte)0x5d, (byte)0x62, (byte)0xa7, (byte)0xbd, (byte)0x61, (byte)0xdf};
public void testVectorsVersion1() throws Exception {
public void testVectors() throws Exception {
IdentityKey aliceIdentityKey = new IdentityKey(ALICE_IDENTITY, 0);
IdentityKey bobIdentityKey = new IdentityKey(BOB_IDENTITY, 0);
byte[] aliceStableId = "+14152222222".getBytes();
byte[] bobStableId = "+14153333333".getBytes();
NumericFingerprintGenerator generator = new NumericFingerprintGenerator(5200);
NumericFingerprintGenerator generator = new NumericFingerprintGenerator(5200);
Fingerprint aliceFingerprint = generator.createFor("+14152222222", aliceIdentityKey,
"+14153333333", bobIdentityKey);
Fingerprint aliceFingerprint = generator.createFor(VERSION_1,
aliceStableId, aliceIdentityKey,
bobStableId, bobIdentityKey);
Fingerprint bobFingerprint = generator.createFor("+14153333333", bobIdentityKey,
"+14152222222", aliceIdentityKey);
Fingerprint bobFingerprint = generator.createFor(VERSION_1,
bobStableId, bobIdentityKey,
aliceStableId, aliceIdentityKey);
assertEquals(aliceFingerprint.getDisplayableFingerprint().getDisplayText(), DISPLAYABLE_FINGERPRINT);
assertEquals(bobFingerprint.getDisplayableFingerprint().getDisplayText(), DISPLAYABLE_FINGERPRINT);
assertEquals(aliceFingerprint.getDisplayableFingerprint().getDisplayText(), DISPLAYABLE_FINGERPRINT_V1);
assertEquals(bobFingerprint.getDisplayableFingerprint().getDisplayText(), DISPLAYABLE_FINGERPRINT_V1);
assertTrue(Arrays.equals(aliceFingerprint.getScannableFingerprint().getSerialized(), ALICE_SCANNABLE_FINGERPRINT_V1));
assertTrue(Arrays.equals(bobFingerprint.getScannableFingerprint().getSerialized(), BOB_SCANNABLE_FINGERPRINT_V1));
}
public void testVectorsVersion2() throws Exception {
IdentityKey aliceIdentityKey = new IdentityKey(ALICE_IDENTITY, 0);
IdentityKey bobIdentityKey = new IdentityKey(BOB_IDENTITY, 0);
byte[] aliceStableId = "+14152222222".getBytes();
byte[] bobStableId = "+14153333333".getBytes();
NumericFingerprintGenerator generator = new NumericFingerprintGenerator(5200);
Fingerprint aliceFingerprint = generator.createFor(VERSION_2,
aliceStableId, aliceIdentityKey,
bobStableId, bobIdentityKey);
Fingerprint bobFingerprint = generator.createFor(VERSION_2,
bobStableId, bobIdentityKey,
aliceStableId, aliceIdentityKey);
assertEquals(aliceFingerprint.getDisplayableFingerprint().getDisplayText(), DISPLAYABLE_FINGERPRINT_V2);
assertEquals(bobFingerprint.getDisplayableFingerprint().getDisplayText(), DISPLAYABLE_FINGERPRINT_V2);
assertTrue(Arrays.equals(aliceFingerprint.getScannableFingerprint().getSerialized(), ALICE_SCANNABLE_FINGERPRINT_V2));
assertTrue(Arrays.equals(bobFingerprint.getScannableFingerprint().getSerialized(), BOB_SCANNABLE_FINGERPRINT_V2));
assertTrue(Arrays.equals(aliceFingerprint.getScannableFingerprint().getSerialized(), ALICE_SCANNABLE_FINGERPRINT));
assertTrue(Arrays.equals(bobFingerprint.getScannableFingerprint().getSerialized(), BOB_SCANNABLE_FINGERPRINT));
}
public void testMatchingFingerprints() throws FingerprintVersionMismatchException, FingerprintIdentifierMismatchException, FingerprintParsingException {
@ -77,13 +47,11 @@ public class NumericFingerprintGeneratorTest extends TestCase {
IdentityKey bobIdentityKey = new IdentityKey(bobKeyPair.getPublicKey());
NumericFingerprintGenerator generator = new NumericFingerprintGenerator(1024);
Fingerprint aliceFingerprint = generator.createFor(VERSION_1,
"+14152222222".getBytes(), aliceIdentityKey,
"+14153333333".getBytes(), bobIdentityKey);
Fingerprint aliceFingerprint = generator.createFor("+14152222222", aliceIdentityKey,
"+14153333333", bobIdentityKey);
Fingerprint bobFingerprint = generator.createFor(VERSION_1,
"+14153333333".getBytes(), bobIdentityKey,
"+14152222222".getBytes(), aliceIdentityKey);
Fingerprint bobFingerprint = generator.createFor("+14153333333", bobIdentityKey,
"+14152222222", aliceIdentityKey);
assertEquals(aliceFingerprint.getDisplayableFingerprint().getDisplayText(),
bobFingerprint.getDisplayableFingerprint().getDisplayText());
@ -104,16 +72,14 @@ public class NumericFingerprintGeneratorTest extends TestCase {
IdentityKey mitmIdentityKey = new IdentityKey(mitmKeyPair.getPublicKey());
NumericFingerprintGenerator generator = new NumericFingerprintGenerator(1024);
Fingerprint aliceFingerprint = generator.createFor(VERSION_1,
"+14152222222".getBytes(), aliceIdentityKey,
"+14153333333".getBytes(), mitmIdentityKey);
Fingerprint aliceFingerprint = generator.createFor("+14152222222", aliceIdentityKey,
"+14153333333", mitmIdentityKey);
Fingerprint bobFingerprint = generator.createFor(VERSION_1,
"+14153333333".getBytes(), bobIdentityKey,
"+14152222222".getBytes(), aliceIdentityKey);
Fingerprint bobFingerprint = generator.createFor("+14153333333", bobIdentityKey,
"+14152222222", aliceIdentityKey);
assertFalse(aliceFingerprint.getDisplayableFingerprint().getDisplayText().equals(
bobFingerprint.getDisplayableFingerprint().getDisplayText()));
assertNotSame(aliceFingerprint.getDisplayableFingerprint().getDisplayText(),
bobFingerprint.getDisplayableFingerprint().getDisplayText());
assertFalse(aliceFingerprint.getScannableFingerprint().compareTo(bobFingerprint.getScannableFingerprint().getSerialized()));
assertFalse(bobFingerprint.getScannableFingerprint().compareTo(aliceFingerprint.getScannableFingerprint().getSerialized()));
@ -127,43 +93,28 @@ public class NumericFingerprintGeneratorTest extends TestCase {
IdentityKey bobIdentityKey = new IdentityKey(bobKeyPair.getPublicKey());
NumericFingerprintGenerator generator = new NumericFingerprintGenerator(1024);
Fingerprint aliceFingerprint = generator.createFor(VERSION_1,
"+141512222222".getBytes(), aliceIdentityKey,
"+14153333333".getBytes(), bobIdentityKey);
Fingerprint aliceFingerprint = generator.createFor("+141512222222", aliceIdentityKey,
"+14153333333", bobIdentityKey);
Fingerprint bobFingerprint = generator.createFor(VERSION_1,
"+14153333333".getBytes(), bobIdentityKey,
"+14152222222".getBytes(), aliceIdentityKey);
Fingerprint bobFingerprint = generator.createFor("+14153333333", bobIdentityKey,
"+14152222222", aliceIdentityKey);
assertFalse(aliceFingerprint.getDisplayableFingerprint().getDisplayText().equals(
bobFingerprint.getDisplayableFingerprint().getDisplayText()));
assertNotSame(aliceFingerprint.getDisplayableFingerprint().getDisplayText(),
bobFingerprint.getDisplayableFingerprint().getDisplayText());
assertFalse(aliceFingerprint.getScannableFingerprint().compareTo(bobFingerprint.getScannableFingerprint().getSerialized()));
assertFalse(bobFingerprint.getScannableFingerprint().compareTo(aliceFingerprint.getScannableFingerprint().getSerialized()));
}
try {;
aliceFingerprint.getScannableFingerprint().compareTo(bobFingerprint.getScannableFingerprint().getSerialized());
throw new AssertionError("Should mismatch!");
} catch (FingerprintIdentifierMismatchException e) {
// good
}
public void testDifferentVersionsMakeSameFingerPrintsButDifferentScannable() throws Exception {
IdentityKey aliceIdentityKey = new IdentityKey(ALICE_IDENTITY, 0);
IdentityKey bobIdentityKey = new IdentityKey(BOB_IDENTITY, 0);
byte[] aliceStableId = "+14152222222".getBytes();
byte[] bobStableId = "+14153333333".getBytes();
NumericFingerprintGenerator generator = new NumericFingerprintGenerator(5200);
Fingerprint aliceFingerprintV1 = generator.createFor(VERSION_1,
aliceStableId, aliceIdentityKey,
bobStableId, bobIdentityKey);
Fingerprint aliceFingerprintV2 = generator.createFor(VERSION_2,
aliceStableId, aliceIdentityKey,
bobStableId, bobIdentityKey);
assertTrue(aliceFingerprintV1.getDisplayableFingerprint().getDisplayText().equals(
aliceFingerprintV2.getDisplayableFingerprint().getDisplayText()));
assertFalse(Arrays.equals(aliceFingerprintV1.getScannableFingerprint().getSerialized(),
aliceFingerprintV2.getScannableFingerprint().getSerialized()));
try {
bobFingerprint.getScannableFingerprint().compareTo(aliceFingerprint.getScannableFingerprint().getSerialized());
throw new AssertionError("Should mismatch!");
} catch (FingerprintIdentifierMismatchException e) {
// good
}
}
}