Disclaimer: I'm no expert but sharing what I've learned as I set up dm-verity on a RPi. I'll try to address questions 1 and 2.
- Considering the explanations of dm-verity that I have found that actually describe the algorithm, including the one in this answer, they explain away the actual algorithm by referring to Merkle Trees. This latter reference has a useful example:
For example, in the picture, the integrity of data block L2 can be verified immediately if the tree already contains hash 0-0 and hash 1 by hashing the data block and iteratively combining the result with hash 0-0 and then hash 1 and finally comparing the result with the top hash. Similarly, the integrity of data block L3 can be verified if the tree already has hash 1-1 and hash 0.
When setting up dm-verity, you will create a hash tree and store it on a separate partition. The system can then verify the block being read by
- calculating the hash of the block being read
- combine this calculated hash with the saved hash of the other block to compute the upper level hash.
- combine this upper level hash with the saved hash at the same level to compute the next upper level hash.
- repeat until the root hash has been calculated
- compare the calculated root hash with the saved root hash
You would need to update the stored hash tree each time you write any block to continue to be able to detect tampering.
- I suppose it's implementation-dependent. But the implementation in the cryptsetup-bin Debian package and as explained in the first part of this reference checks the entire hash on boot. (The Wikipedia link above also explains that computing the hash tree starting with hashes of blocks is faster than the computing a single is more efficient than computing the hash of the whole data set -- I'm still trying to figure that part out). As I understand it, you can also configure it to verify a block on each read (see this reference).