use of org.cyanogenmod.pushsms.bencode.BEncodedDictionary in project PushSms by koush.
the class Registration method parse.
static Registration parse(String data) {
try {
byte[] bytes = Base64.decode(data, Base64.NO_WRAP);
BEncodedDictionary dict = BEncodedDictionary.parseDictionary(ByteBuffer.wrap(bytes));
Registration ret = new Registration();
ret.date = dict.getLong("date");
ret.registrationId = dict.getString("registration_id");
ret.endpoint = dict.getString("endpoint");
ret.localSequenceNumber = dict.getInt("local_sequence_number");
ret.remoteSequenceNumber = dict.getInt("remote_sequence_number");
ret.state = dict.getInt("state");
byte[] publicModulus = dict.getBytes("public_modulus");
byte[] publicExponent = dict.getBytes("public_exponent");
if (publicModulus != null && publicExponent != null) {
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
RSAPublicKeySpec rsaPublicKeySpec = new RSAPublicKeySpec(new BigInteger(publicModulus), new BigInteger(publicExponent));
ret.remotePublicKey = keyFactory.generatePublic(rsaPublicKeySpec);
}
return ret;
} catch (Exception e) {
return null;
}
}
use of org.cyanogenmod.pushsms.bencode.BEncodedDictionary in project PushSms by koush.
the class GcmText method send.
public void send(GcmSocket gcmSocket, String id) {
// serialize the message
BEncodedDictionary message = new BEncodedDictionary();
// mark the type as a message
message.put("t", MessageTypes.MESSAGE);
// grant an id to acknowledge
message.put("id", id);
message.put("sca", scAddr);
BEncodedList texts = new BEncodedList();
message.put("ts", texts);
for (String text : this.texts) {
texts.add(text);
}
gcmSocket.write(new ByteBufferList(message.toByteArray()));
}
use of org.cyanogenmod.pushsms.bencode.BEncodedDictionary in project PushSms by koush.
the class MiddlewareService method parseGcmMessage.
private void parseGcmMessage(GcmSocket gcmSocket, ByteBufferList bb) {
try {
BEncodedDictionary message = BEncodedDictionary.parseDictionary(bb.getAllByteArray());
String messageType = message.getString("t");
if (MessageTypes.MESSAGE.equals(messageType)) {
// incoming text via gcm
GcmText gcmText = GcmText.parse(gcmSocket, message);
if (gcmText == null)
return;
// ack the message
String messageId = message.getString("id");
if (messageId != null) {
BEncodedDictionary ack = new BEncodedDictionary();
ack.put("t", MessageTypes.ACK);
ack.put("id", messageId);
gcmSocket.write(new ByteBufferList(ack.toByteArray()));
}
// synthesize a fake message for the android system
smsTransport.synthesizeMessages(gcmSocket.getNumber(), gcmText.scAddr, gcmText.texts, System.currentTimeMillis());
} else if (MessageTypes.ACK.equals(messageType)) {
// incoming ack
String messageId = message.getString("id");
if (messageId == null)
return;
GcmText gcmText = messagesAwaitingAck.remove(messageId);
if (gcmText == null)
return;
firePendingIntents(gcmText.sentIntents);
firePendingIntents(gcmText.deliveryIntents);
}
} catch (Exception e) {
Log.e(LOGTAG, "Error handling GCM socket message", e);
}
}
use of org.cyanogenmod.pushsms.bencode.BEncodedDictionary in project PushSms by koush.
the class Registration method encode.
public String encode() {
try {
BEncodedDictionary dict = new BEncodedDictionary();
if (remotePublicKey != null) {
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
RSAPublicKeySpec publicKeySpec = keyFactory.getKeySpec(remotePublicKey, RSAPublicKeySpec.class);
dict.put("public_modulus", publicKeySpec.getModulus().toByteArray());
dict.put("public_exponent", publicKeySpec.getPublicExponent().toByteArray());
}
dict.put("state", state);
dict.put("date", date);
dict.put("endpoint", endpoint);
dict.put("registration_id", registrationId);
dict.put("local_sequence_number", localSequenceNumber);
dict.put("remote_sequence_number", remoteSequenceNumber);
return Base64.encodeToString(dict.toByteArray(), Base64.NO_WRAP);
} catch (Exception e) {
return "";
}
}
use of org.cyanogenmod.pushsms.bencode.BEncodedDictionary in project PushSms by koush.
the class GcmSocket method onGcmMessage.
public void onGcmMessage(String dataString, String from) {
try {
// base64 decode the payload that contains the encrypted symmetric key
// and the corresponding encryptedSignedMessage
BEncodedDictionary encryptedKeyMessagePair = BEncodedDictionary.parseDictionary(Base64.decode(dataString, Base64.NO_WRAP));
BEncodedList encryptedSymmetricKeys = encryptedKeyMessagePair.getBEncodedList("esk");
byte[] encryptedSignedMessage = encryptedKeyMessagePair.getBytes("esm");
// to support multi recipient scenarios, the protocol sends an array
// of encrypted symmetric keys. each key is encrypted once per intended recipient.
// attempt to decode all of them, use the one that works.
byte[] symmetricKey = null;
for (int i = 0; i < encryptedSymmetricKeys.size(); i++) {
try {
byte[] encryptedSymmetricKey = encryptedSymmetricKeys.getBytes(i);
// decrypt the symmetric key
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.DECRYPT_MODE, gcmConnectionManager.privateKey);
symmetricKey = cipher.doFinal(encryptedSymmetricKey);
break;
} catch (Exception e) {
}
}
if (symmetricKey == null)
throw new Exception("could not decrypt symmetric key");
// decrypt the message
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(symmetricKey, "AES"));
BEncodedDictionary signedMessage = BEncodedDictionary.parseDictionary(cipher.doFinal(encryptedSignedMessage));
// grab the signature and the signed data, and verify the sender authenticity
byte[] signature = signedMessage.getBytes("s");
byte[] signedEnvelope = signedMessage.getBytes("d");
Signature verifier = Signature.getInstance("SHA1withRSA");
verifier.initVerify(registration.remotePublicKey);
verifier.update(signedEnvelope);
if (!verifier.verify(signature)) {
// keys or something changed? force a server refresh
registration.refresh();
throw new Exception("unable to verify signature");
}
originatingNumber = from;
BEncodedDictionary envelope = BEncodedDictionary.parseDictionary(signedEnvelope);
int seq = envelope.getInt("lsn");
if (registration.remoteSequenceNumber <= seq) {
// wtf? replay attack?
// how to handle? Ignore? prompt?
}
registration.remoteSequenceNumber = seq;
// the remote end will send us what they think our local sequence number is... just use
// the max of the two
registration.localSequenceNumber = Math.max(envelope.getInt("rsn"), registration.localSequenceNumber);
Util.emitAllData(this, new ByteBufferList(envelope.getBytes("p")));
} catch (Exception e) {
Log.e(LOGTAG, "Parse exception", e);
}
}
Aggregations