From c906c8b1b8be7a05c3ce0a49e465205d337971b1 Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Wed, 12 Oct 2022 22:07:18 +0700 Subject: Refactoring SphincsPlus (performance) --- crypto/src/pqc/crypto/sphincsplus/Fors.cs | 11 +- crypto/src/pqc/crypto/sphincsplus/HT.cs | 33 ++- crypto/src/pqc/crypto/sphincsplus/HarakaSXof.cs | 57 +++- crypto/src/pqc/crypto/sphincsplus/HarakaS_X86.cs | 26 +- .../pqc/crypto/sphincsplus/SPHINCSPlusEngine.cs | 319 +++++++++++++++++---- crypto/src/pqc/crypto/sphincsplus/WotsPlus.cs | 146 +++++++++- 6 files changed, 489 insertions(+), 103 deletions(-) diff --git a/crypto/src/pqc/crypto/sphincsplus/Fors.cs b/crypto/src/pqc/crypto/sphincsplus/Fors.cs index 1698d1be7..7c83c2017 100644 --- a/crypto/src/pqc/crypto/sphincsplus/Fors.cs +++ b/crypto/src/pqc/crypto/sphincsplus/Fors.cs @@ -48,7 +48,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.SphincsPlus adrsTreeIndex = (adrsTreeIndex - 1) / 2; adrs.SetTreeIndex(adrsTreeIndex); - node = engine.H(pkSeed, adrs, stack.Pop().nodeValue, node); + engine.H(pkSeed, adrs, stack.Pop().nodeValue, node, node); //topmost node is now one layer higher adrs.SetTreeHeight(++adrsTreeHeight); @@ -123,13 +123,13 @@ namespace Org.BouncyCastle.Pqc.Crypto.SphincsPlus { adrsTreeIndex = adrsTreeIndex / 2; adrs.SetTreeIndex(adrsTreeIndex); - node = engine.H(pkSeed, adrs, node, authPath[j]); + engine.H(pkSeed, adrs, node, authPath[j], node); } else { adrsTreeIndex = (adrsTreeIndex - 1) / 2; adrs.SetTreeIndex(adrsTreeIndex); - node = engine.H(pkSeed, adrs, authPath[j], node); + engine.H(pkSeed, adrs, authPath[j], node, node); } } @@ -139,7 +139,10 @@ namespace Org.BouncyCastle.Pqc.Crypto.SphincsPlus Adrs forspkAdrs = new Adrs(adrs); // copy address to create FTS public key address forspkAdrs.SetAdrsType(Adrs.FORS_PK); forspkAdrs.SetKeyPairAddress(adrs.GetKeyPairAddress()); - return engine.T_l(pkSeed, forspkAdrs, Arrays.ConcatenateAll(root)); + + byte[] result = new byte[engine.N]; + engine.T_l(pkSeed, forspkAdrs, Arrays.ConcatenateAll(root), result); + return result; } /** diff --git a/crypto/src/pqc/crypto/sphincsplus/HT.cs b/crypto/src/pqc/crypto/sphincsplus/HT.cs index 59d0aeb1d..15893fc46 100644 --- a/crypto/src/pqc/crypto/sphincsplus/HT.cs +++ b/crypto/src/pqc/crypto/sphincsplus/HT.cs @@ -1,11 +1,10 @@ -using System; using System.Collections.Generic; using Org.BouncyCastle.Utilities; namespace Org.BouncyCastle.Pqc.Crypto.SphincsPlus { - class HT + internal class HT { private byte[] skSeed; private byte[] pkSeed; @@ -14,7 +13,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.SphincsPlus internal byte[] HTPubKey; - public HT(SphincsPlusEngine engine, byte[] skSeed, byte[] pkSeed) + internal HT(SphincsPlusEngine engine, byte[] skSeed, byte[] pkSeed) { this.skSeed = skSeed; this.pkSeed = pkSeed; @@ -75,14 +74,14 @@ namespace Org.BouncyCastle.Pqc.Crypto.SphincsPlus return Arrays.ConcatenateAll(totSigs); } - byte[] xmss_PKgen(byte[] skSeed, byte[] pkSeed, Adrs adrs) + private byte[] xmss_PKgen(byte[] skSeed, byte[] pkSeed, Adrs adrs) { return TreeHash(skSeed, 0, engine.H_PRIME, pkSeed, adrs); } // Input: index idx, XMSS signature SIG_XMSS = (sig || AUTH), n-byte message M, public seed PK.seed, address Adrs // Output: n-byte root value node[0] - byte[] xmss_pkFromSig(uint idx, SIG_XMSS sig_xmss, byte[] M, byte[] pkSeed, Adrs paramAdrs) + private byte[] xmss_pkFromSig(uint idx, SIG_XMSS sig_xmss, byte[] M, byte[] pkSeed, Adrs paramAdrs) { Adrs adrs = new Adrs(paramAdrs); @@ -92,8 +91,8 @@ namespace Org.BouncyCastle.Pqc.Crypto.SphincsPlus byte[] sig = sig_xmss.WotsSig; byte[][] AUTH = sig_xmss.XmssAuth; - byte[] node0 = wots.PKFromSig(sig, M, pkSeed, adrs); - byte[] node1 = null; + byte[] node = new byte[engine.N]; + wots.PKFromSig(sig, M, pkSeed, adrs, node); // compute root from WOTS+ pk and AUTH adrs.SetAdrsType(Adrs.TREE); @@ -104,24 +103,22 @@ namespace Org.BouncyCastle.Pqc.Crypto.SphincsPlus if (((idx / (1 << (int)k)) % 2) == 0) { adrs.SetTreeIndex(adrs.GetTreeIndex() / 2); - node1 = engine.H(pkSeed, adrs, node0, AUTH[k]); + engine.H(pkSeed, adrs, node, AUTH[k], node); } else { adrs.SetTreeIndex((adrs.GetTreeIndex() - 1) / 2); - node1 = engine.H(pkSeed, adrs, AUTH[k], node0); + engine.H(pkSeed, adrs, AUTH[k], node, node); } - - node0 = node1; } - return node0; + return node; } // # Input: n-byte message M, secret seed SK.seed, index idx, public seed PK.seed, // address Adrs // # Output: XMSS signature SIG_XMSS = (sig || AUTH) - SIG_XMSS xmss_sign(byte[] M, byte[] skSeed, uint idx, byte[] pkSeed, Adrs paramAdrs) + private SIG_XMSS xmss_sign(byte[] M, byte[] skSeed, uint idx, byte[] pkSeed, Adrs paramAdrs) { byte[][] AUTH = new byte[engine.H_PRIME][]; @@ -151,7 +148,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.SphincsPlus // Input: Secret seed SK.seed, start index s, target node height z, public seed //PK.seed, address Adrs // Output: n-byte root node - top node on Stack - byte[] TreeHash(byte[] skSeed, uint s, uint z, byte[] pkSeed, Adrs adrsParam) + private byte[] TreeHash(byte[] skSeed, uint s, uint z, byte[] pkSeed, Adrs adrsParam) { if (s % (1 << (int)z) != 0) return null; @@ -163,7 +160,9 @@ namespace Org.BouncyCastle.Pqc.Crypto.SphincsPlus { adrs.SetAdrsType(Adrs.WOTS_HASH); adrs.SetKeyPairAddress(s + idx); - byte[] node = wots.PKGen(skSeed, pkSeed, adrs); + + byte[] node = new byte[engine.N]; + wots.PKGen(skSeed, pkSeed, adrs, node); adrs.SetAdrsType(Adrs.TREE); adrs.SetTreeHeight(1); @@ -178,7 +177,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.SphincsPlus adrsTreeIndex = (adrsTreeIndex - 1) / 2; adrs.SetTreeIndex(adrsTreeIndex); - node = engine.H(pkSeed, adrs, stack.Pop().nodeValue, node); + engine.H(pkSeed, adrs, stack.Pop().nodeValue, node, node); //topmost node is now one layer higher adrs.SetTreeHeight(++adrsTreeHeight); @@ -193,7 +192,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.SphincsPlus // # Input: Message M, signature SIG_HT, public seed PK.seed, tree index idx_tree, // leaf index idx_leaf, HT public key PK_HT. // # Output: bool - public bool Verify(byte[] M, SIG_XMSS[] sig_ht, byte[] pkSeed, ulong idx_tree, uint idx_leaf, byte[] PK_HT) + internal bool Verify(byte[] M, SIG_XMSS[] sig_ht, byte[] pkSeed, ulong idx_tree, uint idx_leaf, byte[] PK_HT) { // init Adrs adrs = new Adrs(); diff --git a/crypto/src/pqc/crypto/sphincsplus/HarakaSXof.cs b/crypto/src/pqc/crypto/sphincsplus/HarakaSXof.cs index f55a87778..beff653a7 100644 --- a/crypto/src/pqc/crypto/sphincsplus/HarakaSXof.cs +++ b/crypto/src/pqc/crypto/sphincsplus/HarakaSXof.cs @@ -1,5 +1,9 @@ using System; +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER +using Org.BouncyCastle.Utilities; +#endif + namespace Org.BouncyCastle.Pqc.Crypto.SphincsPlus { internal sealed class HarakaSXof @@ -35,8 +39,8 @@ namespace Org.BouncyCastle.Pqc.Crypto.SphincsPlus public void BlockUpdate(byte[] input, int inOff, int len) { - int i = inOff, j, loop = (len + off) >> 5; - for (j = 0; j < loop; ++j) + int i = inOff, loop = (len + off) >> 5; + for (int j = 0; j < loop; ++j) { while (off < 32) { @@ -51,6 +55,27 @@ namespace Org.BouncyCastle.Pqc.Crypto.SphincsPlus } } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public void BlockUpdate(ReadOnlySpan input) + { + int len = input.Length; + int i = 0, loop = (len + off) >> 5; + for (int j = 0; j < loop; ++j) + { + while (off < 32) + { + buffer[off++] ^= input[i++]; + } + Haraka512Perm(buffer); + off = 0; + } + while (i < len) + { + buffer[off++] ^= input[i++]; + } + } +#endif + public int OutputFinal(byte[] output, int outOff, int len) { int outLen = len; @@ -77,5 +102,33 @@ namespace Org.BouncyCastle.Pqc.Crypto.SphincsPlus return outLen; } + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public int OutputFinal(Span output) + { + int outLen = output.Length; + + //Finalize + buffer[off] ^= 0x1F; + buffer[31] ^= 128; + + //Squeeze + while (output.Length >= 32) + { + Haraka512Perm(buffer); + output[..32].CopyFrom(buffer); + output = output[32..]; + } + if (!output.IsEmpty) + { + Haraka512Perm(buffer); + output.CopyFrom(buffer); + } + + Reset(); + + return outLen; + } +#endif } } diff --git a/crypto/src/pqc/crypto/sphincsplus/HarakaS_X86.cs b/crypto/src/pqc/crypto/sphincsplus/HarakaS_X86.cs index 35d7c883e..3975f02ff 100644 --- a/crypto/src/pqc/crypto/sphincsplus/HarakaS_X86.cs +++ b/crypto/src/pqc/crypto/sphincsplus/HarakaS_X86.cs @@ -121,26 +121,32 @@ namespace Org.BouncyCastle.Pqc.Crypto.SphincsPlus public int Output(Span output) { + int result = output.Length; + if (m_state != State.Squeezing) { m_buf[m_bufPos] ^= 0x1F; m_buf[31] ^= 0x80; m_bufPos = 32; m_state = State.Squeezing; - } - int result = output.Length; - - int available = 32 - m_bufPos; - if (output.Length <= available) + if (output.IsEmpty) + return result; + } + else { - output.CopyFrom(m_buf.AsSpan(m_bufPos)); - m_bufPos += available; - return result; + int available = 32 - m_bufPos; + if (output.Length <= available) + { + output.CopyFrom(m_buf.AsSpan(m_bufPos)); + m_bufPos += available; + return result; + } + + output[..available].CopyFrom(m_buf.AsSpan(m_bufPos)); + output = output[available..]; } - output[..available].CopyFrom(m_buf.AsSpan(m_bufPos)); - output = output[available..]; Debug.Assert(!output.IsEmpty); while (output.Length > 32) diff --git a/crypto/src/pqc/crypto/sphincsplus/SPHINCSPlusEngine.cs b/crypto/src/pqc/crypto/sphincsplus/SPHINCSPlusEngine.cs index 3c295c3bd..72fd471d6 100644 --- a/crypto/src/pqc/crypto/sphincsplus/SPHINCSPlusEngine.cs +++ b/crypto/src/pqc/crypto/sphincsplus/SPHINCSPlusEngine.cs @@ -94,11 +94,23 @@ namespace Org.BouncyCastle.Pqc.Crypto.SphincsPlus public abstract byte[] F(byte[] pkSeed, Adrs adrs, byte[] m1); - public abstract byte[] H(byte[] pkSeed, Adrs adrs, byte[] m1, byte[] m2); +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public abstract void F(byte[] pkSeed, Adrs adrs, Span m1); +#endif + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public abstract void H(byte[] pkSeed, Adrs adrs, byte[] m1, byte[] m2, Span output); +#else + public abstract void H(byte[] pkSeed, Adrs adrs, byte[] m1, byte[] m2, byte[] output); +#endif public abstract IndexedDigest H_msg(byte[] prf, byte[] pkSeed, byte[] pkRoot, byte[] message); - public abstract byte[] T_l(byte[] pkSeed, Adrs adrs, byte[] m); +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public abstract void T_l(byte[] pkSeed, Adrs adrs, byte[] m, Span output); +#else + public abstract void T_l(byte[] pkSeed, Adrs adrs, byte[] m, byte[] output); +#endif public abstract void PRF(byte[] pkSeed, byte[] skSeed, Adrs adrs, byte[] prf, int prfOff); @@ -177,7 +189,54 @@ namespace Org.BouncyCastle.Pqc.Crypto.SphincsPlus return Arrays.CopyOfRange(sha256Buf, 0, N); } - public override byte[] H(byte[] pkSeed, Adrs adrs, byte[] m1, byte[] m2) +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public override void F(byte[] pkSeed, Adrs adrs, Span m1) + { + byte[] compressedAdrs = CompressedAdrs(adrs); + + ((IMemoable)sha256).Reset(sha256Memo); + + sha256.BlockUpdate(compressedAdrs); + + if (robust) + { + sha256.BlockUpdate(Bitmask256(Arrays.Concatenate(pkSeed, compressedAdrs), m1)); + } + else + { + sha256.BlockUpdate(m1); + } + + sha256.DoFinal(sha256Buf); + m1.CopyFrom(sha256Buf); + } +#endif + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public override void H(byte[] pkSeed, Adrs adrs, byte[] m1, byte[] m2, Span output) + { + byte[] compressedAdrs = CompressedAdrs(adrs); + + ((IMemoable)msgDigest).Reset(msgMemo); + + msgDigest.BlockUpdate(compressedAdrs); + if (robust) + { + byte[] m1m2 = Bitmask(Arrays.Concatenate(pkSeed, compressedAdrs), m1, m2); + msgDigest.BlockUpdate(m1m2); + } + else + { + msgDigest.BlockUpdate(m1); + msgDigest.BlockUpdate(m2); + } + + msgDigest.DoFinal(msgDigestBuf); + + output[..N].CopyFrom(msgDigestBuf); + } +#else + public override void H(byte[] pkSeed, Adrs adrs, byte[] m1, byte[] m2, byte[] output) { byte[] compressedAdrs = CompressedAdrs(adrs); @@ -197,8 +256,9 @@ namespace Org.BouncyCastle.Pqc.Crypto.SphincsPlus msgDigest.DoFinal(msgDigestBuf, 0); - return Arrays.CopyOfRange(msgDigestBuf, 0, N); + Array.Copy(msgDigestBuf, 0, output, 0, N); } +#endif public override IndexedDigest H_msg(byte[] prf, byte[] pkSeed, byte[] pkRoot, byte[] message) { @@ -230,7 +290,11 @@ namespace Org.BouncyCastle.Pqc.Crypto.SphincsPlus return new IndexedDigest(treeIndex, leafIndex, Arrays.CopyOfRange(output, 0, forsMsgBytes)); } - public override byte[] T_l(byte[] pkSeed, Adrs adrs, byte[] m) +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public override void T_l(byte[] pkSeed, Adrs adrs, byte[] m, Span output) +#else + public override void T_l(byte[] pkSeed, Adrs adrs, byte[] m, byte[] output) +#endif { byte[] compressedAdrs = CompressedAdrs(adrs); if (robust) @@ -238,14 +302,17 @@ namespace Org.BouncyCastle.Pqc.Crypto.SphincsPlus m = Bitmask(Arrays.Concatenate(pkSeed, compressedAdrs), m); } - ((IMemoable)msgDigest).Reset(msgMemo); msgDigest.BlockUpdate(compressedAdrs, 0, compressedAdrs.Length); msgDigest.BlockUpdate(m, 0, m.Length); msgDigest.DoFinal(msgDigestBuf, 0); - return Arrays.CopyOfRange(msgDigestBuf, 0, N); +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + output[..N].CopyFrom(msgDigestBuf); +#else + Array.Copy(msgDigestBuf, 0, output, 0, N); +#endif } public override void PRF(byte[] pkSeed, byte[] skSeed, Adrs adrs, byte[] prf, int prfOff) @@ -300,7 +367,6 @@ namespace Org.BouncyCastle.Pqc.Crypto.SphincsPlus return mask; } - protected byte[] Bitmask(byte[] key, byte[] m1, byte[] m2) { byte[] mask = new byte[m1.Length + m2.Length]; @@ -322,7 +388,11 @@ namespace Org.BouncyCastle.Pqc.Crypto.SphincsPlus return mask; } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + protected byte[] Bitmask256(byte[] key, ReadOnlySpan m) +#else protected byte[] Bitmask256(byte[] key, byte[] m) +#endif { byte[] mask = new byte[m.Length]; @@ -368,18 +438,50 @@ namespace Org.BouncyCastle.Pqc.Crypto.SphincsPlus } byte[] rv = new byte[N]; - treeDigest.BlockUpdate(pkSeed, 0, pkSeed.Length); treeDigest.BlockUpdate(adrs.value, 0, adrs.value.Length); treeDigest.BlockUpdate(mTheta, 0, mTheta.Length); treeDigest.OutputFinal(rv, 0, rv.Length); - return rv; } - public override byte[] H(byte[] pkSeed, Adrs adrs, byte[] m1, byte[] m2) +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public override void F(byte[] pkSeed, Adrs adrs, Span m1) + { + if (robust) + { + Bitmask(pkSeed, adrs, m1); + } + + treeDigest.BlockUpdate(pkSeed); + treeDigest.BlockUpdate(adrs.value); + treeDigest.BlockUpdate(m1); + treeDigest.OutputFinal(m1); + } +#endif + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public override void H(byte[] pkSeed, Adrs adrs, byte[] m1, byte[] m2, Span output) + { + treeDigest.BlockUpdate(pkSeed); + treeDigest.BlockUpdate(adrs.value); + + if (robust) + { + byte[] m1m2 = Bitmask(pkSeed, adrs, m1, m2); + treeDigest.BlockUpdate(m1m2); + } + else + { + treeDigest.BlockUpdate(m1); + treeDigest.BlockUpdate(m2); + } + + treeDigest.OutputFinal(output[..N]); + } +#else + public override void H(byte[] pkSeed, Adrs adrs, byte[] m1, byte[] m2, byte[] output) { - byte[] rv = new byte[N]; treeDigest.BlockUpdate(pkSeed, 0, pkSeed.Length); treeDigest.BlockUpdate(adrs.value, 0, adrs.value.Length); @@ -395,10 +497,9 @@ namespace Org.BouncyCastle.Pqc.Crypto.SphincsPlus treeDigest.BlockUpdate(m2, 0, m2.Length); } - treeDigest.OutputFinal(rv, 0, rv.Length); - - return rv; + treeDigest.OutputFinal(output, 0, N); } +#endif public override IndexedDigest H_msg(byte[] R, byte[] pkSeed, byte[] pkRoot, byte[] message) { @@ -427,7 +528,11 @@ namespace Org.BouncyCastle.Pqc.Crypto.SphincsPlus return new IndexedDigest(treeIndex, leafIndex, Arrays.CopyOfRange(output, 0, forsMsgBytes)); } - public override byte[] T_l(byte[] pkSeed, Adrs adrs, byte[] m) +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public override void T_l(byte[] pkSeed, Adrs adrs, byte[] m, Span output) +#else + public override void T_l(byte[] pkSeed, Adrs adrs, byte[] m, byte[] output) +#endif { byte[] mTheta = m; if (robust) @@ -435,14 +540,14 @@ namespace Org.BouncyCastle.Pqc.Crypto.SphincsPlus mTheta = Bitmask(pkSeed, adrs, m); } - byte[] rv = new byte[N]; - treeDigest.BlockUpdate(pkSeed, 0, pkSeed.Length); treeDigest.BlockUpdate(adrs.value, 0, adrs.value.Length); treeDigest.BlockUpdate(mTheta, 0, mTheta.Length); - treeDigest.OutputFinal(rv, 0, rv.Length); - - return rv; +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + treeDigest.OutputFinal(output[..N]); +#else + treeDigest.OutputFinal(output, 0, N); +#endif } public override void PRF(byte[] pkSeed, byte[] skSeed, Adrs adrs, byte[] prf, int prfOff) @@ -479,6 +584,21 @@ namespace Org.BouncyCastle.Pqc.Crypto.SphincsPlus return mask; } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + protected void Bitmask(ReadOnlySpan pkSeed, Adrs adrs, Span m) + { + Span mask = stackalloc byte[m.Length]; + maskDigest.BlockUpdate(pkSeed); + maskDigest.BlockUpdate(adrs.value); + maskDigest.OutputFinal(mask); + + for (int i = 0; i < m.Length; ++i) + { + m[i] ^= mask[i]; + } + } +#endif + protected byte[] Bitmask(byte[] pkSeed, Adrs adrs, byte[] m1, byte[] m2) { byte[] mask = new byte[m1.Length + m2.Length]; @@ -544,18 +664,59 @@ namespace Org.BouncyCastle.Pqc.Crypto.SphincsPlus return N == 32 ? hash : Arrays.CopyOfRange(hash, 0, N); } - public override byte[] H(byte[] pkSeed, Adrs adrs, byte[] m1, byte[] m2) +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public override void F(byte[] pkSeed, Adrs adrs, Span m1) + { + Span hash = stackalloc byte[32]; + if (robust) + { + harakaS256Digest.BlockUpdate(adrs.value); + harakaS256Digest.DoFinal(hash); + for (int i = 0; i < m1.Length; ++i) + { + m1[i] ^= hash[i]; + } + } + + harakaS512Digest.BlockUpdate(adrs.value); + harakaS512Digest.BlockUpdate(m1); + // NOTE The digest implementation implicitly pads the input with zeros up to 64 length + harakaS512Digest.DoFinal(hash); + m1.CopyFrom(hash); + } +#endif + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public override void H(byte[] pkSeed, Adrs adrs, byte[] m1, byte[] m2, Span output) + { + Span m = stackalloc byte[m1.Length + m2.Length]; + m1.CopyTo(m); + m2.CopyTo(m[m1.Length..]); + if (robust) + { + Bitmask(adrs, m); + } + + harakaSXof.BlockUpdate(adrs.value); + harakaSXof.BlockUpdate(m); + harakaSXof.OutputFinal(output[..N]); + } +#else + public override void H(byte[] pkSeed, Adrs adrs, byte[] m1, byte[] m2, byte[] output) { - byte[] rv = new byte[N]; byte[] m = new byte[m1.Length + m2.Length]; Array.Copy(m1, 0, m, 0, m1.Length); Array.Copy(m2, 0, m, m1.Length, m2.Length); - m = Bitmask(adrs, m); + if (robust) + { + Bitmask(adrs, m); + } + harakaSXof.BlockUpdate(adrs.value, 0, adrs.value.Length); harakaSXof.BlockUpdate(m, 0, m.Length); - harakaSXof.OutputFinal(rv, 0, rv.Length); - return rv; + harakaSXof.OutputFinal(output, 0, N); } +#endif public override IndexedDigest H_msg(byte[] prf, byte[] pkSeed, byte[] pkRoot, byte[] message) { @@ -582,14 +743,24 @@ namespace Org.BouncyCastle.Pqc.Crypto.SphincsPlus return new IndexedDigest(treeIndex, leafIndex, Arrays.CopyOfRange(output, 0, forsMsgBytes)); } - public override byte[] T_l(byte[] pkSeed, Adrs adrs, byte[] m) +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public override void T_l(byte[] pkSeed, Adrs adrs, byte[] m, Span output) +#else + public override void T_l(byte[] pkSeed, Adrs adrs, byte[] m, byte[] output) +#endif { - byte[] rv = new byte[N]; - m = Bitmask(adrs, m); + if (robust) + { + Bitmask(adrs, m); + } + harakaSXof.BlockUpdate(adrs.value, 0, adrs.value.Length); harakaSXof.BlockUpdate(m, 0, m.Length); - harakaSXof.OutputFinal(rv, 0, rv.Length); - return rv; +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + harakaSXof.OutputFinal(output[..N]); +#else + harakaSXof.OutputFinal(output, 0, N); +#endif } public override void PRF(byte[] pkSeed, byte[] skSeed, Adrs adrs, byte[] prf, int prfOff) @@ -611,20 +782,29 @@ namespace Org.BouncyCastle.Pqc.Crypto.SphincsPlus return rv; } - protected byte[] Bitmask(Adrs adrs, byte[] m) + protected void Bitmask(Adrs adrs, byte[] m) { - if (robust) + byte[] mask = new byte[m.Length]; + harakaSXof.BlockUpdate(adrs.value, 0, adrs.value.Length); + harakaSXof.OutputFinal(mask, 0, mask.Length); + for (int i = 0; i < m.Length; ++i) { - byte[] mask = new byte[m.Length]; - harakaSXof.BlockUpdate(adrs.value, 0, adrs.value.Length); - harakaSXof.OutputFinal(mask, 0, mask.Length); - for (int i = 0; i < m.Length; ++i) - { - m[i] ^= mask[i]; - } + m[i] ^= mask[i]; } - return m; } + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + protected void Bitmask(Adrs adrs, Span m) + { + Span mask = stackalloc byte[m.Length]; + harakaSXof.BlockUpdate(adrs.value); + harakaSXof.OutputFinal(mask); + for (int i = 0; i < m.Length; ++i) + { + m[i] ^= mask[i]; + } + } +#endif } #if NETCOREAPP3_0_OR_GREATER @@ -668,18 +848,41 @@ namespace Org.BouncyCastle.Pqc.Crypto.SphincsPlus return buf[..N].ToArray(); } - public override byte[] H(byte[] pkSeed, Adrs adrs, byte[] m1, byte[] m2) + public override void F(byte[] pkSeed, Adrs adrs, Span m1) + { + Span buf = stackalloc byte[64]; + adrs.value.CopyTo(buf); + + if (robust) + { + Span mask = stackalloc byte[32]; + Haraka256_X86.Hash(adrs.value, mask, m_harakaS.RoundConstants); + for (int i = 0; i < m1.Length; ++i) + { + buf[32 + i] = (byte)(m1[i] ^ mask[i]); + } + } + else + { + m1.CopyTo(buf[32..]); + } + Haraka512_X86.Hash(buf, buf, m_harakaS.RoundConstants); + m1.CopyFrom(buf); + } + + public override void H(byte[] pkSeed, Adrs adrs, byte[] m1, byte[] m2, Span output) { Span m = stackalloc byte[m1.Length + m2.Length]; m1.CopyTo(m); m2.CopyTo(m[m1.Length..]); - Bitmask(adrs, m); + if (robust) + { + Bitmask(adrs, m); + } - byte[] rv = new byte[N]; m_harakaS.BlockUpdate(adrs.value); m_harakaS.BlockUpdate(m); - m_harakaS.OutputFinal(rv); - return rv; + m_harakaS.OutputFinal(output[..N]); } public override IndexedDigest H_msg(byte[] prf, byte[] pkSeed, byte[] pkRoot, byte[] message) @@ -710,15 +913,16 @@ namespace Org.BouncyCastle.Pqc.Crypto.SphincsPlus return new IndexedDigest(treeIndex, leafIndex, output); } - public override byte[] T_l(byte[] pkSeed, Adrs adrs, byte[] m) + public override void T_l(byte[] pkSeed, Adrs adrs, byte[] m, Span output) { - Bitmask(adrs, m); + if (robust) + { + Bitmask(adrs, m); + } - byte[] rv = new byte[N]; m_harakaS.BlockUpdate(adrs.value); m_harakaS.BlockUpdate(m); - m_harakaS.OutputFinal(rv); - return rv; + m_harakaS.OutputFinal(output[..N]); } public override void PRF(byte[] pkSeed, byte[] skSeed, Adrs adrs, byte[] prf, int prfOff) @@ -742,15 +946,12 @@ namespace Org.BouncyCastle.Pqc.Crypto.SphincsPlus protected void Bitmask(Adrs adrs, Span m) { - if (robust) + Span mask = stackalloc byte[m.Length]; + m_harakaS.BlockUpdate(adrs.value); + m_harakaS.OutputFinal(mask); + for (int i = 0; i < m.Length; ++i) { - Span mask = stackalloc byte[m.Length]; - m_harakaS.BlockUpdate(adrs.value); - m_harakaS.OutputFinal(mask); - for (int i = 0; i < m.Length; ++i) - { - m[i] ^= mask[i]; - } + m[i] ^= mask[i]; } } } diff --git a/crypto/src/pqc/crypto/sphincsplus/WotsPlus.cs b/crypto/src/pqc/crypto/sphincsplus/WotsPlus.cs index b254530d9..bd2d306b1 100644 --- a/crypto/src/pqc/crypto/sphincsplus/WotsPlus.cs +++ b/crypto/src/pqc/crypto/sphincsplus/WotsPlus.cs @@ -16,12 +16,20 @@ namespace Org.BouncyCastle.Pqc.Crypto.SphincsPlus this.w = this.engine.WOTS_W; } - internal byte[] PKGen(byte[] skSeed, byte[] pkSeed, Adrs paramAdrs) +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + internal void PKGen(byte[] skSeed, byte[] pkSeed, Adrs paramAdrs, Span output) +#else + internal void PKGen(byte[] skSeed, byte[] pkSeed, Adrs paramAdrs, byte[] output) +#endif { Adrs wotspkAdrs = new Adrs(paramAdrs); // copy address to create OTS public key address +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + byte[] tmpConcat = new byte[engine.WOTS_LEN * engine.N]; +#else byte[][] tmp = new byte[engine.WOTS_LEN][]; byte[] sk = new byte[engine.N]; +#endif for (uint i = 0; i < engine.WOTS_LEN; i++) { Adrs adrs = new Adrs(paramAdrs); @@ -30,29 +38,63 @@ namespace Org.BouncyCastle.Pqc.Crypto.SphincsPlus adrs.SetChainAddress(i); adrs.SetHashAddress(0); +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + engine.PRF(pkSeed, skSeed, adrs, tmpConcat, engine.N * (int)i); +#else engine.PRF(pkSeed, skSeed, adrs, sk, 0); +#endif adrs.SetAdrsType(Adrs.WOTS_HASH); adrs.SetKeyPairAddress(paramAdrs.GetKeyPairAddress()); adrs.SetChainAddress(i); adrs.SetHashAddress(0); +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + Chain(0, w - 1, pkSeed, adrs, tmpConcat.AsSpan(engine.N * (int)i, engine.N)); +#else tmp[i] = Chain(sk, 0, w - 1, pkSeed, adrs); +#endif } wotspkAdrs.SetAdrsType(Adrs.WOTS_PK); wotspkAdrs.SetKeyPairAddress(paramAdrs.GetKeyPairAddress()); - return engine.T_l(pkSeed, wotspkAdrs, Arrays.ConcatenateAll(tmp)); +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + engine.T_l(pkSeed, wotspkAdrs, tmpConcat, output); +#else + engine.T_l(pkSeed, wotspkAdrs, Arrays.ConcatenateAll(tmp), output); +#endif } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER // #Input: Input string X, start index i, number of steps s, public seed PK.seed, address Adrs // #Output: value of F iterated s times on X - internal byte[] Chain(byte[] X, uint i, uint s, byte[] pkSeed, Adrs adrs) + private bool Chain(uint i, uint s, byte[] pkSeed, Adrs adrs, Span X) + { + if (s == 0) + return true; + + // TODO Check this since the highest we use is i + s - 1 + if ((i + s) > (this.w - 1)) + return false; + + for (uint j = 0; j < s; ++j) + { + adrs.SetHashAddress(i + j); + engine.F(pkSeed, adrs, X); + } + + return true; + } +#else + // #Input: Input string X, start index i, number of steps s, public seed PK.seed, address Adrs + // #Output: value of F iterated s times on X + private byte[] Chain(byte[] X, uint i, uint s, byte[] pkSeed, Adrs adrs) { if (s == 0) return Arrays.Clone(X); + // TODO Check this since the highest we use is i + s - 1 if ((i + s) > (this.w - 1)) return null; @@ -64,6 +106,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.SphincsPlus } return result; } +#endif // #Input: Message M, secret seed SK.seed, public seed PK.seed, address Adrs // #Output: WOTS+ signature sig @@ -71,10 +114,17 @@ namespace Org.BouncyCastle.Pqc.Crypto.SphincsPlus { Adrs adrs = new Adrs(paramAdrs); +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + Span msg = stackalloc uint[engine.WOTS_LEN]; + + // convert message to base w + BaseW(M, w, msg[..engine.WOTS_LEN1]); +#else uint[] msg = new uint[engine.WOTS_LEN]; // convert message to base w BaseW(M, 0, w, msg, 0, engine.WOTS_LEN1); +#endif // compute checksum uint csum = 0; @@ -89,34 +139,76 @@ namespace Org.BouncyCastle.Pqc.Crypto.SphincsPlus csum <<= 8 - (engine.WOTS_LEN2 * engine.WOTS_LOGW % 8); } int len_2_bytes = (engine.WOTS_LEN2 * engine.WOTS_LOGW + 7) / 8; + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + Span csum_bytes = stackalloc byte[4]; + Pack.UInt32_To_BE(csum, csum_bytes); + BaseW(csum_bytes[^len_2_bytes..], w, msg[engine.WOTS_LEN1..]); + + byte[] sigConcat = new byte[engine.WOTS_LEN * engine.N]; +#else byte[] csum_bytes = Pack.UInt32_To_BE(csum); BaseW(csum_bytes, 4 - len_2_bytes, w, msg, engine.WOTS_LEN1, engine.WOTS_LEN2); byte[][] sig = new byte[engine.WOTS_LEN][]; byte[] sk = new byte[engine.N]; - for (uint i = 0; i < engine.WOTS_LEN; i++) +#endif + for (int i = 0; i < engine.WOTS_LEN; i++) { adrs.SetAdrsType(Adrs.WOTS_PRF); adrs.SetKeyPairAddress(paramAdrs.GetKeyPairAddress()); - adrs.SetChainAddress(i); + adrs.SetChainAddress((uint)i); adrs.SetHashAddress(0); +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + engine.PRF(pkSeed, skSeed, adrs, sigConcat, engine.N * i); +#else engine.PRF(pkSeed, skSeed, adrs, sk, 0); +#endif adrs.SetAdrsType(Adrs.WOTS_HASH); adrs.SetKeyPairAddress(paramAdrs.GetKeyPairAddress()); - adrs.SetChainAddress(i); + adrs.SetChainAddress((uint)i); adrs.SetHashAddress(0); +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + Chain(0, msg[i], pkSeed, adrs, sigConcat.AsSpan(engine.N * i, engine.N)); +#else sig[i] = Chain(sk, 0, msg[i], pkSeed, adrs); +#endif } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + return sigConcat; +#else return Arrays.ConcatenateAll(sig); +#endif } // // Input: len_X-byte string X, int w, output length out_len // Output: outLen int array basew +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + internal void BaseW(ReadOnlySpan X, uint w, Span output) + { + int total = 0; + int bits = 0; + int XOff = 0; + int outOff = 0; + + for (int consumed = 0; consumed < output.Length; consumed++) + { + if (bits == 0) + { + total = X[XOff++]; + bits += 8; + } + + bits -= engine.WOTS_LOGW; + output[outOff++] = (uint)((total >> bits) & (w - 1)); + } + } +#else internal void BaseW(byte[] X, int XOff, uint w, uint[] output, int outOff, int outLen) { int total = 0; @@ -134,15 +226,27 @@ namespace Org.BouncyCastle.Pqc.Crypto.SphincsPlus output[outOff++] = (uint)((total >> bits) & (w - 1)); } } +#endif - internal byte[] PKFromSig(byte[] sig, byte[] M, byte[] pkSeed, Adrs adrs) +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + internal void PKFromSig(byte[] sig, byte[] M, byte[] pkSeed, Adrs adrs, Span output) +#else + internal void PKFromSig(byte[] sig, byte[] M, byte[] pkSeed, Adrs adrs, byte[] output) +#endif { Adrs wotspkAdrs = new Adrs(adrs); +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + Span msg = stackalloc uint[engine.WOTS_LEN]; + + // convert message to base w + BaseW(M, w, msg[..engine.WOTS_LEN1]); +#else uint[] msg = new uint[engine.WOTS_LEN]; // convert message to base w BaseW(M, 0, w, msg, 0, engine.WOTS_LEN1); +#endif // compute checksum uint csum = 0; @@ -154,22 +258,42 @@ namespace Org.BouncyCastle.Pqc.Crypto.SphincsPlus // convert csum to base w csum <<= 8 - (engine.WOTS_LEN2 * engine.WOTS_LOGW % 8); int len_2_bytes = (engine.WOTS_LEN2 * engine.WOTS_LOGW + 7) / 8; + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + Span csum_bytes = stackalloc byte[4]; + Pack.UInt32_To_BE(csum, csum_bytes); + BaseW(csum_bytes[^len_2_bytes..], w, msg[engine.WOTS_LEN1..]); + + byte[] tmpConcat = new byte[engine.WOTS_LEN * engine.N]; +#else byte[] csum_bytes = Pack.UInt32_To_BE(csum); BaseW(csum_bytes, 4 - len_2_bytes, w, msg, engine.WOTS_LEN1, engine.WOTS_LEN2); byte[] sigI = new byte[engine.N]; byte[][] tmp = new byte[engine.WOTS_LEN][]; - for (uint i = 0; i < engine.WOTS_LEN; i++) +#endif + for (int i = 0; i < engine.WOTS_LEN; i++) { - adrs.SetChainAddress(i); - Array.Copy(sig, i * engine.N, sigI, 0, engine.N); + adrs.SetChainAddress((uint)i); + + int sigPos = engine.N * i; +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + Array.Copy(sig, sigPos, tmpConcat, sigPos, engine.N); + Chain(msg[i], w - 1 - msg[i], pkSeed, adrs, tmpConcat.AsSpan(sigPos, engine.N)); +#else + Array.Copy(sig, sigPos, sigI, 0, engine.N); tmp[i] = Chain(sigI, msg[i], w - 1 - msg[i], pkSeed, adrs); +#endif } wotspkAdrs.SetAdrsType(Adrs.WOTS_PK); wotspkAdrs.SetKeyPairAddress(adrs.GetKeyPairAddress()); - return engine.T_l(pkSeed, wotspkAdrs, Arrays.ConcatenateAll(tmp)); +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + engine.T_l(pkSeed, wotspkAdrs, tmpConcat, output); +#else + engine.T_l(pkSeed, wotspkAdrs, Arrays.ConcatenateAll(tmp), output); +#endif } } } -- cgit 1.4.1