github.com/hyperledger/aries-framework-go@v0.3.2/pkg/client/presentproof/client.go (about)

     1  /*
     2  Copyright SecureKey Technologies Inc. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package presentproof
     8  
     9  import (
    10  	"errors"
    11  
    12  	"github.com/hyperledger/aries-framework-go/pkg/didcomm/common/service"
    13  	"github.com/hyperledger/aries-framework-go/pkg/didcomm/protocol/decorator"
    14  	"github.com/hyperledger/aries-framework-go/pkg/didcomm/protocol/presentproof"
    15  	"github.com/hyperledger/aries-framework-go/pkg/doc/verifiable"
    16  	"github.com/hyperledger/aries-framework-go/pkg/store/connection"
    17  )
    18  
    19  type (
    20  	// ProposePresentation is an optional message sent by the Prover to the verifier to initiate a proof
    21  	// presentation process, or in response to a request-presentation message when the Prover wants to
    22  	// propose using a different presentation format.
    23  	ProposePresentation = presentproof.ProposePresentationParams
    24  	// RequestPresentation describes values that need to be revealed and predicates that need to be fulfilled.
    25  	RequestPresentation = presentproof.RequestPresentationParams
    26  	// Presentation is a response to a RequestPresentation message and contains signed presentations.
    27  	Presentation = presentproof.PresentationParams
    28  	// ProposePresentationV2 is an optional message sent by the Prover to the verifier to initiate a proof
    29  	// presentation process, or in response to a request-presentation message when the Prover wants to
    30  	// propose using a different presentation format.
    31  	ProposePresentationV2 presentproof.ProposePresentationV2
    32  	// RequestPresentationV2 describes values that need to be revealed and predicates that need to be fulfilled.
    33  	RequestPresentationV2 presentproof.RequestPresentationV2
    34  	// PresentationV2 is a response to a RequestPresentationV2 message and contains signed presentations.
    35  	PresentationV2 presentproof.PresentationV2
    36  	// ProposePresentationV3 is an optional message sent by the Prover to the verifier to initiate a proof
    37  	// presentation process, or in response to a request-presentation message when the Prover wants to
    38  	// propose using a different presentation format.
    39  	ProposePresentationV3 presentproof.ProposePresentationV3
    40  	// RequestPresentationV3 describes values that need to be revealed and predicates that need to be fulfilled.
    41  	RequestPresentationV3 presentproof.RequestPresentationV3
    42  	// PresentationV3 is a response to a RequestPresentationV3 message and contains signed presentations.
    43  	PresentationV3 presentproof.PresentationV3
    44  	// Action contains helpful information about action.
    45  	Action presentproof.Action
    46  )
    47  
    48  const (
    49  	// web redirect decorator.
    50  	webRedirectDecorator  = "~web-redirect"
    51  	webRedirectStatusOK   = "OK"
    52  	webRedirectStatusFAIL = "FAIL"
    53  )
    54  
    55  var (
    56  	errEmptyRequestPresentation = errors.New("request presentation message is empty")
    57  	errEmptyProposePresentation = errors.New("propose presentation message is empty")
    58  )
    59  
    60  // Provider contains dependencies for the protocol and is typically created by using aries.Context().
    61  type Provider interface {
    62  	Service(id string) (interface{}, error)
    63  }
    64  
    65  // ProtocolService defines the presentproof service.
    66  type ProtocolService interface {
    67  	service.DIDComm
    68  	Actions() ([]presentproof.Action, error)
    69  	ActionContinue(piID string, opt ...presentproof.Opt) error
    70  	ActionStop(piID string, err error, opt ...presentproof.Opt) error
    71  }
    72  
    73  // Client enable access to presentproof API
    74  // https://github.com/hyperledger/aries-rfcs/tree/master/features/0037-present-proof
    75  type Client struct {
    76  	service.Event
    77  	service ProtocolService
    78  }
    79  
    80  // New returns new instance of the presentproof client.
    81  func New(ctx Provider) (*Client, error) {
    82  	raw, err := ctx.Service(presentproof.Name)
    83  	if err != nil {
    84  		return nil, err
    85  	}
    86  
    87  	svc, ok := raw.(ProtocolService)
    88  	if !ok {
    89  		return nil, errors.New("cast service to presentproof service failed")
    90  	}
    91  
    92  	return &Client{
    93  		Event:   svc,
    94  		service: svc,
    95  	}, nil
    96  }
    97  
    98  // Actions returns pending actions that have yet to be executed or cancelled.
    99  func (c *Client) Actions() ([]Action, error) {
   100  	actions, err := c.service.Actions()
   101  	if err != nil {
   102  		return nil, err
   103  	}
   104  
   105  	result := make([]Action, len(actions))
   106  	for i, action := range actions {
   107  		result[i] = Action(action)
   108  	}
   109  
   110  	return result, nil
   111  }
   112  
   113  // SendRequestPresentation is used by the Verifier to send a request presentation.
   114  // It returns the threadID of the new instance of the protocol.
   115  func (c *Client) SendRequestPresentation(
   116  	params *RequestPresentation, connRec *connection.Record) (string, error) {
   117  	if params == nil {
   118  		return "", errEmptyRequestPresentation
   119  	}
   120  
   121  	switch connRec.DIDCommVersion {
   122  	default:
   123  		fallthrough // use didcomm v1 + present-proof v2 by default, if the connection record doesn't indicate version.
   124  	case service.V1:
   125  		return c.service.HandleOutbound(service.NewDIDCommMsgMap(&RequestPresentationV2{
   126  			Type:                       presentproof.RequestPresentationMsgTypeV2,
   127  			Comment:                    params.Comment,
   128  			WillConfirm:                params.WillConfirm,
   129  			Formats:                    params.Formats,
   130  			RequestPresentationsAttach: decorator.GenericAttachmentsToV1(params.Attachments),
   131  		}), connRec.MyDID, connRec.TheirDID)
   132  	case service.V2:
   133  		return c.service.HandleOutbound(service.NewDIDCommMsgMap(&RequestPresentationV3{
   134  			Type: presentproof.RequestPresentationMsgTypeV3,
   135  			Body: presentproof.RequestPresentationV3Body{
   136  				GoalCode:    params.GoalCode,
   137  				Comment:     params.Comment,
   138  				WillConfirm: params.WillConfirm,
   139  			},
   140  			Attachments: decorator.GenericAttachmentsToV2(params.Attachments),
   141  		}), connRec.MyDID, connRec.TheirDID)
   142  	}
   143  }
   144  
   145  type addProof func(presentation *verifiable.Presentation) error
   146  
   147  // AcceptRequestPresentation is used by the Prover is to accept a presentation request.
   148  func (c *Client) AcceptRequestPresentation(piID string, msg *Presentation, sign addProof) error {
   149  	return c.service.ActionContinue(piID, WithMultiOptions(WithPresentation(msg), WithAddProofFn(sign)))
   150  }
   151  
   152  // NegotiateRequestPresentation is used by the Prover to counter a presentation request they received with a proposal.
   153  func (c *Client) NegotiateRequestPresentation(piID string, msg *ProposePresentation) error {
   154  	return c.service.ActionContinue(piID, WithProposePresentation(msg))
   155  }
   156  
   157  // DeclineRequestPresentation is used when the Prover does not want to accept the request presentation.
   158  func (c *Client) DeclineRequestPresentation(piID, reason string) error {
   159  	return c.service.ActionStop(piID, errors.New(reason))
   160  }
   161  
   162  // SendProposePresentation is used by the Prover to send a propose presentation.
   163  // It returns the threadID of the new instance of the protocol.
   164  func (c *Client) SendProposePresentation(
   165  	params *ProposePresentation, connRec *connection.Record) (string, error) {
   166  	if params == nil {
   167  		return "", errEmptyProposePresentation
   168  	}
   169  
   170  	switch connRec.DIDCommVersion {
   171  	default:
   172  		fallthrough // use didcomm v1 + present-proof v2 by default, if the connection record doesn't indicate version.
   173  	case service.V1:
   174  		return c.service.HandleOutbound(service.NewDIDCommMsgMap(&ProposePresentationV2{
   175  			Type:            presentproof.ProposePresentationMsgTypeV2,
   176  			Comment:         params.Comment,
   177  			Formats:         params.Formats,
   178  			ProposalsAttach: decorator.GenericAttachmentsToV1(params.Attachments),
   179  		}), connRec.MyDID, connRec.TheirDID)
   180  	case service.V2:
   181  		return c.service.HandleOutbound(service.NewDIDCommMsgMap(&ProposePresentationV3{
   182  			Type: presentproof.ProposePresentationMsgTypeV3,
   183  			Body: presentproof.ProposePresentationV3Body{
   184  				GoalCode: params.GoalCode,
   185  				Comment:  params.Comment,
   186  			},
   187  			Attachments: decorator.GenericAttachmentsToV2(params.Attachments),
   188  		}), connRec.MyDID, connRec.TheirDID)
   189  	}
   190  }
   191  
   192  // AcceptProposePresentation is used when the Verifier is willing to accept the propose presentation.
   193  func (c *Client) AcceptProposePresentation(piID string, msg *RequestPresentation) error {
   194  	return c.service.ActionContinue(piID, WithRequestPresentation(msg))
   195  }
   196  
   197  // DeclineProposePresentation is used when the Verifier does not want to accept the propose presentation.
   198  func (c *Client) DeclineProposePresentation(piID string, options ...DeclinePresentationOptions) error {
   199  	opts := &declinePresentationOpts{}
   200  
   201  	for _, option := range options {
   202  		option(opts)
   203  	}
   204  
   205  	return c.service.ActionStop(piID, opts.reason, prepareRedirectProperties(opts.redirect, webRedirectStatusFAIL))
   206  }
   207  
   208  // AcceptPresentation is used by the Verifier to accept a presentation.
   209  func (c *Client) AcceptPresentation(piID string, options ...AcceptPresentationOptions) error {
   210  	opts := &acceptPresentationOpts{}
   211  
   212  	for _, option := range options {
   213  		option(opts)
   214  	}
   215  
   216  	return c.service.ActionContinue(piID, presentproof.WithFriendlyNames(opts.names...),
   217  		prepareRedirectProperties(opts.redirect, webRedirectStatusOK))
   218  }
   219  
   220  // DeclinePresentation is used by the Verifier to decline a presentation.
   221  func (c *Client) DeclinePresentation(piID string, options ...DeclinePresentationOptions) error {
   222  	opts := &declinePresentationOpts{}
   223  
   224  	for _, option := range options {
   225  		option(opts)
   226  	}
   227  
   228  	return c.service.ActionStop(piID, opts.reason, prepareRedirectProperties(opts.redirect, webRedirectStatusFAIL))
   229  }
   230  
   231  // AcceptProblemReport accepts problem report action.
   232  func (c *Client) AcceptProblemReport(piID string) error {
   233  	return c.service.ActionContinue(piID)
   234  }
   235  
   236  // WithPresentation allows providing Presentation message.
   237  // Use this option to respond to RequestPresentation.
   238  func WithPresentation(msg *Presentation) presentproof.Opt {
   239  	return presentproof.WithPresentation(msg)
   240  }
   241  
   242  // WithMultiOptions allows combining several options into one.
   243  func WithMultiOptions(opts ...presentproof.Opt) presentproof.Opt {
   244  	return presentproof.WithMultiOptions(opts...)
   245  }
   246  
   247  // WithAddProofFn allows providing function that will sign the Presentation.
   248  // Use this option to respond to RequestPresentation.
   249  func WithAddProofFn(sign addProof) presentproof.Opt {
   250  	return presentproof.WithAddProofFn(sign)
   251  }
   252  
   253  // WithProposePresentation allows providing ProposePresentation message.
   254  // Use this option to respond to RequestPresentation.
   255  func WithProposePresentation(msg *ProposePresentation) presentproof.Opt {
   256  	return presentproof.WithProposePresentation(msg)
   257  }
   258  
   259  // WithRequestPresentation allows providing RequestPresentation message.
   260  // Use this option to respond to ProposePresentation.
   261  func WithRequestPresentation(msg *RequestPresentation) presentproof.Opt {
   262  	return presentproof.WithRequestPresentation(msg)
   263  }
   264  
   265  // create web redirect properties to add ~web-redirect decorator.
   266  func prepareRedirectProperties(redirect, status string) presentproof.Opt {
   267  	properties := map[string]interface{}{}
   268  
   269  	if redirect != "" {
   270  		properties[webRedirectDecorator] = &decorator.WebRedirect{
   271  			Status: status,
   272  			URL:    redirect,
   273  		}
   274  	}
   275  
   276  	return presentproof.WithProperties(properties)
   277  }
   278  
   279  // declinePresentationOpts options for declining propose presentation and presentation.
   280  type declinePresentationOpts struct {
   281  	reason   error
   282  	redirect string
   283  }
   284  
   285  // DeclinePresentationOptions is custom option for declining propose presentation and presentation messages from prover.
   286  type DeclinePresentationOptions func(opts *declinePresentationOpts)
   287  
   288  // DeclineReason option to provide optional reason for declining given message.
   289  func DeclineReason(reason string) DeclinePresentationOptions {
   290  	return func(opts *declinePresentationOpts) {
   291  		if reason != "" {
   292  			opts.reason = errors.New(reason)
   293  		}
   294  	}
   295  }
   296  
   297  // DeclineRedirect option to provide optional redirect URL requesting prover to redirect.
   298  func DeclineRedirect(url string) DeclinePresentationOptions {
   299  	return func(opts *declinePresentationOpts) {
   300  		opts.redirect = url
   301  	}
   302  }
   303  
   304  // acceptPresentationOpts options for accepting presentation message.
   305  type acceptPresentationOpts struct {
   306  	names    []string
   307  	redirect string
   308  }
   309  
   310  // AcceptPresentationOptions is custom option for accepting presentation message from prover.
   311  type AcceptPresentationOptions func(opts *acceptPresentationOpts)
   312  
   313  // AcceptByFriendlyNames option to provide optional friendly names for accepting presentation message.
   314  func AcceptByFriendlyNames(names ...string) AcceptPresentationOptions {
   315  	return func(opts *acceptPresentationOpts) {
   316  		opts.names = names
   317  	}
   318  }
   319  
   320  // AcceptByRequestingRedirect option to provide optional redirect URL requesting prover to redirect.
   321  func AcceptByRequestingRedirect(url string) AcceptPresentationOptions {
   322  	return func(opts *acceptPresentationOpts) {
   323  		opts.redirect = url
   324  	}
   325  }