├── .gitignore ├── README.md ├── libs └── com │ └── ponderingpanda │ └── protobuf │ └── protobuf-mobile │ ├── 1.0 │ ├── protobuf-mobile-1.0.jar │ └── protobuf-mobile-1.0.pom │ └── maven-metadata-local.xml ├── pom.xml ├── protobuf ├── LocalStorageProtocol.proto ├── Makefile └── WhisperTextProtocol.proto └── src ├── main └── java │ └── org │ └── whispersystems │ └── libaxolotl │ ├── AxolotlAddress.java │ ├── DecryptionCallback.java │ ├── DuplicateMessageException.java │ ├── IdentityKey.java │ ├── IdentityKeyPair.java │ ├── InvalidKeyException.java │ ├── InvalidKeyIdException.java │ ├── InvalidMacException.java │ ├── InvalidMessageException.java │ ├── InvalidVersionException.java │ ├── LegacyMessageException.java │ ├── NoSessionException.java │ ├── SessionBuilder.java │ ├── SessionCipher.java │ ├── StaleKeyExchangeException.java │ ├── UntrustedIdentityException.java │ ├── ecc │ ├── Curve.java │ ├── DjbECPrivateKey.java │ ├── DjbECPublicKey.java │ ├── ECKeyPair.java │ ├── ECPrivateKey.java │ └── ECPublicKey.java │ ├── groups │ ├── GroupCipher.java │ ├── GroupSessionBuilder.java │ ├── SenderKeyName.java │ ├── ratchet │ │ ├── SenderChainKey.java │ │ └── SenderMessageKey.java │ └── state │ │ ├── SenderKeyRecord.java │ │ ├── SenderKeyState.java │ │ └── SenderKeyStore.java │ ├── j2me │ ├── Arrays.java │ ├── AssertionError.java │ ├── BigInteger.java │ ├── MessageDigest.java │ ├── NestedException.java │ ├── ParseException.java │ └── jce │ │ ├── BCJmeSecurityProvider.java │ │ ├── JmeSecurity.java │ │ ├── JmeSecurityProvider.java │ │ ├── ciphers │ │ ├── BlockCipher.java │ │ ├── BouncyCBCBlockCipher.java │ │ └── BouncyCTRBlockCipher.java │ │ └── mac │ │ ├── BouncyMacSha256.java │ │ └── Mac.java │ ├── kdf │ ├── DerivedMessageSecrets.java │ ├── DerivedRootSecrets.java │ ├── HKDF.java │ ├── HKDFv2.java │ └── HKDFv3.java │ ├── logging │ ├── AxolotlLogger.java │ ├── AxolotlLoggerProvider.java │ └── Log.java │ ├── protocol │ ├── CiphertextMessage.java │ ├── KeyExchangeMessage.java │ ├── PreKeyWhisperMessage.java │ ├── SenderKeyDistributionMessage.java │ ├── SenderKeyMessage.java │ ├── WhisperMessage.java │ └── protos │ │ ├── KeyExchangeMessage.java │ │ ├── PreKeyWhisperMessage.java │ │ ├── SenderKeyDistributionMessage.java │ │ ├── SenderKeyMessage.java │ │ └── WhisperMessage.java │ ├── ratchet │ ├── AliceAxolotlParameters.java │ ├── BobAxolotlParameters.java │ ├── ChainKey.java │ ├── MessageKeys.java │ ├── RatchetingSession.java │ ├── RootKey.java │ └── SymmetricAxolotlParameters.java │ ├── state │ ├── AxolotlStore.java │ ├── IdentityKeyStore.java │ ├── PreKeyBundle.java │ ├── PreKeyRecord.java │ ├── PreKeyStore.java │ ├── SessionRecord.java │ ├── SessionState.java │ ├── SessionStore.java │ ├── SignedPreKeyRecord.java │ ├── SignedPreKeyStore.java │ └── protos │ │ ├── IdentityKeyPairStructure.java │ │ ├── PreKeyRecordStructure.java │ │ ├── RecordStructure.java │ │ ├── SenderKeyRecordStructure.java │ │ ├── SenderKeyStateStructure.java │ │ ├── SessionStructure.java │ │ └── SignedPreKeyRecordStructure.java │ └── util │ ├── ByteUtil.java │ ├── Hex.java │ ├── KeyHelper.java │ ├── Medium.java │ ├── Pair.java │ └── guava │ ├── Absent.java │ ├── Optional.java │ ├── Preconditions.java │ ├── Present.java │ └── Supplier.java └── test └── java └── org └── whispersystems └── libaxolotl ├── AxolotlBaseTestCase.java ├── InMemoryAxolotlStore.java ├── InMemoryIdentityKeyStore.java ├── InMemoryPreKeyStore.java ├── InMemorySessionStore.java ├── InMemorySignedPreKeyStore.java ├── SessionBuilderTest.java ├── SessionCipherTest.java ├── SimultaneousInitiateTest.java ├── ecc └── Curve25519Test.java ├── groups ├── GroupCipherTest.java └── InMemorySenderKeyStore.java ├── j2me ├── Collections.java └── FakeSecureRandomProvider.java ├── kdf └── HKDFTest.java └── ratchet ├── ChainKeyTest.java ├── RatchetingSessionTest.java └── RootKeyTest.java /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | /obj 3 | *.iml 4 | .gradle 5 | .idea 6 | gradle.properties 7 | local.properties 8 | target/ 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # In Progress 3 | 4 | -------------------------------------------------------------------------------- /libs/com/ponderingpanda/protobuf/protobuf-mobile/1.0/protobuf-mobile-1.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/signalapp/libaxolotl-j2me/8842511ee123c6ee6568f9bd098ecab32ab01f16/libs/com/ponderingpanda/protobuf/protobuf-mobile/1.0/protobuf-mobile-1.0.jar -------------------------------------------------------------------------------- /libs/com/ponderingpanda/protobuf/protobuf-mobile/1.0/protobuf-mobile-1.0.pom: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | com.ponderingpanda.protobuf 6 | protobuf-mobile 7 | 1.0 8 | POM was created from install:install-file 9 | 10 | -------------------------------------------------------------------------------- /libs/com/ponderingpanda/protobuf/protobuf-mobile/maven-metadata-local.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | com.ponderingpanda.protobuf 4 | protobuf-mobile 5 | 6 | 1.0 7 | 8 | 1.0 9 | 10 | 20150209194159 11 | 12 | 13 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | 3.0.0 8 | 9 | 10 | org.whispersystems 11 | axolotl-j2me 12 | 0.2.0 13 | 14 | axolotl-j2me 15 | A J2ME compatible Axolotl impolementation 16 | https://github.com/WhisperSystems/libaxolotl-j2me 17 | 18 | 19 | 20 | GPLv3 21 | https://www.gnu.org/licenses/gpl-3.0.html 22 | repo 23 | 24 | 25 | 26 | 27 | 28 | Moxie Marlinspike 29 | 30 | 31 | 32 | 33 | https://github.com/WhisperSystems/libaxolotl-j2me 34 | scm:git:https://github.com/WhisperSystems/libaxolotl-j2me.git 35 | scm:git:https://github.com/WhisperSystems/libaxolotl-j2me.git 36 | 37 | 38 | 39 | 40 | libs 41 | file://${basedir}/libs 42 | 43 | 44 | central 45 | Maven Repository Switchboard 46 | default 47 | http://repo1.maven.org/maven2 48 | 49 | 50 | 51 | 52 | 53 | com.google.protobuf 54 | protobuf-java 55 | 2.5.0 56 | 57 | 58 | org.whispersystems 59 | curve25519-j2me 60 | 0.2.3 61 | 62 | 63 | org.bouncycastle 64 | bcprov-jdk12 65 | 130 66 | 67 | 68 | com.ponderingpanda.protobuf 69 | protobuf-mobile 70 | 1.0 71 | 72 | 73 | junit 74 | junit 75 | 3.7 76 | 77 | 78 | 79 | 80 | 81 | 82 | org.apache.maven.plugins 83 | maven-compiler-plugin 84 | 85 | 1.2 86 | 1.2 87 | 88 | 89 | 90 | org.apache.maven.plugins 91 | maven-source-plugin 92 | 2.2.1 93 | 94 | 95 | attach-sources 96 | 97 | jar 98 | 99 | 100 | 101 | 102 | 103 | org.apache.maven.plugins 104 | maven-jar-plugin 105 | 2.4 106 | 107 | 108 | 109 | true 110 | 111 | 112 | 113 | 114 | 115 | org.apache.maven.plugins 116 | maven-gpg-plugin 117 | 118 | 119 | sign-artifacts 120 | verify 121 | 122 | sign 123 | 124 | 125 | 101BFC88 126 | 127 | 128 | 129 | 130 | 131 | org.apache.maven.plugins 132 | maven-javadoc-plugin 133 | 2.8.1 134 | 135 | -Xdoclint:none 136 | 137 | 138 | 139 | attach-javadocs 140 | 141 | jar 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | ossrh 152 | https://oss.sonatype.org/content/repositories/snapshots 153 | 154 | 155 | ossrh 156 | https://oss.sonatype.org/service/local/staging/deploy/maven2/ 157 | 158 | 159 | 160 | 161 | 162 | -------------------------------------------------------------------------------- /protobuf/LocalStorageProtocol.proto: -------------------------------------------------------------------------------- 1 | package textsecure; 2 | 3 | option java_package = "org.whispersystems.libaxolotl.state.protos"; 4 | option java_outer_classname = "StorageProtos"; 5 | 6 | message SessionStructure { 7 | message Chain { 8 | optional bytes senderRatchetKey = 1; 9 | optional bytes senderRatchetKeyPrivate = 2; 10 | 11 | message ChainKey { 12 | optional uint32 index = 1; 13 | optional bytes key = 2; 14 | } 15 | 16 | optional ChainKey chainKey = 3; 17 | 18 | message MessageKey { 19 | optional uint32 index = 1; 20 | optional bytes cipherKey = 2; 21 | optional bytes macKey = 3; 22 | optional bytes iv = 4; 23 | } 24 | 25 | repeated MessageKey messageKeys = 4; 26 | } 27 | 28 | message PendingKeyExchange { 29 | optional uint32 sequence = 1; 30 | optional bytes localBaseKey = 2; 31 | optional bytes localBaseKeyPrivate = 3; 32 | optional bytes localRatchetKey = 4; 33 | optional bytes localRatchetKeyPrivate = 5; 34 | optional bytes localIdentityKey = 7; 35 | optional bytes localIdentityKeyPrivate = 8; 36 | } 37 | 38 | message PendingPreKey { 39 | optional uint32 preKeyId = 1; 40 | optional int32 signedPreKeyId = 3; 41 | optional bytes baseKey = 2; 42 | } 43 | 44 | optional uint32 sessionVersion = 1; 45 | optional bytes localIdentityPublic = 2; 46 | optional bytes remoteIdentityPublic = 3; 47 | 48 | optional bytes rootKey = 4; 49 | optional uint32 previousCounter = 5; 50 | 51 | optional Chain senderChain = 6; 52 | repeated Chain receiverChains = 7; 53 | 54 | optional PendingKeyExchange pendingKeyExchange = 8; 55 | optional PendingPreKey pendingPreKey = 9; 56 | 57 | optional uint32 remoteRegistrationId = 10; 58 | optional uint32 localRegistrationId = 11; 59 | 60 | optional bool needsRefresh = 12; 61 | optional bytes aliceBaseKey = 13; 62 | } 63 | 64 | message RecordStructure { 65 | optional SessionStructure currentSession = 1; 66 | repeated SessionStructure previousSessions = 2; 67 | } 68 | 69 | message PreKeyRecordStructure { 70 | optional uint32 id = 1; 71 | optional bytes publicKey = 2; 72 | optional bytes privateKey = 3; 73 | } 74 | 75 | message SignedPreKeyRecordStructure { 76 | optional uint32 id = 1; 77 | optional bytes publicKey = 2; 78 | optional bytes privateKey = 3; 79 | optional bytes signature = 4; 80 | optional fixed64 timestamp = 5; 81 | } 82 | 83 | message IdentityKeyPairStructure { 84 | optional bytes publicKey = 1; 85 | optional bytes privateKey = 2; 86 | } 87 | 88 | message SenderKeyStateStructure { 89 | message SenderChainKey { 90 | optional uint32 iteration = 1; 91 | optional bytes seed = 2; 92 | } 93 | 94 | message SenderMessageKey { 95 | optional uint32 iteration = 1; 96 | optional bytes seed = 2; 97 | } 98 | 99 | message SenderSigningKey { 100 | optional bytes public = 1; 101 | optional bytes private = 2; 102 | } 103 | 104 | optional uint32 senderKeyId = 1; 105 | optional SenderChainKey senderChainKey = 2; 106 | optional SenderSigningKey senderSigningKey = 3; 107 | repeated SenderMessageKey senderMessageKeys = 4; 108 | } 109 | 110 | message SenderKeyRecordStructure { 111 | repeated SenderKeyStateStructure senderKeyStates = 1; 112 | } -------------------------------------------------------------------------------- /protobuf/Makefile: -------------------------------------------------------------------------------- 1 | 2 | all: 3 | protoc --java_out=../src/main/java/ WhisperTextProtocol.proto LocalStorageProtocol.proto 4 | -------------------------------------------------------------------------------- /protobuf/WhisperTextProtocol.proto: -------------------------------------------------------------------------------- 1 | package textsecure; 2 | 3 | option java_package = "org.whispersystems.libaxolotl.protocol.protos"; 4 | option java_outer_classname = "WhisperProtos"; 5 | 6 | message WhisperMessage { 7 | optional bytes ratchetKey = 1; 8 | optional uint32 counter = 2; 9 | optional uint32 previousCounter = 3; 10 | optional bytes ciphertext = 4; 11 | } 12 | 13 | message PreKeyWhisperMessage { 14 | optional uint32 registrationId = 5; 15 | optional uint32 preKeyId = 1; 16 | optional uint32 signedPreKeyId = 6; 17 | optional bytes baseKey = 2; 18 | optional bytes identityKey = 3; 19 | optional bytes message = 4; // WhisperMessage 20 | } 21 | 22 | message KeyExchangeMessage { 23 | optional uint32 id = 1; 24 | optional bytes baseKey = 2; 25 | optional bytes ratchetKey = 3; 26 | optional bytes identityKey = 4; 27 | optional bytes baseKeySignature = 5; 28 | } 29 | 30 | message SenderKeyMessage { 31 | optional uint32 id = 1; 32 | optional uint32 iteration = 2; 33 | optional bytes ciphertext = 3; 34 | } 35 | 36 | message SenderKeyDistributionMessage { 37 | optional uint32 id = 1; 38 | optional uint32 iteration = 2; 39 | optional bytes chainKey = 3; 40 | optional bytes signingKey = 4; 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/org/whispersystems/libaxolotl/AxolotlAddress.java: -------------------------------------------------------------------------------- 1 | package org.whispersystems.libaxolotl; 2 | 3 | public class AxolotlAddress { 4 | 5 | private final String name; 6 | private final int deviceId; 7 | 8 | public AxolotlAddress(String name, int deviceId) { 9 | this.name = name; 10 | this.deviceId = deviceId; 11 | } 12 | 13 | public String getName() { 14 | return name; 15 | } 16 | 17 | public int getDeviceId() { 18 | return deviceId; 19 | } 20 | 21 | // @Override 22 | public String toString() { 23 | return name + ":" + deviceId; 24 | } 25 | 26 | // @Override 27 | public boolean equals(Object other) { 28 | if (other == null) return false; 29 | if (!(other instanceof AxolotlAddress)) return false; 30 | 31 | AxolotlAddress that = (AxolotlAddress)other; 32 | return this.name.equals(that.name) && this.deviceId == that.deviceId; 33 | } 34 | 35 | // @Override 36 | public int hashCode() { 37 | int hash = 7; 38 | hash = 31 * hash + deviceId; 39 | hash = 31 * hash + name.hashCode(); 40 | return hash; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/org/whispersystems/libaxolotl/DecryptionCallback.java: -------------------------------------------------------------------------------- 1 | package org.whispersystems.libaxolotl; 2 | 3 | public interface DecryptionCallback { 4 | public void handlePlaintext(byte[] plaintext); 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/org/whispersystems/libaxolotl/DuplicateMessageException.java: -------------------------------------------------------------------------------- 1 | package org.whispersystems.libaxolotl; 2 | 3 | public class DuplicateMessageException extends Exception { 4 | public DuplicateMessageException(String s) { 5 | super(s); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/org/whispersystems/libaxolotl/IdentityKey.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2014 Open Whisper Systems 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package org.whispersystems.libaxolotl; 18 | 19 | 20 | import org.whispersystems.libaxolotl.ecc.Curve; 21 | import org.whispersystems.libaxolotl.ecc.ECPublicKey; 22 | import org.whispersystems.libaxolotl.util.Hex; 23 | 24 | /** 25 | * A class for representing an identity key. 26 | * 27 | * @author Moxie Marlinspike 28 | */ 29 | 30 | public class IdentityKey { 31 | 32 | private final ECPublicKey publicKey; 33 | 34 | public IdentityKey(ECPublicKey publicKey) { 35 | this.publicKey = publicKey; 36 | } 37 | 38 | public IdentityKey(byte[] bytes, int offset) throws InvalidKeyException { 39 | this.publicKey = Curve.decodePoint(bytes, offset); 40 | } 41 | 42 | public ECPublicKey getPublicKey() { 43 | return publicKey; 44 | } 45 | 46 | public byte[] serialize() { 47 | return publicKey.serialize(); 48 | } 49 | 50 | public String getFingerprint() { 51 | return Hex.toString(publicKey.serialize()); 52 | } 53 | 54 | // @Override 55 | public boolean equals(Object other) { 56 | if (other == null) return false; 57 | if (!(other instanceof IdentityKey)) return false; 58 | 59 | return publicKey.equals(((IdentityKey) other).getPublicKey()); 60 | } 61 | 62 | // @Override 63 | public int hashCode() { 64 | return publicKey.hashCode(); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/main/java/org/whispersystems/libaxolotl/IdentityKeyPair.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2013 Open Whisper Systems 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package org.whispersystems.libaxolotl; 18 | 19 | import org.whispersystems.libaxolotl.ecc.Curve; 20 | import org.whispersystems.libaxolotl.ecc.ECPrivateKey; 21 | import org.whispersystems.libaxolotl.state.protos.IdentityKeyPairStructure; 22 | 23 | 24 | /** 25 | * Holder for public and private identity key pair. 26 | * 27 | * @author Moxie Marlinspike 28 | */ 29 | public class IdentityKeyPair { 30 | 31 | private final IdentityKey publicKey; 32 | private final ECPrivateKey privateKey; 33 | 34 | public IdentityKeyPair(IdentityKey publicKey, ECPrivateKey privateKey) { 35 | this.publicKey = publicKey; 36 | this.privateKey = privateKey; 37 | } 38 | 39 | public IdentityKeyPair(byte[] serialized) throws InvalidKeyException { 40 | IdentityKeyPairStructure structure = IdentityKeyPairStructure.fromBytes(serialized); 41 | this.publicKey = new IdentityKey(structure.getPublickey(), 0); 42 | this.privateKey = Curve.decodePrivatePoint(structure.getPrivatekey()); 43 | } 44 | 45 | public IdentityKey getPublicKey() { 46 | return publicKey; 47 | } 48 | 49 | public ECPrivateKey getPrivateKey() { 50 | return privateKey; 51 | } 52 | 53 | public byte[] serialize() { 54 | IdentityKeyPairStructure structure = new IdentityKeyPairStructure(); 55 | structure.setPublickey(publicKey.serialize()); 56 | structure.setPrivatekey(privateKey.serialize()); 57 | 58 | return structure.toBytes(); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/org/whispersystems/libaxolotl/InvalidKeyException.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2014 Open Whisper Systems 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package org.whispersystems.libaxolotl; 18 | 19 | import org.whispersystems.libaxolotl.j2me.NestedException; 20 | 21 | public class InvalidKeyException extends NestedException { 22 | 23 | public InvalidKeyException() {} 24 | 25 | public InvalidKeyException(String detailMessage) { 26 | super(detailMessage); 27 | } 28 | 29 | public InvalidKeyException(Throwable throwable) { 30 | super(throwable); 31 | } 32 | 33 | public InvalidKeyException(String detailMessage, Throwable throwable) { 34 | super(detailMessage, throwable); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/org/whispersystems/libaxolotl/InvalidKeyIdException.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2014 Open Whisper Systems 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package org.whispersystems.libaxolotl; 18 | 19 | import org.whispersystems.libaxolotl.j2me.NestedException; 20 | 21 | public class InvalidKeyIdException extends NestedException { 22 | public InvalidKeyIdException(String detailMessage) { 23 | super(detailMessage); 24 | } 25 | 26 | public InvalidKeyIdException(Throwable throwable) { 27 | super(throwable); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/org/whispersystems/libaxolotl/InvalidMacException.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2014 Open Whisper Systems 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package org.whispersystems.libaxolotl; 18 | 19 | import org.whispersystems.libaxolotl.j2me.NestedException; 20 | 21 | public class InvalidMacException extends NestedException { 22 | 23 | public InvalidMacException(String detailMessage) { 24 | super(detailMessage); 25 | } 26 | 27 | public InvalidMacException(Throwable throwable) { 28 | super(throwable); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/org/whispersystems/libaxolotl/InvalidMessageException.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2014 Open Whisper Systems 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package org.whispersystems.libaxolotl; 18 | 19 | import org.whispersystems.libaxolotl.j2me.NestedException; 20 | 21 | import java.util.Vector; 22 | 23 | public class InvalidMessageException extends NestedException { 24 | 25 | public InvalidMessageException() {} 26 | 27 | public InvalidMessageException(String detailMessage) { 28 | super(detailMessage); 29 | } 30 | 31 | public InvalidMessageException(Throwable throwable) { 32 | super(throwable); 33 | } 34 | 35 | public InvalidMessageException(String detailMessage, Throwable throwable) { 36 | super(detailMessage, throwable); 37 | } 38 | 39 | public InvalidMessageException(String detailMessage, Vector exceptions) { 40 | super(detailMessage, (Throwable)exceptions.elementAt(0)); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/org/whispersystems/libaxolotl/InvalidVersionException.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2014 Open Whisper Systems 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package org.whispersystems.libaxolotl; 18 | 19 | public class InvalidVersionException extends Exception { 20 | public InvalidVersionException(String detailMessage) { 21 | super(detailMessage); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/org/whispersystems/libaxolotl/LegacyMessageException.java: -------------------------------------------------------------------------------- 1 | package org.whispersystems.libaxolotl; 2 | 3 | public class LegacyMessageException extends Exception { 4 | public LegacyMessageException(String s) { 5 | super(s); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/org/whispersystems/libaxolotl/NoSessionException.java: -------------------------------------------------------------------------------- 1 | package org.whispersystems.libaxolotl; 2 | 3 | import org.whispersystems.libaxolotl.j2me.NestedException; 4 | 5 | public class NoSessionException extends NestedException { 6 | public NoSessionException(String s) { 7 | super(s); 8 | } 9 | 10 | public NoSessionException(Exception nested) { 11 | super(nested); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/org/whispersystems/libaxolotl/StaleKeyExchangeException.java: -------------------------------------------------------------------------------- 1 | package org.whispersystems.libaxolotl; 2 | 3 | public class StaleKeyExchangeException extends Throwable { 4 | } 5 | -------------------------------------------------------------------------------- /src/main/java/org/whispersystems/libaxolotl/UntrustedIdentityException.java: -------------------------------------------------------------------------------- 1 | package org.whispersystems.libaxolotl; 2 | 3 | public class UntrustedIdentityException extends Exception { 4 | } 5 | -------------------------------------------------------------------------------- /src/main/java/org/whispersystems/libaxolotl/ecc/Curve.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2013 Open Whisper Systems 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package org.whispersystems.libaxolotl.ecc; 18 | 19 | import org.whispersystems.curve25519.Curve25519; 20 | import org.whispersystems.curve25519.Curve25519KeyPair; 21 | import org.whispersystems.curve25519.SecureRandomProvider; 22 | import org.whispersystems.libaxolotl.InvalidKeyException; 23 | 24 | public class Curve { 25 | 26 | public static final int DJB_TYPE = 0x05; 27 | 28 | public static boolean isNative() { 29 | return Curve25519.getInstance(Curve25519.J2ME).isNative(); 30 | } 31 | 32 | public static ECKeyPair generateKeyPair(SecureRandomProvider secureRandom) { 33 | Curve25519KeyPair keyPair = Curve25519.getInstance(Curve25519.J2ME, secureRandom).generateKeyPair(); 34 | 35 | return new ECKeyPair(new DjbECPublicKey(keyPair.getPublicKey()), 36 | new DjbECPrivateKey(keyPair.getPrivateKey())); 37 | } 38 | 39 | public static ECPublicKey decodePoint(byte[] bytes, int offset) 40 | throws InvalidKeyException 41 | { 42 | int type = bytes[offset] & 0xFF; 43 | 44 | switch (type) { 45 | case Curve.DJB_TYPE: 46 | byte[] keyBytes = new byte[32]; 47 | System.arraycopy(bytes, offset+1, keyBytes, 0, keyBytes.length); 48 | return new DjbECPublicKey(keyBytes); 49 | default: 50 | throw new InvalidKeyException("Bad key type: " + type); 51 | } 52 | } 53 | 54 | public static ECPrivateKey decodePrivatePoint(byte[] bytes) { 55 | return new DjbECPrivateKey(bytes); 56 | } 57 | 58 | public static byte[] calculateAgreement(ECPublicKey publicKey, ECPrivateKey privateKey) 59 | throws InvalidKeyException 60 | { 61 | if (publicKey.getType() != privateKey.getType()) { 62 | throw new InvalidKeyException("Public and private keys must be of the same type!"); 63 | } 64 | 65 | if (publicKey.getType() == DJB_TYPE) { 66 | return Curve25519.getInstance(Curve25519.J2ME) 67 | .calculateAgreement(((DjbECPublicKey) publicKey).getPublicKey(), 68 | ((DjbECPrivateKey) privateKey).getPrivateKey()); 69 | } else { 70 | throw new InvalidKeyException("Unknown type: " + publicKey.getType()); 71 | } 72 | } 73 | 74 | public static boolean verifySignature(ECPublicKey signingKey, byte[] message, byte[] signature) 75 | throws InvalidKeyException 76 | { 77 | if (signingKey.getType() == DJB_TYPE) { 78 | return Curve25519.getInstance(Curve25519.J2ME) 79 | .verifySignature(((DjbECPublicKey) signingKey).getPublicKey(), message, signature); 80 | } else { 81 | throw new InvalidKeyException("Unknown type: " + signingKey.getType()); 82 | } 83 | } 84 | 85 | public static byte[] calculateSignature(SecureRandomProvider secureRandom, 86 | ECPrivateKey signingKey, byte[] message) 87 | throws InvalidKeyException 88 | { 89 | if (signingKey.getType() == DJB_TYPE) { 90 | return Curve25519.getInstance(Curve25519.J2ME, secureRandom) 91 | .calculateSignature(((DjbECPrivateKey) signingKey).getPrivateKey(), message); 92 | } else { 93 | throw new InvalidKeyException("Unknown type: " + signingKey.getType()); 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/main/java/org/whispersystems/libaxolotl/ecc/DjbECPrivateKey.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2013 Open Whisper Systems 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package org.whispersystems.libaxolotl.ecc; 19 | 20 | public class DjbECPrivateKey implements ECPrivateKey { 21 | 22 | private final byte[] privateKey; 23 | 24 | DjbECPrivateKey(byte[] privateKey) { 25 | this.privateKey = privateKey; 26 | } 27 | 28 | // @Override 29 | public byte[] serialize() { 30 | return privateKey; 31 | } 32 | 33 | // @Override 34 | public int getType() { 35 | return Curve.DJB_TYPE; 36 | } 37 | 38 | public byte[] getPrivateKey() { 39 | return privateKey; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/org/whispersystems/libaxolotl/ecc/DjbECPublicKey.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2013 Open Whisper Systems 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package org.whispersystems.libaxolotl.ecc; 19 | 20 | import org.whispersystems.libaxolotl.j2me.Arrays; 21 | import org.whispersystems.libaxolotl.j2me.BigInteger; 22 | import org.whispersystems.libaxolotl.util.ByteUtil; 23 | 24 | public class DjbECPublicKey implements ECPublicKey { 25 | 26 | private final byte[] publicKey; 27 | 28 | DjbECPublicKey(byte[] publicKey) { 29 | this.publicKey = publicKey; 30 | } 31 | 32 | // @Override 33 | public byte[] serialize() { 34 | byte[] type = {Curve.DJB_TYPE}; 35 | return ByteUtil.combine(type, publicKey); 36 | } 37 | 38 | // @Override 39 | public int getType() { 40 | return Curve.DJB_TYPE; 41 | } 42 | 43 | // @Override 44 | public boolean equals(Object other) { 45 | if (other == null) return false; 46 | if (!(other instanceof DjbECPublicKey)) return false; 47 | 48 | DjbECPublicKey that = (DjbECPublicKey)other; 49 | return Arrays.equals(this.publicKey, that.publicKey); 50 | } 51 | 52 | // @Override 53 | public int hashCode() { 54 | return Arrays.hashCode(publicKey); 55 | } 56 | 57 | // @Override 58 | public int compareTo(ECPublicKey another) { 59 | return new BigInteger(publicKey).compareTo(new BigInteger(((DjbECPublicKey)another).publicKey)); 60 | } 61 | 62 | public byte[] getPublicKey() { 63 | return publicKey; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/org/whispersystems/libaxolotl/ecc/ECKeyPair.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2013 Open Whisper Systems 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package org.whispersystems.libaxolotl.ecc; 19 | 20 | public class ECKeyPair { 21 | 22 | private final ECPublicKey publicKey; 23 | private final ECPrivateKey privateKey; 24 | 25 | public ECKeyPair(ECPublicKey publicKey, ECPrivateKey privateKey) { 26 | this.publicKey = publicKey; 27 | this.privateKey = privateKey; 28 | } 29 | 30 | public ECPublicKey getPublicKey() { 31 | return publicKey; 32 | } 33 | 34 | public ECPrivateKey getPrivateKey() { 35 | return privateKey; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/org/whispersystems/libaxolotl/ecc/ECPrivateKey.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2013 Open Whisper Systems 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package org.whispersystems.libaxolotl.ecc; 19 | 20 | public interface ECPrivateKey { 21 | public byte[] serialize(); 22 | public int getType(); 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/org/whispersystems/libaxolotl/ecc/ECPublicKey.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2013 Open Whisper Systems 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package org.whispersystems.libaxolotl.ecc; 19 | 20 | public interface ECPublicKey { 21 | 22 | public static final int KEY_SIZE = 33; 23 | 24 | public byte[] serialize(); 25 | 26 | public int getType(); 27 | 28 | public int compareTo(ECPublicKey other); 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/org/whispersystems/libaxolotl/groups/GroupSessionBuilder.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2014-2015 Open Whisper Systems 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package org.whispersystems.libaxolotl.groups; 19 | 20 | import org.whispersystems.curve25519.SecureRandomProvider; 21 | import org.whispersystems.libaxolotl.InvalidKeyException; 22 | import org.whispersystems.libaxolotl.InvalidKeyIdException; 23 | import org.whispersystems.libaxolotl.groups.state.SenderKeyRecord; 24 | import org.whispersystems.libaxolotl.groups.state.SenderKeyState; 25 | import org.whispersystems.libaxolotl.groups.state.SenderKeyStore; 26 | import org.whispersystems.libaxolotl.j2me.AssertionError; 27 | import org.whispersystems.libaxolotl.protocol.SenderKeyDistributionMessage; 28 | import org.whispersystems.libaxolotl.util.KeyHelper; 29 | 30 | /** 31 | * GroupSessionBuilder is responsible for setting up group SenderKey encrypted sessions. 32 | * 33 | * Once a session has been established, {@link org.whispersystems.libaxolotl.groups.GroupCipher} 34 | * can be used to encrypt/decrypt messages in that session. 35 | *

