Esempio n. 1
0
        /// <summary>
        /// Verifies the provided encrypted serialized authentication acknowledgement data received from the initiator.
        /// If verification fails, an appropriate exception will be thrown. If no exception is thrown, the verification
        /// has succeeded.
        /// </summary>
        /// <param name="authAckData">The encrypted serialized authentication acknowledgement data to verify.</param>
        public void VerifyAuthenticationAcknowledgement(byte[] authAckData)
        {
            // Verify this is the initiator role.
            if (Role != RLPxSessionRole.Initiator)
            {
                throw new Exception("RLPx auth-ack data should only be verified by the initiator.");
            }

            // Verify the session state is correct.
            if (SessionState != RLPxSessionState.AuthenticationCompleted)
            {
                throw new Exception("RLPx auth-ack verification should only be performed after auth was created/sent.");
            }

            // Try to deserialize the data as a standard authentication packet.
            // The data is currently encrypted serialized data, so it will also need to be decrypted.
            RLPxAuthAckBase authAckMessage = null;

            try
            {
                // Set the auth-ack data
                AuthAckData = authAckData.Slice(0, AUTH_ACK_STANDARD_ENCRYPTED_SIZE);

                // The authentication data can simply be decrypted without any shared mac.
                byte[] decryptedAuthData = Ecies.Decrypt(LocalPrivateKey, AuthAckData, null);

                // Try to parse the decrypted authentication data.
                authAckMessage          = new RLPxAuthAckStandard(decryptedAuthData);
                UsingEIP8Authentication = false;

                // Set the remote version
                RemoteVersion = MAX_SUPPORTED_VERSION;
            }
            catch (Exception authAckStandardEx)
            {
                try
                {
                    // EIP8 has a uint16 denoting encrypted data size, and the data to decrypt follows.
                    Memory <byte> authAckDataMem   = authAckData;
                    Memory <byte> sharedMacDataMem = authAckDataMem.Slice(0, 2);
                    ushort        encryptedSize    = (ushort)BigIntegerConverter.GetBigInteger(sharedMacDataMem.Span, false, sharedMacDataMem.Length);
                    Memory <byte> encryptedData    = authAckDataMem.Slice(2, encryptedSize);

                    // Set our auth-ack data
                    AuthAckData = authAckDataMem.Slice(0, sharedMacDataMem.Length + encryptedSize).ToArray();

                    // Split the shared mac from the authentication data
                    byte[] decryptedAuthData = Ecies.Decrypt(LocalPrivateKey, encryptedData, sharedMacDataMem);

                    // Try to parse the decrypted EIP8 authentication data.
                    RLPxAuthAckEIP8 authEip8Message = new RLPxAuthAckEIP8(decryptedAuthData);
                    UsingEIP8Authentication = true;

                    // Set the generic authentication data object.
                    authAckMessage = authEip8Message;

                    // Set the remote version
                    RemoteVersion = authEip8Message.Version;
                }
                catch (Exception authAckEip8Ex)
                {
                    string exceptionMessage = "Could not deserialize RLPx auth-ack data in either standard or EIP8 format.\r\n";
                    exceptionMessage += $"Standard Auth-Ack Error: {authAckStandardEx.Message}\r\n";
                    exceptionMessage += $"EIP8 Auth-Ack Error: {authAckEip8Ex.Message}";
                    throw new Exception(exceptionMessage);
                }
            }

            // Set the responder nonce
            ResponderNonce = authAckMessage.Nonce;

            // Set the remote public key.
            RemoteEphermalPublicKey = EthereumEcdsa.Create(authAckMessage.EphemeralPublicKey, EthereumEcdsaKeyType.Public);

            // Set the session state
            SessionState = RLPxSessionState.AcknowledgementCompleted;
        }
Esempio n. 2
0
        /// <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);
        }