public static string FixXmlHash(string str, uint target_hash) { str = Utils.StripBOM(str); str = Utils.NormalizeLineEndings(str); str += $"\n{XML_MARKER}\n<!-- Fix hash: "; str += new CBDataHasher(str).CalculatePreimage(target_hash, " -->\n"); return(str); }
/// <summary> /// Finds a string that when appended to another string with the current hash, will have a final /// hash equal to targetHash. /// /// Hence, to bring the hash of a string to any value, do: /// /// str += new CBDataHasher(str).CalculatePreimage(targetHash, " whatever text "); /// </summary> /// <param name="targetHash">The target hash value.</param> /// <param name="tail">An optional suffix that the output string must end with.</param> /// <returns>The string to append.</returns> public string CalculatePreimage(uint targetHash, string tail = "") { // The effect of appending n characters to a string with this hash algorithm is effectively: // // hash(str + chars) = hash(str) * 33^n.Length + hash(chars) mod 2^32 // // We calculate the hash value `chars` would need to have to set the hash to `targetHash`, // assuming it is 5 characters. // // In this way, we can use an algorithm that only needs to bring 0 to a certain hash value, // and not any value to any value. CBDataHasher target = new CBDataHasher(targetHash); target.UpdateReverse(tail); // account for the tail target.State -= this.State * 39135393; // 33^5 // We calculate the string in reverse, effectively trying to bring the value of target down // to zero. The calculation in reverse is: // // hash(c + str) = (hash(str) - c) * 33^1 mod 2^32 // // As long as 33 evenly divides (hash(str) - c), the modular division will behave as ordinary // division. As this value will always decrease, it eventually reaches zero, at which point // we can just output null characters until we have 5 characters. var outStr = ""; for (int i = 0; i < 5; i++) { var tint = target.State; // Avoid generating surrogate pairs or BOMs (The CLR does weird things to it) var next = (tint >= 0xD800 && tint <= 0xDFFF) || tint == 0xFEFF ? 0x752D + (tint % 33) : tint <= 0xFFFF ? tint : 0xFFC0 + (tint % 33); var next_ch = (char)next; outStr = next_ch + outStr; target.UpdateReverse(next_ch); } Trace.Assert(target.State == 0, "CalculatePreimage failed."); return(outStr + tail); }