Application Integration

Arrow Encryption for Data in Transit

Appcelerator mobile apps use Secure Sockets Layer (SSL) for encrypting and decrypting all data transmitted and received by the device. However, for certain types of apps, one may wish to add another layer of encryption for added security. This post describes how to programmatically add additional encryption for data in transit between an Appcelerator app and an ArrowDB as illustrated below.

Background

The basic idea is to add a pre block to your Arrow model for decrypting data on a POST or PUT from the client app. This will decrypt data sent by the client app. Also, add a post block for encrypting data being sent to the client app on a GET.

For the purpose of this post, we will use a very simple XOR encryption JavaScript function from Henry Algus which is also the decryption function and is shown below:

exports.jsEncode = function(s,k) {
    var enc = "";
    var str = "";
    str = s.toString();
    for (var i = 0; i < s.length; i++) {
        var a = s.charCodeAt(i);
        var b = a ^ k;
        enc = enc + String.fromCharCode(b);
    }
    return enc;
}

Furthermore, since the Appcelerator Platform is a full stack JavaScript platform, this exact same function is used for both encryption and decryption in both the Appcelerator mobile app and the Arrow app.

Note that this XOR encryption should not be used for production apps and is strictly being used for demonstration purposes. In a production app, you may want to look at more secure encryption libraries such as the Appcelerator Premium “Crypto” Module, Securely, crypto-js or sjcl.

Arrow Model

Here is a simple ArrowDB model, database, and a pre (decrypt) and post ((encrypt)) block that illustrates how to encrypt and decrypt data in Arrow:

database.js

var Arrow = require("arrow");
var Model = Arrow.createModel("database",{
    "fields": {
        "name": {
            "type": "String"
        }
    },
    "connector": "appc.arrowdb",
    "actions": [
        "create",
        "read",
        "update",
        "delete",
        "deleteAll"
    ],
    "before": "decrypt",
    "after": "encrypt",
    "singular": "database",
    "plural": "databases"
});
module.exports = Model;

decrypt.js

var ENC_KEY = '123';
var Arrow = require('arrow');
var Utils = require('../lib/utils');
var PreBlock = Arrow.Block.extend({
    name: 'decrypt',
    description: 'will decrypt data from mobile app',
    action: function (req, resp, next) {
        if((req.method==="POST" || req.method==="PUT")) {
            req.params.name = Utils.jsEncode(req.params.name, ENC_KEY);
        }
        next();
    }
});
module.exports = PreBlock;

encrypt.js

var ENC_KEY = '123';
var Arrow = require('arrow');
var Utils = require('../lib/utils');
var PostBlock = Arrow.Block.extend({
    name: 'encrypt',
    description: 'will encrypt data to mobile app',
    action: function (req, resp, next) {
        if(req.method==="GET") {
            var body = JSON.parse(resp.body);
            var data = body[body.key];
            var dataLen = data.length;
            if(dataLen){ //findAll
                data.forEach(function (_row, _index) {
                    _row.name = Utils.jsEncode(data[_index].name, ENC_KEY);
                });
            } else { //findOne
                if(data.name) {
                        data.name = Utils.jsEncode(data.name, ENC_KEY);
                }
            }
            resp.success(body[body.key], next);
        } else {
                next();
        }
    }
});
module.exports = PostBlock;

In the example above, a key of ‘123’ is used to encrypt and decrypt the data.

Mobile App

The mobile app should encrypt the data prior to POSTing data using the jsEncode function with the same key that is used in the Arrow app (e.g. ‘123’). The example below illustrates encrypting the value of a TextField with an Alloy id of “nameTF” prior to POSTing the data in an Appcelerator mobile app.

