diff options
Diffstat (limited to 'src/mscng/certkeys.c')
-rw-r--r-- | src/mscng/certkeys.c | 1649 |
1 files changed, 1649 insertions, 0 deletions
diff --git a/src/mscng/certkeys.c b/src/mscng/certkeys.c new file mode 100644 index 00000000..55cfde26 --- /dev/null +++ b/src/mscng/certkeys.c @@ -0,0 +1,1649 @@ +/* + * XML Security Library (http://www.aleksey.com/xmlsec). + * + * + * This is free software; see Copyright file in the source + * distribution for preciese wording. + * + * Copyright (C) 2018 Miklos Vajna. All Rights Reserved. + */ +/** + * SECTION:certkeys + * @Short_description: Certificate keys support functions for Microsoft Cryptography API: Next Generation (CNG). + * @Stability: Stable + * + */ + +#include "globals.h" + +#include <string.h> + +#define WIN32_NO_STATUS +#include <windows.h> +#undef WIN32_NO_STATUS +#include <wincrypt.h> +#include <ntstatus.h> +#include <bcrypt.h> +#include <ncrypt.h> + +#include <xmlsec/xmlsec.h> +#include <xmlsec/xmltree.h> +#include <xmlsec/keys.h> +#include <xmlsec/keyinfo.h> +#include <xmlsec/transforms.h> +#include <xmlsec/errors.h> +#include <xmlsec/bn.h> + +#include <xmlsec/mscng/crypto.h> + +typedef struct _xmlSecMSCngKeyDataCtx xmlSecMSCngKeyDataCtx, + *xmlSecMSCngKeyDataCtxPtr; + +struct _xmlSecMSCngKeyDataCtx { + PCCERT_CONTEXT cert; + NCRYPT_KEY_HANDLE privkey; + BCRYPT_KEY_HANDLE pubkey; +}; + +#define xmlSecMSCngKeyDataSize \ + (sizeof(xmlSecKeyData) + sizeof(xmlSecMSCngKeyDataCtx)) +#define xmlSecMSCngKeyDataGetCtx(data) \ + ((xmlSecMSCngKeyDataCtxPtr)(((xmlSecByte*)(data)) + sizeof(xmlSecKeyData))) + +static int xmlSecMSCngKeyDataGetSize(xmlSecKeyDataPtr data); + +static int +xmlSecMSCngKeyDataCertGetPubkey(PCCERT_CONTEXT cert, BCRYPT_KEY_HANDLE* key) { + xmlSecAssert2(cert != NULL, -1); + xmlSecAssert2(key != NULL, -1); + + if(!CryptImportPublicKeyInfoEx2(X509_ASN_ENCODING, + &cert->pCertInfo->SubjectPublicKeyInfo, + 0, + NULL, + key)) { + xmlSecMSCngLastError("CryptImportPublicKeyInfoEx2", NULL); + return(-1); + } + + return(0); +} + +static int +xmlSecMSCngKeyDataCertGetPrivkey(PCCERT_CONTEXT cert, NCRYPT_KEY_HANDLE* key) { + int ret; + + xmlSecAssert2(cert != NULL, -1); + xmlSecAssert2(key != NULL, -1); + + DWORD keySpec = 0; + BOOL callerFree = FALSE; + + ret = CryptAcquireCertificatePrivateKey( + cert, + CRYPT_ACQUIRE_COMPARE_KEY_FLAG | CRYPT_ACQUIRE_ONLY_NCRYPT_KEY_FLAG, + NULL, + key, + &keySpec, + &callerFree); + if(ret == FALSE) { + xmlSecMSCngLastError("CryptAcquireCertificatePrivateKey", NULL); + return(-1); + } + + return(0); +} + +/** + * xmlSecMSCngKeyDataAdoptCert: + * @data: the pointer to MSCng pccert data. + * @cert: the pointer to PCCERT key. + * + * Sets the value of key data. + * + * Returns: 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; + + ret = xmlSecMSCngKeyDataCertGetPrivkey(cert, &hPrivKey); + if(ret < 0) { + xmlSecInternalError("xmlSecMSCngKeyDataCertGetPrivkey", NULL); + return(-1); + } + + ctx->privkey = hPrivKey; + } + + ret = xmlSecMSCngKeyDataCertGetPubkey(cert, &hPubKey); + if(ret < 0) { + xmlSecInternalError("xmlSecMSCngKeyDataCertGetPubkey", NULL); + return(-1); + } + + ctx->pubkey = hPubKey; + ctx->cert = cert; + + return(0); +} + +static 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); +} + +/** + * xmlSecMSCngCertAdopt: + * @pCert: the pointer to cert. + * @type: the expected key type. + * + * Creates key data value from the cert. + * + * Returns: 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); + +#ifndef XMLSEC_NO_DSA + if(!strcmp(pCert->pCertInfo->SubjectPublicKeyInfo.Algorithm.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(pCert->pCertInfo->SubjectPublicKeyInfo.Algorithm.pszObjId, szOID_RSA_RSA)) { + data = xmlSecKeyDataCreate(xmlSecMSCngKeyDataRsaId); + if(data == NULL) { + xmlSecInternalError("xmlSecKeyDataCreate(KeyDataRsaId)", NULL); + return(NULL); + } + } +#endif /* XMLSEC_NO_RSA */ + +#ifndef XMLSEC_NO_ECDSA + if(!strcmp(pCert->pCertInfo->SubjectPublicKeyInfo.Algorithm.pszObjId, szOID_ECC_PUBLIC_KEY)) { + data = xmlSecKeyDataCreate(xmlSecMSCngKeyDataEcdsaId); + if(data == NULL) { + xmlSecInternalError("xmlSecKeyDataCreate(KeyDataEcdsaId)", NULL); + return(NULL); + } + } +#endif /* XMLSEC_NO_ECDSA */ + + if(data == NULL) { + xmlSecInvalidStringTypeError("PCCERT_CONTEXT key type", + pCert->pCertInfo->SubjectPublicKeyInfo.Algorithm.pszObjId, + "unsupported keytype", + NULL); + return(NULL); + } + + ret = xmlSecMSCngKeyDataAdoptCert(data, pCert, type); + if(ret < 0) { + xmlSecInternalError("xmlSecMSCngKeyDataAdoptCert", NULL); + xmlSecKeyDataDestroy(data); + return(NULL); + } + + return(data); +} + +/** + * xmlSecMSCngKeyDataGetPubKey: + * @data: the key data to retrieve certificate from. + * + * Native MSCng public key retrieval from xmlsec keydata. The returned key must + * not be destroyed by the caller. + * + * Returns: 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); +} + +/** + * xmlSecMSCngKeyDataGetPrivKey: + * @data: the key data to retrieve certificate from. + * + * Native MSCng private key retrieval from xmlsec keydata. The returned key + * must not be destroyed by the caller. + * + * Returns: 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 +xmlSecMSCngKeyDataInitialize(xmlSecKeyDataPtr data) { + xmlSecMSCngKeyDataCtxPtr ctx; + + xmlSecAssert2(xmlSecKeyDataIsValid(data), -1); + xmlSecAssert2(xmlSecKeyDataCheckSize(data, xmlSecMSCngKeyDataSize), -1); + + ctx = xmlSecMSCngKeyDataGetCtx(data); + xmlSecAssert2(ctx != NULL, -1); + + memset(ctx, 0, sizeof(xmlSecMSCngKeyDataCtx)); + + return(0); +} + +static void +xmlSecMSCngKeyDataFinalize(xmlSecKeyDataPtr data) { + xmlSecMSCngKeyDataCtxPtr ctx; + NTSTATUS status; + + xmlSecAssert(xmlSecKeyDataIsValid(data)); + xmlSecAssert(xmlSecKeyDataCheckSize(data, xmlSecMSCngKeyDataSize)); + + ctx = xmlSecMSCngKeyDataGetCtx(data); + xmlSecAssert(ctx != NULL); + + if(ctx->privkey != 0) { + status = NCryptFreeObject(ctx->privkey); + if(status != STATUS_SUCCESS) { + xmlSecMSCngNtError("BCryptDestroyKey", NULL, status); + } + } + + if(ctx->pubkey != 0) { + status = BCryptDestroyKey(ctx->pubkey); + if(status != STATUS_SUCCESS) { + xmlSecMSCngNtError("BCryptDestroyKey", NULL, status); + } + } + + if(ctx->cert != NULL) { + CertFreeCertificateContext(ctx->cert); + } + + memset(ctx, 0, sizeof(xmlSecMSCngKeyDataCtx)); +} + +static int +xmlSecMSCngKeyDataDuplicate(xmlSecKeyDataPtr dst, xmlSecKeyDataPtr src) { + xmlSecMSCngKeyDataCtxPtr dstCtx; + xmlSecMSCngKeyDataCtxPtr srcCtx; + NTSTATUS status; + DWORD cbBlob = 0; + PUCHAR pbBlob; + BCRYPT_ALG_HANDLE hAlg = NULL; + LPCWSTR pszAlgId; + 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); + + 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); + 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, &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: + pszAlgId = BCRYPT_DSA_ALGORITHM; + break; +#endif +#ifndef XMLSEC_NO_RSA + case BCRYPT_RSAPUBLIC_MAGIC: + pszAlgId = BCRYPT_RSA_ALGORITHM; + break; +#endif + default: + xmlSecNotImplementedError(NULL); + xmlFree(pbBlob); + return(-1); + } + + status = BCryptOpenAlgorithmProvider( + &hAlg, + pszAlgId, + NULL, + 0); + if(status != STATUS_SUCCESS) { + xmlSecMSCngNtError("BCryptOpenAlgorithmProvider", + NULL, status); + xmlFree(pbBlob); + 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); + } + + return(0); +} + +#ifndef XMLSEC_NO_DSA +static int +xmlSecMSCngKeyDataDsaDuplicate(xmlSecKeyDataPtr dst, xmlSecKeyDataPtr src) { + xmlSecAssert2(xmlSecKeyDataCheckId(dst, xmlSecMSCngKeyDataDsaId), -1); + xmlSecAssert2(xmlSecKeyDataCheckId(src, xmlSecMSCngKeyDataDsaId), -1); + + return(xmlSecMSCngKeyDataDuplicate(dst, src)); +} + +static xmlSecKeyDataType +xmlSecMSCngKeyDataDsaGetType(xmlSecKeyDataPtr data) { + xmlSecMSCngKeyDataCtxPtr ctx; + + xmlSecAssert2(xmlSecKeyDataCheckId(data, xmlSecMSCngKeyDataDsaId), xmlSecKeyDataTypeUnknown); + + ctx = xmlSecMSCngKeyDataGetCtx(data); + xmlSecAssert2(ctx != NULL, xmlSecKeyDataTypeUnknown); + + if(ctx->privkey != 0) { + return(xmlSecKeyDataTypePrivate | xmlSecKeyDataTypePublic); + } + + return(xmlSecKeyDataTypePublic); +} + +static xmlSecSize +xmlSecMSCngKeyDataDsaGetSize(xmlSecKeyDataPtr data) { + xmlSecAssert2(xmlSecKeyDataCheckId(data, xmlSecMSCngKeyDataDsaId), 0); + + return(xmlSecMSCngKeyDataGetSize(data)); +} + +static int +xmlSecMSCngKeyDataDsaXmlRead(xmlSecKeyDataId id, xmlSecKeyPtr key, + xmlNodePtr node, xmlSecKeyInfoCtxPtr keyInfoCtx) { + xmlSecBn p; + xmlSecBn q; + xmlSecBn g; + xmlSecBn y; + xmlSecBuffer blob; + xmlNodePtr cur; + xmlSecSize length; + xmlSecSize offset; + xmlSecSize blobLen; + unsigned char* blobData; + BCRYPT_DSA_KEY_BLOB* dsakey; + LPCWSTR lpszBlobType; + BCRYPT_KEY_HANDLE hKey = NULL; + NTSTATUS status; + BCRYPT_ALG_HANDLE hAlg = NULL; + xmlSecKeyDataPtr keyData = NULL; + int res = -1; + int ret; + + xmlSecAssert2(id == xmlSecMSCngKeyDataDsaId, -1); + xmlSecAssert2(key != NULL, -1); + xmlSecAssert2(node != NULL, -1); + xmlSecAssert2(keyInfoCtx != NULL, -1); + + if(xmlSecKeyGetValue(key) != NULL) { + xmlSecOtherError(XMLSEC_ERRORS_R_INVALID_KEY_DATA, + xmlSecKeyDataKlassGetName(id), "key already has a value"); + return(-1); + } + + /* initialize buffers */ + ret = xmlSecBnInitialize(&p, 0); + if(ret < 0) { + xmlSecInternalError("xmlSecBnInitialize(p)", + xmlSecKeyDataKlassGetName(id)); + return(-1); + } + + ret = xmlSecBnInitialize(&q, 0); + if(ret < 0) { + xmlSecInternalError("xmlSecBnInitialize(q)", + xmlSecKeyDataKlassGetName(id)); + xmlSecBnFinalize(&p); + return(-1); + } + + ret = xmlSecBnInitialize(&g, 0); + if(ret < 0) { + xmlSecInternalError("xmlSecBnInitialize(g)", + xmlSecKeyDataKlassGetName(id)); + xmlSecBnFinalize(&p); + xmlSecBnFinalize(&q); + return(-1); + } + + ret = xmlSecBnInitialize(&y, 0); + if(ret < 0) { + xmlSecInternalError("xmlSecBnInitialize(g)", + xmlSecKeyDataKlassGetName(id)); + xmlSecBnFinalize(&p); + xmlSecBnFinalize(&q); + xmlSecBnFinalize(&g); + return(-1); + } + + ret = xmlSecBufferInitialize(&blob, 0); + if(ret < 0) { + xmlSecInternalError("xmlSecBufferInitialize", + xmlSecKeyDataKlassGetName(id)); + xmlSecBnFinalize(&p); + xmlSecBnFinalize(&q); + xmlSecBnFinalize(&g); + xmlSecBnFinalize(&y); + return(-1); + } + + /* read xml */ + cur = xmlSecGetNextElementNode(node->children); + + /* P node */ + if((cur == NULL) || (!xmlSecCheckNodeName(cur, xmlSecNodeDSAP, xmlSecDSigNs))) { + xmlSecInvalidNodeError(cur, xmlSecNodeDSAP, + xmlSecKeyDataKlassGetName(id)); + goto done; + } + + /* 0 as both the XML and CNG works with big-endian */ + ret = xmlSecBnGetNodeValue(&p, cur, xmlSecBnBase64, 0); + if((ret < 0) || (xmlSecBnGetSize(&p) == 0)) { + xmlSecInternalError("xmlSecBnGetNodeValue(p)", + xmlSecKeyDataKlassGetName(id)); + goto done; + } + + cur = xmlSecGetNextElementNode(cur->next); + + /* Q node */ + if((cur == NULL) || (!xmlSecCheckNodeName(cur, xmlSecNodeDSAQ, xmlSecDSigNs))) { + xmlSecInvalidNodeError(cur, xmlSecNodeDSAQ, + xmlSecKeyDataKlassGetName(id)); + goto done; + } + + ret = xmlSecBnGetNodeValue(&q, cur, xmlSecBnBase64, 0); + if((ret < 0) || (xmlSecBnGetSize(&q) == 0)) { + xmlSecInternalError("xmlSecBnGetNodeValue(q)", + xmlSecKeyDataKlassGetName(id)); + goto done; + } + + cur = xmlSecGetNextElementNode(cur->next); + + /* G node */ + if((cur == NULL) || (!xmlSecCheckNodeName(cur, xmlSecNodeDSAG, xmlSecDSigNs))) { + xmlSecInvalidNodeError(cur, xmlSecNodeDSAG, + xmlSecKeyDataKlassGetName(id)); + goto done; + } + + ret = xmlSecBnGetNodeValue(&g, cur, xmlSecBnBase64, 0); + if((ret < 0) || (xmlSecBnGetSize(&q) == 0)) { + xmlSecInternalError("xmlSecBnGetNodeValue(g)", + xmlSecKeyDataKlassGetName(id)); + goto done; + } + + cur = xmlSecGetNextElementNode(cur->next); + + /* TODO X node */ + if((cur != NULL) && (xmlSecCheckNodeName(cur, xmlSecNodeDSAX, xmlSecNs))) { + cur = xmlSecGetNextElementNode(cur->next); + } + + /* Y node */ + if((cur == NULL) || (!xmlSecCheckNodeName(cur, xmlSecNodeDSAY, xmlSecDSigNs))) { + xmlSecInvalidNodeError(cur, xmlSecNodeDSAY, + xmlSecKeyDataKlassGetName(id)); + goto done; + } + + ret = xmlSecBnGetNodeValue(&y, cur, xmlSecBnBase64, 0); + if((ret < 0) || (xmlSecBnGetSize(&y) == 0)) { + xmlSecInternalError("xmlSecBnGetNodeValue(y)", + xmlSecKeyDataKlassGetName(id)); + goto done; + } + + cur = xmlSecGetNextElementNode(cur->next); + + /* TODO J node */ + if((cur != NULL) && (xmlSecCheckNodeName(cur, xmlSecNodeDSAJ, xmlSecDSigNs))) { + cur = xmlSecGetNextElementNode(cur->next); + } + + /* TODO Seed node */ + if((cur != NULL) && (xmlSecCheckNodeName(cur, xmlSecNodeDSASeed, xmlSecDSigNs))) { + cur = xmlSecGetNextElementNode(cur->next); + } + + /* TODO PgenCounter node */ + if((cur != NULL) && (xmlSecCheckNodeName(cur, xmlSecNodeDSAPgenCounter, xmlSecDSigNs))) { + cur = xmlSecGetNextElementNode(cur->next); + } + + if(cur != NULL) { + xmlSecUnexpectedNodeError(cur, xmlSecKeyDataKlassGetName(id)); + goto done; + } + + /* turn the read data into a public key blob, as documented at + * <https://msdn.microsoft.com/library/windows/desktop/aa833126.aspx>: Q is + * part of the struct, need to write P, G, Y after it */ + length = xmlSecBnGetSize(&p); + offset = sizeof(BCRYPT_DSA_KEY_BLOB); + blobLen = offset + length * 3; + + ret = xmlSecBufferSetSize(&blob, blobLen); + if(ret < 0) { + xmlSecInternalError2("xmlSecBufferSetSize", NULL, "size=%d", blobLen); + goto done; + } + + blobData = xmlSecBufferGetData(&blob); + dsakey = (BCRYPT_DSA_KEY_BLOB *)blobData; + dsakey->cbKey = length; + + memset(dsakey->Count, -1, sizeof(dsakey->Count)); + memset(dsakey->Seed, -1, sizeof(dsakey->Seed)); + + if(xmlSecBnGetSize(&q) != 20) { + xmlSecInternalError("assumed sizeof(q) == 20", xmlSecKeyDataKlassGetName(id)); + goto done; + } + + memcpy(dsakey->q, xmlSecBnGetData(&q), 20); + + memcpy(blobData + offset, xmlSecBnGetData(&p), length); + offset += length; + + if(xmlSecBnGetSize(&g) != xmlSecBnGetSize(&p)) { + xmlSecInternalError("assumed sizeof(g) == sizeof(p)", xmlSecKeyDataKlassGetName(id)); + goto done; + } + + memcpy(blobData + offset, xmlSecBnGetData(&g), length); + offset += length; + + if(xmlSecBnGetSize(&y) != xmlSecBnGetSize(&p)) { + xmlSecInternalError("assumed sizeof(y) == sizeof(p)", xmlSecKeyDataKlassGetName(id)); + goto done; + } + + memcpy(blobData + offset, xmlSecBnGetData(&y), length); + + lpszBlobType = BCRYPT_DSA_PUBLIC_BLOB; + dsakey->dwMagic = BCRYPT_DSA_PUBLIC_MAGIC; + + status = BCryptOpenAlgorithmProvider( + &hAlg, + BCRYPT_DSA_ALGORITHM, + NULL, + 0); + if(status != STATUS_SUCCESS) { + xmlSecMSCngNtError("BCryptOpenAlgorithmProvider", + xmlSecKeyDataKlassGetName(id), status); + goto done; + } + + status = BCryptImportKeyPair(hAlg, NULL, lpszBlobType, &hKey, blobData, + blobLen, 0); + if(status != STATUS_SUCCESS) { + xmlSecMSCngNtError("BCryptImportKeyPair", + xmlSecKeyDataKlassGetName(id), status); + goto done; + } + + keyData = xmlSecKeyDataCreate(id); + if(keyData == NULL) { + xmlSecInternalError("xmlSecKeyDataCreate", + xmlSecKeyDataKlassGetName(id)); + goto done; + } + + ret = xmlSecMSCngKeyDataAdoptKey(keyData, hKey); + if(ret < 0) { + xmlSecInternalError("xmlSecMSCngKeyDataAdoptKey", + xmlSecKeyDataGetName(keyData)); + goto done; + } + + hKey = 0; + ret = xmlSecKeySetValue(key, keyData); + if(ret < 0) { + xmlSecInternalError("xmlSecKeySetValue", + xmlSecKeyDataGetName(keyData)); + goto done; + } + + keyData = NULL; + res = 0; + +done: + xmlSecBnFinalize(&p); + xmlSecBnFinalize(&q); + xmlSecBnFinalize(&g); + xmlSecBnFinalize(&y); + xmlSecBufferFinalize(&blob); + + if(hAlg != 0) { + BCryptCloseAlgorithmProvider(hAlg, 0); + } + + if(hKey != 0) { + BCryptDestroyKey(hKey); + } + + return(res); +} + +static int +xmlSecMSCngKeyDataDsaXmlWrite(xmlSecKeyDataId id, xmlSecKeyPtr key, + xmlNodePtr node, xmlSecKeyInfoCtxPtr keyInfoCtx) { + xmlSecMSCngKeyDataCtxPtr ctx; + NTSTATUS status; + xmlSecBuffer buf; + xmlSecByte* bufData; + DWORD bufLen; + BCRYPT_DSA_KEY_BLOB* dsakey; + xmlNodePtr cur; + int ret; + + xmlSecAssert2(id == xmlSecMSCngKeyDataDsaId, -1); + xmlSecAssert2(key != NULL, -1); + xmlSecAssert2(xmlSecKeyDataCheckId(xmlSecKeyGetValue(key), + xmlSecMSCngKeyDataDsaId), -1); + xmlSecAssert2(node != NULL, -1); + xmlSecAssert2(keyInfoCtx != NULL, -1); + + ctx = xmlSecMSCngKeyDataGetCtx(xmlSecKeyGetValue(key)); + xmlSecAssert2(ctx != NULL, -1); + xmlSecAssert2(ctx->pubkey, -1); + + /* turn ctx->pubkey into dsakey */ + status = BCryptExportKey(ctx->pubkey, + NULL, + BCRYPT_DSA_PUBLIC_BLOB, + NULL, + 0, + &bufLen, + 0); + if(status != STATUS_SUCCESS) { + xmlSecMSCngNtError("BCryptExportKey", xmlSecKeyDataKlassGetName(id), + status); + return(-1); + } + + ret = xmlSecBufferInitialize(&buf, bufLen); + if(ret < 0) { + xmlSecInternalError2("xmlSecBufferInitialize", + xmlSecKeyDataKlassGetName(id), "size=%ld", bufLen); + return(-1); + } + + bufData = xmlSecBufferGetData(&buf); + dsakey = (BCRYPT_DSA_KEY_BLOB*)bufData; + + status = BCryptExportKey(ctx->pubkey, + NULL, + BCRYPT_DSA_PUBLIC_BLOB, + (PUCHAR)dsakey, + bufLen, + &bufLen, + 0); + if(status != STATUS_SUCCESS) { + xmlSecMSCngNtError("BCryptExportKey", xmlSecKeyDataKlassGetName(id), + status); + xmlSecBufferFinalize(&buf); + return(-1); + } + + /* write dsaykey in XML format, see xmlSecMSCngKeyDataDsaXmlRead() on the + * memory layout of bufData: the struct contains Q, and P, G, Y follows it */ + + /* P node */ + cur = xmlSecAddChild(node, xmlSecNodeDSAP, xmlSecDSigNs); + if(cur == NULL) { + xmlSecInternalError("xmlSecAddChild(p)", + xmlSecKeyDataKlassGetName(id)); + xmlSecBufferFinalize(&buf); + return(-1); + } + + /* reverse is 0, both CNG and XML is big-endian */ + bufData += sizeof(BCRYPT_DSA_KEY_BLOB); + ret = xmlSecBnBlobSetNodeValue(bufData, dsakey->cbKey, cur, xmlSecBnBase64, 0, 1); + if(ret < 0) { + xmlSecInternalError("xmlSecBnBlobSetNodeValue(p)", + xmlSecKeyDataKlassGetName(id)); + xmlSecBufferFinalize(&buf); + return(-1); + } + + /* Q node */ + cur = xmlSecAddChild(node, xmlSecNodeDSAQ, xmlSecDSigNs); + if(cur == NULL) { + xmlSecInternalError("xmlSecAddChild(q)", + xmlSecKeyDataKlassGetName(id)); + xmlSecBufferFinalize(&buf); + return(-1); + } + + /* 20 is the documented size of BCRYPT_DSA_KEY_BLOB.q */ + ret = xmlSecBnBlobSetNodeValue((xmlSecByte*)dsakey->q, 20, cur, xmlSecBnBase64, 0, 1); + if(ret < 0) { + xmlSecInternalError("xmlSecBnBlobSetNodeValue(q)", + xmlSecKeyDataKlassGetName(id)); + xmlSecBufferFinalize(&buf); + return(-1); + } + + /* G node */ + cur = xmlSecAddChild(node, xmlSecNodeDSAG, xmlSecDSigNs); + if(cur == NULL) { + xmlSecInternalError("xmlSecAddChild(g)", + xmlSecKeyDataKlassGetName(id)); + xmlSecBufferFinalize(&buf); + return(-1); + } + + bufData += dsakey->cbKey; + ret = xmlSecBnBlobSetNodeValue(bufData, dsakey->cbKey, cur, xmlSecBnBase64, 0, 1); + if(ret < 0) { + xmlSecInternalError("xmlSecBnBlobSetNodeValue(g)", + xmlSecKeyDataKlassGetName(id)); + xmlSecBufferFinalize(&buf); + return(-1); + } + + /* Y node */ + cur = xmlSecAddChild(node, xmlSecNodeDSAY, xmlSecDSigNs); + if(cur == NULL) { + xmlSecInternalError("xmlSecAddChild(y)", + xmlSecKeyDataKlassGetName(id)); + xmlSecBufferFinalize(&buf); + return(-1); + } + + bufData += dsakey->cbKey; + ret = xmlSecBnBlobSetNodeValue(bufData, dsakey->cbKey, cur, xmlSecBnBase64, 0, 1); + if(ret < 0) { + xmlSecInternalError("xmlSecBnBlobSetNodeValue(y)", + xmlSecKeyDataKlassGetName(id)); + xmlSecBufferFinalize(&buf); + return(-1); + } + + xmlSecBufferFinalize(&buf); + + return(0); +} + +static void +xmlSecMSCngKeyDataDsaDebugDump(xmlSecKeyDataPtr data, FILE* output) { + xmlSecAssert(xmlSecKeyDataCheckId(data, xmlSecMSCngKeyDataDsaId)); + xmlSecAssert(output != NULL); + + fprintf(output, "=== rsa key: size = %d\n", + (int)xmlSecMSCngKeyDataDsaGetSize(data)); +} + +static void xmlSecMSCngKeyDataDsaDebugXmlDump(xmlSecKeyDataPtr data, FILE* output) { + xmlSecAssert(xmlSecKeyDataCheckId(data, xmlSecMSCngKeyDataDsaId)); + xmlSecAssert(output != NULL); + + fprintf(output, "<DSAKeyValue size=\"%d\" />\n", + (int)xmlSecMSCngKeyDataDsaGetSize(data)); +} + +static int +xmlSecMSCngKeyDataDsaGenerate(xmlSecKeyDataPtr data, xmlSecSize sizeBits, + xmlSecKeyDataType type) { + UNREFERENCED_PARAMETER(type); + xmlSecMSCngKeyDataCtxPtr ctx; + BCRYPT_ALG_HANDLE hAlg = 0; + BCRYPT_KEY_HANDLE hKey = 0; + int res = -1; + NTSTATUS status; + int ret; + + 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; + } + + status = BCryptGenerateKeyPair( + hAlg, + &hKey, + sizeBits, + 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); +} + +static xmlSecKeyDataKlass xmlSecMSCngKeyDataDsaKlass = { + sizeof(xmlSecKeyDataKlass), + xmlSecMSCngKeyDataSize, + + /* data */ + xmlSecNameDSAKeyValue, + xmlSecKeyDataUsageKeyValueNode | xmlSecKeyDataUsageRetrievalMethodNodeXml, + /* xmlSecKeyDataUsage usage; */ + xmlSecHrefDSAKeyValue, /* const xmlChar* href; */ + xmlSecNodeDSAKeyValue, /* const xmlChar* dataNodeName; */ + xmlSecDSigNs, /* const xmlChar* dataNodeNs; */ + + /* constructors/destructor */ + xmlSecMSCngKeyDataInitialize, /* xmlSecKeyDataInitializeMethod initialize; */ + xmlSecMSCngKeyDataDsaDuplicate, /* xmlSecKeyDataDuplicateMethod duplicate; */ + xmlSecMSCngKeyDataFinalize, /* xmlSecKeyDataFinalizeMethod finalize; */ + xmlSecMSCngKeyDataDsaGenerate, /* xmlSecKeyDataGenerateMethod generate; */ + + /* get info */ + xmlSecMSCngKeyDataDsaGetType, /* xmlSecKeyDataGetTypeMethod getType; */ + xmlSecMSCngKeyDataDsaGetSize, /* xmlSecKeyDataGetSizeMethod getSize; */ + NULL, /* xmlSecKeyDataGetIdentifier getIdentifier; */ + + /* read/write */ + xmlSecMSCngKeyDataDsaXmlRead, /* xmlSecKeyDataXmlReadMethod xmlRead; */ + xmlSecMSCngKeyDataDsaXmlWrite, /* xmlSecKeyDataXmlWriteMethod xmlWrite; */ + NULL, /* xmlSecKeyDataBinReadMethod binRead; */ + NULL, /* xmlSecKeyDataBinWriteMethod binWrite; */ + + /* debug */ + xmlSecMSCngKeyDataDsaDebugDump, /* xmlSecKeyDataDebugDumpMethod debugDump; */ + xmlSecMSCngKeyDataDsaDebugXmlDump, /* xmlSecKeyDataDebugDumpMethod debugXmlDump; */ + + /* reserved for the future */ + NULL, /* void* reserved0; */ + NULL, /* void* reserved1; */ +}; + +/** + * xmlSecMSCngKeyDataDsaGetKlass: + * + * The MSCng DSA CertKey data klass. + * + * Returns: pointer to MSCng DSA key data klass. + */ +xmlSecKeyDataId +xmlSecMSCngKeyDataDsaGetKlass(void) { + return(&xmlSecMSCngKeyDataDsaKlass); +} +#endif /* XMLSEC_NO_DSA */ + +#ifndef XMLSEC_NO_RSA +static int +xmlSecMSCngKeyDataRsaDuplicate(xmlSecKeyDataPtr dst, xmlSecKeyDataPtr src) { + xmlSecAssert2(xmlSecKeyDataCheckId(dst, xmlSecMSCngKeyDataRsaId), -1); + xmlSecAssert2(xmlSecKeyDataCheckId(src, xmlSecMSCngKeyDataRsaId), -1); + + return(xmlSecMSCngKeyDataDuplicate(dst, src)); +} + +static xmlSecKeyDataType +xmlSecMSCngKeyDataRsaGetType(xmlSecKeyDataPtr data) { + xmlSecMSCngKeyDataCtxPtr ctx; + + xmlSecAssert2(xmlSecKeyDataCheckId(data, xmlSecMSCngKeyDataRsaId), xmlSecKeyDataTypeUnknown); + + ctx = xmlSecMSCngKeyDataGetCtx(data); + xmlSecAssert2(ctx != NULL, xmlSecKeyDataTypeUnknown); + + if(ctx->privkey != 0) { + return(xmlSecKeyDataTypePrivate | xmlSecKeyDataTypePublic); + } + + return(xmlSecKeyDataTypePublic); +} + +static int +xmlSecMSCngKeyDataGetSize(xmlSecKeyDataPtr data) { + NTSTATUS status; + xmlSecMSCngKeyDataCtxPtr ctx; + + 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); + return(CertGetPublicKeyLength(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, + &ctx->cert->pCertInfo->SubjectPublicKeyInfo)); + } else if(ctx->pubkey != 0) { + DWORD length = 0; + DWORD lenlen = sizeof(DWORD); + + status = BCryptGetProperty(ctx->pubkey, + BCRYPT_KEY_STRENGTH, + (PUCHAR)&length, + lenlen, + &lenlen, + 0); + if(status != STATUS_SUCCESS) { + xmlSecMSCngNtError("BCryptGetproperty", NULL, status); + return(0); + } + + return(length); + } else if(ctx->privkey != 0) { + xmlSecNotImplementedError(NULL); + return(0); + } + + return(0); +} + +static xmlSecSize +xmlSecMSCngKeyDataRsaGetSize(xmlSecKeyDataPtr data) { + xmlSecAssert2(xmlSecKeyDataCheckId(data, xmlSecMSCngKeyDataRsaId), 0); + + return(xmlSecMSCngKeyDataGetSize(data)); +} + + +static void +xmlSecMSCngKeyDataRsaDebugDump(xmlSecKeyDataPtr data, FILE* output) { + xmlSecAssert(xmlSecKeyDataCheckId(data, xmlSecMSCngKeyDataRsaId)); + xmlSecAssert(output != NULL); + + fprintf(output, "=== rsa key: size = %d\n", + (int)xmlSecMSCngKeyDataRsaGetSize(data)); +} + +static void xmlSecMSCngKeyDataRsaDebugXmlDump(xmlSecKeyDataPtr data, FILE* output) { + xmlSecAssert(xmlSecKeyDataCheckId(data, xmlSecMSCngKeyDataRsaId)); + xmlSecAssert(output != NULL); + + fprintf(output, "<RSAKeyValue size=\"%d\" />\n", + (int)xmlSecMSCngKeyDataRsaGetSize(data)); +} + +static int +xmlSecMSCngKeyDataRsaXmlRead(xmlSecKeyDataId id, xmlSecKeyPtr key, + xmlNodePtr node, xmlSecKeyInfoCtxPtr keyInfoCtx) { + xmlSecBn modulus, exponent; + xmlSecBuffer blob; + xmlSecSize blobBufferLen; + xmlSecSize offset; + BCRYPT_RSAKEY_BLOB* rsakey; + LPCWSTR lpszBlobType; + BCRYPT_ALG_HANDLE hAlg = NULL; + xmlSecKeyDataPtr keyData = NULL; + BCRYPT_KEY_HANDLE hKey = 0; + xmlNodePtr cur; + int res = -1; + NTSTATUS status; + int ret; + + xmlSecAssert2(id == xmlSecMSCngKeyDataRsaId, -1); + xmlSecAssert2(key != NULL, -1); + xmlSecAssert2(node != NULL, -1); + xmlSecAssert2(keyInfoCtx != NULL, -1); + + if(xmlSecKeyGetValue(key) != NULL) { + xmlSecOtherError(XMLSEC_ERRORS_R_INVALID_KEY_DATA, + xmlSecKeyDataKlassGetName(id), + "key already has a value"); + return(-1); + } + + /* initialize buffers */ + ret = xmlSecBnInitialize(&modulus, 0); + if(ret < 0) { + xmlSecInternalError("xmlSecBnInitialize", + xmlSecKeyDataKlassGetName(id)); + return(-1); + } + + ret = xmlSecBnInitialize(&exponent, 0); + if(ret < 0) { + xmlSecInternalError("xmlSecBnInitialize", + xmlSecKeyDataKlassGetName(id)); + xmlSecBnFinalize(&modulus); + return(-1); + } + + ret = xmlSecBufferInitialize(&blob, 0); + if(ret < 0) { + xmlSecInternalError("xmlSecBufferInitialize", + xmlSecKeyDataKlassGetName(id)); + xmlSecBnFinalize(&modulus); + xmlSecBnFinalize(&exponent); + return(-1); + } + + /* read xml */ + cur = xmlSecGetNextElementNode(node->children); + + /* first is Modulus node, it is required because we do not support Seed and PgenCounter */ + if((cur == NULL) || (!xmlSecCheckNodeName(cur, xmlSecNodeRSAModulus, xmlSecDSigNs))) { + xmlSecInvalidNodeError(cur, xmlSecNodeRSAModulus, + xmlSecKeyDataKlassGetName(id)); + goto done; + } + + /* 0 as both the XML and CNG works with big-endian */ + ret = xmlSecBnGetNodeValue(&modulus, cur, xmlSecBnBase64, 0); + if((ret < 0) || (xmlSecBnGetSize(&modulus) == 0)) { + xmlSecInternalError("xmlSecBnGetNodeValue", + xmlSecKeyDataKlassGetName(id)); + goto done; + } + + cur = xmlSecGetNextElementNode(cur->next); + + /* next is Exponent node, it is required because we do not support Seed and PgenCounter */ + if((cur == NULL) || (!xmlSecCheckNodeName(cur, xmlSecNodeRSAExponent, xmlSecDSigNs))) { + xmlSecInvalidNodeError(cur, xmlSecNodeRSAExponent, xmlSecKeyDataKlassGetName(id)); + goto done; + } + + ret = xmlSecBnGetNodeValue(&exponent, cur, xmlSecBnBase64, 0); + if((ret < 0) || (xmlSecBnGetSize(&exponent) == 0)) { + xmlSecInternalError("xmlSecBnGetNodeValue", + xmlSecKeyDataKlassGetName(id)); + goto done; + } + cur = xmlSecGetNextElementNode(cur->next); + + /* TODO X node */ + if((cur != NULL) && (xmlSecCheckNodeName(cur, xmlSecNodeRSAPrivateExponent, xmlSecNs))) { + cur = xmlSecGetNextElementNode(cur->next); + } + + if(cur != NULL) { + xmlSecUnexpectedNodeError(cur, xmlSecKeyDataKlassGetName(id)); + goto done; + } + + /* 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 */ + blobBufferLen = sizeof(BCRYPT_RSAKEY_BLOB) + xmlSecBnGetSize(&exponent) + + xmlSecBnGetSize(&modulus); + ret = xmlSecBufferSetSize(&blob, blobBufferLen); + if(ret < 0) { + xmlSecInternalError2("xmlSecBufferSetSize", + xmlSecKeyDataKlassGetName(id), "size=%d", blobBufferLen); + goto done; + } + + rsakey = (BCRYPT_RSAKEY_BLOB *)xmlSecBufferGetData(&blob); + rsakey->Magic = BCRYPT_RSAPUBLIC_MAGIC; + rsakey->BitLength = xmlSecBnGetSize(&modulus) * 8; + rsakey->cbPublicExp = xmlSecBnGetSize(&exponent); + rsakey->cbModulus = xmlSecBnGetSize(&modulus); + offset = sizeof(BCRYPT_RSAKEY_BLOB); + + memcpy(xmlSecBufferGetData(&blob) + offset, xmlSecBnGetData(&exponent), + xmlSecBnGetSize(&exponent)); + offset += xmlSecBnGetSize(&exponent); + + memcpy(xmlSecBufferGetData(&blob) + offset, xmlSecBnGetData(&modulus), + xmlSecBnGetSize(&modulus)); + + lpszBlobType = BCRYPT_RSAPUBLIC_BLOB; + + status = BCryptOpenAlgorithmProvider( + &hAlg, + BCRYPT_RSA_ALGORITHM, + NULL, + 0); + if(status != STATUS_SUCCESS) { + xmlSecMSCngNtError("BCryptOpenAlgorithmProvider", + xmlSecKeyDataKlassGetName(id), status); + goto done; + } + + status = BCryptImportKeyPair(hAlg, NULL, lpszBlobType, &hKey, + xmlSecBufferGetData(&blob), xmlSecBufferGetSize(&blob), 0); + if(status != STATUS_SUCCESS) { + xmlSecMSCngNtError("BCryptImportKeyPair", + xmlSecKeyDataKlassGetName(id), status); + goto done; + } + + keyData = xmlSecKeyDataCreate(id); + if(keyData == NULL) { + xmlSecInternalError("xmlSecKeyDataCreate", + xmlSecKeyDataKlassGetName(id)); + goto done; + } + + ret = xmlSecMSCngKeyDataAdoptKey(keyData, hKey); + if(ret < 0) { + xmlSecInternalError("xmlSecMSCngKeyDataAdoptKey", + xmlSecKeyDataGetName(keyData)); + goto done; + } + + hKey = 0; + ret = xmlSecKeySetValue(key, keyData); + if(ret < 0) { + xmlSecInternalError("xmlSecKeySetValue", + xmlSecKeyDataGetName(keyData)); + goto done; + } + + keyData = NULL; + res = 0; + +done: + xmlSecBnFinalize(&exponent); + xmlSecBnFinalize(&modulus); + xmlSecBufferFinalize(&blob); + + if(hKey != 0) { + BCryptDestroyKey(hKey); + } + + if(hAlg != 0) { + BCryptCloseAlgorithmProvider(hAlg, 0); + } + + return(res); +} + +static int +xmlSecMSCngKeyDataRsaXmlWrite(xmlSecKeyDataId id, xmlSecKeyPtr key, + xmlNodePtr node, xmlSecKeyInfoCtxPtr keyInfoCtx) { + xmlSecMSCngKeyDataCtxPtr ctx; + NTSTATUS status; + xmlSecBuffer buf; + xmlSecByte* bufData; + DWORD bufLen; + BCRYPT_RSAKEY_BLOB* rsakey; + xmlNodePtr cur; + int ret; + + xmlSecAssert2(id == xmlSecMSCngKeyDataRsaId, -1); + xmlSecAssert2(key != NULL, -1); + xmlSecAssert2(xmlSecKeyDataCheckId(xmlSecKeyGetValue(key), + xmlSecMSCngKeyDataRsaId), -1); + xmlSecAssert2(node != NULL, -1); + xmlSecAssert2(keyInfoCtx != NULL, -1); + + ctx = xmlSecMSCngKeyDataGetCtx(xmlSecKeyGetValue(key)); + 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) { + xmlSecMSCngNtError("BCryptExportKey", xmlSecKeyDataKlassGetName(id), + status); + return(-1); + } + + ret = xmlSecBufferInitialize(&buf, bufLen); + if(ret < 0) { + xmlSecInternalError2("xmlSecBufferInitialize", + xmlSecKeyDataKlassGetName(id), "size=%ld", bufLen); + return(-1); + } + + bufData = xmlSecBufferGetData(&buf); + rsakey = (BCRYPT_RSAKEY_BLOB*)bufData; + + status = BCryptExportKey(ctx->pubkey, + NULL, + BCRYPT_RSAPUBLIC_BLOB, + (PUCHAR)rsakey, + bufLen, + &bufLen, + 0); + if(status != STATUS_SUCCESS) { + xmlSecMSCngNtError("BCryptExportKey", xmlSecKeyDataKlassGetName(id), + status); + xmlSecBufferFinalize(&buf); + return(-1); + } + + /* write rsaykey in XML format, see xmlSecMSCngKeyDataRsaXmlRead() on the + * memory layout of bufData: the struct is followed by Exponent and Modulus */ + + /* Modulus node */ + cur = xmlSecAddChild(node, xmlSecNodeRSAModulus, xmlSecDSigNs); + if(cur == NULL) { + xmlSecInternalError("xmlSecAddChild", + xmlSecKeyDataKlassGetName(id)); + xmlSecBufferFinalize(&buf); + return(-1); + } + + bufData += sizeof(BCRYPT_RSAKEY_BLOB) + rsakey->cbPublicExp; + ret = xmlSecBnBlobSetNodeValue(bufData, rsakey->cbModulus, cur, xmlSecBnBase64, 0, 1); + if(ret < 0) { + xmlSecInternalError("xmlSecBnBlobSetNodeValue", + xmlSecKeyDataKlassGetName(id)); + xmlSecBufferFinalize(&buf); + return(-1); + } + + /* Exponent node */ + cur = xmlSecAddChild(node, xmlSecNodeRSAExponent, xmlSecDSigNs); + if(cur == NULL) { + xmlSecInternalError("xmlSecAddChild", + xmlSecKeyDataKlassGetName(id)); + xmlSecBufferFinalize(&buf); + return(-1); + } + + bufData = xmlSecBufferGetData(&buf); + bufData += sizeof(BCRYPT_RSAKEY_BLOB); + ret = xmlSecBnBlobSetNodeValue(bufData, rsakey->cbPublicExp, cur, xmlSecBnBase64, 0, 1); + if(ret < 0) { + xmlSecInternalError("xmlSecBnBlobSetNodeValue", + xmlSecKeyDataKlassGetName(id)); + xmlSecBufferFinalize(&buf); + return(-1); + } + + xmlSecBufferFinalize(&buf); + + return(0); +} + +static int +xmlSecMSCngKeyDataRsaGenerate(xmlSecKeyDataPtr data, xmlSecSize sizeBits, + xmlSecKeyDataType type) { + UNREFERENCED_PARAMETER(type); + xmlSecMSCngKeyDataCtxPtr ctx; + BCRYPT_ALG_HANDLE hAlg = 0; + BCRYPT_KEY_HANDLE hKey = 0; + int res = -1; + NTSTATUS status; + int ret; + + 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; + } + + status = BCryptGenerateKeyPair( + hAlg, + &hKey, + sizeBits, + 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); +} + +static xmlSecKeyDataKlass xmlSecMSCngKeyDataRsaKlass = { + sizeof(xmlSecKeyDataKlass), + xmlSecMSCngKeyDataSize, + + /* data */ + xmlSecNameRSAKeyValue, + xmlSecKeyDataUsageKeyValueNode | xmlSecKeyDataUsageRetrievalMethodNodeXml, + /* xmlSecKeyDataUsage usage; */ + xmlSecHrefRSAKeyValue, /* const xmlChar* href; */ + xmlSecNodeRSAKeyValue, /* const xmlChar* dataNodeName; */ + xmlSecDSigNs, /* const xmlChar* dataNodeNs; */ + + /* constructors/destructor */ + xmlSecMSCngKeyDataInitialize, /* xmlSecKeyDataInitializeMethod initialize; */ + xmlSecMSCngKeyDataRsaDuplicate, /* xmlSecKeyDataDuplicateMethod duplicate; */ + xmlSecMSCngKeyDataFinalize, /* xmlSecKeyDataFinalizeMethod finalize; */ + xmlSecMSCngKeyDataRsaGenerate, /* xmlSecKeyDataGenerateMethod generate; */ + + /* get info */ + xmlSecMSCngKeyDataRsaGetType, /* xmlSecKeyDataGetTypeMethod getType; */ + xmlSecMSCngKeyDataRsaGetSize, /* xmlSecKeyDataGetSizeMethod getSize; */ + NULL, /* xmlSecKeyDataGetIdentifier getIdentifier; */ + + /* read/write */ + xmlSecMSCngKeyDataRsaXmlRead, /* xmlSecKeyDataXmlReadMethod xmlRead; */ + xmlSecMSCngKeyDataRsaXmlWrite, /* xmlSecKeyDataXmlWriteMethod xmlWrite; */ + NULL, /* xmlSecKeyDataBinReadMethod binRead; */ + NULL, /* xmlSecKeyDataBinWriteMethod binWrite; */ + + /* debug */ + xmlSecMSCngKeyDataRsaDebugDump, /* xmlSecKeyDataDebugDumpMethod debugDump; */ + xmlSecMSCngKeyDataRsaDebugXmlDump, /* xmlSecKeyDataDebugDumpMethod debugXmlDump; */ + + /* reserved for the future */ + NULL, /* void* reserved0; */ + NULL, /* void* reserved1; */ +}; + +/** + * xmlSecMSCngKeyDataRsaGetKlass: + * + * The MSCng RSA CertKey data klass. + * + * Returns: pointer to MSCng RSA key data klass. + */ +xmlSecKeyDataId +xmlSecMSCngKeyDataRsaGetKlass(void) { + return(&xmlSecMSCngKeyDataRsaKlass); +} +#endif /* XMLSEC_NO_RSA */ + +#ifndef XMLSEC_NO_ECDSA +static int +xmlSecMSCngKeyDataEcdsaDuplicate(xmlSecKeyDataPtr dst, xmlSecKeyDataPtr src) { + xmlSecAssert2(xmlSecKeyDataCheckId(dst, xmlSecMSCngKeyDataEcdsaId), -1); + xmlSecAssert2(xmlSecKeyDataCheckId(src, xmlSecMSCngKeyDataEcdsaId), -1); + + return(xmlSecMSCngKeyDataDuplicate(dst, src)); +} + +static xmlSecKeyDataType +xmlSecMSCngKeyDataEcdsaGetType(xmlSecKeyDataPtr data) { + xmlSecMSCngKeyDataCtxPtr ctx; + + xmlSecAssert2(xmlSecKeyDataCheckId(data, xmlSecMSCngKeyDataEcdsaId), xmlSecKeyDataTypeUnknown); + + ctx = xmlSecMSCngKeyDataGetCtx(data); + xmlSecAssert2(ctx != NULL, xmlSecKeyDataTypeUnknown); + + if(ctx->privkey != 0) { + return(xmlSecKeyDataTypePrivate | xmlSecKeyDataTypePublic); + } + + return(xmlSecKeyDataTypePublic); +} + +static xmlSecSize +xmlSecMSCngKeyDataEcdsaGetSize(xmlSecKeyDataPtr data) { + xmlSecAssert2(xmlSecKeyDataCheckId(data, xmlSecMSCngKeyDataEcdsaId), 0); + + return(xmlSecMSCngKeyDataGetSize(data)); +} + + +static void +xmlSecMSCngKeyDataEcdsaDebugDump(xmlSecKeyDataPtr data, FILE* output) { + xmlSecAssert(xmlSecKeyDataCheckId(data, xmlSecMSCngKeyDataEcdsaId)); + xmlSecAssert(output != NULL); + + fprintf(output, "=== rsa key: size = %d\n", + (int)xmlSecMSCngKeyDataEcdsaGetSize(data)); +} + +static void xmlSecMSCngKeyDataEcdsaDebugXmlDump(xmlSecKeyDataPtr data, FILE* output) { + xmlSecAssert(xmlSecKeyDataCheckId(data, xmlSecMSCngKeyDataEcdsaId)); + xmlSecAssert(output != NULL); + + fprintf(output, "<ECDSAKeyValue size=\"%d\" />\n", + (int)xmlSecMSCngKeyDataEcdsaGetSize(data)); +} + +static xmlSecKeyDataKlass xmlSecMSCngKeyDataEcdsaKlass = { + sizeof(xmlSecKeyDataKlass), + xmlSecMSCngKeyDataSize, + + /* data */ + xmlSecNameECDSAKeyValue, + xmlSecKeyDataUsageKeyValueNode | xmlSecKeyDataUsageRetrievalMethodNodeXml, + /* xmlSecKeyDataUsage usage; */ + xmlSecHrefECDSAKeyValue, /* const xmlChar* href; */ + xmlSecNodeECDSAKeyValue, /* const xmlChar* dataNodeName; */ + xmlSecDSigNs, /* const xmlChar* dataNodeNs; */ + + /* constructors/destructor */ + xmlSecMSCngKeyDataInitialize, /* xmlSecKeyDataInitializeMethod initialize; */ + xmlSecMSCngKeyDataEcdsaDuplicate, /* xmlSecKeyDataDuplicateMethod duplicate; */ + xmlSecMSCngKeyDataFinalize, /* xmlSecKeyDataFinalizeMethod finalize; */ + NULL, /* xmlSecKeyDataGenerateMethod generate; */ + + /* get info */ + xmlSecMSCngKeyDataEcdsaGetType, /* xmlSecKeyDataGetTypeMethod getType; */ + xmlSecMSCngKeyDataEcdsaGetSize, /* xmlSecKeyDataGetSizeMethod getSize; */ + NULL, /* xmlSecKeyDataGetIdentifier getIdentifier; */ + + /* read/write */ + NULL, /* xmlSecKeyDataXmlReadMethod xmlRead; */ + NULL, /* xmlSecKeyDataXmlWriteMethod xmlWrite; */ + NULL, /* xmlSecKeyDataBinReadMethod binRead; */ + NULL, /* xmlSecKeyDataBinWriteMethod binWrite; */ + + /* debug */ + xmlSecMSCngKeyDataEcdsaDebugDump, /* xmlSecKeyDataDebugDumpMethod debugDump; */ + xmlSecMSCngKeyDataEcdsaDebugXmlDump, /* xmlSecKeyDataDebugDumpMethod debugXmlDump; */ + + /* reserved for the future */ + NULL, /* void* reserved0; */ + NULL, /* void* reserved1; */ +}; + +/** + * xmlSecMSCngKeyDataEcdsaGetKlass: + * + * The MSCng ECDSA CertKey data klass. + * + * Returns: pointer to MSCng ECDSA key data klass. + */ +xmlSecKeyDataId +xmlSecMSCngKeyDataEcdsaGetKlass(void) { + return(&xmlSecMSCngKeyDataEcdsaKlass); +} +#endif /* XMLSEC_NO_ECDSA */ |