github.com/decred/politeia@v1.4.0/politeiad/backendv2/verify.go (about)

     1  // Copyright (c) 2020-2021 The Decred developers
     2  // Use of this source code is governed by an ISC
     3  // license that can be found in the LICENSE file.
     4  
     5  package backendv2
     6  
     7  import (
     8  	"crypto/sha256"
     9  	"encoding/base64"
    10  	"encoding/hex"
    11  	"encoding/json"
    12  	"errors"
    13  	"fmt"
    14  
    15  	"github.com/decred/dcrtime/merkle"
    16  	dmerkle "github.com/decred/dcrtime/merkle"
    17  	"github.com/decred/politeia/util"
    18  	"github.com/google/trillian/merkle/logverifier"
    19  	"github.com/google/trillian/merkle/rfc6962"
    20  )
    21  
    22  const (
    23  	// ProofTypeTrillianRFC6962 represents a trillian proof that uses
    24  	// the trillian hashing strategy HashStrategy_RFC6962_SHA256.
    25  	ProofTypeTrillianRFC6962 = "trillian-rfc6962"
    26  
    27  	// ProofTypeDcrtime represents a dcrtime proof.
    28  	ProofTypeDcrtime = "dcrtime"
    29  )
    30  
    31  // ExtraDataTrillianRFC6962 contains the extra data required to verify a
    32  // trillian inclusion proof.
    33  type ExtraDataTrillianRFC6962 struct {
    34  	LeafIndex int64 `json:"leafindex"`
    35  	TreeSize  int64 `json:"treesize"`
    36  }
    37  
    38  // verifyProofTrillian verifies a proof with the type ProofTypeTrillianRFC6962.
    39  func verifyProofTrillian(p Proof) error {
    40  	// Verify type
    41  	if p.Type != ProofTypeTrillianRFC6962 {
    42  		return fmt.Errorf("invalid proof type")
    43  	}
    44  
    45  	// The digest of the data is stored in trillian as the leaf value.
    46  	// The digest of the leaf value is the digest that is included in
    47  	// the log merkle root.
    48  	h := rfc6962.DefaultHasher
    49  	leafValue, err := hex.DecodeString(p.Digest)
    50  	if err != nil {
    51  		return err
    52  	}
    53  	leafHash := h.HashLeaf(leafValue)
    54  
    55  	merkleRoot, err := hex.DecodeString(p.MerkleRoot)
    56  	if err != nil {
    57  		return err
    58  	}
    59  
    60  	merklePath := make([][]byte, 0, len(p.MerklePath))
    61  	for _, v := range p.MerklePath {
    62  		b, err := hex.DecodeString(v)
    63  		if err != nil {
    64  			return err
    65  		}
    66  		merklePath = append(merklePath, b)
    67  	}
    68  
    69  	var ed ExtraDataTrillianRFC6962
    70  	err = json.Unmarshal([]byte(p.ExtraData), &ed)
    71  	if err != nil {
    72  		return err
    73  	}
    74  
    75  	verifier := logverifier.New(h)
    76  	return verifier.VerifyInclusionProof(ed.LeafIndex, ed.TreeSize,
    77  		merklePath, merkleRoot, leafHash)
    78  }
    79  
    80  // ExtraDataDcrtime contains the extra data required to verify a dcrtime
    81  // inclusion proof.
    82  type ExtraDataDcrtime struct {
    83  	NumLeaves uint32 // Nuber of leaves
    84  	Flags     string // Bitmap of merkle tree, base64 encoded
    85  }
    86  
    87  // verifyProofDcrtime verifies a proof with the type ProofTypeDcrtime.
    88  func verifyProofDcrtime(p Proof) error {
    89  	if p.Type != ProofTypeDcrtime {
    90  		return fmt.Errorf("invalid proof type")
    91  	}
    92  
    93  	// Verify digest is part of merkle path
    94  	var found bool
    95  	for _, v := range p.MerklePath {
    96  		if v == p.Digest {
    97  			found = true
    98  			break
    99  		}
   100  	}
   101  	if !found {
   102  		return fmt.Errorf("digest %v not found in merkle path %v",
   103  			p.Digest, p.MerklePath)
   104  	}
   105  
   106  	// Decode extra data
   107  	var ed ExtraDataDcrtime
   108  	err := json.Unmarshal([]byte(p.ExtraData), &ed)
   109  	if err != nil {
   110  		return err
   111  	}
   112  	flags, err := base64.StdEncoding.DecodeString(ed.Flags)
   113  	if err != nil {
   114  		return err
   115  	}
   116  
   117  	// Calculate merkle root
   118  	digests := make([][sha256.Size]byte, 0, len(p.MerklePath))
   119  	for _, v := range p.MerklePath {
   120  		b, err := hex.DecodeString(v)
   121  		if err != nil {
   122  			return err
   123  		}
   124  		var d [sha256.Size]byte
   125  		copy(d[:], b)
   126  		digests = append(digests, d)
   127  	}
   128  	mb := merkle.Branch{
   129  		NumLeaves: ed.NumLeaves,
   130  		Hashes:    digests,
   131  		Flags:     flags,
   132  	}
   133  	mr, err := dmerkle.VerifyAuthPath(&mb)
   134  	if err != nil {
   135  		return err
   136  	}
   137  	merkleRoot := hex.EncodeToString(mr[:])
   138  
   139  	// Verify merkle root matches
   140  	if merkleRoot != p.MerkleRoot {
   141  		return fmt.Errorf("invalid merkle root: got %v, want %v",
   142  			merkleRoot, p.MerkleRoot)
   143  	}
   144  
   145  	return nil
   146  }
   147  
   148  // verifyProof verifies a backend proof.
   149  func verifyProof(p Proof) error {
   150  	switch p.Type {
   151  	case ProofTypeTrillianRFC6962:
   152  		return verifyProofTrillian(p)
   153  	case ProofTypeDcrtime:
   154  		return verifyProofDcrtime(p)
   155  	}
   156  	return fmt.Errorf("invalid proof type")
   157  }
   158  
   159  var (
   160  	// ErrNotTimestamped is returned when a timestamp does not contain
   161  	// a TxID. This indicates that the data has yet to be included in
   162  	// a DCR transaction.
   163  	ErrNotTimestamped = errors.New("data has not be included in a dcr tx yet")
   164  )
   165  
   166  // VerifyTimestamp verifies the inclusion of the data in the merkle root that
   167  // was timestamped onto the dcr blockchain.
   168  func VerifyTimestamp(t Timestamp) error {
   169  	if t.TxID == "" {
   170  		return ErrNotTimestamped
   171  	}
   172  
   173  	// Verify digest. The data blob may not be included in certain
   174  	// scenerios such as if it has been censored.
   175  	if t.Data != "" {
   176  		d := hex.EncodeToString(util.Digest([]byte(t.Data)))
   177  		if d != t.Digest {
   178  			return fmt.Errorf("invalid digest: got %v, want %v", d, t.Digest)
   179  		}
   180  	}
   181  
   182  	// Verify proof ordering. The digest of the first proof should be
   183  	// the data digest. The digest of every subsequent proof should be
   184  	// the merkle root of the previous proof.
   185  	if len(t.Proofs) == 0 {
   186  		return fmt.Errorf("no proofs found")
   187  	}
   188  	if t.Digest != t.Proofs[0].Digest {
   189  		return fmt.Errorf("invalid proofs: digest %v not found", t.Digest)
   190  	}
   191  	nextDigest := t.Proofs[0].MerkleRoot
   192  	for i := 1; i < len(t.Proofs); i++ {
   193  		p := t.Proofs[i]
   194  		if p.Digest != nextDigest {
   195  			return fmt.Errorf("invalid proof %v digest: got %v, want %v",
   196  				i, p.Digest, nextDigest)
   197  		}
   198  		nextDigest = t.MerkleRoot
   199  	}
   200  
   201  	// Verify the merkle root of the last proof is the merkle root
   202  	// that was included in the dcr transaction.
   203  	if nextDigest != t.MerkleRoot {
   204  		return fmt.Errorf("merkle root of last proof does not match timestamped "+
   205  			"merkle root: got %v, want %v", nextDigest, t.MerkleRoot)
   206  	}
   207  
   208  	// Verify proofs
   209  	for _, v := range t.Proofs {
   210  		err := verifyProof(v)
   211  		if err != nil {
   212  			return fmt.Errorf("invalid %v proof: %v", v.Type, err)
   213  		}
   214  	}
   215  
   216  	return nil
   217  }