Skip to content

Commit

Permalink
feat: temp cred sdjwt
Browse files Browse the repository at this point in the history
Signed-off-by: Stas D <[email protected]>
  • Loading branch information
skynet2 committed Aug 8, 2023
1 parent ee63a56 commit 88514cf
Show file tree
Hide file tree
Showing 10 changed files with 305 additions and 58 deletions.
39 changes: 32 additions & 7 deletions pkg/doc/vc/crypto/crypto.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,12 @@ SPDX-License-Identifier: Apache-2.0
package crypto

import (
"crypto"
"fmt"
"strings"
"time"

"github.com/piprate/json-gold/ld"

"github.com/hyperledger/aries-framework-go/pkg/doc/did"
"github.com/hyperledger/aries-framework-go/pkg/doc/signature/jsonld"
ariessigner "github.com/hyperledger/aries-framework-go/pkg/doc/signature/signer"
Expand All @@ -23,7 +24,6 @@ import (
"github.com/hyperledger/aries-framework-go/pkg/doc/signature/suite/jsonwebsignature2020"
"github.com/hyperledger/aries-framework-go/pkg/doc/verifiable"
vdrapi "github.com/hyperledger/aries-framework-go/pkg/framework/aries/api/vdr"
"github.com/piprate/json-gold/ld"

"github.com/trustbloc/vcs/pkg/doc/vc"
"github.com/trustbloc/vcs/pkg/doc/vc/jws"
Expand Down Expand Up @@ -92,6 +92,7 @@ type signingOpts struct {
Created *time.Time
Challenge string
Domain string
SDJWTTemplateData *vc.SelectiveDisclosureTemplate
}

// SigningOpts is signing credential option.
Expand All @@ -104,6 +105,12 @@ func WithVerificationMethod(verificationMethod string) SigningOpts {
}
}

func WithSDJWTTemplateData(template *vc.SelectiveDisclosureTemplate) SigningOpts {
return func(opts *signingOpts) {
opts.SDJWTTemplateData = template
}
}

// WithPurpose is an option to pass proof purpose option for signing.
func WithPurpose(purpose string) SigningOpts {
return func(opts *signingOpts) {
Expand Down Expand Up @@ -198,7 +205,9 @@ func (c *Crypto) signCredentialLDP(

// signCredentialJWT returns vc in JWT format including the signature section.
func (c *Crypto) signCredentialJWT(
signerData *vc.Signer, credential *verifiable.Credential, opts ...SigningOpts) (*verifiable.Credential, error) {
signerData *vc.Signer,
credential *verifiable.Credential,
opts ...SigningOpts) (*verifiable.Credential, error) {
signOpts := &signingOpts{}
// apply opts
for _, opt := range opts {
Expand Down Expand Up @@ -238,7 +247,21 @@ func (c *Crypto) signCredentialJWT(
}

if signerData.SDJWT.Enable {
return c.getSDJWTSignedCredential(credential, s, jwsAlgo, signerData.SDJWT.HashAlg, method)
options := []verifiable.MakeSDJWTOption{
verifiable.MakeSDJWTWithHash(signerData.SDJWT.HashAlg),
}

if signOpts.SDJWTTemplateData != nil {
options = append(options,
verifiable.MakeSDJWTWithVersion(signOpts.SDJWTTemplateData.Version),
verifiable.MakeSDJWTWithRecursiveClaimsObjects(signOpts.SDJWTTemplateData.RecursiveClaims),
verifiable.MakeSDJWTWithAlwaysIncludeObjects(signOpts.SDJWTTemplateData.AlwaysInclude),
verifiable.MakeSDJWTWithNonSelectivelyDisclosableClaims(
signOpts.SDJWTTemplateData.NonSelectivelyDisclosable),
)
}

return c.getSDJWTSignedCredential(credential, s, jwsAlgo, method, options...)
}

return c.getJWTSignedCredential(credential, s, jwsAlgo, method)
Expand Down Expand Up @@ -268,16 +291,18 @@ func (c *Crypto) getSDJWTSignedCredential(
credential *verifiable.Credential,
signer vc.SignerAlgorithm,
jwsAlgo verifiable.JWSAlgorithm,
digestHashAlgo crypto.Hash,
signingKeyID string) (*verifiable.Credential, error) {
signingKeyID string,
options ...verifiable.MakeSDJWTOption,
) (*verifiable.Credential, error) {
jwsAlgName, err := jwsAlgo.Name()
if err != nil {
return nil, fmt.Errorf("getting JWS algo name error: %w", err)
}

joseSigner := jws.NewSigner(signingKeyID, jwsAlgName, signer)

sdjwt, err := credential.MakeSDJWT(joseSigner, signingKeyID, verifiable.MakeSDJWTWithHash(digestHashAlgo))
//
sdjwt, err := credential.MakeSDJWT(joseSigner, signingKeyID, options...)
if err != nil {
return nil, fmt.Errorf("make SDJWT credential error: %w", err)
}
Expand Down
13 changes: 12 additions & 1 deletion pkg/doc/vc/sdjwt.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,21 @@ SPDX-License-Identifier: Apache-2.0

package vc

import "crypto"
import (
"crypto"

"github.com/hyperledger/aries-framework-go/component/models/sdjwt/common"
)

// SDJWT represents the SD-JWT configuration.
type SDJWT struct {
Enable bool `json:"enable,omitempty"`
HashAlg crypto.Hash `json:"hashAlg,omitempty"`
}

type SelectiveDisclosureTemplate struct { // todo
Version common.SDJWTVersion `json:"version"`
AlwaysInclude []string `json:"alwaysInclude"`
RecursiveClaims []string `json:"recursiveClaims"`
NonSelectivelyDisclosable []string `json:"nonSelectivelyDisclosable"`
}
1 change: 1 addition & 0 deletions pkg/doc/vc/signer.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,5 @@ type Signer struct {
KMS keyManager
VCStatusListType StatusType // Type of VC status list
SDJWT SDJWT
SDJWTemplateData SelectiveDisclosureTemplate
}
25 changes: 18 additions & 7 deletions pkg/profile/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import (
"time"

"github.com/hyperledger/aries-framework-go-ext/component/vdr/orb"

"github.com/hyperledger/aries-framework-go/component/models/sdjwt/common"
"github.com/hyperledger/aries-framework-go/pkg/doc/presexch"
"github.com/hyperledger/aries-framework-go/pkg/doc/verifiable"
"github.com/hyperledger/aries-framework-go/pkg/kms"
Expand All @@ -24,7 +26,8 @@ import (
type (
ID = string // ID of the Profile.
Version = string // Profile version.
Method string // DID method of the Profile.

Method string // DID method of the Profile.
)

const (
Expand Down Expand Up @@ -52,12 +55,20 @@ type Issuer struct {
}

type CredentialTemplate struct {
Contexts []string `json:"contexts"`
ID string `json:"id"`
Type string `json:"type"`
CredentialSubject json.RawMessage `json:"credentialSubject"`
CredentialDefaultExpirationDuration *time.Duration `json:"credentialDefaultExpirationDuration"`
Checks CredentialTemplateChecks `json:"checks"`
Contexts []string `json:"contexts"`
ID string `json:"id"`
Type string `json:"type"`
CredentialSubject json.RawMessage `json:"credentialSubject"`
CredentialDefaultExpirationDuration *time.Duration `json:"credentialDefaultExpirationDuration"`
Checks CredentialTemplateChecks `json:"checks"`
SdJWT *SelectiveDisclosureTemplate `json:"sdJWT"`
}

type SelectiveDisclosureTemplate struct {
Version common.SDJWTVersion `json:"version"`
AlwaysInclude []string `json:"alwaysInclude"`
RecursiveClaims []string `json:"recursiveClaims"`
NonSelectivelyDisclosable []string `json:"nonSelectivelyDisclosable"`
}

type CredentialTemplateChecks struct {
Expand Down
17 changes: 12 additions & 5 deletions pkg/restapi/v1/issuer/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,16 @@ import (
"time"

"github.com/google/uuid"
"github.com/hyperledger/aries-framework-go/component/models/ld/validator"
"github.com/hyperledger/aries-framework-go/pkg/doc/jsonld"
util2 "github.com/hyperledger/aries-framework-go/pkg/doc/util"
"github.com/hyperledger/aries-framework-go/pkg/doc/verifiable"
"github.com/labstack/echo/v4"
"github.com/piprate/json-gold/ld"
"github.com/samber/lo"
"go.opentelemetry.io/otel/trace"

"github.com/hyperledger/aries-framework-go/component/models/ld/validator"
"github.com/hyperledger/aries-framework-go/pkg/doc/jsonld"
util2 "github.com/hyperledger/aries-framework-go/pkg/doc/util"
"github.com/hyperledger/aries-framework-go/pkg/doc/verifiable"

"github.com/trustbloc/vcs/pkg/doc/vc"
"github.com/trustbloc/vcs/pkg/doc/vc/crypto"
vcsverifiable "github.com/trustbloc/vcs/pkg/doc/verifiable"
Expand Down Expand Up @@ -654,7 +655,13 @@ func (c *Controller) PrepareCredential(e echo.Context) error {
return err
}

signedCredential, err := c.signCredential(ctx, result.Credential, nil, profile)
var signOpts []crypto.SigningOpts
if result.CredentialTemplate != nil && result.CredentialTemplate.SdJWT != nil {
cast := vc.SelectiveDisclosureTemplate(*result.CredentialTemplate.SdJWT)
signOpts = append(signOpts, crypto.WithSDJWTTemplateData(&cast))
}

signedCredential, err := c.signCredential(ctx, result.Credential, signOpts, profile)
if err != nil {
return err
}
Expand Down
3 changes: 2 additions & 1 deletion pkg/service/issuecredential/issuecredential_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,8 @@ func (s *Service) IssueCredential(
ctx context.Context,
credential *verifiable.Credential,
issuerSigningOpts []crypto.SigningOpts,
profile *profileapi.Issuer) (*verifiable.Credential, error) {
profile *profileapi.Issuer,
) (*verifiable.Credential, error) {
kms, err := s.kmsRegistry.GetKeyManager(profile.KMSConfig)
if err != nil {
return nil, fmt.Errorf("get kms: %w", err)
Expand Down
1 change: 1 addition & 0 deletions pkg/service/oidc4ci/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@ type PrepareCredentialResult struct {
Retry bool
EnforceStrictValidation bool
OidcFormat vcsverifiable.OIDCFormat
CredentialTemplate *profileapi.CredentialTemplate
}

type InsertOptions struct {
Expand Down
4 changes: 3 additions & 1 deletion pkg/service/oidc4ci/oidc4ci_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,10 @@ import (
"time"

"github.com/google/uuid"
"github.com/trustbloc/logutil-go/pkg/log"

"github.com/hyperledger/aries-framework-go/pkg/doc/util"
"github.com/hyperledger/aries-framework-go/pkg/doc/verifiable"
"github.com/trustbloc/logutil-go/pkg/log"

"github.com/trustbloc/vcs/pkg/dataprotect"
"github.com/trustbloc/vcs/pkg/event/spi"
Expand Down Expand Up @@ -488,6 +489,7 @@ func (s *Service) PrepareCredential(
OidcFormat: tx.OIDCCredentialFormat,
Retry: false,
EnforceStrictValidation: tx.CredentialTemplate.Checks.Strict,
CredentialTemplate: tx.CredentialTemplate,
}, nil
}

Expand Down
73 changes: 37 additions & 36 deletions test/bdd/features/oidc4vc_api.feature
Original file line number Diff line number Diff line change
Expand Up @@ -31,29 +31,30 @@ Feature: OIDC4VC REST API
## LDP issuer, LDP verifier, no limit disclosure and schema match in PD query.
# | i_myprofile_cmtr_p256_ldp/v1.0 | CrudeProductCredential | pre-registered | crudeProductCredentialTemplateID | v_myprofile_ldp/v1.0 | lp403pb9-schema-match | schema_id |
#
# Scenario Outline: OIDC credential issuance and verification Pre Auth flow
# Given Organization "test_org" has been authorized with client id "f13d1va9lp403pb9lyj89vk55" and secret "ejqxi9jb1vew2jbdnogpjcgrz"
# And Issuer with id "<issuerProfile>" is authorized as a Profile user
# And User holds credential "<credentialType>" with templateID "<credentialTemplate>"
#
# When User interacts with Wallet to initiate credential issuance using pre authorization code flow
# Then credential is issued
# Then User interacts with Verifier and initiate OIDC4VP interaction under "<verifierProfile>" profile for organization "test_org" with presentation definition ID "<presentationDefinitionID>" and fields "<fields>"
# And Verifier from organization "test_org" retrieves interactions claims
# Then we wait 2 seconds
# And Verifier form organization "test_org" requests deleted interactions claims
#
# Examples:
# | issuerProfile | credentialType | credentialTemplate | verifierProfile | presentationDefinitionID | fields |
## SDJWT issuer, JWT verifier, no limit disclosure in PD query.
Scenario Outline: OIDC credential issuance and verification Pre Auth flow
Given Organization "test_org" has been authorized with client id "f13d1va9lp403pb9lyj89vk55" and secret "ejqxi9jb1vew2jbdnogpjcgrz"
And Issuer with id "<issuerProfile>" is authorized as a Profile user
And User holds credential "<credentialType>" with templateID "<credentialTemplate>"

When User interacts with Wallet to initiate credential issuance using pre authorization code flow
Then credential is issued
Then User interacts with Verifier and initiate OIDC4VP interaction under "<verifierProfile>" profile for organization "test_org" with presentation definition ID "<presentationDefinitionID>" and fields "<fields>"
And Verifier from organization "test_org" retrieves interactions claims
Then we wait 2 seconds
And Verifier form organization "test_org" requests deleted interactions claims

Examples:
| issuerProfile | credentialType | credentialTemplate | verifierProfile | presentationDefinitionID | fields |
# SDJWT issuer, JWT verifier, no limit disclosure in PD query.
# | bank_issuer/v1.0 | UniversityDegreeCredential | universityDegreeTemplateID | v_myprofile_jwt/v1.0 | 32f54163-no-limit-disclosure-single-field | degree_type_id |
## SDJWT issuer, JWT verifier, limit disclosure and optional fields in PD query.
# | bank_issuer/v1.0 | CrudeProductCredential | crudeProductCredentialTemplateID | v_myprofile_jwt/v1.0 | 3c8b1d9a-limit-disclosure-optional-fields | unit_of_measure_barrel,api_gravity,category,supplier_address |
# SDJWT issuer, JWT verifier, limit disclosure and optional fields in PD query.
# | bank_issuer/v1.0 | CrudeProductCredential | crudeProductCredentialTemplateID | v_myprofile_jwt/v1.0 | 3c8b1d9a-limit-disclosure-optional-fields | unit_of_measure_barrel,api_gravity,category,supplier_address |
| bank_issuer_sdjwt_v5/v1.0 | CrudeProductCredential | crudeProductCredentialTemplateID | v_myprofile_jwt/v1.0 | 3c8b1d9a-limit-disclosure-optional-fields | unit_of_measure_barrel,api_gravity,category,supplier_address |
## JWT issuer, JWT verifier, no limit disclosure and optional fields in PD query.
# | i_myprofile_ud_es256k_jwt/v1.0 | PermanentResidentCard | permanentResidentCardTemplateID | v_myprofile_jwt/v1.0 | 32f54163-no-limit-disclosure-optional-fields | lpr_category_id,commuter_classification,registration_city |
## LDP issuer, LDP verifier, no limit disclosure and schema match in PD query.
# | i_myprofile_cmtr_p256_ldp/v1.0 | CrudeProductCredential | crudeProductCredentialTemplateID | v_myprofile_ldp/v1.0 | lp403pb9-schema-match | schema_id |
#

## Error cases
# Scenario: OIDC credential issuance and verification Pre Auth flow (Invalid Claims)
# Given Organization "test_org" has been authorized with client id "f13d1va9lp403pb9lyj89vk55" and secret "ejqxi9jb1vew2jbdnogpjcgrz"
Expand Down Expand Up @@ -97,25 +98,25 @@ Feature: OIDC4VC REST API
# | i_myprofile_ud_es256k_jwt/v1.0 | PermanentResidentCard | permanentResidentCardTemplateID | v_myprofile_jwt/v1.0 | 32f54163-no-limit-disclosure-optional-fields | lpr_category_id,registration_city,commuter_classification |
# | i_myprofile_cmtr_p256_ldp/v1.0 | CrudeProductCredential | crudeProductCredentialTemplateID | v_myprofile_ldp/v1.0 | lp403pb9-schema-match | schema_id |

Scenario Outline: OIDC credential issuance and verification Pre Auth flow (Limit Disclosures enabled for JWT and LDP VC)
Given Organization "test_org" has been authorized with client id "f13d1va9lp403pb9lyj89vk55" and secret "ejqxi9jb1vew2jbdnogpjcgrz"
And Issuer with id "<issuerProfile>" is authorized as a Profile user
And User holds credential "<credentialType>" with templateID "<credentialTemplate>"

When User interacts with Wallet to initiate credential issuance using pre authorization code flow
Then credential is issued
And User interacts with Verifier and initiate OIDC4VP interaction under "<verifierProfile>" profile for organization "test_org" with presentation definition ID "<presentationDefinitionID>" and fields "<fields>" and receives "verifiable credential doesn't contains proof" error
Then we wait 15 seconds
And Verifier form organization "test_org" requests expired interactions claims

Examples:
| issuerProfile | credentialType | credentialTemplate | verifierProfile | presentationDefinitionID | fields |
# JWT issuer, JWT verifier, limit disclosure enabled in PD query.
| i_myprofile_ud_es256k_jwt/v1.0 | CrudeProductCredential | crudeProductCredentialTemplateID | v_myprofile_jwt/v1.0 | 3c8b1d9a-limit-disclosure-optional-fields | unit_of_measure_barrel,api_gravity,category,supplier_address |
# LDP issuer, LDP verifier, limit disclosure enabled in PD query.
| i_myprofile_cmtr_p256_ldp/v1.0 | CrudeProductCredential | crudeProductCredentialTemplateID | v_myprofile_ldp/v1.0 | 3c8b1d9a-limit-disclosure-optional-fields | unit_of_measure_barrel,api_gravity,category,supplier_address |
# Scenario Outline: OIDC credential issuance and verification Pre Auth flow (Limit Disclosures enabled for JWT and LDP VC)
# Given Organization "test_org" has been authorized with client id "f13d1va9lp403pb9lyj89vk55" and secret "ejqxi9jb1vew2jbdnogpjcgrz"
# And Issuer with id "<issuerProfile>" is authorized as a Profile user
# And User holds credential "<credentialType>" with templateID "<credentialTemplate>"
#
# When User interacts with Wallet to initiate credential issuance using pre authorization code flow
# Then credential is issued
# And User interacts with Verifier and initiate OIDC4VP interaction under "<verifierProfile>" profile for organization "test_org" with presentation definition ID "<presentationDefinitionID>" and fields "<fields>" and receives "verifiable credential doesn't contains proof" error
# Then we wait 15 seconds
# And Verifier form organization "test_org" requests expired interactions claims
#
# Scenario Outline: OIDC credential issuance and verification Pre Auth flow (OIDC4VP flow - unsupported vp_token format)
# Examples:
# | issuerProfile | credentialType | credentialTemplate | verifierProfile | presentationDefinitionID | fields |
## JWT issuer, JWT verifier, limit disclosure enabled in PD query.
# | i_myprofile_ud_es256k_jwt/v1.0 | CrudeProductCredential | crudeProductCredentialTemplateID | v_myprofile_jwt/v1.0 | 3c8b1d9a-limit-disclosure-optional-fields | unit_of_measure_barrel,api_gravity,category,supplier_address |
## LDP issuer, LDP verifier, limit disclosure enabled in PD query.
# | i_myprofile_cmtr_p256_ldp/v1.0 | CrudeProductCredential | crudeProductCredentialTemplateID | v_myprofile_ldp/v1.0 | 3c8b1d9a-limit-disclosure-optional-fields | unit_of_measure_barrel,api_gravity,category,supplier_address |
##
## Scenario Outline: OIDC credential issuance and verification Pre Auth flow (OIDC4VP flow - unsupported vp_token format)
# Given Organization "test_org" has been authorized with client id "f13d1va9lp403pb9lyj89vk55" and secret "ejqxi9jb1vew2jbdnogpjcgrz"
# And Issuer with id "<issuerProfile>" is authorized as a Profile user
# And User holds credential "<credentialType>" with templateID "<credentialTemplate>"
Expand Down
Loading

0 comments on commit 88514cf

Please sign in to comment.