<PackageReference Include="BouncyCastle.Cryptography" Version="2.6.0" />

SphincsPlusEngine

abstract class SphincsPlusEngine
using Org.BouncyCastle.Crypto; using Org.BouncyCastle.Crypto.Digests; using Org.BouncyCastle.Crypto.Generators; using Org.BouncyCastle.Crypto.Macs; using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Crypto.Utilities; using Org.BouncyCastle.Utilities; using System; namespace Org.BouncyCastle.Pqc.Crypto.SphincsPlus { internal abstract class SphincsPlusEngine { internal class Sha2Engine : SphincsPlusEngine { private HMac treeHMac; private Mgf1BytesGenerator mgf1; private byte[] hmacBuf; private IDigest msgDigest; private byte[] msgDigestBuf; private int bl; private IDigest sha256; private byte[] sha256Buf; private IMemoable msgMemo; private IMemoable sha256Memo; public Sha2Engine(bool robust, int n, uint w, uint d, int a, int k, uint h) : base(robust, n, w, d, a, k, h) { sha256 = new Sha256Digest(); sha256Buf = new byte[sha256.GetDigestSize()]; if (n == 16) { msgDigest = new Sha256Digest(); treeHMac = new HMac(new Sha256Digest()); mgf1 = new Mgf1BytesGenerator(new Sha256Digest()); bl = 64; } else { msgDigest = new Sha512Digest(); treeHMac = new HMac(new Sha512Digest()); mgf1 = new Mgf1BytesGenerator(new Sha512Digest()); bl = 128; } hmacBuf = new byte[treeHMac.GetMacSize()]; msgDigestBuf = new byte[msgDigest.GetDigestSize()]; } public override void Init(byte[] pkSeed) { byte[] input = new byte[bl]; msgDigest.BlockUpdate(pkSeed, 0, pkSeed.Length); msgDigest.BlockUpdate(input, 0, bl - N); msgMemo = ((IMemoable)msgDigest).Copy(); msgDigest.Reset(); sha256.BlockUpdate(pkSeed, 0, pkSeed.Length); sha256.BlockUpdate(input, 0, 64 - N); sha256Memo = ((IMemoable)sha256).Copy(); sha256.Reset(); } public override byte[] F(byte[] pkSeed, Adrs adrs, byte[] m1) { byte[] array = CompressedAdrs(adrs); if (robust) m1 = Bitmask256(Arrays.Concatenate(pkSeed, array), m1); ((IMemoable)sha256).Reset(sha256Memo); sha256.BlockUpdate(array, 0, array.Length); sha256.BlockUpdate(m1, 0, m1.Length); sha256.DoFinal(sha256Buf, 0); return Arrays.CopyOfRange(sha256Buf, 0, N); } public override void H(byte[] pkSeed, Adrs adrs, byte[] m1, byte[] m2, byte[] output) { byte[] array = CompressedAdrs(adrs); ((IMemoable)msgDigest).Reset(msgMemo); msgDigest.BlockUpdate(array, 0, array.Length); if (robust) { byte[] array2 = Bitmask(Arrays.Concatenate(pkSeed, array), m1, m2); msgDigest.BlockUpdate(array2, 0, array2.Length); } else { msgDigest.BlockUpdate(m1, 0, m1.Length); msgDigest.BlockUpdate(m2, 0, m2.Length); } msgDigest.DoFinal(msgDigestBuf, 0); Array.Copy(msgDigestBuf, 0, output, 0, N); } public override IndexedDigest H_msg(byte[] prf, byte[] pkSeed, byte[] pkRoot, byte[] msg, int msgOff, int msgLen) { int num = (A * K + 7) / 8; uint num2 = FH / D; uint num3 = FH - num2; uint num4 = (num2 + 7) / 8; uint num5 = (num3 + 7) / 8; int num6 = num + (int)num5 + (int)num4; byte[] array = new byte[msgDigest.GetDigestSize()]; msgDigest.BlockUpdate(prf, 0, prf.Length); msgDigest.BlockUpdate(pkSeed, 0, pkSeed.Length); msgDigest.BlockUpdate(pkRoot, 0, pkRoot.Length); msgDigest.BlockUpdate(msg, msgOff, msgLen); msgDigest.DoFinal(array, 0); byte[] m = new byte[num6]; m = Bitmask(Arrays.ConcatenateAll(prf, pkSeed, array), m); ulong idx_tree = Pack.BE_To_UInt64_Low(m, num, (int)num5) & (ulong.MaxValue >> (int)(64 - num3)); uint idx_leaf = Pack.BE_To_UInt32_Low(m, num + (int)num5, (int)num4) & (uint.MaxValue >> (int)(32 - num2)); return new IndexedDigest(idx_tree, idx_leaf, Arrays.CopyOfRange(m, 0, num)); } public override void T_l(byte[] pkSeed, Adrs adrs, byte[] m, byte[] output) { byte[] array = CompressedAdrs(adrs); if (robust) m = Bitmask(Arrays.Concatenate(pkSeed, array), m); ((IMemoable)msgDigest).Reset(msgMemo); msgDigest.BlockUpdate(array, 0, array.Length); msgDigest.BlockUpdate(m, 0, m.Length); msgDigest.DoFinal(msgDigestBuf, 0); Array.Copy(msgDigestBuf, 0, output, 0, N); } public override void PRF(byte[] pkSeed, byte[] skSeed, Adrs adrs, byte[] prf, int prfOff) { int length = skSeed.Length; ((IMemoable)sha256).Reset(sha256Memo); byte[] array = CompressedAdrs(adrs); sha256.BlockUpdate(array, 0, array.Length); sha256.BlockUpdate(skSeed, 0, skSeed.Length); sha256.DoFinal(sha256Buf, 0); Array.Copy(sha256Buf, 0, prf, prfOff, length); } public override byte[] PRF_msg(byte[] prf, byte[] randomiser, byte[] msg, int msgOff, int msgLen) { treeHMac.Init(new KeyParameter(prf)); treeHMac.BlockUpdate(randomiser, 0, randomiser.Length); treeHMac.BlockUpdate(msg, msgOff, msgLen); treeHMac.DoFinal(hmacBuf, 0); return Arrays.CopyOfRange(hmacBuf, 0, N); } private byte[] CompressedAdrs(Adrs adrs) { byte[] array = new byte[22]; Array.Copy(adrs.value, 3, array, 0, 1); Array.Copy(adrs.value, 8, array, 1, 8); Array.Copy(adrs.value, 19, array, 9, 1); Array.Copy(adrs.value, 20, array, 10, 12); return array; } protected byte[] Bitmask(byte[] key, byte[] m) { byte[] array = new byte[m.Length]; mgf1.Init(new MgfParameters(key)); mgf1.GenerateBytes(array, 0, array.Length); Bytes.XorTo(m.Length, m, array); return array; } protected byte[] Bitmask(byte[] key, byte[] m1, byte[] m2) { byte[] array = new byte[m1.Length + m2.Length]; mgf1.Init(new MgfParameters(key)); mgf1.GenerateBytes(array, 0, array.Length); Bytes.XorTo(m1.Length, m1, array); Bytes.XorTo(m2.Length, m2, 0, array, m1.Length); return array; } protected byte[] Bitmask256(byte[] key, byte[] m) { byte[] array = new byte[m.Length]; Mgf1BytesGenerator mgf1BytesGenerator = new Mgf1BytesGenerator(new Sha256Digest()); mgf1BytesGenerator.Init(new MgfParameters(key)); mgf1BytesGenerator.GenerateBytes(array, 0, array.Length); Bytes.XorTo(m.Length, m, array); return array; } } internal class Shake256Engine : SphincsPlusEngine { private IXof treeDigest; private IXof maskDigest; public Shake256Engine(bool robust, int n, uint w, uint d, int a, int k, uint h) : base(robust, n, w, d, a, k, h) { treeDigest = new ShakeDigest(256); maskDigest = new ShakeDigest(256); } public override void Init(byte[] pkSeed) { } public override byte[] F(byte[] pkSeed, Adrs adrs, byte[] m1) { byte[] array = m1; if (robust) array = Bitmask(pkSeed, adrs, m1); byte[] array2 = new byte[N]; treeDigest.BlockUpdate(pkSeed, 0, pkSeed.Length); treeDigest.BlockUpdate(adrs.value, 0, adrs.value.Length); treeDigest.BlockUpdate(array, 0, array.Length); treeDigest.OutputFinal(array2, 0, array2.Length); return array2; } public override void H(byte[] pkSeed, Adrs adrs, byte[] m1, byte[] m2, byte[] output) { treeDigest.BlockUpdate(pkSeed, 0, pkSeed.Length); treeDigest.BlockUpdate(adrs.value, 0, adrs.value.Length); if (robust) { byte[] array = Bitmask(pkSeed, adrs, m1, m2); treeDigest.BlockUpdate(array, 0, array.Length); } else { treeDigest.BlockUpdate(m1, 0, m1.Length); treeDigest.BlockUpdate(m2, 0, m2.Length); } treeDigest.OutputFinal(output, 0, N); } public override IndexedDigest H_msg(byte[] R, byte[] pkSeed, byte[] pkRoot, byte[] msg, int msgOff, int msgLen) { int num = (A * K + 7) / 8; uint num2 = FH / D; uint num3 = FH - num2; uint num4 = (num2 + 7) / 8; uint num5 = (num3 + 7) / 8; byte[] array = new byte[(int)(num + num5 + num4)]; treeDigest.BlockUpdate(R, 0, R.Length); treeDigest.BlockUpdate(pkSeed, 0, pkSeed.Length); treeDigest.BlockUpdate(pkRoot, 0, pkRoot.Length); treeDigest.BlockUpdate(msg, msgOff, msgLen); treeDigest.OutputFinal(array, 0, array.Length); ulong idx_tree = Pack.BE_To_UInt64_Low(array, num, (int)num5) & (ulong.MaxValue >> (int)(64 - num3)); uint idx_leaf = Pack.BE_To_UInt32_Low(array, num + (int)num5, (int)num4) & (uint.MaxValue >> (int)(32 - num2)); return new IndexedDigest(idx_tree, idx_leaf, Arrays.CopyOfRange(array, 0, num)); } public override void T_l(byte[] pkSeed, Adrs adrs, byte[] m, byte[] output) { byte[] array = m; if (robust) array = Bitmask(pkSeed, adrs, m); treeDigest.BlockUpdate(pkSeed, 0, pkSeed.Length); treeDigest.BlockUpdate(adrs.value, 0, adrs.value.Length); treeDigest.BlockUpdate(array, 0, array.Length); treeDigest.OutputFinal(output, 0, N); } public override void PRF(byte[] pkSeed, byte[] skSeed, Adrs adrs, byte[] prf, int prfOff) { treeDigest.BlockUpdate(pkSeed, 0, pkSeed.Length); treeDigest.BlockUpdate(adrs.value, 0, adrs.value.Length); treeDigest.BlockUpdate(skSeed, 0, skSeed.Length); treeDigest.OutputFinal(prf, prfOff, N); } public override byte[] PRF_msg(byte[] prf, byte[] randomiser, byte[] msg, int msgOff, int msgLen) { treeDigest.BlockUpdate(prf, 0, prf.Length); treeDigest.BlockUpdate(randomiser, 0, randomiser.Length); treeDigest.BlockUpdate(msg, msgOff, msgLen); byte[] array = new byte[N]; treeDigest.OutputFinal(array, 0, array.Length); return array; } protected byte[] Bitmask(byte[] pkSeed, Adrs adrs, byte[] m) { byte[] array = new byte[m.Length]; maskDigest.BlockUpdate(pkSeed, 0, pkSeed.Length); maskDigest.BlockUpdate(adrs.value, 0, adrs.value.Length); maskDigest.OutputFinal(array, 0, array.Length); Bytes.XorTo(m.Length, m, array); return array; } protected byte[] Bitmask(byte[] pkSeed, Adrs adrs, byte[] m1, byte[] m2) { byte[] array = new byte[m1.Length + m2.Length]; maskDigest.BlockUpdate(pkSeed, 0, pkSeed.Length); maskDigest.BlockUpdate(adrs.value, 0, adrs.value.Length); maskDigest.OutputFinal(array, 0, array.Length); Bytes.XorTo(m1.Length, m1, array); Bytes.XorTo(m2.Length, m2, 0, array, m1.Length); return array; } } internal class HarakaSEngine : SphincsPlusEngine { public HarakaSXof harakaSXof; public HarakaS256Digest harakaS256Digest; public HarakaS512Digest harakaS512Digest; public HarakaSEngine(bool robust, int n, uint w, uint d, int a, int k, uint h) : base(robust, n, w, d, a, k, h) { } public override void Init(byte[] pkSeed) { harakaSXof = new HarakaSXof(pkSeed); harakaS256Digest = new HarakaS256Digest(harakaSXof); harakaS512Digest = new HarakaS512Digest(harakaSXof); } public override byte[] F(byte[] pkSeed, Adrs adrs, byte[] m1) { byte[] array = new byte[32]; harakaS512Digest.BlockUpdate(adrs.value, 0, adrs.value.Length); if (robust) { harakaS256Digest.BlockUpdate(adrs.value, 0, adrs.value.Length); harakaS256Digest.DoFinal(array, 0); Bytes.XorTo(m1.Length, m1, array); harakaS512Digest.BlockUpdate(array, 0, m1.Length); } else harakaS512Digest.BlockUpdate(m1, 0, m1.Length); harakaS512Digest.DoFinal(array, 0); if (N != 32) return Arrays.CopyOfRange(array, 0, N); return array; } public override void H(byte[] pkSeed, Adrs adrs, byte[] m1, byte[] m2, byte[] output) { byte[] array = new byte[m1.Length + m2.Length]; Array.Copy(m1, 0, array, 0, m1.Length); Array.Copy(m2, 0, array, m1.Length, m2.Length); if (robust) Bitmask(adrs, array); harakaSXof.BlockUpdate(adrs.value, 0, adrs.value.Length); harakaSXof.BlockUpdate(array, 0, array.Length); harakaSXof.OutputFinal(output, 0, N); } public override IndexedDigest H_msg(byte[] prf, byte[] pkSeed, byte[] pkRoot, byte[] msg, int msgOff, int msgLen) { int num = A * K + 7 >> 3; uint num2 = FH / D; uint num3 = FH - num2; uint num4 = num2 + 7 >> 3; uint num5 = num3 + 7 >> 3; byte[] array = new byte[num + num5 + num4]; harakaSXof.BlockUpdate(prf, 0, prf.Length); harakaSXof.BlockUpdate(pkRoot, 0, pkRoot.Length); harakaSXof.BlockUpdate(msg, msgOff, msgLen); harakaSXof.OutputFinal(array, 0, array.Length); ulong idx_tree = Pack.BE_To_UInt64_Low(array, num, (int)num5) & (ulong.MaxValue >> (int)(64 - num3)); uint idx_leaf = Pack.BE_To_UInt32_Low(array, num + (int)num5, (int)num4) & (uint.MaxValue >> (int)(32 - num2)); return new IndexedDigest(idx_tree, idx_leaf, Arrays.CopyOfRange(array, 0, num)); } public override void T_l(byte[] pkSeed, Adrs adrs, byte[] m, byte[] output) { if (robust) Bitmask(adrs, m); harakaSXof.BlockUpdate(adrs.value, 0, adrs.value.Length); harakaSXof.BlockUpdate(m, 0, m.Length); harakaSXof.OutputFinal(output, 0, N); } public override void PRF(byte[] pkSeed, byte[] skSeed, Adrs adrs, byte[] prf, int prfOff) { byte[] array = new byte[32]; harakaS512Digest.BlockUpdate(adrs.value, 0, adrs.value.Length); harakaS512Digest.BlockUpdate(skSeed, 0, skSeed.Length); harakaS512Digest.DoFinal(array, 0); Array.Copy(array, 0, prf, prfOff, N); } public override byte[] PRF_msg(byte[] prf, byte[] randomiser, byte[] msg, int msgOff, int msgLen) { byte[] array = new byte[N]; harakaSXof.BlockUpdate(prf, 0, prf.Length); harakaSXof.BlockUpdate(randomiser, 0, randomiser.Length); harakaSXof.BlockUpdate(msg, msgOff, msgLen); harakaSXof.OutputFinal(array, 0, array.Length); return array; } protected void Bitmask(Adrs adrs, byte[] m) { byte[] array = new byte[m.Length]; harakaSXof.BlockUpdate(adrs.value, 0, adrs.value.Length); harakaSXof.OutputFinal(array, 0, array.Length); Bytes.XorTo(m.Length, array, m); } } internal readonly bool robust; internal readonly int N; internal readonly uint WOTS_W; internal readonly int WOTS_LOGW; internal readonly int WOTS_LEN; internal readonly int WOTS_LEN1; internal readonly int WOTS_LEN2; internal readonly uint D; internal readonly int A; internal readonly int K; internal readonly uint FH; internal readonly uint H_PRIME; internal readonly uint T; internal readonly int SignatureLength; internal SphincsPlusEngine(bool robust, int n, uint w, uint d, int a, int k, uint h) { N = n; switch (w) { case 16: WOTS_LOGW = 4; WOTS_LEN1 = 8 * N / WOTS_LOGW; if (N <= 8) WOTS_LEN2 = 2; else if (N <= 136) { WOTS_LEN2 = 3; } else { if (N > 256) throw new ArgumentException("cannot precompute SPX_WOTS_LEN2 for n outside {2, .., 256}"); WOTS_LEN2 = 4; } break; case 256: WOTS_LOGW = 8; WOTS_LEN1 = 8 * N / WOTS_LOGW; if (N <= 1) WOTS_LEN2 = 1; else { if (N > 256) throw new ArgumentException("cannot precompute SPX_WOTS_LEN2 for n outside {2, .., 256}"); WOTS_LEN2 = 2; } break; default: throw new ArgumentException("wots_w assumed 16 or 256"); } WOTS_W = w; WOTS_LEN = WOTS_LEN1 + WOTS_LEN2; this.robust = robust; D = d; A = a; K = k; FH = h; H_PRIME = h / d; T = (uint)(1 << a); SignatureLength = (1 + K * (1 + A) + (int)FH + (int)D * WOTS_LEN) * N; } public abstract void Init(byte[] pkSeed); public abstract byte[] F(byte[] pkSeed, Adrs adrs, byte[] m1); public abstract void H(byte[] pkSeed, Adrs adrs, byte[] m1, byte[] m2, byte[] output); public abstract IndexedDigest H_msg(byte[] prf, byte[] pkSeed, byte[] pkRoot, byte[] msg, int msgOff, int msgLen); public abstract void T_l(byte[] pkSeed, Adrs adrs, byte[] m, byte[] output); public abstract void PRF(byte[] pkSeed, byte[] skSeed, Adrs adrs, byte[] prf, int prfOff); public abstract byte[] PRF_msg(byte[] prf, byte[] randomiser, byte[] msg, int msgOff, int msgLen); } }