/
githubmirror
/
xmlsec
Обзор
Документация
Войти
/
githubmirror
/
xmlsec
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
src/gnutls/kdf.c
1 163 строки
39 KB
lsh123
Update file headers for doxygen, use common copyright header, bump copyright to 2026 (#1122)
07 апр 2026, 04:13
Не верифицирован
07 апр 2026, 04:13
2e557ee
Код
Авторство
О чём код?
/** * 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) 2002-2026 Aleksey Sanin <aleksey@aleksey.com>. All Rights Reserved. */ /** * @addtogroup xmlsec_gnutls_crypto * @brief KDF (key derivation) transforms implementation for GnuTLS. */ #include "globals.h" #include <stdlib.h> #include <stdio.h> #include <string.h> #include <ctype.h> #include <gnutls/gnutls.h> #include <gnutls/abstract.h> #include <gnutls/crypto.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/gnutls/crypto.h> #include "../cast_helpers.h" #include "../keysdata_helpers.h" #include "../transform_helpers.h" /****************************************************************************** * * Internal KDF CTX * *****************************************************************************/ #define XMLSEC_GNUTLS_KDF_DEFAULT_BUF_SIZE 64 #define XMLSEC_GNUTLS_KDF_MAX_HASH_SIZE 64 /* SHA-512 output = 64 bytes */ typedef enum { xmlSecGnuTLSKdfType_Unknown = 0, xmlSecGnuTLSKdfType_ConcatKdf, xmlSecGnuTLSKdfType_Pbkdf2, xmlSecGnuTLSKdfType_Hkdf } xmlSecGnuTLSKdfType; typedef struct _xmlSecGnuTLSKdfCtx xmlSecGnuTLSKdfCtx, *xmlSecGnuTLSKdfCtxPtr; struct _xmlSecGnuTLSKdfCtx { xmlSecGnuTLSKdfType kdfType; xmlSecKeyDataId keyId; /* key material */ xmlSecBuffer key; /* KDF-specific data */ union { struct { xmlSecTransformConcatKdfParams params; gnutls_digest_algorithm_t dgstAlgo; xmlSecBuffer fixedInfo; /* pre-computed FixedInfo (OtherInfo) */ } concatKdf; struct { xmlSecTransformPbkdf2Params params; gnutls_mac_algorithm_t mac; } pbkdf2; struct { xmlSecTransformHkdfParams params; gnutls_mac_algorithm_t mac; xmlSecBuffer salt; xmlSecBuffer info; } hkdf; } u; }; XMLSEC_TRANSFORM_DECLARE(GnuTLSKdf, xmlSecGnuTLSKdfCtx) #define xmlSecGnuTLSKdfCtxSize XMLSEC_TRANSFORM_SIZE(GnuTLSKdf) static int xmlSecGnuTLSKdfCheckId (xmlSecTransformPtr transform); static int xmlSecGnuTLSKdfInitialize (xmlSecTransformPtr transform); static void xmlSecGnuTLSKdfFinalize (xmlSecTransformPtr transform); static int xmlSecGnuTLSKdfSetKeyReq (xmlSecTransformPtr transform, xmlSecKeyReqPtr keyReq); static int xmlSecGnuTLSKdfSetKey (xmlSecTransformPtr transform, xmlSecKeyPtr key); static int xmlSecGnuTLSKdfExecute (xmlSecTransformPtr transform, int last, xmlSecTransformCtxPtr transformCtx); static int xmlSecGnuTLSKdfCheckId(xmlSecTransformPtr transform) { #ifndef XMLSEC_NO_CONCATKDF if(xmlSecTransformCheckId(transform, xmlSecGnuTLSTransformConcatKdfId)) { return(1); } else #endif /* XMLSEC_NO_CONCATKDF */ #ifndef XMLSEC_NO_PBKDF2 if(xmlSecTransformCheckId(transform, xmlSecGnuTLSTransformPbkdf2Id)) { return(1); } else #endif /* XMLSEC_NO_PBKDF2 */ #ifndef XMLSEC_NO_HKDF if(xmlSecTransformCheckId(transform, xmlSecGnuTLSTransformHkdfId)) { return(1); } else #endif /* XMLSEC_NO_HKDF */ /* not found */ { return(0); } } static int xmlSecGnuTLSKdfInitialize(xmlSecTransformPtr transform) { xmlSecGnuTLSKdfCtxPtr ctx; int ret; xmlSecAssert2(xmlSecGnuTLSKdfCheckId(transform), -1); xmlSecAssert2(xmlSecTransformCheckSize(transform, xmlSecGnuTLSKdfCtxSize), -1); ctx = xmlSecGnuTLSKdfGetCtx(transform); xmlSecAssert2(ctx != NULL, -1); /* initialize context */ memset(ctx, 0, sizeof(xmlSecGnuTLSKdfCtx)); #ifndef XMLSEC_NO_CONCATKDF if(xmlSecTransformCheckId(transform, xmlSecGnuTLSTransformConcatKdfId)) { ctx->kdfType = xmlSecGnuTLSKdfType_ConcatKdf; ctx->keyId = xmlSecGnuTLSKeyDataConcatKdfId; ctx->u.concatKdf.dgstAlgo = GNUTLS_DIG_UNKNOWN; } else #endif /* XMLSEC_NO_CONCATKDF */ #ifndef XMLSEC_NO_PBKDF2 if(xmlSecTransformCheckId(transform, xmlSecGnuTLSTransformPbkdf2Id)) { ctx->kdfType = xmlSecGnuTLSKdfType_Pbkdf2; ctx->keyId = xmlSecGnuTLSKeyDataPbkdf2Id; ctx->u.pbkdf2.mac = GNUTLS_MAC_UNKNOWN; } else #endif /* XMLSEC_NO_PBKDF2 */ #ifndef XMLSEC_NO_HKDF if(xmlSecTransformCheckId(transform, xmlSecGnuTLSTransformHkdfId)) { ctx->kdfType = xmlSecGnuTLSKdfType_Hkdf; ctx->keyId = xmlSecGnuTLSKeyDataHkdfId; ctx->u.hkdf.mac = GNUTLS_MAC_UNKNOWN; } else #endif /* XMLSEC_NO_HKDF */ /* not found */ { xmlSecInvalidTransfromError(transform); return(-1); } /* init key buffer */ ret = xmlSecBufferInitialize(&(ctx->key), XMLSEC_GNUTLS_KDF_DEFAULT_BUF_SIZE); if(ret < 0) { xmlSecInternalError("xmlSecBufferInitialize(key)", NULL); xmlSecGnuTLSKdfFinalize(transform); return(-1); } /* init KDF-specific structures */ if(0) { #ifndef XMLSEC_NO_CONCATKDF } else if(ctx->kdfType == xmlSecGnuTLSKdfType_ConcatKdf) { ret = xmlSecBufferInitialize(&(ctx->u.concatKdf.fixedInfo), XMLSEC_GNUTLS_KDF_DEFAULT_BUF_SIZE); if(ret < 0) { xmlSecInternalError("xmlSecBufferInitialize(fixedInfo)", NULL); xmlSecGnuTLSKdfFinalize(transform); return(-1); } ret = xmlSecTransformConcatKdfParamsInitialize(&(ctx->u.concatKdf.params)); if(ret < 0) { xmlSecInternalError("xmlSecTransformConcatKdfParamsInitialize", NULL); xmlSecGnuTLSKdfFinalize(transform); return(-1); } #endif /* XMLSEC_NO_CONCATKDF */ #ifndef XMLSEC_NO_PBKDF2 } else if(ctx->kdfType == xmlSecGnuTLSKdfType_Pbkdf2) { ret = xmlSecTransformPbkdf2ParamsInitialize(&(ctx->u.pbkdf2.params)); if(ret < 0) { xmlSecInternalError("xmlSecTransformPbkdf2ParamsInitialize", NULL); xmlSecGnuTLSKdfFinalize(transform); return(-1); } #endif /* XMLSEC_NO_PBKDF2 */ #ifndef XMLSEC_NO_HKDF } else if(ctx->kdfType == xmlSecGnuTLSKdfType_Hkdf) { ret = xmlSecBufferInitialize(&(ctx->u.hkdf.salt), 0); if(ret < 0) { xmlSecInternalError("xmlSecBufferInitialize(salt)", NULL); xmlSecGnuTLSKdfFinalize(transform); return(-1); } ret = xmlSecBufferInitialize(&(ctx->u.hkdf.info), 0); if(ret < 0) { xmlSecInternalError("xmlSecBufferInitialize(info)", NULL); xmlSecGnuTLSKdfFinalize(transform); return(-1); } ret = xmlSecTransformHkdfParamsInitialize(&(ctx->u.hkdf.params)); if(ret < 0) { xmlSecInternalError("xmlSecTransformHkdfParamsInitialize", NULL); xmlSecGnuTLSKdfFinalize(transform); return(-1); } #endif /* XMLSEC_NO_HKDF */ } /* done */ return(0); } static void xmlSecGnuTLSKdfFinalize(xmlSecTransformPtr transform) { xmlSecGnuTLSKdfCtxPtr ctx; xmlSecAssert(xmlSecGnuTLSKdfCheckId(transform)); xmlSecAssert(xmlSecTransformCheckSize(transform, xmlSecGnuTLSKdfCtxSize)); ctx = xmlSecGnuTLSKdfGetCtx(transform); xmlSecAssert(ctx != NULL); /* finalize KDF-specific structures */ if(0) { #ifndef XMLSEC_NO_CONCATKDF } else if(ctx->kdfType == xmlSecGnuTLSKdfType_ConcatKdf) { xmlSecBufferFinalize(&(ctx->u.concatKdf.fixedInfo)); xmlSecTransformConcatKdfParamsFinalize(&(ctx->u.concatKdf.params)); #endif /* XMLSEC_NO_CONCATKDF */ #ifndef XMLSEC_NO_PBKDF2 } else if(ctx->kdfType == xmlSecGnuTLSKdfType_Pbkdf2) { xmlSecTransformPbkdf2ParamsFinalize(&(ctx->u.pbkdf2.params)); #endif /* XMLSEC_NO_PBKDF2 */ #ifndef XMLSEC_NO_HKDF } else if(ctx->kdfType == xmlSecGnuTLSKdfType_Hkdf) { xmlSecBufferFinalize(&(ctx->u.hkdf.salt)); xmlSecBufferFinalize(&(ctx->u.hkdf.info)); xmlSecTransformHkdfParamsFinalize(&(ctx->u.hkdf.params)); #endif /* XMLSEC_NO_HKDF */ } xmlSecBufferFinalize(&(ctx->key)); memset(ctx, 0, sizeof(xmlSecGnuTLSKdfCtx)); } static int xmlSecGnuTLSKdfSetKeyReq(xmlSecTransformPtr transform, xmlSecKeyReqPtr keyReq) { xmlSecGnuTLSKdfCtxPtr ctx; xmlSecAssert2(xmlSecGnuTLSKdfCheckId(transform), -1); xmlSecAssert2(xmlSecTransformCheckSize(transform, xmlSecGnuTLSKdfCtxSize), -1); xmlSecAssert2(keyReq != NULL, -1); ctx = xmlSecGnuTLSKdfGetCtx(transform); xmlSecAssert2(ctx->keyId != NULL, -1); keyReq->keyId = ctx->keyId; keyReq->keyType = xmlSecKeyDataTypeSymmetric; keyReq->keyUsage = xmlSecKeyUsageKeyDerive; return(0); } static int xmlSecGnuTLSKdfSetKey(xmlSecTransformPtr transform, xmlSecKeyPtr key) { xmlSecGnuTLSKdfCtxPtr ctx; xmlSecKeyDataPtr value; xmlSecBufferPtr buffer; xmlSecByte * keyData; xmlSecSize keySize; int ret; xmlSecAssert2(xmlSecGnuTLSKdfCheckId(transform), -1); xmlSecAssert2(((transform->operation == xmlSecTransformOperationEncrypt) || (transform->operation == xmlSecTransformOperationDecrypt)), -1); xmlSecAssert2(xmlSecTransformCheckSize(transform, xmlSecGnuTLSKdfCtxSize), -1); xmlSecAssert2(key != NULL, -1); ctx = xmlSecGnuTLSKdfGetCtx(transform); xmlSecAssert2(ctx != NULL, -1); xmlSecAssert2(ctx->keyId != NULL, -1); xmlSecAssert2(xmlSecKeyCheckId(key, ctx->keyId), -1); value = xmlSecKeyGetValue(key); xmlSecAssert2(value != NULL, -1); buffer = xmlSecKeyDataBinaryValueGetBuffer(value); xmlSecAssert2(buffer != NULL, -1); keyData = xmlSecBufferGetData(buffer); keySize = xmlSecBufferGetSize(buffer); 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); } /* Helper macro to define the KDF transform klass */ #define XMLSEC_GNUTLS_KDF_KLASS(name, readNode) \ static xmlSecTransformKlass xmlSecGnuTLS ## name ## Klass = { \ sizeof(xmlSecTransformKlass), /* xmlSecSize klassSize */ \ xmlSecGnuTLSKdfCtxSize, /* xmlSecSize objSize */ \ xmlSecName ## name, /* const xmlChar* name; */ \ xmlSecHref ## name, /* const xmlChar* href; */ \ xmlSecTransformUsageKeyDerivationMethod, /* xmlSecTransformUsage usage; */ \ xmlSecGnuTLSKdfInitialize, /* xmlSecTransformInitializeMethod initialize; */ \ xmlSecGnuTLSKdfFinalize, /* xmlSecTransformFinalizeMethod finalize; */ \ readNode, /* xmlSecTransformNodeReadMethod readNode; */ \ NULL, /* xmlSecTransformNodeWriteMethod writeNode; */ \ xmlSecGnuTLSKdfSetKeyReq, /* xmlSecTransformSetKeyReqMethod setKeyReq; */ \ xmlSecGnuTLSKdfSetKey, /* xmlSecTransformSetKeyMethod setKey; */ \ NULL, /* xmlSecTransformValidateMethod validate; */ \ xmlSecTransformDefaultGetDataType, /* xmlSecTransformGetDataTypeMethod getDataType; */ \ xmlSecTransformDefaultPushBin, /* xmlSecTransformPushBinMethod pushBin; */ \ xmlSecTransformDefaultPopBin, /* xmlSecTransformPopBinMethod popBin; */ \ NULL, /* xmlSecTransformPushXmlMethod pushXml; */ \ NULL, /* xmlSecTransformPopXmlMethod popXml; */ \ xmlSecGnuTLSKdfExecute, /* xmlSecTransformExecuteMethod execute; */ \ NULL, /* void* reserved0; */ \ NULL, /* void* reserved1; */ \ }; #ifndef XMLSEC_NO_CONCATKDF /****************************************************************************** * * ConcatKDF (SP 800-56A single-step KDF) transform * https://www.w3.org/TR/xmlenc-core1/#sec-ConcatKDF * *****************************************************************************/ static int xmlSecGnuTLSConcatKdfNodeRead (xmlSecTransformPtr transform, xmlNodePtr node, xmlSecTransformCtxPtr transformCtx); /* convert DigestMethod href to GnuTLS digest algo */ static gnutls_digest_algorithm_t xmlSecGnuTLSConcatKdfGetDigestFromHref(const xmlChar* href) { /* use SHA256 by default */ if(href == NULL) { #ifndef XMLSEC_NO_SHA256 return(GNUTLS_DIG_SHA256); #else /* XMLSEC_NO_SHA256 */ xmlSecOtherError2(XMLSEC_ERRORS_R_INVALID_ALGORITHM, NULL, "SHA256 is disabled; href=%s", xmlSecErrorsSafeString(href)); return(GNUTLS_DIG_UNKNOWN); #endif /* XMLSEC_NO_SHA256 */ } else #ifndef XMLSEC_NO_SHA1 if(xmlStrcmp(href, xmlSecHrefSha1) == 0) { return(GNUTLS_DIG_SHA1); } else #endif /* XMLSEC_NO_SHA1 */ #ifndef XMLSEC_NO_SHA224 if(xmlStrcmp(href, xmlSecHrefSha224) == 0) { return(GNUTLS_DIG_SHA224); } else #endif /* XMLSEC_NO_SHA224 */ #ifndef XMLSEC_NO_SHA256 if(xmlStrcmp(href, xmlSecHrefSha256) == 0) { return(GNUTLS_DIG_SHA256); } else #endif /* XMLSEC_NO_SHA256 */ #ifndef XMLSEC_NO_SHA384 if(xmlStrcmp(href, xmlSecHrefSha384) == 0) { return(GNUTLS_DIG_SHA384); } else #endif /* XMLSEC_NO_SHA384 */ #ifndef XMLSEC_NO_SHA512 if(xmlStrcmp(href, xmlSecHrefSha512) == 0) { return(GNUTLS_DIG_SHA512); } else #endif /* XMLSEC_NO_SHA512 */ #ifndef XMLSEC_NO_SHA3 if(xmlStrcmp(href, xmlSecHrefSha3_224) == 0) { return(GNUTLS_DIG_SHA3_224); } else if(xmlStrcmp(href, xmlSecHrefSha3_256) == 0) { return(GNUTLS_DIG_SHA3_256); } else if(xmlStrcmp(href, xmlSecHrefSha3_384) == 0) { return(GNUTLS_DIG_SHA3_384); } else if(xmlStrcmp(href, xmlSecHrefSha3_512) == 0) { return(GNUTLS_DIG_SHA3_512); } else #endif /* XMLSEC_NO_SHA3 */ { xmlSecOtherError2(XMLSEC_ERRORS_R_INVALID_ALGORITHM, NULL, "href=%s", xmlSecErrorsSafeString(href)); return(GNUTLS_DIG_UNKNOWN); } } static int xmlSecGnuTLSConcatKdfNodeRead(xmlSecTransformPtr transform, xmlNodePtr node, xmlSecTransformCtxPtr transformCtx XMLSEC_ATTRIBUTE_UNUSED) { xmlSecGnuTLSKdfCtxPtr ctx; xmlNodePtr cur; int ret; xmlSecAssert2(xmlSecTransformCheckId(transform, xmlSecGnuTLSTransformConcatKdfId), -1); xmlSecAssert2(xmlSecTransformCheckSize(transform, xmlSecGnuTLSKdfCtxSize), -1); xmlSecAssert2(node != NULL, -1); UNREFERENCED_PARAMETER(transformCtx); ctx = xmlSecGnuTLSKdfGetCtx(transform); xmlSecAssert2(ctx != NULL, -1); xmlSecAssert2(ctx->kdfType == xmlSecGnuTLSKdfType_ConcatKdf, -1); /* first (and only) node is required ConcatKDFParams */ cur = xmlSecGetNextElementNode(node->children); if((cur == NULL) || (!xmlSecCheckNodeName(cur, xmlSecNodeConcatKDFParams, xmlSecEnc11Ns))) { xmlSecInvalidNodeError(cur, xmlSecNodeConcatKDFParams, NULL); return(-1); } ret = xmlSecTransformConcatKdfParamsRead(&(ctx->u.concatKdf.params), cur); if(ret < 0) { xmlSecInternalError("xmlSecTransformConcatKdfParamsRead", 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 digest algorithm */ ctx->u.concatKdf.dgstAlgo = xmlSecGnuTLSConcatKdfGetDigestFromHref(ctx->u.concatKdf.params.digestMethod); if(ctx->u.concatKdf.dgstAlgo == GNUTLS_DIG_UNKNOWN) { xmlSecInternalError("xmlSecGnuTLSConcatKdfGetDigestFromHref", xmlSecTransformGetName(transform)); return(-1); } /* pre-compute fixedInfo = AlgorithmID || PartyUInfo || PartyVInfo [|| SuppPubInfo [|| SuppPrivInfo]] */ ret = xmlSecTransformConcatKdfParamsGetFixedInfo(&(ctx->u.concatKdf.params), &(ctx->u.concatKdf.fixedInfo)); if(ret < 0) { xmlSecInternalError("xmlSecTransformConcatKdfParamsGetFixedInfo", xmlSecTransformGetName(transform)); return(-1); } /* success */ return(0); } /* SP 800-56A single-step KDF: H(counter || Z || OtherInfo) */ static int xmlSecGnuTLSConcatKdfGenerateKey(xmlSecGnuTLSKdfCtxPtr ctx, xmlSecSize outLen, xmlSecBufferPtr out) { xmlSecByte * keyData; xmlSecSize keySize; xmlSecByte * fixedInfoData; xmlSecSize fixedInfoSize; xmlSecByte * outData; xmlSecSize hashLen; xmlSecSize pos; xmlSecByte hashBuf[XMLSEC_GNUTLS_KDF_MAX_HASH_SIZE]; xmlSecByte counter[4]; uint32_t counterVal; gnutls_hash_hd_t hash; int err; int ret; xmlSecAssert2(ctx != NULL, -1); xmlSecAssert2(ctx->kdfType == xmlSecGnuTLSKdfType_ConcatKdf, -1); xmlSecAssert2(ctx->u.concatKdf.dgstAlgo != GNUTLS_DIG_UNKNOWN, -1); xmlSecAssert2(outLen > 0, -1); xmlSecAssert2(out != NULL, -1); /* get keying material */ keyData = xmlSecBufferGetData(&(ctx->key)); keySize = xmlSecBufferGetSize(&(ctx->key)); if((keyData == NULL) || (keySize == 0)) { xmlSecInvalidZeroKeyDataSizeError(NULL); return(-1); } /* get fixedInfo (may be empty) */ fixedInfoData = xmlSecBufferGetData(&(ctx->u.concatKdf.fixedInfo)); fixedInfoSize = xmlSecBufferGetSize(&(ctx->u.concatKdf.fixedInfo)); /* get hash output length */ hashLen = (xmlSecSize)gnutls_hash_get_len(ctx->u.concatKdf.dgstAlgo); if(hashLen == 0) { xmlSecGnuTLSError("gnutls_hash_get_len", GNUTLS_E_SUCCESS, NULL); return(-1); } if(hashLen > XMLSEC_GNUTLS_KDF_MAX_HASH_SIZE) { xmlSecInternalError("hash output size exceeds buffer", NULL); return(-1); } /* allocate output buffer */ ret = xmlSecBufferSetSize(out, outLen); if(ret < 0) { xmlSecInternalError2("xmlSecBufferSetSize", NULL, "size=" XMLSEC_SIZE_FMT, outLen); return(-1); } outData = xmlSecBufferGetData(out); xmlSecAssert2(outData != NULL, -1); /* process rounds: K(i) = Hash(counter_i || Z || OtherInfo) */ pos = 0; counterVal = 1; while(pos < outLen) { /* encode counter as 4-byte big-endian */ counter[0] = (xmlSecByte)((counterVal >> 24) & 0xFF); counter[1] = (xmlSecByte)((counterVal >> 16) & 0xFF); counter[2] = (xmlSecByte)((counterVal >> 8) & 0xFF); counter[3] = (xmlSecByte)((counterVal ) & 0xFF); /* init hash */ err = gnutls_hash_init(&hash, ctx->u.concatKdf.dgstAlgo); if(err != GNUTLS_E_SUCCESS) { xmlSecGnuTLSError("gnutls_hash_init", err, NULL); return(-1); } /* hash: counter || Z || OtherInfo */ err = gnutls_hash(hash, counter, 4); if(err != GNUTLS_E_SUCCESS) { xmlSecGnuTLSError("gnutls_hash(counter)", err, NULL); gnutls_hash_deinit(hash, NULL); return(-1); } err = gnutls_hash(hash, keyData, keySize); if(err != GNUTLS_E_SUCCESS) { xmlSecGnuTLSError("gnutls_hash(Z)", err, NULL); gnutls_hash_deinit(hash, NULL); return(-1); } if((fixedInfoData != NULL) && (fixedInfoSize > 0)) { err = gnutls_hash(hash, fixedInfoData, fixedInfoSize); if(err != GNUTLS_E_SUCCESS) { xmlSecGnuTLSError("gnutls_hash(OtherInfo)", err, NULL); gnutls_hash_deinit(hash, NULL); return(-1); } } /* finalize hash into hashBuf */ gnutls_hash_deinit(hash, hashBuf); /* copy to output (only as many bytes as needed) */ { xmlSecSize toCopy = outLen - pos; if(toCopy > hashLen) { toCopy = hashLen; } memcpy(outData + pos, hashBuf, toCopy); pos += toCopy; } counterVal++; } /* securely wipe sensitive data from stack */ memset(hashBuf, 0, sizeof(hashBuf)); /* success */ return(0); } /****************************************************************************** * * ConcatKDF key derivation transform klass * *****************************************************************************/ XMLSEC_GNUTLS_KDF_KLASS(ConcatKdf, xmlSecGnuTLSConcatKdfNodeRead) /** * @brief The ConcatKDF key derivation transform klass. * @return the ConcatKDF key derivation transform klass. */ xmlSecTransformId xmlSecGnuTLSTransformConcatKdfGetKlass(void) { return(&xmlSecGnuTLSConcatKdfKlass); } #endif /* XMLSEC_NO_CONCATKDF */ #ifndef XMLSEC_NO_PBKDF2 /****************************************************************************** * * PBKDF2 transform (https://gnutls.org/reference/gnutls-crypto.html#gnutls-pbkdf2) * *****************************************************************************/ static int xmlSecGnuTLSPbkdf2NodeRead (xmlSecTransformPtr transform, xmlNodePtr node, xmlSecTransformCtxPtr transformCtx); /* convert PRF algorithm href to GnuTLS mac algo */ static gnutls_mac_algorithm_t xmlSecGnuTLSPbkdf2GetMacFromHref(const xmlChar* href) { /* use SHA256 by default */ if(href == NULL) { #ifndef XMLSEC_NO_SHA256 return(GNUTLS_MAC_SHA256); #else /* XMLSEC_NO_SHA256 */ xmlSecOtherError2(XMLSEC_ERRORS_R_INVALID_ALGORITHM, NULL, "SHA256 is disabled; href=%s", xmlSecErrorsSafeString(href)); return(GNUTLS_MAC_UNKNOWN); #endif /* XMLSEC_NO_SHA256 */ } else #ifndef XMLSEC_NO_SHA1 if(xmlStrcmp(href, xmlSecHrefHmacSha1) == 0) { return(GNUTLS_MAC_SHA1); } else #endif /* XMLSEC_NO_SHA1 */ #ifndef XMLSEC_NO_SHA224 if(xmlStrcmp(href, xmlSecHrefHmacSha224) == 0) { return(GNUTLS_MAC_SHA224); } else #endif /* XMLSEC_NO_SHA224 */ #ifndef XMLSEC_NO_SHA256 if(xmlStrcmp(href, xmlSecHrefHmacSha256) == 0) { return(GNUTLS_MAC_SHA256); } else #endif /* XMLSEC_NO_SHA256 */ #ifndef XMLSEC_NO_SHA384 if(xmlStrcmp(href, xmlSecHrefHmacSha384) == 0) { return(GNUTLS_MAC_SHA384); } else #endif /* XMLSEC_NO_SHA384 */ #ifndef XMLSEC_NO_SHA512 if(xmlStrcmp(href, xmlSecHrefHmacSha512) == 0) { return(GNUTLS_MAC_SHA512); } else #endif /* XMLSEC_NO_SHA512 */ { xmlSecOtherError2(XMLSEC_ERRORS_R_INVALID_ALGORITHM, NULL, "href=%s", xmlSecErrorsSafeString(href)); return(GNUTLS_MAC_UNKNOWN); } } static int xmlSecGnuTLSPbkdf2NodeRead(xmlSecTransformPtr transform, xmlNodePtr node, xmlSecTransformCtxPtr transformCtx XMLSEC_ATTRIBUTE_UNUSED) { xmlSecGnuTLSKdfCtxPtr ctx; xmlNodePtr cur; int ret; xmlSecAssert2(xmlSecTransformCheckId(transform, xmlSecGnuTLSTransformPbkdf2Id), -1); xmlSecAssert2(xmlSecTransformCheckSize(transform, xmlSecGnuTLSKdfCtxSize), -1); xmlSecAssert2(node!= NULL, -1); UNREFERENCED_PARAMETER(transformCtx); ctx = xmlSecGnuTLSKdfGetCtx(transform); xmlSecAssert2(ctx != NULL, -1); xmlSecAssert2(ctx->kdfType == xmlSecGnuTLSKdfType_Pbkdf2, -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->u.pbkdf2.params), cur); if(ret < 0) { xmlSecInternalError("xmlSecTransformPbkdf2ParamsRead", NULL); return(-1); } /* set mac */ ctx->u.pbkdf2.mac = xmlSecGnuTLSPbkdf2GetMacFromHref(ctx->u.pbkdf2.params.prfAlgorithmHref); if(ctx->u.pbkdf2.mac == GNUTLS_MAC_UNKNOWN) { xmlSecInternalError("xmlSecGnuTLSPbkdf2GetMacFromHref", xmlSecTransformGetName(transform)); return(-1); } /* success */ return(0); } static int xmlSecGnuTLSPbkdf2GenerateKey(xmlSecGnuTLSKdfCtxPtr ctx, xmlSecBufferPtr out) { xmlSecSize size; xmlSecByte * outData; unsigned iterCount; gnutls_datum_t key; gnutls_datum_t salt; int err; int ret; xmlSecAssert2(ctx != NULL, -1); xmlSecAssert2(ctx->kdfType == xmlSecGnuTLSKdfType_Pbkdf2, -1); xmlSecAssert2(ctx->u.pbkdf2.mac != GNUTLS_MAC_UNKNOWN, -1); xmlSecAssert2(ctx->u.pbkdf2.params.keyLength > 0, -1); xmlSecAssert2(out != NULL, -1); ret = xmlSecBufferSetSize(out, ctx->u.pbkdf2.params.keyLength); if(ret < 0) { xmlSecInternalError2("xmlSecBufferSetSize", NULL, "size=" XMLSEC_SIZE_FMT, ctx->u.pbkdf2.params.keyLength); return(-1); } outData = xmlSecBufferGetData(out); xmlSecAssert2(outData != NULL, -1); /* prep params */ size = xmlSecBufferGetSize(&(ctx->key)); XMLSEC_SAFE_CAST_SIZE_TO_UINT(size, key.size, return(-1), NULL); key.data = xmlSecBufferGetData(&(ctx->key)); xmlSecAssert2(key.data != NULL, -1); xmlSecAssert2(key.size > 0, -1); size = xmlSecBufferGetSize(&(ctx->u.pbkdf2.params.salt)); XMLSEC_SAFE_CAST_SIZE_TO_UINT(size, salt.size, return(-1), NULL); salt.data = xmlSecBufferGetData(&(ctx->u.pbkdf2.params.salt)); xmlSecAssert2(salt.data != NULL, -1); xmlSecAssert2(salt.size > 0, -1); XMLSEC_SAFE_CAST_SIZE_TO_UINT(ctx->u.pbkdf2.params.iterationCount, iterCount, return(-1), NULL); xmlSecAssert2(iterCount > 0, -1); /* do the work! */ err = gnutls_pbkdf2(ctx->u.pbkdf2.mac, &key, &salt, iterCount, outData, ctx->u.pbkdf2.params.keyLength); if(err != GNUTLS_E_SUCCESS) { xmlSecGnuTLSError("gnutls_pbkdf2", err, NULL); return(-1); } /* success */ return(0); } /****************************************************************************** * * PBKDF2 key derivation algorithm * *****************************************************************************/ XMLSEC_GNUTLS_KDF_KLASS(Pbkdf2, xmlSecGnuTLSPbkdf2NodeRead) /** * @brief The PBKDF2 key derivation transform klass. * @return the PBKDF2 key derivation transform klass. */ xmlSecTransformId xmlSecGnuTLSTransformPbkdf2GetKlass(void) { return(&xmlSecGnuTLSPbkdf2Klass); } #endif /* XMLSEC_NO_PBKDF2 */ #ifndef XMLSEC_NO_HKDF /****************************************************************************** * * HKDF transform (https://gnutls.org/reference/gnutls-crypto.html#gnutls-hkdf-extract) * *****************************************************************************/ static int xmlSecGnuTLSHkdfNodeRead (xmlSecTransformPtr transform, xmlNodePtr node, xmlSecTransformCtxPtr transformCtx); /* convert PRF algorithm href to GnuTLS mac algo */ static gnutls_mac_algorithm_t xmlSecGnuTLSHkdfGetMacFromHref(const xmlChar* href) { /* PRF is required for HKDF */ if(href == NULL) { xmlSecOtherError(XMLSEC_ERRORS_R_INVALID_ALGORITHM, NULL, "HKDF PRF algorithm is required"); return(GNUTLS_MAC_UNKNOWN); } else #ifndef XMLSEC_NO_SHA1 if(xmlStrcmp(href, xmlSecHrefHmacSha1) == 0) { return(GNUTLS_MAC_SHA1); } else #endif /* XMLSEC_NO_SHA1 */ #ifndef XMLSEC_NO_SHA224 if(xmlStrcmp(href, xmlSecHrefHmacSha224) == 0) { return(GNUTLS_MAC_SHA224); } else #endif /* XMLSEC_NO_SHA224 */ #ifndef XMLSEC_NO_SHA256 if(xmlStrcmp(href, xmlSecHrefHmacSha256) == 0) { return(GNUTLS_MAC_SHA256); } else #endif /* XMLSEC_NO_SHA256 */ #ifndef XMLSEC_NO_SHA384 if(xmlStrcmp(href, xmlSecHrefHmacSha384) == 0) { return(GNUTLS_MAC_SHA384); } else #endif /* XMLSEC_NO_SHA384 */ #ifndef XMLSEC_NO_SHA512 if(xmlStrcmp(href, xmlSecHrefHmacSha512) == 0) { return(GNUTLS_MAC_SHA512); } else #endif /* XMLSEC_NO_SHA512 */ { xmlSecOtherError2(XMLSEC_ERRORS_R_INVALID_ALGORITHM, NULL, "href=%s", xmlSecErrorsSafeString(href)); return(GNUTLS_MAC_UNKNOWN); } } static int xmlSecGnuTLSHkdfNodeRead(xmlSecTransformPtr transform, xmlNodePtr node, xmlSecTransformCtxPtr transformCtx XMLSEC_ATTRIBUTE_UNUSED) { xmlSecGnuTLSKdfCtxPtr ctx; xmlNodePtr cur; int ret; xmlSecAssert2(xmlSecTransformCheckId(transform, xmlSecGnuTLSTransformHkdfId), -1); xmlSecAssert2(xmlSecTransformCheckSize(transform, xmlSecGnuTLSKdfCtxSize), -1); xmlSecAssert2(node != NULL, -1); UNREFERENCED_PARAMETER(transformCtx); ctx = xmlSecGnuTLSKdfGetCtx(transform); xmlSecAssert2(ctx != NULL, -1); xmlSecAssert2(ctx->kdfType == xmlSecGnuTLSKdfType_Hkdf, -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->u.hkdf.params), 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 PRF (required) */ ctx->u.hkdf.mac = xmlSecGnuTLSHkdfGetMacFromHref(ctx->u.hkdf.params.prfAlgorithmHref); if(ctx->u.hkdf.mac == GNUTLS_MAC_UNKNOWN) { xmlSecInternalError("xmlSecGnuTLSHkdfGetMacFromHref", xmlSecTransformGetName(transform)); return(-1); } /* copy salt (optional) */ if(xmlSecBufferGetSize(&(ctx->u.hkdf.params.salt)) > 0) { ret = xmlSecBufferSetData(&(ctx->u.hkdf.salt), xmlSecBufferGetData(&(ctx->u.hkdf.params.salt)), xmlSecBufferGetSize(&(ctx->u.hkdf.params.salt))); if(ret < 0) { xmlSecInternalError("xmlSecBufferSetData(salt)", xmlSecTransformGetName(transform)); return(-1); } } /* copy info (optional) */ if(xmlSecBufferGetSize(&(ctx->u.hkdf.params.info)) > 0) { ret = xmlSecBufferSetData(&(ctx->u.hkdf.info), xmlSecBufferGetData(&(ctx->u.hkdf.params.info)), xmlSecBufferGetSize(&(ctx->u.hkdf.params.info))); if(ret < 0) { xmlSecInternalError("xmlSecBufferSetData(info)", xmlSecTransformGetName(transform)); return(-1); } } /* success */ return(0); } static int xmlSecGnuTLSHkdfGenerateKey(xmlSecGnuTLSKdfCtxPtr ctx, xmlSecSize outLen, xmlSecBufferPtr out) { xmlSecByte * keyData; xmlSecSize keySize; xmlSecByte * saltData; xmlSecSize saltSize; xmlSecByte * infoData; xmlSecSize infoSize; xmlSecByte * outData; xmlSecByte * prk = NULL; xmlSecSize prkLen; gnutls_datum_t keyDatum; gnutls_datum_t saltDatum; gnutls_datum_t infoDatum; int err; int ret; xmlSecAssert2(ctx != NULL, -1); xmlSecAssert2(ctx->kdfType == xmlSecGnuTLSKdfType_Hkdf, -1); xmlSecAssert2(ctx->u.hkdf.mac != GNUTLS_MAC_UNKNOWN, -1); xmlSecAssert2(outLen > 0, -1); xmlSecAssert2(out != NULL, -1); /* get input keying material (IKM) */ keyData = xmlSecBufferGetData(&(ctx->key)); keySize = xmlSecBufferGetSize(&(ctx->key)); if((keyData == NULL) || (keySize == 0)) { xmlSecInvalidZeroKeyDataSizeError(NULL); return(-1); } /* prepare key datum */ XMLSEC_SAFE_CAST_SIZE_TO_UINT(keySize, keyDatum.size, return(-1), NULL); keyDatum.data = keyData; /* get salt (optional) */ saltData = xmlSecBufferGetData(&(ctx->u.hkdf.salt)); saltSize = xmlSecBufferGetSize(&(ctx->u.hkdf.salt)); if((saltData != NULL) && (saltSize > 0)) { XMLSEC_SAFE_CAST_SIZE_TO_UINT(saltSize, saltDatum.size, return(-1), NULL); saltDatum.data = saltData; } else { saltDatum.data = NULL; saltDatum.size = 0; } /* get info (optional) */ infoData = xmlSecBufferGetData(&(ctx->u.hkdf.info)); infoSize = xmlSecBufferGetSize(&(ctx->u.hkdf.info)); if((infoData != NULL) && (infoSize > 0)) { XMLSEC_SAFE_CAST_SIZE_TO_UINT(infoSize, infoDatum.size, return(-1), NULL); infoDatum.data = infoData; } else { infoDatum.data = NULL; infoDatum.size = 0; } /* allocate output buffer */ ret = xmlSecBufferSetSize(out, outLen); if(ret < 0) { xmlSecInternalError2("xmlSecBufferSetSize", NULL, "size=" XMLSEC_SIZE_FMT, outLen); return(-1); } outData = xmlSecBufferGetData(out); xmlSecAssert2(outData != NULL, -1); /* get PRK length for this MAC */ prkLen = (xmlSecSize)gnutls_hmac_get_len(ctx->u.hkdf.mac); if(prkLen == 0) { xmlSecGnuTLSError("gnutls_hmac_get_len", GNUTLS_E_SUCCESS, NULL); return(-1); } /* allocate PRK buffer */ prk = (xmlSecByte *)xmlMalloc(prkLen); if(prk == NULL) { xmlSecMallocError(prkLen, NULL); return(-1); } /* HKDF-Extract: PRK = HMAC-Hash(salt, IKM) * gnutls_hkdf_extract(mac, key, salt, output) */ err = gnutls_hkdf_extract(ctx->u.hkdf.mac, &keyDatum, &saltDatum, prk); if(err != GNUTLS_E_SUCCESS) { xmlSecGnuTLSError("gnutls_hkdf_extract", err, NULL); memset(prk, 0, prkLen); xmlFree(prk); return(-1); } /* HKDF-Expand: OKM = HKDF-Expand(PRK, info, L) * gnutls_hkdf_expand(mac, key, info, output, length) */ { gnutls_datum_t prkDatum; XMLSEC_SAFE_CAST_SIZE_TO_UINT(prkLen, prkDatum.size, memset(prk, 0, prkLen); xmlFree(prk); return(-1), NULL); prkDatum.data = prk; err = gnutls_hkdf_expand(ctx->u.hkdf.mac, &prkDatum, &infoDatum, outData, outLen); } if(err != GNUTLS_E_SUCCESS) { xmlSecGnuTLSError("gnutls_hkdf_expand", err, NULL); memset(prk, 0, prkLen); xmlFree(prk); return(-1); } /* clean up PRK */ memset(prk, 0, prkLen); xmlFree(prk); /* success */ return(0); } /****************************************************************************** * * HKDF key derivation algorithm * *****************************************************************************/ XMLSEC_GNUTLS_KDF_KLASS(Hkdf, xmlSecGnuTLSHkdfNodeRead) /** * @brief The HKDF key derivation transform klass. * @return the HKDF key derivation transform klass. */ xmlSecTransformId xmlSecGnuTLSTransformHkdfGetKlass(void) { return(&xmlSecGnuTLSHkdfKlass); } #endif /* XMLSEC_NO_HKDF */ /****************************************************************************** * * Common KDF Execute * *****************************************************************************/ static int xmlSecGnuTLSKdfExecute(xmlSecTransformPtr transform, int last, xmlSecTransformCtxPtr transformCtx) { xmlSecGnuTLSKdfCtxPtr ctx; xmlSecBufferPtr in, out; int ret; xmlSecAssert2(xmlSecTransformIsValid(transform), -1); xmlSecAssert2(((transform->operation == xmlSecTransformOperationEncrypt) || (transform->operation == xmlSecTransformOperationDecrypt)), -1); xmlSecAssert2(xmlSecTransformCheckSize(transform, xmlSecGnuTLSKdfCtxSize), -1); xmlSecAssert2(transformCtx != NULL, -1); in = &(transform->inBuf); out = &(transform->outBuf); ctx = xmlSecGnuTLSKdfGetCtx(transform); xmlSecAssert2(ctx != NULL, -1); if(transform->status == xmlSecTransformStatusNone) { transform->status = xmlSecTransformStatusWorking; } if((transform->status == xmlSecTransformStatusWorking) && (last == 0)) { /* do nothing */ } else if((transform->status == xmlSecTransformStatusWorking) && (last != 0)) { xmlSecSize expectedOutputSize; /* verify output size */ if(transform->expectedOutputSize <= 0) { xmlSecOtherError(XMLSEC_ERRORS_R_INVALID_ALGORITHM, NULL, "KDF output key size is not specified"); return(-1); } expectedOutputSize = transform->expectedOutputSize; /* execute KDF-specific key generation */ if(0) { #ifndef XMLSEC_NO_CONCATKDF } else if(ctx->kdfType == xmlSecGnuTLSKdfType_ConcatKdf) { ret = xmlSecGnuTLSConcatKdfGenerateKey(ctx, expectedOutputSize, out); if(ret < 0) { xmlSecInternalError("xmlSecGnuTLSConcatKdfGenerateKey", xmlSecTransformGetName(transform)); return(-1); } #endif /* XMLSEC_NO_CONCATKDF */ #ifndef XMLSEC_NO_PBKDF2 } else if(ctx->kdfType == xmlSecGnuTLSKdfType_Pbkdf2) { /* PBKDF2 may have keyLength in params, verify it matches */ if((ctx->u.pbkdf2.params.keyLength > 0) && (ctx->u.pbkdf2.params.keyLength != expectedOutputSize)){ xmlSecInvalidSizeError("Output kdf size doesn't match expected", expectedOutputSize, ctx->u.pbkdf2.params.keyLength, xmlSecTransformGetName(transform)); return(-1); } ctx->u.pbkdf2.params.keyLength = expectedOutputSize; ret = xmlSecGnuTLSPbkdf2GenerateKey(ctx, out); if(ret < 0) { xmlSecInternalError("xmlSecGnuTLSPbkdf2GenerateKey", xmlSecTransformGetName(transform)); return(-1); } #endif /* XMLSEC_NO_PBKDF2 */ #ifndef XMLSEC_NO_HKDF } else if(ctx->kdfType == xmlSecGnuTLSKdfType_Hkdf) { /* HKDF may have keyLength in params, verify it matches */ if((ctx->u.hkdf.params.keyLength > 0) && (ctx->u.hkdf.params.keyLength != expectedOutputSize)){ xmlSecInvalidSizeError("Output kdf size doesn't match expected", expectedOutputSize, ctx->u.hkdf.params.keyLength, xmlSecTransformGetName(transform)); return(-1); } ret = xmlSecGnuTLSHkdfGenerateKey(ctx, expectedOutputSize, out); if(ret < 0) { xmlSecInternalError("xmlSecGnuTLSHkdfGenerateKey", xmlSecTransformGetName(transform)); return(-1); } #endif /* XMLSEC_NO_HKDF */ } else { xmlSecInvalidTransfromError(transform); return(-1); } 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); }