Remove limits on IVT public key lengths. (#24042)
[platform/upstream/coreclr.git] / src / binder / textualidentityparser.cpp
1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
4 // ============================================================
5 //
6 // TextualIdentityParser.cpp
7 //
8
9
10 //
11 // Implements the TextualIdentityParser class
12 //
13 // ============================================================
14
15 #define DISABLE_BINDER_DEBUG_LOGGING
16
17 #include "textualidentityparser.hpp"
18 #include "assemblyidentity.hpp"
19 #include "utils.hpp"
20
21 #include "ex.h"
22
23 #define GO_IF_SEEN(kAssemblyIdentityFlag)                       \
24     if ((m_dwAttributesSeen & kAssemblyIdentityFlag) != 0)      \
25     {                                                           \
26         fIsValid = FALSE;                                       \
27         goto Exit;                                              \
28     }                                                           \
29     else                                                        \
30     {                                                           \
31           m_dwAttributesSeen |= kAssemblyIdentityFlag;          \
32     }
33
34 #define GO_IF_WILDCARD(valueString)             \
35     {                                           \
36         SmallStackSString wildCard(W("*"));       \
37         if (valueString.Equals(wildCard))       \
38         {                                       \
39             goto Exit;                          \
40         }                                       \
41     }
42
43 #define GO_IF_VALIDATE_FAILED(validateProc, kIdentityFlag)      \
44     if (!validateProc(valueString))                             \
45     {                                                           \
46         fIsValid = FALSE;                                       \
47         goto Exit;                                              \
48     }                                                           \
49     else                                                        \
50     {                                                           \
51         m_pAssemblyIdentity->SetHave(kIdentityFlag);            \
52    }
53
54 #define FROMHEX(a) ((a)>=W('a') ? a - W('a') + 10 : a - W('0'))
55 #define TOHEX(a) ((a)>=10 ? W('a')+(a)-10 : W('0')+(a))
56 #define TOLOWER(a) (((a) >= W('A') && (a) <= W('Z')) ? (W('a') + (a - W('A'))) : (a))
57
58 namespace BINDER_SPACE
59 {
60     namespace
61     {
62         const int iPublicKeyTokenLength = 8;
63
64         const int iVersionMax = 65535;
65         const int iVersionParts = 4;
66
67         inline void UnicodeHexToBin(LPCWSTR pSrc, UINT cSrc, LPBYTE pDest)
68         {
69             BYTE v;
70             LPBYTE pd = pDest;
71             LPCWSTR ps = pSrc;
72
73             if (cSrc == 0)
74                 return;
75
76             for (UINT i = 0; i < cSrc-1; i+=2)
77             {
78                 v =  (BYTE)FROMHEX(TOLOWER(ps[i])) << 4;
79                 v |= FROMHEX(TOLOWER(ps[i+1]));
80                 *(pd++) = v;
81             }
82         }
83
84         inline void BinToUnicodeHex(const BYTE *pSrc, UINT cSrc, __out_ecount(2*cSrc) LPWSTR pDst)
85         {
86             UINT x;
87             UINT y;
88
89             for (x = 0, y = 0 ; x < cSrc; ++x)
90             {
91                 UINT v;
92
93                 v = pSrc[x]>>4;
94                 pDst[y++] = (WCHAR)TOHEX(v);  
95                 v = pSrc[x] & 0x0f;                 
96                 pDst[y++] = (WCHAR)TOHEX(v); 
97             }                                    
98         }
99
100         inline BOOL EqualsCaseInsensitive(SString &a, LPCWSTR wzB)
101         {
102             SString b(SString::Literal, wzB);
103
104             return ::BINDER_SPACE::EqualsCaseInsensitive(a, b);
105         }
106
107         BOOL ValidateHex(SString &publicKeyOrToken)
108         {
109             if ((publicKeyOrToken.GetCount() == 0) || ((publicKeyOrToken.GetCount() % 2) != 0))
110             {
111                 return FALSE;
112             }
113
114             SString::Iterator cursor = publicKeyOrToken.Begin();
115             SString::Iterator end = publicKeyOrToken.End() - 1;
116
117             while (cursor <= end)
118             {
119                 WCHAR wcCurrentChar = cursor[0];
120
121                 if (((wcCurrentChar >= W('0')) && (wcCurrentChar <= W('9'))) ||
122                     ((wcCurrentChar >= W('a')) && (wcCurrentChar <= W('f'))) ||
123                     ((wcCurrentChar >= W('A')) && (wcCurrentChar <= W('F'))))
124                 {
125                     cursor++;
126                     continue;
127                 }
128
129                 return FALSE;
130             }
131
132             return TRUE;
133         }
134
135         inline BOOL ValidatePublicKeyToken(SString &publicKeyToken)
136         {
137             return ((publicKeyToken.GetCount() == (iPublicKeyTokenLength * 2)) &&
138                     ValidateHex(publicKeyToken));
139         }
140
141         inline BOOL ValidatePublicKey(SString &publicKey)
142         {
143             return ValidateHex(publicKey);
144         }
145
146         const struct {
147             LPCWSTR strValue;
148             PEKIND enumValue;
149         } wszKnownArchitectures[] = { { W("x86"), peI386 },
150                                       { W("IA64"), peIA64 }, 
151                                       { W("AMD64"), peAMD64 },
152                                       { W("ARM"), peARM },
153                                       { W("MSIL"), peMSIL } };
154
155         BOOL ValidateAndConvertProcessorArchitecture(SString &processorArchitecture,
156                                                      PEKIND *pkProcessorArchitecture)
157         {
158             for (int i = LENGTH_OF(wszKnownArchitectures); i--;)
159             {
160                 if (EqualsCaseInsensitive(processorArchitecture, wszKnownArchitectures[i].strValue))
161                 {
162                     *pkProcessorArchitecture = wszKnownArchitectures[i].enumValue;
163                     return TRUE;
164                 }
165             }
166
167             return FALSE;
168         }
169
170         LPCWSTR PeKindToString(PEKIND kProcessorArchitecture)
171         {
172             _ASSERTE(kProcessorArchitecture != peNone);
173
174             for (int i = LENGTH_OF(wszKnownArchitectures); i--;)
175             {
176                 if (wszKnownArchitectures[i].enumValue == kProcessorArchitecture)
177                 {
178                     return wszKnownArchitectures[i].strValue;
179                 }
180             }
181
182             return NULL;
183         }
184
185         LPCWSTR ContentTypeToString(AssemblyContentType kContentType)
186         {
187             _ASSERTE(kContentType != AssemblyContentType_Default);
188             
189             if (kContentType == AssemblyContentType_WindowsRuntime)
190             {
191                 return W("WindowsRuntime");
192             }
193
194             return NULL;
195         }
196     };  // namespace (anonymous)
197
198     TextualIdentityParser::TextualIdentityParser(AssemblyIdentity *pAssemblyIdentity)
199     {
200         m_pAssemblyIdentity = pAssemblyIdentity;
201         m_dwAttributesSeen = AssemblyIdentity::IDENTITY_FLAG_EMPTY;
202     }
203
204     TextualIdentityParser::~TextualIdentityParser()
205     {
206         // Nothing to do here
207     }
208
209     BOOL TextualIdentityParser::IsSeparatorChar(WCHAR wcChar)
210     {
211         return ((wcChar == W(',')) || (wcChar == W('=')));
212     }
213
214     StringLexer::LEXEME_TYPE TextualIdentityParser::GetLexemeType(WCHAR wcChar)
215     {
216         switch (wcChar)
217         {
218         case W('='):
219             return LEXEME_TYPE_EQUALS;
220         case W(','):
221             return LEXEME_TYPE_COMMA;
222         case 0:
223             return LEXEME_TYPE_END_OF_STREAM;
224         default:
225             return LEXEME_TYPE_STRING;
226         }
227     }
228
229     /* static */
230     HRESULT TextualIdentityParser::Parse(SString          &textualIdentity,
231                                          AssemblyIdentity *pAssemblyIdentity,
232                                          BOOL              fPermitUnescapedQuotes)
233     {
234         HRESULT hr = S_OK;
235         BINDER_LOG_ENTER(W("TextualIdentityParser::Parse"));
236
237         IF_FALSE_GO(pAssemblyIdentity != NULL);
238
239         BINDER_LOG_STRING(W("textualIdentity"), textualIdentity);
240
241         EX_TRY
242         {
243             TextualIdentityParser identityParser(pAssemblyIdentity);
244
245             if (!identityParser.Parse(textualIdentity, fPermitUnescapedQuotes))
246             {
247                 IF_FAIL_GO(FUSION_E_INVALID_NAME);
248             }
249         }
250         EX_CATCH_HRESULT(hr);
251
252     Exit:
253         BINDER_LOG_LEAVE_HR(W("TextualIdentityParser::Parse"), hr);
254         return hr;
255     }
256
257     /* static */
258     HRESULT TextualIdentityParser::ToString(AssemblyIdentity *pAssemblyIdentity,
259                                             DWORD             dwIdentityFlags,
260                                             SString          &textualIdentity)
261     {
262         HRESULT hr = S_OK;
263         BINDER_LOG_ENTER(W("TextualIdentityParser::ToString"));
264
265         IF_FALSE_GO(pAssemblyIdentity != NULL);
266
267         EX_TRY
268         {
269             SmallStackSString tmpString;
270
271             textualIdentity.Clear();
272
273             if (pAssemblyIdentity->m_simpleName.IsEmpty())
274             {
275                 goto Exit;
276             }
277
278             EscapeString(pAssemblyIdentity->m_simpleName, tmpString);
279             textualIdentity.Append(tmpString);
280
281             if (AssemblyIdentity::Have(dwIdentityFlags, AssemblyIdentity::IDENTITY_FLAG_VERSION))
282             {
283                 tmpString.Clear();
284                 tmpString.Printf(W("%d.%d.%d.%d"),
285                                  (DWORD)(USHORT)pAssemblyIdentity->m_version.GetMajor(),
286                                  (DWORD)(USHORT)pAssemblyIdentity->m_version.GetMinor(),
287                                  (DWORD)(USHORT)pAssemblyIdentity->m_version.GetBuild(),
288                                  (DWORD)(USHORT)pAssemblyIdentity->m_version.GetRevision());
289
290                 textualIdentity.Append(W(", Version="));
291                 textualIdentity.Append(tmpString);
292             }
293
294             if (AssemblyIdentity::Have(dwIdentityFlags, AssemblyIdentity::IDENTITY_FLAG_CULTURE))
295             {
296                 textualIdentity.Append(W(", Culture="));
297                 if (pAssemblyIdentity->m_cultureOrLanguage.IsEmpty())
298                 {
299                     textualIdentity.Append(W("neutral"));
300                 }
301                 else
302                 {
303                     EscapeString(pAssemblyIdentity->m_cultureOrLanguage, tmpString);
304                     textualIdentity.Append(tmpString);
305                 }
306             }
307
308             if (AssemblyIdentity::Have(dwIdentityFlags, AssemblyIdentity::IDENTITY_FLAG_PUBLIC_KEY))
309             {
310                 textualIdentity.Append(W(", PublicKey="));
311                 tmpString.Clear();
312                 BlobToHex(pAssemblyIdentity->m_publicKeyOrTokenBLOB, tmpString);
313                 textualIdentity.Append(tmpString);
314             }
315             else if (AssemblyIdentity::Have(dwIdentityFlags,
316                                             AssemblyIdentity::IDENTITY_FLAG_PUBLIC_KEY_TOKEN))
317             {
318                 textualIdentity.Append(W(", PublicKeyToken="));
319                 tmpString.Clear();
320                 BlobToHex(pAssemblyIdentity->m_publicKeyOrTokenBLOB, tmpString);
321                 textualIdentity.Append(tmpString);
322             }
323             else if (AssemblyIdentity::Have(dwIdentityFlags,
324                                             AssemblyIdentity::IDENTITY_FLAG_PUBLIC_KEY_TOKEN_NULL))
325             {
326                 textualIdentity.Append(W(", PublicKeyToken=null"));
327             }
328
329             if (AssemblyIdentity::Have(dwIdentityFlags,
330                                        AssemblyIdentity::IDENTITY_FLAG_PROCESSOR_ARCHITECTURE))
331             {
332                 textualIdentity.Append(W(", processorArchitecture="));
333                 textualIdentity.Append(PeKindToString(pAssemblyIdentity->m_kProcessorArchitecture));
334             }
335
336             if (AssemblyIdentity::Have(dwIdentityFlags,
337                                        AssemblyIdentity::IDENTITY_FLAG_RETARGETABLE))
338             {
339                 textualIdentity.Append(W(", Retargetable=Yes"));
340             }
341
342             if (AssemblyIdentity::Have(dwIdentityFlags,
343                                        AssemblyIdentity::IDENTITY_FLAG_CONTENT_TYPE))
344             {
345                 textualIdentity.Append(W(", ContentType="));
346                 textualIdentity.Append(ContentTypeToString(pAssemblyIdentity->m_kContentType));
347             }
348
349             if (AssemblyIdentity::Have(dwIdentityFlags, AssemblyIdentity::IDENTITY_FLAG_CUSTOM))
350             {
351                 textualIdentity.Append(W(", Custom="));
352                 tmpString.Clear();
353                 BlobToHex(pAssemblyIdentity->m_customBLOB, tmpString);
354                 textualIdentity.Append(tmpString);
355             }
356             else if (AssemblyIdentity::Have(dwIdentityFlags,
357                                             AssemblyIdentity::IDENTITY_FLAG_CUSTOM_NULL))
358             {
359                 textualIdentity.Append(W(", Custom=null"));
360             }
361         }
362         EX_CATCH_HRESULT(hr);
363         
364     Exit:
365         BINDER_LOG_LEAVE_HR(W("TextualIdentityParser::ToString"), hr);
366         return hr;
367     }
368
369     /* static */
370     BOOL TextualIdentityParser::ParseVersion(SString         &versionString,
371                                              AssemblyVersion *pAssemblyVersion)
372     {
373         BOOL fIsValid = FALSE;
374         DWORD dwFoundNumbers = 0;
375         bool foundUnspecifiedComponent = false;
376         const DWORD UnspecifiedNumber = (DWORD)-1;
377         DWORD dwCurrentNumber = UnspecifiedNumber;
378         DWORD dwNumbers[iVersionParts] = {UnspecifiedNumber, UnspecifiedNumber, UnspecifiedNumber, UnspecifiedNumber};
379
380         BINDER_LOG_ENTER(W("TextualIdentityParser::ParseVersion"));
381
382         if (versionString.GetCount() > 0) {
383             SString::Iterator cursor = versionString.Begin();
384             SString::Iterator end = versionString.End();
385
386             while (cursor <= end)
387             {
388                 WCHAR wcCurrentChar = cursor[0];
389
390                 if (dwFoundNumbers >= static_cast<DWORD>(iVersionParts))
391                 {
392                     goto Exit;
393                 }
394                 else if (wcCurrentChar == W('.') || wcCurrentChar == W('\0'))
395                 {
396                     if (dwCurrentNumber == UnspecifiedNumber)
397                     {
398                         // Difference from .NET Framework, compat with Version(string) constructor: A missing version component
399                         // is considered invalid.
400                         //
401                         // Examples:
402                         //   "MyAssembly, Version=."
403                         //   "MyAssembly, Version=1."
404                         //   "MyAssembly, Version=.1"
405                         //   "MyAssembly, Version=1..1"
406                         goto Exit;
407                     }
408
409                     // Compat with .NET Framework: A value of iVersionMax is considered unspecified. Once an unspecified
410                     // component is found, validate the remaining components but consider them as unspecified as well.
411                     if (dwCurrentNumber == iVersionMax)
412                     {
413                         foundUnspecifiedComponent = true;
414                         dwCurrentNumber = UnspecifiedNumber;
415                     }
416                     else if (!foundUnspecifiedComponent)
417                     {
418                         dwNumbers[dwFoundNumbers] = dwCurrentNumber;
419                         dwCurrentNumber = UnspecifiedNumber;
420                     }
421
422                     dwFoundNumbers++;
423                 }
424                 else if ((wcCurrentChar >= W('0')) && (wcCurrentChar <= W('9')))
425                 {
426                     if (dwCurrentNumber == UnspecifiedNumber)
427                     {
428                         dwCurrentNumber = 0;
429                     }
430                     dwCurrentNumber = (dwCurrentNumber * 10) + (wcCurrentChar - W('0'));
431                     
432                     if (dwCurrentNumber > static_cast<DWORD>(iVersionMax))
433                     {
434                         goto Exit;
435                     }
436                 }
437                 else
438                 {
439                     goto Exit;
440                 }
441
442                 cursor++;
443             }
444
445             // Difference from .NET Framework: If the major or minor version are unspecified, the version is considered invalid.
446             //
447             // Examples:
448             //   "MyAssembly, Version="
449             //   "MyAssembly, Version=1"
450             //   "MyAssembly, Version=65535.1"
451             //   "MyAssembly, Version=1.65535"
452             if (dwFoundNumbers < 2 || dwNumbers[0] == UnspecifiedNumber || dwNumbers[1] == UnspecifiedNumber)
453             {
454                 goto Exit;
455             }
456
457             pAssemblyVersion->SetFeatureVersion(dwNumbers[0], dwNumbers[1]);
458             pAssemblyVersion->SetServiceVersion(dwNumbers[2], dwNumbers[3]);
459             fIsValid = TRUE;
460         }
461
462     Exit:
463         BINDER_LOG_LEAVE(W("TextualIdentityParser::ParseVersion"));
464         return fIsValid;
465     }
466
467     /* static */
468     BOOL TextualIdentityParser::HexToBlob(SString &publicKeyOrToken,
469                                           BOOL     fValidateHex,
470                                           BOOL     fIsToken,
471                                           SBuffer &publicKeyOrTokenBLOB)
472     {
473         // Optional input verification
474         if (fValidateHex)
475         {
476             if ((fIsToken && !ValidatePublicKeyToken(publicKeyOrToken)) ||
477                 (!fIsToken && !ValidatePublicKey(publicKeyOrToken)))
478             {
479                 return FALSE;
480             }
481         }
482
483         UINT ccPublicKeyOrToken = publicKeyOrToken.GetCount();
484         BYTE *pByteBLOB = publicKeyOrTokenBLOB.OpenRawBuffer(ccPublicKeyOrToken / 2);
485
486         UnicodeHexToBin(publicKeyOrToken.GetUnicode(), ccPublicKeyOrToken, pByteBLOB);
487         publicKeyOrTokenBLOB.CloseRawBuffer();
488
489         return TRUE;
490     }
491
492     /* static */
493     void TextualIdentityParser::BlobToHex(SBuffer &publicKeyOrTokenBLOB,
494                                           SString &publicKeyOrToken)
495     {
496         UINT cbPublicKeyOrTokenBLOB = publicKeyOrTokenBLOB.GetSize();
497         WCHAR *pwzpublicKeyOrToken =
498             publicKeyOrToken.OpenUnicodeBuffer(cbPublicKeyOrTokenBLOB * 2);
499
500         BinToUnicodeHex(publicKeyOrTokenBLOB, cbPublicKeyOrTokenBLOB, pwzpublicKeyOrToken);
501         publicKeyOrToken.CloseBuffer(cbPublicKeyOrTokenBLOB * 2);
502     }
503
504     BOOL TextualIdentityParser::Parse(SString &textualIdentity, BOOL fPermitUnescapedQuotes)
505     {
506         BOOL fIsValid = TRUE;
507         BINDER_LOG_ENTER(W("TextualIdentityParser::Parse(textualIdentity)"));
508         SString unicodeTextualIdentity;
509
510         // Lexer modifies input string
511         textualIdentity.ConvertToUnicode(unicodeTextualIdentity);
512         Init(unicodeTextualIdentity, TRUE /* fSupportEscaping */);
513
514         SmallStackSString currentString;
515
516         // Identity format is simple name (, attr = value)*
517         GO_IF_NOT_EXPECTED(GetNextLexeme(currentString, fPermitUnescapedQuotes), LEXEME_TYPE_STRING);
518         m_pAssemblyIdentity->m_simpleName.Set(currentString);
519         m_pAssemblyIdentity->m_simpleName.Normalize();
520         m_pAssemblyIdentity->SetHave(AssemblyIdentity::IDENTITY_FLAG_SIMPLE_NAME);
521             
522         for (;;)
523         {
524             SmallStackSString attributeString;
525             SmallStackSString valueString;
526
527             GO_IF_END_OR_NOT_EXPECTED(GetNextLexeme(currentString), LEXEME_TYPE_COMMA);
528             GO_IF_NOT_EXPECTED(GetNextLexeme(attributeString), LEXEME_TYPE_STRING);
529             GO_IF_NOT_EXPECTED(GetNextLexeme(currentString), LEXEME_TYPE_EQUALS);
530             GO_IF_NOT_EXPECTED(GetNextLexeme(valueString), LEXEME_TYPE_STRING);
531
532             if (!PopulateAssemblyIdentity(attributeString, valueString))
533             {
534                 fIsValid = FALSE;
535                 break;
536             }
537         }
538
539     Exit:
540         BINDER_LOG_LEAVE_BOOL(W("TextualIdentityParser::Parse(textualIdentity)"), fIsValid);
541         return fIsValid;
542     }
543
544     BOOL TextualIdentityParser::ParseString(SString &textualString,
545                                             SString &contentString)
546     {
547         BOOL fIsValid = TRUE;
548         BINDER_LOG_ENTER(W("TextualIdentityParser::ParseString"));
549         SString unicodeTextualString;
550
551         // Lexer modifies input string
552         textualString.ConvertToUnicode(unicodeTextualString);
553         Init(unicodeTextualString, TRUE /* fSupportEscaping */);
554
555         SmallStackSString currentString;
556         GO_IF_NOT_EXPECTED(GetNextLexeme(currentString), LEXEME_TYPE_STRING);
557
558         contentString.Set(currentString);
559         currentString.Normalize();
560
561     Exit:
562         BINDER_LOG_LEAVE_BOOL(W("TextualIdentityParser::ParseString"), fIsValid);
563         return fIsValid;
564     }
565
566     BOOL TextualIdentityParser::PopulateAssemblyIdentity(SString &attributeString,
567                                                          SString &valueString)
568     {
569         BINDER_LOG_ENTER(W("TextualIdentityParser::PopulateAssemblyIdentity"));
570         BOOL fIsValid = TRUE;
571
572         if (EqualsCaseInsensitive(attributeString, W("culture")) ||
573             EqualsCaseInsensitive(attributeString, W("language")))
574         {
575             GO_IF_SEEN(AssemblyIdentity::IDENTITY_FLAG_CULTURE);
576             GO_IF_WILDCARD(valueString);
577
578             if (!EqualsCaseInsensitive(valueString, W("neutral")))
579             {
580                 // culture/language is preserved as is
581                 m_pAssemblyIdentity->m_cultureOrLanguage.Set(valueString);
582                 m_pAssemblyIdentity->m_cultureOrLanguage.Normalize();
583             }
584
585             m_pAssemblyIdentity->SetHave(AssemblyIdentity::IDENTITY_FLAG_CULTURE);
586         }
587         else if (EqualsCaseInsensitive(attributeString, W("version")))
588         {
589             AssemblyVersion *pAssemblyVersion = &(m_pAssemblyIdentity->m_version);
590
591             GO_IF_SEEN(AssemblyIdentity::IDENTITY_FLAG_VERSION);
592             GO_IF_WILDCARD(valueString);
593
594             if (ParseVersion(valueString, pAssemblyVersion))
595             {
596                 m_pAssemblyIdentity->SetHave(AssemblyIdentity::IDENTITY_FLAG_VERSION);
597             }
598             else
599             {
600                 fIsValid = FALSE;
601             }
602         }
603         else if (EqualsCaseInsensitive(attributeString, W("publickeytoken")))
604         {
605             GO_IF_SEEN(AssemblyIdentity::IDENTITY_FLAG_PUBLIC_KEY);
606             GO_IF_SEEN(AssemblyIdentity::IDENTITY_FLAG_PUBLIC_KEY_TOKEN);
607             GO_IF_WILDCARD(valueString);
608
609             if (!EqualsCaseInsensitive(valueString, W("null")) &&
610                 !EqualsCaseInsensitive(valueString, W("neutral")))
611             {
612                 GO_IF_VALIDATE_FAILED(ValidatePublicKeyToken,
613                                       AssemblyIdentity::IDENTITY_FLAG_PUBLIC_KEY_TOKEN);
614                 HexToBlob(valueString,
615                           FALSE /* fValidateHex */,
616                           TRUE /* fIsToken */,
617                           m_pAssemblyIdentity->m_publicKeyOrTokenBLOB);
618             }
619             else
620             {
621                 m_pAssemblyIdentity->SetHave(AssemblyIdentity::IDENTITY_FLAG_PUBLIC_KEY_TOKEN_NULL);
622             }
623         }
624         else if (EqualsCaseInsensitive(attributeString, W("publickey")))
625         {
626             GO_IF_SEEN(AssemblyIdentity::IDENTITY_FLAG_PUBLIC_KEY_TOKEN);
627             GO_IF_SEEN(AssemblyIdentity::IDENTITY_FLAG_PUBLIC_KEY);
628
629             if (!EqualsCaseInsensitive(valueString, W("null")) &&
630                 !EqualsCaseInsensitive(valueString, W("neutral")))
631             {
632                 GO_IF_VALIDATE_FAILED(ValidatePublicKey, AssemblyIdentity::IDENTITY_FLAG_PUBLIC_KEY);
633                 HexToBlob(valueString,
634                           FALSE /* fValidateHex */,
635                           FALSE /* fIsToken */,
636                           m_pAssemblyIdentity->m_publicKeyOrTokenBLOB);
637             }
638         }
639         else if (EqualsCaseInsensitive(attributeString, W("processorarchitecture")))
640         {
641             PEKIND kProcessorArchitecture = peNone;
642
643             GO_IF_SEEN(AssemblyIdentity::IDENTITY_FLAG_PROCESSOR_ARCHITECTURE);
644             GO_IF_WILDCARD(valueString);
645
646             if (ValidateAndConvertProcessorArchitecture(valueString, &kProcessorArchitecture))
647             {
648                 m_pAssemblyIdentity->m_kProcessorArchitecture = kProcessorArchitecture;
649                 m_pAssemblyIdentity->SetHave(AssemblyIdentity::IDENTITY_FLAG_PROCESSOR_ARCHITECTURE);
650             }
651             else
652             {
653                 fIsValid = FALSE;
654             }
655         }
656         else if (EqualsCaseInsensitive(attributeString, W("retargetable")))
657         {
658             GO_IF_SEEN(AssemblyIdentity::IDENTITY_FLAG_RETARGETABLE);
659
660             if (EqualsCaseInsensitive(valueString, W("yes")))
661             {
662                 m_pAssemblyIdentity->SetHave(AssemblyIdentity::IDENTITY_FLAG_RETARGETABLE);
663             }
664             else if (!EqualsCaseInsensitive(valueString, W("no")))
665             {
666                 fIsValid = FALSE;
667             }
668         }
669         else if (EqualsCaseInsensitive(attributeString, W("contenttype")))
670         {
671             GO_IF_SEEN(AssemblyIdentity::IDENTITY_FLAG_CONTENT_TYPE);
672             GO_IF_WILDCARD(valueString);
673
674             if (EqualsCaseInsensitive(valueString, W("windowsruntime")))
675             {
676                 m_pAssemblyIdentity->m_kContentType = AssemblyContentType_WindowsRuntime;
677                 m_pAssemblyIdentity->SetHave(AssemblyIdentity::IDENTITY_FLAG_CONTENT_TYPE);
678             }
679             else
680             {
681                 fIsValid = FALSE;
682             }
683         }
684         else if (EqualsCaseInsensitive(attributeString, W("custom")))
685         {
686             GO_IF_SEEN(AssemblyIdentity::IDENTITY_FLAG_CUSTOM);
687
688             if (EqualsCaseInsensitive(valueString, W("null")))
689             {
690                 m_pAssemblyIdentity->SetHave(AssemblyIdentity::IDENTITY_FLAG_CUSTOM_NULL);
691             }
692             else
693             {
694                 GO_IF_VALIDATE_FAILED(ValidateHex, AssemblyIdentity::IDENTITY_FLAG_CUSTOM);
695                 HexToBlob(valueString,
696                           FALSE /* fValidateHex */,
697                           FALSE /* fIsToken */,
698                           m_pAssemblyIdentity->m_customBLOB);
699             }
700         }
701         else
702         {
703             // Fusion compat: Silently drop unknown attribute/value pair
704             BINDER_LOG_STRING(W("unknown attribute"), attributeString);
705             BINDER_LOG_STRING(W("unknown value"), valueString);
706         }
707
708     Exit:
709         BINDER_LOG_LEAVE_HR(W("TextualIdentityParser::PopulateAssemblyIdentity"),
710                             (fIsValid ? S_OK : S_FALSE));
711         return fIsValid;
712     }
713
714     /* static */
715     void TextualIdentityParser::EscapeString(SString &input,
716                                              SString &result)
717     {
718         BINDER_LOG_ENTER(W("TextualIdentityParser::EscapeString"));
719
720         BINDER_LOG_STRING(W("input"), input);
721
722         BOOL fNeedQuotes = FALSE;
723         WCHAR wcQuoteCharacter = W('"');
724
725         SmallStackSString tmpString;
726         SString::Iterator cursor = input.Begin();
727         SString::Iterator end = input.End() - 1;
728
729         // Leading/Trailing white space require quotes
730         if (IsWhitespace(cursor[0]) || IsWhitespace(end[0]))
731         {
732             fNeedQuotes = TRUE;
733         }
734
735         // Fusion textual identity compat: escape all non-quote characters even if quoted
736         while (cursor <= end)
737         {
738             WCHAR wcCurrentChar = cursor[0];
739
740             switch (wcCurrentChar)
741             {
742             case W('"'):
743             case W('\''):
744                 if (fNeedQuotes && (wcQuoteCharacter != wcCurrentChar))
745                 {
746                     tmpString.Append(wcCurrentChar);
747                 }
748                 else if (!fNeedQuotes)
749                 {
750                     fNeedQuotes = TRUE;
751                     wcQuoteCharacter = (wcCurrentChar == W('"') ? W('\'') : W('"'));
752                     tmpString.Append(wcCurrentChar);
753                 }
754                 else
755                 {
756                     tmpString.Append(W('\\'));
757                     tmpString.Append(wcCurrentChar);
758                 }
759                 break;
760             case W('='):
761             case W(','):
762             case W('\\'):
763                 tmpString.Append(W('\\'));
764                 tmpString.Append(wcCurrentChar);
765                 break;
766             case 9:
767                 tmpString.Append(W("\\t"));
768                 break;
769             case 10:
770                 tmpString.Append(W("\\n"));
771                 break;
772             case 13:
773                 tmpString.Append(W("\\r"));
774                 break;
775             default:
776                 tmpString.Append(wcCurrentChar);
777                 break;
778             }
779
780             cursor++;
781         }
782
783         if (fNeedQuotes)
784         {
785             result.Clear();
786             result.Append(wcQuoteCharacter);
787             result.Append(tmpString);
788             result.Append(wcQuoteCharacter);
789         }
790         else
791         {
792             result.Set(tmpString);
793         }
794
795         BINDER_LOG_LEAVE(W("TextualIdentityParser::EscapeString"));
796     }
797 };