/// <summary> /// Recovers the ephemeral key used to sign the transformed nonce in the authentication data. /// Throws an exception if the recovered key is invalid, or could not be recovered. /// </summary> /// <param name="receiverPrivateKey">The private key of the receiver used to generate the shared secret.</param> /// <returns>Returns the remote ephemeral public key for the keypair which signed this authentication data.</returns> public virtual (EthereumEcdsa remoteEphemeralPublicKey, uint?chainId) RecoverDataFromSignature(EthereumEcdsa receiverPrivateKey) { // Create an EC provider with the given public key. EthereumEcdsa publicKey = EthereumEcdsa.Create(PublicKey, EthereumEcdsaKeyType.Public); // Generate the shared secret using ECDH between our local private key and this remote public key byte[] ecdhKey = receiverPrivateKey.ComputeECDHKey(publicKey); // Obtain our transformed nonce data. byte[] transformedNonceData = GetTransformedNonce(ecdhKey); // We want our signature in r,s,v format. BigInteger ecdsa_r = BigIntegerConverter.GetBigInteger(R, false, 32); BigInteger ecdsa_s = BigIntegerConverter.GetBigInteger(S, false, 32); (byte recoveryId, uint?chainId) = EthereumEcdsa.GetRecoveryAndChainIDFromV(V); // Recover the public key from the data provided. EthereumEcdsa remoteEphemeralPublickey = EthereumEcdsa.Recover(transformedNonceData, recoveryId, ecdsa_r, ecdsa_s); // Verify the key is valid // Return the ephemeral key return(remoteEphemeralPublickey, chainId); }
// Precompiles Below /// <summary> /// A precompiled contract which uses v,r,s + hash obtained from the message data to perform elliptic curve public key recovery to obtain a senders address. /// </summary> /// <param name="evm">The Ethereum Virtual Machine instance we are executing inside of.</param> private static EVMExecutionResult Precompile_ECRecover(MeadowEVM evm) { // Charge the gas for the precompile operation before processing. evm.GasState.Deduct(GasDefinitions.GAS_PRECOMPILE_ECRECOVER); // Obtain a memory representation of our data. Span <byte> messageData = new Span <byte>(evm.Message.Data); // We extract our signature information from message data (256-bit each) Span <byte> hash = messageData.Slice(0, EVMDefinitions.WORD_SIZE); BigInteger v = BigIntegerConverter.GetBigInteger(messageData.Slice(32, EVMDefinitions.WORD_SIZE)); BigInteger r = BigIntegerConverter.GetBigInteger(messageData.Slice(64, EVMDefinitions.WORD_SIZE)); BigInteger s = BigIntegerConverter.GetBigInteger(messageData.Slice(96, EVMDefinitions.WORD_SIZE)); // Verify we have a low r, s, and a valid v. if (r >= Secp256k1Curve.N || s >= Secp256k1Curve.N || v < 27 || v > 28) { // We failed v,r,s verification, so we stop executing. return(new EVMExecutionResult(evm, null, true)); } // Obtain our recovery id from v. byte recoveryID = EthereumEcdsa.GetRecoveryAndChainIDFromV((byte)v).recoveryId; // Try to get an address from this. If it fails, it will throw an exception. byte[] senderAddress = null; try { senderAddress = EthereumEcdsa.Recover(hash, recoveryID, r, s).GetPublicKeyHash(); } catch { // Recovery failed, so we stop executing. return(new EVMExecutionResult(evm, null, true)); } // The address portion is at the end, and we zero out the leading portion. for (int i = 0; i < senderAddress.Length - Address.ADDRESS_SIZE; i++) { senderAddress[i] = 0; } // Return the sender address return(new EVMExecutionResult(evm, senderAddress, true)); }
public void SignAndVerify(bool useBouncyCastle) { // Generate ECDSA keypair, compute a hash, sign it, then recover the public key from the signature and verify it matches. EthereumEcdsa provider; if (useBouncyCastle) { provider = EthereumEcdsaBouncyCastle.Generate(new SystemRandomAccountDerivation()); } else { provider = EthereumEcdsaNative.Generate(new SystemRandomAccountDerivation()); } byte[] hash = KeccakHash.ComputeHashBytes(new byte[] { 11, 22, 33, 44 }); (byte RecoveryID, BigInteger r, BigInteger s)signature = provider.SignData(hash); EthereumEcdsa recovered = EthereumEcdsa.Recover(hash, signature.RecoveryID, signature.r, signature.s); Assert.True(provider.GetPublicKeyHash().ValuesEqual(recovered.GetPublicKeyHash())); }