Beispiel #1
0
        // Should be called only once during application startup
        public static void Initialize()
        {
            // NOTE: this is the 5 digit short code associated with your specific permutation .DLLs
            // (See partial class sibling file: SpAgent.Generated.cs)
            _agent = AgentContext.For( PermutationShortId );
            _agent.Configure( x => x
                // NOTE: Sp.Agent expects this folder to be initialized, i.e. your .msi installer needs to create and permission this folder
                .WithLocalSharedStore( SharedDirectoryInitializedByInstaller().FullName )
                .CompleteWithDefaults() );

            _product = _agent.ProductContextFor( ProductName,ProductVersion );

            //Verify that the license store has been initialized
            Product.Stores.Initialization().Verify();
        }
 /// <inheritdoc />
 public Task <(CredentialIssueMessage, CredentialRecord)> CreateCredentialAsync(IAgentContext agentContext, string
                                                                                credentialId)
 {
     return(CreateCredentialAsync(agentContext, credentialId, values: null));
 }
        /// <inheritdoc />
        public async Task <(CredentialRequestMessage, CredentialRecord)> CreateRequestAsync(IAgentContext agentContext,
                                                                                            string credentialId)
        {
            var credential = await GetAsync(agentContext, credentialId);

            if (credential.State != CredentialState.Offered)
            {
                throw new AriesFrameworkException(ErrorCode.RecordInInvalidState,
                                                  $"Credential state was invalid. Expected '{CredentialState.Offered}', found '{credential.State}'");
            }

            string proverDid = null;

            if (credential.ConnectionId != null)
            {
                var connection = await ConnectionService.GetAsync(agentContext, credential.ConnectionId);

                proverDid = connection.MyDid;
            }

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

                proverDid = newDid.Did;
            }

            var definition = await LedgerService.LookupDefinitionAsync(agentContext, credential.CredentialDefinitionId);

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

            var request = await AnonCreds.ProverCreateCredentialReqAsync(
                wallet : agentContext.Wallet,
                proverDid : proverDid,
                credOfferJson : credential.OfferJson,
                credDefJson : definition.ObjectJson,
                masterSecretId : provisioning.MasterSecretId);

            // Update local credential record with new info
            credential.CredentialRequestMetadataJson = request.CredentialRequestMetadataJson;
            await credential.TriggerAsync(CredentialTrigger.Request);

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

            var threadId = credential.GetTag(TagConstants.LastThreadId);

            var response = new CredentialRequestMessage(agentContext.UseMessageTypesHttps)
            {
                // The comment was required by Aca-py, even though it is declared optional in RFC-0036
                // Was added for interoperability
                Comment  = "",
                Requests = new[]
                {
                    new Attachment
                    {
                        Id       = "libindy-cred-request-0",
                        MimeType = CredentialMimeTypes.ApplicationJsonMimeType,
                        Data     = new AttachmentContent
                        {
                            Base64 = request.CredentialRequestJson.GetUTF8Bytes().ToBase64String()
                        }
                    }
                }
            };

            response.ThreadFrom(threadId);
            return(response, credential);
        }
Beispiel #4
0
        /// <inheritdoc />
        public async Task <string> CreateBackupAsync(IAgentContext context, string seed)
        {
            if (seed.Length != 32)
            {
                throw new ArgumentException($"{nameof(seed)} should be 32 characters");
            }

            var path = Path.Combine(Path.GetTempPath(), seed);

            var provRecord = await provisioningService.GetProvisioningAsync(context.Wallet);

            var publicKey = provRecord.GetTag("backup_key");

            if (string.IsNullOrEmpty(publicKey))
            {
                publicKey = await Crypto.CreateKeyAsync(context.Wallet, new { seed }.ToJson());

                provRecord.SetTag("backup_key", publicKey);
                await walletRecordService.UpdateAsync(context.Wallet, provRecord);
            }

            var json = new { path, key = seed }.ToJson();

            await context.Wallet.ExportAsync(json);

            var bytesArray = await Task.Run(() => File.ReadAllBytes(path));

            var signedBytesArray = await Crypto.SignAsync(context.Wallet, publicKey, bytesArray);

            var payload = bytesArray.ToBase64String();

            var backupMessage = new StoreBackupAgentMessage
            {
                BackupId         = publicKey,
                PayloadSignature = signedBytesArray.ToBase64String(),
                Payload          = new List <Attachment>()
                {
                    new Attachment
                    {
                        Id       = "libindy-backup-request-0",
                        MimeType = CredentialMimeTypes.ApplicationJsonMimeType,
                        Data     = new AttachmentContent
                        {
                            Base64 = payload
                        }
                    }
                }
            };

            var connection = await GetMediatorConnectionAsync(context).ConfigureAwait(false);

            if (connection == null)
            {
                throw new AriesFrameworkException(ErrorCode.RecordNotFound,
                                                  "Couldn't locate a connection to mediator agent");
            }

            await messageService
            .SendReceiveAsync <StoreBackupResponseAgentMessage>(context.Wallet, backupMessage, connection)
            .ConfigureAwait(false);

            return(publicKey);
        }
