/// <inheritdoc />
        public virtual async Task <string> ProcessResponseAsync(IAgentContext agentContext, ConnectionResponseMessage response, ConnectionRecord connection)
        {
            Logger.LogTrace(LoggingEvents.AcceptConnectionResponse, "To {1}", connection.MyDid);

            await connection.TriggerAsync(ConnectionTrigger.Response);

            //TODO throw exception or a problem report if the connection request features a did doc that has no indy agent did doc convention featured
            //i.e there is no way for this agent to respond to messages. And or no keys specified
            var connectionObj = await SignatureUtils.UnpackAndVerifyAsync <Common.Connection>(response.ConnectionSig);

            await Did.StoreTheirDidAsync(agentContext.Wallet,
                                         new { did = connectionObj.Did, verkey = connectionObj.DidDoc.Keys[0].PublicKeyBase58 }.ToJson());

            connection.TheirDid = connectionObj.Did;
            connection.TheirVk  = connectionObj.DidDoc.Keys[0].PublicKeyBase58;

            connection.SetTag(TagConstants.LastThreadId, response.GetThreadId());

            if (connectionObj.DidDoc.Services[0] is IndyAgentDidDocService service)
            {
                connection.Endpoint = new AgentEndpoint(service.ServiceEndpoint, null, service.RoutingKeys != null && service.RoutingKeys.Count > 0 ? service.RoutingKeys.ToArray() : null);
            }

            await RecordService.UpdateAsync(agentContext.Wallet, connection);

            EventAggregator.Publish(new ServiceMessageProcessingEvent
            {
                RecordId    = connection.Id,
                MessageType = response.Type,
                ThreadId    = response.GetThreadId()
            });

            return(connection.Id);
        }
        /// <inheritdoc/>
        public async Task <(DidExchangeResponseMessage, ConnectionRecord)> CreateResponseAsync(IAgentContext agentContext, ConnectionRecord connectionRecord)
        {
            await connectionRecord.TriggerAsync(ConnectionTrigger.Response);

            var myDid = await Did.CreateAndStoreMyDidAsync(agentContext.Wallet, "{}");

            connectionRecord.MyDid = DidUtils.ConvertVerkeyToDidKey(myDid.VerKey);
            connectionRecord.MyVk  = myDid.VerKey;

            var provisioningRecord = await _provisioningService.GetProvisioningAsync(agentContext.Wallet);

            var didDoc = new AttachmentContent
            {
                Base64 = connectionRecord.MyDidDoc(provisioningRecord).ToJson().ToBase64Url()
            };
            await didDoc.SignWithJsonWebSignature(agentContext.Wallet, myDid.VerKey);

            var attachment = new Attachment
            {
                Id       = Guid.NewGuid().ToString(),
                MimeType = "application/json",
                Data     = didDoc
            };

            var response = new DidExchangeResponseMessage
            {
                Id     = Guid.NewGuid().ToString(),
                Did    = connectionRecord.MyDid,
                DidDoc = attachment
            };
            await _recordService.UpdateAsync(agentContext.Wallet, connectionRecord);

            return(response, connectionRecord);
        }
