C++程序  |  896行  |  31.46 KB

// This file was extracted from the TCG Published
// Trusted Platform Module Library
// Part 4: Supporting Routines
// Family "2.0"
// Level 00 Revision 01.16
// October 30, 2014

#include       "OsslCryptoEngine.h"
//
//     The following sets of defines are used to allow use of the SM4 algorithm identifier while waiting for the
//     SM4 implementation code to appear.
//
typedef   AES_KEY SM4_KEY;
#define   SM4_set_encrypt_key            AES_set_encrypt_key
#define   SM4_set_decrypt_key            AES_set_decrypt_key
#define   SM4_decrypt                    AES_decrypt
#define   SM4_encrypt                    AES_encrypt
//
//
//      Utility Functions
//
//      _cpri_SymStartup()
//
LIB_EXPORT BOOL
_cpri__SymStartup(
      void
)
{
      return TRUE;
}
//
//
//      _cpri__GetSymmetricBlockSize()
//
//     This function returns the block size of the algorithm.
//
//     Return Value                      Meaning
//
//     <= 0                              cipher not supported
//     >0                                the cipher block size in bytes
//
LIB_EXPORT INT16
_cpri__GetSymmetricBlockSize(
      TPM_ALG_ID         symmetricAlg,        // IN: the symmetric algorithm
      UINT16             keySizeInBits        // IN: the key size
      )
{
   switch (symmetricAlg)
   {
#ifdef TPM_ALG_AES
   case TPM_ALG_AES:
#endif
#ifdef TPM_ALG_SM4 // Both AES and SM4 use the same block size
   case TPM_ALG_SM4:
#endif
       if(keySizeInBits != 0) // This is mostly to have a reference to
              // keySizeInBits for the compiler
              return 16;
         else
             return 0;
         break;
    default:
        return 0;
    }
}
//
//
//      AES Encryption
//
//      _cpri__AESEncryptCBC()
//
//     This function performs AES encryption in CBC chain mode. The input dIn buffer is encrypted into dOut.
//     The input iv buffer is required to have a size equal to the block size (16 bytes). The dInSize is required to
//     be a multiple of the block size.
//
//     Return Value                      Meaning
//
//     CRYPT_SUCCESS                     if success
//     CRYPT_PARAMETER                   dInSize is not a multiple of the block size
//
LIB_EXPORT CRYPT_RESULT
_cpri__AESEncryptCBC(
    BYTE                *dOut,          // OUT:
    UINT32               keySizeInBits, // IN: key size in bit
    BYTE                *key,           // IN: key buffer. The size of this buffer in
                                        //      bytes is (keySizeInBits + 7) / 8
    BYTE                *iv,            // IN/OUT: IV for decryption.
    UINT32               dInSize,       // IN: data size (is required to be a multiple
                                        //      of 16 bytes)
    BYTE                *dIn            // IN: data buffer
    )
{
    AES_KEY         AesKey;
    BYTE           *pIv;
    INT32           dSize;              // Need a signed version
    int             i;
    pAssert(dOut != NULL && key != NULL && iv != NULL && dIn != NULL);
    if(dInSize == 0)
        return CRYPT_SUCCESS;
    pAssert(dInSize <= INT32_MAX);
    dSize = (INT32)dInSize;
    // For CBC, the data size must be an even multiple of the
    // cipher block size
    if((dSize % 16) != 0)
        return CRYPT_PARAMETER;
    // Create AES encrypt key schedule
    if (AES_set_encrypt_key(key, keySizeInBits, &AesKey) != 0)
        FAIL(FATAL_ERROR_INTERNAL);
    // XOR the data block into the IV, encrypt the IV into the IV
    // and then copy the IV to the output
    for(; dSize > 0; dSize -= 16)
    {
         pIv = iv;
         for(i = 16; i > 0; i--)
             *pIv++ ^= *dIn++;
         AES_encrypt(iv, iv, &AesKey);
         pIv = iv;
         for(i = 16; i > 0; i--)
             *dOut++ = *pIv++;
    }
    return CRYPT_SUCCESS;
}
//
//
//       _cpri__AESDecryptCBC()
//
//      This function performs AES decryption in CBC chain mode. The input dIn buffer is decrypted into dOut.
//      The input iv buffer is required to have a size equal to the block size (16 bytes). The dInSize is required to
//      be a multiple of the block size.
//
//      Return Value                     Meaning
//
//      CRYPT_SUCCESS                    if success
//      CRYPT_PARAMETER                  dInSize is not a multiple of the block size
//
LIB_EXPORT CRYPT_RESULT
_cpri__AESDecryptCBC(
    BYTE                *dOut,          // OUT: the decrypted data
    UINT32               keySizeInBits, // IN: key size in bit
    BYTE                *key,           // IN: key buffer. The size of this buffer in
                                        //     bytes is (keySizeInBits + 7) / 8
    BYTE                *iv,            // IN/OUT: IV for decryption. The size of this
                                        //     buffer is 16 byte
    UINT32               dInSize,       // IN: data size
    BYTE                *dIn            // IN: data buffer
    )
{
    AES_KEY         AesKey;
    BYTE           *pIv;
    int             i;
    BYTE            tmp[16];
    BYTE           *pT = NULL;
    INT32           dSize;
    pAssert(dOut != NULL && key != NULL && iv != NULL && dIn != NULL);
    if(dInSize == 0)
        return CRYPT_SUCCESS;
    pAssert(dInSize <= INT32_MAX);
    dSize = (INT32)dInSize;
    // For CBC, the data size must be an even multiple of the
    // cipher block size
    if((dSize % 16) != 0)
        return CRYPT_PARAMETER;
    // Create AES key schedule
    if (AES_set_decrypt_key(key, keySizeInBits, &AesKey) != 0)
        FAIL(FATAL_ERROR_INTERNAL);
    // Copy the input data to a temp buffer, decrypt the buffer into the output;
    // XOR in the IV, and copy the temp buffer to the IV and repeat.
    for(; dSize > 0; dSize -= 16)
    {
//
        pT = tmp;
        for(i = 16; i> 0; i--)
            *pT++ = *dIn++;
        AES_decrypt(tmp, dOut, &AesKey);
        pIv = iv;
        pT = tmp;
        for(i = 16; i> 0; i--)
        {
            *dOut++ ^= *pIv;
            *pIv++ = *pT++;
        }
   }
   return CRYPT_SUCCESS;
}
//
//
//       _cpri__AESEncryptCFB()
//
//      This function performs AES encryption in CFB chain mode. The dOut buffer receives the values
//      encrypted dIn. The input iv is assumed to be the size of an encryption block (16 bytes). The iv buffer will
//      be modified to contain the last encrypted block.
//
//      Return Value                      Meaning
//
//      CRYPT_SUCCESS                     no non-fatal errors
//
LIB_EXPORT CRYPT_RESULT
_cpri__AESEncryptCFB(
   BYTE                *dOut,          // OUT: the encrypted
   UINT32               keySizeInBits, // IN: key size in bit
   BYTE                *key,           // IN: key buffer. The size of this buffer in
                                       //     bytes is (keySizeInBits + 7) / 8
   BYTE                *iv,            // IN/OUT: IV for decryption.
   UINT32               dInSize,       // IN: data size
   BYTE                *dIn            // IN: data buffer
   )
{
   BYTE           *pIv = NULL;
   AES_KEY         AesKey;
   INT32           dSize;               // Need a signed version of dInSize
   int             i;
   pAssert(dOut != NULL && key != NULL && iv != NULL && dIn != NULL);
   if(dInSize == 0)
       return CRYPT_SUCCESS;
   pAssert(dInSize <= INT32_MAX);
   dSize = (INT32)dInSize;
   // Create AES encryption key schedule
   if (AES_set_encrypt_key(key, keySizeInBits, &AesKey) != 0)
       FAIL(FATAL_ERROR_INTERNAL);
   // Encrypt the IV into the IV, XOR in the data, and copy to output
   for(; dSize > 0; dSize -= 16)
   {
       // Encrypt the current value of the IV
       AES_encrypt(iv, iv, &AesKey);
       pIv = iv;
       for(i = (int)(dSize < 16) ? dSize : 16; i > 0; i--)
           // XOR the data into the IV to create the cipher text
           // and put into the output
           *dOut++ = *pIv++ ^= *dIn++;
   }
   // If the inner loop (i loop) was smaller than 16, then dSize would have been
   // smaller than 16 and it is now negative. If it is negative, then it indicates
   // how many bytes are needed to pad out the IV for the next round.
   for(; dSize < 0; dSize++)
       *pIv++ = 0;
   return CRYPT_SUCCESS;
}
//
//
//       _cpri__AESDecryptCFB()
//
//      This function performs AES decrypt in CFB chain mode. The dOut buffer receives the values decrypted
//      from dIn.
//      The input iv is assumed to be the size of an encryption block (16 bytes). The iv buffer will be modified to
//      contain the last decoded block, padded with zeros
//
//      Return Value                    Meaning
//
//      CRYPT_SUCCESS                   no non-fatal errors
//
LIB_EXPORT CRYPT_RESULT
_cpri__AESDecryptCFB(
   BYTE                *dOut,          // OUT: the decrypted data
   UINT32               keySizeInBits, // IN: key size in bit
   BYTE                *key,           // IN: key buffer. The size of this buffer in
                                       //     bytes is (keySizeInBits + 7) / 8
   BYTE                *iv,            // IN/OUT: IV for decryption.
   UINT32               dInSize,       // IN: data size
   BYTE                *dIn            // IN: data buffer
   )
{
   BYTE           *pIv = NULL;
   BYTE            tmp[16];
   int             i;
   BYTE           *pT;
   AES_KEY         AesKey;
   INT32           dSize;
   pAssert(dOut != NULL && key != NULL && iv != NULL && dIn != NULL);
   if(dInSize == 0)
       return CRYPT_SUCCESS;
   pAssert(dInSize <= INT32_MAX);
   dSize = (INT32)dInSize;
   // Create AES encryption key schedule
   if (AES_set_encrypt_key(key, keySizeInBits, &AesKey) != 0)
       FAIL(FATAL_ERROR_INTERNAL);
   for(; dSize > 0; dSize -= 16)
   {
       // Encrypt the IV into the temp buffer
       AES_encrypt(iv, tmp, &AesKey);
       pT = tmp;
       pIv = iv;
       for(i = (dSize < 16) ? dSize : 16; i > 0; i--)
           // Copy the current cipher text to IV, XOR
           // with the temp buffer and put into the output
           *dOut++ = *pT++ ^ (*pIv++ = *dIn++);
   }
   // If the inner loop (i loop) was smaller than 16, then dSize
   // would have been smaller than 16 and it is now negative
   // If it is negative, then it indicates how may fill bytes
   // are needed to pad out the IV for the next round.
   for(; dSize < 0; dSize++)
       *pIv++ = 0;
   return CRYPT_SUCCESS;
}
//
//
//       _cpri__AESEncryptCTR()
//
//      This function performs AES encryption/decryption in CTR chain mode. The dIn buffer is encrypted into
//      dOut. The input iv buffer is assumed to have a size equal to the AES block size (16 bytes). The iv will be
//      incremented by the number of blocks (full and partial) that were encrypted.
//
//      Return Value                      Meaning
//
//      CRYPT_SUCCESS                     no non-fatal errors
//
LIB_EXPORT CRYPT_RESULT
_cpri__AESEncryptCTR(
   BYTE                *dOut,          // OUT: the encrypted data
   UINT32               keySizeInBits, // IN: key size in bit
   BYTE                *key,           // IN: key buffer. The size of this buffer in
                                       //     bytes is (keySizeInBits + 7) / 8
   BYTE                *iv,            // IN/OUT: IV for decryption.
   UINT32               dInSize,       // IN: data size
   BYTE                *dIn            // IN: data buffer
   )
{
   BYTE            tmp[16];
   BYTE           *pT;
   AES_KEY         AesKey;
   int             i;
   INT32           dSize;
   pAssert(dOut != NULL && key != NULL && iv != NULL && dIn != NULL);
   if(dInSize == 0)
       return CRYPT_SUCCESS;
   pAssert(dInSize <= INT32_MAX);
   dSize = (INT32)dInSize;
   // Create AES encryption schedule
   if (AES_set_encrypt_key(key, keySizeInBits, &AesKey) != 0)
       FAIL(FATAL_ERROR_INTERNAL);
   for(; dSize > 0; dSize -= 16)
   {
       // Encrypt the current value of the IV(counter)
       AES_encrypt(iv, (BYTE *)tmp, &AesKey);
        //increment the counter (counter is big-endian so start at end)
        for(i = 15; i >= 0; i--)
            if((iv[i] += 1) != 0)
                break;
        // XOR the encrypted counter value with input and put into output
        pT = tmp;
        for(i = (dSize < 16) ? dSize : 16; i > 0; i--)
            *dOut++ = *dIn++ ^ *pT++;
   }
   return CRYPT_SUCCESS;
}
//
//       _cpri__AESEncryptECB()
//
//      AES encryption in ECB mode. The data buffer is modified to contain the cipher text.
//
//      Return Value                      Meaning
//
//      CRYPT_SUCCESS                     no non-fatal errors
//
LIB_EXPORT CRYPT_RESULT
_cpri__AESEncryptECB(
    BYTE                *dOut,          // OUT: encrypted data
    UINT32               keySizeInBits, // IN: key size in bit
    BYTE                *key,           // IN: key buffer. The size of this buffer in
                                        //     bytes is (keySizeInBits + 7) / 8
    UINT32               dInSize,       // IN: data size
    BYTE                *dIn            // IN: clear text buffer
    )
{
    AES_KEY          AesKey;
    INT32            dSize;
    pAssert(dOut != NULL && key != NULL && dIn != NULL);
    if(dInSize == 0)
        return CRYPT_SUCCESS;
    pAssert(dInSize <= INT32_MAX);
    dSize = (INT32)dInSize;
    // For ECB, the data size must be an even multiple of the
    // cipher block size
    if((dSize % 16) != 0)
        return CRYPT_PARAMETER;
    // Create AES encrypting key schedule
    if (AES_set_encrypt_key(key, keySizeInBits, &AesKey) != 0)
        FAIL(FATAL_ERROR_INTERNAL);
    for(; dSize > 0; dSize -= 16)
    {
        AES_encrypt(dIn, dOut, &AesKey);
        dIn = &dIn[16];
        dOut = &dOut[16];
    }
   return CRYPT_SUCCESS;
}
//
//
//       _cpri__AESDecryptECB()
//
//      This function performs AES decryption using ECB (not recommended). The cipher text dIn is decrypted
//      into dOut.
//
//      Return Value                      Meaning
//
//      CRYPT_SUCCESS                     no non-fatal errors
//
LIB_EXPORT CRYPT_RESULT
_cpri__AESDecryptECB(
   BYTE                *dOut,          // OUT: the clear text data
   UINT32               keySizeInBits, // IN: key size in bit
   BYTE                *key,           // IN: key buffer. The size of this buffer in
                                       //     bytes is (keySizeInBits + 7) / 8
   UINT32               dInSize,       // IN: data size
   BYTE                *dIn            // IN: cipher text buffer
   )
{
   AES_KEY         AesKey;
   INT32           dSize;
   pAssert(dOut != NULL && key != NULL && dIn != NULL);
   if(dInSize == 0)
       return CRYPT_SUCCESS;
   pAssert(dInSize <= INT32_MAX);
   dSize = (INT32)dInSize;
   // For ECB, the data size must be an even multiple of the
   // cipher block size
   if((dSize % 16) != 0)
       return CRYPT_PARAMETER;
   // Create AES decryption key schedule
   if (AES_set_decrypt_key(key, keySizeInBits, &AesKey) != 0)
       FAIL(FATAL_ERROR_INTERNAL);
   for(; dSize > 0; dSize -= 16)
   {
       AES_decrypt(dIn, dOut, &AesKey);
       dIn = &dIn[16];
       dOut = &dOut[16];
   }
   return CRYPT_SUCCESS;
}
//
//
//       _cpri__AESEncryptOFB()
//
//      This function performs AES encryption/decryption in OFB chain mode. The dIn buffer is modified to
//      contain the encrypted/decrypted text.
//      The input iv buffer is assumed to have a size equal to the block size (16 bytes). The returned value of iv
//      will be the nth encryption of the IV, where n is the number of blocks (full or partial) in the data stream.
//
//
//
//
//      Return Value                  Meaning
//
//      CRYPT_SUCCESS                 no non-fatal errors
//
LIB_EXPORT CRYPT_RESULT
_cpri__AESEncryptOFB(
   BYTE               *dOut,          // OUT: the encrypted/decrypted data
   UINT32              keySizeInBits, // IN: key size in bit
   BYTE               *key,           // IN: key buffer. The size of this buffer in
                                      //     bytes is (keySizeInBits + 7) / 8
   BYTE               *iv,            // IN/OUT: IV for decryption. The size of this
                                      //     buffer is 16 byte
   UINT32              dInSize,       // IN: data size
   BYTE               *dIn            // IN: data buffer
   )
{
   BYTE           *pIv;
   AES_KEY         AesKey;
   INT32           dSize;
   int             i;
   pAssert(dOut != NULL && key != NULL && iv != NULL && dIn != NULL);
   if(dInSize == 0)
       return CRYPT_SUCCESS;
   pAssert(dInSize <= INT32_MAX);
   dSize = (INT32)dInSize;
   // Create AES key schedule
   if (AES_set_encrypt_key(key, keySizeInBits, &AesKey) != 0)
       FAIL(FATAL_ERROR_INTERNAL);
   // This is written so that dIn and dOut may be the same
   for(; dSize > 0; dSize -= 16)
   {
       // Encrypt the current value of the "IV"
       AES_encrypt(iv, iv, &AesKey);
        // XOR the encrypted IV into dIn to create the cipher text (dOut)
        pIv = iv;
        for(i = (dSize < 16) ? dSize : 16; i > 0; i--)
            *dOut++ = (*pIv++ ^ *dIn++);
   }
   return CRYPT_SUCCESS;
}
#ifdef    TPM_ALG_SM4
//
//
//       SM4 Encryption
//
//       _cpri__SM4EncryptCBC()
//
//      This function performs SM4 encryption in CBC chain mode. The input dIn buffer is encrypted into dOut.
//      The input iv buffer is required to have a size equal to the block size (16 bytes). The dInSize is required to
//      be a multiple of the block size.
//
//      Return Value                      Meaning
//
//      CRYPT_SUCCESS                     if success
//      CRYPT_PARAMETER                   dInSize is not a multiple of the block size
//
LIB_EXPORT CRYPT_RESULT
_cpri__SM4EncryptCBC(
    BYTE                *dOut,          // OUT:
    UINT32               keySizeInBits, // IN: key size in bit
    BYTE                *key,           // IN: key buffer. The size of this buffer in
                                        //      bytes is (keySizeInBits + 7) / 8
    BYTE                *iv,            // IN/OUT: IV for decryption.
    UINT32               dInSize,       // IN: data size (is required to be a multiple
                                        //      of 16 bytes)
    BYTE                *dIn            // IN: data buffer
    )
{
    SM4_KEY         Sm4Key;
    BYTE           *pIv;
    INT32           dSize;              // Need a signed version
    int             i;
    pAssert(dOut != NULL && key != NULL && iv != NULL && dIn != NULL);
    if(dInSize == 0)
        return CRYPT_SUCCESS;
    pAssert(dInSize <= INT32_MAX);
    dSize = (INT32)dInSize;
    // For CBC, the data size must be an even multiple of the
    // cipher block size
    if((dSize % 16) != 0)
        return CRYPT_PARAMETER;
    // Create SM4 encrypt key schedule
    if (SM4_set_encrypt_key(key, keySizeInBits, &Sm4Key) != 0)
        FAIL(FATAL_ERROR_INTERNAL);
    // XOR the data block into the IV, encrypt the IV into the IV
    // and then copy the IV to the output
    for(; dSize > 0; dSize -= 16)
    {
        pIv = iv;
        for(i = 16; i > 0; i--)
            *pIv++ ^= *dIn++;
        SM4_encrypt(iv, iv, &Sm4Key);
        pIv = iv;
        for(i = 16; i > 0; i--)
            *dOut++ = *pIv++;
    }
    return CRYPT_SUCCESS;
}
//
//
//       _cpri__SM4DecryptCBC()
//
//      This function performs SM4 decryption in CBC chain mode. The input dIn buffer is decrypted into dOut.
//      The input iv buffer is required to have a size equal to the block size (16 bytes). The dInSize is required to
//      be a multiple of the block size.
//
//      Return Value                     Meaning
//
//      CRYPT_SUCCESS                    if success
//      CRYPT_PARAMETER                  dInSize is not a multiple of the block size
//
LIB_EXPORT CRYPT_RESULT
_cpri__SM4DecryptCBC(
    BYTE                *dOut,          // OUT: the decrypted data
    UINT32               keySizeInBits, // IN: key size in bit
    BYTE                *key,           // IN: key buffer. The size of this buffer in
                                        //     bytes is (keySizeInBits + 7) / 8
    BYTE                *iv,            // IN/OUT: IV for decryption. The size of this
                                        //     buffer is 16 byte
    UINT32               dInSize,       // IN: data size
    BYTE                *dIn            // IN: data buffer
    )
{
    SM4_KEY         Sm4Key;
    BYTE           *pIv;
    int             i;
    BYTE            tmp[16];
    BYTE           *pT = NULL;
    INT32           dSize;
    pAssert(dOut != NULL && key != NULL && iv != NULL && dIn != NULL);
    if(dInSize == 0)
        return CRYPT_SUCCESS;
    pAssert(dInSize <= INT32_MAX);
    dSize = (INT32)dInSize;
    // For CBC, the data size must be an even multiple of the
    // cipher block size
    if((dSize % 16) != 0)
        return CRYPT_PARAMETER;
    // Create SM4 key schedule
    if (SM4_set_decrypt_key(key, keySizeInBits, &Sm4Key) != 0)
        FAIL(FATAL_ERROR_INTERNAL);
    // Copy the input data to a temp buffer, decrypt the buffer into the output;
    // XOR in the IV, and copy the temp buffer to the IV and repeat.
    for(; dSize > 0; dSize -= 16)
    {
        pT = tmp;
        for(i = 16; i> 0; i--)
            *pT++ = *dIn++;
        SM4_decrypt(tmp, dOut, &Sm4Key);
        pIv = iv;
        pT = tmp;
        for(i = 16; i> 0; i--)
        {
            *dOut++ ^= *pIv;
//
              *pIv++ = *pT++;
        }
   }
   return CRYPT_SUCCESS;
}
//
//
//       _cpri__SM4EncryptCFB()
//
//      This function performs SM4 encryption in CFB chain mode. The dOut buffer receives the values
//      encrypted dIn. The input iv is assumed to be the size of an encryption block (16 bytes). The iv buffer will
//      be modified to contain the last encrypted block.
//
//      Return Value                      Meaning
//
//      CRYPT_SUCCESS                     no non-fatal errors
//
LIB_EXPORT CRYPT_RESULT
_cpri__SM4EncryptCFB(
   BYTE                *dOut,          // OUT: the encrypted
   UINT32               keySizeInBits, // IN: key size in bit
   BYTE                *key,           // IN: key buffer. The size of this buffer in
                                       //     bytes is (keySizeInBits + 7) / 8
   BYTE                *iv,            // IN/OUT: IV for decryption.
   UINT32               dInSize,       // IN: data size
   BYTE                *dIn            // IN: data buffer
   )
{
   BYTE           *pIv;
   SM4_KEY         Sm4Key;
   INT32           dSize;               // Need a signed version of dInSize
   int             i;
   pAssert(dOut != NULL && key != NULL && iv != NULL && dIn != NULL);
   if(dInSize == 0)
       return CRYPT_SUCCESS;
   pAssert(dInSize <= INT32_MAX);
   dSize = (INT32)dInSize;
   // Create SM4 encryption key schedule
   if (SM4_set_encrypt_key(key, keySizeInBits, &Sm4Key) != 0)
       FAIL(FATAL_ERROR_INTERNAL);
   // Encrypt the IV into the IV, XOR in the data, and copy to output
   for(; dSize > 0; dSize -= 16)
   {
       // Encrypt the current value of the IV
       SM4_encrypt(iv, iv, &Sm4Key);
       pIv = iv;
       for(i = (int)(dSize < 16) ? dSize : 16; i > 0; i--)
           // XOR the data into the IV to create the cipher text
           // and put into the output
           *dOut++ = *pIv++ ^= *dIn++;
   }
   return CRYPT_SUCCESS;
}
//
//
//       _cpri__SM4DecryptCFB()
//
//      This function performs SM4 decrypt in CFB chain mode. The dOut buffer receives the values decrypted
//      from dIn.
//
//      The input iv is assumed to be the size of an encryption block (16 bytes). The iv buffer will be modified to
//      contain the last decoded block, padded with zeros
//
//      Return Value                    Meaning
//
//      CRYPT_SUCCESS                   no non-fatal errors
//
LIB_EXPORT CRYPT_RESULT
_cpri__SM4DecryptCFB(
   BYTE                *dOut,          // OUT: the decrypted data
   UINT32               keySizeInBits, // IN: key size in bit
   BYTE                *key,           // IN: key buffer. The size of this buffer in
                                       //     bytes is (keySizeInBits + 7) / 8
   BYTE                *iv,            // IN/OUT: IV for decryption.
   UINT32               dInSize,       // IN: data size
   BYTE                *dIn            // IN: data buffer
   )
{
   BYTE           *pIv;
   BYTE            tmp[16];
   int             i;
   BYTE           *pT;
   SM4_KEY         Sm4Key;
   INT32           dSize;
   pAssert(dOut != NULL && key != NULL && iv != NULL && dIn != NULL);
   if(dInSize == 0)
       return CRYPT_SUCCESS;
   pAssert(dInSize <= INT32_MAX);
   dSize = (INT32)dInSize;
   // Create SM4 encryption key schedule
   if (SM4_set_encrypt_key(key, keySizeInBits, &Sm4Key) != 0)
       FAIL(FATAL_ERROR_INTERNAL);
   for(; dSize > 0; dSize -= 16)
   {
       // Encrypt the IV into the temp buffer
       SM4_encrypt(iv, tmp, &Sm4Key);
       pT = tmp;
       pIv = iv;
       for(i = (dSize < 16) ? dSize : 16; i > 0; i--)
           // Copy the current cipher text to IV, XOR
           // with the temp buffer and put into the output
           *dOut++ = *pT++ ^ (*pIv++ = *dIn++);
   }
   // If the inner loop (i loop) was smaller than 16, then dSize
   // would have been smaller than 16 and it is now negative
   // If it is negative, then it indicates how may fill bytes
   // are needed to pad out the IV for the next round.
   for(; dSize < 0; dSize++)
       *iv++ = 0;
   return CRYPT_SUCCESS;
}
//
//
//       _cpri__SM4EncryptCTR()
//
//      This function performs SM4 encryption/decryption in CTR chain mode. The dIn buffer is encrypted into
//      dOut. The input iv buffer is assumed to have a size equal to the SM4 block size (16 bytes). The iv will be
//      incremented by the number of blocks (full and partial) that were encrypted.
//
//      Return Value                      Meaning
//
//      CRYPT_SUCCESS                     no non-fatal errors
//
LIB_EXPORT CRYPT_RESULT
_cpri__SM4EncryptCTR(
   BYTE               *dOut,          // OUT: the encrypted data
   UINT32              keySizeInBits, // IN: key size in bit
   BYTE               *key,           // IN: key buffer. The size of this buffer in
                                      //     bytes is (keySizeInBits + 7) / 8
   BYTE               *iv,            // IN/OUT: IV for decryption.
   UINT32              dInSize,       // IN: data size
   BYTE               *dIn            // IN: data buffer
   )
{
   BYTE            tmp[16];
   BYTE           *pT;
   SM4_KEY         Sm4Key;
   int             i;
   INT32           dSize;
   pAssert(dOut != NULL && key != NULL && iv != NULL && dIn != NULL);
   if(dInSize == 0)
       return CRYPT_SUCCESS;
   pAssert(dInSize <= INT32_MAX);
   dSize = (INT32)dInSize;
   // Create SM4 encryption schedule
   if (SM4_set_encrypt_key(key, keySizeInBits, &Sm4Key) != 0)
       FAIL(FATAL_ERROR_INTERNAL);
   for(; dSize > 0; dSize--)
   {
       // Encrypt the current value of the IV(counter)
       SM4_encrypt(iv, (BYTE *)tmp, &Sm4Key);
        //increment the counter
        for(i = 0; i < 16; i++)
            if((iv[i] += 1) != 0)
                break;
        // XOR the encrypted counter value with input and put into output
        pT = tmp;
        for(i = (dSize < 16) ? dSize : 16; i > 0; i--)
            *dOut++ = *dIn++ ^ *pT++;
   }
   return CRYPT_SUCCESS;
}
//
//       _cpri__SM4EncryptECB()
//
//      SM4 encryption in ECB mode. The data buffer is modified to contain the cipher text.
//
//      Return Value                      Meaning
//
//      CRYPT_SUCCESS                     no non-fatal errors
//
LIB_EXPORT CRYPT_RESULT
_cpri__SM4EncryptECB(
    BYTE                *dOut,          // OUT: encrypted data
    UINT32               keySizeInBits, // IN: key size in bit
    BYTE                *key,           // IN: key buffer. The size of this buffer in
                                        //     bytes is (keySizeInBits + 7) / 8
    UINT32               dInSize,       // IN: data size
    BYTE                *dIn            // IN: clear text buffer
    )
{
    SM4_KEY          Sm4Key;
    INT32            dSize;
    pAssert(dOut != NULL && key != NULL && dIn != NULL);
    if(dInSize == 0)
        return CRYPT_SUCCESS;
    pAssert(dInSize <= INT32_MAX);
    dSize = (INT32)dInSize;
    // For ECB, the data size must be an even multiple of the
    // cipher block size
    if((dSize % 16) != 0)
        return CRYPT_PARAMETER;
    // Create SM4 encrypting key schedule
    if (SM4_set_encrypt_key(key, keySizeInBits, &Sm4Key) != 0)
        FAIL(FATAL_ERROR_INTERNAL);
    for(; dSize > 0; dSize -= 16)
    {
        SM4_encrypt(dIn, dOut, &Sm4Key);
        dIn = &dIn[16];
        dOut = &dOut[16];
    }
    return CRYPT_SUCCESS;
}
//
//
//       _cpri__SM4DecryptECB()
//
//      This function performs SM4 decryption using ECB (not recommended). The cipher text dIn is decrypted
//      into dOut.
//
//
//
//
//      Return Value                      Meaning
//
//      CRYPT_SUCCESS                     no non-fatal errors
//
LIB_EXPORT CRYPT_RESULT
_cpri__SM4DecryptECB(
   BYTE                *dOut,          // OUT: the clear text data
   UINT32               keySizeInBits, // IN: key size in bit
   BYTE                *key,           // IN: key buffer. The size of this buffer in
                                       //     bytes is (keySizeInBits + 7) / 8
   UINT32               dInSize,       // IN: data size
   BYTE                *dIn            // IN: cipher text buffer
   )
{
   SM4_KEY         Sm4Key;
   INT32           dSize;
   pAssert(dOut != NULL && key != NULL && dIn != NULL);
   if(dInSize == 0)
       return CRYPT_SUCCESS;
   pAssert(dInSize <= INT32_MAX);
   dSize = (INT32)dInSize;
   // For ECB, the data size must be an even multiple of the
   // cipher block size
   if((dSize % 16) != 0)
       return CRYPT_PARAMETER;
   // Create SM4 decryption key schedule
   if (SM4_set_decrypt_key(key, keySizeInBits, &Sm4Key) != 0)
       FAIL(FATAL_ERROR_INTERNAL);
   for(; dSize > 0; dSize -= 16)
   {
       SM4_decrypt(dIn, dOut, &Sm4Key);
       dIn = &dIn[16];
       dOut = &dOut[16];
   }
   return CRYPT_SUCCESS;
}
//
//
//       _cpri__SM4EncryptOFB()
//
//      This function performs SM4 encryption/decryption in OFB chain mode. The dIn buffer is modified to
//      contain the encrypted/decrypted text.
//      The input iv buffer is assumed to have a size equal to the block size (16 bytes). The returned value of iv
//      will be the nth encryption of the IV, where n is the number of blocks (full or partial) in the data stream.
//
//      Return Value                      Meaning
//
//      CRYPT_SUCCESS                     no non-fatal errors
//
LIB_EXPORT CRYPT_RESULT
_cpri__SM4EncryptOFB(
   BYTE                *dOut,          // OUT: the encrypted/decrypted data
   UINT32               keySizeInBits, // IN: key size in bit
   BYTE                *key,           // IN: key buffer. The size of this buffer in
                                       //     bytes is (keySizeInBits + 7) / 8
   BYTE                *iv,            // IN/OUT: IV for decryption. The size of this
                                       //     buffer is 16 byte
   UINT32              dInSize,         // IN: data size
   BYTE               *dIn              // IN: data buffer
   )
{
   BYTE           *pIv;
   SM4_KEY         Sm4Key;
   INT32           dSize;
   int             i;
   pAssert(dOut != NULL && key != NULL && iv != NULL && dIn != NULL);
   if(dInSize == 0)
       return CRYPT_SUCCESS;
   pAssert(dInSize <= INT32_MAX);
   dSize = (INT32)dInSize;
   // Create SM4 key schedule
   if (SM4_set_encrypt_key(key, keySizeInBits, &Sm4Key) != 0)
       FAIL(FATAL_ERROR_INTERNAL);
   // This is written so that dIn and dOut may be the same
   for(; dSize > 0; dSize -= 16)
   {
       // Encrypt the current value of the "IV"
       SM4_encrypt(iv, iv, &Sm4Key);
        // XOR the encrypted IV into dIn to create the cipher text (dOut)
        pIv = iv;
        for(i = (dSize < 16) ? dSize : 16; i > 0; i--)
            *dOut++ = (*pIv++ ^ *dIn++);
   }
   return CRYPT_SUCCESS;
}
#endif      //% TPM_ALG_SM4