Skip to content

Commit

Permalink
feat: add data integrity proof
Browse files Browse the repository at this point in the history
Signed-off-by: Misha Sizov <[email protected]>
  • Loading branch information
mishasizov-SK committed Dec 20, 2024
1 parent 6f83cdc commit 41f6e6e
Show file tree
Hide file tree
Showing 8 changed files with 303 additions and 63 deletions.
75 changes: 43 additions & 32 deletions doc/did/doc.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ import (
"github.com/xeipuuv/gojsonschema"

"github.com/trustbloc/did-go/doc/did/endpoint"
"github.com/trustbloc/did-go/doc/ld/processor"
sigproof "github.com/trustbloc/did-go/doc/ld/proof"
"github.com/trustbloc/did-go/doc/signature/api"
"github.com/trustbloc/did-go/doc/signature/verifier"
Expand All @@ -48,13 +47,16 @@ const (
jsonldController = "controller"
jsonldOwner = "owner"

jsonldCreator = "creator"
jsonldCreated = "created"
jsonldProofValue = "proofValue"
jsonldSignatureValue = "signatureValue"
jsonldDomain = "domain"
jsonldNonce = "nonce"
jsonldProofPurpose = "proofPurpose"
jsonldCreator = "creator"
jsonldCreated = "created"
jsonldProofValue = "proofValue"
jsonldSignatureValue = "signatureValue"
jsonldDomain = "domain"
jsonldNonce = "nonce"
jsonldProofPurpose = "proofPurpose"
jsonldChallenge = "challenge"
jsonldCryptoSuite = "cryptosuite"
jsonldVerificationMethod = "verificationMethod"

// various public key encodings.
jsonldPublicKeyBase58 = "publicKeyBase58"
Expand Down Expand Up @@ -487,14 +489,17 @@ func (r *rawDoc) UnmarshalJSON(data []byte) error {

// Proof is cryptographic proof of the integrity of the DID Document.
type Proof struct {
Type string
Created *time.Time
Creator string
ProofValue []byte
Domain string
Nonce []byte
ProofPurpose string
relativeURL bool
Type string
Created *time.Time
Creator string
ProofValue []byte
Domain string
Nonce []byte
ProofPurpose string
CryptoSuite string
Challenge string
VerificationMethod string
relativeURL bool
}

// UnmarshalJSON unmarshals a DID Document.
Expand Down Expand Up @@ -659,13 +664,16 @@ func populateProofs(context, didID, baseURI string, rawProofs []interface{}) ([]
}

proof := Proof{
Type: stringEntry(emap[jsonldType]),
Creator: creator,
ProofValue: proofValue,
ProofPurpose: stringEntry(emap[jsonldProofPurpose]),
Domain: stringEntry(emap[jsonldDomain]),
Nonce: nonce,
relativeURL: isRelative,
Type: stringEntry(emap[jsonldType]),
Creator: creator,
ProofValue: proofValue,
ProofPurpose: stringEntry(emap[jsonldProofPurpose]),
Domain: stringEntry(emap[jsonldDomain]),
VerificationMethod: stringEntry(emap[jsonldVerificationMethod]),
CryptoSuite: stringEntry(emap[jsonldCryptoSuite]),
Challenge: stringEntry(emap[jsonldChallenge]),
Nonce: nonce,
relativeURL: isRelative,
}

created := stringEntry(emap[jsonldCreated])
Expand Down Expand Up @@ -1246,7 +1254,7 @@ func (doc *Doc) MarshalJSON() ([]byte, error) {
}

// VerifyProof verifies document proofs.
func (doc *Doc) VerifyProof(suites []api.VerifierSuite, jsonldOpts ...processor.Opts) error {
func (doc *Doc) VerifyProof(suites []api.VerifierSuite, opts ...verifier.Opts) error {
if len(doc.Proof) == 0 {
return ErrProofNotFound
}
Expand All @@ -1261,7 +1269,7 @@ func (doc *Doc) VerifyProof(suites []api.VerifierSuite, jsonldOpts ...processor.
return fmt.Errorf("create verifier: %w", err)
}

return v.Verify(docBytes, jsonldOpts...)
return v.Verify(docBytes, opts...)
}

// VerificationMethods returns verification methods of DID Doc of certain relationship.
Expand Down Expand Up @@ -1565,13 +1573,16 @@ func populateRawProofs(context, didID, baseURI string, proofs []Proof) []interfa
}

rawProofs = append(rawProofs, map[string]interface{}{
jsonldType: p.Type,
jsonldCreated: p.Created,
jsonldCreator: creator,
k: sigproof.EncodeProofValue(p.ProofValue, p.Type),
jsonldDomain: p.Domain,
jsonldNonce: base64.RawURLEncoding.EncodeToString(p.Nonce),
jsonldProofPurpose: p.ProofPurpose,
jsonldType: p.Type,
jsonldCreated: p.Created,
jsonldCreator: creator,
k: sigproof.EncodeProofValue(p.ProofValue, p.Type),
jsonldDomain: p.Domain,
jsonldNonce: base64.RawURLEncoding.EncodeToString(p.Nonce),
jsonldProofPurpose: p.ProofPurpose,
jsonldVerificationMethod: p.VerificationMethod,
jsonldCryptoSuite: p.CryptoSuite,
jsonldChallenge: p.Challenge,
})
}

Expand Down
9 changes: 5 additions & 4 deletions doc/did/doc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
"encoding/pem"
"errors"
"fmt"
"github.com/trustbloc/did-go/doc/signature/verifier"
"testing"
"time"

Expand Down Expand Up @@ -1388,12 +1389,12 @@ func TestVerifyProof(t *testing.T) {
require.NotNil(t, doc)
err = doc.VerifyProof(
[]api.VerifierSuite{&signature.MockVerifierSuite{MockSuite: signature.MockSuite{AcceptVal: true}}},
testutil.WithDocumentLoader(t),
verifier.WithProcessorOpts(testutil.WithDocumentLoader(t)),
)
require.NoError(t, err)

// error - no suites are passed, verifier is not created
err = doc.VerifyProof([]api.VerifierSuite{}, testutil.WithDocumentLoader(t))
err = doc.VerifyProof([]api.VerifierSuite{}, verifier.WithProcessorOpts(testutil.WithDocumentLoader(t)))
require.Error(t, err)
require.Contains(t, err.Error(), "create verifier")

Expand All @@ -1404,7 +1405,7 @@ func TestVerifyProof(t *testing.T) {
MockSuite: signature.MockSuite{AcceptVal: true},
VerifyErr: errors.New("ed25519: invalid signature"),
}},
testutil.WithDocumentLoader(t),
verifier.WithProcessorOpts(testutil.WithDocumentLoader(t)),
)
require.NotNil(t, err)
require.Contains(t, err.Error(), "ed25519: invalid signature")
Expand All @@ -1415,7 +1416,7 @@ func TestVerifyProof(t *testing.T) {
require.NotNil(t, doc)
err = doc.VerifyProof(
[]api.VerifierSuite{&signature.MockVerifierSuite{MockSuite: signature.MockSuite{AcceptVal: true}}},
testutil.WithDocumentLoader(t),
verifier.WithProcessorOpts(testutil.WithDocumentLoader(t)),
)
require.Equal(t, ErrProofNotFound, err)
require.Contains(t, err.Error(), "proof not found")
Expand Down
10 changes: 10 additions & 0 deletions doc/ld/proof/proof.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ const (
jsonldChallenge = "challenge"
// jsonldCapabilityChain is a key for capabilityChain.
jsonldCapabilityChain = "capabilityChain"
// jsonldCryptoSuite is a key for cryptosuite.
jsonldCryptoSuite = "cryptosuite"

ed25519Signature2020 = "Ed25519Signature2020"
)
Expand All @@ -58,6 +60,9 @@ type Proof struct {
SignatureRepresentation SignatureRepresentation
// CapabilityChain must be an array. Each element is either a string or an object.
CapabilityChain []interface{}
CryptoSuite string // Data integrity proof model field.
//ID string // Data integrity proof model field. Not in use.
//PreviousProof string // Data integrity proof model field. Not in use.
}

// NewProof creates new proof.
Expand Down Expand Up @@ -113,6 +118,7 @@ func NewProof(emap map[string]interface{}) (*Proof, error) {
Domain: stringEntry(emap[jsonldDomain]),
Nonce: nonce,
Challenge: stringEntry(emap[jsonldChallenge]),
CryptoSuite: stringEntry(emap[jsonldCryptoSuite]),
CapabilityChain: capabilityChain,
}, nil
}
Expand Down Expand Up @@ -227,6 +233,10 @@ func (p *Proof) JSONLdObject() map[string]interface{} { // nolint:gocyclo
emap[jsonldCapabilityChain] = p.CapabilityChain
}

if p.CryptoSuite != "" {
emap[jsonldCryptoSuite] = p.CryptoSuite
}

return emap
}

