summary refs log tree commit diff
path: root/crypto/src/math/ec/rfc8032
diff options
context:
space:
mode:
authorPeter Dettman <peter.dettman@bouncycastle.org>2022-11-26 22:12:14 +0700
committerPeter Dettman <peter.dettman@bouncycastle.org>2022-11-26 22:12:14 +0700
commit11529c83fbdd81f9a25658ec8d3c526618576be6 (patch)
tree517803479099edff45dedc6799fe184b0f9952c1 /crypto/src/math/ec/rfc8032
parentRefactoring in Math.EC.Rfc8032 (diff)
downloadBouncyCastle.NET-ed25519-11529c83fbdd81f9a25658ec8d3c526618576be6.tar.xz
EdDSA: Hold decoded pubilc point in public keys
Diffstat (limited to 'crypto/src/math/ec/rfc8032')
-rw-r--r--crypto/src/math/ec/rfc8032/Ed25519.cs444
-rw-r--r--crypto/src/math/ec/rfc8032/Ed448.cs499
2 files changed, 827 insertions, 116 deletions
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<byte> 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<byte> 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<byte> 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<byte> r)
+        private static int EncodeResult(ref PointAccum p, Span<byte> 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<byte> sk, out PublicPoint publicPoint)
+        {
+            IDigest d = CreateDigest();
+            Span<byte> h = stackalloc byte[64];
+
+            d.BlockUpdate(sk[..SecretKeySize]);
+            d.DoFinal(h);
+
+            Span<byte> 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<uint> 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<byte> signature = stackalloc byte[SignatureSize];
+            signature.CopyFrom(sig.AsSpan(sigOff, SignatureSize));
+            var R = signature[..PointBytes];
+            var S = signature[PointBytes..];
+
+            if (!CheckPointVar(R))
+                return false;
+
+            Span<uint> 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<byte> A = stackalloc byte[PublicKeySize];
+            EncodePublicPoint(publicPoint, A);
 
-            F.Normalize(pZ.x);
-            F.Normalize(pZ.y);
-            F.Normalize(pZ.z);
+            IDigest d = CreateDigest();
+            Span<byte> 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<byte> k = stackalloc byte[ScalarBytes];
+            Scalar25519.Reduce(h, k);
+
+            Span<uint> nA = stackalloc uint[ScalarUints];
+            Scalar25519.Decode(k, nA);
+
+            Span<uint> v0 = stackalloc uint[4];
+            Span<uint> 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<byte> 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<byte> pk)
+        {
+            Span<byte> 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<byte> pk, out PublicPoint publicPoint)
+        {
+            Span<byte> 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<byte> 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<byte> pk)
+        {
+            Span<byte> 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<byte> pk, out PublicPoint publicPoint)
+        {
+            Span<byte> 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<byte> 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<byte> p, bool negate, ref PointProjective r)
+        private static bool DecodePointVar(ReadOnlySpan<byte> 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<byte> 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<byte> 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<byte> r)
+        private static int EncodeResult(ref PointProjective p, Span<byte> 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<byte> sk, out PublicPoint publicPoint)
+        {
+            IXof d = CreateXof();
+            Span<byte> h = stackalloc byte[ScalarBytes * 2];
+
+            d.BlockUpdate(sk[..SecretKeySize]);
+            d.OutputFinal(h);
+
+            Span<byte> 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<uint> 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<byte> signature = stackalloc byte[SignatureSize];
+            signature.CopyFrom(sig.AsSpan(sigOff, SignatureSize));
+            var R = signature[..PointBytes];
+            var S = signature[PointBytes..];
+
+            if (!CheckPointVar(R))
+                return false;
+
+            Span<uint> 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<byte> A = stackalloc byte[PublicKeySize];
+            EncodePublicPoint(publicPoint, A);
+
+            IXof d = CreateXof();
+            Span<byte> 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<byte> k = stackalloc byte[ScalarBytes];
+            Scalar448.Reduce(h, k);
+
+            Span<uint> nA = stackalloc uint[ScalarUints];
+            Scalar448.Decode(k, nA);
+
+            Span<uint> v0 = stackalloc uint[8];
+            Span<uint> 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<sbyte> 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<uint> nb, ReadOnlySpan<uint> np, ref PointProjective p,
-            ReadOnlySpan<uint> nq, ref PointProjective q, ref PointProjective r)
+        private static void ScalarMultStraus225Var(ReadOnlySpan<uint> nb, ReadOnlySpan<uint> np, ref PointAffine p,
+            ReadOnlySpan<uint> 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<byte> 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<byte> pk)
+        {
+            Span<byte> 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<byte> pk, out PublicPoint publicPoint)
+        {
+            Span<byte> 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<byte> 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<byte> pk)
+        {
+            Span<byte> 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<byte> pk, out PublicPoint publicPoint)
+        {
+            Span<byte> 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);
+        }
     }
 }