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

DID Key Implementation #28

Merged
merged 6 commits into from
Mar 1, 2022
Merged
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
138 changes: 138 additions & 0 deletions crypto/keys.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
package crypto

import (
"crypto"
"crypto/ecdsa"
"crypto/ed25519"
"crypto/elliptic"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"fmt"

"github.com/pkg/errors"

secp "github.com/decred/dcrd/dcrec/secp256k1/v4"

"github.com/lestrrat-go/jwx/x25519"
)

type KeyType string

const (
Ed25519 KeyType = "Ed25519"
X25519 KeyType = "X25519"
Secp256k1 KeyType = "secp256k1"
P224 KeyType = "P-224"
P256 KeyType = "P-256"
P384 KeyType = "P-384"
P521 KeyType = "P-521"
RSA KeyType = "RSA"

RSAKeySize int = 2048
)

// GenerateKeyByKeyType creates a brand-new key, returning the public and private key for the given key type
func GenerateKeyByKeyType(kt KeyType) (crypto.PublicKey, crypto.PrivateKey, error) {
switch kt {
case Ed25519:
return GenerateEd25519Key()
case X25519:
return GenerateX25519Key()
case Secp256k1:
return GenerateSecp256k1Key()
case P224:
return GenerateP224Key()
case P256:
return GenerateP256Key()
case P384:
return GenerateP384Key()
case P521:
return GenerateP521Key()
case RSA:
return GenerateRSA2048Key()
}
return nil, nil, fmt.Errorf("unsupported key type: %s", kt)
}

func PubKeyToBytes(key crypto.PublicKey) ([]byte, error) {
ed25519Key, ok := key.(ed25519.PublicKey)
if ok {
return ed25519Key, nil
}

x25519Key, ok := key.(x25519.PublicKey)
if ok {
return x25519Key, nil
}

secp256k1Key, ok := key.(secp.PublicKey)
if ok {
return secp256k1Key.SerializeCompressed(), nil
}

ecdsaKey, ok := key.(ecdsa.PublicKey)
if ok {
return elliptic.Marshal(ecdsaKey.Curve, ecdsaKey.X, ecdsaKey.Y), nil
}

rsaKey, ok := key.(rsa.PublicKey)
if ok {
return x509.MarshalPKCS1PublicKey(&rsaKey), nil
}

return nil, errors.New("unknown public key type; could not convert to bytes")
}

func GenerateEd25519Key() (ed25519.PublicKey, ed25519.PrivateKey, error) {
return ed25519.GenerateKey(rand.Reader)
}

func GenerateX25519Key() (x25519.PublicKey, x25519.PrivateKey, error) {
return x25519.GenerateKey(rand.Reader)
}

func GenerateSecp256k1Key() (secp.PublicKey, secp.PrivateKey, error) {
privKey, err := secp.GeneratePrivateKey()
if err != nil {
return secp.PublicKey{}, secp.PrivateKey{}, err
}
pubKey := privKey.PubKey()
return *pubKey, *privKey, nil
}

func GenerateP224Key() (ecdsa.PublicKey, ecdsa.PrivateKey, error) {
return generateECDSAKey(elliptic.P224())
}

func GenerateP256Key() (ecdsa.PublicKey, ecdsa.PrivateKey, error) {
return generateECDSAKey(elliptic.P256())
}

func GenerateP384Key() (ecdsa.PublicKey, ecdsa.PrivateKey, error) {
return generateECDSAKey(elliptic.P384())
}

func GenerateP521Key() (ecdsa.PublicKey, ecdsa.PrivateKey, error) {
return generateECDSAKey(elliptic.P521())
}

func generateECDSAKey(curve elliptic.Curve) (ecdsa.PublicKey, ecdsa.PrivateKey, error) {
privKey, err := ecdsa.GenerateKey(curve, rand.Reader)
if err != nil {
return ecdsa.PublicKey{}, ecdsa.PrivateKey{}, err
}
return privKey.PublicKey, *privKey, nil
}

func GenerateRSA2048Key() (rsa.PublicKey, rsa.PrivateKey, error) {
privKey, err := rsa.GenerateKey(rand.Reader, RSAKeySize)
if err != nil {
return rsa.PublicKey{}, rsa.PrivateKey{}, err
}
return privKey.PublicKey, *privKey, nil
}