Beispiel #5
0
        /// <inheritdoc />
        public virtual async Task <bool> DeleteAsync(IAgentContext agentContext, string connectionId)
        {
            Logger.LogInformation(LoggingEvents.DeleteConnection, "ConnectionId {0}", connectionId);

            return(await RecordService.DeleteAsync <ConnectionRecord>(agentContext.Wallet, connectionId));
        }
Beispiel #6
0
        /// <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[0] : 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.InvitationAccept);

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

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

                return(connection.Id);
            }

            var newConnection = connection.DeepCopy();

            newConnection.Id = Guid.NewGuid().ToString();

            await newConnection.TriggerAsync(ConnectionTrigger.InvitationAccept);

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

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

            return(newConnection.Id);
        }
Beispiel #7
0
 public static void SampleStore(this IAgentContext context, string key, object data)
 {
     context.SendServiceMessage("Sample", Tuple.Create(key, data));
 }
Beispiel #8
0
        private async Task <MessageContext> ProcessMessage(IAgentContext agentContext, MessageContext messageContext)
        {
            UnpackResult           unpacked = null;
            UnpackedMessageContext inboundMessageContext = null;

            if (messageContext is PackedMessageContext packedMessageContext)
            {
                (inboundMessageContext, unpacked) = await UnpackAsync(agentContext, packedMessageContext);

                Logger.LogInformation($"Agent Message Received : {inboundMessageContext.ToJson()}");
            }

            if (Handlers.Where(handler => handler != null).FirstOrDefault(
                    handler => handler.SupportedMessageTypes.Any(
                        type => type == inboundMessageContext.GetMessageType())) is
                IMessageHandler messageHandler)
            {
                Logger.LogDebug("Processing message type {MessageType}, {MessageData}",
                                inboundMessageContext.GetMessageType(),
                                inboundMessageContext.Payload.GetUTF8String());

                // Process message in handler
                AgentMessage response;
                try
                {
                    response = await messageHandler.ProcessAsync(agentContext, inboundMessageContext);
                }
                catch (AriesFrameworkException e)
                {
                    throw new AriesFrameworkException(e.ErrorCode, e.Message, inboundMessageContext.ContextRecord, inboundMessageContext.Connection);
                }

                // Process message with any registered middlewares
                foreach (var middleware in Middlewares)
                {
                    await middleware.OnMessageAsync(agentContext, inboundMessageContext);
                }

                if (response != null)
                {
                    if (inboundMessageContext.ReturnRoutingRequested())
                    {
                        var result = inboundMessageContext.Connection != null
                            ? await CryptoUtils.PackAsync(agentContext.Wallet, inboundMessageContext.Connection.TheirVk, response.ToByteArray())
                            : await CryptoUtils.PackAsync(agentContext.Wallet, unpacked.SenderVerkey, response.ToByteArray());

                        return(new PackedMessageContext(result));
                    }
                    if (inboundMessageContext.Connection != null)
                    {
                        await MessageService.SendAsync(agentContext.Wallet, response, inboundMessageContext.Connection);
                    }
                    else
                    {
                        Logger.LogWarning("Return response available, but connection was not found or was in invalid state");
                    }
                }
                return(null);
            }

            throw new AriesFrameworkException(ErrorCode.InvalidMessage,
                                              $"Couldn't locate a message handler for type {inboundMessageContext.GetMessageType()}");
        }
        /// <inheritdoc />
        public async Task InitializeAsync()
        {
            await Host.StartAsync();

            Context = await Host.Services.GetService <IAgentProvider>().GetContextAsync();
        }
