Skip to main content

Base64 Encoding, Decoding On The Device

Base64 encoding (and decoding) is available to agents through the imp API’s http object, but it is not available on the device. This is because base64-encoded data can be encountered when interacting with cloud services, but rarely on the device side of the application. However, there are occasions when your device code might need to make use of this functionality.

For example, if you are storing device-generated information in JSON format, you can not currently store blobs in the structure: blobs are not part of JSON specifications. However, you can use the code below to base64-encode your blobs and then add the resulting strings to your JSON structure. Of course, your JSON decoder at the other end will need to know that any base64-encoded strings it encounters should be parsed as binary data.

Sample Code

The following code provides base64 encoding and decoding. It includes a table, base64, which contains two functions, encode() and decode() which, respectively, generate a base64 encoding of a blob or string, and convert a base64 string into a readable string.

base64 <- {
    // The standard base64 encoding characters
    "charset": "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",

    // 'encode()' takes a string or blob as input and returns the input 
    // encoded as a base64 string
    "encode": function(input) {
        local output = "";
        // Copy the input as it may be changed by the code below
        local data = input.tostring();
        // Add one or two padding bytes
        while (data.len() % 3 != 0) data += "\x00";
        // Perform the encode
        for (local i = 0 ; i < data.len() ; i += 3) {
            // Combine three input bytes into a single value
            local bitfield = (data[i] << 16) | (data[i + 1] << 8) | data[i + 2];
            local padFlag = false;
            for (local j = 0 ; j < 4 ; j++) {
                // Select a group of bits
                local shift = 6 * (3 - j);
                local group = (bitfield & (0x3F << shift)) >> shift;
                // Display the base64 padding indicators as required
                if (group == 0 && padFlag) {
                    output += "==".slice(0, 4 - j);
                    break;
                }
                // Display the current character
                output += charset[group].tochar();
                padFlag = false;
                // Check for what may be the last bits of the source
                if (j == 1 && (group & 0x0F) == 0) padFlag = true;
                if (j == 2 && (group & 0x03) == 0) padFlag = true;
            }
        }
        return output;
    },

    // 'decode()' takes a base64 string and returns the decoded string. If the source
    // was a blob, you will need to use the blob method .writestring() to add this
    // function's output to a new blob
    "decode": function(input) {
        local output = "";
        // Determine if we need to handle any bit padding from the encode
        local padBytes = 0;
        if (input[input.len() - 1] == 61) padBytes = 1;
        if (input[input.len() - 2] == 61) padBytes = 2;
        for (local i = 0 ; i < input.len() ; i += 4) {
            local bitfield = (charset.find(input[i].tochar())) << 18
            bitfield = bitfield | ((charset.find(input[i + 1].tochar())) << 12)
            bitfield = bitfield | ((charset.find(input[i + 2].tochar())) << 6)
            bitfield = bitfield |  (charset.find(input[i + 3].tochar()));
            for (local j = 0 ; j < 3 ; j++) {
                if ((i == input.len() - 4) && (2 - j == padBytes - 1)) break;
                local shift = 8 * (2 - j);
                local byte = (bitfield & (255 << shift)) >> shift;
                output += byte.tochar();
            }
        }
        return output;
    }
}

Usage Example

local source = @"Man is distinguished, not only by his reason, but by this singular passion from other animals,
which is a lust of the mind, that by a perseverance of delight in the continued and indefatigable generation of knowledge,
exceeds the short vehemence of any carnal pleasure.";
local encoded = base64.encode(source);
server.log(encoded);
local decoded = base64.decode(encoded)
server.log(decoded);
assert (source == decoded);

Further Reading