Skip to content

Commit

Permalink
speed-up AES decryption (around factor 2)
Browse files Browse the repository at this point in the history
get rid of as3crypto.swc (avoid ByteArray creation, and a bunch of copy)
  • Loading branch information
mangui committed Jul 28, 2014
1 parent 2e7af5b commit c0284ea
Show file tree
Hide file tree
Showing 19 changed files with 129 additions and 123 deletions.
1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,6 @@ swfobject.embedSWF('StrobeMediaPlayback.swf', 'player', 640, 360, '10.2', null,
## License

- [MPL 2.0](https://github.com/mangui/flashls/blob/master/LICENSE)
- [as3crypto.swc](https://github.com/timkurvers/as3-crypto) is governed by a `BSD` license

## Donation

Expand Down
Binary file modified bin/debug/flashls.swc
Binary file not shown.
Binary file modified bin/debug/flashlsChromeless.swf
Binary file not shown.
Binary file modified bin/debug/flashlsFlowPlayer.swf
Binary file not shown.
Binary file modified bin/debug/flashlsOSMF.swc
Binary file not shown.
Binary file modified bin/debug/flashlsOSMF.swf
Binary file not shown.
Binary file modified bin/release/flashls.swc
Binary file not shown.
Binary file modified bin/release/flashlsChromeless.swf
Binary file not shown.
Binary file modified bin/release/flashlsFlowPlayer.swf
Binary file not shown.
Binary file modified bin/release/flashlsOSMF.swc
Binary file not shown.
Binary file modified bin/release/flashlsOSMF.swf
Binary file not shown.
6 changes: 2 additions & 4 deletions build/build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,13 @@ FLEXPATH=../../flex_sdk_4.6
#FLEXPATH=../../../apache_flex_sdk
#FLEXPATH=../../../AIRSDK_Compiler

OPT_DEBUG="-library-path+=../lib/as3crypto.swc \
-use-network=false \
OPT_DEBUG="-use-network=false \
-optimize=true \
-incremental=true \
-static-link-runtime-shared-libraries=true \
-define=CONFIG::LOGGING,true"

OPT_RELEASE="-library-path+=../lib/as3crypto.swc \
-use-network=false \
OPT_RELEASE="-use-network=false \
-optimize=true \
-incremental=true \
-static-link-runtime-shared-libraries=true \
Expand Down
Binary file removed lib/as3crypto.swc
Binary file not shown.
5 changes: 2 additions & 3 deletions src/org/mangui/hls/demux/TSDemuxer.as
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
package org.mangui.hls.demux {
import org.mangui.hls.HLSSettings;

import com.hurlant.util.Hex;
import org.mangui.hls.utils.Hex;

import org.mangui.hls.flv.FLVTag;
import org.mangui.hls.HLSAudioTrack;
Expand All @@ -14,6 +12,7 @@ package org.mangui.hls.demux {

CONFIG::LOGGING {
import org.mangui.hls.utils.Log;
import org.mangui.hls.HLSSettings;
}

/** Representation of an MPEG transport stream. **/
Expand Down
2 changes: 1 addition & 1 deletion src/org/mangui/hls/playlist/Manifest.as
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
package org.mangui.hls.playlist {
import com.hurlant.util.Hex;
import org.mangui.hls.utils.Hex;

import flash.events.*;
import flash.net.*;
Expand Down
2 changes: 1 addition & 1 deletion src/org/mangui/hls/stream/FragmentLoader.as
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
package org.mangui.hls.stream {
import com.hurlant.util.Hex;

import flash.events.*;
import flash.net.*;
Expand All @@ -18,6 +17,7 @@ package org.mangui.hls.stream {

CONFIG::LOGGING {
import org.mangui.hls.utils.Log;
import org.mangui.hls.utils.Hex;
}

/** Class that fetches fragments. **/
Expand Down
118 changes: 65 additions & 53 deletions src/org/mangui/hls/utils/AES.as
Original file line number Diff line number Diff line change
@@ -1,23 +1,18 @@
package org.mangui.hls.utils {
import com.hurlant.crypto.symmetric.CBCMode;
import com.hurlant.crypto.symmetric.ICipher;
import com.hurlant.crypto.symmetric.IPad;
import com.hurlant.crypto.symmetric.IVMode;
import com.hurlant.crypto.symmetric.NullPad;
import com.hurlant.crypto.symmetric.PKCS5;

import flash.utils.ByteArray;
import flash.utils.Timer;
import flash.events.Event;
import flash.events.TimerEvent;

/**
* Contains Utility functions for Decryption
* Contains Utility functions for AES-128 CBC Decryption
*/
public class AES {
private var _key : FastAESKey;
private var _mode : ICipher;
private var _iv : ByteArray;
private var iv0 : uint;
private var iv1 : uint;
private var iv2 : uint;
private var iv3 : uint;
/* callback function upon decrypt progress */
private var _progress : Function;
/* callback function upon decrypt complete */
Expand All @@ -36,15 +31,12 @@ package org.mangui.hls.utils {
private var _data_complete : Boolean;

public function AES(key : ByteArray, iv : ByteArray, notifyprogress : Function, notifycomplete : Function) {
var pad : IPad = new PKCS5;
_key = new FastAESKey(key);
_mode = new CBCMode(_key, pad);
pad.setBlockSize(_mode.getBlockSize());
_iv = iv;
if (_mode is IVMode) {
var ivmode : IVMode = _mode as IVMode;
ivmode.IV = iv;
}
iv.position = 0;
iv0 = iv.readUnsignedInt();
iv1 = iv.readUnsignedInt();
iv2 = iv.readUnsignedInt();
iv3 = iv.readUnsignedInt();
_data = new ByteArray();
_data_complete = false;
_progress = notifyprogress;
Expand All @@ -61,7 +53,7 @@ package org.mangui.hls.utils {
// }
_data.position = _write_position;
_data.writeBytes(data);
_write_position+= data.length;
_write_position += data.length;
_timer.start();
}

Expand Down Expand Up @@ -91,74 +83,94 @@ package org.mangui.hls.utils {
/** decrypt a small chunk of packets each time to avoid blocking **/
private function _decryptData() : void {
_data.position = _read_position;
var decryptdata : ByteArray;
if (_data.bytesAvailable) {
var dumpByteArray : ByteArray = new ByteArray();
var newIv : ByteArray;
var pad : IPad;
if (_data.bytesAvailable <= CHUNK_SIZE) {
if (_data_complete) {
// CONFIG::LOGGING {
// Log.info("data complete, last chunk");
// }
pad = new PKCS5;
_read_position += _data.bytesAvailable;
_data.readBytes(dumpByteArray, 0, _data.bytesAvailable);
decryptdata = _decryptCBC(_data, _data.bytesAvailable);
unpad(decryptdata);
} else {
// data not complete, and available data less than chunk size, stop timer and return
// CONFIG::LOGGING {
// Log.info("data not complete, stop timer");
// }
// data not complete, and available data less than chunk size, stop timer and return
_timer.stop();
return;
}
} else {
// bytesAvailable > CHUNK_SIZE
// CONFIG::LOGGING {
// Log.info("process chunk");
// }
pad = new NullPad;
_read_position += CHUNK_SIZE;
_data.readBytes(dumpByteArray, 0, CHUNK_SIZE);
// Save new IV from ciphertext
newIv = new ByteArray();
dumpByteArray.position = (CHUNK_SIZE - 16);
dumpByteArray.readBytes(newIv, 0, 16);
}
dumpByteArray.position = 0;
// CONFIG::LOGGING {
// Log.info("before decrypt");
// }
_mode = new CBCMode(_key, pad);
pad.setBlockSize(_mode.getBlockSize());
(_mode as IVMode).IV = _iv;
_mode.decrypt(dumpByteArray);
// CONFIG::LOGGING {
// Log.info("after decrypt");
// }
_progress(dumpByteArray);
// switch IV to new one in case more bytes are available
if (newIv) {
_iv = newIv;
decryptdata = _decryptCBC(_data, CHUNK_SIZE);
}
_progress(decryptdata);
} else {
// CONFIG::LOGGING {
// Log.info("no bytes available, stop timer");
// }
_timer.stop();
if (_data_complete) {
CONFIG::LOGGING {
Log.debug("AES:data+decrypt completed, callback");
Log.debug("AES:data+decrypt completed, callback");
}
// callback
_complete();
}
}
}

/* Cypher Block Chaining Decryption, refer to
* http://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Cipher-block_chaining_
* for algorithm description
*/
private function _decryptCBC(crypt : ByteArray, len : uint) : ByteArray {
var src : Vector.<uint> = new Vector.<uint>(4);
var dst : Vector.<uint> = new Vector.<uint>(4);
var decrypt : ByteArray = new ByteArray();
decrypt.length = len;

for (var i : uint = 0; i < len / 16; i++) {
// read src byte array
src[0] = crypt.readUnsignedInt();
src[1] = crypt.readUnsignedInt();
src[2] = crypt.readUnsignedInt();
src[3] = crypt.readUnsignedInt();

// AES decrypt src vector into dst vector
_key.decrypt128(src, dst);

// CBC : write output = XOR(decrypted,IV)
decrypt.writeUnsignedInt(dst[0] ^ iv0);
decrypt.writeUnsignedInt(dst[1] ^ iv1);
decrypt.writeUnsignedInt(dst[2] ^ iv2);
decrypt.writeUnsignedInt(dst[3] ^ iv3);

// CBC : next IV = (input)
iv0 = src[0];
iv1 = src[1];
iv2 = src[2];
iv3 = src[3];
}
decrypt.position = 0;
return decrypt;
}

public function unpad(a : ByteArray) : void {
var c : uint = a.length % 16;
if (c != 0) throw new Error("PKCS#5::unpad: ByteArray.length isn't a multiple of the blockSize");
c = a[a.length - 1];
for (var i : uint = c; i > 0; i--) {
var v : uint = a[a.length - 1];
a.length--;
if (c != v) throw new Error("PKCS#5:unpad: Invalid padding value. expected [" + c + "], found [" + v + "]");
}
}

public function destroy() : void {
_key.dispose();
// _key = null;
_mode = null;
}
}
}
74 changes: 14 additions & 60 deletions src/org/mangui/hls/utils/FastAESKey.as
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
package org.mangui.hls.utils {
import flash.utils.ByteArray;

import com.hurlant.crypto.symmetric.ISymmetricKey;

/* word based AES encryption/decryption
* inspired by
* https://code.google.com/p/crypto-js/source/browse/tags/3.1.2/src/aes.js
*/
public class FastAESKey implements ISymmetricKey {
public class FastAESKey {
/* private data, specific to each key */
private var keySize : uint;
private var nRounds : uint;
Expand All @@ -19,10 +17,6 @@ package org.mangui.hls.utils {
// static Lookup tables
private static var _SBOX : Vector.<uint>;
private static var _INV_SBOX : Vector.<uint>;
private static var _SUB_MIX_0 : Vector.<uint>;
private static var _SUB_MIX_1 : Vector.<uint>;
private static var _SUB_MIX_2 : Vector.<uint>;
private static var _SUB_MIX_3 : Vector.<uint>;
private static var _INV_SUB_MIX_0 : Vector.<uint>;
private static var _INV_SUB_MIX_1 : Vector.<uint>;
private static var _INV_SUB_MIX_2 : Vector.<uint>;
Expand All @@ -35,10 +29,6 @@ package org.mangui.hls.utils {
private static function _initTable() : void {
_SBOX = new Vector.<uint>(256);
_INV_SBOX = new Vector.<uint>(256);
_SUB_MIX_0 = new Vector.<uint>(256);
_SUB_MIX_1 = new Vector.<uint>(256);
_SUB_MIX_2 = new Vector.<uint>(256);
_SUB_MIX_3 = new Vector.<uint>(256);
_INV_SUB_MIX_0 = new Vector.<uint>(256);
_INV_SUB_MIX_1 = new Vector.<uint>(256);
_INV_SUB_MIX_2 = new Vector.<uint>(256);
Expand Down Expand Up @@ -70,15 +60,8 @@ package org.mangui.hls.utils {
var x4 : uint = d[x2];
var x8 : uint = d[x4];

// Compute sub bytes, mix columns tables
var t : uint = (d[sx] * 0x101) ^ (sx * 0x1010100);
_SUB_MIX_0[x] = (t << 24) | (t >>> 8);
_SUB_MIX_1[x] = (t << 16) | (t >>> 16);
_SUB_MIX_2[x] = (t << 8) | (t >>> 24);
_SUB_MIX_3[x] = t;

// Compute inv sub bytes, inv mix columns tables
t = (x8 * 0x1010101) ^ (x4 * 0x10001) ^ (x2 * 0x101) ^ (x * 0x1010100);
var t: uint = (x8 * 0x1010101) ^ (x4 * 0x10001) ^ (x2 * 0x101) ^ (x * 0x1010100);
_INV_SUB_MIX_0[sx] = (t << 24) | (t >>> 8);
_INV_SUB_MIX_1[sx] = (t << 16) | (t >>> 16);
_INV_SUB_MIX_2[sx] = (t << 8) | (t >>> 24);
Expand Down Expand Up @@ -152,27 +135,19 @@ package org.mangui.hls.utils {
}
}
}

public function decrypt(block : ByteArray, index : uint = 0) : void {
block.position = index;
for (var i : int = 0; i < keySize; i++) {
// state.push(block.readUnsignedInt());
state[i] = block.readUnsignedInt();
}

public function decrypt128(input : Vector.<uint>,output : Vector.<uint>) : void {
// Swap 2nd and 4th rows
var t : uint = state[1];
state[1] = state[3];
state[3] = t;
_doCryptBlock(invKeySchedule, _INV_SUB_MIX_0, _INV_SUB_MIX_1, _INV_SUB_MIX_2, _INV_SUB_MIX_3, _INV_SBOX);
// Inv swap 2nd and 4th rows
t = state[1];
state[1] = state[3];
state[3] = t;

block.position = index;
for (i = 0; i < keySize; i++) {
block.writeUnsignedInt(state[i]);
}
state[0] = input[0];
state[1] = input[3];
state[2] = input[2];
state[3] = input[1];
_doCryptBlock(invKeySchedule, _INV_SUB_MIX_0, _INV_SUB_MIX_1, _INV_SUB_MIX_2, _INV_SUB_MIX_3, _INV_SBOX);
// Inv swap 2nd and 4th rows
output[0] = state[0];
output[1] = state[3];
output[2] = state[2];
output[3] = state[1];
}

private function _doCryptBlock(keySchedule : Vector.<uint>, SUB_MIX_0 : Vector.<uint>, SUB_MIX_1 : Vector.<uint>, SUB_MIX_2 : Vector.<uint>, SUB_MIX_3 : Vector.<uint>, SBOX : Vector.<uint>) : void {
Expand Down Expand Up @@ -220,26 +195,5 @@ package org.mangui.hls.utils {
keyWords.length = 0;
keyWords = null;
}

public function encrypt(block : ByteArray, index : uint = 0) : void {
block.position = index;
for (var i : int = 0; i < keySize; i++) {
// state.push(block.readUnsignedInt());
state[i] = block.readUnsignedInt();
}
_doCryptBlock(keySchedule, _SUB_MIX_0, _SUB_MIX_1, _SUB_MIX_2, _SUB_MIX_3, _SBOX);
block.position = index;
for (i = 0; i < keySize; i++) {
block.writeUnsignedInt(state[i]);
}
}

public function getBlockSize() : uint {
return 16;
}

public function toString() : String {
return "aes" + (32 * keySize);
}
}
}
Loading

0 comments on commit c0284ea

Please sign in to comment.