Beispiel #10
0
 /// <summary>Retrieves a list of rejected/declined credentials.
 /// Rejected credentials will only be found in the issuer wallet, as the rejection is not communicated back to the holder.</summary>
 /// <param name="credentialService">Credential service.</param>
 /// <param name="context">The context.</param>
 /// <param name="count">Count.</param>
 /// <returns>The rejected credentials async.</returns>
 public static Task <List <CredentialRecord> > ListRejectedCredentialsAsync(
     this ICredentialService credentialService, IAgentContext context, int count = 100)
 => credentialService.ListAsync(context,
                                SearchQuery.Equal(nameof(CredentialRecord.State), CredentialState.Rejected.ToString("G")), count);
Beispiel #11
0
 public Task <string> ProcessResponseAsync(IAgentContext agentContext, ConnectionResponseMessage response, ConnectionRecord connection)
 {
     throw new System.NotImplementedException();
 }
Beispiel #12
0
 public Task <bool> DeleteAsync(IAgentContext agentContext, string connectionId)
 {
     throw new System.NotImplementedException();
 }
Beispiel #13
0
 public Task <(ConnectionResponseMessage, ConnectionRecord)> CreateResponseAsync(IAgentContext agentContext, string connectionId)
 {
     throw new System.NotImplementedException();
 }
Beispiel #14
0
 public Task <(ConnectionRequestMessage, ConnectionRecord)> CreateRequestAsync(IAgentContext agentContext, ConnectionInvitationMessage offer)
 {
     throw new System.NotImplementedException();
 }
Beispiel #15
0
 public Task RevokeInvitationAsync(IAgentContext agentContext, string invitationId)
 {
     throw new System.NotImplementedException();
 }
Beispiel #16
0
        public static async Task <(ConnectionRecord firstParty, ConnectionRecord secondParty)> EstablishConnectionAsync(
            IConnectionService connectionService,
            IProducerConsumerCollection <AgentMessage> _messages,
            IAgentContext firstContext,
            IAgentContext secondContext,
            ConnectionInvitationMessage inviteMessage = null,
            string inviteeconnectionId = null)
        {
            // Create invitation by the issuer
            var connectionSecondId = Guid.NewGuid().ToString();

            var inviteConfig = new InviteConfiguration
            {
                ConnectionId = connectionSecondId,
                MyAlias      = new ConnectionAlias
                {
                    Name     = "Issuer",
                    ImageUrl = "www.issuerdomain.com/profilephoto"
                },
                TheirAlias = new ConnectionAlias
                {
                    Name     = "Holder",
                    ImageUrl = "www.holderdomain.com/profilephoto"
                }
            };

            if (inviteMessage == null)
            {
                (inviteMessage, _) = await connectionService.CreateInvitationAsync(firstContext, inviteConfig);
            }

            var connectionFirst = await connectionService.GetAsync(firstContext, inviteeconnectionId ?? inviteConfig.ConnectionId);

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

            // Holder accepts invitation and sends a message request
            (var request, var inviteeConnection) = await connectionService.CreateRequestAsync(secondContext, inviteMessage);

            var connectionSecond = inviteeConnection;

            _messages.TryAdd(request);

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

            // Issuer processes incoming message
            var issuerMessage = _messages.OfType <ConnectionRequestMessage>().FirstOrDefault();

            Assert.NotNull(issuerMessage);

            // Issuer processes the connection request by storing it and accepting it if auto connection flow is enabled
            connectionSecondId = await connectionService.ProcessRequestAsync(firstContext, issuerMessage, connectionFirst);

            connectionFirst = await connectionService.GetAsync(firstContext, connectionSecondId);

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

            // Issuer accepts the connection request
            (var response, var _) = await connectionService.CreateResponseAsync(firstContext, connectionSecondId);

            _messages.TryAdd(response);

            connectionFirst = await connectionService.GetAsync(firstContext, connectionSecondId);

            Assert.Equal(ConnectionState.Connected, connectionFirst.State);

            // Holder processes incoming message
            var holderMessage = _messages.OfType <ConnectionResponseMessage>().FirstOrDefault();

            Assert.NotNull(holderMessage);

            // Holder processes the response message by accepting it
            await connectionService.ProcessResponseAsync(secondContext, holderMessage, connectionSecond);

            // Retrieve updated connection state for both issuer and holder
            connectionFirst = await connectionService.GetAsync(firstContext, connectionFirst.Id);

            connectionSecond = await connectionService.GetAsync(secondContext, connectionSecond.Id);

            return(connectionFirst, connectionSecond);
        }
