Skip to content
This repository has been archived by the owner on Dec 12, 2024. It is now read-only.

Support key access in all services #110

Merged
merged 14 commits into from
Sep 28, 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
9 changes: 8 additions & 1 deletion config/compose.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ log_level = "info"
storage = "bolt"

# per-service configuration
[services.keystore]
name = "keystore"
password = "default-password"

[services.did]
name = "did"
methods = ["key"]
Expand All @@ -33,6 +37,9 @@ name = "schema"
[services.credential]
name = "credential"

[services.manifest]
name = "manifest"

[services.dwn]
name = "dwn"
dwn_endpoint = "http://localhost:4321"
dwn_endpoint = "http://localhost:4321"
49 changes: 40 additions & 9 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,10 @@ type ServicesConfig struct {

// Embed all service-specific configs here. The order matters: from which should be instantiated first, to last

KeyStoreConfig KeyStoreServiceConfig `toml:"keystore,omitempty"`
DIDConfig DIDServiceConfig `toml:"did,omitempty"`
SchemaConfig SchemaServiceConfig `toml:"schema,omitempty"`
CredentialConfig CredentialServiceConfig `toml:"credential,omitempty"`
KeyStoreConfig KeyStoreServiceConfig `toml:"keystore,omitempty"`
ManifestConfig ManifestServiceConfig `toml:"manifest,omitempty"`
DWNConfig DWNServiceConfig `toml:"dwn,omitempty"`
}
Expand All @@ -62,6 +62,20 @@ type BaseServiceConfig struct {
Name string `toml:"name"`
}

type KeyStoreServiceConfig struct {
*BaseServiceConfig
// Service key password. Used by a KDF whose key is used by a symmetric cypher for key encryption.
// The password is salted before usage.
ServiceKeyPassword string `toml:"password"`
}

func (k *KeyStoreServiceConfig) IsEmpty() bool {
if k == nil {
return true
}
return reflect.DeepEqual(k, &KeyStoreServiceConfig{})
}

