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/verity/encoder.go b/osbase/verity/encoder.go
index 5a968cd..6ba8b97 100644
--- a/osbase/verity/encoder.go
+++ b/osbase/verity/encoder.go
@@ -28,7 +28,6 @@
import (
"bytes"
- "crypto/rand"
"crypto/sha256"
"encoding/binary"
"encoding/hex"
@@ -93,19 +92,10 @@
version: 1,
hashType: 1,
algorithm: [32]byte{'s', 'h', 'a', '2', '5', '6'},
- saltSize: 64,
dataBlockSize: 4096,
hashBlockSize: 4096,
}
- // Fill in the superblock UUID and cryptographic salt.
- if _, err := rand.Read(sb.uuid[:]); err != nil {
- return nil, fmt.Errorf("when generating UUID: %w", err)
- }
- if _, err := rand.Read(sb.saltBuffer[:]); err != nil {
- return nil, fmt.Errorf("when generating salt: %w", err)
- }
-
return &sb, nil
}
@@ -308,6 +298,10 @@
// VerityParameterList returns a list of Verity target parameters, ordered
// as they would appear in a parameter string.
func (t *MappingTable) VerityParameterList() []string {
+ salt := hex.EncodeToString(t.superblock.salt())
+ if salt == "" {
+ salt = "-"
+ }
return []string{
"1",
t.DataDevicePath,
@@ -318,7 +312,7 @@
strconv.FormatInt(t.HashStart, 10),
t.superblock.algorithmName(),
hex.EncodeToString(t.rootHash),
- hex.EncodeToString(t.superblock.salt()),
+ salt,
}
}
@@ -433,13 +427,14 @@
// encoder will write to the given io.Writer object.
// A verity superblock will be written, preceding the hash tree, if
// writeSb is true.
-func NewEncoder(out io.Writer, dataBlockSize, hashBlockSize uint32, writeSb bool) (*encoder, error) {
+func NewEncoder(out io.Writer, dataBlockSize, hashBlockSize uint32, salt []byte, writeSb bool) (*encoder, error) {
sb, err := newSuperblock()
if err != nil {
return nil, fmt.Errorf("while creating a superblock: %w", err)
}
sb.dataBlockSize = dataBlockSize
sb.hashBlockSize = hashBlockSize
+ sb.saltSize = uint16(copy(sb.saltBuffer[:], salt))
e := encoder{
out: out,
diff --git a/osbase/verity/encoder_test.go b/osbase/verity/encoder_test.go
index 012cb27..2c9c35b 100644
--- a/osbase/verity/encoder_test.go
+++ b/osbase/verity/encoder_test.go
@@ -93,7 +93,8 @@
// Create a Verity encoder, backed with hfd. Configure it to write the
// Verity superblock. Use 4096-byte blocks.
bs := uint32(4096)
- verityEnc, err := NewEncoder(hfd, bs, bs, true)
+ salt := []byte("testsalt")
+ verityEnc, err := NewEncoder(hfd, bs, bs, salt, true)
require.NoError(t, err, "while creating a Verity encoder")
// Write pseudorandom data both to the Verity-protected data device, and