Skip to content

Commit

Permalink
feat: extend BDD tests
Browse files Browse the repository at this point in the history
Signed-off-by: Mykhailo Sizov <[email protected]>
  • Loading branch information
mishasizov-SK committed Oct 23, 2023
1 parent 0dc0668 commit d80cd6f
Show file tree
Hide file tree
Showing 6 changed files with 147 additions and 8 deletions.
60 changes: 58 additions & 2 deletions component/wallet-cli/pkg/walletrunner/wallet_runner_oidc4vci.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,19 @@ type OIDC4VCIConfig struct {
EnableDiscoverableClientID bool
}

type credentialRequestOpts struct {
signerKeyID string
signature string
nonce string
}

type OauthClientOpt func(config *oauth2.Config)

type CredentialRequestOpt func(credentialRequestOpts *credentialRequestOpts)

type Hooks struct {
BeforeTokenRequest []OauthClientOpt
BeforeTokenRequest []OauthClientOpt
BeforeCredentialRequest []CredentialRequestOpt
}

func WithClientID(clientID string) OauthClientOpt {
Expand All @@ -67,6 +76,27 @@ func WithClientID(clientID string) OauthClientOpt {
}
}

// WithSignerKeyID overrides signerKeyID in credentials request. For testing purpose only.
func WithSignerKeyID(keyID string) CredentialRequestOpt {
return func(credentialRequestOpts *credentialRequestOpts) {
credentialRequestOpts.signerKeyID = keyID
}
}

// WithSignatureValue overrides signature in credentials request. For testing purpose only.
func WithSignatureValue(signature string) CredentialRequestOpt {
return func(credentialRequestOpts *credentialRequestOpts) {
credentialRequestOpts.signature = signature
}
}

// WithNonce overrides nonce in credentials request. For testing purpose only.
func WithNonce(nonce string) CredentialRequestOpt {
return func(credentialRequestOpts *credentialRequestOpts) {
credentialRequestOpts.nonce = nonce
}
}

