Overview​

In this article it is shown how the class AesCrypt() can be specialized to implement some padding schemes for encrypting and decrypting messages.

Background​

The Advanced Encryption Standard (AES) standard is a block cipher algorithm that only allows for the encryption of messages in the length of a multiple of the block size. If the length of the message does not exactly meet a block limit, then the message must be extended accordingly to meet the required criteria. Besdies the default space character padding there have been various more complex packing algorithms developed over time. Part of some padding procedures is the requirement to add padding at the end of the message to embedded the information about how many padding characters have been added to the message. This way, after decryption, the message can be restored to its original length without knowing the message structure/content.

Calculating the padding length​

If padding must be added to the message to carry the length of the message, then the algorithm required to calculate the padding length can be implemented in this way:
Xbase++:
/// <summary>
/// This function calcules the required padding length from the block length
/// and the message.
/// </summary>
/// <returns>
/// Required padding length in bytes. If the length of the message matches a
/// block boundary the length of the block is returned.
/// </returns>
FUNCTION GetPadLength( nBlockBit, cMessage )
   LOCAL nResult
   LOCAL nBlockBytes

   nBlockBytes := Int(nBlockBit / 8)
   nResult := nBlockBytes - Mod( Len(cMessage), nBlockBytes )
RETURN nResult

Specialization of the AesCrypt() class​

In the following code sequences, the AesCryptPKCS5Pad is derived from the AesCrypt() class and the :encrypt() and :decrypt() methods are overriden. The implementations of both methods shown not only perform the cryptographic operation by calling the base class implementation. They also apply the padding algorithm.

PKZS5 / PKZS7 Padding​

Xbase++:
/// <summary>
/// In PKCS5 padding, the value of each byte is the length of the padding.
/// The message is extended by the padding before encryption.
/// After decryption, the padding is removed, which restores the original message.
/// </summary>
CLASS AesCryptPKCS5Pad FROM AesCrypt
   EXPORTED:
      METHOD encrypt
      METHOD decrypt
ENDCLASS

METHOD AesCryptPKCS5Pad:encrypt( cMessage )
   LOCAL nPadLength
   LOCAL cResult
   nPadLength := GetPadLength( ::BlockLength, cMessage )
   cMessage += Replicate( Chr(nPadLength), nPadLength )
   cResult := SUPER:encrypt( cMessage )
RETURN cResult

METHOD AesCryptPKCS5Pad:decrypt( cCipher )
   LOCAL cResult
   LOCAL nPadLength
   LOCAL cPadChar
   LOCAL cPadding

   cResult := SUPER:decrypt( cCipher )

   // The last byte of the message is the padding length
   cPadChar := cResult[-1]
   nPadLength := Asc(cPadChar)

   // Error out when the padding holds an unexpected byte.
   cPadding := SubStr( cResult, Len( cResult ) - nPadLength + 1, nPadLength )
   IF .NOT. cPadding == Replicate( cPadChar, nPadLength )
      // Todo: Handle cryptogrphic error
      RETURN NIL
   ENDIF

   // Remove the padding from the message
   cResult := SubStr( cResult, 1, Len( cResult ) - nPadLength )

RETURN cResult

OneAndZeroes Padding, ISO/IEC 7816-4​

Xbase++:
/// <summary>
/// In One And Zero padding the value of the first byte is 128 (binary 10000000)
/// followed by zero bytes only.
/// The message is extended by the padding before encryption.
/// After decryption, the padding is removed, which restores the original message.
/// </summary>
CLASS AesOneAndZeroPad FROM AesCrypt
   EXPORTED:
      METHOD encrypt
      METHOD decrypt
ENDCLASS

METHOD AesOneAndZeroPad:encrypt( cMessage )
   LOCAL nPadLength
   LOCAL cResult
   nPadLength := GetPadLength( ::BlockLength, cMessage )
   cMessage += Chr(128)+Replicate( Chr(0), nPadLength - 1 )
   cResult := SUPER:encrypt( cMessage )
RETURN cResult

