DebugLoggerMask.cs 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390
  1. using System.Globalization;
  2. using System.Xml;
  3. using System.Text;
  4. namespace Wayne.Lib.Log
  5. {
  6. /// <summary>
  7. /// Static support class for managing masking of debug logs.
  8. /// </summary>
  9. public static class DebugLoggerMask
  10. {
  11. #region Fields
  12. private static bool forceMask;
  13. #endregion
  14. #region XmlWriter Support
  15. /// <summary>
  16. /// Writes processing instructions to an XML document to mask a node.
  17. /// The masking will be active from this point forward in the XML document (until the end or the instruction is redefined).
  18. /// </summary>
  19. /// <param name="xmlWriter">The XML writer.</param>
  20. /// <param name="nodeName">The name of the XML node.</param>
  21. /// <param name="maskKind">The type of masking to use.</param>
  22. public static void MaskXmlNode(XmlWriter xmlWriter, string nodeName, DebugLogMaskKind maskKind)
  23. {
  24. xmlWriter.WriteProcessingInstruction(XmlLogObject.XmlMaskNode,
  25. string.Concat(XmlLogObject.XmlNodeName, "=\"", nodeName, "\" ",
  26. XmlLogObject.XmlMaskKind, "=\"", maskKind, "\""));
  27. }
  28. /// <summary>
  29. /// Writes processing instructions to an XML document to mask the next node.
  30. /// </summary>
  31. /// <param name="xmlWriter">The XML writer.</param>
  32. /// <param name="maskKind">The type of masking to use.</param>
  33. public static void MaskNextXmlNode(XmlWriter xmlWriter, DebugLogMaskKind maskKind)
  34. {
  35. xmlWriter.WriteProcessingInstruction(XmlLogObject.XmlMaskNode,
  36. string.Concat(XmlLogObject.XmlNodeName, "=\"", XmlLogObject.XmlMaskNextNodeName, "\" ",
  37. XmlLogObject.XmlMaskKind, "=\"", maskKind, "\""));
  38. }
  39. /// <summary>
  40. /// Writes processing instructions to an XML document to mask an attribute.
  41. /// The masking will be active from this point forward in the XML document (until the end or the instruction is redefined).
  42. /// </summary>
  43. /// <param name="xmlWriter">The XML writer.</param>
  44. /// <param name="nodeName">The name of the XML node.</param>
  45. /// <param name="attributeName">The name of the node attribute.</param>
  46. /// <param name="maskKind">The type of masking to use.</param>
  47. public static void MaskXmlAttribute(XmlWriter xmlWriter, string nodeName, string attributeName, DebugLogMaskKind maskKind)
  48. {
  49. xmlWriter.WriteProcessingInstruction(XmlLogObject.XmlMaskAttribute,
  50. string.Concat(XmlLogObject.XmlNodeName, "=\"", nodeName, "\" ",
  51. XmlLogObject.XmlAttributeName, "=\"", attributeName, "\" ",
  52. XmlLogObject.XmlMaskKind, "=\"", maskKind, "\""));
  53. }
  54. #endregion
  55. #region XmlDocument Support
  56. /// <summary>
  57. /// Returns the XmlNode needed to be added to an XML document to mask a node.
  58. /// The masking will be active from the point forward where the node is inserted (until the end or the instruction is redefined).
  59. /// </summary>
  60. /// <param name="xmlDocument">The XML document.</param>
  61. /// <param name="nodeName">The name of the XML node.</param>
  62. /// <param name="maskKind">The type of masking to use.</param>
  63. public static XmlProcessingInstruction CreateXmlNodeToMaskNode(XmlDocument xmlDocument, string nodeName, DebugLogMaskKind maskKind)
  64. {
  65. return xmlDocument.CreateProcessingInstruction(XmlLogObject.XmlMaskNode,
  66. string.Concat(XmlLogObject.XmlNodeName, "=\"", nodeName, "\" ",
  67. XmlLogObject.XmlMaskKind, "=\"", maskKind, "\""));
  68. }
  69. /// <summary>
  70. /// Returns the XmlNode needed to be added to an XML document to mask the next node.
  71. /// </summary>
  72. /// <param name="xmlDocument">The XML document.</param>
  73. /// <param name="maskKind">The type of masking to use.</param>
  74. public static XmlProcessingInstruction CreateXmlNodeToMaskNextNode(XmlDocument xmlDocument, DebugLogMaskKind maskKind)
  75. {
  76. return xmlDocument.CreateProcessingInstruction(XmlLogObject.XmlMaskNode,
  77. string.Concat(XmlLogObject.XmlNodeName, "=\"", XmlLogObject.XmlMaskNextNodeName, "\" ",
  78. XmlLogObject.XmlMaskKind, "=\"", maskKind, "\""));
  79. }
  80. /// <summary>
  81. /// Returns the XmlNode needed to be added to an XML document to mask an attribute.
  82. /// The masking will be active from this point forward in the XML document (until the end or the instruction is redefined).
  83. /// </summary>
  84. /// <param name="xmlDocument">The XML document.</param>
  85. /// <param name="nodeName">The name of the XML node.</param>
  86. /// <param name="attributeName">The name of the node attribute.</param>
  87. /// <param name="maskKind">The type of masking to use.</param>
  88. public static XmlProcessingInstruction CreateXmlNodeToMaskAttribute(XmlDocument xmlDocument, string nodeName, string attributeName, DebugLogMaskKind maskKind)
  89. {
  90. return xmlDocument.CreateProcessingInstruction(XmlLogObject.XmlMaskAttribute,
  91. string.Concat(XmlLogObject.XmlNodeName, "=\"", nodeName, "\" ",
  92. XmlLogObject.XmlAttributeName, "=\"", attributeName, "\" ",
  93. XmlLogObject.XmlMaskKind, "=\"", maskKind, "\""));
  94. }
  95. #endregion
  96. #region Properties
  97. /// <summary>
  98. /// Turn on/off masking in debug mode.
  99. /// </summary>
  100. public static bool ForceMask
  101. {
  102. get { return forceMask; }
  103. set { forceMask = value; }
  104. }
  105. /// <summary>
  106. /// Should sensitive data be masked?
  107. /// </summary>
  108. public static bool MaskSensitiveData
  109. {
  110. get
  111. {
  112. #if DEBUG
  113. return forceMask;
  114. #else
  115. return true;
  116. #endif
  117. }
  118. }
  119. #endregion
  120. #region Methods
  121. /// <summary>
  122. /// Masks the data the requested way.
  123. /// </summary>
  124. /// <param name="data">The data to mask.</param>
  125. /// <param name="maskKind">The kind of masking to perform.</param>
  126. /// <returns></returns>
  127. public static string MaskData(string data, DebugLogMaskKind maskKind)
  128. {
  129. #if DEBUG
  130. if (forceMask)
  131. #endif
  132. {
  133. switch (maskKind)
  134. {
  135. case DebugLogMaskKind.Remove: return string.Empty;
  136. case DebugLogMaskKind.Empty: return string.Empty;
  137. case DebugLogMaskKind.Mask: return "****";
  138. case DebugLogMaskKind.MaskDigits:
  139. {
  140. StringBuilder maskedData = new StringBuilder();
  141. if (!string.IsNullOrEmpty(data))
  142. {
  143. for (int i = 0; i < data.Length; i++)
  144. {
  145. char dataChar = data[i];
  146. if ((dataChar >= '0') && (dataChar <= '9'))
  147. maskedData.Append('*');
  148. else
  149. maskedData.Append(dataChar);
  150. }
  151. }
  152. return maskedData.ToString();
  153. }
  154. case DebugLogMaskKind.MaskDigitsAppendHash:
  155. {
  156. StringBuilder maskedData = new StringBuilder();
  157. StringBuilder digits = new StringBuilder();
  158. if (!string.IsNullOrEmpty(data))
  159. {
  160. for (int i = 0; i < data.Length; i++)
  161. {
  162. char dataChar = data[i];
  163. if ((dataChar >= '0') && (dataChar <= '9'))
  164. {
  165. maskedData.Append('*');
  166. digits.Append(dataChar);
  167. }
  168. else
  169. maskedData.Append(dataChar);
  170. }
  171. }
  172. return string.Concat(maskedData, "[#", GetHashCode(digits.ToString()), "#]");
  173. }
  174. case DebugLogMaskKind.MaskHex:
  175. {
  176. StringBuilder maskedData = new StringBuilder();
  177. if (!string.IsNullOrEmpty(data))
  178. {
  179. for (int i = 0; i < data.Length; i++)
  180. {
  181. char dataChar = data[i];
  182. if ((dataChar >= '0') && (dataChar <= '9') || (dataChar >= 'a') && (dataChar <= 'f') || (dataChar >= 'A') && (dataChar <= 'F'))
  183. maskedData.Append('*');
  184. else
  185. maskedData.Append(dataChar);
  186. }
  187. }
  188. return maskedData.ToString();
  189. }
  190. case DebugLogMaskKind.MaskHexAppendHash:
  191. {
  192. StringBuilder maskedData = new StringBuilder();
  193. StringBuilder hex = new StringBuilder();
  194. if (!string.IsNullOrEmpty(data))
  195. {
  196. for (int i = 0; i < data.Length; i++)
  197. {
  198. char dataChar = data[i];
  199. if ((dataChar >= '0') && (dataChar <= '9') || (dataChar >= 'a') && (dataChar <= 'f') || (dataChar >= 'A') && (dataChar <= 'F'))
  200. {
  201. maskedData.Append('*');
  202. hex.Append(dataChar);
  203. }
  204. else
  205. maskedData.Append(dataChar);
  206. }
  207. }
  208. return string.Concat(maskedData, "[#", GetHashCode(hex.ToString()), "#]");
  209. }
  210. case DebugLogMaskKind.MaskPan:
  211. {
  212. if (!string.IsNullOrEmpty(data) && (data.Length > 8))
  213. return string.Concat(data.Substring(0, 4), string.Empty.PadLeft(data.Length - 8, '*'), data.Substring(data.Length - 4, 4));
  214. return data;
  215. }
  216. case DebugLogMaskKind.MaskPan64:
  217. {
  218. if (!string.IsNullOrEmpty(data) && (data.Length > 10))
  219. return string.Concat(data.Substring(0, 6), string.Empty.PadLeft(data.Length - 10, '*'), data.Substring(data.Length - 4, 4));
  220. return data;
  221. }
  222. case DebugLogMaskKind.MaskTrack64:
  223. {
  224. if (!string.IsNullOrEmpty(data) && (data.Length > 10))
  225. {
  226. int sepIndex = data.IndexOf("=");
  227. if (sepIndex < 0)
  228. {
  229. return string.Concat(data.Substring(0, 6), string.Empty.PadLeft(data.Length - 10, '*'), data.Substring(data.Length - 4, 4));
  230. }
  231. else
  232. {
  233. string panData = data.Substring(0, sepIndex);
  234. string otherData = string.Empty;
  235. if (data.Length > sepIndex + 1)
  236. {
  237. otherData = data.Substring(sepIndex + 1, data.Length - sepIndex - 1);
  238. }
  239. return string.Concat(panData.Substring(0, 6), string.Empty.PadLeft(panData.Length - 10, '*'), panData.Substring(panData.Length - 4, 4), "=", otherData);
  240. }
  241. }
  242. return data;
  243. }
  244. case DebugLogMaskKind.MaskPanAppendHash:
  245. {
  246. if (!string.IsNullOrEmpty(data) && (data.Length > 8))
  247. return string.Concat(data.Substring(0, 4), string.Empty.PadLeft(data.Length - 8, '*'), data.Substring(data.Length - 4, 4), "[#", GetHashCode(data), "#]");
  248. return data;
  249. }
  250. case DebugLogMaskKind.MaskEmbeddedPan:
  251. case DebugLogMaskKind.MaskEmbeddedPanAppendHash:
  252. {
  253. StringBuilder maskedData = new StringBuilder();
  254. int stringBuilderOffset = 0;
  255. if (!string.IsNullOrEmpty(data))
  256. {
  257. int digitStart = -1;
  258. for (int i = 0; i < data.Length; i++)
  259. {
  260. char dataChar = data[i];
  261. bool isDigit = (dataChar >= '0') && (dataChar <= '9');
  262. bool isLastChar = (i == data.Length - 1);
  263. bool inDigitSequence = (digitStart > -1);
  264. maskedData.Append(dataChar);
  265. if (isDigit && (!isLastChar || !inDigitSequence))
  266. {
  267. if (digitStart == -1)
  268. digitStart = i + stringBuilderOffset;
  269. }
  270. else
  271. {
  272. if (digitStart > -1)
  273. {
  274. int digitLength = i + stringBuilderOffset - digitStart;
  275. if (isLastChar)
  276. digitLength++;
  277. if (digitLength > 8)
  278. {
  279. if (maskKind == DebugLogMaskKind.MaskEmbeddedPanAppendHash)
  280. {
  281. string hashedPan = string.Concat("[#", GetHashCode(maskedData.ToString(digitStart, digitLength)), "#]");
  282. maskedData.Remove(digitStart + 4, digitLength - 8).Insert(digitStart + 4, "*", digitLength - 8).Insert(digitStart + digitLength, hashedPan);
  283. stringBuilderOffset += hashedPan.Length;
  284. }
  285. else
  286. maskedData.Remove(digitStart + 4, digitLength - 8).Insert(digitStart + 4, "*", digitLength - 8);
  287. }
  288. }
  289. digitStart = -1;
  290. }
  291. }
  292. }
  293. return maskedData.ToString();
  294. }
  295. }
  296. }
  297. return data;
  298. }
  299. /// <summary>
  300. /// Masks the data the requested way.
  301. /// </summary>
  302. /// <param name="bytes">The binary data to mask.</param>
  303. /// <param name="showLength">Should the original length of the bytes be shown in the log?</param>
  304. /// <returns></returns>
  305. public static string MaskData(byte[] bytes, bool showLength)
  306. {
  307. return MaskData(bytes, showLength, false);
  308. }
  309. /// <summary>
  310. /// Masks the data the requested way.
  311. /// </summary>
  312. /// <param name="bytes">The binary data to mask.</param>
  313. /// <param name="showLength">Should the original length of the bytes be shown in the log?</param>
  314. /// <param name="showHashValue">Should a hash value be calculated and shown in the log?</param>
  315. /// <returns></returns>
  316. public static string MaskData(byte[] bytes, bool showLength, bool showHashValue)
  317. {
  318. bool isDebug = false;
  319. #if DEBUG
  320. isDebug = true;
  321. #endif
  322. if (forceMask || !isDebug)
  323. {
  324. StringBuilder text = new StringBuilder();
  325. if (!showLength)
  326. text.Append("***# bytes***");
  327. else if (bytes == null)
  328. text.Append("***null bytes***");
  329. else
  330. {
  331. text.Append(bytes.Length);
  332. text.Append(" bytes");
  333. }
  334. if (showHashValue)
  335. {
  336. if (showLength)
  337. text.Append(",");
  338. if (bytes != null)
  339. {
  340. int hashValue = 0;
  341. for (int i = 0; i < bytes.Length; i++)
  342. hashValue = (hashValue * 31) ^ bytes[i]; // Just a simple "hash" calculation of a byte array.
  343. text.Append("[#");
  344. text.Append(hashValue.ToString("X8"));
  345. text.Append("#]");
  346. }
  347. else
  348. text.Append("[#--------#]");
  349. }
  350. return string.Concat("***", text, "***");
  351. }
  352. return Strings.ByteArrayToString(bytes, StringsByteArrayFormat.CompactHex);
  353. }
  354. private static string GetHashCode(string text)
  355. {
  356. string fullHashCode = text.GetHashCode().ToString("X8");
  357. int firstHashHalf = int.Parse(fullHashCode.Substring(0, 4), NumberStyles.HexNumber);
  358. int lastHashHalf = int.Parse(fullHashCode.Substring(4, 4), NumberStyles.HexNumber);
  359. int hashedHash = firstHashHalf ^ lastHashHalf;
  360. return hashedHash.ToString("X4");
  361. }
  362. #endregion
  363. }
  364. }