diff --git a/pkg/vdr/fingerprint/fingerprint.go b/pkg/vdr/fingerprint/fingerprint.go index cce70eb1b..049ab7343 100644 --- a/pkg/vdr/fingerprint/fingerprint.go +++ b/pkg/vdr/fingerprint/fingerprint.go @@ -8,6 +8,7 @@ package fingerprint import ( "crypto/ecdsa" + "crypto/ed25519" "crypto/elliptic" "encoding/binary" "errors" @@ -68,38 +69,70 @@ func CreateDIDKeyByJwk(jsonWebKey *jwk.JWK) (string, string, error) { switch jsonWebKey.Kty { case "EC": - var curve elliptic.Curve + code, curve, err := ecCodeAndCurve(jsonWebKey.Crv) + if err != nil { + return "", "", err + } + + switch key := jsonWebKey.Key.(type) { + case *ecdsa.PublicKey: + bytes := elliptic.MarshalCompressed(curve, key.X, key.Y) + didKey, keyID := CreateDIDKeyByCode(code, bytes) + return didKey, keyID, nil + default: + return "", "", fmt.Errorf("unexpected EC key type %T", key) + } + case "OKP": var code uint64 switch jsonWebKey.Crv { - case elliptic.P256().Params().Name: - curve = elliptic.P256() - code = P256PubKeyMultiCodec - case elliptic.P384().Params().Name: - curve = elliptic.P384() - code = P384PubKeyMultiCodec - case elliptic.P521().Params().Name: - curve = elliptic.P521() - code = P521PubKeyMultiCodec - default: - return "", "", fmt.Errorf("unsupported crv %s", jsonWebKey.Crv) + case "X25519": + code = X25519PubKeyMultiCodec + case "Ed25519": + code = ED25519PubKeyMultiCodec } switch key := jsonWebKey.Key.(type) { - case *ecdsa.PublicKey: - bytes := elliptic.MarshalCompressed(curve, key.X, key.Y) - didKey, keyID := CreateDIDKeyByCode(code, bytes) + case ed25519.PublicKey: + didKey, keyID := CreateDIDKey(key) + + return didKey, keyID, nil + case []byte: + didKey, keyID := CreateDIDKeyByCode(code, key) return didKey, keyID, nil default: - return "", "", fmt.Errorf("unexpected key type") + return "", "", fmt.Errorf("unexpected OKP key type %T", key) } default: return "", "", fmt.Errorf("unsupported kty %s", jsonWebKey.Kty) } } +func ecCodeAndCurve(ecCurve string) (uint64, elliptic.Curve, error) { + var ( + curve elliptic.Curve + code uint64 + ) + + switch ecCurve { + case elliptic.P256().Params().Name: + curve = elliptic.P256() + code = P256PubKeyMultiCodec + case elliptic.P384().Params().Name: + curve = elliptic.P384() + code = P384PubKeyMultiCodec + case elliptic.P521().Params().Name: + curve = elliptic.P521() + code = P521PubKeyMultiCodec + default: + return 0, nil, fmt.Errorf("unsupported crv %s", ecCurve) + } + + return code, curve, nil +} + // KeyFingerprint generates a multicode fingerprint for pubKeyValue (raw key []byte). // It is mainly used as the controller ID (methodSpecification ID) of a did key. func KeyFingerprint(code uint64, pubKeyValue []byte) string { diff --git a/pkg/vdr/fingerprint/fingerprint_test.go b/pkg/vdr/fingerprint/fingerprint_test.go index 9193f8024..f8284057a 100644 --- a/pkg/vdr/fingerprint/fingerprint_test.go +++ b/pkg/vdr/fingerprint/fingerprint_test.go @@ -8,6 +8,7 @@ package fingerprint import ( "crypto/ecdsa" + "crypto/ed25519" "crypto/elliptic" "encoding/base64" "math/big" @@ -174,11 +175,26 @@ func TestCreateDIDKeyByJwk(t *testing.T) { name string kty string curve elliptic.Curve + valB58 string x string y string DIDKey string DIDKeyID string }{ + { + name: "test Ed25519", + kty: "OKP", + valB58: "B12NYF8RrR3h41TDCTJojY59usg3mbtbjnFs7Eud1Y6u", + DIDKey: "did:key:z6MkpTHR8VNsBxYAAWHut2Geadd9jSwuBV8xRoAnwWsdvktH", + DIDKeyID: "did:key:z6MkpTHR8VNsBxYAAWHut2Geadd9jSwuBV8xRoAnwWsdvktH#z6MkpTHR8VNsBxYAAWHut2Geadd9jSwuBV8xRoAnwWsdvktH", //nolint:lll + }, + { + name: "test X25519", + kty: "OKP", + valB58: "4Dy8E9UaZscuPUf2GLxV44RCNL7oxmEXXkgWXaug1WKV", + DIDKey: "did:key:z6LSeu9HkTHSfLLeUs2nnzUSNedgDUevfNQgQjQC23ZCit6F", + DIDKeyID: "did:key:z6LSeu9HkTHSfLLeUs2nnzUSNedgDUevfNQgQjQC23ZCit6F#z6LSeu9HkTHSfLLeUs2nnzUSNedgDUevfNQgQjQC23ZCit6F", //nolint:lll + }, { name: "test P-256", kty: "EC", @@ -219,25 +235,42 @@ func TestCreateDIDKeyByJwk(t *testing.T) { for _, test := range tests { tc := test + + var ( + jwkKey *jwk.JWK + err error + ) + t.Run(tc.name+" CreateDIDKeyByJwk", func(t *testing.T) { - x := readBigInt(t, tc.x) - y := readBigInt(t, tc.y) - publicKey := ecdsa.PublicKey{ - Curve: tc.curve, - X: x, - Y: y, - } + switch tc.name { + case "test Ed25519": + edKey := ed25519.PublicKey(base58.Decode(tc.valB58)) + jwkKey, err = jwksupport.JWKFromKey(edKey) + require.NoError(t, err) + case "test X25519": + jwkKey, err = jwksupport.JWKFromX25519Key(base58.Decode(tc.valB58)) + require.NoError(t, err) + default: + x := readBigInt(t, tc.x) + y := readBigInt(t, tc.y) + publicKey := ecdsa.PublicKey{ + Curve: tc.curve, + X: x, + Y: y, + } - jwkKey, err := jwksupport.JWKFromKey(&publicKey) - if tc.name == "test EC with invalid curve" { - require.EqualError(t, err, "create JWK: square/go-jose: unsupported/unknown elliptic curve") - jwkKey = &jwk.JWK{ - JSONWebKey: jose.JSONWebKey{}, - Kty: "EC", - Crv: "invalid", + jwkKey, err = jwksupport.JWKFromKey(&publicKey) + + if tc.name == "test EC with invalid curve" { + require.EqualError(t, err, "create JWK: square/go-jose: unsupported/unknown elliptic curve") + jwkKey = &jwk.JWK{ + JSONWebKey: jose.JSONWebKey{}, + Kty: "EC", + Crv: "invalid", + } + } else { + require.NoError(t, err) } - } else { - require.NoError(t, err) } didKey, keyID, err := CreateDIDKeyByJwk(jwkKey) @@ -287,6 +320,23 @@ func TestDIDKeyEd25519(t *testing.T) { require.NoError(t, err) } +func TestDIDKeyX25519(t *testing.T) { + const ( + x25519DIDKey = "did:key:z6LSeu9HkTHSfLLeUs2nnzUSNedgDUevfNQgQjQC23ZCit6F" + x25519Base58 = "4Dy8E9UaZscuPUf2GLxV44RCNL7oxmEXXkgWXaug1WKV" + keyIDX25519 = "did:key:z6LSeu9HkTHSfLLeUs2nnzUSNedgDUevfNQgQjQC23ZCit6F#z6LSeu9HkTHSfLLeUs2nnzUSNedgDUevfNQgQjQC23ZCit6F" //nolint:lll + ) + + didKey, keyID := CreateDIDKeyByCode(X25519PubKeyMultiCodec, base58.Decode(x25519Base58)) + + require.Equal(t, x25519DIDKey, didKey) + require.Equal(t, keyID, keyIDX25519) + + pubKey, err := PubKeyFromDIDKey(x25519DIDKey) + require.NoError(t, err) + require.Equal(t, x25519Base58, base58.Encode(pubKey)) +} + func TestPubKeyFromDIDKeyFailure(t *testing.T) { _, err := PubKeyFromDIDKey("did:key:****") require.EqualError(t, err, "pubKeyFromDIDKey: MethodIDFromDIDKey: failed to parse did:key [did:key:****]:"+