In AIR 3.0, Adobe released a capability called Native Extensions. This allows you to directly extend the capabilities of the AIR Runtime on any platform supported by AIR (iOS, Android, Windows, OSX, etc…). In my consulting work for the NitroLM licensing and security project, I’ve had a chance to really dig into what it takes to develop a cross-platform native extension in C++.

I’m using Microsoft Visual C++ Express on Windows to create a .DLL and XCode 4 on OSX to create a .framework library. In this example, we’ll follow the code of one function in NitroLM called getMessages(). This function retrieves admin-generated messages from the NitroLM servers. For example, a developer might want to send his app users a message about the availability of a new version the next time they launch the software product.

On the AS3 side, you’ll need a class to handle the integration with the native libraries. For NitroLM, this class is called LicenseClient. You can view the full contents of this file on GitHub as the AS3 portion of the NitroLM library is open-source.

https://github.com/westbam/NitroLM/blob/master/nitrolm-air/src/com/nitrolm/LicenseClient.as

In the constructor of this class, we create the ExtensionContext and optionally call a function to turn on debugging in the native code.

public static var context:ExtensionContext;

/**
 * Create a new LicenseClient instance
 *
 * @param debug If enabled, the native library will write a file called nitrolm_debug.out
 */

public function LicenseClient(debug:Boolean = false)
{
  if(!context)
  {
    context = ExtensionContext.createExtensionContext("com.nitrolm.NitroLM", "");
  }
 
  if(context && debug)
  {
    context.call("enableDebug_air");
  }
}

Next, on the actionscript side, we call the getMessages() function to retrieve our list of messages. Since we don’t want to block our GUI thread waiting on an IO server call, we have to handle an event to retrieve the data. We listen for a LicenseClientEvent whose .data property will contain an Array of messages once it comes back.

public function getMessages(email:String, version:String, days:int):void
{
  context.addEventListener(StatusEvent.STATUS, handleStatusEvent);
  context.call("getMessages_air", email, version, days);
}

You’ll notice that we are handling another type of StatusEvent here. This is a message we’ll listen for internally that will let us know that the data is ready to be retrieved on the native side. When we handle this message in handleStatusEvent(), we can then make a second call into our native library to retrieve the message data. This 2-call approach prevents us from ever locking our AIR GUI thread waiting on the native side to complete.

private function handleStatusEvent(event:StatusEvent):void
{
  context.removeEventListener(StatusEvent.STATUS, handleStatusEvent);
  var req_type:int = new int(event.code);
  var response:int = new int(event.level);
  var lce:LicenseClientEvent = new LicenseClientEvent(LicenseClientEvent.LICENSE_RESPONSE, req_type, response);
 
  switch(req_type)
  {
    case NLMConstants.REQUEST_MESSAGES:
      if(response == NLMConstants.RESPONSE_OK)
      {
        lce.data = context.call("getMessages_data");
      }
    break;
  }
 
  dispatchEvent(lce);
}

The reason for the 2-call approach is that we’re using synchronous IO on the native side. If we didn’t spin up a new thread for the IO call on the native side, the AIR GUI would freeze every time we made a server call. To solve this, the first call is made to unpackage our AS3 parameters into native data types and spin up a new Thread to handle the IO. Our thread then tosses up a StatusEvent once it’s done receiving a response from the NitroLM server. A second call is then made to getMessages_data to convert the native response data into actionscript types we can use. Let’s take a look at the native code now.

We first need to define our native initializer, finalizer, and data functions in a header file.

/*
 * AIR_COMMANDS.H
 * Native entry point for Adobe AIR
 *
 * Copyright 2011 Simplified Logic, Inc.  All Rights Reserved.
 * Author: Andrew Westberg
 */


#ifndef __AIR_COMMANDS_H_
#define __AIR_COMMANDS_H_

#include "nlm_constants.h"
#include "nlm_commands.h"
#include "FlashRuntimeExtensions.h"

