-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refactor: move jwk from kms-go, and refactor implementation to be les…
…s dependant on cryto libs. Signed-off-by: Volodymyr Kubiv <[email protected]>
- Loading branch information
Showing
25 changed files
with
1,290 additions
and
1,118 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,142 @@ | ||
package jwksupport | ||
|
||
import ( | ||
"crypto/ed25519" | ||
"crypto/elliptic" | ||
"errors" | ||
"fmt" | ||
"math/big" | ||
|
||
"github.com/btcsuite/btcutil/base58" | ||
"github.com/trustbloc/bbs-signature-go/bbs12381g2pub" | ||
"github.com/trustbloc/kms-go/util/cryptoutil" | ||
|
||
"github.com/trustbloc/did-go/doc/jose/jwk" | ||
) | ||
|
||
const ( | ||
ecKty = "EC" | ||
okpKty = "OKP" | ||
x25519Crv = "X25519" | ||
ed25519Crv = "Ed25519" | ||
bls12381G2Crv = "BLS12381_G2" | ||
bls12381G2Size = 96 | ||
) | ||
|
||
func FromEdPublicKey(pub ed25519.PublicKey) *jwk.JWK { | ||
return &jwk.JWK{ | ||
Kty: "OKP", | ||
Crv: ed25519Crv, | ||
X: jwk.NewBuffer(pub), | ||
} | ||
} | ||
|
||
func FromEdPrivateKey(ed ed25519.PrivateKey) *jwk.JWK { | ||
raw := FromEdPublicKey(ed25519.PublicKey(ed[32:])) | ||
|
||
raw.D = jwk.NewBuffer(ed[0:32]) | ||
return raw | ||
} | ||
|
||
func JWKFromX25519Key(pubKey []byte) (*jwk.JWK, error) { | ||
if len(pubKey) != cryptoutil.Curve25519KeySize { | ||
return nil, errors.New("JWKFromX25519Key: invalid key") | ||
} | ||
|
||
return &jwk.JWK{ | ||
Crv: x25519Crv, | ||
Kty: okpKty, | ||
X: jwk.NewFixedSizeBuffer(pubKey, cryptoutil.Curve25519KeySize), | ||
}, nil | ||
} | ||
|
||
func FromEcdsaPubKeyBytes(curve elliptic.Curve, pubKeyBytes []byte) (*jwk.JWK, error) { | ||
println(base58.Encode(pubKeyBytes)) | ||
x, y := elliptic.UnmarshalCompressed(curve, pubKeyBytes) | ||
if x == nil { | ||
return nil, fmt.Errorf("error unmarshalling key bytes") | ||
} | ||
|
||
return FromEcdsaContent(EcdsaContent{ | ||
Curve: curve, | ||
X: x, | ||
Y: y, | ||
}) | ||
} | ||
|
||
func FromBLS12381G2(key *bbs12381g2pub.PublicKey) (*jwk.JWK, error) { | ||
var raw *jwk.JWK | ||
|
||
mKey, err := key.Marshal() | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
raw = &jwk.JWK{ | ||
Kty: ecKty, | ||
Crv: bls12381G2Crv, | ||
X: jwk.NewFixedSizeBuffer(mKey, bls12381G2Size), | ||
} | ||
|
||
return raw, nil | ||
} | ||
|
||
type EcdsaContent struct { | ||
Curve elliptic.Curve | ||
|
||
X *big.Int | ||
Y *big.Int | ||
} | ||
|
||
func FromEcdsaContent(content EcdsaContent) (*jwk.JWK, error) { | ||
name, err := curveName(content.Curve) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
size := curveSize(content.Curve) | ||
|
||
xBytes := content.X.Bytes() | ||
yBytes := content.Y.Bytes() | ||
|
||
if len(xBytes) > size || len(yBytes) > size { | ||
return nil, fmt.Errorf("go-jose/go-jose: invalid EC key (X/Y too large)") | ||
} | ||
|
||
key := &jwk.JWK{ | ||
Kty: "EC", | ||
Crv: name, | ||
X: jwk.NewFixedSizeBuffer(xBytes, size), | ||
Y: jwk.NewFixedSizeBuffer(yBytes, size), | ||
} | ||
|
||
return key, nil | ||
} | ||
|
||
// Get JOSE name of curve | ||
func curveName(crv elliptic.Curve) (string, error) { | ||
switch crv { | ||
case elliptic.P256(): | ||
return "P-256", nil | ||
case elliptic.P384(): | ||
return "P-384", nil | ||
case elliptic.P521(): | ||
return "P-521", nil | ||
default: | ||
return "", fmt.Errorf("unsupported/unknown elliptic curve") | ||
} | ||
} | ||
|
||
// Get size of curve in bytes | ||
func curveSize(crv elliptic.Curve) int { | ||
bits := crv.Params().BitSize | ||
|
||
div := bits / 8 | ||
mod := bits % 8 | ||
|
||
if mod == 0 { | ||
return div | ||
} | ||
|
||
return div + 1 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
/* | ||
Copyright SecureKey Technologies Inc. All Rights Reserved. | ||
SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
package jwksupport | ||
|
||
import ( | ||
"crypto/elliptic" | ||
"fmt" | ||
|
||
"github.com/trustbloc/kms-go/util/cryptoutil" | ||
|
||
"github.com/trustbloc/did-go/doc/jose/jwk" | ||
|
||
"github.com/trustbloc/did-go/doc/fingerprint" | ||
) | ||
|
||
// CreateDIDKeyByJwk creates a did:key ID using the multicodec key fingerprint as per the did:key format spec found at: | ||
// https://w3c-ccg.github.io/did-method-key/#format. | ||
func CreateDIDKeyByJwk(jsonWebKey *jwk.JWK) (string, string, error) { | ||
if jsonWebKey == nil { | ||
return "", "", fmt.Errorf("jsonWebKey is required") | ||
} | ||
|
||
switch jsonWebKey.Kty { | ||
case "EC": | ||
code, curve, err := ecCodeAndCurve(jsonWebKey.Crv) | ||
if err != nil { | ||
return "", "", err | ||
} | ||
|
||
bytes := elliptic.MarshalCompressed(curve, jsonWebKey.X.BigInt(), jsonWebKey.Y.BigInt()) | ||
didKey, keyID := fingerprint.CreateDIDKeyByCode(code, bytes) | ||
|
||
return didKey, keyID, nil | ||
|
||
case "OKP": | ||
var code uint64 | ||
|
||
switch jsonWebKey.Crv { | ||
case "X25519": | ||
var keyData = jsonWebKey.X.Bytes() | ||
|
||
if len(keyData) != cryptoutil.Curve25519KeySize { | ||
return "", "", jwk.ErrInvalidKey | ||
} | ||
|
||
code = fingerprint.X25519PubKeyMultiCodec | ||
didKey, keyID := fingerprint.CreateDIDKeyByCode(code, keyData) | ||
|
||
return didKey, keyID, nil | ||
case "Ed25519": | ||
keyData, err := ToED25519PublicKeyBytes(jsonWebKey) | ||
if err != nil { | ||
return "", "", err | ||
} | ||
|
||
code = fingerprint.ED25519PubKeyMultiCodec | ||
didKey, keyID := fingerprint.CreateED25519DIDKey(keyData) | ||
|
||
return didKey, keyID, nil | ||
|
||
default: | ||
return "", "", fmt.Errorf( | ||
"unsupported kty %q and crv %q combination", jsonWebKey.Kty, jsonWebKey.Crv) | ||
} | ||
|
||
default: | ||
return "", "", fmt.Errorf("unsupported kty %q", jsonWebKey.Kty) | ||
} | ||
} | ||
|
||
func ecCodeAndCurve(ecCurve string) (uint64, elliptic.Curve, error) { | ||
var ( | ||
curve elliptic.Curve | ||
code uint64 | ||
) | ||
|
||
switch ecCurve { | ||
case elliptic.P256().Params().Name, "NIST_P256": | ||
curve = elliptic.P256() | ||
code = fingerprint.P256PubKeyMultiCodec | ||
case elliptic.P384().Params().Name, "NIST_P384": | ||
curve = elliptic.P384() | ||
code = fingerprint.P384PubKeyMultiCodec | ||
case elliptic.P521().Params().Name, "NIST_P521": | ||
curve = elliptic.P521() | ||
code = fingerprint.P521PubKeyMultiCodec | ||
default: | ||
return 0, nil, fmt.Errorf("unsupported crv %s", ecCurve) | ||
} | ||
|
||
return code, curve, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,134 @@ | ||
package jwksupport_test | ||
|
||
import ( | ||
"crypto/ed25519" | ||
"crypto/elliptic" | ||
"crypto/rand" | ||
"crypto/sha256" | ||
"encoding/base64" | ||
"testing" | ||
|
||
"github.com/btcsuite/btcutil/base58" | ||
"github.com/stretchr/testify/require" | ||
"github.com/trustbloc/bbs-signature-go/bbs12381g2pub" | ||
|
||
"github.com/trustbloc/did-go/crypto-ext/jwksupport" | ||
"github.com/trustbloc/did-go/doc/jose/jwk" | ||
) | ||
|
||
const ( | ||
ecP256PubKeyBase58 = "23youFZZdHMVdpv28DRSWP2zJbTJ8KHBeSKUX3qVqqnmp" | ||
ecP384PubKeyBase58 = "ad1jjx1hRrEkMWSsFsXpLULAbmUq67ii1jRsBNtYEKhCwLXTo4wcjY7C2K4cuGZ859" | ||
ecP521PubKeyBase58 = "4SsRN7NAk3175KrnPVQn5XTZE49MKdFKiq4XhWdhfx3QEUb2e96A3YLonFC6B21sa4uU776QMxEnxAQP6GWko8f3aNV" | ||
x25519KeyBase64 = "egRLO+ygwW/VNHjQhZiHw1vhHwVj4KmzeRnKIEDz6gE=" | ||
) | ||
|
||
func TestFromEdPublicKey(t *testing.T) { | ||
pubKey, _, err := ed25519.GenerateKey(rand.Reader) | ||
require.NoError(t, err) | ||
|
||
jwk := jwksupport.FromEdPublicKey(pubKey) | ||
|
||
_, _, err = jwksupport.CreateDIDKeyByJwk(jwk) | ||
require.NoError(t, err) | ||
} | ||
|
||
func TestFromEdPrivateKey(t *testing.T) { | ||
_, privKey, err := ed25519.GenerateKey(rand.Reader) | ||
require.NoError(t, err) | ||
|
||
jwk := jwksupport.FromEdPrivateKey(privKey) | ||
require.NotEmpty(t, jwk.X.Bytes()) | ||
} | ||
|
||
func TestJWKFromX25519Key(t *testing.T) { | ||
keyBytes, err := base64.StdEncoding.DecodeString(x25519KeyBase64) | ||
require.NoError(t, err) | ||
|
||
jwk, err := jwksupport.JWKFromX25519Key(keyBytes) | ||
require.NoError(t, err) | ||
|
||
_, _, err = jwksupport.CreateDIDKeyByJwk(jwk) | ||
require.NoError(t, err) | ||
|
||
t.Run("Failure", func(t *testing.T) { | ||
_, err = jwksupport.JWKFromX25519Key([]byte{}) | ||
require.ErrorContains(t, err, "invalid key") | ||
}) | ||
} | ||
|
||
func TestFromEcdsaPubKeyBytes(t *testing.T) { | ||
|
||
t.Run("P256", func(t *testing.T) { | ||
jwk, err := jwksupport.FromEcdsaPubKeyBytes(elliptic.P256(), base58.Decode(ecP256PubKeyBase58)) | ||
require.NoError(t, err) | ||
|
||
_, _, err = jwksupport.CreateDIDKeyByJwk(jwk) | ||
require.NoError(t, err) | ||
}) | ||
|
||
t.Run("P384", func(t *testing.T) { | ||
jwk, err := jwksupport.FromEcdsaPubKeyBytes(elliptic.P384(), base58.Decode(ecP384PubKeyBase58)) | ||
require.NoError(t, err) | ||
|
||
_, _, err = jwksupport.CreateDIDKeyByJwk(jwk) | ||
require.NoError(t, err) | ||
}) | ||
|
||
t.Run("P521", func(t *testing.T) { | ||
jwk, err := jwksupport.FromEcdsaPubKeyBytes(elliptic.P521(), base58.Decode(ecP521PubKeyBase58)) | ||
require.NoError(t, err) | ||
|
||
_, _, err = jwksupport.CreateDIDKeyByJwk(jwk) | ||
require.NoError(t, err) | ||
}) | ||
|
||
t.Run("Failure", func(t *testing.T) { | ||
_, err := jwksupport.FromEcdsaPubKeyBytes(elliptic.P256(), []byte{}) | ||
require.ErrorContains(t, err, "error unmarshalling key bytes") | ||
|
||
_, err = jwksupport.FromEcdsaContent(jwksupport.EcdsaContent{Curve: elliptic.P224()}) | ||
require.ErrorContains(t, err, "unsupported/unknown elliptic curve") | ||
}) | ||
} | ||
|
||
func TestFromBLS12381G2(t *testing.T) { | ||
bbsPubKey, _, err := bbs12381g2pub.GenerateKeyPair(sha256.New, nil) | ||
require.NoError(t, err) | ||
|
||
jwk, err := jwksupport.FromBLS12381G2(bbsPubKey) | ||
require.NoError(t, err) | ||
require.NotEmpty(t, jwk.X.Bytes()) | ||
} | ||
|
||
func TestCreateDIDKeyByJwk(t *testing.T) { | ||
t.Run("Failure", func(t *testing.T) { | ||
_, _, err := jwksupport.CreateDIDKeyByJwk(&jwk.JWK{ | ||
Kty: "OKP", | ||
}) | ||
require.ErrorContains(t, err, "unsupported kty \"OKP\" and crv \"\" combination") | ||
|
||
_, _, err = jwksupport.CreateDIDKeyByJwk(&jwk.JWK{}) | ||
|
||
require.ErrorContains(t, err, "unsupported kty \"\"") | ||
|
||
_, _, err = jwksupport.CreateDIDKeyByJwk(&jwk.JWK{ | ||
Kty: "OKP", | ||
Crv: "X25519", | ||
X: jwk.NewBuffer([]byte{}), | ||
}) | ||
require.ErrorContains(t, err, "invalid JWK") | ||
|
||
_, _, err = jwksupport.CreateDIDKeyByJwk(&jwk.JWK{ | ||
Kty: "OKP", | ||
Crv: "Ed25519", | ||
}) | ||
require.ErrorContains(t, err, "invalid Ed key") | ||
|
||
_, _, err = jwksupport.CreateDIDKeyByJwk(&jwk.JWK{ | ||
Kty: "EC", | ||
Crv: "Ed25519", | ||
}) | ||
require.ErrorContains(t, err, "unsupported crv Ed25519") | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
package jwksupport | ||
|
||
import ( | ||
"fmt" | ||
|
||
"github.com/trustbloc/did-go/doc/jose/jwk" | ||
) | ||
|
||
const ( | ||
ed25519PublicKeySize = 32 | ||
) | ||
|
||
func ToED25519PublicKeyBytes(key *jwk.JWK) ([]byte, error) { | ||
if key.X == nil { | ||
return nil, fmt.Errorf("invalid Ed key, missing x value") | ||
} | ||
publicKey := make([]byte, ed25519PublicKeySize) | ||
copy(publicKey[0:ed25519PublicKeySize], key.X.Bytes()) | ||
return publicKey, nil | ||
} |
Oops, something went wrong.