Beispiel #17
0
 /// <summary>
 /// Configures the agent wallet.
 /// </summary>
 /// <returns>The async.</returns>
 /// <param name="record">Record.</param>
 /// <param name="context">Context.</param>
 public virtual Task ConfigureAsync(ProvisioningRecord record, IAgentContext context) => Task.CompletedTask;
Beispiel #18
0
 public static void SampleRestore(this IAgentContext context, string key)
 {
     context.SendServiceMessage("Sample", Tuple.Create(key));
 }
Beispiel #19
0
        /// <inheritdoc />
        public virtual async Task <string> CreateCredentialDefinitionAsync(IAgentContext context, string schemaId,
                                                                           string issuerDid, string tag, bool supportsRevocation, int maxCredentialCount, Uri tailsBaseUri)
        {
            var definitionRecord = new DefinitionRecord();
            var schema           = await LedgerService.LookupSchemaAsync(await context.Pool, schemaId);

            var credentialDefinition = await AnonCreds.IssuerCreateAndStoreCredentialDefAsync(context.Wallet, issuerDid,
                                                                                              schema.ObjectJson, tag, null, new { support_revocation = supportsRevocation }.ToJson());

            var paymentInfo = await paymentService.GetTransactionCostAsync(context, TransactionTypes.CRED_DEF);

            await LedgerService.RegisterCredentialDefinitionAsync(context : context,
                                                                  submitterDid : issuerDid,
                                                                  data : credentialDefinition.CredDefJson,
                                                                  paymentInfo : paymentInfo);

            if (paymentInfo != null)
            {
                await RecordService.UpdateAsync(context.Wallet, paymentInfo.PaymentAddress);
            }

            definitionRecord.SupportsRevocation = supportsRevocation;
            definitionRecord.Id       = credentialDefinition.CredDefId;
            definitionRecord.SchemaId = schemaId;

            if (supportsRevocation)
            {
                definitionRecord.MaxCredentialCount = maxCredentialCount;
                var tailsHandle = await TailsService.CreateTailsAsync();

                var revRegDefConfig =
                    new { issuance_type = "ISSUANCE_ON_DEMAND", max_cred_num = maxCredentialCount }.ToJson();
                var revocationRegistry = await AnonCreds.IssuerCreateAndStoreRevocRegAsync(context.Wallet, issuerDid, null,
                                                                                           "Tag2", credentialDefinition.CredDefId, revRegDefConfig, tailsHandle);

                var revocationRecord = new RevocationRegistryRecord
                {
                    Id = revocationRegistry.RevRegId,
                    CredentialDefinitionId = credentialDefinition.CredDefId
                };

                // Update tails location URI
                var revocationDefinition = JObject.Parse(revocationRegistry.RevRegDefJson);
                if (tailsBaseUri != null)
                {
                    var tailsfile = Path.GetFileName(revocationDefinition["value"]["tailsLocation"].ToObject <string>());
                    revocationDefinition["value"]["tailsLocation"] = new Uri(tailsBaseUri + tailsfile).ToString();
                    revocationRecord.TailsFile = tailsfile;
                }

                paymentInfo = await paymentService.GetTransactionCostAsync(context, TransactionTypes.REVOC_REG_DEF);

                await LedgerService.RegisterRevocationRegistryDefinitionAsync(context : context,
                                                                              submitterDid : issuerDid,
                                                                              data : revocationDefinition.ToString(),
                                                                              paymentInfo : paymentInfo);

                if (paymentInfo != null)
                {
                    await RecordService.UpdateAsync(context.Wallet, paymentInfo.PaymentAddress);
                }

                paymentInfo = await paymentService.GetTransactionCostAsync(context, TransactionTypes.REVOC_REG_ENTRY);

                await LedgerService.SendRevocationRegistryEntryAsync(context : context,
                                                                     issuerDid : issuerDid,
                                                                     revocationRegistryDefinitionId : revocationRegistry.RevRegId,
                                                                     revocationDefinitionType : "CL_ACCUM",
                                                                     value : revocationRegistry.RevRegEntryJson,
                                                                     paymentInfo : paymentInfo);

                if (paymentInfo != null)
                {
                    await RecordService.UpdateAsync(context.Wallet, paymentInfo.PaymentAddress);
                }

                await RecordService.AddAsync(context.Wallet, revocationRecord);
            }

            await RecordService.AddAsync(context.Wallet, definitionRecord);

            return(credentialDefinition.CredDefId);
        }
