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

     1  /*
     2  Copyright SecureKey Technologies Inc. All Rights Reserved.
     3  Copyright Avast Software. All Rights Reserved.
     4  
     5  SPDX-License-Identifier: Apache-2.0
     6  */
     7  
     8  package kmsdidkey
     9  
    10  import (
    11  	"crypto/elliptic"
    12  	"encoding/json"
    13  	"fmt"
    14  
    15  	"github.com/btcsuite/btcutil/base58"
    16  	commonpb "github.com/google/tink/go/proto/common_go_proto"
    17  
    18  	cryptoapi "github.com/hyperledger/aries-framework-go/pkg/crypto"
    19  	"github.com/hyperledger/aries-framework-go/pkg/doc/util/jwkkid"
    20  	"github.com/hyperledger/aries-framework-go/pkg/kms"
    21  	"github.com/hyperledger/aries-framework-go/pkg/vdr/fingerprint"
    22  	didfingerprint "github.com/hyperledger/aries-framework-go/pkg/vdr/fingerprint/didfp"
    23  )
    24  
    25  // keyTypeCodecs maps kms.KeyType to did:key codec.
    26  // nolint: gochecknoglobals
    27  var keyTypeCodecs = map[kms.KeyType]uint64{
    28  	// signing keys
    29  	kms.ED25519Type:            fingerprint.ED25519PubKeyMultiCodec,
    30  	kms.BLS12381G2Type:         fingerprint.BLS12381g2PubKeyMultiCodec,
    31  	kms.ECDSAP256TypeIEEEP1363: fingerprint.P256PubKeyMultiCodec,
    32  	kms.ECDSAP256TypeDER:       fingerprint.P256PubKeyMultiCodec,
    33  	kms.ECDSAP384TypeIEEEP1363: fingerprint.P384PubKeyMultiCodec,
    34  	kms.ECDSAP384TypeDER:       fingerprint.P384PubKeyMultiCodec,
    35  	kms.ECDSAP521TypeIEEEP1363: fingerprint.P521PubKeyMultiCodec,
    36  	kms.ECDSAP521TypeDER:       fingerprint.P521PubKeyMultiCodec,
    37  
    38  	// encryption keys
    39  	kms.X25519ECDHKWType:   fingerprint.X25519PubKeyMultiCodec,
    40  	kms.NISTP256ECDHKWType: fingerprint.P256PubKeyMultiCodec,
    41  	kms.NISTP384ECDHKWType: fingerprint.P384PubKeyMultiCodec,
    42  	kms.NISTP521ECDHKWType: fingerprint.P521PubKeyMultiCodec,
    43  }
    44  
    45  // BuildDIDKeyByKeyType creates a did key for pubKeyBytes based on the kms keyType.
    46  func BuildDIDKeyByKeyType(pubKeyBytes []byte, keyType kms.KeyType) (string, error) {
    47  	switch keyType {
    48  	case kms.X25519ECDHKW:
    49  		pubKey := &cryptoapi.PublicKey{}
    50  
    51  		err := json.Unmarshal(pubKeyBytes, pubKey)
    52  		if err != nil {
    53  			return "", fmt.Errorf("buildDIDkeyByKMSKeyType failed to unmarshal key type %v: %w", keyType, err)
    54  		}
    55  
    56  		pubKeyBytes = make([]byte, len(pubKey.X))
    57  		copy(pubKeyBytes, pubKey.X)
    58  	case kms.NISTP256ECDHKWType, kms.NISTP384ECDHKWType, kms.NISTP521ECDHKWType:
    59  		pubKey := &cryptoapi.PublicKey{}
    60  
    61  		err := json.Unmarshal(pubKeyBytes, pubKey)
    62  		if err != nil {
    63  			return "", fmt.Errorf("buildDIDkeyByKMSKeyType failed to unmarshal key type %v: %w", keyType, err)
    64  		}
    65  
    66  		ecKey, err := cryptoapi.ToECKey(pubKey)
    67  		if err != nil {
    68  			return "", fmt.Errorf("buildDIDkeyByKMSKeyType failed to unmarshal key type %v: %w", keyType, err)
    69  		}
    70  
    71  		// used Compressed EC format for did:key, the same way as vdr key creator.
    72  		pubKeyBytes = elliptic.MarshalCompressed(ecKey.Curve, ecKey.X, ecKey.Y)
    73  	}
    74  
    75  	if codec, ok := keyTypeCodecs[keyType]; ok {
    76  		didKey, _ := fingerprint.CreateDIDKeyByCode(codec, pubKeyBytes)
    77  
    78  		return didKey, nil
    79  	}
    80  
    81  	return "", fmt.Errorf("keyType '%s' does not have a multi-base codec", keyType)
    82  }
    83  
    84  // EncryptionPubKeyFromDIDKey parses the did:key DID and returns the key's raw value.
    85  // note: for NIST P ECDSA keys, the raw value does not have the compression point.
    86  //	In order to use elliptic.Unmarshal() with the raw value, the uncompressed point ([]byte{4}) must be prepended.
    87  //	see https://github.com/golang/go/blob/master/src/crypto/elliptic/elliptic.go#L384.
    88  //nolint:funlen,gocyclo
    89  func EncryptionPubKeyFromDIDKey(didKey string) (*cryptoapi.PublicKey, error) {
    90  	pubKey, code, err := extractRawKey(didKey)
    91  	if err != nil {
    92  		return nil, fmt.Errorf("encryptionPubKeyFromDIDKey: %w", err)
    93  	}
    94  
    95  	var (
    96  		crv   string
    97  		kmtKT kms.KeyType
    98  		kt    string
    99  		x     []byte
   100  		y     []byte
   101  	)
   102  
   103  	switch code {
   104  	case fingerprint.ED25519PubKeyMultiCodec: // TODO remove this case when legacyPacker is decommissioned.
   105  		var edKID string
   106  
   107  		kmtKT = kms.ED25519Type
   108  		pubEDKey := &cryptoapi.PublicKey{
   109  			X:     pubKey,
   110  			Curve: "Ed25519",
   111  			Type:  "OKP",
   112  		}
   113  
   114  		edKID, err = jwkkid.CreateKID(pubKey, kmtKT)
   115  		if err != nil {
   116  			return nil, fmt.Errorf("encryptionPubKeyFromDIDKey: %w", err)
   117  		}
   118  
   119  		pubEDKey.KID = edKID
   120  
   121  		return pubEDKey, nil
   122  	case fingerprint.X25519PubKeyMultiCodec:
   123  		var (
   124  			mPubXKey []byte
   125  			xKID     string
   126  		)
   127  
   128  		kmtKT = kms.X25519ECDHKWType
   129  		pubXKey := &cryptoapi.PublicKey{
   130  			X:     pubKey,
   131  			Curve: "X25519",
   132  			Type:  "OKP",
   133  		}
   134  
   135  		mPubXKey, err = json.Marshal(pubXKey)
   136  		if err != nil {
   137  			return nil, fmt.Errorf("encryptionPubKeyFromDIDKey: %w", err)
   138  		}
   139  
   140  		xKID, err = jwkkid.CreateKID(mPubXKey, kmtKT)
   141  		if err != nil {
   142  			return nil, fmt.Errorf("encryptionPubKeyFromDIDKey: %w", err)
   143  		}
   144  
   145  		pubXKey.KID = xKID
   146  
   147  		return pubXKey, nil
   148  	case fingerprint.P256PubKeyMultiCodec:
   149  		kmtKT = kms.ECDSAP256IEEEP1363
   150  		kt = "EC"
   151  		crv, x, y, pubKey = unmarshalECKey(elliptic.P256(), pubKey)
   152  	case fingerprint.P384PubKeyMultiCodec:
   153  		kmtKT = kms.ECDSAP384IEEEP1363
   154  		kt = "EC"
   155  		crv, x, y, pubKey = unmarshalECKey(elliptic.P384(), pubKey)
   156  	case fingerprint.P521PubKeyMultiCodec:
   157  		kmtKT = kms.ECDSAP521TypeIEEEP1363
   158  		kt = "EC"
   159  		crv, x, y, pubKey = unmarshalECKey(elliptic.P521(), pubKey)
   160  	default:
   161  		return nil, fmt.Errorf("encryptionPubKeyFromDIDKey: unsupported key multicodec code [0x%x]", code)
   162  	}
   163  
   164  	kid, err := jwkkid.CreateKID(pubKey, kmtKT)
   165  	if err != nil {
   166  		return nil, fmt.Errorf("encryptionPubKeyFromDIDKey: %w", err)
   167  	}
   168  
   169  	return &cryptoapi.PublicKey{
   170  		KID:   kid,
   171  		X:     x,
   172  		Y:     y,
   173  		Curve: crv,
   174  		Type:  kt,
   175  	}, nil
   176  }
   177  
   178  func unmarshalECKey(ecCRV elliptic.Curve, pubKey []byte) (string, []byte, []byte, []byte) {
   179  	var (
   180  		x []byte
   181  		y []byte
   182  	)
   183  
   184  	ecCurves := map[elliptic.Curve]string{
   185  		elliptic.P256(): commonpb.EllipticCurveType_NIST_P256.String(),
   186  		elliptic.P384(): commonpb.EllipticCurveType_NIST_P384.String(),
   187  		elliptic.P521(): commonpb.EllipticCurveType_NIST_P521.String(),
   188  	}
   189  
   190  	xBig, yBig := elliptic.UnmarshalCompressed(ecCRV, pubKey)
   191  	if xBig != nil && yBig != nil {
   192  		x = xBig.Bytes()
   193  		y = yBig.Bytes()
   194  
   195  		// need to marshal pubKey in uncompressed format for CreateKID() call in EncryptionPubKeyFromDIDKey above since
   196  		// did:key uses compressed elliptic format.
   197  		pubKey = elliptic.Marshal(ecCRV, xBig, yBig)
   198  	} else { // try normal Unmarshal if compressed returned nil xBig and yBig.
   199  		// add compression byte for uncompressed key, comment of fingerprint.PubKeyFromDIDKey().
   200  		pubKey = append([]byte{4}, pubKey...)
   201  		xBig, yBig = elliptic.Unmarshal(ecCRV, pubKey)
   202  
   203  		x = xBig.Bytes()
   204  		y = yBig.Bytes()
   205  	}
   206  
   207  	return ecCurves[ecCRV], x, y, pubKey
   208  }
   209  
   210  func extractRawKey(didKey string) ([]byte, uint64, error) {
   211  	idMethodSpecificID, err := didfingerprint.MethodIDFromDIDKey(didKey)
   212  	if err != nil {
   213  		return nil, 0, fmt.Errorf("extractRawKey: MethodIDFromDIDKey failure: %w", err)
   214  	}
   215  
   216  	pubKey, code, err := fingerprint.PubKeyFromFingerprint(idMethodSpecificID)
   217  	if err != nil {
   218  		return nil, 0, fmt.Errorf("extractRawKey: PubKeyFromFingerprint failure: %w", err)
   219  	}
   220  
   221  	return pubKey, code, nil
   222  }
   223  
   224  // GetBase58PubKeyFromDIDKey parses the did:key DID and returns the key's base58 encoded value.
   225  func GetBase58PubKeyFromDIDKey(didKey string) (string, error) {
   226  	key, err := EncryptionPubKeyFromDIDKey(didKey)
   227  	if err != nil {
   228  		return "", fmt.Errorf("GetBase58PubKeyFromDIDKey: failed to parse public key bytes from "+
   229  			"%s: %w", didKey, err)
   230  	}
   231  
   232  	return base58.Encode(key.X), nil
   233  }