github.com/trustbloc/kms-go@v1.1.2/doc/util/jwkkid/kid_creator.go (about)

     1  /*
     2  Copyright SecureKey Technologies Inc. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package jwkkid
     8  
     9  import (
    10  	"crypto"
    11  	"crypto/ecdsa"
    12  	"crypto/elliptic"
    13  	"encoding/base64"
    14  	"encoding/json"
    15  	"errors"
    16  	"fmt"
    17  	"math/big"
    18  
    19  	"github.com/trustbloc/kms-go/util/cryptoutil"
    20  
    21  	"github.com/trustbloc/kms-go/spi/kms"
    22  
    23  	cryptoapi "github.com/trustbloc/kms-go/spi/crypto"
    24  
    25  	"github.com/trustbloc/kms-go/doc/jose/jwk"
    26  	"github.com/trustbloc/kms-go/doc/jose/jwk/jwksupport"
    27  )
    28  
    29  var errInvalidKeyType = errors.New("key type is not supported")
    30  
    31  // CreateKID creates a KID value based on the marshalled keyBytes of type kt. This function should be called for
    32  // asymmetric public keys only (ECDSA DER or IEEE-P1363, ED25519, X25519, BLS12381G2).
    33  // returns:
    34  //   - base64 raw (no padding) URL encoded KID
    35  //   - error in case of error
    36  //
    37  //nolint:gocyclo
    38  func CreateKID(keyBytes []byte, kt kms.KeyType) (string, error) {
    39  	if len(keyBytes) == 0 {
    40  		return "", errors.New("createKID: empty key")
    41  	}
    42  
    43  	switch kt {
    44  	case kms.X25519ECDHKWType: // X25519 JWK is not supported by go jose, manually build it and build its resulting KID.
    45  		x25519KID, err := createX25519KID(keyBytes)
    46  		if err != nil {
    47  			return "", fmt.Errorf("createKID: %w", err)
    48  		}
    49  
    50  		return x25519KID, nil
    51  	case kms.BLS12381G2Type: // BBS+ as JWK thumbprint.
    52  		bbsKID, err := createBLS12381G2KID(keyBytes)
    53  		if err != nil {
    54  			return "", fmt.Errorf("createKID: %w", err)
    55  		}
    56  
    57  		return bbsKID, nil
    58  	case kms.ECDSASecp256k1TypeDER, kms.ECDSASecp256k1TypeIEEEP1363:
    59  		secp256k1KID, err := secp256k1Thumbprint(keyBytes, kt)
    60  		if err != nil {
    61  			return "", fmt.Errorf("createKID: %w", err)
    62  		}
    63  
    64  		return secp256k1KID, nil
    65  	}
    66  
    67  	j, err := BuildJWK(keyBytes, kt)
    68  	if err != nil {
    69  		return "", fmt.Errorf("createKID: failed to build jwk: %w", err)
    70  	}
    71  
    72  	tp, err := j.Thumbprint(crypto.SHA256)
    73  	if err != nil {
    74  		return "", fmt.Errorf("createKID: failed to get jwk Thumbprint: %w", err)
    75  	}
    76  
    77  	return base64.RawURLEncoding.EncodeToString(tp), nil
    78  }
    79  
    80  func secp256k1Thumbprint(keyBytes []byte, kt kms.KeyType) (string, error) {
    81  	switch kt {
    82  	case kms.ECDSASecp256k1IEEEP1363:
    83  	case kms.ECDSASecp256k1DER:
    84  	default:
    85  		return "", fmt.Errorf("secp256k1Thumbprint: invalid key type: %s", kt)
    86  	}
    87  
    88  	k, err := jwksupport.PubKeyBytesToKey(keyBytes, kt)
    89  	if err != nil {
    90  		return "", fmt.Errorf("secp256k1Thumbprint: failed to build jwk: %w", err)
    91  	}
    92  
    93  	var input string
    94  
    95  	switch key := k.(type) {
    96  	case *ecdsa.PublicKey:
    97  		input, err = secp256k1ThumbprintInput(key.Curve, key.X, key.Y)
    98  		if err != nil {
    99  			return "", fmt.Errorf("secp256k1Thumbprint: failed to get public key thumbprint input: %w", err)
   100  		}
   101  	default:
   102  		return "", fmt.Errorf("secp256k1Thumbprint: unknown key type '%T'", key)
   103  	}
   104  
   105  	h := crypto.SHA256.New()
   106  	_, _ = h.Write([]byte(input))
   107  
   108  	return base64.RawURLEncoding.EncodeToString(h.Sum(nil)), nil
   109  }
   110  
   111  func secp256k1ThumbprintInput(curve elliptic.Curve, x, y *big.Int) (string, error) {
   112  	ecSecp256K1ThumbprintTemplate := `{"crv":"SECP256K1","kty":"EC","x":"%s","y":"%s"}`
   113  
   114  	coordLength := curveSize(curve)
   115  
   116  	if len(x.Bytes()) > coordLength || len(y.Bytes()) > coordLength {
   117  		return "", errors.New("invalid elliptic secp256k1 key (too large)")
   118  	}
   119  
   120  	return fmt.Sprintf(ecSecp256K1ThumbprintTemplate,
   121  		newFixedSizeBuffer(x.Bytes(), coordLength).base64(),
   122  		newFixedSizeBuffer(y.Bytes(), coordLength).base64()), nil
   123  }
   124  
   125  // byteBuffer represents a slice of bytes that can be serialized to url-safe base64.
   126  type byteBuffer struct {
   127  	data []byte
   128  }
   129  
   130  func (b *byteBuffer) base64() string {
   131  	return base64.RawURLEncoding.EncodeToString(b.data)
   132  }
   133  
   134  func newBuffer(data []byte) *byteBuffer {
   135  	if data == nil {
   136  		return nil
   137  	}
   138  
   139  	return &byteBuffer{
   140  		data: data,
   141  	}
   142  }
   143  
   144  func newFixedSizeBuffer(data []byte, length int) *byteBuffer {
   145  	if len(data) > length {
   146  		panic("newFixedSizeBuffer: invalid call to newFixedSizeBuffer (len(data) > length)")
   147  	}
   148  
   149  	pad := make([]byte, length-len(data))
   150  
   151  	return newBuffer(append(pad, data...))
   152  }
   153  
   154  // Get size of curve in bytes.
   155  func curveSize(crv elliptic.Curve) int {
   156  	bits := crv.Params().BitSize
   157  	byteSize := 8
   158  
   159  	div := bits / byteSize
   160  	mod := bits % byteSize
   161  
   162  	if mod == 0 {
   163  		return div
   164  	}
   165  
   166  	return div + 1
   167  }
   168  
   169  // BuildJWK builds a go jose JWK from keyBytes with key type kt.
   170  func BuildJWK(keyBytes []byte, kt kms.KeyType) (*jwk.JWK, error) { //nolint: gocyclo
   171  	switch kt {
   172  	case
   173  		kms.ECDSAP256TypeDER, kms.ECDSAP384TypeDER, kms.ECDSAP521TypeDER,
   174  		kms.ECDSAP256TypeIEEEP1363, kms.ECDSAP384TypeIEEEP1363, kms.ECDSAP521TypeIEEEP1363,
   175  		kms.NISTP256ECDHKWType, kms.NISTP384ECDHKWType, kms.NISTP521ECDHKWType,
   176  		kms.ECDSASecp256k1DER, kms.ECDSASecp256k1IEEEP1363,
   177  		kms.ED25519Type, kms.X25519ECDHKWType, kms.BLS12381G2Type:
   178  		return jwksupport.PubKeyBytesToJWK(keyBytes, kt)
   179  	default:
   180  		return nil, fmt.Errorf("buildJWK: %w: '%s'", errInvalidKeyType, kt)
   181  	}
   182  }
   183  
   184  func unmarshalECDHKey(keyBytes []byte) (*cryptoapi.PublicKey, error) {
   185  	compositeKey := &cryptoapi.PublicKey{}
   186  
   187  	err := json.Unmarshal(keyBytes, compositeKey)
   188  	if err != nil {
   189  		return nil, fmt.Errorf("unmarshalECDHKey: failed to unmarshal ECDH key: %w", err)
   190  	}
   191  
   192  	return compositeKey, nil
   193  }
   194  
   195  func createX25519KID(marshalledKey []byte) (string, error) {
   196  	compositeKey, err := unmarshalECDHKey(marshalledKey)
   197  	if err != nil {
   198  		return "", fmt.Errorf("createX25519KID: %w", err)
   199  	}
   200  
   201  	j, err := buildX25519JWK(compositeKey.X)
   202  	if err != nil {
   203  		return "", fmt.Errorf("createX25519KID: %w", err)
   204  	}
   205  
   206  	thumbprint := sha256Sum(j)
   207  
   208  	return base64.RawURLEncoding.EncodeToString(thumbprint), nil
   209  }
   210  
   211  func buildX25519JWK(keyBytes []byte) (string, error) {
   212  	const x25519ThumbprintTemplate = `{"crv":"X25519","kty":"OKP","x":"%s"}`
   213  
   214  	lenKey := len(keyBytes)
   215  	if lenKey > cryptoutil.Curve25519KeySize {
   216  		return "", errors.New("buildX25519JWK: invalid ECDH X25519 key")
   217  	}
   218  
   219  	pad := make([]byte, cryptoutil.Curve25519KeySize-lenKey)
   220  	x25519RawKey := append(pad, keyBytes...)
   221  
   222  	j := fmt.Sprintf(x25519ThumbprintTemplate, base64.RawURLEncoding.EncodeToString(x25519RawKey))
   223  
   224  	return j, nil
   225  }
   226  
   227  func createBLS12381G2KID(keyBytes []byte) (string, error) {
   228  	const (
   229  		bls12381g2ThumbprintTemplate = `{"crv":"Bls12381g2","kty":"OKP","x":"%s"}`
   230  		// Default BLS 12-381 public key length in G2 field.
   231  		bls12381G2PublicKeyLen = 96
   232  	)
   233  
   234  	lenKey := len(keyBytes)
   235  
   236  	if lenKey > bls12381G2PublicKeyLen {
   237  		return "", errors.New("invalid BBS+ key")
   238  	}
   239  
   240  	pad := make([]byte, bls12381G2PublicKeyLen-lenKey)
   241  	bbsRawKey := append(pad, keyBytes...)
   242  
   243  	j := fmt.Sprintf(bls12381g2ThumbprintTemplate, base64.RawURLEncoding.EncodeToString(bbsRawKey))
   244  
   245  	thumbprint := sha256Sum(j)
   246  
   247  	return base64.RawURLEncoding.EncodeToString(thumbprint), nil
   248  }
   249  
   250  func sha256Sum(j string) []byte {
   251  	h := crypto.SHA256.New()
   252  	_, _ = h.Write([]byte(j)) // SHA256 digest returns empty error on Write()
   253  
   254  	return h.Sum(nil)
   255  }