METHOD AesOneAndZeroPad:decrypt( cCipher )
   LOCAL cResult
   LOCAL nPos
   LOCAL cZeroBytes

   cResult := SUPER:decrypt( cCipher )

   // Find last 128 byte
   nPos := Rat( Chr(128), cResult )
   cZeroBytes := SubStr( cResult, nPos + 1 )

   // Ensure 128 is followed by zero bytes only
   IF .NOT. cZeroBytes == Replicate( Chr(0), Len( cCipher ) - nPos )
      // Todo: Handle cryptogrphic error
      RETURN NIL
   ENDIF

   cResult := SubStr( cResult, 1, nPos - 1 )

RETURN cResult

ANSI X9.23 Padding​

Xbase++:
/// <summary>
/// In ANSI X9.23 padding the value of the last byte is the length of the
/// padding. All preceding bytes are zero bytes only.
/// The message is extended by the padding before encryption.
/// After decryption, the padding is removed, which restores the original message.
/// </summary>
CLASS AesAnsiX923Pad FROM AesCrypt
   EXPORTED:
      METHOD encrypt
      METHOD decrypt
ENDCLASS

METHOD AesAnsiX923Pad:encrypt( cMessage )
   LOCAL nPadLength
   LOCAL cResult
   nPadLength := GetPadLength( ::BlockLength, cMessage )
   cMessage += Replicate( Chr(0), nPadLength - 1 ) + Chr(nPadLength)
   cResult := SUPER:encrypt( cMessage )
RETURN cResult

METHOD AesAnsiX923Pad:decrypt( cCipher )
   LOCAL cResult
   LOCAL nPadLength
   LOCAL cZeroBytes

   cResult := SUPER:decrypt( cCipher )
   nPadLength := Asc(cResult[-1])

   cZeroBytes := SubStr( cResult, Len( cResult ) - nPadLength + 1, nPadLength - 1 )

   // Ensure padding consists of zero bytes except the last one
   IF .NOT. cZeroBytes == Replicate( Chr(0), nPadLength - 1 )
      // Todo: Handle cryptogrphic error
      RETURN NIL
   ENDIF

   cResult := SubStr( cResult, 1, Len( cResult ) - nPadLength )

RETURN cResult

W3C Padding, ISO 10126​

Xbase++:
/// <summary>
/// In W3C padding, the value of the last byte is the length of the padding. The
/// preceding bytes are randomized.
/// The message is extended by the padding before encryption.
/// After decryption, the padding is removed, which restores the original message.
/// </summary>
CLASS AesW3CPad FROM AesCrypt
   EXPORTED:
      METHOD encrypt
      METHOD decrypt
ENDCLASS

METHOD AesW3CPad:encrypt( cMessage )
   LOCAL nPadLength
   LOCAL cResult
   nPadLength := GetPadLength( ::BlockLength, cMessage )
   cMessage += RandomKey( nPadLength - 1 ) + Chr(nPadLength)
   cResult := SUPER:encrypt( cMessage )
RETURN cResult

METHOD AesW3CPad:decrypt( cCipher )
   LOCAL cResult
   LOCAL nPadLength

   cResult := SUPER:decrypt( cCipher )
   nPadLength := Asc(cResult[-1])
   cResult := SubStr( cResult, 1, Len( cResult ) - nPadLength )

RETURN cResult

Usage​

The user of these spezialized classes with embedded padding does not need to prepare the buffer for the message with the AesCrypt(): prepareBuffer() anymore. The usage in fact is simplified and supports the latest padding algorithms envolved.

Xbase++:
   // Key with 16 bytes for 128 bit encryption
   oKey := SecureKey():new():setBinKey( "1234567890123456" )

   oAesCrypt := AesCryptPKCS5Pad():new(oKey)

   // Message length does not match the block size of 128 bit
   cMessage := "Hello"

   // Encryption
   cCipher := oAesCrypt:encrypt( cMessage )

   // Decryption
   cDecryptedMessage := oAesCrypt:decrypt(cCipher)

   ? cDecryptedMessage == cMessage   // -> .T.

References​