#ifdef __cplusplus
extern "C" {
#endif

...
FREObject getMessages_air(FREContext ctx, void* funcData, uint32_t argc, FREObject argv[]);
FREObject getMessages_data(FREContext ctx, void* funcData, uint32_t argc, FREObject argv[]);

void ContextInitializer(void* extData, const uint8_t* ctxType, FREContext ctx, uint32_t* numFunctions, const FRENamedFunction** functions);
void ContextFinalizer(FREContext ctx);
void ExtInitializer(void** extData, FREContextInitializer* ctxInitializer, FREContextFinalizer* ctxFinalizer);
void ExtFinalizer(void* extData);

#ifdef __cplusplus
} //extern "C"
#endif

#endif //__AIR_COMMANDS_H_

Our initializer function is called when we first create the extension context. We need to tell it which functions the context has defined.

/*
 * AIR_COMMANDS.C
 * Exported functions for Adobe AIR (and related non-exported functions)
 *
 * Copyright 2011 Simplified Logic, Inc.  All Rights Reserved.
 *
 * Written by Andrew Westberg
 */


#include <stdlib.h>
#include "air_commands.h"
#include "CAsyncTask.h"
#include "storage.h"
#include "Debug.h"

#ifdef __cplusplus
extern "C" {
#endif

static CThread asyncThread;
static CAsyncTask asyncTask;

void ExtInitializer(void** extData, FREContextInitializer* ctxInitializer, FREContextFinalizer* ctxFinalizer)
{
  *ctxInitializer = &ContextInitializer;
  *ctxFinalizer = &ContextFinalizer;
}

void ExtFinalizer(void* extData)
{
}

void ContextFinalizer(FREContext ctx)
{
  gracefulShutdown();
}

void ContextInitializer(void* extData, const uint8_t* ctxType, FREContext ctx, uint32_t* numFunctions, const FRENamedFunction** functions)
{
  asyncThread.m_gracefulShutdown = FALSE; //terminate thread hard at shutdown because we're inside a .DLL

  FRENamedFunction *func;

  *numFunctions = 50;
 
  func = (FRENamedFunction*) malloc(sizeof(FRENamedFunction) * (*numFunctions));

  ...
 
  func[24].name = (const uint8_t*) "getMessages_air";
  func[24].functionData = NULL;
  func[24].function = &getMessages_air;

  func[46].name = (const uint8_t*) "getMessages_data";
  func[46].functionData = NULL;
  func[46].function = &getMessages_data;

  ...
 
  *functions = func;
}

#ifdef __cplusplus
} //extern "C"
#endif

In the function getMessages_air(), we parse out our input parameters and store them in our thread task and then spin up the task on our thread for processing. The threading library I’m using was developed by Walter Capers and can be found under the CodeProject article Creating a C++ Thread Class.

FREObject getMessages_air(FREContext ctx, void* funcData, uint32_t argc, FREObject argv[])
{
    while(asyncTask.Status() == TaskStatusWaitingOnQueue || asyncTask.Status() == TaskStatusBeingProcessed)
    {
        //wait until our task is ready to process something new
        Sleep(10);
    }

    asyncTask.initialize(ctx, REQUEST_MESSAGES, argc, argv);

    if(asyncThread.Event(&asyncTask))
        Debug::logDebug("submitted getMessagesTask for background processing");
    else
        Debug::logDebug("ERROR submitting getMessagesTask for background processing");

    return NULL;
}

The asyncTask.initialize() method parses out our input parameters from actionscript and stores them in native object types so our thread can work with them. It’s important to run this code _before_ spinning up the thread because your thread WILL NOT have access to the FRE functions from Adobe. The only capabilities your thread has from Adobe is to toss up a StatusEvent using FREDispatchStatusEventAsync() when it’s done. You’ll also notice that you have to do a lot of error handling on just about every FRE function you call.