Beispiel #20
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);
        }
 protected override Task <AgentMessage> ProcessAsync(DiscoveryQueryMessage message, IAgentContext agentContext, UnpackedMessageContext messageContext)
 {
     return(Task.FromResult <AgentMessage>(_discoveryService.CreateQueryResponse(agentContext, message)));
 }
Beispiel #22
0
        /// <inheritdoc />
        public virtual async Task <(ConnectionResponseMessage, ConnectionRecord)> CreateResponseAsync(IAgentContext agentContext, string connectionId)
        {
            Logger.LogInformation(LoggingEvents.AcceptConnectionRequest, "ConnectionId {0}", connectionId);

            var connection = await GetAsync(agentContext, connectionId);

            if (connection.State != ConnectionState.Negotiating)
            {
                throw new AgentFrameworkException(ErrorCode.RecordInInvalidState,
                                                  $"Connection state was invalid. Expected '{ConnectionState.Negotiating}', found '{connection.State}'");
            }

            await Pairwise.CreateAsync(agentContext.Wallet, connection.TheirDid, connection.MyDid, connection.Endpoint.ToJson());

            await connection.TriggerAsync(ConnectionTrigger.Request);

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

            // Send back response message
            var provisioning = await ProvisioningService.GetProvisioningAsync(agentContext.Wallet);

            var connectionData = new Connection
            {
                Did    = connection.MyDid,
                DidDoc = connection.MyDidDoc(provisioning)
            };

            var sigData = await SignatureUtils.SignData(agentContext, connectionData, connection.GetTag(TagConstants.ConnectionKey));

            var threadId = connection.GetTag(TagConstants.LastThreadId);

            var response = new ConnectionResponseMessage {
                ConnectionSig = sigData
            };

            response.ThreadFrom(threadId);

            return(response, connection);
        }
Beispiel #23
0
        /// <inheritdoc />
        public async Task <AgentOptions> RestoreFromBackupAsync(IAgentContext context, string seed)
        {
            var backupAttachments = await RetrieveBackupAsync(context, seed);

            return(await RestoreFromBackupAsync(context, seed, backupAttachments));
        }
Beispiel #24
0
        /// <inheritdoc />
        public virtual async Task <(ConnectionInvitationMessage, ConnectionRecord)> CreateInvitationAsync(IAgentContext agentContext,
                                                                                                          InviteConfiguration config = null)
        {
            var connectionId = !string.IsNullOrEmpty(config?.ConnectionId)
                ? config.ConnectionId
                : Guid.NewGuid().ToString();

            config = config ?? new InviteConfiguration();

            Logger.LogInformation(LoggingEvents.CreateInvitation, "ConnectionId {0}", connectionId);

            var connectionKey = await Crypto.CreateKeyAsync(agentContext.Wallet, "{}");

            var connection = new ConnectionRecord {
                Id = connectionId
            };

            connection.SetTag(TagConstants.ConnectionKey, connectionKey);

            if (config.AutoAcceptConnection)
            {
                connection.SetTag(TagConstants.AutoAcceptConnection, "true");
            }

            connection.MultiPartyInvitation = config.MultiPartyInvitation;

            if (!config.MultiPartyInvitation)
            {
                connection.Alias = config.TheirAlias;
                if (!string.IsNullOrEmpty(config.TheirAlias.Name))
                {
                    connection.SetTag(TagConstants.Alias, config.TheirAlias.Name);
                }
            }

            foreach (var tag in config.Tags)
            {
                connection.SetTag(tag.Key, tag.Value);
            }

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

            if (string.IsNullOrEmpty(provisioning.Endpoint.Uri))
            {
                throw new AgentFrameworkException(ErrorCode.RecordInInvalidState, "Provision record has no endpoint information specified");
            }

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

            return(new ConnectionInvitationMessage
            {
                ServiceEndpoint = provisioning.Endpoint.Uri,
                RoutingKeys = provisioning.Endpoint.Verkey != null ? new[] { provisioning.Endpoint.Verkey } : null,
                RecipientKeys = new[] { connectionKey },
                Label = config.MyAlias.Name ?? provisioning.Owner.Name,
                ImageUrl = config.MyAlias.ImageUrl ?? provisioning.Owner.ImageUrl
            }, connection);
        }
