diff options
-rw-r--r-- | crypto/src/crypto/IRawAgreement.cs | 4 | ||||
-rw-r--r-- | crypto/src/crypto/agreement/X25519Agreement.cs | 17 | ||||
-rw-r--r-- | crypto/src/crypto/agreement/X448Agreement.cs | 17 | ||||
-rw-r--r-- | crypto/src/crypto/parameters/X25519PrivateKeyParameters.cs | 10 | ||||
-rw-r--r-- | crypto/src/crypto/parameters/X25519PublicKeyParameters.cs | 7 | ||||
-rw-r--r-- | crypto/src/crypto/parameters/X448PrivateKeyParameters.cs | 10 | ||||
-rw-r--r-- | crypto/src/crypto/parameters/X448PublicKeyParameters.cs | 7 | ||||
-rw-r--r-- | crypto/src/math/ec/rfc7748/X25519.cs | 98 | ||||
-rw-r--r-- | crypto/src/math/ec/rfc7748/X25519Field.cs | 429 | ||||
-rw-r--r-- | crypto/src/math/ec/rfc7748/X448.cs | 104 | ||||
-rw-r--r-- | crypto/src/math/ec/rfc7748/X448Field.cs | 87 | ||||
-rw-r--r-- | crypto/src/util/Arrays.cs | 12 |
12 files changed, 795 insertions, 7 deletions
diff --git a/crypto/src/crypto/IRawAgreement.cs b/crypto/src/crypto/IRawAgreement.cs index 63e664888..f8b810ec7 100644 --- a/crypto/src/crypto/IRawAgreement.cs +++ b/crypto/src/crypto/IRawAgreement.cs @@ -9,5 +9,9 @@ namespace Org.BouncyCastle.Crypto int AgreementSize { get; } void CalculateAgreement(ICipherParameters publicKey, byte[] buf, int off); + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + void CalculateAgreement(ICipherParameters publicKey, Span<byte> output); +#endif } } diff --git a/crypto/src/crypto/agreement/X25519Agreement.cs b/crypto/src/crypto/agreement/X25519Agreement.cs index 7e5890c16..b4d2e0e87 100644 --- a/crypto/src/crypto/agreement/X25519Agreement.cs +++ b/crypto/src/crypto/agreement/X25519Agreement.cs @@ -7,11 +7,11 @@ namespace Org.BouncyCastle.Crypto.Agreement public sealed class X25519Agreement : IRawAgreement { - private X25519PrivateKeyParameters privateKey; + private X25519PrivateKeyParameters m_privateKey; public void Init(ICipherParameters parameters) { - this.privateKey = (X25519PrivateKeyParameters)parameters; + m_privateKey = (X25519PrivateKeyParameters)parameters; } public int AgreementSize @@ -21,7 +21,18 @@ namespace Org.BouncyCastle.Crypto.Agreement public void CalculateAgreement(ICipherParameters publicKey, byte[] buf, int off) { - privateKey.GenerateSecret((X25519PublicKeyParameters)publicKey, buf, off); +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + CalculateAgreement(publicKey, buf.AsSpan(off)); +#else + m_privateKey.GenerateSecret((X25519PublicKeyParameters)publicKey, buf, off); +#endif } + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public void CalculateAgreement(ICipherParameters publicKey, Span<byte> buf) + { + m_privateKey.GenerateSecret((X25519PublicKeyParameters)publicKey, buf); + } +#endif } } diff --git a/crypto/src/crypto/agreement/X448Agreement.cs b/crypto/src/crypto/agreement/X448Agreement.cs index 26f608c26..6b91603b5 100644 --- a/crypto/src/crypto/agreement/X448Agreement.cs +++ b/crypto/src/crypto/agreement/X448Agreement.cs @@ -7,11 +7,11 @@ namespace Org.BouncyCastle.Crypto.Agreement public sealed class X448Agreement : IRawAgreement { - private X448PrivateKeyParameters privateKey; + private X448PrivateKeyParameters m_privateKey; public void Init(ICipherParameters parameters) { - this.privateKey = (X448PrivateKeyParameters)parameters; + m_privateKey = (X448PrivateKeyParameters)parameters; } public int AgreementSize @@ -21,7 +21,18 @@ namespace Org.BouncyCastle.Crypto.Agreement public void CalculateAgreement(ICipherParameters publicKey, byte[] buf, int off) { - privateKey.GenerateSecret((X448PublicKeyParameters)publicKey, buf, off); +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + CalculateAgreement(publicKey, buf.AsSpan(off)); +#else + m_privateKey.GenerateSecret((X448PublicKeyParameters)publicKey, buf, off); +#endif } + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public void CalculateAgreement(ICipherParameters publicKey, Span<byte> buf) + { + m_privateKey.GenerateSecret((X448PublicKeyParameters)publicKey, buf); + } +#endif } } diff --git a/crypto/src/crypto/parameters/X25519PrivateKeyParameters.cs b/crypto/src/crypto/parameters/X25519PrivateKeyParameters.cs index 63e72d3a7..a9e28d1cc 100644 --- a/crypto/src/crypto/parameters/X25519PrivateKeyParameters.cs +++ b/crypto/src/crypto/parameters/X25519PrivateKeyParameters.cs @@ -65,6 +65,16 @@ namespace Org.BouncyCastle.Crypto.Parameters throw new InvalidOperationException("X25519 agreement failed"); } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public void GenerateSecret(X25519PublicKeyParameters publicKey, Span<byte> buf) + { + Span<byte> encoded = stackalloc byte[X25519.PointSize]; + publicKey.Encode(encoded); + if (!X25519.CalculateAgreement(data, encoded, buf)) + throw new InvalidOperationException("X25519 agreement failed"); + } +#endif + private static byte[] Validate(byte[] buf) { if (buf.Length != KeySize) diff --git a/crypto/src/crypto/parameters/X25519PublicKeyParameters.cs b/crypto/src/crypto/parameters/X25519PublicKeyParameters.cs index c28e4de16..744a1355f 100644 --- a/crypto/src/crypto/parameters/X25519PublicKeyParameters.cs +++ b/crypto/src/crypto/parameters/X25519PublicKeyParameters.cs @@ -37,6 +37,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<byte> buf) + { + data.CopyTo(buf); + } +#endif + public byte[] GetEncoded() { return Arrays.Clone(data); diff --git a/crypto/src/crypto/parameters/X448PrivateKeyParameters.cs b/crypto/src/crypto/parameters/X448PrivateKeyParameters.cs index d10a9035f..8ae7aa17d 100644 --- a/crypto/src/crypto/parameters/X448PrivateKeyParameters.cs +++ b/crypto/src/crypto/parameters/X448PrivateKeyParameters.cs @@ -65,6 +65,16 @@ namespace Org.BouncyCastle.Crypto.Parameters throw new InvalidOperationException("X448 agreement failed"); } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public void GenerateSecret(X448PublicKeyParameters publicKey, Span<byte> buf) + { + Span<byte> encoded = stackalloc byte[X448.PointSize]; + publicKey.Encode(encoded); + if (!X448.CalculateAgreement(data, encoded, buf)) + throw new InvalidOperationException("X448 agreement failed"); + } +#endif + private static byte[] Validate(byte[] buf) { if (buf.Length != KeySize) diff --git a/crypto/src/crypto/parameters/X448PublicKeyParameters.cs b/crypto/src/crypto/parameters/X448PublicKeyParameters.cs index 704d2f7c1..62e73dda6 100644 --- a/crypto/src/crypto/parameters/X448PublicKeyParameters.cs +++ b/crypto/src/crypto/parameters/X448PublicKeyParameters.cs @@ -37,6 +37,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<byte> buf) + { + data.CopyTo(buf); + } +#endif + public byte[] GetEncoded() { return Arrays.Clone(data); diff --git a/crypto/src/math/ec/rfc7748/X25519.cs b/crypto/src/math/ec/rfc7748/X25519.cs index 217ef8785..2a471ae26 100644 --- a/crypto/src/math/ec/rfc7748/X25519.cs +++ b/crypto/src/math/ec/rfc7748/X25519.cs @@ -26,6 +26,36 @@ namespace Org.BouncyCastle.Math.EC.Rfc7748 return !Arrays.AreAllZeroes(r, rOff, PointSize); } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public static bool CalculateAgreement(ReadOnlySpan<byte> k, ReadOnlySpan<byte> u, Span<byte> r) + { + ScalarMult(k, u, r); + return !Arrays.AreAllZeroes(r[..PointSize]); + } +#endif + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + private static uint Decode32(ReadOnlySpan<byte> bs) + { + uint n = bs[0]; + n |= (uint)bs[1] << 8; + n |= (uint)bs[2] << 16; + n |= (uint)bs[3] << 24; + return n; + } + + private static void DecodeScalar(ReadOnlySpan<byte> k, Span<uint> n) + { + for (int i = 0; i < 8; ++i) + { + n[i] = Decode32(k[(i * 4)..]); + } + + n[0] &= 0xFFFFFFF8U; + n[7] &= 0x7FFFFFFFU; + n[7] |= 0x40000000U; + } +#else private static uint Decode32(byte[] bs, int off) { uint n = bs[off]; @@ -46,6 +76,7 @@ namespace Org.BouncyCastle.Math.EC.Rfc7748 n[7] &= 0x7FFFFFFFU; n[7] |= 0x40000000U; } +#endif public static void GeneratePrivateKey(SecureRandom random, byte[] k) { @@ -83,6 +114,9 @@ namespace Org.BouncyCastle.Math.EC.Rfc7748 public static void ScalarMult(byte[] k, int kOff, byte[] u, int uOff, byte[] r, int rOff) { +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + ScalarMult(k.AsSpan(kOff), u.AsSpan(uOff), r.AsSpan(rOff)); +#else uint[] n = new uint[8]; DecodeScalar(k, kOff, n); int[] x1 = F.Create(); F.Decode(u, uOff, x1); @@ -140,7 +174,71 @@ namespace Org.BouncyCastle.Math.EC.Rfc7748 F.Normalize(x2); F.Encode(x2, r, rOff); +#endif + } + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public static void ScalarMult(ReadOnlySpan<byte> k, ReadOnlySpan<byte> u, Span<byte> r) + { + uint[] n = new uint[8]; DecodeScalar(k, n); + + int[] x1 = F.Create(); F.Decode(u, x1); + int[] x2 = F.Create(); F.Copy(x1, 0, x2, 0); + int[] z2 = F.Create(); z2[0] = 1; + int[] x3 = F.Create(); x3[0] = 1; + int[] z3 = F.Create(); + + int[] t1 = F.Create(); + int[] t2 = F.Create(); + + Debug.Assert(n[7] >> 30 == 1U); + + int bit = 254, swap = 1; + do + { + F.Apm(x3, z3, t1, x3); + F.Apm(x2, z2, z3, x2); + F.Mul(t1, x2, t1); + F.Mul(x3, z3, x3); + F.Sqr(z3, z3); + F.Sqr(x2, x2); + + F.Sub(z3, x2, t2); + F.Mul(t2, C_A24, z2); + F.Add(z2, x2, z2); + F.Mul(z2, t2, z2); + F.Mul(x2, z3, x2); + + F.Apm(t1, x3, x3, z3); + F.Sqr(x3, x3); + F.Sqr(z3, z3); + F.Mul(z3, x1, z3); + + --bit; + + int word = bit >> 5, shift = bit & 0x1F; + int kt = (int)(n[word] >> shift) & 1; + swap ^= kt; + F.CSwap(swap, x2, x3); + F.CSwap(swap, z2, z3); + swap = kt; + } + while (bit >= 3); + + Debug.Assert(swap == 0); + + for (int i = 0; i < 3; ++i) + { + PointDouble(x2, z2); + } + + F.Inv(z2, z2); + F.Mul(x2, z2, x2); + + F.Normalize(x2); + F.Encode(x2, r); } +#endif public static void ScalarMultBase(byte[] k, int kOff, byte[] r, int rOff) { diff --git a/crypto/src/math/ec/rfc7748/X25519Field.cs b/crypto/src/math/ec/rfc7748/X25519Field.cs index e6d5687ec..b4ea9a9ce 100644 --- a/crypto/src/math/ec/rfc7748/X25519Field.cs +++ b/crypto/src/math/ec/rfc7748/X25519Field.cs @@ -3,6 +3,10 @@ using System.Diagnostics; using Org.BouncyCastle.Math.Raw; +#if NET5_0_OR_GREATER +using static System.Math; +#endif + namespace Org.BouncyCastle.Math.EC.Rfc7748 { public static class X25519Field @@ -162,6 +166,15 @@ namespace Org.BouncyCastle.Math.EC.Rfc7748 z[9] &= M24; } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public static void Decode(ReadOnlySpan<byte> x, Span<int> z) + { + Decode128(x, z); + Decode128(x[16..], z[5..]); + z[9] &= M24; + } +#endif + private static void Decode128(uint[] x, int xOff, int[] z, int zOff) { uint t0 = x[xOff + 0], t1 = x[xOff + 1], t2 = x[xOff + 2], t3 = x[xOff + 3]; @@ -181,12 +194,28 @@ namespace Org.BouncyCastle.Math.EC.Rfc7748 uint t3 = Decode32(bs, off + 12); z[zOff + 0] = (int)t0 & M26; - z[zOff + 1] = (int)((t1 << 6) | (t0 >> 26)) & M26; + z[zOff + 1] = (int)((t1 << 6) | (t0 >> 26)) & M26; z[zOff + 2] = (int)((t2 << 12) | (t1 >> 20)) & M25; z[zOff + 3] = (int)((t3 << 19) | (t2 >> 13)) & M26; z[zOff + 4] = (int)(t3 >> 7); } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + private static void Decode128(ReadOnlySpan<byte> bs, Span<int> z) + { + uint t0 = Decode32(bs); + uint t1 = Decode32(bs[4..]); + uint t2 = Decode32(bs[8..]); + uint t3 = Decode32(bs[12..]); + + z[0] = (int)t0 & M26; + z[1] = (int)((t1 << 6) | (t0 >> 26)) & M26; + z[2] = (int)((t2 << 12) | (t1 >> 20)) & M25; + z[3] = (int)((t3 << 19) | (t2 >> 13)) & M26; + z[4] = (int)(t3 >> 7); + } +#endif + private static uint Decode32(byte[] bs, int off) { uint n = bs[off]; @@ -196,6 +225,17 @@ namespace Org.BouncyCastle.Math.EC.Rfc7748 return n; } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + private static uint Decode32(ReadOnlySpan<byte> bs) + { + uint n = bs[0]; + n |= (uint)bs[1] << 8; + n |= (uint)bs[2] << 16; + n |= (uint)bs[3] << 24; + return n; + } +#endif + [CLSCompliant(false)] public static void Encode(int[] x, uint[] z, int zOff) { @@ -209,6 +249,14 @@ namespace Org.BouncyCastle.Math.EC.Rfc7748 Encode128(x, 5, z, zOff + 16); } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public static void Encode(ReadOnlySpan<int> x, Span<byte> z) + { + Encode128(x, z); + Encode128(x[5..], z[16..]); + } +#endif + private static void Encode128(int[] x, int xOff, uint[] z, int zOff) { uint x0 = (uint)x[xOff + 0], x1 = (uint)x[xOff + 1], x2 = (uint)x[xOff + 2], x3 = (uint)x[xOff + 3], @@ -231,6 +279,19 @@ namespace Org.BouncyCastle.Math.EC.Rfc7748 uint t3 = (x3 >> 19) | (x4 << 7); Encode32(t3, bs, off + 12); } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + private static void Encode128(ReadOnlySpan<int> x, Span<byte> bs) + { + uint x0 = (uint)x[0], x1 = (uint)x[1], x2 = (uint)x[2]; + uint x3 = (uint)x[3], x4 = (uint)x[4]; + + uint t0 = x0 | (x1 << 26); Encode32(t0, bs); + uint t1 = (x1 >> 6) | (x2 << 20); Encode32(t1, bs[4..]); + uint t2 = (x2 >> 12) | (x3 << 13); Encode32(t2, bs[8..]); + uint t3 = (x3 >> 19) | (x4 << 7); Encode32(t3, bs[12..]); + } +#endif + private static void Encode32(uint n, byte[] bs, int off) { bs[ off] = (byte)(n ); @@ -239,6 +300,16 @@ namespace Org.BouncyCastle.Math.EC.Rfc7748 bs[++off] = (byte)(n >> 24); } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + private static void Encode32(uint n, Span<byte> bs) + { + bs[0] = (byte)(n ); + bs[1] = (byte)(n >> 8); + bs[2] = (byte)(n >> 16); + bs[3] = (byte)(n >> 24); + } +#endif + public static void Inv(int[] x, int[] z) { //int[] x2 = Create(); @@ -753,5 +824,361 @@ namespace Org.BouncyCastle.Math.EC.Rfc7748 z[i] = 0; } } + +#if NET5_0_OR_GREATER + + public const int Size64 = 5; + + private const long M51 = 0x0007FFFFFFFFFFFFL; + + private static readonly long[] RootNegOne64 = { 0x00061B274A0EA0B0L, 0x0000D5A5FC8F189DL, 0x0007EF5E9CBD0C60L, + 0x00078595A6804C9EL, 0x0002B8324804FC1DL }; + + public static void Add(ReadOnlySpan<long> x, ReadOnlySpan<long> y, Span<long> z) + { + for (int i = 0; i < Size64; ++i) + { + z[i] = x[i] + y[i]; + } + } + + public static void AddOne(Span<long> z) + { + z[0] += 1L; + } + + public static void Apm(ReadOnlySpan<long> x, ReadOnlySpan<long> y, Span<long> zp, Span<long> zm) + { + for (int i = 0; i < Size64; ++i) + { + long xi = x[i], yi = y[i]; + zp[i] = xi + yi; + zm[i] = xi - yi; + } + } + + public static long AreEqual(ReadOnlySpan<long> x, ReadOnlySpan<long> y) + { + long d = 0; + for (int i = 0; i < Size64; ++i) + { + d |= x[i] ^ y[i]; + } + return (~d & (d - 1)) >> 63; + } + + public static bool AreEqualVar(ReadOnlySpan<long> x, ReadOnlySpan<long> y) + { + return 0 != AreEqual(x, y); + } + + public static void CMov(long cond, ReadOnlySpan<long> x, Span<long> z) + { + Debug.Assert(0L == cond || -1L == cond); + + for (int i = 0; i < Size64; ++i) + { + long z_i = z[i], diff = z_i ^ x[i]; + z_i ^= diff & cond; + z[i] = z_i; + } + } + + public static void CNegate(long negate, Span<long> z) + { + Debug.Assert(negate >> 1 == 0L); + + long mask = 0L - negate; + for (int i = 0; i < Size64; ++i) + { + z[i] = (z[i] ^ mask) - mask; + } + } + + public static void Copy(ReadOnlySpan<long> x, Span<long> z) + { + for (int i = 0; i < Size64; ++i) + { + z[i] = x[i]; + } + } + + public static void CSwap(long swap, Span<long> a, Span<long> b) + { + Debug.Assert(swap >> 1 == 0); + Debug.Assert(a != b); + + long mask = 0 - swap; + for (int i = 0; i < Size64; ++i) + { + long ai = a[i], bi = b[i]; + long dummy = mask & (ai ^ bi); + a[i] = ai ^ dummy; + b[i] = bi ^ dummy; + } + } + + public static long IsOne(ReadOnlySpan<long> x) + { + long d = x[0] ^ 1L; + for (int i = 1; i < Size64; ++i) + { + d |= x[i]; + } + return (~d & (d - 1)) >> 63; + } + + public static bool IsOneVar(ReadOnlySpan<long> x) + { + return 0 != IsOne(x); + } + + public static long IsZero(ReadOnlySpan<long> x) + { + long d = x[0]; + for (int i = 1; i < Size64; ++i) + { + d |= x[i]; + } + return (~d & (d - 1)) >> 63; + } + + public static bool IsZeroVar(ReadOnlySpan<long> x) + { + return 0 != IsZero(x); + } + + public static void Mul(ReadOnlySpan<long> x, ReadOnlySpan<long> y, Span<long> z) + { + long x0 = x[0], x1 = x[1], x2 = x[2], x3 = x[3], x4 = x[4]; + long y0 = y[0], y1 = y[1], y2 = y[2], y3 = y[3], y4 = y[4]; + long z0, z1, z2, z3, z4, t5, t6, t7, t8, t9; + + long hi00 = BigMul(x0, y0, out long lo00); + + z0 = lo00 & M51; + z1 = (hi00 << 13) + + (lo00 >> 51); + + long hi01 = BigMul(x0, y1, out long lo01); + long hi10 = BigMul(x1, y0, out long lo10); + + z1 += (lo01 & M51) + (lo10 & M51); + z2 = ((hi01 + hi10) << 13) + + (lo01 >> 51) + (lo10 >> 51); + + long hi02 = BigMul(x0, y2, out long lo02); + long hi11 = BigMul(x1, y1, out long lo11); + long hi20 = BigMul(x2, y0, out long lo20); + + z2 += (lo02 & M51) + (lo11 & M51) + (lo20 & M51); + z3 = ((hi02 + hi11 + hi20) << 13) + + (lo02 >> 51) + (lo11 >> 51) + (lo20 >> 51); + + long hi03 = BigMul(x0, y3, out long lo03); + long hi12 = BigMul(x1, y2, out long lo12); + long hi21 = BigMul(x2, y1, out long lo21); + long hi30 = BigMul(x3, y0, out long lo30); + + z3 += (lo03 & M51) + (lo12 & M51) + (lo21 & M51) + (lo30 & M51); + z4 = ((hi03 + hi12 + hi21 + hi30) << 13) + + (lo03 >> 51) + (lo12 >> 51) + (lo21 >> 51) + (lo30 >> 51); + + long hi04 = BigMul(x0, y4, out long lo04); + long hi13 = BigMul(x1, y3, out long lo13); + long hi22 = BigMul(x2, y2, out long lo22); + long hi31 = BigMul(x3, y1, out long lo31); + long hi40 = BigMul(x4, y0, out long lo40); + + z4 += (lo04 & M51) + (lo13 & M51) + (lo22 & M51) + (lo31 & M51) + (lo40 & M51); + t5 = ((hi04 + hi13 + hi22 + hi31 + hi40) << 13) + + (lo04 >> 51) + (lo13 >> 51) + (lo22 >> 51) + (lo31 >> 51) + (lo40 >> 51); + + long hi14 = BigMul(x1, y4, out long lo14); + long hi23 = BigMul(x2, y3, out long lo23); + long hi32 = BigMul(x3, y2, out long lo32); + long hi41 = BigMul(x4, y1, out long lo41); + + t5 += (lo14 & M51) + (lo23 & M51) + (lo32 & M51) + (lo41 & M51); + t6 = ((hi14 + hi23 + hi32 + hi41) << 13) + + (lo14 >> 51) + (lo23 >> 51) + (lo32 >> 51) + (lo41 >> 51); + + long hi24 = BigMul(x2, y4, out long lo24); + long hi33 = BigMul(x3, y3, out long lo33); + long hi42 = BigMul(x4, y2, out long lo42); + + t6 += (lo24 & M51) + (lo33 & M51) + (lo42 & M51); + t7 = ((hi24 + hi33 + hi42) << 13) + + (lo24 >> 51) + (lo33 >> 51) + (lo42 >> 51); + + long hi34 = BigMul(x3, y4, out long lo34); + long hi43 = BigMul(x4, y3, out long lo43); + + t7 += (lo34 & M51) + (lo43 & M51); + t8 = ((hi34 + hi43) << 13) + + (lo34 >> 51) + (lo43 >> 51); + + long hi44 = BigMul(x4, y4, out long lo44); + + t8 += lo44 & M51; + t9 = (hi44 << 13) + + (lo44 >> 51); + + z3 += t8 * 19; + z4 += t9 * 19; + + z4 += z3 >> 51; z3 &= M51; + t5 += z4 >> 51; z4 &= M51; + + z0 += t5 * 19; + z1 += t6 * 19; + z2 += t7 * 19; + + z1 += z0 >> 51; z0 &= M51; + z2 += z1 >> 51; z1 &= M51; + z3 += z2 >> 51; z2 &= M51; + z4 += z3 >> 51; z3 &= M51; + + z[0] = z0; z[1] = z1; z[2] = z2; z[3] = z3; z[4] = z4; + } + + public static void Negate(ReadOnlySpan<long> x, Span<long> z) + { + for (int i = 0; i < Size64; ++i) + { + z[i] = -x[i]; + } + } + + public static void Normalize(Span<long> z) + { + long x = (z[4] >> 50) & 1L; + Reduce(z, x); + Reduce(z, -x); + Debug.Assert(z[4] >> 51 == 0); + } + + public static void One(Span<long> z) + { + z[0] = 1L; + for (int i = 1; i < Size64; ++i) + { + z[i] = 0L; + } + } + + private static void PowPm5d8(ReadOnlySpan<long> x, Span<long> rx2, Span<long> rz) + { + // z = x^((p-5)/8) = x^FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD + // (250 1s) (1 0s) (1 1s) + // Addition chain: [1] 2 3 5 10 15 25 50 75 125 [250] + + Span<long> x2 = rx2; Sqr(x, x2); Mul(x, x2, x2); + Span<long> x3 = stackalloc long[Size64]; Sqr(x2, x3); Mul(x, x3, x3); + Span<long> x5 = x3; Sqr(x3, 2, x5); Mul(x2, x5, x5); + Span<long> x10 = stackalloc long[Size64]; Sqr(x5, 5, x10); Mul(x5, x10, x10); + Span<long> x15 = stackalloc long[Size64]; Sqr(x10, 5, x15); Mul(x5, x15, x15); + Span<long> x25 = x5; Sqr(x15, 10, x25); Mul(x10, x25, x25); + Span<long> x50 = x10; Sqr(x25, 25, x50); Mul(x25, x50, x50); + Span<long> x75 = x15; Sqr(x50, 25, x75); Mul(x25, x75, x75); + Span<long> x125 = x25; Sqr(x75, 50, x125); Mul(x50, x125, x125); + Span<long> x250 = x50; Sqr(x125, 125, x250); Mul(x125, x250, x250); + + Span<long> t = x125; + Sqr(x250, 2, t); + Mul(t, x, rz); + } + + private static void Reduce(Span<long> z, long x) + { + long t = z[4], z4 = t & M51; + t = (t >> 51) + x; + + long cc = t * 19; + cc += z[0]; z[0] = cc & M51; cc >>= 51; + cc += z[1]; z[1] = cc & M51; cc >>= 51; + cc += z[2]; z[2] = cc & M51; cc >>= 51; + cc += z[3]; z[3] = cc & M51; cc >>= 51; + cc += z4 ; z[4] = cc; + } + + public static void Sqr(ReadOnlySpan<long> x, Span<long> z) + { + Mul(x, x, z); + } + + public static void Sqr(ReadOnlySpan<long> x, int n, Span<long> z) + { + Debug.Assert(n > 0); + + Sqr(x, z); + + while (--n > 0) + { + Sqr(z, z); + } + } + + public static bool SqrtRatioVar(ReadOnlySpan<long> u, ReadOnlySpan<long> v, Span<long> z) + { + Span<long> uv3 = stackalloc long[Size64]; + Span<long> uv7 = stackalloc long[Size64]; + + Mul(u, v, uv3); + Sqr(v, uv7); + Mul(uv3, uv7, uv3); + Sqr(uv7, uv7); + Mul(uv7, uv3, uv7); + + Span<long> t = stackalloc long[Size64]; + Span<long> x = stackalloc long[Size64]; + PowPm5d8(uv7, t, x); + Mul(x, uv3, x); + + Span<long> vx2 = stackalloc long[Size64]; + Sqr(x, vx2); + Mul(vx2, v, vx2); + + Sub(vx2, u, t); + Normalize(t); + if (IsZeroVar(t)) + { + Copy(x, z); + return true; + } + + Add(vx2, u, t); + Normalize(t); + if (IsZeroVar(t)) + { + Mul(x, RootNegOne64, z); + return true; + } + + return false; + } + + public static void Sub(ReadOnlySpan<long> x, ReadOnlySpan<long> y, Span<long> z) + { + for (int i = 0; i < Size64; ++i) + { + z[i] = x[i] - y[i]; + } + } + + public static void SubOne(Span<long> z) + { + z[0] -= 1L; + } + + public static void Zero(Span<long> z) + { + for (int i = 0; i < Size64; ++i) + { + z[i] = 0L; + } + } + +#endif } } diff --git a/crypto/src/math/ec/rfc7748/X448.cs b/crypto/src/math/ec/rfc7748/X448.cs index 7de78ebdc..ba2660f3c 100644 --- a/crypto/src/math/ec/rfc7748/X448.cs +++ b/crypto/src/math/ec/rfc7748/X448.cs @@ -27,6 +27,35 @@ namespace Org.BouncyCastle.Math.EC.Rfc7748 return !Arrays.AreAllZeroes(r, rOff, PointSize); } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public static bool CalculateAgreement(ReadOnlySpan<byte> k, ReadOnlySpan<byte> u, Span<byte> r) + { + ScalarMult(k, u, r); + return !Arrays.AreAllZeroes(r[..PointSize]); + } +#endif + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + private static uint Decode32(ReadOnlySpan<byte> bs) + { + uint n = bs[0]; + n |= (uint)bs[1] << 8; + n |= (uint)bs[2] << 16; + n |= (uint)bs[3] << 24; + return n; + } + + private static void DecodeScalar(ReadOnlySpan<byte> k, uint[] n) + { + for (int i = 0; i < 14; ++i) + { + n[i] = Decode32(k[(i * 4)..]); + } + + n[ 0] &= 0xFFFFFFFCU; + n[13] |= 0x80000000U; + } +#else private static uint Decode32(byte[] bs, int off) { uint n = bs[off]; @@ -46,6 +75,7 @@ namespace Org.BouncyCastle.Math.EC.Rfc7748 n[ 0] &= 0xFFFFFFFCU; n[13] |= 0x80000000U; } +#endif public static void GeneratePrivateKey(SecureRandom random, byte[] k) { @@ -84,6 +114,9 @@ namespace Org.BouncyCastle.Math.EC.Rfc7748 public static void ScalarMult(byte[] k, int kOff, byte[] u, int uOff, byte[] r, int rOff) { +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + ScalarMult(k.AsSpan(kOff), u.AsSpan(uOff), r.AsSpan(rOff)); +#else uint[] n = new uint[14]; DecodeScalar(k, kOff, n); uint[] x1 = F.Create(); F.Decode(u, uOff, x1); @@ -148,7 +181,78 @@ namespace Org.BouncyCastle.Math.EC.Rfc7748 F.Normalize(x2); F.Encode(x2, r, rOff); +#endif + } + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public static void ScalarMult(ReadOnlySpan<byte> k, ReadOnlySpan<byte> u, Span<byte> r) + { + uint[] n = new uint[14]; DecodeScalar(k, n); + + uint[] x1 = F.Create(); F.Decode(u, x1); + uint[] x2 = F.Create(); F.Copy(x1, 0, x2, 0); + uint[] z2 = F.Create(); z2[0] = 1; + uint[] x3 = F.Create(); x3[0] = 1; + uint[] z3 = F.Create(); + + uint[] t1 = F.Create(); + uint[] t2 = F.Create(); + + Debug.Assert(n[13] >> 31 == 1U); + + int bit = 447, swap = 1; + do + { + //F.Apm(x3, z3, t1, x3); + F.Add(x3, z3, t1); + F.Sub(x3, z3, x3); + //F.Apm(x2, z2, z3, x2); + F.Add(x2, z2, z3); + F.Sub(x2, z2, x2); + + F.Mul(t1, x2, t1); + F.Mul(x3, z3, x3); + F.Sqr(z3, z3); + F.Sqr(x2, x2); + + F.Sub(z3, x2, t2); + F.Mul(t2, C_A24, z2); + F.Add(z2, x2, z2); + F.Mul(z2, t2, z2); + F.Mul(x2, z3, x2); + + //F.Apm(t1, x3, x3, z3); + F.Sub(t1, x3, z3); + F.Add(t1, x3, x3); + F.Sqr(x3, x3); + F.Sqr(z3, z3); + F.Mul(z3, x1, z3); + + --bit; + + int word = bit >> 5, shift = bit & 0x1F; + int kt = (int)(n[word] >> shift) & 1; + swap ^= kt; + F.CSwap(swap, x2, x3); + F.CSwap(swap, z2, z3); + swap = kt; + } + while (bit >= 2); + + Debug.Assert(swap == 0); + + for (int i = 0; i < 2; ++i) + { + PointDouble(x2, z2); + } + + F.Inv(z2, z2); + F.Mul(x2, z2, x2); + + F.Normalize(x2); + F.Encode(x2, r); } +#endif public static void ScalarMultBase(byte[] k, int kOff, byte[] r, int rOff) { diff --git a/crypto/src/math/ec/rfc7748/X448Field.cs b/crypto/src/math/ec/rfc7748/X448Field.cs index 70273aea8..a91ea0d5b 100644 --- a/crypto/src/math/ec/rfc7748/X448Field.cs +++ b/crypto/src/math/ec/rfc7748/X448Field.cs @@ -173,6 +173,20 @@ namespace Org.BouncyCastle.Math.EC.Rfc7748 Decode56(x, xOff + 49, z, 14); } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public static void Decode(ReadOnlySpan<byte> x, Span<uint> z) + { + Decode56(x, z); + Decode56(x[7..], z[2..]); + Decode56(x[14..], z[4..]); + Decode56(x[21..], z[6..]); + Decode56(x[28..], z[8..]); + Decode56(x[35..], z[10..]); + Decode56(x[42..], z[12..]); + Decode56(x[49..], z[14..]); + } +#endif + private static void Decode224(uint[] x, int xOff, uint[] z, int zOff) { uint x0 = x[xOff + 0], x1 = x[xOff + 1], x2 = x[xOff + 2], x3 = x[xOff + 3]; @@ -196,6 +210,16 @@ namespace Org.BouncyCastle.Math.EC.Rfc7748 return n; } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + private static uint Decode24(ReadOnlySpan<byte> bs) + { + uint n = bs[0]; + n |= (uint)bs[1] << 8; + n |= (uint)bs[2] << 16; + return n; + } +#endif + private static uint Decode32(byte[] bs, int off) { uint n = bs[off]; @@ -205,6 +229,17 @@ namespace Org.BouncyCastle.Math.EC.Rfc7748 return n; } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + private static uint Decode32(ReadOnlySpan<byte> 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 Decode56(byte[] bs, int off, uint[] z, int zOff) { uint lo = Decode32(bs, off); @@ -213,6 +248,16 @@ namespace Org.BouncyCastle.Math.EC.Rfc7748 z[zOff + 1] = (lo >> 28) | (hi << 4); } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + private static void Decode56(ReadOnlySpan<byte> bs, Span<uint> z) + { + uint lo = Decode32(bs); + uint hi = Decode24(bs[4..]); + z[0] = lo & M28; + z[1] = (lo >> 28) | (hi << 4); + } +#endif + public static void Encode(uint[] x, uint[] z, int zOff) { Encode224(x, 0, z, zOff); @@ -231,6 +276,20 @@ namespace Org.BouncyCastle.Math.EC.Rfc7748 Encode56(x, 14, z, zOff + 49); } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public static void Encode(ReadOnlySpan<uint> x, Span<byte> z) + { + Encode56(x, z); + Encode56(x[2..], z[7..]); + Encode56(x[4..], z[14..]); + Encode56(x[6..], z[21..]); + Encode56(x[8..], z[28..]); + Encode56(x[10..], z[35..]); + Encode56(x[12..], z[42..]); + Encode56(x[14..], z[49..]); + } +#endif + private static void Encode224(uint[] x, int xOff, uint[] z, int zOff) { uint x0 = x[xOff + 0], x1 = x[xOff + 1], x2 = x[xOff + 2], x3 = x[xOff + 3]; @@ -252,6 +311,15 @@ namespace Org.BouncyCastle.Math.EC.Rfc7748 bs[++off] = (byte)(n >> 16); } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + private static void Encode24(uint n, Span<byte> bs) + { + bs[0] = (byte)(n ); + bs[1] = (byte)(n >> 8); + bs[2] = (byte)(n >> 16); + } +#endif + private static void Encode32(uint n, byte[] bs, int off) { bs[ off] = (byte)(n ); @@ -260,6 +328,16 @@ namespace Org.BouncyCastle.Math.EC.Rfc7748 bs[++off] = (byte)(n >> 24); } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + private static void Encode32(uint n, Span<byte> bs) + { + bs[0] = (byte)(n ); + bs[1] = (byte)(n >> 8); + bs[2] = (byte)(n >> 16); + bs[3] = (byte)(n >> 24); + } +#endif + private static void Encode56(uint[] x, int xOff, byte[] bs, int off) { uint lo = x[xOff], hi = x[xOff + 1]; @@ -267,6 +345,15 @@ namespace Org.BouncyCastle.Math.EC.Rfc7748 Encode24(hi >> 4, bs, off + 4); } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + private static void Encode56(ReadOnlySpan<uint> x, Span<byte> bs) + { + uint lo = x[0], hi = x[1]; + Encode32(lo | (hi << 28), bs); + Encode24(hi >> 4, bs[4..]); + } +#endif + public static void Inv(uint[] x, uint[] z) { //uint[] t = Create(); diff --git a/crypto/src/util/Arrays.cs b/crypto/src/util/Arrays.cs index d3dae98a9..25114aca3 100644 --- a/crypto/src/util/Arrays.cs +++ b/crypto/src/util/Arrays.cs @@ -21,6 +21,18 @@ namespace Org.BouncyCastle.Utilities return bits == 0; } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public static bool AreAllZeroes(ReadOnlySpan<byte> buf) + { + uint bits = 0; + for (int i = 0; i < buf.Length; ++i) + { + bits |= buf[i]; + } + return bits == 0; + } +#endif + public static bool AreEqual( bool[] a, bool[] b) |