/// <summary> /// Signs given data and returns the r and s components of the ECDSA signature, along with a recovery ID to recover the public key given the original signed message and the returned components. /// </summary> /// <param name="hash">The hash to be signed.</param> /// <returns>Returns r and s components of an ECDSA signature, along with a recovery ID to recover the signers public key given the original signed message and r, s.</returns> public override (byte RecoveryID, BigInteger r, BigInteger s) SignData(Span <byte> hash) { // Verify we have a private key. if (KeyType != EthereumEcdsaKeyType.Private) { throw _notPrivateKeyException; } // Initialize our crypto provider. ECDsaSigner signer = new ECDsaSigner(); signer.Init(true, PrivateKey); // Obtain our signature, rs[0] ("r") and rs[1] ("s") Org.BouncyCastle.Math.BigInteger[] rs = signer.GenerateSignature(hash.ToArray()); // We want to make sure we enforce a low S value rs[1] = Secp256k1Curve.EnforceLowS(rs[1]); // We need to return a valid recovery ID for this signature. We do this by trying all of our 4 possible recovery IDs to make sure the public key hash recovered is the same as ours. // We start by obtaining our current public key hash byte[] actualPublicKeyHash = GetPublicKeyHash(); // Next we try of our potential recovery IDs until we can obtain the matching public key from the signature. // 2, 3 are usually not used and 0, 1 denote odd or even Y, which can be figured out. for (byte recoveryID = 0; recoveryID < 4; recoveryID++) { // We wrap this in a try in case, as we know one of these IDs will work. try { EthereumEcdsa possibleMatch = Recover(hash, recoveryID, rs[0].ToNumericsBigInteger(), rs[1].ToNumericsBigInteger()); if (actualPublicKeyHash.ValuesEqual(possibleMatch.GetPublicKeyHash())) { return(recoveryID, rs[0].ToNumericsBigInteger(), rs[1].ToNumericsBigInteger());
static EthereumEcdsaBouncyCastle GenerateSingle(uint accountIndex, IAccountDerivation accountFactory) { var privateKey = accountFactory.GeneratePrivateKey(accountIndex); var keyBigInt = BigIntegerConverter.GetBigInteger(privateKey, signed: false, byteCount: PRIVATE_KEY_SIZE); keyBigInt = Secp256k1Curve.EnforceLowS(keyBigInt); // Return our private key instance. return(new EthereumEcdsaBouncyCastle(privateKey, EthereumEcdsaKeyType.Private)); }
static EthereumEcdsaNative Generate(uint accountIndex, Secp256k1 secp256k1, IAccountDerivation accountFactory) { var privateKey = accountFactory.GeneratePrivateKey(accountIndex); if (!secp256k1.SecretKeyVerify(privateKey)) { var errMsg = "Unmanaged EC library failed to valid private key. "; if (IncludeKeyDataInExceptions) { errMsg += $"Private key: {privateKey.ToHexString()}"; } throw new Exception(errMsg); } var keyBigInt = BigIntegerConverter.GetBigInteger(privateKey, signed: false, byteCount: PRIVATE_KEY_SIZE); keyBigInt = Secp256k1Curve.EnforceLowS(keyBigInt); privateKey = BigIntegerConverter.GetBytes(keyBigInt, PRIVATE_KEY_SIZE); return(new EthereumEcdsaNative(privateKey, EthereumEcdsaKeyType.Private)); }