1 package dmverity
2
3 import (
4 "bufio"
5 "bytes"
6 "crypto/rand"
7 "crypto/sha256"
8 "encoding/binary"
9 "fmt"
10 "io"
11 "os"
12
13 "github.com/pkg/errors"
14
15 "github.com/Microsoft/hcsshim/ext4/internal/compactext4"
16 "github.com/Microsoft/hcsshim/internal/memory"
17 )
18
19 const (
20 blockSize = compactext4.BlockSize
21
22 MerkleTreeBufioSize = memory.MiB
23
24 RecommendedVHDSizeGB = 128 * memory.GiB
25
26 VeritySignature = "verity"
27 )
28
29 var (
30 salt = bytes.Repeat([]byte{0}, 32)
31 sbSize = binary.Size(dmveritySuperblock{})
32 )
33
34 var (
35 ErrSuperBlockReadFailure = errors.New("failed to read dm-verity super block")
36 ErrSuperBlockParseFailure = errors.New("failed to parse dm-verity super block")
37 ErrRootHashReadFailure = errors.New("failed to read dm-verity root hash")
38 ErrNotVeritySuperBlock = errors.New("invalid dm-verity super-block signature")
39 )
40
41 type dmveritySuperblock struct {
42
43 Signature [8]byte
44
45 Version uint32
46
47 HashType uint32
48
49 UUID [16]byte
50
51 Algorithm [32]byte
52
53 DataBlockSize uint32
54
55 HashBlockSize uint32
56
57 DataBlocks uint64
58
59 SaltSize uint16
60
61 _ [6]byte
62
63 Salt [256]byte
64
65 _ [168]byte
66 }
67
68
69 type VerityInfo struct {
70
71 HashOffsetInBlocks int64
72
73 SuperBlock bool
74 RootDigest string
75 Salt string
76 Algorithm string
77 DataBlockSize uint32
78 HashBlockSize uint32
79 DataBlocks uint64
80 Version uint32
81 }
82
83
84 func MerkleTree(r io.Reader) ([]byte, error) {
85 layers := make([][]byte, 0)
86 currentLevel := r
87
88 for {
89 nextLevel := bytes.NewBuffer(make([]byte, 0))
90 for {
91 block := make([]byte, blockSize)
92 if _, err := io.ReadFull(currentLevel, block); err != nil {
93 if err == io.EOF {
94 break
95 }
96 return nil, errors.Wrap(err, "failed to read data block")
97 }
98 h := hash2(salt, block)
99 nextLevel.Write(h)
100 }
101
102 if nextLevel.Len()%blockSize != 0 {
103 padding := bytes.Repeat([]byte{0}, blockSize-(nextLevel.Len()%blockSize))
104 nextLevel.Write(padding)
105 }
106
107 layers = append(layers, nextLevel.Bytes())
108 currentLevel = bufio.NewReaderSize(nextLevel, MerkleTreeBufioSize)
109
110
111 if nextLevel.Len() == blockSize {
112 break
113 }
114 }
115
116 tree := bytes.NewBuffer(make([]byte, 0))
117 for i := len(layers) - 1; i >= 0; i-- {
118 if _, err := tree.Write(layers[i]); err != nil {
119 return nil, errors.Wrap(err, "failed to write merkle tree")
120 }
121 }
122
123 return tree.Bytes(), nil
124 }
125
126
127 func RootHash(tree []byte) []byte {
128 return hash2(salt, tree[:blockSize])
129 }
130
131
132
133 func NewDMVeritySuperblock(size uint64) *dmveritySuperblock {
134 superblock := &dmveritySuperblock{
135 Version: 1,
136 HashType: 1,
137 UUID: generateUUID(),
138 DataBlockSize: blockSize,
139 HashBlockSize: blockSize,
140 DataBlocks: size / blockSize,
141 SaltSize: uint16(len(salt)),
142 }
143
144 copy(superblock.Signature[:], VeritySignature)
145 copy(superblock.Algorithm[:], "sha256")
146 copy(superblock.Salt[:], salt)
147
148 return superblock
149 }
150
151 func hash2(a, b []byte) []byte {
152 h := sha256.New()
153 h.Write(append(a, b...))
154 return h.Sum(nil)
155 }
156
157 func generateUUID() [16]byte {
158 res := [16]byte{}
159 if _, err := rand.Read(res[:]); err != nil {
160 panic(err)
161 }
162 return res
163 }
164
165
166 func ReadDMVerityInfo(vhdPath string, offsetInBytes int64) (*VerityInfo, error) {
167 vhd, err := os.OpenFile(vhdPath, os.O_RDONLY, 0)
168 if err != nil {
169 return nil, err
170 }
171 defer vhd.Close()
172
173
174 if s, err := vhd.Seek(offsetInBytes, io.SeekStart); err != nil || s != offsetInBytes {
175 if err != nil {
176 return nil, errors.Wrap(err, "failed to seek dm-verity super block")
177 }
178 return nil, errors.Errorf("failed to seek dm-verity super block: expected bytes=%d, actual=%d", offsetInBytes, s)
179 }
180
181 block := make([]byte, blockSize)
182 if s, err := vhd.Read(block); err != nil || s != blockSize {
183 if err != nil {
184 return nil, errors.Wrapf(err, "%s", ErrSuperBlockReadFailure)
185 }
186 return nil, errors.Wrapf(ErrSuperBlockReadFailure, "unexpected bytes read: expected=%d, actual=%d", blockSize, s)
187 }
188
189 dmvSB := &dmveritySuperblock{}
190 b := bytes.NewBuffer(block)
191 if err := binary.Read(b, binary.LittleEndian, dmvSB); err != nil {
192 return nil, errors.Wrapf(err, "%s", ErrSuperBlockParseFailure)
193 }
194 if string(bytes.Trim(dmvSB.Signature[:], "\x00")[:]) != VeritySignature {
195 return nil, ErrNotVeritySuperBlock
196 }
197
198 if s, err := vhd.Read(block); err != nil || s != blockSize {
199 if err != nil {
200 return nil, errors.Wrapf(err, "%s", ErrRootHashReadFailure)
201 }
202 return nil, errors.Wrapf(ErrRootHashReadFailure, "unexpected bytes read: expected=%d, actual=%d", blockSize, s)
203 }
204 rootHash := hash2(dmvSB.Salt[:dmvSB.SaltSize], block)
205 return &VerityInfo{
206 RootDigest: fmt.Sprintf("%x", rootHash),
207 Algorithm: string(bytes.Trim(dmvSB.Algorithm[:], "\x00")),
208 Salt: fmt.Sprintf("%x", dmvSB.Salt[:dmvSB.SaltSize]),
209 HashOffsetInBlocks: int64(dmvSB.DataBlocks),
210 SuperBlock: true,
211 DataBlocks: dmvSB.DataBlocks,
212 DataBlockSize: dmvSB.DataBlockSize,
213 HashBlockSize: blockSize,
214 Version: dmvSB.Version,
215 }, nil
216 }
217
218
219
220 func ComputeAndWriteHashDevice(r io.ReadSeeker, w io.WriteSeeker) error {
221 if _, err := r.Seek(0, io.SeekStart); err != nil {
222 return err
223 }
224 tree, err := MerkleTree(r)
225 if err != nil {
226 return errors.Wrap(err, "failed to build merkle tree")
227 }
228
229 devSize, err := r.Seek(0, io.SeekEnd)
230 if err != nil {
231 return err
232 }
233 dmVeritySB := NewDMVeritySuperblock(uint64(devSize))
234 if _, err := w.Seek(0, io.SeekEnd); err != nil {
235 return err
236 }
237 if err := binary.Write(w, binary.LittleEndian, dmVeritySB); err != nil {
238 return errors.Wrap(err, "failed to write dm-verity super-block")
239 }
240
241 padding := bytes.Repeat([]byte{0}, blockSize-(sbSize%blockSize))
242 if _, err = w.Write(padding); err != nil {
243 return err
244 }
245
246 if _, err := w.Write(tree); err != nil {
247 return errors.Wrap(err, "failed to write merkle tree")
248 }
249 return nil
250 }
251
View as plain text