AES Dongle Implementation

Overview

The XBE and XBM series of devices include Advanced Encryption Standard (AES) features for encryption and decryption. The mode used is CBC with padded zeros and a keysize of 128. This page shows how to use the AES features as a software dongle for piracy prevention of developer's applications. The main use of the dongle feature is to prevent people from using the developer's software without purchasing a P.I. Engineering Product from that developer. The developer must "initialize" each X-keys unit with a "key". Sample code for setting up a unit for dongle protection is shown in the samples under AES Dongle section Set AES Key. Sample code for checking the unit's key is shown in the samples under AES Dongle section Check AES Key. Below are the steps needed to implement this feature. Note: This feature is available regardless of operating system or if using our PIEHid* dlls.

Set AES Key

This one step procedure is done once per device by the developer prior to sale.

The developer must choose 16 arbitrary values between 0 and 255, but not all 0's and write them to the device using the Set AES Key output report format. See the Set AES Key example code included in each sample for specific details. The 16 bytes must be recorded for later in order to check the key. This key is written to an unreadable section of the device's eeprom.

This process is demonstrated in the Set AES Key code below.

Check AES Key

This is done within the developer application to determine if the proper hardware "dongle" is connected.

Checking the key is done by first encrypting a user defined phrase using X-keys encryption then feeding these results along with the known user's AES key into a decryption function of the user's choice. In the sample provided, we use Microsoft's Aes Class https://learn.microsoft.com/en-us/dotnet/api/system.security.cryptography.aes?view=net-8.0. The decryption will produce the equivalent phrase if the key is matched.

In order to encrypt the phrase, the AES initialization vector must be set.

This process is demonstrated in the Check AES Key in the code below.

