| 本页面之全部或部分原来自哔哩哔哩专栏或百度贴吧或未知来源的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...
没错,就是这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这个单词也在其中
一些全局的字符串,不使用常量,因为程序集编译常量直接替换,无法跨越程序集传递更改的值。
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;
}
}
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;
}
}