From 286b626903b2e3de5610b6cdff9ec196fcf2244e Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Tue, 4 Oct 2022 19:44:44 +0700 Subject: Span-based variants for XDH/EdDSA --- .../parameters/Ed25519PrivateKeyParameters.cs | 26 +- .../parameters/Ed25519PublicKeyParameters.cs | 20 +- .../crypto/parameters/Ed448PrivateKeyParameters.cs | 26 +- .../crypto/parameters/Ed448PublicKeyParameters.cs | 20 +- .../parameters/X25519PrivateKeyParameters.cs | 30 ++- .../crypto/parameters/X25519PublicKeyParameters.cs | 13 +- .../crypto/parameters/X448PrivateKeyParameters.cs | 30 ++- .../crypto/parameters/X448PublicKeyParameters.cs | 13 +- crypto/src/math/ec/rfc7748/X25519.cs | 42 ++++ crypto/src/math/ec/rfc7748/X25519Field.cs | 21 ++ crypto/src/math/ec/rfc7748/X448.cs | 40 ++++ crypto/src/math/ec/rfc7748/X448Field.cs | 23 ++ crypto/src/math/ec/rfc8032/Ed25519.cs | 265 ++++++++++++++++++++- crypto/src/math/ec/rfc8032/Ed448.cs | 258 ++++++++++++++++++++ crypto/src/math/raw/Nat.cs | 30 +++ 15 files changed, 847 insertions(+), 10 deletions(-) diff --git a/crypto/src/crypto/parameters/Ed25519PrivateKeyParameters.cs b/crypto/src/crypto/parameters/Ed25519PrivateKeyParameters.cs index 56ca19ed2..a50f71972 100644 --- a/crypto/src/crypto/parameters/Ed25519PrivateKeyParameters.cs +++ b/crypto/src/crypto/parameters/Ed25519PrivateKeyParameters.cs @@ -35,6 +35,17 @@ namespace Org.BouncyCastle.Crypto.Parameters Array.Copy(buf, off, data, 0, KeySize); } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public Ed25519PrivateKeyParameters(ReadOnlySpan buf) + : base(true) + { + if (buf.Length != KeySize) + throw new ArgumentException("must have length " + KeySize, nameof(buf)); + + buf.CopyTo(data); + } +#endif + public Ed25519PrivateKeyParameters(Stream input) : base(true) { @@ -47,6 +58,13 @@ namespace Org.BouncyCastle.Crypto.Parameters Array.Copy(data, 0, buf, off, KeySize); } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public void Encode(Span buf) + { + data.CopyTo(buf); + } +#endif + public byte[] GetEncoded() { return Arrays.Clone(data); @@ -58,9 +76,15 @@ namespace Org.BouncyCastle.Crypto.Parameters { if (null == cachedPublicKey) { +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + Span publicKey = stackalloc byte[Ed25519.PublicKeySize]; + Ed25519.GeneratePublicKey(data, publicKey); + cachedPublicKey = new Ed25519PublicKeyParameters(publicKey); +#else byte[] publicKey = new byte[Ed25519.PublicKeySize]; Ed25519.GeneratePublicKey(data, 0, publicKey, 0); cachedPublicKey = new Ed25519PublicKeyParameters(publicKey, 0); +#endif } return cachedPublicKey; @@ -108,7 +132,7 @@ namespace Org.BouncyCastle.Crypto.Parameters private static byte[] Validate(byte[] buf) { if (buf.Length != KeySize) - throw new ArgumentException("must have length " + KeySize, "buf"); + throw new ArgumentException("must have length " + KeySize, nameof(buf)); return buf; } diff --git a/crypto/src/crypto/parameters/Ed25519PublicKeyParameters.cs b/crypto/src/crypto/parameters/Ed25519PublicKeyParameters.cs index 8a9139e8d..9b94635d5 100644 --- a/crypto/src/crypto/parameters/Ed25519PublicKeyParameters.cs +++ b/crypto/src/crypto/parameters/Ed25519PublicKeyParameters.cs @@ -25,6 +25,17 @@ namespace Org.BouncyCastle.Crypto.Parameters Array.Copy(buf, off, data, 0, KeySize); } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public Ed25519PublicKeyParameters(ReadOnlySpan buf) + : base(false) + { + if (buf.Length != KeySize) + throw new ArgumentException("must have length " + KeySize, nameof(buf)); + + buf.CopyTo(data); + } +#endif + public Ed25519PublicKeyParameters(Stream input) : base(false) { @@ -37,6 +48,13 @@ namespace Org.BouncyCastle.Crypto.Parameters Array.Copy(data, 0, buf, off, KeySize); } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public void Encode(Span buf) + { + data.CopyTo(buf); + } +#endif + public byte[] GetEncoded() { return Arrays.Clone(data); @@ -45,7 +63,7 @@ namespace Org.BouncyCastle.Crypto.Parameters private static byte[] Validate(byte[] buf) { if (buf.Length != KeySize) - throw new ArgumentException("must have length " + KeySize, "buf"); + throw new ArgumentException("must have length " + KeySize, nameof(buf)); return buf; } diff --git a/crypto/src/crypto/parameters/Ed448PrivateKeyParameters.cs b/crypto/src/crypto/parameters/Ed448PrivateKeyParameters.cs index a6a1b72a1..ac12a2f1d 100644 --- a/crypto/src/crypto/parameters/Ed448PrivateKeyParameters.cs +++ b/crypto/src/crypto/parameters/Ed448PrivateKeyParameters.cs @@ -35,6 +35,17 @@ namespace Org.BouncyCastle.Crypto.Parameters Array.Copy(buf, off, data, 0, KeySize); } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public Ed448PrivateKeyParameters(ReadOnlySpan buf) + : base(true) + { + if (buf.Length != KeySize) + throw new ArgumentException("must have length " + KeySize, nameof(buf)); + + buf.CopyTo(data); + } +#endif + public Ed448PrivateKeyParameters(Stream input) : base(true) { @@ -47,6 +58,13 @@ namespace Org.BouncyCastle.Crypto.Parameters Array.Copy(data, 0, buf, off, KeySize); } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public void Encode(Span buf) + { + data.CopyTo(buf); + } +#endif + public byte[] GetEncoded() { return Arrays.Clone(data); @@ -58,9 +76,15 @@ namespace Org.BouncyCastle.Crypto.Parameters { if (null == cachedPublicKey) { +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + Span publicKey = stackalloc byte[Ed448.PublicKeySize]; + Ed448.GeneratePublicKey(data, publicKey); + cachedPublicKey = new Ed448PublicKeyParameters(publicKey); +#else byte[] publicKey = new byte[Ed448.PublicKeySize]; Ed448.GeneratePublicKey(data, 0, publicKey, 0); cachedPublicKey = new Ed448PublicKeyParameters(publicKey, 0); +#endif } return cachedPublicKey; @@ -100,7 +124,7 @@ namespace Org.BouncyCastle.Crypto.Parameters private static byte[] Validate(byte[] buf) { if (buf.Length != KeySize) - throw new ArgumentException("must have length " + KeySize, "buf"); + throw new ArgumentException("must have length " + KeySize, nameof(buf)); return buf; } diff --git a/crypto/src/crypto/parameters/Ed448PublicKeyParameters.cs b/crypto/src/crypto/parameters/Ed448PublicKeyParameters.cs index 8a89be08f..26f6b5ba9 100644 --- a/crypto/src/crypto/parameters/Ed448PublicKeyParameters.cs +++ b/crypto/src/crypto/parameters/Ed448PublicKeyParameters.cs @@ -25,6 +25,17 @@ namespace Org.BouncyCastle.Crypto.Parameters Array.Copy(buf, off, data, 0, KeySize); } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public Ed448PublicKeyParameters(ReadOnlySpan buf) + : base(false) + { + if (buf.Length != KeySize) + throw new ArgumentException("must have length " + KeySize, nameof(buf)); + + buf.CopyTo(data); + } +#endif + public Ed448PublicKeyParameters(Stream input) : base(false) { @@ -37,6 +48,13 @@ namespace Org.BouncyCastle.Crypto.Parameters Array.Copy(data, 0, buf, off, KeySize); } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public void Encode(Span buf) + { + data.CopyTo(buf); + } +#endif + public byte[] GetEncoded() { return Arrays.Clone(data); @@ -45,7 +63,7 @@ namespace Org.BouncyCastle.Crypto.Parameters private static byte[] Validate(byte[] buf) { if (buf.Length != KeySize) - throw new ArgumentException("must have length " + KeySize, "buf"); + throw new ArgumentException("must have length " + KeySize, nameof(buf)); return buf; } diff --git a/crypto/src/crypto/parameters/X25519PrivateKeyParameters.cs b/crypto/src/crypto/parameters/X25519PrivateKeyParameters.cs index a9e28d1cc..8b263c861 100644 --- a/crypto/src/crypto/parameters/X25519PrivateKeyParameters.cs +++ b/crypto/src/crypto/parameters/X25519PrivateKeyParameters.cs @@ -33,6 +33,17 @@ namespace Org.BouncyCastle.Crypto.Parameters Array.Copy(buf, off, data, 0, KeySize); } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public X25519PrivateKeyParameters(ReadOnlySpan buf) + : base(true) + { + if (buf.Length != KeySize) + throw new ArgumentException("must have length " + KeySize, nameof(buf)); + + buf.CopyTo(data); + } +#endif + public X25519PrivateKeyParameters(Stream input) : base(true) { @@ -45,6 +56,13 @@ namespace Org.BouncyCastle.Crypto.Parameters Array.Copy(data, 0, buf, off, KeySize); } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public void Encode(Span buf) + { + data.CopyTo(buf); + } +#endif + public byte[] GetEncoded() { return Arrays.Clone(data); @@ -52,17 +70,27 @@ namespace Org.BouncyCastle.Crypto.Parameters public X25519PublicKeyParameters GeneratePublicKey() { +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + Span publicKey = stackalloc byte[X25519.PointSize]; + X25519.GeneratePublicKey(data, publicKey); + return new X25519PublicKeyParameters(publicKey); +#else byte[] publicKey = new byte[X25519.PointSize]; X25519.GeneratePublicKey(data, 0, publicKey, 0); return new X25519PublicKeyParameters(publicKey, 0); +#endif } public void GenerateSecret(X25519PublicKeyParameters publicKey, byte[] buf, int off) { +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + GenerateSecret(publicKey, buf.AsSpan(off)); +#else byte[] encoded = new byte[X25519.PointSize]; publicKey.Encode(encoded, 0); if (!X25519.CalculateAgreement(data, 0, encoded, 0, buf, off)) throw new InvalidOperationException("X25519 agreement failed"); +#endif } #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER @@ -78,7 +106,7 @@ namespace Org.BouncyCastle.Crypto.Parameters private static byte[] Validate(byte[] buf) { if (buf.Length != KeySize) - throw new ArgumentException("must have length " + KeySize, "buf"); + throw new ArgumentException("must have length " + KeySize, nameof(buf)); return buf; } diff --git a/crypto/src/crypto/parameters/X25519PublicKeyParameters.cs b/crypto/src/crypto/parameters/X25519PublicKeyParameters.cs index 744a1355f..5d94ac10a 100644 --- a/crypto/src/crypto/parameters/X25519PublicKeyParameters.cs +++ b/crypto/src/crypto/parameters/X25519PublicKeyParameters.cs @@ -25,6 +25,17 @@ namespace Org.BouncyCastle.Crypto.Parameters Array.Copy(buf, off, data, 0, KeySize); } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public X25519PublicKeyParameters(ReadOnlySpan buf) + : base(false) + { + if (buf.Length != KeySize) + throw new ArgumentException("must have length " + KeySize, nameof(buf)); + + buf.CopyTo(data); + } +#endif + public X25519PublicKeyParameters(Stream input) : base(false) { @@ -52,7 +63,7 @@ namespace Org.BouncyCastle.Crypto.Parameters private static byte[] Validate(byte[] buf) { if (buf.Length != KeySize) - throw new ArgumentException("must have length " + KeySize, "buf"); + throw new ArgumentException("must have length " + KeySize, nameof(buf)); return buf; } diff --git a/crypto/src/crypto/parameters/X448PrivateKeyParameters.cs b/crypto/src/crypto/parameters/X448PrivateKeyParameters.cs index 8ae7aa17d..555773b10 100644 --- a/crypto/src/crypto/parameters/X448PrivateKeyParameters.cs +++ b/crypto/src/crypto/parameters/X448PrivateKeyParameters.cs @@ -33,6 +33,17 @@ namespace Org.BouncyCastle.Crypto.Parameters Array.Copy(buf, off, data, 0, KeySize); } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public X448PrivateKeyParameters(ReadOnlySpan buf) + : base(true) + { + if (buf.Length != KeySize) + throw new ArgumentException("must have length " + KeySize, nameof(buf)); + + buf.CopyTo(data); + } +#endif + public X448PrivateKeyParameters(Stream input) : base(true) { @@ -45,6 +56,13 @@ namespace Org.BouncyCastle.Crypto.Parameters Array.Copy(data, 0, buf, off, KeySize); } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public void Encode(Span buf) + { + data.CopyTo(buf); + } +#endif + public byte[] GetEncoded() { return Arrays.Clone(data); @@ -52,17 +70,27 @@ namespace Org.BouncyCastle.Crypto.Parameters public X448PublicKeyParameters GeneratePublicKey() { +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + Span publicKey = stackalloc byte[X448.PointSize]; + X448.GeneratePublicKey(data, publicKey); + return new X448PublicKeyParameters(publicKey); +#else byte[] publicKey = new byte[X448.PointSize]; X448.GeneratePublicKey(data, 0, publicKey, 0); return new X448PublicKeyParameters(publicKey, 0); +#endif } public void GenerateSecret(X448PublicKeyParameters publicKey, byte[] buf, int off) { +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + GenerateSecret(publicKey, buf.AsSpan(off)); +#else byte[] encoded = new byte[X448.PointSize]; publicKey.Encode(encoded, 0); if (!X448.CalculateAgreement(data, 0, encoded, 0, buf, off)) throw new InvalidOperationException("X448 agreement failed"); +#endif } #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER @@ -78,7 +106,7 @@ namespace Org.BouncyCastle.Crypto.Parameters private static byte[] Validate(byte[] buf) { if (buf.Length != KeySize) - throw new ArgumentException("must have length " + KeySize, "buf"); + throw new ArgumentException("must have length " + KeySize, nameof(buf)); return buf; } diff --git a/crypto/src/crypto/parameters/X448PublicKeyParameters.cs b/crypto/src/crypto/parameters/X448PublicKeyParameters.cs index 62e73dda6..94db22147 100644 --- a/crypto/src/crypto/parameters/X448PublicKeyParameters.cs +++ b/crypto/src/crypto/parameters/X448PublicKeyParameters.cs @@ -25,6 +25,17 @@ namespace Org.BouncyCastle.Crypto.Parameters Array.Copy(buf, off, data, 0, KeySize); } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public X448PublicKeyParameters(ReadOnlySpan buf) + : base(false) + { + if (buf.Length != KeySize) + throw new ArgumentException("must have length " + KeySize, nameof(buf)); + + buf.CopyTo(data); + } +#endif + public X448PublicKeyParameters(Stream input) : base(false) { @@ -52,7 +63,7 @@ namespace Org.BouncyCastle.Crypto.Parameters private static byte[] Validate(byte[] buf) { if (buf.Length != KeySize) - throw new ArgumentException("must have length " + KeySize, "buf"); + throw new ArgumentException("must have length " + KeySize, nameof(buf)); return buf; } diff --git a/crypto/src/math/ec/rfc7748/X25519.cs b/crypto/src/math/ec/rfc7748/X25519.cs index 2a471ae26..954b2dd90 100644 --- a/crypto/src/math/ec/rfc7748/X25519.cs +++ b/crypto/src/math/ec/rfc7748/X25519.cs @@ -80,18 +80,42 @@ namespace Org.BouncyCastle.Math.EC.Rfc7748 public static void GeneratePrivateKey(SecureRandom random, byte[] k) { + if (k.Length != ScalarSize) + throw new ArgumentException(nameof(k)); + + random.NextBytes(k); + + k[0] &= 0xF8; + k[ScalarSize - 1] &= 0x7F; + k[ScalarSize - 1] |= 0x40; + } + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public static void GeneratePrivateKey(SecureRandom random, Span k) + { + if (k.Length != ScalarSize) + throw new ArgumentException(nameof(k)); + random.NextBytes(k); k[0] &= 0xF8; k[ScalarSize - 1] &= 0x7F; k[ScalarSize - 1] |= 0x40; } +#endif public static void GeneratePublicKey(byte[] k, int kOff, byte[] r, int rOff) { ScalarMultBase(k, kOff, r, rOff); } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public static void GeneratePublicKey(ReadOnlySpan k, Span r) + { + ScalarMultBase(k, r); + } +#endif + private static void PointDouble(int[] x, int[] z) { int[] a = F.Create(); @@ -255,5 +279,23 @@ namespace Org.BouncyCastle.Math.EC.Rfc7748 F.Normalize(y); F.Encode(y, r, rOff); } + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public static void ScalarMultBase(ReadOnlySpan k, Span r) + { + int[] y = F.Create(); + int[] z = F.Create(); + + Ed25519.ScalarMultBaseYZ(k, y, z); + + F.Apm(z, y, y, z); + + F.Inv(z, z); + F.Mul(y, z, y); + + F.Normalize(y); + F.Encode(y, r); + } +#endif } } diff --git a/crypto/src/math/ec/rfc7748/X25519Field.cs b/crypto/src/math/ec/rfc7748/X25519Field.cs index b4ea9a9ce..8365df03b 100644 --- a/crypto/src/math/ec/rfc7748/X25519Field.cs +++ b/crypto/src/math/ec/rfc7748/X25519Field.cs @@ -107,6 +107,20 @@ namespace Org.BouncyCastle.Math.EC.Rfc7748 } } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public static void CMov(int cond, ReadOnlySpan x, Span z) + { + Debug.Assert(0 == cond || -1 == cond); + + for (int i = 0; i < Size; ++i) + { + int z_i = z[i], diff = z_i ^ x[i]; + z_i ^= (diff & cond); + z[i] = z_i; + } + } +#endif + public static void CNegate(int negate, int[] z) { Debug.Assert(negate >> 1 == 0); @@ -126,6 +140,13 @@ namespace Org.BouncyCastle.Math.EC.Rfc7748 } } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public static void Copy(ReadOnlySpan x, Span z) + { + x[..Size].CopyTo(z); + } +#endif + public static int[] Create() { return new int[Size]; diff --git a/crypto/src/math/ec/rfc7748/X448.cs b/crypto/src/math/ec/rfc7748/X448.cs index ba2660f3c..2f6016a61 100644 --- a/crypto/src/math/ec/rfc7748/X448.cs +++ b/crypto/src/math/ec/rfc7748/X448.cs @@ -79,17 +79,40 @@ namespace Org.BouncyCastle.Math.EC.Rfc7748 public static void GeneratePrivateKey(SecureRandom random, byte[] k) { + if (k.Length != ScalarSize) + throw new ArgumentException(nameof(k)); + random.NextBytes(k); k[0] &= 0xFC; k[ScalarSize - 1] |= 0x80; } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public static void GeneratePrivateKey(SecureRandom random, Span k) + { + if (k.Length != ScalarSize) + throw new ArgumentException(nameof(k)); + + random.NextBytes(k); + + k[0] &= 0xFC; + k[ScalarSize - 1] |= 0x80; + } +#endif + public static void GeneratePublicKey(byte[] k, int kOff, byte[] r, int rOff) { ScalarMultBase(k, kOff, r, rOff); } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public static void GeneratePublicKey(ReadOnlySpan k, Span r) + { + ScalarMultBase(k, r); + } +#endif + private static void PointDouble(uint[] x, uint[] z) { uint[] a = F.Create(); @@ -268,5 +291,22 @@ namespace Org.BouncyCastle.Math.EC.Rfc7748 F.Normalize(x); F.Encode(x, r, rOff); } + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public static void ScalarMultBase(ReadOnlySpan k, Span r) + { + uint[] x = F.Create(); + uint[] y = F.Create(); + + Ed448.ScalarMultBaseXY(k, x, y); + + F.Inv(x, x); + F.Mul(x, y, x); + F.Sqr(x, x); + + F.Normalize(x); + F.Encode(x, r); + } +#endif } } diff --git a/crypto/src/math/ec/rfc7748/X448Field.cs b/crypto/src/math/ec/rfc7748/X448Field.cs index a91ea0d5b..a1a86b61c 100644 --- a/crypto/src/math/ec/rfc7748/X448Field.cs +++ b/crypto/src/math/ec/rfc7748/X448Field.cs @@ -112,6 +112,22 @@ namespace Org.BouncyCastle.Math.EC.Rfc7748 } } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public static void CMov(int cond, ReadOnlySpan x, Span z) + { + Debug.Assert(0 == cond || -1 == cond); + + uint MASK = (uint)cond; + + for (int i = 0; i < Size; ++i) + { + uint z_i = z[i], diff = z_i ^ x[i]; + z_i ^= (diff & MASK); + z[i] = z_i; + } + } +#endif + public static void CNegate(int negate, uint[] z) { Debug.Assert(negate >> 1 == 0); @@ -130,6 +146,13 @@ namespace Org.BouncyCastle.Math.EC.Rfc7748 } } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public static void Copy(ReadOnlySpan x, Span z) + { + x[..Size].CopyTo(z); + } +#endif + public static uint[] Create() { return new uint[Size]; diff --git a/crypto/src/math/ec/rfc8032/Ed25519.cs b/crypto/src/math/ec/rfc8032/Ed25519.cs index d88914c90..128ec4244 100644 --- a/crypto/src/math/ec/rfc8032/Ed25519.cs +++ b/crypto/src/math/ec/rfc8032/Ed25519.cs @@ -92,12 +92,12 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 private static PointPrecomp[] PrecompBaseWnaf = null; private static int[] PrecompBaseComb = null; - private ref struct PointAccum + private struct PointAccum { internal int[] x, y, z, u, v; } - private ref struct PointAffine + private struct PointAffine { internal int[] x, y; } @@ -238,6 +238,17 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 return n; } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + private static uint Decode32(ReadOnlySpan bs) + { + uint n = bs[0]; + n |= (uint)bs[1] << 8; + n |= (uint)bs[2] << 16; + n |= (uint)bs[3] << 24; + return n; + } +#endif + private static void Decode32(byte[] bs, int bsOff, uint[] n, int nOff, int nLen) { for (int i = 0; i < nLen; ++i) @@ -246,6 +257,16 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 } } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + private static void Decode32(ReadOnlySpan bs, Span n) + { + for (int i = 0; i < n.Length; ++i) + { + n[i] = Decode32(bs[(i * 4)..]); + } + } +#endif + private static bool DecodePointVar(byte[] p, int pOff, bool negate, ref PointAffine r) { byte[] py = Copy(p, pOff, PointBytes); @@ -285,6 +306,13 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 Decode32(k, kOff, n, 0, ScalarUints); } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + private static void DecodeScalar(ReadOnlySpan k, Span n) + { + Decode32(k, n[..ScalarUints]); + } +#endif + private static void Dom2(IDigest d, byte phflag, byte[] ctx) { if (ctx != null) @@ -323,6 +351,9 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 private static int EncodePoint(ref PointAccum p, byte[] r, int rOff) { +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + return EncodePoint(ref p, r.AsSpan(rOff)); +#else int[] x = F.Create(); int[] y = F.Create(); @@ -337,14 +368,49 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 F.Encode(y, r, rOff); r[rOff + PointBytes - 1] |= (byte)((x[0] & 1) << 7); + return result; +#endif + } + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + private static int EncodePoint(ref PointAccum p, Span r) + { + int[] x = F.Create(); + int[] y = F.Create(); + + F.Inv(p.z, y); + F.Mul(p.x, y, x); + F.Mul(p.y, y, y); + F.Normalize(x); + F.Normalize(y); + + int result = CheckPoint(x, y); + + F.Encode(y, r); + r[PointBytes - 1] |= (byte)((x[0] & 1) << 7); + return result; } +#endif public static void GeneratePrivateKey(SecureRandom random, byte[] k) { + if (k.Length != SecretKeySize) + throw new ArgumentException(nameof(k)); + random.NextBytes(k); } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public static void GeneratePrivateKey(SecureRandom random, Span k) + { + if (k.Length != SecretKeySize) + throw new ArgumentException(nameof(k)); + + random.NextBytes(k); + } +#endif + public static void GeneratePublicKey(byte[] sk, int skOff, byte[] pk, int pkOff) { IDigest d = CreateDigest(); @@ -359,7 +425,27 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 ScalarMultBaseEncoded(s, pk, pkOff); } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public static void GeneratePublicKey(ReadOnlySpan sk, Span pk) + { + IDigest d = CreateDigest(); + Span h = stackalloc byte[d.GetDigestSize()]; + + d.BlockUpdate(sk[..SecretKeySize]); + d.DoFinal(h); + + Span s = stackalloc byte[ScalarBytes]; + PruneScalar(h, s); + + ScalarMultBaseEncoded(s, pk); + } +#endif + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + private static uint GetWindow4(ReadOnlySpan x, int n) +#else private static uint GetWindow4(uint[] x, int n) +#endif { int w = (int)((uint)n >> 3), b = (n & 7) << 2; return (x[w] >> b) & 15U; @@ -818,6 +904,32 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 } } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + private static void PointLookupZ(ReadOnlySpan x, int n, ReadOnlySpan table, ref PointPrecompZ r) + { + // TODO This method is currently hard-coded to 4-bit windows and 8 precomputed points + + uint w = GetWindow4(x, n); + + int sign = (int)(w >> (4 - 1)) ^ 1; + int abs = ((int)w ^ -sign) & 7; + + Debug.Assert(sign == 0 || sign == 1); + Debug.Assert(0 <= abs && abs < 8); + + for (int i = 0; i < 8; ++i) + { + int cond = ((i ^ abs) - 1) >> 31; + F.CMov(cond, table, r.ymx_h); table = table[F.Size..]; + F.CMov(cond, table, r.ypx_h); table = table[F.Size..]; + F.CMov(cond, table, r.xyd); table = table[F.Size..]; + F.CMov(cond, table, r.z); table = table[F.Size..]; + } + + F.CSwap(sign, r.ymx_h, r.ypx_h); + F.CNegate(sign, r.xyd); + } +#else private static void PointLookupZ(uint[] x, int n, int[] table, ref PointPrecompZ r) { // TODO This method is currently hard-coded to 4-bit windows and 8 precomputed points @@ -842,6 +954,7 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 F.CSwap(sign, r.ymx_h, r.ypx_h); F.CNegate(sign, r.xyd); } +#endif private static void PointPrecompute(ref PointAffine p, PointExtended[] points, int count, ref PointTemp t) { @@ -1070,6 +1183,17 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 r[ScalarBytes - 1] |= 0x40; } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + private static void PruneScalar(ReadOnlySpan n, Span r) + { + n[..ScalarBytes].CopyTo(r); + + r[0] &= 0xF8; + r[ScalarBytes - 1] &= 0x7F; + r[ScalarBytes - 1] |= 0x40; + } +#endif + private static byte[] ReduceScalar(byte[] n) { long x00 = Decode32(n, 0) & M32L; // x00:32/-- @@ -1208,6 +1332,9 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 private static void ScalarMult(byte[] k, ref PointAffine p, ref PointAccum r) { +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + ScalarMult(k.AsSpan(), ref p, ref r); +#else uint[] n = new uint[ScalarUints]; DecodeScalar(k, 0, n); @@ -1237,10 +1364,49 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 PointDouble(ref r); } } +#endif } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + private static void ScalarMult(ReadOnlySpan k, ref PointAffine p, ref PointAccum r) + { + Span n = stackalloc uint[ScalarUints]; + DecodeScalar(k, n); + + // Recode the scalar into signed-digit form + { + uint c1 = Nat.CAdd(ScalarUints, ~(int)n[0] & 1, n, L, n); Debug.Assert(c1 == 0U); + uint c2 = Nat.ShiftDownBit(ScalarUints, n, 1U); Debug.Assert(c2 == (1U << 31)); + } + + Init(out PointPrecompZ q); + Init(out PointTemp t); + int[] table = PointPrecomputeZ(ref p, 8, ref t); + + PointSetNeutral(ref r); + + int w = 63; + for (;;) + { + PointLookupZ(n, w, table, ref q); + PointAdd(ref q, ref r, ref t); + + if (--w < 0) + break; + + for (int i = 0; i < 4; ++i) + { + PointDouble(ref r); + } + } + } +#endif + private static void ScalarMultBase(byte[] k, ref PointAccum r) { +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + ScalarMultBase(k.AsSpan(), ref r); +#else // Equivalent (but much slower) //PointAffine p; Init(out p); //F.Copy(B_x, 0, p.x, 0); @@ -1302,8 +1468,76 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 F.CNegate(resultSign, r.x); F.CNegate(resultSign, r.u); +#endif } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + private static void ScalarMultBase(ReadOnlySpan k, ref PointAccum r) + { + // Equivalent (but much slower) + //Init(out PointAffine p); + //F.Copy(B_x, 0, p.x, 0); + //F.Copy(B_y, 0, p.y, 0); + //ScalarMult(k, ref p, ref r); + + Precompute(); + + Span n = stackalloc uint[ScalarUints]; + DecodeScalar(k, n); + + // Recode the scalar into signed-digit form, then group comb bits in each block + { + uint c1 = Nat.CAdd(ScalarUints, ~(int)n[0] & 1, n, L, n); Debug.Assert(c1 == 0U); + uint c2 = Nat.ShiftDownBit(ScalarUints, n, 1U); Debug.Assert(c2 == (1U << 31)); + + /* + * Because we are using 4 teeth and 8 spacing, each limb of n corresponds to one of the 8 blocks. + * Therefore we can efficiently group the bits for each comb position using a (double) shuffle. + */ + for (int i = 0; i < ScalarUints; ++i) + { + n[i] = Interleave.Shuffle2(n[i]); + } + } + + Init(out PointPrecomp p); + Init(out PointTemp t); + + PointSetNeutral(ref r); + int resultSign = 0; + + int cOff = (PrecompSpacing - 1) * PrecompTeeth; + for (;;) + { + for (int b = 0; b < PrecompBlocks; ++b) + { + uint w = n[b] >> cOff; + int sign = (int)(w >> (PrecompTeeth - 1)) & 1; + int abs = ((int)w ^ -sign) & PrecompMask; + + Debug.Assert(sign == 0 || sign == 1); + Debug.Assert(0 <= abs && abs < PrecompPoints); + + PointLookup(b, abs, ref p); + + F.CNegate(resultSign ^ sign, r.x); + F.CNegate(resultSign ^ sign, r.u); + resultSign = sign; + + PointAdd(ref p, ref r, ref t); + } + + if ((cOff -= PrecompTeeth) < 0) + break; + + PointDouble(ref r); + } + + F.CNegate(resultSign, r.x); + F.CNegate(resultSign, r.u); + } +#endif + private static void ScalarMultBaseEncoded(byte[] k, byte[] r, int rOff) { PointAccum p; Init(out p); @@ -1312,6 +1546,16 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 throw new InvalidOperationException(); } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + private static void ScalarMultBaseEncoded(ReadOnlySpan k, Span r) + { + Init(out PointAccum p); + ScalarMultBase(k, ref p); + if (0 == EncodePoint(ref p, r)) + throw new InvalidOperationException(); + } +#endif + internal static void ScalarMultBaseYZ(byte[] k, int kOff, int[] y, int[] z) { byte[] n = new byte[ScalarBytes]; @@ -1327,6 +1571,23 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 F.Copy(p.z, 0, z, 0); } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + internal static void ScalarMultBaseYZ(ReadOnlySpan k, Span y, Span z) + { + Span n = stackalloc byte[ScalarBytes]; + PruneScalar(k, n); + + Init(out PointAccum p); + ScalarMultBase(n, ref p); + + if (0 == CheckPoint(p.x, p.y, p.z)) + throw new InvalidOperationException(); + + F.Copy(p.y, y); + F.Copy(p.z, z); + } +#endif + private static void ScalarMultOrderVar(ref PointAffine p, ref PointAccum r) { sbyte[] ws_p = GetWnafVar(L, WnafWidth); diff --git a/crypto/src/math/ec/rfc8032/Ed448.cs b/crypto/src/math/ec/rfc8032/Ed448.cs index 55ec5f03b..8595f2f10 100644 --- a/crypto/src/math/ec/rfc8032/Ed448.cs +++ b/crypto/src/math/ec/rfc8032/Ed448.cs @@ -9,6 +9,7 @@ using Org.BouncyCastle.Utilities; namespace Org.BouncyCastle.Math.EC.Rfc8032 { + using static Org.BouncyCastle.Pqc.Crypto.Picnic.Signature; using F = Rfc7748.X448Field; /// @@ -222,6 +223,17 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 return n; } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + private static uint Decode32(ReadOnlySpan bs) + { + uint n = bs[0]; + n |= (uint)bs[1] << 8; + n |= (uint)bs[2] << 16; + n |= (uint)bs[3] << 24; + return n; + } +#endif + private static void Decode32(byte[] bs, int bsOff, uint[] n, int nOff, int nLen) { for (int i = 0; i < nLen; ++i) @@ -230,6 +242,16 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 } } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + private static void Decode32(ReadOnlySpan bs, Span n) + { + for (int i = 0; i < n.Length; ++i) + { + n[i] = Decode32(bs[(i * 4)..]); + } + } +#endif + private static bool DecodePointVar(byte[] p, int pOff, bool negate, ref PointProjective r) { byte[] py = Copy(p, pOff, PointBytes); @@ -273,6 +295,15 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 Decode32(k, kOff, n, 0, ScalarUints); } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + private static void DecodeScalar(ReadOnlySpan k, Span n) + { + Debug.Assert(k[ScalarBytes - 1] == 0x00); + + Decode32(k, n[..ScalarUints]); + } +#endif + private static void Dom4(IXof d, byte phflag, byte[] ctx) { int n = Dom4Prefix.Length; @@ -325,11 +356,45 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 return result; } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + private static int EncodePoint(ref PointProjective p, Span r) + { + uint[] x = F.Create(); + uint[] y = F.Create(); + + F.Inv(p.z, y); + F.Mul(p.x, y, x); + F.Mul(p.y, y, y); + F.Normalize(x); + F.Normalize(y); + + int result = CheckPoint(x, y); + + F.Encode(y, r); + r[PointBytes - 1] = (byte)((x[0] & 1) << 7); + + return result; + } +#endif + public static void GeneratePrivateKey(SecureRandom random, byte[] k) { + if (k.Length != SecretKeySize) + throw new ArgumentException(nameof(k)); + random.NextBytes(k); } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public static void GeneratePrivateKey(SecureRandom random, Span k) + { + if (k.Length != SecretKeySize) + throw new ArgumentException(nameof(k)); + + random.NextBytes(k); + } +#endif + public static void GeneratePublicKey(byte[] sk, int skOff, byte[] pk, int pkOff) { IXof d = CreateXof(); @@ -344,7 +409,27 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 ScalarMultBaseEncoded(s, pk, pkOff); } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public static void GeneratePublicKey(ReadOnlySpan sk, Span pk) + { + IXof d = CreateXof(); + Span h = stackalloc byte[ScalarBytes * 2]; + + d.BlockUpdate(sk[..SecretKeySize]); + d.OutputFinal(h); + + Span s = stackalloc byte[ScalarBytes]; + PruneScalar(h, s); + + ScalarMultBaseEncoded(s, pk); + } +#endif + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + private static uint GetWindow4(ReadOnlySpan x, int n) +#else private static uint GetWindow4(uint[] x, int n) +#endif { int w = (int)((uint)n >> 3), b = (n & 7) << 2; return (x[w] >> b) & 15U; @@ -763,6 +848,30 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 } } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + private static void PointLookup(ReadOnlySpan x, int n, ReadOnlySpan table, ref PointProjective r) + { + // TODO This method is currently hardcoded to 4-bit windows and 8 precomputed points + + uint w = GetWindow4(x, n); + + int sign = (int)(w >> (4 - 1)) ^ 1; + int abs = ((int)w ^ -sign) & 7; + + Debug.Assert(sign == 0 || sign == 1); + Debug.Assert(0 <= abs && abs < 8); + + for (int i = 0; i < 8; ++i) + { + int cond = ((i ^ abs) - 1) >> 31; + F.CMov(cond, table, r.x); table = table[F.Size..]; + F.CMov(cond, table, r.y); table = table[F.Size..]; + F.CMov(cond, table, r.z); table = table[F.Size..]; + } + + F.CNegate(sign, r.x); + } +#else private static void PointLookup(uint[] x, int n, uint[] table, ref PointProjective r) { // TODO This method is currently hardcoded to 4-bit windows and 8 precomputed points @@ -785,6 +894,7 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 F.CNegate(sign, r.x); } +#endif private static void PointLookup15(uint[] table, ref PointProjective r) { @@ -960,6 +1070,17 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 r[ScalarBytes - 1] = 0x00; } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + private static void PruneScalar(ReadOnlySpan n, Span r) + { + n[..(ScalarBytes - 1)].CopyTo(r); + + r[0] &= 0xFC; + r[ScalarBytes - 2] |= 0x80; + r[ScalarBytes - 1] = 0x00; + } +#endif + private static byte[] ReduceScalar(byte[] n) { ulong x00 = Decode32(n, 0); // x00:32/-- @@ -1239,6 +1360,9 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 private static void ScalarMult(byte[] k, ref PointProjective p, ref PointProjective r) { +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + ScalarMult(k.AsSpan(), ref p, ref r); +#else uint[] n = new uint[ScalarUints]; DecodeScalar(k, 0, n); @@ -1271,10 +1395,52 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 PointDouble(ref r); } } +#endif } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + private static void ScalarMult(ReadOnlySpan k, ref PointProjective p, ref PointProjective r) + { + Span n = stackalloc uint[ScalarUints]; + DecodeScalar(k, n); + + // Recode the scalar into signed-digit form + { + uint c1 = Nat.CAdd(ScalarUints, ~(int)n[0] & 1, n, L, n); + uint c2 = Nat.ShiftDownBit(ScalarUints, n, c1); Debug.Assert(c2 == (1U << 31)); + + // NOTE: Bit 448 is implicitly set after the signed-digit recoding + } + + uint[] table = PointPrecompute(ref p, 8); + Init(out PointProjective q); + + // Replace first 4 doublings (2^4 * P) with 1 addition (P + 15 * P) + PointLookup15(table, ref r); + PointAdd(ref p, ref r); + + int w = 111; + for (;;) + { + PointLookup(n, w, table, ref q); + PointAdd(ref q, ref r); + + if (--w < 0) + break; + + for (int i = 0; i < 4; ++i) + { + PointDouble(ref r); + } + } + } +#endif + private static void ScalarMultBase(byte[] k, ref PointProjective r) { +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + ScalarMultBase(k.AsSpan(), ref r); +#else // Equivalent (but much slower) //PointProjective p; Init(out p); //F.Copy(B_x, 0, p.x, 0); @@ -1299,6 +1465,70 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 PointSetNeutral(ref r); + int cOff = PrecompSpacing - 1; + for (;;) + { + int tPos = cOff; + + for (int b = 0; b < PrecompBlocks; ++b) + { + uint w = 0; + for (int t = 0; t < PrecompTeeth; ++t) + { + uint tBit = n[tPos >> 5] >> (tPos & 0x1F); + w &= ~(1U << t); + w ^= (tBit << t); + tPos += PrecompSpacing; + } + + int sign = (int)(w >> (PrecompTeeth - 1)) & 1; + int abs = ((int)w ^ -sign) & PrecompMask; + + Debug.Assert(sign == 0 || sign == 1); + Debug.Assert(0 <= abs && abs < PrecompPoints); + + PointLookup(b, abs, ref p); + + F.CNegate(sign, p.x); + + PointAdd(ref p, ref r); + } + + if (--cOff < 0) + break; + + PointDouble(ref r); + } +#endif + } + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + private static void ScalarMultBase(ReadOnlySpan k, ref PointProjective r) + { + // Equivalent (but much slower) + //Init(out PointProjective p); + //F.Copy(B_x, 0, p.x, 0); + //F.Copy(B_y, 0, p.y, 0); + //F.One(p.z); + //ScalarMult(k, ref p, ref r); + + Precompute(); + + Span n = stackalloc uint[ScalarUints + 1]; + DecodeScalar(k, n); + + // Recode the scalar into signed-digit form + { + n[ScalarUints] = (1U << (PrecompRange - 448)) + + Nat.CAdd(ScalarUints, ~(int)n[0] & 1, n, L, n); + uint c = Nat.ShiftDownBit(n.Length, n, 0); + Debug.Assert(c == (1U << 31)); + } + + Init(out PointAffine p); + + PointSetNeutral(ref r); + int cOff = PrecompSpacing - 1; for (;;) { @@ -1334,6 +1564,7 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 PointDouble(ref r); } } +#endif private static void ScalarMultBaseEncoded(byte[] k, byte[] r, int rOff) { @@ -1343,6 +1574,16 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 throw new InvalidOperationException(); } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + private static void ScalarMultBaseEncoded(ReadOnlySpan k, Span r) + { + Init(out PointProjective p); + ScalarMultBase(k, ref p); + if (0 == EncodePoint(ref p, r)) + throw new InvalidOperationException(); + } +#endif + internal static void ScalarMultBaseXY(byte[] k, int kOff, uint[] x, uint[] y) { byte[] n = new byte[ScalarBytes]; @@ -1358,6 +1599,23 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 F.Copy(p.y, 0, y, 0); } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + internal static void ScalarMultBaseXY(ReadOnlySpan k, Span x, Span y) + { + Span n = stackalloc byte[ScalarBytes]; + PruneScalar(k, n); + + Init(out PointProjective p); + ScalarMultBase(n, ref p); + + if (0 == CheckPoint(p.x, p.y, p.z)) + throw new InvalidOperationException(); + + F.Copy(p.x, x); + F.Copy(p.y, y); + } +#endif + private static void ScalarMultOrderVar(ref PointProjective p, ref PointProjective r) { sbyte[] ws_p = GetWnafVar(L, WnafWidth); diff --git a/crypto/src/math/raw/Nat.cs b/crypto/src/math/raw/Nat.cs index 8e5b7a04c..469d16d55 100644 --- a/crypto/src/math/raw/Nat.cs +++ b/crypto/src/math/raw/Nat.cs @@ -234,6 +234,22 @@ namespace Org.BouncyCastle.Math.Raw return (uint)c; } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public static uint CAdd(int len, int mask, ReadOnlySpan x, ReadOnlySpan y, Span z) + { + uint MASK = (uint)-(mask & 1); + + ulong c = 0; + for (int i = 0; i < len; ++i) + { + c += (ulong)x[i] + (y[i] & MASK); + z[i] = (uint)c; + c >>= 32; + } + return (uint)c; + } +#endif + public static void CMov(int len, int mask, uint[] x, int xOff, uint[] z, int zOff) { uint MASK = (uint)-(mask & 1); @@ -839,6 +855,20 @@ namespace Org.BouncyCastle.Math.Raw return c << 31; } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public static uint ShiftDownBit(int len, Span z, uint c) + { + int i = len; + while (--i >= 0) + { + uint next = z[i]; + z[i] = (next >> 1) | (c << 31); + c = next; + } + return c << 31; + } +#endif + public static uint ShiftDownBit(int len, uint[] z, int zOff, uint c) { int i = len; -- cgit 1.4.1