func (s *Service) RunOIDC4VCI(config *OIDC4VCIConfig, hooks *Hooks) error {
log.Println("Starting OIDC4VCI authorized code flow")
log.Printf("Credential Offer URI:\n\n\t%s\n\n", config.CredentialOfferURI)
Expand Down Expand Up @@ -178,9 +208,11 @@ func (s *Service) RunOIDC4VCI(config *OIDC4VCIConfig, hooks *Hooks) error {
ctx = context.WithValue(ctx, oauth2.HTTPClient, s.httpClient)

var beforeTokenRequestHooks []OauthClientOpt
var beforeCredentialsRequestHooks []CredentialRequestOpt

if hooks != nil {
beforeTokenRequestHooks = hooks.BeforeTokenRequest
beforeCredentialsRequestHooks = hooks.BeforeCredentialRequest
}

for _, f := range beforeTokenRequestHooks {
Expand All @@ -205,6 +237,7 @@ func (s *Service) RunOIDC4VCI(config *OIDC4VCIConfig, hooks *Hooks) error {
config.CredentialType,
config.CredentialFormat,
credentialOfferResponse.CredentialIssuer,
beforeCredentialsRequestHooks...,
)
if err != nil {
return fmt.Errorf("get credential: %w", err)
Expand Down Expand Up @@ -337,9 +370,11 @@ func (s *Service) RunOIDC4CIWalletInitiated(config *OIDC4VCIConfig, hooks *Hooks
ctx = context.WithValue(ctx, oauth2.HTTPClient, s.httpClient)

var beforeTokenRequestHooks []OauthClientOpt
var beforeCredentialsRequestHooks []CredentialRequestOpt

if hooks != nil {
beforeTokenRequestHooks = hooks.BeforeTokenRequest
beforeCredentialsRequestHooks = hooks.BeforeCredentialRequest
}

for _, f := range beforeTokenRequestHooks {
Expand All @@ -362,6 +397,7 @@ func (s *Service) RunOIDC4CIWalletInitiated(config *OIDC4VCIConfig, hooks *Hooks
config.CredentialType,
config.CredentialFormat,
issuerUrl,
beforeCredentialsRequestHooks...,
)
if err != nil {
return fmt.Errorf("get credential: %w", err)
Expand Down Expand Up @@ -483,7 +519,13 @@ func (s *Service) getCredential(
credentialType,
credentialFormat,
issuerURI string,
beforeCredentialRequestOpts ...CredentialRequestOpt,
) (interface{}, time.Duration, error) {
credentialsRequestParamsOverride := &credentialRequestOpts{}
for _, f := range beforeCredentialRequestOpts {
f(credentialsRequestParamsOverride)
}

didKeyID := s.vcProviderConf.WalletParams.DidKeyID[0]

fks, err := s.ariesServices.Suite().FixedKeyMultiSigner(strings.Split(didKeyID, "#")[1])
Expand All @@ -493,11 +535,16 @@ func (s *Service) getCredential(

kmsSigner := signer.NewKMSSigner(fks, s.vcProviderConf.WalletParams.SignType, nil)

nonce := s.token.Extra("c_nonce").(string)
if credentialsRequestParamsOverride.nonce != "" {
nonce = credentialsRequestParamsOverride.nonce
}

claims := &JWTProofClaims{
Issuer: s.oauthClient.ClientID,
IssuedAt: time.Now().Unix(),
Audience: issuerURI,
Nonce: s.token.Extra("c_nonce").(string),
Nonce: nonce,
}

signerKeyID := didKeyID
Expand All @@ -518,6 +565,10 @@ func (s *Service) getCredential(
signerKeyID = res.DIDDocument.VerificationMethod[0].ID
}

if credentialsRequestParamsOverride.signerKeyID != "" {
signerKeyID = credentialsRequestParamsOverride.signerKeyID
}

headers := map[string]interface{}{
jose.HeaderType: jwtProofTypHeader,
}
Expand All @@ -533,6 +584,11 @@ func (s *Service) getCredential(
return nil, 0, fmt.Errorf("serialize signed jwt: %w", err)
}

if credentialsRequestParamsOverride.signature != "" {
chunks := strings.Split(jws, ".")
jws = strings.Join([]string{chunks[0], chunks[1], credentialsRequestParamsOverride.signature}, ".")
}

b, err := json.Marshal(CredentialRequest{
Format: credentialFormat,
Types: []string{"VerifiableCredential", credentialType},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import (
"github.com/trustbloc/vcs/pkg/restapi/v1/oidc4ci"
)

func (s *Service) RunOIDC4CIPreAuth(config *OIDC4VCIConfig) (*verifiable.Credential, error) {
func (s *Service) RunOIDC4CIPreAuth(config *OIDC4VCIConfig, hooks *Hooks) (*verifiable.Credential, error) {
log.Println("Starting OIDC4VCI pre-authorized code flow")

startTime := time.Now()
Expand Down Expand Up @@ -105,13 +105,21 @@ func (s *Service) RunOIDC4CIPreAuth(config *OIDC4VCIConfig) (*verifiable.Credent
"c_nonce": *token.CNonce,
})

var beforeCredentialsRequestHooks []CredentialRequestOpt

if hooks != nil {
beforeCredentialsRequestHooks = hooks.BeforeCredentialRequest
}

s.print("Getting credential")
startTime = time.Now()
vc, vcsDuration, err := s.getCredential(
oidcIssuerCredentialConfig.CredentialEndpoint,
config.CredentialType,
config.CredentialFormat,
credentialOfferResponse.CredentialIssuer)
credentialOfferResponse.CredentialIssuer,
beforeCredentialsRequestHooks...,
)
if err != nil {
return nil, fmt.Errorf("get credential: %w", err)
}
Expand Down
15 changes: 15 additions & 0 deletions test/bdd/features/oidc4vc_api.feature
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,21 @@ Feature: OIDC4VC REST API
And User holds credential "UniversityDegreeCredential" with templateID "universityDegreeTemplateID"
Then Malicious attacker stealing auth code from User and using "malicious_attacker_id" ClientID makes /token request and receives "invalid_client" error

Scenario: OIDC credential issuance and verification Auth flow (Malicious attacker changed signingKeyID & calling credential endpoint with it)
Given Profile "bank_issuer/v1.0" issuer has been authorized with username "profile-user-issuer-1" and password "profile-user-issuer-1-pwd"
And User holds credential "UniversityDegreeCredential" with templateID "universityDegreeTemplateID"
Then Malicious attacker changed JWT kid header and makes /credential request and receives "invalid_or_missing_proof" error

Scenario: OIDC credential issuance and verification Auth flow (Malicious attacker changed JWT signature value & calling credential endpoint with it)
Given Profile "bank_issuer/v1.0" issuer has been authorized with username "profile-user-issuer-1" and password "profile-user-issuer-1-pwd"
And User holds credential "UniversityDegreeCredential" with templateID "universityDegreeTemplateID"
Then Malicious attacker changed signature value and makes /credential request and receives "invalid_or_missing_proof" error

Scenario: OIDC credential issuance and verification Auth flow (Malicious attacker changed nonce & calling credential endpoint with it)
Given Profile "bank_issuer/v1.0" issuer has been authorized with username "profile-user-issuer-1" and password "profile-user-issuer-1-pwd"
And User holds credential "UniversityDegreeCredential" with templateID "universityDegreeTemplateID"
Then Malicious attacker changed nonce value and makes /credential request and receives "invalid_or_missing_proof" error

Scenario: OIDC credential issuance and verification Pre Auth flow (issuer has pre-authorized_grant_anonymous_access_supported disabled)
Given Profile "i_disabled_preauth_without_client_id/v1.0" issuer has been authorized with username "profile-user-issuer-1" and password "profile-user-issuer-1-pwd"
And User holds credential "VerifiedEmployee" with templateID "templateID"
Expand Down
61 changes: 59 additions & 2 deletions test/bdd/pkg/v1/oidc4vc/oidc4ci.go
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ func (s *Steps) runOIDC4CIPreAuth(initiateOIDC4CIRequest initiateOIDC4CIRequest)
CredentialType: s.issuedCredentialType,
CredentialFormat: s.issuerProfile.CredentialMetaData.CredentialsSupported[0]["format"].(string),
Pin: *initiateOIDC4CIResponseData.UserPin,
})
}, nil)
if err != nil {
return fmt.Errorf("s.walletRunner.RunOIDC4CIPreAuth: %w", err)
}
Expand Down Expand Up @@ -250,7 +250,7 @@ func (s *Steps) credentialTypeTemplateID(issuedCredentialType, issuedCredentialT
return nil
}

func (s *Steps) runOIDC4CIAuthWithError(updatedClientID, errorContains string) error {
func (s *Steps) runOIDC4CIAuthWithErrorInvalidClient(updatedClientID, errorContains string) error {
initiateOIDC4CIResponseData, err := s.initiateCredentialIssuance(s.getInitiateIssuanceRequest())
if err != nil {
return fmt.Errorf("initiateCredentialIssuance: %w", err)
Expand Down Expand Up @@ -301,6 +301,63 @@ func (s *Steps) runOIDC4CIAuthWithError(updatedClientID, errorContains string) e
return nil
}

func (s *Steps) runOIDC4CIAuthWithErrorInvalidSigningKeyID(errorContains string) error {
return s.runOIDC4CIAuthWithErrorInvalidSignature(
[]walletrunner.CredentialRequestOpt{
walletrunner.WithSignerKeyID("didID#keyID"),
},
errorContains,
)
}

func (s *Steps) runOIDC4CIAuthWithErrorInvalidSignatureValue(errorContains string) error {
return s.runOIDC4CIAuthWithErrorInvalidSignature(
[]walletrunner.CredentialRequestOpt{
walletrunner.WithSignatureValue(uuid.NewString()),
},
errorContains,
)
}

func (s *Steps) runOIDC4CIAuthWithErrorInvalidNonce(errorContains string) error {
return s.runOIDC4CIAuthWithErrorInvalidSignature(
[]walletrunner.CredentialRequestOpt{
walletrunner.WithNonce(uuid.NewString()),
},
errorContains,
)
}

func (s *Steps) runOIDC4CIAuthWithErrorInvalidSignature(beforeCredentialRequestOpts []walletrunner.CredentialRequestOpt, errorContains string) error {
initiateOIDC4CIResponseData, err := s.initiateCredentialIssuance(s.getInitiateIssuanceRequest())
if err != nil {
return fmt.Errorf("initiateCredentialIssuance: %w", err)
}

err = s.walletRunner.RunOIDC4VCI(&walletrunner.OIDC4VCIConfig{
CredentialOfferURI: initiateOIDC4CIResponseData.OfferCredentialURL,
ClientID: "oidc4vc_client",
Scopes: []string{"openid", "profile"},
RedirectURI: "http://127.0.0.1/callback",
CredentialType: s.issuedCredentialType,
CredentialFormat: s.issuerProfile.CredentialMetaData.CredentialsSupported[0]["format"].(string),
Login: "bdd-test",
Password: "bdd-test-pass",
}, &walletrunner.Hooks{
BeforeCredentialRequest: beforeCredentialRequestOpts,
})

if err == nil {
return fmt.Errorf("error expected, got nil")
}

if !strings.Contains(err.Error(), errorContains) {
return fmt.Errorf("unexpected err: %w", err)
}

return nil
}

func (s *Steps) runOIDC4CIAuth() error {
initiateOIDC4CIResponseData, err := s.initiateCredentialIssuance(s.getInitiateIssuanceRequest())
if err != nil {
Expand Down
5 changes: 4 additions & 1 deletion test/bdd/pkg/v1/oidc4vc/steps.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,10 @@ func (s *Steps) RegisterSteps(sc *godog.ScenarioContext) {
sc.Step(`^Verifier with profile "([^"]*)" requests expired interactions claims$`, s.retrieveExpiredOrDeletedInteractionsClaim)
sc.Step(`^Verifier with profile "([^"]*)" waits for interaction succeeded event$`, s.waitForOIDCInteractionSucceededEvent)
sc.Step(`^User interacts with Verifier and initiate OIDC4VP interaction under "([^"]*)" profile with presentation definition ID "([^"]*)" and fields "([^"]*)" and receives "([^"]*)" error$`, s.runOIDC4VPFlowWithError)
sc.Step(`^Malicious attacker stealing auth code from User and using "([^"]*)" ClientID makes /token request and receives "([^"]*)" error$`, s.runOIDC4CIAuthWithError)
sc.Step(`^Malicious attacker stealing auth code from User and using "([^"]*)" ClientID makes /token request and receives "([^"]*)" error$`, s.runOIDC4CIAuthWithErrorInvalidClient)
sc.Step(`^Malicious attacker changed JWT kid header and makes /credential request and receives "([^"]*)" error$`, s.runOIDC4CIAuthWithErrorInvalidSigningKeyID)
sc.Step(`^Malicious attacker changed signature value and makes /credential request and receives "([^"]*)" error$`, s.runOIDC4CIAuthWithErrorInvalidSignatureValue)
sc.Step(`^Malicious attacker changed nonce value and makes /credential request and receives "([^"]*)" error$`, s.runOIDC4CIAuthWithErrorInvalidNonce)

// Stress test.
sc.Step(`^number of users "([^"]*)" making "([^"]*)" concurrent requests$`, s.getUsersNum)
Expand Down
2 changes: 1 addition & 1 deletion test/stress/pkg/stress/stress_test_case.go
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ func (c *TestCase) Invoke() (string, interface{}, error) {
CredentialType: c.credentialType,
CredentialFormat: c.credentialFormat,
Pin: pin,
})
}, nil)

credID := ""
if credentials != nil {
Expand Down

0 comments on commit d80cd6f

Please sign in to comment.