/*
 * Copyright(C) 2009 Проект ИОК
 *
 * Этот файл содержит информацию, являющуюся
 * собственностью компании Крипто-Про.
 *
 * Любая часть этого файла не может быть скопирована,
 * исправлена, переведена на другие языки,
 * локализована или модифицирована любым способом,
 * откомпилирована, передана по сети с или на
 * любую компьютерную систему без предварительного
 * заключения соглашения с компанией Крипто-Про.
 */

/*!
 * \file $RCSfile$
 * \version $Revision: 205607 $
 * \date $Date:: 2020-01-27 09:28:34 +0300#$
 * \author $Author: dim $
 *
 * \brief Входной и выходной контроль CSP_iovec и CP_CRYPT_HASH_PACKET
 */

#ifndef CSP_iovec_check_h_
#define CSP_iovec_check_h_

#include "CSP_iovec_iterator.h"
#include "CSP_RPC_limits.h"
#include "compiler_attributes.h"

#ifdef __cplusplus
extern "C" {
#endif

typedef enum CPIOVop_ {
    CPIOVopEncrypt,
    CPIOVopDecrypt,
    CPIOVopHashData
} CPIOVop_t;

typedef HRESULT (*CPIOVClnGeneral_t)(
		    void *pvCSPpkh, 
		    BOOL Final, DWORD dwFlags,
		    CSP_iovec *pIOv, DWORD *pdwDataLen, DWORD dwIOcnt);

    // Общее преобразование в IOvec, разрезание на блоки оптимальной
    // длины и проверка ограничений.
    // Количественные ограничения:
    //   CPEncDecMinBlkLen    - максимальное увеличение/уменьшение длины
    //			        в операциях Encrypt()/Decrypt();
    //   dwMaxBlk             - максимальный размер для 
    //				CP_CRYPT_HASH_PACKET;
    //	 CPSrvTransportMaxIOVcnt - максимальное кол-во CSP_iovec;
    // Семантические ограничения:
    //   Если !Final, то размер не меняется.
    //   Если Final, то при шифровании размер не может увеличится 
    //     больше, чем на CPEncDecMinBlkLen.
    //   Если Final, то при расшифровании размер не может уменьшится
    //     больше, чем на CPEncDecMinBlkLen.
    //   pIOv[i].CSPiov_ptr != NULL
    //   pIOv[i].CSPiov_len < CSPIOV_MAX_LEN
    // Возвращает ошибки:
    //   ERROR_MORE_DATA
    //	    - размер буфера меньше размера данных
    //   NTE_NO_MEMORY		
    //	    - нарушение максимальных размеров
    //   RPC_E_CLIENT_CANTMARSHAL_DATA	
    //	    - нарушение семантики протокола в аргументах
    //   RPC_E_CLIENT_CANTUNMARSHAL_DATA 
    //	    - нарушение семантики протокола со стороны сервера

static SUP_INLINE ATTR_USERES HRESULT
CPIOVClnGeneral(CPIOVop_t op, CPIOVClnGeneral_t pfnDoGeneral, 
		DWORD dwOptBlk, DWORD dwMaxBlk, HRESULT hrMaxErr,
		void *pvCSPpkh, 
		BOOL Final, DWORD dwFlags, 
		BYTE *pbData, DWORD *pdwDataLen, DWORD dwBufLen)
{
    HRESULT hres = RPC_E_CLIENT_CANTMARSHAL_DATA;
    CSP_iovec *pIOv = 0;
    CSP_iovec IOv = { 0, 0 };
    DWORD dwIOcnt = 0;

    DWORD ttlOff = 0;	// Начало текущего сегмента в байтах

    DWORD len = 0;	// Длина текущего сегмента
    DWORD ptr = 0;	// Последний CSP_iovec

    CSPiov_len_type iovDelta;
    DWORD lenDelta;

    CSPiov_len_type slen = 0;

    BOOL fFinal = TRUE;

    CSP_iovec_iterator lp;
    CSP_iovec_iterator cp;
    CSP_iovec_iterator np;



     if(pbData) {
	if(dwFlags&CP_CRYPT_DATA_IOVEC) {
	    pIOv = (CSP_iovec *)pbData;
	    dwIOcnt = dwBufLen;
	    if(CPSrvTransportMaxIOVcnt <= dwIOcnt) {
		return NTE_NO_MEMORY;
	    }
	    dwBufLen = 0;
	    for(ptr = 0; ptr < dwIOcnt; ptr++) {
		if(!Ciov_check(pIOv+ptr)) {
		    return RPC_E_CLIENT_CANTMARSHAL_DATA;
		}
		if((dwFlags&CP_CRYPT_HASH_PACKET) && 
		   pIOv[ptr].CSPiov_len >= dwMaxBlk) {
		    return hrMaxErr;
		}
		dwBufLen += IOVEC_LEN(pIOv,ptr);
	    }
	} else {
	    IOv.CSPiov_ptr = Ciov_ptr(pbData);
	    IOv.CSPiov_len = Ciov_dw2len(dwBufLen);
	    pIOv = &IOv;
	    dwIOcnt = 1;
	} 
	if(dwBufLen < *pdwDataLen) {
	    return ERROR_MORE_DATA;
	}
    } else {
	pIOv = NULL;
	dwIOcnt = 0;
	dwBufLen = 0;
    }
    if(!pIOv || (dwFlags&CP_CRYPT_HASH_PACKET)) {
	if(dwFlags&CP_CRYPT_HASH_PACKET) {
	    if(CP_CHP_PUREHEADER_SIZE(dwFlags) +
	       CP_CHP_HASH_SIZE(dwFlags) + 
	       CP_CHP_TRAILER_SIZE(dwFlags) > dwBufLen) {
		return RPC_E_CLIENT_CANTMARSHAL_DATA;
	    }
	    if(*pdwDataLen > dwBufLen) {
		return RPC_E_CLIENT_CANTMARSHAL_DATA;
	    }
	}
	if(dwBufLen >= dwMaxBlk) {
	    return hrMaxErr;
	}
	if(pIOv) {
	    slen = pIOv[dwIOcnt-1].CSPiov_len;
	    dwBufLen = *pdwDataLen;
	}
	hres = (*pfnDoGeneral)(pvCSPpkh, 
				Final, dwFlags, 
				pIOv, pdwDataLen, dwIOcnt);
	if(pIOv && (slen < pIOv[dwIOcnt-1].CSPiov_len)) {
	    return RPC_E_CLIENT_CANTUNMARSHAL_DATA;
	}
	return hres;
    }

    Ciov_setEOB(&lp, pIOv+dwIOcnt-1);
    ttlOff = 0;
    Ciov_set(&cp, pIOv); 
    do {
	np = cp;
        len = Ciov_advance(&np, dwOptBlk, &lp);
	if(ttlOff+len < *pdwDataLen) {
	    fFinal = FALSE;
	} else {
	    fFinal = Final;
	    len -= Ciov_backward(&np, ttlOff+len-*pdwDataLen, &cp);
	    if(ttlOff+len != *pdwDataLen) {
		return RPC_E_CLIENT_CANTUNMARSHAL_DATA;
	    }
	    if(CPIOVopEncrypt == op) {
		if(Ciov_advance(&np, CPEncDecMinBlkLen, &lp) == CSP_UIOV_MAXBAD_LEN) {
		    return RPC_E_CLIENT_CANTMARSHAL_DATA;
		}
	    }
	}
	Ciov_prepare(&cp, &np) {
	    DWORD dwSegIOcnt = Ciov_diff_cnt(&np, &cp);
	    iovDelta = np.ciov->CSPiov_len;
	    lenDelta = len;
	    hres = (*pfnDoGeneral)(pvCSPpkh, 
			    fFinal, dwFlags, 
			    cp.ciov, &len, 
			    (0 == dwSegIOcnt ? 1 : dwSegIOcnt));
	    iovDelta -= np.ciov->CSPiov_len;
	    lenDelta -= len;
	} Ciov_revert();
	if(fFinal && (S_OK == hres || ERROR_MORE_DATA == hres)) {
	    switch(op) {
	      case CPIOVopEncrypt:
		*pdwDataLen = ttlOff + len;
		break;
	      case CPIOVopDecrypt:
	        if(iovDelta > CPEncDecMinBlkLen || 
		   lenDelta > CPEncDecMinBlkLen) {
		    return RPC_E_CLIENT_CANTUNMARSHAL_DATA;
		}
		if(!(dwFlags&CP_CRYPT_DATA_IOVEC)) {
		    *pdwDataLen -= iovDelta;   
		}
		break;
	      case CPIOVopHashData:
		break;
	    }
	    ttlOff += len;
	    break;
	}
	if(hres != S_OK) {
	    break;
	} else if(iovDelta || lenDelta) {
	    return RPC_E_CLIENT_CANTUNMARSHAL_DATA;
	}
	ttlOff += len;
	cp = np;
    } while(Ciov_cmp(&cp, &lp) < 0 && ttlOff < *pdwDataLen);
    if(S_OK == hres && ttlOff < *pdwDataLen) {
	return ERROR_MORE_DATA;
    }
    return hres;
}

    // Ограничение расшифрования
static SUP_INLINE ATTR_USERES HRESULT
CPIOVClnEncrypt(CPIOVClnGeneral_t pfnDoEncrypt,
	       DWORD dwOptBlk, DWORD dwMaxBlk, HRESULT hrMaxErr,
	       void *pvCSPpkh, 
	       BOOL Final, DWORD dwFlags, 
	       BYTE *pbData, DWORD *pdwDataLen, DWORD dwBufLen)
{
    // В сервисе маршалинг мультипакетов пока не работает, поэтому возвращаем ошибку.
    // TODO: CPCSP-4779
    if (dwFlags&CP_CHP_MULTIPACKET) {
	return RPC_E_CLIENT_CANTMARSHAL_DATA;
    }

    return CPIOVClnGeneral(CPIOVopEncrypt, pfnDoEncrypt,
			    dwOptBlk, dwMaxBlk, hrMaxErr,
			    pvCSPpkh, 
			    Final, dwFlags,
			    pbData, pdwDataLen, dwBufLen);
}

    // Ограничение расшифрования
static SUP_INLINE ATTR_USERES HRESULT
CPIOVClnDecrypt(CPIOVClnGeneral_t pfnDoDecrypt,
	       DWORD dwOptBlk, DWORD dwMaxBlk, HRESULT hrMaxErr,
	       void *pvCSPpkh, 
	       BOOL Final, DWORD dwFlags, 
	       BYTE *pbData, DWORD *pdwDataLenIOcnt)
{
    DWORD dwBufLenIOcnt = 0;
    DWORD dwDataLen = 0;

    // В сервисе маршалинг мультипакетов пока не работает, поэтому возвращаем ошибку.
    // TODO: CPCSP-4779
    // CPCSP-10622: Флаг совпадает с CRYPT_DECRYPT_RSA_NO_PADDING_CHECK, так что
    // если буфер передали больше длины 384 / 8 байт, то считаем, что это RSA. Для мультипакетов pdwDataLenIOcnt будет небольшим. 
    if ((dwFlags&CP_CHP_MULTIPACKET) &&
	(*pdwDataLenIOcnt < 48)) {
	return RPC_E_CLIENT_CANTMARSHAL_DATA;
    }

    if(!pbData && (0 < *pdwDataLenIOcnt ||
		    (dwFlags&CP_CRYPT_DATA_IOVEC))) {
	    /* Т.к. маршалинг превращает (pIOv=NULL, dwBufLen=N) в 
	     * (pIOvec=NULL, dwIOVcnt=0)
	     */
	return NTE_BAD_DATA;
    } else if(!(dwFlags&CP_CRYPT_DATA_IOVEC)) {
	dwBufLenIOcnt = *pdwDataLenIOcnt;
    } else {
	DWORD i;
	dwBufLenIOcnt = *pdwDataLenIOcnt;
	dwDataLen = 0;
	for(i = 0; i < dwBufLenIOcnt; i++) {
	    dwDataLen += IOVEC_LEN(pbData, i);
	}
	pdwDataLenIOcnt = &dwDataLen;
    }
    return CPIOVClnGeneral(CPIOVopDecrypt, pfnDoDecrypt, 
			    dwOptBlk, dwMaxBlk, hrMaxErr,
			    pvCSPpkh, 
			    Final, dwFlags,
			    pbData, pdwDataLenIOcnt, dwBufLenIOcnt);
}

    // Ограничение хэш функции
static SUP_INLINE ATTR_USERES HRESULT
CPIOVClnHashData(CPIOVClnGeneral_t pfnDoHashData,
	       DWORD  dwOptBlk, DWORD dwMaxBlk, HRESULT hrMaxErr,
	       void *pvCSPpkh, 
	       const BYTE *pbData, DWORD dwDataLenIOcnt,
	       DWORD dwFlags)
{
    DWORD dwDataLen = 0;

    // В сервисе маршалинг мультипакетов пока не работает, поэтому возвращаем ошибку.
    // TODO: CPCSP-4779
    if (dwFlags&CP_CHP_MULTIPACKET) {
	return RPC_E_CLIENT_CANTMARSHAL_DATA;
    }

    if(!(dwFlags&CP_CRYPT_DATA_IOVEC)) {
	dwDataLen = dwDataLenIOcnt;
    } else {
	DWORD i;
	dwDataLen = 0;
	for(i = 0; i < dwDataLenIOcnt; i++) {
	    dwDataLen += IOVEC_LEN(pbData, i);
	}
    }
    return CPIOVClnGeneral(CPIOVopHashData, pfnDoHashData, 
			    dwOptBlk, dwMaxBlk, hrMaxErr,
			    pvCSPpkh, 
			    FALSE, dwFlags,
			    (BYTE *)pbData, &dwDataLen, dwDataLenIOcnt);
}

    // Ограничение ДСЧ (TODO: подумать над пакетной обработкой)
static SUP_INLINE ATTR_USERES HRESULT
CPIOVClnGenRandom(HRESULT (*pfnDoGenRandom)(
			void *pvCSPpkh,  
	                DWORD dwBufLen, BYTE *pbBuffer),
	       DWORD  dwOptBlk, DWORD  dwMaxBlk, HRESULT hrMaxErr,
	       void *pvCSPpkh, DWORD dwBufLen, BYTE *pbBuffer)
{
    HRESULT hres;
    DWORD dwLen;
    DWORD dwOff;
    #ifdef MT_GENRANDOM_PACKET
	if(dwBufLen&MT_GENRANDOM_PACKET) {
	    dwBufLen &= ~MT_GENRANDOM_PACKET;
	    if(dwMaxBlk < dwBufLen) {
		return hrMaxErr;
	    }
	    return (*pfnDoGenRandom)(pvCSPpkh, 
				     dwBufLen, pbBuffer);
	}
    #else
	UNUSED(dwMaxBlk);
	UNUSED(hrMaxErr);
    #endif /* MT_GENRANDOM_PACKET */
    dwOff = 0;
    do {
	dwLen = dwBufLen - dwOff;
	if(dwLen > dwOptBlk) {
	    dwLen = dwOptBlk;
	}
	hres = (*pfnDoGenRandom)(pvCSPpkh, dwLen, pbBuffer);
	if(hres != S_OK) {
	    break;
	}
	dwOff += dwLen;
	pbBuffer += dwLen;
    } while(dwOff < dwBufLen);
    return hres;
}

typedef HRESULT (*CPIOVSrvGeneral_t)(
		    void *pvCSPpkh, 
		    BOOL Final, DWORD dwFlags,
		    BYTE *pbData, DWORD *pdwDataLen, DWORD dwBufLen) ATTR_USERES;

    // Общее преобразование в/из IOvec и проверка ограничений.
    // Количественные ограничения:
    //   CPEncDecMinBlkLen    - максимальное увеличение/уменьшение длины
    //			        в операциях Encrypt()/Decrypt();
    //   dwMaxBlk             - максимальный размер для 
    //				CP_CRYPT_HASH_PACKET;
    //	 CPSrvTransportMaxIOVcnt - максимальное кол-во CSP_iovec;
    // Семантические ограничения:
    //   Если !Final, то размер не меняется.
    //   Если Final, то при шифровании размер не может увеличится 
    //     больше, чем на CPEncDecMinBlkLen.
    //   Если Final, то при расшифровании размер не может уменьшится
    //     больше, чем на CPEncDecMinBlkLen.
    //   pIOv[i].CSPiov_ptr != NULL
    //   pIOv[i].CSPiov_len < CSPIOV_MAX_LEN
    // Возвращает ошибки:
    //   ERROR_MORE_DATA
    //	    - размер буфера меньше размера данных
    //   RPC_E_OUT_OF_RESOURCES		
    //	    - нарушение максимальных размеров
    //   RPC_E_SERVER_CANTMARSHAL_DATA	
    //	    - нарушение семантики протокола в реализации
    //   RPC_E_SERVER_CANTUNMARSHAL_DATA 
    //	    - нарушение семантики протокола со стороны клиента

static SUP_INLINE ATTR_USERES HRESULT
CPIOVSrvGeneral(CPIOVop_t op, CPIOVSrvGeneral_t pfnDoGeneral,
		DWORD dwMaxBlk, 
		void *pvCSPpkh, BOOL Final, DWORD dwFlags,
		CSP_iovec *pIOv, DWORD *pdwDataLen, DWORD dwIOcnt)
{
    DWORD dwBufLen;
    DWORD ptr;

    if(pIOv) {
	if(CPSrvTransportMaxIOVcnt <= dwIOcnt) {
	    return RPC_E_OUT_OF_RESOURCES;
	}
	dwBufLen = 0;
	for(ptr = 0; ptr < dwIOcnt; ptr++) {
	    if(!Ciov_check(pIOv+ptr)) {
		return RPC_E_SERVER_CANTUNMARSHAL_DATA;
	    }
	    if((dwFlags&CP_CRYPT_HASH_PACKET) && 
	       pIOv[ptr].CSPiov_len >= dwMaxBlk) {
    		return RPC_E_OUT_OF_RESOURCES;
	    }
	    dwBufLen += IOVEC_LEN(pIOv,ptr);
	}
	if(dwFlags&CP_CRYPT_HASH_PACKET) {
	    if(CP_CHP_PUREHEADER_SIZE(dwFlags) +
	       CP_CHP_HASH_SIZE(dwFlags) + 
	       CP_CHP_TRAILER_SIZE(dwFlags) > dwBufLen) {
		return RPC_E_SERVER_CANTUNMARSHAL_DATA;
	    }
	    if(*pdwDataLen > dwBufLen) {
		return RPC_E_SERVER_CANTUNMARSHAL_DATA;
	    }
	}
    }
    if(NULL == pIOv || (dwFlags&CP_CRYPT_DATA_IOVEC)) {
	if(CPIOVopDecrypt == op && (dwFlags&CP_CRYPT_DATA_IOVEC)) {
	    pdwDataLen = &dwIOcnt;
	}
	return (*pfnDoGeneral)(pvCSPpkh, 
			Final, dwFlags,
			(BYTE *)pIOv, pdwDataLen, dwIOcnt);
    }
    if(1 != dwIOcnt) {
	return RPC_E_SERVER_CANTUNMARSHAL_DATA;
    }
    return (*pfnDoGeneral)(pvCSPpkh, 
		    Final, dwFlags,
		    (BYTE *)pIOv[0].CSPiov_ptr, pdwDataLen, pIOv[0].CSPiov_len);
}

static SUP_INLINE ATTR_USERES HRESULT
CPIOVSrvEncrypt(CPIOVSrvGeneral_t pfnDoEncrypt,
		DWORD dwMaxBlk,
		void *pvCSPpkh, BOOL Final, DWORD dwFlags,
		CSP_iovec *pIOv, DWORD *pdwDataLen, DWORD dwIOcnt)
{
    HRESULT hres;
    CSPiov_len_type iovDelta = 0;
    DWORD lenDelta = 0;

    if(pIOv) {
	iovDelta = pIOv[dwIOcnt-1].CSPiov_len;
	lenDelta = *pdwDataLen;
    }
    hres = CPIOVSrvGeneral(CPIOVopEncrypt, pfnDoEncrypt,
			   dwMaxBlk,
			   pvCSPpkh, Final, dwFlags,
			   pIOv, pdwDataLen, dwIOcnt);
    if(S_OK == hres && pIOv) {
	if(!Final) {
	    if(*pdwDataLen != lenDelta) {
		return RPC_E_SERVER_CANTMARSHAL_DATA;
	    }
	} else {
	    if(*pdwDataLen < lenDelta) {
		//тут падаем на Linux
		return RPC_E_SERVER_CANTMARSHAL_DATA;
	    }
	}
	if(pIOv[dwIOcnt-1].CSPiov_len > iovDelta) {
	    return RPC_E_SERVER_CANTMARSHAL_DATA;
	}
    }
    return hres;
}

static SUP_INLINE ATTR_USERES HRESULT
CPIOVSrvDecrypt(CPIOVSrvGeneral_t pfnDoDecrypt,
		DWORD dwMaxBlk,
		void *pvCSPpkh, BOOL Final, DWORD dwFlags,
		CSP_iovec *pIOv, DWORD dwIOcnt)
{
    HRESULT hres;
    CSPiov_len_type iovDelta = 0;
    DWORD lenDelta  = 0;
    DWORD dwDataLen = 0;
    DWORD i;

    if(pIOv) {
	dwDataLen = 0;
	for(i = 0; i < dwIOcnt; i++) {
	    dwDataLen += IOVEC_LEN(pIOv, i);
	}
	iovDelta = pIOv[dwIOcnt-1].CSPiov_len;
	lenDelta = dwDataLen;
    }
    hres = CPIOVSrvGeneral(CPIOVopDecrypt, pfnDoDecrypt,
			    dwMaxBlk,
			    pvCSPpkh, Final, dwFlags,
			    pIOv, &dwDataLen, dwIOcnt);
    if(S_OK == hres && pIOv) {
	if(!Final) {
	    if(dwDataLen != lenDelta ||
	       pIOv[dwIOcnt-1].CSPiov_len != iovDelta) {
		return RPC_E_SERVER_CANTMARSHAL_DATA;
	    }
	} else {
	    if(dwDataLen > lenDelta ||
	       pIOv[dwIOcnt-1].CSPiov_len > iovDelta) {
		return RPC_E_SERVER_CANTMARSHAL_DATA;
	    }
	    if(pIOv[dwIOcnt-1].CSPiov_len < lenDelta - dwDataLen) {
		return RPC_E_SERVER_CANTMARSHAL_DATA;
	    }
	    pIOv[dwIOcnt-1].CSPiov_len -= lenDelta - dwDataLen;
	}
    }
    return hres;
}

static SUP_INLINE ATTR_USERES HRESULT
CPIOVSrvHashData(CPIOVSrvGeneral_t pfnDoHashData,
	     DWORD dwMaxBlk,
	     void *pvCSPpkh, 
	     const CSP_iovec *pIOv, DWORD dwIOcnt, DWORD dwFlags)
{
    HRESULT hres;
    CSPiov_len_type iovDelta = 0;
    DWORD lenDelta = 0;
    DWORD dwDataLen = 0;
    DWORD i;

    if(pIOv) {
	for(i = 0; i < dwIOcnt; i++) {
	    dwDataLen += IOVEC_LEN(pIOv, i);
	}
	iovDelta = pIOv[dwIOcnt-1].CSPiov_len;
	lenDelta = dwDataLen;
    }
    hres = CPIOVSrvGeneral(CPIOVopHashData, pfnDoHashData,
			    dwMaxBlk,
			    pvCSPpkh, FALSE, dwFlags,
			    (CSP_iovec *)pIOv, &dwDataLen, dwIOcnt);
    if(pIOv) {
	if(dwDataLen != lenDelta ||
	   pIOv[dwIOcnt-1].CSPiov_len != iovDelta) {
		return RPC_E_SERVER_CANTMARSHAL_DATA;
	}
    }
    return hres;
}

#ifdef CALLCTX_H_INCLUDED
static SUP_INLINE ATTR_USERES BOOL
CPIOVCPCEncrypt(BOOL (*pfnDoEncrypt)(
			    pCP_CALL_CTX	pCallCtx,
			    LPCRYPT_CONTAINER   pContainer,
			    LPCRYPT_COMMON_KEY  pckKey,
			    LPCRYPT_COMMON_HASH pchHash,
			    BOOL		Final,
			    uint32_t		dwFlags,
			    BYTE		*pbData,
			    uint32_t		*pdwDataLen,
			    uint32_t		dwBufLen),
		pCP_CALL_CTX	    pCallCtx,
		LPCRYPT_CONTAINER   pContainer,
		LPCRYPT_COMMON_KEY  pckKey,
		LPCRYPT_COMMON_HASH pchHash,
		BOOL		    Final,
		uint32_t	    dwFlags,
		BYTE		    *pbData,
		uint32_t	    *pdwDataLen,
		uint32_t	    dwBufLen)
{
    BOOL ret = FALSE;
    CSP_iovec *pIO = Ciov(pbData, dwFlags);
    uint32_t dwDataLen;
    DWORD dwTotlLen;
    DWORD i;

    if(!pbData || (dwFlags&CP_CRYPT_HASH_PACKET) || 
       !(dwFlags&CP_CRYPT_DATA_IOVEC)) {
	return (*pfnDoEncrypt)(pCallCtx, pContainer,
				pckKey, pchHash, Final, dwFlags,
				pbData, pdwDataLen, dwBufLen);
    }
    dwFlags &= ~CP_CRYPT_DATA_IOVEC;
    if(dwBufLen > CSP_UIO_MAXIOV) {
	rSetLastError(pCallCtx, (DWORD)NTE_BAD_LEN);
	return FALSE;
    }
    for(i = 0, dwTotlLen = 0; 
	    i < dwBufLen-1 && 
	    *pdwDataLen > dwTotlLen + (dwDataLen = pIO[i].CSPiov_len);
			i++, dwTotlLen += dwDataLen) {
	if(!Ciov_check(&pIO[i])) {
	    rSetLastError(pCallCtx, (DWORD)NTE_BAD_LEN);
	    return FALSE;
	}
	if(!(*pfnDoEncrypt)(pCallCtx, pContainer,
		    pckKey, pchHash, FALSE, dwFlags,
		    (PBYTE)pIO[i].CSPiov_ptr, &dwDataLen, dwDataLen)) {
	    return FALSE;
	}
    }
    if(!Ciov_check(&pIO[i])) {
	rSetLastError(pCallCtx, (DWORD)NTE_BAD_LEN);
	return FALSE;
    }
    dwDataLen = *pdwDataLen - dwTotlLen;
    ret = (*pfnDoEncrypt)(pCallCtx, pContainer,
		pckKey, pchHash, Final, dwFlags,
		(PBYTE)pIO[i].CSPiov_ptr, &dwDataLen, pIO[i].CSPiov_len);
    *pdwDataLen = dwTotlLen + dwDataLen;
    return ret;
}

static SUP_INLINE ATTR_USERES BOOL
CPIOVCPCDecrypt(BOOL (*pfnDoDecrypt)(
			    pCP_CALL_CTX	pCallCtx,
			    LPCRYPT_CONTAINER   pContainer,
			    LPCRYPT_COMMON_KEY  pckKey,
			    LPCRYPT_COMMON_HASH pchHash,
			    BOOL		Final,
			    uint32_t		dwFlags,
			    BYTE		*pbData,
			    uint32_t		*pdwDataLen),
		pCP_CALL_CTX	    pCallCtx,
		LPCRYPT_CONTAINER   pContainer,
		LPCRYPT_COMMON_KEY  pckKey,
		LPCRYPT_COMMON_HASH pchHash,
		BOOL		    Final,
		uint32_t	    dwFlags,
		BYTE		    *pbData,
		uint32_t    *pdwDataLen)
{
    CSP_iovec *pIO = Ciov(pbData, dwFlags);
    DWORD i;

    if(!pbData || (dwFlags&CP_CRYPT_HASH_PACKET) || 
       !(dwFlags&CP_CRYPT_DATA_IOVEC)) {
	return (*pfnDoDecrypt)(pCallCtx, pContainer,
				pckKey, pchHash, Final, dwFlags,
				pbData, pdwDataLen);
    }
    dwFlags &= ~CP_CRYPT_DATA_IOVEC;
    if((*pdwDataLen) > CSP_UIO_MAXIOV) {
	rSetLastError(pCallCtx, (DWORD)NTE_BAD_LEN);
	return FALSE;
    }
    for(i = 0; i < (*pdwDataLen); i++) {
	if(!Ciov_check(&pIO[i])) {
	    rSetLastError(pCallCtx, (DWORD)NTE_BAD_LEN);
	    return FALSE;
	}
	if(!(*pfnDoDecrypt)(pCallCtx, pContainer,
		pckKey, pchHash, 
		(i >= (*pdwDataLen)-1 ? Final : FALSE), dwFlags,
		(PBYTE)pIO[i].CSPiov_ptr, (uint32_t *)&pIO[i].CSPiov_len)) {
	    return FALSE;
	}
    }
    return TRUE;
}

static SUP_INLINE ATTR_USERES BOOL
CPIOVCPCHashData(BOOL (*pfnHashData)(
			    pCP_CALL_CTX	pCallCtx,
			    /*LPCRYPT_CONTAINER   pContainer,*/
			    LPCRYPT_COMMON_HASH pchHash,
			    CONST BYTE		*pbData,
			    DWORD		dwDataLen
			    /*DWORD		    dwFlags*/),
		pCP_CALL_CTX	    pCallCtx,
		/*LPCRYPT_CONTAINER   pContainer,*/
		LPCRYPT_COMMON_HASH pchHash,
		CONST BYTE	    *pbData,
		DWORD		    dwDataLen,
		DWORD		    dwFlags)
{
    const CSP_iovec *pIO = CCiov(pbData, dwFlags);
    DWORD i;

    if(!pbData || (dwFlags&CP_CRYPT_HASH_PACKET) || 
       !(dwFlags&CP_CRYPT_DATA_IOVEC)) {
	return (*pfnHashData)(pCallCtx, /*pContainer,*/
				pchHash, pbData, dwDataLen);
    }

    if(dwDataLen > CSP_UIO_MAXIOV) {
	rSetLastError(pCallCtx, (DWORD)NTE_BAD_LEN);
	return FALSE;
    }
    for(i = 0; i < dwDataLen; i++) {
	if(!Ciov_check(&pIO[i])) {
	    rSetLastError(pCallCtx, (DWORD)NTE_BAD_LEN);
	    return FALSE;
	}
	if(!(*pfnHashData)(pCallCtx, /*pContainer,*/
		pchHash, (PBYTE)pIO[i].CSPiov_ptr, pIO[i].CSPiov_len)) {
	    return FALSE;
	}
    }
    return TRUE;
}
#endif

#ifdef __cplusplus
}
#endif
#endif /* CSP_iovec_check_h_ */
/* $Id: CSP_iovec_check.h 205607 2020-01-27 06:28:34Z dim $ */
