/
githubmirror
/
xmlsec
Обзор
Документация
Войти
/
githubmirror
/
xmlsec
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
src/mscng/kdf.c
999 строк
33 KB
lsh123
(xmlsec-mscng) Refactored defines for MinGW (#1215)
17 июл 2026, 01:06
Не верифицирован
17 июл 2026, 01:06
cfd6705
Код
Авторство
О чём код?
/** * XML Security Library (http://www.aleksey.com/xmlsec). * * This is free software; see the Copyright file in the source distribution for precise wording. * * Copyright (C) 2018-2026 Aleksey Sanin <aleksey@aleksey.com>. All Rights Reserved. */ /** * @addtogroup xmlsec_mscng_crypto * @brief KDF (key derivation) transforms implementation for MSCng. */ #if !defined(XMLSEC_NO_PBKDF2) || !defined(XMLSEC_NO_HKDF) #include "globals.h" #include <stdlib.h> #include <stdio.h> #include <string.h> #include <ctype.h> #include <xmlsec/xmlsec.h> #include <xmlsec/base64.h> #include <xmlsec/keys.h> #include <xmlsec/errors.h> #include <xmlsec/xmltree.h> #include <xmlsec/private.h> #include <xmlsec/mscng/crypto.h> #include "../cast_helpers.h" #include "../keysdata_helpers.h" #include "../transform_helpers.h" /****************************************************************************** * * Unified KDF transform context (used for both PBKDF2 and HKDF) * *****************************************************************************/ #define XMLSEC_MSCNG_KDF_DEFAULT_BUF_SIZE 64 typedef struct _xmlSecMSCngKdfCtx xmlSecMSCngKdfCtx, *xmlSecMSCngKdfCtxPtr; struct _xmlSecMSCngKdfCtx { xmlSecKeyDataId keyId; LPCWSTR pszAlgId; xmlSecBuffer key; #ifndef XMLSEC_NO_PBKDF2 xmlSecTransformPbkdf2Params pbkdf2Params; #endif /* XMLSEC_NO_PBKDF2 */ #ifndef XMLSEC_NO_HKDF xmlSecTransformHkdfParams hkdfParams; #endif /* XMLSEC_NO_HKDF */ }; XMLSEC_TRANSFORM_DECLARE(MSCngKdf, xmlSecMSCngKdfCtx) #define xmlSecMSCngKdfCtxSize XMLSEC_TRANSFORM_SIZE(MSCngKdf) static int xmlSecMSCngKdfCheckId (xmlSecTransformPtr transform); static int xmlSecMSCngKdfInitialize (xmlSecTransformPtr transform); static void xmlSecMSCngKdfFinalize (xmlSecTransformPtr transform); static int xmlSecMSCngKdfSetKeyReq (xmlSecTransformPtr transform, xmlSecKeyReqPtr keyReq); static int xmlSecMSCngKdfSetKey (xmlSecTransformPtr transform, xmlSecKeyPtr key); static int xmlSecMSCngKdfExecute (xmlSecTransformPtr transform, int last, xmlSecTransformCtxPtr transformCtx); #ifndef XMLSEC_NO_PBKDF2 static int xmlSecMSCngPbkdf2NodeRead (xmlSecTransformPtr transform, xmlNodePtr node, xmlSecTransformCtxPtr transformCtx); static int xmlSecMSCngPbkdf2Derive (xmlSecMSCngKdfCtxPtr ctx, xmlSecBufferPtr out); #endif /* XMLSEC_NO_PBKDF2 */ #ifndef XMLSEC_NO_HKDF static int xmlSecMSCngHkdfNodeRead (xmlSecTransformPtr transform, xmlNodePtr node, xmlSecTransformCtxPtr transformCtx); static int xmlSecMSCngHkdfDerive (xmlSecMSCngKdfCtxPtr ctx, xmlSecBufferPtr out, xmlSecSize outSize); #endif /* XMLSEC_NO_HKDF */ /****************************************************************************** * * Shared KDF functions * *****************************************************************************/ static int xmlSecMSCngKdfCheckId(xmlSecTransformPtr transform) { #ifndef XMLSEC_NO_PBKDF2 if(xmlSecTransformCheckId(transform, xmlSecMSCngTransformPbkdf2Id)) { return(1); } #endif /* XMLSEC_NO_PBKDF2 */ #ifndef XMLSEC_NO_HKDF if(xmlSecTransformCheckId(transform, xmlSecMSCngTransformHkdfId)) { return(1); } #endif /* XMLSEC_NO_HKDF */ /* not found */ return(0); } static int xmlSecMSCngKdfInitialize(xmlSecTransformPtr transform) { xmlSecMSCngKdfCtxPtr ctx; int ret; xmlSecAssert2(xmlSecMSCngKdfCheckId(transform), -1); xmlSecAssert2(xmlSecTransformCheckSize(transform, xmlSecMSCngKdfCtxSize), -1); ctx = xmlSecMSCngKdfGetCtx(transform); xmlSecAssert2(ctx != NULL, -1); /* initialize context */ memset(ctx, 0, sizeof(xmlSecMSCngKdfCtx)); ret = xmlSecBufferInitialize(&(ctx->key), XMLSEC_MSCNG_KDF_DEFAULT_BUF_SIZE); if(ret < 0) { xmlSecInternalError("xmlSecBufferInitialize", NULL); xmlSecMSCngKdfFinalize(transform); return(-1); } #ifndef XMLSEC_NO_PBKDF2 if(xmlSecTransformCheckId(transform, xmlSecMSCngTransformPbkdf2Id)) { ctx->keyId = xmlSecMSCngKeyDataPbkdf2Id; ret = xmlSecTransformPbkdf2ParamsInitialize(&(ctx->pbkdf2Params)); if(ret < 0) { xmlSecInternalError("xmlSecTransformPbkdf2ParamsInitialize", NULL); xmlSecMSCngKdfFinalize(transform); return(-1); } } else #endif /* XMLSEC_NO_PBKDF2 */ #ifndef XMLSEC_NO_HKDF if(xmlSecTransformCheckId(transform, xmlSecMSCngTransformHkdfId)) { ctx->keyId = xmlSecMSCngKeyDataHkdfId; ret = xmlSecTransformHkdfParamsInitialize(&(ctx->hkdfParams)); if(ret < 0) { xmlSecInternalError("xmlSecTransformHkdfParamsInitialize", NULL); xmlSecMSCngKdfFinalize(transform); return(-1); } } else #endif /* XMLSEC_NO_HKDF */ { xmlSecInvalidTransfromError(transform) return(-1); } /* done */ return(0); } static void xmlSecMSCngKdfFinalize(xmlSecTransformPtr transform) { xmlSecMSCngKdfCtxPtr ctx; xmlSecAssert(xmlSecMSCngKdfCheckId(transform)); xmlSecAssert(xmlSecTransformCheckSize(transform, xmlSecMSCngKdfCtxSize)); ctx = xmlSecMSCngKdfGetCtx(transform); xmlSecAssert(ctx != NULL); xmlSecBufferFinalize(&(ctx->key)); #ifndef XMLSEC_NO_PBKDF2 if(xmlSecTransformCheckId(transform, xmlSecMSCngTransformPbkdf2Id)) { xmlSecTransformPbkdf2ParamsFinalize(&(ctx->pbkdf2Params)); } #endif /* XMLSEC_NO_PBKDF2 */ #ifndef XMLSEC_NO_HKDF if(xmlSecTransformCheckId(transform, xmlSecMSCngTransformHkdfId)) { xmlSecTransformHkdfParamsFinalize(&(ctx->hkdfParams)); } #endif /* XMLSEC_NO_HKDF */ memset(ctx, 0, sizeof(xmlSecMSCngKdfCtx)); } static int xmlSecMSCngKdfSetKeyReq(xmlSecTransformPtr transform, xmlSecKeyReqPtr keyReq) { xmlSecMSCngKdfCtxPtr ctx; xmlSecAssert2(xmlSecMSCngKdfCheckId(transform), -1); xmlSecAssert2(xmlSecTransformCheckSize(transform, xmlSecMSCngKdfCtxSize), -1); xmlSecAssert2(keyReq != NULL, -1); ctx = xmlSecMSCngKdfGetCtx(transform); xmlSecAssert2(ctx != NULL, -1); xmlSecAssert2(ctx->keyId != NULL, -1); keyReq->keyId = ctx->keyId; keyReq->keyType = xmlSecKeyDataTypeSymmetric; keyReq->keyUsage = xmlSecKeyUsageKeyDerive; return(0); } static int xmlSecMSCngKdfSetKey(xmlSecTransformPtr transform, xmlSecKeyPtr key) { xmlSecMSCngKdfCtxPtr ctx; xmlSecKeyDataPtr keyValue; xmlSecBufferPtr keyBuffer; xmlSecByte * keyData; xmlSecSize keySize; int ret; xmlSecAssert2(xmlSecMSCngKdfCheckId(transform), -1); xmlSecAssert2(((transform->operation == xmlSecTransformOperationEncrypt) || (transform->operation == xmlSecTransformOperationDecrypt)), -1); xmlSecAssert2(xmlSecTransformCheckSize(transform, xmlSecMSCngKdfCtxSize), -1); xmlSecAssert2(key != NULL, -1); ctx = xmlSecMSCngKdfGetCtx(transform); xmlSecAssert2(ctx != NULL, -1); xmlSecAssert2(ctx->keyId != NULL, -1); xmlSecAssert2(xmlSecKeyCheckId(key, ctx->keyId), -1); keyValue = xmlSecKeyGetValue(key); xmlSecAssert2(keyValue != NULL, -1); keyBuffer = xmlSecKeyDataBinaryValueGetBuffer(keyValue); xmlSecAssert2(keyBuffer != NULL, -1); keyData = xmlSecBufferGetData(keyBuffer); keySize = xmlSecBufferGetSize(keyBuffer); if((keyData == NULL) || (keySize == 0)) { xmlSecInvalidZeroKeyDataSizeError(xmlSecTransformGetName(transform)); return(-1); } ret = xmlSecBufferSetData(&(ctx->key), keyData, keySize); if(ret < 0) { xmlSecInternalError("xmlSecBufferSetData(key)", xmlSecTransformGetName(transform)); return(-1); } /* success */ return(0); } /****************************************************************************** * * PBKDF2 specific functions * *****************************************************************************/ #ifndef XMLSEC_NO_PBKDF2 /* convert PRF algorithm href to MSCng mac algo */ static LPCWSTR xmlSecMSCngPbkdf2GetMacFromHref(const xmlChar* href) { /* use SHA256 by default */ if(href == NULL) { #ifndef XMLSEC_NO_SHA256 return(BCRYPT_SHA256_ALGORITHM); #else /* XMLSEC_NO_SHA256 */ xmlSecOtherError2(XMLSEC_ERRORS_R_INVALID_ALGORITHM, NULL, "SHA256 is disabled; href=%s", xmlSecErrorsSafeString(href)); return(NULL); #endif /* XMLSEC_NO_SHA256 */ } else #ifndef XMLSEC_NO_SHA1 if(xmlStrcmp(href, xmlSecHrefHmacSha1) == 0) { return(BCRYPT_SHA1_ALGORITHM); } else #endif /* XMLSEC_NO_SHA1 */ #ifndef XMLSEC_NO_SHA256 if(xmlStrcmp(href, xmlSecHrefHmacSha256) == 0) { return(BCRYPT_SHA256_ALGORITHM); } else #endif /* XMLSEC_NO_SHA256 */ #ifndef XMLSEC_NO_SHA384 if(xmlStrcmp(href, xmlSecHrefHmacSha384) == 0) { return(BCRYPT_SHA384_ALGORITHM); } else #endif /* XMLSEC_NO_SHA384 */ #ifndef XMLSEC_NO_SHA512 if(xmlStrcmp(href, xmlSecHrefHmacSha512) == 0) { return(BCRYPT_SHA512_ALGORITHM); } else #endif /* XMLSEC_NO_SHA512 */ { xmlSecOtherError2(XMLSEC_ERRORS_R_INVALID_ALGORITHM, NULL, "href=%s", xmlSecErrorsSafeString(href)); return(NULL); } } static int xmlSecMSCngPbkdf2NodeRead(xmlSecTransformPtr transform, xmlNodePtr node, xmlSecTransformCtxPtr transformCtx XMLSEC_ATTRIBUTE_UNUSED) { xmlSecMSCngKdfCtxPtr ctx; xmlNodePtr cur; int ret; xmlSecAssert2(xmlSecTransformCheckId(transform, xmlSecMSCngTransformPbkdf2Id), -1); xmlSecAssert2(xmlSecTransformCheckSize(transform, xmlSecMSCngKdfCtxSize), -1); xmlSecAssert2(node!= NULL, -1); UNREFERENCED_PARAMETER(transformCtx); ctx = xmlSecMSCngKdfGetCtx(transform); xmlSecAssert2(ctx != NULL, -1); /* first (and only) node is required Pbkdf2Params */ cur = xmlSecGetNextElementNode(node->children); if((cur == NULL) || (!xmlSecCheckNodeName(cur, xmlSecNodePbkdf2Params, xmlSecEnc11Ns))) { xmlSecInvalidNodeError(cur, xmlSecNodePbkdf2Params, NULL); return(-1); } ret = xmlSecTransformPbkdf2ParamsRead(&(ctx->pbkdf2Params), cur); if(ret < 0) { xmlSecInternalError("xmlSecTransformPbkdf2ParamsRead", NULL); return(-1); } /* if we have something else then it's an error */ cur = xmlSecGetNextElementNode(cur->next); if(cur != NULL) { xmlSecUnexpectedNodeError(cur, NULL); return(-1); } /* set mac */ ctx->pszAlgId = xmlSecMSCngPbkdf2GetMacFromHref(ctx->pbkdf2Params.prfAlgorithmHref); if(ctx->pszAlgId == NULL) { xmlSecInternalError("xmlSecMSCngPbkdf2GetMacFromHref", xmlSecTransformGetName(transform)); return(-1); } /* success */ return(0); } static int xmlSecMSCngPbkdf2PeformKeyDerivation( LPCWSTR pszHashAlgo, PBYTE pbSecret, ULONG cbSecret, PBYTE pbSalt, ULONG cbSalt, ULONGLONG cbIterationCount, PBYTE pbOut, ULONG cbOut ) { NTSTATUS status; BCRYPT_ALG_HANDLE hKdfAlg = NULL; BCRYPT_KEY_HANDLE hKey= NULL; DWORD cbResultLength = 0; BCryptBuffer paramBufferPBKDF2[3]; BCryptBufferDesc paramsPBKDF2; int res = -1; xmlSecAssert2(pszHashAlgo != NULL, -1); xmlSecAssert2(pbSecret != NULL, -1); xmlSecAssert2(cbSecret > 0, -1); xmlSecAssert2(pbSalt != NULL, -1); xmlSecAssert2(cbSalt > 0, -1); xmlSecAssert2(cbIterationCount > 0, -1); xmlSecAssert2(pbOut != NULL, -1); xmlSecAssert2(cbOut > 0, -1); paramBufferPBKDF2[0].cbBuffer = cbSalt; paramBufferPBKDF2[0].BufferType = KDF_SALT; paramBufferPBKDF2[0].pvBuffer = pbSalt; paramBufferPBKDF2[1].cbBuffer = sizeof(cbIterationCount); paramBufferPBKDF2[1].BufferType = KDF_ITERATION_COUNT; paramBufferPBKDF2[1].pvBuffer = (PBYTE)&cbIterationCount; paramBufferPBKDF2[2].cbBuffer = ((ULONG)wcslen(pszHashAlgo) + 1) * sizeof(WCHAR); paramBufferPBKDF2[2].BufferType = KDF_HASH_ALGORITHM; paramBufferPBKDF2[2].pvBuffer = (LPWSTR)pszHashAlgo; paramsPBKDF2.ulVersion = BCRYPTBUFFER_VERSION; paramsPBKDF2.cBuffers = 3; paramsPBKDF2.pBuffers = paramBufferPBKDF2; /* get algo provider */ status = BCryptOpenAlgorithmProvider( &hKdfAlg, BCRYPT_PBKDF2_ALGORITHM, NULL, 0); if(status != STATUS_SUCCESS) { xmlSecMSCngNtError("BCryptOpenAlgorithmProvider", NULL, status); goto done; } /* create key for pbkdf2 */ status = BCryptGenerateSymmetricKey( hKdfAlg, &hKey, NULL, 0, pbSecret, cbSecret, 0); if(status != STATUS_SUCCESS) { xmlSecMSCngNtError("BCryptGenerateSymmetricKey", NULL, status); goto done; } /* generate the output key */ status = BCryptKeyDerivation( hKey, ¶msPBKDF2, pbOut, cbOut, &cbResultLength, 0); if(status != STATUS_SUCCESS) { xmlSecMSCngNtError("BCryptKeyDerivation", NULL, status); goto done; } if (cbResultLength != cbOut) { xmlSecInvalidSizeError("Derived key length doesn't match requested", (xmlSecSize)cbResultLength, (xmlSecSize)cbOut, NULL); goto done; } /* success */ res = 0; done: if(NULL != hKey) { BCryptDestroyKey(hKey); } if(NULL != hKdfAlg) { BCryptCloseAlgorithmProvider(hKdfAlg, 0); } return(res); } static int xmlSecMSCngPbkdf2Derive(xmlSecMSCngKdfCtxPtr ctx, xmlSecBufferPtr out) { xmlSecByte* passData; xmlSecSize passSize; ULONG passLen; xmlSecByte* saltData; xmlSecSize saltSize; ULONG saltLen; xmlSecByte* outData; ULONG outLen; int ret; xmlSecAssert2(ctx != NULL, -1); xmlSecAssert2(ctx->pszAlgId != NULL, -1); xmlSecAssert2(ctx->pbkdf2Params.keyLength > 0, -1); xmlSecAssert2(out != NULL, -1); /* get data */ passData = xmlSecBufferGetData(&(ctx->key)); passSize = xmlSecBufferGetSize(&(ctx->key)); xmlSecAssert2(passData != NULL, -1); xmlSecAssert2(passSize > 0, -1); XMLSEC_SAFE_CAST_SIZE_TO_ULONG(passSize, passLen, return(-1), NULL); saltData = xmlSecBufferGetData(&(ctx->pbkdf2Params.salt)); saltSize = xmlSecBufferGetSize(&(ctx->pbkdf2Params.salt)); xmlSecAssert2(saltData != NULL, -1); xmlSecAssert2(saltSize > 0, -1); XMLSEC_SAFE_CAST_SIZE_TO_ULONG(saltSize, saltLen, return(-1), NULL); /* allocate output buffer */ ret = xmlSecBufferSetSize(out, ctx->pbkdf2Params.keyLength); if (ret < 0) { xmlSecInternalError2("xmlSecBufferSetSize", NULL, "size=" XMLSEC_SIZE_FMT, ctx->pbkdf2Params.keyLength); return(-1); } outData = xmlSecBufferGetData(out); xmlSecAssert2(outData != NULL, -1); XMLSEC_SAFE_CAST_SIZE_TO_ULONG(ctx->pbkdf2Params.keyLength, outLen, return(-1), NULL); ret = xmlSecMSCngPbkdf2PeformKeyDerivation( ctx->pszAlgId, passData, passLen, saltData, saltLen, ctx->pbkdf2Params.iterationCount, outData, outLen ); if (ret < 0) { xmlSecInternalError2("xmlSecMSCngPbkdf2PeformKeyDerivation", NULL, "size=" XMLSEC_SIZE_FMT, ctx->pbkdf2Params.keyLength); return(-1); } /* success */ return(0); } #endif /* XMLSEC_NO_PBKDF2 */ /****************************************************************************** * * HKDF specific functions * *****************************************************************************/ #ifndef XMLSEC_NO_HKDF /* convert PRF algorithm href to MSCng hash algo */ static LPCWSTR xmlSecMSCngHkdfGetMacFromHref(const xmlChar* href) { /* use SHA256 by default */ if(href == NULL) { #ifndef XMLSEC_NO_SHA256 return(BCRYPT_SHA256_ALGORITHM); #else /* XMLSEC_NO_SHA256 */ xmlSecOtherError2(XMLSEC_ERRORS_R_INVALID_ALGORITHM, NULL, "SHA256 is disabled; href=%s", xmlSecErrorsSafeString(href)); return(NULL); #endif /* XMLSEC_NO_SHA256 */ } else #ifndef XMLSEC_NO_SHA1 if(xmlStrcmp(href, xmlSecHrefHmacSha1) == 0) { return(BCRYPT_SHA1_ALGORITHM); } else #endif /* XMLSEC_NO_SHA1 */ #ifndef XMLSEC_NO_SHA256 if(xmlStrcmp(href, xmlSecHrefHmacSha256) == 0) { return(BCRYPT_SHA256_ALGORITHM); } else #endif /* XMLSEC_NO_SHA256 */ #ifndef XMLSEC_NO_SHA384 if(xmlStrcmp(href, xmlSecHrefHmacSha384) == 0) { return(BCRYPT_SHA384_ALGORITHM); } else #endif /* XMLSEC_NO_SHA384 */ #ifndef XMLSEC_NO_SHA512 if(xmlStrcmp(href, xmlSecHrefHmacSha512) == 0) { return(BCRYPT_SHA512_ALGORITHM); } else #endif /* XMLSEC_NO_SHA512 */ { xmlSecOtherError2(XMLSEC_ERRORS_R_INVALID_ALGORITHM, NULL, "href=%s", xmlSecErrorsSafeString(href)); return(NULL); } } static int xmlSecMSCngHkdfNodeRead(xmlSecTransformPtr transform, xmlNodePtr node, xmlSecTransformCtxPtr transformCtx XMLSEC_ATTRIBUTE_UNUSED) { xmlSecMSCngKdfCtxPtr ctx; xmlNodePtr cur; int ret; xmlSecAssert2(xmlSecTransformCheckId(transform, xmlSecMSCngTransformHkdfId), -1); xmlSecAssert2(xmlSecTransformCheckSize(transform, xmlSecMSCngKdfCtxSize), -1); xmlSecAssert2(node != NULL, -1); UNREFERENCED_PARAMETER(transformCtx); ctx = xmlSecMSCngKdfGetCtx(transform); xmlSecAssert2(ctx != NULL, -1); /* first (and only) node is required HKDFParams */ cur = xmlSecGetNextElementNode(node->children); if((cur == NULL) || (!xmlSecCheckNodeName(cur, xmlSecNodeHkdfParams, xmlSecXmldsig2021MoreNs))) { xmlSecInvalidNodeError(cur, xmlSecNodeHkdfParams, NULL); return(-1); } ret = xmlSecTransformHkdfParamsRead(&(ctx->hkdfParams), cur); if(ret < 0) { xmlSecInternalError("xmlSecTransformHkdfParamsRead", NULL); return(-1); } /* if we have something else then it's an error */ cur = xmlSecGetNextElementNode(cur->next); if(cur != NULL) { xmlSecUnexpectedNodeError(cur, NULL); return(-1); } /* set hash algorithm */ ctx->pszAlgId = xmlSecMSCngHkdfGetMacFromHref(ctx->hkdfParams.prfAlgorithmHref); if(ctx->pszAlgId == NULL) { xmlSecInternalError("xmlSecMSCngHkdfGetMacFromHref", xmlSecTransformGetName(transform)); return(-1); } /* success */ return(0); } static int xmlSecMSCngHkdfPeformKeyDerivation( LPCWSTR pszHashAlgo, PBYTE pbIkm, ULONG cbIkm, PBYTE pbSalt, ULONG cbSalt, PBYTE pbInfo, ULONG cbInfo, PBYTE pbOut, ULONG cbOut ) { NTSTATUS status; BCRYPT_ALG_HANDLE hKdfAlg = NULL; BCRYPT_KEY_HANDLE hKey = NULL; DWORD cbResultLength = 0; ULONG hashAlgoLen; BCryptBuffer paramBuffer[1]; BCryptBufferDesc paramsHKDF; int res = -1; xmlSecAssert2(pszHashAlgo != NULL, -1); xmlSecAssert2(pbIkm != NULL, -1); xmlSecAssert2(cbIkm > 0, -1); xmlSecAssert2(pbOut != NULL, -1); xmlSecAssert2(cbOut > 0, -1); /* get HKDF algo provider */ status = BCryptOpenAlgorithmProvider( &hKdfAlg, BCRYPT_HKDF_ALGORITHM, NULL, 0); if(status != STATUS_SUCCESS) { xmlSecMSCngNtError("BCryptOpenAlgorithmProvider", NULL, status); goto done; } /* create key from IKM (input keying material) */ status = BCryptGenerateSymmetricKey( hKdfAlg, &hKey, NULL, 0, pbIkm, cbIkm, 0); if(status != STATUS_SUCCESS) { xmlSecMSCngNtError("BCryptGenerateSymmetricKey", NULL, status); goto done; } /* set hash algorithm property (required) */ hashAlgoLen = ((ULONG)wcslen(pszHashAlgo) + 1) * sizeof(WCHAR); status = BCryptSetProperty( hKey, BCRYPT_HKDF_HASH_ALGORITHM, (PBYTE)pszHashAlgo, hashAlgoLen, 0); if(status != STATUS_SUCCESS) { xmlSecMSCngNtError("BCryptSetProperty(BCRYPT_HKDF_HASH_ALGORITHM)", NULL, status); goto done; } /* set salt and finalize the key (salt is optional; NULL/0 means no salt) */ status = BCryptSetProperty( hKey, BCRYPT_HKDF_SALT_AND_FINALIZE, pbSalt, /* may be NULL */ cbSalt, /* may be 0 */ 0); if(status != STATUS_SUCCESS) { xmlSecMSCngNtError("BCryptSetProperty(BCRYPT_HKDF_SALT_AND_FINALIZE)", NULL, status); goto done; } /* build params: info (optional) */ paramsHKDF.ulVersion = BCRYPTBUFFER_VERSION; if((pbInfo != NULL) && (cbInfo > 0)) { paramBuffer[0].cbBuffer = cbInfo; paramBuffer[0].BufferType = KDF_HKDF_INFO; paramBuffer[0].pvBuffer = pbInfo; paramsHKDF.cBuffers = 1; paramsHKDF.pBuffers = paramBuffer; } else { paramsHKDF.cBuffers = 0; paramsHKDF.pBuffers = NULL; } /* derive the output key */ status = BCryptKeyDerivation( hKey, ¶msHKDF, pbOut, cbOut, &cbResultLength, 0); if(status != STATUS_SUCCESS) { xmlSecMSCngNtError("BCryptKeyDerivation", NULL, status); goto done; } if(cbResultLength != cbOut) { xmlSecInvalidSizeError("Derived key length doesn't match requested", (xmlSecSize)cbResultLength, (xmlSecSize)cbOut, NULL); goto done; } /* success */ res = 0; done: if(NULL != hKey) { BCryptDestroyKey(hKey); } if(NULL != hKdfAlg) { BCryptCloseAlgorithmProvider(hKdfAlg, 0); } return(res); } static int xmlSecMSCngHkdfDerive(xmlSecMSCngKdfCtxPtr ctx, xmlSecBufferPtr out, xmlSecSize outSize) { xmlSecByte* ikmData; xmlSecSize ikmSize; ULONG ikmLen; xmlSecByte* saltData; xmlSecSize saltSize; ULONG saltLen; xmlSecByte* infoData; xmlSecSize infoSize; ULONG infoLen; xmlSecByte* outData; ULONG outLen; int ret; xmlSecAssert2(ctx != NULL, -1); xmlSecAssert2(ctx->pszAlgId != NULL, -1); xmlSecAssert2(out != NULL, -1); xmlSecAssert2(outSize > 0, -1); /* get IKM (input keying material) */ ikmData = xmlSecBufferGetData(&(ctx->key)); ikmSize = xmlSecBufferGetSize(&(ctx->key)); xmlSecAssert2(ikmData != NULL, -1); xmlSecAssert2(ikmSize > 0, -1); XMLSEC_SAFE_CAST_SIZE_TO_ULONG(ikmSize, ikmLen, return(-1), NULL); /* get optional salt */ saltData = xmlSecBufferGetData(&(ctx->hkdfParams.salt)); saltSize = xmlSecBufferGetSize(&(ctx->hkdfParams.salt)); if((saltData != NULL) && (saltSize > 0)) { XMLSEC_SAFE_CAST_SIZE_TO_ULONG(saltSize, saltLen, return(-1), NULL); } else { saltData = NULL; saltLen = 0; } /* get optional info */ infoData = xmlSecBufferGetData(&(ctx->hkdfParams.info)); infoSize = xmlSecBufferGetSize(&(ctx->hkdfParams.info)); if((infoData != NULL) && (infoSize > 0)) { XMLSEC_SAFE_CAST_SIZE_TO_ULONG(infoSize, infoLen, return(-1), NULL); } else { infoData = NULL; infoLen = 0; } /* allocate output buffer */ ret = xmlSecBufferSetSize(out, outSize); if(ret < 0) { xmlSecInternalError2("xmlSecBufferSetSize", NULL, "size=" XMLSEC_SIZE_FMT, outSize); return(-1); } outData = xmlSecBufferGetData(out); xmlSecAssert2(outData != NULL, -1); XMLSEC_SAFE_CAST_SIZE_TO_ULONG(outSize, outLen, return(-1), NULL); ret = xmlSecMSCngHkdfPeformKeyDerivation( ctx->pszAlgId, ikmData, ikmLen, saltData, saltLen, infoData, infoLen, outData, outLen ); if(ret < 0) { xmlSecInternalError2("xmlSecMSCngHkdfPeformKeyDerivation", NULL, "size=" XMLSEC_SIZE_FMT, outSize); return(-1); } /* success */ return(0); } #endif /* XMLSEC_NO_HKDF */ /****************************************************************************** * * Unified KDF execute function * *****************************************************************************/ static int xmlSecMSCngKdfExecute(xmlSecTransformPtr transform, int last, xmlSecTransformCtxPtr transformCtx) { xmlSecMSCngKdfCtxPtr ctx; xmlSecBufferPtr in, out; int ret; xmlSecAssert2(xmlSecTransformIsValid(transform), -1); xmlSecAssert2(((transform->operation == xmlSecTransformOperationEncrypt) || (transform->operation == xmlSecTransformOperationDecrypt)), -1); xmlSecAssert2(xmlSecTransformCheckSize(transform, xmlSecMSCngKdfCtxSize), -1); xmlSecAssert2(transformCtx != NULL, -1); in = &(transform->inBuf); out = &(transform->outBuf); ctx = xmlSecMSCngKdfGetCtx(transform); xmlSecAssert2(ctx != NULL, -1); if(transform->status == xmlSecTransformStatusNone) { /* we should be already initialized when we set key */ transform->status = xmlSecTransformStatusWorking; } if((transform->status == xmlSecTransformStatusWorking) && (last == 0)) { /* do nothing */ } else if((transform->status == xmlSecTransformStatusWorking) && (last != 0)) { /* verify params */ if(transform->expectedOutputSize <= 0) { xmlSecOtherError(XMLSEC_ERRORS_R_INVALID_ALGORITHM, NULL, "KDF output key size is not specified"); return(-1); } #ifndef XMLSEC_NO_PBKDF2 if(xmlSecTransformCheckId(transform, xmlSecMSCngTransformPbkdf2Id)) { if((ctx->pbkdf2Params.keyLength > 0) && (ctx->pbkdf2Params.keyLength != transform->expectedOutputSize)){ xmlSecInvalidSizeError("Output kdf size doesn't match expected", transform->expectedOutputSize, ctx->pbkdf2Params.keyLength, xmlSecTransformGetName(transform)); return(-1); } ctx->pbkdf2Params.keyLength = transform->expectedOutputSize; /* derive */ ret = xmlSecMSCngPbkdf2Derive(ctx, out); if(ret < 0) { xmlSecInternalError("xmlSecMSCngPbkdf2Derive", xmlSecTransformGetName(transform)); return(-1); } } else #endif /* XMLSEC_NO_PBKDF2 */ #ifndef XMLSEC_NO_HKDF if(xmlSecTransformCheckId(transform, xmlSecMSCngTransformHkdfId)) { if((ctx->hkdfParams.keyLength > 0) && (ctx->hkdfParams.keyLength != transform->expectedOutputSize)){ xmlSecInvalidSizeError("Output kdf size doesn't match expected", transform->expectedOutputSize, ctx->hkdfParams.keyLength, xmlSecTransformGetName(transform)); return(-1); } /* derive */ ret = xmlSecMSCngHkdfDerive(ctx, out, transform->expectedOutputSize); if(ret < 0) { xmlSecInternalError("xmlSecMSCngHkdfDerive", xmlSecTransformGetName(transform)); return(-1); } } else #endif /* XMLSEC_NO_HKDF */ { xmlSecInvalidTransfromError(transform) return(-1); } /* done */ transform->status = xmlSecTransformStatusFinished; return(0); } else if(transform->status == xmlSecTransformStatusFinished) { /* the only way we can get here is if there is no input */ xmlSecAssert2(xmlSecBufferGetSize(in) == 0, -1); } else { xmlSecInvalidTransfromStatusError(transform); return(-1); } return(0); } /****************************************************************************** * * PBKDF2 key derivation algorithm klass * *****************************************************************************/ #ifndef XMLSEC_NO_PBKDF2 /****************************************************************************** * * PBKDF2 key derivation algorithm * *****************************************************************************/ static xmlSecTransformKlass xmlSecMSCngPbkdf2Klass = { /* klass/object sizes */ sizeof(xmlSecTransformKlass), /* xmlSecSize klassSize */ xmlSecMSCngKdfCtxSize, /* xmlSecSize objSize */ /* data */ xmlSecNamePbkdf2, /* const xmlChar* name; */ xmlSecHrefPbkdf2, /* const xmlChar* href; */ xmlSecTransformUsageKeyDerivationMethod, /* xmlSecTransformUsage usage; */ xmlSecMSCngKdfInitialize, /* xmlSecTransformInitializeMethod initialize; */ xmlSecMSCngKdfFinalize, /* xmlSecTransformFinalizeMethod finalize; */ xmlSecMSCngPbkdf2NodeRead, /* xmlSecTransformNodeReadMethod readNode; */ NULL, /* xmlSecTransformNodeWriteMethod writeNode; */ xmlSecMSCngKdfSetKeyReq, /* xmlSecTransformSetKeyReqMethod setKeyReq; */ xmlSecMSCngKdfSetKey, /* xmlSecTransformSetKeyMethod setKey; */ NULL, /* xmlSecTransformValidateMethod validate; */ xmlSecTransformDefaultGetDataType, /* xmlSecTransformGetDataTypeMethod getDataType; */ xmlSecTransformDefaultPushBin, /* xmlSecTransformPushBinMethod pushBin; */ xmlSecTransformDefaultPopBin, /* xmlSecTransformPopBinMethod popBin; */ NULL, /* xmlSecTransformPushXmlMethod pushXml; */ NULL, /* xmlSecTransformPopXmlMethod popXml; */ xmlSecMSCngKdfExecute, /* xmlSecTransformExecuteMethod execute; */ NULL, /* void* reserved0; */ NULL, /* void* reserved1; */ }; /** * @brief The PBKDF2 key derivation transform klass. * @return the PBKDF2 key derivation transform klass. */ xmlSecTransformId xmlSecMSCngTransformPbkdf2GetKlass(void) { return(&xmlSecMSCngPbkdf2Klass); } #endif /* XMLSEC_NO_PBKDF2 */ /****************************************************************************** * * HKDF key derivation algorithm klass * *****************************************************************************/ #ifndef XMLSEC_NO_HKDF /****************************************************************************** * * HKDF key derivation algorithm * *****************************************************************************/ static xmlSecTransformKlass xmlSecMSCngHkdfKlass = { /* klass/object sizes */ sizeof(xmlSecTransformKlass), /* xmlSecSize klassSize */ xmlSecMSCngKdfCtxSize, /* xmlSecSize objSize */ /* data */ xmlSecNameHkdf, /* const xmlChar* name; */ xmlSecHrefHkdf, /* const xmlChar* href; */ xmlSecTransformUsageKeyDerivationMethod, /* xmlSecTransformUsage usage; */ xmlSecMSCngKdfInitialize, /* xmlSecTransformInitializeMethod initialize; */ xmlSecMSCngKdfFinalize, /* xmlSecTransformFinalizeMethod finalize; */ xmlSecMSCngHkdfNodeRead, /* xmlSecTransformNodeReadMethod readNode; */ NULL, /* xmlSecTransformNodeWriteMethod writeNode; */ xmlSecMSCngKdfSetKeyReq, /* xmlSecTransformSetKeyReqMethod setKeyReq; */ xmlSecMSCngKdfSetKey, /* xmlSecTransformSetKeyMethod setKey; */ NULL, /* xmlSecTransformValidateMethod validate; */ xmlSecTransformDefaultGetDataType, /* xmlSecTransformGetDataTypeMethod getDataType; */ xmlSecTransformDefaultPushBin, /* xmlSecTransformPushBinMethod pushBin; */ xmlSecTransformDefaultPopBin, /* xmlSecTransformPopBinMethod popBin; */ NULL, /* xmlSecTransformPushXmlMethod pushXml; */ NULL, /* xmlSecTransformPopXmlMethod popXml; */ xmlSecMSCngKdfExecute, /* xmlSecTransformExecuteMethod execute; */ NULL, /* void* reserved0; */ NULL, /* void* reserved1; */ }; /** * @brief The HKDF key derivation transform klass. * @return the HKDF key derivation transform klass. */ xmlSecTransformId xmlSecMSCngTransformHkdfGetKlass(void) { return(&xmlSecMSCngHkdfKlass); } #endif /* XMLSEC_NO_HKDF */ #else /* !defined(XMLSEC_NO_PBKDF2) || !defined(XMLSEC_NO_HKDF) */ /* ISO C forbids an empty translation unit */ typedef int make_iso_compilers_happy; #endif /* !defined(XMLSEC_NO_PBKDF2) || !defined(XMLSEC_NO_HKDF) */