summaryrefslogtreecommitdiff
path: root/src/mscng/ciphers.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/mscng/ciphers.c')
-rw-r--r--src/mscng/ciphers.c1529
1 files changed, 1529 insertions, 0 deletions
diff --git a/src/mscng/ciphers.c b/src/mscng/ciphers.c
new file mode 100644
index 00000000..a4bef208
--- /dev/null
+++ b/src/mscng/ciphers.c
@@ -0,0 +1,1529 @@
+/*
+ * 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:ciphers
+ * @Short_description: Ciphers transforms implementation for Microsoft Cryptography API: Next Generation (CNG).
+ * @Stability: Private
+ *
+ */
+#include "globals.h"
+
+#include <string.h>
+
+#define WIN32_NO_STATUS
+#include <windows.h>
+#undef WIN32_NO_STATUS
+#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>
+
+/**************************************************************************
+ *
+ * Internal MSCng Block cipher CTX
+ *
+ *****************************************************************************/
+typedef struct _xmlSecMSCngBlockCipherCtx xmlSecMSCngBlockCipherCtx, *xmlSecMSCngBlockCipherCtxPtr;
+
+struct _xmlSecMSCngBlockCipherCtx {
+ LPCWSTR pszAlgId;
+ BCRYPT_ALG_HANDLE hAlg;
+ BCRYPT_KEY_HANDLE hKey;
+ BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO authInfo;
+ PBYTE pbIV;
+ ULONG cbIV;
+ PBYTE pbKeyObject;
+ DWORD dwBlockLen;
+ xmlSecKeyDataId keyId;
+ xmlSecSize keySize;
+ int cbcMode;
+ int ctxInitialized;
+};
+
+#define xmlSecMSCngBlockCipherSize \
+ (sizeof(xmlSecTransform) + sizeof(xmlSecMSCngBlockCipherCtx))
+#define xmlSecMSCngBlockCipherGetCtx(transform) \
+ ((xmlSecMSCngBlockCipherCtxPtr)(((unsigned char*)(transform)) + sizeof(xmlSecTransform)))
+
+#define xmlSecMSCngAesGcmNonceLengthInBytes 12
+#define xmlSecMSCngAesGcmTagLengthInBytes 16
+
+static int
+xmlSecMSCngBlockCipherCheckId(xmlSecTransformPtr transform) {
+#ifndef XMLSEC_NO_AES
+ if(xmlSecTransformCheckId(transform, xmlSecMSCngTransformAes128CbcId)) {
+ return(1);
+ } else if(xmlSecTransformCheckId(transform, xmlSecMSCngTransformAes192CbcId)) {
+ return(1);
+ } else if(xmlSecTransformCheckId(transform, xmlSecMSCngTransformAes256CbcId)) {
+ return(1);
+ } else if(xmlSecTransformCheckId(transform, xmlSecMSCngTransformAes128GcmId)) {
+ return(1);
+ } else if(xmlSecTransformCheckId(transform, xmlSecMSCngTransformAes192GcmId)) {
+ return(1);
+ } else if(xmlSecTransformCheckId(transform, xmlSecMSCngTransformAes256GcmId)) {
+ return(1);
+ }
+#endif /* XMLSEC_NO_AES */
+
+#ifndef XMLSEC_NO_DES
+ if(xmlSecTransformCheckId(transform, xmlSecMSCngTransformDes3CbcId)) {
+ return(1);
+ }
+#endif /* XMLSEC_NO_DES */
+
+ return(0);
+}
+
+static int
+xmlSecMSCngBlockCipherInitialize(xmlSecTransformPtr transform) {
+ xmlSecMSCngBlockCipherCtxPtr ctx;
+ NTSTATUS status;
+
+ xmlSecAssert2(xmlSecMSCngBlockCipherCheckId(transform), -1);
+ xmlSecAssert2(xmlSecTransformCheckSize(transform, xmlSecMSCngBlockCipherSize), -1);
+
+ ctx = xmlSecMSCngBlockCipherGetCtx(transform);
+ xmlSecAssert2(ctx != NULL, -1);
+
+ memset(ctx, 0, sizeof(xmlSecMSCngBlockCipherCtx));
+
+#ifndef XMLSEC_NO_AES
+ if(transform->id == xmlSecMSCngTransformAes128CbcId) {
+ ctx->pszAlgId = BCRYPT_AES_ALGORITHM;
+ ctx->keyId = xmlSecMSCngKeyDataAesId;
+ ctx->keySize = 16;
+ ctx->cbcMode = 1;
+ } else if(transform->id == xmlSecMSCngTransformAes192CbcId) {
+ ctx->pszAlgId = BCRYPT_AES_ALGORITHM;
+ ctx->keyId = xmlSecMSCngKeyDataAesId;
+ ctx->keySize = 24;
+ ctx->cbcMode = 1;
+ } else if(transform->id == xmlSecMSCngTransformAes256CbcId) {
+ ctx->pszAlgId = BCRYPT_AES_ALGORITHM;
+ ctx->keyId = xmlSecMSCngKeyDataAesId;
+ ctx->keySize = 32;
+ ctx->cbcMode = 1;
+ } else if(transform->id == xmlSecMSCngTransformAes128GcmId) {
+ ctx->pszAlgId = BCRYPT_AES_ALGORITHM;
+ ctx->keyId = xmlSecMSCngKeyDataAesId;
+ ctx->keySize = 16;
+ ctx->cbcMode = 0;
+ } else if(transform->id == xmlSecMSCngTransformAes192GcmId) {
+ ctx->pszAlgId = BCRYPT_AES_ALGORITHM;
+ ctx->keyId = xmlSecMSCngKeyDataAesId;
+ ctx->keySize = 24;
+ ctx->cbcMode = 0;
+ } else if(transform->id == xmlSecMSCngTransformAes256GcmId) {
+ ctx->pszAlgId = BCRYPT_AES_ALGORITHM;
+ ctx->keyId = xmlSecMSCngKeyDataAesId;
+ ctx->keySize = 32;
+ ctx->cbcMode = 0;
+ } else
+#endif /* XMLSEC_NO_AES */
+
+#ifndef XMLSEC_NO_DES
+ if(transform->id == xmlSecMSCngTransformDes3CbcId) {
+ ctx->pszAlgId = BCRYPT_3DES_ALGORITHM;
+ ctx->keyId = xmlSecMSCngKeyDataDesId;
+ ctx->keySize = 24;
+ ctx->cbcMode = 1;
+ } else
+#endif /* XMLSEC_NO_DES */
+
+ {
+ xmlSecInvalidTransfromError(transform)
+ return(-1);
+ }
+
+ status = BCryptOpenAlgorithmProvider(
+ &ctx->hAlg,
+ ctx->pszAlgId,
+ NULL,
+ 0);
+ if(status != STATUS_SUCCESS) {
+ xmlSecMSCngNtError("BCryptOpenAlgorithmProvider",
+ xmlSecTransformGetName(transform), status);
+ return(-1);
+ }
+
+ if(ctx->cbcMode) {
+ status = BCryptSetProperty(ctx->hAlg,
+ BCRYPT_CHAINING_MODE,
+ (PUCHAR)BCRYPT_CHAIN_MODE_CBC,
+ sizeof(BCRYPT_CHAIN_MODE_CBC),
+ 0);
+ } else {
+ status = BCryptSetProperty(ctx->hAlg,
+ BCRYPT_CHAINING_MODE,
+ (PUCHAR)BCRYPT_CHAIN_MODE_GCM,
+ sizeof(BCRYPT_CHAIN_MODE_GCM),
+ 0);
+ }
+ if(status != STATUS_SUCCESS) {
+ xmlSecMSCngNtError("BCryptSetProperty", xmlSecTransformGetName(transform), status);
+ return(-1);
+ }
+
+ ctx->ctxInitialized = 0;
+
+ return(0);
+}
+
+static void
+xmlSecMSCngBlockCipherFinalize(xmlSecTransformPtr transform) {
+ xmlSecMSCngBlockCipherCtxPtr ctx;
+
+ xmlSecAssert(xmlSecMSCngBlockCipherCheckId(transform));
+ xmlSecAssert(xmlSecTransformCheckSize(transform, xmlSecMSCngBlockCipherSize));
+
+ ctx = xmlSecMSCngBlockCipherGetCtx(transform);
+ xmlSecAssert(ctx != NULL);
+
+ if(ctx->pbIV != NULL) {
+ xmlFree(ctx->pbIV);
+ }
+
+ if(ctx->authInfo.pbNonce != NULL) {
+ xmlFree(ctx->authInfo.pbNonce);
+ }
+ if(ctx->authInfo.pbTag != NULL) {
+ xmlFree(ctx->authInfo.pbTag);
+ }
+ if(ctx->authInfo.pbMacContext != NULL) {
+ xmlFree(ctx->authInfo.pbMacContext);
+ }
+
+ if(ctx->hKey != NULL) {
+ BCryptDestroyKey(ctx->hKey);
+ }
+
+ if(ctx->pbKeyObject != NULL) {
+ xmlFree(ctx->pbKeyObject);
+ }
+
+ if(ctx->hAlg != NULL) {
+ BCryptCloseAlgorithmProvider(ctx->hAlg, 0);
+ }
+
+ memset(ctx, 0, sizeof(xmlSecMSCngBlockCipherCtx));
+}
+
+static int
+xmlSecMSCngBlockCipherSetKeyReq(xmlSecTransformPtr transform, xmlSecKeyReqPtr keyReq) {
+ xmlSecMSCngBlockCipherCtxPtr ctx;
+
+ xmlSecAssert2(xmlSecMSCngBlockCipherCheckId(transform), -1);
+ xmlSecAssert2((transform->operation == xmlSecTransformOperationEncrypt) || (transform->operation == xmlSecTransformOperationDecrypt), -1);
+ xmlSecAssert2(xmlSecTransformCheckSize(transform, xmlSecMSCngBlockCipherSize), -1);
+ xmlSecAssert2(keyReq != NULL, -1);
+
+ ctx = xmlSecMSCngBlockCipherGetCtx(transform);
+ xmlSecAssert2(ctx != NULL, -1);
+ xmlSecAssert2(ctx->hAlg != 0, -1);
+
+ keyReq->keyId = ctx->keyId;
+ keyReq->keyType = xmlSecKeyDataTypeSymmetric;
+ if(transform->operation == xmlSecTransformOperationEncrypt) {
+ keyReq->keyUsage = xmlSecKeyUsageEncrypt;
+ } else {
+ keyReq->keyUsage = xmlSecKeyUsageDecrypt;
+ }
+
+ keyReq->keyBitsSize = 8 * ctx->keySize;
+ return(0);
+}
+
+static int
+xmlSecMSCngBlockCipherSetKey(xmlSecTransformPtr transform, xmlSecKeyPtr key) {
+ xmlSecMSCngBlockCipherCtxPtr ctx;
+ xmlSecBufferPtr buffer;
+ xmlSecBuffer blob;
+ BCRYPT_KEY_DATA_BLOB_HEADER* blobHeader;
+ xmlSecSize blobHeaderLen;
+ BYTE* bufData;
+ DWORD dwKeyObjectLength, bytesWritten;
+ NTSTATUS status;
+ int ret;
+
+ xmlSecAssert2(xmlSecMSCngBlockCipherCheckId(transform), -1);
+ xmlSecAssert2((transform->operation == xmlSecTransformOperationEncrypt) || (transform->operation == xmlSecTransformOperationDecrypt), -1);
+ xmlSecAssert2(xmlSecTransformCheckSize(transform, xmlSecMSCngBlockCipherSize), -1);
+ xmlSecAssert2(key != NULL, -1);
+
+ /* get the symmetric key into bufData */
+ ctx = xmlSecMSCngBlockCipherGetCtx(transform);
+ xmlSecAssert2(ctx != NULL, -1);
+ xmlSecAssert2(ctx->hKey == 0, -1);
+ xmlSecAssert2(ctx->keyId != NULL, -1);
+ xmlSecAssert2(xmlSecKeyCheckId(key, ctx->keyId), -1);
+ xmlSecAssert2(ctx->keySize > 0, -1);
+ xmlSecAssert2(ctx->pbKeyObject == NULL, -1);
+
+ buffer = xmlSecKeyDataBinaryValueGetBuffer(xmlSecKeyGetValue(key));
+ xmlSecAssert2(buffer != NULL, -1);
+
+ if(xmlSecBufferGetSize(buffer) < ctx->keySize) {
+ xmlSecInvalidKeyDataSizeError(xmlSecBufferGetSize(buffer), ctx->keySize,
+ xmlSecTransformGetName(transform));
+ return(-1);
+ }
+
+ bufData = xmlSecBufferGetData(buffer);
+ xmlSecAssert2(bufData != NULL, -1);
+
+ /* allocate the key object */
+ dwKeyObjectLength = 0;
+ status = BCryptGetProperty(ctx->hAlg,
+ BCRYPT_OBJECT_LENGTH,
+ (PUCHAR)&dwKeyObjectLength,
+ (ULONG)sizeof(DWORD),
+ &bytesWritten, 0);
+ if(status != STATUS_SUCCESS) {
+ xmlSecMSCngNtError("BCryptGetProperty",
+ xmlSecTransformGetName(transform), status);
+ return(-1);
+ }
+
+ ctx->pbKeyObject = xmlMalloc(dwKeyObjectLength);
+ if(ctx->pbKeyObject == NULL) {
+ xmlSecMallocError(dwKeyObjectLength, xmlSecTransformGetName(transform));
+ return(-1);
+ }
+
+ /* prefix the key with a BCRYPT_KEY_DATA_BLOB_HEADER */
+ blobHeaderLen = sizeof(BCRYPT_KEY_DATA_BLOB_HEADER) + xmlSecBufferGetSize(buffer);
+ ret = xmlSecBufferInitialize(&blob, blobHeaderLen);
+ if(ret < 0) {
+ xmlSecInternalError2("xmlSecBufferInitialize",
+ xmlSecTransformGetName(transform), "size=%d", blobHeaderLen);
+ return(-1);
+ }
+
+ blobHeader = (BCRYPT_KEY_DATA_BLOB_HEADER*)xmlSecBufferGetData(&blob);
+ blobHeader->dwMagic = BCRYPT_KEY_DATA_BLOB_MAGIC;
+ blobHeader->dwVersion = BCRYPT_KEY_DATA_BLOB_VERSION1;
+ blobHeader->cbKeyData = (ULONG)xmlSecBufferGetSize(buffer);
+ memcpy(xmlSecBufferGetData(&blob) + sizeof(BCRYPT_KEY_DATA_BLOB_HEADER),
+ bufData, xmlSecBufferGetSize(buffer));
+ xmlSecBufferSetSize(&blob, blobHeaderLen);
+
+ /* perform the actual import */
+ status = BCryptImportKey(ctx->hAlg,
+ NULL,
+ BCRYPT_KEY_DATA_BLOB,
+ &ctx->hKey,
+ ctx->pbKeyObject,
+ dwKeyObjectLength,
+ xmlSecBufferGetData(&blob),
+ (ULONG)xmlSecBufferGetSize(&blob),
+ 0);
+ if(status != STATUS_SUCCESS) {
+ xmlSecMSCngNtError("BCryptImportKey",
+ xmlSecTransformGetName(transform), status);
+ xmlSecBufferFinalize(&blob);
+ return(-1);
+ }
+
+ xmlSecBufferFinalize(&blob);
+
+ return(0);
+}
+
+static int xmlSecMSCngCBCBlockCipherCtxInit(xmlSecMSCngBlockCipherCtxPtr ctx,
+ xmlSecBufferPtr in, xmlSecBufferPtr out, int encrypt,
+ const xmlChar* cipherName, xmlSecTransformCtxPtr transformCtx) {
+
+ NTSTATUS status;
+ int ret;
+
+ /* unreferenced parameter */
+ (void)transformCtx;
+
+ xmlSecAssert2(ctx != NULL, -1);
+ xmlSecAssert2(ctx->hKey != 0, -1);
+ xmlSecAssert2(ctx->ctxInitialized == 0, -1);
+ xmlSecAssert2(ctx->dwBlockLen > 0, -1);
+ xmlSecAssert2(in != NULL, -1);
+ xmlSecAssert2(out != NULL, -1);
+ xmlSecAssert2(transformCtx != NULL, -1);
+
+ /* iv len == block len */
+ ctx->cbIV = ctx->dwBlockLen;
+
+ if(encrypt) {
+ unsigned char* iv;
+ xmlSecSize outSize;
+
+ /* allocate space for IV */
+ outSize = xmlSecBufferGetSize(out);
+ ret = xmlSecBufferSetSize(out, outSize + ctx->dwBlockLen);
+ if(ret < 0) {
+ xmlSecInternalError2("xmlSecBufferSetSize", cipherName,
+ "size=%d", outSize + ctx->dwBlockLen);
+ return(-1);
+ }
+ iv = xmlSecBufferGetData(out) + outSize;
+
+ /* generate and use random iv */
+ status = BCryptGenRandom(NULL,
+ (PBYTE)iv,
+ ctx->dwBlockLen,
+ BCRYPT_USE_SYSTEM_PREFERRED_RNG);
+ if(status != STATUS_SUCCESS) {
+ xmlSecMSCngNtError("BCryptGenRandom", cipherName, status);
+ return(-1);
+ }
+
+ if(ctx->pbIV == NULL) {
+ ctx->pbIV = xmlMalloc(ctx->dwBlockLen);
+ }
+ if(ctx->pbIV == NULL) {
+ xmlSecMallocError(ctx->dwBlockLen, cipherName);
+ return(-1);
+ }
+
+ memcpy(ctx->pbIV, iv, ctx->dwBlockLen);
+ } else {
+ /* if we don't have enough data, exit and hope that
+ * we'll have iv next time */
+ if(xmlSecBufferGetSize(in) < XMLSEC_SIZE_BAD_CAST(ctx->dwBlockLen)) {
+ return(0);
+ }
+ xmlSecAssert2(xmlSecBufferGetData(in) != NULL, -1);
+
+ /* set iv */
+ ctx->pbIV = xmlMalloc(ctx->dwBlockLen);
+ if(ctx->pbIV == NULL) {
+ xmlSecMallocError(ctx->dwBlockLen, cipherName);
+ return(-1);
+ }
+ memcpy(ctx->pbIV, xmlSecBufferGetData(in), ctx->dwBlockLen);
+
+ /* and remove from input */
+ ret = xmlSecBufferRemoveHead(in, ctx->dwBlockLen);
+ if(ret < 0) {
+ xmlSecInternalError2("xmlSecBufferRemoveHead", cipherName,
+ "size=%d", ctx->dwBlockLen);
+ return(-1);
+
+ }
+ }
+
+ ctx->ctxInitialized = 1;
+ return(0);
+}
+
+static int xmlSecMSCngGCMBlockCipherCtxInit(xmlSecMSCngBlockCipherCtxPtr ctx,
+ xmlSecBufferPtr in, xmlSecBufferPtr out, int encrypt, int last,
+ const xmlChar* cipherName, xmlSecTransformCtxPtr transformCtx) {
+
+ NTSTATUS status;
+ int ret;
+ xmlSecByte *bufferPtr;
+ xmlSecSize bufferSize;
+ DWORD bytesRead;
+ BCRYPT_AUTH_TAG_LENGTHS_STRUCT authTagLengths;
+
+ /* unreferenced parameter */
+ (void)transformCtx;
+
+ xmlSecAssert2(ctx != NULL, -1);
+ xmlSecAssert2(ctx->hKey != 0, -1);
+ xmlSecAssert2(ctx->ctxInitialized == 0, -1);
+ xmlSecAssert2(ctx->dwBlockLen > 0, -1);
+ xmlSecAssert2(in != NULL, -1);
+ xmlSecAssert2(out != NULL, -1);
+ xmlSecAssert2(transformCtx != NULL, -1);
+
+ /* Check that we haven't already allocated space for the nonce. Might
+ * happen if the context is initialised more that once */
+ if(ctx->authInfo.pbNonce == NULL) {
+ ctx->authInfo.pbNonce = xmlMalloc(xmlSecMSCngAesGcmNonceLengthInBytes);
+ if(ctx->authInfo.pbNonce == NULL) {
+ xmlSecMallocError(xmlSecMSCngAesGcmNonceLengthInBytes, cipherName);
+ return(-1);
+ }
+ }
+ ctx->authInfo.cbNonce = xmlSecMSCngAesGcmNonceLengthInBytes;
+
+ /* Tag length is 128 bits */
+ /* See http://www.w3.org/TR/xmlenc-core1/#sec-AES-GCM */
+ if(ctx->authInfo.pbTag == NULL) {
+ ctx->authInfo.pbTag = xmlMalloc(xmlSecMSCngAesGcmTagLengthInBytes);
+ if(ctx->authInfo.pbTag == NULL) {
+ xmlSecMallocError(xmlSecMSCngAesGcmTagLengthInBytes, cipherName);
+ return(-1);
+ }
+ }
+ memset(ctx->authInfo.pbTag, 0, xmlSecMSCngAesGcmTagLengthInBytes);
+ ctx->authInfo.cbTag = xmlSecMSCngAesGcmTagLengthInBytes;
+
+ if(last == 0) {
+ /* Need some working buffers */
+
+ /* iv len == block len */
+ if(ctx->pbIV == NULL) {
+ ctx->pbIV = xmlMalloc(ctx->dwBlockLen);
+ if(ctx->pbIV == NULL) {
+ xmlSecMallocError(ctx->dwBlockLen, cipherName);
+ return(-1);
+ }
+ }
+ ctx->cbIV = ctx->dwBlockLen;
+ memset(ctx->pbIV, 0, ctx->dwBlockLen);
+
+ /* Setup an empty MAC context if we're chaining calls */
+ status = BCryptGetProperty(ctx->hAlg,
+ BCRYPT_AUTH_TAG_LENGTH,
+ (PUCHAR)&authTagLengths,
+ (ULONG)sizeof(authTagLengths),
+ &bytesRead,
+ 0);
+ if(status != STATUS_SUCCESS) {
+ xmlSecMSCngNtError("BCryptGetProperty", cipherName, status);
+ return(-1);
+ }
+
+ if(ctx->authInfo.pbMacContext == NULL) {
+ ctx->authInfo.pbMacContext = xmlMalloc(authTagLengths.dwMaxLength);
+ if(ctx->authInfo.pbMacContext == NULL) {
+ xmlSecMallocError(authTagLengths.dwMaxLength, cipherName);
+ return(-1);
+ }
+ }
+ ctx->authInfo.cbMacContext = authTagLengths.dwMaxLength;
+ memset(ctx->authInfo.pbMacContext, 0, authTagLengths.dwMaxLength);
+ ctx->authInfo.dwFlags |= BCRYPT_AUTH_MODE_CHAIN_CALLS_FLAG;
+ } else {
+ ctx->pbIV = NULL;
+ ctx->cbIV = 0;
+ }
+
+ if(encrypt) {
+
+ /* allocate space for nonce in the output buffer - it is 96 bits for GCM mode */
+ /* See http://www.w3.org/TR/xmlenc-core1/#sec-AES-GCM */
+ bufferSize = xmlSecBufferGetSize(out);
+ ret = xmlSecBufferSetSize(out, bufferSize + xmlSecMSCngAesGcmNonceLengthInBytes);
+ if(ret < 0) {
+ xmlSecInternalError2("xmlSecBufferSetSize", cipherName,
+ "size=%d", bufferSize + xmlSecMSCngAesGcmNonceLengthInBytes);
+ return(-1);
+ }
+ bufferPtr = xmlSecBufferGetData(out) + bufferSize;
+
+ /* generate and use random nonce */
+ status = BCryptGenRandom(NULL,
+ (PBYTE)bufferPtr,
+ xmlSecMSCngAesGcmNonceLengthInBytes,
+ BCRYPT_USE_SYSTEM_PREFERRED_RNG);
+ if(status != STATUS_SUCCESS) {
+ xmlSecMSCngNtError("BCryptGenRandom", cipherName, status);
+ return(-1);
+ }
+ /* copy the nonce into the padding info */
+ memcpy(ctx->authInfo.pbNonce, bufferPtr, xmlSecMSCngAesGcmNonceLengthInBytes);
+
+ } else {
+ /* if we don't have enough data, exit and hope that
+ we'll have the nonce next time */
+ bufferSize = xmlSecBufferGetSize(in);
+ if(bufferSize < xmlSecMSCngAesGcmNonceLengthInBytes) {
+ return(0);
+ }
+
+ bufferPtr = xmlSecBufferGetData(in);
+
+ xmlSecAssert2(bufferPtr != NULL, -1);
+
+ /* set nonce */
+ memcpy(ctx->authInfo.pbNonce, bufferPtr, xmlSecMSCngAesGcmNonceLengthInBytes);
+
+ /* remove nonce from input */
+ ret = xmlSecBufferRemoveHead(in, xmlSecMSCngAesGcmNonceLengthInBytes);
+ if(ret < 0) {
+ xmlSecInternalError2("xmlSecBufferRemoveHead", cipherName,
+ "size=%d", xmlSecMSCngAesGcmNonceLengthInBytes);
+ return(-1);
+ }
+ }
+
+ ctx->ctxInitialized = 1;
+ return(0);
+}
+
+static int
+xmlSecMSCngBlockCipherCtxInit(xmlSecMSCngBlockCipherCtxPtr ctx,
+ xmlSecBufferPtr in, xmlSecBufferPtr out, int encrypt, int last,
+ const xmlChar* cipherName, xmlSecTransformCtxPtr transformCtx) {
+ NTSTATUS status;
+ DWORD dwBlockLenLen;
+
+ xmlSecAssert2(ctx != NULL, -1);
+ xmlSecAssert2(ctx->hKey != 0, -1);
+ xmlSecAssert2(ctx->hAlg != 0, -1);
+ xmlSecAssert2(ctx->ctxInitialized == 0, -1);
+
+ /* Get the cipher block length */
+ dwBlockLenLen = sizeof(DWORD);
+ status = BCryptGetProperty(ctx->hAlg,
+ BCRYPT_BLOCK_LENGTH,
+ (PUCHAR)&ctx->dwBlockLen,
+ sizeof(ctx->dwBlockLen),
+ &dwBlockLenLen,
+ 0);
+ if(status != STATUS_SUCCESS) {
+ xmlSecMSCngNtError("BCryptGetProperty", cipherName, status);
+ return(-1);
+ }
+
+ xmlSecAssert2(ctx->dwBlockLen > 0, -1);
+
+ if(ctx->cbcMode) {
+ return xmlSecMSCngCBCBlockCipherCtxInit(ctx, in, out, encrypt,
+ cipherName, transformCtx);
+ } else {
+ return xmlSecMSCngGCMBlockCipherCtxInit(ctx, in, out, encrypt, last,
+ cipherName, transformCtx);
+ }
+}
+
+static int
+xmlSecMSCngCBCBlockCipherCtxUpdate(xmlSecMSCngBlockCipherCtxPtr ctx,
+ xmlSecBufferPtr in, xmlSecBufferPtr out, int encrypt,
+ const xmlChar* cipherName, xmlSecTransformCtxPtr transformCtx) {
+ xmlSecSize inSize, inBlocks, outSize;
+ unsigned char* outBuf;
+ unsigned char* inBuf;
+ DWORD dwCLen;
+ NTSTATUS status;
+ int ret;
+
+ /* unreferenced parameter */
+ (void)transformCtx;
+
+ xmlSecAssert2(ctx != NULL, -1);
+ xmlSecAssert2(ctx->ctxInitialized != 0, -1);
+ xmlSecAssert2(ctx->dwBlockLen > 0, -1);
+ xmlSecAssert2(in != NULL, -1);
+ xmlSecAssert2(out != NULL, -1);
+ xmlSecAssert2(transformCtx != NULL, -1);
+
+ inSize = xmlSecBufferGetSize(in);
+ outSize = xmlSecBufferGetSize(out);
+
+ if(inSize < XMLSEC_SIZE_BAD_CAST(ctx->dwBlockLen)) {
+ return(0);
+ }
+
+ if(encrypt) {
+ inBlocks = inSize / XMLSEC_SIZE_BAD_CAST(ctx->dwBlockLen);
+ } else {
+ /* we want to have the last block in the input buffer
+ * for padding check */
+ inBlocks = (inSize - 1) / XMLSEC_SIZE_BAD_CAST(ctx->dwBlockLen);
+ }
+ inSize = inBlocks * XMLSEC_SIZE_BAD_CAST(ctx->dwBlockLen);
+
+ /* we write out the input size plus maybe one block */
+ ret = xmlSecBufferSetMaxSize(out, outSize + inSize + ctx->dwBlockLen);
+ if(ret < 0) {
+ xmlSecInternalError2("xmlSecBufferSetMaxSize", cipherName,
+ "size=%d", outSize + inSize + ctx->dwBlockLen);
+ return(-1);
+ }
+ outBuf = xmlSecBufferGetData(out) + outSize;
+ inBuf = xmlSecBufferGetData(in);
+ xmlSecAssert2(inBuf != NULL, -1);
+
+ dwCLen = (DWORD)inSize;
+ if(encrypt) {
+ status = BCryptEncrypt(ctx->hKey,
+ inBuf,
+ (ULONG)inSize,
+ NULL,
+ ctx->pbIV,
+ ctx->cbIV,
+ outBuf,
+ (ULONG)inSize,
+ &dwCLen,
+ 0);
+ if(status != STATUS_SUCCESS) {
+ xmlSecMSCngNtError("BCryptEncrypt", cipherName, status);
+ return(-1);
+ }
+
+ /* check if we really have encrypted the numbers of bytes that we
+ * requested */
+ if(dwCLen != inSize) {
+ xmlSecInternalError2("BCryptEncrypt", cipherName, "size=%ld",
+ dwCLen);
+ return(-1);
+ }
+ } else {
+ status = BCryptDecrypt(ctx->hKey,
+ inBuf,
+ (ULONG)inSize,
+ NULL,
+ ctx->pbIV,
+ ctx->cbIV,
+ outBuf,
+ (ULONG)inSize,
+ &dwCLen,
+ 0);
+ if(status != STATUS_SUCCESS) {
+ xmlSecMSCngNtError("BCryptDecrypt", cipherName, status);
+ return(-1);
+ }
+
+ /* check if we really have decrypted the numbers of bytes that we
+ * requested */
+ if(dwCLen != inSize) {
+ xmlSecInternalError2("BCryptDecrypt", cipherName, "size=%ld",
+ dwCLen);
+ return(-1);
+ }
+ }
+
+ /* set correct output buffer size */
+ ret = xmlSecBufferSetSize(out, outSize + inSize);
+ if(ret < 0) {
+ xmlSecInternalError2("xmlSecBufferSetSize", cipherName, "size=%d",
+ outSize + inSize);
+ return(-1);
+ }
+
+ /* remove the processed block from input */
+ ret = xmlSecBufferRemoveHead(in, inSize);
+ if(ret < 0) {
+ xmlSecInternalError2("xmlSecBufferRemoveHead", cipherName, "size=%d",
+ inSize);
+ return(-1);
+ }
+
+ return(0);
+}
+
+static int
+xmlSecMSCngGCMBlockCipherCtxUpdate(xmlSecMSCngBlockCipherCtxPtr ctx,
+ xmlSecBufferPtr in, xmlSecBufferPtr out, int encrypt, int last,
+ const xmlChar* cipherName, xmlSecTransformCtxPtr transformCtx) {
+
+ NTSTATUS status;
+ xmlSecSize inSize, outSize;
+ xmlSecByte *inBuf, *outBuf;
+ DWORD dwCLen;
+ int ret;
+
+ /* unreferenced parameter */
+ (void)transformCtx;
+
+ xmlSecAssert2(ctx != NULL, -1);
+ xmlSecAssert2(ctx->ctxInitialized != 0, -1);
+ xmlSecAssert2(ctx->dwBlockLen > 0, -1);
+ xmlSecAssert2(in != NULL, -1);
+ xmlSecAssert2(out != NULL, -1);
+ xmlSecAssert2(transformCtx != NULL, -1);
+
+ if(last != 0) {
+ /* We handle everything in finalize for the last block of data */
+ return(0);
+ }
+
+ inBuf = xmlSecBufferGetData(in);
+ xmlSecAssert2(inBuf != NULL, -1);
+
+ if(xmlSecBufferGetSize(in) < ctx->dwBlockLen) {
+ return 0;
+ }
+
+ if(encrypt) {
+ /* Round to the block size. We will finalize this later */
+ inSize = (xmlSecBufferGetSize(in) / XMLSEC_SIZE_BAD_CAST(ctx->dwBlockLen)) * XMLSEC_SIZE_BAD_CAST(ctx->dwBlockLen);
+ } else {
+ /* If we've been called here, we know there is more data
+ * to come, but we don't know how much. The spec tells us that
+ * the tag is the last 16 bytes of the data when decrypting, so to make sure
+ * we don't try to decrypt it, we leave at least 16 bytes in the buffer
+ * until we know we're processing the last one */
+ inSize = ((xmlSecBufferGetSize(in) - xmlSecMSCngAesGcmTagLengthInBytes) / XMLSEC_SIZE_BAD_CAST(ctx->dwBlockLen)) * XMLSEC_SIZE_BAD_CAST(ctx->dwBlockLen);
+ if (inSize < ctx->dwBlockLen) {
+ return 0;
+ }
+ }
+
+ outSize = xmlSecBufferGetSize(out);
+ ret = xmlSecBufferSetMaxSize(out, outSize + inSize);
+ if(ret < 0) {
+ xmlSecInternalError2("xmlSecBufferSetMaxSize", cipherName,
+ "size=%d", outSize + inSize);
+ return(-1);
+ }
+
+ outBuf = xmlSecBufferGetData(out) + outSize;
+
+ dwCLen = 0;
+ if(encrypt) {
+ status = BCryptEncrypt(ctx->hKey,
+ inBuf,
+ (ULONG)inSize,
+ &ctx->authInfo,
+ ctx->pbIV,
+ ctx->cbIV,
+ outBuf,
+ (ULONG)inSize,
+ &dwCLen,
+ 0);
+
+ if(status != STATUS_SUCCESS) {
+ xmlSecMSCngNtError("BCryptEncrypt", cipherName, status);
+ return(-1);
+ }
+
+ /* check if we really have encrypted the numbers of bytes that we
+ * requested */
+ if(dwCLen != inSize) {
+ xmlSecInternalError2("BCryptEncrypt", cipherName, "size=%ld",
+ dwCLen);
+ return(-1);
+ }
+
+ } else {
+ status = BCryptDecrypt(ctx->hKey,
+ inBuf,
+ (ULONG)inSize,
+ &ctx->authInfo,
+ ctx->pbIV,
+ ctx->cbIV,
+ outBuf,
+ (ULONG)inSize,
+ &dwCLen,
+ 0);
+
+ if(status != STATUS_SUCCESS) {
+ xmlSecMSCngNtError("BCryptDecrypt", cipherName, status);
+ return(-1);
+ }
+
+ /* check if we really have decrypted the numbers of bytes that we
+ * requested */
+ if(dwCLen != inSize) {
+ xmlSecInternalError2("BCryptDecrypt", cipherName, "size=%ld",
+ dwCLen);
+ return(-1);
+ }
+ }
+
+ /* set correct output buffer size */
+ ret = xmlSecBufferSetSize(out, outSize + dwCLen);
+ if(ret < 0) {
+ xmlSecInternalError2("xmlSecBufferSetSize", cipherName, "size=%d",
+ outSize + dwCLen);
+ return(-1);
+ }
+
+ /* remove the processed data from input */
+ ret = xmlSecBufferRemoveHead(in, dwCLen);
+ if(ret < 0) {
+ xmlSecInternalError2("xmlSecBufferRemoveHead", cipherName, "size=%d",
+ dwCLen);
+ return(-1);
+ }
+
+ return(0);
+}
+
+static int
+xmlSecMSCngBlockCipherCtxUpdate(xmlSecMSCngBlockCipherCtxPtr ctx,
+ xmlSecBufferPtr in, xmlSecBufferPtr out, int encrypt, int last,
+ const xmlChar* cipherName, xmlSecTransformCtxPtr transformCtx) {
+
+ xmlSecAssert2(ctx != NULL, -1);
+
+ if(ctx->cbcMode) {
+ return xmlSecMSCngCBCBlockCipherCtxUpdate(ctx, in, out, encrypt,
+ cipherName, transformCtx);
+ } else {
+ return xmlSecMSCngGCMBlockCipherCtxUpdate(ctx, in, out, encrypt, last,
+ cipherName, transformCtx);
+ }
+}
+
+static int
+xmlSecMSCngCBCBlockCipherCtxFinal(xmlSecMSCngBlockCipherCtxPtr ctx,
+ xmlSecBufferPtr in, xmlSecBufferPtr out, int encrypt,
+ const xmlChar* cipherName, xmlSecTransformCtxPtr transformCtx) {
+ xmlSecSize inSize, outSize;
+ int outLen;
+ unsigned char* inBuf;
+ unsigned char* outBuf;
+ DWORD dwCLen;
+ NTSTATUS status;
+ int ret;
+
+ /* unreferenced parameter */
+ (void)transformCtx;
+
+ inSize = xmlSecBufferGetSize(in);
+ outSize = xmlSecBufferGetSize(out);
+
+ if(encrypt != 0) {
+ xmlSecAssert2(inSize < XMLSEC_SIZE_BAD_CAST(ctx->dwBlockLen), -1);
+
+ /* create padding */
+ ret = xmlSecBufferSetMaxSize(in, ctx->dwBlockLen);
+ if(ret < 0) {
+ xmlSecInternalError2("xmlSecBufferSetMaxSize", cipherName,
+ "size=%d", ctx->dwBlockLen);
+ return(-1);
+ }
+ inBuf = xmlSecBufferGetData(in);
+
+ /* create random padding */
+ if(XMLSEC_SIZE_BAD_CAST(ctx->dwBlockLen) > (inSize + 1)) {
+ status = BCryptGenRandom(NULL,
+ (PBYTE) inBuf + inSize,
+ (ULONG)(ctx->dwBlockLen - inSize - 1),
+ BCRYPT_USE_SYSTEM_PREFERRED_RNG);
+ if(status != STATUS_SUCCESS) {
+ xmlSecMSCngNtError("BCryptGetProperty", cipherName, status);
+ return(-1);
+ }
+ }
+ inBuf[ctx->dwBlockLen - 1] = (unsigned char)(ctx->dwBlockLen - inSize);
+ inSize = ctx->dwBlockLen;
+ } else {
+ if(inSize != XMLSEC_SIZE_BAD_CAST(ctx->dwBlockLen)) {
+ xmlSecInvalidSizeError("Input data", inSize, ctx->dwBlockLen, cipherName);
+ return(-1);
+ }
+ inBuf = xmlSecBufferGetData(in);
+ }
+
+ /* process last block */
+ ret = xmlSecBufferSetMaxSize(out, outSize + 2 * ctx->dwBlockLen);
+ if(ret < 0) {
+ xmlSecInternalError2("xmlSecBufferSetMaxSize", cipherName, "size=%d",
+ outSize + 2 * ctx->dwBlockLen);
+ return(-1);
+ }
+
+ outBuf = xmlSecBufferGetData(out) + outSize;
+
+ dwCLen = (ULONG)inSize;
+ if(encrypt) {
+ status = BCryptEncrypt(ctx->hKey,
+ inBuf,
+ (ULONG)inSize,
+ NULL,
+ ctx->pbIV,
+ ctx->cbIV,
+ outBuf,
+ (ULONG)(inSize + ctx->dwBlockLen),
+ &dwCLen,
+ 0);
+ if(status != STATUS_SUCCESS) {
+ xmlSecMSCngNtError("BCryptDecrypt", cipherName, status);
+ return(-1);
+ }
+
+ /* check if we really have encrypted the numbers of bytes that we
+ * requested */
+ if(dwCLen != inSize) {
+ xmlSecInternalError2("BCryptEncrypt", cipherName, "size=%ld",
+ dwCLen);
+ return(-1);
+ }
+ } else {
+ status = BCryptDecrypt(ctx->hKey,
+ inBuf,
+ (ULONG)inSize,
+ NULL,
+ ctx->pbIV,
+ ctx->cbIV,
+ outBuf,
+ (ULONG)inSize,
+ &dwCLen,
+ 0);
+ if(status != STATUS_SUCCESS) {
+ xmlSecMSCngNtError("BCryptDecrypt", cipherName, status);
+ return(-1);
+ }
+
+ /* check if we really have decrypted the numbers of bytes that we
+ * requested */
+ if(dwCLen != inSize) {
+ xmlSecInternalError2("BCryptDecrypt", cipherName, "size=%ld",
+ dwCLen);
+ return(-1);
+ }
+ }
+
+ if(encrypt == 0) {
+ /* check padding */
+ if(inSize < outBuf[ctx->dwBlockLen - 1]) {
+ xmlSecInvalidSizeLessThanError("Input data padding", inSize,
+ outBuf[ctx->dwBlockLen - 1], cipherName);
+ return(-1);
+ }
+ outLen = (int)(inSize - outBuf[ctx->dwBlockLen - 1]);
+ } else {
+ outLen = (int)inSize;
+ }
+
+ /* set correct output buffer size */
+ ret = xmlSecBufferSetSize(out, outSize + outLen);
+ if(ret < 0) {
+ xmlSecInternalError2("xmlSecBufferSetSize", cipherName, "size=%d",
+ outSize + outLen);
+ return(-1);
+ }
+
+ /* remove the processed block from input */
+ ret = xmlSecBufferRemoveHead(in, inSize);
+ if(ret < 0) {
+ xmlSecInternalError2("xmlSecBufferRemoveHead", cipherName, "size=%d",
+ inSize);
+ return(-1);
+ }
+
+ return(0);
+}
+
+static int
+xmlSecMSCngGCMBlockCipherCtxFinal(xmlSecMSCngBlockCipherCtxPtr ctx,
+ xmlSecBufferPtr in, xmlSecBufferPtr out, int encrypt,
+ const xmlChar* cipherName, xmlSecTransformCtxPtr transformCtx)
+{
+ xmlSecByte *inBuf, *outBuf;
+ xmlSecSize inBufSize, outBufSize, outLen;
+ DWORD dwCLen;
+ int ret;
+ NTSTATUS status;
+
+ /* unreferenced parameter */
+ (void)transformCtx;
+
+ ctx->authInfo.dwFlags &= ~BCRYPT_AUTH_MODE_CHAIN_CALLS_FLAG; /* clear chaining flag */
+
+ outBufSize = xmlSecBufferGetSize(out);
+ inBufSize = xmlSecBufferGetSize(in);
+ inBuf = xmlSecBufferGetData(in);
+
+ if(encrypt) {
+ ret = xmlSecBufferSetMaxSize(out,
+ outBufSize + inBufSize + xmlSecMSCngAesGcmTagLengthInBytes); /* add space for the tag */
+ if(ret < 0) {
+ xmlSecInternalError2("xmlSecBufferSetMaxSize", cipherName,
+ "size=%d", outBufSize + inBufSize + xmlSecMSCngAesGcmTagLengthInBytes);
+ return(-1);
+ }
+
+ outBuf = xmlSecBufferGetData(out) + outBufSize;
+
+ status = BCryptEncrypt(ctx->hKey,
+ inBuf,
+ (ULONG)inBufSize,
+ &ctx->authInfo,
+ ctx->pbIV,
+ ctx->cbIV,
+ outBuf,
+ (ULONG)inBufSize,
+ &dwCLen,
+ 0);
+
+ if(status != STATUS_SUCCESS) {
+ xmlSecMSCngNtError("BCryptEncrypt", cipherName, status);
+ return(-1);
+ }
+
+ /* check if we really have encrypted the numbers of bytes that we
+ * requested */
+ if(dwCLen != inBufSize) {
+ xmlSecInternalError2("BCryptEncrypt", cipherName, "size=%ld",
+ dwCLen);
+ return(-1);
+ }
+
+ /* Now add the tag at the end of the buffer */
+ memcpy(outBuf + inBufSize, ctx->authInfo.pbTag, xmlSecMSCngAesGcmTagLengthInBytes);
+
+ outLen = inBufSize + xmlSecMSCngAesGcmTagLengthInBytes;
+
+ } else {
+ /* Get the tag */
+ memcpy(ctx->authInfo.pbTag, inBuf + inBufSize - xmlSecMSCngAesGcmTagLengthInBytes,
+ xmlSecMSCngAesGcmTagLengthInBytes);
+
+ /* remove the tag from the buffer */
+ ret = xmlSecBufferRemoveTail(in, xmlSecMSCngAesGcmTagLengthInBytes);
+ if(ret < 0) {
+ xmlSecInternalError2("xmlSecBufferRemoveTail", cipherName,
+ "size=%d", xmlSecMSCngAesGcmTagLengthInBytes);
+ return(-1);
+ }
+
+ inBuf = xmlSecBufferGetData(in);
+ inBufSize = xmlSecBufferGetSize(in);
+
+ ret = xmlSecBufferSetMaxSize(out, outBufSize + inBufSize);
+ if(ret < 0) {
+ xmlSecInternalError2("xmlSecBufferSetMaxSize", cipherName,
+ "size=%d", outBufSize + inBufSize);
+ return(-1);
+ }
+
+ outBuf = xmlSecBufferGetData(out) + outBufSize;
+
+ status = BCryptDecrypt(ctx->hKey,
+ inBuf,
+ (ULONG)inBufSize,
+ &ctx->authInfo,
+ ctx->pbIV,
+ ctx->cbIV,
+ outBuf,
+ (ULONG)inBufSize,
+ &dwCLen,
+ 0);
+
+ if(status != STATUS_SUCCESS) {
+ xmlSecMSCngNtError("BCryptDecrypt", cipherName, status);
+ return(-1);
+ }
+
+ /* check if we really have decrypted the numbers of bytes that we
+ * requested */
+ if(dwCLen != inBufSize) {
+ xmlSecInternalError2("BCryptDecrypt", cipherName, "size=%ld",
+ dwCLen);
+ return(-1);
+ }
+
+ outLen = inBufSize;
+ }
+
+ /* set correct output buffer size */
+ ret = xmlSecBufferSetSize(out, outBufSize + outLen);
+ if(ret < 0) {
+ xmlSecInternalError2("xmlSecBufferSetSize", cipherName, "size=%d",
+ outBufSize + outLen);
+ return(-1);
+ }
+
+ /* remove the processed block from input */
+ ret = xmlSecBufferRemoveHead(in, inBufSize);
+ if(ret < 0) {
+ xmlSecInternalError2("xmlSecBufferRemoveHead", cipherName, "size=%d",
+ inBufSize);
+ return(-1);
+ }
+
+ return(0);
+}
+
+static int
+xmlSecMSCngBlockCipherCtxFinal(xmlSecMSCngBlockCipherCtxPtr ctx,
+ xmlSecBufferPtr in, xmlSecBufferPtr out, int encrypt,
+ const xmlChar* cipherName, xmlSecTransformCtxPtr transformCtx)
+{
+ xmlSecAssert2(ctx != NULL, -1);
+ xmlSecAssert2(ctx->ctxInitialized != 0, -1);
+ xmlSecAssert2(in != NULL, -1);
+ xmlSecAssert2(out != NULL, -1);
+ xmlSecAssert2(transformCtx != NULL, -1);
+
+ if(ctx->cbcMode) {
+ return xmlSecMSCngCBCBlockCipherCtxFinal(ctx, in, out, encrypt,
+ cipherName, transformCtx);
+ } else {
+ return xmlSecMSCngGCMBlockCipherCtxFinal(ctx, in, out, encrypt,
+ cipherName, transformCtx);
+ }
+}
+
+static int
+xmlSecMSCngBlockCipherExecute(xmlSecTransformPtr transform, int last,
+ xmlSecTransformCtxPtr transformCtx) {
+ xmlSecMSCngBlockCipherCtxPtr ctx;
+ xmlSecBufferPtr in, out;
+ int ret, encrypt;
+
+ xmlSecAssert2(xmlSecMSCngBlockCipherCheckId(transform), -1);
+ xmlSecAssert2((transform->operation == xmlSecTransformOperationEncrypt) || (transform->operation == xmlSecTransformOperationDecrypt), -1);
+ xmlSecAssert2(xmlSecTransformCheckSize(transform, xmlSecMSCngBlockCipherSize), -1);
+ xmlSecAssert2(transformCtx != NULL, -1);
+
+ in = &(transform->inBuf);
+ out = &(transform->outBuf);
+
+ ctx = xmlSecMSCngBlockCipherGetCtx(transform);
+ xmlSecAssert2(ctx != NULL, -1);
+
+ if(transform->status == xmlSecTransformStatusNone) {
+ /* This should only be done once, before the context has been initialised */
+ BCRYPT_INIT_AUTH_MODE_INFO(ctx->authInfo);
+ transform->status = xmlSecTransformStatusWorking;
+ }
+
+ if(transform->status == xmlSecTransformStatusWorking) {
+
+ encrypt = (transform->operation == xmlSecTransformOperationEncrypt) ? 1 : 0;
+
+ if(ctx->ctxInitialized == 0) {
+ ret = xmlSecMSCngBlockCipherCtxInit(ctx,
+ in,
+ out,
+ encrypt,
+ last,
+ xmlSecTransformGetName(transform),
+ transformCtx);
+ if(ret < 0) {
+ xmlSecInternalError("xmlSecMSCngBlockCipherCtxInit",
+ xmlSecTransformGetName(transform));
+ return(-1);
+ }
+
+ }
+ if((ctx->ctxInitialized == 0) && (last != 0)) {
+ xmlSecInvalidDataError("not enough data to initialize transform",
+ xmlSecTransformGetName(transform));
+ return(-1);
+ }
+
+ if(ctx->ctxInitialized != 0) {
+ ret = xmlSecMSCngBlockCipherCtxUpdate(ctx, in, out,
+ encrypt,
+ last,
+ xmlSecTransformGetName(transform), transformCtx);
+ if(ret < 0) {
+ xmlSecInternalError("xmlSecMSCngBlockCipherCtxUpdate",
+ xmlSecTransformGetName(transform));
+ return(-1);
+ }
+ }
+
+ if(last) {
+ ret = xmlSecMSCngBlockCipherCtxFinal(ctx, in, out,
+ encrypt,
+ xmlSecTransformGetName(transform), transformCtx);
+
+ if(ret < 0) {
+ xmlSecInternalError("xmlSecMSCngBlockCipherCtxFinal",
+ xmlSecTransformGetName(transform));
+ return(-1);
+ }
+
+ transform->status = xmlSecTransformStatusFinished;
+ }
+ } else if(transform->status == xmlSecTransformStatusFinished) {
+ /* the only way we can get here is if there is no input */
+ xmlSecAssert2(xmlSecBufferGetSize(in) == 0, -1);
+ } else if(transform->status == xmlSecTransformStatusNone) {
+ /* the only way we can get here is if there is not enough data in the input */
+ xmlSecAssert2(last == 0, -1);
+ } else {
+ xmlSecInvalidTransfromStatusError(transform);
+ return(-1);
+ }
+
+ return(0);
+}
+
+#ifndef XMLSEC_NO_AES
+
+static xmlSecTransformKlass xmlSecMSCngAes128CbcKlass = {
+ /* klass/object sizes */
+ sizeof(xmlSecTransformKlass), /* xmlSecSize klassSize */
+ xmlSecMSCngBlockCipherSize, /* xmlSecSize objSize */
+
+ xmlSecNameAes128Cbc, /* const xmlChar* name; */
+ xmlSecHrefAes128Cbc, /* const xmlChar* href; */
+ xmlSecTransformUsageEncryptionMethod, /* xmlSecAlgorithmUsage usage; */
+
+ xmlSecMSCngBlockCipherInitialize, /* xmlSecTransformInitializeMethod initialize; */
+ xmlSecMSCngBlockCipherFinalize, /* xmlSecTransformFinalizeMethod finalize; */
+ NULL, /* xmlSecTransformNodeReadMethod readNode; */
+ NULL, /* xmlSecTransformNodeWriteMethod writeNode; */
+ xmlSecMSCngBlockCipherSetKeyReq, /* xmlSecTransformSetKeyMethod setKeyReq; */
+ xmlSecMSCngBlockCipherSetKey, /* xmlSecTransformSetKeyMethod setKey; */
+ NULL, /* xmlSecTransformValidateMethod validate; */
+ xmlSecTransformDefaultGetDataType, /* xmlSecTransformGetDataTypeMethod getDataType; */
+ xmlSecTransformDefaultPushBin, /* xmlSecTransformPushBinMethod pushBin; */
+ xmlSecTransformDefaultPopBin, /* xmlSecTransformPopBinMethod popBin; */
+ NULL, /* xmlSecTransformPushXmlMethod pushXml; */
+ NULL, /* xmlSecTransformPopXmlMethod popXml; */
+ xmlSecMSCngBlockCipherExecute, /* xmlSecTransformExecuteMethod execute; */
+
+ NULL, /* void* reserved0; */
+ NULL, /* void* reserved1; */
+};
+
+/**
+ * xmlSecMSCngTransformAes128CbcGetKlass:
+ *
+ * AES 128 CBC encryption transform klass.
+ *
+ * Returns: pointer to AES 128 CBC encryption transform.
+ */
+xmlSecTransformId
+xmlSecMSCngTransformAes128CbcGetKlass(void) {
+ return(&xmlSecMSCngAes128CbcKlass);
+}
+
+static xmlSecTransformKlass xmlSecMSCngAes192CbcKlass = {
+ /* klass/object sizes */
+ sizeof(xmlSecTransformKlass), /* xmlSecSize klassSize */
+ xmlSecMSCngBlockCipherSize, /* xmlSecSize objSize */
+
+ xmlSecNameAes192Cbc, /* const xmlChar* name; */
+ xmlSecHrefAes192Cbc, /* const xmlChar* href; */
+ xmlSecTransformUsageEncryptionMethod, /* xmlSecAlgorithmUsage usage; */
+
+ xmlSecMSCngBlockCipherInitialize, /* xmlSecTransformInitializeMethod initialize; */
+ xmlSecMSCngBlockCipherFinalize, /* xmlSecTransformFinalizeMethod finalize; */
+ NULL, /* xmlSecTransformNodeReadMethod readNode; */
+ NULL, /* xmlSecTransformNodeWriteMethod writeNode; */
+ xmlSecMSCngBlockCipherSetKeyReq, /* xmlSecTransformSetKeyMethod setKeyReq; */
+ xmlSecMSCngBlockCipherSetKey, /* xmlSecTransformSetKeyMethod setKey; */
+ NULL, /* xmlSecTransformValidateMethod validate; */
+ xmlSecTransformDefaultGetDataType, /* xmlSecTransformGetDataTypeMethod getDataType; */
+ xmlSecTransformDefaultPushBin, /* xmlSecTransformPushBinMethod pushBin; */
+ xmlSecTransformDefaultPopBin, /* xmlSecTransformPopBinMethod popBin; */
+ NULL, /* xmlSecTransformPushXmlMethod pushXml; */
+ NULL, /* xmlSecTransformPopXmlMethod popXml; */
+ xmlSecMSCngBlockCipherExecute, /* xmlSecTransformExecuteMethod execute; */
+
+ NULL, /* void* reserved0; */
+ NULL, /* void* reserved1; */
+};
+
+/**
+ * xmlSecMSCngTransformAes192CbcGetKlass:
+ *
+ * AES 192 CBC encryption transform klass.
+ *
+ * Returns: pointer to AES 192 CBC encryption transform.
+ */
+xmlSecTransformId
+xmlSecMSCngTransformAes192CbcGetKlass(void) {
+ return(&xmlSecMSCngAes192CbcKlass);
+}
+
+static xmlSecTransformKlass xmlSecMSCngAes256CbcKlass = {
+ /* klass/object sizes */
+ sizeof(xmlSecTransformKlass), /* xmlSecSize klassSize */
+ xmlSecMSCngBlockCipherSize, /* xmlSecSize objSize */
+
+ xmlSecNameAes256Cbc, /* const xmlChar* name; */
+ xmlSecHrefAes256Cbc, /* const xmlChar* href; */
+ xmlSecTransformUsageEncryptionMethod, /* xmlSecAlgorithmUsage usage; */
+
+ xmlSecMSCngBlockCipherInitialize, /* xmlSecTransformInitializeMethod initialize; */
+ xmlSecMSCngBlockCipherFinalize, /* xmlSecTransformFinalizeMethod finalize; */
+ NULL, /* xmlSecTransformNodeReadMethod readNode; */
+ NULL, /* xmlSecTransformNodeWriteMethod writeNode; */
+ xmlSecMSCngBlockCipherSetKeyReq, /* xmlSecTransformSetKeyMethod setKeyReq; */
+ xmlSecMSCngBlockCipherSetKey, /* xmlSecTransformSetKeyMethod setKey; */
+ NULL, /* xmlSecTransformValidateMethod validate; */
+ xmlSecTransformDefaultGetDataType, /* xmlSecTransformGetDataTypeMethod getDataType; */
+ xmlSecTransformDefaultPushBin, /* xmlSecTransformPushBinMethod pushBin; */
+ xmlSecTransformDefaultPopBin, /* xmlSecTransformPopBinMethod popBin; */
+ NULL, /* xmlSecTransformPushXmlMethod pushXml; */
+ NULL, /* xmlSecTransformPopXmlMethod popXml; */
+ xmlSecMSCngBlockCipherExecute, /* xmlSecTransformExecuteMethod execute; */
+
+ NULL, /* void* reserved0; */
+ NULL, /* void* reserved1; */
+};
+
+/**
+ * xmlSecMSCngTransformAes256CbcGetKlass:
+ *
+ * AES 256 CBC encryption transform klass.
+ *
+ * Returns: pointer to AES 256 CBC encryption transform.
+ */
+xmlSecTransformId
+xmlSecMSCngTransformAes256CbcGetKlass(void) {
+ return(&xmlSecMSCngAes256CbcKlass);
+}
+
+static xmlSecTransformKlass xmlSecMSCngAes128GcmKlass = {
+ /* klass/object sizes */
+ sizeof(xmlSecTransformKlass), /* xmlSecSize klassSize */
+ xmlSecMSCngBlockCipherSize, /* xmlSecSize objSize */
+
+ xmlSecNameAes128Gcm, /* const xmlChar* name; */
+ xmlSecHrefAes128Gcm, /* const xmlChar* href; */
+ xmlSecTransformUsageEncryptionMethod, /* xmlSecAlgorithmUsage usage; */
+
+ xmlSecMSCngBlockCipherInitialize, /* xmlSecTransformInitializeMethod initialize; */
+ xmlSecMSCngBlockCipherFinalize, /* xmlSecTransformFinalizeMethod finalize; */
+ NULL, /* xmlSecTransformNodeReadMethod readNode; */
+ NULL, /* xmlSecTransformNodeWriteMethod writeNode; */
+ xmlSecMSCngBlockCipherSetKeyReq, /* xmlSecTransformSetKeyMethod setKeyReq; */
+ xmlSecMSCngBlockCipherSetKey, /* xmlSecTransformSetKeyMethod setKey; */
+ NULL, /* xmlSecTransformValidateMethod validate; */
+ xmlSecTransformDefaultGetDataType, /* xmlSecTransformGetDataTypeMethod getDataType; */
+ xmlSecTransformDefaultPushBin, /* xmlSecTransformPushBinMethod pushBin; */
+ xmlSecTransformDefaultPopBin, /* xmlSecTransformPopBinMethod popBin; */
+ NULL, /* xmlSecTransformPushXmlMethod pushXml; */
+ NULL, /* xmlSecTransformPopXmlMethod popXml; */
+ xmlSecMSCngBlockCipherExecute, /* xmlSecTransformExecuteMethod execute; */
+
+ NULL, /* void* reserved0; */
+ NULL, /* void* reserved1; */
+};
+
+/**
+ * xmlSecMSCngTransformAes128GcmGetKlass:
+ *
+ * AES 128 GCM encryption transform klass.
+ *
+ * Returns: pointer to AES 128 GCM encryption transform.
+ */
+xmlSecTransformId
+xmlSecMSCngTransformAes128GcmGetKlass(void) {
+ return(&xmlSecMSCngAes128GcmKlass);
+}
+
+static xmlSecTransformKlass xmlSecMSCngAes192GcmKlass = {
+ /* klass/object sizes */
+ sizeof(xmlSecTransformKlass), /* xmlSecSize klassSize */
+ xmlSecMSCngBlockCipherSize, /* xmlSecSize objSize */
+
+ xmlSecNameAes192Gcm, /* const xmlChar* name; */
+ xmlSecHrefAes192Gcm, /* const xmlChar* href; */
+ xmlSecTransformUsageEncryptionMethod, /* xmlSecAlgorithmUsage usage; */
+
+ xmlSecMSCngBlockCipherInitialize, /* xmlSecTransformInitializeMethod initialize; */
+ xmlSecMSCngBlockCipherFinalize, /* xmlSecTransformFinalizeMethod finalize; */
+ NULL, /* xmlSecTransformNodeReadMethod readNode; */
+ NULL, /* xmlSecTransformNodeWriteMethod writeNode; */
+ xmlSecMSCngBlockCipherSetKeyReq, /* xmlSecTransformSetKeyMethod setKeyReq; */
+ xmlSecMSCngBlockCipherSetKey, /* xmlSecTransformSetKeyMethod setKey; */
+ NULL, /* xmlSecTransformValidateMethod validate; */
+ xmlSecTransformDefaultGetDataType, /* xmlSecTransformGetDataTypeMethod getDataType; */
+ xmlSecTransformDefaultPushBin, /* xmlSecTransformPushBinMethod pushBin; */
+ xmlSecTransformDefaultPopBin, /* xmlSecTransformPopBinMethod popBin; */
+ NULL, /* xmlSecTransformPushXmlMethod pushXml; */
+ NULL, /* xmlSecTransformPopXmlMethod popXml; */
+ xmlSecMSCngBlockCipherExecute, /* xmlSecTransformExecuteMethod execute; */
+
+ NULL, /* void* reserved0; */
+ NULL, /* void* reserved1; */
+};
+
+/**
+ * xmlSecMSCngTransformAes192GcmGetKlass:
+ *
+ * AES 192 GCM encryption transform klass.
+ *
+ * Returns: pointer to AES 192 GCM encryption transform.
+ */
+xmlSecTransformId
+xmlSecMSCngTransformAes192GcmGetKlass(void) {
+ return(&xmlSecMSCngAes192GcmKlass);
+}
+
+
+static xmlSecTransformKlass xmlSecMSCngAes256GcmKlass = {
+ /* klass/object sizes */
+ sizeof(xmlSecTransformKlass), /* xmlSecSize klassSize */
+ xmlSecMSCngBlockCipherSize, /* xmlSecSize objSize */
+
+ xmlSecNameAes256Gcm, /* const xmlChar* name; */
+ xmlSecHrefAes256Gcm, /* const xmlChar* href; */
+ xmlSecTransformUsageEncryptionMethod, /* xmlSecAlgorithmUsage usage; */
+
+ xmlSecMSCngBlockCipherInitialize, /* xmlSecTransformInitializeMethod initialize; */
+ xmlSecMSCngBlockCipherFinalize, /* xmlSecTransformFinalizeMethod finalize; */
+ NULL, /* xmlSecTransformNodeReadMethod readNode; */
+ NULL, /* xmlSecTransformNodeWriteMethod writeNode; */
+ xmlSecMSCngBlockCipherSetKeyReq, /* xmlSecTransformSetKeyMethod setKeyReq; */
+ xmlSecMSCngBlockCipherSetKey, /* xmlSecTransformSetKeyMethod setKey; */
+ NULL, /* xmlSecTransformValidateMethod validate; */
+ xmlSecTransformDefaultGetDataType, /* xmlSecTransformGetDataTypeMethod getDataType; */
+ xmlSecTransformDefaultPushBin, /* xmlSecTransformPushBinMethod pushBin; */
+ xmlSecTransformDefaultPopBin, /* xmlSecTransformPopBinMethod popBin; */
+ NULL, /* xmlSecTransformPushXmlMethod pushXml; */
+ NULL, /* xmlSecTransformPopXmlMethod popXml; */
+ xmlSecMSCngBlockCipherExecute, /* xmlSecTransformExecuteMethod execute; */
+
+ NULL, /* void* reserved0; */
+ NULL, /* void* reserved1; */
+};
+
+/**
+ * xmlSecMSCngTransformAes256GcmGetKlass:
+ *
+ * AES 256 GCM encryption transform klass.
+ *
+ * Returns: pointer to AES 256 GCM encryption transform.
+ */
+xmlSecTransformId
+xmlSecMSCngTransformAes256GcmGetKlass(void) {
+ return(&xmlSecMSCngAes256GcmKlass);
+}
+
+#endif /* XMLSEC_NO_AES */
+
+#ifndef XMLSEC_NO_DES
+
+static xmlSecTransformKlass xmlSecMSCngDes3CbcKlass = {
+ /* klass/object sizes */
+ sizeof(xmlSecTransformKlass), /* size_t klassSize */
+ xmlSecMSCngBlockCipherSize, /* size_t objSize */
+
+ xmlSecNameDes3Cbc, /* const xmlChar* name; */
+ xmlSecHrefDes3Cbc, /* const xmlChar* href; */
+ xmlSecTransformUsageEncryptionMethod,/* xmlSecAlgorithmUsage usage; */
+
+ xmlSecMSCngBlockCipherInitialize, /* xmlSecTransformInitializeMethod initialize; */
+ xmlSecMSCngBlockCipherFinalize, /* xmlSecTransformFinalizeMethod finalize; */
+ NULL, /* xmlSecTransformNodeReadMethod readNode; */
+ NULL, /* xmlSecTransformNodeWriteMethod writeNode; */
+ xmlSecMSCngBlockCipherSetKeyReq, /* xmlSecTransformSetKeyMethod setKeyReq; */
+ xmlSecMSCngBlockCipherSetKey, /* xmlSecTransformSetKeyMethod setKey; */
+ NULL, /* xmlSecTransformValidateMethod validate; */
+ xmlSecTransformDefaultGetDataType, /* xmlSecTransformGetDataTypeMethod getDataType; */
+ xmlSecTransformDefaultPushBin, /* xmlSecTransformPushBinMethod pushBin; */
+ xmlSecTransformDefaultPopBin, /* xmlSecTransformPopBinMethod popBin; */
+ NULL, /* xmlSecTransformPushXmlMethod pushXml; */
+ NULL, /* xmlSecTransformPopXmlMethod popXml; */
+ xmlSecMSCngBlockCipherExecute, /* xmlSecTransformExecuteMethod execute; */
+
+ NULL, /* void* reserved0; */
+ NULL, /* void* reserved1; */
+};
+
+/**
+ * xmlSecMSCngTransformDes3CbcGetKlass:
+ *
+ * Triple DES CBC encryption transform klass.
+ *
+ * Returns: pointer to Triple DES encryption transform.
+ */
+xmlSecTransformId
+xmlSecMSCngTransformDes3CbcGetKlass(void) {
+ return(&xmlSecMSCngDes3CbcKlass);
+}
+
+#endif /* XMLSEC_NO_DES */