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 void GeneratePublicKeyFromPrivateWithSomeManipulationEnsureNoLongerValidKeyPair() { var privateKey = new BitString("4C089A9597865D316B5163A01F85458A0B954CD542B9B2D83E39CB3CBA010441"); var expectedPublic = new BitString("EBB9897DF6C5E4E42999578ECA0F48B0985FF99032E80244C4679032F1132A24"); // Gen public key from private var subject = new EdDsa(EntropyProviderTypes.Testable); subject.AddEntropy(privateKey.ToPositiveBigInteger()); var factory = new EdwardsCurveFactory(); var curve = factory.GetCurve(Curve.Ed25519); var domainParams = new EdDomainParameters(curve, new NativeShaFactory()); var keyPair = subject.GenerateKeyPair(domainParams).KeyPair; Assert.AreEqual(expectedPublic, keyPair.PublicQ, "expected public key"); // Check x/y, record var x = new BitString("172B32732D86C9D9D63B11957AAD1364E9D3C1EC258CD13AB012E10648942C4E") .ToPositiveBigInteger(); var y = new BitString("242A13F1329067C44402E83290F95F98B0480FCA8E579929E4E4C5F67D89B9EB") .ToPositiveBigInteger(); // encode public key (done by default, and represented in Q // decode public key var decoded = domainParams.CurveE.Decode(keyPair.PublicQ); // check that the decoded x/y match the previous prior to encode/decode Assert.AreEqual(x, decoded.X, "x decoded matches"); Assert.AreEqual(y, decoded.Y, "y decoded matches"); Assert.IsTrue(subject.ValidateKeyPair(domainParams, keyPair).Success, "original key pair should be valid"); // Modify the public key value until the point is no longer on the curve var modifiedPublicQ = curve.Decode(keyPair.PublicQ); var addedX = modifiedPublicQ.X; var addedY = modifiedPublicQ.Y; var adds = 0; do { modifiedPublicQ = new EdPoint(modifiedPublicQ.X, modifiedPublicQ.Y + 8); addedX = modifiedPublicQ.X + 41; addedY = modifiedPublicQ.Y + 23; keyPair = new EdKeyPair(curve.Encode(modifiedPublicQ), keyPair.PrivateD.GetDeepCopy()); adds++; } while (subject.ValidateKeyPair(domainParams, keyPair).Success); Assert.IsFalse(curve.PointExistsOnCurve(modifiedPublicQ), "check point not on curve prior to encode"); Assert.IsFalse(subject.ValidateKeyPair(domainParams, keyPair).Success, "keypair should not be valid."); }
public void ShouldValidateKeyPairsCorrectlyWithNewMangleLogic(Curve curveEnum, string dHex, string qHex, bool expectedResult) { var d = LoadValue(dHex); var q = LoadValue(qHex); var factory = new EdwardsCurveFactory(); var curve = factory.GetCurve(curveEnum); var domainParams = new EdDomainParameters(curve, new NativeShaFactory()); var keyPair = new EdKeyPair(q, d); var subject = new EdDsa(); var result = subject.ValidateKeyPair(domainParams, keyPair); Assert.AreEqual(expectedResult, result.Success); }
public void ShouldGenerateKeyPairsCorrectly(Curve curveEnum, string dHex, string qHex) { var d = LoadValue(dHex); var q = LoadValue(qHex); var factory = new EdwardsCurveFactory(); var curve = factory.GetCurve(curveEnum); var domainParams = new EdDomainParameters(curve, new NativeShaFactory()); var subject = new EdDsa(EntropyProviderTypes.Testable); subject.AddEntropy(d.ToPositiveBigInteger()); var result = subject.GenerateKeyPair(domainParams); Assert.IsTrue(result.Success); Assert.AreEqual(result.KeyPair.PrivateD, d, "d"); Assert.AreEqual(q, result.KeyPair.PublicQ, "q"); }
public void ShouldGenerateSignaturesCorrectly(Curve curveEnum, string dHex, string qHex, string msgHex, string sigHex) { var d = LoadValue(dHex); var q = LoadValue(qHex); var msg = new BitString(msgHex); var expectedSig = LoadValue(sigHex); var factory = new EdwardsCurveFactory(); var curve = factory.GetCurve(curveEnum); var domainParams = new EdDomainParameters(curve, new NativeShaFactory()); var keyPair = new EdKeyPair(q, d); var subject = new EdDsa(EntropyProviderTypes.Testable); var result = subject.Sign(domainParams, keyPair, msg); Assert.IsTrue(result.Success); Assert.AreEqual(expectedSig, result.Signature.Sig, "sig"); }
public EdKeyPairValidateResult ValidateKeyPair(EdDomainParameters domainParameters, EdKeyPair keyPair) { // If D is out of bounds, reject if (keyPair.PrivateD.ToPositiveBigInteger() < 1 || keyPair.PrivateD.ToPositiveBigInteger() > NumberTheory.Pow2(domainParameters.CurveE.VariableB) - 1) { return new EdKeyPairValidateResult("D must be able to be a b-bit string"); } EdPoint Q; try { Q = domainParameters.CurveE.Decode(keyPair.PublicQ); } catch (Exception e) { return new EdKeyPairValidateResult(e.Message); } // If Q == (0, 1), invalid if (Q.Equals(new EdPoint(0, 1))) { return new EdKeyPairValidateResult("Q cannot be neutral element"); } // If Q is not a valid point on the specific curve, invalid // could make this more efficient if (!domainParameters.CurveE.PointExistsOnCurve(Q)) { return new EdKeyPairValidateResult("Q does not lie on the curve"); } // If n * Q == 0, valid // This is fast because the scalar (n) is taken modulo n... so it's 0 if (domainParameters.CurveE.Multiply(Q, domainParameters.CurveE.OrderN).Equals(new EdPoint(0, 1))) { return new EdKeyPairValidateResult(); } // Otherwise invalid return new EdKeyPairValidateResult("n * Q must equal (0, 1)"); }
public void ShouldValidateSignaturesCorrectly(Curve curveEnum, string dHex, string qHex, string msgHex, string sigHex, bool expectedResult) { var d = LoadValue(dHex); var q = LoadValue(qHex); var msg = new BitString(msgHex); var expectedSig = LoadValue(sigHex); var factory = new EdwardsCurveFactory(); var curve = factory.GetCurve(curveEnum); var domainParams = new EdDomainParameters(curve, new NativeShaFactory()); var keyPair = new EdKeyPair(q); var signature = new EdSignature(expectedSig); var subject = new EdDsa(); var result = subject.Verify(domainParams, keyPair, msg, signature); Assert.AreEqual(expectedResult, result.Success); }
/// <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 EdKeyPairGenerateResult GenerateKeyPair(EdDomainParameters domainParameters) { Sha = domainParameters.Hash; // Generate random number d var d = _entropyProvider.GetEntropy(1, NumberTheory.Pow2(domainParameters.CurveE.VariableB) - 1); // 1. Hash the private key // 2. Prune the buffer // Both accomplished by this function var h = HashPrivate(domainParameters, new BitString(d, domainParameters.CurveE.VariableB)).Buffer; // 3. Determine s var s = NumberTheory.Pow2(domainParameters.CurveE.VariableN) + h.ToPositiveBigInteger(); // 4. Compute Q such that Q = s * G var Q = domainParameters.CurveE.Multiply(domainParameters.CurveE.BasePointG, s); // Encode Q var qEncoded = EdPointEncoder.Encode(Q, domainParameters.CurveE.VariableB); // Return key pair (Q, d) return new EdKeyPairGenerateResult(new EdKeyPair(qEncoded, new BitString(d, domainParameters.CurveE.VariableB))); }
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"); }
public EdVerificationResult Verify(EdDomainParameters domainParameters, EdKeyPair keyPair, BitString message, EdSignature signature, bool preHash = false) { return Verify(domainParameters, keyPair, message, signature, null, preHash); }
public EdSignatureResult Sign(EdDomainParameters domainParameters, EdKeyPair keyPair, BitString message, bool preHash = false) { return Sign(domainParameters, keyPair, message, null, preHash); }