/
githubmirror
/
xmlsec
Обзор
Документация
Войти
/
githubmirror
/
xmlsec
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
src/mscng/certkeys.c
2 441 строка
77 KB
Javid Khan
fix double-free in mscng dh key adoption on SetDhQ failure (#1183)
28 май 2026, 16:28
Не верифицирован
28 май 2026, 16:28
639b1eb
Код
Авторство
О чём код?
/** * 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. * Copyright (C) 2018 Miklos Vajna. All Rights Reserved. */ /** * @addtogroup xmlsec_mscng_certkeys * @brief Certificate keys support functions for Microsoft Cryptography API: Next Generation (CNG). */ #include "globals.h" #include <string.h> #include <xmlsec/xmlsec.h> #include <xmlsec/base64.h> #include <xmlsec/bn.h> #include <xmlsec/errors.h> #include <xmlsec/keyinfo.h> #include <xmlsec/keys.h> #include <xmlsec/transforms.h> #include <xmlsec/xmltree.h> #include <xmlsec/private.h> #include <xmlsec/mscng/certkeys.h> #include <xmlsec/mscng/crypto.h> #include "../cast_helpers.h" #include "../keysdata_helpers.h" #include "private.h" typedef struct _xmlSecMSCngKeyDataCtx xmlSecMSCngKeyDataCtx, *xmlSecMSCngKeyDataCtxPtr; struct _xmlSecMSCngKeyDataCtx { PCCERT_CONTEXT cert; NCRYPT_KEY_HANDLE privkey; BCRYPT_KEY_HANDLE pubkey; BOOL privkeyNeedsFree; BCRYPT_KEY_HANDLE bcryptPrivkey; /* BCrypt DH private key (loaded from DER/PKCS8) */ xmlSecBuffer dhQ; }; XMLSEC_KEY_DATA_DECLARE(MSCngKeyData, xmlSecMSCngKeyDataCtx) #define xmlSecMSCngKeyDataSize XMLSEC_KEY_DATA_SIZE(MSCngKeyData) int xmlSecMSCngKeyDataCertGetPubkey(PCERT_PUBLIC_KEY_INFO spki, BCRYPT_KEY_HANDLE* key) { xmlSecAssert2(spki != NULL, -1); xmlSecAssert2(key != NULL, -1); /* Try the standard API first: works for RSA, EC, and DSA up to 1024-bit. * For DSA > 1024-bit it fails (E_INVALIDARG) due to legacy CryptoAPI limits. */ if(CryptImportPublicKeyInfoEx2(X509_ASN_ENCODING, spki, 0, NULL, key)) { return(0); } #ifndef XMLSEC_NO_DSA /* CryptImportPublicKeyInfoEx2 fails for DSA > 1024-bit. For large DSA keys * fall back to BCryptImportKeyPair with a manually constructed blob. */ if((spki->Algorithm.pszObjId != NULL) && (strcmp(spki->Algorithm.pszObjId, szOID_X957_DSA) == 0)) { return(xmlSecMSCngKeyDataCertGetDsaPubkey(spki, key)); } #endif /* XMLSEC_NO_DSA */ #ifndef XMLSEC_NO_XDH /* X25519 (OID 1.3.101.110) uses a 32-byte raw u-coordinate in SubjectPublicKey; * CryptImportPublicKeyInfoEx2 does not support this OID. */ if((spki->Algorithm.pszObjId != NULL) && (strcmp(spki->Algorithm.pszObjId, szOID_X25519) == 0)) { return(xmlSecMSCngKeyDataCertGetXdhPubkey(spki, key)); } #endif /* XMLSEC_NO_XDH */ xmlSecMSCngLastError("CryptImportPublicKeyInfoEx2", NULL); return(-1); } static int xmlSecMSCngKeyDataCertGetPrivkey(PCCERT_CONTEXT cert, NCRYPT_KEY_HANDLE* key, BOOL* needsFree) { int ret; xmlSecAssert2(cert != NULL, -1); xmlSecAssert2(key != NULL, -1); xmlSecAssert2(needsFree != NULL, -1); /* try non persistent key */ CERT_KEY_CONTEXT ckc; DWORD dwCkcLen = sizeof(ckc); if (CertGetCertificateContextProperty(cert, CERT_KEY_CONTEXT_PROP_ID, &ckc, &dwCkcLen)) { (*key) = ckc.hNCryptKey; (*needsFree) = FALSE; /* this key doesnt need NCryptFreeObject */ return(0); } /* try persistent key */ DWORD dwData = 0; DWORD dwDataLen = sizeof(dwData); if (CertGetCertificateContextProperty(cert, CERT_KEY_SPEC_PROP_ID, &dwData, &dwDataLen)) { DWORD keySpec = 0; BOOL fCallerFreeProvOrNCryptKey = FALSE; ret = CryptAcquireCertificatePrivateKey( cert, CRYPT_ACQUIRE_COMPARE_KEY_FLAG | CRYPT_ACQUIRE_ONLY_NCRYPT_KEY_FLAG, NULL, key, &keySpec, &fCallerFreeProvOrNCryptKey); if (ret == FALSE) { xmlSecMSCngLastError("CryptAcquireCertificatePrivateKey", NULL); return(-1); } (*needsFree) = TRUE; return(0); } /* no luck */ xmlSecMSCngLastError("CertGetCertificateContextProperty(): cert doesn't have private key", NULL); return(-1); } /** * @brief Sets the value of key data. * @param data the pointer to MSCng pccert data. * @param cert the pointer to PCCERT key. * @param type the certificate type (trusted/untrusted). * @return 0 on success or a negative value otherwise. */ static int xmlSecMSCngKeyDataAdoptCert(xmlSecKeyDataPtr data, PCCERT_CONTEXT cert, xmlSecKeyDataType type) { xmlSecMSCngKeyDataCtxPtr ctx; BCRYPT_KEY_HANDLE hPubKey; int ret; xmlSecAssert2(xmlSecKeyDataIsValid(data), -1); xmlSecAssert2(xmlSecKeyDataCheckSize(data, xmlSecMSCngKeyDataSize), -1); xmlSecAssert2(cert != NULL, -1); xmlSecAssert2(cert->pCertInfo != NULL, -1); xmlSecAssert2((type & (xmlSecKeyDataTypePublic | xmlSecKeyDataTypePrivate)) != 0, -1); ctx = xmlSecMSCngKeyDataGetCtx(data); xmlSecAssert2(ctx != NULL, -1); xmlSecAssert2(ctx->pubkey == NULL, -1); xmlSecAssert2(ctx->cert == NULL, -1); /* acquire the CNG key handle from the certificate */ if((type & xmlSecKeyDataTypePrivate) != 0) { NCRYPT_KEY_HANDLE hPrivKey = 0; BOOL needsFree = TRUE; ret = xmlSecMSCngKeyDataCertGetPrivkey(cert, &hPrivKey, &needsFree); if(ret < 0) { xmlSecInternalError("xmlSecMSCngKeyDataCertGetPrivkey", NULL); return(-1); } ctx->privkey = hPrivKey; ctx->privkeyNeedsFree = needsFree; } ret = xmlSecMSCngKeyDataCertGetPubkey(&(cert->pCertInfo->SubjectPublicKeyInfo), &hPubKey); if(ret < 0) { xmlSecInternalError("xmlSecMSCngKeyDataCertGetPubkey", NULL); return(-1); } ctx->pubkey = hPubKey; ctx->cert = cert; return(0); } int xmlSecMSCngKeyDataAdoptKey(xmlSecKeyDataPtr data, BCRYPT_KEY_HANDLE hPubKey) { xmlSecMSCngKeyDataCtxPtr ctx; xmlSecAssert2(xmlSecKeyDataIsValid(data), -1); xmlSecAssert2(xmlSecKeyDataCheckSize(data, xmlSecMSCngKeyDataSize), -1); xmlSecAssert2(hPubKey != NULL, -1); ctx = xmlSecMSCngKeyDataGetCtx(data); xmlSecAssert2(ctx != NULL, -1); xmlSecAssert2(ctx->pubkey == NULL, -1); ctx->pubkey = hPubKey; return(0); } int xmlSecMSCngKeyDataAdoptBCryptPrivKey(xmlSecKeyDataPtr data, BCRYPT_KEY_HANDLE hKey) { xmlSecMSCngKeyDataCtxPtr ctx; xmlSecAssert2(xmlSecKeyDataIsValid(data), -1); xmlSecAssert2(xmlSecKeyDataCheckSize(data, xmlSecMSCngKeyDataSize), -1); xmlSecAssert2(hKey != NULL, -1); ctx = xmlSecMSCngKeyDataGetCtx(data); xmlSecAssert2(ctx != NULL, -1); xmlSecAssert2(ctx->bcryptPrivkey == NULL, -1); ctx->bcryptPrivkey = hKey; return(0); } /** * @brief BCrypt DH private key retrieval (for keys loaded from DER/PKCS8). * @param data the key data. * * The returned key must not be destroyed by the caller. * * @return key handle on success or NULL otherwise. */ BCRYPT_KEY_HANDLE xmlSecMSCngKeyDataGetBCryptPrivKey(xmlSecKeyDataPtr data) { xmlSecMSCngKeyDataCtxPtr ctx; xmlSecAssert2(xmlSecKeyDataIsValid(data), NULL); xmlSecAssert2(xmlSecKeyDataCheckSize(data, xmlSecMSCngKeyDataSize), NULL); ctx = xmlSecMSCngKeyDataGetCtx(data); xmlSecAssert2(ctx != NULL, NULL); return(ctx->bcryptPrivkey); } #ifndef XMLSEC_NO_DH int xmlSecMSCngKeyDataSetDhQ(xmlSecKeyDataPtr data, const xmlSecByte* q, DWORD qLen) { xmlSecMSCngKeyDataCtxPtr ctx; xmlSecSize qSize; int ret; xmlSecAssert2(xmlSecKeyDataIsValid(data), -1); xmlSecAssert2(xmlSecKeyDataCheckSize(data, xmlSecMSCngKeyDataSize), -1); xmlSecAssert2(xmlSecKeyDataCheckId(data, xmlSecMSCngKeyDataDhId), -1); ctx = xmlSecMSCngKeyDataGetCtx(data); xmlSecAssert2(ctx != NULL, -1); if((q == NULL) || (qLen == 0)) { xmlSecBufferEmpty(&(ctx->dhQ)); return(0); } XMLSEC_SAFE_CAST_ULONG_TO_SIZE(qLen, qSize, return(-1), NULL); ret = xmlSecBufferSetData(&(ctx->dhQ), q, qSize); if(ret < 0) { xmlSecInternalError2("xmlSecBufferSetData(dhQ)", NULL, "size=" XMLSEC_SIZE_FMT, qSize); return(-1); } return(0); } #endif /* XMLSEC_NO_DH */ xmlSecKeyDataPtr xmlSecMSCngKeyDataFromAlgorithm(LPSTR pszObjId) { xmlSecKeyDataPtr data = NULL; xmlSecAssert2(pszObjId != NULL, NULL); #ifndef XMLSEC_NO_DSA if (!strcmp(pszObjId, szOID_X957_DSA)) { data = xmlSecKeyDataCreate(xmlSecMSCngKeyDataDsaId); if (data == NULL) { xmlSecInternalError("xmlSecKeyDataCreate(KeyDataDsaId)", NULL); return(NULL); } } #endif /* XMLSEC_NO_DSA */ #ifndef XMLSEC_NO_RSA if (!strcmp(pszObjId, szOID_RSA_RSA)) { data = xmlSecKeyDataCreate(xmlSecMSCngKeyDataRsaId); if (data == NULL) { xmlSecInternalError("xmlSecKeyDataCreate(KeyDataRsaId)", NULL); return(NULL); } } #endif /* XMLSEC_NO_RSA */ #ifndef XMLSEC_NO_EC if (!strcmp(pszObjId, szOID_ECC_PUBLIC_KEY)) { data = xmlSecKeyDataCreate(xmlSecMSCngKeyDataEcId); if (data == NULL) { xmlSecInternalError("xmlSecKeyDataCreate(KeyDataEcId)", NULL); return(NULL); } } #endif /* XMLSEC_NO_EC */ #ifndef XMLSEC_NO_DH /* OID for DH public key (X9.42 dhPublicKey, RFC 3279) */ if (!strcmp(pszObjId, szOID_X942_DH)) { data = xmlSecKeyDataCreate(xmlSecMSCngKeyDataDhId); if (data == NULL) { xmlSecInternalError("xmlSecKeyDataCreate(KeyDataDhId)", NULL); return(NULL); } } #endif /* XMLSEC_NO_DH */ #ifndef XMLSEC_NO_XDH /* OID for X25519 public key (id-X25519, RFC 8410) */ if (!strcmp(pszObjId, szOID_X25519)) { data = xmlSecKeyDataCreate(xmlSecMSCngKeyDataXdhId); if (data == NULL) { xmlSecInternalError("xmlSecKeyDataCreate(KeyDataXdhId)", NULL); return(NULL); } } #endif /* XMLSEC_NO_XDH */ if (data == NULL) { xmlSecInvalidStringTypeError("Algorithm", pszObjId, "unsupported keytype", NULL); return(NULL); } return(data); } /** * @brief Creates key data value from the cert. * @param pCert the pointer to cert. * @param type the expected key type. * @return pointer to newly created xmlsec key or NULL if an error occurs. */ xmlSecKeyDataPtr xmlSecMSCngCertAdopt(PCCERT_CONTEXT pCert, xmlSecKeyDataType type) { xmlSecKeyDataPtr data = NULL; int ret; xmlSecAssert2(pCert != NULL, NULL); xmlSecAssert2(pCert->pCertInfo != NULL, NULL); xmlSecAssert2(pCert->pCertInfo->SubjectPublicKeyInfo.Algorithm.pszObjId != NULL, NULL); data = xmlSecMSCngKeyDataFromAlgorithm(pCert->pCertInfo->SubjectPublicKeyInfo.Algorithm.pszObjId); if (data == NULL) { xmlSecInternalError("xmlSecMSCngKeyDataAdoptCert", NULL); return(NULL); } ret = xmlSecMSCngKeyDataAdoptCert(data, pCert, type); if(ret < 0) { xmlSecInternalError("xmlSecMSCngKeyDataAdoptCert", NULL); xmlSecKeyDataDestroy(data); return(NULL); } return(data); } /** * @brief Native MSCng public key retrieval from xmlsec keydata. The returned key must * @param data the key data to retrieve certificate from. * * not be destroyed by the caller. * * @return key on success or 0 otherwise. */ BCRYPT_KEY_HANDLE xmlSecMSCngKeyDataGetPubKey(xmlSecKeyDataPtr data) { xmlSecMSCngKeyDataCtxPtr ctx; xmlSecAssert2(xmlSecKeyDataIsValid(data), 0); xmlSecAssert2(xmlSecKeyDataCheckSize(data, xmlSecMSCngKeyDataSize), 0); ctx = xmlSecMSCngKeyDataGetCtx(data); xmlSecAssert2(ctx != NULL, 0); return(ctx->pubkey); } /** * @brief Native MSCng private key retrieval from xmlsec keydata. The returned key * @param data the key data to retrieve certificate from. * * must not be destroyed by the caller. * * @return key on success or 0 otherwise. */ NCRYPT_KEY_HANDLE xmlSecMSCngKeyDataGetPrivKey(xmlSecKeyDataPtr data) { xmlSecMSCngKeyDataCtxPtr ctx; xmlSecAssert2(xmlSecKeyDataIsValid(data), 0); xmlSecAssert2(xmlSecKeyDataCheckSize(data, xmlSecMSCngKeyDataSize), 0); ctx = xmlSecMSCngKeyDataGetCtx(data); xmlSecAssert2(ctx != NULL, 0); return(ctx->privkey); } static int xmlSecMSCngCertKeyDataInitialize(xmlSecKeyDataPtr data) { xmlSecMSCngKeyDataCtxPtr ctx; int ret; xmlSecAssert2(xmlSecKeyDataIsValid(data), -1); xmlSecAssert2(xmlSecKeyDataCheckSize(data, xmlSecMSCngKeyDataSize), -1); ctx = xmlSecMSCngKeyDataGetCtx(data); xmlSecAssert2(ctx != NULL, -1); memset(ctx, 0, sizeof(xmlSecMSCngKeyDataCtx)); ret = xmlSecBufferInitialize(&(ctx->dhQ), 0); if(ret < 0) { xmlSecInternalError("xmlSecBufferInitialize(dhQ)", NULL); return(-1); } return(0); } static void xmlSecMSCngCertKeyDataFinalize(xmlSecKeyDataPtr data) { xmlSecMSCngKeyDataCtxPtr ctx; NTSTATUS status; xmlSecAssert(xmlSecKeyDataIsValid(data)); xmlSecAssert(xmlSecKeyDataCheckSize(data, xmlSecMSCngKeyDataSize)); ctx = xmlSecMSCngKeyDataGetCtx(data); xmlSecAssert(ctx != NULL); if((ctx->privkey != 0) && (ctx->privkeyNeedsFree == TRUE)) { status = NCryptFreeObject(ctx->privkey); if(status != STATUS_SUCCESS) { xmlSecMSCngNtError("BCryptDestroyKey", NULL, status); /* ignore error */ } } if(ctx->pubkey != 0) { status = BCryptDestroyKey(ctx->pubkey); if(status != STATUS_SUCCESS) { xmlSecMSCngNtError("BCryptDestroyKey", NULL, status); /* ignore error */ } } if(ctx->bcryptPrivkey != 0) { status = BCryptDestroyKey(ctx->bcryptPrivkey); if(status != STATUS_SUCCESS) { xmlSecMSCngNtError("BCryptDestroyKey(bcryptPrivkey)", NULL, status); /* ignore error */ } } xmlSecBufferFinalize(&(ctx->dhQ)); if(ctx->cert != NULL) { CertFreeCertificateContext(ctx->cert); } memset(ctx, 0, sizeof(xmlSecMSCngKeyDataCtx)); } static int xmlSecMSCngCertKeyDataDuplicate(xmlSecKeyDataPtr dst, xmlSecKeyDataPtr src) { xmlSecMSCngKeyDataCtxPtr dstCtx; xmlSecMSCngKeyDataCtxPtr srcCtx; NTSTATUS status; DWORD cbBlob = 0; PUCHAR pbBlob; BCRYPT_ALG_HANDLE hAlg = NULL; LPCWSTR pszAlgId; LPCWSTR pszCurveName = NULL; /* non-NULL iff a curve property must be set before import */ int ret; xmlSecAssert2(xmlSecKeyDataIsValid(dst), -1); xmlSecAssert2(xmlSecKeyDataCheckSize(dst, xmlSecMSCngKeyDataSize), -1); xmlSecAssert2(xmlSecKeyDataIsValid(src), -1); xmlSecAssert2(xmlSecKeyDataCheckSize(src, xmlSecMSCngKeyDataSize), -1); dstCtx = xmlSecMSCngKeyDataGetCtx(dst); xmlSecAssert2(dstCtx != NULL, -1); xmlSecAssert2(dstCtx->cert == NULL, -1); xmlSecAssert2(dstCtx->privkey == 0, -1); xmlSecAssert2(dstCtx->pubkey == NULL, -1); xmlSecAssert2(dstCtx->bcryptPrivkey == NULL, -1); xmlSecAssert2(xmlSecBufferGetSize(&(dstCtx->dhQ)) == 0, -1); srcCtx = xmlSecMSCngKeyDataGetCtx(src); xmlSecAssert2(srcCtx != NULL, -1); if(srcCtx->cert != NULL) { dstCtx->cert = CertDuplicateCertificateContext(srcCtx->cert); if(dstCtx->cert == NULL) { xmlSecMSCngLastError("CertDuplicateCertificateContext", NULL); return(-1); } } if(srcCtx->privkey != 0) { ret = xmlSecMSCngKeyDataCertGetPrivkey(dstCtx->cert, &dstCtx->privkey, &dstCtx->privkeyNeedsFree); if(ret < 0) { xmlSecInternalError("xmlSecMSCngKeyDataCertGetPrivkey", NULL); return(-1); } } if(dstCtx->cert != NULL) { /* avoid BCryptDuplicateKey() here as that works for symmetric keys only */ ret = xmlSecMSCngKeyDataCertGetPubkey(&(dstCtx->cert->pCertInfo->SubjectPublicKeyInfo), &dstCtx->pubkey); if(ret < 0) { xmlSecInternalError("xmlSecMSCngKeyDataCertGetPubkey", NULL); return(-1); } } else if(srcCtx->pubkey != NULL) { /* BCryptDuplicateKey() works with symmetric keys only, so go with * export + import instead */ status = BCryptExportKey(srcCtx->pubkey, NULL, BCRYPT_PUBLIC_KEY_BLOB, NULL, 0, &cbBlob, 0); if(status != STATUS_SUCCESS) { xmlSecMSCngNtError("BCryptExportKey", NULL, status); return(-1); } pbBlob = (PUCHAR)xmlMalloc(cbBlob); if(pbBlob == NULL) { xmlSecMallocError(cbBlob, NULL); return(-1); } status = BCryptExportKey(srcCtx->pubkey, NULL, BCRYPT_PUBLIC_KEY_BLOB, pbBlob, cbBlob, &cbBlob, 0); if(status != STATUS_SUCCESS) { xmlSecMSCngNtError("BCryptExportKey", NULL, status); xmlFree(pbBlob); return(-1); } switch(((BCRYPT_KEY_BLOB*)pbBlob)->Magic) { #ifndef XMLSEC_NO_DSA case BCRYPT_DSA_PUBLIC_MAGIC: #if XMLSEC_MSCNG_HAVE_DSA_V2 case BCRYPT_DSA_PUBLIC_MAGIC_V2: #endif /* XMLSEC_MSCNG_HAVE_DSA_V2 */ pszAlgId = BCRYPT_DSA_ALGORITHM; break; #endif /* XMLSEC_NO_DSA */ #ifndef XMLSEC_NO_RSA case BCRYPT_RSAPUBLIC_MAGIC: pszAlgId = BCRYPT_RSA_ALGORITHM; break; #endif /* XMLSEC_NO_RSA */ #ifndef XMLSEC_NO_EC case BCRYPT_ECDH_PUBLIC_P256_MAGIC: case BCRYPT_ECDSA_PUBLIC_P256_MAGIC: pszAlgId = BCRYPT_ECDSA_P256_ALGORITHM; break; case BCRYPT_ECDH_PUBLIC_P384_MAGIC: case BCRYPT_ECDSA_PUBLIC_P384_MAGIC: pszAlgId = BCRYPT_ECDSA_P384_ALGORITHM; break; case BCRYPT_ECDH_PUBLIC_P521_MAGIC: case BCRYPT_ECDSA_PUBLIC_P521_MAGIC: pszAlgId = BCRYPT_ECDSA_P521_ALGORITHM; break; #endif /* XMLSEC_NO_EC */ #ifndef XMLSEC_NO_DH case BCRYPT_DH_PUBLIC_MAGIC: pszAlgId = BCRYPT_DH_ALGORITHM; break; #endif /* XMLSEC_NO_DH */ #ifndef XMLSEC_NO_XDH case BCRYPT_ECDH_PUBLIC_GENERIC_MAGIC: pszAlgId = BCRYPT_ECDH_ALGORITHM; pszCurveName = BCRYPT_ECC_CURVE_25519; break; #endif /* XMLSEC_NO_XDH */ default: xmlSecNotImplementedError2("Unexpected key magic value: %llu", (unsigned long long)(((BCRYPT_KEY_BLOB*)pbBlob)->Magic)); xmlFree(pbBlob); return(-1); } status = BCryptOpenAlgorithmProvider( &hAlg, pszAlgId, NULL, 0); if(status != STATUS_SUCCESS) { xmlSecMSCngNtError("BCryptOpenAlgorithmProvider", NULL, status); xmlFree(pbBlob); return(-1); } /* For generic-curve algorithms (e.g. X25519), set the curve name property */ if(pszCurveName != NULL) { status = BCryptSetProperty(hAlg, BCRYPT_ECC_CURVE_NAME, (PUCHAR)pszCurveName, (ULONG)((wcslen(pszCurveName) + 1) * sizeof(WCHAR)), 0); if(status != STATUS_SUCCESS) { xmlSecMSCngNtError("BCryptSetProperty(BCRYPT_ECC_CURVE_NAME)", NULL, status); xmlFree(pbBlob); BCryptCloseAlgorithmProvider(hAlg, 0); return(-1); } } status = BCryptImportKeyPair( hAlg, NULL, BCRYPT_PUBLIC_KEY_BLOB, &dstCtx->pubkey, pbBlob, cbBlob, 0); if(status != STATUS_SUCCESS) { xmlSecMSCngNtError("BCryptImportKeyPair", NULL, status); xmlFree(pbBlob); BCryptCloseAlgorithmProvider(hAlg, 0); return(-1); } xmlFree(pbBlob); BCryptCloseAlgorithmProvider(hAlg, 0); } if(srcCtx->bcryptPrivkey != NULL) { #ifndef XMLSEC_NO_DH if(xmlSecKeyDataCheckId(src, xmlSecMSCngKeyDataDhId)) { ret = xmlSecMSCngKeyDataDuplicateBCryptDhPrivKey(srcCtx->bcryptPrivkey, &dstCtx->bcryptPrivkey); if(ret < 0) { xmlSecInternalError("xmlSecMSCngKeyDataDuplicateBCryptDhPrivKey", NULL); return(-1); } } else #endif /* XMLSEC_NO_DH */ #ifndef XMLSEC_NO_XDH if(xmlSecKeyDataCheckId(src, xmlSecMSCngKeyDataXdhId)) { ret = xmlSecMSCngKeyDataDuplicateBCryptXdhPrivKey(srcCtx->bcryptPrivkey, &dstCtx->bcryptPrivkey); if(ret < 0) { xmlSecInternalError("xmlSecMSCngKeyDataDuplicateBCryptXdhPrivKey", NULL); return(-1); } } else #endif /* XMLSEC_NO_XDH */ { xmlSecNotImplementedError("BCrypt private key duplication for unknown key type"); return(-1); } } if(xmlSecBufferGetSize(&(srcCtx->dhQ)) > 0) { ret = xmlSecBufferSetData(&(dstCtx->dhQ), xmlSecBufferGetData(&(srcCtx->dhQ)), xmlSecBufferGetSize(&(srcCtx->dhQ))); if(ret < 0) { xmlSecInternalError("xmlSecBufferSetData(dhQ)", NULL); return(-1); } } return(0); } static xmlSecKeyDataType xmlSecMSCngCertKeyDataGetType(xmlSecKeyDataPtr data) { xmlSecMSCngKeyDataCtxPtr ctx; xmlSecAssert2(xmlSecKeyDataIsValid(data), xmlSecKeyDataTypeUnknown); xmlSecAssert2(xmlSecKeyDataCheckSize(data, xmlSecMSCngKeyDataSize), xmlSecKeyDataTypeUnknown); ctx = xmlSecMSCngKeyDataGetCtx(data); xmlSecAssert2(ctx != NULL, xmlSecKeyDataTypeUnknown); if(ctx->privkey != 0) { return(xmlSecKeyDataTypePrivate | xmlSecKeyDataTypePublic); } if(ctx->bcryptPrivkey != 0) { return(xmlSecKeyDataTypePrivate | xmlSecKeyDataTypePublic); } return(xmlSecKeyDataTypePublic); } xmlSecSize xmlSecMSCngCertKeyDataGetSize(xmlSecKeyDataPtr data) { NTSTATUS status; xmlSecMSCngKeyDataCtxPtr ctx; DWORD length = 0; xmlSecSize res; xmlSecAssert2(xmlSecKeyDataIsValid(data), 0); xmlSecAssert2(xmlSecKeyDataCheckSize(data, xmlSecMSCngKeyDataSize), 0); ctx = xmlSecMSCngKeyDataGetCtx(data); xmlSecAssert2(ctx != NULL, 0); if(ctx->cert != NULL) { xmlSecAssert2(ctx->cert->pCertInfo != NULL, 0); length = CertGetPublicKeyLength(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, &ctx->cert->pCertInfo->SubjectPublicKeyInfo); } else if(ctx->pubkey != 0) { DWORD lenlen = sizeof(length); status = BCryptGetProperty(ctx->pubkey, BCRYPT_KEY_STRENGTH, (PUCHAR)&length, lenlen, &lenlen, 0); if(status != STATUS_SUCCESS) { xmlSecMSCngNtError("BCryptGetproperty", NULL, status); return(0); } xmlSecAssert2(lenlen == sizeof(length), 0); } else if(ctx->privkey != 0) { xmlSecNotImplementedError("MSCNG doesn't support getting key length from private key"); return(0); } XMLSEC_SAFE_CAST_ULONG_TO_SIZE(length, res, return(0), NULL); return(res); } #define XMLSEC_MSCNG_CERTKEY_KLASS_EX(klassName, xmlName, usage, dataNodeName, dataNodeNs, generate, xmlRead, xmlWrite) \ static xmlSecKeyDataKlass xmlSecMSCngKeyData ## klassName ## Klass = { \ sizeof(xmlSecKeyDataKlass), /* xmlSecSize klassSize */ \ xmlSecMSCngKeyDataSize, /* xmlSecSize objSize */ \ \ /* data */ \ xmlSecName ## xmlName ## KeyValue, /* const xmlChar* name; */ \ usage, /* xmlSecKeyDataUsage usage; */ \ xmlSecHref ## xmlName ## KeyValue, /* const xmlChar* href; */ \ dataNodeName, /* const xmlChar* dataNodeName; */ \ dataNodeNs, /* const xmlChar* dataNodeNs; */ \ \ /* constructors/destructor */ \ xmlSecMSCngCertKeyDataInitialize, /* xmlSecKeyDataInitializeMethod initialize; */ \ xmlSecMSCngCertKeyDataDuplicate, /* xmlSecKeyDataDuplicateMethod duplicate; */ \ xmlSecMSCngCertKeyDataFinalize, /* xmlSecKeyDataFinalizeMethod finalize; */ \ generate, /* xmlSecKeyDataGenerateMethod generate; */ \ \ /* get info */ \ xmlSecMSCngCertKeyDataGetType, /* xmlSecKeyDataGetTypeMethod getType; */ \ xmlSecMSCngCertKeyDataGetSize, /* xmlSecKeyDataGetSizeMethod getSize; */ \ NULL, /* DEPRECATED xmlSecKeyDataGetIdentifier getIdentifier; */ \ \ /* read/write */ \ xmlRead, /* xmlSecKeyDataXmlReadMethod xmlRead; */ \ xmlWrite, /* xmlSecKeyDataXmlWriteMethod xmlWrite; */ \ NULL, /* xmlSecKeyDataBinReadMethod binRead; */ \ NULL, /* xmlSecKeyDataBinWriteMethod binWrite; */ \ \ /* debug */ \ xmlSecKeyDataDebugDumpImpl, /* xmlSecKeyDataDebugDumpMethod debugDump; */ \ xmlSecKeyDataDebugXmlDumpImpl, /* xmlSecKeyDataDebugDumpMethod debugXmlDump; */ \ \ /* reserved for the future */ \ NULL, /* void* reserved0; */ \ NULL, /* void* reserved1; */ \ }; #ifndef XMLSEC_NO_DSA static int xmlSecMSCngKeyDataDsaWrite(xmlSecKeyDataId id, xmlSecKeyDataPtr data, xmlSecKeyValueDsaPtr dsaValue, int writePrivateKey XMLSEC_ATTRIBUTE_UNUSED) { xmlSecMSCngKeyDataCtxPtr ctx; int ret; xmlSecAssert2(id == xmlSecMSCngKeyDataDsaId, -1); xmlSecAssert2(data != NULL, -1); xmlSecAssert2(xmlSecKeyDataCheckId(data, xmlSecMSCngKeyDataDsaId), -1); xmlSecAssert2(dsaValue != NULL, -1); UNREFERENCED_PARAMETER(writePrivateKey); ctx = xmlSecMSCngKeyDataGetCtx(data); xmlSecAssert2(ctx != NULL, -1); xmlSecAssert2(ctx->pubkey, -1); ret = xmlSecMSCngKeyDataDsaPubkeyWrite(ctx->pubkey, dsaValue); if(ret < 0) { xmlSecInternalError("xmlSecMSCngKeyDataDsaPubkeyWrite", xmlSecKeyDataGetName(data)); return(-1); } return(0); } static int xmlSecMSCngKeyDataDsaXmlRead(xmlSecKeyDataId id, xmlSecKeyPtr key, xmlNodePtr node, xmlSecKeyInfoCtxPtr keyInfoCtx) { xmlSecAssert2(id == xmlSecMSCngKeyDataDsaId, -1); return(xmlSecKeyDataDsaXmlRead(id, key, node, keyInfoCtx, xmlSecMSCngKeyDataDsaRead)); } static int xmlSecMSCngKeyDataDsaXmlWrite(xmlSecKeyDataId id, xmlSecKeyPtr key, xmlNodePtr node, xmlSecKeyInfoCtxPtr keyInfoCtx) { xmlSecAssert2(id == xmlSecMSCngKeyDataDsaId, -1); return(xmlSecKeyDataDsaXmlWrite(id, key, node, keyInfoCtx, xmlSecBase64GetDefaultLineSize(), 1, /* add line breaks */ xmlSecMSCngKeyDataDsaWrite)); } static int xmlSecMSCngKeyDataDsaGenerate(xmlSecKeyDataPtr data, xmlSecSize sizeBits, xmlSecKeyDataType type) { UNREFERENCED_PARAMETER(type); xmlSecMSCngKeyDataCtxPtr ctx; BCRYPT_ALG_HANDLE hAlg = 0; BCRYPT_KEY_HANDLE hKey = 0; NTSTATUS status; DWORD dwSizeBits; int ret; int res = -1; xmlSecAssert2(xmlSecKeyDataIsValid(data), xmlSecKeyDataTypeUnknown); xmlSecAssert2(xmlSecKeyDataCheckSize(data, xmlSecMSCngKeyDataSize), xmlSecKeyDataTypeUnknown); xmlSecAssert2(xmlSecKeyDataCheckId(data, xmlSecMSCngKeyDataDsaId), -1); xmlSecAssert2(sizeBits > 0, -1); ctx = xmlSecMSCngKeyDataGetCtx(data); xmlSecAssert2(ctx != NULL, -1); status = BCryptOpenAlgorithmProvider(&hAlg, BCRYPT_DSA_ALGORITHM, NULL, 0); if(status != STATUS_SUCCESS) { xmlSecMSCngNtError("BCryptOpenAlgorithmProvider", xmlSecKeyDataGetName(data), status); goto done; } XMLSEC_SAFE_CAST_SIZE_TO_ULONG(sizeBits, dwSizeBits, goto done, xmlSecKeyDataGetName(data)); status = BCryptGenerateKeyPair(hAlg, &hKey, dwSizeBits, 0); if(status != STATUS_SUCCESS) { xmlSecMSCngNtError("BCryptGenerateKeyPair", xmlSecKeyDataGetName(data), status); goto done; } /* need to finalize the key before it can be used */ status = BCryptFinalizeKeyPair(hKey, 0); if(status != STATUS_SUCCESS) { xmlSecMSCngNtError("BCryptFinalizeKeyPair", xmlSecKeyDataGetName(data), status); goto done; } ret = xmlSecMSCngKeyDataAdoptKey(data, hKey); if(ret < 0) { xmlSecInternalError("xmlSecMSCngKeyDataAdoptKey", xmlSecKeyDataGetName(data)); goto done; } hKey = 0; /* success */ res = 0; done: if (hKey != 0) { BCryptDestroyKey(hKey); } if (hAlg != 0) { BCryptCloseAlgorithmProvider(hAlg, 0); } return(res); } XMLSEC_MSCNG_CERTKEY_KLASS_EX(Dsa, DSA, xmlSecKeyDataUsageReadFromFile | xmlSecKeyDataUsageKeyValueNode | xmlSecKeyDataUsageRetrievalMethodNodeXml, xmlSecNodeDSAKeyValue, xmlSecDSigNs, xmlSecMSCngKeyDataDsaGenerate, xmlSecMSCngKeyDataDsaXmlRead, xmlSecMSCngKeyDataDsaXmlWrite) /** * @brief The MSCng DSA CertKey data klass. * @return pointer to MSCng DSA key data klass. */ xmlSecKeyDataId xmlSecMSCngKeyDataDsaGetKlass(void) { return(&xmlSecMSCngKeyDataDsaKlass); } #endif /* XMLSEC_NO_DSA */ #ifndef XMLSEC_NO_RSA static xmlSecKeyDataPtr xmlSecMSCngKeyDataRsaRead(xmlSecKeyDataId id, xmlSecKeyValueRsaPtr rsaValue) { xmlSecKeyDataPtr data = NULL; xmlSecKeyDataPtr res = NULL; xmlSecBuffer blob; int blobInitialized = 0; xmlSecSize blobBufferSize, offset; xmlSecSize mSize, peSize; xmlSecByte* blobData; DWORD dwSize; BCRYPT_RSAKEY_BLOB* rsakey; BCRYPT_ALG_HANDLE hAlg = NULL; BCRYPT_KEY_HANDLE hKey = 0; size_t size; NTSTATUS status; int ret; xmlSecAssert2(id == xmlSecMSCngKeyDataRsaId, NULL); xmlSecAssert2(rsaValue != NULL, NULL); xmlSecAssert2(xmlSecBufferGetData(&(rsaValue->modulus)) != NULL, NULL); xmlSecAssert2(xmlSecBufferGetData(&(rsaValue->publicExponent)) != NULL, NULL); /* dont reverse blobs as both the XML and CNG works with big-endian */ mSize = xmlSecBufferGetSize(&(rsaValue->modulus)); peSize = xmlSecBufferGetSize(&(rsaValue->publicExponent)); xmlSecAssert2(mSize > 0, NULL); xmlSecAssert2(peSize > 0, NULL); /* turn the read data into a public key blob, as documented at * <https://msdn.microsoft.com/en-us/library/windows/desktop/aa375531(v=vs.85).aspx>: * need to write exponent and modulus after the struct */ size = sizeof(BCRYPT_RSAKEY_BLOB) + mSize + peSize; XMLSEC_SAFE_CAST_SIZE_T_TO_SIZE(size, blobBufferSize, goto done, xmlSecKeyDataKlassGetName(id)); ret = xmlSecBufferInitialize(&blob, blobBufferSize); if (ret < 0) { xmlSecInternalError2("xmlSecBufferInitialize", xmlSecKeyDataKlassGetName(id), "size=" XMLSEC_SIZE_FMT, blobBufferSize); goto done; } blobInitialized = 1; ret = xmlSecBufferSetSize(&blob, blobBufferSize); if (ret < 0) { xmlSecInternalError2("xmlSecBufferSetSize", NULL, "size=" XMLSEC_SIZE_FMT, blobBufferSize); goto done; } blobData = xmlSecBufferGetData(&blob); xmlSecAssert2(blobData != NULL, NULL); memset(blobData, 0, blobBufferSize); // ensure all padding with 0s work rsakey = (BCRYPT_RSAKEY_BLOB*)blobData; rsakey->Magic = BCRYPT_RSAPUBLIC_MAGIC; XMLSEC_SAFE_CAST_SIZE_TO_ULONG((mSize * 8), rsakey->BitLength, goto done, xmlSecKeyDataKlassGetName(id)); XMLSEC_SAFE_CAST_SIZE_TO_ULONG(peSize, rsakey->cbPublicExp, goto done, xmlSecKeyDataKlassGetName(id)); XMLSEC_SAFE_CAST_SIZE_TO_ULONG(mSize, rsakey->cbModulus, goto done, xmlSecKeyDataKlassGetName(id)); offset = sizeof(BCRYPT_RSAKEY_BLOB); /* public exponent */ memcpy(blobData + offset, xmlSecBufferGetData(&(rsaValue->publicExponent)), peSize); offset += peSize; /* modulus */ memcpy(blobData + offset, xmlSecBufferGetData(&(rsaValue->modulus)), mSize); offset += mSize; /* PrivateExponent is REQUIRED for private key but MSCng does not support it, * so we just ignore it */ /* Now that we have the blob, import */ status = BCryptOpenAlgorithmProvider( &hAlg, BCRYPT_RSA_ALGORITHM, NULL, 0); if (status != STATUS_SUCCESS) { xmlSecMSCngNtError("BCryptOpenAlgorithmProvider", xmlSecKeyDataKlassGetName(id), status); goto done; } XMLSEC_SAFE_CAST_SIZE_TO_ULONG(blobBufferSize, dwSize, goto done, xmlSecKeyDataKlassGetName(id)); status = BCryptImportKeyPair( hAlg, NULL, BCRYPT_RSAPUBLIC_BLOB, &hKey, blobData, dwSize, 0); if (status != STATUS_SUCCESS) { xmlSecMSCngNtError2("BCryptImportKeyPair", xmlSecKeyDataKlassGetName(id), status, "dwSize=%lu", dwSize); goto done; } data = xmlSecKeyDataCreate(id); if (data == NULL) { xmlSecInternalError("xmlSecKeyDataCreate", xmlSecKeyDataKlassGetName(id)); goto done; } ret = xmlSecMSCngKeyDataAdoptKey(data, hKey); if (ret < 0) { xmlSecInternalError("xmlSecMSCngKeyDataAdoptKey", xmlSecKeyDataGetName(data)); goto done; } hKey = 0; /* now owned by data */ /* success */ res = data; data = NULL; done: if (data != NULL) { xmlSecKeyDataDestroy(data); } if (hKey != 0) { BCryptDestroyKey(hKey); } if (hAlg != 0) { BCryptCloseAlgorithmProvider(hAlg, 0); } if (blobInitialized != 0) { xmlSecBufferFinalize(&blob); } return(res); } static int xmlSecMSCngKeyDataRsaWrite(xmlSecKeyDataId id, xmlSecKeyDataPtr data, xmlSecKeyValueRsaPtr rsaValue, int writePrivateKey XMLSEC_ATTRIBUTE_UNUSED) { xmlSecMSCngKeyDataCtxPtr ctx; NTSTATUS status; xmlSecBuffer buf; int bufInitialized = 0; xmlSecByte* bufData; DWORD bufLen = 0; BCRYPT_RSAKEY_BLOB* rsakey; int ret; int res = -1; xmlSecAssert2(id == xmlSecMSCngKeyDataRsaId, -1); xmlSecAssert2(data != NULL, -1); xmlSecAssert2(xmlSecKeyDataCheckId(data, xmlSecMSCngKeyDataRsaId), -1); xmlSecAssert2(rsaValue != NULL, -1); UNREFERENCED_PARAMETER(writePrivateKey); ctx = xmlSecMSCngKeyDataGetCtx(data); xmlSecAssert2(ctx != NULL, -1); xmlSecAssert2(ctx->pubkey, -1); /* turn ctx->pubkey into rsakey */ status = BCryptExportKey(ctx->pubkey, NULL, BCRYPT_RSAPUBLIC_BLOB, NULL, 0, &bufLen, 0); if ((status != STATUS_SUCCESS) || (bufLen <= 0)) { xmlSecMSCngNtError2("BCryptExportKey", xmlSecKeyDataKlassGetName(id), status, "bufLen=%lu", bufLen); goto done; } ret = xmlSecBufferInitialize(&buf, bufLen); if (ret < 0) { xmlSecInternalError2("xmlSecBufferInitialize", xmlSecKeyDataKlassGetName(id), "size=%lu", bufLen); goto done; } bufInitialized = 1; bufData = xmlSecBufferGetData(&buf); xmlSecAssert2(bufData != NULL, -1); status = BCryptExportKey(ctx->pubkey, NULL, BCRYPT_RSAPUBLIC_BLOB, bufData, bufLen, &bufLen, 0); if ((status != STATUS_SUCCESS) || (bufLen <= 0)) { xmlSecMSCngNtError2("BCryptExportKey", xmlSecKeyDataKlassGetName(id), status, "bufLen=%lu", bufLen); goto done; } ret = xmlSecBufferSetSize(&buf, bufLen); if (ret < 0) { xmlSecInternalError2("xmlSecBufferSetSize", xmlSecKeyDataKlassGetName(id), "size=%lu", bufLen); goto done; } /* check BCRYPT_RSAKEY_BLOB */ if (bufLen < sizeof(BCRYPT_RSAKEY_BLOB)) { xmlSecMSCngNtError2("BCRYPT_RSAKEY_BLOB", xmlSecKeyDataKlassGetName(id), STATUS_SUCCESS, "dwBlobLen=%lu", bufLen); goto done; } rsakey = (BCRYPT_RSAKEY_BLOB*)bufData; /* check sizes */ if (bufLen < (sizeof(BCRYPT_RSAKEY_BLOB) + rsakey->cbPublicExp + rsakey->cbModulus)) { xmlSecMSCngNtError3("CryptExportKey", xmlSecKeyDataKlassGetName(id), STATUS_SUCCESS, "dwBlobLen: %lu; keyLen: %lu", bufLen, rsakey->cbPublicExp); goto done; } bufData += sizeof(BCRYPT_RSAKEY_BLOB); /* public exponent */ ret = xmlSecBufferSetData(&(rsaValue->publicExponent), bufData, rsakey->cbPublicExp); if (ret < 0) { xmlSecInternalError2("xmlSecBufferSetData(publicExponent)", xmlSecKeyDataKlassGetName(id), "cbPublicExp=%lu", rsakey->cbPublicExp); goto done; } bufData += rsakey->cbPublicExp; /* modulus */ ret = xmlSecBufferSetData(&(rsaValue->modulus), bufData, rsakey->cbModulus); if (ret < 0) { xmlSecInternalError2("xmlSecBufferSetData(modulus)", xmlSecKeyDataKlassGetName(id), "cbModulus=%lu", rsakey->cbModulus); goto done; } bufData += rsakey->cbModulus; /* next is PrivateExponent node: not supported in MSCrypto */ /* dont reverse blobs as both the XML and CNG works with big-endian */ /* success */ res = 0; done: if (bufInitialized != 0) { xmlSecBufferFinalize(&buf); } return(res); } static int xmlSecMSCngKeyDataRsaXmlRead(xmlSecKeyDataId id, xmlSecKeyPtr key, xmlNodePtr node, xmlSecKeyInfoCtxPtr keyInfoCtx) { xmlSecAssert2(id == xmlSecMSCngKeyDataRsaId, -1); return(xmlSecKeyDataRsaXmlRead(id, key, node, keyInfoCtx, xmlSecMSCngKeyDataRsaRead)); } static int xmlSecMSCngKeyDataRsaXmlWrite(xmlSecKeyDataId id, xmlSecKeyPtr key, xmlNodePtr node, xmlSecKeyInfoCtxPtr keyInfoCtx) { xmlSecAssert2(id == xmlSecMSCngKeyDataRsaId, -1); return(xmlSecKeyDataRsaXmlWrite(id, key, node, keyInfoCtx, xmlSecBase64GetDefaultLineSize(), 1, /* add line breaks */ xmlSecMSCngKeyDataRsaWrite)); } static int xmlSecMSCngKeyDataRsaGenerate(xmlSecKeyDataPtr data, xmlSecSize sizeBits, xmlSecKeyDataType type) { UNREFERENCED_PARAMETER(type); xmlSecMSCngKeyDataCtxPtr ctx; BCRYPT_ALG_HANDLE hAlg = 0; BCRYPT_KEY_HANDLE hKey = 0; DWORD dwSizeBits; NTSTATUS status; int ret; int res = -1; xmlSecAssert2(xmlSecKeyDataIsValid(data), xmlSecKeyDataTypeUnknown); xmlSecAssert2(xmlSecKeyDataCheckSize(data, xmlSecMSCngKeyDataSize), xmlSecKeyDataTypeUnknown); xmlSecAssert2(xmlSecKeyDataCheckId(data, xmlSecMSCngKeyDataRsaId), -1); xmlSecAssert2(sizeBits > 0, -1); ctx = xmlSecMSCngKeyDataGetCtx(data); xmlSecAssert2(ctx != NULL, -1); status = BCryptOpenAlgorithmProvider( &hAlg, BCRYPT_RSA_ALGORITHM, NULL, 0); if(status != STATUS_SUCCESS) { xmlSecMSCngNtError("BCryptOpenAlgorithmProvider", xmlSecKeyDataGetName(data), status); goto done; } XMLSEC_SAFE_CAST_SIZE_TO_ULONG(sizeBits, dwSizeBits, goto done, xmlSecKeyDataGetName(data)); status = BCryptGenerateKeyPair(hAlg, &hKey, dwSizeBits, 0); if(status != STATUS_SUCCESS) { xmlSecMSCngNtError("BCryptGenerateKeyPair", xmlSecKeyDataGetName(data), status); goto done; } /* need to finalize the key before it can be used */ status = BCryptFinalizeKeyPair(hKey, 0); if(status != STATUS_SUCCESS) { xmlSecMSCngNtError("BCryptFinalizeKeyPair", xmlSecKeyDataGetName(data), status); goto done; } ret = xmlSecMSCngKeyDataAdoptKey(data, hKey); if(ret < 0) { xmlSecInternalError("xmlSecMSCngKeyDataAdoptKey", xmlSecKeyDataGetName(data)); goto done; } /* hKey is owned by data now */ hKey = 0; /* success */ res = 0; done: if (hKey != 0) { BCryptDestroyKey(hKey); } if (hAlg != 0) { BCryptCloseAlgorithmProvider(hAlg, 0); } return(res); } XMLSEC_MSCNG_CERTKEY_KLASS_EX(Rsa, RSA, xmlSecKeyDataUsageReadFromFile | xmlSecKeyDataUsageKeyValueNode | xmlSecKeyDataUsageRetrievalMethodNodeXml, xmlSecNodeRSAKeyValue, xmlSecDSigNs, xmlSecMSCngKeyDataRsaGenerate, xmlSecMSCngKeyDataRsaXmlRead, xmlSecMSCngKeyDataRsaXmlWrite) /** * @brief The MSCng RSA CertKey data klass. * @return pointer to MSCng RSA key data klass. */ xmlSecKeyDataId xmlSecMSCngKeyDataRsaGetKlass(void) { return(&xmlSecMSCngKeyDataRsaKlass); } #endif /* XMLSEC_NO_RSA */ #ifndef XMLSEC_NO_EC typedef struct _xmlSecMSCngKeyDataEccCurveNameAndMagic { ULONG magic; LPCWSTR blobType; xmlChar oid[128]; } xmlSecMSCngKeyDataEccCurveNameAndMagic; /* https://learn.microsoft.com/en-us/windows/win32/api/bcrypt/ns-bcrypt-bcrypt_ecckey_blob */ static const xmlSecMSCngKeyDataEccCurveNameAndMagic g_xmlSecMSCngKeyDataEccCurveNameAndMagic[] = { { BCRYPT_ECDSA_PUBLIC_P256_MAGIC, BCRYPT_ECDSA_P256_ALGORITHM, "1.2.840.10045.3.1.7" }, /* prime256v1 */ { BCRYPT_ECDSA_PUBLIC_P384_MAGIC, BCRYPT_ECDSA_P384_ALGORITHM, "1.3.132.0.34" }, /* secp384r1 */ { BCRYPT_ECDSA_PUBLIC_P521_MAGIC, BCRYPT_ECDSA_P521_ALGORITHM, "1.3.132.0.35" } /* secp521r1 */ }; static const xmlChar* xmlSecOpenSSLKeyDataEcGetOidFromMagic(ULONG magic) { xmlSecSize size = sizeof(g_xmlSecMSCngKeyDataEccCurveNameAndMagic) / sizeof(g_xmlSecMSCngKeyDataEccCurveNameAndMagic[0]); xmlSecAssert2(magic != 0, NULL); for (xmlSecSize ii = 0; ii < size; ++ii) { if (magic == g_xmlSecMSCngKeyDataEccCurveNameAndMagic[ii].magic) { return(g_xmlSecMSCngKeyDataEccCurveNameAndMagic[ii].oid); } } return(NULL); } static LPCWSTR xmlSecMSCngKeyDataEcGetTypeAndMagicFromOid(const xmlChar* oid, ULONG * magic) { xmlSecSize size = sizeof(g_xmlSecMSCngKeyDataEccCurveNameAndMagic) / sizeof(g_xmlSecMSCngKeyDataEccCurveNameAndMagic[0]); xmlSecAssert2(oid != NULL, 0); xmlSecAssert2(magic != NULL, 0); for (xmlSecSize ii = 0; ii < size; ++ii) { if (xmlStrcmp(oid, g_xmlSecMSCngKeyDataEccCurveNameAndMagic[ii].oid) == 0) { (*magic) = g_xmlSecMSCngKeyDataEccCurveNameAndMagic[ii].magic; return(g_xmlSecMSCngKeyDataEccCurveNameAndMagic[ii].blobType); } } return(0); } static xmlSecKeyDataPtr xmlSecMSCngKeyDataEcRead(xmlSecKeyDataId id, xmlSecKeyValueEcPtr ecValue) { xmlSecKeyDataPtr data = NULL; xmlSecKeyDataPtr res = NULL; xmlSecBuffer blob; int blobInitialized = 0; xmlSecByte* blobData; xmlSecByte* pubkeyData; xmlSecSize pubkeySize; xmlSecSize offset, blobSize; DWORD dwBlobSize; BCRYPT_ECCKEY_BLOB* eckey; LPCWSTR blobType; BCRYPT_KEY_HANDLE hKey = NULL; NTSTATUS status; BCRYPT_ALG_HANDLE hAlg = NULL; int ret; xmlSecAssert2(id == xmlSecMSCngKeyDataEcId, NULL); xmlSecAssert2(ecValue != NULL, NULL); xmlSecAssert2(ecValue->curve != NULL, NULL); /* first byte in ecValue->pubkey is the magical byte, we don't need it */ pubkeyData = xmlSecBufferGetData(&(ecValue->pubkey)); pubkeySize = xmlSecBufferGetSize(&(ecValue->pubkey)); xmlSecAssert2(pubkeyData != NULL, NULL); xmlSecAssert2(pubkeySize > 1, NULL); pubkeyData += 1; pubkeySize -= 1; /* turn the read data into a public key blob, as documented at * https://learn.microsoft.com/en-us/windows/win32/api/bcrypt/ns-bcrypt-bcrypt_ecckey_blob> * * dont reverse blobs as both the XML and CNG works with big-endian * */ offset = sizeof(BCRYPT_ECCKEY_BLOB); blobSize = offset + pubkeySize; ret = xmlSecBufferInitialize(&blob, blobSize); if (ret < 0) { xmlSecInternalError2("xmlSecBufferSetSize", xmlSecKeyDataKlassGetName(id), "size=" XMLSEC_SIZE_FMT, blobSize); goto done; } blobInitialized = 1; ret = xmlSecBufferSetSize(&blob, blobSize); if (ret < 0) { xmlSecInternalError2("xmlSecBufferSetSize", xmlSecKeyDataKlassGetName(id), "size=" XMLSEC_SIZE_FMT, blobSize); goto done; } memset(xmlSecBufferGetData(&blob), 0, blobSize); // ensure all padding with 0s work blobData = xmlSecBufferGetData(&blob); eckey = (BCRYPT_ECCKEY_BLOB*)blobData; blobType = xmlSecMSCngKeyDataEcGetTypeAndMagicFromOid(ecValue->curve, &(eckey->dwMagic)); if ((blobType == NULL) || (eckey->dwMagic == 0)) { xmlSecInternalError2("xmlSecOpenSSLKeyDataEcGetOidFromNid", xmlSecKeyDataKlassGetName(id), "curve=%s", xmlSecErrorsSafeString(ecValue->curve)); goto done; } XMLSEC_SAFE_CAST_SIZE_TO_UINT(pubkeySize / 2, eckey->cbKey, goto done, xmlSecKeyDataKlassGetName(id)); /* pubkey */ memcpy(blobData + offset, pubkeyData, pubkeySize); /* import the key blob */ status = BCryptOpenAlgorithmProvider( &hAlg, blobType, NULL, 0); if (status != STATUS_SUCCESS) { xmlSecMSCngNtError("BCryptOpenAlgorithmProvider", xmlSecKeyDataKlassGetName(id), status); goto done; } XMLSEC_SAFE_CAST_SIZE_TO_UINT(blobSize, dwBlobSize, goto done, xmlSecKeyDataKlassGetName(id)); status = BCryptImportKeyPair( hAlg, NULL, BCRYPT_ECCPUBLIC_BLOB, &hKey, blobData, dwBlobSize, 0); if (status != STATUS_SUCCESS) { xmlSecMSCngNtError("BCryptImportKeyPair", xmlSecKeyDataKlassGetName(id), status); goto done; } data = xmlSecKeyDataCreate(id); if (data == NULL) { xmlSecInternalError("xmlSecKeyDataCreate", xmlSecKeyDataKlassGetName(id)); goto done; } ret = xmlSecMSCngKeyDataAdoptKey(data, hKey); if (ret < 0) { xmlSecInternalError("xmlSecMSCngKeyDataAdoptKey", xmlSecKeyDataGetName(data)); goto done; } hKey = 0; /* now owned by data */ /* success */ res = data; data = NULL; done: if (data != NULL) { xmlSecKeyDataDestroy(data); } if (hAlg != 0) { BCryptCloseAlgorithmProvider(hAlg, 0); } if (hKey != 0) { BCryptDestroyKey(hKey); } if (blobInitialized != 0) { xmlSecBufferFinalize(&blob); } return(res); } static int xmlSecMSCngKeyDataEcWrite(xmlSecKeyDataId id, xmlSecKeyDataPtr data, xmlSecKeyValueEcPtr ecValue) { xmlSecMSCngKeyDataCtxPtr ctx; NTSTATUS status; xmlSecBuffer buf; int bufInitialized = 0; xmlSecByte* bufData; DWORD bufLen = 0; BCRYPT_ECCKEY_BLOB* eckey; const xmlChar* curve; int ret; int res = -1; xmlSecAssert2(id == xmlSecMSCngKeyDataEcId, -1); xmlSecAssert2(data != NULL, -1); xmlSecAssert2(xmlSecKeyDataCheckId(data, xmlSecMSCngKeyDataEcId), -1); xmlSecAssert2(ecValue != NULL, -1); ctx = xmlSecMSCngKeyDataGetCtx(data); xmlSecAssert2(ctx != NULL, -1); xmlSecAssert2(ctx->pubkey, -1); /* turn ctx->pubkey into eckey */ status = BCryptExportKey(ctx->pubkey, NULL, BCRYPT_ECCPUBLIC_BLOB, NULL, 0, &bufLen, 0); if ((status != STATUS_SUCCESS) || (bufLen <= 0)) { xmlSecMSCngNtError2("BCryptExportKey", xmlSecKeyDataKlassGetName(id), status, "bufLen=%lu", bufLen); goto done; } ret = xmlSecBufferInitialize(&buf, bufLen); if (ret < 0) { xmlSecInternalError2("xmlSecBufferInitialize", xmlSecKeyDataKlassGetName(id), "size=%lu", bufLen); goto done; } bufInitialized = 1; bufData = xmlSecBufferGetData(&buf); xmlSecAssert2(bufData != NULL, -1); status = BCryptExportKey(ctx->pubkey, NULL, BCRYPT_ECCPUBLIC_BLOB, bufData, bufLen, &bufLen, 0); if ((status != STATUS_SUCCESS) || (bufLen <= 0)) { xmlSecMSCngNtError2("BCryptExportKey", xmlSecKeyDataKlassGetName(id), status, "bufLen=%lu", bufLen); goto done; } /* check BCRYPT_ECCKEY_BLOB */ if (bufLen < sizeof(BCRYPT_ECCKEY_BLOB)) { xmlSecMSCngNtError2("BCRYPT_ECCKEY_BLOB", xmlSecKeyDataKlassGetName(id), STATUS_SUCCESS, "dwBlobLen=%lu", bufLen); goto done; } eckey = (BCRYPT_ECCKEY_BLOB*)bufData; bufData += sizeof(BCRYPT_ECCKEY_BLOB); bufLen -= (DWORD)sizeof(BCRYPT_ECCKEY_BLOB); if (bufLen != 2 * eckey->cbKey) { xmlSecMSCngNtError3("BCRYPT_ECCKEY_BLOB", xmlSecKeyDataKlassGetName(id), STATUS_SUCCESS, "bufLen=%lu, eckey->cbKey=%lu", bufLen, eckey->cbKey); goto done; } /* curve */ curve = xmlSecOpenSSLKeyDataEcGetOidFromMagic(eckey->dwMagic); if (curve == NULL) { xmlSecInternalError2("xmlSecOpenSSLKeyDataEcGetOidFromMagic", xmlSecKeyDataKlassGetName(id), "magic=%lu", eckey->dwMagic); goto done; } ecValue->curve = xmlStrdup(curve); if (ecValue->curve == NULL) { xmlSecStrdupError(curve, xmlSecKeyDataKlassGetName(id)); goto done; } /* public key is prefixed with magic 0x04 */ xmlSecByte magic[] = { 0x04 }; ret = xmlSecBufferSetData(&(ecValue->pubkey), magic, sizeof(magic)); if (ret < 0) { xmlSecInternalError2("xmlSecBufferSetData(pubkey)", xmlSecKeyDataKlassGetName(id), "magic size=%d", (int)sizeof(magic)); goto done; } ret = xmlSecBufferAppend(&(ecValue->pubkey), bufData, bufLen); if (ret < 0) { xmlSecInternalError2("xmlSecBufferAppend(pubkey)", xmlSecKeyDataKlassGetName(id), "bufLen=%lu", bufLen); goto done; } /* dont reverse blobs as both the XML and CNG works with big-endian */ /* success */ res = 0; done: if (bufInitialized != 0) { xmlSecBufferFinalize(&buf); } return(res); } static int xmlSecMSCngKeyDataEcXmlRead(xmlSecKeyDataId id, xmlSecKeyPtr key, xmlNodePtr node, xmlSecKeyInfoCtxPtr keyInfoCtx) { xmlSecAssert2(id == xmlSecMSCngKeyDataEcId, -1); return(xmlSecKeyDataEcXmlRead(id, key, node, keyInfoCtx, xmlSecMSCngKeyDataEcRead)); } static int xmlSecMSCngKeyDataEcXmlWrite(xmlSecKeyDataId id, xmlSecKeyPtr key, xmlNodePtr node, xmlSecKeyInfoCtxPtr keyInfoCtx) { xmlSecAssert2(id == xmlSecMSCngKeyDataEcId, -1); return(xmlSecKeyDataEcXmlWrite(id, key, node, keyInfoCtx, xmlSecBase64GetDefaultLineSize(), 1, /* add line breaks */ xmlSecMSCngKeyDataEcWrite)); } XMLSEC_MSCNG_CERTKEY_KLASS_EX(Ec, EC, xmlSecKeyDataUsageReadFromFile | xmlSecKeyDataUsageKeyValueNode | xmlSecKeyDataUsageRetrievalMethodNodeXml, xmlSecNodeECKeyValue, xmlSecDSig11Ns, NULL, xmlSecMSCngKeyDataEcXmlRead, xmlSecMSCngKeyDataEcXmlWrite) /** * @brief The MSCng EC CertKey data klass. * @return pointer to MSCng EC key data klass. */ xmlSecKeyDataId xmlSecMSCngKeyDataEcGetKlass(void) { return(&xmlSecMSCngKeyDataEcKlass); } #endif /* XMLSEC_NO_EC */ /****************************************************************************** * * DH key data * *****************************************************************************/ #ifndef XMLSEC_NO_DH static int xmlSecMSCngKeyDataDhWrite(xmlSecKeyDataId id, xmlSecKeyDataPtr data, xmlSecKeyValueDhPtr dhValue, int writePrivateKey XMLSEC_ATTRIBUTE_UNUSED) { xmlSecMSCngKeyDataCtxPtr ctx; int ret; UNREFERENCED_PARAMETER(writePrivateKey); xmlSecAssert2(id == xmlSecMSCngKeyDataDhId, -1); xmlSecAssert2(data != NULL, -1); xmlSecAssert2(xmlSecKeyDataCheckId(data, xmlSecMSCngKeyDataDhId), -1); xmlSecAssert2(dhValue != NULL, -1); ctx = xmlSecMSCngKeyDataGetCtx(data); xmlSecAssert2(ctx != NULL, -1); xmlSecAssert2(ctx->pubkey, -1); ret = xmlSecMSCngKeyDataDhPubkeyWrite(ctx->pubkey, dhValue); if (ret < 0) { xmlSecInternalError("xmlSecMSCngKeyDataDhPubkeyWrite", xmlSecKeyDataKlassGetName(id)); return(-1); } if(xmlSecBufferGetSize(&(ctx->dhQ)) > 0) { ret = xmlSecBufferSetData(&(dhValue->q), xmlSecBufferGetData(&(ctx->dhQ)), xmlSecBufferGetSize(&(ctx->dhQ))); if(ret < 0) { xmlSecInternalError("xmlSecBufferSetData(q)", xmlSecKeyDataKlassGetName(id)); return(-1); } } /* done */ return(0); } static int xmlSecMSCngKeyDataDhXmlRead(xmlSecKeyDataId id, xmlSecKeyPtr key, xmlNodePtr node, xmlSecKeyInfoCtxPtr keyInfoCtx) { xmlSecAssert2(id == xmlSecMSCngKeyDataDhId, -1); return(xmlSecKeyDataDhXmlRead(id, key, node, keyInfoCtx, xmlSecMSCngKeyDataDhRead)); } static int xmlSecMSCngKeyDataDhXmlWrite(xmlSecKeyDataId id, xmlSecKeyPtr key, xmlNodePtr node, xmlSecKeyInfoCtxPtr keyInfoCtx) { xmlSecAssert2(id == xmlSecMSCngKeyDataDhId, -1); return(xmlSecKeyDataDhXmlWrite(id, key, node, keyInfoCtx, xmlSecBase64GetDefaultLineSize(), 1, /* add line breaks */ xmlSecMSCngKeyDataDhWrite)); } XMLSEC_MSCNG_CERTKEY_KLASS_EX(Dh, DH, xmlSecKeyDataUsageReadFromFile | xmlSecKeyDataUsageKeyValueNode | xmlSecKeyDataUsageRetrievalMethodNodeXml, xmlSecNodeDHKeyValue, xmlSecEncNs, NULL, xmlSecMSCngKeyDataDhXmlRead, xmlSecMSCngKeyDataDhXmlWrite) /** * @brief The MSCng DH CertKey data klass. * @return pointer to MSCng DH key data klass. */ xmlSecKeyDataId xmlSecMSCngKeyDataDhGetKlass(void) { return(&xmlSecMSCngKeyDataDhKlass); } #endif /* XMLSEC_NO_DH */ #ifndef XMLSEC_NO_DH static int xmlSecMSCngDhValueInitialize(xmlSecKeyValueDhPtr dhValue) { int ret; xmlSecAssert2(dhValue != NULL, -1); memset(dhValue, 0, sizeof(*dhValue)); ret = xmlSecBufferInitialize(&(dhValue->p), 0); if(ret < 0) { xmlSecInternalError("xmlSecBufferInitialize(p)", NULL); return(-1); } ret = xmlSecBufferInitialize(&(dhValue->q), 0); if(ret < 0) { xmlSecInternalError("xmlSecBufferInitialize(q)", NULL); xmlSecBufferFinalize(&(dhValue->p)); return(-1); } ret = xmlSecBufferInitialize(&(dhValue->generator), 0); if(ret < 0) { xmlSecInternalError("xmlSecBufferInitialize(generator)", NULL); xmlSecBufferFinalize(&(dhValue->q)); xmlSecBufferFinalize(&(dhValue->p)); return(-1); } ret = xmlSecBufferInitialize(&(dhValue->public), 0); if(ret < 0) { xmlSecInternalError("xmlSecBufferInitialize(public)", NULL); xmlSecBufferFinalize(&(dhValue->generator)); xmlSecBufferFinalize(&(dhValue->q)); xmlSecBufferFinalize(&(dhValue->p)); return(-1); } ret = xmlSecBufferInitialize(&(dhValue->seed), 0); if(ret < 0) { xmlSecInternalError("xmlSecBufferInitialize(seed)", NULL); xmlSecBufferFinalize(&(dhValue->public)); xmlSecBufferFinalize(&(dhValue->generator)); xmlSecBufferFinalize(&(dhValue->q)); xmlSecBufferFinalize(&(dhValue->p)); return(-1); } ret = xmlSecBufferInitialize(&(dhValue->pgenCounter), 0); if(ret < 0) { xmlSecInternalError("xmlSecBufferInitialize(pgenCounter)", NULL); xmlSecBufferFinalize(&(dhValue->seed)); xmlSecBufferFinalize(&(dhValue->public)); xmlSecBufferFinalize(&(dhValue->generator)); xmlSecBufferFinalize(&(dhValue->q)); xmlSecBufferFinalize(&(dhValue->p)); return(-1); } return(0); } static void xmlSecMSCngDhValueFinalize(xmlSecKeyValueDhPtr dhValue) { xmlSecAssert(dhValue != NULL); xmlSecBufferFinalize(&(dhValue->p)); xmlSecBufferFinalize(&(dhValue->q)); xmlSecBufferFinalize(&(dhValue->generator)); xmlSecBufferFinalize(&(dhValue->public)); xmlSecBufferFinalize(&(dhValue->seed)); xmlSecBufferFinalize(&(dhValue->pgenCounter)); memset(dhValue, 0, sizeof(*dhValue)); } static int xmlSecMSCngKeyDataDhExportValue(xmlSecKeyDataPtr data, xmlSecKeyValueDhPtr dhValue) { xmlSecMSCngKeyDataCtxPtr ctx; int ret; xmlSecAssert2(data != NULL, -1); xmlSecAssert2(xmlSecKeyDataCheckId(data, xmlSecMSCngKeyDataDhId), -1); xmlSecAssert2(dhValue != NULL, -1); ctx = xmlSecMSCngKeyDataGetCtx(data); xmlSecAssert2(ctx != NULL, -1); xmlSecAssert2(ctx->pubkey != NULL, -1); ret = xmlSecMSCngKeyDataDhPubkeyWrite(ctx->pubkey, dhValue); if(ret < 0) { xmlSecInternalError("xmlSecMSCngKeyDataDhPubkeyWrite", NULL); return(-1); } if(xmlSecBufferGetSize(&(ctx->dhQ)) > 0) { ret = xmlSecBufferSetData(&(dhValue->q), xmlSecBufferGetData(&(ctx->dhQ)), xmlSecBufferGetSize(&(ctx->dhQ))); if(ret < 0) { xmlSecInternalError("xmlSecBufferSetData(q)", NULL); return(-1); } } return(0); } static int xmlSecMSCngDhCompareBigNums(const xmlSecByte* left, xmlSecSize leftSize, const xmlSecByte* right, xmlSecSize rightSize) { while((leftSize > 0) && (left[0] == 0)) { ++left; --leftSize; } while((rightSize > 0) && (right[0] == 0)) { ++right; --rightSize; } if(leftSize != rightSize) { return((leftSize > rightSize) ? 1 : -1); } if(leftSize == 0) { return(0); } return(memcmp(left, right, leftSize)); } static int xmlSecMSCngDhEnsureEqualParam(xmlSecBufferPtr left, xmlSecBufferPtr right, const char* name) { int cmp; xmlSecAssert2(left != NULL, -1); xmlSecAssert2(right != NULL, -1); cmp = xmlSecMSCngDhCompareBigNums( xmlSecBufferGetData(left), xmlSecBufferGetSize(left), xmlSecBufferGetData(right), xmlSecBufferGetSize(right)); if(cmp != 0) { xmlSecOtherError2(XMLSEC_ERRORS_R_INVALID_DATA, NULL, "DH group parameter mismatch: %s", xmlSecErrorsSafeString(name)); return(-1); } return(0); } static int xmlSecMSCngDhValidatePublicRange(xmlSecBufferPtr p, xmlSecBufferPtr publicValue) { static const xmlSecByte two[] = { 0x02 }; xmlSecBn pMinusTwo; int bnInitialized = 0; int cmp; int ret; xmlSecAssert2(p != NULL, -1); xmlSecAssert2(publicValue != NULL, -1); cmp = xmlSecMSCngDhCompareBigNums( xmlSecBufferGetData(publicValue), xmlSecBufferGetSize(publicValue), two, sizeof(two)); if(cmp < 0) { xmlSecInvalidDataError("DH public key is smaller than 2", NULL); return(-1); } ret = xmlSecBnInitialize(&pMinusTwo, xmlSecBufferGetSize(p)); if(ret < 0) { xmlSecInternalError("xmlSecBnInitialize", NULL); return(-1); } bnInitialized = 1; ret = xmlSecBnSetData(&pMinusTwo, xmlSecBufferGetData(p), xmlSecBufferGetSize(p)); if(ret < 0) { xmlSecInternalError("xmlSecBnSetData", NULL); goto done; } ret = xmlSecBnAdd(&pMinusTwo, -2); if(ret < 0) { xmlSecInternalError("xmlSecBnAdd", NULL); goto done; } cmp = xmlSecBnCompare(&pMinusTwo, xmlSecBufferGetData(publicValue), xmlSecBufferGetSize(publicValue)); if(cmp < 0) { xmlSecInvalidDataError("DH public key is greater than p - 2", NULL); ret = -1; goto done; } ret = 0; done: if(bnInitialized != 0) { xmlSecBnFinalize(&pMinusTwo); } return(ret); } static int xmlSecMSCngDhValidatePublicSubgroup(xmlSecBufferPtr p, xmlSecBufferPtr g, xmlSecBufferPtr q, BCRYPT_KEY_HANDLE publicKey) { DWORD cbKey; DWORD cbPrivBlob; BCRYPT_DH_KEY_BLOB* dhPriv; BCRYPT_ALG_HANDLE hAlg = NULL; BCRYPT_KEY_HANDLE hQPrivKey = NULL; BCRYPT_SECRET_HANDLE hSecret = NULL; PUCHAR pbPrivBlob = NULL; PUCHAR pbSecret = NULL; DWORD cbSecret = 0; NTSTATUS status; int ret = -1; xmlSecAssert2(p != NULL, -1); xmlSecAssert2(g != NULL, -1); xmlSecAssert2(q != NULL, -1); xmlSecAssert2(publicKey != NULL, -1); xmlSecAssert2(xmlSecBufferGetSize(p) > 0, -1); xmlSecAssert2(xmlSecBufferGetSize(g) > 0, -1); xmlSecAssert2(xmlSecBufferGetSize(q) > 0, -1); XMLSEC_SAFE_CAST_SIZE_TO_UINT(xmlSecBufferGetSize(p), cbKey, return(-1), NULL); if(cbKey > XMLSEC_MSCNG_DSA_MAX_P_SIZE) { xmlSecInvalidSizeMoreThanError("DSA P size", (xmlSecSize)cbKey, (xmlSecSize)XMLSEC_MSCNG_DSA_MAX_P_SIZE, NULL); goto done; } cbPrivBlob = (DWORD)sizeof(BCRYPT_DH_KEY_BLOB) + cbKey * 4U; pbPrivBlob = (PUCHAR)xmlMalloc(cbPrivBlob); if(pbPrivBlob == NULL) { xmlSecMallocError(cbPrivBlob, NULL); goto done; } memset(pbPrivBlob, 0, cbPrivBlob); dhPriv = (BCRYPT_DH_KEY_BLOB*)pbPrivBlob; dhPriv->dwMagic = BCRYPT_DH_PRIVATE_MAGIC; dhPriv->cbKey = cbKey; ret = xmlSecMSCngDhBlobCopy(pbPrivBlob + sizeof(BCRYPT_DH_KEY_BLOB), cbKey, xmlSecBufferGetData(p), cbKey); if(ret < 0) { xmlSecInternalError("xmlSecMSCngDhBlobCopy(P)", NULL); goto done; } ret = xmlSecMSCngDhBlobCopy(pbPrivBlob + sizeof(BCRYPT_DH_KEY_BLOB) + cbKey, cbKey, xmlSecBufferGetData(g), xmlSecBufferGetSize(g)); if(ret < 0) { xmlSecInternalError("xmlSecMSCngDhBlobCopy(G)", NULL); goto done; } ret = xmlSecMSCngDhBlobCopy(pbPrivBlob + sizeof(BCRYPT_DH_KEY_BLOB) + cbKey * 2, cbKey, xmlSecBufferGetData(g), xmlSecBufferGetSize(g)); if(ret < 0) { xmlSecInternalError("xmlSecMSCngDhBlobCopy(Y-placeholder)", NULL); goto done; } ret = xmlSecMSCngDhBlobCopy(pbPrivBlob + sizeof(BCRYPT_DH_KEY_BLOB) + cbKey * 3, cbKey, xmlSecBufferGetData(q), xmlSecBufferGetSize(q)); if(ret < 0) { xmlSecInternalError("xmlSecMSCngDhBlobCopy(Q)", NULL); goto done; } status = BCryptOpenAlgorithmProvider(&hAlg, BCRYPT_DH_ALGORITHM, NULL, 0); if(status != STATUS_SUCCESS) { xmlSecMSCngNtError("BCryptOpenAlgorithmProvider(DH subgroup)", NULL, status); goto done; } status = BCryptImportKeyPair(hAlg, NULL, BCRYPT_DH_PRIVATE_BLOB, &hQPrivKey, pbPrivBlob, cbPrivBlob, 0); if(status != STATUS_SUCCESS) { xmlSecMSCngNtError("BCryptImportKeyPair(DH subgroup)", NULL, status); goto done; } status = BCryptSecretAgreement(hQPrivKey, publicKey, &hSecret, 0); if((status != STATUS_SUCCESS) || (hSecret == NULL)) { xmlSecMSCngNtError("BCryptSecretAgreement(DH subgroup)", NULL, status); goto done; } status = BCryptDeriveKey(hSecret, BCRYPT_KDF_RAW_SECRET, NULL, NULL, 0, &cbSecret, 0); if((status != STATUS_SUCCESS) || (cbSecret == 0)) { xmlSecMSCngNtError("BCryptDeriveKey(DH subgroup size)", NULL, status); goto done; } pbSecret = (PUCHAR)xmlMalloc(cbSecret); if(pbSecret == NULL) { xmlSecMallocError(cbSecret, NULL); goto done; } status = BCryptDeriveKey(hSecret, BCRYPT_KDF_RAW_SECRET, NULL, pbSecret, cbSecret, &cbSecret, 0); if((status != STATUS_SUCCESS) || (cbSecret == 0)) { xmlSecMSCngNtError("BCryptDeriveKey(DH subgroup data)", NULL, status); goto done; } if(pbSecret[0] != 0x01) { xmlSecInvalidDataError("DH public key is not in the expected subgroup", NULL); goto done; } while(cbSecret > 1) { --cbSecret; if(pbSecret[cbSecret] != 0x00) { xmlSecInvalidDataError("DH public key is not in the expected subgroup", NULL); goto done; } } ret = 0; done: if(pbSecret != NULL) { xmlFree(pbSecret); } if(hSecret != NULL) { BCryptDestroySecret(hSecret); } if(hQPrivKey != NULL) { BCryptDestroyKey(hQPrivKey); } if(hAlg != NULL) { BCryptCloseAlgorithmProvider(hAlg, 0); } if(pbPrivBlob != NULL) { xmlFree(pbPrivBlob); } return(ret); } int xmlSecMSCngKeyDataDhEnsureValidAgreement(xmlSecKeyDataPtr myData, xmlSecKeyDataPtr otherData) { xmlSecMSCngKeyDataCtxPtr myCtx; xmlSecMSCngKeyDataCtxPtr otherCtx; xmlSecKeyValueDh myDh; xmlSecKeyValueDh otherDh; int myDhInitialized = 0; int otherDhInitialized = 0; int ret; xmlSecAssert2(myData != NULL, -1); xmlSecAssert2(otherData != NULL, -1); xmlSecAssert2(xmlSecKeyDataCheckId(myData, xmlSecMSCngKeyDataDhId), -1); xmlSecAssert2(xmlSecKeyDataCheckId(otherData, xmlSecMSCngKeyDataDhId), -1); myCtx = xmlSecMSCngKeyDataGetCtx(myData); otherCtx = xmlSecMSCngKeyDataGetCtx(otherData); xmlSecAssert2(myCtx != NULL, -1); xmlSecAssert2(otherCtx != NULL, -1); xmlSecAssert2(myCtx->pubkey != NULL, -1); xmlSecAssert2(otherCtx->pubkey != NULL, -1); ret = xmlSecMSCngDhValueInitialize(&myDh); if(ret < 0) { return(-1); } myDhInitialized = 1; ret = xmlSecMSCngDhValueInitialize(&otherDh); if(ret < 0) { goto done; } otherDhInitialized = 1; ret = xmlSecMSCngKeyDataDhExportValue(myData, &myDh); if(ret < 0) { xmlSecInternalError("xmlSecMSCngKeyDataDhExportValue(my)", NULL); goto done; } ret = xmlSecMSCngKeyDataDhExportValue(otherData, &otherDh); if(ret < 0) { xmlSecInternalError("xmlSecMSCngKeyDataDhExportValue(other)", NULL); goto done; } if((xmlSecBufferGetSize(&(myDh.q)) == 0) || (xmlSecBufferGetSize(&(otherDh.q)) == 0)) { xmlSecInvalidDataError("DH subgroup order q is required for DH-ES", NULL); ret = -1; goto done; } ret = xmlSecMSCngDhEnsureEqualParam(&(myDh.p), &(otherDh.p), "p"); if(ret < 0) { goto done; } ret = xmlSecMSCngDhEnsureEqualParam(&(myDh.generator), &(otherDh.generator), "g"); if(ret < 0) { goto done; } ret = xmlSecMSCngDhEnsureEqualParam(&(myDh.q), &(otherDh.q), "q"); if(ret < 0) { goto done; } ret = xmlSecMSCngDhValidatePublicRange(&(myDh.p), &(myDh.public)); if(ret < 0) { goto done; } ret = xmlSecMSCngDhValidatePublicRange(&(otherDh.p), &(otherDh.public)); if(ret < 0) { goto done; } ret = xmlSecMSCngDhValidatePublicSubgroup(&(myDh.p), &(myDh.generator), &(myDh.q), myCtx->pubkey); if(ret < 0) { goto done; } ret = xmlSecMSCngDhValidatePublicSubgroup(&(otherDh.p), &(otherDh.generator), &(otherDh.q), otherCtx->pubkey); if(ret < 0) { goto done; } ret = 0; done: if(otherDhInitialized != 0) { xmlSecMSCngDhValueFinalize(&otherDh); } if(myDhInitialized != 0) { xmlSecMSCngDhValueFinalize(&myDh); } return(ret); } #endif /* XMLSEC_NO_DH */ /** * @brief Exports a BCrypt public key as DER SPKI blob. * @details Exports a BCrypt public key as a DER-encoded SubjectPublicKeyInfo blob. * @param data the MSCng key data. * @param ppDer output pointer for the DER-encoded SubjectPublicKeyInfo blob (caller must LocalFree). * @param pcbDer output length of the DER blob. * @return 0 on success or a negative value if an error occurs. */ int xmlSecMSCngCreateDerForBcryptPubkey(xmlSecKeyDataPtr data, LPVOID* ppDer, DWORD* pcbDer) { BCRYPT_KEY_HANDLE hPubkey; PUCHAR pInfo = NULL; DWORD cbInfo = 0; BOOL status; int ret; xmlSecAssert2(data != NULL, -1); xmlSecAssert2(ppDer != NULL, -1); xmlSecAssert2(pcbDer != NULL, -1); hPubkey = xmlSecMSCngKeyDataGetPubKey(data); if(hPubkey == 0) { xmlSecInternalError("xmlSecMSCngKeyDataGetPubKey", NULL); return(-1); } *ppDer = NULL; *pcbDer = 0; #ifndef XMLSEC_NO_DSA ret = xmlSecMSCngIsDsaBcryptKey(hPubkey); if(ret < 0) { xmlSecInternalError("xmlSecMSCngIsDsaBcryptKey", NULL); return(-1); } if(ret == 1) { return xmlSecMSCngDsaBuildSubjectPublicKeyInfoDer(hPubkey, ppDer, pcbDer); } #endif /* XMLSEC_NO_DSA */ status = CryptExportPublicKeyInfoFromBCryptKeyHandle( hPubkey, X509_ASN_ENCODING, NULL, 0, NULL, NULL, &cbInfo ); if((status != TRUE) || (cbInfo <= 0)) { xmlSecMSCngNtError("CryptExportPublicKeyInfoFromBCryptKeyHandle", NULL, STATUS_SUCCESS); goto done; } pInfo = (PUCHAR)xmlMalloc(cbInfo); if(pInfo == NULL) { xmlSecMallocError(cbInfo, NULL); goto done; } status = CryptExportPublicKeyInfoFromBCryptKeyHandle( hPubkey, X509_ASN_ENCODING, NULL, 0, NULL, (PCERT_PUBLIC_KEY_INFO)pInfo, &cbInfo ); if((status != TRUE) || (cbInfo <= 0)) { xmlSecMSCngNtError("CryptExportPublicKeyInfoFromBCryptKeyHandle", NULL, STATUS_SUCCESS); goto done; } status = CryptEncodeObjectEx( X509_ASN_ENCODING, X509_PUBLIC_KEY_INFO, pInfo, CRYPT_ENCODE_ALLOC_FLAG, NULL, ppDer, pcbDer ); if((status != TRUE) || (*ppDer == NULL) || (*pcbDer <= 0)) { xmlSecMSCngNtError("CryptEncodeObjectEx", NULL, STATUS_SUCCESS); goto done; } xmlFree(pInfo); return(0); done: if(pInfo != NULL) { xmlFree(pInfo); } if(*ppDer != NULL) { LocalFree(*ppDer); *ppDer = NULL; } *pcbDer = 0; return(-1); } /** * @brief Loads a public key of any supported type (RSA, DSA, EC, DH) from a raw * @param derData DER-encoded SubjectPublicKeyInfo. * @param derDataLen length of @p derData. * * SubjectPublicKeyInfo DER blob. * * @return new key data or NULL on failure. */ xmlSecKeyDataPtr xmlSecMSCngAppKeyReadPubKeyFromDer(const xmlSecByte* derData, DWORD derDataLen) { xmlSecKeyDataPtr data = NULL; xmlSecKeyDataPtr res = NULL; CERT_PUBLIC_KEY_INFO* spki = NULL; DWORD spkiLen = 0; BCRYPT_KEY_HANDLE hPubKey = NULL; int ret; xmlSecAssert2(derData != NULL, NULL); xmlSecAssert2(derDataLen > 0, NULL); /* Decode SubjectPublicKeyInfo */ if(!CryptDecodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, X509_PUBLIC_KEY_INFO, derData, derDataLen, CRYPT_DECODE_ALLOC_FLAG | CRYPT_DECODE_NOCOPY_FLAG, NULL, &spki, &spkiLen)) { xmlSecMSCngLastError("CryptDecodeObjectEx(SPKI)", NULL); goto done; } if(spki == NULL || spki->Algorithm.pszObjId == NULL) { xmlSecInternalError("CryptDecodeObjectEx returned NULL or no OID", NULL); goto done; } #ifndef XMLSEC_NO_DH /* DH public keys require custom BCrypt blob construction since * CryptImportPublicKeyInfoEx2 does not support X9.42 DH (szOID_X942_DH). */ if(strcmp(spki->Algorithm.pszObjId, szOID_X942_DH) == 0) { const xmlSecByte* pP = NULL; DWORD pPLen = 0; const xmlSecByte* pG = NULL; DWORD pGLen = 0; const xmlSecByte* pY = NULL; DWORD pYLen = 0; DWORD cbKey; PUCHAR pbPubBlob = NULL; DWORD cbPubBlob; BCRYPT_DH_KEY_BLOB* dhPub; BCRYPT_ALG_HANDLE hAlg = NULL; NTSTATUS status; if(spki->Algorithm.Parameters.cbData == 0 || spki->Algorithm.Parameters.pbData == NULL) { xmlSecInternalError("DH SPKI: missing AlgorithmIdentifier.Parameters", NULL); goto done; } if(spki->PublicKey.cbData == 0 || spki->PublicKey.pbData == NULL) { xmlSecInternalError("DH SPKI: missing PublicKey bits", NULL); goto done; } const xmlSecByte* pQ = NULL; DWORD pQLen = 0; ret = xmlSecMSCngDhParseDhParameters( spki->Algorithm.Parameters.pbData, spki->Algorithm.Parameters.cbData, &pP, &pPLen, &pG, &pGLen, &pQ, &pQLen); if(ret < 0) { xmlSecInternalError("xmlSecMSCngDhParseDhParameters", NULL); goto done; } /* Parse Y from PublicKey bits (BIT STRING content = DER INTEGER Y) */ { const xmlSecByte* pkBits = spki->PublicKey.pbData; DWORD pkBitsLen = spki->PublicKey.cbData; if(pkBitsLen > 1 && *pkBits == 0x00) { pkBits++; pkBitsLen--; } pY = xmlSecMSCngDerDecodeInteger(pkBits, pkBits + pkBitsLen, &pYLen); if(pY == NULL) { xmlSecInternalError("DH SPKI: failed to parse public key INTEGER Y", NULL); goto done; } } cbKey = pPLen; cbPubBlob = sizeof(BCRYPT_DH_KEY_BLOB) + cbKey * 3; pbPubBlob = (PUCHAR)xmlMalloc(cbPubBlob); if(pbPubBlob == NULL) { xmlSecMallocError(cbPubBlob, NULL); goto done; } memset(pbPubBlob, 0, cbPubBlob); dhPub = (BCRYPT_DH_KEY_BLOB*)pbPubBlob; dhPub->dwMagic = BCRYPT_DH_PUBLIC_MAGIC; dhPub->cbKey = cbKey; ret = xmlSecMSCngDhBlobCopy(pbPubBlob + sizeof(BCRYPT_DH_KEY_BLOB), cbKey, pP, pPLen); if(ret < 0) { xmlSecInternalError("xmlSecMSCngDhBlobCopy(P)", NULL); xmlFree(pbPubBlob); goto done; } ret = xmlSecMSCngDhBlobCopy(pbPubBlob + sizeof(BCRYPT_DH_KEY_BLOB) + cbKey, cbKey, pG, pGLen); if(ret < 0) { xmlSecInternalError("xmlSecMSCngDhBlobCopy(G)", NULL); xmlFree(pbPubBlob); goto done; } ret = xmlSecMSCngDhBlobCopy(pbPubBlob + sizeof(BCRYPT_DH_KEY_BLOB) + cbKey * 2, cbKey, pY, pYLen); if(ret < 0) { xmlSecInternalError("xmlSecMSCngDhBlobCopy(Y)", NULL); xmlFree(pbPubBlob); goto done; } status = BCryptOpenAlgorithmProvider(&hAlg, BCRYPT_DH_ALGORITHM, NULL, 0); if(status != STATUS_SUCCESS) { xmlFree(pbPubBlob); xmlSecMSCngNtError("BCryptOpenAlgorithmProvider(DH pub)", NULL, status); goto done; } status = BCryptImportKeyPair(hAlg, NULL, BCRYPT_DH_PUBLIC_BLOB, &hPubKey, pbPubBlob, cbPubBlob, 0); BCryptCloseAlgorithmProvider(hAlg, 0); xmlFree(pbPubBlob); if(status != STATUS_SUCCESS) { xmlSecMSCngNtError("BCryptImportKeyPair(DH pub SPKI)", NULL, status); goto done; } data = xmlSecKeyDataCreate(xmlSecMSCngKeyDataDhId); if(data == NULL) { xmlSecInternalError("xmlSecKeyDataCreate(DH)", NULL); goto done; } ret = xmlSecMSCngKeyDataAdoptKey(data, hPubKey); if(ret < 0) { xmlSecInternalError("xmlSecMSCngKeyDataAdoptKey(DH pub)", NULL); goto done; } hPubKey = NULL; /* owned by data */ ret = xmlSecMSCngKeyDataSetDhQ(data, pQ, pQLen); if(ret < 0) { xmlSecInternalError("xmlSecMSCngKeyDataSetDhQ", NULL); goto done; } res = data; data = NULL; goto done; } #endif /* XMLSEC_NO_DH */ /* All other key types: RSA, DSA, EC */ ret = xmlSecMSCngKeyDataCertGetPubkey(spki, &hPubKey); if(ret < 0) { xmlSecInternalError("xmlSecMSCngKeyDataCertGetPubkey", NULL); goto done; } data = xmlSecMSCngKeyDataFromAlgorithm(spki->Algorithm.pszObjId); if(data == NULL) { xmlSecInternalError("xmlSecMSCngKeyDataFromAlgorithm", NULL); goto done; } ret = xmlSecMSCngKeyDataAdoptKey(data, hPubKey); if(ret < 0) { xmlSecInternalError("xmlSecMSCngKeyDataAdoptKey", NULL); goto done; } hPubKey = NULL; /* owned by data */ res = data; data = NULL; done: if(hPubKey != NULL) { BCryptDestroyKey(hPubKey); } if(data != NULL) { xmlSecKeyDataDestroy(data); } if(spki != NULL) { LocalFree(spki); } return(res); } /** * @brief Loads a private key from a raw PKCS8 PrivateKeyInfo DER blob. * @param data DER-encoded PKCS8 PrivateKeyInfo blob. * @param dataSize length of @p data. * * Currently only DH private keys are supported. * * @return new key data or NULL on failure. */ xmlSecKeyDataPtr xmlSecMSCngAppKeyReadPrivKeyFromDer(const xmlSecByte* data, DWORD dataSize) { xmlSecAssert2(data != NULL, NULL); xmlSecAssert2(dataSize > 0, NULL); #ifndef XMLSEC_NO_DH { xmlSecKeyDataPtr res; res = xmlSecMSCngKeyDataDhReadFromPkcs8Der(data, dataSize); if(res != NULL) { return(res); } } #endif /* XMLSEC_NO_DH */ #ifndef XMLSEC_NO_XDH { xmlSecKeyDataPtr res; res = xmlSecMSCngKeyDataXdhReadFromPkcs8Der(data, dataSize); if(res != NULL) { return(res); } } #endif /* XMLSEC_NO_XDH */ xmlSecNotImplementedError("Only DH and XDH private keys are supported in DER format"); return(NULL); } /****************************************************************************** * XDH (X25519) key data *****************************************************************************/ #ifndef XMLSEC_NO_XDH XMLSEC_MSCNG_CERTKEY_KLASS_EX(Xdh, XDH, xmlSecKeyDataUsageReadFromFile | xmlSecKeyDataUsageRetrievalMethodNodeXml, NULL, /* dataNodeName */ NULL, /* dataNodeNs */ NULL, /* generate (not supported) */ NULL, /* xmlRead */ NULL) /* xmlWrite */ /** * @brief The MSCng XDH (X25519) key data klass. * @return pointer to MSCng XDH key data klass. */ xmlSecKeyDataId xmlSecMSCngKeyDataXdhGetKlass(void) { return(&xmlSecMSCngKeyDataXdhKlass); } #endif /* XMLSEC_NO_XDH */