2023年政策修订增补工作正在进行中,欢迎参与!
  • Moegirl.ICU:萌娘百科流亡社群 581077156(QQ),欢迎对萌娘百科运营感到失望的编辑者加入
  • Moegirl.ICU:账号认领正在试运行,有意者请参照账号认领流程

使用者:Selfice/圖書館/標準SH5零碎信息補充

萌娘百科,萬物皆可萌的百科全書!轉載請標註來源頁面的網頁連結,並聲明引自萌娘百科。內容不可商用。
跳至導覽 跳至搜尋

一小段補充

煙樓-天青-鳴
2022-03-15 11:29

有人問我在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,以下過程完全是兼容的。

$U>11$时:$U\times V \equiv (U-11)\times V(\mod 11)$

所以在1組的sh5中有一個額外的計算規則。

$(U\times V +\lfloor U/11 \rfloor) \mod 11$

這樣就相當於是提供了額外的組合,而不會因為循環而重複。

全部可能的組合

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";

}

進位轉換類,不過僅僅使用了ToBase36FromBase36(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;
    }

}