示例#3
0
        /// <inheritdoc />
        public virtual async Task <string> AcceptInvitationAsync(Wallet wallet, ConnectionInvitationMessage invitation)
        {
            Logger.LogInformation(LoggingEvents.AcceptInvitation, "Key {0}, Endpoint {1}",
                                  invitation.ConnectionKey, invitation.Endpoint.Uri);

            var my = await Did.CreateAndStoreMyDidAsync(wallet, "{}");

            var connection = new ConnectionRecord
            {
                Endpoint     = invitation.Endpoint,
                MyDid        = my.Did,
                MyVk         = my.VerKey,
                ConnectionId = Guid.NewGuid().ToString().ToLowerInvariant()
            };

            connection.Tags.Add(TagConstants.MyDid, my.Did);

            if (!string.IsNullOrEmpty(invitation.Name) || !string.IsNullOrEmpty(invitation.ImageUrl))
            {
                connection.Alias = new ConnectionAlias
                {
                    Name     = invitation.Name,
                    ImageUrl = invitation.ImageUrl
                };

                if (string.IsNullOrEmpty(invitation.Name))
                {
                    connection.Tags.Add(TagConstants.Alias, invitation.Name);
                }
            }

            await connection.TriggerAsync(ConnectionTrigger.InvitationAccept);

            await RecordService.AddAsync(wallet, connection);

            var provisioning = await ProvisioningService.GetProvisioningAsync(wallet);

            var connectionDetails = new ConnectionDetails
            {
                Did      = my.Did,
                Verkey   = my.VerKey,
                Endpoint = provisioning.Endpoint
            };

            var request = await MessageSerializer.PackSealedAsync <ConnectionRequestMessage>(connectionDetails, wallet,
                                                                                             my.VerKey, invitation.ConnectionKey);

            request.Key  = invitation.ConnectionKey;
            request.Type = MessageUtils.FormatKeyMessageType(invitation.ConnectionKey, MessageTypes.ConnectionRequest);

            var forwardMessage = new ForwardToKeyEnvelopeMessage
            {
                Type    = MessageUtils.FormatKeyMessageType(invitation.ConnectionKey, MessageTypes.ForwardToKey),
                Content = request.ToJson()
            };

            await RouterService.ForwardAsync(forwardMessage, invitation.Endpoint);

            return(connection.GetId());
        }
