crypt32: Implement X509_UNICODE_NAME_VALUE encoding/decoding.

This commit is contained in:
Juan Lang 2006-07-19 07:44:48 -07:00 committed by Alexandre Julliard
parent 0042c010af
commit ead21189c2
4 changed files with 736 additions and 0 deletions

View File

@ -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

View File

@ -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;

View File

@ -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;

View File

@ -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]);