internal static bool TrySolve(ulong challenge, byte difficulty, ulong maxIterations, out ulong additionsRequired) { additionsRequired = 0; ulong iterations = 0; ulong workingValue = HashProvider.GetStableHash64(challenge + additionsRequired); while (difficulty > 0 && ((workingValue << ((sizeof(ulong) * 8) - difficulty)) >> ((sizeof(ulong) * 8) - difficulty)) != 0 && iterations < maxIterations) { ++iterations; ++additionsRequired; workingValue = HashProvider.GetStableHash64(challenge + additionsRequired); } return(Validate(challenge, additionsRequired, difficulty)); }
internal void HandleChallengeRequest(ulong challenge, byte difficulty) { _stateLock.EnterReadLock(); try { if (State == ConnectionState.RequestingConnection) { LastMessageIn = NetTime.Now; ulong additionsRequired = 0; ulong workingValue = challenge; // Solve the hashcash // TODO: Solve thread while (difficulty > 0 && ((workingValue << ((sizeof(ulong) * 8) - difficulty)) >> ((sizeof(ulong) * 8) - difficulty)) != 0) { additionsRequired++; workingValue = HashProvider.GetStableHash64(challenge + additionsRequired); } ConnectionChallenge = challenge; ChallengeDifficulty = difficulty; ChallengeAnswer = additionsRequired; // Set resend values HandshakeResendAttempts = 0; HandshakeStarted = NetTime.Now; HandshakeLastSendTime = NetTime.Now; State = ConnectionState.SolvingChallenge; // Send the response SendChallengeResponse(); } } finally { _stateLock.ExitReadLock(); } }
private void CheckConnectionResends() { _stateLock.EnterReadLock(); try { switch (State) { case ConnectionState.RequestingConnection: { if ((!Config.TimeBasedConnectionChallenge || PreConnectionChallengeSolved) && (NetTime.Now - HandshakeLastSendTime).TotalMilliseconds > Config.ConnectionRequestMinResendDelay && HandshakeResendAttempts <= Config.MaxConnectionRequestResends) { HandshakeResendAttempts++; HandshakeLastSendTime = NetTime.Now; // Calculate the minimum size we can fit the packet in int minSize = 1 + Constants.RUFFLES_PROTOCOL_IDENTIFICATION.Length + (Config.TimeBasedConnectionChallenge ? sizeof(ulong) * 3 : 0); // Calculate the actual size with respect to amplification padding int size = Math.Max(minSize, (int)Config.AmplificationPreventionHandshakePadding); // Allocate memory HeapMemory memory = MemoryManager.AllocHeapMemory((uint)size); // Write the header memory.Buffer[0] = HeaderPacker.Pack((byte)MessageType.ConnectionRequest); // Copy the identification token Buffer.BlockCopy(Constants.RUFFLES_PROTOCOL_IDENTIFICATION, 0, memory.Buffer, 1, Constants.RUFFLES_PROTOCOL_IDENTIFICATION.Length); if (Config.TimeBasedConnectionChallenge) { // Write the response unix time for (byte x = 0; x < sizeof(ulong); x++) { memory.Buffer[1 + Constants.RUFFLES_PROTOCOL_IDENTIFICATION.Length + x] = ((byte)(PreConnectionChallengeTimestamp >> (x * 8))); } // Write counter for (byte x = 0; x < sizeof(ulong); x++) { memory.Buffer[1 + Constants.RUFFLES_PROTOCOL_IDENTIFICATION.Length + sizeof(ulong) + x] = ((byte)(PreConnectionChallengeCounter >> (x * 8))); } // Write IV for (byte x = 0; x < sizeof(ulong); x++) { memory.Buffer[1 + Constants.RUFFLES_PROTOCOL_IDENTIFICATION.Length + (sizeof(ulong) * 2) + x] = ((byte)(PreConnectionChallengeIV >> (x * 8))); } // Print debug if (Logging.CurrentLogLevel <= LogLevel.Debug) { Logging.LogInfo("Resending ConnectionRequest with challenge [Counter=" + PreConnectionChallengeCounter + "] [IV=" + PreConnectionChallengeIV + "] [Time=" + PreConnectionChallengeTimestamp + "] [Hash=" + HashProvider.GetStableHash64(PreConnectionChallengeTimestamp, PreConnectionChallengeCounter, PreConnectionChallengeIV) + "]"); } } else { // Print debug if (Logging.CurrentLogLevel <= LogLevel.Debug) { Logging.LogInfo("Resending ConnectionRequest"); } } SendInternal(new ArraySegment <byte>(memory.Buffer, 0, (int)memory.VirtualCount), true); // Release memory MemoryManager.DeAlloc(memory); } } break; case ConnectionState.RequestingChallenge: { if ((NetTime.Now - HandshakeLastSendTime).TotalMilliseconds > Config.HandshakeResendDelay && HandshakeResendAttempts <= Config.MaxHandshakeResends) { // Resend challenge request SendChallengeRequest(); } } break; case ConnectionState.SolvingChallenge: { if ((NetTime.Now - HandshakeLastSendTime).TotalMilliseconds > Config.HandshakeResendDelay && HandshakeResendAttempts <= Config.MaxHandshakeResends) { // Resend response SendChallengeResponse(); } } break; case ConnectionState.Connected: { if (!HailStatus.HasAcked && (NetTime.Now - HailStatus.LastAttempt).TotalMilliseconds > Config.HandshakeResendDelay && HailStatus.Attempts <= Config.MaxHandshakeResends) { // Resend hail SendHail(); } } break; } } finally { _stateLock.ExitReadLock(); } }
internal static bool Validate(ulong challenge, ulong additionsRequired, byte difficulty) { return(difficulty == 0 || ((HashProvider.GetStableHash64(challenge + additionsRequired) << ((sizeof(ulong) * 8) - difficulty)) >> ((sizeof(ulong) * 8) - difficulty)) == 0); }
internal void HandleChallengeResponse(ulong proposedSolution) { _stateLock.EnterUpgradeableReadLock(); try { if (State == ConnectionState.RequestingChallenge) { ulong claimedCollision = ConnectionChallenge + proposedSolution; // Check if it is solved bool isCollided = ChallengeDifficulty == 0 || ((HashProvider.GetStableHash64(claimedCollision) << ((sizeof(ulong) * 8) - ChallengeDifficulty)) >> ((sizeof(ulong) * 8) - ChallengeDifficulty)) == 0; if (isCollided) { // Success, they completed the hashcash challenge if (Logging.CurrentLogLevel <= LogLevel.Debug) { Logging.LogInfo("Client " + EndPoint + " successfully completed challenge of difficulty " + ChallengeDifficulty); } // Assign the channels for (byte i = 0; i < Config.ChannelTypes.Length; i++) { Channels[i] = Socket.ChannelPool.GetChannel(Config.ChannelTypes[i], i, this, Config, MemoryManager); } // Reset hail status HailStatus = new MessageStatus() { Attempts = 0, HasAcked = false, LastAttempt = NetTime.MinValue }; _stateLock.EnterWriteLock(); try { // Change state to connected State = ConnectionState.Connected; } finally { _stateLock.ExitWriteLock(); } // Save time ConnectionCompleted = NetTime.Now; // Print connected if (Logging.CurrentLogLevel <= LogLevel.Info) { Logging.LogInfo("Client " + EndPoint + " successfully connected"); } // Send hail SendHail(); // Send to userspace Socket.PublishEvent(new NetworkEvent() { Connection = this, Socket = Socket, Type = NetworkEventType.Connect, AllowUserRecycle = false, ChannelId = 0, Data = new ArraySegment <byte>(), InternalMemory = null, SocketReceiveTime = NetTime.Now, MemoryManager = MemoryManager, EndPoint = EndPoint }); } else { // Failed, disconnect them if (Logging.CurrentLogLevel <= LogLevel.Warning) { Logging.LogWarning("Client " + EndPoint + " failed the challenge"); } } } } finally { _stateLock.ExitUpgradeableReadLock(); } }