Skip to content

Commit f4fa2dd

Browse files
authored
Merge pull request #249 from pusher/dev-decrypt-plus-retry
Dev decrypt plus retry
2 parents d346cea + e694400 commit f4fa2dd

File tree

6 files changed

+326
-28
lines changed

6 files changed

+326
-28
lines changed

src/main/java/com/pusher/client/channel/PrivateEncryptedChannelEventListener.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,5 @@
66
*/
77
public interface PrivateEncryptedChannelEventListener extends PrivateChannelEventListener {
88

9-
// TODO: add onDecryptionFailure
9+
void onDecryptionFailure(String event, String reason);
1010
}

src/main/java/com/pusher/client/channel/impl/ChannelImpl.java

Lines changed: 31 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -89,33 +89,29 @@ public boolean isSubscribed() {
8989

9090
/* InternalChannel implementation */
9191

92+
@Override
93+
public PusherEvent prepareEvent(String event, String message) {
94+
return GSON.fromJson(message, PusherEvent.class);
95+
}
96+
9297
@Override
9398
public void onMessage(final String event, final String message) {
9499

95100
if (event.equals(SUBSCRIPTION_SUCCESS_EVENT)) {
96101
updateState(ChannelState.SUBSCRIBED);
97-
}
98-
else {
99-
final Set<SubscriptionEventListener> listeners;
100-
synchronized (lock) {
101-
final Set<SubscriptionEventListener> sharedListeners = eventNameToListenerMap.get(event);
102-
if (sharedListeners != null) {
103-
listeners = new HashSet<SubscriptionEventListener>(sharedListeners);
104-
}
105-
else {
106-
listeners = null;
107-
}
108-
}
109-
102+
} else {
103+
final Set<SubscriptionEventListener> listeners = getInterestedListeners(event);
110104
if (listeners != null) {
111-
for (final SubscriptionEventListener listener : listeners) {
112-
final PusherEvent e = GSON.fromJson(message, PusherEvent.class);
113-
factory.queueOnEventThread(new Runnable() {
114-
@Override
115-
public void run() {
116-
listener.onEvent(e);
117-
}
118-
});
105+
final PusherEvent pusherEvent = prepareEvent(event, message);
106+
if (pusherEvent != null) {
107+
for (final SubscriptionEventListener listener : listeners) {
108+
factory.queueOnEventThread(new Runnable() {
109+
@Override
110+
public void run() {
111+
listener.onEvent(pusherEvent);
112+
}
113+
});
114+
}
119115
}
120116
}
121117
}
@@ -213,4 +209,18 @@ private void validateArguments(final String eventName, final SubscriptionEventLi
213209
"Cannot bind or unbind to events on a channel that has been unsubscribed. Call Pusher.subscribe() to resubscribe to this channel");
214210
}
215211
}
212+
213+
protected Set<SubscriptionEventListener> getInterestedListeners(String event) {
214+
synchronized (lock) {
215+
216+
final Set<SubscriptionEventListener> sharedListeners =
217+
eventNameToListenerMap.get(event);
218+
219+
if (sharedListeners == null) {
220+
return null;
221+
}
222+
223+
return new HashSet<>(sharedListeners);
224+
}
225+
}
216226
}

src/main/java/com/pusher/client/channel/impl/InternalChannel.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,16 @@
33
import com.pusher.client.channel.Channel;
44
import com.pusher.client.channel.ChannelEventListener;
55
import com.pusher.client.channel.ChannelState;
6+
import com.pusher.client.channel.PusherEvent;
67

