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
- Xbase++ documentation Class AesCrypt()
- Wikepedia article Padding (cryptography)