/// <summary> /// Verifies a signature of <paramref name="Data"/> made by the EdDSA algorithm. /// </summary> /// <param name="Data">Payload to sign.</param> /// <param name="PublicKey">Public Key of the entity that generated the signature.</param> /// <param name="HashFunction">Hash function to use.</param> /// <param name="Curve">Elliptic curve</param> /// <param name="Signature">Signature</param> /// <returns>If the signature is valid.</returns> public static bool Verify(byte[] Data, byte[] PublicKey, HashFunctionArray HashFunction, EdwardsCurveBase Curve, byte[] Signature) { try { int ScalarBytes = Signature.Length; if ((ScalarBytes & 1) != 0) { return(false); } ScalarBytes >>= 1; byte[] R = new byte[ScalarBytes]; Array.Copy(Signature, 0, R, 0, ScalarBytes); PointOnCurve r = Decode(R, Curve); byte[] S = new byte[ScalarBytes]; Array.Copy(Signature, ScalarBytes, S, 0, ScalarBytes); BigInteger s = EllipticCurve.ToInt(S); if (s >= Curve.Order) { return(false); } int c = Data.Length; byte[] Bin = new byte[(ScalarBytes << 1) + c]; // dom2(F, C) = blank string Array.Copy(R, 0, Bin, 0, ScalarBytes); Array.Copy(PublicKey, 0, Bin, ScalarBytes, ScalarBytes); Array.Copy(Data, 0, Bin, ScalarBytes << 1, c); // PH(M)=M byte[] h = HashFunction(Bin); BigInteger k = BigInteger.Remainder(EllipticCurve.ToInt(h), Curve.Order); PointOnCurve P1 = Curve.ScalarMultiplication(s, Curve.BasePoint, false); PointOnCurve P2 = Curve.ScalarMultiplication(k, Curve.Decode(PublicKey), false); Curve.AddTo(ref P2, r); P1.Normalize(Curve); P2.Normalize(Curve); return(P1.Equals(P2)); } catch (ArgumentException) { return(false); } }
/// <summary> /// Converts a pair of (X,Y) coordinates for the birational Edwards curve /// to a pair of (U,V) coordinates. /// </summary> /// <param name="XY">(X,Y) coordinates.</param> /// <returns>(U,V) coordinates.</returns> public override PointOnCurve ToUV(PointOnCurve XY) { BigInteger U = this.modP.Divide(XY.Y + BigInteger.One, BigInteger.One - XY.Y); BigInteger V = this.modP.Multiply(SqrtMinus486664, this.modP.Divide(U, XY.X)); if (U.Sign < 0) { U += this.p; } if (V.Sign < 0) { V += this.p; } return(new PointOnCurve(U, V)); }
/// <summary> /// Converts a pair of (U,V) coordinates to a pair of (X,Y) coordinates /// in the birational Edwards curve. /// </summary> /// <param name="UV">(U,V) coordinates.</param> /// <returns>(X,Y) coordinates.</returns> public override PointOnCurve ToXY(PointOnCurve UV) { BigInteger X = this.modP.Multiply(SqrtMinus486664, this.modP.Divide(UV.X, UV.Y)); BigInteger Y = this.modP.Divide(UV.X - BigInteger.One, UV.X + BigInteger.One); if (X.Sign < 0) { X += this.p; } if (Y.Sign < 0) { Y += this.p; } return(new PointOnCurve(X, Y)); }
/// <summary> /// Gets a shared key using the Elliptic Curve Diffie-Hellman (ECDH) algorithm. /// </summary> /// <param name="LocalPrivateKey">Local private key.</param> /// <param name="RemotePublicKey">Public key of the remote party.</param> /// <param name="HashFunction">A Hash function is applied to the derived key to generate the shared secret. /// The derived key, as a byte array of equal size as the order of the prime field, ordered by most significant byte first, /// is passed on to the hash function before being returned as the shared key.</param> /// <param name="Curve">Elliptic curve used.</param> /// <returns>Shared secret.</returns> public static byte[] GetSharedKey(byte[] LocalPrivateKey, byte[] RemotePublicKey, HashFunctionArray HashFunction, EllipticCurve Curve) { PointOnCurve PublicKey = Curve.Decode(RemotePublicKey); PointOnCurve P = Curve.ScalarMultiplication(LocalPrivateKey, PublicKey, true); byte[] B = P.X.ToByteArray(); if (B.Length != Curve.OrderBytes) { Array.Resize <byte>(ref B, Curve.OrderBytes); } Array.Reverse(B); // Most significant byte first. return(HashFunction(B)); }
/// <summary> /// Doubles a point on the curve. /// </summary> /// <param name="P">Point</param> public override void Double(ref PointOnCurve P) { if (!P.IsHomogeneous) { P.Z = BigInteger.One; } BigInteger A = this.modP.Add(P.X, P.Y); BigInteger B = this.modP.Multiply(A, A); BigInteger C = this.modP.Multiply(P.X, P.X); BigInteger D = this.modP.Multiply(P.Y, P.Y); BigInteger E = this.modP.Add(C, D); BigInteger H = this.modP.Multiply(P.Z, P.Z); BigInteger J = this.modP.Subtract(E, H << 1); P.X = this.modP.Multiply(B - E, J); P.Y = this.modP.Multiply(E, C - D); P.Z = this.modP.Multiply(E, J); }
/// <summary> /// Creates the Edwards Curve pair. /// </summary> /// <returns>Edwards curve.</returns> public override EdwardsCurveBase CreatePair() { PointOnCurve PublicKeyUV = this.PublicKeyPoint; PointOnCurve PublicKeyXY = this.ToXY(PublicKeyUV); Edwards448 Candidate = new Edwards448(this.PrivateKey, false); PointOnCurve PublicKeyXY2 = Candidate.PublicKeyPoint; PublicKeyXY2 = Candidate.ScalarMultiplication(4, PublicKeyXY2, true); if (PublicKeyXY.Y.Equals(PublicKeyXY2.Y)) { return(Candidate); } else { throw new InvalidOperationException("Unable to create pair curve."); } }
/// <summary> /// Converts a pair of (X,Y) coordinates for the birational Edwards curve /// to a pair of (U,V) coordinates. /// </summary> /// <param name="XY">(X,Y) coordinates.</param> /// <returns>(U,V) coordinates.</returns> public override PointOnCurve ToUV(PointOnCurve XY) { BigInteger X2 = this.modP.Multiply(XY.X, XY.X); BigInteger Y2 = this.modP.Multiply(XY.Y, XY.Y); BigInteger U = this.modP.Divide(Y2, X2); BigInteger X3 = this.modP.Multiply(XY.X, X2); BigInteger V = this.modP.Divide(this.modP.Multiply(Two - X2 - Y2, XY.Y), X3); if (U.Sign < 0) { U += this.p; } if (V.Sign < 0) { V += this.p; } return(new PointOnCurve(U, V)); }
/// <summary> /// Performs the scalar multiplication of <paramref name="N"/>*<paramref name="P"/>. /// </summary> /// <param name="N">Scalar</param> /// <param name="P">Point</param> /// <returns><paramref name="N"/>*<paramref name="P"/></returns> public PointOnCurve ScalarMultiplication(BigInteger N, PointOnCurve P) { PointOnCurve Result = PointOnCurve.Zero; byte[] Bin = N.ToByteArray(); int i; int c = Math.Min(this.orderBits, Bin.Length << 3); for (i = 0; i < c; i++) { if ((Bin[i >> 3] & (1 << (i & 7))) != 0) { this.AddTo(ref Result, P); } this.Double(ref P); } return(Result); }
/// <summary> /// Encodes a point on the curve. /// </summary> /// <param name="Point">Normalized point to encode.</param> /// <returns>Encoded point.</returns> public virtual byte[] Encode(PointOnCurve Point) { byte[] X = Point.X.ToByteArray(); byte[] Y = Point.Y.ToByteArray(); if (X.Length != this.orderBytes) { Array.Resize <byte>(ref X, this.orderBytes); } if (Y.Length != this.orderBytes) { Array.Resize <byte>(ref Y, this.orderBytes); } byte[] Result = new byte[this.orderBytes << 1]; Array.Copy(X, 0, Result, 0, this.orderBytes); Array.Copy(Y, 0, Result, this.orderBytes, this.orderBytes); return(Result); }
/// <summary> /// Base class of Elliptic curves over a prime field. /// </summary> /// <param name="Prime">Prime base of field.</param> /// <param name="BasePoint">Base-point.</param> /// <param name="A">a Coefficient in the definition of the curve E: y^2=x^3+a*x+b</param> /// <param name="Order">Order of base-point.</param> /// <param name="OrderBits">Number of bits used to encode order.</param> /// <param name="D">Private key.</param> public CurvePrimeField(BigInteger Prime, PointOnCurve BasePoint, BigInteger A, BigInteger Order, int OrderBits, BigInteger?D) : base(Prime) { if (Prime <= BigInteger.One) { throw new ArgumentException("Invalid prime base.", nameof(Prime)); } this.g = BasePoint; this.a = A; this.n = Order; this.orderBits = OrderBits; this.orderBytes = (OrderBits + 7) >> 3; this.modN = new ModulusP(Order); this.msbMask = 0xff; int MaskBits = (8 - OrderBits) & 7; if (MaskBits == 0) { this.orderBytes++; this.msbMask = 0; } else { this.msbMask >>= MaskBits; } if (D.HasValue) { this.publicKey = this.ScalarMultiplication(D.Value, this.g); this.d = D.Value; } else { this.GenerateKeys(); } }
/// <summary> /// Adds <paramref name="Q"/> to <paramref name="P"/>. /// </summary> /// <param name="P">Point 1.</param> /// <param name="Q">Point 2.</param> /// <returns>P+Q</returns> public void AddTo(ref PointOnCurve P, PointOnCurve Q) { if (P.IsZero) { P.X = Q.X; P.Y = Q.Y; P.IsZero = Q.IsZero; } else if (!Q.IsZero) { BigInteger sDividend = this.Subtract(P.Y, Q.Y); BigInteger sDivisor = this.Subtract(P.X, Q.X); BigInteger s, xR, yR; if (sDivisor.IsZero) { if (sDividend.IsZero) // P=Q { this.Double(ref P); } else { P.X = BigInteger.Zero; P.Y = BigInteger.Zero; P.IsZero = true; } } else { s = this.Divide(sDividend, sDivisor); xR = this.Subtract(this.Multiply(s, s), this.Add(P.X, Q.X)); yR = this.Add(P.Y, this.Multiply(s, this.Subtract(xR, P.X))); P.X = xR; P.Y = this.p - yR; } } }
/// <summary> /// Performs the scalar multiplication of <paramref name="N"/>*<paramref name="P"/>. /// </summary> /// <param name="N">Scalar, in binary, little-endian form.</param> /// <param name="P">Point</param> /// <param name="Normalize">If normalized output is expected.</param> /// <returns><paramref name="N"/>*<paramref name="P"/></returns> public virtual PointOnCurve ScalarMultiplication(byte[] N, PointOnCurve P, bool Normalize) { PointOnCurve Result = this.Zero; int i, c = N.Length; byte b, Bit; for (i = 0; i < c; i++) { b = N[i]; for (Bit = 1; Bit != 0; Bit <<= 1) { if ((b & Bit) != 0) { this.AddTo(ref Result, P); } this.Double(ref P); } } return(Result); }
/// <summary> /// Adds <paramref name="Q"/> to <paramref name="P"/>. /// </summary> /// <param name="P">Point 1.</param> /// <param name="Q">Point 2.</param> /// <returns>P+Q</returns> public override void AddTo(ref PointOnCurve P, PointOnCurve Q) { if (P.NonZero) { if (Q.NonZero) { BigInteger sDividend = this.modP.Subtract(P.Y, Q.Y); BigInteger sDivisor = this.modP.Subtract(P.X, Q.X); BigInteger s, xR, yR; if (sDivisor.IsZero) { if (sDividend.IsZero) // P=Q { this.Double(ref P); } else { P = this.Zero; } } else { s = this.modP.Divide(sDividend, sDivisor); xR = this.modP.Subtract(this.modP.Multiply(s, s), this.modP.Add(P.X, Q.X)); yR = this.modP.Add(P.Y, this.modP.Multiply(s, this.modP.Subtract(xR, P.X))); P.X = xR; P.Y = this.p - yR; } } } else { P.CopyFrom(Q); } }
/// <summary> /// Encodes a point on the curve in accordance with §5.1.2 of RFC 8032. /// </summary> /// <param name="P">Point</param> /// <param name="Curve">Edwards curve.</param> /// <returns>Encoding</returns> public static byte[] Encode(PointOnCurve P, EdwardsCurveBase Curve) { int ScalarBits = Curve.CoordinateBits; int ScalarBytes = (ScalarBits + 9) >> 3; byte[] y = P.Y.ToByteArray(); if (y.Length != ScalarBytes) { Array.Resize <byte>(ref y, ScalarBytes); } byte[] x = P.X.ToByteArray(); int Msb = (ScalarBits + 1) & 7; byte Mask = (byte)(0xff >> (8 - Msb)); y[ScalarBytes - 1] &= Mask; if ((x[0] & 1) != 0) { y[ScalarBytes - 1] |= 0x80; // Always MSB } return(y); }
/// <summary> /// Base class of Edwards curves (x²+y²=1+dx²y²) over a prime field. /// </summary> /// <param name="Prime">Prime base of field.</param> /// <param name="BasePoint">Base-point in (X,Y) coordinates.</param> /// <param name="d">Coefficient in the curve equation (x²+y²=1+dx²y²)</param> /// <param name="Order">Order of base-point.</param> /// <param name="Cofactor">Cofactor of curve.</param> public EdwardsCurve(BigInteger Prime, PointOnCurve BasePoint, BigInteger d, BigInteger Order, int Cofactor) : this(Prime, BasePoint, d, Order, Cofactor, null) { }
/// <summary> /// Negates a point on the curve. /// </summary> /// <param name="P">Point</param> public void Negate(ref PointOnCurve P) { P.Y = this.p - P.Y; }
/// <summary> /// Base class of Elliptic curves over a prime field defined by NIST. /// </summary> /// <param name="Prime">Prime base of field.</param> /// <param name="BasePoint">Base-point.</param> /// <param name="Order">Order of base-point.</param> /// <param name="Secret">Secret.</param> public NistPrimeCurve(BigInteger Prime, PointOnCurve BasePoint, BigInteger Order, byte[] Secret) : base(Prime, BasePoint, -3, Order, 1, Secret) { }
/// <summary> /// Doubles a point on the curve. /// </summary> /// <param name="P">Point</param> public override void Double(ref PointOnCurve P) { throw new NotSupportedException("Scalar multiplication is performed using a Montgomery ladder."); }
/// <summary> /// Performs the scalar multiplication of <paramref name="N"/>*<paramref name="P"/>. /// </summary> /// <param name="N">Scalar</param> /// <param name="P">Point</param> /// <param name="Normalize">If normalized output is expected.</param> /// <returns><paramref name="N"/>*<paramref name="P"/></returns> public PointOnCurve ScalarMultiplication(BigInteger N, PointOnCurve P, bool Normalize) { return(this.ScalarMultiplication(N.ToByteArray(), P, Normalize)); }
/// <summary> /// Converts a pair of (X,Y) coordinates for the birational Edwards curve /// to a pair of (U,V) coordinates. /// </summary> /// <param name="XY">(X,Y) coordinates.</param> /// <returns>(U,V) coordinates.</returns> public abstract PointOnCurve ToUV(PointOnCurve XY);
/// <summary> /// Adds <paramref name="Q"/> to <paramref name="P"/>. /// </summary> /// <param name="P">Point 1.</param> /// <param name="Q">Point 2.</param> /// <returns>P+Q</returns> public override void AddTo(ref PointOnCurve P, PointOnCurve Q) { this.Double(ref P); }
/// <summary> /// Base class of Montgomery curves, with biratinal Edwards equivalent /// over a prime field. /// </summary> /// <param name="Prime">Prime base of field.</param> /// <param name="BasePoint">Base-point in (U,V) coordinates.</param> /// <param name="Order">Order of base-point.</param> /// <param name="Cofactor">Cofactor of curve.</param> /// <param name="Secret">Secret.</param> public MontgomeryCurve(BigInteger Prime, PointOnCurve BasePoint, BigInteger Order, int Cofactor, byte[] Secret) : base(Prime, BasePoint, Order, Cofactor, Secret) { }
/// <summary> /// Converts a pair of (U,V) coordinates to a pair of (X,Y) coordinates /// in the birational Edwards curve. /// </summary> /// <param name="UV">(U,V) coordinates.</param> /// <returns>(X,Y) coordinates.</returns> public abstract PointOnCurve ToXY(PointOnCurve UV);
/// <summary> /// Base class of Montgomery curves (y²=x³+Ax²+x), with biratinal Edwards equivalent /// over a prime field. /// </summary> /// <param name="Prime">Prime base of field.</param> /// <param name="BasePoint">Base-point in (U,V) coordinates.</param> /// <param name="Order">Order of base-point.</param> /// <param name="Cofactor">Cofactor of curve.</param> public MontgomeryCurve(BigInteger Prime, PointOnCurve BasePoint, BigInteger Order, int Cofactor) : base(Prime, BasePoint, Order, Cofactor) { }
/// <summary> /// Performs the scalar multiplication of <paramref name="N"/>*<paramref name="P"/>. /// </summary> /// <param name="N">Scalar, in binary, little-endian form.</param> /// <param name="P">Point</param> /// <param name="Normalize">If normalized output is expected.</param> /// <returns><paramref name="N"/>*<paramref name="P"/></returns> public override PointOnCurve ScalarMultiplication(byte[] N, PointOnCurve P, bool Normalize) { return(new PointOnCurve(this.ScalarMultiplication(N, P.X), BigInteger.Zero)); }
/// <summary> /// Adds <paramref name="Q"/> to <paramref name="P"/>. /// </summary> /// <param name="P">Point 1.</param> /// <param name="Q">Point 2.</param> /// <returns>P+Q</returns> public abstract void AddTo(ref PointOnCurve P, PointOnCurve Q);
/// <summary> /// Doubles a point on the curve. /// </summary> /// <param name="P">Point</param> public abstract void Double(ref PointOnCurve P);
/// <summary> /// Abstract base class for elliptic curves. /// </summary> /// <param name="BasePoint">Base-point.</param> /// <param name="Order">Order of base-point.</param> /// <param name="Cofactor">Cofactor of curve.</param> public EllipticCurve(PointOnCurve BasePoint, BigInteger Order, int Cofactor) : this(BasePoint, Order, Cofactor, null) { }
/// <summary> /// Signs data using the EdDSA algorithm. /// </summary> /// <param name="Data">Data to be signed.</param> /// <param name="PrivateKey">Private key.</param> /// <param name="Prefix">Prefix</param> /// <param name="HashFunction">Hash function to use</param> /// <param name="Curve">Elliptic curve</param> /// <returns>Signature</returns> public static byte[] Sign(Stream Data, byte[] PrivateKey, byte[] Prefix, HashFunctionStream HashFunction, EdwardsCurveBase Curve) { // 5.1.6 of RFC 8032 int ScalarBytes = PrivateKey.Length; if (Prefix.Length != ScalarBytes) { throw new ArgumentException("Invalid prefix.", nameof(Prefix)); } BigInteger a = EllipticCurve.ToInt(PrivateKey); PointOnCurve P = Curve.ScalarMultiplication(PrivateKey, Curve.BasePoint, true); byte[] A = Encode(P, Curve); byte[] h; using (TemporaryStream TempFile = new TemporaryStream()) // dom2(F, C) = blank string { TempFile.Write(Prefix, 0, ScalarBytes); // prefix Data.Position = 0; Data.CopyTo(TempFile); // PH(M)=M TempFile.Position = 0; h = HashFunction(TempFile); } BigInteger r = BigInteger.Remainder(EllipticCurve.ToInt(h), Curve.Order); PointOnCurve R = Curve.ScalarMultiplication(r, Curve.BasePoint, true); byte[] Rs = Encode(R, Curve); using (TemporaryStream TempFile = new TemporaryStream()) // dom2(F, C) = blank string { TempFile.Write(Rs, 0, ScalarBytes); TempFile.Write(A, 0, ScalarBytes); Data.Position = 0; Data.CopyTo(TempFile); // PH(M)=M TempFile.Position = 0; h = HashFunction(TempFile); } BigInteger k = BigInteger.Remainder(EllipticCurve.ToInt(h), Curve.Order); BigInteger s = Curve.ModulusN.Add(r, Curve.ModulusN.Multiply(k, a)); byte[] Bin = s.ToByteArray(); if (Bin.Length != ScalarBytes) { Array.Resize <byte>(ref Bin, ScalarBytes); } byte[] Signature = new byte[ScalarBytes << 1]; Array.Copy(Rs, 0, Signature, 0, ScalarBytes); Array.Copy(Bin, 0, Signature, ScalarBytes, ScalarBytes); return(Signature); }
/// <summary> /// Base class of Edwards curves (x²+y²=1+dx²y²) over a prime field. /// </summary> /// <param name="Prime">Prime base of field.</param> /// <param name="BasePoint">Base-point in (X,Y) coordinates.</param> /// <param name="d">Coefficient in the curve equation (x²+y²=1+dx²y²)</param> /// <param name="Order">Order of base-point.</param> /// <param name="Cofactor">Cofactor of curve.</param> /// <param name="Secret">Secret.</param> public EdwardsCurve(BigInteger Prime, PointOnCurve BasePoint, BigInteger d, BigInteger Order, int Cofactor, byte[] Secret) : base(Prime, BasePoint, d, Order, Cofactor, Secret) { this.p34 = (this.p - 3) / 4; }