crypt32: Implement X509_UNICODE_NAME_VALUE encoding/decoding.
This commit is contained in:
parent
0042c010af
commit
ead21189c2
|
@ -23,13 +23,20 @@
|
|||
#define ASN_BOOL (ASN_UNIVERSAL | ASN_PRIMITIVE | 0x01)
|
||||
#define ASN_BITSTRING (ASN_UNIVERSAL | ASN_PRIMITIVE | 0x03)
|
||||
#define ASN_ENUMERATED (ASN_UNIVERSAL | ASN_PRIMITIVE | 0x0a)
|
||||
#define ASN_UTF8STRING (ASN_UNIVERSAL | ASN_PRIMITIVE | 0x0c)
|
||||
#define ASN_SETOF (ASN_UNIVERSAL | ASN_PRIMITIVE | 0x11)
|
||||
#define ASN_NUMERICSTRING (ASN_UNIVERSAL | ASN_PRIMITIVE | 0x12)
|
||||
#define ASN_PRINTABLESTRING (ASN_UNIVERSAL | ASN_PRIMITIVE | 0x13)
|
||||
#define ASN_T61STRING (ASN_UNIVERSAL | ASN_PRIMITIVE | 0x14)
|
||||
#define ASN_VIDEOTEXSTRING (ASN_UNIVERSAL | ASN_PRIMITIVE | 0x15)
|
||||
#define ASN_IA5STRING (ASN_UNIVERSAL | ASN_PRIMITIVE | 0x16)
|
||||
#define ASN_UTCTIME (ASN_UNIVERSAL | ASN_PRIMITIVE | 0x17)
|
||||
#define ASN_GENERALTIME (ASN_UNIVERSAL | ASN_PRIMITIVE | 0x18)
|
||||
#define ASN_GRAPHICSTRING (ASN_UNIVERSAL | ASN_PRIMITIVE | 0x19)
|
||||
#define ASN_VISIBLESTRING (ASN_UNIVERSAL | ASN_PRIMITIVE | 0x1a)
|
||||
#define ASN_GENERALSTRING (ASN_UNIVERSAL | ASN_PRIMITIVE | 0x1b)
|
||||
#define ASN_UNIVERSALSTRING (ASN_UNIVERSAL | ASN_PRIMITIVE | 0x1c)
|
||||
#define ASN_BMPSTRING (ASN_UNIVERSAL | ASN_PRIMITIVE | 0x1e)
|
||||
|
||||
/* The following aren't defined in wincrypt.h, as they're "reserved" */
|
||||
#define CERT_CERT_PROP_ID 32
|
||||
|
|
|
@ -42,6 +42,7 @@
|
|||
#include "winbase.h"
|
||||
#include "excpt.h"
|
||||
#include "wincrypt.h"
|
||||
#include "winnls.h"
|
||||
#include "winreg.h"
|
||||
#include "snmp.h"
|
||||
#include "wine/debug.h"
|
||||
|
@ -1395,6 +1396,179 @@ static BOOL WINAPI CRYPT_AsnDecodeNameValue(DWORD dwCertEncodingType,
|
|||
return ret;
|
||||
}
|
||||
|
||||
static BOOL WINAPI CRYPT_AsnDecodeUnicodeNameValueInternal(
|
||||
DWORD dwCertEncodingType, LPCSTR lpszStructType, const BYTE *pbEncoded,
|
||||
DWORD cbEncoded, DWORD dwFlags, PCRYPT_DECODE_PARA pDecodePara,
|
||||
void *pvStructInfo, DWORD *pcbStructInfo)
|
||||
{
|
||||
BOOL ret = TRUE;
|
||||
DWORD dataLen;
|
||||
CERT_NAME_VALUE *value = (CERT_NAME_VALUE *)pvStructInfo;
|
||||
|
||||
if ((ret = CRYPT_GetLen(pbEncoded, cbEncoded, &dataLen)))
|
||||
{
|
||||
BYTE lenBytes = GET_LEN_BYTES(pbEncoded[1]);
|
||||
DWORD bytesNeeded = sizeof(CERT_NAME_VALUE), valueType;
|
||||
|
||||
switch (pbEncoded[0])
|
||||
{
|
||||
case ASN_NUMERICSTRING:
|
||||
valueType = CERT_RDN_NUMERIC_STRING;
|
||||
bytesNeeded += (dataLen + 1) * 2;
|
||||
break;
|
||||
case ASN_PRINTABLESTRING:
|
||||
valueType = CERT_RDN_PRINTABLE_STRING;
|
||||
bytesNeeded += (dataLen + 1) * 2;
|
||||
break;
|
||||
case ASN_IA5STRING:
|
||||
valueType = CERT_RDN_IA5_STRING;
|
||||
bytesNeeded += (dataLen + 1) * 2;
|
||||
break;
|
||||
case ASN_T61STRING:
|
||||
valueType = CERT_RDN_T61_STRING;
|
||||
bytesNeeded += (dataLen + 1) * 2;
|
||||
break;
|
||||
case ASN_VIDEOTEXSTRING:
|
||||
valueType = CERT_RDN_VIDEOTEX_STRING;
|
||||
bytesNeeded += (dataLen + 1) * 2;
|
||||
break;
|
||||
case ASN_GRAPHICSTRING:
|
||||
valueType = CERT_RDN_GRAPHIC_STRING;
|
||||
bytesNeeded += (dataLen + 1) * 2;
|
||||
break;
|
||||
case ASN_VISIBLESTRING:
|
||||
valueType = CERT_RDN_VISIBLE_STRING;
|
||||
bytesNeeded += (dataLen + 1) * 2;
|
||||
break;
|
||||
case ASN_GENERALSTRING:
|
||||
valueType = CERT_RDN_GENERAL_STRING;
|
||||
bytesNeeded += (dataLen + 1) * 2;
|
||||
break;
|
||||
case ASN_UNIVERSALSTRING:
|
||||
valueType = CERT_RDN_UNIVERSAL_STRING;
|
||||
bytesNeeded += dataLen / 2 + 2;
|
||||
break;
|
||||
case ASN_BMPSTRING:
|
||||
valueType = CERT_RDN_BMP_STRING;
|
||||
bytesNeeded += dataLen + 2;
|
||||
break;
|
||||
case ASN_UTF8STRING:
|
||||
valueType = CERT_RDN_UTF8_STRING;
|
||||
bytesNeeded += MultiByteToWideChar(CP_UTF8, 0,
|
||||
(LPSTR)pbEncoded + 1 + lenBytes, dataLen, NULL, 0) * 2 + 2;
|
||||
break;
|
||||
default:
|
||||
SetLastError(CRYPT_E_ASN1_BADTAG);
|
||||
ret = FALSE;
|
||||
}
|
||||
if (ret)
|
||||
{
|
||||
if (!value)
|
||||
*pcbStructInfo = bytesNeeded;
|
||||
else if (*pcbStructInfo < bytesNeeded)
|
||||
{
|
||||
*pcbStructInfo = bytesNeeded;
|
||||
SetLastError(ERROR_MORE_DATA);
|
||||
ret = FALSE;
|
||||
}
|
||||
else
|
||||
{
|
||||
*pcbStructInfo = bytesNeeded;
|
||||
value->dwValueType = valueType;
|
||||
if (dataLen)
|
||||
{
|
||||
DWORD i;
|
||||
LPWSTR str = (LPWSTR)value->Value.pbData;
|
||||
|
||||
assert(value->Value.pbData);
|
||||
switch (pbEncoded[0])
|
||||
{
|
||||
case ASN_NUMERICSTRING:
|
||||
case ASN_PRINTABLESTRING:
|
||||
case ASN_IA5STRING:
|
||||
case ASN_T61STRING:
|
||||
case ASN_VIDEOTEXSTRING:
|
||||
case ASN_GRAPHICSTRING:
|
||||
case ASN_VISIBLESTRING:
|
||||
case ASN_GENERALSTRING:
|
||||
value->Value.cbData = dataLen * 2 + 2;
|
||||
for (i = 0; i < dataLen; i++)
|
||||
str[i] = pbEncoded[1 + lenBytes + i];
|
||||
str[i] = 0;
|
||||
break;
|
||||
case ASN_UNIVERSALSTRING:
|
||||
value->Value.cbData = dataLen / 2 + 2;
|
||||
for (i = 0; i < dataLen / 4; i++)
|
||||
str[i] = (pbEncoded[1 + lenBytes + 2 * i + 2] << 8)
|
||||
| pbEncoded[1 + lenBytes + 2 * i + 3];
|
||||
str[i] = 0;
|
||||
break;
|
||||
case ASN_BMPSTRING:
|
||||
value->Value.cbData = dataLen + 2;
|
||||
for (i = 0; i < dataLen / 2; i++)
|
||||
str[i] = (pbEncoded[1 + lenBytes + 2 * i] << 8) |
|
||||
pbEncoded[1 + lenBytes + 2 * i + 1];
|
||||
str[i] = 0;
|
||||
break;
|
||||
case ASN_UTF8STRING:
|
||||
value->Value.cbData = MultiByteToWideChar(CP_UTF8, 0,
|
||||
(LPSTR)pbEncoded + 1 + lenBytes, dataLen,
|
||||
str, bytesNeeded - sizeof(CERT_NAME_VALUE)) * 2;
|
||||
str[value->Value.cbData / 2] = 0;
|
||||
value->Value.cbData += 2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
value->Value.cbData = 0;
|
||||
value->Value.pbData = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static BOOL WINAPI CRYPT_AsnDecodeUnicodeNameValue(DWORD dwCertEncodingType,
|
||||
LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
|
||||
PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo)
|
||||
{
|
||||
BOOL ret = TRUE;
|
||||
|
||||
__TRY
|
||||
{
|
||||
ret = CRYPT_AsnDecodeUnicodeNameValueInternal(dwCertEncodingType,
|
||||
lpszStructType, pbEncoded, cbEncoded,
|
||||
dwFlags & ~CRYPT_DECODE_ALLOC_FLAG, NULL, NULL, pcbStructInfo);
|
||||
if (ret && pvStructInfo)
|
||||
{
|
||||
ret = CRYPT_DecodeEnsureSpace(dwFlags, pDecodePara, pvStructInfo,
|
||||
pcbStructInfo, *pcbStructInfo);
|
||||
if (ret)
|
||||
{
|
||||
CERT_NAME_VALUE *value;
|
||||
|
||||
if (dwFlags & CRYPT_DECODE_ALLOC_FLAG)
|
||||
pvStructInfo = *(BYTE **)pvStructInfo;
|
||||
value = (CERT_NAME_VALUE *)pvStructInfo;
|
||||
value->Value.pbData = ((BYTE *)value + sizeof(CERT_NAME_VALUE));
|
||||
ret = CRYPT_AsnDecodeUnicodeNameValueInternal(
|
||||
dwCertEncodingType, lpszStructType, pbEncoded, cbEncoded,
|
||||
dwFlags & ~CRYPT_DECODE_ALLOC_FLAG, NULL, pvStructInfo,
|
||||
pcbStructInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
__EXCEPT_PAGE_FAULT
|
||||
{
|
||||
SetLastError(STATUS_ACCESS_VIOLATION);
|
||||
ret = FALSE;
|
||||
}
|
||||
__ENDTRY
|
||||
return ret;
|
||||
}
|
||||
|
||||
static BOOL WINAPI CRYPT_AsnDecodeRdnAttr(DWORD dwCertEncodingType,
|
||||
LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
|
||||
PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo)
|
||||
|
@ -3190,6 +3364,9 @@ BOOL WINAPI CryptDecodeObjectEx(DWORD dwCertEncodingType, LPCSTR lpszStructType,
|
|||
case (WORD)RSA_CSP_PUBLICKEYBLOB:
|
||||
decodeFunc = CRYPT_AsnDecodeRsaPubKey;
|
||||
break;
|
||||
case (WORD)X509_UNICODE_NAME_VALUE:
|
||||
decodeFunc = CRYPT_AsnDecodeUnicodeNameValue;
|
||||
break;
|
||||
case (WORD)X509_OCTET_STRING:
|
||||
decodeFunc = CRYPT_AsnDecodeOctets;
|
||||
break;
|
||||
|
|
|
@ -46,6 +46,7 @@
|
|||
#include "snmp.h"
|
||||
#include "wine/debug.h"
|
||||
#include "wine/exception.h"
|
||||
#include "wine/unicode.h"
|
||||
#include "crypt32_private.h"
|
||||
|
||||
WINE_DEFAULT_DEBUG_CHANNEL(crypt);
|
||||
|
@ -989,6 +990,353 @@ static BOOL WINAPI CRYPT_AsnEncodeNameValue(DWORD dwCertEncodingType,
|
|||
return ret;
|
||||
}
|
||||
|
||||
static BOOL CRYPT_AsnEncodeUnicodeStringCoerce(const CERT_NAME_VALUE *value,
|
||||
BYTE tag, DWORD dwFlags, PCRYPT_ENCODE_PARA pEncodePara, BYTE *pbEncoded,
|
||||
DWORD *pcbEncoded)
|
||||
{
|
||||
BOOL ret = TRUE;
|
||||
LPCWSTR str = (LPCWSTR)value->Value.pbData;
|
||||
DWORD bytesNeeded, lenBytes, encodedLen;
|
||||
|
||||
encodedLen = value->Value.cbData ? value->Value.cbData / sizeof(WCHAR) :
|
||||
lstrlenW(str);
|
||||
CRYPT_EncodeLen(encodedLen, NULL, &lenBytes);
|
||||
bytesNeeded = 1 + lenBytes + encodedLen;
|
||||
if (!pbEncoded)
|
||||
*pcbEncoded = bytesNeeded;
|
||||
else
|
||||
{
|
||||
if ((ret = CRYPT_EncodeEnsureSpace(dwFlags, pEncodePara,
|
||||
pbEncoded, pcbEncoded, bytesNeeded)))
|
||||
{
|
||||
DWORD i;
|
||||
|
||||
if (dwFlags & CRYPT_ENCODE_ALLOC_FLAG)
|
||||
pbEncoded = *(BYTE **)pbEncoded;
|
||||
*pbEncoded++ = tag;
|
||||
CRYPT_EncodeLen(encodedLen, pbEncoded, &lenBytes);
|
||||
pbEncoded += lenBytes;
|
||||
for (i = 0; i < encodedLen; i++)
|
||||
*pbEncoded++ = (BYTE)str[i];
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static BOOL CRYPT_AsnEncodeNumericString(const CERT_NAME_VALUE *value,
|
||||
DWORD dwFlags, PCRYPT_ENCODE_PARA pEncodePara, BYTE *pbEncoded,
|
||||
DWORD *pcbEncoded)
|
||||
{
|
||||
BOOL ret = TRUE;
|
||||
LPCWSTR str = (LPCWSTR)value->Value.pbData;
|
||||
DWORD bytesNeeded, lenBytes, encodedLen;
|
||||
|
||||
encodedLen = value->Value.cbData ? value->Value.cbData / sizeof(WCHAR) :
|
||||
lstrlenW(str);
|
||||
CRYPT_EncodeLen(encodedLen, NULL, &lenBytes);
|
||||
bytesNeeded = 1 + lenBytes + encodedLen;
|
||||
if (!pbEncoded)
|
||||
*pcbEncoded = bytesNeeded;
|
||||
else
|
||||
{
|
||||
if ((ret = CRYPT_EncodeEnsureSpace(dwFlags, pEncodePara,
|
||||
pbEncoded, pcbEncoded, bytesNeeded)))
|
||||
{
|
||||
DWORD i;
|
||||
|
||||
if (dwFlags & CRYPT_ENCODE_ALLOC_FLAG)
|
||||
pbEncoded = *(BYTE **)pbEncoded;
|
||||
*pbEncoded++ = ASN_NUMERICSTRING;
|
||||
CRYPT_EncodeLen(encodedLen, pbEncoded, &lenBytes);
|
||||
pbEncoded += lenBytes;
|
||||
for (i = 0; ret && i < encodedLen; i++)
|
||||
{
|
||||
if (isdigitW(str[i]))
|
||||
*pbEncoded++ = (BYTE)str[i];
|
||||
else
|
||||
{
|
||||
*pcbEncoded = i;
|
||||
SetLastError(CRYPT_E_INVALID_NUMERIC_STRING);
|
||||
ret = FALSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int isprintableW(WCHAR wc)
|
||||
{
|
||||
return isalnumW(wc) || isspaceW(wc) || wc == '\'' || wc == '(' ||
|
||||
wc == ')' || wc == '+' || wc == ',' || wc == '-' || wc == '.' ||
|
||||
wc == '/' || wc == ':' || wc == '=' || wc == '?';
|
||||
}
|
||||
|
||||
static BOOL CRYPT_AsnEncodePrintableString(const CERT_NAME_VALUE *value,
|
||||
DWORD dwFlags, PCRYPT_ENCODE_PARA pEncodePara, BYTE *pbEncoded,
|
||||
DWORD *pcbEncoded)
|
||||
{
|
||||
BOOL ret = TRUE;
|
||||
LPCWSTR str = (LPCWSTR)value->Value.pbData;
|
||||
DWORD bytesNeeded, lenBytes, encodedLen;
|
||||
|
||||
encodedLen = value->Value.cbData ? value->Value.cbData / sizeof(WCHAR) :
|
||||
lstrlenW(str);
|
||||
CRYPT_EncodeLen(encodedLen, NULL, &lenBytes);
|
||||
bytesNeeded = 1 + lenBytes + encodedLen;
|
||||
if (!pbEncoded)
|
||||
*pcbEncoded = bytesNeeded;
|
||||
else
|
||||
{
|
||||
if ((ret = CRYPT_EncodeEnsureSpace(dwFlags, pEncodePara,
|
||||
pbEncoded, pcbEncoded, bytesNeeded)))
|
||||
{
|
||||
DWORD i;
|
||||
|
||||
if (dwFlags & CRYPT_ENCODE_ALLOC_FLAG)
|
||||
pbEncoded = *(BYTE **)pbEncoded;
|
||||
*pbEncoded++ = ASN_PRINTABLESTRING;
|
||||
CRYPT_EncodeLen(encodedLen, pbEncoded, &lenBytes);
|
||||
pbEncoded += lenBytes;
|
||||
for (i = 0; ret && i < encodedLen; i++)
|
||||
{
|
||||
if (isprintableW(str[i]))
|
||||
*pbEncoded++ = (BYTE)str[i];
|
||||
else
|
||||
{
|
||||
*pcbEncoded = i;
|
||||
SetLastError(CRYPT_E_INVALID_PRINTABLE_STRING);
|
||||
ret = FALSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static BOOL CRYPT_AsnEncodeIA5String(const CERT_NAME_VALUE *value,
|
||||
DWORD dwFlags, PCRYPT_ENCODE_PARA pEncodePara, BYTE *pbEncoded,
|
||||
DWORD *pcbEncoded)
|
||||
{
|
||||
BOOL ret = TRUE;
|
||||
LPCWSTR str = (LPCWSTR)value->Value.pbData;
|
||||
DWORD bytesNeeded, lenBytes, encodedLen;
|
||||
|
||||
encodedLen = value->Value.cbData ? value->Value.cbData / sizeof(WCHAR) :
|
||||
lstrlenW(str);
|
||||
CRYPT_EncodeLen(encodedLen, NULL, &lenBytes);
|
||||
bytesNeeded = 1 + lenBytes + encodedLen;
|
||||
if (!pbEncoded)
|
||||
*pcbEncoded = bytesNeeded;
|
||||
else
|
||||
{
|
||||
if ((ret = CRYPT_EncodeEnsureSpace(dwFlags, pEncodePara,
|
||||
pbEncoded, pcbEncoded, bytesNeeded)))
|
||||
{
|
||||
DWORD i;
|
||||
|
||||
if (dwFlags & CRYPT_ENCODE_ALLOC_FLAG)
|
||||
pbEncoded = *(BYTE **)pbEncoded;
|
||||
*pbEncoded++ = ASN_IA5STRING;
|
||||
CRYPT_EncodeLen(encodedLen, pbEncoded, &lenBytes);
|
||||
pbEncoded += lenBytes;
|
||||
for (i = 0; ret && i < encodedLen; i++)
|
||||
{
|
||||
if (str[i] <= 0x7f)
|
||||
*pbEncoded++ = (BYTE)str[i];
|
||||
else
|
||||
{
|
||||
*pcbEncoded = i;
|
||||
SetLastError(CRYPT_E_INVALID_IA5_STRING);
|
||||
ret = FALSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static BOOL CRYPT_AsnEncodeUTF8String(const CERT_NAME_VALUE *value,
|
||||
DWORD dwFlags, PCRYPT_ENCODE_PARA pEncodePara, BYTE *pbEncoded,
|
||||
DWORD *pcbEncoded)
|
||||
{
|
||||
BOOL ret = TRUE;
|
||||
LPCWSTR str = (LPCWSTR)value->Value.pbData;
|
||||
DWORD bytesNeeded, lenBytes, encodedLen, strLen;
|
||||
|
||||
strLen = value->Value.cbData ? value->Value.cbData / sizeof(WCHAR) :
|
||||
lstrlenW(str);
|
||||
encodedLen = WideCharToMultiByte(CP_UTF8, 0, str, strLen, NULL, 0, NULL,
|
||||
NULL);
|
||||
CRYPT_EncodeLen(encodedLen, NULL, &lenBytes);
|
||||
bytesNeeded = 1 + lenBytes + encodedLen;
|
||||
if (!pbEncoded)
|
||||
*pcbEncoded = bytesNeeded;
|
||||
else
|
||||
{
|
||||
if ((ret = CRYPT_EncodeEnsureSpace(dwFlags, pEncodePara,
|
||||
pbEncoded, pcbEncoded, bytesNeeded)))
|
||||
{
|
||||
if (dwFlags & CRYPT_ENCODE_ALLOC_FLAG)
|
||||
pbEncoded = *(BYTE **)pbEncoded;
|
||||
*pbEncoded++ = ASN_UTF8STRING;
|
||||
CRYPT_EncodeLen(encodedLen, pbEncoded, &lenBytes);
|
||||
pbEncoded += lenBytes;
|
||||
WideCharToMultiByte(CP_UTF8, 0, str, strLen, (LPSTR)pbEncoded,
|
||||
bytesNeeded - lenBytes - 1, NULL, NULL);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static BOOL CRYPT_AsnEncodeUniversalString(const CERT_NAME_VALUE *value,
|
||||
DWORD dwFlags, PCRYPT_ENCODE_PARA pEncodePara, BYTE *pbEncoded,
|
||||
DWORD *pcbEncoded)
|
||||
{
|
||||
BOOL ret = TRUE;
|
||||
LPCWSTR str = (LPCWSTR)value->Value.pbData;
|
||||
DWORD bytesNeeded, lenBytes, strLen;
|
||||
|
||||
/* FIXME: doesn't handle composite characters */
|
||||
strLen = value->Value.cbData ? value->Value.cbData / sizeof(WCHAR) :
|
||||
lstrlenW(str);
|
||||
CRYPT_EncodeLen(strLen * 4, NULL, &lenBytes);
|
||||
bytesNeeded = 1 + lenBytes + strLen * 4;
|
||||
if (!pbEncoded)
|
||||
*pcbEncoded = bytesNeeded;
|
||||
else
|
||||
{
|
||||
if ((ret = CRYPT_EncodeEnsureSpace(dwFlags, pEncodePara,
|
||||
pbEncoded, pcbEncoded, bytesNeeded)))
|
||||
{
|
||||
DWORD i;
|
||||
|
||||
if (dwFlags & CRYPT_ENCODE_ALLOC_FLAG)
|
||||
pbEncoded = *(BYTE **)pbEncoded;
|
||||
*pbEncoded++ = ASN_UNIVERSALSTRING;
|
||||
CRYPT_EncodeLen(strLen * 4, pbEncoded, &lenBytes);
|
||||
pbEncoded += lenBytes;
|
||||
for (i = 0; i < strLen; i++)
|
||||
{
|
||||
*pbEncoded++ = 0;
|
||||
*pbEncoded++ = 0;
|
||||
*pbEncoded++ = (BYTE)((str[i] & 0xff00) >> 8);
|
||||
*pbEncoded++ = (BYTE)(str[i] & 0x00ff);
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static BOOL CRYPT_AsnEncodeBMPString(const CERT_NAME_VALUE *value,
|
||||
DWORD dwFlags, PCRYPT_ENCODE_PARA pEncodePara, BYTE *pbEncoded,
|
||||
DWORD *pcbEncoded)
|
||||
{
|
||||
BOOL ret = TRUE;
|
||||
LPCWSTR str = (LPCWSTR)value->Value.pbData;
|
||||
DWORD bytesNeeded, lenBytes, strLen;
|
||||
|
||||
strLen = value->Value.cbData ? value->Value.cbData / sizeof(WCHAR) :
|
||||
lstrlenW(str);
|
||||
CRYPT_EncodeLen(strLen * 2, NULL, &lenBytes);
|
||||
bytesNeeded = 1 + lenBytes + strLen * 2;
|
||||
if (!pbEncoded)
|
||||
*pcbEncoded = bytesNeeded;
|
||||
else
|
||||
{
|
||||
if ((ret = CRYPT_EncodeEnsureSpace(dwFlags, pEncodePara,
|
||||
pbEncoded, pcbEncoded, bytesNeeded)))
|
||||
{
|
||||
DWORD i;
|
||||
|
||||
if (dwFlags & CRYPT_ENCODE_ALLOC_FLAG)
|
||||
pbEncoded = *(BYTE **)pbEncoded;
|
||||
*pbEncoded++ = ASN_BMPSTRING;
|
||||
CRYPT_EncodeLen(strLen * 2, pbEncoded, &lenBytes);
|
||||
pbEncoded += lenBytes;
|
||||
for (i = 0; i < strLen; i++)
|
||||
{
|
||||
*pbEncoded++ = (str[i] & 0xff00) >> 8;
|
||||
*pbEncoded++ = str[i] & 0x00ff;
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static BOOL WINAPI CRYPT_AsnEncodeUnicodeNameValue(DWORD dwCertEncodingType,
|
||||
LPCSTR lpszStructType, const void *pvStructInfo, DWORD dwFlags,
|
||||
PCRYPT_ENCODE_PARA pEncodePara, BYTE *pbEncoded, DWORD *pcbEncoded)
|
||||
{
|
||||
BOOL ret = FALSE;
|
||||
|
||||
__TRY
|
||||
{
|
||||
const CERT_NAME_VALUE *value = (CERT_NAME_VALUE *)pvStructInfo;
|
||||
|
||||
switch (value->dwValueType)
|
||||
{
|
||||
case CERT_RDN_ANY_TYPE:
|
||||
case CERT_RDN_ENCODED_BLOB:
|
||||
case CERT_RDN_OCTET_STRING:
|
||||
SetLastError(CRYPT_E_NOT_CHAR_STRING);
|
||||
break;
|
||||
case CERT_RDN_NUMERIC_STRING:
|
||||
ret = CRYPT_AsnEncodeNumericString(value, dwFlags, pEncodePara,
|
||||
pbEncoded, pcbEncoded);
|
||||
break;
|
||||
case CERT_RDN_PRINTABLE_STRING:
|
||||
ret = CRYPT_AsnEncodePrintableString(value, dwFlags, pEncodePara,
|
||||
pbEncoded, pcbEncoded);
|
||||
break;
|
||||
case CERT_RDN_TELETEX_STRING:
|
||||
ret = CRYPT_AsnEncodeUnicodeStringCoerce(value, ASN_T61STRING,
|
||||
dwFlags, pEncodePara, pbEncoded, pcbEncoded);
|
||||
break;
|
||||
case CERT_RDN_VIDEOTEX_STRING:
|
||||
ret = CRYPT_AsnEncodeUnicodeStringCoerce(value,
|
||||
ASN_VIDEOTEXSTRING, dwFlags, pEncodePara, pbEncoded, pcbEncoded);
|
||||
break;
|
||||
case CERT_RDN_IA5_STRING:
|
||||
ret = CRYPT_AsnEncodeIA5String(value, dwFlags, pEncodePara,
|
||||
pbEncoded, pcbEncoded);
|
||||
break;
|
||||
case CERT_RDN_GRAPHIC_STRING:
|
||||
ret = CRYPT_AsnEncodeUnicodeStringCoerce(value, ASN_GRAPHICSTRING,
|
||||
dwFlags, pEncodePara, pbEncoded, pcbEncoded);
|
||||
break;
|
||||
case CERT_RDN_VISIBLE_STRING:
|
||||
ret = CRYPT_AsnEncodeUnicodeStringCoerce(value, ASN_VISIBLESTRING,
|
||||
dwFlags, pEncodePara, pbEncoded, pcbEncoded);
|
||||
break;
|
||||
case CERT_RDN_GENERAL_STRING:
|
||||
ret = CRYPT_AsnEncodeUnicodeStringCoerce(value, ASN_GENERALSTRING,
|
||||
dwFlags, pEncodePara, pbEncoded, pcbEncoded);
|
||||
break;
|
||||
case CERT_RDN_UNIVERSAL_STRING:
|
||||
ret = CRYPT_AsnEncodeUniversalString(value, dwFlags, pEncodePara,
|
||||
pbEncoded, pcbEncoded);
|
||||
break;
|
||||
case CERT_RDN_BMP_STRING:
|
||||
ret = CRYPT_AsnEncodeBMPString(value, dwFlags, pEncodePara,
|
||||
pbEncoded, pcbEncoded);
|
||||
break;
|
||||
case CERT_RDN_UTF8_STRING:
|
||||
ret = CRYPT_AsnEncodeUTF8String(value, dwFlags, pEncodePara,
|
||||
pbEncoded, pcbEncoded);
|
||||
break;
|
||||
default:
|
||||
SetLastError(CRYPT_E_ASN1_CHOICE);
|
||||
}
|
||||
}
|
||||
__EXCEPT_PAGE_FAULT
|
||||
{
|
||||
SetLastError(STATUS_ACCESS_VIOLATION);
|
||||
}
|
||||
__ENDTRY
|
||||
return ret;
|
||||
}
|
||||
|
||||
static BOOL WINAPI CRYPT_AsnEncodeRdnAttr(DWORD dwCertEncodingType,
|
||||
CERT_RDN_ATTR *attr, BYTE *pbEncoded, DWORD *pcbEncoded)
|
||||
{
|
||||
|
@ -2403,6 +2751,9 @@ BOOL WINAPI CryptEncodeObjectEx(DWORD dwCertEncodingType, LPCSTR lpszStructType,
|
|||
case (WORD)RSA_CSP_PUBLICKEYBLOB:
|
||||
encodeFunc = CRYPT_AsnEncodeRsaPubKey;
|
||||
break;
|
||||
case (WORD)X509_UNICODE_NAME_VALUE:
|
||||
encodeFunc = CRYPT_AsnEncodeUnicodeNameValue;
|
||||
break;
|
||||
case (WORD)X509_OCTET_STRING:
|
||||
encodeFunc = CRYPT_AsnEncodeOctets;
|
||||
break;
|
||||
|
|
|
@ -1199,6 +1199,205 @@ static void test_decodeAltName(DWORD dwEncoding)
|
|||
}
|
||||
}
|
||||
|
||||
struct UnicodeExpectedError
|
||||
{
|
||||
DWORD valueType;
|
||||
LPCWSTR str;
|
||||
DWORD errorIndex;
|
||||
DWORD error;
|
||||
};
|
||||
|
||||
static const WCHAR oneW[] = { '1',0 };
|
||||
static const WCHAR aW[] = { 'a',0 };
|
||||
static const WCHAR quoteW[] = { '"', 0 };
|
||||
|
||||
static struct UnicodeExpectedError unicodeErrors[] = {
|
||||
{ CERT_RDN_ANY_TYPE, oneW, 0, CRYPT_E_NOT_CHAR_STRING },
|
||||
{ CERT_RDN_ENCODED_BLOB, oneW, 0, CRYPT_E_NOT_CHAR_STRING },
|
||||
{ CERT_RDN_OCTET_STRING, oneW, 0, CRYPT_E_NOT_CHAR_STRING },
|
||||
{ 14, oneW, 0, CRYPT_E_ASN1_CHOICE },
|
||||
{ CERT_RDN_NUMERIC_STRING, aW, 0, CRYPT_E_INVALID_NUMERIC_STRING },
|
||||
{ CERT_RDN_PRINTABLE_STRING, quoteW, 0, CRYPT_E_INVALID_PRINTABLE_STRING },
|
||||
{ CERT_RDN_IA5_STRING, nihongoURL, 7, CRYPT_E_INVALID_IA5_STRING },
|
||||
};
|
||||
|
||||
struct UnicodeExpectedResult
|
||||
{
|
||||
DWORD valueType;
|
||||
LPCWSTR str;
|
||||
CRYPT_DATA_BLOB encoded;
|
||||
};
|
||||
|
||||
static BYTE oneNumeric[] = { 0x12, 0x01, 0x31 };
|
||||
static BYTE onePrintable[] = { 0x13, 0x01, 0x31 };
|
||||
static BYTE oneTeletex[] = { 0x14, 0x01, 0x31 };
|
||||
static BYTE oneVideotex[] = { 0x15, 0x01, 0x31 };
|
||||
static BYTE oneIA5[] = { 0x16, 0x01, 0x31 };
|
||||
static BYTE oneGraphic[] = { 0x19, 0x01, 0x31 };
|
||||
static BYTE oneVisible[] = { 0x1a, 0x01, 0x31 };
|
||||
static BYTE oneUniversal[] = { 0x1c, 0x04, 0x00, 0x00, 0x00, 0x31 };
|
||||
static BYTE oneGeneral[] = { 0x1b, 0x01, 0x31 };
|
||||
static BYTE oneBMP[] = { 0x1e, 0x02, 0x00, 0x31 };
|
||||
static BYTE oneUTF8[] = { 0x0c, 0x01, 0x31 };
|
||||
static BYTE nihongoT61[] = { 0x14,0x09,0x68,0x74,0x74,0x70,0x3a,0x2f,0x2f,0x6f,
|
||||
0x5b };
|
||||
static BYTE nihongoGeneral[] = { 0x1b,0x09,0x68,0x74,0x74,0x70,0x3a,0x2f,0x2f,
|
||||
0x6f,0x5b };
|
||||
static BYTE nihongoBMP[] = { 0x1e,0x12,0x00,0x68,0x00,0x74,0x00,0x74,0x00,0x70,
|
||||
0x00,0x3a,0x00,0x2f,0x00,0x2f,0x22,0x6f,0x57,0x5b };
|
||||
static BYTE nihongoUTF8[] = { 0x0c,0x0d,0x68,0x74,0x74,0x70,0x3a,0x2f,0x2f,
|
||||
0xe2,0x89,0xaf,0xe5,0x9d,0x9b };
|
||||
|
||||
static struct UnicodeExpectedResult unicodeResults[] = {
|
||||
{ CERT_RDN_NUMERIC_STRING, oneW, { sizeof(oneNumeric), oneNumeric } },
|
||||
{ CERT_RDN_PRINTABLE_STRING, oneW, { sizeof(onePrintable), onePrintable } },
|
||||
{ CERT_RDN_TELETEX_STRING, oneW, { sizeof(oneTeletex), oneTeletex } },
|
||||
{ CERT_RDN_VIDEOTEX_STRING, oneW, { sizeof(oneVideotex), oneVideotex } },
|
||||
{ CERT_RDN_IA5_STRING, oneW, { sizeof(oneIA5), oneIA5 } },
|
||||
{ CERT_RDN_GRAPHIC_STRING, oneW, { sizeof(oneGraphic), oneGraphic } },
|
||||
{ CERT_RDN_VISIBLE_STRING, oneW, { sizeof(oneVisible), oneVisible } },
|
||||
{ CERT_RDN_UNIVERSAL_STRING, oneW, { sizeof(oneUniversal), oneUniversal } },
|
||||
{ CERT_RDN_GENERAL_STRING, oneW, { sizeof(oneGeneral), oneGeneral } },
|
||||
{ CERT_RDN_BMP_STRING, oneW, { sizeof(oneBMP), oneBMP } },
|
||||
{ CERT_RDN_UTF8_STRING, oneW, { sizeof(oneUTF8), oneUTF8 } },
|
||||
{ CERT_RDN_BMP_STRING, nihongoURL, { sizeof(nihongoBMP), nihongoBMP } },
|
||||
{ CERT_RDN_UTF8_STRING, nihongoURL, { sizeof(nihongoUTF8), nihongoUTF8 } },
|
||||
};
|
||||
|
||||
static struct UnicodeExpectedResult unicodeWeirdness[] = {
|
||||
{ CERT_RDN_TELETEX_STRING, nihongoURL, { sizeof(nihongoT61), nihongoT61 } },
|
||||
{ CERT_RDN_GENERAL_STRING, nihongoURL, { sizeof(nihongoGeneral), nihongoGeneral } },
|
||||
};
|
||||
|
||||
static void test_encodeUnicodeNameValue(DWORD dwEncoding)
|
||||
{
|
||||
BYTE *buf = NULL;
|
||||
DWORD size = 0, i;
|
||||
BOOL ret;
|
||||
CERT_NAME_VALUE value;
|
||||
|
||||
ret = CryptEncodeObjectEx(dwEncoding, X509_UNICODE_NAME_VALUE, NULL,
|
||||
CRYPT_ENCODE_ALLOC_FLAG, NULL, (BYTE *)&buf, &size);
|
||||
ok(!ret && GetLastError() == STATUS_ACCESS_VIOLATION,
|
||||
"Expected STATUS_ACCESS_VIOLATION, got %08lx\n", GetLastError());
|
||||
/* Have to have a string of some sort */
|
||||
value.dwValueType = 0; /* aka CERT_RDN_ANY_TYPE */
|
||||
value.Value.pbData = NULL;
|
||||
value.Value.cbData = 0;
|
||||
ret = CryptEncodeObjectEx(dwEncoding, X509_UNICODE_NAME_VALUE, &value,
|
||||
CRYPT_ENCODE_ALLOC_FLAG, NULL, (BYTE *)&buf, &size);
|
||||
ok(!ret && GetLastError() == CRYPT_E_NOT_CHAR_STRING,
|
||||
"Expected CRYPT_E_NOT_CHAR_STRING, got %08lx\n", GetLastError());
|
||||
value.dwValueType = CERT_RDN_ENCODED_BLOB;
|
||||
ret = CryptEncodeObjectEx(dwEncoding, X509_UNICODE_NAME_VALUE, &value,
|
||||
CRYPT_ENCODE_ALLOC_FLAG, NULL, (BYTE *)&buf, &size);
|
||||
ok(!ret && GetLastError() == CRYPT_E_NOT_CHAR_STRING,
|
||||
"Expected CRYPT_E_NOT_CHAR_STRING, got %08lx\n", GetLastError());
|
||||
value.dwValueType = CERT_RDN_ANY_TYPE;
|
||||
value.Value.pbData = (LPBYTE)oneW;
|
||||
ret = CryptEncodeObjectEx(dwEncoding, X509_UNICODE_NAME_VALUE, &value,
|
||||
CRYPT_ENCODE_ALLOC_FLAG, NULL, (BYTE *)&buf, &size);
|
||||
ok(!ret && GetLastError() == CRYPT_E_NOT_CHAR_STRING,
|
||||
"Expected CRYPT_E_NOT_CHAR_STRING, got %08lx\n", GetLastError());
|
||||
value.Value.cbData = sizeof(oneW);
|
||||
ret = CryptEncodeObjectEx(dwEncoding, X509_UNICODE_NAME_VALUE, &value,
|
||||
CRYPT_ENCODE_ALLOC_FLAG, NULL, (BYTE *)&buf, &size);
|
||||
ok(!ret && GetLastError() == CRYPT_E_NOT_CHAR_STRING,
|
||||
"Expected CRYPT_E_NOT_CHAR_STRING, got %08lx\n", GetLastError());
|
||||
/* An encoded string with specified length isn't good enough either */
|
||||
value.dwValueType = CERT_RDN_ENCODED_BLOB;
|
||||
value.Value.pbData = oneUniversal;
|
||||
value.Value.cbData = sizeof(oneUniversal);
|
||||
ret = CryptEncodeObjectEx(dwEncoding, X509_UNICODE_NAME_VALUE, &value,
|
||||
CRYPT_ENCODE_ALLOC_FLAG, NULL, (BYTE *)&buf, &size);
|
||||
ok(!ret && GetLastError() == CRYPT_E_NOT_CHAR_STRING,
|
||||
"Expected CRYPT_E_NOT_CHAR_STRING, got %08lx\n", GetLastError());
|
||||
/* More failure checking */
|
||||
value.Value.cbData = 0;
|
||||
for (i = 0; i < sizeof(unicodeErrors) / sizeof(unicodeErrors[0]); i++)
|
||||
{
|
||||
value.Value.pbData = (LPBYTE)unicodeErrors[i].str;
|
||||
value.dwValueType = unicodeErrors[i].valueType;
|
||||
ret = CryptEncodeObjectEx(dwEncoding, X509_UNICODE_NAME_VALUE, &value,
|
||||
CRYPT_ENCODE_ALLOC_FLAG, NULL, (BYTE *)&buf, &size);
|
||||
ok(!ret && GetLastError() == unicodeErrors[i].error,
|
||||
"Value type %ld: expected %08lx, got %08lx\n", value.dwValueType,
|
||||
unicodeErrors[i].error, GetLastError());
|
||||
ok(size == unicodeErrors[i].errorIndex,
|
||||
"Expected error index %ld, got %ld\n", unicodeErrors[i].errorIndex,
|
||||
size);
|
||||
}
|
||||
/* cbData can be zero if the string is NULL-terminated */
|
||||
value.Value.cbData = 0;
|
||||
for (i = 0; i < sizeof(unicodeResults) / sizeof(unicodeResults[0]); i++)
|
||||
{
|
||||
value.Value.pbData = (LPBYTE)unicodeResults[i].str;
|
||||
value.dwValueType = unicodeResults[i].valueType;
|
||||
ret = CryptEncodeObjectEx(dwEncoding, X509_UNICODE_NAME_VALUE, &value,
|
||||
CRYPT_ENCODE_ALLOC_FLAG, NULL, (BYTE *)&buf, &size);
|
||||
ok(ret, "CryptEncodeObjectEx failed: %08lx\n", GetLastError());
|
||||
if (buf)
|
||||
{
|
||||
ok(size == unicodeResults[i].encoded.cbData,
|
||||
"Value type %ld: expected size %ld, got %ld\n",
|
||||
value.dwValueType, unicodeResults[i].encoded.cbData, size);
|
||||
ok(!memcmp(unicodeResults[i].encoded.pbData, buf, size),
|
||||
"Value type %ld: unexpected value\n", value.dwValueType);
|
||||
LocalFree(buf);
|
||||
}
|
||||
}
|
||||
/* These "encode," but they do so by truncating each unicode character
|
||||
* rather than properly encoding it. Kept separate from the proper results,
|
||||
* because the encoded forms won't decode to their original strings.
|
||||
*/
|
||||
for (i = 0; i < sizeof(unicodeWeirdness) / sizeof(unicodeWeirdness[0]); i++)
|
||||
{
|
||||
value.Value.pbData = (LPBYTE)unicodeWeirdness[i].str;
|
||||
value.dwValueType = unicodeWeirdness[i].valueType;
|
||||
ret = CryptEncodeObjectEx(dwEncoding, X509_UNICODE_NAME_VALUE, &value,
|
||||
CRYPT_ENCODE_ALLOC_FLAG, NULL, (BYTE *)&buf, &size);
|
||||
ok(ret, "CryptEncodeObjectEx failed: %08lx\n", GetLastError());
|
||||
if (buf)
|
||||
{
|
||||
ok(size == unicodeWeirdness[i].encoded.cbData,
|
||||
"Value type %ld: expected size %ld, got %ld\n",
|
||||
value.dwValueType, unicodeWeirdness[i].encoded.cbData, size);
|
||||
ok(!memcmp(unicodeWeirdness[i].encoded.pbData, buf, size),
|
||||
"Value type %ld: unexpected value\n", value.dwValueType);
|
||||
LocalFree(buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void test_decodeUnicodeNameValue(DWORD dwEncoding)
|
||||
{
|
||||
DWORD i;
|
||||
|
||||
for (i = 0; i < sizeof(unicodeResults) / sizeof(unicodeResults[0]); i++)
|
||||
{
|
||||
BYTE *buf = NULL;
|
||||
BOOL ret;
|
||||
DWORD size = 0;
|
||||
|
||||
ret = CryptDecodeObjectEx(dwEncoding, X509_UNICODE_NAME_VALUE,
|
||||
unicodeResults[i].encoded.pbData, unicodeResults[i].encoded.cbData,
|
||||
CRYPT_DECODE_ALLOC_FLAG, NULL, (BYTE *)&buf, &size);
|
||||
ok(ret, "CryptDecodeObjectEx failed: %08lx\n", GetLastError());
|
||||
if (ret && buf)
|
||||
{
|
||||
PCERT_NAME_VALUE value = (PCERT_NAME_VALUE)buf;
|
||||
|
||||
ok(value->dwValueType == unicodeResults[i].valueType,
|
||||
"Expected value type %ld, got %ld\n", unicodeResults[i].valueType,
|
||||
value->dwValueType);
|
||||
ok(!lstrcmpW((LPWSTR)value->Value.pbData, unicodeResults[i].str),
|
||||
"Unexpected decoded value for index %ld (value type %ld)\n", i,
|
||||
unicodeResults[i].valueType);
|
||||
LocalFree(buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct encodedOctets
|
||||
{
|
||||
const BYTE *val;
|
||||
|
@ -3392,6 +3591,8 @@ START_TEST(encode)
|
|||
test_decodeName(encodings[i]);
|
||||
test_encodeNameValue(encodings[i]);
|
||||
test_decodeNameValue(encodings[i]);
|
||||
test_encodeUnicodeNameValue(encodings[i]);
|
||||
test_decodeUnicodeNameValue(encodings[i]);
|
||||
test_encodeAltName(encodings[i]);
|
||||
test_decodeAltName(encodings[i]);
|
||||
test_encodeOctets(encodings[i]);
|
||||
|
|
Loading…
Reference in New Issue