blob: 82db0da2a8d9b3469cf1f5b0988994e3505ea166 [file] [log] [blame]
Tim Windelschmidt6d33a432025-02-04 14:34:25 +01001// Copyright The Monogon Project Authors.
Mateusz Zalega356b8962021-08-10 17:27:15 +02002// SPDX-License-Identifier: Apache-2.0
Mateusz Zalega356b8962021-08-10 17:27:15 +02003
4// This package implements a command line tool that creates dm-verity hash
5// images at a selected path, given an existing data image. The tool
6// outputs a Verity mapping table on success.
7//
8// For more information, see:
Tim Windelschmidt9f21f532024-05-07 15:14:20 +02009// - source.monogon.dev/osbase/verity
Mateusz Zalega356b8962021-08-10 17:27:15 +020010// - https://gitlab.com/cryptsetup/cryptsetup/wikis/DMVerity
11package main
12
13import (
Mateusz Zalegaba1da9d2022-01-25 19:12:02 +010014 "flag"
Mateusz Zalega356b8962021-08-10 17:27:15 +020015 "fmt"
16 "io"
17 "log"
18 "os"
19
Tim Windelschmidt9f21f532024-05-07 15:14:20 +020020 "source.monogon.dev/osbase/verity"
Mateusz Zalega356b8962021-08-10 17:27:15 +020021)
22
Mateusz Zalegaba1da9d2022-01-25 19:12:02 +010023// createImage creates a dm-verity target image by combining the input image
24// with Verity metadata. Contents of the data image are copied to the output
25// image. Then, the same contents are verity-encoded and appended to the
26// output image. The verity superblock is written only if wsb is true. It
27// returns either a dm-verity target table, or an error.
28func createImage(dataImagePath, outputImagePath string, wsb bool) (*verity.MappingTable, error) {
29 // Hardcode both the data block size and the hash block size as 4096 bytes.
30 bs := uint32(4096)
31
Mateusz Zalega356b8962021-08-10 17:27:15 +020032 // Open the data image for reading.
33 dataImage, err := os.Open(dataImagePath)
34 if err != nil {
35 return nil, fmt.Errorf("while opening the data image: %w", err)
36 }
37 defer dataImage.Close()
Mateusz Zalegaba1da9d2022-01-25 19:12:02 +010038
39 // Check that the data image is well-formed.
40 ds, err := dataImage.Stat()
41 if err != nil {
42 return nil, fmt.Errorf("while stat-ing the data image: %w", err)
43 }
44 if !ds.Mode().IsRegular() {
45 return nil, fmt.Errorf("the data image must be a regular file")
46 }
47 if ds.Size()%int64(bs) != 0 {
Tim Windelschmidt73e98822024-04-18 23:13:49 +020048 return nil, fmt.Errorf("the data image must end on a %d-byte block boundary", bs)
Mateusz Zalegaba1da9d2022-01-25 19:12:02 +010049 }
50
Mateusz Zalega356b8962021-08-10 17:27:15 +020051 // Create an empty hash image file.
Mateusz Zalegaba1da9d2022-01-25 19:12:02 +010052 outputImage, err := os.OpenFile(outputImagePath, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0644)
Mateusz Zalega356b8962021-08-10 17:27:15 +020053 if err != nil {
54 return nil, fmt.Errorf("while opening the hash image for writing: %w", err)
55 }
Mateusz Zalegaba1da9d2022-01-25 19:12:02 +010056 defer outputImage.Close()
Mateusz Zalega356b8962021-08-10 17:27:15 +020057
Mateusz Zalegaba1da9d2022-01-25 19:12:02 +010058 // Copy the input data into the output file, then rewind dataImage to be read
59 // again by the Verity encoder.
60 _, err = io.Copy(outputImage, dataImage)
61 if err != nil {
62 return nil, err
63 }
64 _, err = dataImage.Seek(0, os.SEEK_SET)
65 if err != nil {
66 return nil, err
67 }
68
69 // Write outputImage contents. Start with initializing a verity encoder,
70 // seting outputImage as its output.
71 v, err := verity.NewEncoder(outputImage, bs, bs, wsb)
Mateusz Zalega356b8962021-08-10 17:27:15 +020072 if err != nil {
73 return nil, fmt.Errorf("while initializing a verity encoder: %w", err)
74 }
75 // Hash the contents of dataImage, block by block.
76 _, err = io.Copy(v, dataImage)
77 if err != nil {
78 return nil, fmt.Errorf("while reading the data image: %w", err)
79 }
80 // The resulting hash tree won't be written until Close is called.
81 err = v.Close()
82 if err != nil {
83 return nil, fmt.Errorf("while writing the hash image: %w", err)
84 }
85
Mateusz Zalegaba1da9d2022-01-25 19:12:02 +010086 // Return an encoder-generated verity mapping table, containing the salt and
87 // the root hash. First, calculate the starting hash block by dividing the
88 // data image size by the encoder data block size.
89 hashStart := ds.Size() / int64(bs)
90 mt, err := v.MappingTable(dataImagePath, outputImagePath, hashStart)
Mateusz Zalega356b8962021-08-10 17:27:15 +020091 if err != nil {
92 return nil, fmt.Errorf("while querying for the mapping table: %w", err)
93 }
94 return mt, nil
95}
96
Mateusz Zalegaba1da9d2022-01-25 19:12:02 +010097var (
98 input = flag.String("input", "", "input disk image (required)")
99 output = flag.String("output", "", "output disk image with Verity metadata appended (required)")
100 dataDeviceAlias = flag.String("data_alias", "", "data device alias used in the mapping table")
101 hashDeviceAlias = flag.String("hash_alias", "", "hash device alias used in the mapping table")
102 table = flag.String("table", "", "a file the mapping table will be saved to; disables stdout")
103)
Mateusz Zalega356b8962021-08-10 17:27:15 +0200104
105func main() {
Mateusz Zalegaba1da9d2022-01-25 19:12:02 +0100106 flag.Parse()
Mateusz Zalega356b8962021-08-10 17:27:15 +0200107
Mateusz Zalegaba1da9d2022-01-25 19:12:02 +0100108 // Ensure that required parameters were provided before continuing.
109 if *input == "" {
110 log.Fatalf("-input must be set.")
111 }
112 if *output == "" {
113 log.Fatalf("-output must be set.")
114 }
115
116 // Build the image.
117 mt, err := createImage(*input, *output, false)
Mateusz Zalega356b8962021-08-10 17:27:15 +0200118 if err != nil {
119 log.Fatal(err)
120 }
Mateusz Zalegaba1da9d2022-01-25 19:12:02 +0100121
122 // Patch the device names, if alternatives were provided.
123 if *dataDeviceAlias != "" {
124 mt.DataDevicePath = *dataDeviceAlias
125 }
126 if *hashDeviceAlias != "" {
127 mt.HashDevicePath = *hashDeviceAlias
128 }
129
130 // Print a DeviceMapper target table, or save it to a file, if the table
131 // parameter was specified.
132 if *table != "" {
133 if err := os.WriteFile(*table, []byte(mt.String()), 0644); err != nil {
134 log.Fatal(err)
135 }
136 } else {
137 fmt.Println(mt)
138 }
Mateusz Zalega356b8962021-08-10 17:27:15 +0200139}