type DIDServiceConfig struct {
*BaseServiceConfig
Methods []string `toml:"methods"`
Expand Down Expand Up @@ -90,20 +104,34 @@ type CredentialServiceConfig struct {
// TODO(gabe) supported key and signature types
}

func (c *CredentialServiceConfig) IsEmpty() bool {
if c == nil {
return true
}
return reflect.DeepEqual(c, &CredentialServiceConfig{})
}

type ManifestServiceConfig struct {
*BaseServiceConfig
}

func (m *ManifestServiceConfig) IsEmpty() bool {
if m == nil {
return true
}
return reflect.DeepEqual(m, &ManifestServiceConfig{})
}

type DWNServiceConfig struct {
*BaseServiceConfig
DWNEndpoint string `toml:"dwn_endpoint"`
}

type KeyStoreServiceConfig struct {
*BaseServiceConfig
// Service key password. Used by a KDF whose key is used by a symmetric cypher for key encryption.
// The password is salted before usage.
ServiceKeyPassword string
func (d *DWNServiceConfig) IsEmpty() bool {
if d == nil {
return true
}
return reflect.DeepEqual(d, &DWNServiceConfig{})
}

// LoadConfig attempts to load a TOML config file from the given path, and coerce it into our object model.
Expand Down Expand Up @@ -149,6 +177,10 @@ func LoadConfig(path string) (*SSIServiceConfig, error) {
if defaultConfig {
config.Services = ServicesConfig{
StorageProvider: "bolt",
KeyStoreConfig: KeyStoreServiceConfig{
BaseServiceConfig: &BaseServiceConfig{Name: "keystore"},
ServiceKeyPassword: "default-password",
},
DIDConfig: DIDServiceConfig{
BaseServiceConfig: &BaseServiceConfig{Name: "did"},
Methods: []string{"key"},
Expand All @@ -159,9 +191,8 @@ func LoadConfig(path string) (*SSIServiceConfig, error) {
CredentialConfig: CredentialServiceConfig{
BaseServiceConfig: &BaseServiceConfig{Name: "credential"},
},
KeyStoreConfig: KeyStoreServiceConfig{
BaseServiceConfig: &BaseServiceConfig{Name: "keystore"},
ServiceKeyPassword: "default-password",
ManifestConfig: ManifestServiceConfig{
BaseServiceConfig: &BaseServiceConfig{Name: "manifest"},
},
DWNConfig: DWNServiceConfig{
BaseServiceConfig: &BaseServiceConfig{Name: "dwn"},
Expand Down
13 changes: 12 additions & 1 deletion config/config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ log_level = "debug"
storage = "bolt"

# per-service configuration
[services.keystore]
name = "keystore"
password = "default-password"

[services.did]
name = "did"
methods = ["key"]
Expand All @@ -29,4 +33,11 @@ methods = ["key"]
name = "schema"

[services.credential]
name = "credential"
name = "credential"

[services.manifest]
name = "manifest"

[services.dwn]
name = "dwn"
dwn_endpoint = "http://localhost:4321"
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ go 1.19

require (
github.com/BurntSushi/toml v1.2.0
github.com/TBD54566975/ssi-sdk v0.0.1-alpha.0.20220923181557-56fc273d1dcd
github.com/TBD54566975/ssi-sdk v0.0.1-alpha.0.20220921215642-f749c4d89b8b
github.com/ardanlabs/conf v1.5.0
github.com/dimfeld/httptreemux/v5 v5.4.0
github.com/go-playground/locales v0.14.0
Expand Down
6 changes: 2 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
github.com/BurntSushi/toml v1.2.0 h1:Rt8g24XnyGTyglgET/PRUNlrUeu9F5L+7FilkXfZgs0=
github.com/BurntSushi/toml v1.2.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
github.com/TBD54566975/ssi-sdk v0.0.1-alpha.0.20220915152202-18c69f474901 h1:7cT1hMDiWQlsQYNP2U0mxpUjc5AxKVElcAdvTt5amnM=
github.com/TBD54566975/ssi-sdk v0.0.1-alpha.0.20220915152202-18c69f474901/go.mod h1:uXrbtCwqgsbZvL/zM3+DpAOuuIax9qG2aeUhZrGPCck=
github.com/TBD54566975/ssi-sdk v0.0.1-alpha.0.20220919021815-57be087eca80 h1:1HBBtzkjsgjCYjMVWi6+785SO+Q7kSOOrA0Njw3JON0=
github.com/TBD54566975/ssi-sdk v0.0.1-alpha.0.20220919021815-57be087eca80/go.mod h1:ZUcjj/CHvtmz48c9GapcbavlTRsN9JZ9pKdT+co/EvY=
github.com/TBD54566975/ssi-sdk v0.0.1-alpha.0.20220920154030-cbf6b4665a6d h1:NW/9Qg2Z+Yhh6PtzgV/cFwgNz7Sgng3E25IYQiUV34c=
github.com/TBD54566975/ssi-sdk v0.0.1-alpha.0.20220920154030-cbf6b4665a6d/go.mod h1:jkbaZT2qU+g6weOHpjPdxuHdmfOApdT8CQg41GEyZhA=
github.com/TBD54566975/ssi-sdk v0.0.1-alpha.0.20220923181557-56fc273d1dcd h1:BTbS7+/QsnxvVhP0LwB3JJxnT/9qHC8msEtku2JF4QY=
github.com/TBD54566975/ssi-sdk v0.0.1-alpha.0.20220923181557-56fc273d1dcd/go.mod h1:jkbaZT2qU+g6weOHpjPdxuHdmfOApdT8CQg41GEyZhA=
github.com/TBD54566975/ssi-sdk v0.0.1-alpha.0.20220921215642-f749c4d89b8b h1:0IJJPN8qBGEINcNheyMN/VztIj/Wu9OLzoaEdwfKMWM=
github.com/TBD54566975/ssi-sdk v0.0.1-alpha.0.20220921215642-f749c4d89b8b/go.mod h1:jkbaZT2qU+g6weOHpjPdxuHdmfOApdT8CQg41GEyZhA=
github.com/ardanlabs/conf v1.5.0 h1:5TwP6Wu9Xi07eLFEpiCUF3oQXh9UzHMDVnD3u/I5d5c=
github.com/ardanlabs/conf v1.5.0/go.mod h1:ILsMo9dMqYzCxDjDXTiwMI0IgxOJd0MOiucbQY2wlJw=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
Expand Down
10 changes: 8 additions & 2 deletions internal/keyaccess/dataintegrity.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,13 @@ func NewDataIntegrityKeyAccess(kid string, key gocrypto.PrivateKey) (*DataIntegr
}, nil
}

func (ka DataIntegrityKeyAccess) Sign(payload cryptosuite.Provable) ([]byte, error) {
// DataIntegrityJSON represents a response from a DataIntegrityKeyAccess.Sign() call represented
// as a serialized JSON object
type DataIntegrityJSON struct {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice

Data []byte `json:"data" validate:"required"`
}

func (ka DataIntegrityKeyAccess) Sign(payload cryptosuite.Provable) (*DataIntegrityJSON, error) {
if payload == nil {
return nil, errors.New("payload cannot be nil")
}
Expand All @@ -58,7 +64,7 @@ func (ka DataIntegrityKeyAccess) Sign(payload cryptosuite.Provable) ([]byte, err
if err != nil {
return nil, errors.Wrap(err, "could not marshal signed payload")
}
return signedJSONBytes, nil
return &DataIntegrityJSON{Data: signedJSONBytes}, nil
}

func (ka DataIntegrityKeyAccess) Verify(payload cryptosuite.Provable) error {
Expand Down
6 changes: 3 additions & 3 deletions internal/keyaccess/dataintegrity_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ func TestDataIntegrityKeyAccessSignVerify(t *testing.T) {
assert.NotEmpty(tt, signedCred)

var cred credential.VerifiableCredential
err = json.Unmarshal(signedCred, &cred)
err = json.Unmarshal(signedCred.Data, &cred)
assert.NoError(tt, err)

// verify
Expand Down Expand Up @@ -104,11 +104,11 @@ func TestDataIntegrityKeyAccessSignVerify(t *testing.T) {
assert.NotEmpty(tt, signedPres)

var pres credential.VerifiablePresentation
err = json.Unmarshal(signedPres, &pres)
err = json.Unmarshal(signedPres.Data, &pres)
assert.NoError(tt, err)

// verify
err = ka.Verify(&pres)
assert.NoError(tt, err)
})
}
}
47 changes: 32 additions & 15 deletions internal/keyaccess/jwt.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,44 +42,61 @@ func NewJWKKeyAccess(kid string, key gocrypto.PrivateKey) (*JWKKeyAccess, error)
}, nil
}

func (ka JWKKeyAccess) Sign(payload map[string]interface{}) ([]byte, error) {
// A JWKToken is a utility that wraps a string representation of a JSON Web Token.
type JWKToken struct {
Token string `json:"token" validate:"required"`
}

func (ka JWKKeyAccess) Sign(payload map[string]interface{}) (*JWKToken, error) {
if payload == nil {
return nil, errors.New("payload cannot be nil")
}
return ka.SignJWT(payload)
tokenBytes, err := ka.SignJWT(payload)
if err != nil {
return nil, errors.Wrap(err, "could not sign payload")
}
return &JWKToken{Token: string(tokenBytes)}, nil
}

func (ka JWKKeyAccess) Verify(token string) error {
if token == "" {
func (ka JWKKeyAccess) Verify(token JWKToken) error {
if token.Token == "" {
return errors.New("token cannot be empty")
}
return ka.VerifyJWT(token)
return ka.VerifyJWT(token.Token)
}

func (ka JWKKeyAccess) SignVerifiableCredential(credential credential.VerifiableCredential) ([]byte, error) {
func (ka JWKKeyAccess) SignVerifiableCredential(credential credential.VerifiableCredential) (*JWKToken, error) {
if err := credential.IsValid(); err != nil {
return nil, errors.New("cannot sign invalid credential")
}
return signing.SignVerifiableCredentialJWT(ka.JWTSigner, credential)
tokenBytes, err := signing.SignVerifiableCredentialJWT(ka.JWTSigner, credential)
if err != nil {
return nil, errors.Wrap(err, "could not sign credential")
}
return &JWKToken{Token: string(tokenBytes)}, nil
}

func (ka JWKKeyAccess) VerifyVerifiableCredential(token string) (*credential.VerifiableCredential, error) {
if token == "" {
func (ka JWKKeyAccess) VerifyVerifiableCredential(token JWKToken) (*credential.VerifiableCredential, error) {
if token.Token == "" {
return nil, errors.New("token cannot be empty")
}
return signing.VerifyVerifiableCredentialJWT(ka.JWTVerifier, token)
return signing.VerifyVerifiableCredentialJWT(ka.JWTVerifier, token.Token)
}

func (ka JWKKeyAccess) SignVerifiablePresentation(presentation credential.VerifiablePresentation) ([]byte, error) {
func (ka JWKKeyAccess) SignVerifiablePresentation(presentation credential.VerifiablePresentation) (*JWKToken, error) {
if err := presentation.IsValid(); err != nil {
return nil, errors.New("cannot sign invalid presentation")
}
return signing.SignVerifiablePresentationJWT(ka.JWTSigner, presentation)
tokenBytes, err := signing.SignVerifiablePresentationJWT(ka.JWTSigner, presentation)
if err != nil {
return nil, errors.Wrap(err, "could not sign presentation")
}
return &JWKToken{Token: string(tokenBytes)}, nil
}

func (ka JWKKeyAccess) VerifyVerifiablePresentation(token string) (*credential.VerifiablePresentation, error) {
if token == "" {
func (ka JWKKeyAccess) VerifyVerifiablePresentation(token JWKToken) (*credential.VerifiablePresentation, error) {
if token.Token == "" {
return nil, errors.New("token cannot be empty")
}
return signing.VerifyVerifiablePresentationJWT(ka.JWTVerifier, token)
return signing.VerifyVerifiablePresentationJWT(ka.JWTVerifier, token.Token)
}
16 changes: 8 additions & 8 deletions internal/keyaccess/jwt_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,11 @@ func TestJWKKeyAccessSignVerify(t *testing.T) {
data := map[string]interface{}{
"test": "test",
}
tokenBytes, err := ka.Sign(data)
token, err := ka.Sign(data)
assert.NoError(tt, err)
assert.NotEmpty(tt, tokenBytes)
assert.NotEmpty(tt, token)

err = ka.Verify(string(tokenBytes))
err = ka.Verify(*token)
assert.NoError(tt, err)
})

Expand All @@ -79,7 +79,7 @@ func TestJWKKeyAccessSignVerify(t *testing.T) {
assert.NoError(tt, err)
assert.NotEmpty(tt, ka)

err = ka.Verify("")
err = ka.Verify(JWKToken{})
assert.Error(tt, err)
assert.Contains(tt, err.Error(), "token cannot be empty")
})
Expand All @@ -101,7 +101,7 @@ func TestJWKKeyAccessSignVerifyCredentials(t *testing.T) {
assert.NotEmpty(tt, signedCred)

// verify
verifiedCred, err := ka.VerifyVerifiableCredential(string(signedCred))
verifiedCred, err := ka.VerifyVerifiableCredential(*signedCred)
assert.NoError(tt, err)
assert.NotEmpty(tt, verifiedCred)

Expand Down Expand Up @@ -136,7 +136,7 @@ func TestJWKKeyAccessSignVerifyCredentials(t *testing.T) {
assert.NotEmpty(tt, ka)

// verify
_, err = ka.VerifyVerifiableCredential("bad")
_, err = ka.VerifyVerifiableCredential(JWKToken{"bad"})
assert.Error(tt, err)
assert.Contains(tt, err.Error(), "could not verify JWT and its signature")
})
Expand All @@ -158,7 +158,7 @@ func TestJWKKeyAccessSignVerifyPresentations(t *testing.T) {
assert.NotEmpty(tt, signedPres)

// verify
verifiedPres, err := ka.VerifyVerifiablePresentation(string(signedPres))
verifiedPres, err := ka.VerifyVerifiablePresentation(*signedPres)
assert.NoError(tt, err)
assert.NotEmpty(tt, verifiedPres)

Expand Down Expand Up @@ -193,7 +193,7 @@ func TestJWKKeyAccessSignVerifyPresentations(t *testing.T) {
assert.NotEmpty(tt, ka)

// verify
_, err = ka.VerifyVerifiablePresentation("bad")
_, err = ka.VerifyVerifiablePresentation(JWKToken{"bad"})
assert.Error(tt, err)
assert.Contains(tt, err.Error(), "could not verify JWT and its signature")
})
Expand Down
4 changes: 2 additions & 2 deletions internal/util/crypto.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,11 @@ func XChaCha20Poly1305Decrypt(key, data []byte) ([]byte, error) {
nonce, ciphertext := data[:aead.NonceSize()], data[aead.NonceSize():]

// Decrypt the message and check it wasn't tampered with.
decyrpted, err := aead.Open(nil, nonce, ciphertext, nil)
decrypted, err := aead.Open(nil, nonce, ciphertext, nil)
if err != nil {
return nil, errors.Wrap(err, "could not decrypt data")
}
return decyrpted, nil
return decrypted, nil
}

// Argon2KeyGen returns an encoded string generation of a key generated using the go crypto argon2 impl
Expand Down
Loading