33 | * Clients should maintain a registration ID, a random number
34 | * between 1 and 16380 that's generated once at install time.
35 | *
36 | * @return the local client's registration ID.
37 | */
38 | public int getLocalRegistrationId();
39 |
40 | /**
41 | * Save a remote client's identity key
42 | *
43 | * Store a remote client's identity key as trusted.
44 | *
45 | * @param address The address of the remote client.
46 | * @param identityKey The remote client's identity key.
47 | * @return True if the identity key replaces a previous identity, false if not
48 | */
49 | public boolean saveIdentity(SignalProtocolAddress address, IdentityKey identityKey);
50 |
51 |
52 | /**
53 | * Verify a remote client's identity key.
54 | *
55 | * Determine whether a remote client's identity is trusted. Convention is
56 | * that the Signal Protocol is 'trust on first use.' This means that
57 | * an identity key is considered 'trusted' if there is no entry for the recipient
58 | * in the local store, or if it matches the saved key for a recipient in the local
59 | * store. Only if it mismatches an entry in the local store is it considered
60 | * 'untrusted.'
61 | *
62 | * Clients may wish to make a distinction as to how keys are trusted based on the
63 | * direction of travel. For instance, clients may wish to accept all 'incoming' identity
64 | * key changes, while only blocking identity key changes when sending a message.
65 | *
66 | * @param address The address of the remote client.
67 | * @param identityKey The identity key to verify.
68 | * @param direction The direction (sending or receiving) this identity is being used for.
69 | * @return true if trusted, false if untrusted.
70 | */
71 | public boolean isTrustedIdentity(SignalProtocolAddress address, IdentityKey identityKey, Direction direction);
72 |
73 |
74 | /**
75 | * Return the saved public identity key for a remote client
76 | *
77 | * @param address The address of the remote client
78 | * @return The public identity key, or null if absent
79 | */
80 | public IdentityKey getIdentity(SignalProtocolAddress address);
81 |
82 | }
83 |
--------------------------------------------------------------------------------
/java/src/main/java/org/whispersystems/libsignal/state/PreKeyBundle.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2014-2016 Open Whisper Systems
3 | *
4 | * Licensed according to the LICENSE file in this repository.
5 | */
6 | package org.whispersystems.libsignal.state;
7 |
8 | import org.whispersystems.libsignal.IdentityKey;
9 | import org.whispersystems.libsignal.ecc.ECPublicKey;
10 |
11 | /**
12 | * A class that contains a remote PreKey and collection
13 | * of associated items.
14 | *
15 | * @author Moxie Marlinspike
16 | */
17 | public class PreKeyBundle {
18 |
19 | private int registrationId;
20 |
21 | private int deviceId;
22 |
23 | private int preKeyId;
24 | private ECPublicKey preKeyPublic;
25 |
26 | private int signedPreKeyId;
27 | private ECPublicKey signedPreKeyPublic;
28 | private byte[] signedPreKeySignature;
29 |
30 | private IdentityKey identityKey;
31 |
32 | public PreKeyBundle(int registrationId, int deviceId, int preKeyId, ECPublicKey preKeyPublic,
33 | int signedPreKeyId, ECPublicKey signedPreKeyPublic, byte[] signedPreKeySignature,
34 | IdentityKey identityKey)
35 | {
36 | this.registrationId = registrationId;
37 | this.deviceId = deviceId;
38 | this.preKeyId = preKeyId;
39 | this.preKeyPublic = preKeyPublic;
40 | this.signedPreKeyId = signedPreKeyId;
41 | this.signedPreKeyPublic = signedPreKeyPublic;
42 | this.signedPreKeySignature = signedPreKeySignature;
43 | this.identityKey = identityKey;
44 | }
45 |
46 | /**
47 | * @return the device ID this PreKey belongs to.
48 | */
49 | public int getDeviceId() {
50 | return deviceId;
51 | }
52 |
53 | /**
54 | * @return the unique key ID for this PreKey.
55 | */
56 | public int getPreKeyId() {
57 | return preKeyId;
58 | }
59 |
60 | /**
61 | * @return the public key for this PreKey.
62 | */
63 | public ECPublicKey getPreKey() {
64 | return preKeyPublic;
65 | }
66 |
67 | /**
68 | * @return the unique key ID for this signed prekey.
69 | */
70 | public int getSignedPreKeyId() {
71 | return signedPreKeyId;
72 | }
73 |
74 | /**
75 | * @return the signed prekey for this PreKeyBundle.
76 | */
77 | public ECPublicKey getSignedPreKey() {
78 | return signedPreKeyPublic;
79 | }
80 |
81 | /**
82 | * @return the signature over the signed prekey.
83 | */
84 | public byte[] getSignedPreKeySignature() {
85 | return signedPreKeySignature;
86 | }
87 |
88 | /**
89 | * @return the {@link org.whispersystems.libsignal.IdentityKey} of this PreKeys owner.
90 | */
91 | public IdentityKey getIdentityKey() {
92 | return identityKey;
93 | }
94 |
95 | /**
96 | * @return the registration ID associated with this PreKey.
97 | */
98 | public int getRegistrationId() {
99 | return registrationId;
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/java/src/main/java/org/whispersystems/libsignal/state/PreKeyRecord.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2014-2016 Open Whisper Systems
3 | *
4 | * Licensed according to the LICENSE file in this repository.
5 | */
6 | package org.whispersystems.libsignal.state;
7 |
8 | import com.google.protobuf.ByteString;
9 |
10 | import org.whispersystems.libsignal.InvalidKeyException;
11 | import org.whispersystems.libsignal.ecc.Curve;
12 | import org.whispersystems.libsignal.ecc.ECKeyPair;
13 | import org.whispersystems.libsignal.ecc.ECPrivateKey;
14 | import org.whispersystems.libsignal.ecc.ECPublicKey;
15 |
16 | import java.io.IOException;
17 |
18 | import static org.whispersystems.libsignal.state.StorageProtos.PreKeyRecordStructure;
19 |
20 | public class PreKeyRecord {
21 |
22 | private PreKeyRecordStructure structure;
23 |
24 | public PreKeyRecord(int id, ECKeyPair keyPair) {
25 | this.structure = PreKeyRecordStructure.newBuilder()
26 | .setId(id)
27 | .setPublicKey(ByteString.copyFrom(keyPair.getPublicKey()
28 | .serialize()))
29 | .setPrivateKey(ByteString.copyFrom(keyPair.getPrivateKey()
30 | .serialize()))
31 | .build();
32 | }
33 |
34 | public PreKeyRecord(byte[] serialized) throws IOException {
35 | this.structure = PreKeyRecordStructure.parseFrom(serialized);
36 | }
37 |
38 | public int getId() {
39 | return this.structure.getId();
40 | }
41 |
42 | public ECKeyPair getKeyPair() {
43 | try {
44 | ECPublicKey publicKey = Curve.decodePoint(this.structure.getPublicKey().toByteArray(), 0);
45 | ECPrivateKey privateKey = Curve.decodePrivatePoint(this.structure.getPrivateKey().toByteArray());
46 |
47 | return new ECKeyPair(publicKey, privateKey);
48 | } catch (InvalidKeyException e) {
49 | throw new AssertionError(e);
50 | }
51 | }
52 |
53 | public byte[] serialize() {
54 | return this.structure.toByteArray();
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/java/src/main/java/org/whispersystems/libsignal/state/PreKeyStore.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2014-2016 Open Whisper Systems
3 | *
4 | * Licensed according to the LICENSE file in this repository.
5 | */
6 | package org.whispersystems.libsignal.state;
7 |
8 | import org.whispersystems.libsignal.InvalidKeyIdException;
9 |
10 | /**
11 | * An interface describing the local storage of {@link PreKeyRecord}s.
12 | *
13 | * @author Moxie Marlinspike
14 | */
15 | public interface PreKeyStore {
16 |
17 | /**
18 | * Load a local PreKeyRecord.
19 | *
20 | * @param preKeyId the ID of the local PreKeyRecord.
21 | * @return the corresponding PreKeyRecord.
22 | * @throws InvalidKeyIdException when there is no corresponding PreKeyRecord.
23 | */
24 | public PreKeyRecord loadPreKey(int preKeyId) throws InvalidKeyIdException;
25 |
26 | /**
27 | * Store a local PreKeyRecord.
28 | *
29 | * @param preKeyId the ID of the PreKeyRecord to store.
30 | * @param record the PreKeyRecord.
31 | */
32 | public void storePreKey(int preKeyId, PreKeyRecord record);
33 |
34 | /**
35 | * @param preKeyId A PreKeyRecord ID.
36 | * @return true if the store has a record for the preKeyId, otherwise false.
37 | */
38 | public boolean containsPreKey(int preKeyId);
39 |
40 | /**
41 | * Delete a PreKeyRecord from local storage.
42 | *
43 | * @param preKeyId The ID of the PreKeyRecord to remove.
44 | */
45 | public void removePreKey(int preKeyId);
46 |
47 | }
48 |
--------------------------------------------------------------------------------
/java/src/main/java/org/whispersystems/libsignal/state/SessionRecord.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2014-2016 Open Whisper Systems
3 | *
4 | * Licensed according to the LICENSE file in this repository.
5 | */
6 | package org.whispersystems.libsignal.state;
7 |
8 | import java.io.IOException;
9 | import java.util.Arrays;
10 | import java.util.LinkedList;
11 | import java.util.List;
12 |
13 | import static org.whispersystems.libsignal.state.StorageProtos.RecordStructure;
14 | import static org.whispersystems.libsignal.state.StorageProtos.SessionStructure;
15 |
16 | /**
17 | * A SessionRecord encapsulates the state of an ongoing session.
18 | *
19 | * @author Moxie Marlinspike
20 | */
21 | public class SessionRecord {
22 |
23 | private static final int ARCHIVED_STATES_MAX_LENGTH = 40;
24 |
25 | private SessionState sessionState = new SessionState();
26 | private LinkedList previousStates = new LinkedList<>();
27 | private boolean fresh = false;
28 |
29 | public SessionRecord() {
30 | this.fresh = true;
31 | }
32 |
33 | public SessionRecord(SessionState sessionState) {
34 | this.sessionState = sessionState;
35 | this.fresh = false;
36 | }
37 |
38 | public SessionRecord(byte[] serialized) throws IOException {
39 | RecordStructure record = RecordStructure.parseFrom(serialized);
40 | this.sessionState = new SessionState(record.getCurrentSession());
41 | this.fresh = false;
42 |
43 | for (SessionStructure previousStructure : record.getPreviousSessionsList()) {
44 | previousStates.add(new SessionState(previousStructure));
45 | }
46 | }
47 |
48 | public boolean hasSessionState(int version, byte[] aliceBaseKey) {
49 | if (sessionState.getSessionVersion() == version &&
50 | Arrays.equals(aliceBaseKey, sessionState.getAliceBaseKey()))
51 | {
52 | return true;
53 | }
54 |
55 | for (SessionState state : previousStates) {
56 | if (state.getSessionVersion() == version &&
57 | Arrays.equals(aliceBaseKey, state.getAliceBaseKey()))
58 | {
59 | return true;
60 | }
61 | }
62 |
63 | return false;
64 | }
65 |
66 | public SessionState getSessionState() {
67 | return sessionState;
68 | }
69 |
70 | /**
71 | * @return the list of all currently maintained "previous" session states.
72 | */
73 | public List getPreviousSessionStates() {
74 | return previousStates;
75 | }
76 |
77 | public void removePreviousSessionStates() {
78 | previousStates.clear();
79 | }
80 |
81 | public boolean isFresh() {
82 | return fresh;
83 | }
84 |
85 | /**
86 | * Move the current {@link SessionState} into the list of "previous" session states,
87 | * and replace the current {@link org.whispersystems.libsignal.state.SessionState}
88 | * with a fresh reset instance.
89 | */
90 | public void archiveCurrentState() {
91 | promoteState(new SessionState());
92 | }
93 |
94 | public void promoteState(SessionState promotedState) {
95 | this.previousStates.addFirst(sessionState);
96 | this.sessionState = promotedState;
97 |
98 | if (previousStates.size() > ARCHIVED_STATES_MAX_LENGTH) {
99 | previousStates.removeLast();
100 | }
101 | }
102 |
103 | public void setState(SessionState sessionState) {
104 | this.sessionState = sessionState;
105 | }
106 |
107 | /**
108 | * @return a serialized version of the current SessionRecord.
109 | */
110 | public byte[] serialize() {
111 | List previousStructures = new LinkedList<>();
112 |
113 | for (SessionState previousState : previousStates) {
114 | previousStructures.add(previousState.getStructure());
115 | }
116 |
117 | RecordStructure record = RecordStructure.newBuilder()
118 | .setCurrentSession(sessionState.getStructure())
119 | .addAllPreviousSessions(previousStructures)
120 | .build();
121 |
122 | return record.toByteArray();
123 | }
124 |
125 | }
126 |
--------------------------------------------------------------------------------
/java/src/main/java/org/whispersystems/libsignal/state/SessionStore.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2014-2016 Open Whisper Systems
3 | *
4 | * Licensed according to the LICENSE file in this repository.
5 | */
6 | package org.whispersystems.libsignal.state;
7 |
8 | import org.whispersystems.libsignal.SignalProtocolAddress;
9 |
10 | import java.util.List;
11 |
12 | /**
13 | * The interface to the durable store of session state information
14 | * for remote clients.
15 | *
16 | * @author Moxie Marlinspike
17 | */
18 | public interface SessionStore {
19 |
20 | /**
21 | * Returns a copy of the {@link SessionRecord} corresponding to the recipientId + deviceId tuple,
22 | * or a new SessionRecord if one does not currently exist.
23 | *
24 | * It is important that implementations return a copy of the current durable information. The
25 | * returned SessionRecord may be modified, but those changes should not have an effect on the
26 | * durable session state (what is returned by subsequent calls to this method) without the
27 | * store method being called here first.
28 | *
29 | * @param address The name and device ID of the remote client.
30 | * @return a copy of the SessionRecord corresponding to the recipientId + deviceId tuple, or
31 | * a new SessionRecord if one does not currently exist.
32 | */
33 | public SessionRecord loadSession(SignalProtocolAddress address);
34 |
35 | /**
36 | * Returns all known devices with active sessions for a recipient
37 | *
38 | * @param name the name of the client.
39 | * @return all known sub-devices with active sessions.
40 | */
41 | public List getSubDeviceSessions(String name);
42 |
43 | /**
44 | * Commit to storage the {@link SessionRecord} for a given recipientId + deviceId tuple.
45 | * @param address the address of the remote client.
46 | * @param record the current SessionRecord for the remote client.
47 | */
48 | public void storeSession(SignalProtocolAddress address, SessionRecord record);
49 |
50 | /**
51 | * Determine whether there is a committed {@link SessionRecord} for a recipientId + deviceId tuple.
52 | * @param address the address of the remote client.
53 | * @return true if a {@link SessionRecord} exists, false otherwise.
54 | */
55 | public boolean containsSession(SignalProtocolAddress address);
56 |
57 | /**
58 | * Remove a {@link SessionRecord} for a recipientId + deviceId tuple.
59 | *
60 | * @param address the address of the remote client.
61 | */
62 | public void deleteSession(SignalProtocolAddress address);
63 |
64 | /**
65 | * Remove the {@link SessionRecord}s corresponding to all devices of a recipientId.
66 | *
67 | * @param name the name of the remote client.
68 | */
69 | public void deleteAllSessions(String name);
70 |
71 | }
72 |
--------------------------------------------------------------------------------
/java/src/main/java/org/whispersystems/libsignal/state/SignalProtocolStore.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2014-2016 Open Whisper Systems
3 | *
4 | * Licensed according to the LICENSE file in this repository.
5 | */
6 | package org.whispersystems.libsignal.state;
7 |
8 | public interface SignalProtocolStore
9 | extends IdentityKeyStore, PreKeyStore, SessionStore, SignedPreKeyStore
10 | {
11 | }
12 |
--------------------------------------------------------------------------------
/java/src/main/java/org/whispersystems/libsignal/state/SignedPreKeyRecord.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2014-2016 Open Whisper Systems
3 | *
4 | * Licensed according to the LICENSE file in this repository.
5 | */
6 | package org.whispersystems.libsignal.state;
7 |
8 | import com.google.protobuf.ByteString;
9 |
10 | import org.whispersystems.libsignal.InvalidKeyException;
11 | import org.whispersystems.libsignal.ecc.Curve;
12 | import org.whispersystems.libsignal.ecc.ECKeyPair;
13 | import org.whispersystems.libsignal.ecc.ECPrivateKey;
14 | import org.whispersystems.libsignal.ecc.ECPublicKey;
15 |
16 | import java.io.IOException;
17 |
18 | import static org.whispersystems.libsignal.state.StorageProtos.SignedPreKeyRecordStructure;
19 |
20 | public class SignedPreKeyRecord {
21 |
22 | private SignedPreKeyRecordStructure structure;
23 |
24 | public SignedPreKeyRecord(int id, long timestamp, ECKeyPair keyPair, byte[] signature) {
25 | this.structure = SignedPreKeyRecordStructure.newBuilder()
26 | .setId(id)
27 | .setPublicKey(ByteString.copyFrom(keyPair.getPublicKey()
28 | .serialize()))
29 | .setPrivateKey(ByteString.copyFrom(keyPair.getPrivateKey()
30 | .serialize()))
31 | .setSignature(ByteString.copyFrom(signature))
32 | .setTimestamp(timestamp)
33 | .build();
34 | }
35 |
36 | public SignedPreKeyRecord(byte[] serialized) throws IOException {
37 | this.structure = SignedPreKeyRecordStructure.parseFrom(serialized);
38 | }
39 |
40 | public int getId() {
41 | return this.structure.getId();
42 | }
43 |
44 | public long getTimestamp() {
45 | return this.structure.getTimestamp();
46 | }
47 |
48 | public ECKeyPair getKeyPair() {
49 | try {
50 | ECPublicKey publicKey = Curve.decodePoint(this.structure.getPublicKey().toByteArray(), 0);
51 | ECPrivateKey privateKey = Curve.decodePrivatePoint(this.structure.getPrivateKey().toByteArray());
52 |
53 | return new ECKeyPair(publicKey, privateKey);
54 | } catch (InvalidKeyException e) {
55 | throw new AssertionError(e);
56 | }
57 | }
58 |
59 | public byte[] getSignature() {
60 | return this.structure.getSignature().toByteArray();
61 | }
62 |
63 | public byte[] serialize() {
64 | return this.structure.toByteArray();
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/java/src/main/java/org/whispersystems/libsignal/state/SignedPreKeyStore.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2014-2016 Open Whisper Systems
3 | *
4 | * Licensed according to the LICENSE file in this repository.
5 | */
6 | package org.whispersystems.libsignal.state;
7 |
8 | import org.whispersystems.libsignal.InvalidKeyIdException;
9 |
10 | import java.util.List;
11 |
12 | public interface SignedPreKeyStore {
13 |
14 |
15 | /**
16 | * Load a local SignedPreKeyRecord.
17 | *
18 | * @param signedPreKeyId the ID of the local SignedPreKeyRecord.
19 | * @return the corresponding SignedPreKeyRecord.
20 | * @throws InvalidKeyIdException when there is no corresponding SignedPreKeyRecord.
21 | */
22 | public SignedPreKeyRecord loadSignedPreKey(int signedPreKeyId) throws InvalidKeyIdException;
23 |
24 | /**
25 | * Load all local SignedPreKeyRecords.
26 | *
27 | * @return All stored SignedPreKeyRecords.
28 | */
29 | public List loadSignedPreKeys();
30 |
31 | /**
32 | * Store a local SignedPreKeyRecord.
33 | *
34 | * @param signedPreKeyId the ID of the SignedPreKeyRecord to store.
35 | * @param record the SignedPreKeyRecord.
36 | */
37 | public void storeSignedPreKey(int signedPreKeyId, SignedPreKeyRecord record);
38 |
39 | /**
40 | * @param signedPreKeyId A SignedPreKeyRecord ID.
41 | * @return true if the store has a record for the signedPreKeyId, otherwise false.
42 | */
43 | public boolean containsSignedPreKey(int signedPreKeyId);
44 |
45 | /**
46 | * Delete a SignedPreKeyRecord from local storage.
47 | *
48 | * @param signedPreKeyId The ID of the SignedPreKeyRecord to remove.
49 | */
50 | public void removeSignedPreKey(int signedPreKeyId);
51 |
52 | }
53 |
--------------------------------------------------------------------------------
/java/src/main/java/org/whispersystems/libsignal/state/impl/InMemoryIdentityKeyStore.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2014-2016 Open Whisper Systems
3 | *
4 | * Licensed according to the LICENSE file in this repository.
5 | */
6 | package org.whispersystems.libsignal.state.impl;
7 |
8 | import org.whispersystems.libsignal.IdentityKey;
9 | import org.whispersystems.libsignal.IdentityKeyPair;
10 | import org.whispersystems.libsignal.SignalProtocolAddress;
11 | import org.whispersystems.libsignal.state.IdentityKeyStore;
12 |
13 | import java.util.HashMap;
14 | import java.util.Map;
15 |
16 | public class InMemoryIdentityKeyStore implements IdentityKeyStore {
17 |
18 | private final Map trustedKeys = new HashMap<>();
19 |
20 | private final IdentityKeyPair identityKeyPair;
21 | private final int localRegistrationId;
22 |
23 | public InMemoryIdentityKeyStore(IdentityKeyPair identityKeyPair, int localRegistrationId) {
24 | this.identityKeyPair = identityKeyPair;
25 | this.localRegistrationId = localRegistrationId;
26 | }
27 |
28 | @Override
29 | public IdentityKeyPair getIdentityKeyPair() {
30 | return identityKeyPair;
31 | }
32 |
33 | @Override
34 | public int getLocalRegistrationId() {
35 | return localRegistrationId;
36 | }
37 |
38 | @Override
39 | public boolean saveIdentity(SignalProtocolAddress address, IdentityKey identityKey) {
40 | IdentityKey existing = trustedKeys.get(address);
41 |
42 | if (!identityKey.equals(existing)) {
43 | trustedKeys.put(address, identityKey);
44 | return true;
45 | } else {
46 | return false;
47 | }
48 | }
49 |
50 | @Override
51 | public boolean isTrustedIdentity(SignalProtocolAddress address, IdentityKey identityKey, Direction direction) {
52 | IdentityKey trusted = trustedKeys.get(address);
53 | return (trusted == null || trusted.equals(identityKey));
54 | }
55 |
56 | @Override
57 | public IdentityKey getIdentity(SignalProtocolAddress address) {
58 | return trustedKeys.get(address);
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/java/src/main/java/org/whispersystems/libsignal/state/impl/InMemoryPreKeyStore.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2014-2016 Open Whisper Systems
3 | *
4 | * Licensed according to the LICENSE file in this repository.
5 | */
6 | package org.whispersystems.libsignal.state.impl;
7 |
8 | import org.whispersystems.libsignal.InvalidKeyIdException;
9 | import org.whispersystems.libsignal.state.PreKeyRecord;
10 | import org.whispersystems.libsignal.state.PreKeyStore;
11 |
12 | import java.io.IOException;
13 | import java.util.HashMap;
14 | import java.util.Map;
15 |
16 | public class InMemoryPreKeyStore implements PreKeyStore {
17 |
18 | private final Map store = new HashMap<>();
19 |
20 | @Override
21 | public PreKeyRecord loadPreKey(int preKeyId) throws InvalidKeyIdException {
22 | try {
23 | if (!store.containsKey(preKeyId)) {
24 | throw new InvalidKeyIdException("No such prekeyrecord!");
25 | }
26 |
27 | return new PreKeyRecord(store.get(preKeyId));
28 | } catch (IOException e) {
29 | throw new AssertionError(e);
30 | }
31 | }
32 |
33 | @Override
34 | public void storePreKey(int preKeyId, PreKeyRecord record) {
35 | store.put(preKeyId, record.serialize());
36 | }
37 |
38 | @Override
39 | public boolean containsPreKey(int preKeyId) {
40 | return store.containsKey(preKeyId);
41 | }
42 |
43 | @Override
44 | public void removePreKey(int preKeyId) {
45 | store.remove(preKeyId);
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/java/src/main/java/org/whispersystems/libsignal/state/impl/InMemorySessionStore.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2014-2016 Open Whisper Systems
3 | *
4 | * Licensed according to the LICENSE file in this repository.
5 | */
6 | package org.whispersystems.libsignal.state.impl;
7 |
8 | import org.whispersystems.libsignal.SignalProtocolAddress;
9 | import org.whispersystems.libsignal.state.SessionRecord;
10 | import org.whispersystems.libsignal.state.SessionStore;
11 |
12 | import java.io.IOException;
13 | import java.util.HashMap;
14 | import java.util.LinkedList;
15 | import java.util.List;
16 | import java.util.Map;
17 |
18 | public class InMemorySessionStore implements SessionStore {
19 |
20 | private Map sessions = new HashMap<>();
21 |
22 | public InMemorySessionStore() {}
23 |
24 | @Override
25 | public synchronized SessionRecord loadSession(SignalProtocolAddress remoteAddress) {
26 | try {
27 | if (containsSession(remoteAddress)) {
28 | return new SessionRecord(sessions.get(remoteAddress));
29 | } else {
30 | return new SessionRecord();
31 | }
32 | } catch (IOException e) {
33 | throw new AssertionError(e);
34 | }
35 | }
36 |
37 | @Override
38 | public synchronized List getSubDeviceSessions(String name) {
39 | List deviceIds = new LinkedList<>();
40 |
41 | for (SignalProtocolAddress key : sessions.keySet()) {
42 | if (key.getName().equals(name) &&
43 | key.getDeviceId() != 1)
44 | {
45 | deviceIds.add(key.getDeviceId());
46 | }
47 | }
48 |
49 | return deviceIds;
50 | }
51 |
52 | @Override
53 | public synchronized void storeSession(SignalProtocolAddress address, SessionRecord record) {
54 | sessions.put(address, record.serialize());
55 | }
56 |
57 | @Override
58 | public synchronized boolean containsSession(SignalProtocolAddress address) {
59 | return sessions.containsKey(address);
60 | }
61 |
62 | @Override
63 | public synchronized void deleteSession(SignalProtocolAddress address) {
64 | sessions.remove(address);
65 | }
66 |
67 | @Override
68 | public synchronized void deleteAllSessions(String name) {
69 | for (SignalProtocolAddress key : sessions.keySet()) {
70 | if (key.getName().equals(name)) {
71 | sessions.remove(key);
72 | }
73 | }
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/java/src/main/java/org/whispersystems/libsignal/state/impl/InMemorySignalProtocolStore.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2014-2016 Open Whisper Systems
3 | *
4 | * Licensed according to the LICENSE file in this repository.
5 | */
6 | package org.whispersystems.libsignal.state.impl;
7 |
8 | import org.whispersystems.libsignal.SignalProtocolAddress;
9 | import org.whispersystems.libsignal.IdentityKey;
10 | import org.whispersystems.libsignal.IdentityKeyPair;
11 | import org.whispersystems.libsignal.InvalidKeyIdException;
12 | import org.whispersystems.libsignal.state.SignalProtocolStore;
13 | import org.whispersystems.libsignal.state.PreKeyRecord;
14 | import org.whispersystems.libsignal.state.SessionRecord;
15 | import org.whispersystems.libsignal.state.SignedPreKeyRecord;
16 |
17 | import java.util.List;
18 |
19 | public class InMemorySignalProtocolStore implements SignalProtocolStore {
20 |
21 | private final InMemoryPreKeyStore preKeyStore = new InMemoryPreKeyStore();
22 | private final InMemorySessionStore sessionStore = new InMemorySessionStore();
23 | private final InMemorySignedPreKeyStore signedPreKeyStore = new InMemorySignedPreKeyStore();
24 |
25 | private final InMemoryIdentityKeyStore identityKeyStore;
26 |
27 | public InMemorySignalProtocolStore(IdentityKeyPair identityKeyPair, int registrationId) {
28 | this.identityKeyStore = new InMemoryIdentityKeyStore(identityKeyPair, registrationId);
29 | }
30 |
31 | @Override
32 | public IdentityKeyPair getIdentityKeyPair() {
33 | return identityKeyStore.getIdentityKeyPair();
34 | }
35 |
36 | @Override
37 | public int getLocalRegistrationId() {
38 | return identityKeyStore.getLocalRegistrationId();
39 | }
40 |
41 | @Override
42 | public boolean saveIdentity(SignalProtocolAddress address, IdentityKey identityKey) {
43 | return identityKeyStore.saveIdentity(address, identityKey);
44 | }
45 |
46 | @Override
47 | public boolean isTrustedIdentity(SignalProtocolAddress address, IdentityKey identityKey, Direction direction) {
48 | return identityKeyStore.isTrustedIdentity(address, identityKey, direction);
49 | }
50 |
51 | @Override
52 | public IdentityKey getIdentity(SignalProtocolAddress address) {
53 | return identityKeyStore.getIdentity(address);
54 | }
55 |
56 | @Override
57 | public PreKeyRecord loadPreKey(int preKeyId) throws InvalidKeyIdException {
58 | return preKeyStore.loadPreKey(preKeyId);
59 | }
60 |
61 | @Override
62 | public void storePreKey(int preKeyId, PreKeyRecord record) {
63 | preKeyStore.storePreKey(preKeyId, record);
64 | }
65 |
66 | @Override
67 | public boolean containsPreKey(int preKeyId) {
68 | return preKeyStore.containsPreKey(preKeyId);
69 | }
70 |
71 | @Override
72 | public void removePreKey(int preKeyId) {
73 | preKeyStore.removePreKey(preKeyId);
74 | }
75 |
76 | @Override
77 | public SessionRecord loadSession(SignalProtocolAddress address) {
78 | return sessionStore.loadSession(address);
79 | }
80 |
81 | @Override
82 | public List getSubDeviceSessions(String name) {
83 | return sessionStore.getSubDeviceSessions(name);
84 | }
85 |
86 | @Override
87 | public void storeSession(SignalProtocolAddress address, SessionRecord record) {
88 | sessionStore.storeSession(address, record);
89 | }
90 |
91 | @Override
92 | public boolean containsSession(SignalProtocolAddress address) {
93 | return sessionStore.containsSession(address);
94 | }
95 |
96 | @Override
97 | public void deleteSession(SignalProtocolAddress address) {
98 | sessionStore.deleteSession(address);
99 | }
100 |
101 | @Override
102 | public void deleteAllSessions(String name) {
103 | sessionStore.deleteAllSessions(name);
104 | }
105 |
106 | @Override
107 | public SignedPreKeyRecord loadSignedPreKey(int signedPreKeyId) throws InvalidKeyIdException {
108 | return signedPreKeyStore.loadSignedPreKey(signedPreKeyId);
109 | }
110 |
111 | @Override
112 | public List loadSignedPreKeys() {
113 | return signedPreKeyStore.loadSignedPreKeys();
114 | }
115 |
116 | @Override
117 | public void storeSignedPreKey(int signedPreKeyId, SignedPreKeyRecord record) {
118 | signedPreKeyStore.storeSignedPreKey(signedPreKeyId, record);
119 | }
120 |
121 | @Override
122 | public boolean containsSignedPreKey(int signedPreKeyId) {
123 | return signedPreKeyStore.containsSignedPreKey(signedPreKeyId);
124 | }
125 |
126 | @Override
127 | public void removeSignedPreKey(int signedPreKeyId) {
128 | signedPreKeyStore.removeSignedPreKey(signedPreKeyId);
129 | }
130 | }
131 |
--------------------------------------------------------------------------------
/java/src/main/java/org/whispersystems/libsignal/state/impl/InMemorySignedPreKeyStore.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2014-2016 Open Whisper Systems
3 | *
4 | * Licensed according to the LICENSE file in this repository.
5 | */
6 | package org.whispersystems.libsignal.state.impl;
7 |
8 | import org.whispersystems.libsignal.InvalidKeyIdException;
9 | import org.whispersystems.libsignal.state.SignedPreKeyRecord;
10 | import org.whispersystems.libsignal.state.SignedPreKeyStore;
11 |
12 | import java.io.IOException;
13 | import java.util.HashMap;
14 | import java.util.LinkedList;
15 | import java.util.List;
16 | import java.util.Map;
17 |
18 | public class InMemorySignedPreKeyStore implements SignedPreKeyStore {
19 |
20 | private final Map store = new HashMap<>();
21 |
22 | @Override
23 | public SignedPreKeyRecord loadSignedPreKey(int signedPreKeyId) throws InvalidKeyIdException {
24 | try {
25 | if (!store.containsKey(signedPreKeyId)) {
26 | throw new InvalidKeyIdException("No such signedprekeyrecord! " + signedPreKeyId);
27 | }
28 |
29 | return new SignedPreKeyRecord(store.get(signedPreKeyId));
30 | } catch (IOException e) {
31 | throw new AssertionError(e);
32 | }
33 | }
34 |
35 | @Override
36 | public List loadSignedPreKeys() {
37 | try {
38 | List results = new LinkedList<>();
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(signedPreKeyId, record.serialize());
53 | }
54 |
55 | @Override
56 | public boolean containsSignedPreKey(int signedPreKeyId) {
57 | return store.containsKey(signedPreKeyId);
58 | }
59 |
60 | @Override
61 | public void removeSignedPreKey(int signedPreKeyId) {
62 | store.remove(signedPreKeyId);
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/java/src/main/java/org/whispersystems/libsignal/util/ByteArrayComparator.java:
--------------------------------------------------------------------------------
1 | package org.whispersystems.libsignal.util;
2 |
3 | public abstract class ByteArrayComparator {
4 |
5 | protected int compare(byte[] left, byte[] right) {
6 | for (int i = 0, j = 0; i < left.length && j < right.length; i++, j++) {
7 | int a = (left[i] & 0xff);
8 | int b = (right[j] & 0xff);
9 |
10 | if (a != b) {
11 | return a - b;
12 | }
13 | }
14 |
15 | return left.length - right.length;
16 | }
17 |
18 | }
19 |
--------------------------------------------------------------------------------
/java/src/main/java/org/whispersystems/libsignal/util/Hex.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2014-2016 Open Whisper Systems
3 | *
4 | * Licensed according to the LICENSE file in this repository.
5 | */
6 | package org.whispersystems.libsignal.util;
7 |
8 | import java.io.IOException;
9 |
10 | /**
11 | * Utility for generating hex dumps.
12 | */
13 | public class Hex {
14 |
15 | private final static char[] HEX_DIGITS = {
16 | '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
17 | };
18 |
19 | public static String toString(byte[] bytes) {
20 | return toString(bytes, 0, bytes.length);
21 | }
22 |
23 | public static String toString(byte[] bytes, int offset, int length) {
24 | StringBuffer buf = new StringBuffer();
25 | for (int i = 0; i < length; i++) {
26 | appendHexChar(buf, bytes[offset + i]);
27 | buf.append(", ");
28 | }
29 | return buf.toString();
30 | }
31 |
32 | public static String toStringCondensed(byte[] bytes) {
33 | StringBuffer buf = new StringBuffer();
34 | for (int i=0;i> 1];
49 |
50 | for (int i = 0, j = 0; j < len; i++) {
51 | int f = Character.digit(data[j], 16) << 4;
52 | j++;
53 | f = f | Character.digit(data[j], 16);
54 | j++;
55 | out[i] = (byte) (f & 0xFF);
56 | }
57 |
58 | return out;
59 | }
60 |
61 | private static void appendHexChar(StringBuffer buf, int b) {
62 | buf.append("(byte)0x");
63 | buf.append(HEX_DIGITS[(b >> 4) & 0xf]);
64 | buf.append(HEX_DIGITS[b & 0xf]);
65 | }
66 |
67 | }
68 |
--------------------------------------------------------------------------------
/java/src/main/java/org/whispersystems/libsignal/util/IdentityKeyComparator.java:
--------------------------------------------------------------------------------
1 | package org.whispersystems.libsignal.util;
2 |
3 | import org.whispersystems.libsignal.IdentityKey;
4 |
5 | import java.util.Comparator;
6 |
7 | public class IdentityKeyComparator extends ByteArrayComparator implements Comparator {
8 |
9 | @Override
10 | public int compare(IdentityKey first, IdentityKey second) {
11 | return compare(first.getPublicKey().serialize(), second.getPublicKey().serialize());
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/java/src/main/java/org/whispersystems/libsignal/util/KeyHelper.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (C) 2014-2016 Open Whisper Systems
3 | *
4 | * Licensed according to the LICENSE file in this repository.
5 | */
6 | package org.whispersystems.libsignal.util;
7 |
8 | import org.whispersystems.libsignal.IdentityKey;
9 | import org.whispersystems.libsignal.IdentityKeyPair;
10 | import org.whispersystems.libsignal.InvalidKeyException;
11 | import org.whispersystems.libsignal.ecc.Curve;
12 | import org.whispersystems.libsignal.ecc.ECKeyPair;
13 | import org.whispersystems.libsignal.state.PreKeyRecord;
14 | import org.whispersystems.libsignal.state.SignedPreKeyRecord;
15 |
16 | import java.security.NoSuchAlgorithmException;
17 | import java.security.SecureRandom;
18 | import java.util.LinkedList;
19 | import java.util.List;
20 |
21 | /**
22 | * Helper class for generating keys of different types.
23 | *
24 | * @author Moxie Marlinspike
25 | */
26 | public class KeyHelper {
27 |
28 | private KeyHelper() {}
29 |
30 | /**
31 | * Generate an identity key pair. Clients should only do this once,
32 | * at install time.
33 | *
34 | * @return the generated IdentityKeyPair.
35 | */
36 | public static IdentityKeyPair generateIdentityKeyPair() {
37 | ECKeyPair keyPair = Curve.generateKeyPair();
38 | IdentityKey publicKey = new IdentityKey(keyPair.getPublicKey());
39 | return new IdentityKeyPair(publicKey, keyPair.getPrivateKey());
40 | }
41 |
42 | /**
43 | * Generate a registration ID. Clients should only do this once,
44 | * at install time.
45 | *
46 | * @param extendedRange By default (false), the generated registration
47 | * ID is sized to require the minimal possible protobuf
48 | * encoding overhead. Specify true if the caller needs
49 | * the full range of MAX_INT at the cost of slightly
50 | * higher encoding overhead.
51 | * @return the generated registration ID.
52 | */
53 | public static int generateRegistrationId(boolean extendedRange) {
54 | try {
55 | SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");
56 | if (extendedRange) return secureRandom.nextInt(Integer.MAX_VALUE - 1) + 1;
57 | else return secureRandom.nextInt(16380) + 1;
58 | } catch (NoSuchAlgorithmException e) {
59 | throw new AssertionError(e);
60 | }
61 | }
62 |
63 | public static int getRandomSequence(int max) {
64 | try {
65 | return SecureRandom.getInstance("SHA1PRNG").nextInt(max);
66 | } catch (NoSuchAlgorithmException e) {
67 | throw new AssertionError(e);
68 | }
69 | }
70 |
71 | /**
72 | * Generate a list of PreKeys. Clients should do this at install time, and
73 | * subsequently any time the list of PreKeys stored on the server runs low.
74 | *
75 | * PreKey IDs are shorts, so they will eventually be repeated. Clients should
76 | * store PreKeys in a circular buffer, so that they are repeated as infrequently
77 | * as possible.
78 | *
79 | * @param start The starting PreKey ID, inclusive.
80 | * @param count The number of PreKeys to generate.
81 | * @return the list of generated PreKeyRecords.
82 | */
83 | public static List generatePreKeys(int start, int count) {
84 | List results = new LinkedList<>();
85 |
86 | start--;
87 |
88 | for (int i=0;i {
9 | private final T1 v1;
10 | private final T2 v2;
11 |
12 | public Pair(T1 v1, T2 v2) {
13 | this.v1 = v1;
14 | this.v2 = v2;
15 | }
16 |
17 | public T1 first(){
18 | return v1;
19 | }
20 |
21 | public T2 second(){
22 | return v2;
23 | }
24 |
25 | public boolean equals(Object o) {
26 | return o instanceof Pair &&
27 | equal(((Pair) o).first(), first()) &&
28 | equal(((Pair) o).second(), second());
29 | }
30 |
31 | public int hashCode() {
32 | return first().hashCode() ^ second().hashCode();
33 | }
34 |
35 | private boolean equal(Object first, Object second) {
36 | if (first == null && second == null) return true;
37 | if (first == null || second == null) return false;
38 | return first.equals(second);
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/java/src/main/java/org/whispersystems/libsignal/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.libsignal.util.guava;
18 |
19 | import static org.whispersystems.libsignal.util.guava.Preconditions.checkNotNull;
20 |
21 |
22 |
23 | import java.util.Collections;
24 | import java.util.Set;
25 |
26 |
27 | /**
28 | * Implementation of an {@link Optional} not containing a reference.
29 | */
30 |
31 | final class Absent extends Optional