void CAsyncTask::initialize(FREContext ctx, const int requestType, int argc, FREObject *argv)
{
  //reset the task
  ThreadId_t id = 0;
  SetTaskStatus(TaskStatusNotSubmitted);
  Thread(&id);

  CAsyncTask::ctx = ctx;
  CAsyncTask::requestType = requestType;

  uint32_t len = -1;
  const uint8_t *email = 0;
  const uint8_t *version = 0;
  int32_t days = 0;
 
  switch(requestType)
  {
    case REQUEST_MESSAGES:
      if((res=FREGetObjectAsUTF8(argv[0], &len, &email)) != FRE_OK)
      {
        Debug::logDebug("ERROR: FREGetObjectAsUTF8(argv[0], &len, &email) = %d", res);
        dispatchEvent(REQUEST_MESSAGES, RESPONSE_INTERNAL_CLIENT_ERROR);
        return;
      }
      strcpy(CAsyncTask::email, (const char*)email);

      if(argv[1] != NULL)
      {
        if((res=FREGetObjectAsUTF8(argv[1], &len, &version)) != FRE_OK)
        {
          Debug::logDebug("ERROR: FREGetObjectAsUTF8(argv[1], &len, &version) = %d", res);
          dispatchEvent(REQUEST_MESSAGES, RESPONSE_INTERNAL_CLIENT_ERROR);
          return;
        }
        strcpy(CAsyncTask::version, (const char*)version);
      }
      else
        strcpy(CAsyncTask::version, "");

      if((res=FREGetObjectAsInt32(argv[2], &days)) != FRE_OK)
      {
        Debug::logDebug("ERROR: FREGetObjectAsUTF8(argv[2], &days) = %d", res);
        dispatchEvent(REQUEST_MESSAGES, RESPONSE_INTERNAL_CLIENT_ERROR);
        return;
      }
      CAsyncTask::days = days;
    break;
  }
}

Our CThread will call the Task() method of CAsyncTask once it starts processing. In this function, we make our synchronous IO call to the NitroLM server by calling the internal getMessages() method and dispatch an event once it’s complete.

void CAsyncTask::dispatchEvent(const int req_type, const int response)
{
  FREResult res;
  char code[5];
  char level[5];

  sprintf(code, "%d", req_type);
  sprintf(level, "%d", response);
  if((res=FREDispatchStatusEventAsync(ctx, (const uint8_t*)code, (const uint8_t*)level)) != FRE_OK)
  {
    Debug::logDebug("ERROR: FREDispatchStatusEventAsync(ctx, (const uint8_t*)code, (const uint8_t*)level) = %d", res);
    return;
  }
}

BOOL CAsyncTask::Task()
{
  ThreadId_t id = 0;
  Thread(&id);

  int ret;

  switch(CAsyncTask::requestType)
  {
    case REQUEST_MESSAGES:
      outMsgs = NULL;
      ret = getMessages(email, version, days, &outMsgs);
      dispatchEvent(REQUEST_MESSAGES, ret);
    break;
  }

  return TRUE;
}

Finally, we package the data as an Array of Objects when getMessages_data() is called from AIR.