示例#4
0
        /// <inheritdoc />
        public virtual async Task <(ConnectionRequestMessage, ConnectionRecord)> CreateRequestAsync(IAgentContext agentContext, ConnectionInvitationMessage invitation)
        {
            Logger.LogInformation(LoggingEvents.AcceptInvitation, "Key {0}, Endpoint {1}",
                                  invitation.RecipientKeys[0], invitation.ServiceEndpoint);

            var my = await Did.CreateAndStoreMyDidAsync(agentContext.Wallet, "{}");

            var connection = new ConnectionRecord
            {
                Endpoint = new AgentEndpoint(invitation.ServiceEndpoint, null, invitation.RoutingKeys != null && invitation.RoutingKeys.Count != 0 ? invitation.RoutingKeys[0] : null),
                MyDid    = my.Did,
                MyVk     = my.VerKey,
                Id       = Guid.NewGuid().ToString().ToLowerInvariant()
            };

            if (!string.IsNullOrEmpty(invitation.Label) || !string.IsNullOrEmpty(invitation.ImageUrl))
            {
                connection.Alias = new ConnectionAlias
                {
                    Name     = invitation.Label,
                    ImageUrl = invitation.ImageUrl
                };

                if (string.IsNullOrEmpty(invitation.Label))
                {
                    connection.SetTag(TagConstants.Alias, invitation.Label);
                }
            }

            await connection.TriggerAsync(ConnectionTrigger.InvitationAccept);

            var provisioning = await ProvisioningService.GetProvisioningAsync(agentContext.Wallet);

            var request = new ConnectionRequestMessage
            {
                Connection = new Connection {
                    Did = connection.MyDid, DidDoc = connection.MyDidDoc(provisioning)
                },
                Label    = provisioning.Owner?.Name,
                ImageUrl = provisioning.Owner?.ImageUrl
            };

            // also set image as attachment
            if (provisioning.Owner?.ImageUrl != null)
            {
                request.AddAttachment(new Attachment
                {
                    Nickname = "profile-image",
                    Content  = new AttachmentContent {
                        Links = new[] { provisioning.Owner.ImageUrl }
                    }
                });
            }

            await RecordService.AddAsync(agentContext.Wallet, connection);

            return(request,
                   connection);
        }
        public async Task OnlyRequestAllowsTransitionFromInvitedToNegotiating(ConnectionTrigger trigger)
        {
            var record = new ConnectionRecord();

            Assert.Equal(ConnectionState.Invited, record.State);

            await Assert.ThrowsAsync <InvalidOperationException>(async() => await record.TriggerAsync(trigger));

            Assert.Equal(ConnectionState.Invited, record.State);
        }
        public async Task CannotTransitionFromAbandonedToAnotherState(ConnectionTrigger trigger)
        {
            var record = new ConnectionRecord {
                State = ConnectionState.Abandoned
            };

            await Assert.ThrowsAsync <InvalidOperationException>(async() => await record.TriggerAsync(trigger));

            Assert.Equal(ConnectionState.Abandoned, record.State);
        }
        public async Task CanTransitionFromDisconnetedToNegotiatingWithInvitationAccept()
        {
            var record = new ConnectionRecord();

            Assert.True(ConnectionState.Invited == record.State);

            await record.TriggerAsync(ConnectionTrigger.InvitationAccept);

            Assert.True(ConnectionState.Negotiating == record.State);
        }
        public async Task CanTransitionToAbandonedFromAllStates(ConnectionState state)
        {
            var record = new ConnectionRecord {
                State = state
            };

            Assert.Equal(state, record.State);

            await record.TriggerAsync(ConnectionTrigger.Abandon);

            Assert.Equal(ConnectionState.Abandoned, record.State);
        }
        public async Task CannotTransitionFromNegotiatingToConnectedWithRequest()
        {
            var record = new ConnectionRecord {
                State = ConnectionState.Negotiating
            };

            Assert.True(ConnectionState.Negotiating == record.State);

            await Assert.ThrowsAsync <InvalidOperationException>(async() => await record.TriggerAsync(ConnectionTrigger.Request));

            Assert.True(ConnectionState.Negotiating == record.State);
        }
        public async Task CanTransitionFromNegotiatingToConnectedWithRequest()
        {
            var record = new ConnectionRecord {
                State = ConnectionState.Negotiating
            };

            Assert.True(ConnectionState.Negotiating == record.State);

            await record.TriggerAsync(ConnectionTrigger.Request);

            Assert.True(ConnectionState.Connected == record.State);
        }
        /// <inheritdoc/>
        public async Task <(DidExchangeCompleteMessage, ConnectionRecord)> CreateComplete(IAgentContext agentContext, ConnectionRecord connectionRecord)
        {
            await connectionRecord.TriggerAsync(ConnectionTrigger.Complete);

            await _recordService.UpdateAsync(agentContext.Wallet, connectionRecord);

            var completeMessage = new DidExchangeCompleteMessage
            {
                Id = Guid.NewGuid().ToString()
            };

            return(completeMessage, connectionRecord);
        }
        public async Task OnlyRespondAllowsTransitionFromNegotiatingToConnected(ConnectionTrigger trigger)
        {
            var record = new ConnectionRecord()
            {
                State = ConnectionState.Negotiating
            };

            Assert.Equal(ConnectionState.Negotiating, record.State);

            await Assert.ThrowsAsync <InvalidOperationException>(async() => await record.TriggerAsync(trigger));

            Assert.Equal(ConnectionState.Negotiating, record.State);
        }
        /// <inheritdoc/>
        public async Task <ConnectionRecord> ProcessResponseAsync(IAgentContext agentContext, DidExchangeResponseMessage responseMessage, ConnectionRecord connectionRecord)
        {
            await connectionRecord.TriggerAsync(ConnectionTrigger.Response);

            DidDoc didDoc = null;

            if (responseMessage.DidDoc.Data.Base64 is { } data)
            {
                var isValidSignature = await responseMessage.DidDoc.Data.VerifyJsonWebSignature();

                if (isValidSignature == false)
                {
                    throw new AriesFrameworkException(ErrorCode.InvalidSignatureEncoding,
                                                      "The given JSON web signature is invalid");
                }

                var json = data.FromBase64Url();
                didDoc = json.ToObject <DidDoc>();
            }

            if (didDoc == null)
            {
                throw new NotImplementedException("Response message must provide an attached did document");
            }

            if (didDoc.Keys.All(key => key.Type == DidDocExtensions.DefaultKeyType) == false)
            {
                throw new NotImplementedException($"Only {DidDocExtensions.DefaultKeyType} is supported");
            }

            var indyService = (IndyAgentDidDocService)didDoc.Services.First(service => service is IndyAgentDidDocService);

            var agentEndpoint = new AgentEndpoint(indyService.ServiceEndpoint, null, indyService.RoutingKeys.ToArray());

            connectionRecord.TheirDid = responseMessage.Did;
            connectionRecord.TheirVk  =
                didDoc.Keys.FirstOrDefault(key => key.Controller == responseMessage.Did)?.PublicKeyBase58
                ?? throw new NullReferenceException("Missing public key for controller");
            connectionRecord.Endpoint = agentEndpoint;

            await _recordService.UpdateAsync(agentContext.Wallet, connectionRecord);

            _eventAggregator.Publish(new ServiceMessageProcessingEvent()
            {
                MessageType = responseMessage.Type,
                RecordId    = connectionRecord.Id,
                ThreadId    = responseMessage.GetThreadId()
            });

            return(connectionRecord);
        }
        public async Task CannotTransitionFromInvitedWithAccept()
        {
            var record = new ConnectionRecord {
                State = ConnectionState.Negotiating
            };

            Assert.True(ConnectionState.Negotiating == record.State);

            var exception =
                await Assert.ThrowsAsync <InvalidOperationException>(
                    () => record.TriggerAsync(ConnectionTrigger.InvitationAccept));

            Assert.Equal("Stateless", exception.Source);
        }
        /// <inheritdoc/>
        public async Task <ConnectionRecord> ProcessProblemReportMessage(IAgentContext agentContext, DidExchangeProblemReportMessage problemReportMessage, ConnectionRecord connectionRecord)
        {
            await connectionRecord.TriggerAsync(ConnectionTrigger.Abandon);

            await _recordService.UpdateAsync(agentContext.Wallet, connectionRecord);

            _eventAggregator.Publish(new ServiceMessageProcessingEvent
            {
                MessageType = problemReportMessage.Type,
                RecordId    = connectionRecord.Id,
                ThreadId    = problemReportMessage.GetThreadId()
            });

            return(connectionRecord);
        }
        /// <inheritdoc />
        public virtual async Task <AcceptInvitationResult> AcceptInvitationAsync(IAgentContext agentContext, ConnectionInvitationMessage invitation)
        {
            Logger.LogInformation(LoggingEvents.AcceptInvitation, "Key {0}, Endpoint {1}",
                                  invitation.ConnectionKey, invitation.Endpoint.Uri);

            var my = await Did.CreateAndStoreMyDidAsync(agentContext.Wallet, "{}");

            var connection = new ConnectionRecord
            {
                Endpoint = invitation.Endpoint,
                MyDid    = my.Did,
                MyVk     = my.VerKey,
                Id       = Guid.NewGuid().ToString().ToLowerInvariant()
            };

            if (!string.IsNullOrEmpty(invitation.Name) || !string.IsNullOrEmpty(invitation.ImageUrl))
            {
                connection.Alias = new ConnectionAlias
                {
                    Name     = invitation.Name,
                    ImageUrl = invitation.ImageUrl
                };

                if (string.IsNullOrEmpty(invitation.Name))
                {
                    connection.SetTag(TagConstants.Alias, invitation.Name);
                }
            }

            await connection.TriggerAsync(ConnectionTrigger.InvitationAccept);

            await RecordService.AddAsync(agentContext.Wallet, connection);

            var provisioning = await ProvisioningService.GetProvisioningAsync(agentContext.Wallet);

            return(new AcceptInvitationResult
            {
                Request = new ConnectionRequestMessage
                {
                    Did = my.Did,
                    Verkey = my.VerKey,
                    Endpoint = provisioning.Endpoint,
                    Name = provisioning.Owner?.Name,
                    ImageUrl = provisioning.Owner?.ImageUrl
                },
                Connection = connection
            });
        }
        /// <inheritdoc/>
        public async Task <(DidExchangeProblemReportMessage, ConnectionRecord)> AbandonDidExchange(IAgentContext agentContext, ConnectionRecord connectionRecord)
        {
            await connectionRecord.TriggerAsync(ConnectionTrigger.Abandon);

            await _recordService.UpdateAsync(agentContext.Wallet, connectionRecord);

            var myRole      = connectionRecord.Role;
            var problemCode = myRole == ConnectionRole.Invitee
                ? DidExchangeProblemReportMessage.Error.ResponseNotAccepted
                : DidExchangeProblemReportMessage.Error.RequestNotAccepted;

            var problemReport = new DidExchangeProblemReportMessage {
                ProblemCode = problemCode
            };

            return(problemReport, connectionRecord);
        }
        /// <inheritdoc />
        public async Task <(ConnectionRequestMessage, ConnectionRecord)> CreateRequestAsync(IAgentContext agentContext, ConnectionRecord connection)
        {
            Logger.LogInformation(LoggingEvents.AcceptInvitation, "Key {0}, Endpoint {1}",
                                  connection.Endpoint.Verkey, connection.Endpoint.Uri);

            await connection.TriggerAsync(ConnectionTrigger.Request);

            var provisioning = await ProvisioningService.GetProvisioningAsync(agentContext.Wallet);

            var request = new ConnectionRequestMessage(agentContext.UseMessageTypesHttps)
            {
                Connection = new Common.Connection
                {
                    Did    = connection.MyDid,
                    DidDoc = connection.MyDidDoc(provisioning)
                },
                Label    = provisioning.Owner?.Name,
                ImageUrl = provisioning.Owner?.ImageUrl
            };

            // also set image as attachment
            if (provisioning.Owner?.ImageUrl != null)
            {
                request.AddAttachment(new Attachment
                {
                    Nickname = "profile-image",
                    Data     = new AttachmentContent {
                        Links = new[] { provisioning.Owner.ImageUrl }
                    }
                });
            }

            await RecordService.UpdateAsync(agentContext.Wallet, connection);

            return(request, connection);
        }
        /// <inheritdoc />
        public virtual async Task <string> ProcessRequestAsync(IAgentContext agentContext, ConnectionRequestMessage request, ConnectionRecord connection)
        {
            Logger.LogInformation(LoggingEvents.ProcessConnectionRequest, "Did {0}", request.Connection.Did);

            var my = await Did.CreateAndStoreMyDidAsync(agentContext.Wallet, "{}");

            //TODO throw exception or a problem report if the connection request features a did doc that has no indy agent did doc convention featured
            //i.e there is no way for this agent to respond to messages. And or no keys specified
            await Did.StoreTheirDidAsync(agentContext.Wallet, new { did = request.Connection.Did, verkey = request.Connection.DidDoc.Keys[0].PublicKeyBase58 }.ToJson());

            if (request.Connection.DidDoc.Services != null &&
                request.Connection.DidDoc.Services.Count > 0 &&
                request.Connection.DidDoc.Services[0] is IndyAgentDidDocService service)
            {
                connection.Endpoint = new AgentEndpoint(service.ServiceEndpoint, null, service.RoutingKeys != null && service.RoutingKeys.Count > 0 ? service.RoutingKeys.ToArray() : null);
            }

            connection.TheirDid = request.Connection.Did;
            connection.TheirVk  = request.Connection.DidDoc.Keys[0].PublicKeyBase58;
            connection.MyDid    = my.Did;
            connection.MyVk     = my.VerKey;

            connection.SetTag(TagConstants.LastThreadId, request.Id);

            if (connection.Alias == null)
            {
                connection.Alias = new ConnectionAlias();
            }

            if (!string.IsNullOrEmpty(request.Label) && string.IsNullOrEmpty(connection.Alias.Name))
            {
                connection.Alias.Name = request.Label;
            }

            if (!string.IsNullOrEmpty(request.ImageUrl) && string.IsNullOrEmpty(connection.Alias.ImageUrl))
            {
                connection.Alias.ImageUrl = request.ImageUrl;
            }

            if (!connection.MultiPartyInvitation)
            {
                await connection.TriggerAsync(ConnectionTrigger.Request);

                await RecordService.UpdateAsync(agentContext.Wallet, connection);

                EventAggregator.Publish(new ServiceMessageProcessingEvent
                {
                    RecordId    = connection.Id,
                    MessageType = request.Type,
                    ThreadId    = request.GetThreadId()
                });

                return(connection.Id);
            }
            else
            {
                var newConnection = connection.DeepCopy();
                newConnection.Id = Guid.NewGuid().ToString();
                newConnection.MultiPartyInvitation = false;

                await newConnection.TriggerAsync(ConnectionTrigger.Request);

                await RecordService.AddAsync(agentContext.Wallet, newConnection);

                EventAggregator.Publish(new ServiceMessageProcessingEvent
                {
                    RecordId    = newConnection.Id,
                    MessageType = request.Type,
                    ThreadId    = request.GetThreadId()
                });
                return(newConnection.Id);
            }
        }