Beispiel #25
0
        /// <inheritdoc />
        public async Task <(CredentialIssueMessage, CredentialRecord)> CreateCredentialAsync(IAgentContext agentContext,
                                                                                             string credentialId, IEnumerable <CredentialPreviewAttribute> values)
        {
            var credentialRecord = await GetAsync(agentContext, credentialId);

            if (credentialRecord.State != CredentialState.Requested)
            {
                throw new AriesFrameworkException(ErrorCode.RecordInInvalidState,
                                                  $"Credential state was invalid. Expected '{CredentialState.Requested}', found '{credentialRecord.State}'");
            }
            if (values != null && values.Any())
            {
                credentialRecord.CredentialAttributesValues = values;
            }

            var definitionRecord =
                await SchemaService.GetCredentialDefinitionAsync(agentContext.Wallet,
                                                                 credentialRecord.CredentialDefinitionId);

            if (credentialRecord.ConnectionId != null)
            {
                var connection = await ConnectionService.GetAsync(agentContext, credentialRecord.ConnectionId);

                if (connection.State != ConnectionState.Connected)
                {
                    throw new AriesFrameworkException(ErrorCode.RecordInInvalidState,
                                                      $"Connection state was invalid. Expected '{ConnectionState.Connected}', found '{connection.State}'");
                }
            }

            var(issuedCredential, revocationRecord) = await IssueCredentialSafeAsync(agentContext, definitionRecord,
                                                                                     credentialRecord);

            if (definitionRecord.SupportsRevocation)
            {
                var provisioning = await ProvisioningService.GetProvisioningAsync(agentContext.Wallet);

                var paymentInfo =
                    await PaymentService.GetTransactionCostAsync(agentContext, TransactionTypes.REVOC_REG_ENTRY);

                if (issuedCredential.RevocRegDeltaJson != null)
                {
                    await LedgerService.SendRevocationRegistryEntryAsync(
                        context : agentContext,
                        issuerDid : provisioning.IssuerDid,
                        revocationRegistryDefinitionId : revocationRecord.Id,
                        revocationDefinitionType : "CL_ACCUM",
                        value : issuedCredential.RevocRegDeltaJson,
                        paymentInfo : paymentInfo);
                }

                // Store data relevant for credential revocation
                credentialRecord.CredentialRevocationId = issuedCredential.RevocId;
                credentialRecord.RevocationRegistryId   = revocationRecord.Id;

                if (paymentInfo != null)
                {
                    await RecordService.UpdateAsync(agentContext.Wallet, paymentInfo.PaymentAddress);
                }
            }

            await credentialRecord.TriggerAsync(CredentialTrigger.Issue);

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

            var threadId = credentialRecord.GetTag(TagConstants.LastThreadId);

            var credentialMsg = new CredentialIssueMessage
            {
                Credentials = new[]
                {
                    new Attachment
                    {
                        Id       = "libindy-cred-0",
                        MimeType = CredentialMimeTypes.ApplicationJsonMimeType,
                        Data     = new AttachmentContent
                        {
                            Base64 = issuedCredential.CredentialJson
                                     .GetUTF8Bytes()
                                     .ToBase64String()
                        }
                    }
                }
            };

            credentialMsg.ThreadFrom(threadId);
            return(credentialMsg, credentialRecord);
        }
 /// <inheritdoc />
 public virtual Task <List <CredentialRecord> > ListAsync(IAgentContext agentContext, ISearchQuery query = null,
                                                          int count = 100, int skip = 0) =>
 RecordService.SearchAsync <CredentialRecord>(agentContext.Wallet, query, null, count, skip);