Sample [C#]

.......

//Set AES Key
if (selecteddevice != -1) //do nothing if not enumerated
{
	//Pick a secret 16 byte key and save this Key!! Valid values are 0 to 255 but not all 0's
	myKey[0] = 7;
	myKey[1] = 58;
	myKey[2] = 33;
	myKey[3] = 243;
	myKey[4] = 7;
	myKey[5] = 58;
	myKey[6] = 33;
	myKey[7] = 243;
	myKey[8] = 7;
	myKey[9] = 58;
	myKey[10] = 33;
	myKey[11] = 243;
	myKey[12] = 7;
	myKey[13] = 58;
	myKey[14] = 33;
	myKey[15] = 243;
	//Save these numbers, they are needed to check the key

	//Write the AES key to the device
	for (int j = 0; j < devices[selecteddevice].WriteLength; j++)
	{
		wData[j] = 0;
	}
	wData[0] = 0;
	wData[1] = 137; //0x89 Set AES Key
	for (int i = 0; i < 16; i++)
	{
		wData[2 + i] = myKey[i];
	}

	int result = 404;
	while (result == 404) { result = devices[selecteddevice].WriteData(wData); }
	if (result != 0)
	{
		//"Write Fail: " + result;
	}
	else
	{
		//"Write Success - Set AES Dongle";
	}
}
//----------------------------------------------
//Check AES Key
//Check dongle by encrypting a phrase and checking with C# decryption

if (selecteddevice != -1) //do nothing if not enumerated
{
	//Before each encryption MUST set the initialization vector. The initialzation vector is set to all 0s after each encryption and decryption in the X-keys.   
	Random rnd = new Random();
	for (int i = 0; i < 16; i++)
	{
		myIV[i] = (byte)rnd.Next(0, 254); //valid values are 0-255 HOWEVER all 0s is not allowed because that is interpreted as an non-initialized IV
	}

	for (int j = 0; j < devices[selecteddevice].WriteLength; j++)
	{
		wData[j] = 0;
	}
	wData[0] = 0;
	wData[1] = 138; //0x8A Set AES IV
	for (int i = 0; i < 16; i++)
	{
		wData[2 + i] = myIV[i];
	}

	int result = 404;
	while (result == 404) { result = devices[selecteddevice].WriteData(wData); }

	//Encrypt
	bool savecallbackstate = devices[selecteddevice].callNever; //Turn off callback to avoid capturing data there
	devices[selecteddevice].callNever = true;

	string mymessage = "Enter any phrase";
	for (int j = 0; j < devices[selecteddevice].WriteLength; j++)
	{
		wData[j] = 0;
	}
	wData[0] = 0;
	wData[1] = 139; //0x8B AES Encrypt
	for (int i = 0; i < mymessage.Length; i++)
	{
		wData[2 + i] = (byte)mymessage[i];
	}

	result = 404;
	while (result == 404) { result = devices[selecteddevice].WriteData(wData); }
	if (result != 0)
	{
		//"Write Fail: " + result;
	}
	else
	{
		//"Write Success - AES Encrypt";
	}
	//read back the encrypted data
	byte[] encrypteddata = new byte[32];
	byte[] data = null;
	int countout = 0;
	data = new byte[80];
	int ret = devices[selecteddevice].BlockingReadData(ref data, 100);
	while ((ret == 0 && data[2] != 151) || ret == 304)
	{
		if (ret == 304)
		{
			// Didn't get any data for 100ms, increment the countout extra
			countout += 99;
		}
		countout++;
		if (countout > 1000) //increase this if have to check more than once
			break;
		ret = devices[selecteddevice].BlockingReadData(ref data, 100);
	}
	for (int i = 0; i < 32; i++)
	{
		encrypteddata[i] = data[i + 3];
	}

	devices[selecteddevice].callNever = savecallbackstate; //return callback to previous state

	//Decrypt
	//use the same secret 16 byte key that was used in Set AES Key and the same IV as used above to encrypt
	myKey[0] = 7;
	myKey[1] = 58;
	myKey[2] = 33;
	myKey[3] = 243;
	myKey[4] = 7;
	myKey[5] = 58;
	myKey[6] = 33;
	myKey[7] = 243;
	myKey[8] = 7;
	myKey[9] = 58;
	myKey[10] = 33;
	myKey[11] = 243;
	myKey[12] = 7;
	myKey[13] = 58;
	myKey[14] = 33;
	myKey[15] = 243;

	string decryptresults = DecryptStringFromBytes_Aes(encrypteddata, myKey, myIV, CipherMode.CBC, PaddingMode.Zeros);
	//remove padded 0s
	decryptresults = decryptresults.Replace("\0", string.Empty);
	if (mymessage == decryptresults)
	{
		//"Pass"
	}
	else
	{
    	//"Fail" 
	}
}
//--------------------------------------------------------
using System.Security.Cryptography; //AES https://learn.microsoft.com/en-us/dotnet/api/system.security.cryptography.aes?view=net-8.0
using System.IO; //AES

static string DecryptStringFromBytes_Aes(byte[] cipherText, byte[] Key, byte[] IV, CipherMode thismode, PaddingMode thispadding)
{
            // Check arguments.
            if (cipherText == null || cipherText.Length <= 0)
                throw new ArgumentNullException("cipherText");
            if (Key == null || Key.Length <= 0)
                throw new ArgumentNullException("Key");
            if (IV == null || IV.Length <= 0)
                throw new ArgumentNullException("IV");

            // Declare the string used to hold
            // the decrypted text.
            string plaintext = null;

            // Create an Aes object
            // with the specified key and IV.
            using (Aes aesAlg = Aes.Create())
            {
                aesAlg.Key = Key;
                aesAlg.IV = IV; 
                aesAlg.Mode = thismode; // CipherMode.CBC; 
                aesAlg.Padding = thispadding; // PaddingMode.Zeros; 

                // Create a decryptor to perform the stream transform.
                ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);


                // Create the streams used for decryption.
                using (MemoryStream msDecrypt = new MemoryStream(cipherText))
                {
                    using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
                    {
                        using (StreamReader srDecrypt = new StreamReader(csDecrypt))
                        {

                            // Read the decrypted bytes from the decrypting stream
                            // and place them in a string.
                            plaintext = srDecrypt.ReadToEnd();
                        }
                    }
                }
            }

            return plaintext;
}
........

 

Back to top