Converting an ActionScript 2 Class to ActionScript 3

I am trying to be able to encrypt data in ColdFusion, and decrypt it using Flex (or more specifically, ActionScript 3). After some searching, I found a set of ActionScript 2 classes for encryption. They are open source and (as best I could tell) have no license attached to them.

I was specifically interested in Rijndael (AKA AES) encryption, because that is supported natively in ColdFusion via the Encrypt and Decrypt functions.

My first step was to take the Rijndael.as file from the ASCrypt component and see if I could get it working in Flex 2. It took about two hours, but the conversion was relatively smooth. These are some of the steps I took:

  1. Add a Package Definition: The ActionScript class did not include a package definition. The original class definition was like this:

    class com.meychi.ascrypt.Rijndael

    I modified this, first adding a package description to the AS file, like this:

    package com.meychi.ascrypt{

    And then modifying the class description like this:

    public class Rijndael {

  2. Defining an Array: This wasn't throwing compile errors, but I changed it anyway. A few of the instance variables were arrays, and were defined like this:

    private var Rcon:Array = [0x01, 0x02, ... ];

    It seemed odd to me that they weren't explicitly using a constructor, so I changed it to this:

    private var Rcon:Array = new Array(0x01, 0x02, ...);

    I'm not sure if the first syntax is supported for backward compatibility reasons, or if it a preferred approach, or whatever. I don't have a specific reason for making this change, it was made while I was trying to debug another error (It didn't address that other error, mind you)

  3. Null Checking: This one I don't have a specific solution for either. The original constructor was like this:

    public function Rijndael(keySize:Number, blockSize:Number){
    if (keySize != null) this.keySize = keySize;
    ....
    }

    The conditional "keySize != null" always threw compiler errors. I tried adding a code block and using an "is" operator to compare (because that's how compare against database nulls in SQL). The code would be like this:

    public function Rijndael(keySize:Number, blockSize:Number){
    if ( !(keySize is null) ){
    this.keySize = keySize;
    }
    ....
    }

    That would compile okay, but throw run-time errors. Eventually, I just removed the check to see if keySize is null. It appears to me that the listed arguments (keySize and blockSize) are both required. If you try to use the constructor w/o passing them in, you get an error (I believe at compile time). If you try to pass in a null value, it gives an error (I believe at run-time) saying that 'null' is not a valid Number.

    Based on those two reasons, my solution was to remove the null check altogether:

    public function Rijndael(keySize:Number, blockSize:Number){
    this.keySize = keySize;
    ...
    }

    I feel like I'm missing something obvious here, but it worked.

  4. Void vs void: ActionScript (Like JavaScript, Java, et all) is case sensitive. The original function had a few private functions with a specified returntype of Void (With a capital V). This was throwing compile errors. I changed it to 'void' (all lowercase). The old way:

    private function byteSub(state:Array, dir:String):Void {

    And the corrected way:

    private function byteSub(state:Array, dir:String):void {

    I wonder why that was accepted in ActionScript 2. I don't know ActionScript 2 very well, so.. your guess is as good as mine, perhaps better.

  5. Variable Declared Multiple Times: I had a few spots where a variable was declared multiple times within a function. Like this:

    if(dir == "encrypt")
    var S:Array = this.SBox;
    else
    var S:Array = this.SBoxInverse;

    The second declaration was throwing a compiler error. My first solution was along the right path (but wrong). I added an explicit var at the top of the function and remove var from the assignments:

    var S:Array;

    if(dir == "encrypt")
    S:Array = this.SBox;
    else
    S:Array = this.SBoxInverse;

    This was still throwing compile errors. Do you see what I did wrong? It was trying to solve this error that made me change the array declarations (See item 2) on the private class variables (but that change had no affect). My problem here was that I was specifying a 'type' in the assignment. Instead of S:Array = this.SBox I should have specified "S = this.sBox". Like this:

    var S:Array;

    if(dir == "encrypt") {
    S= this.SBox;
    } else {
    S = this.SBoxInverse;
    }

    (I like to use code blocks wherever possible, so when I was changing the code, I added the curly brace code blocks as relevant including this if else statement )

  6. Loop counters Declared Twice or not declared at all: The original code had a bunch of for loops in it. Nothing out of the ordinary with that. The loops were often set up like this:

    for (var j:Number = 0; j<this.Nb; j++) {
    ...
    }

    You'll notice that the loop counter 'j' is defined as part of the loop. That's fine and dandy. But, with multiple loops using the same counter, the second loop was throwing a 'variable already defined' type of error. I just moved the declaration out of the loop:

    var j:Number;

    for (j = 0; j<this.Nb; j++) {

    In one case, the loop counter variable was not defined at all. I assume this was a typo in the original code. I just added a definition at the top of the method (just like I did for j above).


After those changes, the AS file started working. You can test the code like this:

<mx:Script>
<![CDATA[
import com.meychi.ascrypt.Rijndael;         

public var rijndael_test:Rijndael = new Rijndael (128, 128);
public var rijndael_text:String = "Rijndael:keysize192/24chars";
public var rijndael_key:String = "123456789012345612345678";

public var rijndael_hash:String = rijndael_test.encrypt(rijndael_text, rijndael_key, "ECB");
         
public function EncryptString():void{
encrypted.text = rijndael_hash;            
decrypted.text = rijndael_test.decrypt(rijndael_hash, rijndael_key, "ECB");
}
]]>
</mx:Script>

<mx:Label x="36" y="20" text="Label" width="600" id="encrypted"/>
<mx:Label x="36" y="40" text="Label" width="600" id="decrypted"/>
<mx:Button x="76" y="140" label="RunEncrypt" click="{EncryptString()}"/>

That is, roughly, the same sample from the original ASCrypt component, I just added code to assign the results to labels instead of using trace.

After all is said and done, Encryption using AES and ColdFusion is producing different results than encryption using this Rijndael.as file. ColdFusion threw an error on the key specified above, saying it was invalid.

I suspect it has something to do with the keysize and blocksize, which are configurable in the ActionScript class, but are not in ColdFusion. Anyone have any thoughts on that?

I'm releasing my code changes under the same license that the original was released under (that is to say, none at all )

Related Blog Entries

Comments
senocular's Gravatar 1) For checking value Number values, you should test with isNaN
2) Typo: #4 in code you have :oid where should be :void ;)
# Posted By senocular | 10/18/06 2:22 PM
spender's Gravatar Hello Jeff,

To answer some of the things you were unsure about:

It's perfectly legal to define arrays either way. In AS2 the [..] notation was used because it performed faster than declaring new Array. I think [..] notation will remain.

the reason you can't check if keySize is null is because of the way that intrinsic types (Boolean, int, Number, String, uint etc..) are stored in AS3. It is no longer possible to store null in these types of variables... on complex datatypes can be null (i.e. Objects). Hence it would never be possible to store null in variable keySize as it is a Number, so the compiler is essentially telling you that you are trying to compare oranges and pears, and throws an error.

Void is now void. A political decision from the creators of AS3. Otherwise it is no different.

HTH
# Posted By spender | 10/18/06 2:29 PM
Mike J's Gravatar 2. It's just a shorthand syntax. =)

3. Actually those parms are not required for the constructor, as they are set by default when defined:
public var blockSize:Number = 128;
public var keySize:Number = 128;
Though I had thought that if something wasn't passed in the constructor that the intrinsic value of undefined should be used for comparison ( ie, val == undefined ).

4. In AS2, it was indeed Void (and was still in the initial releases of Flex2).

6. There should be no harm in redeclaring a loop variable in each loop, since they should only be valid for the context of that loop that way. In other words, when the loop is done, the var should be able to be marked for garbage collection.
# Posted By Mike J | 10/18/06 2:33 PM
Jeff Houser's Gravatar Senocular,

I fixed the typo w/ 'oid;. And just tried the isNaN function. It worked. I'll upload a new version of the code.
# Posted By Jeff Houser | 10/18/06 2:49 PM
Jeff Houser's Gravatar Spender, thanks!

Mike, If the parameters are not passed into the constructor, I recieve a compiler error. So, something must be passed in. I assume the file won't compile with these errors? Is there a way to define arguments as conditional?

It does not seem possible to define an alternate constructor with different arguments.

If I use undefined I get a compiler warning that says "variables of type Number cannot be undefined. The value undefined will be type coerced to Number before comparison"
# Posted By Jeff Houser | 10/18/06 2:59 PM
Jeff Houser's Gravatar oh, I forgot to add...

Redeclaring a loop variable in each loop was throwing a compile warning (not an error) which is why I removed the duplicate definition.

I'm willing to accept that it'll work w/o problems, but didn't test it.
# Posted By Jeff Houser | 10/18/06 3:03 PM
Mike J's Gravatar Sorry, I didn't intend 3 to be a commentary on how to do it in AS3. =)

Parms in AS3 are now required, and there is not an "official" way to do method overloading. There IS the rest (...) way of defining additional optional parms. However, I personally think it's a bit of a kludge to use it to do method overloading. Given a choice between using a generic arguments array or forcing usage of the class to pass values, I would opt for forcing usage, as you had done.

http://livedocs.macromedia.com/labs/as3preview/lan...#..._(rest)_parameter

Essentially:
function myMethod( ... args ):void {
if ( args.length > 0 ) { blockSize = args[ 0 ]; }
if ( args.length > 1 ) { keySize = args[ 1 ]; }
}

I think clean self-documenting code would be much better, especially for something like this. Using mysterious parameters like this is (IMO) akin to using magic-numbers all over. ^_^
# Posted By Mike J | 10/18/06 3:13 PM
Jeff Houser's Gravatar Mike,

Thanks for the info.
ColdFusion deals with optional arguments in a similar manner.
# Posted By Jeff Houser | 10/18/06 4:35 PM
David R's Gravatar "Redeclaring a loop variable in each loop was throwing a compile warning (not an error) which is why I removed the duplicate definition."

Wow, that is completely braindead, if that's what is actually going on here. If they want to get strict about it, a variable declared inside the for(var x;..;..) should only have scope within the for loop. Being strict about it and going the other way, just isn't right.
# Posted By David R | 10/18/06 8:02 PM
Alex Cook's Gravatar Hey do you have the old AS 2.0 class somewhere? Could you possibly forward that to me? The link to it is dead and I really wish I had the installer for it!

Alex
# Posted By Alex Cook | 5/1/07 6:57 PM
# Posted By grymmace | 5/5/08 11:36 AM
grymmace's Gravatar Also, for the various algorithms use the WayBackMachine to view the old meychi.com:
http://web.archive.org/web/20070312155637/http://w...

My WinZip could not process the .zip files, I had to rely on windows' 'open compressed folder' thing.
# Posted By grymmace | 5/5/08 11:58 AM
All Content Copyright 2005, 2006, 2007 Jeffry Houser. May not be reused without permission
BlogCFC was created by Raymond Camden. This blog is running version 5.8.