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<byte> 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<byte> 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<byte> 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<byte> m1);
+#endif
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+ public abstract void H(byte[] pkSeed, Adrs adrs, byte[] m1, byte[] m2, Span<byte> 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<byte> 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<byte> 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<byte> 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<byte> 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<byte> 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<byte> 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<byte> 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<byte> 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<byte> pkSeed, Adrs adrs, Span<byte> m)
+ {
+ Span<byte> 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<byte> m1)
+ {
+ Span<byte> 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<byte> output)
+ {
+ Span<byte> 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<byte> 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<byte> m)
+ {
+ Span<byte> 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<byte> m1)
+ {
+ Span<byte> buf = stackalloc byte[64];
+ adrs.value.CopyTo(buf);
+
+ if (robust)
+ {
+ Span<byte> 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<byte> output)
{
Span<byte> 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<byte> 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<byte> m)
{
- if (robust)
+ Span<byte> mask = stackalloc byte[m.Length];
+ m_harakaS.BlockUpdate(adrs.value);
+ m_harakaS.OutputFinal(mask);
+ for (int i = 0; i < m.Length; ++i)
{
- Span<byte> 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<byte> 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<byte> 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<uint> 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<byte> 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<byte> X, uint w, Span<uint> 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<byte> 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<uint> 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<byte> 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
}
}
}
|