Ejemplo n.º 1
0
        public void DeriveEncryptionParameters()
        {
            // Verify the session state is correct.
            if (SessionState != RLPxSessionState.AcknowledgementCompleted)
            {
                throw new Exception("RLPx encryption parameter deriviation should only occur after authentication and acknowledgement was processed.");
            }

            // Verify we have all required information
            if (AuthData == null || AuthAckData == null || RemoteEphermalPublicKey == null || InitiatorNonce == null || ResponderNonce == null)
            {
                throw new Exception("RLPx deriving encryption information failed: Insufficient data collected from handshake.");
            }

            // Generate the ecdh key with both ephemeral keys
            byte[] ecdhEphemeralKey = LocalEphemeralPrivateKey.ComputeECDHKey(RemoteEphermalPublicKey);

            // Generate a shared secret: Keccak256(ecdhEphemeralKey || Keccak256(ResponderNonce || InitiatorNonce))
            byte[] combinedNonceHash = KeccakHash.ComputeHashBytes(ResponderNonce.Concat(InitiatorNonce));
            byte[] sharedSecret      = KeccakHash.ComputeHashBytes(ecdhEphemeralKey.Concat(combinedNonceHash));

            // Derive the token as a hash of the shared secret.
            Token = KeccakHash.ComputeHashBytes(sharedSecret);

            // Derive AES secret: Keccak256(ecdhEphemeralKey || sharedSecret)
            AesSecret = KeccakHash.ComputeHashBytes(ecdhEphemeralKey.Concat(sharedSecret));

            // Derive Mac secret: Keccak256(ecdhEphemeralKey || AesSecret)
            MacSecret = KeccakHash.ComputeHashBytes(ecdhEphemeralKey.Concat(AesSecret));

            // Create our AES providers for incoming and outgoing traffic/frames.
            // Counter is 0, so it doesn't need to be provided, default value will handle this.
            IngressAes = new AesCtr(AesSecret);
            EgressAes  = new AesCtr(AesSecret);

            // Next we'll want to derive our incoming (ingress) and outgoing (egress) traffic message authentication code ("MAC")
            // The initial state is based off of keccak((MacSecret ^ nonce) || auth/auth-ack). Later states update data from packet frames.

            // We begin by calculating the xor'd nonces
            byte[] initiatorTranformedNonce  = (byte[])InitiatorNonce.Clone();
            byte[] responderTransformedNonce = (byte[])ResponderNonce.Clone();
            int    loopSize = Math.Min(initiatorTranformedNonce.Length, MacSecret.Length);

            for (int i = 0; i < loopSize; i++)
            {
                initiatorTranformedNonce[i]  ^= MacSecret[i];
                responderTransformedNonce[i] ^= MacSecret[i];
            }

            // Next we'll want to hash the data with our hash providers.
            KeccakHash initiatorOutgoing = KeccakHash.Create();

            initiatorOutgoing.Update(responderTransformedNonce, 0, responderTransformedNonce.Length);
            initiatorOutgoing.Update(AuthData, 0, AuthData.Length);
            KeccakHash responderOutgoing = KeccakHash.Create();

            responderOutgoing.Update(initiatorTranformedNonce, 0, initiatorTranformedNonce.Length);
            responderOutgoing.Update(AuthAckData, 0, AuthAckData.Length);

            // Assign the correct hash providers based off of role
            if (Role == RLPxSessionRole.Initiator)
            {
                EgressMac  = initiatorOutgoing;
                IngressMac = responderOutgoing;
            }
            else
            {
                EgressMac  = responderOutgoing;
                IngressMac = initiatorOutgoing;
            }
        }
Ejemplo 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);
        }