bcgit / bc-csharp

BouncyCastle.NET Cryptography Library (Mirror)
https://www.bouncycastle.org/csharp
MIT License
1.68k stars 558 forks source link

Embed Sike data in source to make it trim-friendly #539

Closed Rob-Hague closed 6 months ago

Rob-Hague commented 6 months ago

Follow-up to 76e24757cf (https://github.com/bcgit/bc-csharp/pull/534), this time for the constant Sike data.

In the same test app on top of the same branch:

before #534 after #534 now
untrimmed 6989 KB 6975 KB 6892 KB
trimmed 3993 KB 2791 KB 564 KB

i.e. the assembly trims nicely there with no cost to the untrimmed size.

As before, the source code was generated by reflecting over instances of the existing classes:

using Org.BouncyCastle.Pqc.Crypto.Sike;
using System;
using System.CodeDom.Compiler;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;

P434 lc = new(isCompressed: true);

string[] bz2FieldNames =
[
    "ph2_path",
    "ph3_path",
    "A_gen",
    "B_gen",
    "XQB3",
    "A_basis_zero",
    "B_basis_zero",
    "B_gen_3_tors",
    "g_R_S_im",
    "g_phiR_phiS_re",
    "g_phiR_phiS_im",
    "Montgomery_RB1",
    "Montgomery_RB2",
    "threeinv",
    "u_entang",
    "u0_entang",
    "table_r_qr",
    "table_r_qnr",
    "table_v_qr",
    "table_v_qnr",
    "v_3_torsion",
    "T_tate3",
    "T_tate2_firststep_P",
    "T_tate2_P",
    "T_tate2_firststep_Q",
    "T_tate2_Q",
    "ph2_T",
    "ph3_T1",
    "ph3_T2",
];

FieldInfo[] fields = lc.GetType().GetFields(BindingFlags.NonPublic | BindingFlags.Instance);

using StreamWriter sw = new(@"C:\tmp\out.txt");
using IndentedTextWriter tw = new(sw);

tw.Indent++; //class
tw.WriteLine(); // to get tabs on next line

foreach (string fieldName in bz2FieldNames)
{
    FieldInfo field = fields.Single(f => f.Name == fieldName);

    if (field.FieldType == typeof(uint[]))
    {
        uint[] data = (uint[])field.GetValue(lc);

        int nonZeroDataLength = data.AsSpan().LastIndexOfAnyExcept(0u) + 1;

        tw.WriteLine($"private static readonly uint[] s_{field.Name} = new uint[{nonZeroDataLength}]");
        tw.WriteLine("{");
        tw.Indent++;

        foreach (uint[] chunk in data.Take(nonZeroDataLength).Chunk(8))
        {
            tw.Write("0x");
            tw.Write(string.Join(", 0x", chunk.Select(u => u.ToString("X8"))));
            tw.WriteLine(",");
        }

        tw.Indent--;
        tw.WriteLine("};");
        tw.WriteLine();
    }
    else if (field.FieldType == typeof(ulong[]))
    {
        ulong[] data = (ulong[])field.GetValue(lc);

        int nonZeroDataLength = data.AsSpan().LastIndexOfAnyExcept(0u) + 1;

        tw.WriteLine($"private static readonly ulong[] s_{field.Name} = new ulong[{nonZeroDataLength}]");
        tw.WriteLine("{");
        tw.Indent++;

        foreach (ulong[] chunk in data.Take(nonZeroDataLength).Chunk(4))
        {
            tw.Write("0x");
            tw.Write(string.Join(", 0x", chunk.Select(u => u.ToString("X16"))));
            tw.WriteLine(",");
        }

        tw.Indent--;
        tw.WriteLine("};");
        tw.WriteLine();
    }
    else if (field.FieldType == typeof(ulong[][]))
    {
        ulong[][] data = (ulong[][])field.GetValue(lc);

        tw.WriteLine($"private static readonly ulong[][] s_{field.Name} = new ulong[{data.Length}][]");
        tw.WriteLine("{");
        tw.Indent++;
        for (int i = 0; i < data.Length; i++)
        {
            int nonZeroDataLength = data[i].AsSpan().LastIndexOfAnyExcept(0u) + 1;

            tw.WriteLine($"new ulong[{nonZeroDataLength}]");
            tw.WriteLine("{");
            tw.Indent++;

            foreach (ulong[] chunk in data[i].Take(nonZeroDataLength).Chunk(4))
            {
                tw.Write("0x");
                tw.Write(string.Join(", 0x", chunk.Select(u => u.ToString("X16"))));
                tw.WriteLine(",");
            }

            tw.Indent--;
            tw.WriteLine("},");

        }
        tw.Indent--;
        tw.WriteLine("};");
        tw.WriteLine();
    }
    else if (field.FieldType == typeof(ulong[][][]))
    {
        ulong[][][] data = (ulong[][][])field.GetValue(lc);

        tw.WriteLine($"private static readonly ulong[][][] s_{field.Name} = new ulong[{data.Length}][][]");
        tw.WriteLine("{");
        tw.Indent++;
        for (int i = 0; i < data.Length; i++)
        {
            tw.WriteLine($"new ulong[{data[i].Length}][]");
            tw.WriteLine("{");
            tw.Indent++;

            for (int j = 0; j < data[i].Length; j++)
            {
                int nonZeroDataLength = data[i][j].AsSpan().LastIndexOfAnyExcept(0u) + 1;

                tw.WriteLine($"new ulong[{nonZeroDataLength}]");
                tw.WriteLine("{");
                tw.Indent++;

                foreach (ulong[] chunk in data[i][j].Take(nonZeroDataLength).Chunk(4))
                {
                    tw.Write("0x");
                    tw.Write(string.Join(", 0x", chunk.Select(u => u.ToString("X16"))));
                    tw.WriteLine(",");
                }

                tw.Indent--;
                tw.WriteLine("},");
            }

            tw.Indent--;
            tw.WriteLine("},");
        }
        tw.Indent--;
        tw.WriteLine("};");
        tw.WriteLine();
    }
    else
    {
        Debug.Fail(field.FieldType.ToString());
    }
}

This would complete #422

peterdettman commented 6 months ago

Merged, thanks again.

Rob-Hague commented 6 months ago

Great, thanks