Esempio n. 1
0
        /// <inheritdoc />
        public virtual async Task RevokeCredentialAsync(IAgentContext agentContext, string credentialId)
        {
            var credentialRecord = await GetAsync(agentContext, credentialId);

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

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

            // Check if the state machine is valid for revocation
            await credentialRecord.TriggerAsync(CredentialTrigger.Revoke);

            var revocationRecord =
                await RecordService.GetAsync <RevocationRegistryRecord>(agentContext.Wallet,
                                                                        credentialRecord.RevocationRegistryId);

            // Revoke the credential
            var tailsReader = await TailsService.OpenTailsAsync(revocationRecord.TailsFile);

            var revocRegistryDeltaJson = await AnonCreds.IssuerRevokeCredentialAsync(
                agentContext.Wallet,
                tailsReader,
                revocationRecord.Id,
                credentialRecord.CredentialRevocationId);

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

            // Write the delta state on the ledger for the corresponding revocation registry
            await LedgerService.SendRevocationRegistryEntryAsync(
                context : agentContext,
                issuerDid : provisioning.IssuerDid,
                revocationRegistryDefinitionId : revocationRecord.Id,
                revocationDefinitionType : "CL_ACCUM",
                value : revocRegistryDeltaJson,
                paymentInfo : paymentInfo);

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

            // Update local credential record
            await RecordService.UpdateAsync(agentContext.Wallet, credentialRecord);
        }
