// Type handling public static RLPByteArray FromInteger(BigInteger bigInteger, int byteCount = 32, bool removeLeadingZeros = false) { if (!removeLeadingZeros) { return(new RLPByteArray(BigIntegerConverter.GetBytes(bigInteger, byteCount))); } else { return(new RLPByteArray(BigIntegerConverter.GetBytesWithoutLeadingZeros(bigInteger, byteCount))); } }
public async Task ModExpTest() { // Test the modexp precompile. var gas = await _precompiles.testModExp( BigIntegerConverter.GetBytes(BigInteger.Parse("1212121323543453245345678346345737475734753745737774573475377734577", CultureInfo.InvariantCulture)), BigIntegerConverter.GetBytes(BigInteger.Parse("3", CultureInfo.InvariantCulture)), BigIntegerConverter.GetBytes(BigInteger.Parse("4345328123928357434573234217343477", CultureInfo.InvariantCulture))) .EstimateGas(); Assert.AreEqual(29900, gas); }
public async Task EcRecoverTest() { var ecRecoverTest = await _contract.testECRecover( new byte[] { 0xc9, 0xf1, 0xc7, 0x66, 0x85, 0x84, 0x5e, 0xa8, 0x1c, 0xac, 0x99, 0x25, 0xa7, 0x56, 0x58, 0x87, 0xb7, 0x77, 0x1b, 0x34, 0xb3, 0x5e, 0x64, 0x1c, 0xca, 0x85, 0xdb, 0x9f, 0xef, 0xd0, 0xe7, 0x1f }, 0x1c, BigIntegerConverter.GetBytes(BigInteger.Parse("68932463183462156574914988273446447389145511361487771160486080715355143414637", CultureInfo.InvariantCulture)), BigIntegerConverter.GetBytes(BigInteger.Parse("47416572686988136438359045243120473513988610648720291068939984598262749281683", CultureInfo.InvariantCulture))) .Call(); Assert.AreEqual("0x75c8aa4b12bc52c1f1860bc4e8af981d6542cccd", ecRecoverTest.GetHexString(hexPrefix: true)); }
public async Task EcRecoverTest() { var gas = await _precompiles.testECRecover( new byte[] { 0xc9, 0xf1, 0xc7, 0x66, 0x85, 0x84, 0x5e, 0xa8, 0x1c, 0xac, 0x99, 0x25, 0xa7, 0x56, 0x58, 0x87, 0xb7, 0x77, 0x1b, 0x34, 0xb3, 0x5e, 0x64, 0x1c, 0xca, 0x85, 0xdb, 0x9f, 0xef, 0xd0, 0xe7, 0x1f }, 0x1c, BigIntegerConverter.GetBytes(BigInteger.Parse("68932463183462156574914988273446447389145511361487771160486080715355143414637", CultureInfo.InvariantCulture)), BigIntegerConverter.GetBytes(BigInteger.Parse("47416572686988136438359045243120473513988610648720291068939984598262749281683", CultureInfo.InvariantCulture))) .EstimateGas(); Assert.AreEqual(32437, gas); }
public void Push(BigInteger obj) { // Verify we aren't reaching our maximum stack size. if (Count >= MAX_STACK_SIZE) { throw new EVMException($"Stack has overflowed past the maximum size of {MAX_STACK_SIZE} entries."); } // Push the object to the top of the stack. _internalStack.Add(BigIntegerConverter.GetBytes(obj)); }
private static EVMExecutionResult Precompile_ECAdd(MeadowEVM evm) { // Verify we're past the byzantium fork if (evm.Version < EthereumRelease.Byzantium) { return(new EVMExecutionResult(evm, null, true)); } // Charge the gas for the precompile operation before processing. BigInteger gasCharge = GasDefinitions.GAS_PRECOMPILE_ECADD_BASE; evm.GasState.Deduct(gasCharge); // Obtain a memory representation of our data. Span <byte> messageData = new Span <byte>(evm.Message.Data); // Obtain our component data BigInteger x1 = BigIntegerConverter.GetBigInteger(messageData.Slice(0, EVMDefinitions.WORD_SIZE)); BigInteger y1 = BigIntegerConverter.GetBigInteger(messageData.Slice(32, EVMDefinitions.WORD_SIZE)); BigInteger x2 = BigIntegerConverter.GetBigInteger(messageData.Slice(64, EVMDefinitions.WORD_SIZE)); BigInteger y2 = BigIntegerConverter.GetBigInteger(messageData.Slice(96, EVMDefinitions.WORD_SIZE)); // Parse and verify our points. FpVector3 <Fp> point1 = ParsePoint(x1, y1); if (point1 == null) { throw new EVMException("ECAdd precompile failed because point 1 was deemed invalid when parsing."); } FpVector3 <Fp> point2 = ParsePoint(x2, y2); if (point1 == null) { throw new EVMException("ECAdd precompile failed because point 2 was deemed invalid when parsing."); } // Add the two points together FpVector3 <Fp> additionResult = point1.Add(point2); // Normalize the result into X/Y components. (Fp resultX, Fp resultY) = additionResult.Normalize(); // Obtain the binary data for these results byte[] resultXData = BigIntegerConverter.GetBytes(resultX.N, EVMDefinitions.WORD_SIZE); byte[] resultYData = BigIntegerConverter.GetBytes(resultY.N, EVMDefinitions.WORD_SIZE); // Concat them to a singular result byte[] returnData = resultXData.Concat(resultYData); // Return our result return(new EVMExecutionResult(evm, returnData, true)); }
public void LeadingBytesNegative() { BigInteger bigInt = -1; byte[] result = BigIntegerConverter.GetBytes(bigInt); for (int i = 0; i < result.Length; i++) { Assert.Equal(0xFF, result[i]); } Assert.Equal(0x20, result.Length); }
public async Task ModExpTest() { // Test the modexp precompile. var modExpTestBytes = await _contract.testModExp( BigIntegerConverter.GetBytes(BigInteger.Parse("1212121323543453245345678346345737475734753745737774573475377734577", CultureInfo.InvariantCulture)), BigIntegerConverter.GetBytes(BigInteger.Parse("3", CultureInfo.InvariantCulture)), BigIntegerConverter.GetBytes(BigInteger.Parse("4345328123928357434573234217343477", CultureInfo.InvariantCulture))) .Call(); // Convert the result into an integer. var modExpTest = BigIntegerConverter.GetBigInteger(modExpTestBytes, false, modExpTestBytes.Length); Assert.AreEqual("856753145937825219130387866259147", modExpTest.ToString(CultureInfo.InvariantCulture)); }
public void ValuesUnchanged() { Random random = new Random(); for (int i = 0; i < 100; i++) { byte[] data = new byte[random.Next(1, 0x20 + 1)]; random.NextBytes(data); BigInteger bigInteger = BigIntegerConverter.GetBigInteger(data); byte[] parsed = BigIntegerConverter.GetBytes(bigInteger); for (int x = 0; x < Math.Min(data.Length, parsed.Length); x++) { Assert.Equal(data[data.Length - x - 1], parsed[parsed.Length - x - 1]); } } }
/// <summary> /// Generates a bloom filter for a given item with a given byte count. /// </summary> /// <param name="item">The item to generate a bloom filter for.</param> /// <param name="byteCount">The amount of bytes that the item is made up of.</param> /// <returns>Returns the bloom filter generated for this item.</returns> public static BigInteger Generate(BigInteger item, int byteCount = EVMDefinitions.WORD_SIZE) { // Compute our hash for this item. byte[] hash = KeccakHash.ComputeHashBytes(BigIntegerConverter.GetBytes(item, byteCount)); // Create our resulting bloom filter BigInteger bloomFilter = 0; // Out of the hash, for every 16-bit bit chunk we should cycle through, we use the 11-bits of the 16-bit integer as a bit index to set in our filter. for (int i = 0; i < BLOOM_CHUNK_COUNT * 2; i += 2) { int bitIndex = ((hash[i] << 8) | (hash[i + 1])) & 0x7FF; // mask into an 11-bit integer. bloomFilter |= ((BigInteger)1 << bitIndex); // set the bit at that index } return(bloomFilter); }
public async Task TestPrecompiles() { // Grab a list of accounts var accounts = await Client.Accounts(); // Deploy our test contract var contract = await BasicContract.New($"TestName", true, 34, Client, new TransactionParams { From = accounts[0], Gas = 4712388 }, accounts[0]); // 1) This ECRecover test should yield 0x75c8aa4b12bc52c1f1860bc4e8af981d6542cccd. This test data is taken from SigningTests.cs var ecRecoverTest = await contract.testECRecover( new byte[] { 0xc9, 0xf1, 0xc7, 0x66, 0x85, 0x84, 0x5e, 0xa8, 0x1c, 0xac, 0x99, 0x25, 0xa7, 0x56, 0x58, 0x87, 0xb7, 0x77, 0x1b, 0x34, 0xb3, 0x5e, 0x64, 0x1c, 0xca, 0x85, 0xdb, 0x9f, 0xef, 0xd0, 0xe7, 0x1f }, 0x1c, BigIntegerConverter.GetBytes(BigInteger.Parse("68932463183462156574914988273446447389145511361487771160486080715355143414637", CultureInfo.InvariantCulture)), BigIntegerConverter.GetBytes(BigInteger.Parse("47416572686988136438359045243120473513988610648720291068939984598262749281683", CultureInfo.InvariantCulture))) .Call(); Assert.Equal("0x75c8aa4b12bc52c1f1860bc4e8af981d6542cccd", ecRecoverTest); // Precompile hash tests // 2) SHA-256 var sha256HashTest = await contract.sha256str("hello world").Call(); // should be 0xb94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9 Assert.Equal("0xb94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9", sha256HashTest.ToHexString(hexPrefix: true)); // 3) RIPEMD160 var ripemd160HashTest = await contract.ripemd160str("hello world").Call(); // should be 0x98c615784ccb5fe5936fbc0cbe9dfdb408d92f0f Assert.Equal("0x98c615784ccb5fe5936fbc0cbe9dfdb408d92f0f", ripemd160HashTest.ToHexString(hexPrefix: true)); // 4) IDENTITY + 5) MODEXP (this function uses both) var modExpTestBytes = await contract.testModExp( BigIntegerConverter.GetBytes(BigInteger.Parse("1212121323543453245345678346345737475734753745737774573475377734577", CultureInfo.InvariantCulture)), BigIntegerConverter.GetBytes(BigInteger.Parse("3", CultureInfo.InvariantCulture)), BigIntegerConverter.GetBytes(BigInteger.Parse("4345328123928357434573234217343477", CultureInfo.InvariantCulture))) .Call(); var modExpTest = BigIntegerConverter.GetBigInteger(modExpTestBytes, false, modExpTestBytes.Length); // should be 0x856753145937825219130387866259147 Assert.Equal(BigInteger.Parse("856753145937825219130387866259147", CultureInfo.InvariantCulture), modExpTest); // TODO: 6) Checking a pairing equation on bn128 curve // TODO: 7) Addition of bn128 curve // TODO: 8) Scalar multiplication of bn128 curve. }
/// <summary> /// Verifies a hash was signed correctly given the r and s signature components. /// </summary> /// <param name="hash">The hash which was signed.</param> /// <param name="r">The ECDSA signature component r.</param> /// <param name="s">The ECDSA signature component s.</param> /// <returns>Returns a boolean indicating whether the data was properly signed.</returns> public override bool VerifyData(Span <byte> hash, BigInteger r, BigInteger s) { // Obtain our secp256k1 instance. using (AutoObjectPool <Secp256k1> .Get(out var secp256k1)) { // Allocate memory for the signature and call our sign function. Span <byte> signature = BigIntegerConverter.GetBytes(r).Concat(BigIntegerConverter.GetBytes(s)); Span <byte> signatureParsed = new byte[SIGNATURE_RSV_SIZE]; // The verify method requires a parsed signature. The serialized signature we provide // should have recovery ID, but we can provide anything, since verifying signature // ignores the recovery ID, and only focuses on r + s components. if (!secp256k1.RecoverableSignatureParseCompact(signatureParsed, signature, 0)) { var errMsg = "Unmanaged EC library failed to parse the signature when verifying signature. "; if (IncludeKeyDataInExceptions) { errMsg += $"MessageHash: {hash.ToHexString()}, Key: {UnmanagedKey.Span.ToHexString()}"; } throw new Exception(errMsg); } // Define the parsed public key array Span <byte> parsedPublicKeyData = new byte[PUBLIC_KEY_SIZE]; // Get a serialized public key, then parse it. byte[] serializedPublicKey = ToPublicKeyArray(false, false); if (!secp256k1.PublicKeyParse(parsedPublicKeyData, serializedPublicKey)) { var errMsg = "Unmanaged EC library failed to parse public key when verifying signature. "; if (IncludeKeyDataInExceptions) { errMsg += $"MessageHash: {hash.ToHexString()}, Key: {UnmanagedKey.Span.ToHexString()}"; } throw new Exception(errMsg); } // Verify the signature using the parsed public key. return(secp256k1.Verify(signatureParsed, hash, parsedPublicKeyData)); } }
public void LeadingBytesPositive() { BigInteger bigInt = 1; byte[] result = BigIntegerConverter.GetBytes(bigInt); for (int i = 0; i < result.Length; i++) { if (i == result.Length - 1) { Assert.Equal(0x01, result[i]); } else { Assert.Equal(0x00, result[i]); } } Assert.Equal(0x20, result.Length); }
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)); }
public void Push(BigInteger obj, uint byteCount) { // Obtain the bytes for this BigInteger. byte[] data = BigIntegerConverter.GetBytes(obj); // If the byte count is larger than the data, something is wrong. if (byteCount > data.Length) { throw new ArgumentException("Tried to push a value onto the stack that was larger than 256-bit. This should not have happened."); } // Determine how many bytes we're going to zero out to keep our desired bytes only. int relevantBytesStart = data.Length - (int)byteCount; for (int i = 0; i < relevantBytesStart; i++) { data[i] = 0x00; } // Push this data. _internalStack.Add(data); }
public static Org.BouncyCastle.Math.BigInteger ToBouncyCastleBigInteger(this BigInteger bigInteger) { return(new Org.BouncyCastle.Math.BigInteger(1, BigIntegerConverter.GetBytes(bigInteger))); }
/// <summary> /// Creates an ECDSA instance by recovering a public key given a hash, recovery ID, and r and s components of the resulting signature of the hash. Throws an exception if recovery is not possible. /// </summary> /// <param name="hash">The hash of the data which was signed.</param> /// <param name="recoveryId">The recovery ID of ECDSA during signing.</param> /// <param name="ecdsa_r">The r component of the ECDSA signature for the provided hash.</param> /// <param name="ecdsa_s">The s component of the ECDSA signature for the provided hash.</param> /// <returns>Returns the quotient/public key which was used to sign this hash.</returns> public static new EthereumEcdsaNative Recover(Span <byte> hash, byte recoveryId, BigInteger ecdsa_r, BigInteger ecdsa_s) { // Source: http://www.secg.org/sec1-v2.pdf (Section 4.1.6 - Public Key Recovery Operation) // Recovery ID must be between 0 and 4 (0 and 1 is all that should be used, but we support multiple cases in case) if (recoveryId < 0 || recoveryId > 3) { throw new ArgumentException($"ECDSA public key recovery must have a v parameter between [0, 3]. Value provided is {recoveryId.ToString(CultureInfo.InvariantCulture)}"); } // NOTES: // First bit of recoveryID being set means y is odd, otherwise it is even. // The second bit indicates which item of the two to choose. // If the hash is null, we'll assume it's a zero length byte array if (hash == null) { hash = Array.Empty <byte>(); } using (AutoObjectPool <Secp256k1> .Get(out var secp256k1)) { // Allocate memory for the signature and create a serialized-format signature to deserialize into our native format (platform dependent, hence why we do this). Span <byte> publicKeyOutput = new byte[PUBLIC_KEY_SIZE]; Span <byte> serializedSignature = BigIntegerConverter.GetBytes(ecdsa_r).Concat(BigIntegerConverter.GetBytes(ecdsa_s)); Span <byte> deserializedSignature = new byte[Secp256k1.UNSERIALIZED_SIGNATURE_SIZE]; if (!secp256k1.RecoverableSignatureParseCompact(deserializedSignature, serializedSignature, recoveryId)) { var errMsg = "Unmanaged EC library failed to parse serialized signature. "; if (IncludeKeyDataInExceptions) { errMsg += $"CompactSignature: {serializedSignature.ToHexString()}, RecoveryID: {recoveryId}"; } throw new Exception(errMsg); } // Recovery from our deserialized signature. if (!secp256k1.Recover(publicKeyOutput, deserializedSignature, hash)) { var errMsg = "Unmanaged EC library failed to recover public key. "; if (IncludeKeyDataInExceptions) { errMsg += $"Signature: {deserializedSignature.ToHexString()}, Message: {hash.ToHexString()}"; } throw new Exception(errMsg); } // Serialize the public key Span <byte> serializedKey = new byte[Secp256k1.SERIALIZED_UNCOMPRESSED_PUBKEY_LENGTH]; if (!secp256k1.PublicKeySerialize(serializedKey, publicKeyOutput)) { var errMsg = "Unmanaged EC library failed to serialize public key. "; if (IncludeKeyDataInExceptions) { errMsg += $"PublicKey: {publicKeyOutput.ToHexString()}"; } throw new Exception(errMsg); } // Slice off any prefix. serializedKey = serializedKey.Slice(serializedKey.Length - PUBLIC_KEY_SIZE); // Obtain our public key from this return(new EthereumEcdsaNative(serializedKey.ToArray(), EthereumEcdsaKeyType.Public)); } }
/// <summary> /// Writes a 256-bit integer to virtual memory at the provided address. /// </summary> /// <param name="address">The address to write to.</param> /// <param name="bigInteger">The 256-bit integer to write.</param> public void Write(long address, BigInteger bigInteger) { Write(address, BigIntegerConverter.GetBytes(bigInteger)); }
/// <summary> /// Obtains the parent private key if provided the parent public key. This key must be a private key. /// </summary> /// <param name="parentPublicKey">The public key of the parent of this extended key.</param> /// <returns>Returns the private key of the parent of this extended key.</returns> public ExtendedKey GetParentPrivateKey(ExtendedKey parentPublicKey) { // Verify this key is a private key if (KeyType != EthereumEcdsaKeyType.Private) { // This key is not a private key. throw new ArgumentNullException("Could not obtain parent private key. Can only obtain the parent private key of a private key. This key is a public key."); } else if (parentPublicKey == null) { // The public key is null. throw new ArgumentNullException("Could not obtain parent private key. Provided parent public key argument is null."); } else if (parentPublicKey.KeyType == EthereumEcdsaKeyType.Private) { // The public key was not a public key. throw new ArgumentException("Could not obtain parent private key. Provided parent public key argument is not a public key."); } else if (Hardened) { throw new ArgumentException("Could not obtain parent private key if this key is a hardened key."); } else if (Depth == 0) { throw new ArgumentException("Could not obtain parent private key for this key because this key is a top level key."); } else if (!parentPublicKey.IsChild(this)) { // The provided parent public key is not a parent. throw new ArgumentException("Could not obtain parent private key for this key because the provided parent public key argument is not a parent to this key."); } // Obtain the hash used to derive this current key, from the parent. byte[] hash = parentPublicKey.ComputeChildHash(ChildIndex, parentPublicKey.InternalKey.ToPublicKeyArray(true, false)); // Set the child key data as the first 32 bytes of "hash" byte[] childKeyData = new byte[EthereumEcdsa.PRIVATE_KEY_SIZE]; Array.Copy(hash, 0, childKeyData, 0, childKeyData.Length); // Initialize the child chain code byte[] childChainCode = new byte[CHAIN_CODE_SIZE]; // We derive the child chain code as the data immediately following key data. Array.Copy(hash, 32, childChainCode, 0, childChainCode.Length); // Verify the chain code is equal if (!ChainCode.SequenceEqual(childChainCode)) { throw new ArgumentException("Derived chain code from the parent at this key's child index that did not match this key's chain code."); } // Convert the key data to an integer BigInteger childKeyInt = BigIntegerConverter.GetBigInteger(childKeyData, false, childChainCode.Length); // Convert this private key to an integer byte[] thisKeyData = InternalKey.ToPrivateKeyArray(); BigInteger thisKeyInt = BigIntegerConverter.GetBigInteger(thisKeyData, false, thisKeyData.Length); // Compute our parent key BigInteger parentPrivateKeyInt = ((thisKeyInt - childKeyInt) + Secp256k1Curve.N) % Secp256k1Curve.N; // Obtain our new key from this byte[] computedParentKeyData = BigIntegerConverter.GetBytes(parentPrivateKeyInt, EthereumEcdsa.PRIVATE_KEY_SIZE); // Obtain the parent private key EthereumEcdsa parentPrivateKey = EthereumEcdsa.Create(computedParentKeyData, EthereumEcdsaKeyType.Private); // Create the parent extended private key return(new ExtendedKey(parentPrivateKey, parentPublicKey.ChainCode, parentPublicKey.Depth, parentPublicKey.ChildIndex, parentPublicKey.Fingerprint)); }
public void SigningTest() { using (var secp256k1 = new Secp256k1()) { Span <byte> signature = new byte[Secp256k1.UNSERIALIZED_SIGNATURE_SIZE]; Span <byte> messageHash = new byte[] { 0xc9, 0xf1, 0xc7, 0x66, 0x85, 0x84, 0x5e, 0xa8, 0x1c, 0xac, 0x99, 0x25, 0xa7, 0x56, 0x58, 0x87, 0xb7, 0x77, 0x1b, 0x34, 0xb3, 0x5e, 0x64, 0x1c, 0xca, 0x85, 0xdb, 0x9f, 0xef, 0xd0, 0xe7, 0x1f }; Span <byte> secretKey = "e815acba8fcf085a0b4141060c13b8017a08da37f2eb1d6a5416adbb621560ef".HexToBytes(); bool result = secp256k1.SignRecoverable(signature, messageHash, secretKey); Assert.True(result); // Recover the public key Span <byte> publicKeyOutput = new byte[Secp256k1.PUBKEY_LENGTH]; result = secp256k1.Recover(publicKeyOutput, signature, messageHash); Assert.True(result); // Serialize the public key Span <byte> serializedKey = new byte[Secp256k1.SERIALIZED_UNCOMPRESSED_PUBKEY_LENGTH]; result = secp256k1.PublicKeySerialize(serializedKey, publicKeyOutput); Assert.True(result); // Slice off any prefix. serializedKey = serializedKey.Slice(serializedKey.Length - Secp256k1.PUBKEY_LENGTH); Assert.Equal("0x3a2361270fb1bdd220a2fa0f187cc6f85079043a56fb6a968dfad7d7032b07b01213e80ecd4fb41f1500f94698b1117bc9f3335bde5efbb1330271afc6e85e92", serializedKey.ToHexString(true), true); // Verify we could obtain the correct sender from the signature. Span <byte> senderAddress = KeccakHash.ComputeHash(serializedKey).Slice(KeccakHash.HASH_SIZE - Address.ADDRESS_SIZE); Assert.Equal("0x75c8aa4b12bc52c1f1860bc4e8af981d6542cccd", senderAddress.ToArray().ToHexString(true), true); // Verify it works with variables generated from our managed code. BigInteger ecdsa_r = BigInteger.Parse("68932463183462156574914988273446447389145511361487771160486080715355143414637", CultureInfo.InvariantCulture); BigInteger ecdsa_s = BigInteger.Parse("47416572686988136438359045243120473513988610648720291068939984598262749281683", CultureInfo.InvariantCulture); byte recoveryId = 1; byte[] ecdsa_r_bytes = BigIntegerConverter.GetBytes(ecdsa_r); byte[] ecdsa_s_bytes = BigIntegerConverter.GetBytes(ecdsa_s); signature = ecdsa_r_bytes.Concat(ecdsa_s_bytes); // Allocate memory for the signature and create a serialized-format signature to deserialize into our native format (platform dependent, hence why we do this). Span <byte> serializedSignature = ecdsa_r_bytes.Concat(ecdsa_s_bytes); signature = new byte[Secp256k1.UNSERIALIZED_SIGNATURE_SIZE]; result = secp256k1.RecoverableSignatureParseCompact(signature, serializedSignature, recoveryId); if (!result) { throw new Exception("Unmanaged EC library failed to parse serialized signature."); } // Recover the public key publicKeyOutput = new byte[Secp256k1.PUBKEY_LENGTH]; result = secp256k1.Recover(publicKeyOutput, signature, messageHash); Assert.True(result); // Serialize the public key serializedKey = new byte[Secp256k1.SERIALIZED_UNCOMPRESSED_PUBKEY_LENGTH]; result = secp256k1.PublicKeySerialize(serializedKey, publicKeyOutput); Assert.True(result); // Slice off any prefix. serializedKey = serializedKey.Slice(serializedKey.Length - Secp256k1.PUBKEY_LENGTH); // Assert our key Assert.Equal("0x3a2361270fb1bdd220a2fa0f187cc6f85079043a56fb6a968dfad7d7032b07b01213e80ecd4fb41f1500f94698b1117bc9f3335bde5efbb1330271afc6e85e92", serializedKey.ToHexString(true), true); //senderAddress = EthereumEcdsa.Recover(messageHash.ToArray(), recoveryId, ecdsa_r, ecdsa_s).GetPublicKeyHash(); //senderAddress = senderAddress.Slice(KeccakHash.HASH_SIZE - Address.ADDRESS_SIZE); //Assert.Equal("0x75c8aa4b12bc52c1f1860bc4e8af981d6542cccd", senderAddress.ToArray().ToHexString(true), true); } }
private static EVMExecutionResult Precompile_ECPairing(MeadowEVM evm) { // Verify we're past the byzantium fork if (evm.Version < EthereumRelease.Byzantium) { return(new EVMExecutionResult(evm, null, true)); } const int PAIRING_SIZE_PER_POINT = 192; // Verify our messatge data is divisible by our size per point. if (evm.Message.Data.Length % PAIRING_SIZE_PER_POINT != 0) { throw new EVMException($"ECPairing precompile failed because the call data was not divisible by the size per point ({PAIRING_SIZE_PER_POINT})."); } // Charge the gas for the precompile operation before processing. BigInteger gasCharge = GasDefinitions.GAS_PRECOMPILE_ECPAIRING_BASE + ((evm.Message.Data.Length / PAIRING_SIZE_PER_POINT) * GasDefinitions.GAS_PRECOMPILE_ECPAIRING_PER_POINT); evm.GasState.Deduct(gasCharge); // Obtain a memory representation of our data. Span <byte> messageData = new Span <byte>(evm.Message.Data); // Define some constant variables for arithmetic. FpVector3 <Fp2> zero = new FpVector3 <Fp2>(Fp2.OneValue, Fp2.OneValue, Fp2.ZeroValue); Fp12 exponent = Fp12.OneValue; // Loop for each point in our data for (int i = 0; i < evm.Message.Data.Length; i += PAIRING_SIZE_PER_POINT) { // Obtain our component data BigInteger x1 = BigIntegerConverter.GetBigInteger(messageData.Slice(i + 0, EVMDefinitions.WORD_SIZE)); BigInteger y1 = BigIntegerConverter.GetBigInteger(messageData.Slice(i + 32, EVMDefinitions.WORD_SIZE)); BigInteger x2_i = BigIntegerConverter.GetBigInteger(messageData.Slice(i + 64, EVMDefinitions.WORD_SIZE)); BigInteger x2_r = BigIntegerConverter.GetBigInteger(messageData.Slice(i + 96, EVMDefinitions.WORD_SIZE)); BigInteger y2_i = BigIntegerConverter.GetBigInteger(messageData.Slice(i + 128, EVMDefinitions.WORD_SIZE)); BigInteger y2_r = BigIntegerConverter.GetBigInteger(messageData.Slice(i + 160, EVMDefinitions.WORD_SIZE)); // Parse and verify our point. FpVector3 <Fp> point1 = ParsePoint(x1, y1); if (point1 == null) { throw new EVMException("ECPairing precompile failed because point 1 was deemed invalid when parsing."); } // If our coordinates exceed our modulo divisor, they are invalid. if (x2_i >= Bn128Curve.P || x2_r >= Bn128Curve.P || y2_i >= Bn128Curve.P || y2_r >= Bn128Curve.P) { throw new EVMException("ECPairing precompile failed because point 2 was deemed invalid when parsing."); } Fp2 x2 = new Fp2(x2_r, x2_i); Fp2 y2 = new Fp2(y2_r, y2_i); FpVector3 <Fp2> point2 = null; if (x2 == Fp2.ZeroValue && y2 == Fp2.ZeroValue) { // Our point is the zero point. point2 = zero; } else { // Initialize our point from components. point2 = new FpVector3 <Fp2>(x2, y2, Fp2.OneValue); // Verify our desired point is on the curve if (!point2.IsOnCurveCheck(Bn128Curve.B2)) { throw new EVMException("ECPairing precompile failed because point 2 was not on the curve."); } } // Verify multiplying by the curve order is non-zero. if (point2.Multiply(Bn128Curve.N).Z != Fp2.ZeroValue) { throw new EVMException("ECPairing precompile failed because point 2 was deemed invalid. Points multiplied by the curve order should equal infinity (zero)."); } // Pair our points and multiply the exponent by them. exponent *= Bn128Pairing.Pair(point2, point1, false); } // Return our result bool returnStatus = Bn128Pairing.FinalExponentiate(exponent) == Fp12.OneValue; byte[] returnData = BigIntegerConverter.GetBytes(returnStatus ? 1 : 0, EVMDefinitions.WORD_SIZE); return(new EVMExecutionResult(evm, returnData, true)); }
/// <summary> /// Creates authentication data to send to the responder. /// </summary> /// <param name="receiverPublicKey">The responder/receiver's public key.</param> /// <param name="nonce">The nonce to use for the authentication data. If null, new data is generated.</param> /// <returns>Returns the encrypted serialized authentication data.</returns> public byte[] CreateAuthentiation(EthereumEcdsa receiverPublicKey, byte[] nonce = null) { // Verify this is the initiator role. if (Role != RLPxSessionRole.Initiator) { throw new Exception("RLPx auth data should only be created by the initiator."); } // Verify the session state is correct. if (SessionState != RLPxSessionState.Initial) { throw new Exception("RLPx auth creation should only be performed on a new session."); } // If the nonce is null, generate a new one. nonce = nonce ?? GenerateNonce(); // Set our initiator nonce InitiatorNonce = nonce; // Set the remote public key RemotePublicKey = receiverPublicKey; // Create a new authentication message based off of our configured setting. RLPxAuthBase authMessage = null; if (UsingEIP8Authentication) { // Create our EIP8 authentication message. authMessage = new RLPxAuthEIP8() { Nonce = InitiatorNonce, Version = MAX_SUPPORTED_VERSION, }; } else { // Create our standard authentication message. authMessage = new RLPxAuthStandard() { Nonce = InitiatorNonce, }; } // Sign the authentication message authMessage.Sign(LocalPrivateKey, LocalEphemeralPrivateKey, RemotePublicKey, ChainId); // Serialize the authentication data byte[] serializedData = authMessage.Serialize(); // Encrypt the data accordingly (standard uses no shared mac data, EIP8 has 2 bytes which prefix the resulting encrypted data). if (UsingEIP8Authentication) { // Obtain two bytes of mac data by EIP8 standards (big endian 16-bit unsigned integer equal to the size of the resulting ECIES encrypted data). // We can calculate this as the amount of overhead data ECIES adds is static in size. byte[] sharedMacData = BigIntegerConverter.GetBytes(serializedData.Length + Ecies.ECIES_ADDITIONAL_OVERHEAD, 2); // Encrypt the serialized data with the shared mac data, and prefix the result with it. byte[] encryptedSerializedData = Ecies.Encrypt(RemotePublicKey, serializedData, sharedMacData); AuthData = sharedMacData.Concat(encryptedSerializedData); } else { // Encrypt it using ECIES without any shared mac data. AuthData = Ecies.Encrypt(RemotePublicKey, serializedData, null); } // Set the session state SessionState = RLPxSessionState.AuthenticationCompleted; // Return the auth data return(AuthData); }
/// <summary> /// Obtains a string representation of our instruction /// </summary> /// <returns>Returns a string representation of our instruction.</returns> public override string ToString() { return($"{Opcode} 0x{BigIntegerConverter.GetBytes(PushData, (int)PushSize).ToHexString()}"); }
public byte[] ToByteArray() { // Obtain the amount of bytes that constitute an address from the least significant bit side. return(BigIntegerConverter.GetBytes(_internalAddress, ADDRESS_SIZE)); }
private static EVMExecutionResult Precompile_ModExp(MeadowEVM evm) { // Verify we're past the byzantium fork if (evm.Version < EthereumRelease.Byzantium) { return(new EVMExecutionResult(evm, null, true)); } // Source: https://github.com/ethereum/EIPs/blob/master/EIPS/eip-198.md // Obtain a memory representation of our data. Span <byte> messageData = new Memory <byte>(evm.Message.Data).Span; // Extract our base length, exponent length, and mod length (in bytes) BigInteger baseLength = BigIntegerConverter.GetBigInteger(messageData.Slice(0, EVMDefinitions.WORD_SIZE)); BigInteger exponentLength = BigIntegerConverter.GetBigInteger(messageData.Slice(32, EVMDefinitions.WORD_SIZE)); BigInteger modLength = BigIntegerConverter.GetBigInteger(messageData.Slice(64, EVMDefinitions.WORD_SIZE)); // GAS CALCULATION START: Exponent is leading the word of data, so we obtain a numeric representation of the first bytes. BigInteger exponentHead = BigIntegerConverter.GetBigInteger(messageData.Slice(96 + (int)baseLength, EVMDefinitions.WORD_SIZE)); // Shift our head so we only have relevant bytes (they're leading the word, so we want to cut the tail off by bitshifting). exponentHead >>= (8 * (int)BigInteger.Max(32 - exponentLength, 0)); // Count our bits in our exponent head. int exponentHeadBitCount = -1; while (exponentHead > 0) { exponentHead >>= 1; exponentHeadBitCount++; } // Obtain our adjusted exponent length. // 1) If exponent length <= 32, and exponent bits are 0, this is 0. // 2) If exponent length <= 32, then return the index of the highest bit in exponent. // 3) If exponent length > 32, then we return (8 * (exponent-length - 32)) + the index of the highest bit in the exponent. BigInteger adjustedExponentLength = Math.Max(exponentHeadBitCount, 0) + (8 * BigInteger.Max(exponentLength - 32, 0)); adjustedExponentLength = BigInteger.Max(adjustedExponentLength, 1); // GAS CALCULATION END: Calculate the final gas cost from the length of our biggest parameter, times the exponent length, divided by our divisor. BigInteger biggestLength = BigInteger.Max(modLength, baseLength); BigInteger gasCost = (Estimate_karatsuba_difficulty(biggestLength) * adjustedExponentLength) / GasDefinitions.GAS_PRECOMPILE_MODEXP_QUAD_DIVISOR; // Deduct our gas cost. evm.GasState.Deduct(gasCost); // Verify our base length. if (baseLength == 0) { return(new EVMExecutionResult(evm, new byte[(int)modLength], true)); } // Verify our mod length. if (modLength == 0) { return(new EVMExecutionResult(evm, null, true)); } // Obtain our base, exponent and mod Span <byte> memBase = messageData.Slice(96, (int)baseLength); BigInteger numBase = BigIntegerConverter.GetBigInteger(memBase, false, memBase.Length); Span <byte> memExponent = messageData.Slice(96 + (int)baseLength, (int)exponentLength); BigInteger numExponent = BigIntegerConverter.GetBigInteger(memExponent, false, memExponent.Length); Span <byte> memMod = messageData.Slice(96 + (int)baseLength + (int)exponentLength, (int)modLength); BigInteger numMod = BigIntegerConverter.GetBigInteger(memMod, false, memMod.Length); // Verify our divisor isn't 0. if (numMod == 0) { return(new EVMExecutionResult(evm, new byte[(int)modLength], true)); } // Obtain our modexp result, which, by definition, we know won't be bigger than our divisor, so we bind our length to the modulo divisor length. BigInteger numResult = BigInteger.ModPow(numBase, numExponent, numMod); byte[] result = BigIntegerConverter.GetBytes(numResult, (int)modLength); // Return our result return(new EVMExecutionResult(evm, result, true)); }
/// <summary> /// Computes the child key and child chain code for a given child key/index relative from this extended key. /// </summary> /// <param name="index">The child key/index to derive a key/chain code for.</param> /// <returns>Returns a key and chain code for a child relative from this extended key. Derives a key of the same type as this key.</returns> private (EthereumEcdsa childKey, byte[] childChainCode) GetChildKeyInternal(uint index) { // Declare a hash we will obtain of our key. byte[] hash = null; // If this is a hardened directory/key/index if (KeyPath.CheckHardenedDirectoryIndex(index)) { // Verify we aren't trying to derive a hardened key from a public key (since private is required). if (KeyType == EthereumEcdsaKeyType.Public) { // Throw an exception because hardened keys mean we need the private key, but this is the public key. throw new ArgumentException("Hierarchically deterministic child key cannot be derived from a public key when the child key index is hardened. Hardened keys can only be derived when the private key is known."); } // Obtain the message to hash. (0x00 byte prefixing the private key to pad it to 33 bytes long). byte[] hashMessage = new byte[] { 0 }.Concat(InternalKey.ToPrivateKeyArray()); // Next we hash our key data. hash = ComputeChildHash(index, hashMessage); } else { // Compute the hash on our public key and index. hash = ComputeChildHash(index, InternalKey.ToPublicKeyArray(true, false)); } // Set the child key data as the first 32 bytes of "hash" byte[] childKeyData = new byte[EthereumEcdsa.PRIVATE_KEY_SIZE]; Array.Copy(hash, 0, childKeyData, 0, childKeyData.Length); // Initialize the child chain code byte[] childChainCode = new byte[CHAIN_CODE_SIZE]; // We derive the child chain code as the data immediately following key data. Array.Copy(hash, 32, childChainCode, 0, childChainCode.Length); // Convert the key data to an integer BigInteger childKeyInt = BigIntegerConverter.GetBigInteger(childKeyData, false, childChainCode.Length); // If the child key is above N if (childKeyInt >= Secp256k1Curve.N) { throw new ArgumentException("Calculated child key value cannot exceed or equal N on the secp256k1 curve. Hierarchically deterministic child key cannot derive here. Try again."); } // Define our resulting key to obtain EthereumEcdsa childKey = null; // Obtain our child key depending on type.; if (KeyType == EthereumEcdsaKeyType.Public) { // Obtain our public key and add it to G * childKey var q = Secp256k1Curve.Parameters.Curve.DecodePoint(InternalKey.ToPublicKeyArray(true, false)); q = Secp256k1Curve.Parameters.G.Multiply(childKeyInt.ToBouncyCastleBigInteger()).Add(q); if (q.IsInfinity) { throw new ArgumentException("Calculated child key value point is infinity. This is a very rare occurrence. Hierarchically deterministic child key cannot derive here."); } // Normalize our point. q = q.Normalize(); var p = Secp256k1Curve.DomainParameters.Curve.CreatePoint(q.XCoord.ToBigInteger(), q.YCoord.ToBigInteger()); var encoded = p.GetEncoded(compressed: true); // Derive our child data. childKey = EthereumEcdsa.Create(encoded, EthereumEcdsaKeyType.Public); } else { // Add our private key to our parsed new key, mod N, to derive our new key. BigInteger computedChildKeyInt = (BigIntegerConverter.GetBigInteger(InternalKey.ToPrivateKeyArray()) + childKeyInt) % Secp256k1Curve.N; // Verify our computed child key is non-zero if (computedChildKeyInt == 0) { throw new ArgumentException("Calculated child private key is zero. This is a very rare occurrence. Hierarchically deterministic child key cannot derive here."); } // Obtain our new key from this byte[] computedChildKeyData = BigIntegerConverter.GetBytes(computedChildKeyInt, EthereumEcdsa.PRIVATE_KEY_SIZE); // Initialize our key childKey = EthereumEcdsa.Create(computedChildKeyData, EthereumEcdsaKeyType.Private); } // Return our obtained data. return(childKey, childChainCode); }
static async Task Main(string[] args) { Console.WriteLine("Starting..."); int port; bool meadow = true; if (!meadow) { port = 7545; } else { // Bootstrap our meadow test node server. var testNode = new Meadow.TestNode.TestNodeServer(); // Print our undefined rpc methods. var undefinedRpcMethods = testNode.GetUndefinedRpcMethods(); Console.WriteLine("Warning: following RPC methods are not defined: \n" + string.Join(", ", undefinedRpcMethods.Select(r => r.Value()))); // Start up the server and obtain the port. await testNode.RpcServer.StartAsync(); port = testNode.RpcServer.ServerPort; } async Task <Exception> GetExecutionTraceException(IJsonRpcClient rpcClient, JsonRpcError error) { if (meadow) { var executionTrace = await rpcClient.GetExecutionTrace(); var traceAnalysis = new ExecutionTraceAnalysis(executionTrace); // Build our aggregate exception var aggregateException = traceAnalysis.GetAggregateException(error.ToException()); return(aggregateException); } else { if (error != null) { throw error.ToException(); } else { throw new Exception("Execution exception"); } } } // Connect our client to the server. var client = JsonRpcClient.Create( new Uri($"http://{IPAddress.Loopback}:{port}"), defaultGasLimit: 6_000_000, defaultGasPrice: 0); client.ErrorFormatter = GetExecutionTraceException; // If we're testing meadow, we enable special coverage/tracing options. if (meadow) { await client.SetCoverageEnabled(true); await client.SetTracingEnabled(true); } //var missingConstructorContract = await MissingConstructorChild.New(client); //var someNum = await missingConstructorContract.someNum().Call(); #region ErrorContract // Try deploying the error generating contract and test some calls. var errorContract = await ErrorContract.New(client, new TransactionParams { Gas = 4712388 }); //await errorContract.doRevert().ExpectRevertTransaction(); //await errorContract.doAssert().ExpectRevertTransaction(); //await errorContract.doThrow().ExpectRevertCall(); #endregion #region Inheritance Tests // Test inheritance with an inherited contract. var inheritedContract = await InheritedContract.New(client, new TransactionParams { Gas = 4712388 }); await inheritedContract.testFunction(); await inheritedContract.testFunctionWithInheritedModifier(); // Test inheritance with a seperate file which we inherited from. var multiInheritedContract = await MultifileInheritedContract.New(client, new TransactionParams { Gas = 4712388 }); await multiInheritedContract.testFunction(); await multiInheritedContract.testFunctionWithInheritedModifier(); #endregion try { await FailDeploymentContract.New(client); } catch { } #region Callstack Tests // Try throwing asserts in further call depth and spanning more than one file. await multiInheritedContract.testInheritedAssertThrow().ExpectRevertTransaction(); if (meadow) { // Test parsing an execution trace of the last call/transaction. var executionTrace = await client.GetExecutionTrace(); ExecutionTraceAnalysis traceAnalysis = new ExecutionTraceAnalysis(executionTrace); // Testing: Build our aggregate exception var aggregateException = traceAnalysis.GetAggregateException(); try { throw aggregateException; } catch { } } #endregion #region ExampleContract Tests // Deploy our main example contract var exContract = await ExampleContract.New($"TestName", true, 34, client, new TransactionParams { Gas = 4712388 }); await exContract.testRevert().ExpectRevertTransaction(); await exContract.testBranchCoverage().ExpectRevertTransaction(); if (meadow) { // Test parsing an execution trace of the last call/transaction. var executionTrace = await client.GetExecutionTrace(); ExecutionTraceAnalysis traceAnalysis = new ExecutionTraceAnalysis(executionTrace); // Testing: Build our aggregate exception var aggregateException = traceAnalysis.GetAggregateException(); try { throw aggregateException; } catch { } } // Test the modexp precompile. var modExpTestBytes = await exContract.testModExp( BigIntegerConverter.GetBytes(BigInteger.Parse("1212121323543453245345678346345737475734753745737774573475377734577", CultureInfo.InvariantCulture)), BigIntegerConverter.GetBytes(BigInteger.Parse("3", CultureInfo.InvariantCulture)), BigIntegerConverter.GetBytes(BigInteger.Parse("4345328123928357434573234217343477", CultureInfo.InvariantCulture))) .Call(); var modExpTest = BigIntegerConverter.GetBigInteger(modExpTestBytes, false, modExpTestBytes.Length); // should be 856753145937825219130387866259147 // Test events var eventTest = await exContract.emitDataEvent(1, 2, 3).TransactionReceipt(); var eventLogFirst = eventTest.FirstEventLog <ExampleContract.DataEvent>(); var eventLogLast = eventTest.LastEventLog <ExampleContract.DataEvent>(); var eventLogAll = eventTest.EventLogs <ExampleContract.DataEvent>(); var eventLogsBase = eventTest.EventLogs(); // Test for chris var result = await exContract.getFirstByteFromByte32(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 }).Call(); // Try testing branch coverage (which ends in an assert being thrown) await exContract.testBranchCoverage().ExpectRevertTransaction(); //await exContract.noopFunc(); var echoGasEst = await exContract.echoInt24(34).EstimateGas(); await exContract.testInstructions1().TransactionReceipt(); var transactionReceipt = await exContract.echoInt24(22).TransactionReceipt(); #endregion #region Performance Tests // Define start time for an execution loop to test performance of a few basic calls. DateTime start = DateTime.Now; DateTime iteration = start; // Note: Change the loop condition here as needed. for (int i = 0; i < 1; i++) { var addr = await exContract.echoAddress(new Address(new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 })); var int24Test = await exContract.echoInt24(7777); var stringTest = await exContract.echoString("This is a string."); var enabledThingTest = await exContract.enabledThing(); var isNineTest = await exContract.myFunc(9); var givenName = await exContract.givenName(); // Precompile hash tests var sha256HashTest = await exContract.sha256str("hello world"); // should be 0xb94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9 var ripemd160HashTest = await exContract.ripemd160str("hello world"); // should be 0x98c615784ccb5fe5936fbc0cbe9dfdb408d92f0f // This ECRecover test should yield 0x75c8aa4b12bc52c1f1860bc4e8af981d6542cccd. This test data is taken from SigningTests.cs var ecRecoverTest = await exContract.testECRecover( new byte[] { 0xc9, 0xf1, 0xc7, 0x66, 0x85, 0x84, 0x5e, 0xa8, 0x1c, 0xac, 0x99, 0x25, 0xa7, 0x56, 0x58, 0x87, 0xb7, 0x77, 0x1b, 0x34, 0xb3, 0x5e, 0x64, 0x1c, 0xca, 0x85, 0xdb, 0x9f, 0xef, 0xd0, 0xe7, 0x1f }, 0x1c, BigIntegerConverter.GetBytes(BigInteger.Parse("68932463183462156574914988273446447389145511361487771160486080715355143414637", CultureInfo.InvariantCulture)), BigIntegerConverter.GetBytes(BigInteger.Parse("47416572686988136438359045243120473513988610648720291068939984598262749281683", CultureInfo.InvariantCulture))); Console.WriteLine($"#{i}: {DateTime.Now - iteration}"); iteration = DateTime.Now; } DateTime end = DateTime.Now; #endregion // If this is meadow, we do special post-execution tasks. if (meadow) { // Test obtaining coverage maps, disabling them, clearing them. var coverageMaps = await client.GetAllCoverageMaps(); await client.SetCoverageEnabled(false); await client.ClearCoverage(); // Obtain our generated solc data, create a report, and open it. var solcData = GeneratedSolcData.Default.GetSolcData(); // Match coverage contract addresses with deployed contracts that the client keeps track of. var contractInstances = GeneratedSolcData.Default.MatchCoverageData(coverageMaps); var reportPath = Path.GetFullPath("Report"); MiscUtil.ResetDirectory(reportPath); ReportGenerator.CreateReport(GeneratedSolcData.Default.SolidityCompilerVersion, contractInstances, solcData.SolcSourceInfo, solcData.SolcBytecodeInfo, null, null, reportPath); MiscUtil.OpenBrowser(Path.Join(reportPath, ReportGenerator.REPORT_INDEX_FILE)); } // Output our execution loop time. Console.WriteLine($"Total Time: {end - start}"); Console.ReadLine(); }
/// <summary> /// Creates authentication acknowledgement data to send to the initiator, signalling that their authentication /// data was successfully verified, and providing the initiator with information that they have already provided /// to the responder (so they can both derive the same shared secrets). /// </summary> /// <param name="nonce">The nonce to use for the authentication data. If null, new data is generated.</param> /// <returns>Returns the encrypted serialized authentication acknowledgement data.</returns> public byte[] CreateAuthenticationAcknowledgement(byte[] nonce = null) { // Verify this is the responder role. if (Role != RLPxSessionRole.Responder) { throw new Exception("RLPx auth-ack data should only be created by the responder."); } // Verify the session state is correct. if (SessionState != RLPxSessionState.AuthenticationCompleted) { throw new Exception("RLPx auth-ack creation should only be performed on a session after auth was received/verified."); } // If the nonce is null, generate a new one. if (nonce == null) { nonce = new byte[NONCE_SIZE]; _randomNumberGenerator.GetBytes(nonce); } // Set the responder nonce ResponderNonce = nonce ?? GenerateNonce(); // If we are using EIP8 RLPxAuthAckBase authAckMessage = null; if (UsingEIP8Authentication) { // We use EIP8 authentication acknowledgement authAckMessage = new RLPxAuthAckEIP8() { EphemeralPublicKey = LocalEphemeralPrivateKey.ToPublicKeyArray(false, true), Nonce = ResponderNonce, Version = MAX_SUPPORTED_VERSION, }; } else { // We use standard authentication acknowledgement authAckMessage = new RLPxAuthAckStandard() { EphemeralPublicKey = LocalEphemeralPrivateKey.ToPublicKeyArray(false, true), Nonce = ResponderNonce, TokenFound = false, // TODO: Check for a saved session key from before, and set this accordingly. }; } // Serialize the authentication-acknowledgement data byte[] serializedData = authAckMessage.Serialize(); // Encrypt the data accordingly (standard uses no shared mac data, EIP8 has 2 bytes which prefix the resulting encrypted data). if (UsingEIP8Authentication) { // Obtain two bytes of mac data by EIP8 standards (big endian 16-bit unsigned integer equal to the size of the resulting ECIES encrypted data). // We can calculate this as the amount of overhead data ECIES adds is static in size. byte[] sharedMacData = BigIntegerConverter.GetBytes(serializedData.Length + Ecies.ECIES_ADDITIONAL_OVERHEAD, 2); // Encrypt the serialized data with the shared mac data, prefix the result with it. byte[] encryptedSerializedData = Ecies.Encrypt(RemotePublicKey, serializedData, sharedMacData); AuthAckData = sharedMacData.Concat(encryptedSerializedData); } else { // Encrypt it using ECIES without any shared mac data. AuthAckData = Ecies.Encrypt(RemotePublicKey, serializedData, null); } // Set the session state SessionState = RLPxSessionState.AcknowledgementCompleted; // Return the auth-ack data return(AuthAckData); }