|
|
//+-------------------------------------------------------------------------
//
// Microsoft Windows
//
// Copyright (C) Microsoft Corporation, 1997 - 1999
//
// File: msgstrm.cpp
//
// Contents: Cryptographic Message Streaming API support
//
// APIs:
//
// History: 20-Feb-97 kevinr created
//
//--------------------------------------------------------------------------
#include "global.hxx"
#define ICMS_NOCRYPT 0
#if (DBG && ICMS_NOCRYPT)
#define CryptEncrypt ICMS_PlainEncrypt
#define CryptDecrypt ICMS_PlainDecrypt
//+-------------------------------------------------------------------------
// Encrypt a buffer using a NOP algorithm, ie. ciphertext == plaintext
// Assumes that all but the last block are a multiple of the block
// size in length.
//--------------------------------------------------------------------------
BOOL WINAPI ICMS_PlainEncrypt( IN HCRYPTKEY hkeyCrypt, IN HCRYPTHASH hHash, IN BOOL fFinal, IN DWORD dwFlags, IN OUT PBYTE pbData, IN OUT PDWORD pcbData, IN DWORD cbBuf) { BOOL fRet; DWORD cbBlockLen; BOOL fBlockCipher; DWORD cbCipher; DWORD cbPlain = *pcbData; DWORD cbPad; DWORD i;
if (!fFinal) goto SuccessReturn;
if (!ICM_GetKeyBlockSize( hkeyCrypt, &cbBlockLen, &fBlockCipher)) goto GetKeyBlockSizeError; if (!fBlockCipher) goto SuccessReturn; // if stream, cipher == plain
cbCipher = cbPlain; cbCipher += cbBlockLen; cbCipher -= cbCipher % cbBlockLen; // make a multiple of block size
cbPad = cbCipher - cbPlain;
if (cbCipher > cbBuf) goto BufferTooSmallError;
// pad the "ciphertext"
FillMemory( pbData + cbPlain, cbPad, cbPad); *pcbData = cbCipher;
SuccessReturn: fRet = TRUE; CommonReturn: return fRet;
ErrorReturn: fRet = FALSE; goto CommonReturn; TRACE_ERROR(GetKeyBlockSizeError) // error already set
TRACE_ERROR(BufferTooSmallError) // error already set
}
//+-------------------------------------------------------------------------
// Decrypt a buffer using a NOP algorithm, ie. ciphertext == plaintext
// Assumes all input sizes are multiples of the block size.
//--------------------------------------------------------------------------
BOOL WINAPI ICMS_PlainDecrypt( IN HCRYPTKEY hkeyCrypt, IN HCRYPTHASH hHash, IN BOOL fFinal, IN DWORD dwFlags, IN OUT PBYTE pbData, IN OUT PDWORD pcbData) { BOOL fRet; PBYTE pb; DWORD cbBlockLen; BOOL fBlockCipher; DWORD cbCipher = *pcbData; DWORD cbPlain; DWORD cbPad;
if (!fFinal) goto SuccessReturn;
if (!ICM_GetKeyBlockSize( hkeyCrypt, &cbBlockLen, &fBlockCipher)) goto GetKeyBlockSizeError; if (!fBlockCipher) goto SuccessReturn; // if stream, cipher == plain
cbPad = (DWORD)(*(pbData + cbCipher - 1)); // check last byte
if (cbCipher < cbPad) goto CipherTextTooSmallError;
cbPlain = cbCipher - cbPad; *pcbData = cbPlain;
SuccessReturn: fRet = TRUE; CommonReturn: return fRet;
ErrorReturn: fRet = FALSE; goto CommonReturn; TRACE_ERROR(GetKeyBlockSizeError) // error already set
TRACE_ERROR(CipherTextTooSmallError) // error already set
} #endif // (DBG && ICMS_NOCRYPT)
//+-------------------------------------------------------------------------
// Do a CryptMsgGetParam to a buffer alloc'd by ICM_Alloc
//--------------------------------------------------------------------------
BOOL WINAPI ICMS_AllocGetParam( IN HCRYPTMSG hCryptMsg, IN DWORD dwParamType, IN DWORD dwIndex, OUT PBYTE *ppbData, OUT DWORD *pcbData) { BOOL fRet; DWORD cb; PBYTE pb = NULL; if (!CryptMsgGetParam( hCryptMsg, dwParamType, dwIndex, NULL, &cb)) goto GetEncodedSizeError; if (NULL == (pb = (PBYTE)ICM_Alloc(cb))) goto AllocEncodedError; if (!CryptMsgGetParam( hCryptMsg, dwParamType, dwIndex, pb, &cb)) goto GetEncodedError;
fRet = TRUE; CommonReturn: *ppbData = pb; *pcbData = cb; return fRet;
ErrorReturn: ICM_Free(pb); pb = NULL; cb = 0; fRet = FALSE; goto CommonReturn; TRACE_ERROR(GetEncodedSizeError) TRACE_ERROR(AllocEncodedError) TRACE_ERROR(GetEncodedError) }
//+-------------------------------------------------------------------------
// Peel off the identifier and length octets.
//--------------------------------------------------------------------------
BOOL WINAPI ICMS_ExtractContent( IN PCRYPT_MSG_INFO pcmi, IN const BYTE *pbDER, IN DWORD cbDER, OUT PDWORD pcbContent, OUT const BYTE **ppbContent) { BOOL fRet; LONG cbSkipped = 0; DWORD cbEntireContent;
if (!pcmi->fStreamContentExtracted) { if (0 > (cbSkipped = Asn1UtilExtractContent( pbDER, cbDER, &cbEntireContent, ppbContent))) goto ExtractContentError; pcmi->fStreamContentExtracted = TRUE; } else { *ppbContent = pbDER; }
*pcbContent = cbDER - cbSkipped;
fRet = TRUE; CommonReturn: return fRet;
ErrorReturn: fRet = FALSE; goto CommonReturn; TRACE_ERROR(ExtractContentError) // error already set
}
//+-------------------------------------------------------------------------
// Get the next token from the buffer.
// If the encoding is definite-length, set *pcbContent to be the size of the
// contents octets.
//
// Here, a "token" is either identifier/length octets, or the double-NULL
// terminating an indefinite-length encoding.
//--------------------------------------------------------------------------
BOOL WINAPI ICMS_GetToken( IN PICM_BUFFER pbuf, OUT PDWORD pdwToken, OUT OPTIONAL PDWORD pcbContent) { DWORD dwError = ERROR_SUCCESS; BOOL fRet; DWORD dwToken; LONG lth; DWORD cbContent = 0; const BYTE *pbContent; PBYTE pbData = pbuf->pbData + pbuf->cbDead; DWORD cbData = pbuf->cbUsed - pbuf->cbDead; DWORD cbConsumed = 0;
if (2 > cbData) { dwToken = ICMS_TOKEN_INCOMPLETE; } else if (0 == pbData[0] && 0 == pbData[1]) { dwToken = ICMS_TOKEN_NULLPAIR; cbConsumed = 2; } else { if (0 > (lth = Asn1UtilExtractContent( pbData, cbData, &cbContent, &pbContent))) { if (ASN1UTIL_INSUFFICIENT_DATA != lth) goto ExtractContentError; dwToken = ICMS_TOKEN_INCOMPLETE; } else { dwToken = (CMSG_INDEFINITE_LENGTH == cbContent) ? ICMS_TOKEN_INDEFINITE : ICMS_TOKEN_DEFINITE; cbConsumed = (DWORD)lth; } }
if (ICMS_TOKEN_INCOMPLETE != dwToken) pbuf->cbDead += cbConsumed;
fRet = TRUE; CommonReturn: *pdwToken = dwToken; if (pcbContent) *pcbContent = cbContent; ICM_SetLastError(dwError); return fRet;
ErrorReturn: dwError = GetLastError(); dwToken = 0; cbContent = 0; fRet = FALSE; goto CommonReturn; TRACE_ERROR(ExtractContentError) // error already set
}
//+-------------------------------------------------------------------------
// Process incremental content data, for a string.
//--------------------------------------------------------------------------
BOOL WINAPI ICMS_ProcessStringContent( IN PICM_BUFFER pbuf, IN OUT PDWORD paflStream, IN OUT PDWORD pcbPending, IN OUT PDWORD pcLevelIndefiniteInner, IN POSTRCALLBACK postrcbk, IN const void *pvArg) { BOOL fRet; DWORD dwToken; DWORD cbContent;
while (TRUE) { if (*pcbPending) { // *pcbPending bytes need to be processed, so we process
// as many as possible from the buffer.
if (!postrcbk( pvArg, pbuf, pcbPending, FALSE)) goto CallbackError; }
if (0 == *pcbPending) { // No bytes currently counted for processing. One of:
// 1. first time through
// 2. last time through
// 3. nested within an indefinite-length encoding
if (0 == *pcLevelIndefiniteInner) { // The first time through, and also when we have processed the
// entire octet string, we get here. The flag is clear
// the first time (so we set it after getting a token, which
// either sets *pcbPending or bumps *pcLevelIndefiniteInner),
// and set afterwards (so we mark done and bail).
if (*paflStream & ICMS_PROCESS_CONTENT_BEGUN) { // 2. last time through
if (!postrcbk( pvArg, pbuf, pcbPending, TRUE)) goto CallbackFinalError; *paflStream |= ICMS_PROCESS_CONTENT_DONE; goto SuccessReturn; // All done
} } // One of:
// 1. first time through
// 3. nested within an indefinite-length encoding
if (!ICMS_GetToken( pbuf, &dwToken, &cbContent)) goto GetTokenError; switch(dwToken) { case ICMS_TOKEN_INDEFINITE: ++*pcLevelIndefiniteInner; break; case ICMS_TOKEN_NULLPAIR: --*pcLevelIndefiniteInner; break; case ICMS_TOKEN_DEFINITE: *pcbPending = cbContent; break; case ICMS_TOKEN_INCOMPLETE: goto SuccessReturn; // need input
default: goto InvalidTokenError; }
*paflStream |= ICMS_PROCESS_CONTENT_BEGUN; } else { // More definite-length data remains to be copied out, but it
// is not yet in the buffer.
break; } }
SuccessReturn: fRet = TRUE; CommonReturn: return fRet;
ErrorReturn: fRet = FALSE; goto CommonReturn; TRACE_ERROR(InvalidTokenError) // error already set
TRACE_ERROR(GetTokenError) // error already set
TRACE_ERROR(CallbackError) // error already set
TRACE_ERROR(CallbackFinalError) // error already set
}
//+-------------------------------------------------------------------------
// Queue data to the buffer.
//--------------------------------------------------------------------------
BOOL WINAPI ICMS_QueueToBuffer( IN PICM_BUFFER pbuf, IN PBYTE pbData, IN DWORD cbData) { BOOL fRet; DWORD cbNewSize; DWORD cbNewUsed;
if (0 == cbData) goto SuccessReturn;
if (pbuf->pbData && pbuf->cbDead) { // Move the still-active bytes up to the front of the buffer.
// NB- Might overlap, so use MoveMemory.
MoveMemory( pbuf->pbData, pbuf->pbData + pbuf->cbDead, pbuf->cbUsed - pbuf->cbDead); pbuf->cbUsed -= pbuf->cbDead; pbuf->cbDead = 0; }
for (cbNewUsed=pbuf->cbUsed + cbData, cbNewSize=pbuf->cbSize; cbNewUsed > cbNewSize; cbNewSize += ICM_BUFFER_SIZE_INCR) ; if (cbNewSize > pbuf->cbSize) { if (NULL == (pbuf->pbData=(PBYTE)ICM_ReAlloc( pbuf->pbData, cbNewSize))) goto ReAllocBufferError; pbuf->cbSize = cbNewSize; }
CopyMemory( pbuf->pbData + pbuf->cbUsed, pbData, cbData); pbuf->cbUsed += cbData;
SuccessReturn: fRet = TRUE; CommonReturn: return fRet;
ErrorReturn: fRet = FALSE; goto CommonReturn; TRACE_ERROR(ReAllocBufferError) // error already set
}
//+-------------------------------------------------------------------------
// Copy out or queue some data eventually destined for the callback.
//--------------------------------------------------------------------------
BOOL WINAPI ICMS_Output( IN PCRYPT_MSG_INFO pcmi, IN PBYTE pbData, IN DWORD cbData, IN BOOL fFinal) { BOOL fRet; PCMSG_STREAM_INFO pcsi = pcmi->pStreamInfo; PFN_CMSG_STREAM_OUTPUT pfnStreamOutput = pcsi->pfnStreamOutput; void *pvArg = pcsi->pvArg; PICM_BUFFER pbuf = &pcmi->bufOutput;
if (pcmi->fStreamCallbackOutput) { if (pbuf->cbUsed) { // Copy out the queued data
if (!pfnStreamOutput( pvArg, pbuf->pbData, pbuf->cbUsed, FALSE)) goto OutputBufferError; pbuf->cbUsed = 0; } if (cbData || fFinal) { if (!pfnStreamOutput( pvArg, pbData, cbData, fFinal)) goto OutputError; } } else { if (!ICMS_QueueToBuffer( pbuf, pbData, cbData)) goto QueueOutputError; }
fRet = TRUE; CommonReturn: return fRet;
ErrorReturn: fRet = FALSE; goto CommonReturn; TRACE_ERROR(OutputBufferError) // error already set
TRACE_ERROR(QueueOutputError) // error already set
TRACE_ERROR(OutputError) // error already set
}
//+-------------------------------------------------------------------------
// Copy out the pair of NULLs following the contents octets of an indefinite-
// length encoding.
//--------------------------------------------------------------------------
BOOL WINAPI ICMS_OutputNullPairs( IN PCRYPT_MSG_INFO pcmi, IN DWORD cPairs, IN BOOL fFinal) { BOOL fRet; BYTE abNULL[8*2]; ZEROSTRUCT(abNULL);
if (cPairs > (sizeof(abNULL)/2)) goto CountOfNullPairsTooLargeError;
if (!ICMS_Output( pcmi, abNULL, cPairs * 2, fFinal)) goto OutputError;
fRet = TRUE; CommonReturn: return fRet;
ErrorReturn: fRet = FALSE; goto CommonReturn; TRACE_ERROR(CountOfNullPairsTooLargeError) // error already set
TRACE_ERROR(OutputError) // error already set
}
//+-------------------------------------------------------------------------
// Copy out the part of the encoding preceding the contents octets.
//--------------------------------------------------------------------------
BOOL WINAPI ICMS_OutputEncodedPrefix( IN PCRYPT_MSG_INFO pcmi, IN BYTE bTag, IN DWORD cbData) { BOOL fRet; DWORD dwError = ERROR_SUCCESS; BYTE abPrefix[6]; DWORD cbPrefix;
abPrefix[0] = bTag; if (CMSG_INDEFINITE_LENGTH == cbData) { abPrefix[1] = ICM_LENGTH_INDEFINITE; cbPrefix = 1; } else { cbPrefix = sizeof(abPrefix) - 1; ICM_GetLengthOctets( cbData, abPrefix + 1, &cbPrefix); }
if (!ICMS_Output( pcmi, abPrefix, cbPrefix + 1, FALSE)) goto OutputError;
fRet = TRUE; CommonReturn: ICM_SetLastError(dwError); return fRet;
ErrorReturn: dwError = GetLastError(); fRet = FALSE; goto CommonReturn; TRACE_ERROR(OutputError) // error already set
}
//+-------------------------------------------------------------------------
// Copy out the part of the ContentInfo encoding preceding
// the content's content.
//--------------------------------------------------------------------------
BOOL WINAPI ICMS_OutputEncodedPrefixContentInfo( IN PCRYPT_MSG_INFO pcmi, IN LPSTR pszContentType, IN DWORD cbData, IN DWORD dwFlags = 0) { BOOL fRet; DWORD dwError = ERROR_SUCCESS; ASN1error_e Asn1Err; ASN1encoding_t pEnc = ICM_GetEncoder(); PBYTE pbEncoded = NULL; DWORD cbEncoded; ObjectID ossObjID; BYTE abContentInfo[6]; DWORD cbContentInfo; BYTE abContent[6]; DWORD cbContent = 0; BYTE abContentOctetString[6]; DWORD cbContentOctetString = 0; DWORD cbSize = cbData;
if (dwFlags & CMSG_DETACHED_FLAG) { // NoContent
if (CMSG_INDEFINITE_LENGTH != cbData) cbSize = 0; } else { if (NULL == pszContentType #ifdef CMS_PKCS7
|| (dwFlags & CMSG_CMS_ENCAPSULATED_CONTENT_FLAG) #endif // CMS_PKCS7
) { // The content is not already encoded, so encode it as an octet string.
abContentOctetString[0] = ICM_TAG_OCTETSTRING; if (CMSG_INDEFINITE_LENGTH == cbData) { abContentOctetString[0] |= ICM_TAG_CONSTRUCTED; abContentOctetString[1] = ICM_LENGTH_INDEFINITE; cbContentOctetString = 1; } else { cbContentOctetString = sizeof(abContentOctetString) - 1; ICM_GetLengthOctets( cbData, abContentOctetString + 1, &cbContentOctetString); cbSize += 1 + cbContentOctetString; } }
// content, [0] EXPLICIT
abContent[0] = ICM_TAG_CONSTRUCTED | ICM_TAG_CONTEXT_0; if (CMSG_INDEFINITE_LENGTH == cbData) { abContent[1] = ICM_LENGTH_INDEFINITE; cbContent = 1; } else { cbContent = sizeof(abContent) - 1; ICM_GetLengthOctets( cbSize, abContent + 1, &cbContent); cbSize += 1 + cbContent; } }
// contentType
ossObjID.count = SIZE_OSS_OID; if (!PkiAsn1ToObjectIdentifier( pszContentType ? pszContentType : pszObjIdDataType, &ossObjID.count, ossObjID.value)) goto ConvToObjectIdentifierError; if (0 != (Asn1Err = PkiAsn1Encode( pEnc, &ossObjID, ObjectIdentifierType_PDU, &pbEncoded, &cbEncoded))) goto EncodeObjectIdentifierError; cbSize += cbEncoded;
abContentInfo[0] = ICM_TAG_SEQ; if (CMSG_INDEFINITE_LENGTH == cbData) { abContentInfo[1] = ICM_LENGTH_INDEFINITE; cbContentInfo = 1; } else { cbContentInfo = sizeof(abContentInfo) - 1; ICM_GetLengthOctets( cbSize, abContentInfo + 1, &cbContentInfo); }
if (!ICMS_Output( pcmi, abContentInfo, cbContentInfo + 1, FALSE)) goto OutputContentInfoError; if (!ICMS_Output( pcmi, pbEncoded, cbEncoded, FALSE)) goto OutputContentTypeError; if (0 == (dwFlags & CMSG_DETACHED_FLAG)) { if (!ICMS_Output( pcmi, abContent, cbContent + 1, FALSE)) goto OutputContentError; if (NULL == pszContentType #ifdef CMS_PKCS7
|| (dwFlags & CMSG_CMS_ENCAPSULATED_CONTENT_FLAG) #endif // CMS_PKCS7
) { if (!ICMS_Output( pcmi, abContentOctetString, cbContentOctetString + 1, FALSE)) goto OutputContentOctetStringError; } }
fRet = TRUE; CommonReturn: PkiAsn1FreeEncoded(pEnc, pbEncoded); ICM_SetLastError(dwError); return fRet;
ErrorReturn: dwError = GetLastError(); fRet = FALSE; goto CommonReturn; SET_ERROR_VAR(EncodeObjectIdentifierError, PkiAsn1ErrToHr(Asn1Err)) TRACE_ERROR(ConvToObjectIdentifierError) // error already set
TRACE_ERROR(OutputContentInfoError) // error already set
TRACE_ERROR(OutputContentTypeError) // error already set
TRACE_ERROR(OutputContentError) // error already set
TRACE_ERROR(OutputContentOctetStringError) // error already set
}
//+-------------------------------------------------------------------------
// Copy out the part of the EncryptedContentInfo encoding preceding
// the content's content.
//--------------------------------------------------------------------------
BOOL WINAPI ICMS_OutputEncodedPrefixEncryptedContentInfo( IN PCRYPT_MSG_INFO pcmi, IN LPSTR pszContentType, IN AlgorithmIdentifier *poaiContentEncryption, IN DWORD cbData) { BOOL fRet; DWORD dwError = ERROR_SUCCESS; ASN1error_e Asn1Err; ASN1encoding_t pEnc = ICM_GetEncoder(); PBYTE pbEncodedContentType = NULL; DWORD cbEncodedContentType; PBYTE pbEncodedContentEncryptionAlgorithm = NULL; DWORD cbEncodedContentEncryptionAlgorithm; ObjectID ossObjID; BYTE abEncryptedContentInfo[6]; DWORD cbEncryptedContentInfo; BYTE abEncryptedContent[6]; DWORD cbEncryptedContent; DWORD cbSize = 0; DWORD cbCipher = cbData; DWORD cbBlockSize = pcmi->cbBlockSize;
if (pcmi->fBlockCipher && 0 < cbCipher) { cbCipher += cbBlockSize; cbCipher -= cbCipher % cbBlockSize; }
// encryptedContent, [0] IMPLICIT
abEncryptedContent[0] = ICM_TAG_CONTEXT_0; if (CMSG_INDEFINITE_LENGTH == cbData) { abEncryptedContent[0] |= ICM_TAG_CONSTRUCTED; abEncryptedContent[1] = ICM_LENGTH_INDEFINITE; cbEncryptedContent = 1; } else { // NOTE: for nonData, either encapsulated or the cbData excludes
// the outer tag and length octets.
cbEncryptedContent = sizeof(abEncryptedContent) - 1; ICM_GetLengthOctets( cbCipher, abEncryptedContent + 1, &cbEncryptedContent); cbSize = 1 + cbEncryptedContent + cbCipher; }
// contentType
ossObjID.count = SIZE_OSS_OID; if (!PkiAsn1ToObjectIdentifier( pszContentType ? pszContentType : pszObjIdDataType, &ossObjID.count, ossObjID.value)) goto ConvToObjectIdentifierError; if (0 != (Asn1Err = PkiAsn1Encode( pEnc, &ossObjID, ObjectIdentifierType_PDU, &pbEncodedContentType, &cbEncodedContentType))) goto EncodeObjectIdentifierError; cbSize += cbEncodedContentType;
// contentEncryptionAlgorithm
if (0 != (Asn1Err = PkiAsn1Encode( pEnc, poaiContentEncryption, AlgorithmIdentifier_PDU, &pbEncodedContentEncryptionAlgorithm, &cbEncodedContentEncryptionAlgorithm))) goto EncodeContentEncryptionAlgorithmError; cbSize += cbEncodedContentEncryptionAlgorithm;
// EncryptedContentInfo
abEncryptedContentInfo[0] = ICM_TAG_SEQ; if (CMSG_INDEFINITE_LENGTH == cbData) { abEncryptedContentInfo[1] = ICM_LENGTH_INDEFINITE; cbEncryptedContentInfo = 1; } else { cbEncryptedContentInfo = sizeof(abEncryptedContentInfo) - 1; ICM_GetLengthOctets( cbSize, abEncryptedContentInfo + 1, &cbEncryptedContentInfo); }
// Queue the encoded header
if (!ICMS_Output( pcmi, abEncryptedContentInfo, cbEncryptedContentInfo + 1, FALSE)) goto OutputContentInfoError; if (!ICMS_Output( pcmi, pbEncodedContentType, cbEncodedContentType, FALSE)) goto OutputContentTypeError; if (!ICMS_Output( pcmi, pbEncodedContentEncryptionAlgorithm, cbEncodedContentEncryptionAlgorithm, FALSE)) goto OutputContentEncryptionAlgorithmError; if (!ICMS_Output( pcmi, abEncryptedContent, cbEncryptedContent + 1, FALSE)) goto OutputEncryptedContentError;
fRet = TRUE; CommonReturn: PkiAsn1FreeEncoded(pEnc, pbEncodedContentType); PkiAsn1FreeEncoded(pEnc, pbEncodedContentEncryptionAlgorithm); ICM_SetLastError(dwError); return fRet;
ErrorReturn: dwError = GetLastError(); fRet = FALSE; goto CommonReturn; SET_ERROR_VAR(EncodeObjectIdentifierError, PkiAsn1ErrToHr(Asn1Err)) SET_ERROR_VAR(EncodeContentEncryptionAlgorithmError, PkiAsn1ErrToHr(Asn1Err)) TRACE_ERROR(ConvToObjectIdentifierError) // error already set
TRACE_ERROR(OutputContentInfoError) // error already set
TRACE_ERROR(OutputContentTypeError) // error already set
TRACE_ERROR(OutputContentEncryptionAlgorithmError) // error already set
TRACE_ERROR(OutputEncryptedContentError) // error already set
}
//+-------------------------------------------------------------------------
// Copy out the encoding of an OSS type.
//--------------------------------------------------------------------------
BOOL WINAPI ICMS_OutputEncoded( IN PCRYPT_MSG_INFO pcmi, IN int iPDU, IN OPTIONAL BYTE bTag, IN PVOID pv, IN BOOL fFinal) { BOOL fRet; DWORD dwError = ERROR_SUCCESS; ASN1error_e Asn1Err; ASN1encoding_t pEnc = ICM_GetEncoder(); PBYTE pbEncoded = NULL; DWORD cbEncoded;
if (0 != (Asn1Err = PkiAsn1Encode( pEnc, pv, iPDU, &pbEncoded, &cbEncoded))) goto EncodeError;
if (bTag) pbEncoded[0] = bTag; // poke in the right tag
if (!ICMS_Output(pcmi, pbEncoded, cbEncoded, fFinal)) goto OutputError;
fRet = TRUE; CommonReturn: PkiAsn1FreeEncoded(pEnc, pbEncoded); ICM_SetLastError(dwError); return fRet;
ErrorReturn: dwError = GetLastError(); fRet = FALSE; goto CommonReturn; SET_ERROR_VAR(EncodeError, PkiAsn1ErrToHr(Asn1Err)) TRACE_ERROR(OutputError) // error already set
}
//+-------------------------------------------------------------------------
// Create the buffer for an enveloped message.
//--------------------------------------------------------------------------
BOOL WINAPI ICMS_CreateEnvelopedBuffer( IN PCRYPT_MSG_INFO pcmi) { DWORD dwError = ERROR_SUCCESS; BOOL fRet; PBYTE pbBuffer = NULL; DWORD cbBuffer; DWORD cbAlloc; DWORD cbBlockSize; BOOL fBlockCipher; PICM_BUFFER pbuf = &pcmi->bufCrypt;
if (!ICM_GetKeyBlockSize( pcmi->hkeyContentCrypt, &cbBlockSize, &fBlockCipher)) goto GetEncryptBlockSizeError;
pcmi->cbBlockSize = cbBlockSize; pcmi->fBlockCipher = fBlockCipher;
cbBuffer = min( cbBlockSize * CMSGP_STREAM_CRYPT_BLOCK_COUNT, CMSGP_STREAM_MAX_ENCRYPT_BUFFER); if (fBlockCipher) { cbBuffer += cbBlockSize; cbBuffer -= cbBuffer % cbBlockSize; // make a multiple of block size
}
// Add one block for growth during encrypt, and to save during decrypt.
cbAlloc = cbBuffer + 1 * cbBlockSize; // Block ciphers pad the ciphertext, and if the plaintext is a
// multiple of the block size the padding is one block.
if (NULL == (pbBuffer = (PBYTE)ICM_Alloc( cbAlloc))) goto AllocBufferError; pbuf->pbData = pbBuffer; pbuf->cbSize = cbBuffer;
fRet = TRUE; CommonReturn: ICM_SetLastError(dwError); return fRet;
ErrorReturn: dwError = GetLastError(); ICM_Free( pbBuffer); fRet = FALSE; goto CommonReturn; TRACE_ERROR(GetEncryptBlockSizeError) // error already set
TRACE_ERROR(AllocBufferError) // error already set
}
//+-------------------------------------------------------------------------
// Encode and copy out the part of the data message up to the inner content.
//--------------------------------------------------------------------------
BOOL WINAPI ICMS_OpenToEncodeData( IN PCRYPT_MSG_INFO pcmi) { BOOL fRet; DWORD dwError = ERROR_SUCCESS; DWORD cbData = pcmi->pStreamInfo->cbContent;
if (pcmi->dwFlags & CMSG_BARE_CONTENT_FLAG) { BYTE bTag;
if (CMSG_INDEFINITE_LENGTH == cbData) bTag = ICM_TAG_OCTETSTRING | ICM_TAG_CONSTRUCTED; else bTag = ICM_TAG_OCTETSTRING;
// Output octet string
if (!ICMS_OutputEncodedPrefix( pcmi, bTag, cbData)) goto OutputOctetStringError; } else { // Output ContentInfo
if (!ICMS_OutputEncodedPrefixContentInfo( pcmi, NULL, cbData)) goto OutputContentInfoError; }
fRet = TRUE; CommonReturn: ICM_SetLastError(dwError); return fRet;
ErrorReturn: dwError = GetLastError(); fRet = FALSE; goto CommonReturn; TRACE_ERROR(OutputContentInfoError) // error already set
TRACE_ERROR(OutputOctetStringError) // error already set
}
//+-------------------------------------------------------------------------
// Encode and copy out the part of the data message after the inner content.
//--------------------------------------------------------------------------
BOOL WINAPI ICMS_UpdateEncodingData( IN PCRYPT_MSG_INFO pcmi, IN PBYTE pbData, IN DWORD cbData, IN BOOL fFinal) { BOOL fRet; DWORD dwError = ERROR_SUCCESS; BOOL fDefinite = (CMSG_INDEFINITE_LENGTH != pcmi->pStreamInfo->cbContent); DWORD cNullPairs;
pcmi->fStreamCallbackOutput = TRUE; // Enable the callback
if (!fDefinite) { // The content is an indefinite-length octet string encoded by us,
// so make each output chunk definite-length.
if (!ICMS_OutputEncodedPrefix( pcmi, ICM_TAG_OCTETSTRING, cbData)) goto OutputOctetStringError; } if (!ICMS_Output( pcmi, pbData, cbData, fFinal && fDefinite)) goto OutputError; if (fFinal && !fDefinite) { // End of indefinite-length encoding, so emit some NULL pairs
cNullPairs = 1; // content
if (0 == (pcmi->dwFlags & CMSG_BARE_CONTENT_FLAG)) cNullPairs += 2; if (!ICMS_OutputNullPairs( pcmi, cNullPairs, TRUE)) goto OutputNullPairsError; }
fRet = TRUE; CommonReturn: ICM_SetLastError(dwError); return fRet;
ErrorReturn: dwError = GetLastError(); fRet = FALSE; goto CommonReturn; TRACE_ERROR(OutputOctetStringError) // error already set
TRACE_ERROR(OutputError) // error already set
TRACE_ERROR(OutputNullPairsError) // error already set
}
//+-------------------------------------------------------------------------
// Encode and copy out the part of the signed message up to the inner content.
//--------------------------------------------------------------------------
BOOL WINAPI ICMS_OpenToEncodeSignedData( IN PCRYPT_MSG_INFO pcmi, IN PCMSG_SIGNED_ENCODE_INFO psmei) { BOOL fRet; DWORD dwError = ERROR_SUCCESS; SignedData *psd = (SignedData *)pcmi->pvMsg; DWORD cbData = pcmi->pStreamInfo->cbContent; LPSTR pszInnerContentObjID = pcmi->pszInnerContentObjID; DWORD cbSigned; DWORD cbSignedDataContent;
// Output ContentInfo, if appropriate
if (CMSG_INDEFINITE_LENGTH == cbData) { cbSigned = CMSG_INDEFINITE_LENGTH; cbSignedDataContent = CMSG_INDEFINITE_LENGTH; } else { if (INVALID_ENCODING_SIZE == (cbSigned = ICM_LengthSigned( psmei, pcmi->dwFlags, pszInnerContentObjID, cbData, &cbSignedDataContent))) goto LengthSignedError; } if (0 == (pcmi->dwFlags & CMSG_BARE_CONTENT_FLAG)) { if (!ICMS_OutputEncodedPrefixContentInfo( pcmi, szOID_RSA_signedData, cbSigned)) goto OutputContentInfoError; } if (!ICMS_OutputEncodedPrefix( pcmi, ICM_TAG_SEQ, cbSignedDataContent)) goto OutputSignedDataError;
// version
if (!ICMS_OutputEncoded( pcmi, IntegerType_PDU, 0, // bTag
&psd->version, FALSE)) goto OutputIntegerError;
// digestAlgorithms
if (!ICMS_OutputEncoded( pcmi, AlgorithmIdentifiers_PDU, 0, // bTag
&psd->digestAlgorithms, FALSE)) goto OutputAlgorithmIdentifiersError;
// contentInfo
if (!ICMS_OutputEncodedPrefixContentInfo( pcmi, pcmi->pszInnerContentObjID, cbData, pcmi->dwFlags)) goto OutputInnerContentInfoError;
fRet = TRUE; CommonReturn: ICM_SetLastError(dwError); return fRet;
ErrorReturn: dwError = GetLastError(); fRet = FALSE; goto CommonReturn; TRACE_ERROR(LengthSignedError) // error already set
TRACE_ERROR(OutputContentInfoError) // error already set
TRACE_ERROR(OutputSignedDataError) // error already set
TRACE_ERROR(OutputIntegerError) // error already set
TRACE_ERROR(OutputAlgorithmIdentifiersError) // error already set
TRACE_ERROR(OutputInnerContentInfoError) // error already set
}
//+-------------------------------------------------------------------------
// Encode and copy out the part of the signed message after the inner content.
//--------------------------------------------------------------------------
BOOL WINAPI ICMS_UpdateEncodingSignedData( IN PCRYPT_MSG_INFO pcmi, IN PBYTE pbData, IN DWORD cbData, IN BOOL fFinal) { BOOL fRet; DWORD dwError = ERROR_SUCCESS; SignedData *psd = (SignedData *)pcmi->pvMsg; PCMSG_STREAM_INFO pcsi = pcmi->pStreamInfo; BOOL fDefinite = (CMSG_INDEFINITE_LENGTH != pcsi->cbContent); DWORD cNullPairs;
if (pcmi->pszInnerContentObjID #ifdef CMS_PKCS7
&& 0 == (pcmi->dwFlags & CMSG_CMS_ENCAPSULATED_CONTENT_FLAG) #endif // CMS_PKCS7
) { if (0 == (pcmi->aflStream & ICMS_PROCESS_CONTENT_DONE)) { if (!ICMS_HashContent( pcmi, pbData, cbData)) goto HashContentError; } } else { if (!ICM_UpdateListDigest( pcmi->pHashList, pbData, cbData)) goto UpdateDigestError; }
pcmi->fStreamCallbackOutput = TRUE; // Enable the callback
if (0 == (pcmi->dwFlags & CMSG_DETACHED_FLAG)) { if (!fDefinite && (NULL == pcmi->pszInnerContentObjID #ifdef CMS_PKCS7
|| (pcmi->dwFlags & CMSG_CMS_ENCAPSULATED_CONTENT_FLAG) #endif // CMS_PKCS7
)) { // The content is an indefinite-length octet string encoded by us,
// so make each output chunk definite-length.
if (!ICMS_OutputEncodedPrefix( pcmi, ICM_TAG_OCTETSTRING, cbData)) goto OutputOctetStringError; } if (!ICMS_Output( pcmi, pbData, cbData, FALSE)) goto OutputError; } // else
// detached => don't output the detached content to be hashed
if (fFinal) { if (!fDefinite) { // End of indefinite-length encoding, so emit some NULL pairs
cNullPairs = 1; // ContentInfo
if (0 == (pcmi->dwFlags & CMSG_DETACHED_FLAG)) { cNullPairs++; // [0] EXPLICIT
if (NULL == pcmi->pszInnerContentObjID #ifdef CMS_PKCS7
|| (pcmi->dwFlags & CMSG_CMS_ENCAPSULATED_CONTENT_FLAG) #endif // CMS_PKCS7
) cNullPairs++; // We did the octet string encoding
} // else
// detached => no content ([0] EXPLICIT)
if (!ICMS_OutputNullPairs( pcmi, cNullPairs, FALSE)) goto OutputNullPairsError; }
if ((psd->bit_mask & certificates_present) && !ICMS_OutputEncoded( pcmi, SetOfAny_PDU, ICM_TAG_CONSTRUCTED | ICM_TAG_CONTEXT_0, &psd->certificates, FALSE)) goto OutputCertsError;
if ((psd->bit_mask & crls_present) && !ICMS_OutputEncoded( pcmi, SetOfAny_PDU, ICM_TAG_CONSTRUCTED | ICM_TAG_CONTEXT_1, &psd->crls, FALSE)) goto OutputCrlsError;
#ifdef CMS_PKCS7
if (pcmi->rgSignerEncodeDataInfo) { if (!ICM_FillSignerEncodeEncryptedDigests( pcmi, fDefinite)) // fMaxLength
goto FillSignerEncodeEncryptedDigestsError; } #else
if (pcmi->pHashList) { if (!ICM_FillSignerEncryptedDigest( psd->signerInfos.value, pcmi->pszInnerContentObjID, pcmi->pHashList->Head(), pcmi->dwKeySpec, fDefinite)) // fMaxLength
goto FillSignerEncryptedDigestError; } #endif // CMS_PKCS7
if (!ICMS_OutputEncoded( pcmi, SignerInfos_PDU, 0, // bTag
&psd->signerInfos, fDefinite)) goto OutputSignerInfosError;
if (!fDefinite) { // End of indefinite-length encoding, so emit some NULL pairs
cNullPairs = 1; // SignedData
if (0 == (pcmi->dwFlags & CMSG_BARE_CONTENT_FLAG)) cNullPairs += 2; if (!ICMS_OutputNullPairs( pcmi, cNullPairs, TRUE)) goto OutputNullPairsError; } }
fRet = TRUE; CommonReturn: ICM_SetLastError(dwError); return fRet;
ErrorReturn: dwError = GetLastError(); fRet = FALSE; goto CommonReturn; TRACE_ERROR(HashContentError) // error already set
TRACE_ERROR(UpdateDigestError) // error already set
TRACE_ERROR(OutputOctetStringError) // error already set
TRACE_ERROR(OutputError) // error already set
TRACE_ERROR(OutputCertsError) // error already set
TRACE_ERROR(OutputCrlsError) // error already set
#ifdef CMS_PKCS7
TRACE_ERROR(FillSignerEncodeEncryptedDigestsError) // error already set
#else
TRACE_ERROR(FillSignerEncryptedDigestError) // error already set
#endif // CMS_PKCS7
TRACE_ERROR(OutputSignerInfosError) // error already set
TRACE_ERROR(OutputNullPairsError) // error already set
}
//+-------------------------------------------------------------------------
// Encode and copy out the part of the enveloped message up to the inner
// content.
//--------------------------------------------------------------------------
BOOL WINAPI ICMS_OpenToEncodeEnvelopedData( IN PCRYPT_MSG_INFO pcmi, IN PCMSG_ENVELOPED_ENCODE_INFO pemei) { DWORD dwError = ERROR_SUCCESS; BOOL fRet; #ifdef CMS_PKCS7
CmsEnvelopedData *ped = (CmsEnvelopedData *)pcmi->pvMsg; #else
EnvelopedData *ped = (EnvelopedData *)pcmi->pvMsg; #endif // CMS_PKCS7
DWORD cbData = pcmi->pStreamInfo->cbContent; LPSTR pszInnerContentObjID = pcmi->pszInnerContentObjID; DWORD cbEnveloped; DWORD cbEnvelopedDataContent;
if (!ICMS_CreateEnvelopedBuffer( pcmi)) goto CreateEnvelopedBufferError;
// Output ContentInfo, if appropriate
if (CMSG_INDEFINITE_LENGTH == cbData) { cbEnveloped = CMSG_INDEFINITE_LENGTH; cbEnvelopedDataContent = CMSG_INDEFINITE_LENGTH; } else { // NOTE: for nonData, either encapsulated or cbData excludes the
// outer tag and length octets.
if (INVALID_ENCODING_SIZE == (cbEnveloped = ICM_LengthEnveloped( pemei, pcmi->dwFlags, pszInnerContentObjID, cbData, &cbEnvelopedDataContent))) goto LengthEnvelopedError; } if (0 == (pcmi->dwFlags & CMSG_BARE_CONTENT_FLAG)) { if (!ICMS_OutputEncodedPrefixContentInfo( pcmi, szOID_RSA_envelopedData, cbEnveloped)) goto OutputContentInfoError; } if (!ICMS_OutputEncodedPrefix( pcmi, ICM_TAG_SEQ, cbEnvelopedDataContent)) goto OutputEnvelopedDataError;
// version
if (!ICMS_OutputEncoded( pcmi, IntegerType_PDU, 0, // bTag
&ped->version, FALSE)) goto OutputIntegerError;
#ifdef CMS_PKCS7
// originatorInfo OPTIONAL
if (ped->bit_mask & originatorInfo_present) { if (!ICMS_OutputEncoded( pcmi, OriginatorInfo_PDU, ICM_TAG_CONSTRUCTED | ICM_TAG_CONTEXT_0, &ped->originatorInfo, FALSE)) goto OutputOriginatorInfoError; } #endif // CMS_PKCS7
// recipientInfos
if (!ICMS_OutputEncoded( pcmi, #ifdef CMS_PKCS7
CmsRecipientInfos_PDU, #else
RecipientInfos_PDU, #endif // CMS_PKCS7
0, // bTag
&ped->recipientInfos, FALSE)) goto OutputRecipientInfosError;
// encryptedContentInfo
if (!ICMS_OutputEncodedPrefixEncryptedContentInfo( pcmi, pcmi->pszInnerContentObjID, &ped->encryptedContentInfo.contentEncryptionAlgorithm, cbData)) goto OutputInnerContentInfoError;
fRet = TRUE; CommonReturn: ICM_SetLastError(dwError); return fRet;
ErrorReturn: dwError = GetLastError(); fRet = FALSE; goto CommonReturn; TRACE_ERROR(CreateEnvelopedBufferError) // error already set
TRACE_ERROR(LengthEnvelopedError) // error already set
TRACE_ERROR(OutputContentInfoError) // error already set
TRACE_ERROR(OutputEnvelopedDataError) // error already set
TRACE_ERROR(OutputIntegerError) // error already set
#ifdef CMS_PKCS7
TRACE_ERROR(OutputOriginatorInfoError) // error already set
#endif // CMS_PKCS7
TRACE_ERROR(OutputRecipientInfosError) // error already set
TRACE_ERROR(OutputInnerContentInfoError) // error already set
}
//+-------------------------------------------------------------------------
// Encrypt and copy out some bytes.
//--------------------------------------------------------------------------
BOOL WINAPI ICMS_EncodeEncryptAndOutput( IN PCRYPT_MSG_INFO pcmi, IN const BYTE *pbPlainOrg, IN DWORD cbPlainOrg, IN BOOL fFinal) { DWORD dwError = ERROR_SUCCESS; BOOL fRet; BOOL fDefinite = (CMSG_INDEFINITE_LENGTH != pcmi->pStreamInfo->cbContent); BOOL fBlockCipher = pcmi->fBlockCipher; PICM_BUFFER pbufCrypt = &pcmi->bufCrypt; PBYTE pbPlain; DWORD cbPlainRemain; DWORD cb;
for (cbPlainRemain = cbPlainOrg, pbPlain = (PBYTE)pbPlainOrg; cbPlainRemain > 0;) { cb = min( cbPlainRemain, pbufCrypt->cbSize - pbufCrypt->cbUsed); // must fit
CopyMemory( pbufCrypt->pbData + pbufCrypt->cbUsed, pbPlain, cb); pbufCrypt->cbUsed += cb; pbPlain += cb; cbPlainRemain -= cb; if (pbufCrypt->cbSize == pbufCrypt->cbUsed) { // Encrypt and copy out the buffer
cb = pbufCrypt->cbSize; if (fBlockCipher) { // Leave the last block
cb -= pcmi->cbBlockSize; } if (!CryptEncrypt( pcmi->hkeyContentCrypt, NULL, // hHash
FALSE, // fFinal
0, // dwFlags
pbufCrypt->pbData, &cb, pbufCrypt->cbSize + pcmi->cbBlockSize)) goto EncryptError; if (!fDefinite) { // The ciphertext is indefinite-length, so make each
// output chunk definite-length.
if (!ICMS_OutputEncodedPrefix( pcmi, ICM_TAG_OCTETSTRING, cb)) goto OutputOctetStringError; } if (!ICMS_Output( pcmi, pbufCrypt->pbData, cb, FALSE)) // fFinal
goto OutputError;
if (fBlockCipher) { // Move the last block to the beginning of the buffer
// and reset the count to start after this block.
// Since we are sure the src and dst do not overlap,
// use CopyMemory (faster than MoveMemory).
cb = pbufCrypt->cbSize - pcmi->cbBlockSize; CopyMemory( pbufCrypt->pbData, pbufCrypt->pbData + cb, pcmi->cbBlockSize); pbufCrypt->cbUsed = pcmi->cbBlockSize; } else { pbufCrypt->cbUsed = 0; } } }
if (fFinal) { #ifdef CMS_PKCS7
CmsEnvelopedData *ped = (CmsEnvelopedData *)pcmi->pvMsg; #else
EnvelopedData *ped = (EnvelopedData *)pcmi->pvMsg; #endif // CMS_PKCS7
if (cb = pbufCrypt->cbUsed) { if (!CryptEncrypt( pcmi->hkeyContentCrypt, NULL, // hHash
TRUE, // fFinal
0, // dwFlags
pbufCrypt->pbData, &cb, pbufCrypt->cbSize + pcmi->cbBlockSize)) goto FinalEncryptError; } if (!fDefinite && cb) { // The ciphertext is indefinite-length, so make each
// output chunk definite-length.
if (!ICMS_OutputEncodedPrefix( pcmi, ICM_TAG_OCTETSTRING, cb)) goto OutputOctetStringError; } if (!ICMS_Output( pcmi, pbufCrypt->pbData, cb, fDefinite && 0 == (ped->bit_mask & unprotectedAttrs_present) // fFinal
)) goto FinalOutputError; }
fRet = TRUE; CommonReturn: ICM_SetLastError(dwError); return fRet;
ErrorReturn: dwError = GetLastError(); fRet = FALSE; goto CommonReturn; TRACE_ERROR(EncryptError) // error already set
TRACE_ERROR(FinalEncryptError) // error already set
TRACE_ERROR(OutputOctetStringError) // error already set
TRACE_ERROR(OutputError) // error already set
TRACE_ERROR(FinalOutputError) // error already set
}
//+-------------------------------------------------------------------------
// Encode encrypt callback for octet string.
//--------------------------------------------------------------------------
BOOL WINAPI ICMS_EncryptCallback( IN const void *pvArg, IN OUT PICM_BUFFER pbuf, IN OUT PDWORD pcbPending, IN BOOL fFinal) { DWORD dwError = ERROR_SUCCESS; BOOL fRet; PBYTE pbData = pbuf->pbData + pbuf->cbDead; DWORD cbData = min( *pcbPending, pbuf->cbUsed - pbuf->cbDead);
if (!ICMS_EncodeEncryptAndOutput( (PCRYPT_MSG_INFO)pvArg, pbData, cbData, fFinal)) goto EncodeEncryptAndOutputError;
pbuf->cbDead += cbData; *pcbPending -= cbData;
fRet = TRUE; CommonReturn: ICM_SetLastError(dwError); return fRet;
ErrorReturn: dwError = GetLastError(); fRet = FALSE; goto CommonReturn; TRACE_ERROR(EncodeEncryptAndOutputError) // error already set
}
//+-------------------------------------------------------------------------
// Encode and copy out the part of the enveloped message after the inner content.
//--------------------------------------------------------------------------
BOOL WINAPI ICMS_UpdateEncodingEnvelopedData( IN PCRYPT_MSG_INFO pcmi, IN const BYTE *pbPlain, IN DWORD cbPlain, IN BOOL fFinal) { DWORD dwError = ERROR_SUCCESS; BOOL fRet; BOOL fDefinite = (CMSG_INDEFINITE_LENGTH != pcmi->pStreamInfo->cbContent); DWORD cNullPairs;
if (!pcmi->fStreamCallbackOutput) { pcmi->fStreamCallbackOutput = TRUE; // Enable the callback
if (!ICMS_Output( pcmi, NULL, 0, FALSE)) // Flush the header
goto FlushOutputError; }
if (pcmi->pszInnerContentObjID #ifdef CMS_PKCS7
&& 0 == (pcmi->dwFlags & CMSG_CMS_ENCAPSULATED_CONTENT_FLAG) #endif // CMS_PKCS7
) { if (!ICMS_QueueToBuffer( &pcmi->bufEncode, (PBYTE)pbPlain, cbPlain)) goto QueueToBufferError;
if (!ICMS_ProcessStringContent( &pcmi->bufEncode, &pcmi->aflStream, &pcmi->cbDefiniteRemain, &pcmi->cLevelIndefiniteInner, ICMS_EncryptCallback, pcmi)) goto ProcessContentError; } else { if (!ICMS_EncodeEncryptAndOutput( pcmi, pbPlain, cbPlain, fFinal)) goto EncodeEncryptAndOutputError; }
if (fFinal) { #ifdef CMS_PKCS7
CmsEnvelopedData *ped = (CmsEnvelopedData *)pcmi->pvMsg; #else
EnvelopedData *ped = (EnvelopedData *)pcmi->pvMsg; #endif // CMS_PKCS7
if (!fDefinite) { // End of indefinite-length encoding, so emit some NULL pairs,
// one each for encryptedContent, encryptedContentInfo
if (!ICMS_OutputNullPairs( pcmi, 2, FALSE)) goto OutputNullPairsError; }
if (ped->bit_mask & unprotectedAttrs_present) { #ifdef CMS_PKCS7
if (!ICMS_OutputEncoded( pcmi, Attributes_PDU, ICM_TAG_CONSTRUCTED | ICM_TAG_CONTEXT_1, &ped->unprotectedAttrs, fDefinite)) // fFinal
goto OutputAttributesError; #endif // CMS_PKCS7
}
if (!fDefinite) { // End of indefinite-length encoding, so emit some NULL pairs
cNullPairs = 1; // EnvelopedData
if (0 == (pcmi->dwFlags & CMSG_BARE_CONTENT_FLAG)) cNullPairs += 2; if (!ICMS_OutputNullPairs( pcmi, cNullPairs, TRUE)) goto OutputNullPairsError; } }
fRet = TRUE; CommonReturn: ICM_SetLastError(dwError); return fRet;
ErrorReturn: dwError = GetLastError(); fRet = FALSE; goto CommonReturn; TRACE_ERROR(FlushOutputError) // error already set
TRACE_ERROR(QueueToBufferError) // error already set
TRACE_ERROR(ProcessContentError) // error already set
TRACE_ERROR(EncodeEncryptAndOutputError) // error already set
TRACE_ERROR(OutputNullPairsError) // error already set
#ifdef CMS_PKCS7
TRACE_ERROR(OutputAttributesError) // error already set
#endif // CMS_PKCS7
}
//+-------------------------------------------------------------------------
// Decode a PDU from the decode buffer.
//--------------------------------------------------------------------------
BOOL WINAPI ICMS_DecodePDU( IN PCRYPT_MSG_INFO pcmi, IN ASN1decoding_t pDec, IN ASN1uint32_t pdunum, OUT PVOID *ppvPDU, OUT OPTIONAL PDWORD pcbConsumed = NULL) { DWORD dwError = ERROR_SUCCESS; BOOL fRet; ASN1error_e Asn1Err; PICM_BUFFER pbuf = &pcmi->bufDecode; PVOID pvPDU = NULL; DWORD cbBufSizeOrg; PBYTE pbData = pbuf->pbData + pbuf->cbDead; DWORD cbData = pbuf->cbUsed - pbuf->cbDead;
#if DBG && defined(OSS_CRYPT_ASN1)
DWORD dwDecodingFlags;
dwDecodingFlags = ossGetDecodingFlags((OssGlobal *) pDec); ossSetDecodingFlags( (OssGlobal *) pDec, RELAXBER); // turn off squirties
#endif
cbBufSizeOrg = cbData; if (0 != (Asn1Err = PkiAsn1Decode2( pDec, &pvPDU, pdunum, &pbData, &cbData))) { if (ASN1_ERR_EOD != Asn1Err) goto DecodeError; } #if DBG && defined(OSS_CRYPT_ASN1)
ossSetDecodingFlags( (OssGlobal *) pDec, dwDecodingFlags); // restore
#endif
if (ASN1_ERR_EOD == Asn1Err || (cbData > pbuf->cbUsed - pbuf->cbDead)) { PkiAsn1FreeInfo(pDec, pdunum, pvPDU); pvPDU = NULL; cbData = cbBufSizeOrg; } pbuf->cbDead += cbBufSizeOrg - cbData; if (pcbConsumed) *pcbConsumed = cbBufSizeOrg - cbData;
fRet = TRUE; CommonReturn: *ppvPDU = pvPDU; ICM_SetLastError(dwError); return fRet;
ErrorReturn: dwError = GetLastError(); if (pcbConsumed) *pcbConsumed = 0; pvPDU = NULL; fRet = FALSE; goto CommonReturn; SET_ERROR_VAR(DecodeError, PkiAsn1ErrToHr(Asn1Err)) }
//+-------------------------------------------------------------------------
// Decode a ContentInfo prefix
//--------------------------------------------------------------------------
BOOL WINAPI ICMS_DecodePrefixContentInfo( IN PCRYPT_MSG_INFO pcmi, OUT ObjectIdentifierType **ppooidContentType, IN OUT PDWORD pcTrailingNullPairs, IN OUT PDWORD pafl, OUT BOOL *pfNoContent) { DWORD dwError = ERROR_SUCCESS; BOOL fRet; ASN1decoding_t pDec = ICM_GetDecoder(); DWORD dwToken;
// ContentInfo sequence, step into it
if (0 == (*pafl & ICMS_DECODED_CONTENTINFO_SEQ)) { if (!ICMS_GetToken( &pcmi->bufDecode, &dwToken, &pcmi->cbContentInfo)) goto ContentInfoGetTokenError; switch (dwToken) { case ICMS_TOKEN_INDEFINITE: ++*pcTrailingNullPairs; break; case ICMS_TOKEN_DEFINITE: break; case ICMS_TOKEN_INCOMPLETE: goto SuccessReturn; default: goto InvalidTokenError; } *pafl |= ICMS_DECODED_CONTENTINFO_SEQ; }
// contentType, decode it
if (NULL == *ppooidContentType) { DWORD cbConsumed;
if (!ICMS_DecodePDU( pcmi, pDec, ObjectIdentifierType_PDU, (void **)ppooidContentType, &cbConsumed)) goto DecodeContentTypeError;
if (NULL != *ppooidContentType && CMSG_INDEFINITE_LENGTH != pcmi->cbContentInfo && cbConsumed == pcmi->cbContentInfo) { // Only has contentType. The optional content has
// been omitted.
*pfNoContent = TRUE; *pafl |= ICMS_DECODED_CONTENTINFO_CONTENT; goto SuccessReturn; } } if (NULL == *ppooidContentType) goto SuccessReturn; // not enough data
// [0] EXPLICIT, step into it
if (0 == (*pafl & ICMS_DECODED_CONTENTINFO_CONTENT)) { if (CMSG_INDEFINITE_LENGTH == pcmi->cbContentInfo) { PICM_BUFFER pbuf = &pcmi->bufDecode;
if (pbuf->cbUsed > pbuf->cbDead) { // Check for trailing Null Pairs (00, 00)
if (ICM_TAG_NULL == *(pbuf->pbData + pbuf->cbDead)) { // Only has contentType. The optional content has
// been omitted.
*pfNoContent = TRUE; *pafl |= ICMS_DECODED_CONTENTINFO_CONTENT; goto SuccessReturn; } } else goto SuccessReturn; // not enough data
}
if (!ICMS_GetToken( &pcmi->bufDecode, &dwToken, NULL)) goto ContentGetTokenError; switch (dwToken) { case ICMS_TOKEN_INDEFINITE: ++*pcTrailingNullPairs; break; case ICMS_TOKEN_DEFINITE: break; case ICMS_TOKEN_INCOMPLETE: goto SuccessReturn; default: goto InvalidTokenError; } *pfNoContent = FALSE; *pafl |= ICMS_DECODED_CONTENTINFO_CONTENT; }
SuccessReturn: fRet = TRUE; CommonReturn: ICM_SetLastError(dwError); return fRet;
ErrorReturn: dwError = GetLastError(); fRet = FALSE; goto CommonReturn; TRACE_ERROR(DecodeContentTypeError) // error already set
TRACE_ERROR(ContentGetTokenError) // error already set
TRACE_ERROR(InvalidTokenError) // error already set
TRACE_ERROR(ContentInfoGetTokenError) // error already set
}
//+-------------------------------------------------------------------------
// Consume the NULL pairs which terminate the indefinite-length encoding.
//--------------------------------------------------------------------------
BOOL WINAPI ICMS_ConsumeTrailingNulls( IN PCRYPT_MSG_INFO pcmi, IN OUT PDWORD pcNullPairs, IN BOOL fFinal) { BOOL fRet; DWORD dwToken;
for (; *pcNullPairs; (*pcNullPairs)--) { if (!ICMS_GetToken( &pcmi->bufDecode, &dwToken, NULL)) goto GetTokenError; if ((ICMS_TOKEN_INCOMPLETE == dwToken) && !fFinal) goto SuccessReturn; if (ICMS_TOKEN_NULLPAIR != dwToken) goto WrongTokenError; }
SuccessReturn: fRet = TRUE; CommonReturn: return fRet;
ErrorReturn: fRet = FALSE; goto CommonReturn; TRACE_ERROR(GetTokenError) // error already set
TRACE_ERROR(WrongTokenError) // error already set
}
//+-------------------------------------------------------------------------
// Handle incremental suffix data to be decoded, for a data message.
//--------------------------------------------------------------------------
BOOL WINAPI ICMS_DecodeSuffixData( IN PCRYPT_MSG_INFO pcmi, IN BOOL fFinal) { BOOL fRet;
if (!ICMS_ConsumeTrailingNulls( pcmi, &pcmi->cEndNullPairs, fFinal)) goto ConsumeTrailingNullsError; if (0 == pcmi->cEndNullPairs) pcmi->aflStream |= ICMS_DECODED_SUFFIX;
if (fFinal && (pcmi->bufDecode.cbUsed > pcmi->bufDecode.cbDead)) goto ExcessDataError;
fRet = TRUE; CommonReturn: return fRet;
ErrorReturn: fRet = FALSE; goto CommonReturn; TRACE_ERROR(ConsumeTrailingNullsError) // error already set
TRACE_ERROR(ExcessDataError) // error already set
}
//+-------------------------------------------------------------------------
// Handle incremental suffix data to be decoded, for a signed message.
//--------------------------------------------------------------------------
BOOL WINAPI ICMS_DecodeSuffixSigned( IN PCRYPT_MSG_INFO pcmi, IN BOOL fFinal) { DWORD dwError = ERROR_SUCCESS; BOOL fRet; ASN1decoding_t pDec = ICM_GetDecoder(); PSIGNED_DATA_INFO psdi = pcmi->psdi; PICM_BUFFER pbuf = &pcmi->bufDecode; CertificatesNC *pCertificates = NULL; CrlsNC *pCrls = NULL; SignerInfosNC *pSignerInfos = NULL; Any *pAny; DWORD i;
if (!ICMS_ConsumeTrailingNulls( pcmi, &pcmi->cInnerNullPairs, fFinal)) goto ConsumeInnerNullsError; if (pcmi->cInnerNullPairs) goto SuccessReturn;
// certificates
if (0 == (pcmi->aflDecode & ICMS_DECODED_SIGNED_CERTIFICATES)) { if (pbuf->cbUsed > pbuf->cbDead) { if (ICM_TAG_CONSTRUCTED_CONTEXT_0 == *(pbuf->pbData + pbuf->cbDead)) { // Detected the [0] IMPLICIT indicating certificates.
// Change the identifier octet so it will decode properly.
*(pbuf->pbData + pbuf->cbDead) = ICM_TAG_SET; if (!ICMS_DecodePDU( pcmi, pDec, CertificatesNC_PDU, (void **)&pCertificates)) goto DecodeCertificatesError; if (pCertificates) { for (i=pCertificates->count, #ifdef OSS_CRYPT_ASN1
pAny=pCertificates->certificates; #else
pAny=pCertificates->value; #endif // OSS_CRYPT_ASN1
i>0; i--, pAny++) { if (!ICM_InsertTailBlob( psdi->pCertificateList, pAny)) goto CertInsertTailBlobError; } pcmi->aflDecode |= ICMS_DECODED_SIGNED_CERTIFICATES; } else { // The decode failed, presumably due to insufficient data.
// Restore the original tag, so we will enter this block
// and try again on the next call.
*(pbuf->pbData + pbuf->cbDead) = ICM_TAG_CONSTRUCTED_CONTEXT_0; } } else { // Certificates not present. Mark them as decoded.
pcmi->aflDecode |= ICMS_DECODED_SIGNED_CERTIFICATES; } } } if (0 == (pcmi->aflDecode & ICMS_DECODED_SIGNED_CERTIFICATES)) goto SuccessReturn;
// crls
if (0 == (pcmi->aflDecode & ICMS_DECODED_SIGNED_CRLS)) { if (pbuf->cbUsed > pbuf->cbDead) { if (ICM_TAG_CONSTRUCTED_CONTEXT_1 == *(pbuf->pbData + pbuf->cbDead)) { // Detected the [1] IMPLICIT indicating crls.
// Change the identifier octet so it will decode properly.
*(pbuf->pbData + pbuf->cbDead) = ICM_TAG_SET; if (!ICMS_DecodePDU( pcmi, pDec, CrlsNC_PDU, (void **)&pCrls)) goto DecodeCrlsError; if (pCrls) { for (i=pCrls->count, #ifdef OSS_CRYPT_ASN1
pAny=pCrls->crls; #else
pAny=pCrls->value; #endif // OSS_CRYPT_ASN1
i>0; i--, pAny++) { if (!ICM_InsertTailBlob( psdi->pCrlList, pAny)) goto CrlInsertTailBlobError; } pcmi->aflDecode |= ICMS_DECODED_SIGNED_CRLS; } else { // The decode failed, presumably due to insufficient data.
// Restore the original tag, so we will enter this block
// and try again on the next call.
*(pbuf->pbData + pbuf->cbDead) = ICM_TAG_CONSTRUCTED_CONTEXT_1; } } else { // Crls not present. Mark them as decoded.
pcmi->aflDecode |= ICMS_DECODED_SIGNED_CRLS; } } } if (0 == (pcmi->aflDecode & ICMS_DECODED_SIGNED_CRLS)) goto SuccessReturn;
// signerInfos
if (0 == (pcmi->aflDecode & ICMS_DECODED_SIGNED_SIGNERINFOS)) { if (!ICMS_DecodePDU( pcmi, pDec, SignerInfosNC_PDU, (void **)&pSignerInfos)) goto DecodeSignerInfosError; if (pSignerInfos) { for (i=pSignerInfos->count, pAny=pSignerInfos->value; i>0; i--, pAny++) { if (!ICM_InsertTailSigner( psdi->pSignerList, pAny)) goto SignerInfoInsertTailBlobError; } pcmi->aflDecode |= ICMS_DECODED_SIGNED_SIGNERINFOS; } } if (0 == (pcmi->aflDecode & ICMS_DECODED_SIGNED_SIGNERINFOS)) goto SuccessReturn;
if (!ICMS_ConsumeTrailingNulls( pcmi, &pcmi->cEndNullPairs, fFinal)) goto ConsumeEndNullsError; if (0 == pcmi->cEndNullPairs) pcmi->aflStream |= ICMS_DECODED_SUFFIX;
SuccessReturn: fRet = TRUE; CommonReturn: PkiAsn1FreeInfo( pDec, CertificatesNC_PDU, pCertificates); PkiAsn1FreeInfo( pDec, CrlsNC_PDU, pCrls); PkiAsn1FreeInfo( pDec, SignerInfosNC_PDU, pSignerInfos); ICM_SetLastError(dwError); return fRet;
ErrorReturn: dwError = GetLastError(); fRet = FALSE; goto CommonReturn; TRACE_ERROR(ConsumeInnerNullsError) // error already set
TRACE_ERROR(DecodeCertificatesError) // error already set
TRACE_ERROR(CertInsertTailBlobError) // error already set
TRACE_ERROR(DecodeCrlsError) // error already set
TRACE_ERROR(CrlInsertTailBlobError) // error already set
TRACE_ERROR(DecodeSignerInfosError) // error already set
TRACE_ERROR(SignerInfoInsertTailBlobError) // error already set
TRACE_ERROR(ConsumeEndNullsError) // error already set
}
//+-------------------------------------------------------------------------
// Handle incremental suffix data to be decoded, for an enveloped message.
//--------------------------------------------------------------------------
BOOL WINAPI ICMS_DecodeSuffixEnveloped( IN PCRYPT_MSG_INFO pcmi, IN BOOL fFinal) { BOOL fRet; ASN1decoding_t pDec = ICM_GetDecoder(); PICM_BUFFER pbuf = &pcmi->bufDecode; Attributes *pAttributes = NULL; #ifdef CMS_PKCS7
CmsEnvelopedData *ped = (CmsEnvelopedData *)pcmi->pvMsg; #endif // CMS_PKCS7
OSS_DECODE_INFO odi; COssDecodeInfoNode *pnOssDecodeInfo;
if (!ICMS_ConsumeTrailingNulls( pcmi, &pcmi->cInnerNullPairs, fFinal)) goto ConsumeInnerNullsError; if (pcmi->cInnerNullPairs) goto SuccessReturn;
// unprotectedAttrs[1] IMPLICIT UnprotectedAttributes OPTIONAL
if (0 == (pcmi->aflDecode & ICMS_DECODED_ENVELOPED_ATTR)) { if (pbuf->cbUsed > pbuf->cbDead) { if (ICM_TAG_CONSTRUCTED_CONTEXT_1 == *(pbuf->pbData + pbuf->cbDead)) { // Detected the [1] IMPLICIT indicating unprotectedAttrs.
// Change the identifier octet so it will decode properly.
*(pbuf->pbData + pbuf->cbDead) = ICM_TAG_SET;
if (!ICMS_DecodePDU( pcmi, pDec, Attributes_PDU, (void **)&pAttributes)) goto DecodeAttributesError; if (pAttributes) { #ifdef CMS_PKCS7
ped->unprotectedAttrs = *pAttributes; ped->bit_mask |= unprotectedAttrs_present; #endif // CMS_PKCS7
odi.iPDU = Attributes_PDU; odi.pvPDU = pAttributes; if (NULL == (pnOssDecodeInfo = new COssDecodeInfoNode( &odi))) { PkiAsn1FreeInfo( pDec, odi.iPDU, odi.pvPDU); goto NewOssDecodeInfoNodeError; } pcmi->aflDecode |= ICMS_DECODED_ENVELOPED_ATTR; pcmi->plDecodeInfo->InsertTail( pnOssDecodeInfo); } else { // The decode failed, presumably due to insufficient data.
// Restore the original tag, so we will enter this block
// and try again on the next call.
*(pbuf->pbData + pbuf->cbDead) = ICM_TAG_CONSTRUCTED_CONTEXT_1; } } else { // unprotectedAttrs not present. Mark them as decoded.
pcmi->aflDecode |= ICMS_DECODED_ENVELOPED_ATTR; } } else if (fFinal) // unprotectedAttrs not present. Mark them as decoded.
pcmi->aflDecode |= ICMS_DECODED_ENVELOPED_ATTR;
} if (0 == (pcmi->aflDecode & ICMS_DECODED_ENVELOPED_ATTR)) goto SuccessReturn;
if (!ICMS_ConsumeTrailingNulls( pcmi, &pcmi->cEndNullPairs, fFinal)) goto ConsumeEndNullsError; if (0 == pcmi->cEndNullPairs) pcmi->aflStream |= ICMS_DECODED_SUFFIX; SuccessReturn: fRet = TRUE; CommonReturn: return fRet;
ErrorReturn: fRet = FALSE; goto CommonReturn; TRACE_ERROR(ConsumeInnerNullsError) // error already set
TRACE_ERROR(DecodeAttributesError) // error already set
TRACE_ERROR(NewOssDecodeInfoNodeError) // error already set
TRACE_ERROR(ConsumeEndNullsError) // error already set
}
//+-------------------------------------------------------------------------
// Handle incremental suffix data to be decoded.
//--------------------------------------------------------------------------
BOOL WINAPI ICMS_DecodeSuffix( IN PCRYPT_MSG_INFO pcmi, IN BOOL fFinal) { DWORD dwError = ERROR_SUCCESS; BOOL fRet;
switch (pcmi->dwMsgType) { case CMSG_DATA: fRet = ICMS_DecodeSuffixData( pcmi, fFinal); break; case CMSG_SIGNED: fRet = ICMS_DecodeSuffixSigned( pcmi, fFinal); break; case CMSG_ENVELOPED: fRet = ICMS_DecodeSuffixEnveloped( pcmi, fFinal); break; case CMSG_HASHED: // fRet = ICMS_DecodeSuffixDigested( pcmi, fFinal);
// break;
case CMSG_SIGNED_AND_ENVELOPED: case CMSG_ENCRYPTED: goto MessageTypeNotSupportedYet; default: goto InvalidMsgType; }
if (!fRet) goto ErrorReturn;
CommonReturn: ICM_SetLastError(dwError); return fRet;
ErrorReturn: dwError = GetLastError(); fRet = FALSE; goto CommonReturn; SET_ERROR(MessageTypeNotSupportedYet,CRYPT_E_INVALID_MSG_TYPE) TRACE_ERROR(InvalidMsgType) // error already set
}
//+-------------------------------------------------------------------------
// Decrypt and output pending decode data.
//--------------------------------------------------------------------------
BOOL WINAPI ICMS_DecodeDecryptAndOutput( IN PCRYPT_MSG_INFO pcmi, IN OUT PICM_BUFFER pbufDecode, IN OUT PDWORD pcbPending, IN BOOL fFinal) { DWORD dwError = ERROR_SUCCESS; BOOL fRet; BOOL fBlockCipher = pcmi->fBlockCipher; PICM_BUFFER pbufCrypt = &pcmi->bufCrypt; DWORD cbCipher; DWORD cb;
for (cbCipher = min( *pcbPending, pbufDecode->cbUsed - pbufDecode->cbDead); cbCipher > 0;) { cb = min( cbCipher, pbufCrypt->cbSize - pbufCrypt->cbUsed); // must fit
CopyMemory( pbufCrypt->pbData + pbufCrypt->cbUsed, pbufDecode->pbData + pbufDecode->cbDead, cb); pbufCrypt->cbUsed += cb; pbufDecode->cbDead += cb; *pcbPending -= cb; cbCipher -= cb; if (pbufCrypt->cbSize == pbufCrypt->cbUsed) { // Decrypt and copy out the buffer
cb = pbufCrypt->cbSize; if (fBlockCipher) { // Keep the last block
cb -= pcmi->cbBlockSize; } if (!CryptDecrypt( pcmi->hkeyContentCrypt, NULL, // hHash
FALSE, // fFinal
0, // dwFlags
pbufCrypt->pbData, &cb)) goto DecryptError; if (!ICMS_Output( pcmi, pbufCrypt->pbData, cb, FALSE)) // fFinal
goto OutputError;
if (fBlockCipher) { // Move the last block to the beginning of the buffer
// and reset the count to start after this block.
// Since we are sure the src and dst do not overlap,
// use CopyMemory (faster than MoveMemory).
cb = pbufCrypt->cbSize - pcmi->cbBlockSize; CopyMemory( pbufCrypt->pbData, pbufCrypt->pbData + cb, pcmi->cbBlockSize); pbufCrypt->cbUsed = pcmi->cbBlockSize; } else { pbufCrypt->cbUsed = 0; } } }
if (fFinal) { if (cb = pbufCrypt->cbUsed) { if (!CryptDecrypt( pcmi->hkeyContentCrypt, NULL, // hHash
TRUE, // fFinal
0, // dwFlags
pbufCrypt->pbData, &cb)) goto FinalDecryptError; } if (!ICMS_Output( pcmi, pbufCrypt->pbData, cb, TRUE)) // fFinal
goto FinalOutputError; }
fRet = TRUE; CommonReturn: ICM_SetLastError(dwError); return fRet;
ErrorReturn: dwError = GetLastError(); fRet = FALSE; goto CommonReturn; TRACE_ERROR(DecryptError) // error already set
TRACE_ERROR(FinalDecryptError) // error already set
TRACE_ERROR(OutputError) // error already set
TRACE_ERROR(FinalOutputError) // error already set
}
//+-------------------------------------------------------------------------
// Given a key for decryption, prepare for the decryption to proceed.
//--------------------------------------------------------------------------
BOOL WINAPI ICMS_SetDecryptKey( IN PCRYPT_MSG_INFO pcmi, IN HCRYPTKEY hkeyDecrypt) { BOOL fRet; DWORD cbPending; PICM_BUFFER pbufPendingCrypt = &pcmi->bufPendingCrypt;
if (pcmi->hkeyContentCrypt) { SetLastError((DWORD) CRYPT_E_ALREADY_DECRYPTED); return FALSE; }
pcmi->hkeyContentCrypt = hkeyDecrypt;
if (!ICMS_CreateEnvelopedBuffer( pcmi)) goto CreateEnvelopedBufferError; pcmi->bufCrypt.cbSize += pcmi->cbBlockSize; // use whole thing for decode
// Decrypt any pending ciphertext
cbPending = pbufPendingCrypt->cbUsed - pbufPendingCrypt->cbDead; if (!ICMS_DecodeDecryptAndOutput( pcmi, pbufPendingCrypt, &cbPending, 0 != (pcmi->aflStream & (ICMS_DECODED_CONTENT | ICMS_FINAL)))) goto DecryptAndOutputError;
fRet = TRUE; CommonReturn: return fRet;
ErrorReturn: pcmi->hkeyContentCrypt = 0; // caller closes hkeyDecrypt on
// error
fRet = FALSE; goto CommonReturn; TRACE_ERROR(CreateEnvelopedBufferError) // error already set
TRACE_ERROR(DecryptAndOutputError) // error already set
}
//+-------------------------------------------------------------------------
// Decode callback.
//--------------------------------------------------------------------------
BOOL WINAPI ICMS_DecodeCallback( IN const void *pvArg, IN OUT PICM_BUFFER pbuf, IN OUT PDWORD pcbPending, IN BOOL fFinal) { DWORD dwError = ERROR_SUCCESS; BOOL fRet; PCRYPT_MSG_INFO pcmi = (PCRYPT_MSG_INFO)pvArg; PBYTE pbData = pbuf->pbData + pbuf->cbDead; DWORD cbData = min( *pcbPending, pbuf->cbUsed - pbuf->cbDead);
if (CMSG_ENVELOPED == pcmi->dwMsgType) { if (NULL == pcmi->hkeyContentCrypt) { // Allow ciphertext to pile up until the decrypt key is set via
// CryptMsgControl(... CMSG_CTRL_DECRYPT ...)
if (!ICMS_QueueToBuffer(&pcmi->bufPendingCrypt, pbData, cbData)) goto QueuePendingCryptError;
pbuf->cbDead += cbData; *pcbPending -= cbData; } else if (!ICMS_DecodeDecryptAndOutput( pcmi, pbuf, pcbPending, fFinal)) goto DecryptAndOutputError; } else { if (cbData && pcmi->pHashList) { if (!ICM_UpdateListDigest( pcmi->pHashList, pbData, cbData)) goto UpdateDigestError; }
pbuf->cbDead += cbData; *pcbPending -= cbData; if (!ICMS_Output( pcmi, pbData, cbData, fFinal)) goto OutputError; }
fRet = TRUE; CommonReturn: ICM_SetLastError(dwError); return fRet;
ErrorReturn: dwError = GetLastError(); fRet = FALSE; goto CommonReturn; TRACE_ERROR(QueuePendingCryptError) // error already set
TRACE_ERROR(DecryptAndOutputError) // error already set
TRACE_ERROR(UpdateDigestError) // error already set
TRACE_ERROR(OutputError) // error already set
}
//+-------------------------------------------------------------------------
// Hash callback.
//--------------------------------------------------------------------------
BOOL WINAPI ICMS_HashCallback( IN const void *pvArg, IN OUT PICM_BUFFER pbuf, IN OUT PDWORD pcbPending, IN BOOL fFinal) { DWORD dwError = ERROR_SUCCESS; BOOL fRet; PBYTE pbData = pbuf->pbData + pbuf->cbDead; DWORD cbData = min( *pcbPending, pbuf->cbUsed - pbuf->cbDead);
if (pvArg) { if (!ICM_UpdateListDigest( (CHashList *)pvArg, pbData, cbData)) goto UpdateDigestError; }
pbuf->cbDead += cbData; *pcbPending -= cbData;
fRet = TRUE; CommonReturn: ICM_SetLastError(dwError); return fRet;
ErrorReturn: dwError = GetLastError(); fRet = FALSE; goto CommonReturn; TRACE_ERROR(UpdateDigestError) // error already set
fFinal; }
//+-------------------------------------------------------------------------
// Hash incremental content data to be encoded, for an octet string.
//--------------------------------------------------------------------------
BOOL WINAPI ICMS_HashContent( IN PCRYPT_MSG_INFO pcmi, IN PBYTE pbData, IN DWORD cbData) { BOOL fRet;
if (!ICMS_QueueToBuffer( &pcmi->bufEncode, (PBYTE)pbData, cbData)) goto QueueToBufferError;
if (!ICMS_ProcessStringContent( &pcmi->bufEncode, &pcmi->aflStream, &pcmi->cbDefiniteRemain, &pcmi->cLevelIndefiniteInner, ICMS_HashCallback, pcmi->pHashList)) goto ProcessStringContentError;
fRet = TRUE; CommonReturn: return fRet;
ErrorReturn: fRet = FALSE; goto CommonReturn; TRACE_ERROR(QueueToBufferError) // error already set
TRACE_ERROR(ProcessStringContentError) // error already set
}
//+-------------------------------------------------------------------------
// Handle incremental content data to be decoded, for an octet string.
//--------------------------------------------------------------------------
BOOL WINAPI ICMS_DecodeContentOctetString( IN PCRYPT_MSG_INFO pcmi, IN BOOL fFinal) { BOOL fRet;
if (!ICMS_ProcessStringContent( &pcmi->bufDecode, &pcmi->aflStream, &pcmi->cbDefiniteRemain, &pcmi->cLevelIndefiniteInner, ICMS_DecodeCallback, pcmi)) goto ProcessStringContentError;
if (pcmi->aflStream & ICMS_PROCESS_CONTENT_DONE) pcmi->aflStream |= ICMS_DECODED_CONTENT;
if (fFinal && (pcmi->cbDefiniteRemain || pcmi->cLevelIndefiniteInner || (0 == (pcmi->aflStream & ICMS_DECODED_CONTENT)))) { goto PrematureFinalError; }
fRet = TRUE; CommonReturn: return fRet;
ErrorReturn: fRet = FALSE; goto CommonReturn; SET_ERROR(PrematureFinalError,CRYPT_E_STREAM_INSUFFICIENT_DATA) TRACE_ERROR(ProcessStringContentError) // error already set
}
//+-------------------------------------------------------------------------
// Handle incremental content data to be decoded, for a sequence.
//--------------------------------------------------------------------------
BOOL WINAPI ICMS_DecodeContentSequence( IN PCRYPT_MSG_INFO pcmi, IN BOOL fFinal) { BOOL fRet; PICM_BUFFER pbuf = &pcmi->bufDecode; PBYTE pbData = pbuf->pbData + pbuf->cbDead; DWORD cbData = pbuf->cbUsed - pbuf->cbDead; LONG lSkipped; DWORD cbContent; const BYTE *pbContent;
if (pcmi->aflStream & ICMS_PROCESS_CONTENT_BEGUN) goto MultipleContentSequenceError;
// Get the tag and length for the inner content
if (0 > (lSkipped = Asn1UtilExtractContent( pbData, cbData, &cbContent, &pbContent))) { if (ASN1UTIL_INSUFFICIENT_DATA != lSkipped) goto ExtractContentError; else goto SuccessReturn; }
if (CMSG_INDEFINITE_LENGTH == cbContent) goto IndefiniteLengthInnerContentNotImplemented;
// Output the tag and length octets for the encoded inner content.
// Note, not included in the content to be verified in a signature.
if (!ICMS_Output( pcmi, pbData, (DWORD) lSkipped, FALSE)) goto OutputError;
pcmi->aflStream |= ICMS_INNER_OCTETSTRING; // Decode as an octet string. Will skip the tag and length octets
fRet = ICMS_DecodeContentOctetString(pcmi, fFinal);
CommonReturn: return fRet;
SuccessReturn: fRet = TRUE; goto CommonReturn;
ErrorReturn: fRet = FALSE; goto CommonReturn; SET_ERROR(MultipleContentSequenceError, CRYPT_E_MSG_ERROR) TRACE_ERROR(ExtractContentError) SET_ERROR(IndefiniteLengthInnerContentNotImplemented, E_NOTIMPL) TRACE_ERROR(OutputError) }
//+-------------------------------------------------------------------------
// Handle incremental prefix data to be decoded.
//--------------------------------------------------------------------------
BOOL WINAPI ICMS_DecodeContent( IN PCRYPT_MSG_INFO pcmi, IN BOOL fFinal) { DWORD dwError = ERROR_SUCCESS; BOOL fRet; PICM_BUFFER pbuf = &pcmi->bufDecode;
if (pcmi->aflStream & ICMS_RAW_DATA) { // Should be able to skip bufDecode for this case.
if (!ICMS_Output( pcmi, pbuf->pbData + pbuf->cbDead, pbuf->cbUsed - pbuf->cbDead, fFinal)) goto RawOutputError; pbuf->cbDead = pbuf->cbUsed;
if (fFinal) pcmi->aflStream |= ICMS_DECODED_CONTENT | ICMS_DECODED_SUFFIX;
} else if (pcmi->aflStream & ICMS_INNER_OCTETSTRING) { if (!ICMS_DecodeContentOctetString( pcmi, fFinal)) goto DecodeContentOctetStringError;
} else { if (!ICMS_DecodeContentSequence( pcmi, fFinal)) goto DecodeContentSequenceError; }
fRet = TRUE; CommonReturn: ICM_SetLastError(dwError); return fRet;
ErrorReturn: dwError = GetLastError(); fRet = FALSE; goto CommonReturn; TRACE_ERROR(RawOutputError) // error already set
TRACE_ERROR(DecodeContentOctetStringError) // error already set
TRACE_ERROR(DecodeContentSequenceError) // error already set
}
//+-------------------------------------------------------------------------
// Handle incremental prefix data to be decoded, for a data message.
//--------------------------------------------------------------------------
BOOL WINAPI ICMS_DecodePrefixData( IN PCRYPT_MSG_INFO pcmi, IN BOOL fFinal) { if (0 ==(pcmi->aflStream & ICMS_NONBARE)) pcmi->aflStream |= ICMS_RAW_DATA; pcmi->aflStream |= ICMS_DECODED_PREFIX | ICMS_INNER_OCTETSTRING; return TRUE; }
//+-------------------------------------------------------------------------
// Handle incremental prefix data to be decoded, for a signed message.
//--------------------------------------------------------------------------
BOOL WINAPI ICMS_DecodePrefixSigned( IN PCRYPT_MSG_INFO pcmi, IN BOOL fFinal) { DWORD dwError = ERROR_SUCCESS; BOOL fRet; ASN1decoding_t pDec = ICM_GetDecoder(); PSIGNED_DATA_INFO psdi = pcmi->psdi; DWORD dwToken; int *piVersion = NULL; DigestAlgorithmIdentifiersNC *pDigestAlgorithms = NULL; Any *pAny; DWORD cb; DWORD i; BOOL fNoContent;
if (NULL == psdi) { if (NULL == (psdi = (PSIGNED_DATA_INFO)ICM_AllocZero( sizeof(SIGNED_DATA_INFO)))) goto SdiAllocError; pcmi->psdi = psdi;
if (NULL == (psdi->pAlgidList = new CBlobList)) goto NewAlgidListError; if (NULL == (psdi->pCertificateList = new CBlobList)) goto NewCertificateListError; if (NULL == (psdi->pCrlList = new CBlobList)) goto NewCrlListError; if (NULL == (psdi->pSignerList = new CSignerList)) goto NewSignerListError; }
// SignedData sequence
if (0 == (pcmi->aflDecode & ICMS_DECODED_SIGNED_SEQ)) { if (!ICMS_GetToken( &pcmi->bufDecode, &dwToken, NULL)) goto GetTokenError; switch(dwToken) { case ICMS_TOKEN_INDEFINITE: pcmi->cEndNullPairs++; break; case ICMS_TOKEN_DEFINITE: break; case ICMS_TOKEN_INCOMPLETE: goto SuccessReturn; default: goto InvalidTokenError; } pcmi->aflDecode |= ICMS_DECODED_SIGNED_SEQ; } if (0 == (pcmi->aflDecode & ICMS_DECODED_SIGNED_SEQ)) goto SuccessReturn;
// version
if (0 == (pcmi->aflDecode & ICMS_DECODED_SIGNED_VERSION)) { if (!ICMS_DecodePDU( pcmi, pDec, IntegerType_PDU, (void **)&piVersion)) goto DecodeVersionError; if (piVersion) { psdi->version = *piVersion; pcmi->aflDecode |= ICMS_DECODED_SIGNED_VERSION; } } if (0 == (pcmi->aflDecode & ICMS_DECODED_SIGNED_VERSION)) goto SuccessReturn;
// digestAlgorithms
if (0 == (pcmi->aflDecode & ICMS_DECODED_SIGNED_DIGESTALGOS)) { if (!ICMS_DecodePDU( pcmi, pDec, DigestAlgorithmIdentifiersNC_PDU, (void **)&pDigestAlgorithms)) goto DecodeDigestAlgorithmsError; if (pDigestAlgorithms) { for (i=pDigestAlgorithms->count, pAny=pDigestAlgorithms->value; i>0; i--, pAny++) { if (!ICM_InsertTailBlob( psdi->pAlgidList, pAny)) goto DigestAlgorithmInsertTailBlobError; } // We have the algorithms. Now create the hash handles.
if (!ICM_CreateHashList( pcmi->hCryptProv, &pcmi->pHashList, pcmi->psdi->pAlgidList)) goto CreateHashListError; pcmi->aflDecode |= ICMS_DECODED_SIGNED_DIGESTALGOS; } } if (0 == (pcmi->aflDecode & ICMS_DECODED_SIGNED_DIGESTALGOS)) goto SuccessReturn;
// contentInfo
if (0 == (pcmi->aflDecode & ICMS_DECODED_SIGNED_CONTENTINFO)) { if (!ICMS_DecodePrefixContentInfo( pcmi, &pcmi->pooid, &pcmi->cInnerNullPairs, &pcmi->aflInner, &fNoContent)) goto DecodePrefixSignedContentInfoError; if (pcmi->aflInner & ICMS_DECODED_CONTENTINFO_CONTENT) { // We cracked the whole header.
// Translate the inner contentType oid into a string.
if (!PkiAsn1FromObjectIdentifier( pcmi->pooid->count, pcmi->pooid->value, NULL, &cb)) goto PkiAsn1FromObjectIdentifierSizeError; if (NULL == (psdi->pci = (PCONTENT_INFO)ICM_Alloc( cb + INFO_LEN_ALIGN(sizeof(CONTENT_INFO))))) goto AllocContentInfoError; psdi->pci->pszContentType = (LPSTR)(psdi->pci) + INFO_LEN_ALIGN(sizeof(CONTENT_INFO)); psdi->pci->content.cbData = 0; psdi->pci->content.pbData = NULL; if (!PkiAsn1FromObjectIdentifier( pcmi->pooid->count, pcmi->pooid->value, psdi->pci->pszContentType, &cb)) goto PkiAsn1FromObjectIdentifierError; PkiAsn1FreeDecoded(pDec, pcmi->pooid, ObjectIdentifierType_PDU); pcmi->pooid = NULL; pcmi->aflDecode |= ICMS_DECODED_SIGNED_CONTENTINFO;
if (fNoContent) { // No content. Output final flag with no content.
if (!ICMS_Output(pcmi, NULL, 0, TRUE)) goto OutputError; pcmi->aflStream |= ICMS_DECODED_CONTENT; } else { if (0 == strcmp( psdi->pci->pszContentType, pszObjIdDataType) #ifdef CMS_PKCS7
|| psdi->version >= CMSG_SIGNED_DATA_V3 #endif // CMS_PKCS7
) pcmi->aflStream |= ICMS_INNER_OCTETSTRING; } pcmi->aflStream |= ICMS_DECODED_PREFIX; } }
SuccessReturn: fRet = TRUE; CommonReturn: PkiAsn1FreeInfo( pDec, IntegerType_PDU, piVersion); PkiAsn1FreeInfo( pDec, DigestAlgorithmIdentifiersNC_PDU, pDigestAlgorithms); ICM_SetLastError(dwError); return fRet;
ErrorReturn: dwError = GetLastError(); // note, pcmi->psdi and pcmi->pooid are freed in CryptMsgClose
fRet = FALSE; goto CommonReturn; TRACE_ERROR(SdiAllocError) // error already set
TRACE_ERROR(NewAlgidListError) // error already set
TRACE_ERROR(NewCertificateListError) // error already set
TRACE_ERROR(NewCrlListError) // error already set
TRACE_ERROR(NewSignerListError) // error already set
TRACE_ERROR(GetTokenError) // error already set
TRACE_ERROR(InvalidTokenError) // error already set
TRACE_ERROR(DecodeVersionError) // error already set
TRACE_ERROR(DecodeDigestAlgorithmsError) // error already set
TRACE_ERROR(DigestAlgorithmInsertTailBlobError) // error already set
TRACE_ERROR(CreateHashListError) // error already set
TRACE_ERROR(DecodePrefixSignedContentInfoError) // error already set
TRACE_ERROR(PkiAsn1FromObjectIdentifierSizeError) // error already set
TRACE_ERROR(AllocContentInfoError) // error already set
TRACE_ERROR(PkiAsn1FromObjectIdentifierError) // error already set
TRACE_ERROR(OutputError) // error already set
}
//+-------------------------------------------------------------------------
// Handle incremental prefix data to be decoded, for an enveloped message.
//--------------------------------------------------------------------------
BOOL WINAPI ICMS_DecodePrefixEnveloped( IN PCRYPT_MSG_INFO pcmi, IN BOOL fFinal) { DWORD dwError = ERROR_SUCCESS; BOOL fRet; ASN1decoding_t pDec = ICM_GetDecoder(); PICM_BUFFER pbuf = &pcmi->bufDecode; #ifdef CMS_PKCS7
CmsEnvelopedData *ped = (CmsEnvelopedData *)pcmi->pvMsg; #else
EnvelopedData *ped = (EnvelopedData *)pcmi->pvMsg; #endif // CMS_PKCS7
DWORD dwToken; int *piVersion = NULL; #ifdef CMS_PKCS7
CmsRecipientInfos *pRecipientInfos = NULL; #else
RecipientInfos *pRecipientInfos = NULL; #endif // CMS_PKCS7
ObjectIdentifierType *pooidContentType = NULL; AlgorithmIdentifier *poaidContentEncryption = NULL; COssDecodeInfoNode *pnOssDecodeInfo; OSS_DECODE_INFO odi; DWORD cbConsumed;
#ifdef CMS_PKCS7
OriginatorInfoNC *pOriginatorInfo = NULL; Any *pAny; DWORD i; #endif // CMS_PKCS7
if (NULL == ped) { #ifdef CMS_PKCS7
if (NULL == (ped = (CmsEnvelopedData *)ICM_AllocZero( sizeof(CmsEnvelopedData)))) #else
if (NULL == (ped = (EnvelopedData *)ICM_AllocZero( sizeof(EnvelopedData)))) #endif // CMS_PKCS7
goto AllocEnvelopedDataError; pcmi->pvMsg = ped; if (NULL == (pcmi->plDecodeInfo = new COssDecodeInfoList)) goto NewCOssDecodeInfoListError;
#ifdef CMS_PKCS7
if (NULL == (pcmi->pCertificateList = new CBlobList)) goto NewCertificateListError; if (NULL == (pcmi->pCrlList = new CBlobList)) goto NewCrlListError; #endif // CMS_PKCS7
}
// EnvelopedData SEQ
if (0 == (pcmi->aflDecode & ICMS_DECODED_ENVELOPED_SEQ)) { if (!ICMS_GetToken( &pcmi->bufDecode, &dwToken, NULL)) goto EnvelopedDataSeqGetTokenError; switch(dwToken) { case ICMS_TOKEN_INDEFINITE: pcmi->cEndNullPairs++; break; case ICMS_TOKEN_DEFINITE: break; case ICMS_TOKEN_INCOMPLETE: goto SuccessReturn; default: goto InvalidTokenError; } pcmi->aflDecode |= ICMS_DECODED_ENVELOPED_SEQ; } if (0 == (pcmi->aflDecode & ICMS_DECODED_ENVELOPED_SEQ)) goto SuccessReturn;
// version
if (0 == (pcmi->aflDecode & ICMS_DECODED_ENVELOPED_VERSION)) { if (!ICMS_DecodePDU( pcmi, pDec, IntegerType_PDU, (void **)&piVersion)) goto DecodeVersionError; if (piVersion) { ped->version = *piVersion; pcmi->aflDecode |= ICMS_DECODED_ENVELOPED_VERSION; } } if (0 == (pcmi->aflDecode & ICMS_DECODED_ENVELOPED_VERSION)) goto SuccessReturn;
#ifdef CMS_PKCS7
// originatorInfo OPTIONAL
if (0 == (pcmi->aflDecode & ICMS_DECODED_ENVELOPED_ORIGINATOR)) { if (pbuf->cbUsed > pbuf->cbDead) { if (ICM_TAG_CONSTRUCTED_CONTEXT_0 == *(pbuf->pbData + pbuf->cbDead)) { // Detected the [0] IMPLICIT indicating originatorInfo.
// Change the identifier octet so it will decode properly.
*(pbuf->pbData + pbuf->cbDead) = ICM_TAG_SEQ; if (!ICMS_DecodePDU( pcmi, pDec, OriginatorInfoNC_PDU, (void **)&pOriginatorInfo)) goto DecodeOriginatorInfoError; if (pOriginatorInfo) { if (pOriginatorInfo->bit_mask & certificates_present) { for (i=pOriginatorInfo->certificates.count, #ifdef OSS_CRYPT_ASN1
pAny=pOriginatorInfo->certificates.certificates; #else
pAny=pOriginatorInfo->certificates.value; #endif // OSS_CRYPT_ASN1
i>0; i--, pAny++) { if (!ICM_InsertTailBlob( pcmi->pCertificateList, pAny)) goto CertInsertTailBlobError; } }
if (pOriginatorInfo->bit_mask & crls_present) { for (i=pOriginatorInfo->crls.count, #ifdef OSS_CRYPT_ASN1
pAny=pOriginatorInfo->crls.crls; #else
pAny=pOriginatorInfo->crls.value; #endif // OSS_CRYPT_ASN1
i>0; i--, pAny++) { if (!ICM_InsertTailBlob( pcmi->pCrlList, pAny)) goto CrlInsertTailBlobError; } } pcmi->aflDecode |= ICMS_DECODED_ENVELOPED_ORIGINATOR; } else { // The decode failed, presumably due to insufficient data.
// Restore the original tag, so we will enter this block
// and try again on the next call.
*(pbuf->pbData + pbuf->cbDead) = ICM_TAG_CONSTRUCTED_CONTEXT_0; } } else { // originatorInfo not present. Mark as decoded.
pcmi->aflDecode |= ICMS_DECODED_ENVELOPED_ORIGINATOR; } } } if (0 == (pcmi->aflDecode & ICMS_DECODED_ENVELOPED_ORIGINATOR)) goto SuccessReturn; #endif // CMS_PKCS7
// recipientInfos
if (0 == (pcmi->aflDecode & ICMS_DECODED_ENVELOPED_RECIPINFOS)) { if (!ICMS_DecodePDU( pcmi, pDec, #ifdef CMS_PKCS7
CmsRecipientInfos_PDU, #else
RecipientInfos_PDU, #endif // CMS_PKCS7
(void **)&pRecipientInfos)) goto DecodeRecipientInfosError; if (pRecipientInfos) { ped->recipientInfos = *pRecipientInfos; #ifdef CMS_PKCS7
odi.iPDU = CmsRecipientInfos_PDU; #else
odi.iPDU = RecipientInfos_PDU; #endif // CMS_PKCS7
odi.pvPDU = pRecipientInfos; if (NULL == (pnOssDecodeInfo = new COssDecodeInfoNode( &odi))) { PkiAsn1FreeInfo( pDec, odi.iPDU, odi.pvPDU); goto NewOssDecodeInfoNodeError; } pcmi->plDecodeInfo->InsertTail( pnOssDecodeInfo); pcmi->aflDecode |= ICMS_DECODED_ENVELOPED_RECIPINFOS; } } if (0 == (pcmi->aflDecode & ICMS_DECODED_ENVELOPED_RECIPINFOS)) goto SuccessReturn;
// encryptedContentInfo SEQ
if (0 == (pcmi->aflDecode & ICMS_DECODED_ENVELOPED_ECISEQ)) { if (!ICMS_GetToken( &pcmi->bufDecode, &dwToken, &pcmi->cbContentInfo)) goto EncryptedContentInfoSeqGetTokenError; switch(dwToken) { case ICMS_TOKEN_INDEFINITE: pcmi->cInnerNullPairs++; break; case ICMS_TOKEN_DEFINITE: break; case ICMS_TOKEN_INCOMPLETE: goto SuccessReturn; default: goto InvalidTokenError; } pcmi->aflDecode |= ICMS_DECODED_ENVELOPED_ECISEQ; } if (0 == (pcmi->aflDecode & ICMS_DECODED_ENVELOPED_ECISEQ)) goto SuccessReturn;
// contentType
if (0 == (pcmi->aflDecode & ICMS_DECODED_ENVELOPED_ECITYPE)) { if (!ICMS_DecodePDU( pcmi, pDec, ObjectIdentifierType_PDU, (void **)&pooidContentType, &cbConsumed)) goto DecodeContentTypeError; if (pooidContentType) { ICM_CopyOssObjectIdentifier(&ped->encryptedContentInfo.contentType, pooidContentType); // NB- Since ContentType is self-contained and we have saved
// a copy, we can always free pooidContentType when this
// routine exits.
pcmi->aflDecode |= ICMS_DECODED_ENVELOPED_ECITYPE;
if (CMSG_INDEFINITE_LENGTH != pcmi->cbContentInfo) { if (cbConsumed > pcmi->cbContentInfo) goto InvalidEncryptedContentInfoLength; pcmi->cbContentInfo -= cbConsumed; } } } if (0 == (pcmi->aflDecode & ICMS_DECODED_ENVELOPED_ECITYPE)) goto SuccessReturn;
// contentEncryptionAlgorithm
if (0 == (pcmi->aflDecode & ICMS_DECODED_ENVELOPED_ECIALGID)) { if (!ICMS_DecodePDU( pcmi, pDec, AlgorithmIdentifier_PDU, (void **)&poaidContentEncryption, &cbConsumed)) goto DecodeContentEncryptionAlgorithmError; if (poaidContentEncryption) { ped->encryptedContentInfo.contentEncryptionAlgorithm = *poaidContentEncryption; odi.iPDU = AlgorithmIdentifier_PDU; odi.pvPDU = poaidContentEncryption; if (NULL == (pnOssDecodeInfo = new COssDecodeInfoNode( &odi))) { PkiAsn1FreeInfo( pDec, AlgorithmIdentifier_PDU, poaidContentEncryption); goto NewOssDecodeInfoNodeError; } pcmi->plDecodeInfo->InsertTail( pnOssDecodeInfo); pcmi->aflDecode |= ICMS_DECODED_ENVELOPED_ECIALGID;
if (CMSG_INDEFINITE_LENGTH != pcmi->cbContentInfo && cbConsumed == pcmi->cbContentInfo) { // The encryptedContent has been omitted
pcmi->aflDecode |= ICMS_DECODED_ENVELOPED_ECICONTENT; pcmi->aflStream |= ICMS_DECODED_PREFIX | ICMS_DECODED_CONTENT;
if (pcmi->hkeyContentCrypt) { if (!ICMS_Output( pcmi, NULL, // pbData
0, // cbData
TRUE)) // fFinal
goto FinalOutputError; } } } } if (0 == (pcmi->aflDecode & ICMS_DECODED_ENVELOPED_ECIALGID)) goto SuccessReturn;
// encryptedContent [0] IMPLICIT OPTIONAL
//
// Only support DATA or encapsulated encrypted content.
if (0 == (pcmi->aflDecode & ICMS_DECODED_ENVELOPED_ECICONTENT)) { BOOL fNoEncryptedContent = FALSE; if (pbuf->cbUsed > pbuf->cbDead) { BYTE bTag = *(pbuf->pbData + pbuf->cbDead); if (ICM_TAG_CONTEXT_0 == (bTag & ~ICM_TAG_CONSTRUCTED)) { // Detected the [0] IMPLICIT indicating encryptedContent.
// Change the identifier octet so it will decode properly.
*(pbuf->pbData + pbuf->cbDead) = ICM_TAG_OCTETSTRING | (bTag & ICM_TAG_CONSTRUCTED);
pcmi->aflDecode |= ICMS_DECODED_ENVELOPED_ECICONTENT; // The inner type is always OCTET STRING
pcmi->aflStream |= ICMS_DECODED_PREFIX | ICMS_INNER_OCTETSTRING; } else fNoEncryptedContent = TRUE; } else if (fFinal) fNoEncryptedContent = TRUE;
if (fNoEncryptedContent) { // The encryptedContent has been omitted
pcmi->aflDecode |= ICMS_DECODED_ENVELOPED_ECICONTENT; pcmi->aflStream |= ICMS_DECODED_PREFIX | ICMS_DECODED_CONTENT;
if (pcmi->hkeyContentCrypt) { if (!ICMS_Output( pcmi, NULL, // pbData
0, // cbData
TRUE)) // fFinal
goto FinalOutputError; } } }
SuccessReturn: fRet = TRUE; CommonReturn: PkiAsn1FreeInfo( pDec, IntegerType_PDU, piVersion); #ifdef CMS_PKCS7
PkiAsn1FreeInfo( pDec, OriginatorInfoNC_PDU, pOriginatorInfo); #endif // CMS_PKCS7
PkiAsn1FreeInfo( pDec, ObjectIdentifierType_PDU, pooidContentType); ICM_SetLastError(dwError); return fRet;
ErrorReturn: dwError = GetLastError(); fRet = FALSE; goto CommonReturn; TRACE_ERROR(EnvelopedDataSeqGetTokenError) // error already set
TRACE_ERROR(EncryptedContentInfoSeqGetTokenError) // error already set
TRACE_ERROR(InvalidTokenError) // error already set
TRACE_ERROR(DecodeVersionError) // error already set
TRACE_ERROR(AllocEnvelopedDataError) // error already set
TRACE_ERROR(NewCOssDecodeInfoListError) // error already set
#ifdef CMS_PKCS7
TRACE_ERROR(NewCertificateListError) // error already set
TRACE_ERROR(NewCrlListError) // error already set
TRACE_ERROR(DecodeOriginatorInfoError) // error already set
TRACE_ERROR(CertInsertTailBlobError) // error already set
TRACE_ERROR(CrlInsertTailBlobError) // error already set
#endif // CMS_PKCS7
TRACE_ERROR(DecodeRecipientInfosError) // error already set
TRACE_ERROR(DecodeContentTypeError) // error already set
SET_ERROR(InvalidEncryptedContentInfoLength, CRYPT_E_MSG_ERROR) TRACE_ERROR(DecodeContentEncryptionAlgorithmError) // error already set
TRACE_ERROR(NewOssDecodeInfoNodeError) // error already set
TRACE_ERROR(FinalOutputError) // error already set
}
//+-------------------------------------------------------------------------
// Handle incremental prefix data to be decoded.
//--------------------------------------------------------------------------
BOOL WINAPI ICMS_DecodePrefix( IN PCRYPT_MSG_INFO pcmi, IN BOOL fFinal) { DWORD dwError = ERROR_SUCCESS; BOOL fRet; LONG lth; BOOL fNoContent;
if (0 == pcmi->dwMsgType) { pcmi->aflStream |= ICMS_NONBARE; if (!ICMS_DecodePrefixContentInfo( pcmi, &pcmi->pooid, &pcmi->cEndNullPairs, &pcmi->aflOuter, &fNoContent)) goto DecodePrefixContentInfoError;
if (pcmi->aflOuter & ICMS_DECODED_CONTENTINFO_CONTENT) { // We cracked the whole header.
// Translate the contentType oid into a message type.
if (0 == (lth = ICM_ObjIdToIndex( pcmi->pooid))) goto UnknownContentTypeError; pcmi->dwMsgType = (DWORD)lth; PkiAsn1FreeDecoded(ICM_GetDecoder(), pcmi->pooid, ObjectIdentifierType_PDU); pcmi->pooid = NULL;
// Address case of no content
} }
switch (pcmi->dwMsgType) { case 0: if (fFinal) goto FinalWithoutMessageTypeError; break; case CMSG_DATA: if (!ICMS_DecodePrefixData( pcmi, fFinal)) goto DecodePrefixDataError; break; case CMSG_SIGNED: if (!ICMS_DecodePrefixSigned( pcmi, fFinal)) goto DecodePrefixSignedError; break; case CMSG_ENVELOPED: if (!ICMS_DecodePrefixEnveloped( pcmi, fFinal)) goto DecodePrefixEnvelopedError; break; case CMSG_HASHED: // if (!ICMS_DecodePrefixDigested( pcmi, fFinal))
// goto DecodePrefixDigestedError;
// break;
case CMSG_SIGNED_AND_ENVELOPED: case CMSG_ENCRYPTED: goto MessageTypeNotSupportedYet; default: goto InvalidMsgType; }
fRet = TRUE; CommonReturn: ICM_SetLastError(dwError); return fRet;
ErrorReturn: dwError = GetLastError(); fRet = FALSE; goto CommonReturn; SET_ERROR(FinalWithoutMessageTypeError,CRYPT_E_STREAM_INSUFFICIENT_DATA) TRACE_ERROR(UnknownContentTypeError) // error already set
TRACE_ERROR(DecodePrefixContentInfoError) // error already set
TRACE_ERROR(DecodePrefixDataError) // error already set
TRACE_ERROR(DecodePrefixSignedError) // error already set
TRACE_ERROR(DecodePrefixEnvelopedError) // error already set
SET_ERROR(MessageTypeNotSupportedYet,CRYPT_E_INVALID_MSG_TYPE) TRACE_ERROR(InvalidMsgType) // error already set
}
//+-------------------------------------------------------------------------
// Handle incremental data to be decoded (work done here).
//--------------------------------------------------------------------------
BOOL WINAPI ICMS_UpdateDecodingInner( IN PCRYPT_MSG_INFO pcmi, IN BOOL fFinal) { DWORD dwError = ERROR_SUCCESS; BOOL fRet;
if (0 == (pcmi->aflStream & ICMS_DECODED_PREFIX)) { if (!ICMS_DecodePrefix( pcmi, fFinal)) goto DecodePrefixError; } if (0 == (pcmi->aflStream & ICMS_DECODED_PREFIX)) goto SuccessReturn;
if (0 == (pcmi->aflStream & ICMS_DECODED_CONTENT)) { if (!ICMS_DecodeContent( pcmi, fFinal)) goto DecodeContentError; // NB- Do not trash err from callback!
} if (0 == (pcmi->aflStream & ICMS_DECODED_CONTENT)) goto SuccessReturn;
if (0 == (pcmi->aflStream & ICMS_DECODED_SUFFIX)) { if (!ICMS_DecodeSuffix( pcmi, fFinal)) goto DecodeSuffixError; } if (0 == (pcmi->aflStream & ICMS_DECODED_SUFFIX)) goto SuccessReturn;
SuccessReturn: fRet = TRUE; CommonReturn: ICM_SetLastError(dwError); return fRet;
ErrorReturn: dwError = GetLastError(); fRet = FALSE; goto CommonReturn; TRACE_ERROR(DecodePrefixError) // error already set
TRACE_ERROR(DecodeContentError) // error already set
TRACE_ERROR(DecodeSuffixError) // error already set
}
//+-------------------------------------------------------------------------
// Handle incremental data to be decoded.
//
// Note, the buffer to be decoded may have some of its tags modified.
// Therefore, we always need to copy to our own decode buffer.
//--------------------------------------------------------------------------
BOOL WINAPI ICMS_UpdateDecoding( IN PCRYPT_MSG_INFO pcmi, IN const BYTE *pbData, IN DWORD cbData, IN BOOL fFinal) { DWORD dwError = ERROR_SUCCESS; BOOL fRet;
pcmi->fStreamCallbackOutput = TRUE;
if (!ICMS_QueueToBuffer( &pcmi->bufDecode, (PBYTE)pbData, cbData)) goto QueueToBufferError;
if (!ICMS_UpdateDecodingInner( pcmi, fFinal)) goto UpdateDecodingInnerError;
if (fFinal) { if (pcmi->bufDecode.cbUsed > pcmi->bufDecode.cbDead) goto ExcessDataError; pcmi->aflStream |= ICMS_FINAL; }
fRet = TRUE; CommonReturn: ICM_SetLastError(dwError); return fRet;
ErrorReturn: dwError = GetLastError(); fRet = FALSE; goto CommonReturn; TRACE_ERROR(QueueToBufferError) // error already set
TRACE_ERROR(UpdateDecodingInnerError) // error already set
SET_ERROR(ExcessDataError, CRYPT_E_MSG_ERROR) }
#if 0
// When we fix the decoding of [0] Certificates and [1] Crls not to modify
// the encoded data we can replace the above with the following:
//+-------------------------------------------------------------------------
// Handle incremental data to be decoded.
//--------------------------------------------------------------------------
BOOL WINAPI ICMS_UpdateDecoding( IN PCRYPT_MSG_INFO pcmi, IN const BYTE *pbData, IN DWORD cbData, IN BOOL fFinal) { DWORD dwError = ERROR_SUCCESS; BOOL fRet; PICM_BUFFER pbuf = &pcmi->bufDecode; BOOL fNoCopy;
pcmi->fStreamCallbackOutput = TRUE; if (fFinal && NULL == pbuf->pbData) { // We're able to use the input buffer without copying
fNoCopy = TRUE; assert(0 == pbuf->cbSize && 0 == pbuf->cbUsed && 0 == pbuf->cbDead); pbuf->pbData = (PBYTE) pbData; pbuf->cbSize = cbData; pbuf->cbUsed = cbData; pbuf->cbDead = 0; } else { fNoCopy = FALSE; if (!ICMS_QueueToBuffer( pbuf, (PBYTE)pbData, cbData)) goto QueueToBufferError; }
if (!ICMS_UpdateDecodingInner( pcmi, fFinal)) goto UpdateDecodingInnerError;
if (fFinal) { if (pcmi->bufDecode.cbUsed > pcmi->bufDecode.cbDead) goto ExcessDataError; pcmi->aflStream |= ICMS_FINAL; }
fRet = TRUE; CommonReturn: if (fNoCopy) memset(pbuf, 0, sizeof(*pbuf)); ICM_SetLastError(dwError); return fRet;
ErrorReturn: dwError = GetLastError(); fRet = FALSE; goto CommonReturn; TRACE_ERROR(QueueToBufferError) // error already set
TRACE_ERROR(UpdateDecodingInnerError) // error already set
SET_ERROR(ExcessDataError, CRYPT_E_MSG_ERROR) }
#endif
|