Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1204 lines
35 KiB

  1. #include "nt.h"
  2. #include "ntdef.h"
  3. #include "ntrtl.h"
  4. #include "nturtl.h"
  5. #include "stdio.h"
  6. #include "sxs-rtl.h"
  7. #include "fasterxml.h"
  8. #include "skiplist.h"
  9. #include "namespacemanager.h"
  10. #include "xmlstructure.h"
  11. #undef INVALID_HANDLE_VALUE
  12. #include "windows.h"
  13. #include "sha.h"
  14. #include "sha2.h"
  15. #include "md4.h"
  16. #include "rsa.h"
  17. #include "bcl_w32unicodestringbuffer.h"
  18. #include "bcl_common.h"
  19. #include "hashers.h"
  20. #include "environment.h"
  21. #pragma warning(disable: 4200)
  22. void __cdecl wmain(int argc, wchar_t** argv);
  23. void GetSignatureOf(PCWSTR pcwsz);
  24. void ValidateSignature(PCWSTR pcwsz);
  25. template <typename TStored, typename TCount>
  26. class CArrayBlob
  27. {
  28. public:
  29. typedef BCL::CMutablePointerAndCountPair<TStored, TCount> TRange;
  30. typedef BCL::CConstantPointerAndCountPair<TStored, TCount> TConstantRange;
  31. private:
  32. TRange m_InternalRange;
  33. bool ResizeInternal(TCount cNewCount, bool fPreserve = false)
  34. {
  35. //
  36. // No previous allocation or previous too small
  37. //
  38. if ((m_InternalRange.GetPointer() == NULL) || (cNewCount > m_InternalRange.GetCount()))
  39. {
  40. //
  41. // Don't bother preserving if there was no original buffer.
  42. // Allocate, copy, reset pointers
  43. //
  44. if (fPreserve && m_InternalRange.GetPointer())
  45. {
  46. TRange NewRange(new TStored[cNewCount], cNewCount);
  47. TStored *pNewSet = NewRange.GetPointer();
  48. TStored *pOld = m_InternalRange.GetPointer();
  49. if (pNewSet == NULL)
  50. return false;
  51. for (TCount c = 0; c < m_InternalRange.GetCount(); c++)
  52. pNewSet[c] = pOld[c];
  53. delete [] m_InternalRange.GetPointer();
  54. m_InternalRange = NewRange;
  55. }
  56. //
  57. // Otherwise, don't care - free old, allocate new, swap pointers
  58. //
  59. else
  60. {
  61. TStored *pOld = m_InternalRange.GetPointer();
  62. TStored *pNew = new TStored[cNewCount];
  63. if (pOld != NULL)
  64. {
  65. delete [] pOld;
  66. pOld = NULL;
  67. }
  68. if (pNew == NULL)
  69. return false;
  70. m_InternalRange.SetPointerAndCount(pNew, cNewCount);
  71. }
  72. }
  73. return true;
  74. }
  75. public:
  76. CArrayBlob() { }
  77. ~CArrayBlob() { if (m_InternalRange.GetPointer()) { delete [] m_InternalRange.GetPointer(); } }
  78. bool Initialize(const TConstantRange &src)
  79. {
  80. if (ResizeInternal(src.GetCount(), false))
  81. {
  82. TStored *pNew = m_InternalRange.GetPointer();
  83. const TStored *pOld = src.GetPointer();
  84. for (TCount c = 0; c < src.GetCount(); c++)
  85. pNew[c] = pOld[c];
  86. return true;
  87. }
  88. else
  89. {
  90. return false;
  91. }
  92. }
  93. bool Initialize(const CArrayBlob<TStored, TCount>& Other)
  94. {
  95. if (&Other != this)
  96. {
  97. return this->Initialize(Other.m_InternalRange);
  98. }
  99. else
  100. {
  101. return true;
  102. }
  103. }
  104. bool EnsureSize(TCount c)
  105. {
  106. return ResizeInternal(c, true);
  107. }
  108. const TConstantRange &GetRange() const { return m_InternalRange; }
  109. TRange GetMutableRange() { return m_InternalRange; }
  110. };
  111. typedef CArrayBlob<BYTE, SIZE_T> CByteBlob;
  112. template <
  113. typename TStoredObject,
  114. int iInitialSize = 0
  115. >
  116. class CGrowingList : public RTL_GROWING_LIST
  117. {
  118. // Each chunk is therefore half a page.
  119. enum { eDefaultElementsPerChunk = (2048 / sizeof(TStoredObject)) };
  120. TStoredObject m_InternalObjects[iInitialSize];
  121. public:
  122. bool Initialize(PRTL_ALLOCATOR Allocator = &g_DefaultAllocator)
  123. {
  124. NTSTATUS status =
  125. RtlInitializeGrowingList(
  126. this,
  127. sizeof(TStoredObject),
  128. eDefaultElementsPerChunk,
  129. m_InternalObjects,
  130. iInitialSize * sizeof(TStoredObject),
  131. Allocator);
  132. return NT_SUCCESS(status);
  133. }
  134. ~CGrowingList() { Destroy(); }
  135. bool Destroy()
  136. {
  137. return NT_SUCCESS(RtlDestroyGrowingList(this));
  138. }
  139. inline TStoredObject &operator[](ULONG i) {
  140. TStoredObject *pvObject = NULL;
  141. NTSTATUS status = RtlIndexIntoGrowingList(this, i, (PVOID*)&pvObject, TRUE);
  142. if (!NT_SUCCESS(status)) {
  143. EXCEPTION_RECORD exr = {STATUS_INVALID_PARAMETER, 0, NULL, NULL, 3 };
  144. exr.ExceptionInformation[0] = i;
  145. exr.ExceptionInformation[1] = (ULONG_PTR)this;
  146. exr.ExceptionInformation[2] = status;
  147. RtlRaiseException(&exr);
  148. }
  149. return *pvObject;
  150. }
  151. };
  152. typedef CGrowingList<XMLDOC_ATTRIBUTE, 50> CAttributeList;
  153. class CLogicalXmlParser;
  154. class CXmlMiniTokenizer
  155. {
  156. XML_RAWTOKENIZATION_STATE RawState;
  157. NTXML_RAW_TOKEN TokenName;
  158. ULONG ulCharacter;
  159. public:
  160. CXmlMiniTokenizer() { }
  161. bool Initialize(XML_EXTENT &Source, CLogicalXmlParser &SourceParser);
  162. bool More() { return RawState.pvCursor <= RawState.pvDocumentEnd; }
  163. void Next();
  164. ULONG Name() { return TokenName; }
  165. ULONG Character() { return ulCharacter; }
  166. };
  167. class CLogicalXmlParser
  168. {
  169. public:
  170. typedef CLogicalXmlParser CThis;
  171. protected:
  172. XML_LOGICAL_STATE m_XmlState;
  173. NS_MANAGER m_Namespaces;
  174. bool m_fInitialized;
  175. CAttributeList m_Attributes;
  176. friend CXmlMiniTokenizer;
  177. public:
  178. CLogicalXmlParser() : m_fInitialized(false) { }
  179. ~CLogicalXmlParser() { this->Reset(); }
  180. bool Reset();
  181. bool Initialize(PVOID pvXmlBase, SIZE_T cbDocumentSize);
  182. CAttributeList& Attributes() { return this->m_Attributes; }
  183. bool More() const;
  184. bool Next(XMLDOC_THING &XmlDocThing);
  185. bool SkipElement(XMLDOC_ELEMENT &Element);
  186. bool IsThisNode(XMLDOC_ELEMENT &Element, PCXML_SPECIAL_STRING pName, PCXML_SPECIAL_STRING pNamespace);
  187. template <typename TStringType>
  188. bool ConvertToString(PXML_EXTENT pExtent, TStringType &Target)
  189. {
  190. bool fSuccess = false;
  191. SIZE_T cch;
  192. NTSTATUS status;
  193. if (!Target.EnsureSizeChars(pExtent->ulCharacters))
  194. goto Exit;
  195. status = RtlXmlExtentToString(&m_XmlState.ParseState.RawTokenState, pExtent, &Target, &cch);
  196. if (status == STATUS_BUFFER_TOO_SMALL)
  197. {
  198. if (!Target.EnsureSizeChars(cch))
  199. goto Exit;
  200. if (!NT_SUCCESS(status = RtlXmlExtentToString(&m_XmlState.ParseState.RawTokenState, pExtent, &Target, &cch)))
  201. goto Exit;
  202. }
  203. fSuccess = true;
  204. Exit:
  205. return fSuccess;
  206. }
  207. private:
  208. static NTSTATUS StaticCompareStrings(PVOID pv, PCXML_EXTENT pcLeft, PCXML_EXTENT pcRight, XML_STRING_COMPARE *pfMatching)
  209. {
  210. CThis *pThis = reinterpret_cast<CThis*>(pv);
  211. return RtlXmlDefaultCompareStrings(&pThis->m_XmlState.ParseState, pcLeft, pcRight, pfMatching);
  212. }
  213. static NTSTATUS FASTCALL StaticAllocate(SIZE_T cb, PVOID* ppvOutput, PVOID pvContext)
  214. {
  215. return (NULL != (*ppvOutput = HeapAlloc(GetProcessHeap(), 0, cb))) ? STATUS_SUCCESS : STATUS_NO_MEMORY;
  216. }
  217. static NTSTATUS FASTCALL StaticFree(PVOID pvPointer, PVOID pvContext)
  218. {
  219. return HeapFree(GetProcessHeap(), 0, pvPointer);
  220. }
  221. };
  222. bool
  223. CLogicalXmlParser::IsThisNode(
  224. XMLDOC_ELEMENT &Element,
  225. PCXML_SPECIAL_STRING pName,
  226. PCXML_SPECIAL_STRING pNamespace
  227. )
  228. {
  229. XML_STRING_COMPARE Comparison;
  230. if (pNamespace != NULL)
  231. {
  232. m_XmlState.ParseState.pfnCompareSpecialString(
  233. &m_XmlState.ParseState,
  234. &Element.NsPrefix,
  235. pNamespace,
  236. &Comparison);
  237. if (Comparison != XML_STRING_COMPARE_EQUALS)
  238. return false;
  239. }
  240. m_XmlState.ParseState.pfnCompareSpecialString(
  241. &m_XmlState.ParseState,
  242. &Element.Name,
  243. pName,
  244. &Comparison);
  245. return Comparison == XML_STRING_COMPARE_EQUALS;
  246. }
  247. bool CXmlMiniTokenizer::Initialize(
  248. XML_EXTENT &Source,
  249. CLogicalXmlParser &BaseParser
  250. )
  251. {
  252. NTSTATUS status;
  253. status = RtlXmlCloneRawTokenizationState(
  254. &BaseParser.m_XmlState.ParseState.RawTokenState,
  255. &RawState);
  256. ulCharacter = 0;
  257. RawState.pvLastCursor = RawState.pvCursor = Source.pvData;
  258. RawState.pvDocumentEnd = (PVOID)(((ULONG_PTR)Source.pvData) + Source.cbData);
  259. return NT_SUCCESS(status);
  260. }
  261. void CXmlMiniTokenizer::Next()
  262. {
  263. ASSERT(this->More());
  264. ASSERT(RawState.cbBytesInLastRawToken == RawState.DefaultCharacterSize);
  265. ASSERT(RawState.NextCharacterResult == STATUS_SUCCESS);
  266. this->ulCharacter = RawState.pfnNextChar(&RawState);
  267. if ((ulCharacter == 0) && !NT_SUCCESS(RawState.NextCharacterResult)) {
  268. this->TokenName = NTXML_RAWTOKEN_ERROR;
  269. return;
  270. }
  271. this->TokenName = _RtlpDecodeCharacter(this->ulCharacter);
  272. RawState.pvCursor = (PVOID)(((ULONG_PTR)RawState.pvCursor) + RawState.cbBytesInLastRawToken);
  273. if (RawState.cbBytesInLastRawToken != RawState.DefaultCharacterSize)
  274. RawState.cbBytesInLastRawToken = RawState.DefaultCharacterSize;
  275. }
  276. //
  277. // The default digestion operation is to digest with UTF-8 encoding
  278. // of characters.
  279. //
  280. class CUTF8BaseDigester
  281. {
  282. CHashObject &m_Context;
  283. CLogicalXmlParser *m_pXmlParser;
  284. protected:
  285. enum { eMaxCharacterEncodingBytes = 3 };
  286. //
  287. // These are 'special' XML characters that are already UTF-8
  288. // (or whatever) encoded. These can be hashed as-is
  289. //
  290. class XmlSpecialMarkers {
  291. public:
  292. static CHAR s_XmlOpenTag[];
  293. static CHAR s_XmlCloseTag[];
  294. static CHAR s_XmlCloseEmptyTag[];
  295. static CHAR s_XmlOpenCloseTag[];
  296. static CHAR s_XmlNsDelimiter[];
  297. static CHAR s_XmlWhitespace[];
  298. static CHAR s_XmlEqualsDQuote[];
  299. static CHAR s_XmlDQuote[];
  300. };
  301. //
  302. // This encoder uses UTF-8; feel free to derive from this class and implement
  303. // your own encoding; do -not- make this virtual, force the compiler to use
  304. // yours so you get the inline/fastcall benefits. CDige
  305. //
  306. inline SIZE_T __fastcall EncodeCharacter(ULONG ucs2Char, PBYTE pbTarget)
  307. {
  308. if (ucs2Char <= 0x7f)
  309. {
  310. pbTarget[0] = (BYTE)(ucs2Char & 0x7f);
  311. return 1;
  312. }
  313. else if (ucs2Char <= 0x7ff)
  314. {
  315. pbTarget[0] = (BYTE)(0xC0 | ((ucs2Char >> 6) & 0x1f));
  316. pbTarget[1] = (BYTE)(0x80 | (ucs2Char & 0x3F));
  317. return 2;
  318. }
  319. else if (ucs2Char <= 0x7fff)
  320. {
  321. pbTarget[0] = (BYTE)(0xE0 | ((ucs2Char >> 12) & 0xF));
  322. pbTarget[1] = (BYTE)(0x80 | ((ucs2Char >> 6) & 0x3F));
  323. pbTarget[2] = (BYTE)(0x80 | (ucs2Char & 0x3F));
  324. return 3;
  325. }
  326. else
  327. {
  328. return 0;
  329. }
  330. }
  331. inline bool __fastcall EncodeAndHash(const ULONG *ucs2Char, SIZE_T cChars)
  332. {
  333. BYTE bDumpArea[eMaxCharacterEncodingBytes];
  334. SIZE_T cCursor = 0;
  335. while (cCursor < cChars)
  336. {
  337. const SIZE_T cThisSize = EncodeCharacter(ucs2Char[cCursor++], bDumpArea);
  338. if (cThisSize == 0)
  339. return false;
  340. AddHashData(bDumpArea, cThisSize);
  341. }
  342. return true;
  343. }
  344. bool HashDirectly(XML_EXTENT &eExtent)
  345. {
  346. ASSERT(eExtent.Encoding == XMLEF_UTF_8_OR_ASCII);
  347. if (eExtent.Encoding != XMLEF_UTF_8_OR_ASCII)
  348. return false;
  349. return AddHashData(eExtent.pvData, eExtent.cbData);
  350. }
  351. //
  352. // This digests an element open tag as follows:
  353. //
  354. // Element, no attributes: <{ns:}name>
  355. // Empty element, no attributes: <{ns:}name></{ns:}name>
  356. // Element, attributes: <{ns:}name [{atns:}attrib="text"]xN>
  357. // Empty element, attributes: <{ns:}name [{atns:}attrib="text"]xN/>
  358. //
  359. template <CHAR *szChars>
  360. FastHash() {
  361. #define IS_MARKER(q) if (szChars == XmlSpecialMarkers::q) { AddHashData(XmlSpecialMarkers::q, NUMBER_OF(XmlSpecialMarkers::q)); }
  362. IS_MARKER(s_XmlOpenTag) else
  363. IS_MARKER(s_XmlCloseTag) else
  364. IS_MARKER(s_XmlCloseEmptyTag) else
  365. IS_MARKER(s_XmlOpenCloseTag) else
  366. IS_MARKER(s_XmlNsDelimiter) else
  367. IS_MARKER(s_XmlWhitespace) else
  368. IS_MARKER(s_XmlEqualsDQuote) else
  369. IS_MARKER(s_XmlDQuote);
  370. }
  371. BYTE m_bHashPrebuffer[64];
  372. SIZE_T m_cHashPrebufferUsed;
  373. // This could be more intelligent about ensuring that we fill the buffer up from the input
  374. // before hashing, but it seems like any sort of buffering at all is a huge win.
  375. inline bool __fastcall AddHashDataInternal(PVOID pvData, SIZE_T cbData) {
  376. // If this would overflow the internal buffer, or the input size is larger than
  377. // the available buffer, then always flush.
  378. if (((m_cHashPrebufferUsed + cbData) > NUMBER_OF(m_bHashPrebuffer)) || (cbData > NUMBER_OF(m_bHashPrebuffer))) {
  379. m_Context.Hash(CEnv::CByteRegion(m_bHashPrebuffer, m_cHashPrebufferUsed));
  380. m_cHashPrebufferUsed = 0;
  381. }
  382. // The input size was too large to fit in the prebuffer, hash it directly
  383. if (cbData > NUMBER_OF(m_bHashPrebuffer))
  384. {
  385. if (CEnv::DidFail(m_Context.Hash(CEnv::CByteRegion((PBYTE)pvData, m_cHashPrebufferUsed))))
  386. return false;
  387. }
  388. // Otherwise, copy the data into the prebuffer, update the used size
  389. else
  390. {
  391. memcpy(&m_bHashPrebuffer[m_cHashPrebufferUsed], pvData, cbData);
  392. m_cHashPrebufferUsed += cbData;
  393. }
  394. return true;
  395. }
  396. inline bool __fastcall AddHashData(PVOID pvData, SIZE_T cData) { return AddHashDataInternal(pvData, cData); }
  397. template <typename T> inline bool __fastcall AddHashData(T *pData, SIZE_T cData) { return AddHashDataInternal((PVOID)pData, cData * sizeof(T)); }
  398. template <typename T> inline bool __fastcall AddHashData(T cSingleData) { return AddHashDataInternal(&cSingleData, sizeof(T)); }
  399. bool Digest(XMLDOC_ELEMENT &Element, CAttributeList &Attributes)
  400. {
  401. bool fSuccess = false;
  402. FastHash<XmlSpecialMarkers::s_XmlOpenTag>();
  403. if (Element.NsPrefix.ulCharacters != 0)
  404. {
  405. if (!Digest(Element.NsPrefix, false))
  406. goto Exit;
  407. FastHash<XmlSpecialMarkers::s_XmlNsDelimiter>();
  408. }
  409. if (!Digest(Element.Name, false))
  410. goto Exit;
  411. //
  412. // Now digest the attributes, ensuring that a whitespace appears between them. The
  413. // initial whitespace ensures one between the element name and the first attribute
  414. //
  415. for (ULONG ul = 0; ul < Element.ulAttributeCount; ul++)
  416. {
  417. XMLDOC_ATTRIBUTE &Attrib = Attributes[ul];
  418. FastHash<XmlSpecialMarkers::s_XmlWhitespace>();
  419. if (!Digest(Attrib))
  420. goto Exit;
  421. }
  422. FastHash<XmlSpecialMarkers::s_XmlCloseTag>();
  423. //
  424. // Empty elements implicitly get a </close>, so do the above stuff again
  425. //
  426. if (Element.fElementEmpty)
  427. {
  428. FastHash<XmlSpecialMarkers::s_XmlOpenCloseTag>();
  429. if (Element.NsPrefix.ulCharacters != 0)
  430. {
  431. if (!Digest(Element.NsPrefix, false))
  432. goto Exit;
  433. FastHash<XmlSpecialMarkers::s_XmlNsDelimiter>();
  434. }
  435. if (!Digest(Element.Name, false))
  436. goto Exit;
  437. FastHash<XmlSpecialMarkers::s_XmlCloseTag>();
  438. }
  439. fSuccess = true;
  440. Exit:
  441. return fSuccess;
  442. }
  443. //
  444. // Attributes get digested as:
  445. //
  446. // {attrnamespace:}attribname="attribvalue"
  447. //
  448. // where attribvalue is treated like pcdata for whitespace-compression purposes
  449. //
  450. bool Digest(XMLDOC_ATTRIBUTE &Attribute)
  451. {
  452. bool fSuccess = false;
  453. if (Attribute.NsPrefix.ulCharacters != 0)
  454. {
  455. if (!Digest(Attribute.NsPrefix, false))
  456. goto Exit;
  457. FastHash<XmlSpecialMarkers::s_XmlNsDelimiter>();
  458. }
  459. if (!Digest(Attribute.Name, false))
  460. goto Exit;
  461. FastHash<XmlSpecialMarkers::s_XmlEqualsDQuote>();
  462. if (!Digest(Attribute.Value, true))
  463. goto Exit;
  464. FastHash<XmlSpecialMarkers::s_XmlDQuote>();
  465. fSuccess = true;
  466. Exit:
  467. return fSuccess;
  468. }
  469. //
  470. // Digests the xml extent in question. If the xml parser is in UTF-8 mode, and we're
  471. // doing CData mode (ie: pcdata == false), then we can simply throw the raw bits through
  472. // the hasher. Otherwise, we have to do whitespace compression and whatnot.
  473. //
  474. bool Digest(XML_EXTENT &CData, bool WhitespaceCompression)
  475. {
  476. CXmlMiniTokenizer MiniTokenizer;
  477. bool fFoundSomethingLast = false;
  478. bool fSuccess = false;
  479. if (CData.cbData == 0)
  480. return true;
  481. //
  482. // PCDATA (the stuff in attributes, between elements, etc.) gets
  483. // whitespace-compressed by the following rules:
  484. //
  485. // Complete-whitespace hyperspace chunk becomes "nothing"
  486. // - ex: <foo> <bar> Zero bytes
  487. // - ex: <foo></foo> Zero bytes (see <foo/> in the ELEMENT digester above
  488. // - ex: <foo> f </foo> Effectively, "f"
  489. // - ex: <foo> a b </foo> Effectively, "a b"
  490. // - ex: <foo> a </foo>
  491. //
  492. MiniTokenizer.Initialize(CData, *this->m_pXmlParser);
  493. #define FLUSH_BUFFER(buff, used) do { \
  494. if (!EncodeAndHash((buff), (used))) goto Exit; \
  495. (used) = 0; \
  496. } while (0)
  497. #define CHECK_FLUSH_BUFFER(buff, used, toaddsize) do { \
  498. if (((used) + (toaddsize)) >= NUMBER_OF(buff)) { \
  499. FLUSH_BUFFER(buff, used); \
  500. (used) = 0; \
  501. } } while (0)
  502. #define ADD_BUFFER(buff, used, toadd, toaddsize) do { \
  503. CHECK_FLUSH_BUFFER(buff, used, toaddsize); \
  504. ASSERT(toaddsize < NUMBER_OF(buff)); \
  505. memcpy(&((buff)[used]), (toadd), sizeof(toadd[0]) * toaddsize); \
  506. } while (0)
  507. #define ADD_SINGLE(buff, used, toadd) do { \
  508. CHECK_FLUSH_BUFFER(buff, used, 1); \
  509. (buff)[(used)++] = toadd; \
  510. } while (0)
  511. if (WhitespaceCompression)
  512. {
  513. ULONG ulDecodedBuffer[128];
  514. SIZE_T cDecodedUsed = 0;
  515. MiniTokenizer.Next();
  516. do
  517. {
  518. //
  519. // Skip past all whitespace
  520. //
  521. while ((MiniTokenizer.More() && (MiniTokenizer.Name() == NTXML_RAWTOKEN_WHITESPACE)))
  522. MiniTokenizer.Next();
  523. //
  524. // Stop if we ran out
  525. //
  526. if (!MiniTokenizer.More())
  527. break;
  528. //
  529. // Now, if we'd found something before, add a whitespace marker onto the
  530. // list of items to encode
  531. //
  532. if (fFoundSomethingLast)
  533. {
  534. FLUSH_BUFFER(ulDecodedBuffer, cDecodedUsed);
  535. FastHash<XmlSpecialMarkers::s_XmlWhitespace>();
  536. }
  537. //
  538. // Spin through the elements that are now present, up to another whitespace
  539. //
  540. while (MiniTokenizer.More() && (MiniTokenizer.Name() != NTXML_RAWTOKEN_WHITESPACE))
  541. {
  542. if (!fFoundSomethingLast)
  543. fFoundSomethingLast = true;
  544. ADD_SINGLE(ulDecodedBuffer, cDecodedUsed, MiniTokenizer.Character());
  545. MiniTokenizer.Next();
  546. }
  547. }
  548. while (MiniTokenizer.More());
  549. //
  550. // Flush out leftover elements
  551. //
  552. if (cDecodedUsed != 0)
  553. {
  554. EncodeAndHash(ulDecodedBuffer, cDecodedUsed);
  555. }
  556. }
  557. else
  558. {
  559. if (CData.Encoding == XMLEF_UTF_8_OR_ASCII)
  560. {
  561. HashDirectly(CData);
  562. }
  563. else
  564. {
  565. ULONG ulBuffer[50];
  566. SIZE_T cTotal = 0;
  567. MiniTokenizer.Next();
  568. while (MiniTokenizer.More()) {
  569. ADD_SINGLE(ulBuffer, cTotal, MiniTokenizer.Character());
  570. MiniTokenizer.Next();
  571. }
  572. if (cTotal > 0)
  573. {
  574. EncodeAndHash(ulBuffer, cTotal);
  575. }
  576. }
  577. }
  578. fSuccess = true;
  579. Exit:
  580. return fSuccess;
  581. }
  582. //
  583. // To digest an end element, we use </{ns:}element>
  584. //
  585. bool Digest(XMLDOC_ENDELEMENT &EndElement)
  586. {
  587. bool fSuccess = false;
  588. FastHash<XmlSpecialMarkers::s_XmlOpenCloseTag>();
  589. if (EndElement.NsPrefix.ulCharacters != 0)
  590. {
  591. if (!Digest(EndElement.NsPrefix, true))
  592. goto Exit;
  593. FastHash<XmlSpecialMarkers::s_XmlNsDelimiter>();
  594. }
  595. if (!Digest(EndElement.Name, true))
  596. goto Exit;
  597. FastHash<XmlSpecialMarkers::s_XmlCloseTag>();
  598. fSuccess = true;
  599. Exit:
  600. return fSuccess;
  601. }
  602. const static CEnv::CConstantUnicodeStringPair DigesterIdentifier;
  603. public:
  604. CUTF8BaseDigester(CHashObject &Hasher) : m_cHashPrebufferUsed(0), m_Context(Hasher), m_pXmlParser(NULL) { }
  605. static const CEnv::CConstantUnicodeStringPair &GetDigesterIdentifier() { return DigesterIdentifier; }
  606. void SetXmlParser(CLogicalXmlParser *pSourceParser) { this->m_pXmlParser = pSourceParser; }
  607. bool Digest(XMLDOC_THING &Thing, CAttributeList &Attributes)
  608. {
  609. switch (Thing.ulThingType)
  610. {
  611. case XMLDOC_THING_ELEMENT:
  612. return Digest(Thing.Element, Attributes);
  613. break;
  614. case XMLDOC_THING_HYPERSPACE:
  615. return Digest(Thing.Hyperspace, true);
  616. break;
  617. case XMLDOC_THING_CDATA:
  618. return HashDirectly(Thing.CDATA);
  619. case XMLDOC_THING_END_ELEMENT:
  620. return Digest(Thing.EndElement);
  621. }
  622. return false;
  623. }
  624. bool Finalize() {
  625. if (m_cHashPrebufferUsed > 0) {
  626. return !CEnv::DidFail(m_Context.Hash(CEnv::CByteRegion(m_bHashPrebuffer, m_cHashPrebufferUsed)));
  627. }
  628. return true;
  629. }
  630. };
  631. const CEnv::CConstantUnicodeStringPair CUTF8BaseDigester::DigesterIdentifier = CEnv::CConstantUnicodeStringPair(L"sxs-dsig-ops#default-digestion", NUMBER_OF(L"default-digestion"));
  632. CHAR CUTF8BaseDigester::XmlSpecialMarkers::s_XmlOpenTag[] = { '<' };
  633. CHAR CUTF8BaseDigester::XmlSpecialMarkers::s_XmlCloseTag[] = { '>' };
  634. CHAR CUTF8BaseDigester::XmlSpecialMarkers::s_XmlCloseEmptyTag[] = { '/', '>' };
  635. CHAR CUTF8BaseDigester::XmlSpecialMarkers::s_XmlOpenCloseTag[] = { '<', '/' };
  636. CHAR CUTF8BaseDigester::XmlSpecialMarkers::s_XmlNsDelimiter[] = { ':' };
  637. CHAR CUTF8BaseDigester::XmlSpecialMarkers::s_XmlWhitespace[] = { ' ' };
  638. CHAR CUTF8BaseDigester::XmlSpecialMarkers::s_XmlEqualsDQuote[] = { '=', '\"' };
  639. CHAR CUTF8BaseDigester::XmlSpecialMarkers::s_XmlDQuote[] = { '\"' };
  640. const XML_SPECIAL_STRING c_ss_Signature = MAKE_SPECIAL_STRING("Signature");
  641. const XML_SPECIAL_STRING c_ss_SignedInfo = MAKE_SPECIAL_STRING("SignedInfo");
  642. const XML_SPECIAL_STRING c_ss_SignatureValue = MAKE_SPECIAL_STRING("SignatureValue");
  643. const XML_SPECIAL_STRING c_ss_KeyInfo = MAKE_SPECIAL_STRING("KeyInfo");
  644. const XML_SPECIAL_STRING c_ss_Object = MAKE_SPECIAL_STRING("Object");
  645. const XML_SPECIAL_STRING c_ss_XmlNsSignature = MAKE_SPECIAL_STRING("http://www.w3.org/2000/09/xmldsig#");
  646. bool operator==(const XMLDOC_ELEMENT &left, const XMLDOC_ELEMENT &right)
  647. {
  648. return (left.Name.pvData == right.Name.pvData);
  649. }
  650. void ReverseMemCpy(
  651. PVOID pvTarget,
  652. const void* pcvSource,
  653. SIZE_T cbBytes
  654. )
  655. {
  656. const BYTE *pbSource = ((const BYTE*)pcvSource) + cbBytes - 1;
  657. PBYTE pbTarget = (PBYTE)pvTarget;
  658. while (cbBytes--)
  659. *pbTarget++ = *pbSource--;
  660. }
  661. CEnv::StatusCode
  662. EncodePKCS1Hash(
  663. const CHashObject &SourceHash,
  664. SIZE_T cbPubKeyDataLen,
  665. CByteBlob &Output
  666. )
  667. {
  668. const CEnv::CConstantByteRegion &HashOid = SourceHash.GetOid();
  669. CEnv::CConstantByteRegion HashData;
  670. CEnv::CByteRegion OutputRange;
  671. CEnv::StatusCode Result = CEnv::SuccessCode;
  672. PBYTE pbWorking;
  673. if (!Output.EnsureSize(cbPubKeyDataLen)) {
  674. Result = CEnv::OutOfMemory;
  675. goto Exit;
  676. }
  677. if (CEnv::DidFail(Result = SourceHash.GetValue(HashData)))
  678. goto Exit;
  679. OutputRange = Output.GetMutableRange();
  680. pbWorking = OutputRange.GetPointer();
  681. //
  682. // Set the well-known bytes
  683. //
  684. pbWorking[cbPubKeyDataLen - 1] = 0x01;
  685. memset(pbWorking, 0xff, cbPubKeyDataLen - 1);
  686. //
  687. // Copy the source hash data 'backwards'
  688. //
  689. ReverseMemCpy(pbWorking, HashData.GetPointer(), HashData.GetCount());
  690. pbWorking += HashData.GetCount();
  691. memcpy(pbWorking, HashOid.GetPointer(), HashOid.GetCount());
  692. pbWorking += HashOid.GetCount();
  693. *pbWorking = 0;
  694. Result = CEnv::SuccessCode;
  695. Exit:
  696. return Result;
  697. }
  698. bool SignHashContext(
  699. const CHashObject &SourceHash,
  700. LPBSAFE_PRV_KEY lpPrivateKey,
  701. LPBSAFE_PUB_KEY lpPublicKey,
  702. CByteBlob &Output
  703. )
  704. {
  705. CEnv::CByteRegion OutputRange = Output.GetMutableRange();
  706. SIZE_T cbSignatureSize = (lpPublicKey->bitlen+7)/8;
  707. PBYTE pbInput, pbWork, pbSigT;
  708. CByteBlob WorkingBlob;
  709. //
  710. // Set up and clear the output buffer
  711. //
  712. Output.EnsureSize(lpPublicKey->keylen);
  713. memset(OutputRange.GetPointer(), 0, OutputRange.GetCount());
  714. //
  715. // Put this object into a PKCS1-compliant structure for signing
  716. //
  717. if (EncodePKCS1Hash(SourceHash, lpPublicKey->keylen, WorkingBlob))
  718. {
  719. if (BSafeDecPrivate(lpPrivateKey, WorkingBlob.GetMutableRange().GetPointer(), OutputRange.GetPointer()))
  720. {
  721. return true;
  722. }
  723. }
  724. return false;
  725. }
  726. bool VerifySignature(
  727. const CHashObject &SourceHash,
  728. const CEnv::CConstantByteRegion &Signature,
  729. LPBSAFE_PUB_KEY lpPublicKey
  730. )
  731. {
  732. return true;
  733. }
  734. /*
  735. Validating a document signature is pretty easy from our current point of view.
  736. You spin over the contents of the document, hashing all the hashable stuff.
  737. At some point, you should find a <Signature> element, inside which is a <SignedInfo>
  738. element. You should start another hashing context over the contents of the
  739. <SignedInfo> data. When that's done, you should have found:
  740. <Signature>
  741. <SignedInfo>
  742. <CanonicalizationMethod Algorithm="sidebyside-manifest-canonicalizer"/>
  743. <Reference>
  744. <Transforms Algorithm="sidebyside-manifest-digestion#standard-transform"/>
  745. <DigestMethod Algorithm="sidebyside-manifest-digestion#sha1"/>
  746. <DigestValue>...</DigestValue>
  747. </Reference>
  748. <SignatureMethod Algorithm="sidebyside-manifest-digestion#dsa-sha1"/>
  749. </SignedInfo>
  750. <SignatureValue>(signed data goes here)</SignatureValue>
  751. <KeyInfo>...</KeyInfo>
  752. </Signature>
  753. */
  754. bool Base64EncodeBytes(
  755. const CEnv::CConstantByteRegion &Bytes,
  756. CEnv::CStringBuffer &Output
  757. )
  758. {
  759. SIZE_T cCharsNeeded = 0;
  760. CArrayBlob<WCHAR, SIZE_T> B64Encoding;
  761. RtlBase64Encode((PVOID)Bytes.GetPointer(), Bytes.GetCount(), NULL, &cCharsNeeded);
  762. if (!B64Encoding.EnsureSize(cCharsNeeded))
  763. return false;
  764. RtlBase64Encode(
  765. (PVOID)Bytes.GetPointer(),
  766. Bytes.GetCount(),
  767. B64Encoding.GetMutableRange().GetPointer(),
  768. &cCharsNeeded);
  769. if (!Output.Assign(B64Encoding.GetRange()))
  770. return false;
  771. return true;
  772. }
  773. bool
  774. CreateSignatureElement(
  775. CEnv::CStringBuffer &Target,
  776. const CEnv::CConstantByteRegion &HashValue,
  777. const CEnv::CConstantUnicodeStringPair &DigestMethod
  778. )
  779. {
  780. static const WCHAR chFormatString[] =
  781. L"<SignedInfo>\r\n"
  782. L" <CanonicalizationMethod Algorithm='%ls'/>\r\n"
  783. L" <Reference>\r\n"
  784. L" <DigestMethod Algorithm='%ls'/>\r\n"
  785. L" <DigestValue>%ls</DigestValue>\r\n"
  786. L" </Reference>\r\n"
  787. L" <SignatureMethod Algorithm='%ls'/>\r\n"
  788. L"</SignedInfo>\r\n";
  789. Target.Clear();
  790. return true;
  791. }
  792. template <typename TDigester, typename THashContext>
  793. bool HashXmlSection(
  794. CEnv::CConstantByteRegion &XmlRange,
  795. CByteBlob &HashValue
  796. )
  797. {
  798. THashContext HashContext;
  799. TDigester DigestEngine(HashContext);
  800. CEnv::CConstantByteRegion InternalHashValue;
  801. CLogicalXmlParser Parser;
  802. Parser.Initialize((PVOID)XmlRange.GetPointer(), XmlRange.GetCount());
  803. DigestEngine.SetXmlParser(&Parser);
  804. HashContext.Initialize();
  805. do
  806. {
  807. XMLDOC_THING ThisThing;
  808. if (!Parser.Next(ThisThing))
  809. break;
  810. //
  811. // If this thing is a "signature" element, then we need
  812. // to skip its body
  813. //
  814. if ((ThisThing.ulThingType == XMLDOC_THING_ELEMENT) &&
  815. Parser.IsThisNode(ThisThing.Element, &c_ss_Signature, &c_ss_XmlNsSignature))
  816. {
  817. Parser.SkipElement(ThisThing.Element);
  818. }
  819. //
  820. // Otherwise, everybody gets hashed
  821. //
  822. else
  823. {
  824. DigestEngine.Digest(ThisThing, Parser.Attributes());
  825. }
  826. }
  827. while (Parser.More());
  828. DigestEngine.Finalize();
  829. HashContext.Finalize();
  830. HashContext.GetValue(InternalHashValue);
  831. HashValue.Initialize(InternalHashValue);
  832. return true;
  833. }
  834. void GetSignatureOf(PCWSTR pcwsz)
  835. {
  836. PVOID pvFileBase;
  837. SIZE_T cbFileBase;
  838. NTSTATUS status;
  839. UNICODE_STRING usFilePath;
  840. XMLDOC_THING XmlThing = { XMLDOC_THING_PROCESSINGINSTRUCTION };
  841. LARGE_INTEGER liStart, liEnd, liTotal;
  842. DWORD dwBitLength = 2048;
  843. DWORD dwPubKeySize, dwPrivKeySize;
  844. LPBSAFE_PUB_KEY pPubKey;
  845. LPBSAFE_PRV_KEY pPriKey;
  846. CByteBlob SignedResult;
  847. const int iCount = 100;
  848. CEnv::CConstantByteRegion XmlSection;
  849. status = RtlOpenAndMapEntireFile(pcwsz, &pvFileBase, &cbFileBase);
  850. if (!NT_SUCCESS(status)) {
  851. return;
  852. }
  853. XmlSection.SetPointerAndCount((PBYTE)pvFileBase, cbFileBase);
  854. //
  855. // Print, then sign the hash
  856. //
  857. BSafeComputeKeySizes(&dwPubKeySize, &dwPrivKeySize, &dwBitLength);
  858. pPubKey = (LPBSAFE_PUB_KEY)HeapAlloc(GetProcessHeap(), 0, dwPubKeySize);
  859. pPriKey = (LPBSAFE_PRV_KEY)HeapAlloc(GetProcessHeap(), 0, dwPrivKeySize);
  860. BSafeMakeKeyPair(pPubKey, pPriKey, dwBitLength);
  861. liTotal.QuadPart = 0;
  862. for (int i = 0; i < iCount; i++)
  863. {
  864. QueryPerformanceCounter(&liStart);
  865. CEnv::CStringBuffer SignatureBlob;
  866. CEnv::CConstantByteRegion HashResults;
  867. CByteBlob HashValue;
  868. HashXmlSection<CUTF8BaseDigester, CSha1HashObject>(XmlSection, HashValue);
  869. QueryPerformanceCounter(&liEnd);
  870. liTotal.QuadPart += liEnd.QuadPart - liStart.QuadPart;
  871. if (i == 0)
  872. {
  873. HashResults = HashValue.GetRange();
  874. printf("\r\n");
  875. for (SIZE_T c = 0; c < HashResults.GetCount(); c++) {
  876. printf("%02x", HashResults.GetPointer()[c]);
  877. }
  878. printf("\r\n");
  879. }
  880. }
  881. QueryPerformanceFrequency(&liEnd);
  882. wprintf(
  883. L"%I64d cycles, %f seconds",
  884. liTotal.QuadPart / iCount,
  885. (double)((((double)liTotal.QuadPart) / iCount) / ((double)liEnd.QuadPart)));
  886. RtlUnmapViewOfFile(pvFileBase);
  887. }
  888. void __cdecl wmain(int argc, wchar_t *argv[])
  889. {
  890. GetSignatureOf(argv[1]);
  891. }
  892. bool CLogicalXmlParser::Reset()
  893. {
  894. bool fSuccess = true;
  895. if (!m_fInitialized)
  896. return true;
  897. if (!NT_SUCCESS(RtlNsDestroy(&m_Namespaces)))
  898. fSuccess = false;
  899. if (!NT_SUCCESS(RtlXmlDestroyNextLogicalThing(&m_XmlState)))
  900. fSuccess = false;
  901. m_fInitialized = false;
  902. return fSuccess;
  903. }
  904. bool CLogicalXmlParser::Initialize(PVOID pvXmlBase, SIZE_T cbDocumentSize)
  905. {
  906. NTSTATUS status;
  907. ASSERT(!m_fInitialized);
  908. RTL_ALLOCATOR Alloc = { StaticAllocate, StaticFree, this };
  909. status = RtlNsInitialize(
  910. &m_Namespaces,
  911. StaticCompareStrings, this,
  912. &Alloc);
  913. if (!NT_SUCCESS(status)) {
  914. return false;
  915. }
  916. status = RtlXmlInitializeNextLogicalThing(
  917. &m_XmlState,
  918. pvXmlBase,
  919. cbDocumentSize,
  920. &Alloc);
  921. if (!NT_SUCCESS(status)) {
  922. RtlNsDestroy(&m_Namespaces);
  923. return false;
  924. }
  925. if (!m_Attributes.Initialize()) {
  926. RtlNsDestroy(&m_Namespaces);
  927. RtlXmlDestroyNextLogicalThing(&m_XmlState);
  928. return false;
  929. }
  930. m_fInitialized = true;
  931. return true;
  932. }
  933. bool CLogicalXmlParser::More() const
  934. {
  935. return m_XmlState.ParseState.PreviousState != XTSS_STREAM_END;
  936. }
  937. bool CLogicalXmlParser::Next(XMLDOC_THING &XmlDocThing)
  938. {
  939. NTSTATUS status;
  940. status = RtlXmlNextLogicalThing(&m_XmlState, &m_Namespaces, &XmlDocThing, &m_Attributes);
  941. return NT_SUCCESS(status);
  942. }
  943. //
  944. // This parades through the document looking for the "end" of this element.
  945. // When this function returns, the following "Next" call will get the document
  946. // chunklet that's after the closing of the element.
  947. //
  948. bool CLogicalXmlParser::SkipElement(XMLDOC_ELEMENT &Element)
  949. {
  950. if (Element.fElementEmpty)
  951. {
  952. return true;
  953. }
  954. else
  955. {
  956. XMLDOC_THING NextThing;
  957. do
  958. {
  959. if (!this->Next(NextThing))
  960. return false;
  961. if ((NextThing.ulThingType == XMLDOC_THING_END_ELEMENT) &&
  962. (NextThing.EndElement.OpeningElement == Element))
  963. {
  964. break;
  965. }
  966. }
  967. while (this->More());
  968. return true;
  969. }
  970. }