FREObject getMessages_data(FREContext ctx, void* funcData, uint32_t argc, FREObject argv[])
{
  int i;
  FREResult res;
  FREObject result;
  FREObject subject;
  FREObject body;
  FREObject message;
  FREObject thrownException;
  FREObject stackTrace;
  const uint8_t *stack;
  uint32_t len;

  if(asyncTask.outMsgs != NULL)
  {
    if((res=FRENewObject((const uint8_t*)"Array", 0, NULL, &result, &thrownException)) != FRE_OK)
    {
      Debug::logDebug("ERROR: FRENewObject((const uint8_t*)\"Array\", 0, NULL, &result, &thrownException) = %d", res);
      freeMessages(asyncTask.outMsgs);
      FRECallObjectMethod(thrownException, (const uint8_t*)"getStackTrace", 0, NULL, &stackTrace, NULL);
      FREGetObjectAsUTF8(stackTrace, &len, &stack);
      Debug::logDebug((const char*)stack);
      return NULL;
    }

    FRESetArrayLength(result, asyncTask.outMsgs->size);

    for(i=0;i<asyncTask.outMsgs->size;i++)
    {
      subject = 0;
      if(asyncTask.outMsgs->list[i].subject != NULL)
      {
        if((res=FRENewObjectFromUTF8(strlen(asyncTask.outMsgs->list[i].subject), (const uint8_t*)asyncTask.outMsgs->list[i].subject, &subject)) != FRE_OK)
        {
          Debug::logDebug("ERROR: FRENewObjectFromUTF8(strlen(asyncTask.outMsgs->list[i].subject), (const uint8_t*)asyncTask.outMsgs->list[i].subject, &subject) = %d", res);
          freeMessages(asyncTask.outMsgs);
          return NULL;
        }
      }
      if((res=FRENewObjectFromUTF8(strlen(asyncTask.outMsgs->list[i].msg), (const uint8_t*)asyncTask.outMsgs->list[i].msg, &body)) != FRE_OK)
      {
        Debug::logDebug("ERROR: FRENewObjectFromUTF8(strlen(asyncTask.outMsgs->list[i].msg), (const uint8_t*)asyncTask.outMsgs->list[i].msg, &body) = %d", res);
        freeMessages(asyncTask.outMsgs);
        return NULL;
      }

      if((res=FRENewObject((const uint8_t*)"Object", 0, NULL, &message, &thrownException)) != FRE_OK)
      {
        Debug::logDebug("ERROR: FRENewObject((const uint8_t*)\"Object\", 0, NULL, &message, &thrownException) = %d", res);
        freeMessages(asyncTask.outMsgs);
        FRECallObjectMethod(thrownException, (const uint8_t*)"getStackTrace", 0, NULL, &stackTrace, NULL);
        FREGetObjectAsUTF8(stackTrace, &len, &stack);
        Debug::logDebug((const char*)stack);
        return NULL;
      }

      if((res=FRESetObjectProperty(message, (const uint8_t*)"subject", subject, &thrownException)) != FRE_OK)
      {
        Debug::logDebug("ERROR: FRESetObjectProperty(message, (const uint8_t*)\"subject\", subject, &thrownException) = %d", res);
        freeMessages(asyncTask.outMsgs);
        FRECallObjectMethod(thrownException, (const uint8_t*)"getStackTrace", 0, NULL, &stackTrace, NULL);
        FREGetObjectAsUTF8(stackTrace, &len, &stack);
        Debug::logDebug((const char*)stack);
        return NULL;
      }
      if((res=FRESetObjectProperty(message, (const uint8_t*)"body", body, &thrownException)) != FRE_OK)
      {
        Debug::logDebug("ERROR: FRESetObjectProperty(message, (const uint8_t*)\"body\", body, &thrownException) = %d", res);
        freeMessages(asyncTask.outMsgs);
        FRECallObjectMethod(thrownException, (const uint8_t*)"getStackTrace", 0, NULL, &stackTrace, NULL);
        FREGetObjectAsUTF8(stackTrace, &len, &stack);
        Debug::logDebug((const char*)stack);
        return NULL;
      }

      if((res=FRESetArrayElementAt(result, i, message)) != FRE_OK)
      {
        Debug::logDebug("ERROR: FRESetArrayElementAt(result, i, message) = %d", res);
        freeMessages(asyncTask.outMsgs);
        return NULL;
      }
    }

    freeMessages(asyncTask.outMsgs);
    return result;
  }

  return NULL;
}

Hopefully, this code helps you get a start on writing your own NativeExtensions for Adobe AIR. If you’d like more information about this particular NitroLM project, you can browse the GitHub projects and the NitroLM documentation.

Post to Twitter

Posted by Andrew, filed under AIR, as3, C++, security. Date: November 30, 2011, 1:48 pm | 10 Comments »

I recently purchased a block of 8 Static IP addresses for use with my U-Verse plan. U-Verse has always been a very stable and reliable ISP for me the last two years and their price of $15/month was pretty good so I jumped. This gives me 5 usable IPs that I can use to host various websites.

I want to host websites on a machine on my internal LAN in the 192.168.x.x block, but use port forwarding on my firewall so that port 80 can be sent on through. The reason for this is mostly so that I can access the server machine on the internal LAN, host print servers, etc… while still using it as a secured internet webserver.

Normally, ISPs give you a block of IP addresses, you assign them in your router and send them on where you want to go. Not so with U-Verse. Each static IP is actually dynamically assigned via DHCP from the U-Verse gateway router box. Each IP address pulled dynamically off the gateway MUST have a different mac address. Unfortunately, most off the shelf firewall routers don’t support this capability. They have one, maybe two, mac addresses that they use. Normally, the uplink port has a single mac address that it pulls a dhcp ip address from.

