📄 decode.c
字号:
/*
* Copyright 2005 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
*
* This file implements ASN.1 DER decoding of a limited set of types.
* It isn't a full ASN.1 implementation. Microsoft implements BER
* encoding of many of the basic types in msasn1.dll, but that interface is
* undocumented, so I implement them here.
*
* References:
* "A Layman's Guide to a Subset of ASN.1, BER, and DER", by Burton Kaliski
* (available online, look for a PDF copy as the HTML versions tend to have
* translation errors.)
*
* RFC3280, http://www.faqs.org/rfcs/rfc3280.html
*
* MSDN, especially:
* http://msdn.microsoft.com/library/en-us/seccrypto/security/constants_for_cryptencodeobject_and_cryptdecodeobject.asp
*/
#include <assert.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#define NONAMELESSUNION
#include "windef.h"
#include "winbase.h"
#include "excpt.h"
#include "wincrypt.h"
#include "winnls.h"
#include "winreg.h"
#include "snmp.h"
#include "wine/debug.h"
#include "wine/exception.h"
#include "crypt32_private.h"
/* This is a bit arbitrary, but to set some limit: */
#define MAX_ENCODED_LEN 0x02000000
#define ASN_FLAGS_MASK 0xe0
#define ASN_TYPE_MASK 0x1f
WINE_DEFAULT_DEBUG_CHANNEL(crypt);
struct GenericArray
{
DWORD cItems;
BYTE *rgItems;
};
typedef BOOL (WINAPI *CryptDecodeObjectFunc)(DWORD, LPCSTR, const BYTE *,
DWORD, DWORD, void *, DWORD *);
typedef BOOL (WINAPI *CryptDecodeObjectExFunc)(DWORD, LPCSTR, const BYTE *,
DWORD, DWORD, PCRYPT_DECODE_PARA, void *, DWORD *);
/* Prototypes for built-in decoders. They follow the Ex style prototypes.
* The dwCertEncodingType and lpszStructType are ignored by the built-in
* functions, but the parameters are retained to simplify CryptDecodeObjectEx,
* since it must call functions in external DLLs that follow these signatures.
*/
static BOOL WINAPI CRYPT_AsnDecodeChoiceOfTime(DWORD dwCertEncodingType,
LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo);
static BOOL WINAPI CRYPT_AsnDecodePubKeyInfoInternal(DWORD dwCertEncodingType,
LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo);
/* Like CRYPT_AsnDecodeExtensions, except assumes rgExtension is set ahead of
* time, doesn't do memory allocation, and doesn't do exception handling.
* (This isn't intended to be the externally-called one.)
*/
static BOOL WINAPI CRYPT_AsnDecodeExtensionsInternal(DWORD dwCertEncodingType,
LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo);
/* Assumes algo->Parameters.pbData is set ahead of time. Internal func. */
static BOOL WINAPI CRYPT_AsnDecodeAlgorithmId(DWORD dwCertEncodingType,
LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo);
/* Internal function */
static BOOL WINAPI CRYPT_AsnDecodeBool(DWORD dwCertEncodingType,
LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo);
/* Assumes the CRYPT_DATA_BLOB's pbData member has been initialized */
static BOOL WINAPI CRYPT_AsnDecodeOctetsInternal(DWORD dwCertEncodingType,
LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo);
/* Like CRYPT_AsnDecodeBits, but assumes the CRYPT_INTEGER_BLOB's pbData
* member has been initialized, doesn't do exception handling, and doesn't do
* memory allocation.
*/
static BOOL WINAPI CRYPT_AsnDecodeBitsInternal(DWORD dwCertEncodingType,
LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo);
static BOOL WINAPI CRYPT_AsnDecodeBits(DWORD dwCertEncodingType,
LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo);
static BOOL WINAPI CRYPT_AsnDecodeInt(DWORD dwCertEncodingType,
LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo);
/* Like CRYPT_AsnDecodeInteger, but assumes the CRYPT_INTEGER_BLOB's pbData
* member has been initialized, doesn't do exception handling, and doesn't do
* memory allocation. Also doesn't check tag, assumes the caller has checked
* it.
*/
static BOOL WINAPI CRYPT_AsnDecodeIntegerInternal(DWORD dwCertEncodingType,
LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo);
/* Like CRYPT_AsnDecodeInteger, but unsigned. */
static BOOL WINAPI CRYPT_AsnDecodeUnsignedIntegerInternal(
DWORD dwCertEncodingType, LPCSTR lpszStructType, const BYTE *pbEncoded,
DWORD cbEncoded, DWORD dwFlags, PCRYPT_DECODE_PARA pDecodePara,
void *pvStructInfo, DWORD *pcbStructInfo);
BOOL WINAPI CryptDecodeObject(DWORD dwCertEncodingType, LPCSTR lpszStructType,
const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags, void *pvStructInfo,
DWORD *pcbStructInfo)
{
static HCRYPTOIDFUNCSET set = NULL;
BOOL ret = FALSE;
CryptDecodeObjectFunc pCryptDecodeObject;
HCRYPTOIDFUNCADDR hFunc;
TRACE("(0x%08x, %s, %p, %d, 0x%08x, %p, %p)\n", dwCertEncodingType,
debugstr_a(lpszStructType), pbEncoded, cbEncoded, dwFlags,
pvStructInfo, pcbStructInfo);
if (!pvStructInfo && !pcbStructInfo)
{
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
/* Try registered DLL first.. */
if (!set)
set = CryptInitOIDFunctionSet(CRYPT_OID_DECODE_OBJECT_FUNC, 0);
CryptGetOIDFunctionAddress(set, dwCertEncodingType, lpszStructType, 0,
(void **)&pCryptDecodeObject, &hFunc);
if (pCryptDecodeObject)
{
ret = pCryptDecodeObject(dwCertEncodingType, lpszStructType,
pbEncoded, cbEncoded, dwFlags, pvStructInfo, pcbStructInfo);
CryptFreeOIDFunctionAddress(hFunc, 0);
}
else
{
/* If not, use CryptDecodeObjectEx */
ret = CryptDecodeObjectEx(dwCertEncodingType, lpszStructType, pbEncoded,
cbEncoded, dwFlags, NULL, pvStructInfo, pcbStructInfo);
}
return ret;
}
/* Gets the number of length bytes from the given (leading) length byte */
#define GET_LEN_BYTES(b) ((b) <= 0x7f ? 1 : 1 + ((b) & 0x7f))
/* Helper function to get the encoded length of the data starting at pbEncoded,
* where pbEncoded[0] is the tag. If the data are too short to contain a
* length or if the length is too large for cbEncoded, sets an appropriate
* error code and returns FALSE.
*/
static BOOL WINAPI CRYPT_GetLen(const BYTE *pbEncoded, DWORD cbEncoded,
DWORD *len)
{
BOOL ret;
if (cbEncoded <= 1)
{
SetLastError(CRYPT_E_ASN1_CORRUPT);
ret = FALSE;
}
else if (pbEncoded[1] <= 0x7f)
{
if (pbEncoded[1] + 1 > cbEncoded)
{
SetLastError(CRYPT_E_ASN1_EOD);
ret = FALSE;
}
else
{
*len = pbEncoded[1];
ret = TRUE;
}
}
else
{
BYTE lenLen = GET_LEN_BYTES(pbEncoded[1]);
if (lenLen > sizeof(DWORD) + 1)
{
SetLastError(CRYPT_E_ASN1_LARGE);
ret = FALSE;
}
else if (lenLen + 2 > cbEncoded)
{
SetLastError(CRYPT_E_ASN1_CORRUPT);
ret = FALSE;
}
else
{
DWORD out = 0;
pbEncoded += 2;
while (--lenLen)
{
out <<= 8;
out |= *pbEncoded++;
}
if (out + lenLen + 1 > cbEncoded)
{
SetLastError(CRYPT_E_ASN1_EOD);
ret = FALSE;
}
else
{
*len = out;
ret = TRUE;
}
}
}
return ret;
}
/* Helper function to check *pcbStructInfo, set it to the required size, and
* optionally to allocate memory. Assumes pvStructInfo is not NULL.
* If CRYPT_DECODE_ALLOC_FLAG is set in dwFlags, *pvStructInfo will be set to a
* pointer to the newly allocated memory.
*/
static BOOL CRYPT_DecodeEnsureSpace(DWORD dwFlags,
PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo,
DWORD bytesNeeded)
{
BOOL ret = TRUE;
if (dwFlags & CRYPT_DECODE_ALLOC_FLAG)
{
if (pDecodePara && pDecodePara->pfnAlloc)
*(BYTE **)pvStructInfo = pDecodePara->pfnAlloc(bytesNeeded);
else
*(BYTE **)pvStructInfo = LocalAlloc(0, bytesNeeded);
if (!*(BYTE **)pvStructInfo)
ret = FALSE;
else
*pcbStructInfo = bytesNeeded;
}
else if (*pcbStructInfo < bytesNeeded)
{
*pcbStructInfo = bytesNeeded;
SetLastError(ERROR_MORE_DATA);
ret = FALSE;
}
return ret;
}
/* tag:
* The expected tag of the item. If tag is 0, decodeFunc is called
* regardless of the tag value seen.
* offset:
* A sequence is decoded into a struct. The offset member is the
* offset of this item within that struct.
* decodeFunc:
* The decoder function to use. If this is NULL, then the member isn't
* decoded, but minSize space is reserved for it.
* minSize:
* The minimum amount of space occupied after decoding. You must set this.
* optional:
* If true, and the tag doesn't match the expected tag for this item,
* or the decodeFunc fails with CRYPT_E_ASN1_BADTAG, then minSize space is
* filled with 0 for this member.
* hasPointer, pointerOffset, minSize:
* If the item has dynamic data, set hasPointer to TRUE, pointerOffset to
* the offset within the (outer) struct of the data pointer (or to the
* first data pointer, if more than one exist).
* size:
* Used by CRYPT_AsnDecodeSequence, not for your use.
*/
struct AsnDecodeSequenceItem
{
BYTE tag;
DWORD offset;
CryptDecodeObjectExFunc decodeFunc;
DWORD minSize;
BOOL optional;
BOOL hasPointer;
DWORD pointerOffset;
DWORD size;
};
static BOOL CRYPT_AsnDecodeSequenceItems(DWORD dwCertEncodingType,
struct AsnDecodeSequenceItem items[], DWORD cItem, const BYTE *pbEncoded,
DWORD cbEncoded, DWORD dwFlags, void *pvStructInfo, BYTE *nextData)
{
BOOL ret;
DWORD i;
const BYTE *ptr;
ptr = pbEncoded + 1 + GET_LEN_BYTES(pbEncoded[1]);
for (i = 0, ret = TRUE; ret && i < cItem; i++)
{
if (cbEncoded - (ptr - pbEncoded) != 0)
{
DWORD nextItemLen;
if ((ret = CRYPT_GetLen(ptr, cbEncoded - (ptr - pbEncoded),
&nextItemLen)))
{
BYTE nextItemLenBytes = GET_LEN_BYTES(ptr[1]);
if (ptr[0] == items[i].tag || !items[i].tag)
{
if (nextData && pvStructInfo && items[i].hasPointer)
{
TRACE("Setting next pointer to %p\n",
nextData);
*(BYTE **)((BYTE *)pvStructInfo +
items[i].pointerOffset) = nextData;
}
if (items[i].decodeFunc)
{
if (pvStructInfo)
TRACE("decoding item %d\n", i);
else
TRACE("sizing item %d\n", i);
ret = items[i].decodeFunc(dwCertEncodingType,
NULL, ptr, 1 + nextItemLenBytes + nextItemLen,
dwFlags & ~CRYPT_DECODE_ALLOC_FLAG, NULL,
pvStructInfo ? (BYTE *)pvStructInfo + items[i].offset
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -