osbase/build/mkverity: make build reproducible
The verity encoder previously generated a random salt. To make the build
reproducible, the salt is now taken from a hash of the entire input
file.
I shortened the salt from 64 bytes to 16 bytes. This is enough for the
purpose of the salt, which is to make hash collisions not reusable
across images. A potential benefit of the 64 byte salt is that it fills
a sha256 block and thus the remaining data is aligned to that block
size. On the other hand, with a 16 byte salt, one fewer hash block is
needed because the sha256 length fits in the last partially filled
block.
The encoder also generated a random UUID, but this did not affect
reproducibility as we do not write the superblock. For now, I removed
the UUID generation as it is completely unused.
Now, the build of //metropolis/node:oci_image is reproducible on my
machine.
Change-Id: I756ca31d02e65c7d6ce7bbfd6749c835ab696f3f
Reviewed-on: https://review.monogon.dev/c/monogon/+/4418
Reviewed-by: Lorenz Brun <lorenz@monogon.tech>
Tested-by: Jenkins CI
diff --git a/osbase/build/mkverity/mkverity.go b/osbase/build/mkverity/mkverity.go
index 82db0da..f75845d 100644
--- a/osbase/build/mkverity/mkverity.go
+++ b/osbase/build/mkverity/mkverity.go
@@ -11,6 +11,7 @@
package main
import (
+ "crypto/sha256"
"flag"
"fmt"
"io"
@@ -55,20 +56,34 @@
}
defer outputImage.Close()
- // Copy the input data into the output file, then rewind dataImage to be read
- // again by the Verity encoder.
+ // Copy the input data into the output file, then rewind dataImage.
_, err = io.Copy(outputImage, dataImage)
if err != nil {
return nil, err
}
- _, err = dataImage.Seek(0, os.SEEK_SET)
+ _, err = dataImage.Seek(0, io.SeekStart)
if err != nil {
return nil, err
}
+ // Hash the input data for use as the salt, then rewind dataImage. The purpose
+ // of the salt is to prevent reuse of collisions across different images. 16
+ // bytes is enough for this. We use a hash of the input instead of generating
+ // random bytes to make the build reproducible.
+ dataHash := sha256.New()
+ _, err = io.Copy(dataHash, dataImage)
+ if err != nil {
+ return nil, err
+ }
+ _, err = dataImage.Seek(0, io.SeekStart)
+ if err != nil {
+ return nil, err
+ }
+ salt := dataHash.Sum(nil)[:16]
+
// Write outputImage contents. Start with initializing a verity encoder,
- // seting outputImage as its output.
- v, err := verity.NewEncoder(outputImage, bs, bs, wsb)
+ // setting outputImage as its output.
+ v, err := verity.NewEncoder(outputImage, bs, bs, salt, wsb)
if err != nil {
return nil, fmt.Errorf("while initializing a verity encoder: %w", err)
}