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

Doc updates & DID Key Test #36

Merged
merged 2 commits into from
Mar 2, 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
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

# Introduction

Named `did-sdk`, this SDK refers to "DID" as Decentralized IDentity, not specifically the
Named `did-sdk`, this SDK refers to "DID" as **D**ecentralized **ID**entity, not specifically the
[did-core specification](https://www.w3.org/TR/did-core/). The `did-sdk` intends to provide a set of standards-based
primitives for building decentralized identity applications in a modular manner: with limited dependencies between
components.
Expand Down
2 changes: 2 additions & 0 deletions cryptosuite/cryptosuite.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,8 @@ type ProofOptions struct {
Contexts []string
}

// GetContextsFromProvable searches from a Linked Data `@context` property in the document and returns the value
// associated with the context, if it exists.
func GetContextsFromProvable(p Provable) ([]string, error) {
provableBytes, err := json.Marshal(p)
if err != nil {
Expand Down
23 changes: 20 additions & 3 deletions cryptosuite/jsonwebkey2020.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,8 @@ func GenerateJSONWebKey2020(kty KTY, crv *CRV) (*JSONWebKey2020, error) {
return nil, fmt.Errorf("unsupported key type: %s", kty)
}

// GenerateRSAJSONWebKey2020 returns a JsonWebKey2020 value, containing both public and private keys
// for an RSA-2048 key.
func GenerateRSAJSONWebKey2020() (*JSONWebKey2020, error) {
_, privKey, err := crypto.GenerateRSA2048Key()
if err != nil {
Expand Down Expand Up @@ -170,6 +172,8 @@ func GenerateRSAJSONWebKey2020() (*JSONWebKey2020, error) {
}, nil
}

// GenerateEd25519JSONWebKey2020 returns a JsonWebKey2020 value, containing both public and
// private keys for an Ed25519 key.
func GenerateEd25519JSONWebKey2020() (*JSONWebKey2020, error) {
_, privKey, err := crypto.GenerateEd25519Key()
if err != nil {
Expand Down Expand Up @@ -199,6 +203,8 @@ func GenerateEd25519JSONWebKey2020() (*JSONWebKey2020, error) {
}, nil
}

// GenerateX25519JSONWebKey2020 returns a JsonWebKey2020 value, containing both public and
// private keys for an Ed25519 key transformed to a bi-rationally equivalent X25519 key.
func GenerateX25519JSONWebKey2020() (*JSONWebKey2020, error) {
_, privKey, err := crypto.GenerateX25519Key()
if err != nil {
Expand Down Expand Up @@ -228,10 +234,12 @@ func GenerateX25519JSONWebKey2020() (*JSONWebKey2020, error) {
}, nil
}

// GenerateSECP256k1JSONWebKey2020 returns a JsonWebKey2020 value, containing both public and
// private keys for a secp256k1 key transformed to an ecdsa key.
// 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
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 := crypto.GenerateSecp256k1Key()
if err != nil {
return nil, err
Expand Down Expand Up @@ -263,6 +271,8 @@ func GenerateSECP256k1JSONWebKey2020() (*JSONWebKey2020, error) {
}, nil
}

// GenerateP256JSONWebKey2020 returns a JsonWebKey2020 value, containing both public and
// private keys for a P-256 ECDSA key.
func GenerateP256JSONWebKey2020() (*JSONWebKey2020, error) {
_, privKey, err := crypto.GenerateP256Key()
if err != nil {
Expand Down Expand Up @@ -294,6 +304,8 @@ func GenerateP256JSONWebKey2020() (*JSONWebKey2020, error) {
}, nil
}

// GenerateP384JSONWebKey2020 returns a JsonWebKey2020 value, containing both public and
// private keys for a P-384 ECDSA key.
func GenerateP384JSONWebKey2020() (*JSONWebKey2020, error) {
_, privKey, err := crypto.GenerateP384Key()
if err != nil {
Expand Down Expand Up @@ -346,6 +358,7 @@ func (s JSONWebKeySigner) SigningAlgorithm() string {
return s.Algorithm()
}

// Sign returns a byte array signature value for a message `tbs`
func (s JSONWebKeySigner) Sign(tbs []byte) ([]byte, error) {
headers := jws.NewHeaders()
if err := headers.Set("b64", false); err != nil {
Expand Down Expand Up @@ -394,6 +407,8 @@ func (v JSONWebKeyVerifier) KeyType() string {
return string(v.Key.KeyType())
}

// Verify attempts to verify a `signature` against a given `message`, returning nil if the verification is successful
// and an error should it fail.
func (v JSONWebKeyVerifier) Verify(message, signature []byte) error {
_, err := jws.Verify(signature, v.SignatureAlgorithm, v.Key, jws.VerifyOption(jws.WithDetachedPayload(message)))
return err
Expand Down Expand Up @@ -422,6 +437,8 @@ func NewJSONWebKeyVerifier(key PublicKeyJWK) (*JSONWebKeyVerifier, error) {
}, nil
}

// AlgFromKeyAndCurve returns the supported JSON Web Algorithm for signing for a given key type and curve pair
// The curve parameter is optional (e.g. "") as in the case of RSA.
func AlgFromKeyAndCurve(kty jwa.KeyType, crv jwa.EllipticCurveAlgorithm) (jwa.SignatureAlgorithm, error) {
if kty == jwa.RSA {
return jwa.RS256, nil
Expand Down
9 changes: 9 additions & 0 deletions did/key.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,15 @@ const (
EcdsaSecp256k1VerificationKey2019 cryptosuite.LDKeyType = "EcdsaSecp256k1VerificationKey2019"
)

// GenerateDIDKey takes in a key type value that this library supports and constructs a conformant did:key identifier.
// The function returns the associated private key value cast to the generic golang crypto.PrivateKey interface.
// To use the private key, it is recommended to re-cast to the associated type. For example, called with the input
// for a secp256k1 key:
// privKey, didKey, err := GenerateDIDKey(Secp256k1)
// if err != nil { ... }
// // where secp is an import alias to the secp256k1 library we use "github.com/decred/dcrd/dcrec/secp256k1/v4"
// secpPrivKey, ok := privKey.(secp.PrivateKey)
// if !ok { ... }
func GenerateDIDKey(kt crypto.KeyType) (gocrypto.PrivateKey, *DIDKey, error) {
if !isSupportedKeyType(kt) {
return nil, nil, fmt.Errorf("unsupported did:key type: %s", kt)
Expand Down
129 changes: 128 additions & 1 deletion did/key_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,17 @@
package did

import (
gocrypto "crypto"
"crypto/ecdsa"
"crypto/ed25519"
"crypto/rand"
"crypto/rsa"
"crypto/sha256"
"strings"
"testing"

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

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

"github.com/multiformats/go-multicodec"
Expand Down Expand Up @@ -93,9 +101,128 @@ func TestGenerateDIDKey(t *testing.T) {
}
}

func TestDIDKeySignVerify(t *testing.T) {
t.Run("Test Ed25519 did:key", func(t *testing.T) {
privKey, didKey, err := GenerateDIDKey(crypto.Ed25519)
assert.NoError(t, err)
assert.NotNil(t, didKey)
assert.NotEmpty(t, privKey)

ed25519PrivKey, ok := privKey.(ed25519.PrivateKey)
assert.True(t, ok)
ed25519PubKey, ok := ed25519PrivKey.Public().(ed25519.PublicKey)
assert.True(t, ok)

msg := []byte("hello world")
signature := ed25519.Sign(ed25519PrivKey, msg)
verified := ed25519.Verify(ed25519PubKey, msg, signature)
assert.True(t, verified)
})

t.Run("Test secp256k1 did:key", func(t *testing.T) {
privKey, didKey, err := GenerateDIDKey(crypto.Secp256k1)
assert.NoError(t, err)
assert.NotNil(t, didKey)
assert.NotEmpty(t, privKey)

secp256k1PrivKey, ok := privKey.(secp.PrivateKey)
assert.True(t, ok)

ecdsaPrivKey := secp256k1PrivKey.ToECDSA()
ecdsaPubKey := ecdsaPrivKey.PublicKey

msg := []byte("hello world")
digest := sha256.Sum256(msg)
r, s, err := ecdsa.Sign(rand.Reader, ecdsaPrivKey, digest[:])
assert.NoError(t, err)

verified := ecdsa.Verify(&ecdsaPubKey, digest[:], r, s)
assert.True(t, verified)
})

t.Run("Test P-256 did:key", func(t *testing.T) {
privKey, didKey, err := GenerateDIDKey(crypto.P256)
assert.NoError(t, err)
assert.NotNil(t, didKey)
assert.NotEmpty(t, privKey)

ecdsaPrivKey, ok := privKey.(ecdsa.PrivateKey)
assert.True(t, ok)

ecdsaPubKey := ecdsaPrivKey.PublicKey

msg := []byte("hello world")
digest := sha256.Sum256(msg)
r, s, err := ecdsa.Sign(rand.Reader, &ecdsaPrivKey, digest[:])
assert.NoError(t, err)

verified := ecdsa.Verify(&ecdsaPubKey, digest[:], r, s)
assert.True(t, verified)
})

t.Run("Test P-384 did:key", func(t *testing.T) {
privKey, didKey, err := GenerateDIDKey(crypto.P384)
assert.NoError(t, err)
assert.NotNil(t, didKey)
assert.NotEmpty(t, privKey)

ecdsaPrivKey, ok := privKey.(ecdsa.PrivateKey)
assert.True(t, ok)

ecdsaPubKey := ecdsaPrivKey.PublicKey

msg := []byte("hello world")
digest := sha256.Sum256(msg)
r, s, err := ecdsa.Sign(rand.Reader, &ecdsaPrivKey, digest[:])
assert.NoError(t, err)

verified := ecdsa.Verify(&ecdsaPubKey, digest[:], r, s)
assert.True(t, verified)
})

t.Run("Test P-521 did:key", func(t *testing.T) {
privKey, didKey, err := GenerateDIDKey(crypto.P521)
assert.NoError(t, err)
assert.NotNil(t, didKey)
assert.NotEmpty(t, privKey)

ecdsaPrivKey, ok := privKey.(ecdsa.PrivateKey)
assert.True(t, ok)

ecdsaPubKey := ecdsaPrivKey.PublicKey

msg := []byte("hello world")
digest := sha256.Sum256(msg)
r, s, err := ecdsa.Sign(rand.Reader, &ecdsaPrivKey, digest[:])
assert.NoError(t, err)

verified := ecdsa.Verify(&ecdsaPubKey, digest[:], r, s)
assert.True(t, verified)
})

t.Run("Test RSA 4096 did:key", func(t *testing.T) {
privKey, didKey, err := GenerateDIDKey(crypto.RSA)
assert.NoError(t, err)
assert.NotNil(t, didKey)
assert.NotEmpty(t, privKey)

rsaPrivKey, ok := privKey.(rsa.PrivateKey)
assert.True(t, ok)
rsaPubKey := rsaPrivKey.PublicKey

msg := []byte("hello world")
digest := sha256.Sum256(msg)
signature, err := rsa.SignPKCS1v15(rand.Reader, &rsaPrivKey, gocrypto.SHA256, digest[:])
assert.NoError(t, err)
assert.NotEmpty(t, signature)

err = rsa.VerifyPKCS1v15(&rsaPubKey, gocrypto.SHA256, digest[:], signature)
assert.NoError(t, err)
})
}

// From https://w3c-ccg.github.io/did-method-key/#test-vectors
func TestKnownTestVectors(t *testing.T) {

t.Run("Ed25519 / X25519", func(tt *testing.T) {
did1 := "did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp"
didKey1 := DIDKey(did1)
Expand Down
Binary file modified docs/did-sdk.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.