Beispiel #27
0
        private async Task <(IssuerCreateCredentialResult, RevocationRegistryRecord)> IssueCredentialSafeAsync(
            IAgentContext agentContext,
            DefinitionRecord definitionRecord,
            CredentialRecord credentialRecord)
        {
            BlobStorageReader tailsReader = null;

            RevocationRegistryRecord revocationRecord = null;

            if (definitionRecord.SupportsRevocation)
            {
                revocationRecord =
                    await RecordService.GetAsync <RevocationRegistryRecord>(agentContext.Wallet,
                                                                            definitionRecord.CurrentRevocationRegistryId);

                tailsReader = await TailsService.OpenTailsAsync(revocationRecord.TailsFile);
            }

            try
            {
                return(await AnonCreds.IssuerCreateCredentialAsync(
                           agentContext.Wallet,
                           credentialRecord.OfferJson,
                           credentialRecord.RequestJson,
                           CredentialUtils.FormatCredentialValues(credentialRecord.CredentialAttributesValues),
                           definitionRecord.CurrentRevocationRegistryId,
                           tailsReader), revocationRecord);
            }
            catch (RevocationRegistryFullException)
            {
                if (!definitionRecord.RevocationAutoScale)
                {
                    throw;
                }
            }

            var registryIndex = definitionRecord.CurrentRevocationRegistryId.Split(':').LastOrDefault()?.Split('-')
                                .FirstOrDefault();

            string registryTag;

            if (int.TryParse(registryIndex, out var currentIndex))
            {
                registryTag = $"{currentIndex + 1}-{definitionRecord.MaxCredentialCount}";
            }
            else
            {
                registryTag = $"1-{definitionRecord.MaxCredentialCount}";
            }

            var(_, nextRevocationRecord) = await SchemaService.CreateRevocationRegistryAsync(agentContext, registryTag,
                                                                                             definitionRecord);

            definitionRecord.CurrentRevocationRegistryId = nextRevocationRecord.Id;
            await RecordService.UpdateAsync(agentContext.Wallet, definitionRecord);

            tailsReader = await TailsService.OpenTailsAsync(nextRevocationRecord.TailsFile);

            return(await AnonCreds.IssuerCreateCredentialAsync(
                       agentContext.Wallet,
                       credentialRecord.OfferJson,
                       credentialRecord.RequestJson,
                       CredentialUtils.FormatCredentialValues(credentialRecord.CredentialAttributesValues),
                       nextRevocationRecord.Id,
                       tailsReader), nextRevocationRecord);
        }
        /// <inheritdoc />
        public async Task <(CredentialOfferMessage, CredentialRecord)> CreateOfferAsync(
            IAgentContext agentContext, OfferConfiguration config, string connectionId)
        {
            Logger.LogInformation(LoggingEvents.CreateCredentialOffer, "DefinitionId {0}, IssuerDid {1}",
                                  config.CredentialDefinitionId, config.IssuerDid);

            var threadId = Guid.NewGuid().ToString();

            if (!string.IsNullOrEmpty(connectionId))
            {
                var connection = await ConnectionService.GetAsync(agentContext, connectionId);

                if (connection.State != ConnectionState.Connected)
                {
                    throw new AriesFrameworkException(ErrorCode.RecordInInvalidState,
                                                      $"Connection state was invalid. Expected '{ConnectionState.Connected}', found '{connection.State}'");
                }
            }

            if (config.CredentialAttributeValues != null && config.CredentialAttributeValues.Any())
            {
                CredentialUtils.ValidateCredentialPreviewAttributes(config.CredentialAttributeValues);
            }

            var offerJson = await AnonCreds.IssuerCreateCredentialOfferAsync(
                agentContext.Wallet, config.CredentialDefinitionId);

            var offerJobj = JObject.Parse(offerJson);
            var schemaId  = offerJobj["schema_id"].ToObject <string>();

            // Write offer record to local wallet
            var credentialRecord = new CredentialRecord
            {
                Id = threadId,
                CredentialDefinitionId = config.CredentialDefinitionId,
                OfferJson    = offerJson,
                ConnectionId = connectionId,
                SchemaId     = schemaId,
                CredentialAttributesValues = config.CredentialAttributeValues,
                State = CredentialState.Offered,
            };

            credentialRecord.SetTag(TagConstants.LastThreadId, threadId);
            credentialRecord.SetTag(TagConstants.Role, TagConstants.Issuer);
            if (!string.IsNullOrEmpty(config.IssuerDid))
            {
                credentialRecord.SetTag(TagConstants.IssuerDid, config.IssuerDid);
            }
            if (config.Tags != null)
            {
                foreach (var tag in config.Tags)
                {
                    if (!credentialRecord.Tags.Keys.Contains(tag.Key))
                    {
                        credentialRecord.Tags.Add(tag.Key, tag.Value);
                    }
                }
            }

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

            return(new CredentialOfferMessage(agentContext.UseMessageTypesHttps)
            {
                Id = threadId,
                Offers = new Attachment[]
                {
                    new Attachment
                    {
                        Id = "libindy-cred-offer-0",
                        MimeType = CredentialMimeTypes.ApplicationJsonMimeType,
                        Data = new AttachmentContent
                        {
                            Base64 = offerJson.GetUTF8Bytes().ToBase64String()
                        }
                    }
                },
                CredentialPreview = credentialRecord.CredentialAttributesValues != null
                    ? new CredentialPreviewMessage(agentContext.UseMessageTypesHttps)
                {
                    Attributes = credentialRecord.CredentialAttributesValues.Select(x =>
                                                                                    new CredentialPreviewAttribute
                    {
                        Name = x.Name,
                        MimeType = x.MimeType,
                        Value = x.Value?.ToString()
                    }).ToArray()
                }
                    : null
            }, credentialRecord);
        }
