github.com/hyperledger/aries-framework-go@v0.3.2/pkg/doc/jose/kid/resolver/resolver.go (about)

     1  /*
     2  Copyright SecureKey Technologies Inc. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package resolver
     8  
     9  import (
    10  	"crypto/ecdsa"
    11  	"crypto/elliptic"
    12  	"encoding/json"
    13  	"errors"
    14  	"fmt"
    15  	"strings"
    16  
    17  	cryptoapi "github.com/hyperledger/aries-framework-go/pkg/crypto"
    18  	"github.com/hyperledger/aries-framework-go/pkg/doc/did"
    19  	"github.com/hyperledger/aries-framework-go/pkg/doc/util/jwkkid"
    20  	"github.com/hyperledger/aries-framework-go/pkg/doc/util/kmsdidkey"
    21  	vdrapi "github.com/hyperledger/aries-framework-go/pkg/framework/aries/api/vdr"
    22  	"github.com/hyperledger/aries-framework-go/pkg/kms"
    23  	"github.com/hyperledger/aries-framework-go/spi/storage"
    24  )
    25  
    26  const (
    27  	jsonWebKey2020            = "JsonWebKey2020"
    28  	x25519KeyAgreementKey2019 = "X25519KeyAgreementKey2019"
    29  )
    30  
    31  // KIDResolver helps resolve the kid public key from a recipient 'kid' or a sender 'skid' during JWE decryption.
    32  // The JWEDecrypter should be able to load the public key using a resolution scheme for a key reference found in the
    33  // 'skid' JWE protected header/'kid' recipient header.
    34  type KIDResolver interface {
    35  	// Resolve a 'kid'/'skid' into a marshalled public key or error if key resolution fails.
    36  	Resolve(string) (*cryptoapi.PublicKey, error)
    37  }
    38  
    39  // DIDKeyResolver resolves a 'kid'/'skid' containing a did:key value.
    40  type DIDKeyResolver struct{}
    41  
    42  // Resolve a 'kid'/'skid' protected header with a did:key value into a marshalled public key or error if key
    43  // resolution fails.
    44  func (k *DIDKeyResolver) Resolve(kid string) (*cryptoapi.PublicKey, error) {
    45  	return kmsdidkey.EncryptionPubKeyFromDIDKey(kid)
    46  }
    47  
    48  // StoreResolver resolves a 'kid'/'skid' containing a kms ID value (JWK fingerprint) from a dedicated pre-loaded store.
    49  // Note: this is not a kms keystore. This StoreResolver is useful in cases where a thirdparty store is needed. This is
    50  // useful in unit tests and especially for test vectors using the ECDH-1PU Appendix B example to load the sender key
    51  // so that recipients can resolve a predefined 'skid'. Aries Framework Go is using the DIDKeyResolver by default (for
    52  // request without DID docs) and DIDDocResolver (for requests with existing DID connections).
    53  type StoreResolver struct {
    54  	// store where the kid key is potentially stored.
    55  	Store storage.Store
    56  }
    57  
    58  // Resolve a 'kid'/'skid' by loading kid's PublicKey from a store or return an error if it fails.
    59  func (s *StoreResolver) Resolve(kid string) (*cryptoapi.PublicKey, error) {
    60  	var pubKey *cryptoapi.PublicKey
    61  
    62  	mPubKey, err := s.Store.Get(kid)
    63  	if err != nil {
    64  		return nil, fmt.Errorf("storeResolver: failed to resolve kid from store: %w", err)
    65  	}
    66  
    67  	err = json.Unmarshal(mPubKey, &pubKey)
    68  	if err != nil {
    69  		return nil, fmt.Errorf("storeResolver: failed to unmarshal public key from DB: %w", err)
    70  	}
    71  
    72  	return pubKey, nil
    73  }
    74  
    75  // DIDDocResolver helps resolves a KMS kid from 'kid'/'skid' with values set as didDoc[].KeyAgreement[].ID. The list of
    76  // DIDDocs should contain both sender and recipients docs for proper resolutio during unpacking.
    77  type DIDDocResolver struct {
    78  	VDRRegistry vdrapi.Registry
    79  }
    80  
    81  // Resolve kid into a *cryptoapi.PublicKey with ID set as the KMS kid. Where kid matches the DID doc found in the vdr
    82  // registry with first key entry matching doc.keyAgreement[].VerificationMethod.ID.
    83  func (d *DIDDocResolver) Resolve(kid string) (*cryptoapi.PublicKey, error) {
    84  	var (
    85  		pubKey *cryptoapi.PublicKey
    86  		err    error
    87  	)
    88  
    89  	if d.VDRRegistry == nil {
    90  		return nil, errors.New("didDocResolver: missing vdr registry")
    91  	}
    92  
    93  	i := strings.Index(kid, "#")
    94  
    95  	if i < 0 {
    96  		return nil, fmt.Errorf("didDocResolver: kid is not KeyAgreement.ID: '%v'", kid)
    97  	}
    98  
    99  	didDoc, err := d.VDRRegistry.Resolve(kid[:i])
   100  	if err != nil {
   101  		return nil, fmt.Errorf("didDocResolver: for recipient DID doc resolution %w", err)
   102  	}
   103  
   104  	for _, ka := range didDoc.DIDDocument.KeyAgreement {
   105  		k := &ka //nolint:gosec
   106  		keyAgreementID := ka.VerificationMethod.ID
   107  
   108  		if strings.HasPrefix(keyAgreementID, "#") {
   109  			keyAgreementID = didDoc.DIDDocument.ID + keyAgreementID
   110  		}
   111  
   112  		pubKey, err = extractKey(kid, keyAgreementID, k)
   113  		if err != nil {
   114  			return nil, err
   115  		}
   116  	}
   117  
   118  	return pubKey, nil
   119  }
   120  
   121  func extractKey(kid, keyAgreementID string, ka *did.Verification) (*cryptoapi.PublicKey, error) {
   122  	var (
   123  		pubKey *cryptoapi.PublicKey
   124  		err    error
   125  	)
   126  
   127  	if strings.EqualFold(kid, keyAgreementID) {
   128  		switch ka.VerificationMethod.Type {
   129  		case x25519KeyAgreementKey2019:
   130  			pubKey, err = buildX25519Key(ka)
   131  			if err != nil {
   132  				return nil, fmt.Errorf("didDocResolver: %w", err)
   133  			}
   134  		case jsonWebKey2020:
   135  			pubKey, err = buildJWKKey(ka)
   136  			if err != nil {
   137  				return nil, fmt.Errorf("didDocResolver: %w", err)
   138  			}
   139  		default:
   140  			return nil, fmt.Errorf("didDocResolver: can't build key from KayAgreement with type: '%v'",
   141  				ka.VerificationMethod.Type)
   142  		}
   143  	}
   144  
   145  	return pubKey, nil
   146  }
   147  
   148  func buildX25519Key(ka *did.Verification) (*cryptoapi.PublicKey, error) {
   149  	pubKey := &cryptoapi.PublicKey{
   150  		X:     ka.VerificationMethod.Value,
   151  		Curve: "X25519",
   152  		Type:  "OKP",
   153  	}
   154  
   155  	mPubKey, err := json.Marshal(pubKey)
   156  	if err != nil {
   157  		return nil, fmt.Errorf("buildX25519: marshal key error: %w", err)
   158  	}
   159  
   160  	x25519KMSKID, err := jwkkid.CreateKID(mPubKey, kms.X25519ECDHKWType)
   161  	if err != nil {
   162  		return nil, fmt.Errorf("buildX25519: createKID error:%w", err)
   163  	}
   164  
   165  	pubKey.KID = x25519KMSKID
   166  
   167  	return pubKey, nil
   168  }
   169  
   170  func buildJWKKey(ka *did.Verification) (*cryptoapi.PublicKey, error) {
   171  	var (
   172  		x  []byte
   173  		y  []byte
   174  		kt kms.KeyType
   175  	)
   176  
   177  	jwkKey := ka.VerificationMethod.JSONWebKey()
   178  	switch k := jwkKey.Key.(type) {
   179  	case *ecdsa.PublicKey:
   180  		x = k.X.Bytes()
   181  		y = k.Y.Bytes()
   182  	case []byte:
   183  		x = make([]byte, len(k))
   184  		copy(x, k)
   185  	default:
   186  		return nil, fmt.Errorf("buildJWKKey: unsupported JWK format: (%T)", k)
   187  	}
   188  
   189  	pubKey := &cryptoapi.PublicKey{
   190  		X:     x,
   191  		Y:     y,
   192  		Curve: jwkKey.Crv,
   193  		Type:  jwkKey.Kty,
   194  	}
   195  
   196  	switch jwkKey.Crv {
   197  	case elliptic.P256().Params().Name:
   198  		kt = kms.NISTP256ECDHKWType
   199  	case elliptic.P384().Params().Name:
   200  		kt = kms.NISTP384ECDHKWType
   201  	case elliptic.P521().Params().Name:
   202  		kt = kms.NISTP521ECDHKWType
   203  	case "X25519":
   204  		kt = kms.X25519ECDHKWType
   205  	}
   206  
   207  	mPubKey, err := json.Marshal(pubKey)
   208  	if err != nil {
   209  		return nil, fmt.Errorf("buildJWKKey: marshal key error: %w", err)
   210  	}
   211  
   212  	jwkKMSKID, err := jwkkid.CreateKID(mPubKey, kt)
   213  	if err != nil {
   214  		return nil, fmt.Errorf("buildJWKKey: createKID error:%w", err)
   215  	}
   216  
   217  	pubKey.KID = jwkKMSKID
   218  
   219  	return pubKey, nil
   220  }