func GetSupportedKeyTypes() []KeyType {
return []KeyType{Ed25519, X25519, Secp256k1, P224, P256, P384, P521, RSA}
}
2 changes: 1 addition & 1 deletion cryptosuite/cryptosuite.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ type CryptoSuite interface {

type CryptoSuiteInfo interface {
ID() string
Type() string
Type() LDKeyType
CanonicalizationAlgorithm() string
MessageDigestAlgorithm() crypto.Hash
SignatureAlgorithm() SignatureType
Expand Down
71 changes: 39 additions & 32 deletions cryptosuite/jsonwebkey2020.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,36 +3,29 @@
package cryptosuite

import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/rsa"
"encoding/base64"
"encoding/json"
"fmt"

"github.com/lestrrat-go/jwx/x25519"
"github.com/TBD54566975/did-sdk/crypto"

"github.com/lestrrat-go/jwx/jwa"
"github.com/lestrrat-go/jwx/jws"

"github.com/lestrrat-go/jwx/jwk"

"github.com/TBD54566975/did-sdk/util"

secp "github.com/decred/dcrd/dcrec/secp256k1/v4"

"github.com/pkg/errors"
)

type (
KTY string
CRV string
ALG string
KTY string
CRV string
ALG string
LDKeyType string
)

const (
JsonWebKey2020 string = "JsonWebKey2020"
JsonWebKey2020 LDKeyType = "JsonWebKey2020"

// Supported key types

Expand All @@ -44,20 +37,16 @@ const (

Ed25519 CRV = "Ed25519"
X25519 CRV = "X25519"
SECP256k1 CRV = "secp256k1"
Secp256k1 CRV = "secp256k1"
P256 CRV = "P-256"
P384 CRV = "P-384"

// Known key sizes

RSAKeySize int = 2048
)

// JSONWebKey2020 complies with https://w3c-ccg.github.io/lds-jws2020/#json-web-key-2020
type JSONWebKey2020 struct {
ID string `json:"id,omitempty"`
Type string `json:"type,omitempty"`
Controller string `json:"controller,omitempty"`
ID string `json:"id,omitempty"`
Type LDKeyType `json:"type,omitempty"`
Controller string `json:"controller,omitempty"`
PrivateKeyJWK `json:"privateKeyJwk,omitempty"`
PublicKeyJWK `json:"publicKeyJwk,omitempty"`
}
Expand Down Expand Up @@ -96,11 +85,26 @@ type PublicKeyJWK struct {
KID string `json:"kid,omitempty"`
}

func ToPublicKeyJWK(key jwk.Key) (*PublicKeyJWK, error) {
keyBytes, err := json.Marshal(key)
if err != nil {
return nil, err
}
var pubKeyJWK PublicKeyJWK
if err := json.Unmarshal(keyBytes, &pubKeyJWK); err != nil {
return nil, err
}
return &pubKeyJWK, nil
}

// GenerateJSONWebKey2020 The JSONWebKey2020 type specifies a number of key type and curve pairs to enable JOSE conformance
// these pairs are supported in this library and generated via the function below
// https://w3c-ccg.github.io/lds-jws2020/#dfn-jsonwebkey2020
func GenerateJSONWebKey2020(kty KTY, crv *CRV) (*JSONWebKey2020, error) {
if kty == RSA {
if crv != nil {
return nil, fmt.Errorf("RSA key type cannot have curve specified: %s", *crv)
}
return GenerateRSAJSONWebKey2020()
}
if crv == nil {
Expand All @@ -120,7 +124,7 @@ func GenerateJSONWebKey2020(kty KTY, crv *CRV) (*JSONWebKey2020, error) {
}
if kty == EC {
switch curve {
case SECP256k1:
case Secp256k1:
return GenerateSECP256k1JSONWebKey2020()
case P256:
return GenerateP256JSONWebKey2020()
Expand All @@ -134,12 +138,12 @@ func GenerateJSONWebKey2020(kty KTY, crv *CRV) (*JSONWebKey2020, error) {
}

func GenerateRSAJSONWebKey2020() (*JSONWebKey2020, error) {
privateKey, err := rsa.GenerateKey(rand.Reader, RSAKeySize)
_, privKey, err := crypto.GenerateRSA2048Key()
if err != nil {
return nil, err
}
rsaJWK := jwk.NewRSAPrivateKey()
if err := rsaJWK.FromRaw(privateKey); err != nil {
if err := rsaJWK.FromRaw(&privKey); err != nil {
return nil, errors.Wrap(err, "failed to generate rsa jwk")
}
kty := rsaJWK.KeyType().String()
Expand Down Expand Up @@ -167,7 +171,7 @@ func GenerateRSAJSONWebKey2020() (*JSONWebKey2020, error) {
}

func GenerateEd25519JSONWebKey2020() (*JSONWebKey2020, error) {
_, privKey, err := util.GenerateEd25519Key()
_, privKey, err := crypto.GenerateEd25519Key()
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -196,7 +200,7 @@ func GenerateEd25519JSONWebKey2020() (*JSONWebKey2020, error) {
}

func GenerateX25519JSONWebKey2020() (*JSONWebKey2020, error) {
_, privKey, err := x25519.GenerateKey(rand.Reader)
_, privKey, err := crypto.GenerateX25519Key()
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -228,7 +232,7 @@ func GenerateSECP256k1JSONWebKey2020() (*JSONWebKey2020, error) {
// We use the secp256k1 implementation from Decred https://github.com/decred/dcrd
// which is utilized in the widely accepted go bitcoin node implementation from the btcsuite project
// https://github.com/btcsuite/btcd/blob/master/btcec/btcec.go#L23
privKey, err := secp.GeneratePrivateKey()
_, privKey, err := crypto.GenerateSecp256k1Key()
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -260,12 +264,12 @@ func GenerateSECP256k1JSONWebKey2020() (*JSONWebKey2020, error) {
}

func GenerateP256JSONWebKey2020() (*JSONWebKey2020, error) {
privKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
_, privKey, err := crypto.GenerateP256Key()
if err != nil {
return nil, err
}
p256JWK := jwk.NewECDSAPrivateKey()
if err := p256JWK.FromRaw(privKey); err != nil {
if err := p256JWK.FromRaw(&privKey); err != nil {
return nil, errors.Wrap(err, "failed to generate p-256 jwk")
}
kty := p256JWK.KeyType().String()
Expand All @@ -291,12 +295,12 @@ func GenerateP256JSONWebKey2020() (*JSONWebKey2020, error) {
}

func GenerateP384JSONWebKey2020() (*JSONWebKey2020, error) {
privKey, err := ecdsa.GenerateKey(elliptic.P384(), rand.Reader)
_, privKey, err := crypto.GenerateP384Key()
if err != nil {
return nil, err
}
p384JWK := jwk.NewECDSAPrivateKey()
if err := p384JWK.FromRaw(privKey); err != nil {
if err := p384JWK.FromRaw(&privKey); err != nil {
return nil, errors.Wrap(err, "failed to generate p-384 jwk")
}
kty := p384JWK.KeyType().String()
Expand Down Expand Up @@ -439,7 +443,7 @@ func AlgFromKeyAndCurve(kty jwa.KeyType, crv jwa.EllipticCurveAlgorithm) (jwa.Si

if kty == jwa.EC {
switch curve {
case jwa.EllipticCurveAlgorithm(SECP256k1):
case jwa.EllipticCurveAlgorithm(Secp256k1):
return jwa.ES256K, nil
case jwa.P256:
return jwa.ES256, nil
Expand All @@ -453,6 +457,9 @@ func AlgFromKeyAndCurve(kty jwa.KeyType, crv jwa.EllipticCurveAlgorithm) (jwa.Si
}

func crvPtr(crv CRV) *CRV {
if crv == "" {
return nil
}
return &crv
}

Expand Down
2 changes: 1 addition & 1 deletion cryptosuite/jsonwebkey2020_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ func TestJSONWebKey2020SignerVerifier(t *testing.T) {
{
name: "secpk256k1",
kty: EC,
crv: crvPtr(SECP256k1),
crv: crvPtr(Secp256k1),
},
{
name: "P-256",
Expand Down
6 changes: 4 additions & 2 deletions cryptosuite/jwssignaturesuite.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
//go:build jwx_es256k

package cryptosuite

import (
Expand All @@ -18,7 +20,7 @@ const (
JSONWebSignature2020Context string = "https://w3id.org/security/suites/jws-2020/v1"
JSONWebSignature2020 SignatureType = "JsonWebSignature2020"
JWSSignatureSuiteID string = "https://w3c-ccg.github.io/security-vocab/#JsonWebSignature2020"
JWSSignatureSuiteType = JsonWebKey2020
JWSSignatureSuiteType LDKeyType = JsonWebKey2020
JWSSignatureSuiteCanonicalizationAlgorithm string = "https://w3id.org/security#URDNA2015"
// JWSSignatureSuiteDigestAlgorithm uses https://www.rfc-editor.org/rfc/rfc4634
JWSSignatureSuiteDigestAlgorithm crypto.Hash = crypto.SHA256
Expand All @@ -34,7 +36,7 @@ func (j JWSSignatureSuite) ID() string {
return JWSSignatureSuiteID
}

func (j JWSSignatureSuite) Type() string {
func (j JWSSignatureSuite) Type() LDKeyType {
return JWSSignatureSuiteType
}

Expand Down
Loading