@@ -2,7 +2,6 @@ package logverification
2
2
3
3
import (
4
4
"context"
5
- "crypto/ecdsa"
6
5
"errors"
7
6
"fmt"
8
7
"hash"
@@ -17,10 +16,10 @@ import (
17
16
// MMRState is an abstraction, but it is assumed that logStateA comes from a local, trusted copy of the data
18
17
// rather than a fresh download from DataTrails.
19
18
//
19
+ // This function assumes the two log states are from the same massif.
20
+ //
20
21
// NOTE: the log state's signatures are not verified in this function, it is expected that the signature verification
21
22
// is done as a separate step to the consistency verification.
22
- //
23
- // NOTE: it is expected that both logStateA and logStateB have had their root recalculated.
24
23
func VerifyConsistency (
25
24
ctx context.Context ,
26
25
hasher hash.Hash ,
@@ -30,102 +29,44 @@ func VerifyConsistency(
30
29
logStateB * massifs.MMRState ,
31
30
) (bool , error ) {
32
31
33
- if logStateA .Root == nil || logStateB .Root == nil {
32
+ if logStateA .Peaks == nil || logStateB .Peaks == nil {
34
33
return false , errors .New ("VerifyConsistency failed: the roots for both log state A and log state B need to be set" )
35
34
}
36
35
37
- if len (logStateA .Root ) == 0 || len (logStateB .Root ) == 0 {
36
+ if len (logStateA .Peaks ) == 0 || len (logStateB .Peaks ) == 0 {
38
37
return false , errors .New ("VerifyConsistency failed: the roots for both log state A and log state B need to be set" )
39
38
}
40
39
41
40
massifReader := massifs .NewMassifReader (logger .Sugar , reader )
42
41
43
- // last massif in the merkle log for log state A
44
- massifContextA , err := Massif (logStateA .MMRSize - 1 , massifReader , tenantID , DefaultMassifHeight )
45
- if err != nil {
46
- return false , fmt .Errorf ("VerifyConsistency failed: unable to get the last massif for log state A: %w" , err )
47
- }
48
-
49
42
// last massif in the merkle log for log state B
50
43
massifContextB , err := Massif (logStateB .MMRSize - 1 , massifReader , tenantID , DefaultMassifHeight )
51
44
if err != nil {
52
45
return false , fmt .Errorf ("VerifyConsistency failed: unable to get the last massif for log state B: %w" , err )
53
46
}
54
47
55
- // We construct a proof of consistency between logStateA and logStateB.
56
- // This will be a proof that logStateB derives from logStateA.
57
- consistencyProof , err := mmr .IndexConsistencyProof (logStateA .MMRSize , logStateB .MMRSize , massifContextB , hasher )
58
- if err != nil {
59
- return false , fmt .Errorf ("VerifyConsistency failed: unable to generate consistency proof: %w" , err )
60
- }
61
-
62
- // In order to verify the proof we take the hashes of all of the peaks in logStateA.
63
- // The hash of each of these peaks guarantees the integrity of all of its child nodes, so we
64
- // don't need to check every hash.
65
-
66
- // Peaks returned as MMR positions (1-based), not MMR indices (0-based). The location of these
67
- // is deterministic: Given an MMR of a particular size, the peaks will always be in the same place.
68
- logPeaksA := mmr .Peaks (logStateA .MMRSize )
69
-
70
- // Get the hashes of all of the peaks.
71
- logPeakHashesA , err := mmr .PeakBagRHS (massifContextA , hasher , 0 , logPeaksA )
72
- if err != nil {
73
- return false , errors .New ("error" )
74
- }
75
-
76
- // Lastly, verify the consistency proof using the peak hashes from our backed-up log. If this
77
- // returns true, then we can confidently say that everything in the backed-up log is in the state
78
- // of the log described by this signed state.
79
- verified := mmr .VerifyConsistency (hasher , logPeakHashesA , consistencyProof , logStateA .Root , logStateB .Root )
80
- return verified , nil
81
- }
82
-
83
- // VerifyConsistencyFromMassifs takes a massif context providing access to data from the past, and a massif
84
- // context providing access to the current version of the log. It returns whether or not the
85
- // new version of the log is consistent with the previous version (i.e. it contains all of the
86
- // same nodes in the same positions.)
87
- //
88
- // It is assumed that in a production use case, massifContextBefore provides access to a trusted
89
- // local copy of the massif, rather than a fresh download from DataTrails.
90
- func VerifyConsistencyFromMassifs (
91
- ctx context.Context ,
92
- verificationKey ecdsa.PublicKey ,
93
- hasher hash.Hash ,
94
- blobReader azblob.Reader ,
95
- massifContextBefore * massifs.MassifContext ,
96
- massifContextNow * massifs.MassifContext ,
97
- logStateNow * massifs.MMRState ,
98
- ) (bool , error ) {
99
- // Grab some core info about our backed up merkle log, which we'll need to prove consistency
100
- mmrSizeBefore := massifContextBefore .Count ()
101
- rootBefore , err := mmr .GetRoot (mmrSizeBefore , massifContextBefore , hasher )
102
- if err != nil {
103
- return false , fmt .Errorf ("VerifyConsistency failed: unable to get root for massifContextBefore: %w" , err )
104
- }
105
-
106
- // We construct a proof of consistency between the backed up MMR log and the head of the log.
107
- consistencyProof , err := mmr .IndexConsistencyProof (mmrSizeBefore , logStateNow .MMRSize , massifContextNow , hasher )
108
- if err != nil {
109
- return false , errors .New ("error" )
110
- }
111
-
112
- // In order to verify the proof we take the hashes of all of the peaks in the backed up log.
113
- // The hash of each of these peaks guarantees the integrity of all of its child nodes, so we
114
- // don't need to check every hash.
115
-
116
- // Peaks returned as MMR positions (1-based), not MMR indices (0-based). The location of these
117
- // is deterministic: Given an MMR of a particular size, the peaks will always be in the same place.
118
- backupLogPeaks := mmr .Peaks (mmrSizeBefore )
119
-
120
- // Get the hashes of all of the peaks.
121
- backupLogPeakHashes , err := mmr .PeakBagRHS (massifContextNow , hasher , 0 , backupLogPeaks )
122
- if err != nil {
123
- return false , errors .New ("error" )
124
- }
125
-
126
- // Lastly, verify the consistency proof using the peak hashes from our backed-up log. If this
127
- // returns true, then we can confidently say that everything in the backed-up log is in the state
128
- // of the log described by this signed state.
129
- verified := mmr .VerifyConsistency (hasher , backupLogPeakHashes , consistencyProof , rootBefore , logStateNow .Root )
130
- return verified , nil
48
+ // We check a proof of consistency between logStateA and logStateB.
49
+ // This will be a proof that logStateB includes all elements from logStateA,
50
+ // and includes them in the same positions.
51
+
52
+ // In order to verify the proof we verify that the inclusion proofs of each of
53
+ // the peaks from the old log matches a peak in the new log.
54
+ // Because a proof of inclusion requires that the proof reproduces the peak,
55
+ // and because all nodes in the old tree have proofs that pass through the
56
+ // old peaks and then reach the new peaks, we know it is not possible for
57
+ // the children to verify unless their peaks also verify. So we don't need
58
+ // to check every hash.
59
+
60
+ verified , _ /*peaksB*/ , err := mmr .CheckConsistency (massifContextB , hasher , logStateA .MMRSize , logStateB .MMRSize , logStateA .Peaks )
61
+
62
+ // A tampered node can not be proven unless the entire log is re-built. If
63
+ // a log is re-built, any proof held by a relying party will not verify. And
64
+ // as it is signed, it is evidence the log was re-built by someone with
65
+ // access to our signing key.
66
+ // In the case of a tamper (or corruption) without re-build, the proof of inclusion will fail.
67
+ // Examining the parent and sibling of an individually tampered node will reveal the tamper.
68
+ // This means we are always fail safe in the case of a tampered node - a
69
+ // party relying on the log can guarantee the will never use unverifiable
70
+ // data.
71
+ return verified , err
131
72
}
0 commit comments