From 11529c83fbdd81f9a25658ec8d3c526618576be6 Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Sat, 26 Nov 2022 22:12:14 +0700 Subject: EdDSA: Hold decoded pubilc point in public keys --- .../parameters/Ed25519PrivateKeyParameters.cs | 9 +- .../parameters/Ed25519PublicKeyParameters.cs | 43 +- .../crypto/parameters/Ed448PrivateKeyParameters.cs | 9 +- .../crypto/parameters/Ed448PublicKeyParameters.cs | 41 +- crypto/src/math/ec/rfc7748/X25519Field.cs | 6 + crypto/src/math/ec/rfc7748/X448Field.cs | 12 + crypto/src/math/ec/rfc8032/Ed25519.cs | 444 ++++++++++++++++-- crypto/src/math/ec/rfc8032/Ed448.cs | 499 ++++++++++++++++++--- .../test/src/math/ec/rfc8032/test/Ed25519Test.cs | 99 +++- crypto/test/src/math/ec/rfc8032/test/Ed448Test.cs | 70 ++- 10 files changed, 1057 insertions(+), 175 deletions(-) diff --git a/crypto/src/crypto/parameters/Ed25519PrivateKeyParameters.cs b/crypto/src/crypto/parameters/Ed25519PrivateKeyParameters.cs index 2bd6445b8..161dedb6d 100644 --- a/crypto/src/crypto/parameters/Ed25519PrivateKeyParameters.cs +++ b/crypto/src/crypto/parameters/Ed25519PrivateKeyParameters.cs @@ -77,14 +77,11 @@ 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); + Ed25519.GeneratePublicKey(data, out var publicPoint); #else - byte[] publicKey = new byte[Ed25519.PublicKeySize]; - Ed25519.GeneratePublicKey(data, 0, publicKey, 0); - cachedPublicKey = new Ed25519PublicKeyParameters(publicKey, 0); + Ed25519.GeneratePublicKey(data, 0, out var publicPoint); #endif + cachedPublicKey = new Ed25519PublicKeyParameters(publicPoint); } return cachedPublicKey; diff --git a/crypto/src/crypto/parameters/Ed25519PublicKeyParameters.cs b/crypto/src/crypto/parameters/Ed25519PublicKeyParameters.cs index a454754f5..57c63e624 100644 --- a/crypto/src/crypto/parameters/Ed25519PublicKeyParameters.cs +++ b/crypto/src/crypto/parameters/Ed25519PublicKeyParameters.cs @@ -2,7 +2,6 @@ using System.IO; using Org.BouncyCastle.Math.EC.Rfc8032; -using Org.BouncyCastle.Utilities; using Org.BouncyCastle.Utilities.IO; namespace Org.BouncyCastle.Crypto.Parameters @@ -12,7 +11,7 @@ namespace Org.BouncyCastle.Crypto.Parameters { public static readonly int KeySize = Ed25519.PublicKeySize; - private readonly byte[] data = new byte[KeySize]; + private readonly Ed25519.PublicPoint m_publicPoint; public Ed25519PublicKeyParameters(byte[] buf) : this(Validate(buf), 0) @@ -22,7 +21,8 @@ namespace Org.BouncyCastle.Crypto.Parameters public Ed25519PublicKeyParameters(byte[] buf, int off) : base(false) { - Array.Copy(buf, off, data, 0, KeySize); + if (!Ed25519.ValidatePublicKeyPartial(buf, off, out m_publicPoint)) + throw new ArgumentException("invalid public key"); } #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER @@ -32,32 +32,55 @@ namespace Org.BouncyCastle.Crypto.Parameters if (buf.Length != KeySize) throw new ArgumentException("must have length " + KeySize, nameof(buf)); - buf.CopyTo(data); + if (!Ed25519.ValidatePublicKeyPartial(buf, out m_publicPoint)) + throw new ArgumentException("invalid public key"); } #endif public Ed25519PublicKeyParameters(Stream input) : base(false) { +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + Span data = stackalloc byte[KeySize]; +#else + byte[] data = new byte[KeySize]; +#endif + if (KeySize != Streams.ReadFully(input, data)) throw new EndOfStreamException("EOF encountered in middle of Ed25519 public key"); + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + if (!Ed25519.ValidatePublicKeyPartial(data, out m_publicPoint)) + throw new ArgumentException("invalid public key"); +#else + if (!Ed25519.ValidatePublicKeyPartial(data, 0, out m_publicPoint)) + throw new ArgumentException("invalid public key"); +#endif + } + + public Ed25519PublicKeyParameters(Ed25519.PublicPoint publicPoint) + : base(false) + { + m_publicPoint = publicPoint ?? throw new ArgumentNullException(nameof(publicPoint)); } public void Encode(byte[] buf, int off) { - Array.Copy(data, 0, buf, off, KeySize); + Ed25519.EncodePublicPoint(m_publicPoint, buf, off); } #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER public void Encode(Span buf) { - data.CopyTo(buf); + Ed25519.EncodePublicPoint(m_publicPoint, buf); } #endif public byte[] GetEncoded() { - return Arrays.Clone(data); + byte[] data = new byte[KeySize]; + Encode(data, 0); + return data; } public bool Verify(Ed25519.Algorithm algorithm, byte[] ctx, byte[] msg, int msgOff, int msgLen, @@ -70,7 +93,7 @@ namespace Org.BouncyCastle.Crypto.Parameters if (null != ctx) throw new ArgumentOutOfRangeException(nameof(ctx)); - return Ed25519.Verify(sig, sigOff, data, 0, msg, msgOff, msgLen); + return Ed25519.Verify(sig, sigOff, m_publicPoint, msg, msgOff, msgLen); } case Ed25519.Algorithm.Ed25519ctx: { @@ -79,7 +102,7 @@ namespace Org.BouncyCastle.Crypto.Parameters if (ctx.Length > 255) throw new ArgumentOutOfRangeException(nameof(ctx)); - return Ed25519.Verify(sig, sigOff, data, 0, ctx, msg, msgOff, msgLen); + return Ed25519.Verify(sig, sigOff, m_publicPoint, ctx, msg, msgOff, msgLen); } case Ed25519.Algorithm.Ed25519ph: { @@ -90,7 +113,7 @@ namespace Org.BouncyCastle.Crypto.Parameters if (Ed25519.PrehashSize != msgLen) throw new ArgumentOutOfRangeException(nameof(msgLen)); - return Ed25519.VerifyPrehash(sig, sigOff, data, 0, ctx, msg, msgOff); + return Ed25519.VerifyPrehash(sig, sigOff, m_publicPoint, ctx, msg, msgOff); } default: { diff --git a/crypto/src/crypto/parameters/Ed448PrivateKeyParameters.cs b/crypto/src/crypto/parameters/Ed448PrivateKeyParameters.cs index 9f442b21a..b80b68529 100644 --- a/crypto/src/crypto/parameters/Ed448PrivateKeyParameters.cs +++ b/crypto/src/crypto/parameters/Ed448PrivateKeyParameters.cs @@ -77,14 +77,11 @@ 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); + Ed448.GeneratePublicKey(data, out var publicPoint); #else - byte[] publicKey = new byte[Ed448.PublicKeySize]; - Ed448.GeneratePublicKey(data, 0, publicKey, 0); - cachedPublicKey = new Ed448PublicKeyParameters(publicKey, 0); + Ed448.GeneratePublicKey(data, 0, out var publicPoint); #endif + cachedPublicKey = new Ed448PublicKeyParameters(publicPoint); } return cachedPublicKey; diff --git a/crypto/src/crypto/parameters/Ed448PublicKeyParameters.cs b/crypto/src/crypto/parameters/Ed448PublicKeyParameters.cs index d2ef7c891..0f07fa783 100644 --- a/crypto/src/crypto/parameters/Ed448PublicKeyParameters.cs +++ b/crypto/src/crypto/parameters/Ed448PublicKeyParameters.cs @@ -2,7 +2,6 @@ using System.IO; using Org.BouncyCastle.Math.EC.Rfc8032; -using Org.BouncyCastle.Utilities; using Org.BouncyCastle.Utilities.IO; namespace Org.BouncyCastle.Crypto.Parameters @@ -12,7 +11,7 @@ namespace Org.BouncyCastle.Crypto.Parameters { public static readonly int KeySize = Ed448.PublicKeySize; - private readonly byte[] data = new byte[KeySize]; + private readonly Ed448.PublicPoint m_publicPoint; public Ed448PublicKeyParameters(byte[] buf) : this(Validate(buf), 0) @@ -22,7 +21,8 @@ namespace Org.BouncyCastle.Crypto.Parameters public Ed448PublicKeyParameters(byte[] buf, int off) : base(false) { - Array.Copy(buf, off, data, 0, KeySize); + if (!Ed448.ValidatePublicKeyPartial(buf, off, out m_publicPoint)) + throw new ArgumentException("invalid public key"); } #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER @@ -32,32 +32,55 @@ namespace Org.BouncyCastle.Crypto.Parameters if (buf.Length != KeySize) throw new ArgumentException("must have length " + KeySize, nameof(buf)); - buf.CopyTo(data); + if (!Ed448.ValidatePublicKeyPartial(buf, out m_publicPoint)) + throw new ArgumentException("invalid public key"); } #endif public Ed448PublicKeyParameters(Stream input) : base(false) { +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + Span data = stackalloc byte[KeySize]; +#else + byte[] data = new byte[KeySize]; +#endif + if (KeySize != Streams.ReadFully(input, data)) throw new EndOfStreamException("EOF encountered in middle of Ed448 public key"); + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + if (!Ed448.ValidatePublicKeyPartial(data, out m_publicPoint)) + throw new ArgumentException("invalid public key"); +#else + if (!Ed448.ValidatePublicKeyPartial(data, 0, out m_publicPoint)) + throw new ArgumentException("invalid public key"); +#endif + } + + public Ed448PublicKeyParameters(Ed448.PublicPoint publicPoint) + : base(false) + { + m_publicPoint = publicPoint ?? throw new ArgumentNullException(nameof(publicPoint)); } public void Encode(byte[] buf, int off) { - Array.Copy(data, 0, buf, off, KeySize); + Ed448.EncodePublicPoint(m_publicPoint, buf, off); } #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER public void Encode(Span buf) { - data.CopyTo(buf); + Ed448.EncodePublicPoint(m_publicPoint, buf); } #endif public byte[] GetEncoded() { - return Arrays.Clone(data); + byte[] data = new byte[KeySize]; + Encode(data, 0); + return data; } public bool Verify(Ed448.Algorithm algorithm, byte[] ctx, byte[] msg, int msgOff, int msgLen, @@ -72,7 +95,7 @@ namespace Org.BouncyCastle.Crypto.Parameters if (ctx.Length > 255) throw new ArgumentOutOfRangeException(nameof(ctx)); - return Ed448.Verify(sig, sigOff, data, 0, ctx, msg, msgOff, msgLen); + return Ed448.Verify(sig, sigOff, m_publicPoint, ctx, msg, msgOff, msgLen); } case Ed448.Algorithm.Ed448ph: { @@ -83,7 +106,7 @@ namespace Org.BouncyCastle.Crypto.Parameters if (Ed448.PrehashSize != msgLen) throw new ArgumentOutOfRangeException(nameof(msgLen)); - return Ed448.VerifyPrehash(sig, sigOff, data, 0, ctx, msg, msgOff); + return Ed448.VerifyPrehash(sig, sigOff, m_publicPoint, ctx, msg, msgOff); } default: { diff --git a/crypto/src/math/ec/rfc7748/X25519Field.cs b/crypto/src/math/ec/rfc7748/X25519Field.cs index 2504592aa..1c0b928f7 100644 --- a/crypto/src/math/ec/rfc7748/X25519Field.cs +++ b/crypto/src/math/ec/rfc7748/X25519Field.cs @@ -444,6 +444,12 @@ namespace Org.BouncyCastle.Math.EC.Rfc7748 Encode128(x, 5, z, zOff + 16); } + public static void Encode(int[] x, int xOff, byte[] z, int zOff) + { + Encode128(x, xOff, z, zOff); + Encode128(x, xOff + 5, z, zOff + 16); + } + #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER public static void Encode(ReadOnlySpan x, Span z) { diff --git a/crypto/src/math/ec/rfc7748/X448Field.cs b/crypto/src/math/ec/rfc7748/X448Field.cs index 7169bd6d8..d812172bb 100644 --- a/crypto/src/math/ec/rfc7748/X448Field.cs +++ b/crypto/src/math/ec/rfc7748/X448Field.cs @@ -405,6 +405,18 @@ namespace Org.BouncyCastle.Math.EC.Rfc7748 Encode56(x, 14, z, zOff + 49); } + public static void Encode(uint[] x, int xOff, byte[] z, int zOff) + { + Encode56(x, xOff, z, zOff); + Encode56(x, xOff + 2, z, zOff + 7); + Encode56(x, xOff + 4, z, zOff + 14); + Encode56(x, xOff + 6, z, zOff + 21); + Encode56(x, xOff + 8, z, zOff + 28); + Encode56(x, xOff + 10, z, zOff + 35); + Encode56(x, xOff + 12, z, zOff + 42); + Encode56(x, xOff + 14, z, zOff + 49); + } + #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER public static void Encode(ReadOnlySpan x, Span z) { diff --git a/crypto/src/math/ec/rfc8032/Ed25519.cs b/crypto/src/math/ec/rfc8032/Ed25519.cs index c1d8fa624..2479d94d4 100644 --- a/crypto/src/math/ec/rfc8032/Ed25519.cs +++ b/crypto/src/math/ec/rfc8032/Ed25519.cs @@ -34,6 +34,16 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 Ed25519ph = 2, } + public sealed class PublicPoint + { + internal readonly int[] m_data; + + internal PublicPoint(int[] data) + { + m_data = data; + } + } + private const int CoordUints = 8; private const int PointBytes = CoordUints * 4; private const int ScalarUints = 8; @@ -151,14 +161,14 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 || ctx != null && ctx.Length < 256; } - private static int CheckPoint(int[] x, int[] y) + private static int CheckPoint(ref PointAffine p) { int[] t = F.Create(); int[] u = F.Create(); int[] v = F.Create(); - F.Sqr(x, u); - F.Sqr(y, v); + F.Sqr(p.x, u); + F.Sqr(p.y, v); F.Mul(u, v, t); F.Sub(v, u, v); F.Mul(t, C_d, t); @@ -191,6 +201,13 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 return F.IsZero(t); } + private static bool CheckPointOrderVar(ref PointAffine p) + { + Init(out PointAccum r); + ScalarMultOrderVar(ref p, ref r); + return NormalizeToNeutralElementVar(ref r); + } + #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER private static bool CheckPointVar(ReadOnlySpan p) { @@ -339,6 +356,7 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 if (negate ^ (x_0 != (r.x[0] & 1))) { F.Negate(r.x, r.x); + F.Normalize(r.x); } return true; @@ -369,50 +387,73 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 #endif } - private static int EncodePoint(ref PointAccum p, byte[] r, int rOff) + private static void EncodePoint(ref PointAffine p, byte[] r, int rOff) { + F.Encode(p.y, r, rOff); + r[rOff + PointBytes - 1] |= (byte)((p.x[0] & 1) << 7); + } + #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(); + private static void EncodePoint(ref PointAffine p, Span r) + { + F.Encode(p.y, r); + r[PointBytes - 1] |= (byte)((p.x[0] & 1) << 7); + } +#endif + + public static void EncodePublicPoint(PublicPoint publicPoint, byte[] pk, int pkOff) + { + F.Encode(publicPoint.m_data, F.Size, pk, pkOff); + pk[pkOff + PointBytes - 1] |= (byte)((publicPoint.m_data[0] & 1) << 7); + } - F.Inv(p.z, y); - F.Mul(p.x, y, x); - F.Mul(p.y, y, y); - F.Normalize(x); - F.Normalize(y); +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public static void EncodePublicPoint(PublicPoint publicPoint, Span pk) + { + F.Encode(publicPoint.m_data.AsSpan(F.Size), pk); + pk[PointBytes - 1] |= (byte)((publicPoint.m_data[0] & 1) << 7); + } +#endif + + private static int EncodeResult(ref PointAccum p, byte[] r, int rOff) + { +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + return EncodeResult(ref p, r.AsSpan(rOff)); +#else + Init(out PointAffine q); + NormalizeToAffine(ref p, ref q); - int result = CheckPoint(x, y); + int result = CheckPoint(ref q); - F.Encode(y, r, rOff); - r[rOff + PointBytes - 1] |= (byte)((x[0] & 1) << 7); + EncodePoint(ref q, r, rOff); return result; #endif } #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER - private static int EncodePoint(ref PointAccum p, Span r) + private static int EncodeResult(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); + Init(out PointAffine q); + NormalizeToAffine(ref p, ref q); - int result = CheckPoint(x, y); + int result = CheckPoint(ref q); - F.Encode(y, r); - r[PointBytes - 1] |= (byte)((x[0] & 1) << 7); + EncodePoint(ref q, r); return result; } #endif + private static void ExportPoint(ref PointAffine p, out PublicPoint publicPoint) + { + int[] data = new int[F.Size * 2]; + F.Copy(p.x, 0, data, 0); + F.Copy(p.y, 0, data, F.Size); + + publicPoint = new PublicPoint(data); + } + public static void GeneratePrivateKey(SecureRandom random, byte[] k) { if (k.Length != SecretKeySize) @@ -465,6 +506,58 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 } #endif + public static void GeneratePublicKey(byte[] sk, int skOff, out PublicPoint publicPoint) + { +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + GeneratePublicKey(sk.AsSpan(skOff), out publicPoint); +#else + IDigest d = CreateDigest(); + byte[] h = new byte[64]; + + d.BlockUpdate(sk, skOff, SecretKeySize); + d.DoFinal(h, 0); + + byte[] s = new byte[ScalarBytes]; + PruneScalar(h, 0, s); + + Init(out PointAccum p); + ScalarMultBase(s, ref p); + + Init(out PointAffine q); + NormalizeToAffine(ref p, ref q); + + if (0 == CheckPoint(ref q)) + throw new InvalidOperationException(); + + ExportPoint(ref q, out publicPoint); +#endif + } + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public static void GeneratePublicKey(ReadOnlySpan sk, out PublicPoint publicPoint) + { + IDigest d = CreateDigest(); + Span h = stackalloc byte[64]; + + d.BlockUpdate(sk[..SecretKeySize]); + d.DoFinal(h); + + Span s = stackalloc byte[ScalarBytes]; + PruneScalar(h, s); + + Init(out PointAccum p); + ScalarMultBase(s, ref p); + + Init(out PointAffine q); + NormalizeToAffine(ref p, ref q); + + if (0 == CheckPoint(ref q)) + throw new InvalidOperationException(); + + ExportPoint(ref q, out publicPoint); + } +#endif + #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER private static uint GetWindow4(ReadOnlySpan x, int n) #else @@ -663,12 +756,108 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 Init(out PointAccum pZ); ScalarMultStraus128Var(nS, v0, ref pA, v1, ref pR, ref pZ); + return NormalizeToNeutralElementVar(ref pZ); + } + + private static bool ImplVerify(byte[] sig, int sigOff, PublicPoint publicPoint, byte[] ctx, byte phflag, + byte[] m, int mOff, int mLen) + { + if (!CheckContextVar(ctx, phflag)) + throw new ArgumentException("ctx"); + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + Span signature = stackalloc byte[SignatureSize]; + signature.CopyFrom(sig.AsSpan(sigOff, SignatureSize)); + var R = signature[..PointBytes]; + var S = signature[PointBytes..]; + + if (!CheckPointVar(R)) + return false; + + Span nS = stackalloc uint[ScalarUints]; + if (!Scalar25519.CheckVar(S, nS)) + return false; + + Init(out PointAffine pR); + if (!DecodePointVar(R, true, ref pR)) + return false; + + Init(out PointAffine pA); + F.Negate(publicPoint.m_data, pA.x); + F.Copy(publicPoint.m_data.AsSpan(F.Size), pA.y); + + Span A = stackalloc byte[PublicKeySize]; + EncodePublicPoint(publicPoint, A); - F.Normalize(pZ.x); - F.Normalize(pZ.y); - F.Normalize(pZ.z); + IDigest d = CreateDigest(); + Span h = stackalloc byte[64]; - return IsNeutralElementVar(pZ.x, pZ.y, pZ.z); + if (ctx != null) + { + Dom2(d, phflag, ctx); + } + d.BlockUpdate(R); + d.BlockUpdate(A); + d.BlockUpdate(m.AsSpan(mOff, mLen)); + d.DoFinal(h); + + Span k = stackalloc byte[ScalarBytes]; + Scalar25519.Reduce(h, k); + + Span nA = stackalloc uint[ScalarUints]; + Scalar25519.Decode(k, nA); + + Span v0 = stackalloc uint[4]; + Span v1 = stackalloc uint[4]; +#else + byte[] R = Copy(sig, sigOff, PointBytes); + byte[] S = Copy(sig, sigOff + PointBytes, ScalarBytes); + + if (!CheckPointVar(R)) + return false; + + uint[] nS = new uint[ScalarUints]; + if (!Scalar25519.CheckVar(S, nS)) + return false; + + Init(out PointAffine pR); + if (!DecodePointVar(R, true, ref pR)) + return false; + + Init(out PointAffine pA); + F.Negate(publicPoint.m_data, pA.x); + F.Copy(publicPoint.m_data, F.Size, pA.y, 0); + + byte[] A = new byte[PublicKeySize]; + EncodePublicPoint(publicPoint, A, 0); + + IDigest d = CreateDigest(); + byte[] h = new byte[64]; + + if (ctx != null) + { + Dom2(d, phflag, ctx); + } + d.BlockUpdate(R, 0, PointBytes); + d.BlockUpdate(A, 0, PointBytes); + d.BlockUpdate(m, mOff, mLen); + d.DoFinal(h, 0); + + byte[] k = Scalar25519.Reduce(h); + + uint[] nA = new uint[ScalarUints]; + Scalar25519.Decode(k, nA); + + uint[] v0 = new uint[4]; + uint[] v1 = new uint[4]; +#endif + + Scalar25519.ReduceBasisVar(nA, v0, v1); + Scalar25519.Multiply128Var(nS, v1, nS); + + Init(out PointAccum pZ); + ScalarMultStraus128Var(nS, v0, ref pA, v1, ref pR, ref pZ); + return NormalizeToNeutralElementVar(ref pZ); } private static void Init(out PointAccum r) @@ -759,6 +948,24 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 return F.IsZeroVar(x) && F.AreEqualVar(y, z); } + private static void NormalizeToAffine(ref PointAccum p, ref PointAffine r) + { + F.Inv(p.z, r.y); + F.Mul(r.y, p.x, r.x); + F.Mul(r.y, p.y, r.y); + F.Normalize(r.x); + F.Normalize(r.y); + } + + private static bool NormalizeToNeutralElementVar(ref PointAccum p) + { + F.Normalize(p.x); + F.Normalize(p.y); + F.Normalize(p.z); + + return IsNeutralElementVar(p.x, p.y, p.z); + } + private static void PointAdd(ref PointExtended p, ref PointExtended q, ref PointExtended r, ref PointTemp t) { // p may ref the same point as r (or q), but q may not ref the same point as r. @@ -1143,6 +1350,7 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 { Init(out toothPowers[tooth]); } + Init(out PointExtended u); for (int block = 0; block < PrecompBlocks; ++block) { @@ -1393,7 +1601,7 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 #else Init(out PointAccum p); ScalarMultBase(k, ref p); - if (0 == EncodePoint(ref p, r, rOff)) + if (0 == EncodeResult(ref p, r, rOff)) throw new InvalidOperationException(); #endif } @@ -1403,7 +1611,7 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 { Init(out PointAccum p); ScalarMultBase(k, ref p); - if (0 == EncodePoint(ref p, r)) + if (0 == EncodeResult(ref p, r)) throw new InvalidOperationException(); } #endif @@ -1624,11 +1832,26 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 public static bool ValidatePublicKeyFull(byte[] pk, int pkOff) { #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER - Span A = stackalloc byte[PublicKeySize]; - A.CopyFrom(pk.AsSpan(pkOff)); + return ValidatePublicKeyFull(pk.AsSpan(pkOff)); #else byte[] A = Copy(pk, pkOff, PublicKeySize); + + if (!CheckPointFullVar(A)) + return false; + + Init(out PointAffine pA); + if (!DecodePointVar(A, false, ref pA)) + return false; + + return CheckPointOrderVar(ref pA); #endif + } + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public static bool ValidatePublicKeyFull(ReadOnlySpan pk) + { + Span A = stackalloc byte[PublicKeySize]; + A.CopyFrom(pk); if (!CheckPointFullVar(A)) return false; @@ -1637,24 +1860,79 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 if (!DecodePointVar(A, false, ref pA)) return false; - Init(out PointAccum pR); - ScalarMultOrderVar(ref pA, ref pR); + return CheckPointOrderVar(ref pA); + } +#endif - F.Normalize(pR.x); - F.Normalize(pR.y); - F.Normalize(pR.z); + public static bool ValidatePublicKeyFull(byte[] pk, int pkOff, out PublicPoint publicPoint) + { +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + return ValidatePublicKeyFull(pk.AsSpan(pkOff), out publicPoint); +#else + byte[] A = Copy(pk, pkOff, PublicKeySize); - return IsNeutralElementVar(pR.x, pR.y, pR.z); + if (CheckPointFullVar(A)) + { + Init(out PointAffine pA); + if (DecodePointVar(A, false, ref pA)) + { + if (CheckPointOrderVar(ref pA)) + { + ExportPoint(ref pA, out publicPoint); + return true; + } + } + } + + publicPoint = null; + return false; +#endif } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public static bool ValidatePublicKeyFull(ReadOnlySpan pk, out PublicPoint publicPoint) + { + Span A = stackalloc byte[PublicKeySize]; + A.CopyFrom(pk); + + if (CheckPointFullVar(A)) + { + Init(out PointAffine pA); + if (DecodePointVar(A, false, ref pA)) + { + if (CheckPointOrderVar(ref pA)) + { + ExportPoint(ref pA, out publicPoint); + return true; + } + } + } + + publicPoint = null; + return false; + } +#endif + public static bool ValidatePublicKeyPartial(byte[] pk, int pkOff) { #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER - Span A = stackalloc byte[PublicKeySize]; - A.CopyFrom(pk.AsSpan(pkOff)); + return ValidatePublicKeyPartial(pk.AsSpan(pkOff)); #else byte[] A = Copy(pk, pkOff, PublicKeySize); + + if (!CheckPointFullVar(A)) + return false; + + Init(out PointAffine pA); + return DecodePointVar(A, false, ref pA); #endif + } + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public static bool ValidatePublicKeyPartial(ReadOnlySpan pk) + { + Span A = stackalloc byte[PublicKeySize]; + A.CopyFrom(pk); if (!CheckPointFullVar(A)) return false; @@ -1662,6 +1940,50 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 Init(out PointAffine pA); return DecodePointVar(A, false, ref pA); } +#endif + + public static bool ValidatePublicKeyPartial(byte[] pk, int pkOff, out PublicPoint publicPoint) + { +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + return ValidatePublicKeyPartial(pk.AsSpan(pkOff), out publicPoint); +#else + byte[] A = Copy(pk, pkOff, PublicKeySize); + + if (CheckPointFullVar(A)) + { + Init(out PointAffine pA); + if (DecodePointVar(A, false, ref pA)) + { + ExportPoint(ref pA, out publicPoint); + return true; + } + } + + publicPoint = null; + return false; +#endif + } + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public static bool ValidatePublicKeyPartial(ReadOnlySpan pk, out PublicPoint publicPoint) + { + Span A = stackalloc byte[PublicKeySize]; + A.CopyFrom(pk); + + if (CheckPointFullVar(A)) + { + Init(out PointAffine pA); + if (DecodePointVar(A, false, ref pA)) + { + ExportPoint(ref pA, out publicPoint); + return true; + } + } + + publicPoint = null; + return false; + } +#endif public static bool Verify(byte[] sig, int sigOff, byte[] pk, int pkOff, byte[] m, int mOff, int mLen) { @@ -1671,6 +1993,14 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 return ImplVerify(sig, sigOff, pk, pkOff, ctx, phflag, m, mOff, mLen); } + public static bool Verify(byte[] sig, int sigOff, PublicPoint publicPoint, byte[] m, int mOff, int mLen) + { + byte[] ctx = null; + byte phflag = 0x00; + + return ImplVerify(sig, sigOff, publicPoint, ctx, phflag, m, mOff, mLen); + } + public static bool Verify(byte[] sig, int sigOff, byte[] pk, int pkOff, byte[] ctx, byte[] m, int mOff, int mLen) { byte phflag = 0x00; @@ -1678,6 +2008,13 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 return ImplVerify(sig, sigOff, pk, pkOff, ctx, phflag, m, mOff, mLen); } + public static bool Verify(byte[] sig, int sigOff, PublicPoint publicPoint, byte[] ctx, byte[] m, int mOff, int mLen) + { + byte phflag = 0x00; + + return ImplVerify(sig, sigOff, publicPoint, ctx, phflag, m, mOff, mLen); + } + public static bool VerifyPrehash(byte[] sig, int sigOff, byte[] pk, int pkOff, byte[] ctx, byte[] ph, int phOff) { byte phflag = 0x01; @@ -1685,6 +2022,14 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 return ImplVerify(sig, sigOff, pk, pkOff, ctx, phflag, ph, phOff, PrehashSize); } + public static bool VerifyPrehash(byte[] sig, int sigOff, PublicPoint publicPoint, byte[] ctx, byte[] ph, + int phOff) + { + byte phflag = 0x01; + + return ImplVerify(sig, sigOff, publicPoint, ctx, phflag, ph, phOff, PrehashSize); + } + public static bool VerifyPrehash(byte[] sig, int sigOff, byte[] pk, int pkOff, byte[] ctx, IDigest ph) { byte[] m = new byte[PrehashSize]; @@ -1695,5 +2040,16 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 return ImplVerify(sig, sigOff, pk, pkOff, ctx, phflag, m, 0, m.Length); } + + public static bool VerifyPrehash(byte[] sig, int sigOff, PublicPoint publicPoint, byte[] ctx, IDigest ph) + { + byte[] m = new byte[PrehashSize]; + if (PrehashSize != ph.DoFinal(m, 0)) + throw new ArgumentException("ph"); + + byte phflag = 0x01; + + return ImplVerify(sig, sigOff, publicPoint, ctx, phflag, m, 0, m.Length); + } } } diff --git a/crypto/src/math/ec/rfc8032/Ed448.cs b/crypto/src/math/ec/rfc8032/Ed448.cs index d2c29a41c..8ffbccfa4 100644 --- a/crypto/src/math/ec/rfc8032/Ed448.cs +++ b/crypto/src/math/ec/rfc8032/Ed448.cs @@ -31,6 +31,16 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 Ed448ph = 1, } + public sealed class PublicPoint + { + internal readonly uint[] m_data; + + internal PublicPoint(uint[] data) + { + m_data = data; + } + } + private const int CoordUints = 14; private const int PointBytes = CoordUints * 4 + 1; private const int ScalarUints = 14; @@ -113,14 +123,14 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 return ctx != null && ctx.Length < 256; } - private static int CheckPoint(uint[] x, uint[] y) + private static int CheckPoint(ref PointAffine p) { uint[] t = F.Create(); uint[] u = F.Create(); uint[] v = F.Create(); - F.Sqr(x, u); - F.Sqr(y, v); + F.Sqr(p.x, u); + F.Sqr(p.y, v); F.Mul(u, v, t); F.Add(u, v, u); F.Mul(t, -C_d, t); @@ -153,6 +163,13 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 return F.IsZero(t); } + private static bool CheckPointOrderVar(ref PointAffine p) + { + Init(out PointProjective r); + ScalarMultOrderVar(ref p, ref r); + return NormalizeToNeutralElementVar(ref r); + } + #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER private static bool CheckPointVar(ReadOnlySpan p) { @@ -277,9 +294,9 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 } #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER - private static bool DecodePointVar(ReadOnlySpan p, bool negate, ref PointProjective r) + private static bool DecodePointVar(ReadOnlySpan p, bool negate, ref PointAffine r) #else - private static bool DecodePointVar(byte[] p, bool negate, ref PointProjective r) + private static bool DecodePointVar(byte[] p, bool negate, ref PointAffine r) #endif { int x_0 = (p[PointBytes - 1] & 0x80) >> 7; @@ -305,9 +322,9 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 if (negate ^ (x_0 != (r.x[0] & 1))) { F.Negate(r.x, r.x); + F.Normalize(r.x); } - F.One(r.z); return true; } @@ -334,46 +351,73 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 #endif } - private static int EncodePoint(ref PointProjective p, byte[] r, int rOff) + private static void EncodePoint(ref PointAffine p, byte[] r, int rOff) { - uint[] x = F.Create(); - uint[] y = F.Create(); + F.Encode(p.y, r, rOff); + r[rOff + PointBytes - 1] = (byte)((p.x[0] & 1) << 7); + } - F.Inv(p.z, y); - F.Mul(p.x, y, x); - F.Mul(p.y, y, y); - F.Normalize(x); - F.Normalize(y); +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + private static void EncodePoint(ref PointAffine p, Span r) + { + F.Encode(p.y, r); + r[PointBytes - 1] = (byte)((p.x[0] & 1) << 7); + } +#endif - int result = CheckPoint(x, y); + public static void EncodePublicPoint(PublicPoint publicPoint, byte[] pk, int pkOff) + { + F.Encode(publicPoint.m_data, F.Size, pk, pkOff); + pk[pkOff + PointBytes - 1] = (byte)((publicPoint.m_data[0] & 1) << 7); + } - F.Encode(y, r, rOff); - r[rOff + PointBytes - 1] = (byte)((x[0] & 1) << 7); +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public static void EncodePublicPoint(PublicPoint publicPoint, Span pk) + { + F.Encode(publicPoint.m_data.AsSpan(F.Size), pk); + pk[PointBytes - 1] = (byte)((publicPoint.m_data[0] & 1) << 7); + } +#endif + + private static int EncodeResult(ref PointProjective p, byte[] r, int rOff) + { +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + return EncodeResult(ref p, r.AsSpan(rOff)); +#else + Init(out PointAffine q); + NormalizeToAffine(ref p, ref q); + + int result = CheckPoint(ref q); + + EncodePoint(ref q, r, rOff); return result; +#endif } #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER - private static int EncodePoint(ref PointProjective p, Span r) + private static int EncodeResult(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); + Init(out PointAffine q); + NormalizeToAffine(ref p, ref q); - int result = CheckPoint(x, y); + int result = CheckPoint(ref q); - F.Encode(y, r); - r[PointBytes - 1] = (byte)((x[0] & 1) << 7); + EncodePoint(ref q, r); return result; } #endif + private static void ExportPoint(ref PointAffine p, out PublicPoint publicPoint) + { + uint[] data = new uint[F.Size * 2]; + F.Copy(p.x, 0, data, 0); + F.Copy(p.y, 0, data, F.Size); + + publicPoint = new PublicPoint(data); + } + public static void GeneratePrivateKey(SecureRandom random, byte[] k) { if (k.Length != SecretKeySize) @@ -426,6 +470,58 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 } #endif + public static void GeneratePublicKey(byte[] sk, int skOff, out PublicPoint publicPoint) + { +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + GeneratePublicKey(sk.AsSpan(skOff), out publicPoint); +#else + IXof d = CreateXof(); + byte[] h = new byte[ScalarBytes * 2]; + + d.BlockUpdate(sk, skOff, SecretKeySize); + d.OutputFinal(h, 0, h.Length); + + byte[] s = new byte[ScalarBytes]; + PruneScalar(h, 0, s); + + Init(out PointProjective p); + ScalarMultBase(s, ref p); + + Init(out PointAffine q); + NormalizeToAffine(ref p, ref q); + + if (0 == CheckPoint(ref q)) + throw new InvalidOperationException(); + + ExportPoint(ref q, out publicPoint); +#endif + } + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public static void GeneratePublicKey(ReadOnlySpan sk, out PublicPoint publicPoint) + { + IXof d = CreateXof(); + Span h = stackalloc byte[ScalarBytes * 2]; + + d.BlockUpdate(sk[..SecretKeySize]); + d.OutputFinal(h); + + Span s = stackalloc byte[ScalarBytes]; + PruneScalar(h, s); + + Init(out PointProjective p); + ScalarMultBase(s, ref p); + + Init(out PointAffine q); + NormalizeToAffine(ref p, ref q); + + if (0 == CheckPoint(ref q)) + throw new InvalidOperationException(); + + ExportPoint(ref q, out publicPoint); + } +#endif + #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER private static uint GetWindow4(ReadOnlySpan x, int n) #else @@ -525,11 +621,11 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 if (!CheckPointFullVar(A)) return false; - Init(out PointProjective pR); + Init(out PointAffine pR); if (!DecodePointVar(R, true, ref pR)) return false; - Init(out PointProjective pA); + Init(out PointAffine pA); if (!DecodePointVar(A, true, ref pA)) return false; @@ -565,11 +661,11 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 if (!CheckPointFullVar(A)) return false; - Init(out PointProjective pR); + Init(out PointAffine pR); if (!DecodePointVar(R, true, ref pR)) return false; - Init(out PointProjective pA); + Init(out PointAffine pA); if (!DecodePointVar(A, true, ref pA)) return false; @@ -596,12 +692,102 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 Init(out PointProjective pZ); ScalarMultStraus225Var(nS, v0, ref pA, v1, ref pR, ref pZ); + return NormalizeToNeutralElementVar(ref pZ); + } + + private static bool ImplVerify(byte[] sig, int sigOff, PublicPoint publicPoint, byte[] ctx, byte phflag, + byte[] m, int mOff, int mLen) + { + if (!CheckContextVar(ctx)) + throw new ArgumentException("ctx"); + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + Span signature = stackalloc byte[SignatureSize]; + signature.CopyFrom(sig.AsSpan(sigOff, SignatureSize)); + var R = signature[..PointBytes]; + var S = signature[PointBytes..]; + + if (!CheckPointVar(R)) + return false; + + Span nS = stackalloc uint[ScalarUints]; + if (!Scalar448.CheckVar(S, nS)) + return false; + + Init(out PointAffine pR); + if (!DecodePointVar(R, true, ref pR)) + return false; + + Init(out PointAffine pA); + F.Negate(publicPoint.m_data, pA.x); + F.Copy(publicPoint.m_data.AsSpan(F.Size), pA.y); + + Span A = stackalloc byte[PublicKeySize]; + EncodePublicPoint(publicPoint, A); + + IXof d = CreateXof(); + Span h = stackalloc byte[ScalarBytes * 2]; + + Dom4(d, phflag, ctx); + d.BlockUpdate(R); + d.BlockUpdate(A); + d.BlockUpdate(m.AsSpan(mOff, mLen)); + d.OutputFinal(h); + + Span k = stackalloc byte[ScalarBytes]; + Scalar448.Reduce(h, k); + + Span nA = stackalloc uint[ScalarUints]; + Scalar448.Decode(k, nA); + + Span v0 = stackalloc uint[8]; + Span v1 = stackalloc uint[8]; +#else + byte[] R = Copy(sig, sigOff, PointBytes); + byte[] S = Copy(sig, sigOff + PointBytes, ScalarBytes); + + if (!CheckPointVar(R)) + return false; + + uint[] nS = new uint[ScalarUints]; + if (!Scalar448.CheckVar(S, nS)) + return false; + + Init(out PointAffine pR); + if (!DecodePointVar(R, true, ref pR)) + return false; + + Init(out PointAffine pA); + F.Negate(publicPoint.m_data, pA.x); + F.Copy(publicPoint.m_data, F.Size, pA.y, 0); + + byte[] A = new byte[PublicKeySize]; + EncodePublicPoint(publicPoint, A, 0); + + IXof d = CreateXof(); + byte[] h = new byte[ScalarBytes * 2]; + + Dom4(d, phflag, ctx); + d.BlockUpdate(R, 0, PointBytes); + d.BlockUpdate(A, 0, PointBytes); + d.BlockUpdate(m, mOff, mLen); + d.OutputFinal(h, 0, h.Length); + + byte[] k = Scalar448.Reduce(h); + + uint[] nA = new uint[ScalarUints]; + Scalar448.Decode(k, nA); + + uint[] v0 = new uint[8]; + uint[] v1 = new uint[8]; +#endif - F.Normalize(pZ.x); - F.Normalize(pZ.y); - F.Normalize(pZ.z); + Scalar448.ReduceBasisVar(nA, v0, v1); + Scalar448.Multiply225Var(nS, v1, nS); - return IsNeutralElementVar(pZ.x, pZ.y, pZ.z); + Init(out PointProjective pZ); + ScalarMultStraus225Var(nS, v0, ref pA, v1, ref pR, ref pZ); + return NormalizeToNeutralElementVar(ref pZ); } private static void Init(out PointAffine r) @@ -655,6 +841,24 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 return F.IsZeroVar(x) && F.AreEqualVar(y, z); } + private static void NormalizeToAffine(ref PointProjective p, ref PointAffine r) + { + F.Inv(p.z, r.y); + F.Mul(r.y, p.x, r.x); + F.Mul(r.y, p.y, r.y); + F.Normalize(r.x); + F.Normalize(r.y); + } + + private static bool NormalizeToNeutralElementVar(ref PointProjective p) + { + F.Normalize(p.x); + F.Normalize(p.y); + F.Normalize(p.z); + + return IsNeutralElementVar(p.x, p.y, p.z); + } + private static void PointAdd(ref PointAffine p, ref PointProjective r) { uint[] b = F.Create(); @@ -813,6 +1017,13 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 F.Mul(f, g, r.z); } + private static void PointCopy(ref PointAffine p, ref PointProjective r) + { + F.Copy(p.x, 0, r.x, 0); + F.Copy(p.y, 0, r.y, 0); + F.One(r.z); + } + private static void PointCopy(ref PointProjective p, ref PointProjective r) { F.Copy(p.x, 0, r.x, 0); @@ -948,8 +1159,7 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 return table; } - private static void PointPrecomputeVar(ref PointProjective p, PointProjective[] points, int pointsOff, - int pointsLen) + private static void PointPrecompute(ref PointAffine p, PointProjective[] points, int pointsOff, int pointsLen) { Debug.Assert(pointsLen > 0); @@ -959,6 +1169,7 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 Init(out points[pointsOff]); PointCopy(ref p, ref points[pointsOff]); + for (int i = 1; i < pointsLen; ++i) { Init(out points[pointsOff + i]); @@ -990,19 +1201,20 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 PointProjective[] points = new PointProjective[totalPoints]; - Init(out PointProjective p); - F.Copy(B_x, 0, p.x, 0); - F.Copy(B_y, 0, p.y, 0); - F.One(p.z); + Init(out PointAffine B); + F.Copy(B_x, 0, B.x, 0); + F.Copy(B_y, 0, B.y, 0); - PointPrecomputeVar(ref p, points, 0, wnafPoints); + PointPrecompute(ref B, points, 0, wnafPoints); - Init(out PointProjective p225); - F.Copy(B225_x, 0, p225.x, 0); - F.Copy(B225_y, 0, p225.y, 0); - F.One(p225.z); + Init(out PointAffine B225); + F.Copy(B225_x, 0, B225.x, 0); + F.Copy(B225_y, 0, B225.y, 0); - PointPrecomputeVar(ref p225, points, wnafPoints, wnafPoints); + PointPrecompute(ref B225, points, wnafPoints, wnafPoints); + + Init(out PointProjective p); + PointCopy(ref B, ref p); int pointsIndex = wnafPoints * 2; PointProjective[] toothPowers = new PointProjective[PrecompTeeth]; @@ -1010,6 +1222,7 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 { Init(out toothPowers[tooth]); } + for (int block = 0; block < PrecompBlocks; ++block) { ref PointProjective sum = ref points[pointsIndex++]; @@ -1225,7 +1438,7 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 #else Init(out PointProjective p); ScalarMultBase(k, ref p); - if (0 == EncodePoint(ref p, r, rOff)) + if (0 == EncodeResult(ref p, r, rOff)) throw new InvalidOperationException(); #endif } @@ -1235,7 +1448,7 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 { Init(out PointProjective p); ScalarMultBase(k, ref p); - if (0 == EncodePoint(ref p, r)) + if (0 == EncodeResult(ref p, r)) throw new InvalidOperationException(); } #endif @@ -1276,7 +1489,7 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 } #endif - private static void ScalarMultOrderVar(ref PointProjective p, ref PointProjective r) + private static void ScalarMultOrderVar(ref PointAffine p, ref PointProjective r) { #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER Span ws_p = stackalloc sbyte[447]; @@ -1288,7 +1501,7 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 int count = 1 << (WnafWidth225 - 2); PointProjective[] tp = new PointProjective[count]; - PointPrecomputeVar(ref p, tp, 0, count); + PointPrecompute(ref p, tp, 0, count); PointSetNeutral(ref r); @@ -1309,11 +1522,11 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 } #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER - private static void ScalarMultStraus225Var(ReadOnlySpan nb, ReadOnlySpan np, ref PointProjective p, - ReadOnlySpan nq, ref PointProjective q, ref PointProjective r) + private static void ScalarMultStraus225Var(ReadOnlySpan nb, ReadOnlySpan np, ref PointAffine p, + ReadOnlySpan nq, ref PointAffine q, ref PointProjective r) #else - private static void ScalarMultStraus225Var(uint[] nb, uint[] np, ref PointProjective p, uint[] nq, - ref PointProjective q, ref PointProjective r) + private static void ScalarMultStraus225Var(uint[] nb, uint[] np, ref PointAffine p, uint[] nq, + ref PointAffine q, ref PointProjective r) #endif { Debug.Assert(nb.Length == ScalarUints); @@ -1342,8 +1555,8 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 int count = 1 << (WnafWidth225 - 2); PointProjective[] tp = new PointProjective[count]; PointProjective[] tq = new PointProjective[count]; - PointPrecomputeVar(ref p, tp, 0, count); - PointPrecomputeVar(ref q, tq, 0, count); + PointPrecompute(ref p, tp, 0, count); + PointPrecompute(ref q, tq, 0, count); PointSetNeutral(ref r); @@ -1438,52 +1651,175 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 public static bool ValidatePublicKeyFull(byte[] pk, int pkOff) { #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER - Span A = stackalloc byte[PublicKeySize]; - A.CopyFrom(pk.AsSpan(pkOff)); + return ValidatePublicKeyFull(pk.AsSpan(pkOff)); #else byte[] A = Copy(pk, pkOff, PublicKeySize); + + if (!CheckPointFullVar(A)) + return false; + + Init(out PointAffine pA); + if (!DecodePointVar(A, false, ref pA)) + return false; + + return CheckPointOrderVar(ref pA); #endif + } + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public static bool ValidatePublicKeyFull(ReadOnlySpan pk) + { + Span A = stackalloc byte[PublicKeySize]; + A.CopyFrom(pk); if (!CheckPointFullVar(A)) return false; - Init(out PointProjective pA); + Init(out PointAffine pA); if (!DecodePointVar(A, false, ref pA)) return false; - Init(out PointProjective pR); - ScalarMultOrderVar(ref pA, ref pR); + return CheckPointOrderVar(ref pA); + } +#endif - F.Normalize(pR.x); - F.Normalize(pR.y); - F.Normalize(pR.z); + public static bool ValidatePublicKeyFull(byte[] pk, int pkOff, out PublicPoint publicPoint) + { +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + return ValidatePublicKeyFull(pk.AsSpan(pkOff), out publicPoint); +#else + byte[] A = Copy(pk, pkOff, PublicKeySize); - return IsNeutralElementVar(pR.x, pR.y, pR.z); + if (CheckPointFullVar(A)) + { + Init(out PointAffine pA); + if (DecodePointVar(A, false, ref pA)) + { + if (CheckPointOrderVar(ref pA)) + { + ExportPoint(ref pA, out publicPoint); + return true; + } + } + } + + publicPoint = null; + return false; +#endif } +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public static bool ValidatePublicKeyFull(ReadOnlySpan pk, out PublicPoint publicPoint) + { + Span A = stackalloc byte[PublicKeySize]; + A.CopyFrom(pk); + + if (CheckPointFullVar(A)) + { + Init(out PointAffine pA); + if (DecodePointVar(A, false, ref pA)) + { + if (CheckPointOrderVar(ref pA)) + { + ExportPoint(ref pA, out publicPoint); + return true; + } + } + } + + publicPoint = null; + return false; + } +#endif + public static bool ValidatePublicKeyPartial(byte[] pk, int pkOff) { #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER - Span A = stackalloc byte[PublicKeySize]; - A.CopyFrom(pk.AsSpan(pkOff)); + return ValidatePublicKeyPartial(pk.AsSpan(pkOff)); #else byte[] A = Copy(pk, pkOff, PublicKeySize); + + if (!CheckPointFullVar(A)) + return false; + + Init(out PointAffine pA); + return DecodePointVar(A, false, ref pA); #endif + } + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public static bool ValidatePublicKeyPartial(ReadOnlySpan pk) + { + Span A = stackalloc byte[PublicKeySize]; + A.CopyFrom(pk); if (!CheckPointFullVar(A)) return false; - Init(out PointProjective pA); + Init(out PointAffine pA); return DecodePointVar(A, false, ref pA); } +#endif - public static bool Verify(byte[] sig, int sigOff, byte[] pk, int pkOff, byte[] ctx, byte[] m, int mOff, int mLen) + public static bool ValidatePublicKeyPartial(byte[] pk, int pkOff, out PublicPoint publicPoint) + { +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + return ValidatePublicKeyPartial(pk.AsSpan(pkOff), out publicPoint); +#else + byte[] A = Copy(pk, pkOff, PublicKeySize); + + if (CheckPointFullVar(A)) + { + Init(out PointAffine pA); + if (DecodePointVar(A, false, ref pA)) + { + ExportPoint(ref pA, out publicPoint); + return true; + } + } + + publicPoint = null; + return false; +#endif + } + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public static bool ValidatePublicKeyPartial(ReadOnlySpan pk, out PublicPoint publicPoint) + { + Span A = stackalloc byte[PublicKeySize]; + A.CopyFrom(pk); + + if (CheckPointFullVar(A)) + { + Init(out PointAffine pA); + if (DecodePointVar(A, false, ref pA)) + { + ExportPoint(ref pA, out publicPoint); + return true; + } + } + + publicPoint = null; + return false; + } +#endif + + public static bool Verify(byte[] sig, int sigOff, byte[] pk, int pkOff, byte[] ctx, byte[] m, int mOff, + int mLen) { byte phflag = 0x00; return ImplVerify(sig, sigOff, pk, pkOff, ctx, phflag, m, mOff, mLen); } + public static bool Verify(byte[] sig, int sigOff, PublicPoint publicPoint, byte[] ctx, byte[] m, int mOff, + int mLen) + { + byte phflag = 0x00; + + return ImplVerify(sig, sigOff, publicPoint, ctx, phflag, m, mOff, mLen); + } + public static bool VerifyPrehash(byte[] sig, int sigOff, byte[] pk, int pkOff, byte[] ctx, byte[] ph, int phOff) { byte phflag = 0x01; @@ -1491,6 +1827,14 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 return ImplVerify(sig, sigOff, pk, pkOff, ctx, phflag, ph, phOff, PrehashSize); } + public static bool VerifyPrehash(byte[] sig, int sigOff, PublicPoint publicPoint, byte[] ctx, byte[] ph, + int phOff) + { + byte phflag = 0x01; + + return ImplVerify(sig, sigOff, publicPoint, ctx, phflag, ph, phOff, PrehashSize); + } + public static bool VerifyPrehash(byte[] sig, int sigOff, byte[] pk, int pkOff, byte[] ctx, IXof ph) { byte[] m = new byte[PrehashSize]; @@ -1501,5 +1845,16 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032 return ImplVerify(sig, sigOff, pk, pkOff, ctx, phflag, m, 0, m.Length); } + + public static bool VerifyPrehash(byte[] sig, int sigOff, PublicPoint publicPoint, byte[] ctx, IXof ph) + { + byte[] m = new byte[PrehashSize]; + if (PrehashSize != ph.OutputFinal(m, 0, PrehashSize)) + throw new ArgumentException("ph"); + + byte phflag = 0x01; + + return ImplVerify(sig, sigOff, publicPoint, ctx, phflag, m, 0, m.Length); + } } } diff --git a/crypto/test/src/math/ec/rfc8032/test/Ed25519Test.cs b/crypto/test/src/math/ec/rfc8032/test/Ed25519Test.cs index 90fc24a4c..4155d4a0f 100644 --- a/crypto/test/src/math/ec/rfc8032/test/Ed25519Test.cs +++ b/crypto/test/src/math/ec/rfc8032/test/Ed25519Test.cs @@ -26,6 +26,7 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032.Tests { byte[] sk = new byte[Ed25519.SecretKeySize]; byte[] pk = new byte[Ed25519.PublicKeySize]; + byte[] pk2 = new byte[Ed25519.PublicKeySize]; byte[] m = new byte[255]; byte[] sig1 = new byte[Ed25519.SignatureSize]; byte[] sig2 = new byte[Ed25519.SignatureSize]; @@ -35,7 +36,14 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032.Tests for (int i = 0; i < 10; ++i) { Random.NextBytes(sk); - Ed25519.GeneratePublicKey(sk, 0, pk, 0); + Ed25519.GeneratePublicKey(sk, 0, out var publicPoint); + Ed25519.EncodePublicPoint(publicPoint, pk, 0); + + { + Ed25519.GeneratePublicKey(sk, 0, pk2, 0); + + Assert.IsTrue(Arrays.AreEqual(pk, pk2), "Ed25519 consistent generation #" + i); + } int mLen = Random.NextInt() & 255; @@ -44,14 +52,29 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032.Tests Assert.IsTrue(Arrays.AreEqual(sig1, sig2), "Ed25519 consistent signatures #" + i); - bool shouldVerify = Ed25519.Verify(sig1, 0, pk, 0, m, 0, mLen); + { + bool shouldVerify = Ed25519.Verify(sig1, 0, pk, 0, m, 0, mLen); - Assert.IsTrue(shouldVerify, "Ed25519 consistent sign/verify #" + i); + Assert.IsTrue(shouldVerify, "Ed25519 consistent sign/verify #" + i); + } + { + bool shouldVerify = Ed25519.Verify(sig1, 0, publicPoint, m, 0, mLen); + + Assert.IsTrue(shouldVerify, "Ed25519 consistent sign/verify #" + i); + } sig1[Ed25519.PublicKeySize - 1] ^= 0x80; - bool shouldNotVerify = Ed25519.Verify(sig1, 0, pk, 0, m, 0, mLen); - Assert.IsFalse(shouldNotVerify, "Ed25519 consistent verification failure #" + i); + { + bool shouldNotVerify = Ed25519.Verify(sig1, 0, pk, 0, m, 0, mLen); + + Assert.IsFalse(shouldNotVerify, "Ed25519 consistent verification failure #" + i); + } + { + bool shouldNotVerify = Ed25519.Verify(sig1, 0, publicPoint, m, 0, mLen); + + Assert.IsFalse(shouldNotVerify, "Ed25519 consistent verification failure #" + i); + } } } @@ -60,6 +83,7 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032.Tests { byte[] sk = new byte[Ed25519.SecretKeySize]; byte[] pk = new byte[Ed25519.PublicKeySize]; + byte[] pk2 = new byte[Ed25519.PublicKeySize]; byte[] ctx = new byte[Random.NextInt() & 7]; byte[] m = new byte[255]; byte[] sig1 = new byte[Ed25519.SignatureSize]; @@ -71,7 +95,14 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032.Tests for (int i = 0; i < 10; ++i) { Random.NextBytes(sk); - Ed25519.GeneratePublicKey(sk, 0, pk, 0); + Ed25519.GeneratePublicKey(sk, 0, out var publicPoint); + Ed25519.EncodePublicPoint(publicPoint, pk, 0); + + { + Ed25519.GeneratePublicKey(sk, 0, pk2, 0); + + Assert.IsTrue(Arrays.AreEqual(pk, pk2), "Ed25519 consistent generation #" + i); + } int mLen = Random.NextInt() & 255; @@ -80,14 +111,29 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032.Tests Assert.IsTrue(Arrays.AreEqual(sig1, sig2), "Ed25519ctx consistent signatures #" + i); - bool shouldVerify = Ed25519.Verify(sig1, 0, pk, 0, ctx, m, 0, mLen); + { + bool shouldVerify = Ed25519.Verify(sig1, 0, pk, 0, ctx, m, 0, mLen); - Assert.IsTrue(shouldVerify, "Ed25519ctx consistent sign/verify #" + i); + Assert.IsTrue(shouldVerify, "Ed25519ctx consistent sign/verify #" + i); + } + { + bool shouldVerify = Ed25519.Verify(sig1, 0, publicPoint, ctx, m, 0, mLen); + + Assert.IsTrue(shouldVerify, "Ed25519ctx consistent sign/verify #" + i); + } sig1[Ed25519.PublicKeySize - 1] ^= 0x80; - bool shouldNotVerify = Ed25519.Verify(sig1, 0, pk, 0, ctx, m, 0, mLen); - Assert.IsFalse(shouldNotVerify, "Ed25519ctx consistent verification failure #" + i); + { + bool shouldNotVerify = Ed25519.Verify(sig1, 0, pk, 0, ctx, m, 0, mLen); + + Assert.IsFalse(shouldNotVerify, "Ed25519ctx consistent verification failure #" + i); + } + { + bool shouldNotVerify = Ed25519.Verify(sig1, 0, publicPoint, ctx, m, 0, mLen); + + Assert.IsFalse(shouldNotVerify, "Ed25519ctx consistent verification failure #" + i); + } } } @@ -96,6 +142,7 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032.Tests { byte[] sk = new byte[Ed25519.SecretKeySize]; byte[] pk = new byte[Ed25519.PublicKeySize]; + byte[] pk2 = new byte[Ed25519.PublicKeySize]; byte[] ctx = new byte[Random.NextInt() & 7]; byte[] m = new byte[255]; byte[] ph = new byte[Ed25519.PrehashSize]; @@ -108,7 +155,14 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032.Tests for (int i = 0; i < 10; ++i) { Random.NextBytes(sk); - Ed25519.GeneratePublicKey(sk, 0, pk, 0); + Ed25519.GeneratePublicKey(sk, 0, out var publicPoint); + Ed25519.EncodePublicPoint(publicPoint, pk, 0); + + { + Ed25519.GeneratePublicKey(sk, 0, pk2, 0); + + Assert.IsTrue(Arrays.AreEqual(pk, pk2), "Ed25519 consistent generation #" + i); + } int mLen = Random.NextInt() & 255; @@ -121,14 +175,29 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032.Tests Assert.IsTrue(Arrays.AreEqual(sig1, sig2), "Ed25519ph consistent signatures #" + i); - bool shouldVerify = Ed25519.VerifyPrehash(sig1, 0, pk, 0, ctx, ph, 0); + { + bool shouldVerify = Ed25519.VerifyPrehash(sig1, 0, pk, 0, ctx, ph, 0); + + Assert.IsTrue(shouldVerify, "Ed25519ph consistent sign/verify #" + i); + } + { + bool shouldVerify = Ed25519.VerifyPrehash(sig1, 0, publicPoint, ctx, ph, 0); - Assert.IsTrue(shouldVerify, "Ed25519ph consistent sign/verify #" + i); + Assert.IsTrue(shouldVerify, "Ed25519ph consistent sign/verify #" + i); + } sig1[Ed25519.PublicKeySize - 1] ^= 0x80; - bool shouldNotVerify = Ed25519.VerifyPrehash(sig1, 0, pk, 0, ctx, ph, 0); - Assert.IsFalse(shouldNotVerify, "Ed25519ph consistent verification failure #" + i); + { + bool shouldNotVerify = Ed25519.VerifyPrehash(sig1, 0, pk, 0, ctx, ph, 0); + + Assert.IsFalse(shouldNotVerify, "Ed25519ph consistent verification failure #" + i); + } + { + bool shouldNotVerify = Ed25519.VerifyPrehash(sig1, 0, publicPoint, ctx, ph, 0); + + Assert.IsFalse(shouldNotVerify, "Ed25519ph consistent verification failure #" + i); + } } } diff --git a/crypto/test/src/math/ec/rfc8032/test/Ed448Test.cs b/crypto/test/src/math/ec/rfc8032/test/Ed448Test.cs index a26ef5579..38cde3b01 100644 --- a/crypto/test/src/math/ec/rfc8032/test/Ed448Test.cs +++ b/crypto/test/src/math/ec/rfc8032/test/Ed448Test.cs @@ -1,6 +1,4 @@ -using System; - -using NUnit.Framework; +using NUnit.Framework; using Org.BouncyCastle.Crypto; using Org.BouncyCastle.Security; @@ -25,6 +23,7 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032.Tests { byte[] sk = new byte[Ed448.SecretKeySize]; byte[] pk = new byte[Ed448.PublicKeySize]; + byte[] pk2 = new byte[Ed448.PublicKeySize]; byte[] ctx = new byte[Random.NextInt() & 7]; byte[] m = new byte[255]; byte[] sig1 = new byte[Ed448.SignatureSize]; @@ -36,7 +35,14 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032.Tests for (int i = 0; i < 10; ++i) { Random.NextBytes(sk); - Ed448.GeneratePublicKey(sk, 0, pk, 0); + Ed448.GeneratePublicKey(sk, 0, out var publicPoint); + Ed448.EncodePublicPoint(publicPoint, pk, 0); + + { + Ed448.GeneratePublicKey(sk, 0, pk2, 0); + + Assert.IsTrue(Arrays.AreEqual(pk, pk2), "Ed448 consistent generation #" + i); + } int mLen = Random.NextInt() & 255; @@ -45,14 +51,29 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032.Tests Assert.IsTrue(Arrays.AreEqual(sig1, sig2), "Ed448 consistent signatures #" + i); - bool shouldVerify = Ed448.Verify(sig1, 0, pk, 0, ctx, m, 0, mLen); + { + bool shouldVerify = Ed448.Verify(sig1, 0, pk, 0, ctx, m, 0, mLen); - Assert.IsTrue(shouldVerify, "Ed448 consistent sign/verify #" + i); + Assert.IsTrue(shouldVerify, "Ed448 consistent sign/verify #" + i); + } + { + bool shouldVerify = Ed448.Verify(sig1, 0, publicPoint, ctx, m, 0, mLen); + + Assert.IsTrue(shouldVerify, "Ed448 consistent sign/verify #" + i); + } sig1[Ed448.PublicKeySize - 1] ^= 0x80; - bool shouldNotVerify = Ed448.Verify(sig1, 0, pk, 0, ctx, m, 0, mLen); - Assert.IsFalse(shouldNotVerify, "Ed448 consistent verification failure #" + i); + { + bool shouldNotVerify = Ed448.Verify(sig1, 0, pk, 0, ctx, m, 0, mLen); + + Assert.IsFalse(shouldNotVerify, "Ed448 consistent verification failure #" + i); + } + { + bool shouldNotVerify = Ed448.Verify(sig1, 0, publicPoint, ctx, m, 0, mLen); + + Assert.IsFalse(shouldNotVerify, "Ed448 consistent verification failure #" + i); + } } } @@ -61,6 +82,7 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032.Tests { byte[] sk = new byte[Ed448.SecretKeySize]; byte[] pk = new byte[Ed448.PublicKeySize]; + byte[] pk2 = new byte[Ed448.PublicKeySize]; byte[] ctx = new byte[Random.NextInt() & 7]; byte[] m = new byte[255]; byte[] ph = new byte[Ed448.PrehashSize]; @@ -73,7 +95,14 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032.Tests for (int i = 0; i < 10; ++i) { Random.NextBytes(sk); - Ed448.GeneratePublicKey(sk, 0, pk, 0); + Ed448.GeneratePublicKey(sk, 0, out var publicPoint); + Ed448.EncodePublicPoint(publicPoint, pk, 0); + + { + Ed448.GeneratePublicKey(sk, 0, pk2, 0); + + Assert.IsTrue(Arrays.AreEqual(pk, pk2), "Ed448 consistent generation #" + i); + } int mLen = Random.NextInt() & 255; @@ -86,14 +115,29 @@ namespace Org.BouncyCastle.Math.EC.Rfc8032.Tests Assert.IsTrue(Arrays.AreEqual(sig1, sig2), "Ed448ph consistent signatures #" + i); - bool shouldVerify = Ed448.VerifyPrehash(sig1, 0, pk, 0, ctx, ph, 0); + { + bool shouldVerify = Ed448.VerifyPrehash(sig1, 0, pk, 0, ctx, ph, 0); - Assert.IsTrue(shouldVerify, "Ed448ph consistent sign/verify #" + i); + Assert.IsTrue(shouldVerify, "Ed448ph consistent sign/verify #" + i); + } + { + bool shouldVerify = Ed448.VerifyPrehash(sig1, 0, publicPoint, ctx, ph, 0); + + Assert.IsTrue(shouldVerify, "Ed448ph consistent sign/verify #" + i); + } sig1[Ed448.PublicKeySize - 1] ^= 0x80; - bool shouldNotVerify = Ed448.VerifyPrehash(sig1, 0, pk, 0, ctx, ph, 0); - Assert.IsFalse(shouldNotVerify, "Ed448ph consistent verification failure #" + i); + { + bool shouldNotVerify = Ed448.VerifyPrehash(sig1, 0, pk, 0, ctx, ph, 0); + + Assert.IsFalse(shouldNotVerify, "Ed448ph consistent verification failure #" + i); + } + { + bool shouldNotVerify = Ed448.VerifyPrehash(sig1, 0, publicPoint, ctx, ph, 0); + + Assert.IsFalse(shouldNotVerify, "Ed448ph consistent verification failure #" + i); + } } } -- cgit 1.4.1