/
githubmirror
/
xmlsec
Обзор
Документация
Войти
/
githubmirror
/
xmlsec
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
src/openssl/key_agrmnt.c
566 строк
21 KB
lsh123
Refactor KeyAgreement processing to cache key lookup results when signing / encrypting data (#1145)
22 апр 2026, 20:01
Не верифицирован
22 апр 2026, 20:01
6e931bd
Код
Авторство
О чём код?
/** * 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_openssl_crypto * @brief Key agreement transforms implementation for OpenSSL. */ #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/membuf.h> #include <xmlsec/private.h> #include <xmlsec/xmltree.h> #include <xmlsec/openssl/crypto.h> #include <xmlsec/openssl/evp.h> #include "openssl_compat.h" #include "../cast_helpers.h" #include "../keysdata_helpers.h" #include "../transform_helpers.h" /****************************************************************************** * * Generic Key Agreement Framework * - Unified implementation for ECDH, DH, and XDH (X25519/X448) key agreements * *****************************************************************************/ typedef struct _xmlSecOpenSSLKeyAgreementCtx xmlSecOpenSSLKeyAgreementCtx, *xmlSecOpenSSLKeyAgreementCtxPtr; struct _xmlSecOpenSSLKeyAgreementCtx { xmlSecTransformKAM params; xmlSecKeyDataId keyDataId; /* Key data type (EC, DH, or XDH) */ size_t expected_secret_len; /* Expected secret length (0 = dynamic) */ }; /* Unified transform functions */ static int xmlSecOpenSSLKeyAgreementInitialize (xmlSecTransformPtr transform); static void xmlSecOpenSSLKeyAgreementFinalize (xmlSecTransformPtr transform); static int xmlSecOpenSSLKeyAgreementSetKeyReq (xmlSecTransformPtr transform, xmlSecKeyReqPtr keyReq); static int xmlSecOpenSSLKeyAgreementSetKey (xmlSecTransformPtr transform, xmlSecKeyPtr key); static int xmlSecOpenSSLKeyAgreementNodeRead (xmlSecTransformPtr transform, xmlNodePtr node, xmlSecTransformCtxPtr transformCtx); static int xmlSecOpenSSLKeyAgreementNodeWrite (xmlSecTransformPtr transform, xmlNodePtr node, xmlSecTransformCtxPtr transformCtx); static int xmlSecOpenSSLKeyAgreementExecute (xmlSecTransformPtr transform, int last, xmlSecTransformCtxPtr transformCtx); /* Helper functions */ static int xmlSecOpenSSLKeyAgreementGenerateSecret (xmlSecOpenSSLKeyAgreementCtxPtr ctx, xmlSecTransformOperation operation, xmlSecKeyDataPtr kamKeyData, xmlSecBufferPtr secret); /****************************************************************************** * * Key Agreement transforms - unified context structure * *****************************************************************************/ XMLSEC_TRANSFORM_DECLARE(OpenSSLKeyAgreement, xmlSecOpenSSLKeyAgreementCtx) #define xmlSecOpenSSLKeyAgreementSize XMLSEC_TRANSFORM_SIZE(OpenSSLKeyAgreement) /****************************************************************************** * * Unified transform lifecycle functions (used by ECDH, DH, and XDH) * *****************************************************************************/ static int xmlSecOpenSSLKeyAgreementInitialize(xmlSecTransformPtr transform) { xmlSecOpenSSLKeyAgreementCtxPtr ctx; int ret; xmlSecAssert2(xmlSecTransformIsValid(transform), -1); xmlSecAssert2(xmlSecTransformCheckSize(transform, xmlSecOpenSSLKeyAgreementSize), -1); ctx = xmlSecOpenSSLKeyAgreementGetCtx(transform); xmlSecAssert2(ctx != NULL, -1); /* initialize context */ memset(ctx, 0, sizeof(xmlSecOpenSSLKeyAgreementCtx)); /* initialize algorithm-specific configuration */ #ifndef XMLSEC_NO_EC if(xmlSecTransformCheckId(transform, xmlSecOpenSSLTransformEcdhId)) { ctx->keyDataId = xmlSecOpenSSLKeyDataEcId; ctx->expected_secret_len = 0; /* dynamic */ } else #endif /* XMLSEC_NO_EC */ #ifndef XMLSEC_NO_DH if(xmlSecTransformCheckId(transform, xmlSecOpenSSLTransformDhEsId)) { ctx->keyDataId = xmlSecOpenSSLKeyDataDhId; ctx->expected_secret_len = 0; /* dynamic */ } else #endif /* XMLSEC_NO_DH */ #ifndef XMLSEC_NO_XDH if(xmlSecTransformCheckId(transform, xmlSecOpenSSLTransformX25519Id)) { ctx->keyDataId = xmlSecOpenSSLKeyDataXdhId; ctx->expected_secret_len = 32; } else if(xmlSecTransformCheckId(transform, xmlSecOpenSSLTransformX448Id)) { ctx->keyDataId = xmlSecOpenSSLKeyDataXdhId; ctx->expected_secret_len = 56; } else #endif /* XMLSEC_NO_XDH */ { xmlSecInternalError("Unknown key agreement transform", xmlSecTransformGetName(transform)); xmlSecOpenSSLKeyAgreementFinalize(transform); return(-1); } ret = xmlSecTransformKAMInitialize(&(ctx->params)); if(ret < 0) { xmlSecInternalError("xmlSecTransformKAMInitialize", xmlSecTransformGetName(transform)); xmlSecOpenSSLKeyAgreementFinalize(transform); return(-1); } /* done */ return(0); } static void xmlSecOpenSSLKeyAgreementFinalize(xmlSecTransformPtr transform) { xmlSecOpenSSLKeyAgreementCtxPtr ctx; xmlSecAssert(xmlSecTransformIsValid(transform)); xmlSecAssert(xmlSecTransformCheckSize(transform, xmlSecOpenSSLKeyAgreementSize)); ctx = xmlSecOpenSSLKeyAgreementGetCtx(transform); xmlSecAssert(ctx != NULL); xmlSecTransformKAMFinalize(&(ctx->params)); memset(ctx, 0, sizeof(xmlSecOpenSSLKeyAgreementCtx)); } static int xmlSecOpenSSLKeyAgreementSetKeyReq(xmlSecTransformPtr transform, xmlSecKeyReqPtr keyReq) { xmlSecOpenSSLKeyAgreementCtxPtr ctx; xmlSecAssert2(xmlSecTransformIsValid(transform), -1); xmlSecAssert2(xmlSecTransformCheckSize(transform, xmlSecOpenSSLKeyAgreementSize), -1); xmlSecAssert2(keyReq != NULL, -1); ctx = xmlSecOpenSSLKeyAgreementGetCtx(transform); xmlSecAssert2(ctx != NULL, -1); keyReq->keyId = ctx->keyDataId; keyReq->keyType = xmlSecKeyDataTypePrivate; keyReq->keyUsage = xmlSecKeyUsageKeyAgreement; return(0); } static int xmlSecOpenSSLKeyAgreementSetKey(xmlSecTransformPtr transform, xmlSecKeyPtr key) { xmlSecAssert2(xmlSecTransformIsValid(transform), -1); xmlSecAssert2(xmlSecTransformCheckSize(transform, xmlSecOpenSSLKeyAgreementSize), -1); xmlSecAssert2(key != NULL, -1); /* key agreement uses two keys from ctxTransform->extraKeyData (KAM key data) */ return(0); } static int xmlSecOpenSSLKeyAgreementNodeRead(xmlSecTransformPtr transform, xmlNodePtr node, xmlSecTransformCtxPtr transformCtx) { xmlSecOpenSSLKeyAgreementCtxPtr ctx; int ret; xmlSecAssert2(xmlSecTransformIsValid(transform), -1); xmlSecAssert2(xmlSecTransformCheckSize(transform, xmlSecOpenSSLKeyAgreementSize), -1); xmlSecAssert2(node != NULL, -1); xmlSecAssert2(transformCtx != NULL, -1); ctx = xmlSecOpenSSLKeyAgreementGetCtx(transform); xmlSecAssert2(ctx != NULL, -1); ret = xmlSecTransformKAMRead(&(ctx->params), node, transform, transformCtx); if(ret < 0) { xmlSecInternalError("xmlSecTransformKAMRead", xmlSecTransformGetName(transform)); return(-1); } /* done */ return(0); } static int xmlSecOpenSSLKeyAgreementNodeWrite(xmlSecTransformPtr transform, xmlNodePtr node, xmlSecTransformCtxPtr transformCtx) { xmlSecOpenSSLKeyAgreementCtxPtr ctx; int ret; xmlSecAssert2(xmlSecTransformIsValid(transform), -1); xmlSecAssert2(xmlSecTransformCheckSize(transform, xmlSecOpenSSLKeyAgreementSize), -1); xmlSecAssert2(node != NULL, -1); xmlSecAssert2(transformCtx != NULL, -1); ctx = xmlSecOpenSSLKeyAgreementGetCtx(transform); xmlSecAssert2(ctx != NULL, -1); ret = xmlSecTransformKAMWrite(&(ctx->params), node, transform, transformCtx); if(ret < 0) { xmlSecInternalError("xmlSecTransformKAMWrite", xmlSecTransformGetName(transform)); return(-1); } return(0); } static int xmlSecOpenSSLKeyAgreementExecute(xmlSecTransformPtr transform, int last, xmlSecTransformCtxPtr transformCtx) { xmlSecOpenSSLKeyAgreementCtxPtr ctx; xmlSecKeyDataPtr kamKeyData; xmlSecBufferPtr in, out; int ret; xmlSecAssert2(xmlSecTransformIsValid(transform), -1); xmlSecAssert2(((transform->operation == xmlSecTransformOperationEncrypt) || (transform->operation == xmlSecTransformOperationDecrypt)), -1); xmlSecAssert2(transformCtx != NULL, -1); in = &(transform->inBuf); out = &(transform->outBuf); ctx = xmlSecOpenSSLKeyAgreementGetCtx(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)) { xmlSecBuffer secret; ret = xmlSecBufferInitialize(&secret, 128); if(ret < 0) { xmlSecInternalError("xmlSecBufferInitialize", xmlSecTransformGetName(transform)); return(-1); } /* Step 1: derive shared secret (keyOriginator required) */ kamKeyData = xmlSecTransformCtxExtraKeyDataGet(transformCtx, xmlSecKeyDataKAMId); if(kamKeyData == NULL) { xmlSecInternalError("xmlSecTransformCtxExtraKeyDataGet", xmlSecTransformGetName(transform)); xmlSecBufferFinalize(&secret); return(-1); } ret = xmlSecOpenSSLKeyAgreementGenerateSecret(ctx, transform->operation, kamKeyData, &secret); if(ret < 0) { xmlSecInternalError("xmlSecOpenSSLKeyAgreementGenerateSecret", xmlSecTransformGetName(transform)); /* Securely clear secret before finalize */ xmlSecBufferEmpty(&secret); xmlSecBufferFinalize(&secret); return(-1); } /* step 2: generate key with kdf from secret */ ret = xmlSecTransformKAMExecuteKdf(&(ctx->params), transform->operation, &secret, out, transform->expectedOutputSize, transformCtx); if(ret < 0) { xmlSecInternalError("xmlSecTransformKAMExecuteKdf", xmlSecTransformGetName(transform)); /* Securely clear secret before finalize */ xmlSecBufferEmpty(&secret); xmlSecBufferFinalize(&secret); return(-1); } /* Securely clear secret before finalize */ xmlSecBufferEmpty(&secret); xmlSecBufferFinalize(&secret); 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); } /* Generate shared secret using key agreement (unified for ECDH/DH/XDH) */ static int xmlSecOpenSSLKeyAgreementGenerateSecret(xmlSecOpenSSLKeyAgreementCtxPtr ctx, xmlSecTransformOperation operation, xmlSecKeyDataPtr kamKeyData, xmlSecBufferPtr secret) { xmlSecKeyDataKAM* kamData; EVP_PKEY_CTX *pKeyCtx = NULL; xmlSecKeyDataPtr myKeyValue, otherKeyValue; EVP_PKEY *myPrivKey; EVP_PKEY *otherPubKey; size_t secret_len = 0; xmlSecByte * secretData; xmlSecSize secretSize; int ret; int res = -1; xmlSecAssert2(ctx != NULL, -1); xmlSecAssert2(kamKeyData != NULL, -1); xmlSecAssert2(xmlSecKeyDataCheckId(kamKeyData, xmlSecKeyDataKAMId), -1); xmlSecAssert2(secret != NULL, -1); kamData = (xmlSecKeyDataKAM*)kamKeyData; xmlSecAssert2(kamData != NULL, -1); xmlSecAssert2(kamData->keyOriginator != NULL, -1); xmlSecAssert2(kamData->keyRecipient != NULL, -1); /* get key values */ if(operation == xmlSecTransformOperationEncrypt) { /* encrypting on originator side who needs priv key */ myKeyValue = xmlSecKeyGetValue(kamData->keyOriginator); if(myKeyValue == NULL) { xmlSecInternalError("xmlSecKeyGetValue(keyOriginator)", NULL); goto done; } otherKeyValue = xmlSecKeyGetValue(kamData->keyRecipient); if(otherKeyValue == NULL) { xmlSecInternalError("xmlSecKeyGetValue(keyRecipient)", NULL); goto done; } } else { /* decrypting on recipient side who needs priv key */ myKeyValue = xmlSecKeyGetValue(kamData->keyRecipient); if(myKeyValue == NULL) { xmlSecInternalError("xmlSecKeyGetValue(keyRecipient)", NULL); goto done; } otherKeyValue = xmlSecKeyGetValue(kamData->keyOriginator); if(otherKeyValue == NULL) { xmlSecInternalError("xmlSecKeyGetValue(keyOriginator)", NULL); goto done; } } /* get pkeys - use generic EVP getter that works for all key types */ myPrivKey = xmlSecOpenSSLEvpKeyDataGetEvp(myKeyValue); if(myPrivKey == NULL) { xmlSecInternalError("xmlSecOpenSSLEvpKeyDataGetEvp(myKey)", NULL); goto done; } otherPubKey = xmlSecOpenSSLEvpKeyDataGetEvp(otherKeyValue); if(otherPubKey == NULL) { xmlSecInternalError("xmlSecOpenSSLEvpKeyDataGetEvp(otherKey)", NULL); goto done; } /* create and init pkey ctx */ #ifndef XMLSEC_OPENSSL_API_300 pKeyCtx = EVP_PKEY_CTX_new(myPrivKey, NULL); if(pKeyCtx == NULL) { xmlSecOpenSSLError("EVP_PKEY_CTX_new", NULL); goto done; } #else /* XMLSEC_OPENSSL_API_300 */ pKeyCtx = EVP_PKEY_CTX_new_from_pkey(xmlSecOpenSSLGetLibCtx(), myPrivKey, NULL); if(pKeyCtx == NULL) { xmlSecOpenSSLError("EVP_PKEY_CTX_new_from_pkey", NULL); goto done; } #endif /* XMLSEC_OPENSSL_API_300 */ ret = EVP_PKEY_derive_init(pKeyCtx); if(ret != 1) { xmlSecOpenSSLError("EVP_PKEY_derive_init", NULL); goto done; } ret = EVP_PKEY_derive_set_peer(pKeyCtx, otherPubKey); if(ret != 1) { xmlSecOpenSSLError("EVP_PKEY_derive_set_peer", NULL); goto done; } /* determine output buffer size */ ret = EVP_PKEY_derive(pKeyCtx, NULL, &secret_len); if((ret != 1) || (secret_len == 0)) { xmlSecOpenSSLError("EVP_PKEY_derive", NULL); goto done; } /* Validate secret size matches expected value (if specified) */ if((ctx->expected_secret_len != 0) && (secret_len != ctx->expected_secret_len)) { xmlSecInvalidSizeDataError("EVP_PKEY_derive secret size", secret_len, "expected", NULL); goto done; } /* allocate buffer */ XMLSEC_SAFE_CAST_SIZE_T_TO_SIZE(secret_len, secretSize, goto done, NULL); ret = xmlSecBufferSetSize(secret, secretSize); if(ret < 0) { xmlSecInternalError2("xmlSecBufferSetSize", NULL, "size=" XMLSEC_SIZE_FMT, secretSize); goto done; } secretData = xmlSecBufferGetData(secret); if(secretData == NULL) { xmlSecInternalError("xmlSecBufferGetData", NULL); goto done; } /* derive the shared secret */ ret = EVP_PKEY_derive(pKeyCtx, secretData, &secret_len); if((ret != 1) || (secret_len == 0)) { xmlSecOpenSSLError("EVP_PKEY_derive", NULL); /* Clear partial secret data on error */ xmlSecBufferEmpty(secret); goto done; } /* success */ res = 0; done: if(pKeyCtx != NULL) { EVP_PKEY_CTX_free(pKeyCtx); } /* Clear secret buffer on error path */ if((res != 0) && (secret != NULL)) { xmlSecBufferEmpty(secret); } return(res); } /* Helper macros to define the key agreement transform klass */ #define XMLSEC_OPENSSL_KEY_AGREEMENT_KLASS_EX(name, transformName, transformHref) \ static xmlSecTransformKlass xmlSecOpenSSL ## name ## Klass = { \ sizeof(xmlSecTransformKlass), /* xmlSecSize klassSize */ \ xmlSecOpenSSLKeyAgreementSize, /* xmlSecSize objSize */ \ transformName, /* const xmlChar* name; */ \ transformHref, /* const xmlChar* href; */ \ xmlSecTransformUsageAgreementMethod, /* xmlSecTransformUsage usage; */ \ xmlSecOpenSSLKeyAgreementInitialize, /* xmlSecTransformInitializeMethod initialize; */ \ xmlSecOpenSSLKeyAgreementFinalize, /* xmlSecTransformFinalizeMethod finalize; */ \ xmlSecOpenSSLKeyAgreementNodeRead, /* xmlSecTransformNodeReadMethod readNode; */ \ xmlSecOpenSSLKeyAgreementNodeWrite, /* xmlSecTransformNodeWriteMethod writeNode; */ \ xmlSecOpenSSLKeyAgreementSetKeyReq, /* xmlSecTransformSetKeyReqMethod setKeyReq; */ \ xmlSecOpenSSLKeyAgreementSetKey, /* xmlSecTransformSetKeyMethod setKey; */ \ NULL, /* xmlSecTransformValidateMethod validate; */ \ xmlSecTransformDefaultGetDataType, /* xmlSecTransformGetDataTypeMethod getDataType; */ \ xmlSecTransformDefaultPushBin, /* xmlSecTransformPushBinMethod pushBin; */ \ xmlSecTransformDefaultPopBin, /* xmlSecTransformPopBinMethod popBin; */ \ NULL, /* xmlSecTransformPushXmlMethod pushXml; */ \ NULL, /* xmlSecTransformPopXmlMethod popXml; */ \ xmlSecOpenSSLKeyAgreementExecute, /* xmlSecTransformExecuteMethod execute; */ \ NULL, /* void* reserved0; */ \ NULL, /* void* reserved1; */ \ }; #define XMLSEC_OPENSSL_KEY_AGREEMENT_KLASS(name) \ XMLSEC_OPENSSL_KEY_AGREEMENT_KLASS_EX(name, xmlSecName ## name, xmlSecHref ## name) #ifndef XMLSEC_NO_EC /****************************************************************************** * * ECDH KeyAgreement context. * - OpenSSL doc: https://wiki.openssl.org/index.php/Elliptic_Curve_Diffie_Hellman * - XMLEnc spec: https://www.w3.org/TR/xmlenc-core1/#sec-ECDH-ES * *****************************************************************************/ /****************************************************************************** * * Ecdh key derivation algorithm * *****************************************************************************/ XMLSEC_OPENSSL_KEY_AGREEMENT_KLASS(Ecdh) /** * @brief The ECDH key agreement transform klass. * @return the ECDH key agreement transform klass. */ xmlSecTransformId xmlSecOpenSSLTransformEcdhGetKlass(void) { return(&xmlSecOpenSSLEcdhKlass); } #endif /* XMLSEC_NO_EC */ #ifndef XMLSEC_NO_DH /****************************************************************************** * * DH KeyAgreement context. * - OpenSSL doc: https://wiki.openssl.org/index.php/Diffie_Hellman * - XMLEnc spec: https://www.w3.org/TR/xmlenc-core1/#sec-DHKeyAgreementExplicitKDF * *****************************************************************************/ /****************************************************************************** * * Dh key derivation algorithm * *****************************************************************************/ XMLSEC_OPENSSL_KEY_AGREEMENT_KLASS_EX(Dh, xmlSecNameDhEs, xmlSecHrefDhEs) /** * @brief The DH key agreement transform klass. * @return the DH key agreement transform klass. */ xmlSecTransformId xmlSecOpenSSLTransformDhEsGetKlass(void) { return(&xmlSecOpenSSLDhKlass); } #endif /* XMLSEC_NO_DH */ #ifndef XMLSEC_NO_XDH /****************************************************************************** * * XDH KeyAgreement context (X25519 and X448) * *****************************************************************************/ /* X25519 Transform Klass */ XMLSEC_OPENSSL_KEY_AGREEMENT_KLASS(X25519) /** * @brief The X25519 key agreement transform klass. * @return the X25519 key agreement transform klass. */ xmlSecTransformId xmlSecOpenSSLTransformX25519GetKlass(void) { return(&xmlSecOpenSSLX25519Klass); } /* X448 Transform Klass */ XMLSEC_OPENSSL_KEY_AGREEMENT_KLASS(X448) /** * @brief The X448 key agreement transform klass. * @return the X448 key agreement transform klass. */ xmlSecTransformId xmlSecOpenSSLTransformX448GetKlass(void) { return(&xmlSecOpenSSLX448Klass); } #endif /* XMLSEC_NO_XDH */