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 23, 2024
1 parent 6f83cdc commit 92907e1
Show file tree
Hide file tree
Showing 6 changed files with 112 additions and 65 deletions.
94 changes: 64 additions & 30 deletions doc/did/doc.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,13 +48,17 @@ 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"
jsonldJWS = "jws"

// various public key encodings.
jsonldPublicKeyBase58 = "publicKeyBase58"
Expand Down Expand Up @@ -487,14 +491,18 @@ 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
JWS string
relativeURL bool
}

// UnmarshalJSON unmarshals a DID Document.
Expand Down Expand Up @@ -624,7 +632,7 @@ func populateVerificationRelationships(doc *Doc, raw *rawDoc) error {
return nil
}

func populateProofs(context, didID, baseURI string, rawProofs []interface{}) ([]Proof, error) {
func populateProofs(context, didID, baseURI string, rawProofs []interface{}) ([]Proof, error) { // nolint:funlen
proofs := make([]Proof, 0, len(rawProofs))

for _, rawProof := range rawProofs {
Expand Down Expand Up @@ -659,13 +667,17 @@ 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]),
JWS: stringEntry(emap[jsonldJWS]),
Nonce: nonce,
relativeURL: isRelative,
}

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

// VerifyProof verifies document proofs.
func (doc *Doc) VerifyProof(suites []api.VerifierSuite, jsonldOpts ...processor.Opts) error {
// Deprecated. Please use vc-go/verifiable.VerifyDIDProof().
func (doc *Doc) VerifyProof(suites []api.VerifierSuite, opts ...processor.Opts) error {
if len(doc.Proof) == 0 {
return ErrProofNotFound
}
Expand All @@ -1261,7 +1274,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 @@ -1552,10 +1565,10 @@ func populateRawVerification(context, baseURI, didID string, verifications []Ver
func populateRawProofs(context, didID, baseURI string, proofs []Proof) []interface{} {
rawProofs := make([]interface{}, 0, len(proofs))

k := jsonldProofValue
proofValueKey := jsonldProofValue

if context == contextV011 {
k = jsonldSignatureValue
proofValueKey = jsonldSignatureValue
}

for _, p := range proofs {
Expand All @@ -1564,15 +1577,36 @@ func populateRawProofs(context, didID, baseURI string, proofs []Proof) []interfa
creator = makeRelativeDIDURL(p.Creator, baseURI, didID)
}

rawProofs = append(rawProofs, map[string]interface{}{
rawProof := 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,
})
}

if len(p.ProofValue) > 0 {
rawProof[proofValueKey] = sigproof.EncodeProofValue(p.ProofValue, p.Type)
}

if p.JWS != "" {
rawProof[jsonldJWS] = p.JWS
}

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

if p.Challenge != "" {
rawProof[jsonldChallenge] = p.Challenge
}

if p.VerificationMethod != "" {
rawProof[jsonldVerificationMethod] = p.VerificationMethod
}

rawProofs = append(rawProofs, rawProof)
}

return rawProofs
Expand Down
48 changes: 26 additions & 22 deletions doc/did/doc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -476,12 +476,18 @@ func TestValidWithProof(t *testing.T) {
require.NoError(t, err)

eProof := Proof{
Type: "Ed25519Signature2018",
Created: &created,
Creator: "did:method:abc#key-1",
ProofValue: proofValue,
Domain: "",
Nonce: nonce,
Type: "Ed25519Signature2018",
Created: &created,
Creator: "did:method:abc#key-1",
ProofValue: proofValue,
Domain: "",
Nonce: nonce,
ProofPurpose: "assertionMethod",
CryptoSuite: "cryptosuite1",
Challenge: "challenge1",
VerificationMethod: "verificationMethod1",
JWS: "jws1",
relativeURL: false,
}
require.Equal(t, []Proof{eProof}, doc.Proof)

Expand Down Expand Up @@ -1153,19 +1159,6 @@ func TestValidateDidDocProof(t *testing.T) {
}
})

t.Run("test did doc proof without proofValue", func(t *testing.T) {
raw := &rawDoc{}
require.NoError(t, json.Unmarshal([]byte(validDocWithProof), &raw))
proof, ok := raw.Proof[0].(map[string]interface{})
require.True(t, ok)
delete(proof, jsonldProofValue)
bytes, err := json.Marshal(raw)
require.NoError(t, err)
err = validate(bytes, raw.schemaLoader())
require.Error(t, err)
require.Contains(t, err.Error(), "proofValue is required")
})

t.Run("test did doc proof without proofValue v0.11", func(t *testing.T) {
raw := &rawDoc{}
require.NoError(t, json.Unmarshal([]byte(validDocV011WithProof), &raw))
Expand Down Expand Up @@ -1234,7 +1227,8 @@ func TestRequiresLegacyHandling(t *testing.T) {

func TestJSONConversion(t *testing.T) {
docs := []string{
validDoc, validDocV011, validDocWithProofAndJWK, docV011WithVerificationRelationships, validDocWithBase,
validDoc, validDocV011, validDocWithProofAndJWK,
docV011WithVerificationRelationships, validDocWithBase, validDocWithProof,
}
for _, d := range docs {
// setup -> create Document from json byte data
Expand Down Expand Up @@ -2019,7 +2013,12 @@ const validDocWithProof = `{
"domain": "",
"nonce": "",
"proofValue": "6mdES87erjP5r1qCSRW__otj-A_Rj0YgRO7XU_0Amhwdfa7AAmtGUSFGflR_fZqPYrY9ceLRVQCJ49s0q7-LBA",
"type": "Ed25519Signature2018"
"type": "Ed25519Signature2018",
"proofPurpose": "assertionMethod",
"challenge": "challenge1",
"cryptosuite": "cryptosuite1",
"verificationMethod": "verificationMethod1",
"jws": "jws1"
}],
"verificationMethod": [{
"controller": "did:method:abc",
Expand Down Expand Up @@ -2074,7 +2073,12 @@ const validDocV011WithProof = `{
"domain": "",
"nonce": "",
"signatureValue": "6mdES87erjP5r1qCSRW__otj-A_Rj0YgRO7XU_0Amhwdfa7AAmtGUSFGflR_fZqPYrY9ceLRVQCJ49s0q7-LBA",
"type": "Ed25519Signature2018"
"type": "Ed25519Signature2018",
"proofPurpose": "assertionMethod",
"challenge": "challenge1",
"cryptosuite": "cryptosuite1",
"verificationMethod": "verificationMethod1",
"jws": "jws1"
}],
"publicKey": [{
"owner": "did:method:abc",
Expand Down
5 changes: 4 additions & 1 deletion doc/did/schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ const (
"definitions": {
"proof": {
"type": "object",
"required": [ "type", "proofValue"],
"required": [ "type"],
"properties": {
"type": {
"type": "string",
Expand All @@ -122,6 +122,9 @@ const (
"proofValue": {
"type": "string"
},
"jws": {
"type": "string"
},
"domain": {
"type": "string"
},
Expand Down
27 changes: 15 additions & 12 deletions doc/ld/proof/proof.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import (
"encoding/base64"
"errors"
"fmt"
"strings"

"github.com/multiformats/go-multibase"

Expand Down Expand Up @@ -41,6 +40,7 @@ const (
jsonldCapabilityChain = "capabilityChain"

ed25519Signature2020 = "Ed25519Signature2020"
dataIntegrityProof = "DataIntegrityProof"
)

// Proof is cryptographic proof of the integrity of the DID Document.
Expand Down Expand Up @@ -150,23 +150,21 @@ func decodeBase64(s string) ([]byte, error) {

// DecodeProofValue decodes proofValue basing on proof type.
func DecodeProofValue(s, proofType string) ([]byte, error) {
if proofType == ed25519Signature2020 {
switch proofType {
case ed25519Signature2020:
_, value, err := multibase.Decode(s)
if err == nil {
return value, nil
}

return nil, errors.New("unsupported encoding")
case dataIntegrityProof:
// No need to decode Data integrity proof as encoding/decoding logic for this proof type
// is managed by vc-go/dataintegrity package.
return []byte(s), nil
default:
return decodeBase64(s)
}

if strings.HasPrefix(s, "z") { // maybe base58
_, value, err := multibase.Decode(s)
if err == nil {
return value, nil
}
}

return decodeBase64(s)
}

// stringEntry.
Expand Down Expand Up @@ -232,9 +230,14 @@ func (p *Proof) JSONLdObject() map[string]interface{} { // nolint:gocyclo

// EncodeProofValue decodes proofValue basing on proof type.
func EncodeProofValue(proofValue []byte, proofType string) string {
if proofType == ed25519Signature2020 {
switch proofType {
case ed25519Signature2020:
encoded, _ := multibase.Encode(multibase.Base58BTC, proofValue) //nolint: errcheck
return encoded
case dataIntegrityProof:
// No need to encode Data integrity proof as encoding/decoding logic for this proof type
// is managed by vc-go/dataintegrity package.
return string(proofValue)
}

return base64.RawURLEncoding.EncodeToString(proofValue)
Expand Down
1 change: 1 addition & 0 deletions doc/signature/signer/signer.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ func New(signatureSuites ...SignatureSuite) *DocumentSigner {
}

// Sign will sign JSON LD document.
// Deprecated. Please use vc-go/verifiable.AddDIDLinkedDataProof().
func (signer *DocumentSigner) Sign(
context *Context,
jsonLdDoc []byte,
Expand Down
2 changes: 2 additions & 0 deletions doc/signature/verifier/verifier.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ func New(resolver keyResolver, suites ...api.VerifierSuite) (*DocumentVerifier,
}

// Verify will verify document proofs.
// Deprecated. Please use vc-go/verifiable.VerifyDIDProof().
func (dv *DocumentVerifier) Verify(jsonLdDoc []byte, opts ...processor.Opts) error {
var jsonLdObject map[string]interface{}

Expand All @@ -52,6 +53,7 @@ func (dv *DocumentVerifier) Verify(jsonLdDoc []byte, opts ...processor.Opts) err
}

// VerifyObject will verify document proofs for JSON LD object.
// Deprecated. Please use vc-go/verifiable.VerifyDIDProof().
func (dv *DocumentVerifier) VerifyObject(jsonLdObject map[string]interface{}, opts ...processor.Opts) error {
proofs, err := proof.GetProofs(jsonLdObject)
if err != nil {
Expand Down

0 comments on commit 92907e1

Please sign in to comment.