Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: move jwk from kms-go, and refactor implementation. #25

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ linters-settings:
gofmt:
simplify: true
gocyclo:
min-complexity: 10
min-complexity: 12
maligned:
suggest-new: true
dupl:
Expand Down Expand Up @@ -61,7 +61,7 @@ linters-settings:
- unnamedResult
- whyNoLint # TODO enable.
funlen:
lines: 60
lines: 80
statements: 40
wsl:
strict-append: true
Expand Down
153 changes: 153 additions & 0 deletions crypto-ext/jwksupport/create.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
/*
Copyright Gen Digital Inc. All Rights Reserved.

SPDX-License-Identifier: Apache-2.0
*/

package jwksupport

import (
"crypto/ed25519"
"crypto/elliptic"
"errors"
"fmt"
"math/big"

"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
)

// FromEdPublicKey creates jwk from ed25519 key.
func FromEdPublicKey(pub ed25519.PublicKey) *jwk.JWK {
return &jwk.JWK{
Kty: "OKP",
Crv: ed25519Crv,
X: jwk.NewBuffer(pub),
}
}

// FromEdPrivateKey creates jwk from ed25519 key.
func FromEdPrivateKey(ed ed25519.PrivateKey) *jwk.JWK {
raw := FromEdPublicKey(ed25519.PublicKey(ed[32:]))
raw.D = jwk.NewBuffer(ed[0:32])

return raw
}

// JWKFromX25519Key creates jwk from x25519 key.
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
}

// FromEcdsaPubKeyBytes creates jwk from ecdsa public key.
func FromEcdsaPubKeyBytes(curve elliptic.Curve, pubKeyBytes []byte) (*jwk.JWK, error) {
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,
})
}

// FromBLS12381G2 creates jwk from bbs12381g2 public key.
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
}

// EcdsaContent represent content of ecdsa key.
type EcdsaContent struct {
Curve elliptic.Curve

X *big.Int
Y *big.Int
}

// FromEcdsaContent creates jwk from ecdsa key.
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
}
95 changes: 95 additions & 0 deletions crypto-ext/jwksupport/fingerprint.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
/*
Copyright Gen Digital 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
}

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
}
Loading
Loading