ARC4 es un algoritmo de encriptación simétrico lo suficientemente rápido y fácil de implementar. El hecho de que sea simétrico significa que puede utilizar la misma función con la misma clave (que puede variar entre 40 y 2048 bits de longitud) tanto para cifrar como para descifrar un bloque de datos dado.
¿Es uno de los algoritmos de encriptación más seguro y robusto de los que puedes encontrar? ¡En absoluto! Pero lo que sí es cierto es que proporciona un muy buen rendimiento y puedes tomar bajo tu control una serie de pasos adicionales para corregir sus puntos más débiles.
Por tanto, si estás interesado en contar con este algoritmo en tu caja de herramientas… sigue leyendo y te mostraré como puedes implementarlo como una Clase, utilizando métodos diferentes para encriptar y desencriptar bloques de datos (incluso aunque se utilice la misma función en realidad).
Añade una nueva Clase a tu proyecto y nómbrala como ARC4. A continuación, añadiremos las tres propiedades que necesitamos para implementarlo:
- Name: mBox
- Type: MemoryBlock
- Scope: Private
- Name: mKeyBlock
- Type: MemoryBlock
- Scope: Private
- Name: mKeyLength
- Type: Integer
- Scope: Private
ARC4 utiliza una “caja de estado” con un tamaño de 256 bytes, siendo esta a la que apuntará nuestra propiedad mBox
. La segunda, mKeyBlock
, está declarada también como un MemoryBlock para que resulte más eficiente el acceso a los bytes individuales que componen la clave proporcionada por el usuario. Por último, la propiedad mKeyLength
es simplemente una propiedad de conveniencia para que podamos acceder a la longitud original de la clave desde los diferentes métodos.
Añadamos ahora los métodos requeridos por la clase, comenzando por el Constructor
. Este permitirá al usuario proporcionar la clave en forma de String como parte de inicialización de la instancia; de modo que no tengamos que pasarla de nuevo cada vez que deseemos cifrar o descifrar datos que utilicen la misma clave.
De modo que, con la clase ARC4 seleccionada en el Navegador del IDE, añade un nuevo método y escribe los siguientes valores en el Panel Inspector:
- Name: Constructor
- Parameters: Key As String
- Scope: Public
A continuación, escribe el siguiente fragmento de código en el Editor de Código asociado con el método:
// If Key is not an empty String // We call the Key method in order // to initialize the State box If Not (key.IsEmpty) Then Me.Key = key Else // Empty String, so we raise an exception Raise New RuntimeException(kkeynotinitialized,Integer(ARCError.KeyNotInitialized)) End If
Como puedes ver, lo primero que hace el Constructor
es llamar al método Key
, siendo este el encargado de inicializar la “caja de estado”, tal y como requiere el algoritmo, en combinación con la clave proporcionada. Si la clave es una cadena vacía, entonces lanzará una Excepción en Tiempo de Ejecución, proporcionando un mensaje con la descripción del error así como el código de error asociado.
De modo que añadamos ahora el método Key a nuestra clase RC4:
- Name: Key
- Parameters: Assigns Value As String
- Scope: Public
El hecho de que sea un método Público significa que puedes asignar una nueva clave sin necesidad de tener que crear una nueva instancia; siempre y cuando sea eso lo que necesites o quieras hacer. Por ejemplo, puedes inicializar la instancia de clase utilizando una clave, encriptar algunos datos utilizando dicha clave… y luego cambiar a una clave diferente para encriptar otra serie de datos. Sólo has de recordar que tendrás que utilizar las mismas claves para desencriptar los datos que hayas utilizado a la hora de encriptarlos.
Adicionalmente, el uso de la palabra clave Assigns
es simplemente azúcar sintáctico de modo que puedas llamar al método utilizando el operador de igualdad para pasar el parámetro, en vez de tener que utilizar la sintaxis habitual en la llamada a métodos en código Xojo. De modo que, por ejemplo, podrías hacer la llamada así:
MyRC4Instance.Key = "MySecretKey"
En vez de:
MyRC4Instance.Key("MySecretKey")
Este es el fragmento de código que se ejecutará en dicho método:
// Disabling some features for better speed #Pragma DisableBackgroundTasks #Pragma DisableBoundsChecking #Pragma NilObjectChecking False #Pragma StackOverflowChecking False // Initialize the index values Var mFirstIndex As UInt8 Var mSecondIndex As UInt8 // Let's check that this is not an Empty Key String If Not (value.IsEmpty) Then // Trim key length if greater than 256 = max 2048 bits supported by ARC4 If value.Length > 256 Then value = value.Left(256) // Pad the key if it is less than the required min 40 bits (5 bytes) // We are going to pad the key repeating the remaining 'n' characters // from the beginining of the key. If value.Length < 5 Then Var pad As Integer = 5-value.Length value = value + value.Left(pad) End If // Initialize the State Box if this is the first call to the method. // The State box has a maximum of 256 bytes. If mBox = Nil Then mBox = New MemoryBlock(256) // Just in case there is an older Key in use // Let's get rid of the old MemoryBlock storing it // And create a new one with the Key lenght (in bytes) mKeyBlock = Nil mkeyBlock = value mKeyLength = value.Length // Required initialization of the State Box For n As Integer = mFirstIndex To 255 mBox.UInt8Value(n) = n Next mFirstIndex = 0 // Last step on State Box initialization // Permutation of values in the State Box // using for that the provided Key. For n As Integer = mFirstIndex To 255 mSecondIndex = (mSecondIndex + mBox.UInt8Value(n) + mkeyblock.UInt8Value(n Mod mKeyLength)) Mod 256 SwapValues(n,mSecondIndex) Next Else // If the provided key is an empty String, we raise a new Runtime Exception // with a descriptive error message and error number. Raise New RuntimeException(kKeyNotInitialized, Integer(ARCError.KeyNotInitialized)) End If
Como puedes ver, tanto el Constructor
como el método Key
elevan una Excepción en Tiempo de Ejecución en el caso de que la clave proporciona sea una cadena vacía. Tanto el mensaje de error como el número están definidas por una Constante
(el mensaje de error) y un Enumerador
(el valor de Error) como para de la clase propiamente dicha. Así que, prosigamos ya añadamos en primer lugar una Constante a la clase ARC4 utilizando los siguientes valores:
- Constant Name: kKeyNotInitialized
- Default Value: Key Not Initialized
- Type: String
- Scope: Protected
Y en el caso del Enumerador:
- Name: ARCError
- Type: Integer
- Scope: Public
- Value: KeyNotInitialized = -1
Adicionalmente, el método Key invoca a su vez al método SwapValues
para realizar la permutación de valores en la “caja de estado”. Por tanto, añade un nuevo método utilizando los siguientes valores en el Panel Inspector:
- Method Name: SwapValues
- Parameters: FirstValue as uint8, SecondValue as UInt8
- Scope: Private
Y escribiendo el siguiente código en el Editor de Código asociado con dicho método:
Var tmp As UInt8 tmp = mBox.UInt8Value(SecondValue) mBox.UInt8Value(SecondValue) = mBox.UInt8Value(FirstValue) mBox.UInt8Value(FirstValue) = tmp
Ahora tan sólo nos queda por añadir dos métodos más en la clase; los encargados de encriptar y desencriptar un bloque de datos dado.
Para encriptar los datos, añade un nuevo método introduciendo los siguientes valores en el Panel Inspector:
- Method Name: Encrypt
- Parameters: Value As String
- Return Type: MemoryBlock
- Scope: Public
Y con el siguiente bloque de código en el Editor de Código asociado:
// Disabling some features for better speed #Pragma DisableBackgroundTasks #Pragma DisableBoundsChecking #Pragma NilObjectChecking False #Pragma StackOverflowChecking False // Index initialization Var mFirstIndex As Integer Var mSecondIndex As Integer Var k As UInt8 // If we have a non initialized mKeyBlock // that means that the key has not being initialized // so we raise an exception If mKeyBlock <> Nil Then //Initialize Key again me.Key = mKeyBlock.StringValue(0,mKeyBlock.Size) // Let's put the text to encrypt into a memoryblock // so it is faster to iterate through their bytes Var target As MemoryBlock = value Var temp As UInt8 Var maxSize As Integer = target.Size-1 // And we calculate the new bytes values (encrypted values) // using the ARC4 algorithm // Basically, every byte in the source block will be XORed // with the calculated byte from the State box. For n As Integer = 0 To maxSize mFirstIndex = (mFirstIndex + 1) Mod 256 mSecondIndex = (mSecondIndex + mBox.UInt8Value(mFirstIndex)) Mod 256 SwapValues(mFirstIndex,mSecondIndex) k = mBox.UInt8Value((mBox.UInt8Value(mFirstIndex) + mBox.UInt8Value(mSecondIndex)) Mod 256) target.UInt8Value(n) = target.UInt8Value(n) Xor k Next //…and return the block of data already encrypted Return target Else Raise New RuntimeException(kKeyNotInitialized, Integer(ARCError.KeyNotInitialized)) End If
El último método, encargado de desencriptar un bloque de datos, sería el siguiente:
- Method Name: Decrypt
- Parameters: Source As MemoryBlock
- Return Type: MemoryBlock
- Scope: Public
Escribiendo el siguiente fragmento de código en el Editor de Código asociado:
If Not (Source Is Nil) Then // Simply call the same method we use to // encrypt data, avoiding code duplication // and returning the now deciphered data to the caller Return Me.Encrypt(Source) End If
¡Y eso es todo! Si estás interesado en obtener más información sobre el algoritmo ARC4, puedes leer este artículo en la Wikipedia… o, mejor aún, los excelentes libros “Applied Cryptography” y “Cryptography Engineering” donde puedes profundizar incluso más sobre este y otros algoritmos de cifrado. Por supuesto, recuerda que en el módulo Crypto del Framework de Xojo ya encontrarás unos cuantos algoritmos listos para usar en tus proyectos.
Por supuesto, siempre puedes descargar el proyecto de ejemplo Xojo junto con la clase ARC4 ya lista para funcionar desde este enlace.
Javier, tienes un proyecto que muestre ambos casos por separado de Encriptado y Desencriptado?
Seria para usar en proyecto web2 pero supongo es genérico.