github.com/hyperledger/aries-framework-go@v0.3.2/pkg/doc/verifiable/test-suite/verifiable_suite_test.go (about)

     1  //go:build testsuite
     2  // +build testsuite
     3  
     4  /*
     5  Copyright SecureKey Technologies Inc. All Rights Reserved.
     6  SPDX-License-Identifier: Apache-2.0
     7  
     8  This is not actually a test but rather a stand-alone generator application
     9  that is used by VC Test Suite (https://github.com/w3c/vc-test-suite).
    10  To run VC Test Suite, execute `make vc-test-suite`.
    11  */
    12  
    13  package main
    14  
    15  import (
    16  	"crypto"
    17  	"crypto/rand"
    18  	"crypto/rsa"
    19  	"crypto/x509"
    20  	_ "embed"
    21  	"encoding/base64"
    22  	"encoding/json"
    23  	"flag"
    24  	"fmt"
    25  	"io/ioutil"
    26  	"os"
    27  	"path/filepath"
    28  
    29  	"github.com/go-jose/go-jose/v3"
    30  	jsonld "github.com/piprate/json-gold/ld"
    31  
    32  	"github.com/hyperledger/aries-framework-go/component/storageutil/mem"
    33  	"github.com/hyperledger/aries-framework-go/pkg/common/log"
    34  	"github.com/hyperledger/aries-framework-go/pkg/doc/ld"
    35  	"github.com/hyperledger/aries-framework-go/pkg/doc/ldcontext"
    36  	"github.com/hyperledger/aries-framework-go/pkg/doc/verifiable"
    37  	"github.com/hyperledger/aries-framework-go/pkg/kms"
    38  	ldstore "github.com/hyperledger/aries-framework-go/pkg/store/ld"
    39  )
    40  
    41  var logger = log.New("aries-framework/doc/verifiable/test-suite")
    42  var loader jsonld.DocumentLoader //nolint:gochecknoglobals
    43  
    44  // nolint:gochecknoglobals //required for go:embed
    45  var (
    46  	//go:embed contexts/credentials-examples_v1.jsonld
    47  	credentialExamplesVocab []byte
    48  	//go:embed contexts/odrl.jsonld
    49  	odrlVocab []byte
    50  )
    51  
    52  func main() {
    53  	inputFile := os.Args[len(os.Args)-1]
    54  
    55  	vcBytes, readErr := ioutil.ReadFile(inputFile) // nolint:gosec
    56  	if readErr != nil {
    57  		abort("cannot open input file %s: %v", inputFile, readErr)
    58  	}
    59  
    60  	contextStore, err := ldstore.NewContextStore(mem.NewProvider())
    61  	if err != nil {
    62  		abort("create JSON-LD context store: %v", err)
    63  	}
    64  
    65  	remoteProviderStore, err := ldstore.NewRemoteProviderStore(mem.NewProvider())
    66  	if err != nil {
    67  		abort("create remote JSON-LD context provider store: %v", err)
    68  	}
    69  
    70  	p := &provider{
    71  		ContextStore:        contextStore,
    72  		RemoteProviderStore: remoteProviderStore,
    73  	}
    74  
    75  	loader, err = ld.NewDocumentLoader(p,
    76  		ld.WithExtraContexts(
    77  			ldcontext.Document{
    78  				URL:     "https://www.w3.org/2018/credentials/examples/v1",
    79  				Content: credentialExamplesVocab,
    80  			},
    81  			ldcontext.Document{
    82  				URL:     "https://www.w3.org/ns/odrl.jsonld",
    83  				Content: odrlVocab,
    84  			},
    85  		),
    86  	)
    87  	if err != nil {
    88  		abort("create document loader: %v", err)
    89  	}
    90  
    91  	jwt := flag.String("jwt", "", "base64encoded JSON object containing es256kPrivateKeyJwk and rs256PrivateKeyJwk.")
    92  	jwtAud := flag.String("jwt-aud", "", "indication to use aud attribute in all JWTs")
    93  	jwtNoJws := flag.Bool("jwt-no-jws", false, "indication to suppress the JWS although keys are present")
    94  	jwtPresentation := flag.Bool("jwt-presentation", false, "indication to generate a verifiable presentation")
    95  	jwtDecode := flag.Bool("jwt-decode", false, "indication to generate a credential from a JWT verifiable credential. The input file will be a JWT instead of a JSON-LD file.") // nolint: lll
    96  	isPresentation := flag.Bool("presentation", false, "presentation is passed")
    97  	flag.Parse()
    98  
    99  	if *jwt == "" {
   100  		if *isPresentation {
   101  			encodeVPToJSON(vcBytes)
   102  		} else {
   103  			encodeVCToJSON(vcBytes, filepath.Base(inputFile))
   104  		}
   105  
   106  		return
   107  	}
   108  
   109  	privateKey, publicKey := parseRsaKeys(*jwt)
   110  
   111  	if *jwtDecode {
   112  		decodeVCJWTToJSON(vcBytes, publicKey)
   113  		return
   114  	}
   115  
   116  	if *jwtNoJws {
   117  		encodeVCToJWTUnsecured(vcBytes)
   118  		return
   119  	}
   120  
   121  	if *jwtPresentation {
   122  		encodeVPToJWS(vcBytes, *jwtAud, privateKey, publicKey)
   123  	} else {
   124  		encodeVCToJWS(vcBytes, privateKey)
   125  	}
   126  }
   127  
   128  func encodeVCToJWS(vcBytes []byte, privateKey *rsa.PrivateKey) {
   129  	credential, err := verifiable.ParseCredential(vcBytes, verifiable.WithNoProofCheck(),
   130  		verifiable.WithJSONLDDocumentLoader(loader))
   131  	if err != nil {
   132  		abort("failed to decode credential: %v", err)
   133  	}
   134  
   135  	jwtClaims, err := credential.JWTClaims(true)
   136  	if err != nil {
   137  		abort("verifiable credential encoding to JWS failed: %v", err)
   138  	}
   139  
   140  	jws, err := jwtClaims.MarshalJWS(verifiable.RS256, getRsaSigner(privateKey), "any")
   141  	if err != nil {
   142  		abort("failed to serialize JWS: %v", err)
   143  	}
   144  
   145  	fmt.Println(jws)
   146  }
   147  
   148  func encodeVPToJWS(vpBytes []byte, audience string, privateKey *rsa.PrivateKey, publicKey *rsa.PublicKey) {
   149  	vp, err := verifiable.ParsePresentation(vpBytes,
   150  		// do not test the cryptographic proofs (see https://github.com/w3c/vc-test-suite/issues/101)
   151  		verifiable.WithPresNoProofCheck(),
   152  		// the public key is used to decode verifiable credentials passed as JWS to the presentation
   153  		verifiable.WithPresPublicKeyFetcher(verifiable.SingleKey(publicKeyPemToBytes(publicKey), kms.RSARS256)),
   154  		verifiable.WithPresJSONLDDocumentLoader(loader))
   155  	if err != nil {
   156  		abort("failed to decode presentation: %v", err)
   157  	}
   158  
   159  	jwtClaims, err := vp.JWTClaims([]string{audience}, true)
   160  	if err != nil {
   161  		abort("failed to build JWT claims: %v", err)
   162  	}
   163  
   164  	jws, err := jwtClaims.MarshalJWS(verifiable.RS256, getRsaSigner(privateKey), "any")
   165  	if err != nil {
   166  		abort("failed to serialize JWS: %v", err)
   167  	}
   168  
   169  	fmt.Println(jws)
   170  }
   171  
   172  func encodeVCToJWTUnsecured(vcBytes []byte) {
   173  	credential, err := verifiable.ParseCredential(vcBytes, verifiable.WithNoProofCheck(),
   174  		verifiable.WithJSONLDDocumentLoader(loader))
   175  	if err != nil {
   176  		abort("failed to decode credential: %v", err)
   177  	}
   178  
   179  	jwtClaims, err := credential.JWTClaims(true)
   180  	if err != nil {
   181  		abort("verifiable credential encoding to JWT failed: %v", err)
   182  	}
   183  
   184  	jwtUnsecured, err := jwtClaims.MarshalUnsecuredJWT()
   185  	if err != nil {
   186  		abort("failed to serialize unsecured JWT: %v", err)
   187  	}
   188  
   189  	fmt.Println(jwtUnsecured)
   190  }
   191  
   192  func decodeVCJWTToJSON(vcBytes []byte, publicKey *rsa.PublicKey) {
   193  	// Asked to decode JWT
   194  	credential, err := verifiable.ParseCredential(vcBytes,
   195  		verifiable.WithPublicKeyFetcher(verifiable.SingleKey(publicKeyPemToBytes(publicKey), kms.RSARS256)),
   196  		// do not test the cryptographic proofs (see https://github.com/w3c/vc-test-suite/issues/101)
   197  		verifiable.WithNoProofCheck(),
   198  		verifiable.WithJSONLDDocumentLoader(loader))
   199  	if err != nil {
   200  		abort("failed to decode credential: %v", err)
   201  	}
   202  
   203  	credential.JWT = ""
   204  
   205  	jsonBytes, err := credential.MarshalJSON()
   206  	if err != nil {
   207  		abort("failed to marshall verifiable credential to JSON: %v", err)
   208  	}
   209  
   210  	fmt.Println(string(jsonBytes))
   211  }
   212  
   213  func parseRsaKeys(packedKeys string) (private *rsa.PrivateKey, public *rsa.PublicKey) {
   214  	// there are several JWKs which are based64
   215  	decodedJwt, err := base64.StdEncoding.DecodeString(packedKeys)
   216  	if err != nil {
   217  		abort("cannot decode base64 of JSON containing JWT keys: %v", err)
   218  	}
   219  
   220  	// found the target JWK
   221  	decodedJwtMap := make(map[string]interface{})
   222  
   223  	err = json.Unmarshal(decodedJwt, &decodedJwtMap)
   224  	if err != nil {
   225  		abort("failed to decode JSON containing JWT keys: %v", err)
   226  	}
   227  
   228  	rs256PrivateKeyJwk, exist := decodedJwtMap["rs256PrivateKeyJwk"]
   229  	if !exist {
   230  		abort("cannot get rs256PrivateKeyJwk key")
   231  	}
   232  
   233  	// marshal found key back to bytes
   234  	jwkBytes, err := json.Marshal(rs256PrivateKeyJwk)
   235  	if err != nil {
   236  		abort("JSON marshalling error: %v", err)
   237  	}
   238  
   239  	jwk := &jose.JSONWebKey{}
   240  
   241  	err = jwk.UnmarshalJSON(jwkBytes)
   242  	if err != nil {
   243  		abort("JWK unmarshalling error: %v", err)
   244  	}
   245  
   246  	privateKey, ok := jwk.Key.(*rsa.PrivateKey)
   247  	if !ok {
   248  		abort("expected to get *rsa.PrivateKey, but got smth different")
   249  	}
   250  
   251  	publicKey := &privateKey.PublicKey
   252  
   253  	return privateKey, publicKey
   254  }
   255  
   256  func encodeVCToJSON(vcBytes []byte, testFileName string) {
   257  	vcOpts := []verifiable.CredentialOpt{
   258  		verifiable.WithNoCustomSchemaCheck(),
   259  		verifiable.WithNoProofCheck(),
   260  		verifiable.WithJSONLDDocumentLoader(loader),
   261  	}
   262  
   263  	// This are special test cases which should be made more precise in VC Test Suite.
   264  	// See https://github.com/w3c/vc-test-suite/issues/96 for more information.
   265  	if testFileName == "example-1-bad-cardinality.jsonld" || testFileName == "example-3-bad-cardinality.jsonld" {
   266  		vcOpts = append(vcOpts, verifiable.WithBaseContextValidation())
   267  	}
   268  
   269  	credential, err := verifiable.ParseCredential(vcBytes, vcOpts...)
   270  	if err != nil {
   271  		abort("failed to decode credential: %v", err)
   272  	}
   273  
   274  	encoded, err := credential.MarshalJSON()
   275  	if err != nil {
   276  		abort("failed to encode credential: %v", err)
   277  	}
   278  
   279  	fmt.Println(string(encoded))
   280  }
   281  
   282  func encodeVPToJSON(vcBytes []byte) {
   283  	// https://www.w3.org/TR/vc-data-model/#presentations-0 states "If present" under verifiableCredential
   284  	// but the test suite requires the element to be present. Hence, WithPresRequireVC is used in test suite runs.
   285  	vp, err := verifiable.ParsePresentation(vcBytes,
   286  		verifiable.WithPresDisabledProofCheck(),
   287  		verifiable.WithPresJSONLDDocumentLoader(loader))
   288  	if err != nil {
   289  		abort("failed to decode presentation: %v", err)
   290  	}
   291  
   292  	encoded, err := vp.MarshalJSON()
   293  	if err != nil {
   294  		abort("failed to encode presentation: %v", err)
   295  	}
   296  
   297  	fmt.Println(string(encoded))
   298  }
   299  
   300  func getRsaSigner(privKey *rsa.PrivateKey) *rsaSigner {
   301  	return &rsaSigner{privateKey: privKey}
   302  }
   303  
   304  type rsaSigner struct {
   305  	privateKey *rsa.PrivateKey
   306  }
   307  
   308  func (s *rsaSigner) Sign(data []byte) ([]byte, error) {
   309  	hash := crypto.SHA256.New()
   310  
   311  	_, err := hash.Write(data)
   312  	if err != nil {
   313  		return nil, err
   314  	}
   315  
   316  	hashed := hash.Sum(nil)
   317  
   318  	return rsa.SignPKCS1v15(rand.Reader, s.privateKey, crypto.SHA256, hashed)
   319  }
   320  
   321  func (s *rsaSigner) Alg() string {
   322  	return "PS256"
   323  }
   324  
   325  func abort(msg string, args ...interface{}) {
   326  	logger.Errorf(msg, args...)
   327  	os.Exit(1)
   328  }
   329  
   330  func publicKeyPemToBytes(key *rsa.PublicKey) []byte {
   331  	return x509.MarshalPKCS1PublicKey(key)
   332  }
   333  
   334  type provider struct {
   335  	ContextStore        ldstore.ContextStore
   336  	RemoteProviderStore ldstore.RemoteProviderStore
   337  }
   338  
   339  func (p *provider) JSONLDContextStore() ldstore.ContextStore {
   340  	return p.ContextStore
   341  }
   342  
   343  func (p *provider) JSONLDRemoteProviderStore() ldstore.RemoteProviderStore {
   344  	return p.RemoteProviderStore
   345  }