Beispiel #29
0
        public static async Task <(CredentialRecord issuerCredential, CredentialRecord holderCredential)> IssueCredentialAsync(
            ISchemaService schemaService, ICredentialService credentialService,
            IProducerConsumerCollection <AgentMessage> messages,
            ConnectionRecord issuerConnection, ConnectionRecord holderConnection,
            IAgentContext issuerContext,
            IAgentContext holderContext,
            Pool pool, string proverMasterSecretId, bool revocable, List <CredentialPreviewAttribute> credentialAttributes, OfferConfiguration offerConfiguration = null)
        {
            // Create an issuer DID/VK. Can also be created during provisioning
            var issuer = await Did.CreateAndStoreMyDidAsync(issuerContext.Wallet,
                                                            new { seed = TestConstants.StewartDid }.ToJson());

            // Create a schema and credential definition for this issuer
            var(definitionId, _) = await CreateDummySchemaAndNonRevokableCredDef(issuerContext, schemaService,
                                                                                 issuer.Did, credentialAttributes.Select(_ => _.Name).ToArray());

            var offerConfig = offerConfiguration ?? new OfferConfiguration
            {
                IssuerDid = issuer.Did,
                CredentialDefinitionId = definitionId
            };

            // Send an offer to the holder using the established connection channel
            var(offerMessage, _) = await credentialService.CreateOfferAsync(issuerContext, offerConfig, issuerConnection.Id);

            messages.TryAdd(offerMessage);

            // Holder retrieves message from their cloud agent
            var credentialOffer = FindContentMessage <CredentialOfferMessage>(messages);

            // Holder processes the credential offer by storing it
            var holderCredentialId =
                await credentialService.ProcessOfferAsync(holderContext, credentialOffer, holderConnection);

            // Holder creates master secret. Will also be created during wallet agent provisioning
            await AnonCreds.ProverCreateMasterSecretAsync(holderContext.Wallet, proverMasterSecretId);

            // Holder accepts the credential offer and sends a credential request
            var(request, _) = await credentialService.CreateCredentialRequestAsync(holderContext, holderCredentialId);

            messages.TryAdd(request);

            // Issuer retrieves credential request from cloud agent
            var credentialRequest = FindContentMessage <CredentialRequestMessage>(messages);

            Assert.NotNull(credentialRequest);

            // Issuer processes the credential request by storing it
            var issuerCredentialId =
                await credentialService.ProcessCredentialRequestAsync(issuerContext, credentialRequest, issuerConnection);

            // Issuer accepts the credential requests and issues a credential
            var(credentialMessage, _) = await credentialService.CreateCredentialAsync(issuerContext, issuer.Did,
                                                                                      issuerCredentialId,
                                                                                      credentialAttributes);

            messages.TryAdd(credentialMessage);

            // Holder retrieves the credential from their cloud agent
            var credential = FindContentMessage <CredentialMessage>(messages);

            Assert.NotNull(credential);

            // Holder processes the credential by storing it in their wallet
            await credentialService.ProcessCredentialAsync(holderContext, credential, holderConnection);

            // Verify states of both credential records are set to 'Issued'
            var issuerCredential = await credentialService.GetAsync(issuerContext, issuerCredentialId);

            var holderCredential = await credentialService.GetAsync(holderContext, holderCredentialId);

            return(issuerCredential, holderCredential);
        }
Beispiel #30
0
        public static async Task <(string, string)> CreateDummySchemaAndNonRevokableCredDef(IAgentContext context, ISchemaService schemaService, string issuerDid, string[] attributeValues)
        {
            // Create a schema and credential definition for this issuer
            var schemaId = await schemaService.CreateSchemaAsync(await context.Pool, context.Wallet, issuerDid,
                                                                 $"Test-Schema-{Guid.NewGuid().ToString()}", "1.0", attributeValues);

            return(await schemaService.CreateCredentialDefinitionAsync(await context.Pool, context.Wallet, schemaId, issuerDid, "Tag", false, 100, new Uri("http://mock/tails")), schemaId);
        }
Beispiel #31
0
 public Task <(ConnectionInvitationMessage, ConnectionRecord)> CreateInvitationAsync(IAgentContext agentContext, InviteConfiguration config = null)
 {
     throw new System.NotImplementedException();
 }