Skip to content

Commit

Permalink
Merge pull request hyperledger-archives#2945 from Baha-sk/okpjwk_didkey
Browse files Browse the repository at this point in the history
Add support for X25519/Ed25519 keys in fingerprint.CreateDIDKeyByJwk()
  • Loading branch information
fqutishat authored Aug 19, 2021
2 parents 6f926d5 + a100bab commit 992239f
Show file tree
Hide file tree
Showing 2 changed files with 115 additions and 32 deletions.
65 changes: 49 additions & 16 deletions pkg/vdr/fingerprint/fingerprint.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ package fingerprint

import (
"crypto/ecdsa"
"crypto/ed25519"
"crypto/elliptic"
"encoding/binary"
"errors"
Expand Down Expand Up @@ -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 {
Expand Down
82 changes: 66 additions & 16 deletions pkg/vdr/fingerprint/fingerprint_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ package fingerprint

import (
"crypto/ecdsa"
"crypto/ed25519"
"crypto/elliptic"
"encoding/base64"
"math/big"
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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:****]:"+
Expand Down

0 comments on commit 992239f

Please sign in to comment.