I’m using a D-Link DIR-615 rev. C that I had laying around and noticed that OpenWRT listed it as a supported device. OpenWRT is custom firmware flash for routers that runs a small version of the Linux operating system on it. I figured if I could get Linux running on the device, then I’d be able to accomplish what I wanted…namely, pull all 5 usable IP addresses off the U-Verse gateway, and forward certain traffic on a per-IP/port basis to particular internal LAN machines.

I first downloaded an OpenWRT package (http://downloads.openwrt.org/snapshots/trunk/ar71xx/openwrt-ar71xx-dir-615-c1-squashfs.uni) for my router. I then held in the reset switch for 30 seconds while plugging in the power. This puts the router into recovery mode. I manually set my computer’s ip address to 192.168.0.2 and navigated to http://192.168.0.1 which brings up the recovery flash page of router where I could apply the .uni flash file for OpenWRT.

I reset my computer’s ip to dynamic and restarted the router. After all the lights stopped blinking, made sure I had a valid 192.168.1.x IP address and did

telnet 192.168.1.1

This brought me up to the linux shell of the router. I ran the passwd command to set my root password for the device and then exited the shell. Once the password for root is set, telnet becomes disabled, but SSH is enabled. I then used putty to connect to 192.168.1.1 again.

Next, I installed the packages ip and kmod-macvlan. These packages allow you to create virtual ethX adapters with separate mac addresses.

opkg update
opkg install ip
opkg install kmod-macvlan
opkg install hostapd-mini
opkg install luci-admin-full  
opkg install luci-fastindex
opkg install luci-app-firewall

I modified /etc/rc.local to create the virtual adapters.

# Put your custom commands here that should be executed once
# the system init finished. By default this file does nothing.

#AMW edits
ip link add link eth1 eth2 type macvlan
ifconfig eth2 hw ether 00:24:01:f5:1b:84

ip link add link eth1 eth3 type macvlan
ifconfig eth3 hw ether 00:24:01:f5:1b:85

ip link add link eth1 eth4 type macvlan
ifconfig eth4 hw ether 00:24:01:f5:1b:86

ip link add link eth1 eth5 type macvlan
ifconfig eth5 hw ether 00:24:01:f5:1b:87

ip link add link eth1 eth6 type macvlan
ifconfig eth6 hw ether 00:24:01:f5:1b:88

ifup -a

route add default gw 75.30.80.1 dev eth1

ntpclient -c 1 -s -h ntp1.dlink.com

exit 0

eth0 is my local LAN interface. eth1 is my main outgoing public internet interface. eth2-6 are virtual macvlan interfaces that will each hold one of the static ip addresses. I also modified the file /etc/config/network so that the new virtual adapters would be included in dhcp setup.

config 'interface' 'loopback'
        option 'ifname' 'lo'
        option 'proto' 'static'
        option 'ipaddr' '127.0.0.1'
        option 'netmask' '255.0.0.0'

config 'interface' 'lan'
        option 'ifname' 'eth0'
        option 'type' 'bridge'
        option 'proto' 'static'
        option 'ipaddr' '192.168.1.1'
        option 'netmask' '255.255.255.0'

config 'interface' 'wan'
        option 'ifname' 'eth1'
        option 'proto' 'dhcp'

#First static IP
config 'interface' 'wan1'
        option 'ifname' 'eth2'
        option 'proto' 'dhcp'
        option 'defaultroute' '0'
        option 'peerdns' '0'
        option 'gateway' '0.0.0.0'

#second static IP
config 'interface' 'wan2'
        option 'ifname' 'eth3'
        option 'proto' 'dhcp'
        option 'defaultroute' '0'
        option 'peerdns' '0'
        option 'gateway' '0.0.0.0'

#third static IP
config 'interface' 'wan3'
        option 'ifname' 'eth4'
        option 'proto' 'dhcp'
        option 'defaultroute' '0'
        option 'peerdns' '0'
        option 'gateway' '0.0.0.0'

#fourth static IP
config 'interface' 'wan4'
        option 'ifname' 'eth5'
        option 'proto' 'dhcp'
        option 'defaultroute' '0'
        option 'peerdns' '0'
        option 'gateway' '0.0.0.0'

#fifth static IP
config 'interface' 'wan5'
        option 'ifname' 'eth6'
        option 'proto' 'dhcp'
        option 'defaultroute' '0'
        option 'peerdns' '0'
        option 'gateway' '0.0.0.0'

I made sure to set the gateway option on all of the virtual adapters to 0.0.0.0 so that the regular default gateway of eth1 would be used instead of the static IP default gateways.

After re-booting the router, I navigated to the U-Verse gateway and configured each of my new mac addresses so they mapped to the static IP addresses instead of internal 10.0.0.x ones.

After rebooting the router one last time, I now had all of my static IPs assigned on the router and could install and utilize the LuCI interface for configuring routing and port forwarding.



Post to Twitter

Posted by Andrew, filed under Linux, security. Date: January 6, 2010, 11:41 am | 17 Comments »

Get Adobe Flash player


View Source

I came across this infosthetics blog post discussing a Javascript interface for doing Chroma password.

I hadn’t yet had a chance to dive into any Flex 4 stuff, so I thought this might be a good little opportunity to play around with it. My algorithm isn’t exactly as described in the blog post, but it seems to work effectively. I’m using a portion of the SHA256 hash to generate the various colors. I’m animating between colors so as long as you type more than 1 character every 1/2 second, a screen grabber can’t grab the intermediary colors and figure out your password through trial/error.

This type of component is useful for ensuring that a person has entered their password correctly or to help them notice right away that their capslock key was on while typing it.

Post to Twitter

Posted by Andrew, filed under Flex, security. Date: July 31, 2009, 4:05 pm | 1 Comment »

browsermob

In the consulting work I’ve been doing for Simplified Logic, they’ve had me working on a new Encryption-only version of Nitro-LM for Flex and AIR. In the past year, they’ve had a number of customers who say something like, “The licensing and everything is really cool, but I’ve already coded my own simple username/password system and I really would just like to encrypt my application.”

For the past few months, I’ve been working on a solution for this. Instead of being username/password based for authentication like Nitro-LM is, I’ve stripped down the API to a 52kb SWC file that uses key-based authentication. If you request a decryption key from the server, as long as you encrypt that request using your application’s public key (embedded in your swf), then you’re good to go.

Having a solution for this brings about some interesting potential problems for Nitro-LM. First off, applications using this encryption-only solution will likely have a MUCH larger install base than a licensed application. For example, games, websites, and other small things where licensing isn’t really needed could be a candidate for the encryption-only solution. These types of applications also have a higher potential for going viral and slamming the Nitro-LM servers with tons of traffic.

To prepare for this potential onslaught of traffic, I did a couple of things. First, the encryption-only client SWC will cache the decryption key in a secure format for 30 days without hitting the server again. The other thing was to push out a trimmed down server instance to the SLI cloud. The encryption-only solution will push the traffic through the cloud first so it can take the brunt of the load.

To test this high-volume scenario, I setup a worst-case scenario test script through BrowserMob. They’re a great company that allows you to define and schedule load tests over the Internet. Patrick from BrowserMob was very responsive to my needs and even added a special API call that would allow me to do binary data POSTs from the service. I wanted to really stress our cloud to see what would happen if someone using our encryption service went totally viral. At the high-side, I was pushing through around 400 transactions-per-second before the cloud provider shut off my application. My guess is that it appeared like a denial-of-service attack since I was sending many identical transactions from the BrowserMob servers.

In any case, I believe the test was very successful. The new Nitro-LM service should be able to handle over 700 million transactions per month as a conservative estimate based on this testing (300 tx/sec over 30 days). I hope to be able to show some of this new Encryption-only stuff in my 360|Flex presentation. Register soon for 360|Flex, I’ve heard they’re filling up quickly.

Post to Twitter

Posted by Andrew, filed under 360 Flex, Flex, security. Date: May 1, 2009, 3:30 pm | No Comments »

23  Aug
360|Flex wrap-up

The 360|Flex event in San Jose was a success in my book.  I manned the Simplified Logic booth most of the time, but I did find some time to go to some great sessions.  I learned many tips and tricks that I could apply to my own development efforts.

My session on Encrypting Flex was fairly well attended.  There were lots of good questions from the audience.  I probably could have done a better job preparing for my talk, but I’ll wait until the comments come back from the surveys to flagellate myself over that too much.

Without further ado, here are the materials from my presentation.  The audio will have to wait until my presentation gets uploaded to the Adobe Media Player RSS Feed.

Encrypting Flex, Protecting Revenue, and Making Cash (PDF)
flexembeddingexample1 (zip)
flexencryptionexample1 (zip)
flexencryptionexample2 (zip)
flexencryptionexample3 (zip)

Post to Twitter

Posted by Andrew, filed under 360 Flex, AIR, as3, Flex, security. Date: August 23, 2008, 8:16 pm | No Comments »

I finished up my 360 presentation on Encrypting Flex, Protecting Revenue, and Making Cash yesterday.  I’m quite pleased with it.  It’s fairly slide-light and demo-heavy which is what I enjoy when going to 360 sessions.

I’m going to be showing a number of different encryption techniques, looking at several obfuscators, and showing what an cairngorm modular app looks like (in development) when each module is encrypted and protected from decompilers.  I’ll also go over a couple of techniques to protect your revenue stream from an application, and also a couple of ways you/your company can make additional cash or save time and money (which is pretty much the same as making cash).

If you weren’t quick enough to get tickets, too bad, so sad…All sold out!  I believe they’re at least going to have screencam and audio from my talk.  If I get ahold of it, I’ll post it here afterward along with my code examples.  See you at eBay.

Post to Twitter

Posted by Andrew, filed under 360 Flex, encryption, Flex, security. Date: August 15, 2008, 7:31 am | 1 Comment »

I work as a contractor for Simplified Logic, one of the sponsors of the 360 Flex San Jose event.  I’m going to be speaking along with David Bigelow in the presentation entitled Making Money with Flex.  At SLI, they use their nitro-lm (license management) product to track and control usage and licensing of an application.  It also has notification features so you can get an e-mail when one of your customers maxes out licenses, isn’t using your software for some reason, or any number of other significant events.  Another aspect of the software is to protect your code from decompilers using a public/private key encryption algorithm.

Given that there is only limited time to present, what areas do you think we should focus on?  The business case stuff, license management and tracking, or a deep-dive into encrypting flex applications?

Post to Twitter

Posted by Andrew, filed under 360 Flex, AIR, encryption, Flex, security. Date: July 19, 2008, 7:43 pm | No Comments »

I’m working on some new types of SWF file encryption to demonstrate at 360 Flex San Jose.  If you’ve read my Inside RIA articles, you know that the main technique used by NitroLM to encrypt swf files is to create a wrapper application and load/decrypt the real application using a SWFLoader.  The problem with this technique is that it’s a little bit kludgy and adds deployment complexity.  It also has some difficulty in AIR in that if you wrap an <mx:Application> inside an AIR app, you won’t be able use some of the Native AIR functionality.

I’ve been dilligently working on a new technique for encrypting modular applications.  Basically, you’ll write your flex or AIR app as you normally would and break up functionality into modules loaded by <mx:ModuleLoader>.  You could also put pretty much all of your code in a single module if you wanted to.  Then, when you’re ready to deploy, just comment out the <mx:ModuleLoader> tags and replace them with <nitrolm:EncryptedModuleLoader> tags.

There’s a few other steps including encrypting the module SWFs with an AIR application called AssetEncrypter, but the process is much more straightforward than the wrapper technique.  It’s also much easier for the developer to code because they don’t have to deal with the keys and decryption themselves.  All of the complex functionality has been done for you in the <nitrolm:EncryptedModuleLoader>

Post to Twitter

Posted by Andrew, filed under 360 Flex, AIR, as3, encryption, Flex, security. Date: June 4, 2008, 11:38 am | No Comments »

Here are my 3 articles for InsideRIA on Encryption in Flex applications.

Encryption in Flex Applications 1 – Simulate EncryptedLocalStore

Encryption in Flex Applications 2 – SWC AS3 Library Encryption

Encryption in Flex Applications 3 – NitroLM SWF Encryption

Post to Twitter

Posted by Andrew, filed under AIR, as3, encryption, Flex, security. Date: April 4, 2008, 12:25 pm | No Comments »