Skip to content

Commit

Permalink
feat: kms-crypto wrapper local&web suites, with APIs for VCS
Browse files Browse the repository at this point in the history
PR also cleans up duplication in public key parsing APIs

Signed-off-by: Filip Burlacu <[email protected]>
  • Loading branch information
Moopli committed Sep 27, 2023
1 parent 24e819a commit dd6e967
Show file tree
Hide file tree
Showing 26 changed files with 1,501 additions and 256 deletions.
146 changes: 106 additions & 40 deletions doc/jose/jwk/jwksupport/jwk.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,14 @@ import (
"crypto/ed25519"
"crypto/elliptic"
"crypto/x509"
"crypto/x509/pkix"
"encoding/asn1"
"encoding/json"
"errors"
"fmt"
"math/big"

"github.com/btcsuite/btcd/btcec"
"github.com/go-jose/go-jose/v3"
"github.com/trustbloc/bbs-signature-go/bbs12381g2pub"

Expand Down Expand Up @@ -56,61 +59,41 @@ func JWKFromKey(opaqueKey interface{}) (*jwk.JWK, error) {
return key, nil
}

// JWKFromX25519Key is similar to JWKFromKey but is specific to X25519 keys when using a public key as raw []byte.
// This builder function presets the curve and key type in the JWK.
// Using JWKFromKey for X25519 raw keys will not have these fields set and will not provide the right JWK output.
func JWKFromX25519Key(pubKey []byte) (*jwk.JWK, error) {
key := &jwk.JWK{
JSONWebKey: jose.JSONWebKey{
Key: pubKey,
},
Crv: x25519Crv,
Kty: okpKty,
}

// marshal/unmarshal to get all JWK's fields other than Key filled.
keyBytes, err := key.MarshalJSON()
if err != nil {
return nil, fmt.Errorf("create JWK: %w", err)
}

err = key.UnmarshalJSON(keyBytes)
if err != nil {
return nil, fmt.Errorf("create JWK: %w", err)
}

return key, nil
}

// PubKeyBytesToJWK converts marshalled bytes of keyType into JWK.
func PubKeyBytesToJWK(bytes []byte, keyType kms.KeyType) (*jwk.JWK, error) { // nolint:gocyclo
// PubKeyBytesToKey creates an opaque key struct from the given public key bytes.
// It's e.g. *ecdsa.PublicKey, *ecdsa.PrivateKey, ed25519.VerificationMethod, *bbs12381g2pub.PrivateKey or
// *bbs12381g2pub.PublicKey.
func PubKeyBytesToKey(bytes []byte, keyType kms.KeyType) (interface{}, error) { // nolint:gocyclo
switch keyType {
case kms.ED25519Type:
return JWKFromKey(ed25519.PublicKey(bytes))
return ed25519.PublicKey(bytes), nil
case kms.X25519ECDHKWType:
return bytes, nil
case kms.BLS12381G2Type:
bbsKey, err := bbs12381g2pub.UnmarshalPublicKey(bytes)
if err != nil {
return nil, err
}

return JWKFromKey(bbsKey)
case kms.ECDSAP256TypeIEEEP1363, kms.ECDSAP384TypeIEEEP1363, kms.ECDSAP521TypeIEEEP1363:
return bbs12381g2pub.UnmarshalPublicKey(bytes)
case kms.ECDSAP256TypeIEEEP1363, kms.ECDSAP384TypeIEEEP1363, kms.ECDSAP521TypeIEEEP1363,
kms.ECDSASecp256k1TypeIEEEP1363:
crv := getECDSACurve(keyType)
x, y := elliptic.Unmarshal(crv, bytes)

return JWKFromKey(&ecdsa.PublicKey{Curve: crv, X: x, Y: y})
return &ecdsa.PublicKey{
Curve: crv,
X: x,
Y: y,
}, nil
case kms.ECDSAP256TypeDER, kms.ECDSAP384TypeDER, kms.ECDSAP521TypeDER:
pubKey, err := x509.ParsePKIXPublicKey(bytes)
if err != nil {
return nil, err
return nil, fmt.Errorf("failed to parse ecdsa key in DER format: %w", err)
}

ecKey, ok := pubKey.(*ecdsa.PublicKey)
if !ok {
return nil, errors.New("invalid EC key")
}

return JWKFromKey(ecKey)
return ecKey, nil
case kms.ECDSASecp256k1TypeDER:
return parseSecp256k1DER(bytes)
case kms.NISTP256ECDHKWType, kms.NISTP384ECDHKWType, kms.NISTP521ECDHKWType:
crv := getECDSACurve(keyType)
pubKey := &cryptoapi.PublicKey{}
Expand All @@ -126,9 +109,62 @@ func PubKeyBytesToJWK(bytes []byte, keyType kms.KeyType) (*jwk.JWK, error) { //
Y: new(big.Int).SetBytes(pubKey.Y),
}

return JWKFromKey(ecdsaKey)
return ecdsaKey, nil
default:
return nil, fmt.Errorf("convertPubKeyJWK: invalid key type: %s", keyType)
}
}

// JWKFromX25519Key is similar to JWKFromKey but is specific to X25519 keys when using a public key as raw []byte.
// This builder function presets the curve and key type in the JWK.
// Using JWKFromKey for X25519 raw keys will not have these fields set and will not provide the right JWK output.
func JWKFromX25519Key(pubKey []byte) (*jwk.JWK, error) {
key := &jwk.JWK{
JSONWebKey: jose.JSONWebKey{
Key: pubKey,
},
Crv: x25519Crv,
Kty: okpKty,
}

// marshal/unmarshal to get all JWK's fields other than Key filled.
keyBytes, err := key.MarshalJSON()
if err != nil {
return nil, fmt.Errorf("create JWK: %w", err)
}

err = key.UnmarshalJSON(keyBytes)
if err != nil {
return nil, fmt.Errorf("create JWK: %w", err)
}

return key, nil
}

// PubKeyBytesToJWK converts marshalled bytes of keyType into JWK.
func PubKeyBytesToJWK(bytes []byte, keyType kms.KeyType) (*jwk.JWK, error) {
switch keyType {
case kms.ED25519Type:
return &jwk.JWK{
JSONWebKey: jose.JSONWebKey{
Key: ed25519.PublicKey(bytes),
},
Kty: "OKP",
Crv: "Ed25519",
}, nil
case kms.X25519ECDHKWType:
return JWKFromX25519Key(bytes)
case kms.BLS12381G2Type,
kms.ECDSASecp256k1TypeIEEEP1363, kms.ECDSASecp256k1TypeDER,
kms.ECDSAP256TypeIEEEP1363, kms.ECDSAP384TypeIEEEP1363, kms.ECDSAP521TypeIEEEP1363,
kms.ECDSAP256TypeDER, kms.ECDSAP384TypeDER, kms.ECDSAP521TypeDER,
kms.NISTP256ECDHKWType, kms.NISTP384ECDHKWType, kms.NISTP521ECDHKWType:
key, err := PubKeyBytesToKey(bytes, keyType)
if err != nil {
return nil, err
}

return JWKFromKey(key)
default:
return nil, fmt.Errorf("convertPubKeyJWK: invalid key type: %s", keyType)
}
Expand All @@ -142,11 +178,41 @@ func getECDSACurve(keyType kms.KeyType) elliptic.Curve {
return elliptic.P384()
case kms.ECDSAP521TypeIEEEP1363, kms.ECDSAP521TypeDER, kms.NISTP521ECDHKWType:
return elliptic.P521()
case kms.ECDSASecp256k1TypeIEEEP1363, kms.ECDSASecp256k1TypeDER:
return btcec.S256()
}

return nil
}

type publicKeyInfo struct {
Raw asn1.RawContent
Algorithm pkix.AlgorithmIdentifier
PublicKey asn1.BitString
}

func parseSecp256k1DER(keyBytes []byte) (*ecdsa.PublicKey, error) {
var (
pki publicKeyInfo
rest []byte
err error
pubKey *btcec.PublicKey
)

if rest, err = asn1.Unmarshal(keyBytes, &pki); err != nil {
return nil, err
} else if len(rest) != 0 {
return nil, fmt.Errorf("x509: trailing data after ASN.1 of public-key")
}

pubKey, err = btcec.ParsePubKey(pki.PublicKey.RightAlign(), btcec.S256())
if err != nil {
return nil, err
}

return pubKey.ToECDSA(), nil
}

// PublicKeyFromJWK builds a cryptoapi.PublicKey from jwkKey.
func PublicKeyFromJWK(jwkKey *jwk.JWK) (*cryptoapi.PublicKey, error) {
if jwkKey != nil {
Expand Down
112 changes: 112 additions & 0 deletions doc/jose/jwk/jwksupport/jwk_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,118 @@ func TestBBSJWK(t *testing.T) {
})
}

func TestPubKeyBytesToKey(t *testing.T) {
tt := []struct {
keyTypes []kms.KeyType
getKey func(keyType kms.KeyType) ([]byte, error)
expectType interface{}
}{
{
keyTypes: []kms.KeyType{kms.ED25519Type},
getKey: func(kms.KeyType) ([]byte, error) {
pubKey, _, err := ed25519.GenerateKey(rand.Reader)
return pubKey, err
},
expectType: ed25519.PublicKey{},
},
{
keyTypes: []kms.KeyType{kms.X25519ECDHKWType},
getKey: func(kms.KeyType) ([]byte, error) {
pubKeyBytes := make([]byte, 32)
_, err := rand.Read(pubKeyBytes)

return pubKeyBytes, err
},
expectType: []byte{},
},
{
keyTypes: []kms.KeyType{kms.BLS12381G2Type},
getKey: func(kms.KeyType) ([]byte, error) {
pubKey, _, err := bbs12381g2pub.GenerateKeyPair(sha256.New, nil)
if err != nil {
return nil, err
}

keyBytes, err := pubKey.Marshal()
return keyBytes, err
},
expectType: &bbs12381g2pub.PublicKey{},
},
{
keyTypes: []kms.KeyType{
kms.ECDSAP256TypeIEEEP1363,
kms.ECDSAP384TypeIEEEP1363,
kms.ECDSAP521TypeIEEEP1363,
},
getKey: func(keyType kms.KeyType) ([]byte, error) {
crv := getECDSACurve(keyType)
privKey, err := ecdsa.GenerateKey(crv, rand.Reader)
if err != nil {
return nil, err
}

keyBytes := elliptic.Marshal(crv, privKey.X, privKey.Y)
return keyBytes, nil
},
expectType: &ecdsa.PublicKey{},
},
{
keyTypes: []kms.KeyType{
kms.ECDSAP256TypeDER,
kms.ECDSAP384TypeDER,
kms.ECDSAP521TypeDER,
},
getKey: func(keyType kms.KeyType) ([]byte, error) {
crv := getECDSACurve(keyType)
privKey, err := ecdsa.GenerateKey(crv, rand.Reader)
if err != nil {
return nil, err
}

return x509.MarshalPKIXPublicKey(&privKey.PublicKey)
},
expectType: &ecdsa.PublicKey{},
},
{
keyTypes: []kms.KeyType{
kms.NISTP256ECDHKWType,
kms.NISTP384ECDHKWType,
kms.NISTP521ECDHKWType,
},
getKey: func(keyType kms.KeyType) ([]byte, error) {
crv := getECDSACurve(keyType)
privKey, err := ecdsa.GenerateKey(crv, rand.Reader)
require.NoError(t, err)

pubKey := &cryptoapi.PublicKey{
X: privKey.X.Bytes(),
Y: privKey.Y.Bytes(),
Curve: crv.Params().Name,
Type: "EC",
}

return json.Marshal(pubKey)
},
expectType: &ecdsa.PublicKey{},
},
}

for _, tc := range tt {
for _, keyType := range tc.keyTypes {

t.Run(string(keyType), func(t *testing.T) {
pkBytes, err := tc.getKey(keyType)
require.NoError(t, err)

pk, err := PubKeyBytesToKey(pkBytes, keyType)
require.NoError(t, err)

require.IsType(t, tc.expectType, pk)
})
}
}
}

func TestPubKeyBytesToJWK(t *testing.T) {
tests := []struct {
name string
Expand Down
Loading

0 comments on commit dd6e967

Please sign in to comment.