Expand Down
6 changes: 6 additions & 0 deletions doc/ld/proof/proof_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ func TestProof(t *testing.T) {
"domain": "abc.com",
"nonce": "",
"proofValue": proofValueBase64,
"cryptosuite": "ecdsa-rdfc-2019",
})
require.NoError(t, err)

Expand All @@ -47,6 +48,7 @@ func TestProof(t *testing.T) {
require.Equal(t, "abc.com", p.Domain)
require.Equal(t, []byte(""), p.Nonce)
require.Equal(t, proofValueBytes, p.ProofValue)
require.Equal(t, "ecdsa-rdfc-2019", p.CryptoSuite)

// test proof with multibase encoding
p, err = NewProof(map[string]interface{}{
Expand All @@ -57,6 +59,7 @@ func TestProof(t *testing.T) {
"domain": "abc.com",
"nonce": "",
"proofValue": proofValueMultibase,
"cryptosuite": "eddsa-rdfc-2022",
})
require.NoError(t, err)

Expand All @@ -74,6 +77,7 @@ func TestProof(t *testing.T) {
require.Equal(t, "abc.com", p.Domain)
require.Equal(t, []byte(""), p.Nonce)
require.Equal(t, proofValueBytes, p.ProofValue)
require.Equal(t, "eddsa-rdfc-2022", p.CryptoSuite)

// test created time with milliseconds section
p, err = NewProof(map[string]interface{}{
Expand Down Expand Up @@ -391,6 +395,7 @@ func TestProof_JSONLdObject(t *testing.T) {
Domain: "internal",
Nonce: nonceBase64,
Challenge: "sample-challenge-xyz",
CryptoSuite: "eddsa-rdfc-2022",
}

pJSONLd := p.JSONLdObject()
Expand All @@ -403,6 +408,7 @@ func TestProof_JSONLdObject(t *testing.T) {
r.Equal("internal", pJSONLd["domain"])
r.Equal("abc", pJSONLd["nonce"])
r.Equal("sample-challenge-xyz", pJSONLd["challenge"])
r.Equal("eddsa-rdfc-2022", pJSONLd["cryptosuite"])

// test created time with milliseconds section
created, err = time.Parse(time.RFC3339Nano, "2018-03-15T00:00:00.972Z")
Expand Down
5 changes: 3 additions & 2 deletions doc/signature/api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"github.com/trustbloc/kms-go/doc/jose/jwk"

"github.com/trustbloc/did-go/doc/ld/processor"
"github.com/trustbloc/did-go/doc/signature/verifier"
)

// Context holds signing options and private key.
Expand All @@ -38,9 +39,9 @@ type Signer interface {
// Verifier wraps a set of VerifierSuite instances and verifies proofs on json LD documents.
type Verifier interface {
// Verify verifies a json LD proof on a document.
Verify(jsonLdDoc []byte, opts ...processor.Opts) error
Verify(jsonLdDoc []byte, opts ...verifier.Opts) error
// VerifyObject verifies a json LD proof on a document unmarshalled following json.Unmarshal conventions.
VerifyObject(jsonLdObject map[string]interface{}, opts ...processor.Opts) error
VerifyObject(jsonLdObject map[string]interface{}, opts ...verifier.Opts) error
}

// SignatureSuite provides common methods for signature suites.
Expand Down
108 changes: 108 additions & 0 deletions doc/signature/signer/dataintegrity.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
/*
Copyright SecureKey Technologies Inc. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/

package signer

//
//import (
// "encoding/json"
// "fmt"
// "time"
//
// "github.com/trustbloc/vc-go/dataintegrity"
// "github.com/trustbloc/vc-go/dataintegrity/models"
//
// "github.com/trustbloc/did-go/doc/ld/proof"
//)
//
//// DataIntegrityProofContext holds parameters for creating a Data Integrity Proof.
//type DataIntegrityProofContext struct {
// SigningKeyID string // eg did:foo:bar#key-1
// ProofPurpose string // assertionMethod
// CryptoSuite string // ecdsa-2019
// Created *time.Time //
// Domain string //
// Challenge string //
//}
//
//// AddDataIntegrityProof adds a Data Integrity Proof to the document.
//func (signer *DocumentSigner) AddDataIntegrityProof(
// doc []byte,
// context *DataIntegrityProofContext,
// diSigner *dataintegrity.Signer,
//) ([]byte, error) {
// var jsonLdObject map[string]interface{}
//
// err := json.Unmarshal(doc, &jsonLdObject)
// if err != nil {
// return nil, fmt.Errorf("failed to unmarshal json ld document: %w", err)
// }
//
// // TODO: rewrite to use json object instead bytes presentation
// diProof, err := createDataIntegrityProof(context, doc, diSigner)
// if err != nil {
// return nil, fmt.Errorf("create data integrity proof: %w", err)
// }
//
// if err = proof.AddProof(jsonLdObject, diProof); err != nil {
// return nil, fmt.Errorf("add data integrity proof: %w", err)
// }
//
// signedDoc, err := json.Marshal(jsonLdObject)
// if err != nil {
// return nil, err
// }
//
// return signedDoc, nil
//}
//
//func createDataIntegrityProof(
// context *DataIntegrityProofContext,
// ldBytes []byte,
// signer *dataintegrity.Signer,
//) (*proof.Proof, error) {
// var createdTime time.Time
// if context.Created == nil {
// createdTime = time.Now()
// } else {
// createdTime = *context.Created
// }
//
// if context.ProofPurpose == "" {
// context.ProofPurpose = defaultProofPurpose
// }
//
// signed, err := signer.AddProof(ldBytes, &models.ProofOptions{
// Purpose: context.ProofPurpose,
// VerificationMethodID: context.SigningKeyID,
// ProofType: models.DataIntegrityProof,
// SuiteType: context.CryptoSuite,
// Domain: context.Domain,
// Challenge: context.Challenge,
// Created: createdTime,
// })
// if err != nil {
// return nil, fmt.Errorf("add proof: %w", err)
// }
//
// type rawProof struct {
// Proof map[string]interface{} `json:"proof,omitempty"`
// }
//
// // Get a proof from json-ld document.
// var rProof rawProof
//
// err = json.Unmarshal(signed, &rProof)
// if err != nil {
// return nil, err
// }
//
// diProof, err := proof.NewProof(rProof.Proof)
// if err != nil {
// return nil, fmt.Errorf("new proof: %w", err)
// }
//
// return diProof, nil
//}
Loading

0 comments on commit 41f6e6e

Please sign in to comment.