Search in sources :

Example 1 with BEncodedList

use of org.cyanogenmod.pushsms.bencode.BEncodedList 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()));
}
Also used : ByteBufferList(com.koushikdutta.async.ByteBufferList) BEncodedDictionary(org.cyanogenmod.pushsms.bencode.BEncodedDictionary) BEncodedList(org.cyanogenmod.pushsms.bencode.BEncodedList)

Example 2 with BEncodedList

use of org.cyanogenmod.pushsms.bencode.BEncodedList in project PushSms by koush.

the class GcmText method parse.

public static GcmText parse(GcmSocket gcmSocket, BEncodedDictionary message) {
    try {
        GcmText ret = new GcmText();
        ret.destAddr = gcmSocket.getNumber();
        ret.scAddr = message.getString("sca");
        BEncodedList texts = message.getBEncodedList("ts");
        for (int i = 0; i < texts.size(); i++) {
            ret.texts.add(texts.getString(i));
        }
        return ret;
    } catch (Exception e) {
        Log.e(LOGTAG, "Parse exception", e);
        return null;
    }
}
Also used : BEncodedList(org.cyanogenmod.pushsms.bencode.BEncodedList)

Example 3 with BEncodedList

use of org.cyanogenmod.pushsms.bencode.BEncodedList 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);
    }
}
Also used : ByteBufferList(com.koushikdutta.async.ByteBufferList) SecretKeySpec(javax.crypto.spec.SecretKeySpec) Signature(java.security.Signature) BEncodedDictionary(org.cyanogenmod.pushsms.bencode.BEncodedDictionary) Cipher(javax.crypto.Cipher) BEncodedList(org.cyanogenmod.pushsms.bencode.BEncodedList)

Example 4 with BEncodedList

use of org.cyanogenmod.pushsms.bencode.BEncodedList in project PushSms by koush.

the class GcmSocket method write.

@Override
public void write(ByteBufferList bb) {
    try {
        assert registration != null;
        // construct a gcm json post object
        JsonObject post = new JsonObject();
        JsonArray regs = new JsonArray();
        regs.add(new JsonPrimitive(registration.registrationId));
        post.add("registration_ids", regs);
        JsonObject data = new JsonObject();
        post.add("data", data);
        // serialize the unencrypted message
        BEncodedDictionary envelope = new BEncodedDictionary();
        // plop in the data we want to deliver
        envelope.put("p", bb.getAllByteArray());
        // include a sequence number to prevent replay attacks
        envelope.put("lsn", registration.localSequenceNumber++);
        // let the other end know what their sequence number is
        // just in case we're out of sync
        envelope.put("rsn", registration.remoteSequenceNumber);
        // sign the data so authenticity can be verified
        BEncodedDictionary signedMessage = new BEncodedDictionary();
        byte[] signedEnvelope = envelope.toByteArray();
        Signature signer = Signature.getInstance("SHA1withRSA");
        signer.initSign(gcmConnectionManager.privateKey);
        signer.update(signedEnvelope);
        byte[] signature = signer.sign();
        signedMessage.put("d", signedEnvelope);
        signedMessage.put("s", signature);
        // generate a symmetric key to be encrypted by the remote public key,
        // and encrypt that. Asymmetric keys have payload limitations.
        // http://en.wikipedia.org/wiki/Hybrid_cryptosystem
        // http://stackoverflow.com/questions/6788018/android-encryption-decryption-with-aes
        // see also the added benefit of multi recipient scenarios:
        // http://security.stackexchange.com/questions/20134/in-pgp-why-not-just-encrypt-message-with-recipients-public-key-why-the-meta-e/20145#20145
        KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
        SecureRandom secureRandom = new SecureRandom();
        keyGenerator.init(128, secureRandom);
        byte[] symmetricKey = keyGenerator.generateKey().getEncoded();
        // Signature AND message should both be encrypted, as much as possible should be opaque:
        // http://stackoverflow.com/questions/6587023/should-i-encrypt-the-signature
        Cipher cipher = Cipher.getInstance("AES");
        cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(symmetricKey, "AES"));
        byte[] encryptedSignedMessage = cipher.doFinal(signedMessage.toByteArray());
        // Encrypt the asymmetric key so only the remote can decrypt it, and
        // thus decrypt the message payload
        cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
        cipher.init(Cipher.ENCRYPT_MODE, registration.remotePublicKey);
        byte[] encryptedSymmetricKey = cipher.doFinal(symmetricKey);
        // put the encrypted symmetric key and encrypted payload into the JSON, and send it off
        BEncodedDictionary payload = new BEncodedDictionary();
        // though right now we only put in a single encrypted symmetric key,
        // wrap that key in an array. this will future proof the protocol
        // for later possible multi recipient scenarios.
        BEncodedList encryptedSymmetricKeys = new BEncodedList();
        encryptedSymmetricKeys.add(encryptedSymmetricKey);
        payload.put("esk", encryptedSymmetricKeys);
        payload.put("esm", encryptedSignedMessage);
        // now base64 the entire encrypted payload
        data.add("p", new JsonPrimitive(Base64.encodeToString(payload.toByteArray(), Base64.NO_WRAP)));
        // include our claimed number, as we would a text, so the other end can find us
        data.addProperty("f", gcmConnectionManager.from);
        // ship it
        Ion.with(context).load("https://android.googleapis.com/gcm/send").setHeader("Authorization", "key=" + gcmConnectionManager.gcmApiKey).setJsonObjectBody(post).as(GcmResults.class).setCallback(new FutureCallback<GcmResults>() {

            @Override
            public void onCompleted(Exception e, GcmResults result) {
                if (result == null || result.failure != 0 || result.success == 0) {
                    if (e == null) {
                        e = new Exception("gcm server failure");
                        // to trigger a refresh
                        if (registration.isRegistered())
                            registration.unregister();
                    }
                    report(e);
                    return;
                }
                currentBackoff = GRACE_DEFAULT;
            }
        });
    } catch (Exception e) {
        Log.e(LOGTAG, "Send exception", e);
        report(e);
    }
}
Also used : JsonPrimitive(com.google.gson.JsonPrimitive) JsonObject(com.google.gson.JsonObject) SecureRandom(java.security.SecureRandom) BEncodedDictionary(org.cyanogenmod.pushsms.bencode.BEncodedDictionary) BEncodedList(org.cyanogenmod.pushsms.bencode.BEncodedList) JsonArray(com.google.gson.JsonArray) SecretKeySpec(javax.crypto.spec.SecretKeySpec) Signature(java.security.Signature) Cipher(javax.crypto.Cipher) KeyGenerator(javax.crypto.KeyGenerator)

Aggregations

BEncodedList (org.cyanogenmod.pushsms.bencode.BEncodedList)4 BEncodedDictionary (org.cyanogenmod.pushsms.bencode.BEncodedDictionary)3 ByteBufferList (com.koushikdutta.async.ByteBufferList)2 Signature (java.security.Signature)2 Cipher (javax.crypto.Cipher)2 SecretKeySpec (javax.crypto.spec.SecretKeySpec)2 JsonArray (com.google.gson.JsonArray)1 JsonObject (com.google.gson.JsonObject)1 JsonPrimitive (com.google.gson.JsonPrimitive)1 SecureRandom (java.security.SecureRandom)1 KeyGenerator (javax.crypto.KeyGenerator)1