var ENC_KEY = '123';
var xhr = Ti.Network.createHTTPClient({
    onload: function onLoad() {
        Ti.API.info("Loaded: " + this.status + ": " + this.responseText);
    },
    onerror: function onError() {
        Ti.API.info("Errored: " + this.status + ": " + this.responseText);
    }
});
xhr.open("POST","https://127.0.0.1:8080/api/database");
var authstr = 'Basic ' + Ti.Utils.base64encode('TCDiEh3jpghZ2rQ7ckSr1NhGo2AZURwG:');
xhr.setRequestHeader("Authorization", authstr);
xhr.setRequestHeader("Content-Type","application/json");
xhr.send(JSON.stringify({
    "name": jsEncode($.nameTF.value, ENC_KEY)
}));

After a GET is called, the encrypted data received by the Appcelerator mobile app must be decrypted as illustrated below. In the example below, the received data is used to populate a TableView with an Alloy id of “TV”.

var ENC_KEY = '123';
var xhr = Ti.Network.createHTTPClient({
    onload: function onLoad() {
        Ti.API.info("Loaded: " + this.status + ": " + this.responseText);
        var body = JSON.parse(this.responseText);
        var data = body[body.key];
        var rows = [];
        if(data.length>0) {
          _.each(data, function(item) {
              rows.push(Alloy.createController('row', {
              name: jsEncode(item.name, ENC_KEY)
            }).getView());
          });
        }
        $.TV.setData(rows);
    },
    onerror: function onError() {
        Ti.API.info("Errored: " + this.status + ": " + this.responseText);
    }
});
xhr.open("GET","https://127.0.0.1:8080/api/database");
var authstr = 'Basic ' + Ti.Utils.base64encode('TCDiEh3jpghZ2rQ7ckSr1NhGo2AZURwG:');
xhr.setRequestHeader("Authorization", authstr);
xhr.send();

Viewing Data in ArrowDB

Recall that we are only encrypting the data in transit. The data in the ArrowDB is unencrypted. However, since we have added an encryption post block to the model for all GET operations, when we try to view the ArrowDB data in the Arrow console, we will see encrypted data as shown below:

So, how do we view/access the unencrypted data?

One way is to use the Appcelerator Dashboard, select the Arrow app and navigate to the ArrowDB, and view the data in the Manage Data -> Custom Object section as shown below:

Another technique for programmatically receiving unencrypted data is to modify the post block to look for a specific header and value and then send the data unencrypted. This code is provided here.

An example curl command using this technique is shown below:

curl -u TCDiEh3jpghZ2rQ7ckSr1NhGo2AZURwG: "https://127.0.0.1:8080/api/database" -H "admin-secret:admin-secret"

Results in the following reply:

{
    "success": true,
    "request-id": "b2a5d659-516d-49ba-a33e-629de4bab195",
    "key": "databases",
    "databases": [
        {
            "id": "564357e0dc26a0090d2f5b82",
            "name": "Leor"
        },
        {
            "id": "563d79043d24bb09100b7f6c",
            "name": "Lillian"
        },
        {
            "id": "563d78c8dc26a0090d0adfb3",
            "name": "Kim"
        },
        {
            "id": "563d78afdc26a009050af1b3",
            "name": "Nate"
        }
    ]
}

Compare this to using the curl command without the header:

curl -u TCDiEh3jpghZ2rQ7ckSr1NhGo2AZURwG: "https://127.0.0.1:8080/api/database"

which returns the following encrypted data:

{
    "success": true,
    "request-id": "bf8cbf08-d2f2-4a24-a9a5-a37c0841535c",
    "key": "databases",
    "databases": [
        {
            "id": "564357e0dc26a0090d2f5b82",
            "name": "7\u001e\u0014\t"
        },
        {
            "id": "563d79043d24bb09100b7f6c",
            "name": "7\u0012\u0017\u0017\u0012\u001a\u0015"
        },
        {
            "id": "563d78c8dc26a0090d0adfb3",
            "name": "0\u0012\u0016"
        },
        {
            "id": "563d78afdc26a009050af1b3",
            "name": "5\u001a\u000f\u001e"
        }
    ]
}

Summary

This post demonstrates how easy Arrow makes it to encrypt data in transit. Code for this post can be found here.