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