crypt32: Implement X509_NAME_VALUE encoding/decoding.
diff --git a/dlls/crypt32/crypt32_private.h b/dlls/crypt32/crypt32_private.h
index f8365d6..8ac0484 100644
--- a/dlls/crypt32/crypt32_private.h
+++ b/dlls/crypt32/crypt32_private.h
@@ -26,6 +26,7 @@
 #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_IA5STRING       (ASN_UNIVERSAL | ASN_PRIMITIVE | 0x16)
 #define ASN_UTCTIME         (ASN_UNIVERSAL | ASN_PRIMITIVE | 0x17)
 #define ASN_GENERALTIME     (ASN_UNIVERSAL | ASN_PRIMITIVE | 0x18)
diff --git a/dlls/crypt32/decode.c b/dlls/crypt32/decode.c
index d2be521..cd0e644 100644
--- a/dlls/crypt32/decode.c
+++ b/dlls/crypt32/decode.c
@@ -1182,7 +1182,7 @@
  * order to avoid overwriting memory.  (In some cases, it may change it, if it
  * doesn't copy anything to memory.)  Be sure to set it correctly!
  */
-static BOOL WINAPI CRYPT_AsnDecodeNameValue(DWORD dwCertEncodingType,
+static BOOL WINAPI CRYPT_AsnDecodeNameValueInternal(DWORD dwCertEncodingType,
  LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
  PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo)
 {
@@ -1199,6 +1199,7 @@
         case ASN_NUMERICSTRING:
         case ASN_PRINTABLESTRING:
         case ASN_IA5STRING:
+        case ASN_T61STRING:
             break;
         default:
             FIXME("Unimplemented string type %02x\n", pbEncoded[0]);
@@ -1214,6 +1215,7 @@
             case ASN_NUMERICSTRING:
             case ASN_PRINTABLESTRING:
             case ASN_IA5STRING:
+            case ASN_T61STRING:
                 if (!(dwFlags & CRYPT_DECODE_NOCOPY_FLAG))
                     bytesNeeded += dataLen;
                 break;
@@ -1240,6 +1242,9 @@
                 case ASN_IA5STRING:
                     value->dwValueType = CERT_RDN_IA5_STRING;
                     break;
+                case ASN_T61STRING:
+                    value->dwValueType = CERT_RDN_T61_STRING;
+                    break;
                 }
                 if (dataLen)
                 {
@@ -1248,6 +1253,7 @@
                     case ASN_NUMERICSTRING:
                     case ASN_PRINTABLESTRING:
                     case ASN_IA5STRING:
+                    case ASN_T61STRING:
                         value->Value.cbData = dataLen;
                         if (dwFlags & CRYPT_DECODE_NOCOPY_FLAG)
                             value->Value.pbData = (BYTE *)pbEncoded + 1 +
@@ -1272,6 +1278,45 @@
     return ret;
 }
 
+static BOOL WINAPI CRYPT_AsnDecodeNameValue(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_AsnDecodeNameValueInternal(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_AsnDecodeNameValueInternal(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)
@@ -1282,7 +1327,7 @@
        CRYPT_AsnDecodeOidInternal, sizeof(LPSTR), FALSE, TRUE,
        offsetof(CERT_RDN_ATTR, pszObjId), 0 },
      { 0, offsetof(CERT_RDN_ATTR, dwValueType),
-       CRYPT_AsnDecodeNameValue, sizeof(CERT_NAME_VALUE),
+       CRYPT_AsnDecodeNameValueInternal, sizeof(CERT_NAME_VALUE),
        FALSE, TRUE, offsetof(CERT_RDN_ATTR, Value.pbData), 0 },
     };
     CERT_RDN_ATTR *attr = (CERT_RDN_ATTR *)pvStructInfo;
@@ -2940,6 +2985,9 @@
         case (WORD)X509_EXTENSIONS:
             decodeFunc = CRYPT_AsnDecodeExtensions;
             break;
+        case (WORD)X509_NAME_VALUE:
+            decodeFunc = CRYPT_AsnDecodeNameValue;
+            break;
         case (WORD)X509_NAME:
             decodeFunc = CRYPT_AsnDecodeName;
             break;
diff --git a/dlls/crypt32/encode.c b/dlls/crypt32/encode.c
index ee1c9cc..3fee0c9 100644
--- a/dlls/crypt32/encode.c
+++ b/dlls/crypt32/encode.c
@@ -906,58 +906,74 @@
 }
 
 static BOOL WINAPI CRYPT_AsnEncodeNameValue(DWORD dwCertEncodingType,
- CERT_NAME_VALUE *value, BYTE *pbEncoded, DWORD *pcbEncoded)
+ LPCSTR lpszStructType, const void *pvStructInfo, DWORD dwFlags,
+ PCRYPT_ENCODE_PARA pEncodePara, BYTE *pbEncoded, DWORD *pcbEncoded)
 {
-    BYTE tag;
-    DWORD bytesNeeded, lenBytes, encodedLen;
     BOOL ret = TRUE;
 
-    switch (value->dwValueType)
+    __TRY
     {
-    case CERT_RDN_NUMERIC_STRING:
-        tag = ASN_NUMERICSTRING;
-        encodedLen = value->Value.cbData;
-        break;
-    case CERT_RDN_PRINTABLE_STRING:
-        tag = ASN_PRINTABLESTRING;
-        encodedLen = value->Value.cbData;
-        break;
-    case CERT_RDN_IA5_STRING:
-        tag = ASN_IA5STRING;
-        encodedLen = value->Value.cbData;
-        break;
-    case CERT_RDN_ANY_TYPE:
-        /* explicitly disallowed */
-        SetLastError(E_INVALIDARG);
-        return FALSE;
-    default:
-        FIXME("String type %ld unimplemented\n", value->dwValueType);
-        return FALSE;
-    }
-    CRYPT_EncodeLen(encodedLen, NULL, &lenBytes);
-    bytesNeeded = 1 + lenBytes + encodedLen;
-    if (pbEncoded)
-    {
-        if (*pcbEncoded < bytesNeeded)
+        BYTE tag;
+        DWORD bytesNeeded, lenBytes, encodedLen;
+        const CERT_NAME_VALUE *value = (CERT_NAME_VALUE *)pvStructInfo;
+
+        switch (value->dwValueType)
         {
-            SetLastError(ERROR_MORE_DATA);
-            ret = FALSE;
+        case CERT_RDN_NUMERIC_STRING:
+            tag = ASN_NUMERICSTRING;
+            encodedLen = value->Value.cbData;
+            break;
+        case CERT_RDN_PRINTABLE_STRING:
+            tag = ASN_PRINTABLESTRING;
+            encodedLen = value->Value.cbData;
+            break;
+        case CERT_RDN_T61_STRING:
+            tag = ASN_T61STRING;
+            encodedLen = value->Value.cbData;
+            break;
+        case CERT_RDN_IA5_STRING:
+            tag = ASN_IA5STRING;
+            encodedLen = value->Value.cbData;
+            break;
+        case CERT_RDN_ANY_TYPE:
+            /* explicitly disallowed */
+            SetLastError(E_INVALIDARG);
+            return FALSE;
+        default:
+            FIXME("String type %ld unimplemented\n", value->dwValueType);
+            return FALSE;
         }
+        CRYPT_EncodeLen(encodedLen, NULL, &lenBytes);
+        bytesNeeded = 1 + lenBytes + encodedLen;
+        if (!pbEncoded)
+            *pcbEncoded = bytesNeeded;
         else
         {
-            *pbEncoded++ = tag;
-            CRYPT_EncodeLen(encodedLen, pbEncoded, &lenBytes);
-            pbEncoded += lenBytes;
-            switch (value->dwValueType)
+            if ((ret = CRYPT_EncodeEnsureSpace(dwFlags, pEncodePara, pbEncoded,
+             pcbEncoded, bytesNeeded)))
             {
-            case CERT_RDN_NUMERIC_STRING:
-            case CERT_RDN_PRINTABLE_STRING:
-            case CERT_RDN_IA5_STRING:
-                memcpy(pbEncoded, value->Value.pbData, value->Value.cbData);
+                if (dwFlags & CRYPT_ENCODE_ALLOC_FLAG)
+                    pbEncoded = *(BYTE **)pbEncoded;
+                *pbEncoded++ = tag;
+                CRYPT_EncodeLen(encodedLen, pbEncoded, &lenBytes);
+                pbEncoded += lenBytes;
+                switch (value->dwValueType)
+                {
+                case CERT_RDN_NUMERIC_STRING:
+                case CERT_RDN_PRINTABLE_STRING:
+                case CERT_RDN_T61_STRING:
+                case CERT_RDN_IA5_STRING:
+                    memcpy(pbEncoded, value->Value.pbData, value->Value.cbData);
+                }
             }
         }
     }
-    *pcbEncoded = bytesNeeded;
+    __EXCEPT_PAGE_FAULT
+    {
+        SetLastError(STATUS_ACCESS_VIOLATION);
+        ret = FALSE;
+    }
+    __ENDTRY
     return ret;
 }
 
