public void AuthValidSignAndRecoverEip8() { // Generate all needed keypairs. EthereumEcdsa localPrivateKey = EthereumEcdsa.Generate(); EthereumEcdsa ephemeralPrivateKey = EthereumEcdsa.Generate(); EthereumEcdsa receiverPrivateKey = EthereumEcdsa.Generate(); // Create an RLPx auth packet and sign it. RLPxAuthEIP8 authPacket = new RLPxAuthEIP8(); authPacket.Sign(localPrivateKey, ephemeralPrivateKey, receiverPrivateKey, null); // Serialize and deserialize it. byte[] serializedData = authPacket.Serialize(); RLPxAuthEIP8 deserializedPacket = new RLPxAuthEIP8(serializedData); // Verify all matches between our serialized/deserialized object data. Assert.Equal(authPacket.Nonce.ToHexString(), deserializedPacket.Nonce.ToHexString()); Assert.Equal(authPacket.PublicKey.ToHexString(), deserializedPacket.PublicKey.ToHexString()); Assert.Equal(authPacket.R.ToHexString(), deserializedPacket.R.ToHexString()); Assert.Equal(authPacket.S.ToHexString(), deserializedPacket.S.ToHexString()); Assert.Equal(authPacket.V, deserializedPacket.V); // Try to recover the public key EthereumEcdsa recoveredEphemeralPublicKey = deserializedPacket.RecoverDataFromSignature(receiverPrivateKey).remoteEphemeralPublicKey; // Verify our public key hashes match Assert.Equal(ephemeralPrivateKey.GetPublicKeyHash().ToHexString(), recoveredEphemeralPublicKey.GetPublicKeyHash().ToHexString()); }
protected override void EndProcessing() { var config = this.ReadConfig(); IAccountDerivation accountDerivation; if (string.IsNullOrWhiteSpace(Mnemonic)) { var hdWalletAccountDerivation = HDAccountDerivation.Create(); accountDerivation = hdWalletAccountDerivation; Host.UI.WriteLine($"Using mnemonic phrase: '{hdWalletAccountDerivation.MnemonicPhrase}'"); Host.UI.WriteErrorLine("Warning: this private key generation is not secure and should not be used in production."); } else { accountDerivation = new HDAccountDerivation(Mnemonic); } var accountKeys = new List <(Address Address, EthereumEcdsa Account)>(); foreach (var account in EthereumEcdsa.Generate(config.AccountCount, accountDerivation)) { // Get an account from the public key hash. var address = account.EcdsaKeyPairToAddress(); accountKeys.Add((address, account)); } var accounts = accountKeys.ToArray(); GlobalVariables.AccountKeys = accounts; Console.WriteLine($"Created {accounts.Length} accounts. Use '{CmdLetExtensions.GetCmdletName<WriteAccountsCommand>()}' to save account keys to disk."); }
public void EciesEncryptDecryptTest() { string[] testDataSets = new string[] { "okayAESstrTest", "test2", "and another \x00 test. but this one is a sentence with \x11 weird characters." }; for (int i = 0; i < testDataSets.Length; i++) { // Generate a keypair EthereumEcdsa keypair = EthereumEcdsa.Generate(); byte[] testData = Encoding.UTF8.GetBytes(testDataSets[i]); byte[] encrypted = Ecies.Encrypt(keypair, testData, null); byte[] decrypted = Ecies.Decrypt(keypair, encrypted, null); string result = Encoding.UTF8.GetString(decrypted); Assert.Equal(testDataSets[i], result); } }
public void FullHandshakeStandard(bool useEip8) { // Create the initiator and responder keypairs EthereumEcdsa initiatorKeyPair = EthereumEcdsa.Generate(); EthereumEcdsa initiatorEphemeralKeyPair = EthereumEcdsa.Generate(); EthereumEcdsa responderKeyPair = EthereumEcdsa.Generate(); EthereumEcdsa responderEphemeralKeypair = EthereumEcdsa.Generate(); // Initiate RLPx sessions for each role. RLPxSession initiatorSession = new RLPxSession(RLPxSessionRole.Initiator, initiatorKeyPair, initiatorEphemeralKeyPair, useEip8); RLPxSession responderSession = new RLPxSession(RLPxSessionRole.Responder, responderKeyPair, responderEphemeralKeypair); // Create authentication data (initiator) (should work) byte[] authData = initiatorSession.CreateAuthentiation(responderKeyPair); // Create authentication data (responder) (should fail, responder should only be receiving auth data, not creating it). Assert.Throws <Exception>(() => { responderSession.CreateAuthentiation(initiatorKeyPair); }); // Verify the authentication data (responder) (should work) responderSession.VerifyAuthentication(authData); // Verify the authentication data (initiator) (should fail, responder should only be creating auth data, not verifying/receiving it). Assert.Throws <Exception>(() => { initiatorSession.VerifyAuthentication(authData); }); // After verification, the responder session should have set it's EIP8 status accordingly based off of what was received. Assert.Equal(useEip8, responderSession.UsingEIP8Authentication); // Create an authentication acknowledgement (responder) (should work) byte[] authAckData = responderSession.CreateAuthenticationAcknowledgement(); // Create an authentication acknowledgement (initiator) (should fail, initiator should only receiving auth-ack) Assert.Throws <Exception>(() => { initiatorSession.CreateAuthenticationAcknowledgement(); }); // Verify the authentication acknowledgement (initiator) (should work) initiatorSession.VerifyAuthenticationAcknowledgement(authAckData); // Verify the authentication acknowledgement (responder) (should fail, responder should not be verifying/receiving auth-ack) Assert.Throws <Exception>(() => { responderSession.VerifyAuthenticationAcknowledgement(authAckData); }); }
public RLPxSession(RLPxSessionRole role, EthereumEcdsa localPrivateKey, EthereumEcdsa ephemeralPrivateKey, bool usingEIP8 = false) { // Verify the private key is not null if (localPrivateKey == null) { throw new ArgumentNullException("Provided local private key for RLPx session was null."); } // Verify the private key is a private key if (localPrivateKey.KeyType != EthereumEcdsaKeyType.Private) { throw new ArgumentException("Provided local private key for RLPx session was not a valid private key."); } // Set the local private key LocalPrivateKey = localPrivateKey; // Verify the ephemeral private key is a private key if (ephemeralPrivateKey != null && ephemeralPrivateKey.KeyType != EthereumEcdsaKeyType.Private) { throw new ArgumentException("Provided local private key for RLPx session was not a valid private key."); } // Set the ephemeral private key. LocalEphemeralPrivateKey = ephemeralPrivateKey ?? EthereumEcdsa.Generate(); // Set the role and session state Role = role; SessionState = RLPxSessionState.Initial; // Set the auth type if we are initiator. if (Role == RLPxSessionRole.Initiator) { UsingEIP8Authentication = usingEIP8; } }
public static byte[] Encrypt(EthereumEcdsa remotePublicKey, byte[] data, byte[] sharedMacData = null) { // If we have no shared mac data, we set it as a blank array sharedMacData = sharedMacData ?? Array.Empty <byte>(); // Generate a random private key EthereumEcdsa senderPrivateKey = EthereumEcdsa.Generate(new SystemRandomAccountDerivation()); // Generate the elliptic curve diffie hellman ("ECDH") shared key byte[] ecdhKey = senderPrivateKey.ComputeECDHKey(remotePublicKey); // Perform NIST SP 800-56 Concatenation Key Derivation Function ("KDF") Memory <byte> keyData = DeriveKeyKDF(ecdhKey, 32); // Split the AES encryption key and MAC from the derived key data. var aesKey = keyData.Slice(0, 16).ToArray(); byte[] hmacSha256Key = keyData.Slice(16, 16).ToArray(); hmacSha256Key = _sha256.ComputeHash(hmacSha256Key); // We generate a counter for our aes-128-ctr operation. byte[] counter = new byte[AesCtr.BLOCK_SIZE]; _randomNumberGenerator.GetBytes(counter); // Encrypt the data accordingly. byte[] encryptedData = AesCtr.Encrypt(aesKey, data, counter); // Obtain the sender's public key to compile our message. byte[] localPublicKey = senderPrivateKey.ToPublicKeyArray(false, true); // We'll want to put this data into the message in the following order (where || is concatenation): // ECIES_HEADER_BYTE (1 byte) || sender's public key (64 bytes) || counter (16 bytes) || encrypted data (arbitrary length) || tag (32 bytes) // This gives us a total size of 113 + data.Length byte[] result = new byte[ECIES_ADDITIONAL_OVERHEAD + encryptedData.Length]; // Define a pointer and copy in our data as suggested. int offset = 0; result[offset++] = ECIES_HEADER_BYTE; Array.Copy(localPublicKey, 0, result, offset, localPublicKey.Length); offset += localPublicKey.Length; Array.Copy(counter, 0, result, offset, counter.Length); offset += counter.Length; Array.Copy(encryptedData, 0, result, offset, encryptedData.Length); offset += encryptedData.Length; // We still have to copy the tag, which is a HMACSHA256 of our counter + encrypted data + shared mac. // We copy the data into a buffer for this hash computation since counter + encrypted data are already aligned. byte[] tagPreimage = new byte[counter.Length + encryptedData.Length + sharedMacData.Length]; Array.Copy(result, 65, tagPreimage, 0, counter.Length + encryptedData.Length); Array.Copy(sharedMacData, 0, tagPreimage, counter.Length + encryptedData.Length, sharedMacData.Length); // Obtain a HMACSHA256 provider HMACSHA256 hmacSha256 = new HMACSHA256(hmacSha256Key); // Compute a hash of our counter + encrypted data + shared mac data. byte[] tag = hmacSha256.ComputeHash(tagPreimage); // Copy the tag into our result buffer. Array.Copy(tag, 0, result, offset, tag.Length); offset += tag.Length; // Return the resulting data. return(result); }