用戶:冰閱/圖書館/標準SH5零碎信息補充
本頁面之全部或部分原來自嗶哩嗶哩專欄或百度貼吧或未知來源的Standard Short Hide5(標準SH5) 等文章,依 CC BY-NC-ND 4.0 授權引入;原貢獻者是煙樓-天青-鳴。 |
一小段補充
有人問我在3組的sh5中X是什麼,這裡明確一下,只有2組的sh5,大寫字母對應關係存在特殊的處理。
1組和3組的sh5,大寫字母和字母表是完全分離的,無關的。
A代表1,B代表2,直到Z代表26,完全正常的字母表順序,字母表的作用只有在後續計算中,求模取字母表長度這一步,使之能對應上結果。
至於首字母問題,在3組中其實不存在這樣的問題,因為對於一個【單詞】來說,本就不會用空格開頭。或者也可以認為成開頭的空格保留不動,反正空格也不是大寫字母,不可能和sh5的大寫字母混淆,和2組的保留X一個道理。
而1組中的首字母問題可以這麼處理,如果真的碰到了會使得首字母丟失的情況,就放棄,然後使用2組的sh5。
Standard Short Hide5...
標準的1組SH5推薦字母表
EADIHTNORFS
沒錯,就是這11個字母,下面說說具體怎麼做。
首先可以確定的是,1組的sh5隻可能加密特點的並且字母種類小於5種的單詞。
並且如果單詞以E開頭,則使用2組的sh5。
1組的sh5使用的大寫字母是正常的26個字母,A代表1......
其實可以注意到,2組的sh5事實上僅僅只有25個有效的大寫字母可用來加密,這裡額外提一下,但這不重要。
至於為什麼選擇EADIHTNORFS這些,這是通過了巧妙的統計得出的一個合理的組合。
在解釋理由之前,先說一下額外的計算規則,因為大寫字母有26個,但是字母表只有11個字母,在乘法和取余的計算中,一旦大寫字母代表的數值大於等於字母表的長度,剩下的數值就會循環,導致浪費可能的組合,如公式所述。
2組的U最大值25<26,3組的U最大值是26<64,以下過程完全是兼容的。
所以在1組的sh5中有一個額外的計算規則。
這樣就相當於是提供了額外的組合,而不會因為循環而重複。
全部可能的組合
EADIH EDHNR EINFA EHRAT ETSHF ENAOD EOISN ERTDS EFOTI ESFRO AAAAA ADIHT AITOF AHOSD ATFDN ANETS AODRI ARHEO AFNIE ASRNH AESFR DDDDD DIHTN DHNRS DTREI DNSIO
解釋一下這個表的含義吧,如果一個單詞,例如here,它僅僅包含了HER這三個字母,而在這張表格中,第二行是EDHNR,它包含了HER這三個字母,這就說明,1組的sh5能夠加密here這個單詞,同理,heart,red,air,ants,hero,deers,die,sine這些單詞也可以在表中找到對應的組合。
並且在英語中,以下的10個單詞是出現頻率最高的。
the of and to a in for is on that
而它們全都包括在這張表里,這就是使用它的有力理由。
當然了,也許存在更好的字母表,只能說,這個是作為一個推薦標準,在標準定義中,它仍然是未定義的,如果你發現了更好的字母表,也歡迎提出。
評論區補充
另外,不是說如果單詞以E開頭,就一定使用2組的sh5。
以A和D開頭也有可能無法加密,總之無法加密的時候就放棄。
也就是說E開頭其實也可能可以,比如說error就可以由ARHEO這一組加密,R1ku是唯一的結果。
自然的,hide這個單詞也在其中
標準ShortHide5的實現代碼
ClassicalCryptography.Utils
一些全局的字符串,不使用常量,因為程序集編譯常量直接替換,無法跨越程序集傳遞更改的值。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace ClassicalCryptography.Utils; /// <summary> /// 全局表与工具类 /// </summary> public static class Globals { /// <summary> /// 非标准的Base64形式,64进制 /// </summary> public static readonly string VBase64 = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz+/"; /// <summary> /// 数字和小写字母组成的36进制 /// </summary> public static readonly string Base36 = "0123456789abcdefghijklmnopqrstuvwxyz"; /// <summary> /// 26个大写字母 /// </summary> public static readonly string ULetters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; }
進制轉換類,不過僅僅使用了ToBase36和FromBase36(ReadOnlySpan<char>)
namespace ClassicalCryptography.Utils; using System; using static ClassicalCryptography.Utils.Globals; /// <summary> /// 进制转换 /// </summary> public static class BaseConverter { /// <summary> /// 转换成36进制 /// </summary> public static string ToBase36(ulong n) { var stack = new Stack<char>(); while (n != 0) { stack.Push(Base36[(int)(n % 36)]); n /= 36; } return new(stack.ToArray()); } /// <summary> /// 从36进制转换 /// </summary> public static ulong FromBase36(string str) => FromBase36(str.AsSpan()); /// <summary> /// 从36进制转换 /// </summary> public static ulong FromBase36(ReadOnlySpan<char> span) { ulong result = 0; ulong b = 1; for (int i = span.Length - 1; i >= 0; i--) { int pos = Base36.IndexOf(span[i]); checked { result += (ulong)pos * b; b = (b << 5) + (b << 2);//base *= 36; } } return result; } /// <summary> /// 转换成5进制 /// </summary> public static byte[] ToBase5(ulong n) { var stack = new Stack<byte>(); while (n != 0) { stack.Push((byte)(n % 5)); n /= 5; } return stack.ToArray(); } /// <summary> /// 从5进制转换 /// </summary> public static ulong FromBase5(byte[] arr) { ulong result = 0; ulong b = 1; for (int i = arr.Length - 1; i >= 0; i--) { checked { result += arr[i] * b; b += b << 2;//base *= 5; } } return result; } }
ClassicalCryptography.Calculation.ShortHide5
SH5結構的等級。
namespace ClassicalCryptography.Calculation.ShortHide5; /// <summary> /// SH5结构的等级 /// </summary> public enum SH5Level : byte { /// <summary> /// 仅一组 /// </summary> Single = 1, /// <summary> /// 两组 /// </summary> Double = 2, /// <summary> /// 三组 /// </summary> Trible = 3 }
SH5結構類型,使用迭代器來計算值。
using ClassicalCryptography.Utils; using System.Collections; using System.Collections.Generic; using System.Text; using System.Text.RegularExpressions; using static ClassicalCryptography.Utils.BaseConverter; using static ClassicalCryptography.Utils.Globals; namespace ClassicalCryptography.Calculation.ShortHide5; /// <summary> /// ShortHide5密文结果的结构 /// </summary> public class SH5 : IEnumerable<int> { /// <summary> /// 1组SH5的字母表(推荐的) /// </summary> public static readonly string AlphaBetS = "EADIHTNORFS"; /// <summary> /// 2组SH5的字母表 /// </summary> public static readonly string AlphaBetD = "XABCDEFGHIJKLMNOPQRSTUVWYZ"; /// <summary> /// 3组SH5的字母表 /// </summary> public static readonly string AlphaBetT = " ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789,"; /// <summary> /// 第一组的大写字母值 <see href="U"/> 和乘数 <see href="V"/> /// </summary> public readonly (int U, ulong V) Pair1; /// <summary> /// 第二组的大写字母值 <see href="U"/> 和乘数 <see href="V"/> /// </summary> public readonly (int U, ulong V) Pair2; /// <summary> /// 第三组的大写字母值 <see href="U"/> 和乘数 <see href="V"/> /// </summary> public readonly (int U, ulong V) Pair3; /// <summary> /// 等级 /// </summary> public readonly SH5Level Level; /// <summary> /// 当前SH5结构的字母表 /// </summary> public string GetAlphaBet() => Level switch { SH5Level.Single => AlphaBetS, SH5Level.Double => AlphaBetD, SH5Level.Trible => AlphaBetT, _ => throw new Exception("不存在的字母表"), }; /// <summary> /// 前缀X的数量 /// </summary> public readonly int PrefixCount; /// <summary> /// 前缀X /// </summary> public string Prefix => PrefixCount switch { 0 => string.Empty, 1 => "X", 2 => "XX", 3 => "XXX", 4 => "XXXX", 5 => "XXXXX", _ => string.Concat(Enumerable.Repeat('X', PrefixCount)), }; /// <summary> /// 获得前缀X数量 /// </summary> public static int GetPrefixCount(string str) { int count = 0; while (str[count] is 'X' or 'x') count++; return count; } /// <summary> /// 仅一组的SH5结构 /// </summary> public SH5(int u, ulong v) { Pair1.U = u; Pair1.V = v; Level = SH5Level.Single; } /// <summary> /// 两组的SH5结构 /// </summary> public SH5(int u1, ulong v1, int u2, ulong v2, int prefixCount = 0) { Pair1.U = u1; Pair1.V = v1; Pair2.U = u2; Pair2.V = v2; Level = SH5Level.Double; PrefixCount = prefixCount; } /// <summary> /// 三组的SH5结构 /// </summary> public SH5(int u1, ulong v1, int u2, ulong v2, int u3, ulong v3) { Pair1.U = u1; Pair1.V = v1; Pair2.U = u2; Pair2.V = v2; Pair3.U = u3; Pair3.V = v3; Level = SH5Level.Trible; } /// <summary> /// 通过字符串创建SH5结构 /// </summary> public SH5(string str) { var matches = Regex.Matches(str, "[A-Z][0-9a-z]+"); Level = (SH5Level)matches.Count; if (!Enum.IsDefined(Level)) throw new FormatException("不正确的SH5格式"); ReadOnlySpan<char> vstr; switch (Level) { case SH5Level.Single: vstr = matches[0].ValueSpan; Pair1.U = ULetters.IndexOf(vstr[0]) + 1; Pair1.V = FromBase36(vstr[1..]); break; case SH5Level.Double: vstr = matches[0].ValueSpan; Pair1.U = AlphaBetD.IndexOf(vstr[0]); Pair1.V = FromBase36(vstr[1..]); vstr = matches[1].ValueSpan; Pair2.U = AlphaBetD.IndexOf(vstr[0]); Pair2.V = FromBase36(vstr[1..]); PrefixCount = GetPrefixCount(str); break; case SH5Level.Trible: vstr = matches[0].ValueSpan; Pair1.U = ULetters.IndexOf(vstr[0]) + 1; Pair1.V = FromBase36(vstr[1..]); vstr = matches[1].ValueSpan; Pair2.U = ULetters.IndexOf(vstr[0]) + 1; Pair2.V = FromBase36(vstr[1..]); vstr = matches[2].ValueSpan; Pair3.U = ULetters.IndexOf(vstr[0]) + 1; Pair3.V = FromBase36(vstr[1..]); break; } } /// <summary> /// 字符串形式 /// </summary> public override string ToString() { var result = new StringBuilder(); switch (Level) { case SH5Level.Single: result.Append(ULetters[Pair1.U - 1]); result.Append(ToBase36(Pair1.V)); break; case SH5Level.Double: result.Append(Prefix); result.Append(AlphaBetD[Pair1.U]); result.Append(ToBase36(Pair1.V)); result.Append(AlphaBetD[Pair2.U]); result.Append(ToBase36(Pair2.V)); break; case SH5Level.Trible: result.Append(ULetters[Pair1.U - 1]); result.Append(ToBase36(Pair1.V)); result.Append(ULetters[Pair2.U - 1]); result.Append(ToBase36(Pair2.V)); result.Append(ULetters[Pair3.U - 1]); result.Append(ToBase36(Pair3.V)); break; } return result.ToString(); } /// <summary> /// 倒序计算的值 /// </summary> public IEnumerator<int> GetEnumerator() { ulong v1, v2, v3; switch (Level) { case SH5Level.Single: v1 = Pair1.V; while (v1 != 0) { yield return Pair1.U * (int)(v1 % 5) + Pair1.U / AlphaBetS.Length;//额外的规则 v1 /= 5; } yield break; case SH5Level.Double: v1 = Pair1.V; v2 = Pair2.V; while (v1 != 0 || v2 != 0) { yield return Pair1.U * (int)(v1 % 5) + Pair2.U * (int)(v2 % 5); v1 /= 5; v2 /= 5; } yield break; case SH5Level.Trible: v1 = Pair1.V; v2 = Pair2.V; v3 = Pair3.V; while (v1 != 0 || v2 != 0 || v3 != 0) { yield return Pair1.U * (int)(v1 % 5) + Pair2.U * (int)(v2 % 5) + Pair3.U * (int)(v3 % 5); v1 /= 5; v2 /= 5; v3 /= 5; } yield break; } } IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); }
RandomItem得到隨機的列表項目,PopRandomItem得到並且移除隨機的列表項目。
FindAll為找到字符在字符串中出現的所有位置。
以下是核心的加密算法和解密算法。
using ClassicalCryptography.Interfaces; using static ClassicalCryptography.Calculation.ShortHide5.SH5; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using ClassicalCryptography.Utils; namespace ClassicalCryptography.Calculation.ShortHide5; /// <summary> /// ShortHide5密码 /// </summary> [Introduction("ShortHide5密码", "一种自创的英文文本加密方法。")] public class ShortHide5 : ICipher<string, string> { private static readonly string[] sGroups;//26 private static readonly string[,] dGroups;//25*25 static ShortHide5() { Span<char> span = stackalloc char[25]; sGroups = new string[26]; int slen = AlphaBetS.Length; for (int u1 = 1; u1 <= 26; u1++) { for (int i = 0; i < 5; i++) span[i] = AlphaBetS[(u1 * i + u1 / slen) % slen]; sGroups[u1 - 1] = new(span[..5]); } dGroups = new string[25, 25]; slen = AlphaBetD.Length; for (int u1 = 1; u1 <= 25; u1++) { for (int u2 = 1; u2 <= 25; u2++) { for (int i = 0; i < 5; i++) for (int j = 0; j < 5; j++) span[i + (i << 2) + j] = AlphaBetD[(u1 * i + u2 * j) % slen]; dGroups[u1 - 1, u2 - 1] = new(span); } } } /// <summary> /// 密码类型 /// </summary> public CipherType Type => CipherType.Calculation; /// <summary> /// 解密指定的内容 /// </summary> /// <param name="cipher"></param> public static string DecryptSH5(SH5 cipher) { var alphaBet = cipher.GetAlphaBet(); int index = 28;//最大加密长度 Span<char> text = stackalloc char[index]; foreach (var value in cipher) text[--index] = alphaBet[value % alphaBet.Length]; return $codeholder_4amp;quot;{cipher.Prefix}{text[index..]}"; } /// <summary> /// 解密指定的内容 /// </summary> /// <param name="cipherText"></param> public string Decrypt(string cipherText) { return DecryptSH5(new(cipherText)); } /// <summary> /// 加密指定的内容 /// </summary> /// <param name="plainText">纯英文单词</param> public static SH5 EncryptSH5(string plainText) { var results = EncryptSingles(plainText); if (results.Count > 0) return results.Count == 1 ? results[0] : results.RandomItem(); results = EncryptDoubles(plainText); if (results.Count > 0) return results.RandomItem(); return EncryptTrible(plainText, plainText.ToHashSet()); } /// <summary> /// 加密指定的内容 /// </summary> /// <param name="plainText">纯英文单词</param> public string Encrypt(string plainText) { return EncryptSH5(plainText).ToString(); } /// <summary> /// 随机的3组加密 /// </summary> /// <param name="plainText">要加密的内容</param> /// <param name="count">要加密的数量(可能有部分重复项)</param> public static List<SH5> EncryptTribles(string plainText, int count = 100) { var result = new List<SH5>(); var set = plainText.ToHashSet(); for (int i = 0; i < count; i++) result.Add(EncryptTrible(plainText, set)); return result; } private static SH5 EncryptTrible(string plainText, HashSet<char> set) { if (set.IsSubsetOf(AlphaBetT)) { char[] group = new char[125]; int u1 = 0, u2 = 0, u3 = 0; while (!set.IsSubsetOf(group)) { u1 = Random.Shared.Next(26) + 1; u2 = Random.Shared.Next(26) + 1; u3 = Random.Shared.Next(26) + 1; int p = 0; for (int i = 0; i < 5; i++) for (int j = 0; j < 5; j++) for (int k = 0; k < 5; k++) group[p++] = AlphaBetT[(i * u1 + j * u2 + k * u3) % 64]; } ulong v1 = 0, v2 = 0, v3 = 0; ulong b = 1; for (int i = plainText.Length - 1; i >= 0; i--) { checked { var t = group.FindAll(plainText[i]).RandomItem(); v1 += (ulong)(t / 25) * b; v2 += (ulong)((t % 25) / 5) * b; v3 += (ulong)(t % 5) * b; b += b << 2;//base *= 5; } } return new(u1, v1, u2, v2, u3, v3); } throw new ArgumentOutOfRangeException(nameof(plainText), "无法加密指定的内容"); } /// <summary> /// 随机的2组加密 /// </summary> /// <param name="uText">要加密的内容</param> /// <param name="count">要加密的数量(可能有部分重复项)</param> /// <param name="minimize">最小化处理(可消除重复项,但结果大大减少)</param> public static List<SH5> EncryptDoubles(string uText, int count = 100, bool minimize = false) { var result = new List<SH5>(); int prefixCount = GetPrefixCount(uText); uText = uText[prefixCount..].ToUpper(); var set = uText.ToHashSet(); if (set.Count > 25) return result; var list = new List<(int U1, int U2)>(); for (int u1 = 0; u1 < 25; u1++) { for (int u2 = 0; u2 < 25; u2++) { if (set.IsSubsetOf(dGroups[u1, u2])) list.Add((u1 + 1, u2 + 1)); } } if (list.Count == 0) return result; if (minimize) count = Math.Min(count, list.Count); for (int j = 0; j < count; j++) { (int u1, int u2) = minimize ? list.PopRandomItem() : list.RandomItem(); ulong v1 = 0, v2 = 0; ulong b = 1; var group = dGroups[u1 - 1, u2 - 1]; for (int i = uText.Length - 1; i >= 0; i--) { checked { var t = group.FindAll(uText[i]).RandomItem(); v1 += (ulong)(t / 5) * b; v2 += (ulong)(t % 5) * b; b += b << 2;//base *= 5; } } result.Add(new(u1, v1, u2, v2, prefixCount)); } return result; } /// <summary> /// 1组加密 /// </summary> public static List<SH5> EncryptSingles(string uText) { var result = new List<SH5>(); uText = uText.ToUpper(); var set = uText.ToHashSet(); if (set.Count > 5) return result; var list = new List<int>(); for (int u1 = 0; u1 < sGroups.Length; u1++) { if (set.IsSubsetOf(sGroups[u1])) list.Add(u1); } for (int j = 0; j < list.Count; j++) { int u1 = list[j]; if (sGroups[u1][0] != uText[0]) { ulong v1 = 0; ulong b = 1; for (int i = uText.Length - 1; i >= 0; i--) { checked { v1 += (ulong)sGroups[u1].IndexOf(uText[i]) * b; b += b << 2;//base *= 5; } } result.Add(new(u1 + 1, v1)); } } return result; } }