// 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); }
/// <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); }
/// <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)); }
/// <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); }
public static void SampleStore(this IAgentContext context, string key, object data) { context.SendServiceMessage("Sample", Tuple.Create(key, data)); }
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(); }
/// <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);
public Task <string> ProcessResponseAsync(IAgentContext agentContext, ConnectionResponseMessage response, ConnectionRecord connection) { throw new System.NotImplementedException(); }
public Task <bool> DeleteAsync(IAgentContext agentContext, string connectionId) { throw new System.NotImplementedException(); }
public Task <(ConnectionResponseMessage, ConnectionRecord)> CreateResponseAsync(IAgentContext agentContext, string connectionId) { throw new System.NotImplementedException(); }
public Task <(ConnectionRequestMessage, ConnectionRecord)> CreateRequestAsync(IAgentContext agentContext, ConnectionInvitationMessage offer) { throw new System.NotImplementedException(); }
public Task RevokeInvitationAsync(IAgentContext agentContext, string invitationId) { throw new System.NotImplementedException(); }
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); }
/// <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;
public static void SampleRestore(this IAgentContext context, string key) { context.SendServiceMessage("Sample", Tuple.Create(key)); }
/// <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); }
/// <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))); }
/// <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); }
/// <inheritdoc /> public async Task <AgentOptions> RestoreFromBackupAsync(IAgentContext context, string seed) { var backupAttachments = await RetrieveBackupAsync(context, seed); return(await RestoreFromBackupAsync(context, seed, backupAttachments)); }
/// <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); }
/// <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);
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); }
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); }
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); }
public Task <(ConnectionInvitationMessage, ConnectionRecord)> CreateInvitationAsync(IAgentContext agentContext, InviteConfiguration config = null) { throw new System.NotImplementedException(); }