36 | * The built sessions are unidirectional: they can be used either for sending or for receiving, 37 | * but not both. 38 | * 39 | * Sessions are constructed per (groupId senderId deviceId) tuple. Remote logical users 40 | * are identified by their senderId, and each logical recipientId can have multiple physical 41 | * devices. 42 | * 43 | * @author Moxie Marlinspike 44 | */ 45 | public class GroupSessionBuilder { 46 | 47 | private final SenderKeyStore senderKeyStore; 48 | 49 | public GroupSessionBuilder(SenderKeyStore senderKeyStore) { 50 | this.senderKeyStore = senderKeyStore; 51 | } 52 | 53 | /** 54 | * Construct a group session for receiving messages from senderKeyName. 55 | * 56 | * @param senderKeyName The (groupId, senderId, deviceId) tuple associated with the SenderKeyDistributionMessage. 57 | * @param senderKeyDistributionMessage A received SenderKeyDistributionMessage. 58 | */ 59 | public void process(SenderKeyName senderKeyName, SenderKeyDistributionMessage senderKeyDistributionMessage) { 60 | synchronized (GroupCipher.LOCK) { 61 | SenderKeyRecord senderKeyRecord = senderKeyStore.loadSenderKey(senderKeyName); 62 | senderKeyRecord.addSenderKeyState(senderKeyDistributionMessage.getId(), 63 | senderKeyDistributionMessage.getIteration(), 64 | senderKeyDistributionMessage.getChainKey(), 65 | senderKeyDistributionMessage.getSignatureKey()); 66 | senderKeyStore.storeSenderKey(senderKeyName, senderKeyRecord); 67 | } 68 | } 69 | 70 | /** 71 | * Construct a group session for sending messages. 72 | * 73 | * @param senderKeyName The (groupId, senderId, deviceId) tuple. In this case, 'senderId' should be the caller. 74 | * @return A SenderKeyDistributionMessage that is individually distributed to each member of the group. 75 | */ 76 | public SenderKeyDistributionMessage create(SenderKeyName senderKeyName, SecureRandomProvider secureRandomProvider) { 77 | synchronized (GroupCipher.LOCK) { 78 | 79 | try { 80 | SenderKeyRecord senderKeyRecord = senderKeyStore.loadSenderKey(senderKeyName); 81 | 82 | if (senderKeyRecord.isEmpty()) { 83 | senderKeyRecord.setSenderKeyState(KeyHelper.generateSenderKeyId(secureRandomProvider), 84 | 0, 85 | KeyHelper.generateSenderKey(secureRandomProvider), 86 | KeyHelper.generateSenderSigningKey(secureRandomProvider)); 87 | senderKeyStore.storeSenderKey(senderKeyName, senderKeyRecord); 88 | } 89 | 90 | SenderKeyState state = senderKeyRecord.getSenderKeyState(); 91 | 92 | return new SenderKeyDistributionMessage(state.getKeyId(), 93 | state.getSenderChainKey().getIteration(), 94 | state.getSenderChainKey().getSeed(), 95 | state.getSigningKeyPublic()); 96 | } catch (InvalidKeyIdException e) { 97 | throw new AssertionError(e); 98 | } catch (InvalidKeyException e) { 99 | throw new AssertionError(e); 100 | } 101 | } 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/main/java/org/whispersystems/libaxolotl/groups/SenderKeyName.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2014-2015 Open Whisper Systems 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package org.whispersystems.libaxolotl.groups; 18 | 19 | import org.whispersystems.libaxolotl.AxolotlAddress; 20 | 21 | /** 22 | * A representation of a (groupId + senderId + deviceId) tuple. 23 | */ 24 | public class SenderKeyName { 25 | 26 | private final String groupId; 27 | private final AxolotlAddress sender; 28 | 29 | public SenderKeyName(String groupId, AxolotlAddress sender) { 30 | this.groupId = groupId; 31 | this.sender = sender; 32 | } 33 | 34 | public String getGroupId() { 35 | return groupId; 36 | } 37 | 38 | public AxolotlAddress getSender() { 39 | return sender; 40 | } 41 | 42 | public String serialize() { 43 | return groupId + "::" + sender.getName() + "::" + String.valueOf(sender.getDeviceId()); 44 | } 45 | 46 | public boolean equals(Object other) { 47 | if (other == null) return false; 48 | if (!(other instanceof SenderKeyName)) return false; 49 | 50 | SenderKeyName that = (SenderKeyName)other; 51 | 52 | return 53 | this.groupId.equals(that.groupId) && 54 | this.sender.equals(that.sender); 55 | } 56 | 57 | public int hashCode() { 58 | int hash = 7; 59 | hash = 31 * hash + groupId.hashCode(); 60 | hash = 31 * hash + sender.hashCode(); 61 | return hash; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/org/whispersystems/libaxolotl/groups/ratchet/SenderChainKey.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2014-2015 Open Whisper Systems 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package org.whispersystems.libaxolotl.groups.ratchet; 18 | 19 | import org.whispersystems.libaxolotl.j2me.jce.JmeSecurity; 20 | import org.whispersystems.libaxolotl.j2me.jce.mac.Mac; 21 | 22 | /** 23 | * Each SenderKey is a "chain" of keys, each derived from the previous. 24 | * 25 | * At any given point in time, the state of a SenderKey can be represented 26 | * as the current chain key value, along with its iteration count. From there, 27 | * subsequent iterations can be derived, as well as individual message keys from 28 | * each chain key. 29 | * 30 | * @author Moxie Marlinspike 31 | */ 32 | public class SenderChainKey { 33 | 34 | private static final byte[] MESSAGE_KEY_SEED = {0x01}; 35 | private static final byte[] CHAIN_KEY_SEED = {0x02}; 36 | 37 | private final int iteration; 38 | private final byte[] chainKey; 39 | 40 | public SenderChainKey(int iteration, byte[] chainKey) { 41 | this.iteration = iteration; 42 | this.chainKey = chainKey; 43 | } 44 | 45 | public int getIteration() { 46 | return iteration; 47 | } 48 | 49 | public SenderMessageKey getSenderMessageKey() { 50 | return new SenderMessageKey(iteration, getDerivative(MESSAGE_KEY_SEED, chainKey)); 51 | } 52 | 53 | public SenderChainKey getNext() { 54 | return new SenderChainKey(iteration + 1, getDerivative(CHAIN_KEY_SEED, chainKey)); 55 | } 56 | 57 | public byte[] getSeed() { 58 | return chainKey; 59 | } 60 | 61 | private byte[] getDerivative(byte[] seed, byte[] key) { 62 | Mac mac = JmeSecurity.getProvider().createMacSha256(key); 63 | byte[] output = new byte[32]; 64 | 65 | mac.update(seed, 0, seed.length); 66 | mac.doFinal(output, 0); 67 | 68 | return output; 69 | } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /src/main/java/org/whispersystems/libaxolotl/groups/ratchet/SenderMessageKey.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2014-2015 Open Whisper Systems 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package org.whispersystems.libaxolotl.groups.ratchet; 18 | 19 | import org.whispersystems.libaxolotl.kdf.HKDFv3; 20 | import org.whispersystems.libaxolotl.util.ByteUtil; 21 | 22 | /** 23 | * The final symmetric material (IV and Cipher Key) used for encrypting 24 | * individual SenderKey messages. 25 | * 26 | * @author Moxie Marlinspike 27 | */ 28 | public class SenderMessageKey { 29 | 30 | private final int iteration; 31 | private final byte[] iv; 32 | private final byte[] cipherKey; 33 | private final byte[] seed; 34 | 35 | public SenderMessageKey(int iteration, byte[] seed) { 36 | byte[] derivative = new HKDFv3().deriveSecrets(seed, "WhisperGroup".getBytes(), 48); 37 | byte[][] parts = ByteUtil.split(derivative, 16, 32); 38 | 39 | this.iteration = iteration; 40 | this.seed = seed; 41 | this.iv = parts[0]; 42 | this.cipherKey = parts[1]; 43 | } 44 | 45 | public int getIteration() { 46 | return iteration; 47 | } 48 | 49 | public byte[] getIv() { 50 | return iv; 51 | } 52 | 53 | public byte[] getCipherKey() { 54 | return cipherKey; 55 | } 56 | 57 | public byte[] getSeed() { 58 | return seed; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/org/whispersystems/libaxolotl/groups/state/SenderKeyRecord.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2014-2015 Open Whisper Systems 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package org.whispersystems.libaxolotl.groups.state; 18 | 19 | import org.whispersystems.libaxolotl.InvalidKeyIdException; 20 | import org.whispersystems.libaxolotl.ecc.ECKeyPair; 21 | import org.whispersystems.libaxolotl.ecc.ECPublicKey; 22 | import org.whispersystems.libaxolotl.state.protos.SenderKeyRecordStructure; 23 | import org.whispersystems.libaxolotl.state.protos.SenderKeyStateStructure; 24 | 25 | import java.io.IOException; 26 | import java.util.Vector; 27 | 28 | /** 29 | * A durable representation of a set of SenderKeyStates for a specific 30 | * SenderKeyName. 31 | * 32 | * @author Moxie Marlisnpike 33 | */ 34 | public class SenderKeyRecord { 35 | 36 | // SenderKeyState 37 | private Vector senderKeyStates = new Vector(); 38 | 39 | private static final int MAX_STATES = 5; 40 | 41 | public SenderKeyRecord() {} 42 | 43 | public SenderKeyRecord(byte[] serialized) throws IOException { 44 | SenderKeyRecordStructure senderKeyRecordStructure = SenderKeyRecordStructure.fromBytes(serialized); 45 | Vector senderKeyStates = senderKeyRecordStructure.getSenderkeystatesVector(); 46 | 47 | for (int i=0;i MAX_STATES) { 78 | senderKeyStates.removeElementAt(senderKeyStates.size()-1); 79 | } 80 | } 81 | 82 | public void setSenderKeyState(int id, int iteration, byte[] chainKey, ECKeyPair signatureKey) { 83 | senderKeyStates.removeAllElements(); 84 | senderKeyStates.addElement(new SenderKeyState(id, iteration, chainKey, signatureKey)); 85 | } 86 | 87 | public byte[] serialize() { 88 | SenderKeyRecordStructure recordStructure = new SenderKeyRecordStructure(); 89 | 90 | for (int i=0;i. 16 | */ 17 | package org.whispersystems.libaxolotl.groups.state; 18 | 19 | import org.whispersystems.libaxolotl.groups.SenderKeyName; 20 | 21 | public interface SenderKeyStore { 22 | /** 23 | * Commit to storage the {@link org.whispersystems.libaxolotl.groups.state.SenderKeyRecord} for a 24 | * given (groupId + senderId + deviceId) tuple. 25 | * 26 | * @param senderKeyName the (groupId + senderId + deviceId) tuple. 27 | * @param record the current SenderKeyRecord for the specified senderKeyName. 28 | */ 29 | public void storeSenderKey(SenderKeyName senderKeyName, SenderKeyRecord record); 30 | 31 | /** 32 | * Returns a copy of the {@link org.whispersystems.libaxolotl.groups.state.SenderKeyRecord} 33 | * corresponding to the (groupId + senderId + deviceId) tuple, or a new SenderKeyRecord if 34 | * one does not currently exist. 35 | *

36 | * It is important that implementations return a copy of the current durable information. The 37 | * returned SenderKeyRecord may be modified, but those changes should not have an effect on the 38 | * durable session state (what is returned by subsequent calls to this method) without the 39 | * store method being called here first. 40 | * 41 | * @param senderKeyName The (groupId + senderId + deviceId) tuple. 42 | * @return a copy of the SenderKeyRecord corresponding to the (groupId + senderId + deviceId tuple, or 43 | * a new SenderKeyRecord if one does not currently exist. 44 | */ 45 | 46 | public SenderKeyRecord loadSenderKey(SenderKeyName senderKeyName); 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/org/whispersystems/libaxolotl/j2me/Arrays.java: -------------------------------------------------------------------------------- 1 | package org.whispersystems.libaxolotl.j2me; 2 | 3 | public class Arrays { 4 | 5 | public static boolean equals(byte[] a, byte[] a2) { 6 | if (a==a2) 7 | return true; 8 | if (a==null || a2==null) 9 | return false; 10 | 11 | int length = a.length; 12 | if (a2.length != length) 13 | return false; 14 | 15 | for (int i=0; i. 16 | */ 17 | 18 | package org.whispersystems.libaxolotl.kdf; 19 | 20 | import org.bouncycastle.crypto.params.KeyParameter; 21 | import org.bouncycastle.crypto.params.ParametersWithIV; 22 | import org.whispersystems.libaxolotl.j2me.AssertionError; 23 | import org.whispersystems.libaxolotl.util.ByteUtil; 24 | import org.whispersystems.libaxolotl.j2me.ParseException; 25 | 26 | public class DerivedMessageSecrets { 27 | 28 | public static final int SIZE = 80; 29 | private static final int CIPHER_KEY_LENGTH = 32; 30 | private static final int MAC_KEY_LENGTH = 32; 31 | private static final int IV_LENGTH = 16; 32 | 33 | private final KeyParameter cipherKey; 34 | private final KeyParameter macKey; 35 | private final ParametersWithIV iv; 36 | 37 | public DerivedMessageSecrets(byte[] okm) { 38 | try { 39 | byte[][] keys = ByteUtil.split(okm, CIPHER_KEY_LENGTH, MAC_KEY_LENGTH, IV_LENGTH); 40 | 41 | this.cipherKey = new KeyParameter(keys[0], 0, keys[0].length); 42 | this.macKey = new KeyParameter(keys[1], 0, keys[1].length); 43 | this.iv = new ParametersWithIV(null, keys[2], 0, keys[2].length); 44 | } catch (ParseException e) { 45 | throw new AssertionError(e); 46 | } 47 | } 48 | 49 | public KeyParameter getCipherKey() { 50 | return cipherKey; 51 | } 52 | 53 | public KeyParameter getMacKey() { 54 | return macKey; 55 | } 56 | 57 | public ParametersWithIV getIv() { 58 | return iv; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/org/whispersystems/libaxolotl/kdf/DerivedRootSecrets.java: -------------------------------------------------------------------------------- 1 | package org.whispersystems.libaxolotl.kdf; 2 | 3 | import org.whispersystems.libaxolotl.util.ByteUtil; 4 | 5 | public class DerivedRootSecrets { 6 | 7 | public static final int SIZE = 64; 8 | 9 | private final byte[] rootKey; 10 | private final byte[] chainKey; 11 | 12 | public DerivedRootSecrets(byte[] okm) { 13 | byte[][] keys = ByteUtil.split(okm, 32, 32); 14 | this.rootKey = keys[0]; 15 | this.chainKey = keys[1]; 16 | } 17 | 18 | public byte[] getRootKey() { 19 | return rootKey; 20 | } 21 | 22 | public byte[] getChainKey() { 23 | return chainKey; 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/org/whispersystems/libaxolotl/kdf/HKDF.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2013 Open Whisper Systems 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package org.whispersystems.libaxolotl.kdf; 19 | 20 | import org.whispersystems.libaxolotl.j2me.AssertionError; 21 | import org.whispersystems.libaxolotl.j2me.jce.JmeSecurity; 22 | import org.whispersystems.libaxolotl.j2me.jce.mac.Mac; 23 | 24 | import java.io.ByteArrayOutputStream; 25 | 26 | public abstract class HKDF { 27 | 28 | private static final int HASH_OUTPUT_SIZE = 32; 29 | 30 | public static HKDF createFor(int messageVersion) { 31 | switch (messageVersion) { 32 | case 2: return new HKDFv2(); 33 | case 3: return new HKDFv3(); 34 | default: throw new AssertionError("Unknown version: " + messageVersion); 35 | } 36 | } 37 | 38 | public byte[] deriveSecrets(byte[] inputKeyMaterial, byte[] info, int outputLength) { 39 | byte[] salt = new byte[HASH_OUTPUT_SIZE]; 40 | return deriveSecrets(inputKeyMaterial, salt, info, outputLength); 41 | } 42 | 43 | public byte[] deriveSecrets(byte[] inputKeyMaterial, byte[] salt, byte[] info, int outputLength) { 44 | byte[] prk = extract(salt, inputKeyMaterial); 45 | return expand(prk, info, outputLength); 46 | } 47 | 48 | private byte[] extract(byte[] salt, byte[] inputKeyMaterial) { 49 | Mac mac = JmeSecurity.getProvider().createMacSha256(salt); 50 | byte[] output = new byte[32]; 51 | 52 | mac.update(inputKeyMaterial, 0, inputKeyMaterial.length); 53 | mac.doFinal(output, 0); 54 | 55 | return output; 56 | } 57 | 58 | private byte[] expand(byte[] prk, byte[] info, int outputSize) { 59 | int iterations = (int) Math.ceil((double) outputSize / (double) HASH_OUTPUT_SIZE); 60 | byte[] mixin = new byte[0]; 61 | ByteArrayOutputStream results = new ByteArrayOutputStream(); 62 | int remainingBytes = outputSize; 63 | 64 | for (int i= getIterationStartOffset();i. 16 | */ 17 | package org.whispersystems.libaxolotl.protocol; 18 | 19 | public interface CiphertextMessage { 20 | 21 | public static final int UNSUPPORTED_VERSION = 1; 22 | public static final int CURRENT_VERSION = 3; 23 | 24 | public static final int WHISPER_TYPE = 2; 25 | public static final int PREKEY_TYPE = 3; 26 | public static final int SENDERKEY_TYPE = 4; 27 | public static final int SENDERKEY_DISTRIBUTION_TYPE = 5; 28 | 29 | // This should be the worst case (worse than V2). So not always accurate, but good enough for padding. 30 | public static final int ENCRYPTED_MESSAGE_OVERHEAD = 53; 31 | 32 | public byte[] serialize(); 33 | public int getType(); 34 | 35 | } -------------------------------------------------------------------------------- /src/main/java/org/whispersystems/libaxolotl/protocol/KeyExchangeMessage.java: -------------------------------------------------------------------------------- 1 | package org.whispersystems.libaxolotl.protocol; 2 | 3 | 4 | import org.whispersystems.libaxolotl.IdentityKey; 5 | import org.whispersystems.libaxolotl.InvalidKeyException; 6 | import org.whispersystems.libaxolotl.InvalidMessageException; 7 | import org.whispersystems.libaxolotl.InvalidVersionException; 8 | import org.whispersystems.libaxolotl.LegacyMessageException; 9 | import org.whispersystems.libaxolotl.ecc.Curve; 10 | import org.whispersystems.libaxolotl.ecc.ECPublicKey; 11 | import org.whispersystems.libaxolotl.util.ByteUtil; 12 | 13 | 14 | public class KeyExchangeMessage { 15 | 16 | public static final int INITIATE_FLAG = 0x01; 17 | public static final int RESPONSE_FLAG = 0X02; 18 | public static final int SIMULTAENOUS_INITIATE_FLAG = 0x04; 19 | 20 | private final int version; 21 | private final int supportedVersion; 22 | private final int sequence; 23 | private final int flags; 24 | 25 | private final ECPublicKey baseKey; 26 | private final byte[] baseKeySignature; 27 | private final ECPublicKey ratchetKey; 28 | private final IdentityKey identityKey; 29 | private final byte[] serialized; 30 | 31 | public KeyExchangeMessage(int messageVersion, int sequence, int flags, 32 | ECPublicKey baseKey, byte[] baseKeySignature, 33 | ECPublicKey ratchetKey, 34 | IdentityKey identityKey) 35 | { 36 | this.supportedVersion = CiphertextMessage.CURRENT_VERSION; 37 | this.version = messageVersion; 38 | this.sequence = sequence; 39 | this.flags = flags; 40 | this.baseKey = baseKey; 41 | this.baseKeySignature = baseKeySignature; 42 | this.ratchetKey = ratchetKey; 43 | this.identityKey = identityKey; 44 | 45 | byte[] version = {ByteUtil.intsToByteHighAndLow(this.version, this.supportedVersion)}; 46 | 47 | org.whispersystems.libaxolotl.protocol.protos.KeyExchangeMessage structure = new org.whispersystems.libaxolotl.protocol.protos.KeyExchangeMessage(); 48 | structure.setId((sequence << 5) | flags); 49 | structure.setBasekey(baseKey.serialize()); 50 | structure.setRatchetkey(ratchetKey.serialize()); 51 | structure.setIdentitykey(identityKey.serialize()); 52 | 53 | if (messageVersion >= 3) { 54 | structure.setBasekeysignature(baseKeySignature); 55 | } 56 | 57 | this.serialized = ByteUtil.combine(version, structure.toBytes()); 58 | } 59 | 60 | public KeyExchangeMessage(byte[] serialized) 61 | throws InvalidMessageException, InvalidVersionException, LegacyMessageException 62 | { 63 | try { 64 | byte[][] parts = ByteUtil.split(serialized, 1, serialized.length - 1); 65 | this.version = ByteUtil.highBitsToInt(parts[0][0]); 66 | this.supportedVersion = ByteUtil.lowBitsToInt(parts[0][0]); 67 | 68 | if (this.version <= CiphertextMessage.UNSUPPORTED_VERSION) { 69 | throw new LegacyMessageException("Unsupported legacy version: " + this.version); 70 | } 71 | 72 | if (this.version > CiphertextMessage.CURRENT_VERSION) { 73 | throw new InvalidVersionException("Unknown version: " + this.version); 74 | } 75 | 76 | org.whispersystems.libaxolotl.protocol.protos.KeyExchangeMessage message = 77 | org.whispersystems.libaxolotl.protocol.protos.KeyExchangeMessage.fromBytes(parts[1]); 78 | 79 | if (!message.hasId() || !message.hasBasekey() || 80 | !message.hasRatchetkey() || !message.hasIdentitykey() || 81 | (this.version >=3 && message.getBasekeysignature() == null)) 82 | { 83 | throw new InvalidMessageException("Some required fields missing!"); 84 | } 85 | 86 | this.sequence = message.getId() >> 5; 87 | this.flags = message.getId() & 0x1f; 88 | this.serialized = serialized; 89 | this.baseKey = Curve.decodePoint(message.getBasekey(), 0); 90 | this.baseKeySignature = message.getBasekeysignature(); 91 | this.ratchetKey = Curve.decodePoint(message.getRatchetkey(), 0); 92 | this.identityKey = new IdentityKey(message.getIdentitykey(), 0); 93 | } catch (InvalidKeyException ike) { 94 | throw new InvalidMessageException(ike); 95 | } 96 | } 97 | 98 | public int getVersion() { 99 | return version; 100 | } 101 | 102 | public ECPublicKey getBaseKey() { 103 | return baseKey; 104 | } 105 | 106 | public byte[] getBaseKeySignature() { 107 | return baseKeySignature; 108 | } 109 | 110 | public ECPublicKey getRatchetKey() { 111 | return ratchetKey; 112 | } 113 | 114 | public IdentityKey getIdentityKey() { 115 | return identityKey; 116 | } 117 | 118 | public boolean hasIdentityKey() { 119 | return true; 120 | } 121 | 122 | public int getMaxVersion() { 123 | return supportedVersion; 124 | } 125 | 126 | public boolean isResponse() { 127 | return ((flags & RESPONSE_FLAG) != 0); 128 | } 129 | 130 | public boolean isInitiate() { 131 | return (flags & INITIATE_FLAG) != 0; 132 | } 133 | 134 | public boolean isResponseForSimultaneousInitiate() { 135 | return (flags & SIMULTAENOUS_INITIATE_FLAG) != 0; 136 | } 137 | 138 | public int getFlags() { 139 | return flags; 140 | } 141 | 142 | public int getSequence() { 143 | return sequence; 144 | } 145 | 146 | public byte[] serialize() { 147 | return serialized; 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /src/main/java/org/whispersystems/libaxolotl/protocol/PreKeyWhisperMessage.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2014 Open Whisper Systems 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package org.whispersystems.libaxolotl.protocol; 18 | 19 | import org.whispersystems.libaxolotl.IdentityKey; 20 | import org.whispersystems.libaxolotl.InvalidKeyException; 21 | import org.whispersystems.libaxolotl.InvalidMessageException; 22 | import org.whispersystems.libaxolotl.InvalidVersionException; 23 | import org.whispersystems.libaxolotl.LegacyMessageException; 24 | import org.whispersystems.libaxolotl.ecc.Curve; 25 | import org.whispersystems.libaxolotl.ecc.ECPublicKey; 26 | import org.whispersystems.libaxolotl.util.ByteUtil; 27 | import org.whispersystems.libaxolotl.util.guava.Optional; 28 | 29 | 30 | public class PreKeyWhisperMessage implements CiphertextMessage { 31 | 32 | private final int version; 33 | private final int registrationId; 34 | private final Optional preKeyId; 35 | private final int signedPreKeyId; 36 | private final ECPublicKey baseKey; 37 | private final IdentityKey identityKey; 38 | private final WhisperMessage message; 39 | private final byte[] serialized; 40 | 41 | public PreKeyWhisperMessage(byte[] serialized) 42 | throws InvalidMessageException, InvalidVersionException 43 | { 44 | try { 45 | this.version = ByteUtil.highBitsToInt(serialized[0]); 46 | 47 | if (this.version > CiphertextMessage.CURRENT_VERSION) { 48 | throw new InvalidVersionException("Unknown version: " + this.version); 49 | } 50 | 51 | byte[] structureBytes = new byte[serialized.length - 1]; 52 | System.arraycopy(serialized, 1, structureBytes, 0, structureBytes.length); 53 | 54 | org.whispersystems.libaxolotl.protocol.protos.PreKeyWhisperMessage structure 55 | = org.whispersystems.libaxolotl.protocol.protos.PreKeyWhisperMessage.fromBytes(structureBytes); 56 | 57 | if ((version == 2 && !structure.hasPrekeyid()) || 58 | (version == 3 && !structure.hasSignedprekeyid()) || 59 | !structure.hasBasekey() || 60 | !structure.hasIdentitykey() || 61 | !structure.hasMessage()) 62 | { 63 | throw new InvalidMessageException("Incomplete message."); 64 | } 65 | 66 | this.serialized = serialized; 67 | this.registrationId = structure.getRegistrationid(); 68 | this.preKeyId = structure.hasPrekeyid() ? Optional.of(new Integer(structure.getPrekeyid())) : Optional.absent(); 69 | this.signedPreKeyId = structure.hasSignedprekeyid() ? structure.getSignedprekeyid() : -1; 70 | this.baseKey = Curve.decodePoint(structure.getBasekey(), 0); 71 | this.identityKey = new IdentityKey(Curve.decodePoint(structure.getIdentitykey(), 0)); 72 | this.message = new WhisperMessage(structure.getMessage()); 73 | } catch (InvalidKeyException ike) { 74 | throw new InvalidMessageException(ike); 75 | } catch (LegacyMessageException e) { 76 | throw new InvalidMessageException(e); 77 | } 78 | } 79 | 80 | public PreKeyWhisperMessage(int messageVersion, int registrationId, Optional preKeyId, 81 | int signedPreKeyId, ECPublicKey baseKey, IdentityKey identityKey, 82 | WhisperMessage message) 83 | { 84 | this.version = messageVersion; 85 | this.registrationId = registrationId; 86 | this.preKeyId = preKeyId; 87 | this.signedPreKeyId = signedPreKeyId; 88 | this.baseKey = baseKey; 89 | this.identityKey = identityKey; 90 | this.message = message; 91 | 92 | org.whispersystems.libaxolotl.protocol.protos.PreKeyWhisperMessage builder = 93 | new org.whispersystems.libaxolotl.protocol.protos.PreKeyWhisperMessage(); 94 | builder.setSignedprekeyid(signedPreKeyId); 95 | builder.setBasekey(baseKey.serialize()); 96 | builder.setIdentitykey(identityKey.serialize()); 97 | builder.setMessage(message.serialize()); 98 | builder.setRegistrationid(registrationId); 99 | 100 | if (preKeyId.isPresent()) { 101 | builder.setPrekeyid(((Integer) preKeyId.get()).intValue()); 102 | } 103 | 104 | byte[] versionBytes = {ByteUtil.intsToByteHighAndLow(this.version, CURRENT_VERSION)}; 105 | byte[] messageBytes = builder.toBytes(); 106 | 107 | this.serialized = ByteUtil.combine(versionBytes, messageBytes); 108 | } 109 | 110 | public int getMessageVersion() { 111 | return version; 112 | } 113 | 114 | public IdentityKey getIdentityKey() { 115 | return identityKey; 116 | } 117 | 118 | public int getRegistrationId() { 119 | return registrationId; 120 | } 121 | 122 | public Optional getPreKeyId() { 123 | return preKeyId; 124 | } 125 | 126 | public int getSignedPreKeyId() { 127 | return signedPreKeyId; 128 | } 129 | 130 | public ECPublicKey getBaseKey() { 131 | return baseKey; 132 | } 133 | 134 | public WhisperMessage getWhisperMessage() { 135 | return message; 136 | } 137 | 138 | // @Override 139 | public byte[] serialize() { 140 | return serialized; 141 | } 142 | 143 | // @Override 144 | public int getType() { 145 | return CiphertextMessage.PREKEY_TYPE; 146 | } 147 | 148 | } 149 | -------------------------------------------------------------------------------- /src/main/java/org/whispersystems/libaxolotl/protocol/SenderKeyDistributionMessage.java: -------------------------------------------------------------------------------- 1 | package org.whispersystems.libaxolotl.protocol; 2 | 3 | import org.whispersystems.libaxolotl.InvalidKeyException; 4 | import org.whispersystems.libaxolotl.InvalidMessageException; 5 | import org.whispersystems.libaxolotl.LegacyMessageException; 6 | import org.whispersystems.libaxolotl.ecc.Curve; 7 | import org.whispersystems.libaxolotl.ecc.ECPublicKey; 8 | import org.whispersystems.libaxolotl.util.ByteUtil; 9 | 10 | public class SenderKeyDistributionMessage implements CiphertextMessage { 11 | 12 | private final int id; 13 | private final int iteration; 14 | private final byte[] chainKey; 15 | private final ECPublicKey signatureKey; 16 | private final byte[] serialized; 17 | 18 | public SenderKeyDistributionMessage(int id, int iteration, byte[] chainKey, ECPublicKey signatureKey) { 19 | byte[] version = {ByteUtil.intsToByteHighAndLow(CURRENT_VERSION, CURRENT_VERSION)}; 20 | 21 | this.id = id; 22 | this.iteration = iteration; 23 | this.chainKey = chainKey; 24 | this.signatureKey = signatureKey; 25 | 26 | org.whispersystems.libaxolotl.protocol.protos.SenderKeyDistributionMessage structure = 27 | new org.whispersystems.libaxolotl.protocol.protos.SenderKeyDistributionMessage(); 28 | 29 | structure.setId(id); 30 | structure.setIteration(iteration); 31 | structure.setChainkey(chainKey); 32 | structure.setSigningkey(signatureKey.serialize()); 33 | 34 | this.serialized = ByteUtil.combine(version, structure.toBytes()); 35 | } 36 | 37 | public SenderKeyDistributionMessage(byte[] serialized) throws LegacyMessageException, InvalidMessageException { 38 | try { 39 | byte[][] messageParts = ByteUtil.split(serialized, 1, serialized.length - 1); 40 | byte version = messageParts[0][0]; 41 | byte[] message = messageParts[1]; 42 | 43 | if (ByteUtil.highBitsToInt(version) < CiphertextMessage.CURRENT_VERSION) { 44 | throw new LegacyMessageException("Legacy message: " + ByteUtil.highBitsToInt(version)); 45 | } 46 | 47 | if (ByteUtil.highBitsToInt(version) > CURRENT_VERSION) { 48 | throw new InvalidMessageException("Unknown version: " + ByteUtil.highBitsToInt(version)); 49 | } 50 | 51 | org.whispersystems.libaxolotl.protocol.protos.SenderKeyDistributionMessage distributionMessage = 52 | org.whispersystems.libaxolotl.protocol.protos.SenderKeyDistributionMessage.fromBytes(message); 53 | 54 | if (!distributionMessage.hasId() || 55 | !distributionMessage.hasIteration() || 56 | !distributionMessage.hasChainkey() || 57 | !distributionMessage.hasSigningkey()) 58 | { 59 | throw new InvalidMessageException("Incomplete message."); 60 | } 61 | 62 | this.serialized = serialized; 63 | this.id = distributionMessage.getId(); 64 | this.iteration = distributionMessage.getIteration(); 65 | this.chainKey = distributionMessage.getChainkey(); 66 | this.signatureKey = Curve.decodePoint(distributionMessage.getSigningkey(), 0); 67 | } catch (InvalidKeyException e) { 68 | throw new InvalidMessageException(e); 69 | } 70 | } 71 | 72 | // @Override 73 | public byte[] serialize() { 74 | return serialized; 75 | } 76 | 77 | // @Override 78 | public int getType() { 79 | return SENDERKEY_DISTRIBUTION_TYPE; 80 | } 81 | 82 | public int getIteration() { 83 | return iteration; 84 | } 85 | 86 | public byte[] getChainKey() { 87 | return chainKey; 88 | } 89 | 90 | public ECPublicKey getSignatureKey() { 91 | return signatureKey; 92 | } 93 | 94 | public int getId() { 95 | return id; 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/main/java/org/whispersystems/libaxolotl/protocol/SenderKeyMessage.java: -------------------------------------------------------------------------------- 1 | package org.whispersystems.libaxolotl.protocol; 2 | 3 | import org.whispersystems.curve25519.SecureRandomProvider; 4 | import org.whispersystems.libaxolotl.InvalidKeyException; 5 | import org.whispersystems.libaxolotl.InvalidMessageException; 6 | import org.whispersystems.libaxolotl.LegacyMessageException; 7 | import org.whispersystems.libaxolotl.ecc.Curve; 8 | import org.whispersystems.libaxolotl.ecc.ECPrivateKey; 9 | import org.whispersystems.libaxolotl.ecc.ECPublicKey; 10 | import org.whispersystems.libaxolotl.j2me.AssertionError; 11 | import org.whispersystems.libaxolotl.util.ByteUtil; 12 | import org.whispersystems.libaxolotl.j2me.ParseException; 13 | 14 | 15 | public class SenderKeyMessage implements CiphertextMessage { 16 | 17 | private static final int SIGNATURE_LENGTH = 64; 18 | 19 | private final int messageVersion; 20 | private final int keyId; 21 | private final int iteration; 22 | private final byte[] ciphertext; 23 | private final byte[] serialized; 24 | 25 | public SenderKeyMessage(byte[] serialized) throws InvalidMessageException, LegacyMessageException { 26 | try { 27 | byte[][] messageParts = ByteUtil.split(serialized, 1, serialized.length - 1 - SIGNATURE_LENGTH, SIGNATURE_LENGTH); 28 | byte version = messageParts[0][0]; 29 | byte[] message = messageParts[1]; 30 | byte[] signature = messageParts[2]; 31 | 32 | if (ByteUtil.highBitsToInt(version) < 3) { 33 | throw new LegacyMessageException("Legacy message: " + ByteUtil.highBitsToInt(version)); 34 | } 35 | 36 | if (ByteUtil.highBitsToInt(version) > CURRENT_VERSION) { 37 | throw new InvalidMessageException("Unknown version: " + ByteUtil.highBitsToInt(version)); 38 | } 39 | 40 | org.whispersystems.libaxolotl.protocol.protos.SenderKeyMessage structure = 41 | org.whispersystems.libaxolotl.protocol.protos.SenderKeyMessage.fromBytes(message); 42 | 43 | if (!structure.hasId() || 44 | !structure.hasIteration() || 45 | !structure.hasCiphertext()) 46 | { 47 | throw new InvalidMessageException("Incomplete message."); 48 | } 49 | 50 | this.serialized = serialized; 51 | this.messageVersion = ByteUtil.highBitsToInt(version); 52 | this.keyId = structure.getId(); 53 | this.iteration = structure.getIteration(); 54 | this.ciphertext = structure.getCiphertext(); 55 | } catch (ParseException e) { 56 | throw new InvalidMessageException(e); 57 | } 58 | } 59 | 60 | public SenderKeyMessage(SecureRandomProvider secureRandom, 61 | int keyId, int iteration, byte[] ciphertext, ECPrivateKey signatureKey) 62 | { 63 | org.whispersystems.libaxolotl.protocol.protos.SenderKeyMessage structure = 64 | new org.whispersystems.libaxolotl.protocol.protos.SenderKeyMessage(); 65 | 66 | structure.setId(keyId); 67 | structure.setIteration(iteration); 68 | structure.setCiphertext(ciphertext); 69 | 70 | byte[] version = {ByteUtil.intsToByteHighAndLow(CURRENT_VERSION, CURRENT_VERSION)}; 71 | byte[] message = structure.toBytes(); 72 | byte[] signature = getSignature(secureRandom, signatureKey, ByteUtil.combine(version, message)); 73 | 74 | this.serialized = ByteUtil.combine(version, message, signature); 75 | this.messageVersion = CURRENT_VERSION; 76 | this.keyId = keyId; 77 | this.iteration = iteration; 78 | this.ciphertext = ciphertext; 79 | } 80 | 81 | public int getKeyId() { 82 | return keyId; 83 | } 84 | 85 | public int getIteration() { 86 | return iteration; 87 | } 88 | 89 | public byte[] getCipherText() { 90 | return ciphertext; 91 | } 92 | 93 | public void verifySignature(ECPublicKey signatureKey) 94 | throws InvalidMessageException 95 | { 96 | try { 97 | byte[][] parts = ByteUtil.split(serialized, serialized.length - SIGNATURE_LENGTH, SIGNATURE_LENGTH); 98 | 99 | if (!Curve.verifySignature(signatureKey, parts[0], parts[1])) { 100 | throw new InvalidMessageException("Invalid signature!"); 101 | } 102 | 103 | } catch (InvalidKeyException e) { 104 | throw new InvalidMessageException(e); 105 | } 106 | } 107 | 108 | private byte[] getSignature(SecureRandomProvider secureRandom, ECPrivateKey signatureKey, byte[] serialized) { 109 | try { 110 | return Curve.calculateSignature(secureRandom, signatureKey, serialized); 111 | } catch (InvalidKeyException e) { 112 | throw new AssertionError(e); 113 | } 114 | } 115 | 116 | // @Override 117 | public byte[] serialize() { 118 | return serialized; 119 | } 120 | 121 | // @Override 122 | public int getType() { 123 | return CiphertextMessage.SENDERKEY_TYPE; 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /src/main/java/org/whispersystems/libaxolotl/protocol/WhisperMessage.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2014 Open Whisper Systems 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package org.whispersystems.libaxolotl.protocol; 18 | 19 | import org.bouncycastle.crypto.params.KeyParameter; 20 | import org.whispersystems.libaxolotl.IdentityKey; 21 | import org.whispersystems.libaxolotl.InvalidKeyException; 22 | import org.whispersystems.libaxolotl.InvalidMessageException; 23 | import org.whispersystems.libaxolotl.LegacyMessageException; 24 | import org.whispersystems.libaxolotl.ecc.Curve; 25 | import org.whispersystems.libaxolotl.ecc.ECPublicKey; 26 | import org.whispersystems.libaxolotl.j2me.MessageDigest; 27 | import org.whispersystems.libaxolotl.j2me.ParseException; 28 | import org.whispersystems.libaxolotl.j2me.jce.JmeSecurity; 29 | import org.whispersystems.libaxolotl.j2me.jce.mac.Mac; 30 | import org.whispersystems.libaxolotl.util.ByteUtil; 31 | 32 | public class WhisperMessage implements CiphertextMessage { 33 | 34 | private static final int MAC_LENGTH = 8; 35 | 36 | private final int messageVersion; 37 | private final ECPublicKey senderRatchetKey; 38 | private final int counter; 39 | private final int previousCounter; 40 | private final byte[] ciphertext; 41 | private final byte[] serialized; 42 | 43 | public WhisperMessage(byte[] serialized) throws InvalidMessageException, LegacyMessageException { 44 | try { 45 | byte[][] messageParts = ByteUtil.split(serialized, 1, serialized.length - 1 - MAC_LENGTH, MAC_LENGTH); 46 | byte version = messageParts[0][0]; 47 | byte[] message = messageParts[1]; 48 | byte[] mac = messageParts[2]; 49 | 50 | if (ByteUtil.highBitsToInt(version) <= CiphertextMessage.UNSUPPORTED_VERSION) { 51 | throw new LegacyMessageException("Legacy message: " + ByteUtil.highBitsToInt(version)); 52 | } 53 | 54 | if (ByteUtil.highBitsToInt(version) > CURRENT_VERSION) { 55 | throw new InvalidMessageException("Unknown version: " + ByteUtil.highBitsToInt(version)); 56 | } 57 | 58 | org.whispersystems.libaxolotl.protocol.protos.WhisperMessage structure = 59 | org.whispersystems.libaxolotl.protocol.protos.WhisperMessage.fromBytes(message); 60 | 61 | if (!structure.hasCiphertext() || 62 | !structure.hasCounter() || 63 | !structure.hasRatchetkey()) 64 | { 65 | throw new InvalidMessageException("Incomplete message."); 66 | } 67 | 68 | this.serialized = serialized; 69 | this.senderRatchetKey = Curve.decodePoint(structure.getRatchetkey(), 0); 70 | this.messageVersion = ByteUtil.highBitsToInt(version); 71 | this.counter = structure.getCounter(); 72 | this.previousCounter = structure.getPreviouscounter(); 73 | this.ciphertext = structure.getCiphertext(); 74 | } catch (InvalidKeyException ike) { 75 | throw new InvalidMessageException(ike); 76 | } catch (ParseException e) { 77 | throw new InvalidMessageException(e); 78 | } 79 | } 80 | 81 | public WhisperMessage(int messageVersion, KeyParameter macKey, ECPublicKey senderRatchetKey, 82 | int counter, int previousCounter, byte[] ciphertext, 83 | IdentityKey senderIdentityKey, 84 | IdentityKey receiverIdentityKey) 85 | { 86 | org.whispersystems.libaxolotl.protocol.protos.WhisperMessage structure = 87 | new org.whispersystems.libaxolotl.protocol.protos.WhisperMessage(); 88 | 89 | structure.setRatchetkey(senderRatchetKey.serialize()); 90 | structure.setCounter(counter); 91 | structure.setPreviouscounter(previousCounter); 92 | structure.setCiphertext(ciphertext); 93 | 94 | byte[] version = {ByteUtil.intsToByteHighAndLow(messageVersion, CURRENT_VERSION)}; 95 | byte[] message = structure.toBytes(); 96 | byte[] mac = getMac(messageVersion, senderIdentityKey, receiverIdentityKey, macKey, 97 | ByteUtil.combine(version, message)); 98 | 99 | this.serialized = ByteUtil.combine(version, message, mac); 100 | this.senderRatchetKey = senderRatchetKey; 101 | this.counter = counter; 102 | this.previousCounter = previousCounter; 103 | this.ciphertext = ciphertext; 104 | this.messageVersion = messageVersion; 105 | } 106 | 107 | public ECPublicKey getSenderRatchetKey() { 108 | return senderRatchetKey; 109 | } 110 | 111 | public int getMessageVersion() { 112 | return messageVersion; 113 | } 114 | 115 | public int getCounter() { 116 | return counter; 117 | } 118 | 119 | public byte[] getBody() { 120 | return ciphertext; 121 | } 122 | 123 | public void verifyMac(int messageVersion, IdentityKey senderIdentityKey, 124 | IdentityKey receiverIdentityKey, KeyParameter macKey) 125 | throws InvalidMessageException 126 | { 127 | byte[][] parts = ByteUtil.split(serialized, serialized.length - MAC_LENGTH, MAC_LENGTH); 128 | byte[] ourMac = getMac(messageVersion, senderIdentityKey, receiverIdentityKey, macKey, parts[0]); 129 | byte[] theirMac = parts[1]; 130 | 131 | if (!MessageDigest.isEqual(ourMac, theirMac)) { 132 | throw new InvalidMessageException("Bad Mac!"); 133 | } 134 | } 135 | 136 | private byte[] getMac(int messageVersion, 137 | IdentityKey senderIdentityKey, 138 | IdentityKey receiverIdentityKey, 139 | KeyParameter macKey, byte[] serialized) 140 | { 141 | Mac mac = JmeSecurity.getProvider().createMacSha256(macKey.getKey()); 142 | byte[] output = new byte[32]; 143 | 144 | if (messageVersion >= 3) { 145 | byte[] senderIdentity = senderIdentityKey.getPublicKey().serialize(); 146 | byte[] receiverIdentity = receiverIdentityKey.getPublicKey().serialize(); 147 | 148 | mac.update(senderIdentity, 0, senderIdentity.length); 149 | mac.update(receiverIdentity, 0, receiverIdentity.length); 150 | } 151 | 152 | mac.update(serialized, 0, serialized.length); 153 | mac.doFinal(output, 0); 154 | 155 | return ByteUtil.trim(output, MAC_LENGTH); 156 | } 157 | 158 | // @Override 159 | public byte[] serialize() { 160 | return serialized; 161 | } 162 | 163 | // @Override 164 | public int getType() { 165 | return CiphertextMessage.WHISPER_TYPE; 166 | } 167 | 168 | public static boolean isLegacy(byte[] message) { 169 | return message != null && message.length >= 1 && 170 | ByteUtil.highBitsToInt(message[0]) <= CiphertextMessage.UNSUPPORTED_VERSION; 171 | } 172 | 173 | } 174 | -------------------------------------------------------------------------------- /src/main/java/org/whispersystems/libaxolotl/protocol/protos/KeyExchangeMessage.java: -------------------------------------------------------------------------------- 1 | package org.whispersystems.libaxolotl.protocol.protos; 2 | 3 | import java.util.Vector; 4 | import java.io.IOException; 5 | 6 | import com.ponderingpanda.protobuf.*; 7 | 8 | 9 | public class KeyExchangeMessage implements Message { 10 | 11 | 12 | 13 | protected int id; // 1 14 | protected boolean _hasId; 15 | protected byte[] basekey; // 2 16 | protected boolean _hasBasekey; 17 | protected byte[] ratchetkey; // 3 18 | protected boolean _hasRatchetkey; 19 | protected byte[] identitykey; // 4 20 | protected boolean _hasIdentitykey; 21 | protected byte[] basekeysignature; // 5 22 | protected boolean _hasBasekeysignature; 23 | 24 | public int getId() { 25 | return id; 26 | } 27 | 28 | public void setId(int id) { 29 | this.id = id; 30 | this._hasId = true; 31 | } 32 | 33 | public void clearId() { 34 | _hasId = false; 35 | } 36 | 37 | public boolean hasId() { 38 | return _hasId; 39 | } 40 | public byte[] getBasekey() { 41 | return basekey; 42 | } 43 | 44 | public void setBasekey(byte[] basekey) { 45 | this.basekey = basekey; 46 | this._hasBasekey = true; 47 | } 48 | 49 | public void clearBasekey() { 50 | _hasBasekey = false; 51 | } 52 | 53 | public boolean hasBasekey() { 54 | return _hasBasekey; 55 | } 56 | public byte[] getRatchetkey() { 57 | return ratchetkey; 58 | } 59 | 60 | public void setRatchetkey(byte[] ratchetkey) { 61 | this.ratchetkey = ratchetkey; 62 | this._hasRatchetkey = true; 63 | } 64 | 65 | public void clearRatchetkey() { 66 | _hasRatchetkey = false; 67 | } 68 | 69 | public boolean hasRatchetkey() { 70 | return _hasRatchetkey; 71 | } 72 | public byte[] getIdentitykey() { 73 | return identitykey; 74 | } 75 | 76 | public void setIdentitykey(byte[] identitykey) { 77 | this.identitykey = identitykey; 78 | this._hasIdentitykey = true; 79 | } 80 | 81 | public void clearIdentitykey() { 82 | _hasIdentitykey = false; 83 | } 84 | 85 | public boolean hasIdentitykey() { 86 | return _hasIdentitykey; 87 | } 88 | public byte[] getBasekeysignature() { 89 | return basekeysignature; 90 | } 91 | 92 | public void setBasekeysignature(byte[] basekeysignature) { 93 | this.basekeysignature = basekeysignature; 94 | this._hasBasekeysignature = true; 95 | } 96 | 97 | public void clearBasekeysignature() { 98 | _hasBasekeysignature = false; 99 | } 100 | 101 | public boolean hasBasekeysignature() { 102 | return _hasBasekeysignature; 103 | } 104 | 105 | public final void serialize(CodedOutputStream out) throws IOException { 106 | if(_hasId) 107 | out.writeUInt32(1, id); 108 | 109 | if(_hasBasekey) 110 | out.writeBytes(2, basekey); 111 | 112 | if(_hasRatchetkey) 113 | out.writeBytes(3, ratchetkey); 114 | 115 | if(_hasIdentitykey) 116 | out.writeBytes(4, identitykey); 117 | 118 | if(_hasBasekeysignature) 119 | out.writeBytes(5, basekeysignature); 120 | 121 | } 122 | 123 | public final void deserialize(CodedInputStream in) throws IOException { 124 | while(true) { 125 | int tag = in.readTag(); 126 | switch(tag) { 127 | case 0: 128 | return; 129 | case 8: { 130 | id = in.readUInt32(); 131 | _hasId = true; 132 | break; } 133 | case 18: { 134 | basekey = in.readBytes(); 135 | _hasBasekey = true; 136 | break; } 137 | case 26: { 138 | ratchetkey = in.readBytes(); 139 | _hasRatchetkey = true; 140 | break; } 141 | case 34: { 142 | identitykey = in.readBytes(); 143 | _hasIdentitykey = true; 144 | break; } 145 | case 42: { 146 | basekeysignature = in.readBytes(); 147 | _hasBasekeysignature = true; 148 | break; } 149 | default: 150 | in.skipTag(tag); 151 | } 152 | } 153 | } 154 | 155 | public static KeyExchangeMessage fromBytes(byte[] in) throws EncodingException { 156 | KeyExchangeMessage message = new KeyExchangeMessage(); 157 | ProtoUtil.messageFromBytes(in, message); 158 | return message; 159 | } 160 | 161 | public byte[] toBytes() throws EncodingException { 162 | return ProtoUtil.messageToBytes(this); 163 | } 164 | 165 | 166 | } 167 | 168 | 169 | 170 | -------------------------------------------------------------------------------- /src/main/java/org/whispersystems/libaxolotl/protocol/protos/PreKeyWhisperMessage.java: -------------------------------------------------------------------------------- 1 | package org.whispersystems.libaxolotl.protocol.protos; 2 | 3 | import java.util.Vector; 4 | import java.io.IOException; 5 | 6 | import com.ponderingpanda.protobuf.*; 7 | 8 | 9 | public class PreKeyWhisperMessage implements Message { 10 | 11 | 12 | 13 | protected int registrationid; // 5 14 | protected boolean _hasRegistrationid; 15 | protected int prekeyid; // 1 16 | protected boolean _hasPrekeyid; 17 | protected int signedprekeyid; // 6 18 | protected boolean _hasSignedprekeyid; 19 | protected byte[] basekey; // 2 20 | protected boolean _hasBasekey; 21 | protected byte[] identitykey; // 3 22 | protected boolean _hasIdentitykey; 23 | protected byte[] message; // 4 24 | protected boolean _hasMessage; 25 | 26 | public int getRegistrationid() { 27 | return registrationid; 28 | } 29 | 30 | public void setRegistrationid(int registrationid) { 31 | this.registrationid = registrationid; 32 | this._hasRegistrationid = true; 33 | } 34 | 35 | public void clearRegistrationid() { 36 | _hasRegistrationid = false; 37 | } 38 | 39 | public boolean hasRegistrationid() { 40 | return _hasRegistrationid; 41 | } 42 | public int getPrekeyid() { 43 | return prekeyid; 44 | } 45 | 46 | public void setPrekeyid(int prekeyid) { 47 | this.prekeyid = prekeyid; 48 | this._hasPrekeyid = true; 49 | } 50 | 51 | public void clearPrekeyid() { 52 | _hasPrekeyid = false; 53 | } 54 | 55 | public boolean hasPrekeyid() { 56 | return _hasPrekeyid; 57 | } 58 | public int getSignedprekeyid() { 59 | return signedprekeyid; 60 | } 61 | 62 | public void setSignedprekeyid(int signedprekeyid) { 63 | this.signedprekeyid = signedprekeyid; 64 | this._hasSignedprekeyid = true; 65 | } 66 | 67 | public void clearSignedprekeyid() { 68 | _hasSignedprekeyid = false; 69 | } 70 | 71 | public boolean hasSignedprekeyid() { 72 | return _hasSignedprekeyid; 73 | } 74 | public byte[] getBasekey() { 75 | return basekey; 76 | } 77 | 78 | public void setBasekey(byte[] basekey) { 79 | this.basekey = basekey; 80 | this._hasBasekey = true; 81 | } 82 | 83 | public void clearBasekey() { 84 | _hasBasekey = false; 85 | } 86 | 87 | public boolean hasBasekey() { 88 | return _hasBasekey; 89 | } 90 | public byte[] getIdentitykey() { 91 | return identitykey; 92 | } 93 | 94 | public void setIdentitykey(byte[] identitykey) { 95 | this.identitykey = identitykey; 96 | this._hasIdentitykey = true; 97 | } 98 | 99 | public void clearIdentitykey() { 100 | _hasIdentitykey = false; 101 | } 102 | 103 | public boolean hasIdentitykey() { 104 | return _hasIdentitykey; 105 | } 106 | public byte[] getMessage() { 107 | return message; 108 | } 109 | 110 | public void setMessage(byte[] message) { 111 | this.message = message; 112 | this._hasMessage = true; 113 | } 114 | 115 | public void clearMessage() { 116 | _hasMessage = false; 117 | } 118 | 119 | public boolean hasMessage() { 120 | return _hasMessage; 121 | } 122 | 123 | public final void serialize(CodedOutputStream out) throws IOException { 124 | if(_hasRegistrationid) 125 | out.writeUInt32(5, registrationid); 126 | 127 | if(_hasPrekeyid) 128 | out.writeUInt32(1, prekeyid); 129 | 130 | if(_hasSignedprekeyid) 131 | out.writeUInt32(6, signedprekeyid); 132 | 133 | if(_hasBasekey) 134 | out.writeBytes(2, basekey); 135 | 136 | if(_hasIdentitykey) 137 | out.writeBytes(3, identitykey); 138 | 139 | if(_hasMessage) 140 | out.writeBytes(4, message); 141 | 142 | } 143 | 144 | public final void deserialize(CodedInputStream in) throws IOException { 145 | while(true) { 146 | int tag = in.readTag(); 147 | switch(tag) { 148 | case 0: 149 | return; 150 | case 40: { 151 | registrationid = in.readUInt32(); 152 | _hasRegistrationid = true; 153 | break; } 154 | case 8: { 155 | prekeyid = in.readUInt32(); 156 | _hasPrekeyid = true; 157 | break; } 158 | case 48: { 159 | signedprekeyid = in.readUInt32(); 160 | _hasSignedprekeyid = true; 161 | break; } 162 | case 18: { 163 | basekey = in.readBytes(); 164 | _hasBasekey = true; 165 | break; } 166 | case 26: { 167 | identitykey = in.readBytes(); 168 | _hasIdentitykey = true; 169 | break; } 170 | case 34: { 171 | message = in.readBytes(); 172 | _hasMessage = true; 173 | break; } 174 | default: 175 | in.skipTag(tag); 176 | } 177 | } 178 | } 179 | 180 | public static PreKeyWhisperMessage fromBytes(byte[] in) throws EncodingException { 181 | PreKeyWhisperMessage message = new PreKeyWhisperMessage(); 182 | ProtoUtil.messageFromBytes(in, message); 183 | return message; 184 | } 185 | 186 | public byte[] toBytes() throws EncodingException { 187 | return ProtoUtil.messageToBytes(this); 188 | } 189 | 190 | 191 | } 192 | 193 | 194 | 195 | -------------------------------------------------------------------------------- /src/main/java/org/whispersystems/libaxolotl/protocol/protos/SenderKeyDistributionMessage.java: -------------------------------------------------------------------------------- 1 | package org.whispersystems.libaxolotl.protocol.protos; 2 | 3 | import java.util.Vector; 4 | import java.io.IOException; 5 | 6 | import com.ponderingpanda.protobuf.*; 7 | 8 | 9 | public class SenderKeyDistributionMessage implements Message { 10 | 11 | 12 | 13 | protected int id; // 1 14 | protected boolean _hasId; 15 | protected int iteration; // 2 16 | protected boolean _hasIteration; 17 | protected byte[] chainkey; // 3 18 | protected boolean _hasChainkey; 19 | protected byte[] signingkey; // 4 20 | protected boolean _hasSigningkey; 21 | 22 | public int getId() { 23 | return id; 24 | } 25 | 26 | public void setId(int id) { 27 | this.id = id; 28 | this._hasId = true; 29 | } 30 | 31 | public void clearId() { 32 | _hasId = false; 33 | } 34 | 35 | public boolean hasId() { 36 | return _hasId; 37 | } 38 | public int getIteration() { 39 | return iteration; 40 | } 41 | 42 | public void setIteration(int iteration) { 43 | this.iteration = iteration; 44 | this._hasIteration = true; 45 | } 46 | 47 | public void clearIteration() { 48 | _hasIteration = false; 49 | } 50 | 51 | public boolean hasIteration() { 52 | return _hasIteration; 53 | } 54 | public byte[] getChainkey() { 55 | return chainkey; 56 | } 57 | 58 | public void setChainkey(byte[] chainkey) { 59 | this.chainkey = chainkey; 60 | this._hasChainkey = true; 61 | } 62 | 63 | public void clearChainkey() { 64 | _hasChainkey = false; 65 | } 66 | 67 | public boolean hasChainkey() { 68 | return _hasChainkey; 69 | } 70 | public byte[] getSigningkey() { 71 | return signingkey; 72 | } 73 | 74 | public void setSigningkey(byte[] signingkey) { 75 | this.signingkey = signingkey; 76 | this._hasSigningkey = true; 77 | } 78 | 79 | public void clearSigningkey() { 80 | _hasSigningkey = false; 81 | } 82 | 83 | public boolean hasSigningkey() { 84 | return _hasSigningkey; 85 | } 86 | 87 | public final void serialize(CodedOutputStream out) throws IOException { 88 | if(_hasId) 89 | out.writeUInt32(1, id); 90 | 91 | if(_hasIteration) 92 | out.writeUInt32(2, iteration); 93 | 94 | if(_hasChainkey) 95 | out.writeBytes(3, chainkey); 96 | 97 | if(_hasSigningkey) 98 | out.writeBytes(4, signingkey); 99 | 100 | } 101 | 102 | public final void deserialize(CodedInputStream in) throws IOException { 103 | while(true) { 104 | int tag = in.readTag(); 105 | switch(tag) { 106 | case 0: 107 | return; 108 | case 8: { 109 | id = in.readUInt32(); 110 | _hasId = true; 111 | break; } 112 | case 16: { 113 | iteration = in.readUInt32(); 114 | _hasIteration = true; 115 | break; } 116 | case 26: { 117 | chainkey = in.readBytes(); 118 | _hasChainkey = true; 119 | break; } 120 | case 34: { 121 | signingkey = in.readBytes(); 122 | _hasSigningkey = true; 123 | break; } 124 | default: 125 | in.skipTag(tag); 126 | } 127 | } 128 | } 129 | 130 | public static SenderKeyDistributionMessage fromBytes(byte[] in) throws EncodingException { 131 | SenderKeyDistributionMessage message = new SenderKeyDistributionMessage(); 132 | ProtoUtil.messageFromBytes(in, message); 133 | return message; 134 | } 135 | 136 | public byte[] toBytes() throws EncodingException { 137 | return ProtoUtil.messageToBytes(this); 138 | } 139 | 140 | 141 | } 142 | 143 | 144 | 145 | -------------------------------------------------------------------------------- /src/main/java/org/whispersystems/libaxolotl/protocol/protos/SenderKeyMessage.java: -------------------------------------------------------------------------------- 1 | package org.whispersystems.libaxolotl.protocol.protos; 2 | 3 | import java.util.Vector; 4 | import java.io.IOException; 5 | 6 | import com.ponderingpanda.protobuf.*; 7 | 8 | 9 | public class SenderKeyMessage implements Message { 10 | 11 | 12 | 13 | protected int id; // 1 14 | protected boolean _hasId; 15 | protected int iteration; // 2 16 | protected boolean _hasIteration; 17 | protected byte[] ciphertext; // 3 18 | protected boolean _hasCiphertext; 19 | 20 | public int getId() { 21 | return id; 22 | } 23 | 24 | public void setId(int id) { 25 | this.id = id; 26 | this._hasId = true; 27 | } 28 | 29 | public void clearId() { 30 | _hasId = false; 31 | } 32 | 33 | public boolean hasId() { 34 | return _hasId; 35 | } 36 | public int getIteration() { 37 | return iteration; 38 | } 39 | 40 | public void setIteration(int iteration) { 41 | this.iteration = iteration; 42 | this._hasIteration = true; 43 | } 44 | 45 | public void clearIteration() { 46 | _hasIteration = false; 47 | } 48 | 49 | public boolean hasIteration() { 50 | return _hasIteration; 51 | } 52 | public byte[] getCiphertext() { 53 | return ciphertext; 54 | } 55 | 56 | public void setCiphertext(byte[] ciphertext) { 57 | this.ciphertext = ciphertext; 58 | this._hasCiphertext = true; 59 | } 60 | 61 | public void clearCiphertext() { 62 | _hasCiphertext = false; 63 | } 64 | 65 | public boolean hasCiphertext() { 66 | return _hasCiphertext; 67 | } 68 | 69 | public final void serialize(CodedOutputStream out) throws IOException { 70 | if(_hasId) 71 | out.writeUInt32(1, id); 72 | 73 | if(_hasIteration) 74 | out.writeUInt32(2, iteration); 75 | 76 | if(_hasCiphertext) 77 | out.writeBytes(3, ciphertext); 78 | 79 | } 80 | 81 | public final void deserialize(CodedInputStream in) throws IOException { 82 | while(true) { 83 | int tag = in.readTag(); 84 | switch(tag) { 85 | case 0: 86 | return; 87 | case 8: { 88 | id = in.readUInt32(); 89 | _hasId = true; 90 | break; } 91 | case 16: { 92 | iteration = in.readUInt32(); 93 | _hasIteration = true; 94 | break; } 95 | case 26: { 96 | ciphertext = in.readBytes(); 97 | _hasCiphertext = true; 98 | break; } 99 | default: 100 | in.skipTag(tag); 101 | } 102 | } 103 | } 104 | 105 | public static SenderKeyMessage fromBytes(byte[] in) throws EncodingException { 106 | SenderKeyMessage message = new SenderKeyMessage(); 107 | ProtoUtil.messageFromBytes(in, message); 108 | return message; 109 | } 110 | 111 | public byte[] toBytes() throws EncodingException { 112 | return ProtoUtil.messageToBytes(this); 113 | } 114 | 115 | 116 | } 117 | 118 | 119 | 120 | -------------------------------------------------------------------------------- /src/main/java/org/whispersystems/libaxolotl/protocol/protos/WhisperMessage.java: -------------------------------------------------------------------------------- 1 | package org.whispersystems.libaxolotl.protocol.protos; 2 | 3 | import java.util.Vector; 4 | import java.io.IOException; 5 | 6 | import com.ponderingpanda.protobuf.*; 7 | 8 | 9 | public class WhisperMessage implements Message { 10 | 11 | 12 | 13 | protected byte[] ratchetkey; // 1 14 | protected boolean _hasRatchetkey; 15 | protected int counter; // 2 16 | protected boolean _hasCounter; 17 | protected int previouscounter; // 3 18 | protected boolean _hasPreviouscounter; 19 | protected byte[] ciphertext; // 4 20 | protected boolean _hasCiphertext; 21 | 22 | public byte[] getRatchetkey() { 23 | return ratchetkey; 24 | } 25 | 26 | public void setRatchetkey(byte[] ratchetkey) { 27 | this.ratchetkey = ratchetkey; 28 | this._hasRatchetkey = true; 29 | } 30 | 31 | public void clearRatchetkey() { 32 | _hasRatchetkey = false; 33 | } 34 | 35 | public boolean hasRatchetkey() { 36 | return _hasRatchetkey; 37 | } 38 | public int getCounter() { 39 | return counter; 40 | } 41 | 42 | public void setCounter(int counter) { 43 | this.counter = counter; 44 | this._hasCounter = true; 45 | } 46 | 47 | public void clearCounter() { 48 | _hasCounter = false; 49 | } 50 | 51 | public boolean hasCounter() { 52 | return _hasCounter; 53 | } 54 | public int getPreviouscounter() { 55 | return previouscounter; 56 | } 57 | 58 | public void setPreviouscounter(int previouscounter) { 59 | this.previouscounter = previouscounter; 60 | this._hasPreviouscounter = true; 61 | } 62 | 63 | public void clearPreviouscounter() { 64 | _hasPreviouscounter = false; 65 | } 66 | 67 | public boolean hasPreviouscounter() { 68 | return _hasPreviouscounter; 69 | } 70 | public byte[] getCiphertext() { 71 | return ciphertext; 72 | } 73 | 74 | public void setCiphertext(byte[] ciphertext) { 75 | this.ciphertext = ciphertext; 76 | this._hasCiphertext = true; 77 | } 78 | 79 | public void clearCiphertext() { 80 | _hasCiphertext = false; 81 | } 82 | 83 | public boolean hasCiphertext() { 84 | return _hasCiphertext; 85 | } 86 | 87 | public final void serialize(CodedOutputStream out) throws IOException { 88 | if(_hasRatchetkey) 89 | out.writeBytes(1, ratchetkey); 90 | 91 | if(_hasCounter) 92 | out.writeUInt32(2, counter); 93 | 94 | if(_hasPreviouscounter) 95 | out.writeUInt32(3, previouscounter); 96 | 97 | if(_hasCiphertext) 98 | out.writeBytes(4, ciphertext); 99 | 100 | } 101 | 102 | public final void deserialize(CodedInputStream in) throws IOException { 103 | while(true) { 104 | int tag = in.readTag(); 105 | switch(tag) { 106 | case 0: 107 | return; 108 | case 10: { 109 | ratchetkey = in.readBytes(); 110 | _hasRatchetkey = true; 111 | break; } 112 | case 16: { 113 | counter = in.readUInt32(); 114 | _hasCounter = true; 115 | break; } 116 | case 24: { 117 | previouscounter = in.readUInt32(); 118 | _hasPreviouscounter = true; 119 | break; } 120 | case 34: { 121 | ciphertext = in.readBytes(); 122 | _hasCiphertext = true; 123 | break; } 124 | default: 125 | in.skipTag(tag); 126 | } 127 | } 128 | } 129 | 130 | public static WhisperMessage fromBytes(byte[] in) throws EncodingException { 131 | WhisperMessage message = new WhisperMessage(); 132 | ProtoUtil.messageFromBytes(in, message); 133 | return message; 134 | } 135 | 136 | public byte[] toBytes() throws EncodingException { 137 | return ProtoUtil.messageToBytes(this); 138 | } 139 | 140 | 141 | } 142 | 143 | 144 | 145 | -------------------------------------------------------------------------------- /src/main/java/org/whispersystems/libaxolotl/ratchet/AliceAxolotlParameters.java: -------------------------------------------------------------------------------- 1 | package org.whispersystems.libaxolotl.ratchet; 2 | 3 | import org.whispersystems.libaxolotl.IdentityKey; 4 | import org.whispersystems.libaxolotl.IdentityKeyPair; 5 | import org.whispersystems.libaxolotl.ecc.ECKeyPair; 6 | import org.whispersystems.libaxolotl.ecc.ECPublicKey; 7 | import org.whispersystems.libaxolotl.util.guava.Optional; 8 | 9 | public class AliceAxolotlParameters { 10 | 11 | private final IdentityKeyPair ourIdentityKey; 12 | private final ECKeyPair ourBaseKey; 13 | 14 | private final IdentityKey theirIdentityKey; 15 | private final ECPublicKey theirSignedPreKey; 16 | private final Optional theirOneTimePreKey; 17 | private final ECPublicKey theirRatchetKey; 18 | 19 | private AliceAxolotlParameters(IdentityKeyPair ourIdentityKey, ECKeyPair ourBaseKey, 20 | IdentityKey theirIdentityKey, ECPublicKey theirSignedPreKey, 21 | ECPublicKey theirRatchetKey, Optional theirOneTimePreKey) 22 | { 23 | this.ourIdentityKey = ourIdentityKey; 24 | this.ourBaseKey = ourBaseKey; 25 | this.theirIdentityKey = theirIdentityKey; 26 | this.theirSignedPreKey = theirSignedPreKey; 27 | this.theirRatchetKey = theirRatchetKey; 28 | this.theirOneTimePreKey = theirOneTimePreKey; 29 | 30 | if (ourIdentityKey == null || ourBaseKey == null || theirIdentityKey == null || 31 | theirSignedPreKey == null || theirRatchetKey == null || theirOneTimePreKey == null) 32 | { 33 | throw new IllegalArgumentException("Null values!"); 34 | } 35 | } 36 | 37 | public IdentityKeyPair getOurIdentityKey() { 38 | return ourIdentityKey; 39 | } 40 | 41 | public ECKeyPair getOurBaseKey() { 42 | return ourBaseKey; 43 | } 44 | 45 | public IdentityKey getTheirIdentityKey() { 46 | return theirIdentityKey; 47 | } 48 | 49 | public ECPublicKey getTheirSignedPreKey() { 50 | return theirSignedPreKey; 51 | } 52 | 53 | public Optional getTheirOneTimePreKey() { 54 | return theirOneTimePreKey; 55 | } 56 | 57 | public static Builder newBuilder() { 58 | return new Builder(); 59 | } 60 | 61 | public ECPublicKey getTheirRatchetKey() { 62 | return theirRatchetKey; 63 | } 64 | 65 | public static class Builder { 66 | private IdentityKeyPair ourIdentityKey; 67 | private ECKeyPair ourBaseKey; 68 | 69 | private IdentityKey theirIdentityKey; 70 | private ECPublicKey theirSignedPreKey; 71 | private ECPublicKey theirRatchetKey; 72 | private Optional theirOneTimePreKey; 73 | 74 | public Builder setOurIdentityKey(IdentityKeyPair ourIdentityKey) { 75 | this.ourIdentityKey = ourIdentityKey; 76 | return this; 77 | } 78 | 79 | public Builder setOurBaseKey(ECKeyPair ourBaseKey) { 80 | this.ourBaseKey = ourBaseKey; 81 | return this; 82 | } 83 | 84 | public Builder setTheirRatchetKey(ECPublicKey theirRatchetKey) { 85 | this.theirRatchetKey = theirRatchetKey; 86 | return this; 87 | } 88 | 89 | public Builder setTheirIdentityKey(IdentityKey theirIdentityKey) { 90 | this.theirIdentityKey = theirIdentityKey; 91 | return this; 92 | } 93 | 94 | public Builder setTheirSignedPreKey(ECPublicKey theirSignedPreKey) { 95 | this.theirSignedPreKey = theirSignedPreKey; 96 | return this; 97 | } 98 | 99 | public Builder setTheirOneTimePreKey(Optional theirOneTimePreKey) { 100 | this.theirOneTimePreKey = theirOneTimePreKey; 101 | return this; 102 | } 103 | 104 | public AliceAxolotlParameters create() { 105 | return new AliceAxolotlParameters(ourIdentityKey, ourBaseKey, theirIdentityKey, 106 | theirSignedPreKey, theirRatchetKey, theirOneTimePreKey); 107 | } 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /src/main/java/org/whispersystems/libaxolotl/ratchet/BobAxolotlParameters.java: -------------------------------------------------------------------------------- 1 | package org.whispersystems.libaxolotl.ratchet; 2 | 3 | import org.whispersystems.libaxolotl.IdentityKey; 4 | import org.whispersystems.libaxolotl.IdentityKeyPair; 5 | import org.whispersystems.libaxolotl.ecc.ECKeyPair; 6 | import org.whispersystems.libaxolotl.ecc.ECPublicKey; 7 | import org.whispersystems.libaxolotl.util.guava.Optional; 8 | 9 | public class BobAxolotlParameters { 10 | 11 | private final IdentityKeyPair ourIdentityKey; 12 | private final ECKeyPair ourSignedPreKey; 13 | private final Optional ourOneTimePreKey; 14 | private final ECKeyPair ourRatchetKey; 15 | 16 | private final IdentityKey theirIdentityKey; 17 | private final ECPublicKey theirBaseKey; 18 | 19 | BobAxolotlParameters(IdentityKeyPair ourIdentityKey, ECKeyPair ourSignedPreKey, 20 | ECKeyPair ourRatchetKey, Optional ourOneTimePreKey, 21 | IdentityKey theirIdentityKey, ECPublicKey theirBaseKey) 22 | { 23 | this.ourIdentityKey = ourIdentityKey; 24 | this.ourSignedPreKey = ourSignedPreKey; 25 | this.ourRatchetKey = ourRatchetKey; 26 | this.ourOneTimePreKey = ourOneTimePreKey; 27 | this.theirIdentityKey = theirIdentityKey; 28 | this.theirBaseKey = theirBaseKey; 29 | 30 | if (ourIdentityKey == null || ourSignedPreKey == null || ourRatchetKey == null || 31 | ourOneTimePreKey == null || theirIdentityKey == null || theirBaseKey == null) 32 | { 33 | throw new IllegalArgumentException("Null value!"); 34 | } 35 | } 36 | 37 | public IdentityKeyPair getOurIdentityKey() { 38 | return ourIdentityKey; 39 | } 40 | 41 | public ECKeyPair getOurSignedPreKey() { 42 | return ourSignedPreKey; 43 | } 44 | 45 | public Optional getOurOneTimePreKey() { 46 | return ourOneTimePreKey; 47 | } 48 | 49 | public IdentityKey getTheirIdentityKey() { 50 | return theirIdentityKey; 51 | } 52 | 53 | public ECPublicKey getTheirBaseKey() { 54 | return theirBaseKey; 55 | } 56 | 57 | public static Builder newBuilder() { 58 | return new Builder(); 59 | } 60 | 61 | public ECKeyPair getOurRatchetKey() { 62 | return ourRatchetKey; 63 | } 64 | 65 | public static class Builder { 66 | private IdentityKeyPair ourIdentityKey; 67 | private ECKeyPair ourSignedPreKey; 68 | private Optional ourOneTimePreKey; 69 | private ECKeyPair ourRatchetKey; 70 | 71 | private IdentityKey theirIdentityKey; 72 | private ECPublicKey theirBaseKey; 73 | 74 | public Builder setOurIdentityKey(IdentityKeyPair ourIdentityKey) { 75 | this.ourIdentityKey = ourIdentityKey; 76 | return this; 77 | } 78 | 79 | public Builder setOurSignedPreKey(ECKeyPair ourSignedPreKey) { 80 | this.ourSignedPreKey = ourSignedPreKey; 81 | return this; 82 | } 83 | 84 | public Builder setOurOneTimePreKey(Optional ourOneTimePreKey) { 85 | this.ourOneTimePreKey = ourOneTimePreKey; 86 | return this; 87 | } 88 | 89 | public Builder setTheirIdentityKey(IdentityKey theirIdentityKey) { 90 | this.theirIdentityKey = theirIdentityKey; 91 | return this; 92 | } 93 | 94 | public Builder setTheirBaseKey(ECPublicKey theirBaseKey) { 95 | this.theirBaseKey = theirBaseKey; 96 | return this; 97 | } 98 | 99 | public Builder setOurRatchetKey(ECKeyPair ourRatchetKey) { 100 | this.ourRatchetKey = ourRatchetKey; 101 | return this; 102 | } 103 | 104 | public BobAxolotlParameters create() { 105 | return new BobAxolotlParameters(ourIdentityKey, ourSignedPreKey, ourRatchetKey, 106 | ourOneTimePreKey, theirIdentityKey, theirBaseKey); 107 | } 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /src/main/java/org/whispersystems/libaxolotl/ratchet/ChainKey.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2014 Open Whisper Systems 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package org.whispersystems.libaxolotl.ratchet; 18 | 19 | 20 | import org.whispersystems.libaxolotl.j2me.jce.JmeSecurity; 21 | import org.whispersystems.libaxolotl.j2me.jce.mac.Mac; 22 | import org.whispersystems.libaxolotl.kdf.DerivedMessageSecrets; 23 | import org.whispersystems.libaxolotl.kdf.HKDF; 24 | 25 | public class ChainKey { 26 | 27 | private static final byte[] MESSAGE_KEY_SEED = {0x01}; 28 | private static final byte[] CHAIN_KEY_SEED = {0x02}; 29 | 30 | private final HKDF kdf; 31 | private final byte[] key; 32 | private final int index; 33 | 34 | public ChainKey(HKDF kdf, byte[] key, int index) { 35 | this.kdf = kdf; 36 | this.key = key; 37 | this.index = index; 38 | } 39 | 40 | public byte[] getKey() { 41 | return key; 42 | } 43 | 44 | public int getIndex() { 45 | return index; 46 | } 47 | 48 | public ChainKey getNextChainKey() { 49 | byte[] nextKey = getBaseMaterial(CHAIN_KEY_SEED); 50 | return new ChainKey(kdf, nextKey, index + 1); 51 | } 52 | 53 | public MessageKeys getMessageKeys() { 54 | byte[] inputKeyMaterial = getBaseMaterial(MESSAGE_KEY_SEED); 55 | byte[] keyMaterialBytes = kdf.deriveSecrets(inputKeyMaterial, "WhisperMessageKeys".getBytes(), DerivedMessageSecrets.SIZE); 56 | DerivedMessageSecrets keyMaterial = new DerivedMessageSecrets(keyMaterialBytes); 57 | 58 | return new MessageKeys(keyMaterial.getCipherKey(), keyMaterial.getMacKey(), keyMaterial.getIv(), index); 59 | } 60 | 61 | private byte[] getBaseMaterial(byte[] seed) { 62 | Mac mac = JmeSecurity.getProvider().createMacSha256(key); 63 | byte[] output = new byte[32]; 64 | 65 | mac.update(seed, 0, seed.length); 66 | mac.doFinal(output, 0); 67 | 68 | return output; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/main/java/org/whispersystems/libaxolotl/ratchet/MessageKeys.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2014 Open Whisper Systems 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package org.whispersystems.libaxolotl.ratchet; 18 | 19 | import org.bouncycastle.crypto.params.KeyParameter; 20 | import org.bouncycastle.crypto.params.ParametersWithIV; 21 | 22 | public class MessageKeys { 23 | 24 | private final KeyParameter cipherKey; 25 | private final KeyParameter macKey; 26 | private final ParametersWithIV iv; 27 | private final int counter; 28 | 29 | public MessageKeys(KeyParameter cipherKey, KeyParameter macKey, ParametersWithIV iv, int counter) { 30 | this.cipherKey = cipherKey; 31 | this.macKey = macKey; 32 | this.iv = iv; 33 | this.counter = counter; 34 | } 35 | 36 | public KeyParameter getCipherKey() { 37 | return cipherKey; 38 | } 39 | 40 | public KeyParameter getMacKey() { 41 | return macKey; 42 | } 43 | 44 | public ParametersWithIV getIv() { 45 | return iv; 46 | } 47 | 48 | public int getCounter() { 49 | return counter; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/org/whispersystems/libaxolotl/ratchet/RootKey.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2014 Open Whisper Systems 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package org.whispersystems.libaxolotl.ratchet; 18 | 19 | import org.whispersystems.libaxolotl.InvalidKeyException; 20 | import org.whispersystems.libaxolotl.ecc.Curve; 21 | import org.whispersystems.libaxolotl.ecc.ECKeyPair; 22 | import org.whispersystems.libaxolotl.ecc.ECPublicKey; 23 | import org.whispersystems.libaxolotl.kdf.DerivedRootSecrets; 24 | import org.whispersystems.libaxolotl.kdf.HKDF; 25 | import org.whispersystems.libaxolotl.util.Pair; 26 | 27 | public class RootKey { 28 | 29 | private final HKDF kdf; 30 | private final byte[] key; 31 | 32 | public RootKey(HKDF kdf, byte[] key) { 33 | this.kdf = kdf; 34 | this.key = key; 35 | } 36 | 37 | public byte[] getKeyBytes() { 38 | return key; 39 | } 40 | 41 | public Pair createChain(ECPublicKey theirRatchetKey, ECKeyPair ourRatchetKey) 42 | throws InvalidKeyException 43 | { 44 | byte[] sharedSecret = Curve.calculateAgreement(theirRatchetKey, ourRatchetKey.getPrivateKey()); 45 | byte[] derivedSecretBytes = kdf.deriveSecrets(sharedSecret, key, "WhisperRatchet".getBytes(), DerivedRootSecrets.SIZE); 46 | DerivedRootSecrets derivedSecrets = new DerivedRootSecrets(derivedSecretBytes); 47 | 48 | RootKey newRootKey = new RootKey(kdf, derivedSecrets.getRootKey()); 49 | ChainKey newChainKey = new ChainKey(kdf, derivedSecrets.getChainKey(), 0); 50 | 51 | return new Pair(newRootKey, newChainKey); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/org/whispersystems/libaxolotl/ratchet/SymmetricAxolotlParameters.java: -------------------------------------------------------------------------------- 1 | package org.whispersystems.libaxolotl.ratchet; 2 | 3 | import org.whispersystems.libaxolotl.IdentityKey; 4 | import org.whispersystems.libaxolotl.IdentityKeyPair; 5 | import org.whispersystems.libaxolotl.ecc.ECKeyPair; 6 | import org.whispersystems.libaxolotl.ecc.ECPublicKey; 7 | 8 | public class SymmetricAxolotlParameters { 9 | 10 | private final ECKeyPair ourBaseKey; 11 | private final ECKeyPair ourRatchetKey; 12 | private final IdentityKeyPair ourIdentityKey; 13 | 14 | private final ECPublicKey theirBaseKey; 15 | private final ECPublicKey theirRatchetKey; 16 | private final IdentityKey theirIdentityKey; 17 | 18 | SymmetricAxolotlParameters(ECKeyPair ourBaseKey, ECKeyPair ourRatchetKey, 19 | IdentityKeyPair ourIdentityKey, ECPublicKey theirBaseKey, 20 | ECPublicKey theirRatchetKey, IdentityKey theirIdentityKey) 21 | { 22 | this.ourBaseKey = ourBaseKey; 23 | this.ourRatchetKey = ourRatchetKey; 24 | this.ourIdentityKey = ourIdentityKey; 25 | this.theirBaseKey = theirBaseKey; 26 | this.theirRatchetKey = theirRatchetKey; 27 | this.theirIdentityKey = theirIdentityKey; 28 | 29 | if (ourBaseKey == null || ourRatchetKey == null || ourIdentityKey == null || 30 | theirBaseKey == null || theirRatchetKey == null || theirIdentityKey == null) 31 | { 32 | throw new IllegalArgumentException("Null values!"); 33 | } 34 | } 35 | 36 | public ECKeyPair getOurBaseKey() { 37 | return ourBaseKey; 38 | } 39 | 40 | public ECKeyPair getOurRatchetKey() { 41 | return ourRatchetKey; 42 | } 43 | 44 | public IdentityKeyPair getOurIdentityKey() { 45 | return ourIdentityKey; 46 | } 47 | 48 | public ECPublicKey getTheirBaseKey() { 49 | return theirBaseKey; 50 | } 51 | 52 | public ECPublicKey getTheirRatchetKey() { 53 | return theirRatchetKey; 54 | } 55 | 56 | public IdentityKey getTheirIdentityKey() { 57 | return theirIdentityKey; 58 | } 59 | 60 | public static Builder newBuilder() { 61 | return new Builder(); 62 | } 63 | 64 | public static class Builder { 65 | private ECKeyPair ourBaseKey; 66 | private ECKeyPair ourRatchetKey; 67 | private IdentityKeyPair ourIdentityKey; 68 | 69 | private ECPublicKey theirBaseKey; 70 | private ECPublicKey theirRatchetKey; 71 | private IdentityKey theirIdentityKey; 72 | 73 | public Builder setOurBaseKey(ECKeyPair ourBaseKey) { 74 | this.ourBaseKey = ourBaseKey; 75 | return this; 76 | } 77 | 78 | public Builder setOurRatchetKey(ECKeyPair ourRatchetKey) { 79 | this.ourRatchetKey = ourRatchetKey; 80 | return this; 81 | } 82 | 83 | public Builder setOurIdentityKey(IdentityKeyPair ourIdentityKey) { 84 | this.ourIdentityKey = ourIdentityKey; 85 | return this; 86 | } 87 | 88 | public Builder setTheirBaseKey(ECPublicKey theirBaseKey) { 89 | this.theirBaseKey = theirBaseKey; 90 | return this; 91 | } 92 | 93 | public Builder setTheirRatchetKey(ECPublicKey theirRatchetKey) { 94 | this.theirRatchetKey = theirRatchetKey; 95 | return this; 96 | } 97 | 98 | public Builder setTheirIdentityKey(IdentityKey theirIdentityKey) { 99 | this.theirIdentityKey = theirIdentityKey; 100 | return this; 101 | } 102 | 103 | public SymmetricAxolotlParameters create() { 104 | return new SymmetricAxolotlParameters(ourBaseKey, ourRatchetKey, ourIdentityKey, 105 | theirBaseKey, theirRatchetKey, theirIdentityKey); 106 | } 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /src/main/java/org/whispersystems/libaxolotl/state/AxolotlStore.java: -------------------------------------------------------------------------------- 1 | package org.whispersystems.libaxolotl.state; 2 | 3 | public interface AxolotlStore 4 | extends IdentityKeyStore, PreKeyStore, SessionStore, SignedPreKeyStore 5 | { 6 | } 7 | -------------------------------------------------------------------------------- /src/main/java/org/whispersystems/libaxolotl/state/IdentityKeyStore.java: -------------------------------------------------------------------------------- 1 | package org.whispersystems.libaxolotl.state; 2 | 3 | import org.whispersystems.libaxolotl.IdentityKey; 4 | import org.whispersystems.libaxolotl.IdentityKeyPair; 5 | 6 | /** 7 | * Provides an interface to identity information. 8 | * 9 | * @author Moxie Marlinspike 10 | */ 11 | public interface IdentityKeyStore { 12 | 13 | /** 14 | * Get the local client's identity key pair. 15 | * 16 | * @return The local client's persistent identity key pair. 17 | */ 18 | public IdentityKeyPair getIdentityKeyPair(); 19 | 20 | /** 21 | * Return the local client's registration ID. 22 | *

23 | * Clients should maintain a registration ID, a random number 24 | * between 1 and 16380 that's generated once at install time. 25 | * 26 | * @return the local client's registration ID. 27 | */ 28 | public int getLocalRegistrationId(); 29 | 30 | /** 31 | * Save a remote client's identity key 32 | *

33 | * Store a remote client's identity key as trusted. 34 | * 35 | * @param name The name of the remote client. 36 | * @param identityKey The remote client's identity key. 37 | */ 38 | public void saveIdentity(String name, IdentityKey identityKey); 39 | 40 | 41 | /** 42 | * Verify a remote client's identity key. 43 | *

44 | * Determine whether a remote client's identity is trusted. Convention is 45 | * that the TextSecure protocol is 'trust on first use.' This means that 46 | * an identity key is considered 'trusted' if there is no entry for the recipient 47 | * in the local store, or if it matches the saved key for a recipient in the local 48 | * store. Only if it mismatches an entry in the local store is it considered 49 | * 'untrusted.' 50 | * 51 | * @param name The name of the remote client. 52 | * @param identityKey The identity key to verify. 53 | * @return true if trusted, false if untrusted. 54 | */ 55 | public boolean isTrustedIdentity(String name, IdentityKey identityKey); 56 | 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/org/whispersystems/libaxolotl/state/PreKeyBundle.java: -------------------------------------------------------------------------------- 1 | package org.whispersystems.libaxolotl.state; 2 | 3 | import org.whispersystems.libaxolotl.IdentityKey; 4 | import org.whispersystems.libaxolotl.ecc.ECPublicKey; 5 | 6 | /** 7 | * A class that contains a remote PreKey and collection 8 | * of associated items. 9 | * 10 | * @author Moxie Marlinspike 11 | */ 12 | public class PreKeyBundle { 13 | 14 | private int registrationId; 15 | 16 | private int deviceId; 17 | 18 | private int preKeyId; 19 | private ECPublicKey preKeyPublic; 20 | 21 | private int signedPreKeyId; 22 | private ECPublicKey signedPreKeyPublic; 23 | private byte[] signedPreKeySignature; 24 | 25 | private IdentityKey identityKey; 26 | 27 | public PreKeyBundle(int registrationId, int deviceId, int preKeyId, ECPublicKey preKeyPublic, 28 | int signedPreKeyId, ECPublicKey signedPreKeyPublic, byte[] signedPreKeySignature, 29 | IdentityKey identityKey) 30 | { 31 | this.registrationId = registrationId; 32 | this.deviceId = deviceId; 33 | this.preKeyId = preKeyId; 34 | this.preKeyPublic = preKeyPublic; 35 | this.signedPreKeyId = signedPreKeyId; 36 | this.signedPreKeyPublic = signedPreKeyPublic; 37 | this.signedPreKeySignature = signedPreKeySignature; 38 | this.identityKey = identityKey; 39 | } 40 | 41 | /** 42 | * @return the device ID this PreKey belongs to. 43 | */ 44 | public int getDeviceId() { 45 | return deviceId; 46 | } 47 | 48 | /** 49 | * @return the unique key ID for this PreKey. 50 | */ 51 | public int getPreKeyId() { 52 | return preKeyId; 53 | } 54 | 55 | /** 56 | * @return the public key for this PreKey. 57 | */ 58 | public ECPublicKey getPreKey() { 59 | return preKeyPublic; 60 | } 61 | 62 | /** 63 | * @return the unique key ID for this signed prekey. 64 | */ 65 | public int getSignedPreKeyId() { 66 | return signedPreKeyId; 67 | } 68 | 69 | /** 70 | * @return the signed prekey for this PreKeyBundle. 71 | */ 72 | public ECPublicKey getSignedPreKey() { 73 | return signedPreKeyPublic; 74 | } 75 | 76 | /** 77 | * @return the signature over the signed prekey. 78 | */ 79 | public byte[] getSignedPreKeySignature() { 80 | return signedPreKeySignature; 81 | } 82 | 83 | /** 84 | * @return the {@link org.whispersystems.libaxolotl.IdentityKey} of this PreKeys owner. 85 | */ 86 | public IdentityKey getIdentityKey() { 87 | return identityKey; 88 | } 89 | 90 | /** 91 | * @return the registration ID associated with this PreKey. 92 | */ 93 | public int getRegistrationId() { 94 | return registrationId; 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/main/java/org/whispersystems/libaxolotl/state/PreKeyRecord.java: -------------------------------------------------------------------------------- 1 | package org.whispersystems.libaxolotl.state; 2 | 3 | 4 | import org.whispersystems.libaxolotl.InvalidKeyException; 5 | import org.whispersystems.libaxolotl.ecc.Curve; 6 | import org.whispersystems.libaxolotl.ecc.ECKeyPair; 7 | import org.whispersystems.libaxolotl.ecc.ECPrivateKey; 8 | import org.whispersystems.libaxolotl.ecc.ECPublicKey; 9 | import org.whispersystems.libaxolotl.state.protos.PreKeyRecordStructure; 10 | import org.whispersystems.libaxolotl.j2me.AssertionError; 11 | 12 | import java.io.IOException; 13 | 14 | 15 | public class PreKeyRecord { 16 | 17 | private PreKeyRecordStructure structure; 18 | 19 | public PreKeyRecord(int id, ECKeyPair keyPair) { 20 | this.structure = new PreKeyRecordStructure(); 21 | this.structure.setId(id); 22 | this.structure.setPublickey(keyPair.getPublicKey().serialize()); 23 | this.structure.setPrivatekey(keyPair.getPrivateKey().serialize()); 24 | } 25 | 26 | public PreKeyRecord(byte[] serialized) throws IOException { 27 | this.structure = PreKeyRecordStructure.fromBytes(serialized); 28 | } 29 | 30 | public int getId() { 31 | return this.structure.getId(); 32 | } 33 | 34 | public ECKeyPair getKeyPair() { 35 | try { 36 | ECPublicKey publicKey = Curve.decodePoint (this.structure.getPublickey (), 0); 37 | ECPrivateKey privateKey = Curve.decodePrivatePoint(this.structure.getPrivatekey() ); 38 | 39 | return new ECKeyPair(publicKey, privateKey); 40 | } catch (InvalidKeyException e) { 41 | throw new AssertionError(e); 42 | } 43 | } 44 | 45 | public byte[] serialize() { 46 | return this.structure.toBytes(); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/org/whispersystems/libaxolotl/state/PreKeyStore.java: -------------------------------------------------------------------------------- 1 | package org.whispersystems.libaxolotl.state; 2 | 3 | import org.whispersystems.libaxolotl.InvalidKeyIdException; 4 | 5 | /** 6 | * An interface describing the local storage of {@link PreKeyRecord}s. 7 | * 8 | * @author Moxie Marlinspike 9 | */ 10 | public interface PreKeyStore { 11 | 12 | /** 13 | * Load a local PreKeyRecord. 14 | * 15 | * @param preKeyId the ID of the local PreKeyRecord. 16 | * @return the corresponding PreKeyRecord. 17 | * @throws InvalidKeyIdException when there is no corresponding PreKeyRecord. 18 | */ 19 | public PreKeyRecord loadPreKey(int preKeyId) throws InvalidKeyIdException; 20 | 21 | /** 22 | * Store a local PreKeyRecord. 23 | * 24 | * @param preKeyId the ID of the PreKeyRecord to store. 25 | * @param record the PreKeyRecord. 26 | */ 27 | public void storePreKey(int preKeyId, PreKeyRecord record); 28 | 29 | /** 30 | * @param preKeyId A PreKeyRecord ID. 31 | * @return true if the store has a record for the preKeyId, otherwise false. 32 | */ 33 | public boolean containsPreKey(int preKeyId); 34 | 35 | /** 36 | * Delete a PreKeyRecord from local storage. 37 | * 38 | * @param preKeyId The ID of the PreKeyRecord to remove. 39 | */ 40 | public void removePreKey(int preKeyId); 41 | 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/org/whispersystems/libaxolotl/state/SessionRecord.java: -------------------------------------------------------------------------------- 1 | package org.whispersystems.libaxolotl.state; 2 | 3 | import org.whispersystems.libaxolotl.state.protos.RecordStructure; 4 | import org.whispersystems.libaxolotl.state.protos.SessionStructure; 5 | import org.whispersystems.libaxolotl.j2me.Arrays; 6 | 7 | import java.io.IOException; 8 | import java.util.Vector; 9 | 10 | 11 | /** 12 | * A SessionRecord encapsulates the state of an ongoing session. 13 | * 14 | * @author Moxie Marlinspike 15 | */ 16 | public class SessionRecord { 17 | 18 | private static final int ARCHIVED_STATES_MAX_LENGTH = 40; 19 | 20 | private SessionState sessionState = new SessionState(); 21 | private Vector previousStates = new Vector(); 22 | private boolean fresh = false; 23 | 24 | public SessionRecord() { 25 | this.fresh = true; 26 | } 27 | 28 | public SessionRecord(SessionState sessionState) { 29 | this.sessionState = sessionState; 30 | this.fresh = false; 31 | } 32 | 33 | public SessionRecord(byte[] serialized) throws IOException { 34 | RecordStructure record = RecordStructure.fromBytes(serialized); 35 | this.sessionState = new SessionState(record.getCurrentsession()); 36 | this.fresh = false; 37 | 38 | for (int i=0;i ARCHIVED_STATES_MAX_LENGTH) { 92 | previousStates.removeElementAt(previousStates.size() - 1); 93 | } 94 | } 95 | 96 | public void setState(SessionState sessionState) { 97 | this.sessionState = sessionState; 98 | } 99 | 100 | /** 101 | * @return a serialized version of the current SessionRecord. 102 | */ 103 | public byte[] serialize() { 104 | RecordStructure record = new RecordStructure(); 105 | record.setCurrentsession(sessionState.getStructure()); 106 | 107 | for (int i=0;i 19 | * It is important that implementations return a copy of the current durable information. The 20 | * returned SessionRecord may be modified, but those changes should not have an effect on the 21 | * durable session state (what is returned by subsequent calls to this method) without the 22 | * store method being called here first. 23 | * 24 | * @param address The name and device ID of the remote client. 25 | * @return a copy of the SessionRecord corresponding to the recipientId + deviceId tuple, or 26 | * a new SessionRecord if one does not currently exist. 27 | */ 28 | public SessionRecord loadSession(AxolotlAddress address); 29 | 30 | /** 31 | * Returns all known devices with active sessions for a recipient 32 | * 33 | * @param name the name of the client. 34 | * @return all known sub-devices with active sessions. 35 | */ 36 | public Vector getSubDeviceSessions(String name); 37 | 38 | /** 39 | * Commit to storage the {@link SessionRecord} for a given recipientId + deviceId tuple. 40 | * @param address the address of the remote client. 41 | * @param record the current SessionRecord for the remote client. 42 | */ 43 | public void storeSession(AxolotlAddress address, SessionRecord record); 44 | 45 | /** 46 | * Determine whether there is a committed {@link SessionRecord} for a recipientId + deviceId tuple. 47 | * @param address the address of the remote client. 48 | * @return true if a {@link SessionRecord} exists, false otherwise. 49 | */ 50 | public boolean containsSession(AxolotlAddress address); 51 | 52 | /** 53 | * Remove a {@link SessionRecord} for a recipientId + deviceId tuple. 54 | * 55 | * @param address the address of the remote client. 56 | */ 57 | public void deleteSession(AxolotlAddress address); 58 | 59 | /** 60 | * Remove the {@link SessionRecord}s corresponding to all devices of a recipientId. 61 | * 62 | * @param name the name of the remote client. 63 | */ 64 | public void deleteAllSessions(String name); 65 | 66 | } 67 | -------------------------------------------------------------------------------- /src/main/java/org/whispersystems/libaxolotl/state/SignedPreKeyRecord.java: -------------------------------------------------------------------------------- 1 | package org.whispersystems.libaxolotl.state; 2 | 3 | import org.whispersystems.libaxolotl.InvalidKeyException; 4 | import org.whispersystems.libaxolotl.ecc.Curve; 5 | import org.whispersystems.libaxolotl.ecc.ECKeyPair; 6 | import org.whispersystems.libaxolotl.ecc.ECPrivateKey; 7 | import org.whispersystems.libaxolotl.ecc.ECPublicKey; 8 | import org.whispersystems.libaxolotl.state.protos.SignedPreKeyRecordStructure; 9 | import org.whispersystems.libaxolotl.j2me.AssertionError; 10 | 11 | import java.io.IOException; 12 | 13 | 14 | public class SignedPreKeyRecord { 15 | 16 | private SignedPreKeyRecordStructure structure; 17 | 18 | public SignedPreKeyRecord(int id, long timestamp, ECKeyPair keyPair, byte[] signature) { 19 | this.structure = new SignedPreKeyRecordStructure(); 20 | structure.setId(id); 21 | structure.setPublickey(keyPair.getPublicKey().serialize()); 22 | structure.setPrivatekey(keyPair.getPrivateKey().serialize()); 23 | structure.setSignature(signature); 24 | structure.setTimestamp(timestamp); 25 | } 26 | 27 | public SignedPreKeyRecord(byte[] serialized) throws IOException { 28 | this.structure = SignedPreKeyRecordStructure.fromBytes(serialized); 29 | } 30 | 31 | public int getId() { 32 | return this.structure.getId(); 33 | } 34 | 35 | public long getTimestamp() { 36 | return this.structure.getTimestamp(); 37 | } 38 | 39 | public ECKeyPair getKeyPair() { 40 | try { 41 | ECPublicKey publicKey = Curve.decodePoint(this.structure.getPublickey(), 0); 42 | ECPrivateKey privateKey = Curve.decodePrivatePoint(this.structure.getPrivatekey()); 43 | 44 | return new ECKeyPair(publicKey, privateKey); 45 | } catch (InvalidKeyException e) { 46 | throw new AssertionError(e); 47 | } 48 | } 49 | 50 | public byte[] getSignature() { 51 | return this.structure.getSignature(); 52 | } 53 | 54 | public byte[] serialize() { 55 | return this.structure.toBytes(); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/org/whispersystems/libaxolotl/state/SignedPreKeyStore.java: -------------------------------------------------------------------------------- 1 | package org.whispersystems.libaxolotl.state; 2 | 3 | import org.whispersystems.libaxolotl.InvalidKeyIdException; 4 | 5 | import java.util.Vector; 6 | 7 | public interface SignedPreKeyStore { 8 | 9 | 10 | /** 11 | * Load a local SignedPreKeyRecord. 12 | * 13 | * @param signedPreKeyId the ID of the local SignedPreKeyRecord. 14 | * @return the corresponding SignedPreKeyRecord. 15 | * @throws InvalidKeyIdException when there is no corresponding SignedPreKeyRecord. 16 | */ 17 | public SignedPreKeyRecord loadSignedPreKey(int signedPreKeyId) throws InvalidKeyIdException; 18 | 19 | /** 20 | * Load all local SignedPreKeyRecords. 21 | * 22 | * @return All stored SignedPreKeyRecords. 23 | */ 24 | public Vector loadSignedPreKeys(); 25 | 26 | /** 27 | * Store a local SignedPreKeyRecord. 28 | * 29 | * @param signedPreKeyId the ID of the SignedPreKeyRecord to store. 30 | * @param record the SignedPreKeyRecord. 31 | */ 32 | public void storeSignedPreKey(int signedPreKeyId, SignedPreKeyRecord record); 33 | 34 | /** 35 | * @param signedPreKeyId A SignedPreKeyRecord ID. 36 | * @return true if the store has a record for the signedPreKeyId, otherwise false. 37 | */ 38 | public boolean containsSignedPreKey(int signedPreKeyId); 39 | 40 | /** 41 | * Delete a SignedPreKeyRecord from local storage. 42 | * 43 | * @param signedPreKeyId The ID of the SignedPreKeyRecord to remove. 44 | */ 45 | public void removeSignedPreKey(int signedPreKeyId); 46 | 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/org/whispersystems/libaxolotl/state/protos/IdentityKeyPairStructure.java: -------------------------------------------------------------------------------- 1 | package org.whispersystems.libaxolotl.state.protos; 2 | 3 | import java.util.Vector; 4 | import java.io.IOException; 5 | 6 | import com.ponderingpanda.protobuf.*; 7 | 8 | 9 | public class IdentityKeyPairStructure implements Message { 10 | 11 | 12 | 13 | protected byte[] publickey; // 1 14 | protected boolean _hasPublickey; 15 | protected byte[] privatekey; // 2 16 | protected boolean _hasPrivatekey; 17 | 18 | public byte[] getPublickey() { 19 | return publickey; 20 | } 21 | 22 | public void setPublickey(byte[] publickey) { 23 | this.publickey = publickey; 24 | this._hasPublickey = true; 25 | } 26 | 27 | public void clearPublickey() { 28 | _hasPublickey = false; 29 | } 30 | 31 | public boolean hasPublickey() { 32 | return _hasPublickey; 33 | } 34 | public byte[] getPrivatekey() { 35 | return privatekey; 36 | } 37 | 38 | public void setPrivatekey(byte[] privatekey) { 39 | this.privatekey = privatekey; 40 | this._hasPrivatekey = true; 41 | } 42 | 43 | public void clearPrivatekey() { 44 | _hasPrivatekey = false; 45 | } 46 | 47 | public boolean hasPrivatekey() { 48 | return _hasPrivatekey; 49 | } 50 | 51 | public final void serialize(CodedOutputStream out) throws IOException { 52 | if(_hasPublickey) 53 | out.writeBytes(1, publickey); 54 | 55 | if(_hasPrivatekey) 56 | out.writeBytes(2, privatekey); 57 | 58 | } 59 | 60 | public final void deserialize(CodedInputStream in) throws IOException { 61 | while(true) { 62 | int tag = in.readTag(); 63 | switch(tag) { 64 | case 0: 65 | return; 66 | case 10: { 67 | publickey = in.readBytes(); 68 | _hasPublickey = true; 69 | break; } 70 | case 18: { 71 | privatekey = in.readBytes(); 72 | _hasPrivatekey = true; 73 | break; } 74 | default: 75 | in.skipTag(tag); 76 | } 77 | } 78 | } 79 | 80 | public static IdentityKeyPairStructure fromBytes(byte[] in) throws EncodingException { 81 | IdentityKeyPairStructure message = new IdentityKeyPairStructure(); 82 | ProtoUtil.messageFromBytes(in, message); 83 | return message; 84 | } 85 | 86 | public byte[] toBytes() throws EncodingException { 87 | return ProtoUtil.messageToBytes(this); 88 | } 89 | 90 | 91 | } 92 | 93 | 94 | 95 | -------------------------------------------------------------------------------- /src/main/java/org/whispersystems/libaxolotl/state/protos/PreKeyRecordStructure.java: -------------------------------------------------------------------------------- 1 | package org.whispersystems.libaxolotl.state.protos; 2 | 3 | import java.util.Vector; 4 | import java.io.IOException; 5 | 6 | import com.ponderingpanda.protobuf.*; 7 | 8 | 9 | public class PreKeyRecordStructure implements Message { 10 | 11 | 12 | 13 | protected int id; // 1 14 | protected boolean _hasId; 15 | protected byte[] publickey; // 2 16 | protected boolean _hasPublickey; 17 | protected byte[] privatekey; // 3 18 | protected boolean _hasPrivatekey; 19 | 20 | public int getId() { 21 | return id; 22 | } 23 | 24 | public void setId(int id) { 25 | this.id = id; 26 | this._hasId = true; 27 | } 28 | 29 | public void clearId() { 30 | _hasId = false; 31 | } 32 | 33 | public boolean hasId() { 34 | return _hasId; 35 | } 36 | public byte[] getPublickey() { 37 | return publickey; 38 | } 39 | 40 | public void setPublickey(byte[] publickey) { 41 | this.publickey = publickey; 42 | this._hasPublickey = true; 43 | } 44 | 45 | public void clearPublickey() { 46 | _hasPublickey = false; 47 | } 48 | 49 | public boolean hasPublickey() { 50 | return _hasPublickey; 51 | } 52 | public byte[] getPrivatekey() { 53 | return privatekey; 54 | } 55 | 56 | public void setPrivatekey(byte[] privatekey) { 57 | this.privatekey = privatekey; 58 | this._hasPrivatekey = true; 59 | } 60 | 61 | public void clearPrivatekey() { 62 | _hasPrivatekey = false; 63 | } 64 | 65 | public boolean hasPrivatekey() { 66 | return _hasPrivatekey; 67 | } 68 | 69 | public final void serialize(CodedOutputStream out) throws IOException { 70 | if(_hasId) 71 | out.writeUInt32(1, id); 72 | 73 | if(_hasPublickey) 74 | out.writeBytes(2, publickey); 75 | 76 | if(_hasPrivatekey) 77 | out.writeBytes(3, privatekey); 78 | 79 | } 80 | 81 | public final void deserialize(CodedInputStream in) throws IOException { 82 | while(true) { 83 | int tag = in.readTag(); 84 | switch(tag) { 85 | case 0: 86 | return; 87 | case 8: { 88 | id = in.readUInt32(); 89 | _hasId = true; 90 | break; } 91 | case 18: { 92 | publickey = in.readBytes(); 93 | _hasPublickey = true; 94 | break; } 95 | case 26: { 96 | privatekey = in.readBytes(); 97 | _hasPrivatekey = true; 98 | break; } 99 | default: 100 | in.skipTag(tag); 101 | } 102 | } 103 | } 104 | 105 | public static PreKeyRecordStructure fromBytes(byte[] in) throws EncodingException { 106 | PreKeyRecordStructure message = new PreKeyRecordStructure(); 107 | ProtoUtil.messageFromBytes(in, message); 108 | return message; 109 | } 110 | 111 | public byte[] toBytes() throws EncodingException { 112 | return ProtoUtil.messageToBytes(this); 113 | } 114 | 115 | 116 | } 117 | 118 | 119 | 120 | -------------------------------------------------------------------------------- /src/main/java/org/whispersystems/libaxolotl/state/protos/RecordStructure.java: -------------------------------------------------------------------------------- 1 | package org.whispersystems.libaxolotl.state.protos; 2 | 3 | import java.util.Vector; 4 | import java.io.IOException; 5 | 6 | import com.ponderingpanda.protobuf.*; 7 | 8 | 9 | public class RecordStructure implements Message { 10 | 11 | 12 | 13 | protected SessionStructure currentsession; // 1 14 | protected Vector previoussessions = new Vector(); // 2 15 | 16 | public SessionStructure getCurrentsession() { 17 | return currentsession; 18 | } 19 | 20 | public void setCurrentsession(SessionStructure currentsession) { 21 | this.currentsession = currentsession; 22 | } 23 | 24 | public void clearCurrentsession() { 25 | currentsession = null; 26 | } 27 | 28 | public boolean hasCurrentsession() { 29 | return currentsession != null; 30 | } 31 | public void addPrevioussessions(SessionStructure value) { 32 | this.previoussessions.addElement(value); 33 | } 34 | 35 | public int getPrevioussessionsCount() { 36 | return this.previoussessions.size(); 37 | } 38 | 39 | public SessionStructure getPrevioussessions(int index) { 40 | return (SessionStructure)this.previoussessions.elementAt(index); 41 | } 42 | 43 | public Vector getPrevioussessionsVector() { 44 | return this.previoussessions; 45 | } 46 | 47 | public void setPrevioussessionsVector(Vector value) { 48 | this.previoussessions = value; 49 | } 50 | 51 | public final void serialize(CodedOutputStream out) throws IOException { 52 | out.writeMessage(1, currentsession); 53 | 54 | for(int i = 0; i < getPrevioussessionsCount(); i++) { 55 | out.writeMessage(2, getPrevioussessions(i)); 56 | } 57 | 58 | } 59 | 60 | public final void deserialize(CodedInputStream in) throws IOException { 61 | while(true) { 62 | int tag = in.readTag(); 63 | switch(tag) { 64 | case 0: 65 | return; 66 | case 10: { 67 | currentsession = new SessionStructure(); 68 | in.readMessage(currentsession); 69 | break; } 70 | case 18: { 71 | SessionStructure message = new SessionStructure(); 72 | in.readMessage(message); 73 | addPrevioussessions(message); 74 | break; } 75 | default: 76 | in.skipTag(tag); 77 | } 78 | } 79 | } 80 | 81 | public static RecordStructure fromBytes(byte[] in) throws EncodingException { 82 | RecordStructure message = new RecordStructure(); 83 | ProtoUtil.messageFromBytes(in, message); 84 | return message; 85 | } 86 | 87 | public byte[] toBytes() throws EncodingException { 88 | return ProtoUtil.messageToBytes(this); 89 | } 90 | 91 | 92 | } 93 | 94 | 95 | 96 | -------------------------------------------------------------------------------- /src/main/java/org/whispersystems/libaxolotl/state/protos/SenderKeyRecordStructure.java: -------------------------------------------------------------------------------- 1 | package org.whispersystems.libaxolotl.state.protos; 2 | 3 | import java.util.Vector; 4 | import java.io.IOException; 5 | 6 | import com.ponderingpanda.protobuf.*; 7 | 8 | 9 | public class SenderKeyRecordStructure implements Message { 10 | 11 | 12 | 13 | protected Vector senderkeystates = new Vector(); // 1 14 | 15 | public void addSenderkeystates(SenderKeyStateStructure value) { 16 | this.senderkeystates.addElement(value); 17 | } 18 | 19 | public int getSenderkeystatesCount() { 20 | return this.senderkeystates.size(); 21 | } 22 | 23 | public SenderKeyStateStructure getSenderkeystates(int index) { 24 | return (SenderKeyStateStructure)this.senderkeystates.elementAt(index); 25 | } 26 | 27 | public Vector getSenderkeystatesVector() { 28 | return this.senderkeystates; 29 | } 30 | 31 | public void setSenderkeystatesVector(Vector value) { 32 | this.senderkeystates = value; 33 | } 34 | 35 | public final void serialize(CodedOutputStream out) throws IOException { 36 | for(int i = 0; i < getSenderkeystatesCount(); i++) { 37 | out.writeMessage(1, getSenderkeystates(i)); 38 | } 39 | 40 | } 41 | 42 | public final void deserialize(CodedInputStream in) throws IOException { 43 | while(true) { 44 | int tag = in.readTag(); 45 | switch(tag) { 46 | case 0: 47 | return; 48 | case 10: { 49 | SenderKeyStateStructure message = new SenderKeyStateStructure(); 50 | in.readMessage(message); 51 | addSenderkeystates(message); 52 | break; } 53 | default: 54 | in.skipTag(tag); 55 | } 56 | } 57 | } 58 | 59 | public static SenderKeyRecordStructure fromBytes(byte[] in) throws EncodingException { 60 | SenderKeyRecordStructure message = new SenderKeyRecordStructure(); 61 | ProtoUtil.messageFromBytes(in, message); 62 | return message; 63 | } 64 | 65 | public byte[] toBytes() throws EncodingException { 66 | return ProtoUtil.messageToBytes(this); 67 | } 68 | 69 | 70 | } 71 | 72 | 73 | 74 | -------------------------------------------------------------------------------- /src/main/java/org/whispersystems/libaxolotl/state/protos/SignedPreKeyRecordStructure.java: -------------------------------------------------------------------------------- 1 | package org.whispersystems.libaxolotl.state.protos; 2 | 3 | import java.util.Vector; 4 | import java.io.IOException; 5 | 6 | import com.ponderingpanda.protobuf.*; 7 | 8 | 9 | public class SignedPreKeyRecordStructure implements Message { 10 | 11 | 12 | 13 | protected int id; // 1 14 | protected boolean _hasId; 15 | protected byte[] publickey; // 2 16 | protected boolean _hasPublickey; 17 | protected byte[] privatekey; // 3 18 | protected boolean _hasPrivatekey; 19 | protected byte[] signature; // 4 20 | protected boolean _hasSignature; 21 | protected long timestamp; // 5 22 | protected boolean _hasTimestamp; 23 | 24 | public int getId() { 25 | return id; 26 | } 27 | 28 | public void setId(int id) { 29 | this.id = id; 30 | this._hasId = true; 31 | } 32 | 33 | public void clearId() { 34 | _hasId = false; 35 | } 36 | 37 | public boolean hasId() { 38 | return _hasId; 39 | } 40 | public byte[] getPublickey() { 41 | return publickey; 42 | } 43 | 44 | public void setPublickey(byte[] publickey) { 45 | this.publickey = publickey; 46 | this._hasPublickey = true; 47 | } 48 | 49 | public void clearPublickey() { 50 | _hasPublickey = false; 51 | } 52 | 53 | public boolean hasPublickey() { 54 | return _hasPublickey; 55 | } 56 | public byte[] getPrivatekey() { 57 | return privatekey; 58 | } 59 | 60 | public void setPrivatekey(byte[] privatekey) { 61 | this.privatekey = privatekey; 62 | this._hasPrivatekey = true; 63 | } 64 | 65 | public void clearPrivatekey() { 66 | _hasPrivatekey = false; 67 | } 68 | 69 | public boolean hasPrivatekey() { 70 | return _hasPrivatekey; 71 | } 72 | public byte[] getSignature() { 73 | return signature; 74 | } 75 | 76 | public void setSignature(byte[] signature) { 77 | this.signature = signature; 78 | this._hasSignature = true; 79 | } 80 | 81 | public void clearSignature() { 82 | _hasSignature = false; 83 | } 84 | 85 | public boolean hasSignature() { 86 | return _hasSignature; 87 | } 88 | public long getTimestamp() { 89 | return timestamp; 90 | } 91 | 92 | public void setTimestamp(long timestamp) { 93 | this.timestamp = timestamp; 94 | this._hasTimestamp = true; 95 | } 96 | 97 | public void clearTimestamp() { 98 | _hasTimestamp = false; 99 | } 100 | 101 | public boolean hasTimestamp() { 102 | return _hasTimestamp; 103 | } 104 | 105 | public final void serialize(CodedOutputStream out) throws IOException { 106 | if(_hasId) 107 | out.writeUInt32(1, id); 108 | 109 | if(_hasPublickey) 110 | out.writeBytes(2, publickey); 111 | 112 | if(_hasPrivatekey) 113 | out.writeBytes(3, privatekey); 114 | 115 | if(_hasSignature) 116 | out.writeBytes(4, signature); 117 | 118 | if(_hasTimestamp) 119 | out.writeFixed64(5, timestamp); 120 | 121 | } 122 | 123 | public final void deserialize(CodedInputStream in) throws IOException { 124 | while(true) { 125 | int tag = in.readTag(); 126 | switch(tag) { 127 | case 0: 128 | return; 129 | case 8: { 130 | id = in.readUInt32(); 131 | _hasId = true; 132 | break; } 133 | case 18: { 134 | publickey = in.readBytes(); 135 | _hasPublickey = true; 136 | break; } 137 | case 26: { 138 | privatekey = in.readBytes(); 139 | _hasPrivatekey = true; 140 | break; } 141 | case 34: { 142 | signature = in.readBytes(); 143 | _hasSignature = true; 144 | break; } 145 | case 41: { 146 | timestamp = in.readFixed64(); 147 | _hasTimestamp = true; 148 | break; } 149 | default: 150 | in.skipTag(tag); 151 | } 152 | } 153 | } 154 | 155 | public static SignedPreKeyRecordStructure fromBytes(byte[] in) throws EncodingException { 156 | SignedPreKeyRecordStructure message = new SignedPreKeyRecordStructure(); 157 | ProtoUtil.messageFromBytes(in, message); 158 | return message; 159 | } 160 | 161 | public byte[] toBytes() throws EncodingException { 162 | return ProtoUtil.messageToBytes(this); 163 | } 164 | 165 | 166 | } 167 | 168 | 169 | 170 | -------------------------------------------------------------------------------- /src/main/java/org/whispersystems/libaxolotl/util/Hex.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2014 Open Whisper Systems 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | package org.whispersystems.libaxolotl.util; 18 | 19 | import java.io.IOException; 20 | 21 | /** 22 | * Utility for generating hex dumps. 23 | */ 24 | public class Hex { 25 | 26 | private final static char[] HEX_DIGITS = { 27 | '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' 28 | }; 29 | 30 | public static String toString(byte[] bytes) { 31 | return toString(bytes, 0, bytes.length); 32 | } 33 | 34 | public static String toString(byte[] bytes, int offset, int length) { 35 | StringBuffer buf = new StringBuffer(); 36 | for (int i = 0; i < length; i++) { 37 | appendHexChar(buf, bytes[offset + i]); 38 | buf.append(" "); 39 | } 40 | return buf.toString(); 41 | } 42 | 43 | public static String toStringCondensed(byte[] bytes) { 44 | StringBuffer buf = new StringBuffer(); 45 | for (int i=0;i> 1]; 60 | 61 | for (int i = 0, j = 0; j < len; i++) { 62 | int f = Character.digit(data[j], 16) << 4; 63 | j++; 64 | f = f | Character.digit(data[j], 16); 65 | j++; 66 | out[i] = (byte) (f & 0xFF); 67 | } 68 | 69 | return out; 70 | } 71 | 72 | private static void appendHexChar(StringBuffer buf, int b) { 73 | buf.append(HEX_DIGITS[(b >> 4) & 0xf]); 74 | buf.append(HEX_DIGITS[b & 0xf]); 75 | } 76 | 77 | } 78 | -------------------------------------------------------------------------------- /src/main/java/org/whispersystems/libaxolotl/util/KeyHelper.java: -------------------------------------------------------------------------------- 1 | package org.whispersystems.libaxolotl.util; 2 | 3 | import org.whispersystems.curve25519.SecureRandomProvider; 4 | import org.whispersystems.libaxolotl.IdentityKey; 5 | import org.whispersystems.libaxolotl.IdentityKeyPair; 6 | import org.whispersystems.libaxolotl.InvalidKeyException; 7 | import org.whispersystems.libaxolotl.ecc.Curve; 8 | import org.whispersystems.libaxolotl.ecc.ECKeyPair; 9 | import org.whispersystems.libaxolotl.state.PreKeyRecord; 10 | import org.whispersystems.libaxolotl.state.SignedPreKeyRecord; 11 | 12 | import java.util.Vector; 13 | 14 | /** 15 | * Helper class for generating keys of different types. 16 | * 17 | * @author Moxie Marlinspike 18 | */ 19 | public class KeyHelper { 20 | 21 | private KeyHelper() {} 22 | 23 | /** 24 | * Generate an identity key pair. Clients should only do this once, 25 | * at install time. 26 | * 27 | * @return the generated IdentityKeyPair. 28 | */ 29 | public static IdentityKeyPair generateIdentityKeyPair(SecureRandomProvider secureRandom) { 30 | ECKeyPair keyPair = Curve.generateKeyPair(secureRandom); 31 | IdentityKey publicKey = new IdentityKey(keyPair.getPublicKey()); 32 | return new IdentityKeyPair(publicKey, keyPair.getPrivateKey()); 33 | } 34 | 35 | /** 36 | * Generate a registration ID. Clients should only do this once, 37 | * at install time. 38 | * 39 | * @param extendedRange By default (false), the generated registration 40 | * ID is sized to require the minimal possible protobuf 41 | * encoding overhead. Specify true if the caller needs 42 | * the full range of MAX_INT at the cost of slightly 43 | * higher encoding overhead. 44 | * @return the generated registration ID. 45 | */ 46 | public static int generateRegistrationId(SecureRandomProvider secureRandom, boolean extendedRange) { 47 | if (extendedRange) return secureRandom.nextInt(Integer.MAX_VALUE - 1) + 1; 48 | else return secureRandom.nextInt(16380) + 1; 49 | } 50 | 51 | public static int getRandomSequence(SecureRandomProvider secureRandomProvider, int max) { 52 | return secureRandomProvider.nextInt(max); 53 | } 54 | 55 | /** 56 | * Generate a list of PreKeys. Clients should do this at install time, and 57 | * subsequently any time the list of PreKeys stored on the server runs low. 58 | *

59 | * PreKey IDs are shorts, so they will eventually be repeated. Clients should 60 | * store PreKeys in a circular buffer, so that they are repeated as infrequently 61 | * as possible. 62 | * 63 | * @param start The starting PreKey ID, inclusive. 64 | * @param count The number of PreKeys to generate. 65 | * @return the list of generated PreKeyRecords. 66 | */ 67 | public static Vector generatePreKeys(SecureRandomProvider secureRandom, int start, int count) { 68 | Vector results = new Vector(); 69 | 70 | start--; 71 | 72 | for (int i=0;i. 16 | */ 17 | package org.whispersystems.libaxolotl.util; 18 | 19 | public class Pair { 20 | private final Object v1; 21 | private final Object v2; 22 | 23 | public Pair(Object v1, Object v2) { 24 | this.v1 = v1; 25 | this.v2 = v2; 26 | } 27 | 28 | public Object first() { 29 | return v1; 30 | } 31 | 32 | public Object second() { 33 | return v2; 34 | } 35 | 36 | public boolean equals(Object o) { 37 | return o instanceof Pair && 38 | equal(((Pair) o).first(), first()) && 39 | equal(((Pair) o).second(), second()); 40 | } 41 | 42 | public int hashCode() { 43 | return first().hashCode() ^ second().hashCode(); 44 | } 45 | 46 | private boolean equal(Object first, Object second) { 47 | if (first == null && second == null) return true; 48 | if (first == null || second == null) return false; 49 | return first.equals(second); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/org/whispersystems/libaxolotl/util/guava/Absent.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 The Guava Authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.whispersystems.libaxolotl.util.guava; 18 | 19 | 20 | /** 21 | * Implementation of an {@link Optional} not containing a reference. 22 | */ 23 | 24 | final class Absent extends Optional { 25 | static final Absent INSTANCE = new Absent(); 26 | 27 | public boolean isPresent() { 28 | return false; 29 | } 30 | 31 | public Object get() { 32 | throw new IllegalStateException("value is absent"); 33 | } 34 | 35 | public Object or(Object defaultValue) { 36 | return Preconditions.checkNotNull(defaultValue, "use orNull() instead of or(null)"); 37 | } 38 | 39 | public Optional or(Optional secondChoice) { 40 | return (Optional) Preconditions.checkNotNull(secondChoice); 41 | } 42 | 43 | public Object or(Supplier supplier) { 44 | return Preconditions.checkNotNull(supplier.get(), 45 | "use orNull() instead of a Supplier that returns null"); 46 | } 47 | 48 | public Object orNull() { 49 | return null; 50 | } 51 | 52 | // @Override public Set asSet() { 53 | // return Collections.emptySet(); 54 | // } 55 | 56 | // @Override 57 | // public Optional transform(Function function) { 58 | // checkNotNull(function); 59 | // return Optional.absent(); 60 | // } 61 | 62 | public boolean equals(Object object) { 63 | return object == this; 64 | } 65 | 66 | public int hashCode() { 67 | return 0x598df91c; 68 | } 69 | 70 | public String toString() { 71 | return "Optional.absent()"; 72 | } 73 | 74 | private Object readResolve() { 75 | return INSTANCE; 76 | } 77 | 78 | private static final long serialVersionUID = 0; 79 | } 80 | -------------------------------------------------------------------------------- /src/main/java/org/whispersystems/libaxolotl/util/guava/Present.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 The Guava Authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.whispersystems.libaxolotl.util.guava; 18 | 19 | /** 20 | * Implementation of an {@link Optional} containing a reference. 21 | */ 22 | 23 | final class Present extends Optional { 24 | private final Object reference; 25 | 26 | Present(Object reference) { 27 | this.reference = reference; 28 | } 29 | 30 | public boolean isPresent() { 31 | return true; 32 | } 33 | 34 | public Object get() { 35 | return reference; 36 | } 37 | 38 | public Object or(Object defaultValue) { 39 | Preconditions.checkNotNull(defaultValue, "use orNull() instead of or(null)"); 40 | return reference; 41 | } 42 | 43 | public Optional or(Optional secondChoice) { 44 | Preconditions.checkNotNull(secondChoice); 45 | return this; 46 | } 47 | 48 | public Object or(Supplier supplier) { 49 | Preconditions.checkNotNull(supplier); 50 | return reference; 51 | } 52 | 53 | public Object orNull() { 54 | return reference; 55 | } 56 | 57 | // public Set asSet() { 58 | // return Collections.singleton(reference); 59 | // } 60 | 61 | // public Optional transform(Function function) { 62 | // return new Present(checkNotNull(function.apply(reference), 63 | // "Transformation function cannot return null.")); 64 | // } 65 | 66 | public boolean equals(Object object) { 67 | if (object instanceof Present) { 68 | Present other = (Present) object; 69 | return reference.equals(other.reference); 70 | } 71 | return false; 72 | } 73 | 74 | public int hashCode() { 75 | return 0x598df91c + reference.hashCode(); 76 | } 77 | 78 | public String toString() { 79 | return "Optional.of(" + reference + ")"; 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/main/java/org/whispersystems/libaxolotl/util/guava/Supplier.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2007 The Guava Authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.whispersystems.libaxolotl.util.guava; 18 | 19 | 20 | /** 21 | * A class that can supply objects of a single type. Semantically, this could 22 | * be a factory, generator, builder, closure, or something else entirely. No 23 | * guarantees are implied by this interface. 24 | * 25 | * @author Harry Heymann 26 | * @since 2.0 (imported from Google Collections Library) 27 | */ 28 | public interface Supplier { 29 | /** 30 | * Retrieves an instance of the appropriate type. The returned object may or 31 | * may not be a new instance, depending on the implementation. 32 | * 33 | * @return an instance of the appropriate type 34 | */ 35 | Object get(); 36 | } 37 | -------------------------------------------------------------------------------- /src/test/java/org/whispersystems/libaxolotl/AxolotlBaseTestCase.java: -------------------------------------------------------------------------------- 1 | package org.whispersystems.libaxolotl; 2 | 3 | import junit.framework.TestCase; 4 | import org.whispersystems.libaxolotl.j2me.FakeSecureRandomProvider; 5 | import org.whispersystems.libaxolotl.j2me.jce.BCJmeSecurityProvider; 6 | import org.whispersystems.libaxolotl.j2me.jce.JmeSecurity; 7 | 8 | public abstract class AxolotlBaseTestCase extends TestCase { 9 | public AxolotlBaseTestCase(String name) { 10 | super(name); 11 | } 12 | 13 | public void setUp() { 14 | JmeSecurity.setProvider(new BCJmeSecurityProvider(new FakeSecureRandomProvider())); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/test/java/org/whispersystems/libaxolotl/InMemoryAxolotlStore.java: -------------------------------------------------------------------------------- 1 | package org.whispersystems.libaxolotl; 2 | 3 | import org.whispersystems.libaxolotl.state.AxolotlStore; 4 | import org.whispersystems.libaxolotl.state.PreKeyRecord; 5 | import org.whispersystems.libaxolotl.state.SessionRecord; 6 | import org.whispersystems.libaxolotl.state.SignedPreKeyRecord; 7 | 8 | import java.util.Vector; 9 | 10 | 11 | public class InMemoryAxolotlStore implements AxolotlStore { 12 | 13 | private final InMemoryIdentityKeyStore identityKeyStore = new InMemoryIdentityKeyStore(); 14 | private final InMemoryPreKeyStore preKeyStore = new InMemoryPreKeyStore(); 15 | private final InMemorySessionStore sessionStore = new InMemorySessionStore(); 16 | private final InMemorySignedPreKeyStore signedPreKeyStore = new InMemorySignedPreKeyStore(); 17 | 18 | 19 | // @Override 20 | public IdentityKeyPair getIdentityKeyPair() { 21 | return identityKeyStore.getIdentityKeyPair(); 22 | } 23 | 24 | // @Override 25 | public int getLocalRegistrationId() { 26 | return identityKeyStore.getLocalRegistrationId(); 27 | } 28 | 29 | // @Override 30 | public void saveIdentity(String name, IdentityKey identityKey) { 31 | identityKeyStore.saveIdentity(name, identityKey); 32 | } 33 | 34 | // @Override 35 | public boolean isTrustedIdentity(String name, IdentityKey identityKey) { 36 | return identityKeyStore.isTrustedIdentity(name, identityKey); 37 | } 38 | 39 | // @Override 40 | public PreKeyRecord loadPreKey(int preKeyId) throws InvalidKeyIdException { 41 | return preKeyStore.loadPreKey(preKeyId); 42 | } 43 | 44 | // @Override 45 | public void storePreKey(int preKeyId, PreKeyRecord record) { 46 | preKeyStore.storePreKey(preKeyId, record); 47 | } 48 | 49 | // @Override 50 | public boolean containsPreKey(int preKeyId) { 51 | return preKeyStore.containsPreKey(preKeyId); 52 | } 53 | 54 | // @Override 55 | public void removePreKey(int preKeyId) { 56 | preKeyStore.removePreKey(preKeyId); 57 | } 58 | 59 | // @Override 60 | public SessionRecord loadSession(AxolotlAddress address) { 61 | return sessionStore.loadSession(address); 62 | } 63 | 64 | // @Override 65 | public Vector getSubDeviceSessions(String name) { 66 | return sessionStore.getSubDeviceSessions(name); 67 | } 68 | 69 | // @Override 70 | public void storeSession(AxolotlAddress address, SessionRecord record) { 71 | sessionStore.storeSession(address, record); 72 | } 73 | 74 | // @Override 75 | public boolean containsSession(AxolotlAddress address) { 76 | return sessionStore.containsSession(address); 77 | } 78 | 79 | // @Override 80 | public void deleteSession(AxolotlAddress address) { 81 | sessionStore.deleteSession(address); 82 | } 83 | 84 | // @Override 85 | public void deleteAllSessions(String name) { 86 | sessionStore.deleteAllSessions(name); 87 | } 88 | 89 | // @Override 90 | public SignedPreKeyRecord loadSignedPreKey(int signedPreKeyId) throws InvalidKeyIdException { 91 | return signedPreKeyStore.loadSignedPreKey(signedPreKeyId); 92 | } 93 | 94 | // @Override 95 | public Vector loadSignedPreKeys() { 96 | return signedPreKeyStore.loadSignedPreKeys(); 97 | } 98 | 99 | // @Override 100 | public void storeSignedPreKey(int signedPreKeyId, SignedPreKeyRecord record) { 101 | signedPreKeyStore.storeSignedPreKey(signedPreKeyId, record); 102 | } 103 | 104 | // @Override 105 | public boolean containsSignedPreKey(int signedPreKeyId) { 106 | return signedPreKeyStore.containsSignedPreKey(signedPreKeyId); 107 | } 108 | 109 | // @Override 110 | public void removeSignedPreKey(int signedPreKeyId) { 111 | signedPreKeyStore.removeSignedPreKey(signedPreKeyId); 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /src/test/java/org/whispersystems/libaxolotl/InMemoryIdentityKeyStore.java: -------------------------------------------------------------------------------- 1 | package org.whispersystems.libaxolotl; 2 | 3 | import org.whispersystems.libaxolotl.ecc.Curve; 4 | import org.whispersystems.libaxolotl.ecc.ECKeyPair; 5 | import org.whispersystems.libaxolotl.j2me.FakeSecureRandomProvider; 6 | import org.whispersystems.libaxolotl.state.IdentityKeyStore; 7 | 8 | import java.util.Hashtable; 9 | 10 | public class InMemoryIdentityKeyStore implements IdentityKeyStore { 11 | 12 | private final Hashtable trustedKeys = new Hashtable(); 13 | // private final Map trustedKeys = new HashMap<>(); 14 | 15 | private final IdentityKeyPair identityKeyPair; 16 | private final int localRegistrationId; 17 | 18 | public InMemoryIdentityKeyStore() { 19 | ECKeyPair identityKeyPairKeys = Curve.generateKeyPair(new FakeSecureRandomProvider()); 20 | 21 | this.identityKeyPair = new IdentityKeyPair(new IdentityKey(identityKeyPairKeys.getPublicKey()), 22 | identityKeyPairKeys.getPrivateKey()); 23 | this.localRegistrationId = new FakeSecureRandomProvider().nextInt(16380) + 1; 24 | } 25 | 26 | // @Override 27 | public IdentityKeyPair getIdentityKeyPair() { 28 | return identityKeyPair; 29 | } 30 | 31 | // @Override 32 | public int getLocalRegistrationId() { 33 | return localRegistrationId; 34 | } 35 | 36 | // @Override 37 | public void saveIdentity(String name, IdentityKey identityKey) { 38 | trustedKeys.put(name, identityKey); 39 | } 40 | 41 | // @Override 42 | public boolean isTrustedIdentity(String name, IdentityKey identityKey) { 43 | IdentityKey trusted = (IdentityKey)trustedKeys.get(name); 44 | return (trusted == null || trusted.equals(identityKey)); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/test/java/org/whispersystems/libaxolotl/InMemoryPreKeyStore.java: -------------------------------------------------------------------------------- 1 | package org.whispersystems.libaxolotl; 2 | 3 | import org.whispersystems.libaxolotl.state.PreKeyRecord; 4 | import org.whispersystems.libaxolotl.state.PreKeyStore; 5 | import org.whispersystems.libaxolotl.j2me.AssertionError; 6 | 7 | import java.io.IOException; 8 | import java.util.Hashtable; 9 | 10 | public class InMemoryPreKeyStore implements PreKeyStore { 11 | 12 | private final Hashtable store = new Hashtable(); 13 | // private final Map store = new HashMap<>(); 14 | 15 | // @Override 16 | public PreKeyRecord loadPreKey(int preKeyId) throws InvalidKeyIdException { 17 | try { 18 | if (!store.containsKey(new Integer(preKeyId))) { 19 | throw new InvalidKeyIdException("No such prekeyrecord!"); 20 | } 21 | 22 | return new PreKeyRecord((byte[])store.get(new Integer(preKeyId))); 23 | } catch (IOException e) { 24 | throw new AssertionError(e); 25 | } 26 | } 27 | 28 | // @Override 29 | public void storePreKey(int preKeyId, PreKeyRecord record) { 30 | store.put(new Integer(preKeyId), record.serialize()); 31 | } 32 | 33 | // @Override 34 | public boolean containsPreKey(int preKeyId) { 35 | return store.containsKey(new Integer(preKeyId)); 36 | } 37 | 38 | // @Override 39 | public void removePreKey(int preKeyId) { 40 | store.remove(new Integer(preKeyId)); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/test/java/org/whispersystems/libaxolotl/InMemorySessionStore.java: -------------------------------------------------------------------------------- 1 | package org.whispersystems.libaxolotl; 2 | 3 | import org.whispersystems.libaxolotl.state.SessionRecord; 4 | import org.whispersystems.libaxolotl.state.SessionStore; 5 | import org.whispersystems.libaxolotl.j2me.AssertionError; 6 | import org.whispersystems.libaxolotl.util.Pair; 7 | 8 | import java.io.IOException; 9 | import java.util.Enumeration; 10 | import java.util.Hashtable; 11 | import java.util.Vector; 12 | 13 | public class InMemorySessionStore implements SessionStore { 14 | 15 | private Hashtable sessions = new Hashtable(); 16 | // private Map sessions = new HashMap<>(); 17 | 18 | public InMemorySessionStore() {} 19 | 20 | // @Override 21 | public synchronized SessionRecord loadSession(AxolotlAddress address) { 22 | try { 23 | if (containsSession(address)) { 24 | return new SessionRecord((byte[])sessions.get(address)); 25 | } else { 26 | return new SessionRecord(); 27 | } 28 | } catch (IOException e) { 29 | throw new AssertionError(e); 30 | } 31 | } 32 | 33 | // @Override 34 | public synchronized Vector getSubDeviceSessions(String name) { 35 | Vector deviceIds = new Vector(); 36 | Enumeration keys = sessions.keys(); 37 | 38 | while (keys.hasMoreElements()) { 39 | AxolotlAddress key = (AxolotlAddress)keys.nextElement(); 40 | 41 | if (key.getName().equals(name)) { 42 | deviceIds.addElement(new Long(key.getDeviceId())); 43 | } 44 | } 45 | 46 | return deviceIds; 47 | } 48 | 49 | // @Override 50 | public synchronized void storeSession(AxolotlAddress address, SessionRecord record) { 51 | sessions.put(address, record.serialize()); 52 | } 53 | 54 | // @Override 55 | public synchronized boolean containsSession(AxolotlAddress address) { 56 | return sessions.containsKey(address); 57 | } 58 | 59 | // @Override 60 | public synchronized void deleteSession(AxolotlAddress address) { 61 | sessions.remove(address); 62 | } 63 | 64 | // @Override 65 | public synchronized void deleteAllSessions(String name) { 66 | Enumeration enumeration = sessions.keys(); 67 | 68 | while (enumeration.hasMoreElements()) { 69 | AxolotlAddress key = (AxolotlAddress)enumeration.nextElement(); 70 | 71 | if (key.getName().equals(name)) { 72 | sessions.remove(key); 73 | } 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/test/java/org/whispersystems/libaxolotl/InMemorySignedPreKeyStore.java: -------------------------------------------------------------------------------- 1 | package org.whispersystems.libaxolotl; 2 | 3 | import org.whispersystems.libaxolotl.state.SignedPreKeyRecord; 4 | import org.whispersystems.libaxolotl.state.SignedPreKeyStore; 5 | import org.whispersystems.libaxolotl.j2me.AssertionError; 6 | 7 | import java.io.IOException; 8 | import java.util.Enumeration; 9 | import java.util.Hashtable; 10 | import java.util.Vector; 11 | 12 | public class InMemorySignedPreKeyStore implements SignedPreKeyStore { 13 | 14 | private final Hashtable store = new Hashtable(); 15 | // private final Map store = new HashMap<>(); 16 | 17 | // @Override 18 | public SignedPreKeyRecord loadSignedPreKey(int signedPreKeyId) throws InvalidKeyIdException { 19 | try { 20 | if (!store.containsKey(new Integer(signedPreKeyId))) { 21 | throw new InvalidKeyIdException("No such signedprekeyrecord! " + signedPreKeyId); 22 | } 23 | 24 | return new SignedPreKeyRecord((byte[])store.get(new Integer(signedPreKeyId))); 25 | } catch (IOException e) { 26 | throw new AssertionError(e); 27 | } 28 | } 29 | 30 | // @Override 31 | public Vector loadSignedPreKeys() { 32 | try { 33 | // List results = new LinkedList<>(); 34 | Vector results = new Vector(); 35 | Enumeration enumeration = store.elements(); 36 | 37 | while (enumeration.hasMoreElements()) { 38 | results.addElement(new SignedPreKeyRecord((byte[]) enumeration.nextElement())); 39 | } 40 | // for (byte[] serialized : store.values()) { 41 | // results.add(new SignedPreKeyRecord(serialized)); 42 | // } 43 | 44 | return results; 45 | } catch (IOException e) { 46 | throw new AssertionError(e); 47 | } 48 | } 49 | // 50 | // @Override 51 | public void storeSignedPreKey(int signedPreKeyId, SignedPreKeyRecord record) { 52 | store.put(new Integer(signedPreKeyId), record.serialize()); 53 | } 54 | 55 | // @Override 56 | public boolean containsSignedPreKey(int signedPreKeyId) { 57 | return store.containsKey(new Integer(signedPreKeyId)); 58 | } 59 | 60 | // @Override 61 | public void removeSignedPreKey(int signedPreKeyId) { 62 | store.remove(new Integer(signedPreKeyId)); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/test/java/org/whispersystems/libaxolotl/groups/InMemorySenderKeyStore.java: -------------------------------------------------------------------------------- 1 | package org.whispersystems.libaxolotl.groups; 2 | 3 | import org.whispersystems.libaxolotl.groups.state.SenderKeyRecord; 4 | import org.whispersystems.libaxolotl.groups.state.SenderKeyStore; 5 | import org.whispersystems.libaxolotl.j2me.AssertionError; 6 | 7 | import java.io.IOException; 8 | import java.util.Hashtable; 9 | 10 | public class InMemorySenderKeyStore implements SenderKeyStore { 11 | 12 | // SenderKeyName -> SenderKeyRecord 13 | private final Hashtable store = new Hashtable(); 14 | 15 | public void storeSenderKey(SenderKeyName senderKeyName, SenderKeyRecord record) { 16 | store.put(senderKeyName, record); 17 | } 18 | 19 | public SenderKeyRecord loadSenderKey(SenderKeyName senderKeyName) { 20 | try { 21 | SenderKeyRecord record = (SenderKeyRecord)store.get(senderKeyName); 22 | 23 | if (record == null) { 24 | return new SenderKeyRecord(); 25 | } else { 26 | return new SenderKeyRecord(record.serialize()); 27 | } 28 | } catch (IOException e) { 29 | throw new AssertionError(e); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/test/java/org/whispersystems/libaxolotl/j2me/Collections.java: -------------------------------------------------------------------------------- 1 | package org.whispersystems.libaxolotl.j2me; 2 | 3 | import java.util.Random; 4 | import java.util.Vector; 5 | 6 | public class Collections { 7 | public static void shuffle(Vector list, Random random) { 8 | for (int i=list.size()-1;i>0;i--) { 9 | int index = random.nextInt(i + 1); 10 | Object randomElement = list.elementAt(index); 11 | Object currentElement = list.elementAt(i ); 12 | 13 | list.setElementAt(currentElement, index); 14 | list.setElementAt(randomElement, i); 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/test/java/org/whispersystems/libaxolotl/j2me/FakeSecureRandomProvider.java: -------------------------------------------------------------------------------- 1 | package org.whispersystems.libaxolotl.j2me; 2 | 3 | import org.whispersystems.curve25519.SecureRandomProvider; 4 | 5 | import java.util.Random; 6 | 7 | 8 | public class FakeSecureRandomProvider implements SecureRandomProvider { 9 | public void nextBytes(byte[] output) { 10 | Random random = new Random(System.currentTimeMillis()); 11 | long something = random.nextLong(); 12 | 13 | output[0] = (byte)(something & 0xFF); 14 | output[1] = (byte)((something >> 8) & 0xFF); 15 | output[2] = (byte)((something >> 16) & 0xFF); 16 | output[3] = (byte)((something >> 24) & 0xFF); 17 | output[4] = (byte)((something >> 32) & 0xFF); 18 | 19 | } 20 | 21 | public int nextInt(int maxValue) { 22 | return new Random(System.currentTimeMillis()).nextInt(maxValue); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/test/java/org/whispersystems/libaxolotl/ratchet/ChainKeyTest.java: -------------------------------------------------------------------------------- 1 | package org.whispersystems.libaxolotl.ratchet; 2 | 3 | import junit.framework.TestCase; 4 | import org.whispersystems.libaxolotl.AxolotlBaseTestCase; 5 | import org.whispersystems.libaxolotl.kdf.HKDF; 6 | import org.whispersystems.libaxolotl.j2me.Arrays; 7 | 8 | 9 | public class ChainKeyTest extends AxolotlBaseTestCase { 10 | 11 | public ChainKeyTest(String testName) { 12 | super(testName); 13 | } 14 | 15 | public void testChainKeyDerivationV2() { 16 | 17 | byte[] seed = {(byte) 0x8a, (byte) 0xb7, (byte) 0x2d, (byte) 0x6f, (byte) 0x4c, 18 | (byte) 0xc5, (byte) 0xac, (byte) 0x0d, (byte) 0x38, (byte) 0x7e, 19 | (byte) 0xaf, (byte) 0x46, (byte) 0x33, (byte) 0x78, (byte) 0xdd, 20 | (byte) 0xb2, (byte) 0x8e, (byte) 0xdd, (byte) 0x07, (byte) 0x38, 21 | (byte) 0x5b, (byte) 0x1c, (byte) 0xb0, (byte) 0x12, (byte) 0x50, 22 | (byte) 0xc7, (byte) 0x15, (byte) 0x98, (byte) 0x2e, (byte) 0x7a, 23 | (byte) 0xd4, (byte) 0x8f}; 24 | 25 | byte[] messageKey = {(byte) 0x02, (byte) 0xa9, (byte) 0xaa, (byte) 0x6c, (byte) 0x7d, 26 | (byte) 0xbd, (byte) 0x64, (byte) 0xf9, (byte) 0xd3, (byte) 0xaa, 27 | (byte) 0x92, (byte) 0xf9, (byte) 0x2a, (byte) 0x27, (byte) 0x7b, 28 | (byte) 0xf5, (byte) 0x46, (byte) 0x09, (byte) 0xda, (byte) 0xdf, 29 | (byte) 0x0b, (byte) 0x00, (byte) 0x82, (byte) 0x8a, (byte) 0xcf, 30 | (byte) 0xc6, (byte) 0x1e, (byte) 0x3c, (byte) 0x72, (byte) 0x4b, 31 | (byte) 0x84, (byte) 0xa7}; 32 | 33 | byte[] macKey = {(byte) 0xbf, (byte) 0xbe, (byte) 0x5e, (byte) 0xfb, (byte) 0x60, 34 | (byte) 0x30, (byte) 0x30, (byte) 0x52, (byte) 0x67, (byte) 0x42, 35 | (byte) 0xe3, (byte) 0xee, (byte) 0x89, (byte) 0xc7, (byte) 0x02, 36 | (byte) 0x4e, (byte) 0x88, (byte) 0x4e, (byte) 0x44, (byte) 0x0f, 37 | (byte) 0x1f, (byte) 0xf3, (byte) 0x76, (byte) 0xbb, (byte) 0x23, 38 | (byte) 0x17, (byte) 0xb2, (byte) 0xd6, (byte) 0x4d, (byte) 0xeb, 39 | (byte) 0x7c, (byte) 0x83}; 40 | 41 | byte[] nextChainKey = {(byte) 0x28, (byte) 0xe8, (byte) 0xf8, (byte) 0xfe, (byte) 0xe5, 42 | (byte) 0x4b, (byte) 0x80, (byte) 0x1e, (byte) 0xef, (byte) 0x7c, 43 | (byte) 0x5c, (byte) 0xfb, (byte) 0x2f, (byte) 0x17, (byte) 0xf3, 44 | (byte) 0x2c, (byte) 0x7b, (byte) 0x33, (byte) 0x44, (byte) 0x85, 45 | (byte) 0xbb, (byte) 0xb7, (byte) 0x0f, (byte) 0xac, (byte) 0x6e, 46 | (byte) 0xc1, (byte) 0x03, (byte) 0x42, (byte) 0xa2, (byte) 0x46, 47 | (byte) 0xd1, (byte) 0x5d}; 48 | 49 | ChainKey chainKey = new ChainKey(HKDF.createFor(2), seed, 0); 50 | 51 | assertTrue(Arrays.equals(chainKey.getKey(), seed)); 52 | assertTrue(Arrays.equals(chainKey.getMessageKeys().getCipherKey().getKey(), messageKey)); 53 | assertTrue(Arrays.equals(chainKey.getMessageKeys().getMacKey().getKey(), macKey)); 54 | assertTrue(Arrays.equals(chainKey.getNextChainKey().getKey(), nextChainKey)); 55 | assertTrue(chainKey.getIndex() == 0); 56 | assertTrue(chainKey.getMessageKeys().getCounter() == 0); 57 | assertTrue(chainKey.getNextChainKey().getIndex() == 1); 58 | assertTrue(chainKey.getNextChainKey().getMessageKeys().getCounter() == 1); 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /src/test/java/org/whispersystems/libaxolotl/ratchet/RootKeyTest.java: -------------------------------------------------------------------------------- 1 | package org.whispersystems.libaxolotl.ratchet; 2 | 3 | 4 | import junit.framework.TestCase; 5 | import org.whispersystems.libaxolotl.AxolotlBaseTestCase; 6 | import org.whispersystems.libaxolotl.InvalidKeyException; 7 | import org.whispersystems.libaxolotl.ecc.Curve; 8 | import org.whispersystems.libaxolotl.ecc.ECKeyPair; 9 | import org.whispersystems.libaxolotl.ecc.ECPrivateKey; 10 | import org.whispersystems.libaxolotl.ecc.ECPublicKey; 11 | import org.whispersystems.libaxolotl.kdf.HKDF; 12 | import org.whispersystems.libaxolotl.j2me.Arrays; 13 | import org.whispersystems.libaxolotl.util.Pair; 14 | 15 | 16 | 17 | public class RootKeyTest extends AxolotlBaseTestCase { 18 | 19 | public RootKeyTest(String name) { 20 | super(name); 21 | } 22 | 23 | public void testRootKeyDerivationV2() throws InvalidKeyException { 24 | byte[] rootKeySeed = {(byte) 0x7b, (byte) 0xa6, (byte) 0xde, (byte) 0xbc, (byte) 0x2b, 25 | (byte) 0xc1, (byte) 0xbb, (byte) 0xf9, (byte) 0x1a, (byte) 0xbb, 26 | (byte) 0xc1, (byte) 0x36, (byte) 0x74, (byte) 0x04, (byte) 0x17, 27 | (byte) 0x6c, (byte) 0xa6, (byte) 0x23, (byte) 0x09, (byte) 0x5b, 28 | (byte) 0x7e, (byte) 0xc6, (byte) 0x6b, (byte) 0x45, (byte) 0xf6, 29 | (byte) 0x02, (byte) 0xd9, (byte) 0x35, (byte) 0x38, (byte) 0x94, 30 | (byte) 0x2d, (byte) 0xcc}; 31 | 32 | byte[] alicePublic = {(byte) 0x05, (byte) 0xee, (byte) 0x4f, (byte) 0xa6, (byte) 0xcd, 33 | (byte) 0xc0, (byte) 0x30, (byte) 0xdf, (byte) 0x49, (byte) 0xec, 34 | (byte) 0xd0, (byte) 0xba, (byte) 0x6c, (byte) 0xfc, (byte) 0xff, 35 | (byte) 0xb2, (byte) 0x33, (byte) 0xd3, (byte) 0x65, (byte) 0xa2, 36 | (byte) 0x7f, (byte) 0xad, (byte) 0xbe, (byte) 0xff, (byte) 0x77, 37 | (byte) 0xe9, (byte) 0x63, (byte) 0xfc, (byte) 0xb1, (byte) 0x62, 38 | (byte) 0x22, (byte) 0xe1, (byte) 0x3a}; 39 | 40 | byte[] alicePrivate = {(byte) 0x21, (byte) 0x68, (byte) 0x22, (byte) 0xec, (byte) 0x67, 41 | (byte) 0xeb, (byte) 0x38, (byte) 0x04, (byte) 0x9e, (byte) 0xba, 42 | (byte) 0xe7, (byte) 0xb9, (byte) 0x39, (byte) 0xba, (byte) 0xea, 43 | (byte) 0xeb, (byte) 0xb1, (byte) 0x51, (byte) 0xbb, (byte) 0xb3, 44 | (byte) 0x2d, (byte) 0xb8, (byte) 0x0f, (byte) 0xd3, (byte) 0x89, 45 | (byte) 0x24, (byte) 0x5a, (byte) 0xc3, (byte) 0x7a, (byte) 0x94, 46 | (byte) 0x8e, (byte) 0x50}; 47 | 48 | byte[] bobPublic = {(byte) 0x05, (byte) 0xab, (byte) 0xb8, (byte) 0xeb, (byte) 0x29, 49 | (byte) 0xcc, (byte) 0x80, (byte) 0xb4, (byte) 0x71, (byte) 0x09, 50 | (byte) 0xa2, (byte) 0x26, (byte) 0x5a, (byte) 0xbe, (byte) 0x97, 51 | (byte) 0x98, (byte) 0x48, (byte) 0x54, (byte) 0x06, (byte) 0xe3, 52 | (byte) 0x2d, (byte) 0xa2, (byte) 0x68, (byte) 0x93, (byte) 0x4a, 53 | (byte) 0x95, (byte) 0x55, (byte) 0xe8, (byte) 0x47, (byte) 0x57, 54 | (byte) 0x70, (byte) 0x8a, (byte) 0x30}; 55 | 56 | byte[] nextRoot = {(byte) 0xb1, (byte) 0x14, (byte) 0xf5, (byte) 0xde, (byte) 0x28, 57 | (byte) 0x01, (byte) 0x19, (byte) 0x85, (byte) 0xe6, (byte) 0xeb, 58 | (byte) 0xa2, (byte) 0x5d, (byte) 0x50, (byte) 0xe7, (byte) 0xec, 59 | (byte) 0x41, (byte) 0xa9, (byte) 0xb0, (byte) 0x2f, (byte) 0x56, 60 | (byte) 0x93, (byte) 0xc5, (byte) 0xc7, (byte) 0x88, (byte) 0xa6, 61 | (byte) 0x3a, (byte) 0x06, (byte) 0xd2, (byte) 0x12, (byte) 0xa2, 62 | (byte) 0xf7, (byte) 0x31}; 63 | 64 | byte[] nextChain = {(byte) 0x9d, (byte) 0x7d, (byte) 0x24, (byte) 0x69, (byte) 0xbc, 65 | (byte) 0x9a, (byte) 0xe5, (byte) 0x3e, (byte) 0xe9, (byte) 0x80, 66 | (byte) 0x5a, (byte) 0xa3, (byte) 0x26, (byte) 0x4d, (byte) 0x24, 67 | (byte) 0x99, (byte) 0xa3, (byte) 0xac, (byte) 0xe8, (byte) 0x0f, 68 | (byte) 0x4c, (byte) 0xca, (byte) 0xe2, (byte) 0xda, (byte) 0x13, 69 | (byte) 0x43, (byte) 0x0c, (byte) 0x5c, (byte) 0x55, (byte) 0xb5, 70 | (byte) 0xca, (byte) 0x5f}; 71 | 72 | ECPublicKey alicePublicKey = Curve.decodePoint(alicePublic, 0); 73 | ECPrivateKey alicePrivateKey = Curve.decodePrivatePoint(alicePrivate); 74 | ECKeyPair aliceKeyPair = new ECKeyPair(alicePublicKey, alicePrivateKey); 75 | 76 | ECPublicKey bobPublicKey = Curve.decodePoint(bobPublic, 0); 77 | RootKey rootKey = new RootKey(HKDF.createFor(2), rootKeySeed); 78 | 79 | Pair rootKeyChainKeyPair = rootKey.createChain(bobPublicKey, aliceKeyPair); 80 | RootKey nextRootKey = (RootKey)rootKeyChainKeyPair.first(); 81 | ChainKey nextChainKey = (ChainKey)rootKeyChainKeyPair.second(); 82 | 83 | assertTrue(Arrays.equals(rootKey.getKeyBytes(), rootKeySeed)); 84 | assertTrue(Arrays.equals(nextRootKey.getKeyBytes(), nextRoot)); 85 | assertTrue(Arrays.equals(nextChainKey.getKey(), nextChain)); 86 | } 87 | } 88 | --------------------------------------------------------------------------------