78
public interface InternalChannel extends Channel, Comparable<InternalChannel> {
89

910
String toSubscribeMessage();
1011

1112
String toUnsubscribeMessage();
1213

14+
PusherEvent prepareEvent(String event, String message);
15+
1316
void onMessage(String event, String message);
1417

1518
void updateState(ChannelState state);

src/main/java/com/pusher/client/channel/impl/PrivateEncryptedChannelImpl.java

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,21 @@
55
import com.pusher.client.channel.ChannelState;
66
import com.pusher.client.channel.PrivateEncryptedChannel;
77
import com.pusher.client.channel.PrivateEncryptedChannelEventListener;
8+
import com.pusher.client.channel.PusherEvent;
89
import com.pusher.client.channel.SubscriptionEventListener;
910
import com.pusher.client.connection.ConnectionEventListener;
1011
import com.pusher.client.connection.ConnectionState;
1112
import com.pusher.client.connection.ConnectionStateChange;
1213
import com.pusher.client.connection.impl.InternalConnection;
14+
import com.pusher.client.crypto.nacl.AuthenticityException;
1315
import com.pusher.client.crypto.nacl.SecretBoxOpener;
1416
import com.pusher.client.crypto.nacl.SecretBoxOpenerFactory;
1517
import com.pusher.client.util.Factory;
1618
import com.pusher.client.util.internal.Base64;
1719

1820
import java.util.LinkedHashMap;
1921
import java.util.Map;
22+
import java.util.Set;
2023

2124
public class PrivateEncryptedChannelImpl extends ChannelImpl implements PrivateEncryptedChannel {
2225

@@ -124,6 +127,69 @@ public void updateState(ChannelState state) {
124127
}
125128
}
126129

130+
@Override
131+
public PusherEvent prepareEvent(String event, String message) {
132+
133+
try {
134+
return decryptMessage(message);
135+
} catch (AuthenticityException e1) {
136+
137+
// retry once only.
138+
disposeSecretBoxOpener();
139+
authenticate();
140+
141+
try {
142+
return decryptMessage(message);
143+
} catch (AuthenticityException e2) {
144+
// deliberately not destroying the secretBoxOpener so the next message
145+
// has an opportunity to fetch a new key and decrypt
146+
notifyListenersOfDecryptFailure(event, "Failed to decrypt message.");
147+
}
148+
}
149+
150+
return null;
151+
}
152+
153+
private void notifyListenersOfDecryptFailure(final String event, final String reason) {
154+
Set<SubscriptionEventListener> listeners = getInterestedListeners(event);
155+
if (listeners != null) {
156+
for (SubscriptionEventListener listener : listeners) {
157+
((PrivateEncryptedChannelEventListener)listener).onDecryptionFailure(
158+
event, reason);
159+
}
160+
}
161+
}
162+
163+
private class EncryptedReceivedData {
164+
String nonce;
165+
String ciphertext;
166+
167+
public byte[] getNonce() {
168+
return Base64.decode(nonce);
169+
}
170+
171+
public byte[] getCiphertext() {
172+
return Base64.decode(ciphertext);
173+
}
174+
}
175+
176+
private PusherEvent decryptMessage(String message) {
177+
178+
Map<String, Object> receivedMessage =
179+
GSON.<Map<String, Object>>fromJson(message, Map.class);
180+
181+
final EncryptedReceivedData encryptedReceivedData =
182+
GSON.fromJson((String)receivedMessage.get("data"), EncryptedReceivedData.class);
183+
184+
String decryptedData = new String(secretBoxOpener.open(
185+
encryptedReceivedData.getCiphertext(),
186+
encryptedReceivedData.getNonce()));
187+
188+
receivedMessage.replace("data", decryptedData);
189+
190+
return new PusherEvent(receivedMessage);
191+
}
192+
127193
private void disposeSecretBoxOpener() {
128194
if (secretBoxOpener != null) {
129195
secretBoxOpener.clearKey();

src/main/java/com/pusher/client/example/PrivateEncryptedChannelExampleApp.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,4 +86,10 @@ public void onError(String message, String code, Exception e) {
8686
code,
8787
e));
8888
}
89+
90+
@Override
91+
public void onDecryptionFailure(String event, String reason) {
92+
System.out.println(String.format(
93+
"An error was received decrypting message for event:[%s] - reason: [%s]", event, reason));
94+
}
8995
}

0 commit comments

Comments
 (0)