Skip to content

Commit

Permalink
feat: adding Iden3commRevocationStatusV1
Browse files Browse the repository at this point in the history
  • Loading branch information
javip97 committed Jan 16, 2024
1 parent eed0901 commit f83d677
Show file tree
Hide file tree
Showing 6 changed files with 137 additions and 5 deletions.
2 changes: 1 addition & 1 deletion internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,7 @@ func (c *Configuration) sanitizeCredentialStatus(_ context.Context, host string)

if c.CredentialStatus.RHSMode == none {
c.CredentialStatus.DirectStatus.URL = host
c.CredentialStatus.CredentialStatusType = sparseMerkleTreeProof
c.CredentialStatus.CredentialStatusType = iden3commRevocationStatusV1
}

if c.CredentialStatus.RHSMode == offChain {
Expand Down
11 changes: 9 additions & 2 deletions internal/config/credential_status.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package config

import (
"fmt"
"strings"
)

const (
sparseMerkleTreeProof = "SparseMerkleTreeProof"
iden3commRevocationStatusV1 = "Iden3commRevocationStatusV1.0"
iden3ReverseSparseMerkleTreeProof = "Iden3ReverseSparseMerkleTreeProof"
iden3OnchainSparseMerkleTreeProof2023 = "Iden3OnchainSparseMerkleTreeProof2023"
onChain = "OnChain"
Expand All @@ -23,19 +25,24 @@ type CredentialStatus struct {
OnchainTreeStore OnchainTreeStore `mapstructure:"OnchainTreeStore"`
RHSMode RHSMode `tip:"Reverse hash service mode (OffChain, OnChain, Mixed, None)"`
SingleIssuer bool
CredentialStatusType string `mapstructure:"CredentialStatusType" default:"SparseMerkleTreeProof"`
CredentialStatusType string `mapstructure:"CredentialStatusType" default:"Iden3commRevocationStatusV1"`
}

// DirectStatus is the type of direct status
type DirectStatus struct {
URL string `mapstructure:"URL"`
}

// GetURL returns the URL of the direct status
// GetURL returns the URL of the di rect status
func (r *DirectStatus) GetURL() string {
return strings.TrimSuffix(r.URL, "/")
}

// GetAgentURL returns the URL of the agent endpoint
func (r *DirectStatus) GetAgentURL() string {
return fmt.Sprintf("%s/v1/agent", strings.TrimSuffix(r.URL, "/"))
}

// RHS is the type of RHS
type RHS struct {
URL string `mapstructure:"URL"`
Expand Down
32 changes: 31 additions & 1 deletion internal/core/services/claims.go
Original file line number Diff line number Diff line change
Expand Up @@ -367,7 +367,14 @@ func (c *claim) Agent(ctx context.Context, req *ports.AgentRequest) (*domain.Age
return nil, fmt.Errorf("cannot proceed with this identity, not found")
}

return c.getAgentCredential(ctx, req) // at this point the type is already validated
switch req.Type {
case protocol.CredentialFetchRequestMessageType:
return c.getAgentCredential(ctx, req)
case protocol.RevocationStatusRequestMessageType:
return c.getRevocationStatus(ctx, req)
default:
return nil, errors.New("invalid type")
}
}

func (c *claim) GetAuthClaim(ctx context.Context, did *w3c.DID) (*domain.Claim, error) {
Expand Down Expand Up @@ -597,6 +604,29 @@ func (c *claim) revoke(ctx context.Context, did *w3c.DID, nonce uint64, descript
return nil
}

func (c *claim) getRevocationStatus(ctx context.Context, basicMessage *ports.AgentRequest) (*domain.Agent, error) {
revData := &protocol.RevocationStatusRequestMessageBody{}
err := json.Unmarshal(basicMessage.Body, revData)
if err != nil {
return nil, fmt.Errorf("invalid revocation request body: %w", err)
}

var revStatus *verifiable.RevocationStatus
revStatus, err = c.GetRevocationStatus(ctx, *basicMessage.IssuerDID, revData.RevocationNonce)
if err != nil {
return nil, fmt.Errorf("failed get revocation status: %w", err)
}

return &domain.Agent{
ID: uuid.NewString(),
Type: protocol.RevocationStatusResponseMessageType,
ThreadID: basicMessage.ThreadID,
Body: protocol.RevocationStatusResponseMessageBody{RevocationStatus: *revStatus},
From: basicMessage.IssuerDID.String(),
To: basicMessage.UserDID.String(),
}, nil
}

func (c *claim) getAgentCredential(ctx context.Context, basicMessage *ports.AgentRequest) (*domain.Agent, error) {
fetchRequestBody := &protocol.CredentialFetchRequestMessageBody{}
err := json.Unmarshal(basicMessage.Body, fetchRequestBody)
Expand Down
76 changes: 76 additions & 0 deletions internal/core/services/revocation.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
package services

import (
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
"io"
"math/big"
"net/http"
"net/url"
Expand All @@ -13,12 +15,16 @@ import (
"time"

ethCommon "github.com/ethereum/go-ethereum/common"
"github.com/google/uuid"
abiOnchain "github.com/iden3/contracts-abi/onchain-credential-status-resolver/go/abi"
"github.com/iden3/contracts-abi/state/go/abi"
core "github.com/iden3/go-iden3-core/v2"
"github.com/iden3/go-iden3-core/v2/w3c"
"github.com/iden3/go-merkletree-sql/v2"
"github.com/iden3/go-schema-processor/v2/verifiable"
"github.com/iden3/iden3comm/v2"
"github.com/iden3/iden3comm/v2/packers"
"github.com/iden3/iden3comm/v2/protocol"
proofHttp "github.com/iden3/merkletree-proof/http"

"github.com/polygonid/sh-id-platform/internal/common"
Expand Down Expand Up @@ -73,6 +79,9 @@ func (r *Revocation) Status(ctx context.Context, credStatus interface{}, issuerD
return r.getRevocationStatusFromRHS(ctx, issuerDID, status, issuerData)
case verifiable.SparseMerkleTreeProof:
return getRevocationProofFromIssuer(ctx, status.ID)
case verifiable.Iden3commRevocationStatusV1:
return getRevocationStatusFromAgent(ctx, issuerDID.String(),
issuerDID.String(), status.ID, status.RevocationNonce)
case verifiable.Iden3OnchainSparseMerkleTreeProof2023:
return r.getRevocationStatusFromOnchainCredStatusResolver(ctx, issuerDID, status)
default:
Expand Down Expand Up @@ -106,6 +115,73 @@ func convertCredentialStatus(credStatus interface{}) (verifiable.CredentialStatu
return verifiable.CredentialStatus{}, errors.New("failed cast credential status to verifiable.CredentialStatus")
}

func getRevocationStatusFromAgent(ctx context.Context, from, to, endpoint string, nonce uint64) (*verifiable.RevocationStatus, error) {
pkg := iden3comm.NewPackageManager()
if err := pkg.RegisterPackers(&packers.PlainMessagePacker{}); err != nil {
return nil, err
}

revocationBody := protocol.RevocationStatusRequestMessageBody{
RevocationNonce: nonce,
}
rawBody, err := json.Marshal(revocationBody)
if err != nil {
return nil, err
}

msg := iden3comm.BasicMessage{
ID: uuid.New().String(),
ThreadID: uuid.New().String(),
From: from,
To: to,
Type: protocol.RevocationStatusRequestMessageType,
Body: rawBody,
}
bytesMsg, err := json.Marshal(msg)
if err != nil {
return nil, err
}

iden3commMsg, err := pkg.Pack(packers.MediaTypePlainMessage, bytesMsg, nil)
if err != nil {
return nil, err
}

resp, err := http.DefaultClient.Post(endpoint, "application/json", bytes.NewBuffer(iden3commMsg))
if err != nil {
return nil, err
}
defer func() {
if err := resp.Body.Close(); err != nil {
log.Warn(ctx, "failed to close response body: %s", err)
}
}()

if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("bad status code: %d", resp.StatusCode)
}

b, err := io.ReadAll(resp.Body)
if err != nil {
return nil, err
}
basicMessage, _, err := pkg.Unpack(b)
if err != nil {
return nil, err
}

if basicMessage.Type != protocol.RevocationStatusResponseMessageType {
return nil, fmt.Errorf("unexpected message type: %s", basicMessage.Type)
}

var revocationStatus protocol.RevocationStatusResponseMessageBody
if err := json.Unmarshal(basicMessage.Body, &revocationStatus); err != nil {
return nil, err
}

return &revocationStatus.RevocationStatus, nil
}

func getRevocationProofFromIssuer(ctx context.Context, url string) (*verifiable.RevocationStatus, error) {
b, err := client.NewClient(*http.DefaultClient).Get(ctx, url)
if err != nil {
Expand Down
18 changes: 18 additions & 0 deletions pkg/credentials/revocation_status/iden3_revocation_status_v1.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package revocation_status

import (
"github.com/iden3/go-iden3-core/v2/w3c"
"github.com/iden3/go-schema-processor/v2/verifiable"

"github.com/polygonid/sh-id-platform/internal/config"
)

type iden3CommRevocationStatusV1Resolver struct{}

func (r *iden3CommRevocationStatusV1Resolver) resolve(credentialStatusSettings config.CredentialStatus, issuerDID w3c.DID, nonce uint64, issuerState string) *verifiable.CredentialStatus {
return &verifiable.CredentialStatus{
ID: credentialStatusSettings.DirectStatus.GetAgentURL(),
Type: verifiable.Iden3commRevocationStatusV1,
RevocationNonce: nonce,
}
}
3 changes: 2 additions & 1 deletion pkg/credentials/revocation_status/resolver.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ func NewRevocationStatusResolver(credentialStatusSettings config.CredentialStatu
resolvers := make(map[verifiable.CredentialStatusType]revocationCredentialStatusResolver, resolversLength)
resolvers[verifiable.Iden3ReverseSparseMerkleTreeProof] = &iden3ReverseSparseMerkleTreeProofResolver{}
resolvers[verifiable.SparseMerkleTreeProof] = &sparseMerkleTreeProofResolver{}
resolvers[verifiable.Iden3commRevocationStatusV1] = &iden3CommRevocationStatusV1Resolver{}
resolvers[verifiable.Iden3OnchainSparseMerkleTreeProof2023] = &iden3OnChainSparseMerkleTreeProof2023Resolver{}
return &RevocationStatusResolver{
credentialStatusSettings: credentialStatusSettings,
Expand All @@ -39,7 +40,7 @@ func NewRevocationStatusResolver(credentialStatusSettings config.CredentialStatu
// If status is supported, a way to check revocation status is returned.
func (rsr *RevocationStatusResolver) GetCredentialRevocationStatus(_ context.Context, issuerDID w3c.DID, nonce uint64, issuerState string, credentialStatusType verifiable.CredentialStatusType) (*verifiable.CredentialStatus, error) {
if credentialStatusType == "" {
credentialStatusType = verifiable.SparseMerkleTreeProof
credentialStatusType = verifiable.Iden3commRevocationStatusV1
}
resolver, ok := rsr.resolvers[credentialStatusType]
if !ok {
Expand Down

0 comments on commit f83d677

Please sign in to comment.