@@ -975,8 +991,8 @@
         /* hack: a CERT_RDN_ATTR is identical to a CERT_NAME_VALUE beginning
          * with dwValueType, so "cast" it to get its encoded size
          */
-        ret = CRYPT_AsnEncodeNameValue(dwCertEncodingType,
-         (CERT_NAME_VALUE *)&attr->dwValueType, NULL, &size);
+        ret = CRYPT_AsnEncodeNameValue(dwCertEncodingType, X509_NAME_VALUE,
+         (CERT_NAME_VALUE *)&attr->dwValueType, 0, NULL, NULL, &size);
         if (ret)
         {
             bytesNeeded += size;
@@ -1003,8 +1019,8 @@
                         pbEncoded += size;
                         size = bytesNeeded - 1 - lenBytes - size;
                         ret = CRYPT_AsnEncodeNameValue(dwCertEncodingType,
-                         (CERT_NAME_VALUE *)&attr->dwValueType, pbEncoded,
-                         &size);
+                         X509_NAME_VALUE, (CERT_NAME_VALUE *)&attr->dwValueType,
+                         0, NULL, pbEncoded, &size);
                     }
                 }
             }
@@ -2264,6 +2280,9 @@
         case (WORD)X509_EXTENSIONS:
             encodeFunc = CRYPT_AsnEncodeExtensions;
             break;
+        case (WORD)X509_NAME_VALUE:
+            encodeFunc = CRYPT_AsnEncodeNameValue;
+            break;
         case (WORD)X509_NAME:
             encodeFunc = CRYPT_AsnEncodeName;
             break;
diff --git a/dlls/crypt32/tests/encode.c b/dlls/crypt32/tests/encode.c
index d5258c1..da5b975 100644
--- a/dlls/crypt32/tests/encode.c
+++ b/dlls/crypt32/tests/encode.c
@@ -3204,10 +3204,8 @@
         test_decodeFiletime(encodings[i]);
         test_encodeName(encodings[i]);
         test_decodeName(encodings[i]);
-        todo_wine {
         test_encodeNameValue(encodings[i]);
         test_decodeNameValue(encodings[i]);
-        }
         test_encodeAltName(encodings[i]);
         test_decodeAltName(encodings[i]);
         test_encodeOctets(encodings[i]);