public EdSignatureResult Sign(EdDomainParameters domainParameters, EdKeyPair keyPair, BitString message, BitString context, bool preHash = false) { Sha = domainParameters.Hash; // If preHash version, then the message becomes the hash of the message if (preHash) { message = Sha.HashMessage(message, 512).Digest; } // 1. Hash the private key var hashResult = HashPrivate(domainParameters, keyPair.PrivateD); // 2. Compute r // Determine dom. Empty if ed25519. Different for preHash function BitString dom; if (preHash) { dom = domainParameters.CurveE.CurveName == Curve.Ed448 ? Dom4(1, context) : Dom2(1, context); } else { dom = domainParameters.CurveE.CurveName == Curve.Ed448 ? Dom4(0, context) : new BitString(""); } // Hash (dom4 || Prefix || message) var rBits = Sha.HashMessage(BitString.ConcatenateBits(dom, BitString.ConcatenateBits(hashResult.HDigest2, message)), 912).Digest; // Convert rBits to little endian and mod order n var r = BitString.ReverseByteOrder(rBits).ToPositiveBigInteger() % domainParameters.CurveE.OrderN; // 3. Compute [r]G. R is the encoding of [r]G var rG = domainParameters.CurveE.Multiply(domainParameters.CurveE.BasePointG, r); // Encode the point rG into a b-bit bitstring var R = domainParameters.CurveE.Encode(rG); // 4. Define S // Hash (dom4 || R || Q || M). Need to use dom4 if ed448 var hashData = BitString.ConcatenateBits(keyPair.PublicQ, message); hashData = BitString.ConcatenateBits(dom, BitString.ConcatenateBits(R, hashData)); var hash = Sha.HashMessage(hashData, 912).Digest; // Convert hash to int from little endian and mod order n var hashInt = BitString.ReverseByteOrder(hash).ToPositiveBigInteger() % domainParameters.CurveE.OrderN; // Determine s as done in key generation var s = NumberTheory.Pow2(domainParameters.CurveE.VariableN) + hashResult.Buffer.ToPositiveBigInteger(); // Calculate S as an BigInteger var Sint = (r + (hashInt * s)).PosMod(domainParameters.CurveE.OrderN); // Encode S in little endian var S = BitString.ReverseByteOrder(new BitString(Sint, domainParameters.CurveE.VariableB)); // 5. Form the signature by concatenating R and S return new EdSignatureResult(new EdSignature(R, S)); }
public static (EdPoint R, BigInteger s) DecodeSig(EdDomainParameters domainParameters, EdSignature sig) { var rBits = sig.Sig.MSBSubstring(0, domainParameters.CurveE.VariableB); var sBits = sig.Sig.Substring(0, domainParameters.CurveE.VariableB); var R = domainParameters.CurveE.Decode(rBits); var s = BitString.ReverseByteOrder(sBits).ToPositiveBigInteger(); return(R, s); }
public static BitString Encode(EdPoint point, int b) { var encoding = new BitString(point.Y, b); var xBit = new BitString(point.X, b).GetLeastSignificantBits(1); var bytes = new byte[b / 8]; bytes[0] = 1 << 7; if (xBit.Equals(BitString.One())) { encoding = encoding.OR(new BitString(bytes)); } else { encoding = encoding.AND(new BitString(bytes).NOT()); } return(BitString.ReverseByteOrder(encoding)); // switch to little endian }
/// <summary> /// Hashs private key and formats both the prefix (used in signing) and A (used in generating the public key) /// </summary> /// <param name="domainParameters"></param> /// <param name="d"></param> /// <returns></returns> private (BitString Buffer, BitString HDigest2) HashPrivate(EdDomainParameters domainParameters, BitString d) { // 912 is the output length for Ed448 when using SHAKE. It will not affect SHA512 output length for Ed25519. var h = Sha.HashMessage(d, 912).Digest; // Split the hash result in half var hDigest2 = h.Substring(0, domainParameters.CurveE.VariableB); var buffer = BitString.ReverseByteOrder(h.MSBSubstring(0, domainParameters.CurveE.VariableB)); // Prune the buffer for (int i = 0; i < domainParameters.CurveE.VariableC; i++) { buffer.Bits.Set(i, false); } for (int i = domainParameters.CurveE.VariableN; i < buffer.Bits.Length; i++) { buffer.Bits.Set(i, false); } return (buffer, hDigest2); }
public static EdPoint Decode(BitString encoded, BigInteger p, BigInteger a, BigInteger d, int b) { var encodedBits = BitString.ReverseByteOrder(encoded); // switch to big endian var x = encodedBits.GetMostSignificantBits(1).ToPositiveBigInteger(); var YBits = BitString.ConcatenateBits(BitString.Zero(), encodedBits.GetLeastSignificantBits(b - 1)); var Y = YBits.ToPositiveBigInteger(); BigInteger X; var u = ((Y * Y) - 1) % p; var v = ((d * Y * Y) - a) % p; if (p % 4 == 3) { var w = (u * u * u * v * BigInteger.ModPow(BigInteger.ModPow(u, 5, p) * BigInteger.ModPow(v, 3, p) % p, (p - 3) / 4, p)) % p; var vwSquare = (v * ((w * w) % p)) % p; if (vwSquare == u) { X = w; } else { throw new Exception("Square root does not exist"); } } else if (p % 8 == 5) { var w = (u * v * v * v * BigInteger.ModPow(u * BigInteger.ModPow(v, 7, p), (p - 5) / 8, p)) % p; var vwSquare = (v * ((w * w) % p)) % p; if (vwSquare == u) { X = w; } else if (vwSquare == (p - u).PosMod(p)) { X = (w * BigInteger.ModPow(2, (p - 1) / 4, p)) % p; } else { throw new Exception("Square root does not exist"); } } else { // need to use Tonelli-Shanks algorithm in SP800-186 Appendix E throw new NotImplementedException("Need to implement Tonelli-Shanks"); } if (X == 0 && x == 1) { throw new Exception("Point Decode failed"); } if (X % 2 == x) { return(new EdPoint(X, Y)); } else { return(new EdPoint(p - X, Y)); } }
public EdVerificationResult Verify(EdDomainParameters domainParameters, EdKeyPair keyPair, BitString message, EdSignature signature, BitString context, bool preHash = false) { Sha = domainParameters.Hash; // If preHash version, then the message becomes the hash of the message if (preHash) { message = Sha.HashMessage(message, 512).Digest; } // 1. Decode R, s, and Q EdPoint R; BigInteger s; EdPoint Q; try { var sigDecoded = SignatureDecoderHelper.DecodeSig(domainParameters, signature); R = sigDecoded.R; s = sigDecoded.s; Q = domainParameters.CurveE.Decode(keyPair.PublicQ); } catch (Exception e) { return new EdVerificationResult(e.Message); } // 2. Concatenate R || Q || M var hashData = BitString.ConcatenateBits(domainParameters.CurveE.Encode(R), BitString.ConcatenateBits(keyPair.PublicQ, message)); // 3. Compute t // Determine dom. Empty if ed25519. Different for preHash function BitString dom; if (preHash) { dom = domainParameters.CurveE.CurveName == Curve.Ed448 ? Dom4(1, context) : Dom2(1, context); } else { dom = domainParameters.CurveE.CurveName == Curve.Ed448 ? Dom4(0, context) : new BitString(""); } // Compute Hash(dom4 || HashData) var hash = Sha.HashMessage(BitString.ConcatenateBits(dom, hashData), 912).Digest; // Interpret hash as a little endian integer var t = BitString.ReverseByteOrder(hash).ToPositiveBigInteger(); // 4. Check the verification equation [2^c * s]G = [2^c]R + [2^c * t]Q // 2^c var powC = NumberTheory.Pow2(domainParameters.CurveE.VariableC); // [2^c * s]G var lhs = domainParameters.CurveE.Multiply(domainParameters.CurveE.BasePointG, (powC * s).PosMod(domainParameters.CurveE.OrderN)); // [2^c]R var rhs1 = domainParameters.CurveE.Multiply(R, powC); // [2^c * t]Q var rhs2 = domainParameters.CurveE.Multiply(Q, (powC * t).PosMod(domainParameters.CurveE.OrderN)); // [2^c]R + [2^c * t]Q var rhs = domainParameters.CurveE.Add(rhs1, rhs2); if (lhs.Equals(rhs)) { return new EdVerificationResult(); } return new EdVerificationResult("The verification equation is not satisfied. Signature not valid"); }