summary refs log tree commit diff
path: root/crypto/src/math/ec/rfc8032/Ed25519.cs
diff options
context:
space:
mode:
Diffstat (limited to 'crypto/src/math/ec/rfc8032/Ed25519.cs')
-rw-r--r--crypto/src/math/ec/rfc8032/Ed25519.cs444
1 files changed, 400 insertions, 44 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);
+        }
     }
 }