private async ValueTask AddConnectionAsync(IConnection connection, OmniAddress address, ConnectionHandshakeType handshakeType, CancellationToken cancellationToken = default) { var status = new ConnectionStatus(connection, address, handshakeType); var myNodeProflie = new NodeProfile(await _connectionController.GetListenEndpointsAsync(cancellationToken)); var myHelloMessage = new NodeExplorerHelloMessage(_myId, myNodeProflie); NodeExplorerHelloMessage?otherHelloMessage = null; var enqueueTask = connection.EnqueueAsync((bufferWriter) => myNodeProflie.Export(bufferWriter, _bytesPool), cancellationToken); var dequeueTask = connection.DequeueAsync((sequence) => otherHelloMessage = NodeExplorerHelloMessage.Import(sequence, _bytesPool), cancellationToken); await ValueTaskHelper.WhenAll(enqueueTask, dequeueTask); if (otherHelloMessage == null) { return; } status.Id = otherHelloMessage.Id; status.NodeProfile = otherHelloMessage.NodeProfile; lock (_lockObject) { _connections.Add(status); } }
private async ValueTask <IReadOnlyCollection <IMessage> > InternalRouteAsync(RouteHierarchy routes, IMessage serializedMessage, bool publish, CancellationToken cancellation) { var localEndPoint = await GetLocalEndPointAsync(cancellation); var tasks = new List <ValueTask <(IMessage response, bool handled)> >(); var handledEndPoints = new HashSet <EndPointAddress>(); _logger?.LogTrace($"Routing a message ({(publish ? "publish" : "p2p")}) with routes: {routes}"); foreach (var route in routes) { var matches = await MatchRouteAsync(route, publish, handledEndPoints, cancellation); if (matches.Any()) { if (!publish) { _logger?.LogTrace($"Found {matches.Count()} matches for route '{route}'."); for (var i = matches.Count - 1; i >= 0; i--) { var(endPoint, options) = matches[i]; if (endPoint == EndPointAddress.UnknownAddress) { continue; } if ((options & RouteRegistrationOptions.PublishOnly) == RouteRegistrationOptions.PublishOnly) { continue; } var(response, handled) = await InternalRouteAsync(route, serializedMessage, publish : false, endPoint, cancellation); if (handled) { return(response.Yield().ToArray()); } } } else { _logger?.LogTrace($"Found {matches.Count()} matches (considering handled end-points) for route '{route}'."); var endPoints = matches.Select(p => p.EndPoint); handledEndPoints.UnionWith(endPoints); tasks.AddRange(endPoints.Select(endPoint => InternalRouteAsync(route, serializedMessage, publish: true, endPoint, cancellation))); } } } var result = await ValueTaskHelper.WhenAll(tasks, preserveOrder : false); _logger?.LogTrace($"Successfully routed a message ({(publish ? "publish" : "p2p")}) with routes: {routes}"); return(result.Where(p => p.handled).Select(p => p.response).ToArray()); }
private async ValueTask HelloAsync(CancellationToken cancellationToken) { HelloMessage sendHelloMessage; HelloMessage?receiveHelloMessage = null; { sendHelloMessage = new HelloMessage(new[] { _version }); var enqueueTask = _connection.EnqueueAsync((bufferWriter) => sendHelloMessage.Export(bufferWriter, _bytesPool), cancellationToken); var dequeueTask = _connection.DequeueAsync((sequence) => receiveHelloMessage = HelloMessage.Import(sequence, _bytesPool), cancellationToken); await ValueTaskHelper.WhenAll(enqueueTask, dequeueTask); if (receiveHelloMessage is null) { throw new NullReferenceException(); } } _version = GetOverlapMaxEnum(sendHelloMessage.Versions, receiveHelloMessage.Versions); }
private async Task <bool> DispatchCoreAsync(ICommit commit, CancellationToken cancellation) { IEnumerable <object> events; using (var scope = _serviceProvider.CreateScope()) { var storageEngine = scope.ServiceProvider.GetRequiredService <IEntityStorageEngine>(); var settingsResolver = scope.ServiceProvider.GetRequiredService <ISerializerSettingsResolver>(); var jsonSerializer = JsonSerializer.Create(settingsResolver.ResolveSettings(storageEngine)); object Deserialize(byte[] data) { if (data == null) { return(null); } var str = CompressionHelper.Unzip(data); using (var textReader = new StringReader(str)) { return(jsonSerializer.Deserialize(textReader, typeof(object))); } } // We need to evaluate the enumerable here, to ensure that the Deserialize method is not called outside the scope. events = commit.Events.Select(p => Deserialize(p.Body as byte[])).ToList(); } var dispatchResults = await ValueTaskHelper.WhenAll(events.Select(p => DispatchEventAsync(p, cancellation)), preserveOrder : false); var dispatchResult = new AggregateDispatchResult(dispatchResults); if (!dispatchResult.IsSuccess) { _logger?.LogWarning($"Dispatching commit {commit.Headers[EntityStorageEngine.ConcurrencyTokenHeaderKey]} of stream {commit.StreamId} failed for reason: {dispatchResult.Message}."); } return(dispatchResult.IsSuccess); }
public async ValueTask Handshake(CancellationToken cancellationToken = default) { ProfileMessage myProfileMessage; ProfileMessage?otherProfileMessage = null; { { var sessionId = new byte[32]; using (var randomNumberGenerator = RandomNumberGenerator.Create()) { randomNumberGenerator.GetBytes(sessionId); } myProfileMessage = new ProfileMessage( sessionId, (_passwords.Count == 0) ? AuthenticationType.None : AuthenticationType.Password, new[] { KeyExchangeAlgorithm.EcDh_P521_Sha2_256 }, new[] { KeyDerivationAlgorithm.Pbkdf2 }, new[] { CryptoAlgorithm.Aes_Gcm_256 }, new[] { HashAlgorithm.Sha2_256 }); } var enqueueTask = _connection.EnqueueAsync((bufferWriter) => myProfileMessage.Export(bufferWriter, _bytesPool), cancellationToken); var dequeueTask = _connection.DequeueAsync((sequence) => otherProfileMessage = ProfileMessage.Import(sequence, _bytesPool), cancellationToken); await ValueTaskHelper.WhenAll(enqueueTask, dequeueTask); if (otherProfileMessage is null) { throw new NullReferenceException(); } if (myProfileMessage.AuthenticationType != otherProfileMessage.AuthenticationType) { throw new OmniSecureConnectionException("AuthenticationType does not match."); } } var keyExchangeAlgorithm = GetOverlapMaxEnum(myProfileMessage.KeyExchangeAlgorithms, otherProfileMessage.KeyExchangeAlgorithms); var keyDerivationAlgorithm = GetOverlapMaxEnum(myProfileMessage.KeyDerivationAlgorithms, otherProfileMessage.KeyDerivationAlgorithms); var cryptoAlgorithm = GetOverlapMaxEnum(myProfileMessage.CryptoAlgorithms, otherProfileMessage.CryptoAlgorithms); var hashAlgorithm = GetOverlapMaxEnum(myProfileMessage.HashAlgorithms, otherProfileMessage.HashAlgorithms); if (!EnumHelper.IsValid(keyExchangeAlgorithm)) { throw new OmniSecureConnectionException("key exchange algorithm does not match."); } if (!EnumHelper.IsValid(keyDerivationAlgorithm)) { throw new OmniSecureConnectionException("key derivation algorithm does not match."); } if (!EnumHelper.IsValid(cryptoAlgorithm)) { throw new OmniSecureConnectionException("Crypto algorithm does not match."); } if (!EnumHelper.IsValid(hashAlgorithm)) { throw new OmniSecureConnectionException("Hash algorithm does not match."); } ReadOnlyMemory <byte> secret = null; if (keyExchangeAlgorithm.HasFlag(KeyExchangeAlgorithm.EcDh_P521_Sha2_256)) { var myAgreement = OmniAgreement.Create(OmniAgreementAlgorithmType.EcDh_P521_Sha2_256); OmniAgreementPrivateKey myAgreementPrivateKey; OmniAgreementPublicKey? otherAgreementPublicKey = null; { { myAgreementPrivateKey = myAgreement.GetOmniAgreementPrivateKey(); var enqueueTask = _connection.EnqueueAsync((bufferWriter) => myAgreement.GetOmniAgreementPublicKey().Export(bufferWriter, _bytesPool), cancellationToken); var dequeueTask = _connection.DequeueAsync((sequence) => otherAgreementPublicKey = OmniAgreementPublicKey.Import(sequence, _bytesPool), cancellationToken); await ValueTaskHelper.WhenAll(enqueueTask, dequeueTask); if (otherAgreementPublicKey is null) { throw new NullReferenceException(); } if ((DateTime.UtcNow - otherAgreementPublicKey.CreationTime.ToDateTime()).TotalMinutes > 30) { throw new OmniSecureConnectionException("Agreement public key has Expired."); } } if (_passwords.Count > 0) { AuthenticationMessage myAuthenticationMessage; AuthenticationMessage?otherAuthenticationMessage = null; { { var myHashAndPasswordList = this.GetHashes(myProfileMessage, myAgreement.GetOmniAgreementPublicKey(), hashAlgorithm).ToList(); _random.Shuffle(myHashAndPasswordList); myAuthenticationMessage = new AuthenticationMessage(myHashAndPasswordList.Select(n => n.Item1).ToArray()); } var enqueueTask = _connection.EnqueueAsync((bufferWriter) => myAuthenticationMessage.Export(bufferWriter, _bytesPool), cancellationToken); var dequeueTask = _connection.DequeueAsync((sequence) => otherAuthenticationMessage = AuthenticationMessage.Import(sequence, _bytesPool), cancellationToken); await ValueTaskHelper.WhenAll(enqueueTask, dequeueTask); if (otherAuthenticationMessage is null) { throw new NullReferenceException(); } var matchedPasswords = new List <string>(); { var equalityComparer = new CustomEqualityComparer <ReadOnlyMemory <byte> >((x, y) => BytesOperations.Equals(x.Span, y.Span), (x) => Fnv1_32.ComputeHash(x.Span)); var receiveHashes = new HashSet <ReadOnlyMemory <byte> >(otherAuthenticationMessage.Hashes, equalityComparer); foreach (var(hash, password) in this.GetHashes(otherProfileMessage, otherAgreementPublicKey, hashAlgorithm)) { if (receiveHashes.Contains(hash)) { matchedPasswords.Add(password); } } } if (matchedPasswords.Count == 0) { throw new OmniSecureConnectionException("Password does not match."); } _matchedPasswords = matchedPasswords.ToArray(); } } } if (hashAlgorithm.HasFlag(HashAlgorithm.Sha2_256)) { secret = OmniAgreement.GetSecret(otherAgreementPublicKey, myAgreementPrivateKey); } } byte[] myCryptoKey; byte[] otherCryptoKey; byte[] myNonce; byte[] otherNonce; if (keyDerivationAlgorithm.HasFlag(KeyDerivationAlgorithm.Pbkdf2)) { byte[] xorSessionId = new byte[Math.Max(myProfileMessage.SessionId.Length, otherProfileMessage.SessionId.Length)]; BytesOperations.Xor(myProfileMessage.SessionId.Span, otherProfileMessage.SessionId.Span, xorSessionId); int cryptoKeyLength = 0; int nonceLength = 0; if (cryptoAlgorithm.HasFlag(CryptoAlgorithm.Aes_Gcm_256)) { cryptoKeyLength = 32; nonceLength = 12; } myCryptoKey = new byte[cryptoKeyLength]; otherCryptoKey = new byte[cryptoKeyLength]; myNonce = new byte[nonceLength]; otherNonce = new byte[nonceLength]; var kdfResult = new byte[(cryptoKeyLength + nonceLength) * 2]; if (hashAlgorithm.HasFlag(HashAlgorithm.Sha2_256)) { Pbkdf2_Sha2_256.TryComputeHash(secret.Span, xorSessionId, 1024, kdfResult); } using (var stream = new MemoryStream(kdfResult)) { if (_type == OmniSecureConnectionType.Connected) { stream.Read(myCryptoKey, 0, myCryptoKey.Length); stream.Read(otherCryptoKey, 0, otherCryptoKey.Length); stream.Read(myNonce, 0, myNonce.Length); stream.Read(otherNonce, 0, otherNonce.Length); } else if (_type == OmniSecureConnectionType.Accepted) { stream.Read(otherCryptoKey, 0, otherCryptoKey.Length); stream.Read(myCryptoKey, 0, myCryptoKey.Length); stream.Read(otherNonce, 0, otherNonce.Length); stream.Read(myNonce, 0, myNonce.Length); } } } else { throw new NotSupportedException(nameof(keyDerivationAlgorithm)); } _state = new State(cryptoAlgorithm, hashAlgorithm, myCryptoKey, otherCryptoKey, myNonce, otherNonce); }