Esempio n. 2
0
        private async Task <string> BuildRevocationStatesAsync(IAgentContext agentContext,
                                                               IEnumerable <CredentialInfo> credentialObjects,
                                                               ProofRequest proofRequest,
                                                               RequestedCredentials requestedCredentials)
        {
            var allCredentials = new List <RequestedAttribute>();

            allCredentials.AddRange(requestedCredentials.RequestedAttributes.Values);
            allCredentials.AddRange(requestedCredentials.RequestedPredicates.Values);

            var result = new Dictionary <string, Dictionary <string, JObject> >();

            foreach (var requestedCredential in allCredentials)
            {
                // ReSharper disable once PossibleMultipleEnumeration
                var credential = credentialObjects.First(x => x.Referent == requestedCredential.CredentialId);
                if (credential.RevocationRegistryId == null ||
                    (proofRequest.NonRevoked == null))
                {
                    continue;
                }

                var registryDefinition = await LedgerService.LookupRevocationRegistryDefinitionAsync(
                    agentContext : agentContext,
                    registryId : credential.RevocationRegistryId);

                var delta = await LedgerService.LookupRevocationRegistryDeltaAsync(
                    agentContext : agentContext,
                    revocationRegistryId : credential.RevocationRegistryId,
                    // Ledger will not return correct revocation state if the 'from' field
                    // is other than 0
                    from : 0, //proofRequest.NonRevoked.From,
                    to : proofRequest.NonRevoked.To);

                var tailsfile = await TailsService.EnsureTailsExistsAsync(agentContext, credential.RevocationRegistryId);

                var tailsReader = await TailsService.OpenTailsAsync(tailsfile);

                var state = await AnonCreds.CreateRevocationStateAsync(
                    blobStorageReader : tailsReader,
                    revRegDef : registryDefinition.ObjectJson,
                    revRegDelta : delta.ObjectJson,
                    timestamp : (long)delta.Timestamp,
                    credRevId : credential.CredentialRevocationId);

                if (!result.ContainsKey(credential.RevocationRegistryId))
                {
                    result.Add(credential.RevocationRegistryId, new Dictionary <string, JObject>());
                }

                requestedCredential.Timestamp = (long)delta.Timestamp;
                if (!result[credential.RevocationRegistryId].ContainsKey($"{delta.Timestamp}"))
                {
                    result[credential.RevocationRegistryId].Add($"{delta.Timestamp}", JObject.Parse(state));
                }
            }

            return(result.ToJson());
        }
        private async Task <string> BuildRevocationStatesAsync(Pool pool,
                                                               IEnumerable <CredentialInfo> credentialObjects,
                                                               RequestedCredentials requestedCredentials)
        {
            var allCredentials = new List <RequestedAttribute>();

            allCredentials.AddRange(requestedCredentials.RequestedAttributes.Values);
            allCredentials.AddRange(requestedCredentials.RequestedPredicates.Values);

            var result = new Dictionary <string, Dictionary <string, JObject> >();

            foreach (var requestedCredential in allCredentials)
            {
                // ReSharper disable once PossibleMultipleEnumeration
                var credential = credentialObjects.First(x => x.Referent == requestedCredential.CredentialId);
                if (credential.RevocationRegistryId == null)
                {
                    continue;
                }

                var timestamp = requestedCredential.Timestamp ??
                                throw new Exception(
                                          "Timestamp must be provided for credential that supports revocation");

                if (result.ContainsKey(credential.RevocationRegistryId) &&
                    result[credential.RevocationRegistryId].ContainsKey($"{timestamp}"))
                {
                    continue;
                }

                var registryDefinition =
                    await LedgerService.LookupRevocationRegistryDefinitionAsync(pool,
                                                                                credential.RevocationRegistryId);

                var delta = await LedgerService.LookupRevocationRegistryDeltaAsync(pool,
                                                                                   credential.RevocationRegistryId, -1, timestamp);

                var tailsfile = await TailsService.EnsureTailsExistsAsync(pool, credential.RevocationRegistryId);

                var tailsReader = await TailsService.OpenTailsAsync(tailsfile);

                var state = await AnonCreds.CreateRevocationStateAsync(tailsReader, registryDefinition.ObjectJson,
                                                                       delta.ObjectJson, (long)delta.Timestamp, credential.CredentialRevocationId);

                if (!result.ContainsKey(credential.RevocationRegistryId))
                {
                    result.Add(credential.RevocationRegistryId, new Dictionary <string, JObject>());
                }

                result[credential.RevocationRegistryId].Add($"{timestamp}", JObject.Parse(state));

                // TODO: Revocation state should provide the state between a certain period
                // that can be requested in the proof request in the 'non_revocation' field.
            }

            return(result.ToJson());
        }
        /// <inheritdoc />
        public virtual async Task IssueCredentialAsync(IAgentContext agentContext, string issuerDid, string credentialId,
                                                       Dictionary <string, string> values)
        {
            var credential = await GetAsync(agentContext, credentialId);

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

            var credentialCopy = credential.DeepCopy();

            if (values != null && values.Count > 0)
            {
                credential.ValuesJson = CredentialUtils.FormatCredentialValues(values);
            }

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

            var connection = await ConnectionService.GetAsync(agentContext, credential.ConnectionId);

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

            string            revocationRegistryId = null;
            BlobStorageReader tailsReader          = null;

            if (definitionRecord.SupportsRevocation)
            {
                var revocationRecordSearch = await RecordService.SearchAsync <RevocationRegistryRecord>(
                    agentContext.Wallet, SearchQuery.Equal(nameof(RevocationRegistryRecord.CredentialDefinitionId), definitionRecord.Id), null, 5);

                var revocationRecord = revocationRecordSearch.Single(); // TODO: Credential definition can have multiple revocation registries

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

            var issuedCredential = await AnonCreds.IssuerCreateCredentialAsync(agentContext.Wallet, credential.OfferJson,
                                                                               credential.RequestJson, credential.ValuesJson, revocationRegistryId, tailsReader);

            if (definitionRecord.SupportsRevocation)
            {
                await LedgerService.SendRevocationRegistryEntryAsync(agentContext.Wallet, agentContext.Pool, issuerDid,
                                                                     revocationRegistryId,
                                                                     "CL_ACCUM", issuedCredential.RevocRegDeltaJson);

                credential.CredentialRevocationId = issuedCredential.RevocId;
            }

            var msg = new CredentialMessage
            {
                CredentialJson       = issuedCredential.CredentialJson,
                RevocationRegistryId = revocationRegistryId
            };

            await credential.TriggerAsync(CredentialTrigger.Issue);

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

            try
            {
                await MessageService.SendAsync(agentContext.Wallet, msg, connection);
            }
            catch (Exception e)
            {
                await RecordService.UpdateAsync(agentContext.Wallet, credentialCopy);

                throw new AgentFrameworkException(ErrorCode.A2AMessageTransmissionError, "Failed to send credential request message", e);
            }
        }
        /// <inheritdoc />
        public virtual async Task <(CredentialMessage, CredentialRecord)> CreateCredentialAsync(IAgentContext agentContext, string issuerDid, string credentialId,
                                                                                                IEnumerable <CredentialPreviewAttribute> values)
        {
            var credential = await GetAsync(agentContext, credentialId);

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

            if (values != null && values.Any())
            {
                credential.CredentialAttributesValues = values;
            }

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

            var connection = await ConnectionService.GetAsync(agentContext, credential.ConnectionId);

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

            string            revocationRegistryId = null;
            BlobStorageReader tailsReader          = null;

            if (definitionRecord.SupportsRevocation)
            {
                var revocationRecordSearch = await RecordService.SearchAsync <RevocationRegistryRecord>(
                    agentContext.Wallet, SearchQuery.Equal(nameof(RevocationRegistryRecord.CredentialDefinitionId), definitionRecord.Id), null, 5);

                var revocationRecord = revocationRecordSearch.Single(); // TODO: Credential definition can have multiple revocation registries

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

            var issuedCredential = await AnonCreds.IssuerCreateCredentialAsync(agentContext.Wallet, credential.OfferJson,
                                                                               credential.RequestJson, CredentialUtils.FormatCredentialValues(credential.CredentialAttributesValues), revocationRegistryId, tailsReader);

            if (definitionRecord.SupportsRevocation)
            {
                var paymentInfo = await PaymentService.GetTransactionCostAsync(agentContext, TransactionTypes.REVOC_REG_ENTRY);

                await LedgerService.SendRevocationRegistryEntryAsync(context : agentContext,
                                                                     issuerDid : issuerDid,
                                                                     revocationRegistryDefinitionId : revocationRegistryId,
                                                                     revocationDefinitionType : "CL_ACCUM",
                                                                     value : issuedCredential.RevocRegDeltaJson,
                                                                     paymentInfo : paymentInfo);

                credential.CredentialRevocationId = issuedCredential.RevocId;

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

            await credential.TriggerAsync(CredentialTrigger.Issue);

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

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

            var credentialMsg = new CredentialMessage
            {
                CredentialJson       = issuedCredential.CredentialJson,
                RevocationRegistryId = revocationRegistryId
            };

            credentialMsg.ThreadFrom(threadId);

            return(credentialMsg, credential);
        }
Esempio n. 6
0
        /// <inheritdoc />
        public virtual async Task IssueCredentialAsync(Pool pool, Wallet wallet, string issuerDid, string credentialId,
                                                       Dictionary <string, string> values)
        {
            var credentialRecord = await RecordService.GetAsync <CredentialRecord>(wallet, credentialId);

            if (values != null && values.Count > 0)
            {
                credentialRecord.ValuesJson = CredentialUtils.FormatCredentialValues(values);
            }

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

            var connection = await ConnectionService.GetAsync(wallet, credentialRecord.ConnectionId);

            if (credentialRecord.State != CredentialState.Requested)
            {
                throw new Exception(
                          $"Credential sate was invalid. Expected '{CredentialState.Requested}', found '{credentialRecord.State}'");
            }

            string            revocationRegistryId = null;
            BlobStorageReader tailsReader          = null;

            if (definitionRecord.SupportsRevocation)
            {
                var revocationRecordSearch = await RecordService.SearchAsync <RevocationRegistryRecord>(
                    wallet, new SearchRecordQuery { { TagConstants.CredentialDefinitionId, definitionRecord.DefinitionId } }, null, 1);

                var revocationRecord = revocationRecordSearch.First();

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

            var issuedCredential = await AnonCreds.IssuerCreateCredentialAsync(wallet, credentialRecord.OfferJson,
                                                                               credentialRecord.RequestJson, credentialRecord.ValuesJson, revocationRegistryId, tailsReader);

            if (definitionRecord.SupportsRevocation)
            {
                await LedgerService.SendRevocationRegistryEntryAsync(wallet, pool, issuerDid,
                                                                     revocationRegistryId,
                                                                     "CL_ACCUM", issuedCredential.RevocRegDeltaJson);

                credentialRecord.CredentialRevocationId = issuedCredential.RevocId;
            }

            var credentialDetails = new CredentialDetails
            {
                CredentialJson       = issuedCredential.CredentialJson,
                RevocationRegistryId = revocationRegistryId
            };

            await credentialRecord.TriggerAsync(CredentialTrigger.Issue);

            await RecordService.UpdateAsync(wallet, credentialRecord);

            var credential = await MessageSerializer.PackSealedAsync <CredentialMessage>(credentialDetails, wallet,
                                                                                         connection.MyVk,
                                                                                         connection.TheirVk);

            credential.Type = MessageUtils.FormatDidMessageType(connection.TheirDid, MessageTypes.Credential);

            await RouterService.ForwardAsync(new ForwardEnvelopeMessage
            {
                Content = credential.ToJson(),
                Type    = MessageUtils.FormatDidMessageType(connection.TheirDid, MessageTypes.Forward)
            }, connection.Endpoint);
        }