* This file is part of the openHiTLS project.
*
* openHiTLS is licensed under the Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
*
* http://license.coscl.org.cn/MulanPSL2
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
*/
#include "hitls_build.h"
#ifdef HITLS_CRYPTO_XTS
#include <string.h>
#include "bsl_err_internal.h"
#include "bsl_sal.h"
#include "eal_cipher_local.h"
#include "crypt_utils.h"
#include "crypt_errno.h"
#include "crypt_modes_xts.h"
#include "modes_local.h"
#include "crypt_modes.h"
#define MODES_XTS_BLOCKSIZE 16
#define MODES_XTS_MAX_BLOCKS_PER_DATA_UNIT (1u << 20)
#define MODES_XTS_MAX_DATA_UNIT_BYTES (MODES_XTS_MAX_BLOCKS_PER_DATA_UNIT * MODES_XTS_BLOCKSIZE)
#define SM4_XTS_POLYNOMIAL 0xE1
#define XTS_UPDATE_VALUES(l, i, o, len) \
do { \
(l) -= (len); \
(i) += (len); \
(o) += (len); \
} while (false)
int32_t MODES_XTS_CheckPara(const uint8_t *key, uint32_t len, const uint8_t *iv)
{
if (key == NULL || iv == NULL) {
BSL_ERR_PUSH_ERROR(CRYPT_NULL_INPUT);
return CRYPT_NULL_INPUT;
}
if (len != 32 && len != 64) {
BSL_ERR_PUSH_ERROR(CRYPT_MODES_ERR_KEYLEN);
return CRYPT_MODES_ERR_KEYLEN;
}
return CRYPT_SUCCESS;
}
int32_t MODES_XTS_SetEncryptKey(MODES_CipherXTSCtx *ctx, const uint8_t *key, uint32_t len)
{
int32_t ret;
uint32_t keyLen = len >> 1;
if (memcmp(key, key + keyLen, keyLen) == 0) {
BSL_ERR_PUSH_ERROR(CRYPT_MODES_ERR_KEY);
return CRYPT_MODES_ERR_KEY;
}
ret = ctx->ciphMeth->setEncryptKey(ctx->ciphCtx, key, keyLen);
if (ret != CRYPT_SUCCESS) {
BSL_ERR_PUSH_ERROR(ret);
return ret;
}
ret = ctx->ciphMeth->setEncryptKey((uint8_t*)ctx->ciphCtx + ctx->ciphMeth->ctxSize, key + keyLen, keyLen);
if (ret != CRYPT_SUCCESS) {
BSL_ERR_PUSH_ERROR(ret);
}
return ret;
}
int32_t MODES_XTS_SetDecryptKey(MODES_CipherXTSCtx *ctx, const uint8_t *key, uint32_t len)
{
int32_t ret;
uint32_t keyLen = len >> 1;
if (memcmp(key + keyLen, key, keyLen) == 0) {
BSL_ERR_PUSH_ERROR(CRYPT_MODES_ERR_KEY);
return CRYPT_MODES_ERR_KEY;
}
ret = ctx->ciphMeth->setEncryptKey((uint8_t*)ctx->ciphCtx + ctx->ciphMeth->ctxSize, key + keyLen, keyLen);
if (ret != CRYPT_SUCCESS) {
BSL_ERR_PUSH_ERROR(ret);
return ret;
}
ret = ctx->ciphMeth->setDecryptKey(ctx->ciphCtx, key, keyLen);
if (ret != CRYPT_SUCCESS) {
BSL_ERR_PUSH_ERROR(ret);
}
return ret;
}
#ifdef HITLS_BIG_ENDIAN
void GF128Mul(uint8_t *a, uint32_t len)
{
uint8_t in;
uint8_t out = 0;
in = 0;
for (uint32_t j = 0; j < len; j++) {
out = (a[j] >> 7) & 1;
a[j] = (uint8_t)((a[j] << 1) + in) & 0xFFu;
in = out;
}
if (out > 0) {
a[0] ^= 0x87;
}
}
#else
void GF128Mul(uint8_t *a, uint32_t len)
{
(void)len;
uint64_t *t = (uint64_t *)a;
uint8_t c = (t[1] >> 63) & 0xff;
t[1] = t[1] << 1 | t[0] >> 63;
t[0] = t[0] << 1;
if (c != 0) {
t[0] ^= 0x87;
}
}
#endif
void GF128Mul_GM(uint8_t *a, uint32_t len)
{
uint8_t in = 0;
uint8_t out = 0;
for (uint32_t j = 0; j < len; j++) {
out = (a[j] << 7) & 0x80;
a[j] = (uint8_t)((a[j] >> 1) + in) & 0xFFu;
in = out;
}
if (out > 0) {
a[0] ^= SM4_XTS_POLYNOMIAL;
}
}
int32_t BlockCrypt(MODES_CipherXTSCtx *ctx, const uint8_t *in, const uint8_t *t, uint8_t *pp, bool enc)
{
int32_t ret;
uint32_t blockSize = ctx->blockSize;
DATA64_XOR(in, t, pp, blockSize);
if (enc) {
ret = ctx->ciphMeth->encryptBlock(ctx->ciphCtx, pp, pp, blockSize);
} else {
ret = ctx->ciphMeth->decryptBlock(ctx->ciphCtx, pp, pp, blockSize);
}
if (ret != CRYPT_SUCCESS) {
BSL_ERR_PUSH_ERROR(ret);
return ret;
}
DATA64_XOR(pp, t, pp, blockSize);
return CRYPT_SUCCESS;
}
int32_t BlocksCrypt(MODES_CipherXTSCtx *ctx, const uint8_t **in, uint8_t **out, uint32_t *tmpLen,
bool enc)
{
int32_t ret;
uint32_t blockSize = ctx->blockSize;
const uint8_t *tmpIn = *in;
uint8_t *tmpOut = *out;
while (*tmpLen >= 2 * blockSize) {
ret = BlockCrypt(ctx, tmpIn, ctx->tweak, tmpOut, enc);
if (ret != CRYPT_SUCCESS) {
return ret;
}
XTS_UPDATE_VALUES(*tmpLen, tmpIn, tmpOut, blockSize);
if (ctx->ciphMeth->algId == CRYPT_SYM_SM4) {
GF128Mul_GM(ctx->tweak, blockSize);
} else {
GF128Mul(ctx->tweak, blockSize);
}
}
*in = tmpIn;
*out = tmpOut;
return CRYPT_SUCCESS;
}
int32_t MODES_XTS_Encrypt(MODES_CipherXTSCtx *ctx, const uint8_t *in, uint8_t *out, uint32_t len)
{
int32_t ret;
uint32_t i;
uint8_t pp[MODES_XTS_BLOCKSIZE];
uint32_t tmpLen = len;
const uint8_t *tmpIn = in;
uint8_t *tmpOut = out;
uint8_t *lastBlock = NULL;
uint32_t blockSize = ctx->blockSize;
if (len < blockSize) {
BSL_ERR_PUSH_ERROR(CRYPT_MODE_BUFF_LEN_NOT_ENOUGH);
return CRYPT_MODE_BUFF_LEN_NOT_ENOUGH;
}
ret = BlocksCrypt(ctx, &tmpIn, &tmpOut, &tmpLen, true);
RETURN_RET_IF(ret != CRYPT_SUCCESS, ret);
ret = BlockCrypt(ctx, tmpIn, ctx->tweak, tmpOut, true);
RETURN_RET_IF(ret != CRYPT_SUCCESS, ret);
XTS_UPDATE_VALUES(tmpLen, tmpIn, tmpOut, blockSize);
if (ctx->ciphMeth->algId == CRYPT_SYM_SM4) {
GF128Mul_GM(ctx->tweak, blockSize);
} else {
GF128Mul(ctx->tweak, blockSize);
}
if (tmpLen == 0) {
return CRYPT_SUCCESS;
}
lastBlock = tmpOut - blockSize;
for (i = 0; i < tmpLen; i++) {
tmpOut[i] = lastBlock[i];
pp[i] = tmpIn[i];
}
for (i = tmpLen; i < blockSize; i++) {
pp[i] = lastBlock[i];
}
ret = BlockCrypt(ctx, pp, ctx->tweak, pp, true);
if (ret != CRYPT_SUCCESS) {
BSL_ERR_PUSH_ERROR(ret);
return ret;
}
tmpOut -= blockSize;
if (blockSize > blockSize + tmpLen) {
BSL_ERR_PUSH_ERROR(CRYPT_MEM_CPY_FAIL);
return CRYPT_MEM_CPY_FAIL;
}
memcpy(tmpOut, pp, blockSize);
return CRYPT_SUCCESS;
}
int32_t MODES_XTS_Decrypt(MODES_CipherXTSCtx *ctx, const uint8_t *in, uint8_t *out, uint32_t len)
{
int32_t ret;
uint8_t pp[MODES_XTS_BLOCKSIZE], t2[MODES_XTS_BLOCKSIZE];
uint32_t i;
uint32_t tmpLen = len;
const uint8_t *tmpIn = in;
uint32_t blockSize = ctx->blockSize;
uint8_t *tmpOut = out;
if (len < blockSize) {
BSL_ERR_PUSH_ERROR(CRYPT_MODE_BUFF_LEN_NOT_ENOUGH);
return CRYPT_MODE_BUFF_LEN_NOT_ENOUGH;
}
ret = BlocksCrypt(ctx, &tmpIn, &tmpOut, &tmpLen, false);
RETURN_RET_IF(ret != CRYPT_SUCCESS, ret);
if (tmpLen == blockSize) {
ret = BlockCrypt(ctx, tmpIn, ctx->tweak, tmpOut, false);
if (ret != CRYPT_SUCCESS) {
BSL_ERR_PUSH_ERROR(ret);
return ret;
}
if (ctx->ciphMeth->algId == CRYPT_SYM_SM4) {
GF128Mul_GM(ctx->tweak, blockSize);
} else {
GF128Mul(ctx->tweak, blockSize);
}
return CRYPT_SUCCESS;
}
memcpy(t2, ctx->tweak, blockSize);
if (ctx->ciphMeth->algId == CRYPT_SYM_SM4) {
GF128Mul_GM(ctx->tweak, blockSize);
} else {
GF128Mul(ctx->tweak, blockSize);
}
ret = BlockCrypt(ctx, tmpIn, ctx->tweak, pp, false);
RETURN_RET_IF(ret != CRYPT_SUCCESS, ret);
tmpLen -= blockSize;
for (i = 0; i < tmpLen; i++) {
tmpOut[i + blockSize] = pp[i];
pp[i] = tmpIn[i + blockSize];
}
ret = BlockCrypt(ctx, pp, t2, pp, false);
if (ret != CRYPT_SUCCESS) {
BSL_ERR_PUSH_ERROR(ret);
return ret;
}
if (blockSize > blockSize + tmpLen) {
BSL_ERR_PUSH_ERROR(CRYPT_MEM_CPY_FAIL);
return CRYPT_MEM_CPY_FAIL;
}
memcpy(tmpOut, pp, blockSize);
return CRYPT_SUCCESS;
}
void MODES_XTS_Clean(MODES_CipherXTSCtx *ctx)
{
if (ctx == NULL) {
return;
}
BSL_SAL_CleanseData((void *)(ctx->iv), MODES_MAX_IV_LENGTH);
BSL_SAL_CleanseData((void *)(ctx->tweak), MODES_MAX_IV_LENGTH);
if (ctx->ciphMeth != NULL && ctx->ciphMeth->cipherDeInitCtx != NULL) {
ctx->ciphMeth->cipherDeInitCtx(ctx->ciphCtx);
ctx->ciphMeth->cipherDeInitCtx((void *)((uintptr_t)ctx->ciphCtx + ctx->ciphMeth->ctxSize));
}
}
int32_t MODES_XTS_SetIv(MODES_CipherXTSCtx *ctx, const uint8_t *val, uint32_t len)
{
int32_t ret;
if (val == NULL) {
BSL_ERR_PUSH_ERROR(CRYPT_NULL_INPUT);
return CRYPT_NULL_INPUT;
}
if (len != ctx->blockSize) {
BSL_ERR_PUSH_ERROR(CRYPT_MODES_IVLEN_ERROR);
return CRYPT_MODES_IVLEN_ERROR;
}
if (len > MODES_MAX_IV_LENGTH) {
BSL_ERR_PUSH_ERROR(CRYPT_MEM_CPY_FAIL);
return CRYPT_MEM_CPY_FAIL;
}
memcpy(ctx->iv, val, len);
ret = ctx->ciphMeth->encryptBlock((uint8_t*)ctx->ciphCtx + ctx->ciphMeth->ctxSize,
ctx->iv, ctx->tweak, ctx->blockSize);
if (ret != CRYPT_SUCCESS) {
BSL_ERR_PUSH_ERROR(ret);
}
return ret;
}
static int32_t GetIv(MODES_CipherXTSCtx *ctx, uint8_t *val, uint32_t len)
{
if (val == NULL) {
BSL_ERR_PUSH_ERROR(CRYPT_NULL_INPUT);
return CRYPT_NULL_INPUT;
}
if (len != ctx->blockSize) {
BSL_ERR_PUSH_ERROR(CRYPT_MODE_ERR_INPUT_LEN);
return CRYPT_MODE_ERR_INPUT_LEN;
}
if (ctx->blockSize > len) {
BSL_ERR_PUSH_ERROR(CRYPT_MEM_CPY_FAIL);
return CRYPT_MEM_CPY_FAIL;
}
memcpy(val, ctx->iv, ctx->blockSize);
return CRYPT_SUCCESS;
}
int32_t MODES_XTS_Ctrl(MODES_XTS_Ctx *modeCtx, int32_t cmd, void *val, uint32_t len)
{
int32_t ret;
if (modeCtx == NULL) {
BSL_ERR_PUSH_ERROR(CRYPT_NULL_INPUT);
return CRYPT_NULL_INPUT;
}
switch (cmd) {
case CRYPT_CTRL_REINIT_STATUS:
ret = MODES_XTS_SetIv(&modeCtx->xtsCtx, val, len);
if (ret == CRYPT_SUCCESS) {
memset(modeCtx->data, 0, EAL_MAX_BLOCK_LENGTH);
modeCtx->dataLen = 0;
modeCtx->totalLen = 0;
}
return ret;
case CRYPT_CTRL_GET_IV:
return GetIv(&modeCtx->xtsCtx, (uint8_t *)val, len);
case CRYPT_CTRL_GET_BLOCKSIZE:
if (val == NULL || len != sizeof(uint32_t)) {
return CRYPT_MODE_ERR_INPUT_LEN;
}
*(int32_t *)val = 1;
return CRYPT_SUCCESS;
default:
BSL_ERR_PUSH_ERROR(CRYPT_MODES_CTRL_TYPE_ERROR);
return CRYPT_MODES_CTRL_TYPE_ERROR;
}
}
MODES_XTS_Ctx *MODES_XTS_NewCtx(int32_t algId)
{
const EAL_SymMethod *method = EAL_GetSymMethod(algId);
if (method == NULL) {
BSL_ERR_PUSH_ERROR(CRYPT_INVALID_ARG);
return NULL;
}
MODES_XTS_Ctx *ctx = BSL_SAL_Calloc(1, sizeof(MODES_XTS_Ctx));
if (ctx == NULL) {
BSL_ERR_PUSH_ERROR(CRYPT_MEM_ALLOC_FAIL);
return ctx;
}
ctx->algId = algId;
ctx->xtsCtx.ciphCtx = BSL_SAL_Calloc(2, method->ctxSize);
if (ctx->xtsCtx.ciphCtx == NULL) {
BSL_ERR_PUSH_ERROR(CRYPT_MEM_ALLOC_FAIL);
BSL_SAL_Free(ctx);
return NULL;
}
ctx->xtsCtx.blockSize = method->blockSize;
ctx->xtsCtx.ciphMeth = method;
return ctx;
}
MODES_XTS_Ctx *MODES_XTS_NewCtxEx(void *libCtx, int32_t algId)
{
(void)libCtx;
return MODES_XTS_NewCtx(algId);
}
int32_t MODES_XTS_InitCtx(MODES_XTS_Ctx *modeCtx, const uint8_t *key, uint32_t keyLen, const uint8_t *iv,
uint32_t ivLen, bool enc)
{
int32_t ret = MODES_XTS_CheckPara(key, keyLen, iv);
if (ret != CRYPT_SUCCESS) {
return ret;
}
if (enc) {
ret = MODES_XTS_SetEncryptKey(&modeCtx->xtsCtx, key, keyLen);
} else {
ret = MODES_XTS_SetDecryptKey(&modeCtx->xtsCtx, key, keyLen);
}
if (ret != CRYPT_SUCCESS) {
BSL_ERR_PUSH_ERROR(ret);
return ret;
}
ret = MODES_XTS_SetIv(&modeCtx->xtsCtx, iv, ivLen);
if (ret != CRYPT_SUCCESS) {
(void)MODES_XTS_DeInitCtx(modeCtx);
return ret;
}
modeCtx->enc = enc;
modeCtx->totalLen = 0;
return ret;
}
int32_t MODES_XTS_Update(MODES_XTS_Ctx *modeCtx, const uint8_t *in, uint32_t inLen, uint8_t *out, uint32_t *outLen)
{
return MODES_CipherStreamProcess(modeCtx->enc ? MODES_XTS_Encrypt : MODES_XTS_Decrypt, &modeCtx->xtsCtx,
in, inLen, out, outLen);
}
int32_t MODES_XTS_Final(MODES_XTS_Ctx *modeCtx, uint8_t *out, uint32_t *outLen)
{
(void) modeCtx;
(void) out;
*outLen = 0;
return CRYPT_SUCCESS;
}
int32_t MODES_XTS_DeInitCtx(MODES_XTS_Ctx *modeCtx)
{
if (modeCtx == NULL) {
BSL_ERR_PUSH_ERROR(CRYPT_NULL_INPUT);
return CRYPT_NULL_INPUT;
}
MODES_XTS_Clean(&modeCtx->xtsCtx);
memset(modeCtx->data, 0, EAL_MAX_BLOCK_LENGTH);
modeCtx->dataLen = 0;
modeCtx->totalLen = 0;
modeCtx->pad = CRYPT_PADDING_NONE;
return CRYPT_SUCCESS;
}
void MODES_XTS_FreeCtx(MODES_XTS_Ctx *modeCtx)
{
if (modeCtx == NULL) {
return;
}
MODES_XTS_Clean(&modeCtx->xtsCtx);
BSL_SAL_FREE(modeCtx->xtsCtx.ciphCtx);
BSL_SAL_FREE(modeCtx);
}
int32_t MODES_XTS_InitCtxEx(MODES_XTS_Ctx *modeCtx, const uint8_t *key, uint32_t keyLen, const uint8_t *iv,
uint32_t ivLen, void *param, bool enc)
{
(void)param;
if (modeCtx == NULL) {
BSL_ERR_PUSH_ERROR(CRYPT_NULL_INPUT);
return CRYPT_NULL_INPUT;
}
switch (modeCtx->algId) {
case CRYPT_CIPHER_SM4_XTS:
#ifdef HITLS_CRYPTO_SM4
return SM4_XTS_InitCtx(modeCtx, key, keyLen, iv, ivLen, enc);
#else
return CRYPT_EAL_ALG_NOT_SUPPORT;
#endif
default:
return MODES_XTS_InitCtx(modeCtx, key, keyLen, iv, ivLen, enc);
}
}
static int32_t XTS_CheckUpdateParam(uint32_t totalLen, uint32_t cacheLen, uint32_t inLen, uint32_t *outLen)
{
uint32_t bufferedLen = inLen + cacheLen;
if (bufferedLen < inLen) {
BSL_ERR_PUSH_ERROR(CRYPT_EAL_BUFF_LEN_TOO_LONG);
return CRYPT_EAL_BUFF_LEN_TOO_LONG;
}
if ((*outLen) < bufferedLen) {
BSL_ERR_PUSH_ERROR(CRYPT_EAL_BUFF_LEN_NOT_ENOUGH);
return CRYPT_EAL_BUFF_LEN_NOT_ENOUGH;
}
if (inLen > MODES_XTS_MAX_DATA_UNIT_BYTES - totalLen) {
BSL_ERR_PUSH_ERROR(CRYPT_MODES_CRYPTLEN_OVERFLOW);
return CRYPT_MODES_CRYPTLEN_OVERFLOW;
}
return CRYPT_SUCCESS;
}
int32_t MODES_XTS_UpdateEx(MODES_XTS_Ctx *modeCtx, const uint8_t *in, uint32_t inLen, uint8_t *out, uint32_t *outLen)
{
if (modeCtx == NULL) {
BSL_ERR_PUSH_ERROR(CRYPT_NULL_INPUT);
return CRYPT_NULL_INPUT;
}
int32_t ret = XTS_CheckUpdateParam(modeCtx->totalLen, modeCtx->dataLen, inLen, outLen);
if (ret != CRYPT_SUCCESS) {
BSL_ERR_PUSH_ERROR(ret);
return ret;
}
switch (modeCtx->algId) {
case CRYPT_CIPHER_AES128_XTS:
case CRYPT_CIPHER_AES256_XTS:
#ifdef HITLS_CRYPTO_AES
ret = AES_XTS_Update(modeCtx, in, inLen, out, outLen);
break;
#else
return CRYPT_EAL_ALG_NOT_SUPPORT;
#endif
case CRYPT_CIPHER_SM4_XTS:
#ifdef HITLS_CRYPTO_SM4
ret = SM4_XTS_Update(modeCtx, in, inLen, out, outLen);
break;
#else
return CRYPT_EAL_ALG_NOT_SUPPORT;
#endif
default:
ret = MODES_XTS_Update(modeCtx, in, inLen, out, outLen);
break;
}
if (ret == CRYPT_SUCCESS) {
modeCtx->totalLen += inLen;
}
return ret;
}
MODES_XTS_Ctx *MODES_XTS_DupCtx(const MODES_XTS_Ctx *modeCtx)
{
if (modeCtx == NULL) {
return NULL;
}
MODES_XTS_Ctx *ctx = BSL_SAL_Dump(modeCtx, sizeof(MODES_XTS_Ctx));
if (ctx == NULL) {
BSL_ERR_PUSH_ERROR(CRYPT_MEM_ALLOC_FAIL);
return ctx;
}
void *ciphCtx = BSL_SAL_Dump(modeCtx->xtsCtx.ciphCtx, modeCtx->xtsCtx.ciphMeth->ctxSize * 2);
if (ciphCtx == NULL) {
BSL_ERR_PUSH_ERROR(CRYPT_MEM_ALLOC_FAIL);
BSL_SAL_CleanseData(ctx->xtsCtx.iv, MODES_MAX_IV_LENGTH);
BSL_SAL_CleanseData(ctx->xtsCtx.tweak, MODES_MAX_IV_LENGTH);
memset(ctx->data, 0, EAL_MAX_BLOCK_LENGTH);
BSL_SAL_Free(ctx);
return NULL;
}
ctx->xtsCtx.ciphCtx = ciphCtx;
return ctx;
}
#endif