mirror of
https://github.com/oxen-io/session-android.git
synced 2025-12-11 18:31:51 +00:00
Break core ratchet out into libaxolotol.
1) Break the core cryptography functions out into libaxolotol. 2) The objective for this code is a Java library that isn't dependent on any Android functions. However, while the code has been separated from any Android functionality, it is still an 'android library project' because of the JNI.
This commit is contained in:
@@ -0,0 +1,321 @@
|
||||
package org.whispersystems.test;
|
||||
|
||||
import org.whispersystems.libaxolotl.IdentityKey;
|
||||
import org.whispersystems.libaxolotl.IdentityKeyPair;
|
||||
import org.whispersystems.libaxolotl.InvalidKeyException;
|
||||
import org.whispersystems.libaxolotl.SessionState;
|
||||
import org.whispersystems.libaxolotl.ecc.ECKeyPair;
|
||||
import org.whispersystems.libaxolotl.ecc.ECPublicKey;
|
||||
import org.whispersystems.libaxolotl.ratchet.ChainKey;
|
||||
import org.whispersystems.libaxolotl.ratchet.MessageKeys;
|
||||
import org.whispersystems.libaxolotl.ratchet.RootKey;
|
||||
import org.whispersystems.libaxolotl.util.Pair;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
|
||||
public class InMemorySessionState implements SessionState {
|
||||
|
||||
private Map<ECPublicKey, InMemoryChain> receiverChains = new HashMap<>();
|
||||
|
||||
private boolean needsRefresh;
|
||||
private int sessionVersion;
|
||||
private IdentityKey remoteIdentityKey;
|
||||
private IdentityKey localIdentityKey;
|
||||
private int previousCounter;
|
||||
private RootKey rootKey;
|
||||
private ECKeyPair senderEphemeral;
|
||||
private ChainKey senderChainKey;
|
||||
private int pendingPreKeyid;
|
||||
private ECPublicKey pendingPreKey;
|
||||
private int remoteRegistrationId;
|
||||
private int localRegistrationId;
|
||||
|
||||
public InMemorySessionState() {}
|
||||
|
||||
public InMemorySessionState(SessionState sessionState) {
|
||||
try {
|
||||
this.needsRefresh = sessionState.getNeedsRefresh();
|
||||
this.sessionVersion = sessionState.getSessionVersion();
|
||||
this.remoteIdentityKey = new IdentityKey(sessionState.getRemoteIdentityKey().serialize(), 0);
|
||||
this.localIdentityKey = new IdentityKey(sessionState.getLocalIdentityKey().serialize(), 0);
|
||||
this.previousCounter = sessionState.getPreviousCounter();
|
||||
this.rootKey = new RootKey(sessionState.getRootKey().getKeyBytes());
|
||||
this.senderEphemeral = sessionState.getSenderEphemeralPair();
|
||||
this.senderChainKey = new ChainKey(sessionState.getSenderChainKey().getKey(),
|
||||
sessionState.getSenderChainKey().getIndex());
|
||||
this.pendingPreKeyid = sessionState.getPendingPreKey().first();
|
||||
this.pendingPreKey = sessionState.getPendingPreKey().second();
|
||||
this.remoteRegistrationId = sessionState.getRemoteRegistrationId();
|
||||
this.localRegistrationId = sessionState.getLocalRegistrationId();
|
||||
} catch (InvalidKeyException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setNeedsRefresh(boolean needsRefresh) {
|
||||
this.needsRefresh = needsRefresh;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getNeedsRefresh() {
|
||||
return needsRefresh;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSessionVersion(int version) {
|
||||
this.sessionVersion = version;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSessionVersion() {
|
||||
return sessionVersion;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRemoteIdentityKey(IdentityKey identityKey) {
|
||||
this.remoteIdentityKey = identityKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLocalIdentityKey(IdentityKey identityKey) {
|
||||
this.localIdentityKey = identityKey;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public IdentityKey getRemoteIdentityKey() {
|
||||
return remoteIdentityKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IdentityKey getLocalIdentityKey() {
|
||||
return localIdentityKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPreviousCounter() {
|
||||
return previousCounter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPreviousCounter(int previousCounter) {
|
||||
this.previousCounter = previousCounter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RootKey getRootKey() {
|
||||
return rootKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRootKey(RootKey rootKey) {
|
||||
this.rootKey = rootKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ECPublicKey getSenderEphemeral() {
|
||||
return senderEphemeral.getPublicKey();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ECKeyPair getSenderEphemeralPair() {
|
||||
return senderEphemeral;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasReceiverChain(ECPublicKey senderEphemeral) {
|
||||
return receiverChains.containsKey(senderEphemeral);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasSenderChain() {
|
||||
return senderChainKey != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChainKey getReceiverChainKey(ECPublicKey senderEphemeral) {
|
||||
InMemoryChain chain = receiverChains.get(senderEphemeral);
|
||||
return new ChainKey(chain.chainKey, chain.index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addReceiverChain(ECPublicKey senderEphemeral, ChainKey chainKey) {
|
||||
InMemoryChain chain = new InMemoryChain();
|
||||
chain.chainKey = chainKey.getKey();
|
||||
chain.index = chainKey.getIndex();
|
||||
|
||||
receiverChains.put(senderEphemeral, chain);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSenderChain(ECKeyPair senderEphemeralPair, ChainKey chainKey) {
|
||||
this.senderEphemeral = senderEphemeralPair;
|
||||
this.senderChainKey = chainKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChainKey getSenderChainKey() {
|
||||
return senderChainKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSenderChainKey(ChainKey nextChainKey) {
|
||||
this.senderChainKey = nextChainKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasMessageKeys(ECPublicKey senderEphemeral, int counter) {
|
||||
InMemoryChain chain = receiverChains.get(senderEphemeral);
|
||||
|
||||
if (chain == null) return false;
|
||||
|
||||
for (InMemoryChain.InMemoryMessageKey messageKey : chain.messageKeys) {
|
||||
if (messageKey.index == counter) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessageKeys removeMessageKeys(ECPublicKey senderEphemeral, int counter) {
|
||||
InMemoryChain chain = receiverChains.get(senderEphemeral);
|
||||
MessageKeys results = null;
|
||||
|
||||
if (chain == null) return null;
|
||||
|
||||
Iterator<InMemoryChain.InMemoryMessageKey> iterator = chain.messageKeys.iterator();
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
InMemoryChain.InMemoryMessageKey messageKey = iterator.next();
|
||||
|
||||
if (messageKey.index == counter) {
|
||||
results = new MessageKeys(new SecretKeySpec(messageKey.cipherKey, "AES"),
|
||||
new SecretKeySpec(messageKey.macKey, "HmacSHA256"),
|
||||
messageKey.index);
|
||||
|
||||
iterator.remove();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMessageKeys(ECPublicKey senderEphemeral, MessageKeys messageKeys) {
|
||||
InMemoryChain chain = receiverChains.get(senderEphemeral);
|
||||
InMemoryChain.InMemoryMessageKey key = new InMemoryChain.InMemoryMessageKey();
|
||||
|
||||
key.cipherKey = messageKeys.getCipherKey().getEncoded();
|
||||
key.macKey = messageKeys.getMacKey().getEncoded();
|
||||
key.index = messageKeys.getCounter();
|
||||
|
||||
chain.messageKeys.add(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setReceiverChainKey(ECPublicKey senderEphemeral, ChainKey chainKey) {
|
||||
InMemoryChain chain = receiverChains.get(senderEphemeral);
|
||||
chain.chainKey = chainKey.getKey();
|
||||
chain.index = chainKey.getIndex();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPendingKeyExchange(int sequence, ECKeyPair ourBaseKey, ECKeyPair ourEphemeralKey, IdentityKeyPair ourIdentityKey) {
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPendingKeyExchangeSequence() {
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ECKeyPair getPendingKeyExchangeBaseKey() throws InvalidKeyException {
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ECKeyPair getPendingKeyExchangeEphemeralKey() throws InvalidKeyException {
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IdentityKeyPair getPendingKeyExchangeIdentityKey() throws InvalidKeyException {
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPendingKeyExchange() {
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPendingPreKey(int preKeyId, ECPublicKey baseKey) {
|
||||
this.pendingPreKeyid = preKeyId;
|
||||
this.pendingPreKey = baseKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPendingPreKey() {
|
||||
return this.pendingPreKey != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Pair<Integer, ECPublicKey> getPendingPreKey() {
|
||||
return new Pair<>(pendingPreKeyid, pendingPreKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearPendingPreKey() {
|
||||
this.pendingPreKey = null;
|
||||
this.pendingPreKeyid = -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRemoteRegistrationId(int registrationId) {
|
||||
this.remoteRegistrationId = registrationId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRemoteRegistrationId() {
|
||||
return remoteRegistrationId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLocalRegistrationId(int registrationId) {
|
||||
this.localRegistrationId = registrationId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLocalRegistrationId() {
|
||||
return localRegistrationId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] serialize() {
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
private static class InMemoryChain {
|
||||
byte[] chainKey;
|
||||
int index;
|
||||
List<InMemoryMessageKey> messageKeys = new LinkedList<>();
|
||||
|
||||
public static class InMemoryMessageKey {
|
||||
public InMemoryMessageKey(){}
|
||||
int index;
|
||||
byte[] cipherKey;
|
||||
byte[] macKey;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package org.whispersystems.test;
|
||||
|
||||
import org.whispersystems.libaxolotl.SessionState;
|
||||
import org.whispersystems.libaxolotl.SessionStore;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
public class InMemorySessionStore implements SessionStore {
|
||||
|
||||
private SessionState currentSessionState;
|
||||
private List<SessionState> previousSessionStates;
|
||||
|
||||
private SessionState checkedOutSessionState;
|
||||
private List<SessionState> checkedOutPreviousSessionStates;
|
||||
|
||||
public InMemorySessionStore(SessionState sessionState) {
|
||||
this.currentSessionState = sessionState;
|
||||
this.previousSessionStates = new LinkedList<>();
|
||||
this.checkedOutPreviousSessionStates = new LinkedList<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SessionState getSessionState() {
|
||||
checkedOutSessionState = new InMemorySessionState(currentSessionState);
|
||||
return checkedOutSessionState;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<SessionState> getPreviousSessionStates() {
|
||||
checkedOutPreviousSessionStates = new LinkedList<>();
|
||||
for (SessionState state : previousSessionStates) {
|
||||
checkedOutPreviousSessionStates.add(new InMemorySessionState(state));
|
||||
}
|
||||
|
||||
return checkedOutPreviousSessionStates;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save() {
|
||||
this.currentSessionState = this.checkedOutSessionState;
|
||||
this.previousSessionStates = this.checkedOutPreviousSessionStates;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
package org.whispersystems.test;
|
||||
|
||||
import android.test.AndroidTestCase;
|
||||
|
||||
import org.whispersystems.libaxolotl.DuplicateMessageException;
|
||||
import org.whispersystems.libaxolotl.IdentityKey;
|
||||
import org.whispersystems.libaxolotl.IdentityKeyPair;
|
||||
import org.whispersystems.libaxolotl.InvalidKeyException;
|
||||
import org.whispersystems.libaxolotl.InvalidMessageException;
|
||||
import org.whispersystems.libaxolotl.LegacyMessageException;
|
||||
import org.whispersystems.libaxolotl.SessionCipher;
|
||||
import org.whispersystems.libaxolotl.SessionState;
|
||||
import org.whispersystems.libaxolotl.SessionStore;
|
||||
import org.whispersystems.libaxolotl.ecc.Curve;
|
||||
import org.whispersystems.libaxolotl.ecc.ECKeyPair;
|
||||
import org.whispersystems.libaxolotl.protocol.CiphertextMessage;
|
||||
import org.whispersystems.libaxolotl.ratchet.RatchetingSession;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
public class SessionCipherTest extends AndroidTestCase {
|
||||
|
||||
public void testBasicSession()
|
||||
throws InvalidKeyException, DuplicateMessageException,
|
||||
LegacyMessageException, InvalidMessageException
|
||||
{
|
||||
SessionState aliceSessionState = new InMemorySessionState();
|
||||
SessionState bobSessionState = new InMemorySessionState();
|
||||
|
||||
initializeSessions(aliceSessionState, bobSessionState);
|
||||
|
||||
SessionStore aliceSessionStore = new InMemorySessionStore(aliceSessionState);
|
||||
SessionStore bobSessionStore = new InMemorySessionStore(bobSessionState);
|
||||
|
||||
SessionCipher aliceCipher = new SessionCipher(aliceSessionStore);
|
||||
SessionCipher bobCipher = new SessionCipher(bobSessionStore);
|
||||
|
||||
byte[] alicePlaintext = "This is a plaintext message.".getBytes();
|
||||
CiphertextMessage message = aliceCipher.encrypt(alicePlaintext);
|
||||
byte[] bobPlaintext = bobCipher.decrypt(message.serialize());
|
||||
|
||||
assertTrue(Arrays.equals(alicePlaintext, bobPlaintext));
|
||||
|
||||
byte[] bobReply = "This is a message from Bob.".getBytes();
|
||||
CiphertextMessage reply = bobCipher.encrypt(bobReply);
|
||||
byte[] receivedReply = aliceCipher.decrypt(reply.serialize());
|
||||
|
||||
assertTrue(Arrays.equals(bobReply, receivedReply));
|
||||
}
|
||||
|
||||
|
||||
private void initializeSessions(SessionState aliceSessionState, SessionState bobSessionState)
|
||||
throws InvalidKeyException
|
||||
{
|
||||
ECKeyPair aliceIdentityKeyPair = Curve.generateKeyPair(false);
|
||||
IdentityKeyPair aliceIdentityKey = new IdentityKeyPair(new IdentityKey(aliceIdentityKeyPair.getPublicKey()),
|
||||
aliceIdentityKeyPair.getPrivateKey());
|
||||
ECKeyPair aliceBaseKey = Curve.generateKeyPair(true);
|
||||
ECKeyPair aliceEphemeralKey = Curve.generateKeyPair(true);
|
||||
|
||||
ECKeyPair bobIdentityKeyPair = Curve.generateKeyPair(false);
|
||||
IdentityKeyPair bobIdentityKey = new IdentityKeyPair(new IdentityKey(bobIdentityKeyPair.getPublicKey()),
|
||||
bobIdentityKeyPair.getPrivateKey());
|
||||
ECKeyPair bobBaseKey = Curve.generateKeyPair(true);
|
||||
ECKeyPair bobEphemeralKey = bobBaseKey;
|
||||
|
||||
|
||||
RatchetingSession.initializeSession(aliceSessionState, aliceBaseKey, bobBaseKey.getPublicKey(),
|
||||
aliceEphemeralKey, bobEphemeralKey.getPublicKey(),
|
||||
aliceIdentityKey, bobIdentityKey.getPublicKey());
|
||||
|
||||
RatchetingSession.initializeSession(bobSessionState, bobBaseKey, aliceBaseKey.getPublicKey(),
|
||||
bobEphemeralKey, aliceEphemeralKey.getPublicKey(),
|
||||
bobIdentityKey, aliceIdentityKey.getPublicKey());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
package org.whispersystems.test.ecc;
|
||||
|
||||
import android.test.AndroidTestCase;
|
||||
|
||||
import org.whispersystems.libaxolotl.InvalidKeyException;
|
||||
import org.whispersystems.libaxolotl.ecc.Curve;
|
||||
import org.whispersystems.libaxolotl.ecc.ECKeyPair;
|
||||
import org.whispersystems.libaxolotl.ecc.ECPrivateKey;
|
||||
import org.whispersystems.libaxolotl.ecc.ECPublicKey;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
|
||||
public class Curve25519Test extends AndroidTestCase {
|
||||
|
||||
public void testAgreement() throws InvalidKeyException {
|
||||
|
||||
byte[] alicePublic = {(byte) 0x05, (byte) 0x1b, (byte) 0xb7, (byte) 0x59, (byte) 0x66,
|
||||
(byte) 0xf2, (byte) 0xe9, (byte) 0x3a, (byte) 0x36, (byte) 0x91,
|
||||
(byte) 0xdf, (byte) 0xff, (byte) 0x94, (byte) 0x2b, (byte) 0xb2,
|
||||
(byte) 0xa4, (byte) 0x66, (byte) 0xa1, (byte) 0xc0, (byte) 0x8b,
|
||||
(byte) 0x8d, (byte) 0x78, (byte) 0xca, (byte) 0x3f, (byte) 0x4d,
|
||||
(byte) 0x6d, (byte) 0xf8, (byte) 0xb8, (byte) 0xbf, (byte) 0xa2,
|
||||
(byte) 0xe4, (byte) 0xee, (byte) 0x28};
|
||||
|
||||
byte[] alicePrivate = {(byte) 0xc8, (byte) 0x06, (byte) 0x43, (byte) 0x9d, (byte) 0xc9,
|
||||
(byte) 0xd2, (byte) 0xc4, (byte) 0x76, (byte) 0xff, (byte) 0xed,
|
||||
(byte) 0x8f, (byte) 0x25, (byte) 0x80, (byte) 0xc0, (byte) 0x88,
|
||||
(byte) 0x8d, (byte) 0x58, (byte) 0xab, (byte) 0x40, (byte) 0x6b,
|
||||
(byte) 0xf7, (byte) 0xae, (byte) 0x36, (byte) 0x98, (byte) 0x87,
|
||||
(byte) 0x90, (byte) 0x21, (byte) 0xb9, (byte) 0x6b, (byte) 0xb4,
|
||||
(byte) 0xbf, (byte) 0x59};
|
||||
|
||||
byte[] bobPublic = {(byte) 0x05, (byte) 0x65, (byte) 0x36, (byte) 0x14, (byte) 0x99,
|
||||
(byte) 0x3d, (byte) 0x2b, (byte) 0x15, (byte) 0xee, (byte) 0x9e,
|
||||
(byte) 0x5f, (byte) 0xd3, (byte) 0xd8, (byte) 0x6c, (byte) 0xe7,
|
||||
(byte) 0x19, (byte) 0xef, (byte) 0x4e, (byte) 0xc1, (byte) 0xda,
|
||||
(byte) 0xae, (byte) 0x18, (byte) 0x86, (byte) 0xa8, (byte) 0x7b,
|
||||
(byte) 0x3f, (byte) 0x5f, (byte) 0xa9, (byte) 0x56, (byte) 0x5a,
|
||||
(byte) 0x27, (byte) 0xa2, (byte) 0x2f};
|
||||
|
||||
byte[] bobPrivate = {(byte) 0xb0, (byte) 0x3b, (byte) 0x34, (byte) 0xc3, (byte) 0x3a,
|
||||
(byte) 0x1c, (byte) 0x44, (byte) 0xf2, (byte) 0x25, (byte) 0xb6,
|
||||
(byte) 0x62, (byte) 0xd2, (byte) 0xbf, (byte) 0x48, (byte) 0x59,
|
||||
(byte) 0xb8, (byte) 0x13, (byte) 0x54, (byte) 0x11, (byte) 0xfa,
|
||||
(byte) 0x7b, (byte) 0x03, (byte) 0x86, (byte) 0xd4, (byte) 0x5f,
|
||||
(byte) 0xb7, (byte) 0x5d, (byte) 0xc5, (byte) 0xb9, (byte) 0x1b,
|
||||
(byte) 0x44, (byte) 0x66};
|
||||
|
||||
byte[] shared = {(byte) 0x32, (byte) 0x5f, (byte) 0x23, (byte) 0x93, (byte) 0x28,
|
||||
(byte) 0x94, (byte) 0x1c, (byte) 0xed, (byte) 0x6e, (byte) 0x67,
|
||||
(byte) 0x3b, (byte) 0x86, (byte) 0xba, (byte) 0x41, (byte) 0x01,
|
||||
(byte) 0x74, (byte) 0x48, (byte) 0xe9, (byte) 0x9b, (byte) 0x64,
|
||||
(byte) 0x9a, (byte) 0x9c, (byte) 0x38, (byte) 0x06, (byte) 0xc1,
|
||||
(byte) 0xdd, (byte) 0x7c, (byte) 0xa4, (byte) 0xc4, (byte) 0x77,
|
||||
(byte) 0xe6, (byte) 0x29};
|
||||
|
||||
ECPublicKey alicePublicKey = Curve.decodePoint(alicePublic, 0);
|
||||
ECPrivateKey alicePrivateKey = Curve.decodePrivatePoint(alicePrivate);
|
||||
|
||||
ECPublicKey bobPublicKey = Curve.decodePoint(bobPublic, 0);
|
||||
ECPrivateKey bobPrivateKey = Curve.decodePrivatePoint(bobPrivate);
|
||||
|
||||
byte[] sharedOne = Curve.calculateAgreement(alicePublicKey, bobPrivateKey);
|
||||
byte[] sharedTwo = Curve.calculateAgreement(bobPublicKey, alicePrivateKey);
|
||||
|
||||
assertTrue(Arrays.equals(sharedOne, shared));
|
||||
assertTrue(Arrays.equals(sharedTwo, shared));
|
||||
}
|
||||
|
||||
public void testRandomAgreements() throws InvalidKeyException {
|
||||
for (int i=0;i<50;i++) {
|
||||
ECKeyPair alice = Curve.generateKeyPair(false);
|
||||
ECKeyPair bob = Curve.generateKeyPair(false);
|
||||
|
||||
byte[] sharedAlice = Curve.calculateAgreement(bob.getPublicKey(), alice.getPrivateKey());
|
||||
byte[] sharedBob = Curve.calculateAgreement(alice.getPublicKey(), bob.getPrivateKey());
|
||||
|
||||
assertTrue(Arrays.equals(sharedAlice, sharedBob));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package org.whispersystems.test.kdf;
|
||||
|
||||
import android.test.AndroidTestCase;
|
||||
|
||||
import org.whispersystems.libaxolotl.kdf.DerivedSecrets;
|
||||
import org.whispersystems.libaxolotl.kdf.HKDF;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
public class HKDFTest extends AndroidTestCase {
|
||||
|
||||
public void testVector() {
|
||||
byte[] ikm = {0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
|
||||
0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
|
||||
0x0b, 0x0b};
|
||||
|
||||
byte[] salt = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
|
||||
0x0a, 0x0b, 0x0c};
|
||||
|
||||
byte[] info = {(byte)0xf0, (byte)0xf1, (byte)0xf2, (byte)0xf3, (byte)0xf4,
|
||||
(byte)0xf5, (byte)0xf6, (byte)0xf7, (byte)0xf8, (byte)0xf9};
|
||||
|
||||
byte[] expectedOutputOne = {(byte)0x6e, (byte)0xc2, (byte)0x55, (byte)0x6d, (byte)0x5d,
|
||||
(byte)0x7b, (byte)0x1d, (byte)0x81, (byte)0xde, (byte)0xe4,
|
||||
(byte)0x22, (byte)0x2a, (byte)0xd7, (byte)0x48, (byte)0x36,
|
||||
(byte)0x95, (byte)0xdd, (byte)0xc9, (byte)0x8f, (byte)0x4f,
|
||||
(byte)0x5f, (byte)0xab, (byte)0xc0, (byte)0xe0, (byte)0x20,
|
||||
(byte)0x5d, (byte)0xc2, (byte)0xef, (byte)0x87, (byte)0x52,
|
||||
(byte)0xd4, (byte)0x1e};
|
||||
|
||||
byte[] expectedOutputTwo = {(byte)0x04, (byte)0xe2, (byte)0xe2, (byte)0x11, (byte)0x01,
|
||||
(byte)0xc6, (byte)0x8f, (byte)0xf0, (byte)0x93, (byte)0x94,
|
||||
(byte)0xb8, (byte)0xad, (byte)0x0b, (byte)0xdc, (byte)0xb9,
|
||||
(byte)0x60, (byte)0x9c, (byte)0xd4, (byte)0xee, (byte)0x82,
|
||||
(byte)0xac, (byte)0x13, (byte)0x19, (byte)0x9b, (byte)0x4a,
|
||||
(byte)0xa9, (byte)0xfd, (byte)0xa8, (byte)0x99, (byte)0xda,
|
||||
(byte)0xeb, (byte)0xec};
|
||||
|
||||
DerivedSecrets derivedSecrets = new HKDF().deriveSecrets(ikm, salt, info);
|
||||
|
||||
assertTrue(Arrays.equals(derivedSecrets.getCipherKey().getEncoded(), expectedOutputOne));
|
||||
assertTrue(Arrays.equals(derivedSecrets.getMacKey().getEncoded(), expectedOutputTwo));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package org.whispersystems.test.ratchet;
|
||||
|
||||
import android.test.AndroidTestCase;
|
||||
|
||||
import org.whispersystems.libaxolotl.ratchet.ChainKey;
|
||||
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.Arrays;
|
||||
|
||||
public class ChainKeyTest extends AndroidTestCase {
|
||||
|
||||
public void testChainKeyDerivation() throws NoSuchAlgorithmException {
|
||||
|
||||
byte[] seed = {(byte) 0x8a, (byte) 0xb7, (byte) 0x2d, (byte) 0x6f, (byte) 0x4c,
|
||||
(byte) 0xc5, (byte) 0xac, (byte) 0x0d, (byte) 0x38, (byte) 0x7e,
|
||||
(byte) 0xaf, (byte) 0x46, (byte) 0x33, (byte) 0x78, (byte) 0xdd,
|
||||
(byte) 0xb2, (byte) 0x8e, (byte) 0xdd, (byte) 0x07, (byte) 0x38,
|
||||
(byte) 0x5b, (byte) 0x1c, (byte) 0xb0, (byte) 0x12, (byte) 0x50,
|
||||
(byte) 0xc7, (byte) 0x15, (byte) 0x98, (byte) 0x2e, (byte) 0x7a,
|
||||
(byte) 0xd4, (byte) 0x8f};
|
||||
|
||||
byte[] messageKey = {(byte) 0x02, (byte) 0xa9, (byte) 0xaa, (byte) 0x6c, (byte) 0x7d,
|
||||
(byte) 0xbd, (byte) 0x64, (byte) 0xf9, (byte) 0xd3, (byte) 0xaa,
|
||||
(byte) 0x92, (byte) 0xf9, (byte) 0x2a, (byte) 0x27, (byte) 0x7b,
|
||||
(byte) 0xf5, (byte) 0x46, (byte) 0x09, (byte) 0xda, (byte) 0xdf,
|
||||
(byte) 0x0b, (byte) 0x00, (byte) 0x82, (byte) 0x8a, (byte) 0xcf,
|
||||
(byte) 0xc6, (byte) 0x1e, (byte) 0x3c, (byte) 0x72, (byte) 0x4b,
|
||||
(byte) 0x84, (byte) 0xa7};
|
||||
|
||||
byte[] macKey = {(byte) 0xbf, (byte) 0xbe, (byte) 0x5e, (byte) 0xfb, (byte) 0x60,
|
||||
(byte) 0x30, (byte) 0x30, (byte) 0x52, (byte) 0x67, (byte) 0x42,
|
||||
(byte) 0xe3, (byte) 0xee, (byte) 0x89, (byte) 0xc7, (byte) 0x02,
|
||||
(byte) 0x4e, (byte) 0x88, (byte) 0x4e, (byte) 0x44, (byte) 0x0f,
|
||||
(byte) 0x1f, (byte) 0xf3, (byte) 0x76, (byte) 0xbb, (byte) 0x23,
|
||||
(byte) 0x17, (byte) 0xb2, (byte) 0xd6, (byte) 0x4d, (byte) 0xeb,
|
||||
(byte) 0x7c, (byte) 0x83};
|
||||
|
||||
byte[] nextChainKey = {(byte) 0x28, (byte) 0xe8, (byte) 0xf8, (byte) 0xfe, (byte) 0xe5,
|
||||
(byte) 0x4b, (byte) 0x80, (byte) 0x1e, (byte) 0xef, (byte) 0x7c,
|
||||
(byte) 0x5c, (byte) 0xfb, (byte) 0x2f, (byte) 0x17, (byte) 0xf3,
|
||||
(byte) 0x2c, (byte) 0x7b, (byte) 0x33, (byte) 0x44, (byte) 0x85,
|
||||
(byte) 0xbb, (byte) 0xb7, (byte) 0x0f, (byte) 0xac, (byte) 0x6e,
|
||||
(byte) 0xc1, (byte) 0x03, (byte) 0x42, (byte) 0xa2, (byte) 0x46,
|
||||
(byte) 0xd1, (byte) 0x5d};
|
||||
|
||||
ChainKey chainKey = new ChainKey(seed, 0);
|
||||
|
||||
assertTrue(Arrays.equals(chainKey.getKey(), seed));
|
||||
assertTrue(Arrays.equals(chainKey.getMessageKeys().getCipherKey().getEncoded(), messageKey));
|
||||
assertTrue(Arrays.equals(chainKey.getMessageKeys().getMacKey().getEncoded(), macKey));
|
||||
assertTrue(Arrays.equals(chainKey.getNextChainKey().getKey(), nextChainKey));
|
||||
assertTrue(chainKey.getIndex() == 0);
|
||||
assertTrue(chainKey.getMessageKeys().getCounter() == 0);
|
||||
assertTrue(chainKey.getNextChainKey().getIndex() == 1);
|
||||
assertTrue(chainKey.getNextChainKey().getMessageKeys().getCounter() == 1);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,218 @@
|
||||
package org.whispersystems.test.ratchet;
|
||||
|
||||
import android.test.AndroidTestCase;
|
||||
|
||||
import org.whispersystems.libaxolotl.IdentityKey;
|
||||
import org.whispersystems.libaxolotl.IdentityKeyPair;
|
||||
import org.whispersystems.test.InMemorySessionState;
|
||||
import org.whispersystems.libaxolotl.InvalidKeyException;
|
||||
import org.whispersystems.libaxolotl.SessionState;
|
||||
import org.whispersystems.libaxolotl.ecc.Curve;
|
||||
import org.whispersystems.libaxolotl.ecc.ECKeyPair;
|
||||
import org.whispersystems.libaxolotl.ecc.ECPrivateKey;
|
||||
import org.whispersystems.libaxolotl.ecc.ECPublicKey;
|
||||
import org.whispersystems.libaxolotl.ratchet.RatchetingSession;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
public class RatchetingSessionTest extends AndroidTestCase {
|
||||
|
||||
public void testRatchetingSessionAsBob() throws InvalidKeyException {
|
||||
byte[] bobPublic = {(byte) 0x05, (byte) 0x2c, (byte) 0xb4, (byte) 0x97,
|
||||
(byte) 0x76, (byte) 0xb8, (byte) 0x77, (byte) 0x02,
|
||||
(byte) 0x05, (byte) 0x74, (byte) 0x5a, (byte) 0x3a,
|
||||
(byte) 0x6e, (byte) 0x24, (byte) 0xf5, (byte) 0x79,
|
||||
(byte) 0xcd, (byte) 0xb4, (byte) 0xba, (byte) 0x7a,
|
||||
(byte) 0x89, (byte) 0x04, (byte) 0x10, (byte) 0x05,
|
||||
(byte) 0x92, (byte) 0x8e, (byte) 0xbb, (byte) 0xad,
|
||||
(byte) 0xc9, (byte) 0xc0, (byte) 0x5a, (byte) 0xd4,
|
||||
(byte) 0x58};
|
||||
|
||||
byte[] bobPrivate = {(byte) 0xa1, (byte) 0xca, (byte) 0xb4, (byte) 0x8f,
|
||||
(byte) 0x7c, (byte) 0x89, (byte) 0x3f, (byte) 0xaf,
|
||||
(byte) 0xa9, (byte) 0x88, (byte) 0x0a, (byte) 0x28,
|
||||
(byte) 0xc3, (byte) 0xb4, (byte) 0x99, (byte) 0x9d,
|
||||
(byte) 0x28, (byte) 0xd6, (byte) 0x32, (byte) 0x95,
|
||||
(byte) 0x62, (byte) 0xd2, (byte) 0x7a, (byte) 0x4e,
|
||||
(byte) 0xa4, (byte) 0xe2, (byte) 0x2e, (byte) 0x9f,
|
||||
(byte) 0xf1, (byte) 0xbd, (byte) 0xd6, (byte) 0x5a};
|
||||
|
||||
byte[] bobIdentityPublic = {(byte) 0x05, (byte) 0xf1, (byte) 0xf4, (byte) 0x38,
|
||||
(byte) 0x74, (byte) 0xf6, (byte) 0x96, (byte) 0x69,
|
||||
(byte) 0x56, (byte) 0xc2, (byte) 0xdd, (byte) 0x47,
|
||||
(byte) 0x3f, (byte) 0x8f, (byte) 0xa1, (byte) 0x5a,
|
||||
(byte) 0xde, (byte) 0xb7, (byte) 0x1d, (byte) 0x1c,
|
||||
(byte) 0xb9, (byte) 0x91, (byte) 0xb2, (byte) 0x34,
|
||||
(byte) 0x16, (byte) 0x92, (byte) 0x32, (byte) 0x4c,
|
||||
(byte) 0xef, (byte) 0xb1, (byte) 0xc5, (byte) 0xe6,
|
||||
(byte) 0x26};
|
||||
|
||||
byte[] bobIdentityPrivate = {(byte) 0x48, (byte) 0x75, (byte) 0xcc, (byte) 0x69,
|
||||
(byte) 0xdd, (byte) 0xf8, (byte) 0xea, (byte) 0x07,
|
||||
(byte) 0x19, (byte) 0xec, (byte) 0x94, (byte) 0x7d,
|
||||
(byte) 0x61, (byte) 0x08, (byte) 0x11, (byte) 0x35,
|
||||
(byte) 0x86, (byte) 0x8d, (byte) 0x5f, (byte) 0xd8,
|
||||
(byte) 0x01, (byte) 0xf0, (byte) 0x2c, (byte) 0x02,
|
||||
(byte) 0x25, (byte) 0xe5, (byte) 0x16, (byte) 0xdf,
|
||||
(byte) 0x21, (byte) 0x56, (byte) 0x60, (byte) 0x5e};
|
||||
|
||||
byte[] aliceBasePublic = {(byte) 0x05, (byte) 0x47, (byte) 0x2d, (byte) 0x1f,
|
||||
(byte) 0xb1, (byte) 0xa9, (byte) 0x86, (byte) 0x2c,
|
||||
(byte) 0x3a, (byte) 0xf6, (byte) 0xbe, (byte) 0xac,
|
||||
(byte) 0xa8, (byte) 0x92, (byte) 0x02, (byte) 0x77,
|
||||
(byte) 0xe2, (byte) 0xb2, (byte) 0x6f, (byte) 0x4a,
|
||||
(byte) 0x79, (byte) 0x21, (byte) 0x3e, (byte) 0xc7,
|
||||
(byte) 0xc9, (byte) 0x06, (byte) 0xae, (byte) 0xb3,
|
||||
(byte) 0x5e, (byte) 0x03, (byte) 0xcf, (byte) 0x89,
|
||||
(byte) 0x50};
|
||||
|
||||
byte[] aliceEphemeralPublic = {(byte) 0x05, (byte) 0x6c, (byte) 0x3e, (byte) 0x0d,
|
||||
(byte) 0x1f, (byte) 0x52, (byte) 0x02, (byte) 0x83,
|
||||
(byte) 0xef, (byte) 0xcc, (byte) 0x55, (byte) 0xfc,
|
||||
(byte) 0xa5, (byte) 0xe6, (byte) 0x70, (byte) 0x75,
|
||||
(byte) 0xb9, (byte) 0x04, (byte) 0x00, (byte) 0x7f,
|
||||
(byte) 0x18, (byte) 0x81, (byte) 0xd1, (byte) 0x51,
|
||||
(byte) 0xaf, (byte) 0x76, (byte) 0xdf, (byte) 0x18,
|
||||
(byte) 0xc5, (byte) 0x1d, (byte) 0x29, (byte) 0xd3,
|
||||
(byte) 0x4b};
|
||||
|
||||
byte[] aliceIdentityPublic = {(byte) 0x05, (byte) 0xb4, (byte) 0xa8, (byte) 0x45,
|
||||
(byte) 0x56, (byte) 0x60, (byte) 0xad, (byte) 0xa6,
|
||||
(byte) 0x5b, (byte) 0x40, (byte) 0x10, (byte) 0x07,
|
||||
(byte) 0xf6, (byte) 0x15, (byte) 0xe6, (byte) 0x54,
|
||||
(byte) 0x04, (byte) 0x17, (byte) 0x46, (byte) 0x43,
|
||||
(byte) 0x2e, (byte) 0x33, (byte) 0x39, (byte) 0xc6,
|
||||
(byte) 0x87, (byte) 0x51, (byte) 0x49, (byte) 0xbc,
|
||||
(byte) 0xee, (byte) 0xfc, (byte) 0xb4, (byte) 0x2b,
|
||||
(byte) 0x4a};
|
||||
|
||||
byte[] senderChain = {(byte)0xd2, (byte)0x2f, (byte)0xd5, (byte)0x6d, (byte)0x3f,
|
||||
(byte)0xec, (byte)0x81, (byte)0x9c, (byte)0xf4, (byte)0xc3,
|
||||
(byte)0xd5, (byte)0x0c, (byte)0x56, (byte)0xed, (byte)0xfb,
|
||||
(byte)0x1c, (byte)0x28, (byte)0x0a, (byte)0x1b, (byte)0x31,
|
||||
(byte)0x96, (byte)0x45, (byte)0x37, (byte)0xf1, (byte)0xd1,
|
||||
(byte)0x61, (byte)0xe1, (byte)0xc9, (byte)0x31, (byte)0x48,
|
||||
(byte)0xe3, (byte)0x6b};
|
||||
|
||||
IdentityKey bobIdentityKeyPublic = new IdentityKey(bobIdentityPublic, 0);
|
||||
ECPrivateKey bobIdentityKeyPrivate = Curve.decodePrivatePoint(bobIdentityPrivate);
|
||||
IdentityKeyPair bobIdentityKey = new IdentityKeyPair(bobIdentityKeyPublic, bobIdentityKeyPrivate);
|
||||
ECPublicKey bobEphemeralPublicKey = Curve.decodePoint(bobPublic, 0);
|
||||
ECPrivateKey bobEphemeralPrivateKey = Curve.decodePrivatePoint(bobPrivate);
|
||||
ECKeyPair bobEphemeralKey = new ECKeyPair(bobEphemeralPublicKey, bobEphemeralPrivateKey);
|
||||
ECKeyPair bobBaseKey = bobEphemeralKey;
|
||||
|
||||
ECPublicKey aliceBasePublicKey = Curve.decodePoint(aliceBasePublic, 0);
|
||||
ECPublicKey aliceEphemeralPublicKey = Curve.decodePoint(aliceEphemeralPublic, 0);
|
||||
IdentityKey aliceIdentityPublicKey = new IdentityKey(aliceIdentityPublic, 0);
|
||||
|
||||
SessionState session = new InMemorySessionState();
|
||||
|
||||
RatchetingSession.initializeSession(session, bobBaseKey, aliceBasePublicKey,
|
||||
bobEphemeralKey, aliceEphemeralPublicKey,
|
||||
bobIdentityKey, aliceIdentityPublicKey);
|
||||
|
||||
assertTrue(session.getLocalIdentityKey().equals(bobIdentityKey.getPublicKey()));
|
||||
assertTrue(session.getRemoteIdentityKey().equals(aliceIdentityPublicKey));
|
||||
assertTrue(Arrays.equals(session.getSenderChainKey().getKey(), senderChain));
|
||||
}
|
||||
|
||||
public void testRatchetingSessionAsAlice() throws InvalidKeyException {
|
||||
byte[] bobPublic = {(byte) 0x05, (byte) 0x2c, (byte) 0xb4, (byte) 0x97, (byte) 0x76,
|
||||
(byte) 0xb8, (byte) 0x77, (byte) 0x02, (byte) 0x05, (byte) 0x74,
|
||||
(byte) 0x5a, (byte) 0x3a, (byte) 0x6e, (byte) 0x24, (byte) 0xf5,
|
||||
(byte) 0x79, (byte) 0xcd, (byte) 0xb4, (byte) 0xba, (byte) 0x7a,
|
||||
(byte) 0x89, (byte) 0x04, (byte) 0x10, (byte) 0x05, (byte) 0x92,
|
||||
(byte) 0x8e, (byte) 0xbb, (byte) 0xad, (byte) 0xc9, (byte) 0xc0,
|
||||
(byte) 0x5a, (byte) 0xd4, (byte) 0x58};
|
||||
|
||||
byte[] bobIdentityPublic = {(byte) 0x05, (byte) 0xf1, (byte) 0xf4, (byte) 0x38, (byte) 0x74,
|
||||
(byte) 0xf6, (byte) 0x96, (byte) 0x69, (byte) 0x56, (byte) 0xc2,
|
||||
(byte) 0xdd, (byte) 0x47, (byte) 0x3f, (byte) 0x8f, (byte) 0xa1,
|
||||
(byte) 0x5a, (byte) 0xde, (byte) 0xb7, (byte) 0x1d, (byte) 0x1c,
|
||||
(byte) 0xb9, (byte) 0x91, (byte) 0xb2, (byte) 0x34, (byte) 0x16,
|
||||
(byte) 0x92, (byte) 0x32, (byte) 0x4c, (byte) 0xef, (byte) 0xb1,
|
||||
(byte) 0xc5, (byte) 0xe6, (byte) 0x26};
|
||||
|
||||
byte[] aliceBasePublic = {(byte) 0x05, (byte) 0x47, (byte) 0x2d, (byte) 0x1f, (byte) 0xb1,
|
||||
(byte) 0xa9, (byte) 0x86, (byte) 0x2c, (byte) 0x3a, (byte) 0xf6,
|
||||
(byte) 0xbe, (byte) 0xac, (byte) 0xa8, (byte) 0x92, (byte) 0x02,
|
||||
(byte) 0x77, (byte) 0xe2, (byte) 0xb2, (byte) 0x6f, (byte) 0x4a,
|
||||
(byte) 0x79, (byte) 0x21, (byte) 0x3e, (byte) 0xc7, (byte) 0xc9,
|
||||
(byte) 0x06, (byte) 0xae, (byte) 0xb3, (byte) 0x5e, (byte) 0x03,
|
||||
(byte) 0xcf, (byte) 0x89, (byte) 0x50};
|
||||
|
||||
byte[] aliceBasePrivate = {(byte) 0x11, (byte) 0xae, (byte) 0x7c, (byte) 0x64, (byte) 0xd1,
|
||||
(byte) 0xe6, (byte) 0x1c, (byte) 0xd5, (byte) 0x96, (byte) 0xb7,
|
||||
(byte) 0x6a, (byte) 0x0d, (byte) 0xb5, (byte) 0x01, (byte) 0x26,
|
||||
(byte) 0x73, (byte) 0x39, (byte) 0x1c, (byte) 0xae, (byte) 0x66,
|
||||
(byte) 0xed, (byte) 0xbf, (byte) 0xcf, (byte) 0x07, (byte) 0x3b,
|
||||
(byte) 0x4d, (byte) 0xa8, (byte) 0x05, (byte) 0x16, (byte) 0xa4,
|
||||
(byte) 0x74, (byte) 0x49};
|
||||
|
||||
byte[] aliceEphemeralPublic = {(byte) 0x05, (byte) 0x6c, (byte) 0x3e, (byte) 0x0d, (byte) 0x1f,
|
||||
(byte) 0x52, (byte) 0x02, (byte) 0x83, (byte) 0xef, (byte) 0xcc,
|
||||
(byte) 0x55, (byte) 0xfc, (byte) 0xa5, (byte) 0xe6, (byte) 0x70,
|
||||
(byte) 0x75, (byte) 0xb9, (byte) 0x04, (byte) 0x00, (byte) 0x7f,
|
||||
(byte) 0x18, (byte) 0x81, (byte) 0xd1, (byte) 0x51, (byte) 0xaf,
|
||||
(byte) 0x76, (byte) 0xdf, (byte) 0x18, (byte) 0xc5, (byte) 0x1d,
|
||||
(byte) 0x29, (byte) 0xd3, (byte) 0x4b};
|
||||
|
||||
byte[] aliceEphemeralPrivate = {(byte) 0xd1, (byte) 0xba, (byte) 0x38, (byte) 0xce, (byte) 0xa9,
|
||||
(byte) 0x17, (byte) 0x43, (byte) 0xd3, (byte) 0x39, (byte) 0x39,
|
||||
(byte) 0xc3, (byte) 0x3c, (byte) 0x84, (byte) 0x98, (byte) 0x65,
|
||||
(byte) 0x09, (byte) 0x28, (byte) 0x01, (byte) 0x61, (byte) 0xb8,
|
||||
(byte) 0xb6, (byte) 0x0f, (byte) 0xc7, (byte) 0x87, (byte) 0x0c,
|
||||
(byte) 0x59, (byte) 0x9c, (byte) 0x1d, (byte) 0x46, (byte) 0x20,
|
||||
(byte) 0x12, (byte) 0x48};
|
||||
|
||||
byte[] aliceIdentityPublic = {(byte) 0x05, (byte) 0xb4, (byte) 0xa8, (byte) 0x45, (byte) 0x56,
|
||||
(byte) 0x60, (byte) 0xad, (byte) 0xa6, (byte) 0x5b, (byte) 0x40,
|
||||
(byte) 0x10, (byte) 0x07, (byte) 0xf6, (byte) 0x15, (byte) 0xe6,
|
||||
(byte) 0x54, (byte) 0x04, (byte) 0x17, (byte) 0x46, (byte) 0x43,
|
||||
(byte) 0x2e, (byte) 0x33, (byte) 0x39, (byte) 0xc6, (byte) 0x87,
|
||||
(byte) 0x51, (byte) 0x49, (byte) 0xbc, (byte) 0xee, (byte) 0xfc,
|
||||
(byte) 0xb4, (byte) 0x2b, (byte) 0x4a};
|
||||
|
||||
byte[] aliceIdentityPrivate = {(byte) 0x90, (byte) 0x40, (byte) 0xf0, (byte) 0xd4, (byte) 0xe0,
|
||||
(byte) 0x9c, (byte) 0xf3, (byte) 0x8f, (byte) 0x6d, (byte) 0xc7,
|
||||
(byte) 0xc1, (byte) 0x37, (byte) 0x79, (byte) 0xc9, (byte) 0x08,
|
||||
(byte) 0xc0, (byte) 0x15, (byte) 0xa1, (byte) 0xda, (byte) 0x4f,
|
||||
(byte) 0xa7, (byte) 0x87, (byte) 0x37, (byte) 0xa0, (byte) 0x80,
|
||||
(byte) 0xeb, (byte) 0x0a, (byte) 0x6f, (byte) 0x4f, (byte) 0x5f,
|
||||
(byte) 0x8f, (byte) 0x58};
|
||||
|
||||
byte[] receiverChain = {(byte) 0xd2, (byte) 0x2f, (byte) 0xd5, (byte) 0x6d, (byte) 0x3f,
|
||||
(byte) 0xec, (byte) 0x81, (byte) 0x9c, (byte) 0xf4, (byte) 0xc3,
|
||||
(byte) 0xd5, (byte) 0x0c, (byte) 0x56, (byte) 0xed, (byte) 0xfb,
|
||||
(byte) 0x1c, (byte) 0x28, (byte) 0x0a, (byte) 0x1b, (byte) 0x31,
|
||||
(byte) 0x96, (byte) 0x45, (byte) 0x37, (byte) 0xf1, (byte) 0xd1,
|
||||
(byte) 0x61, (byte) 0xe1, (byte) 0xc9, (byte) 0x31, (byte) 0x48,
|
||||
(byte) 0xe3, (byte) 0x6b};
|
||||
|
||||
IdentityKey bobIdentityKey = new IdentityKey(bobIdentityPublic, 0);
|
||||
ECPublicKey bobEphemeralPublicKey = Curve.decodePoint(bobPublic, 0);
|
||||
ECPublicKey bobBasePublicKey = bobEphemeralPublicKey;
|
||||
ECPublicKey aliceBasePublicKey = Curve.decodePoint(aliceBasePublic, 0);
|
||||
ECPrivateKey aliceBasePrivateKey = Curve.decodePrivatePoint(aliceBasePrivate);
|
||||
ECKeyPair aliceBaseKey = new ECKeyPair(aliceBasePublicKey, aliceBasePrivateKey);
|
||||
ECPublicKey aliceEphemeralPublicKey = Curve.decodePoint(aliceEphemeralPublic, 0);
|
||||
ECPrivateKey aliceEphemeralPrivateKey = Curve.decodePrivatePoint(aliceEphemeralPrivate);
|
||||
ECKeyPair aliceEphemeralKey = new ECKeyPair(aliceEphemeralPublicKey, aliceEphemeralPrivateKey);
|
||||
IdentityKey aliceIdentityPublicKey = new IdentityKey(aliceIdentityPublic, 0);
|
||||
ECPrivateKey aliceIdentityPrivateKey = Curve.decodePrivatePoint(aliceIdentityPrivate);
|
||||
IdentityKeyPair aliceIdentityKey = new IdentityKeyPair(aliceIdentityPublicKey, aliceIdentityPrivateKey);
|
||||
|
||||
SessionState session = new InMemorySessionState();
|
||||
|
||||
RatchetingSession.initializeSession(session, aliceBaseKey, bobBasePublicKey,
|
||||
aliceEphemeralKey, bobEphemeralPublicKey,
|
||||
aliceIdentityKey, bobIdentityKey);
|
||||
|
||||
assertTrue(session.getLocalIdentityKey().equals(aliceIdentityKey.getPublicKey()));
|
||||
assertTrue(session.getRemoteIdentityKey().equals(bobIdentityKey));
|
||||
assertTrue(Arrays.equals(session.getReceiverChainKey(bobEphemeralPublicKey).getKey(),
|
||||
receiverChain));
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
package org.whispersystems.test.ratchet;
|
||||
|
||||
import android.test.AndroidTestCase;
|
||||
|
||||
import org.whispersystems.libaxolotl.InvalidKeyException;
|
||||
import org.whispersystems.libaxolotl.ecc.Curve;
|
||||
import org.whispersystems.libaxolotl.ecc.ECKeyPair;
|
||||
import org.whispersystems.libaxolotl.ecc.ECPrivateKey;
|
||||
import org.whispersystems.libaxolotl.ecc.ECPublicKey;
|
||||
import org.whispersystems.libaxolotl.ratchet.ChainKey;
|
||||
import org.whispersystems.libaxolotl.ratchet.RootKey;
|
||||
import org.whispersystems.libaxolotl.util.Pair;
|
||||
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.Arrays;
|
||||
|
||||
public class RootKeyTest extends AndroidTestCase {
|
||||
|
||||
public void testRootKeyDerivation() throws NoSuchAlgorithmException, InvalidKeyException {
|
||||
byte[] rootKeySeed = {(byte) 0x7b, (byte) 0xa6, (byte) 0xde, (byte) 0xbc, (byte) 0x2b,
|
||||
(byte) 0xc1, (byte) 0xbb, (byte) 0xf9, (byte) 0x1a, (byte) 0xbb,
|
||||
(byte) 0xc1, (byte) 0x36, (byte) 0x74, (byte) 0x04, (byte) 0x17,
|
||||
(byte) 0x6c, (byte) 0xa6, (byte) 0x23, (byte) 0x09, (byte) 0x5b,
|
||||
(byte) 0x7e, (byte) 0xc6, (byte) 0x6b, (byte) 0x45, (byte) 0xf6,
|
||||
(byte) 0x02, (byte) 0xd9, (byte) 0x35, (byte) 0x38, (byte) 0x94,
|
||||
(byte) 0x2d, (byte) 0xcc};
|
||||
|
||||
byte[] alicePublic = {(byte) 0x05, (byte) 0xee, (byte) 0x4f, (byte) 0xa6, (byte) 0xcd,
|
||||
(byte) 0xc0, (byte) 0x30, (byte) 0xdf, (byte) 0x49, (byte) 0xec,
|
||||
(byte) 0xd0, (byte) 0xba, (byte) 0x6c, (byte) 0xfc, (byte) 0xff,
|
||||
(byte) 0xb2, (byte) 0x33, (byte) 0xd3, (byte) 0x65, (byte) 0xa2,
|
||||
(byte) 0x7f, (byte) 0xad, (byte) 0xbe, (byte) 0xff, (byte) 0x77,
|
||||
(byte) 0xe9, (byte) 0x63, (byte) 0xfc, (byte) 0xb1, (byte) 0x62,
|
||||
(byte) 0x22, (byte) 0xe1, (byte) 0x3a};
|
||||
|
||||
byte[] alicePrivate = {(byte) 0x21, (byte) 0x68, (byte) 0x22, (byte) 0xec, (byte) 0x67,
|
||||
(byte) 0xeb, (byte) 0x38, (byte) 0x04, (byte) 0x9e, (byte) 0xba,
|
||||
(byte) 0xe7, (byte) 0xb9, (byte) 0x39, (byte) 0xba, (byte) 0xea,
|
||||
(byte) 0xeb, (byte) 0xb1, (byte) 0x51, (byte) 0xbb, (byte) 0xb3,
|
||||
(byte) 0x2d, (byte) 0xb8, (byte) 0x0f, (byte) 0xd3, (byte) 0x89,
|
||||
(byte) 0x24, (byte) 0x5a, (byte) 0xc3, (byte) 0x7a, (byte) 0x94,
|
||||
(byte) 0x8e, (byte) 0x50};
|
||||
|
||||
byte[] bobPublic = {(byte) 0x05, (byte) 0xab, (byte) 0xb8, (byte) 0xeb, (byte) 0x29,
|
||||
(byte) 0xcc, (byte) 0x80, (byte) 0xb4, (byte) 0x71, (byte) 0x09,
|
||||
(byte) 0xa2, (byte) 0x26, (byte) 0x5a, (byte) 0xbe, (byte) 0x97,
|
||||
(byte) 0x98, (byte) 0x48, (byte) 0x54, (byte) 0x06, (byte) 0xe3,
|
||||
(byte) 0x2d, (byte) 0xa2, (byte) 0x68, (byte) 0x93, (byte) 0x4a,
|
||||
(byte) 0x95, (byte) 0x55, (byte) 0xe8, (byte) 0x47, (byte) 0x57,
|
||||
(byte) 0x70, (byte) 0x8a, (byte) 0x30};
|
||||
|
||||
byte[] nextRoot = {(byte) 0xb1, (byte) 0x14, (byte) 0xf5, (byte) 0xde, (byte) 0x28,
|
||||
(byte) 0x01, (byte) 0x19, (byte) 0x85, (byte) 0xe6, (byte) 0xeb,
|
||||
(byte) 0xa2, (byte) 0x5d, (byte) 0x50, (byte) 0xe7, (byte) 0xec,
|
||||
(byte) 0x41, (byte) 0xa9, (byte) 0xb0, (byte) 0x2f, (byte) 0x56,
|
||||
(byte) 0x93, (byte) 0xc5, (byte) 0xc7, (byte) 0x88, (byte) 0xa6,
|
||||
(byte) 0x3a, (byte) 0x06, (byte) 0xd2, (byte) 0x12, (byte) 0xa2,
|
||||
(byte) 0xf7, (byte) 0x31};
|
||||
|
||||
byte[] nextChain = {(byte) 0x9d, (byte) 0x7d, (byte) 0x24, (byte) 0x69, (byte) 0xbc,
|
||||
(byte) 0x9a, (byte) 0xe5, (byte) 0x3e, (byte) 0xe9, (byte) 0x80,
|
||||
(byte) 0x5a, (byte) 0xa3, (byte) 0x26, (byte) 0x4d, (byte) 0x24,
|
||||
(byte) 0x99, (byte) 0xa3, (byte) 0xac, (byte) 0xe8, (byte) 0x0f,
|
||||
(byte) 0x4c, (byte) 0xca, (byte) 0xe2, (byte) 0xda, (byte) 0x13,
|
||||
(byte) 0x43, (byte) 0x0c, (byte) 0x5c, (byte) 0x55, (byte) 0xb5,
|
||||
(byte) 0xca, (byte) 0x5f};
|
||||
|
||||
ECPublicKey alicePublicKey = Curve.decodePoint(alicePublic, 0);
|
||||
ECPrivateKey alicePrivateKey = Curve.decodePrivatePoint(alicePrivate);
|
||||
ECKeyPair aliceKeyPair = new ECKeyPair(alicePublicKey, alicePrivateKey);
|
||||
|
||||
ECPublicKey bobPublicKey = Curve.decodePoint(bobPublic, 0);
|
||||
RootKey rootKey = new RootKey(rootKeySeed);
|
||||
|
||||
Pair<RootKey, ChainKey> rootKeyChainKeyPair = rootKey.createChain(bobPublicKey, aliceKeyPair);
|
||||
RootKey nextRootKey = rootKeyChainKeyPair.first();
|
||||
ChainKey nextChainKey = rootKeyChainKeyPair.second();
|
||||
|
||||
assertTrue(Arrays.equals(rootKey.getKeyBytes(), rootKeySeed));
|
||||
assertTrue(Arrays.equals(nextRootKey.getKeyBytes(), nextRoot));
|
||||
assertTrue(Arrays.equals(nextChainKey.getKey(), nextChain));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user