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 }