/* * Copyright 2007 Juan Lang * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ #include #include "windef.h" #include "winbase.h" #include "wincrypt.h" #include "wine/debug.h" WINE_DEFAULT_DEBUG_CHANNEL(crypt); /* Called when a message's ref count reaches zero. Free any message-specific * data here. */ typedef void (*CryptMsgCloseFunc)(HCRYPTMSG msg); typedef BOOL (*CryptMsgGetParamFunc)(HCRYPTMSG hCryptMsg, DWORD dwParamType, DWORD dwIndex, void *pvData, DWORD *pcbData); typedef BOOL (*CryptMsgUpdateFunc)(HCRYPTMSG hCryptMsg, const BYTE *pbData, DWORD cbData, BOOL fFinal); typedef struct _CryptMsgBase { LONG ref; DWORD open_flags; PCMSG_STREAM_INFO stream_info; BOOL finalized; CryptMsgCloseFunc close; CryptMsgUpdateFunc update; CryptMsgGetParamFunc get_param; } CryptMsgBase; static inline void CryptMsgBase_Init(CryptMsgBase *msg, DWORD dwFlags, PCMSG_STREAM_INFO pStreamInfo) { msg->ref = 1; msg->open_flags = dwFlags; msg->stream_info = pStreamInfo; msg->finalized = FALSE; } typedef struct _CDataEncodeMsg { CryptMsgBase base; DWORD bare_content_len; LPBYTE bare_content; } CDataEncodeMsg; static const BYTE empty_data_content[] = { 0x04,0x00 }; static void CDataEncodeMsg_Close(HCRYPTMSG hCryptMsg) { CDataEncodeMsg *msg = (CDataEncodeMsg *)hCryptMsg; if (msg->bare_content != empty_data_content) LocalFree(msg->bare_content); } static BOOL CDataEncodeMsg_Update(HCRYPTMSG hCryptMsg, const BYTE *pbData, DWORD cbData, BOOL fFinal) { CDataEncodeMsg *msg = (CDataEncodeMsg *)hCryptMsg; BOOL ret = FALSE; if (msg->base.finalized) SetLastError(CRYPT_E_MSG_ERROR); else if (!fFinal) { if (msg->base.open_flags & CMSG_DETACHED_FLAG) SetLastError(E_INVALIDARG); else SetLastError(CRYPT_E_MSG_ERROR); } else { msg->base.finalized = TRUE; if (!cbData) SetLastError(E_INVALIDARG); else { CRYPT_DATA_BLOB blob = { cbData, (LPBYTE)pbData }; /* data messages don't allow non-final updates, don't bother * checking whether data already exist, they can't. */ ret = CryptEncodeObjectEx(X509_ASN_ENCODING, X509_OCTET_STRING, &blob, CRYPT_ENCODE_ALLOC_FLAG, NULL, &msg->bare_content, &msg->bare_content_len); if (ret && msg->base.stream_info) FIXME("stream info unimplemented\n"); } } return ret; } static BOOL CDataEncodeMsg_GetParam(HCRYPTMSG hCryptMsg, DWORD dwParamType, DWORD dwIndex, void *pvData, DWORD *pcbData) { CDataEncodeMsg *msg = (CDataEncodeMsg *)hCryptMsg; BOOL ret = FALSE; switch (dwParamType) { case CMSG_CONTENT_PARAM: { CRYPT_CONTENT_INFO info; char rsa_data[] = "1.2.840.113549.1.7.1"; info.pszObjId = rsa_data; info.Content.cbData = msg->bare_content_len; info.Content.pbData = msg->bare_content; ret = CryptEncodeObject(X509_ASN_ENCODING, PKCS_CONTENT_INFO, &info, pvData, pcbData); break; } case CMSG_BARE_CONTENT_PARAM: if (!pvData) { *pcbData = msg->bare_content_len; ret = TRUE; } else if (*pcbData < msg->bare_content_len) { *pcbData = msg->bare_content_len; SetLastError(ERROR_MORE_DATA); } else { *pcbData = msg->bare_content_len; memcpy(pvData, msg->bare_content, msg->bare_content_len); ret = TRUE; } break; default: SetLastError(CRYPT_E_INVALID_MSG_TYPE); } return ret; } static HCRYPTMSG CDataEncodeMsg_Open(DWORD dwFlags, const void *pvMsgEncodeInfo, LPSTR pszInnerContentObjID, PCMSG_STREAM_INFO pStreamInfo) { CDataEncodeMsg *msg; if (pvMsgEncodeInfo) { SetLastError(E_INVALIDARG); return NULL; } msg = CryptMemAlloc(sizeof(CDataEncodeMsg)); if (msg) { CryptMsgBase_Init((CryptMsgBase *)msg, dwFlags, pStreamInfo); msg->base.close = CDataEncodeMsg_Close; msg->base.update = CDataEncodeMsg_Update; msg->base.get_param = CDataEncodeMsg_GetParam; msg->bare_content_len = sizeof(empty_data_content); msg->bare_content = (LPBYTE)empty_data_content; } return (HCRYPTMSG)msg; } static inline const char *MSG_TYPE_STR(DWORD type) { switch (type) { #define _x(x) case (x): return #x _x(CMSG_DATA); _x(CMSG_SIGNED); _x(CMSG_ENVELOPED); _x(CMSG_SIGNED_AND_ENVELOPED); _x(CMSG_HASHED); _x(CMSG_ENCRYPTED); #undef _x default: return wine_dbg_sprintf("unknown (%d)", type); } } HCRYPTMSG WINAPI CryptMsgOpenToEncode(DWORD dwMsgEncodingType, DWORD dwFlags, DWORD dwMsgType, const void *pvMsgEncodeInfo, LPSTR pszInnerContentObjID, PCMSG_STREAM_INFO pStreamInfo) { HCRYPTMSG msg = NULL; TRACE("(%08x, %08x, %08x, %p, %s, %p)\n", dwMsgEncodingType, dwFlags, dwMsgType, pvMsgEncodeInfo, debugstr_a(pszInnerContentObjID), pStreamInfo); if (GET_CMSG_ENCODING_TYPE(dwMsgEncodingType) != PKCS_7_ASN_ENCODING) { SetLastError(E_INVALIDARG); return NULL; } switch (dwMsgType) { case CMSG_DATA: msg = CDataEncodeMsg_Open(dwFlags, pvMsgEncodeInfo, pszInnerContentObjID, pStreamInfo); break; case CMSG_SIGNED: case CMSG_ENVELOPED: case CMSG_HASHED: FIXME("unimplemented for type %s\n", MSG_TYPE_STR(dwMsgType)); break; case CMSG_SIGNED_AND_ENVELOPED: case CMSG_ENCRYPTED: /* defined but invalid, fall through */ default: SetLastError(CRYPT_E_INVALID_MSG_TYPE); } return msg; } HCRYPTMSG WINAPI CryptMsgOpenToDecode(DWORD dwMsgEncodingType, DWORD dwFlags, DWORD dwMsgType, HCRYPTPROV hCryptProv, PCERT_INFO pRecipientInfo, PCMSG_STREAM_INFO pStreamInfo) { FIXME("(%08x, %08x, %08x, %08lx, %p, %p): stub\n", dwMsgEncodingType, dwFlags, dwMsgType, hCryptProv, pRecipientInfo, pStreamInfo); if (GET_CMSG_ENCODING_TYPE(dwMsgEncodingType) != PKCS_7_ASN_ENCODING) { SetLastError(E_INVALIDARG); return NULL; } return NULL; } HCRYPTMSG WINAPI CryptMsgDuplicate(HCRYPTMSG hCryptMsg) { TRACE("(%p)\n", hCryptMsg); if (hCryptMsg) { CryptMsgBase *msg = (CryptMsgBase *)hCryptMsg; InterlockedIncrement(&msg->ref); } return hCryptMsg; } BOOL WINAPI CryptMsgClose(HCRYPTMSG hCryptMsg) { TRACE("(%p)\n", hCryptMsg); if (hCryptMsg) { CryptMsgBase *msg = (CryptMsgBase *)hCryptMsg; if (InterlockedDecrement(&msg->ref) == 0) { TRACE("freeing %p\n", msg); if (msg->close) msg->close(msg); CryptMemFree(msg); } } return TRUE; } BOOL WINAPI CryptMsgUpdate(HCRYPTMSG hCryptMsg, const BYTE *pbData, DWORD cbData, BOOL fFinal) { CryptMsgBase *msg = (CryptMsgBase *)hCryptMsg; BOOL ret = FALSE; TRACE("(%p, %p, %d, %d)\n", hCryptMsg, pbData, cbData, fFinal); if (msg && msg->update) ret = msg->update(hCryptMsg, pbData, cbData, fFinal); return ret; } BOOL WINAPI CryptMsgGetParam(HCRYPTMSG hCryptMsg, DWORD dwParamType, DWORD dwIndex, void *pvData, DWORD *pcbData) { CryptMsgBase *msg = (CryptMsgBase *)hCryptMsg; BOOL ret = FALSE; TRACE("(%p, %d, %d, %p, %p)\n", hCryptMsg, dwParamType, dwIndex, pvData, pcbData); if (msg && msg->get_param) ret = msg->get_param(hCryptMsg, dwParamType, dwIndex, pvData, pcbData); return ret; }