treewide: move build helper to more fitting places
Change-Id: I3d0cfe9283222d403ae369ec9db09201ad511e15
Reviewed-on: https://review.monogon.dev/c/monogon/+/3327
Reviewed-by: Serge Bazanski <serge@monogon.tech>
Tested-by: Jenkins CI
diff --git a/osbase/build/mkverity/BUILD.bazel b/osbase/build/mkverity/BUILD.bazel
new file mode 100644
index 0000000..04a8a54
--- /dev/null
+++ b/osbase/build/mkverity/BUILD.bazel
@@ -0,0 +1,19 @@
+load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library")
+
+go_binary(
+ name = "mkverity",
+ embed = [":mkverity_lib"],
+ visibility = [
+ "//metropolis/installer/test/testos:__pkg__",
+ "//metropolis/node:__pkg__",
+ "//metropolis/node/core/update/e2e/testos:__pkg__",
+ ],
+)
+
+go_library(
+ name = "mkverity_lib",
+ srcs = ["mkverity.go"],
+ importpath = "source.monogon.dev/osbase/build/mkverity",
+ visibility = ["//visibility:private"],
+ deps = ["//osbase/verity"],
+)
diff --git a/osbase/build/mkverity/mkverity.go b/osbase/build/mkverity/mkverity.go
new file mode 100644
index 0000000..ff2807b
--- /dev/null
+++ b/osbase/build/mkverity/mkverity.go
@@ -0,0 +1,152 @@
+// Copyright 2020 The Monogon Project Authors.
+//
+// SPDX-License-Identifier: Apache-2.0
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// This package implements a command line tool that creates dm-verity hash
+// images at a selected path, given an existing data image. The tool
+// outputs a Verity mapping table on success.
+//
+// For more information, see:
+// - source.monogon.dev/osbase/verity
+// - https://gitlab.com/cryptsetup/cryptsetup/wikis/DMVerity
+package main
+
+import (
+ "flag"
+ "fmt"
+ "io"
+ "log"
+ "os"
+
+ "source.monogon.dev/osbase/verity"
+)
+
+// createImage creates a dm-verity target image by combining the input image
+// with Verity metadata. Contents of the data image are copied to the output
+// image. Then, the same contents are verity-encoded and appended to the
+// output image. The verity superblock is written only if wsb is true. It
+// returns either a dm-verity target table, or an error.
+func createImage(dataImagePath, outputImagePath string, wsb bool) (*verity.MappingTable, error) {
+ // Hardcode both the data block size and the hash block size as 4096 bytes.
+ bs := uint32(4096)
+
+ // Open the data image for reading.
+ dataImage, err := os.Open(dataImagePath)
+ if err != nil {
+ return nil, fmt.Errorf("while opening the data image: %w", err)
+ }
+ defer dataImage.Close()
+
+ // Check that the data image is well-formed.
+ ds, err := dataImage.Stat()
+ if err != nil {
+ return nil, fmt.Errorf("while stat-ing the data image: %w", err)
+ }
+ if !ds.Mode().IsRegular() {
+ return nil, fmt.Errorf("the data image must be a regular file")
+ }
+ if ds.Size()%int64(bs) != 0 {
+ return nil, fmt.Errorf("the data image must end on a %d-byte block boundary", bs)
+ }
+
+ // Create an empty hash image file.
+ outputImage, err := os.OpenFile(outputImagePath, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0644)
+ if err != nil {
+ return nil, fmt.Errorf("while opening the hash image for writing: %w", err)
+ }
+ defer outputImage.Close()
+
+ // Copy the input data into the output file, then rewind dataImage to be read
+ // again by the Verity encoder.
+ _, err = io.Copy(outputImage, dataImage)
+ if err != nil {
+ return nil, err
+ }
+ _, err = dataImage.Seek(0, os.SEEK_SET)
+ if err != nil {
+ return nil, err
+ }
+
+ // Write outputImage contents. Start with initializing a verity encoder,
+ // seting outputImage as its output.
+ v, err := verity.NewEncoder(outputImage, bs, bs, wsb)
+ if err != nil {
+ return nil, fmt.Errorf("while initializing a verity encoder: %w", err)
+ }
+ // Hash the contents of dataImage, block by block.
+ _, err = io.Copy(v, dataImage)
+ if err != nil {
+ return nil, fmt.Errorf("while reading the data image: %w", err)
+ }
+ // The resulting hash tree won't be written until Close is called.
+ err = v.Close()
+ if err != nil {
+ return nil, fmt.Errorf("while writing the hash image: %w", err)
+ }
+
+ // Return an encoder-generated verity mapping table, containing the salt and
+ // the root hash. First, calculate the starting hash block by dividing the
+ // data image size by the encoder data block size.
+ hashStart := ds.Size() / int64(bs)
+ mt, err := v.MappingTable(dataImagePath, outputImagePath, hashStart)
+ if err != nil {
+ return nil, fmt.Errorf("while querying for the mapping table: %w", err)
+ }
+ return mt, nil
+}
+
+var (
+ input = flag.String("input", "", "input disk image (required)")
+ output = flag.String("output", "", "output disk image with Verity metadata appended (required)")
+ dataDeviceAlias = flag.String("data_alias", "", "data device alias used in the mapping table")
+ hashDeviceAlias = flag.String("hash_alias", "", "hash device alias used in the mapping table")
+ table = flag.String("table", "", "a file the mapping table will be saved to; disables stdout")
+)
+
+func main() {
+ flag.Parse()
+
+ // Ensure that required parameters were provided before continuing.
+ if *input == "" {
+ log.Fatalf("-input must be set.")
+ }
+ if *output == "" {
+ log.Fatalf("-output must be set.")
+ }
+
+ // Build the image.
+ mt, err := createImage(*input, *output, false)
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ // Patch the device names, if alternatives were provided.
+ if *dataDeviceAlias != "" {
+ mt.DataDevicePath = *dataDeviceAlias
+ }
+ if *hashDeviceAlias != "" {
+ mt.HashDevicePath = *hashDeviceAlias
+ }
+
+ // Print a DeviceMapper target table, or save it to a file, if the table
+ // parameter was specified.
+ if *table != "" {
+ if err := os.WriteFile(*table, []byte(mt.String()), 0644); err != nil {
+ log.Fatal(err)
+ }
+ } else {
+ fmt.Println(mt)
+ }
+}