github.com/hyperledger/aries-framework-go@v0.3.2/pkg/doc/sdjwt/example_test.go (about)

     1  /*
     2  Copyright SecureKey Technologies Inc. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package sdjwt
     8  
     9  import (
    10  	"crypto/ed25519"
    11  	"crypto/rand"
    12  	"encoding/json"
    13  	"fmt"
    14  	"time"
    15  
    16  	"github.com/go-jose/go-jose/v3/jwt"
    17  
    18  	"github.com/hyperledger/aries-framework-go/pkg/doc/jose/jwk"
    19  	"github.com/hyperledger/aries-framework-go/pkg/doc/jose/jwk/jwksupport"
    20  	afjwt "github.com/hyperledger/aries-framework-go/pkg/doc/jwt"
    21  	"github.com/hyperledger/aries-framework-go/pkg/doc/sdjwt/holder"
    22  	"github.com/hyperledger/aries-framework-go/pkg/doc/sdjwt/issuer"
    23  	"github.com/hyperledger/aries-framework-go/pkg/doc/sdjwt/verifier"
    24  )
    25  
    26  func ExampleSimpleClaims() { //nolint:govet
    27  	signer, signatureVerifier, err := setUp()
    28  	if err != nil {
    29  		fmt.Println("failed to set-up test: %w", err.Error())
    30  	}
    31  
    32  	claims := map[string]interface{}{
    33  		"given_name": "Albert",
    34  		"last_name":  "Smith",
    35  	}
    36  
    37  	// Issuer will issue SD-JWT for specified claims.
    38  	token, err := issuer.New(testIssuer, claims, nil, signer)
    39  	if err != nil {
    40  		fmt.Println("failed to issue SD-JWT: %w", err.Error())
    41  	}
    42  
    43  	combinedFormatForIssuance, err := token.Serialize(false)
    44  	if err != nil {
    45  		fmt.Println("failed to issue SD-JWT: %w", err.Error())
    46  	}
    47  
    48  	// Holder will parse combined format for issuance and hold on to that
    49  	// combined format for issuance and the claims that can be selected.
    50  	holderClaims, err := holder.Parse(combinedFormatForIssuance, holder.WithSignatureVerifier(signatureVerifier))
    51  	if err != nil {
    52  		fmt.Println("holder failed to parse SD-JWT: %w", err.Error())
    53  	}
    54  
    55  	// The Holder will only select given_name
    56  	selectedDisclosures := getDisclosuresFromClaimNames([]string{"given_name"}, holderClaims)
    57  
    58  	// Holder will disclose only sub-set of claims to verifier.
    59  	combinedFormatForPresentation, err := holder.CreatePresentation(combinedFormatForIssuance, selectedDisclosures)
    60  	if err != nil {
    61  		fmt.Println("holder failed to create presentation: %w", err.Error())
    62  	}
    63  
    64  	// Verifier will validate combined format for presentation and create verified claims.
    65  	verifiedClaims, err := verifier.Parse(combinedFormatForPresentation,
    66  		verifier.WithSignatureVerifier(signatureVerifier))
    67  	if err != nil {
    68  		fmt.Println("verifier failed to parse holder presentation: %w", err.Error())
    69  	}
    70  
    71  	verifiedClaimsJSON, err := marshalObj(verifiedClaims)
    72  	if err != nil {
    73  		fmt.Println("verifier failed to marshal verified claims: %w", err.Error())
    74  	}
    75  
    76  	fmt.Println(verifiedClaimsJSON)
    77  
    78  	// Output: {
    79  	//	"given_name": "Albert",
    80  	//	"iss": "https://example.com/issuer"
    81  	//}
    82  }
    83  
    84  func ExampleComplexClaimsWithHolderBinding() { //nolint:govet
    85  	signer, signatureVerifier, err := setUp()
    86  	if err != nil {
    87  		fmt.Println("failed to set-up test: %w", err.Error())
    88  	}
    89  
    90  	holderSigner, holderJWK, err := setUpHolderBinding()
    91  	if err != nil {
    92  		fmt.Println("failed to set-up test: %w", err.Error())
    93  	}
    94  
    95  	claims := map[string]interface{}{
    96  		"sub":          "john_doe_42",
    97  		"given_name":   "John",
    98  		"family_name":  "Doe",
    99  		"email":        "johndoe@example.com",
   100  		"phone_number": "+1-202-555-0101",
   101  		"birthdate":    "1940-01-01",
   102  		"address": map[string]interface{}{
   103  			"street_address": "123 Main St",
   104  			"locality":       "Anytown",
   105  			"region":         "Anystate",
   106  			"country":        "US",
   107  		},
   108  	}
   109  
   110  	// Issuer will issue SD-JWT for specified claims. Structured claims not selected as an option hence complex object
   111  	// address will be treated as an object not as a set of properties. Holder public key is provided therefore it will
   112  	// be added as "cnf" claim.
   113  	token, err := issuer.New(testIssuer, claims, nil, signer,
   114  		issuer.WithHolderPublicKey(holderJWK),
   115  	)
   116  	if err != nil {
   117  		fmt.Println("failed to issue SD-JWT: %w", err.Error())
   118  	}
   119  
   120  	combinedFormatForIssuance, err := token.Serialize(false)
   121  	if err != nil {
   122  		fmt.Println("failed to issue SD-JWT: %w", err.Error())
   123  	}
   124  
   125  	// Holder will parse combined format for issuance and hold on to that
   126  	// combined format for issuance and the claims that can be selected.
   127  	holderClaims, err := holder.Parse(combinedFormatForIssuance, holder.WithSignatureVerifier(signatureVerifier))
   128  	if err != nil {
   129  		fmt.Println("holder failed to parse SD-JWT: %w", err.Error())
   130  	}
   131  
   132  	// The Holder will only select given_name, address
   133  	selectedDisclosures := getDisclosuresFromClaimNames([]string{"given_name", "address"}, holderClaims)
   134  
   135  	// Holder will disclose only sub-set of claims to verifier.
   136  	combinedFormatForPresentation, err := holder.CreatePresentation(combinedFormatForIssuance, selectedDisclosures,
   137  		holder.WithHolderBinding(&holder.BindingInfo{
   138  			Payload: holder.BindingPayload{
   139  				Nonce:    "nonce",
   140  				Audience: "https://test.com/verifier",
   141  				IssuedAt: jwt.NewNumericDate(time.Now()),
   142  			},
   143  			Signer: holderSigner,
   144  		}))
   145  	if err != nil {
   146  		fmt.Println("holder failed to create presentation: %w", err.Error())
   147  	}
   148  
   149  	// Verifier will validate combined format for presentation and create verified claims.
   150  	verifiedClaims, err := verifier.Parse(combinedFormatForPresentation,
   151  		verifier.WithSignatureVerifier(signatureVerifier))
   152  	if err != nil {
   153  		fmt.Println("verifier failed to parse holder presentation: %w", err.Error())
   154  	}
   155  
   156  	addressClaimsJSON, err := marshalObj(verifiedClaims["address"])
   157  	if err != nil {
   158  		fmt.Println("verifier failed to marshal verified claims: %w", err.Error())
   159  	}
   160  
   161  	fmt.Println(addressClaimsJSON)
   162  
   163  	// Output: {
   164  	//	"country": "US",
   165  	//	"locality": "Anytown",
   166  	//	"region": "Anystate",
   167  	//	"street_address": "123 Main St"
   168  	//}
   169  }
   170  
   171  func ExampleComplexObjectWithStructuredClaims() { //nolint:govet
   172  	signer, signatureVerifier, err := setUp()
   173  	if err != nil {
   174  		fmt.Println("failed to set-up test: %w", err.Error())
   175  	}
   176  
   177  	claims := map[string]interface{}{
   178  		"sub":          "john_doe_42",
   179  		"given_name":   "John",
   180  		"family_name":  "Doe",
   181  		"email":        "johndoe@example.com",
   182  		"phone_number": "+1-202-555-0101",
   183  		"birthdate":    "1940-01-01",
   184  		"address": map[string]interface{}{
   185  			"street_address": "123 Main St",
   186  			"locality":       "Anytown",
   187  			"region":         "Anystate",
   188  			"country":        "US",
   189  		},
   190  	}
   191  
   192  	// Issuer will issue SD-JWT for specified claims.
   193  	token, err := issuer.New(testIssuer, claims, nil, signer,
   194  		issuer.WithStructuredClaims(true),
   195  		issuer.WithNonSelectivelyDisclosableClaims([]string{"address.country"}),
   196  	)
   197  	if err != nil {
   198  		fmt.Println("failed to issue SD-JWT: %w", err.Error())
   199  	}
   200  
   201  	combinedFormatForIssuance, err := token.Serialize(false)
   202  	if err != nil {
   203  		fmt.Println("failed to issue SD-JWT: %w", err.Error())
   204  	}
   205  
   206  	// Holder will parse combined format for issuance and hold on to that
   207  	// combined format for issuance and the claims that can be selected.
   208  	holderClaims, err := holder.Parse(combinedFormatForIssuance, holder.WithSignatureVerifier(signatureVerifier))
   209  	if err != nil {
   210  		fmt.Println("holder failed to parse SD-JWT: %w", err.Error())
   211  	}
   212  
   213  	// The Holder will only select given_name, street_address
   214  	selectedDisclosures := getDisclosuresFromClaimNames([]string{"given_name", "street_address"}, holderClaims)
   215  
   216  	// Holder will disclose only sub-set of claims to verifier.
   217  	combinedFormatForPresentation, err := holder.CreatePresentation(combinedFormatForIssuance, selectedDisclosures)
   218  	if err != nil {
   219  		fmt.Println("holder failed to create presentation: %w", err.Error())
   220  	}
   221  
   222  	// Verifier will validate combined format for presentation and create verified claims.
   223  	verifiedClaims, err := verifier.Parse(combinedFormatForPresentation,
   224  		verifier.WithSignatureVerifier(signatureVerifier))
   225  	if err != nil {
   226  		fmt.Println("verifier failed to parse holder presentation: %w", err.Error())
   227  	}
   228  
   229  	verifiedClaimsJSON, err := marshalObj(verifiedClaims)
   230  	if err != nil {
   231  		fmt.Println("verifier failed to marshal verified claims: %w", err.Error())
   232  	}
   233  
   234  	fmt.Println(verifiedClaimsJSON)
   235  
   236  	// Output: {
   237  	//	"address": {
   238  	//		"country": "US",
   239  	//		"street_address": "123 Main St"
   240  	//	},
   241  	//	"given_name": "John",
   242  	//	"iss": "https://example.com/issuer"
   243  	//}
   244  }
   245  func setUp() (*afjwt.JoseED25519Signer, *afjwt.JoseEd25519Verifier, error) {
   246  	issuerPublicKey, issuerPrivateKey, err := ed25519.GenerateKey(rand.Reader)
   247  	if err != nil {
   248  		return nil, nil, err
   249  	}
   250  
   251  	signer := afjwt.NewEd25519Signer(issuerPrivateKey)
   252  
   253  	signatureVerifier, err := afjwt.NewEd25519Verifier(issuerPublicKey)
   254  	if err != nil {
   255  		return nil, nil, err
   256  	}
   257  
   258  	return signer, signatureVerifier, nil
   259  }
   260  
   261  func setUpHolderBinding() (*afjwt.JoseED25519Signer, *jwk.JWK, error) {
   262  	holderPublicKey, holderPrivateKey, err := ed25519.GenerateKey(rand.Reader)
   263  	if err != nil {
   264  		return nil, nil, err
   265  	}
   266  
   267  	holderPublicJWK, err := jwksupport.JWKFromKey(holderPublicKey)
   268  	if err != nil {
   269  		return nil, nil, err
   270  	}
   271  
   272  	holderSigner := afjwt.NewEd25519Signer(holderPrivateKey)
   273  
   274  	return holderSigner, holderPublicJWK, nil
   275  }
   276  
   277  func marshalObj(obj interface{}) (string, error) {
   278  	objBytes, err := json.Marshal(obj)
   279  	if err != nil {
   280  		fmt.Println("failed to marshal object: %w", err.Error())
   281  	}
   282  
   283  	return prettyPrint(objBytes)
   284  }