github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/merkletree/merkletree_test.go (about)

     1  // Copyright 2020 The gVisor Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package merkletree
    16  
    17  import (
    18  	"bytes"
    19  	"errors"
    20  	"fmt"
    21  	"io"
    22  	"math/rand"
    23  	"testing"
    24  	"time"
    25  
    26  	"github.com/SagerNet/gvisor/pkg/abi/linux"
    27  
    28  	"github.com/SagerNet/gvisor/pkg/hostarch"
    29  )
    30  
    31  func TestLayout(t *testing.T) {
    32  	testCases := []struct {
    33  		name                  string
    34  		dataSize              int64
    35  		hashAlgorithms        int
    36  		dataAndTreeInSameFile bool
    37  		expectedDigestSize    int64
    38  		expectedLevelOffset   []int64
    39  	}{
    40  		{
    41  			name:                  "SmallSizeSHA256SeparateFile",
    42  			dataSize:              100,
    43  			hashAlgorithms:        linux.FS_VERITY_HASH_ALG_SHA256,
    44  			dataAndTreeInSameFile: false,
    45  			expectedDigestSize:    32,
    46  			expectedLevelOffset:   []int64{0},
    47  		},
    48  		{
    49  			name:                  "SmallSizeSHA512SeparateFile",
    50  			dataSize:              100,
    51  			hashAlgorithms:        linux.FS_VERITY_HASH_ALG_SHA512,
    52  			dataAndTreeInSameFile: false,
    53  			expectedDigestSize:    64,
    54  			expectedLevelOffset:   []int64{0},
    55  		},
    56  		{
    57  			name:                  "SmallSizeSHA256SameFile",
    58  			dataSize:              100,
    59  			hashAlgorithms:        linux.FS_VERITY_HASH_ALG_SHA256,
    60  			dataAndTreeInSameFile: true,
    61  			expectedDigestSize:    32,
    62  			expectedLevelOffset:   []int64{hostarch.PageSize},
    63  		},
    64  		{
    65  			name:                  "SmallSizeSHA512SameFile",
    66  			dataSize:              100,
    67  			hashAlgorithms:        linux.FS_VERITY_HASH_ALG_SHA512,
    68  			dataAndTreeInSameFile: true,
    69  			expectedDigestSize:    64,
    70  			expectedLevelOffset:   []int64{hostarch.PageSize},
    71  		},
    72  		{
    73  			name:                  "MiddleSizeSHA256SeparateFile",
    74  			dataSize:              1000000,
    75  			hashAlgorithms:        linux.FS_VERITY_HASH_ALG_SHA256,
    76  			dataAndTreeInSameFile: false,
    77  			expectedDigestSize:    32,
    78  			expectedLevelOffset:   []int64{0, 2 * hostarch.PageSize, 3 * hostarch.PageSize},
    79  		},
    80  		{
    81  			name:                  "MiddleSizeSHA512SeparateFile",
    82  			dataSize:              1000000,
    83  			hashAlgorithms:        linux.FS_VERITY_HASH_ALG_SHA512,
    84  			dataAndTreeInSameFile: false,
    85  			expectedDigestSize:    64,
    86  			expectedLevelOffset:   []int64{0, 4 * hostarch.PageSize, 5 * hostarch.PageSize},
    87  		},
    88  		{
    89  			name:                  "MiddleSizeSHA256SameFile",
    90  			dataSize:              1000000,
    91  			hashAlgorithms:        linux.FS_VERITY_HASH_ALG_SHA256,
    92  			dataAndTreeInSameFile: true,
    93  			expectedDigestSize:    32,
    94  			expectedLevelOffset:   []int64{245 * hostarch.PageSize, 247 * hostarch.PageSize, 248 * hostarch.PageSize},
    95  		},
    96  		{
    97  			name:                  "MiddleSizeSHA512SameFile",
    98  			dataSize:              1000000,
    99  			hashAlgorithms:        linux.FS_VERITY_HASH_ALG_SHA512,
   100  			dataAndTreeInSameFile: true,
   101  			expectedDigestSize:    64,
   102  			expectedLevelOffset:   []int64{245 * hostarch.PageSize, 249 * hostarch.PageSize, 250 * hostarch.PageSize},
   103  		},
   104  		{
   105  			name:                  "LargeSizeSHA256SeparateFile",
   106  			dataSize:              4096 * int64(hostarch.PageSize),
   107  			hashAlgorithms:        linux.FS_VERITY_HASH_ALG_SHA256,
   108  			dataAndTreeInSameFile: false,
   109  			expectedDigestSize:    32,
   110  			expectedLevelOffset:   []int64{0, 32 * hostarch.PageSize, 33 * hostarch.PageSize},
   111  		},
   112  		{
   113  			name:                  "LargeSizeSHA512SeparateFile",
   114  			dataSize:              4096 * int64(hostarch.PageSize),
   115  			hashAlgorithms:        linux.FS_VERITY_HASH_ALG_SHA512,
   116  			dataAndTreeInSameFile: false,
   117  			expectedDigestSize:    64,
   118  			expectedLevelOffset:   []int64{0, 64 * hostarch.PageSize, 65 * hostarch.PageSize},
   119  		},
   120  		{
   121  			name:                  "LargeSizeSHA256SameFile",
   122  			dataSize:              4096 * int64(hostarch.PageSize),
   123  			hashAlgorithms:        linux.FS_VERITY_HASH_ALG_SHA256,
   124  			dataAndTreeInSameFile: true,
   125  			expectedDigestSize:    32,
   126  			expectedLevelOffset:   []int64{4096 * hostarch.PageSize, 4128 * hostarch.PageSize, 4129 * hostarch.PageSize},
   127  		},
   128  		{
   129  			name:                  "LargeSizeSHA512SameFile",
   130  			dataSize:              4096 * int64(hostarch.PageSize),
   131  			hashAlgorithms:        linux.FS_VERITY_HASH_ALG_SHA512,
   132  			dataAndTreeInSameFile: true,
   133  			expectedDigestSize:    64,
   134  			expectedLevelOffset:   []int64{4096 * hostarch.PageSize, 4160 * hostarch.PageSize, 4161 * hostarch.PageSize},
   135  		},
   136  	}
   137  
   138  	for _, tc := range testCases {
   139  		t.Run(tc.name, func(t *testing.T) {
   140  			l, err := InitLayout(tc.dataSize, tc.hashAlgorithms, tc.dataAndTreeInSameFile)
   141  			if err != nil {
   142  				t.Fatalf("Failed to InitLayout: %v", err)
   143  			}
   144  			if l.blockSize != int64(hostarch.PageSize) {
   145  				t.Errorf("Got blockSize %d, want %d", l.blockSize, hostarch.PageSize)
   146  			}
   147  			if l.digestSize != tc.expectedDigestSize {
   148  				t.Errorf("Got digestSize %d, want %d", l.digestSize, sha256DigestSize)
   149  			}
   150  			if l.numLevels() != len(tc.expectedLevelOffset) {
   151  				t.Errorf("Got levels %d, want %d", l.numLevels(), len(tc.expectedLevelOffset))
   152  			}
   153  			for i := 0; i < l.numLevels() && i < len(tc.expectedLevelOffset); i++ {
   154  				if l.levelOffset[i] != tc.expectedLevelOffset[i] {
   155  					t.Errorf("Got levelStart[%d] %d, want %d", i, l.levelOffset[i], tc.expectedLevelOffset[i])
   156  				}
   157  			}
   158  		})
   159  	}
   160  }
   161  
   162  const (
   163  	defaultName          = "merkle_test"
   164  	defaultMode          = 0644
   165  	defaultUID           = 0
   166  	defaultGID           = 0
   167  	defaultSymlinkPath   = "merkle_test_link"
   168  	defaultHashAlgorithm = linux.FS_VERITY_HASH_ALG_SHA256
   169  )
   170  
   171  // bytesReadWriter is used to read from/write to/seek in a byte array. Unlike
   172  // bytes.Buffer, it keeps the whole buffer during read so that it can be reused.
   173  type bytesReadWriter struct {
   174  	// bytes contains the underlying byte array.
   175  	bytes []byte
   176  	// readPos is the currently location for Read. Write always appends to
   177  	// the end of the array.
   178  	readPos int
   179  }
   180  
   181  func (brw *bytesReadWriter) Write(p []byte) (int, error) {
   182  	brw.bytes = append(brw.bytes, p...)
   183  	return len(p), nil
   184  }
   185  
   186  func (brw *bytesReadWriter) ReadAt(p []byte, off int64) (int, error) {
   187  	bytesRead := copy(p, brw.bytes[off:])
   188  	if bytesRead == 0 {
   189  		return bytesRead, io.EOF
   190  	}
   191  	return bytesRead, nil
   192  }
   193  
   194  func TestGenerate(t *testing.T) {
   195  	// The input data has size dataSize. It starts with the data in startWith,
   196  	// and all other bytes are zeroes.
   197  	testCases := []struct {
   198  		name                  string
   199  		data                  []byte
   200  		hashAlgorithms        int
   201  		dataAndTreeInSameFile bool
   202  		expectedHash          []byte
   203  	}{
   204  		{
   205  			name:                  "OnePageZeroesSHA256SeparateFile",
   206  			data:                  bytes.Repeat([]byte{0}, hostarch.PageSize),
   207  			hashAlgorithms:        linux.FS_VERITY_HASH_ALG_SHA256,
   208  			dataAndTreeInSameFile: false,
   209  			expectedHash:          []byte{78, 38, 225, 107, 61, 246, 26, 6, 71, 163, 254, 97, 112, 200, 87, 232, 190, 87, 231, 160, 119, 124, 61, 229, 49, 126, 90, 223, 134, 51, 77, 182},
   210  		},
   211  		{
   212  			name:                  "OnePageZeroesSHA256SameFile",
   213  			data:                  bytes.Repeat([]byte{0}, hostarch.PageSize),
   214  			hashAlgorithms:        linux.FS_VERITY_HASH_ALG_SHA256,
   215  			dataAndTreeInSameFile: true,
   216  			expectedHash:          []byte{78, 38, 225, 107, 61, 246, 26, 6, 71, 163, 254, 97, 112, 200, 87, 232, 190, 87, 231, 160, 119, 124, 61, 229, 49, 126, 90, 223, 134, 51, 77, 182},
   217  		},
   218  		{
   219  			name:                  "OnePageZeroesSHA512SeparateFile",
   220  			data:                  bytes.Repeat([]byte{0}, hostarch.PageSize),
   221  			hashAlgorithms:        linux.FS_VERITY_HASH_ALG_SHA512,
   222  			dataAndTreeInSameFile: false,
   223  			expectedHash:          []byte{221, 45, 182, 132, 61, 212, 227, 145, 150, 131, 98, 221, 195, 5, 89, 21, 188, 36, 250, 101, 85, 78, 197, 253, 193, 23, 74, 219, 28, 108, 77, 47, 65, 79, 123, 144, 50, 245, 109, 72, 71, 80, 24, 77, 158, 95, 242, 185, 109, 163, 105, 183, 67, 106, 55, 194, 223, 46, 12, 242, 165, 203, 172, 254},
   224  		},
   225  		{
   226  			name:                  "OnePageZeroesSHA512SameFile",
   227  			data:                  bytes.Repeat([]byte{0}, hostarch.PageSize),
   228  			hashAlgorithms:        linux.FS_VERITY_HASH_ALG_SHA512,
   229  			dataAndTreeInSameFile: true,
   230  			expectedHash:          []byte{221, 45, 182, 132, 61, 212, 227, 145, 150, 131, 98, 221, 195, 5, 89, 21, 188, 36, 250, 101, 85, 78, 197, 253, 193, 23, 74, 219, 28, 108, 77, 47, 65, 79, 123, 144, 50, 245, 109, 72, 71, 80, 24, 77, 158, 95, 242, 185, 109, 163, 105, 183, 67, 106, 55, 194, 223, 46, 12, 242, 165, 203, 172, 254},
   231  		},
   232  		{
   233  			name:                  "MultiplePageZeroesSHA256SeparateFile",
   234  			data:                  bytes.Repeat([]byte{0}, 128*hostarch.PageSize+1),
   235  			hashAlgorithms:        linux.FS_VERITY_HASH_ALG_SHA256,
   236  			dataAndTreeInSameFile: false,
   237  			expectedHash:          []byte{131, 122, 73, 143, 4, 202, 193, 156, 218, 169, 196, 223, 70, 100, 117, 191, 241, 113, 134, 11, 229, 231, 105, 157, 156, 0, 66, 213, 122, 145, 174, 8},
   238  		},
   239  		{
   240  			name:                  "MultiplePageZeroesSHA256SameFile",
   241  			data:                  bytes.Repeat([]byte{0}, 128*hostarch.PageSize+1),
   242  			hashAlgorithms:        linux.FS_VERITY_HASH_ALG_SHA256,
   243  			dataAndTreeInSameFile: true,
   244  			expectedHash:          []byte{131, 122, 73, 143, 4, 202, 193, 156, 218, 169, 196, 223, 70, 100, 117, 191, 241, 113, 134, 11, 229, 231, 105, 157, 156, 0, 66, 213, 122, 145, 174, 8},
   245  		},
   246  		{
   247  			name:                  "MultiplePageZeroesSHA512SeparateFile",
   248  			data:                  bytes.Repeat([]byte{0}, 128*hostarch.PageSize+1),
   249  			hashAlgorithms:        linux.FS_VERITY_HASH_ALG_SHA512,
   250  			dataAndTreeInSameFile: false,
   251  			expectedHash:          []byte{211, 48, 232, 110, 240, 51, 99, 241, 123, 138, 42, 76, 94, 86, 59, 200, 3, 246, 137, 148, 189, 226, 111, 103, 146, 29, 12, 218, 40, 182, 33, 99, 193, 163, 238, 26, 184, 13, 165, 187, 68, 173, 139, 9, 208, 59, 0, 192, 180, 50, 221, 35, 43, 119, 194, 16, 64, 84, 116, 63, 158, 195, 194, 226},
   252  		},
   253  		{
   254  			name:                  "MultiplePageZeroesSHA512SameFile",
   255  			data:                  bytes.Repeat([]byte{0}, 128*hostarch.PageSize+1),
   256  			hashAlgorithms:        linux.FS_VERITY_HASH_ALG_SHA512,
   257  			dataAndTreeInSameFile: true,
   258  			expectedHash:          []byte{211, 48, 232, 110, 240, 51, 99, 241, 123, 138, 42, 76, 94, 86, 59, 200, 3, 246, 137, 148, 189, 226, 111, 103, 146, 29, 12, 218, 40, 182, 33, 99, 193, 163, 238, 26, 184, 13, 165, 187, 68, 173, 139, 9, 208, 59, 0, 192, 180, 50, 221, 35, 43, 119, 194, 16, 64, 84, 116, 63, 158, 195, 194, 226},
   259  		},
   260  		{
   261  			name:                  "SingleASHA256SeparateFile",
   262  			data:                  []byte{'a'},
   263  			hashAlgorithms:        linux.FS_VERITY_HASH_ALG_SHA256,
   264  			dataAndTreeInSameFile: false,
   265  			expectedHash:          []byte{26, 47, 238, 138, 235, 244, 140, 231, 129, 240, 155, 252, 219, 44, 46, 72, 57, 249, 139, 88, 132, 238, 86, 108, 181, 115, 96, 72, 99, 210, 134, 47},
   266  		},
   267  		{
   268  			name:                  "SingleASHA256SameFile",
   269  			data:                  []byte{'a'},
   270  			hashAlgorithms:        linux.FS_VERITY_HASH_ALG_SHA256,
   271  			dataAndTreeInSameFile: true,
   272  			expectedHash:          []byte{26, 47, 238, 138, 235, 244, 140, 231, 129, 240, 155, 252, 219, 44, 46, 72, 57, 249, 139, 88, 132, 238, 86, 108, 181, 115, 96, 72, 99, 210, 134, 47},
   273  		},
   274  		{
   275  			name:                  "SingleASHA512SeparateFile",
   276  			data:                  []byte{'a'},
   277  			hashAlgorithms:        linux.FS_VERITY_HASH_ALG_SHA512,
   278  			dataAndTreeInSameFile: false,
   279  			expectedHash:          []byte{44, 30, 224, 12, 102, 119, 163, 171, 119, 175, 212, 121, 231, 188, 125, 171, 79, 28, 144, 234, 75, 122, 44, 75, 15, 101, 173, 92, 233, 109, 234, 60, 173, 148, 125, 85, 94, 234, 95, 91, 16, 196, 88, 175, 23, 129, 226, 110, 24, 238, 5, 49, 186, 128, 72, 188, 193, 180, 207, 193, 203, 119, 40, 191},
   280  		},
   281  		{
   282  			name:                  "SingleASHA512SameFile",
   283  			data:                  []byte{'a'},
   284  			hashAlgorithms:        linux.FS_VERITY_HASH_ALG_SHA512,
   285  			dataAndTreeInSameFile: true,
   286  			expectedHash:          []byte{44, 30, 224, 12, 102, 119, 163, 171, 119, 175, 212, 121, 231, 188, 125, 171, 79, 28, 144, 234, 75, 122, 44, 75, 15, 101, 173, 92, 233, 109, 234, 60, 173, 148, 125, 85, 94, 234, 95, 91, 16, 196, 88, 175, 23, 129, 226, 110, 24, 238, 5, 49, 186, 128, 72, 188, 193, 180, 207, 193, 203, 119, 40, 191},
   287  		},
   288  		{
   289  			name:                  "OnePageASHA256SeparateFile",
   290  			data:                  bytes.Repeat([]byte{'a'}, hostarch.PageSize),
   291  			hashAlgorithms:        linux.FS_VERITY_HASH_ALG_SHA256,
   292  			dataAndTreeInSameFile: false,
   293  			expectedHash:          []byte{166, 254, 83, 46, 241, 111, 18, 47, 79, 6, 181, 197, 176, 143, 211, 204, 53, 5, 245, 134, 172, 95, 97, 131, 236, 132, 197, 138, 123, 78, 43, 13},
   294  		},
   295  		{
   296  			name:                  "OnePageASHA256SameFile",
   297  			data:                  bytes.Repeat([]byte{'a'}, hostarch.PageSize),
   298  			hashAlgorithms:        linux.FS_VERITY_HASH_ALG_SHA256,
   299  			dataAndTreeInSameFile: true,
   300  			expectedHash:          []byte{166, 254, 83, 46, 241, 111, 18, 47, 79, 6, 181, 197, 176, 143, 211, 204, 53, 5, 245, 134, 172, 95, 97, 131, 236, 132, 197, 138, 123, 78, 43, 13},
   301  		},
   302  		{
   303  			name:                  "OnePageASHA512SeparateFile",
   304  			data:                  bytes.Repeat([]byte{'a'}, hostarch.PageSize),
   305  			hashAlgorithms:        linux.FS_VERITY_HASH_ALG_SHA512,
   306  			dataAndTreeInSameFile: false,
   307  			expectedHash:          []byte{23, 69, 6, 79, 39, 232, 90, 246, 62, 55, 4, 229, 47, 36, 230, 24, 233, 47, 55, 36, 26, 139, 196, 78, 242, 12, 194, 77, 109, 81, 151, 188, 63, 201, 127, 235, 81, 214, 91, 200, 19, 232, 240, 14, 197, 1, 99, 224, 18, 213, 203, 242, 44, 102, 25, 62, 90, 189, 106, 107, 129, 61, 115, 39},
   308  		},
   309  		{
   310  			name:                  "OnePageASHA512SameFile",
   311  			data:                  bytes.Repeat([]byte{'a'}, hostarch.PageSize),
   312  			hashAlgorithms:        linux.FS_VERITY_HASH_ALG_SHA512,
   313  			dataAndTreeInSameFile: true,
   314  			expectedHash:          []byte{23, 69, 6, 79, 39, 232, 90, 246, 62, 55, 4, 229, 47, 36, 230, 24, 233, 47, 55, 36, 26, 139, 196, 78, 242, 12, 194, 77, 109, 81, 151, 188, 63, 201, 127, 235, 81, 214, 91, 200, 19, 232, 240, 14, 197, 1, 99, 224, 18, 213, 203, 242, 44, 102, 25, 62, 90, 189, 106, 107, 129, 61, 115, 39},
   315  		},
   316  	}
   317  
   318  	for _, tc := range testCases {
   319  		t.Run(fmt.Sprintf(tc.name), func(t *testing.T) {
   320  			var tree bytesReadWriter
   321  			params := GenerateParams{
   322  				Size:                  int64(len(tc.data)),
   323  				Name:                  defaultName,
   324  				Mode:                  defaultMode,
   325  				UID:                   defaultUID,
   326  				GID:                   defaultGID,
   327  				Children:              []string{},
   328  				HashAlgorithms:        tc.hashAlgorithms,
   329  				TreeReader:            &tree,
   330  				TreeWriter:            &tree,
   331  				DataAndTreeInSameFile: tc.dataAndTreeInSameFile,
   332  			}
   333  			if tc.dataAndTreeInSameFile {
   334  				tree.Write(tc.data)
   335  				params.File = &tree
   336  			} else {
   337  				params.File = &bytesReadWriter{
   338  					bytes: tc.data,
   339  				}
   340  			}
   341  			hash, err := Generate(&params)
   342  			if err != nil {
   343  				t.Fatalf("Got err: %v, want nil", err)
   344  			}
   345  			if !bytes.Equal(hash, tc.expectedHash) {
   346  				t.Errorf("Got hash: %v, want %v", hash, tc.expectedHash)
   347  			}
   348  		})
   349  	}
   350  }
   351  
   352  // prepareVerify generates test data and corresponding Merkle tree, and returns
   353  // the prepared VerifyParams.
   354  // The test data has size dataSize. The data is hashed with hashAlgorithm. The
   355  // portion to be verified is the range [verifyStart, verifyStart + verifySize).
   356  func prepareVerify(t *testing.T, dataSize int64, hashAlgorithm int, dataAndTreeInSameFile, isSymlink bool, verifyStart, verifySize int64, out io.Writer) ([]byte, VerifyParams) {
   357  	t.Helper()
   358  	data := make([]byte, dataSize)
   359  	// Generate random bytes in data.
   360  	rand.Read(data)
   361  
   362  	var tree bytesReadWriter
   363  	genParams := GenerateParams{
   364  		Size:                  int64(len(data)),
   365  		Name:                  defaultName,
   366  		Mode:                  defaultMode,
   367  		UID:                   defaultUID,
   368  		GID:                   defaultGID,
   369  		Children:              []string{},
   370  		HashAlgorithms:        hashAlgorithm,
   371  		TreeReader:            &tree,
   372  		TreeWriter:            &tree,
   373  		DataAndTreeInSameFile: dataAndTreeInSameFile,
   374  	}
   375  	if dataAndTreeInSameFile {
   376  		tree.Write(data)
   377  		genParams.File = &tree
   378  	} else {
   379  		genParams.File = &bytesReadWriter{
   380  			bytes: data,
   381  		}
   382  	}
   383  
   384  	if isSymlink {
   385  		genParams.SymlinkTarget = defaultSymlinkPath
   386  	}
   387  	hash, err := Generate(&genParams)
   388  	if err != nil {
   389  		t.Fatalf("could not generate Merkle tree:%v", err)
   390  	}
   391  
   392  	return data, VerifyParams{
   393  		Out:                   out,
   394  		File:                  bytes.NewReader(data),
   395  		Tree:                  &tree,
   396  		Size:                  dataSize,
   397  		Name:                  defaultName,
   398  		Mode:                  defaultMode,
   399  		UID:                   defaultUID,
   400  		GID:                   defaultGID,
   401  		Children:              []string{},
   402  		HashAlgorithms:        hashAlgorithm,
   403  		ReadOffset:            verifyStart,
   404  		ReadSize:              verifySize,
   405  		Expected:              hash,
   406  		DataAndTreeInSameFile: dataAndTreeInSameFile,
   407  	}
   408  }
   409  
   410  func TestVerifyInvalidRange(t *testing.T) {
   411  	testCases := []struct {
   412  		name        string
   413  		verifyStart int64
   414  		verifySize  int64
   415  	}{
   416  		// Verify range starts outside data range.
   417  		{
   418  			name:        "StartOutsideRange",
   419  			verifyStart: hostarch.PageSize,
   420  			verifySize:  1,
   421  		},
   422  		// Verify range ends outside data range.
   423  		{
   424  			name:        "EndOutsideRange",
   425  			verifyStart: 0,
   426  			verifySize:  2 * hostarch.PageSize,
   427  		},
   428  		// Verify range with negative size.
   429  		{
   430  			name:        "NegativeSize",
   431  			verifyStart: 1,
   432  			verifySize:  -1,
   433  		},
   434  	}
   435  	for _, tc := range testCases {
   436  		t.Run(tc.name, func(t *testing.T) {
   437  			var buf bytes.Buffer
   438  			_, params := prepareVerify(t, hostarch.PageSize /* dataSize */, defaultHashAlgorithm, false /* dataAndTreeInSameFile */, false /* isSymlink */, tc.verifyStart, tc.verifySize, &buf)
   439  			if _, err := Verify(&params); errors.Is(err, nil) {
   440  				t.Errorf("Verification succeeded when expected to fail")
   441  			}
   442  		})
   443  	}
   444  }
   445  
   446  func TestVerifyUnmodifiedMetadata(t *testing.T) {
   447  	testCases := []struct {
   448  		name                  string
   449  		dataAndTreeInSameFile bool
   450  		isSymlink             bool
   451  	}{
   452  		{
   453  			name:                  "SeparateFile",
   454  			dataAndTreeInSameFile: false,
   455  			isSymlink:             true,
   456  		},
   457  		{
   458  			name:                  "SameFile",
   459  			dataAndTreeInSameFile: true,
   460  			isSymlink:             false,
   461  		},
   462  		{
   463  			name:                  "SameFileSymlink",
   464  			dataAndTreeInSameFile: true,
   465  			isSymlink:             true,
   466  		},
   467  	}
   468  	for _, tc := range testCases {
   469  		t.Run(tc.name, func(t *testing.T) {
   470  			var buf bytes.Buffer
   471  			_, params := prepareVerify(t, hostarch.PageSize /* dataSize */, defaultHashAlgorithm, tc.dataAndTreeInSameFile, tc.isSymlink, 0 /* verifyStart */, 0 /* verifySize */, &buf)
   472  			if tc.isSymlink {
   473  				params.SymlinkTarget = defaultSymlinkPath
   474  			}
   475  			if _, err := Verify(&params); !errors.Is(err, nil) {
   476  				t.Errorf("Verification failed when expected to succeed: %v", err)
   477  			}
   478  		})
   479  	}
   480  }
   481  
   482  func TestVerifyModifiedName(t *testing.T) {
   483  	testCases := []struct {
   484  		name                  string
   485  		dataAndTreeInSameFile bool
   486  	}{
   487  		{
   488  			name:                  "SeparateFile",
   489  			dataAndTreeInSameFile: false,
   490  		},
   491  		{
   492  			name:                  "SameFile",
   493  			dataAndTreeInSameFile: true,
   494  		},
   495  	}
   496  	for _, tc := range testCases {
   497  		t.Run(tc.name, func(t *testing.T) {
   498  			var buf bytes.Buffer
   499  			_, params := prepareVerify(t, hostarch.PageSize /* dataSize */, defaultHashAlgorithm, tc.dataAndTreeInSameFile, false /* isSymlink */, 0 /* verifyStart */, 0 /* verifySize */, &buf)
   500  			params.Name += "abc"
   501  			if _, err := Verify(&params); errors.Is(err, nil) {
   502  				t.Errorf("Verification succeeded when expected to fail")
   503  			}
   504  		})
   505  	}
   506  }
   507  
   508  func TestVerifyModifiedSize(t *testing.T) {
   509  	testCases := []struct {
   510  		name                  string
   511  		dataAndTreeInSameFile bool
   512  	}{
   513  		{
   514  			name:                  "SeparateFile",
   515  			dataAndTreeInSameFile: false,
   516  		},
   517  		{
   518  			name:                  "SameFile",
   519  			dataAndTreeInSameFile: true,
   520  		},
   521  	}
   522  	for _, tc := range testCases {
   523  		t.Run(tc.name, func(t *testing.T) {
   524  			var buf bytes.Buffer
   525  			_, params := prepareVerify(t, hostarch.PageSize /* dataSize */, defaultHashAlgorithm, tc.dataAndTreeInSameFile, false /* isSymlink */, 0 /* verifyStart */, 0 /* verifySize */, &buf)
   526  			params.Size--
   527  			if _, err := Verify(&params); errors.Is(err, nil) {
   528  				t.Errorf("Verification succeeded when expected to fail")
   529  			}
   530  		})
   531  	}
   532  }
   533  
   534  func TestVerifyModifiedMode(t *testing.T) {
   535  	testCases := []struct {
   536  		name                  string
   537  		dataAndTreeInSameFile bool
   538  	}{
   539  		{
   540  			name:                  "SeparateFile",
   541  			dataAndTreeInSameFile: false,
   542  		},
   543  		{
   544  			name:                  "SameFile",
   545  			dataAndTreeInSameFile: true,
   546  		},
   547  	}
   548  	for _, tc := range testCases {
   549  		t.Run(tc.name, func(t *testing.T) {
   550  			var buf bytes.Buffer
   551  			_, params := prepareVerify(t, hostarch.PageSize /* dataSize */, defaultHashAlgorithm, tc.dataAndTreeInSameFile, false /* isSymlink */, 0 /* verifyStart */, 0 /* verifySize */, &buf)
   552  			params.Mode++
   553  			if _, err := Verify(&params); errors.Is(err, nil) {
   554  				t.Errorf("Verification succeeded when expected to fail")
   555  			}
   556  		})
   557  	}
   558  }
   559  
   560  func TestVerifyModifiedUID(t *testing.T) {
   561  	testCases := []struct {
   562  		name                  string
   563  		dataAndTreeInSameFile bool
   564  	}{
   565  		{
   566  			name:                  "SeparateFile",
   567  			dataAndTreeInSameFile: false,
   568  		},
   569  		{
   570  			name:                  "SameFile",
   571  			dataAndTreeInSameFile: true,
   572  		},
   573  	}
   574  	for _, tc := range testCases {
   575  		t.Run(tc.name, func(t *testing.T) {
   576  			var buf bytes.Buffer
   577  			_, params := prepareVerify(t, hostarch.PageSize /* dataSize */, defaultHashAlgorithm, tc.dataAndTreeInSameFile, false /* isSymlink */, 0 /* verifyStart */, 0 /* verifySize */, &buf)
   578  			params.UID++
   579  			if _, err := Verify(&params); errors.Is(err, nil) {
   580  				t.Errorf("Verification succeeded when expected to fail")
   581  			}
   582  		})
   583  	}
   584  }
   585  
   586  func TestVerifyModifiedGID(t *testing.T) {
   587  	testCases := []struct {
   588  		name                  string
   589  		dataAndTreeInSameFile bool
   590  	}{
   591  		{
   592  			name:                  "SeparateFile",
   593  			dataAndTreeInSameFile: false,
   594  		},
   595  		{
   596  			name:                  "SameFile",
   597  			dataAndTreeInSameFile: true,
   598  		},
   599  	}
   600  	for _, tc := range testCases {
   601  		t.Run(tc.name, func(t *testing.T) {
   602  			var buf bytes.Buffer
   603  			_, params := prepareVerify(t, hostarch.PageSize /* dataSize */, defaultHashAlgorithm, tc.dataAndTreeInSameFile, false /* isSymlink */, 0 /* verifyStart */, 0 /* verifySize */, &buf)
   604  			params.GID++
   605  			if _, err := Verify(&params); errors.Is(err, nil) {
   606  				t.Errorf("Verification succeeded when expected to fail")
   607  			}
   608  		})
   609  	}
   610  }
   611  
   612  func TestVerifyModifiedChildren(t *testing.T) {
   613  	testCases := []struct {
   614  		name                  string
   615  		dataAndTreeInSameFile bool
   616  	}{
   617  		{
   618  			name:                  "SeparateFile",
   619  			dataAndTreeInSameFile: false,
   620  		},
   621  		{
   622  			name:                  "SameFile",
   623  			dataAndTreeInSameFile: true,
   624  		},
   625  	}
   626  	for _, tc := range testCases {
   627  		t.Run(tc.name, func(t *testing.T) {
   628  			var buf bytes.Buffer
   629  			_, params := prepareVerify(t, hostarch.PageSize /* dataSize */, defaultHashAlgorithm, tc.dataAndTreeInSameFile, false /* isSymlink */, 0 /* verifyStart */, 0 /* verifySize */, &buf)
   630  			params.Children = append(params.Children, "abc")
   631  			if _, err := Verify(&params); errors.Is(err, nil) {
   632  				t.Errorf("Verification succeeded when expected to fail")
   633  			}
   634  		})
   635  	}
   636  }
   637  
   638  func TestVerifyModifiedSymlink(t *testing.T) {
   639  	var buf bytes.Buffer
   640  	_, params := prepareVerify(t, hostarch.PageSize /* dataSize */, defaultHashAlgorithm, false /* dataAndTreeInSameFile */, true /* isSymlink */, 0 /* verifyStart */, 0 /* verifySize */, &buf)
   641  	params.SymlinkTarget = "merkle_modified_test_link"
   642  	if _, err := Verify(&params); err == nil {
   643  		t.Errorf("Verification succeeded when expected to fail")
   644  	}
   645  }
   646  
   647  func TestModifyOutsideVerifyRange(t *testing.T) {
   648  	testCases := []struct {
   649  		name string
   650  		// The byte with index modifyByte is modified.
   651  		modifyByte            int64
   652  		dataAndTreeInSameFile bool
   653  	}{
   654  		{
   655  			name:                  "BeforeRangeSeparateFile",
   656  			modifyByte:            4*hostarch.PageSize - 1,
   657  			dataAndTreeInSameFile: false,
   658  		},
   659  		{
   660  			name:                  "BeforeRangeSameFile",
   661  			modifyByte:            4*hostarch.PageSize - 1,
   662  			dataAndTreeInSameFile: true,
   663  		},
   664  		{
   665  			name:                  "AfterRangeSeparateFile",
   666  			modifyByte:            5 * hostarch.PageSize,
   667  			dataAndTreeInSameFile: false,
   668  		},
   669  		{
   670  			name:                  "AfterRangeSameFile",
   671  			modifyByte:            5 * hostarch.PageSize,
   672  			dataAndTreeInSameFile: true,
   673  		},
   674  	}
   675  	for _, tc := range testCases {
   676  		t.Run(tc.name, func(t *testing.T) {
   677  			dataSize := int64(8 * hostarch.PageSize)
   678  			verifyStart := int64(4 * hostarch.PageSize)
   679  			verifySize := int64(hostarch.PageSize)
   680  			var buf bytes.Buffer
   681  			// Modified byte is outside verify range. Verify should succeed.
   682  			data, params := prepareVerify(t, dataSize, defaultHashAlgorithm, tc.dataAndTreeInSameFile, false /* isSymlink */, verifyStart, verifySize, &buf)
   683  			// Flip a bit in data and checks Verify results.
   684  			data[tc.modifyByte] ^= 1
   685  			n, err := Verify(&params)
   686  			if !errors.Is(err, nil) {
   687  				t.Errorf("Verification failed when expected to succeed: %v", err)
   688  			}
   689  			if n != verifySize {
   690  				t.Errorf("Got Verify output size %d, want %d", n, verifySize)
   691  			}
   692  			if int64(buf.Len()) != verifySize {
   693  				t.Errorf("Got Verify output buf size %d, want %d,", buf.Len(), verifySize)
   694  			}
   695  			if !bytes.Equal(data[verifyStart:verifyStart+verifySize], buf.Bytes()) {
   696  				t.Errorf("Incorrect output buf from Verify")
   697  			}
   698  		})
   699  	}
   700  }
   701  
   702  func TestModifyInsideVerifyRange(t *testing.T) {
   703  	testCases := []struct {
   704  		name        string
   705  		verifyStart int64
   706  		verifySize  int64
   707  		// The byte with index modifyByte is modified.
   708  		modifyByte            int64
   709  		dataAndTreeInSameFile bool
   710  	}{
   711  		// Test a block-aligned verify range.
   712  		// Modifying a byte in the verified range should cause verify
   713  		// to fail.
   714  		{
   715  			name:                  "BlockAlignedRangeSeparateFile",
   716  			verifyStart:           4 * hostarch.PageSize,
   717  			verifySize:            hostarch.PageSize,
   718  			modifyByte:            4 * hostarch.PageSize,
   719  			dataAndTreeInSameFile: false,
   720  		},
   721  		{
   722  			name:                  "BlockAlignedRangeSameFile",
   723  			verifyStart:           4 * hostarch.PageSize,
   724  			verifySize:            hostarch.PageSize,
   725  			modifyByte:            4 * hostarch.PageSize,
   726  			dataAndTreeInSameFile: true,
   727  		},
   728  		// The tests below use a non-block-aligned verify range.
   729  		// Modifying a byte at strat of verify range should cause
   730  		// verify to fail.
   731  		{
   732  			name:                  "ModifyStartSeparateFile",
   733  			verifyStart:           4*hostarch.PageSize + 123,
   734  			verifySize:            2 * hostarch.PageSize,
   735  			modifyByte:            4*hostarch.PageSize + 123,
   736  			dataAndTreeInSameFile: false,
   737  		},
   738  		{
   739  			name:                  "ModifyStartSameFile",
   740  			verifyStart:           4*hostarch.PageSize + 123,
   741  			verifySize:            2 * hostarch.PageSize,
   742  			modifyByte:            4*hostarch.PageSize + 123,
   743  			dataAndTreeInSameFile: true,
   744  		},
   745  		// Modifying a byte at the end of verify range should cause
   746  		// verify to fail.
   747  		{
   748  			name:                  "ModifyEndSeparateFile",
   749  			verifyStart:           4*hostarch.PageSize + 123,
   750  			verifySize:            2 * hostarch.PageSize,
   751  			modifyByte:            6*hostarch.PageSize + 123,
   752  			dataAndTreeInSameFile: false,
   753  		},
   754  		{
   755  			name:                  "ModifyEndSameFile",
   756  			verifyStart:           4*hostarch.PageSize + 123,
   757  			verifySize:            2 * hostarch.PageSize,
   758  			modifyByte:            6*hostarch.PageSize + 123,
   759  			dataAndTreeInSameFile: true,
   760  		},
   761  		// Modifying a byte in the middle verified block should cause
   762  		// verify to fail.
   763  		{
   764  			name:                  "ModifyMiddleSeparateFile",
   765  			verifyStart:           4*hostarch.PageSize + 123,
   766  			verifySize:            2 * hostarch.PageSize,
   767  			modifyByte:            5*hostarch.PageSize + 123,
   768  			dataAndTreeInSameFile: false,
   769  		},
   770  		{
   771  			name:                  "ModifyMiddleSameFile",
   772  			verifyStart:           4*hostarch.PageSize + 123,
   773  			verifySize:            2 * hostarch.PageSize,
   774  			modifyByte:            5*hostarch.PageSize + 123,
   775  			dataAndTreeInSameFile: true,
   776  		},
   777  		// Modifying a byte in the first block in the verified range
   778  		// should cause verify to fail, even the modified bit itself is
   779  		// out of verify range.
   780  		{
   781  			name:                  "ModifyFirstBlockSeparateFile",
   782  			verifyStart:           4*hostarch.PageSize + 123,
   783  			verifySize:            2 * hostarch.PageSize,
   784  			modifyByte:            4*hostarch.PageSize + 122,
   785  			dataAndTreeInSameFile: false,
   786  		},
   787  		{
   788  			name:                  "ModifyFirstBlockSameFile",
   789  			verifyStart:           4*hostarch.PageSize + 123,
   790  			verifySize:            2 * hostarch.PageSize,
   791  			modifyByte:            4*hostarch.PageSize + 122,
   792  			dataAndTreeInSameFile: true,
   793  		},
   794  		// Modifying a byte in the last block in the verified range
   795  		// should cause verify to fail, even the modified bit itself is
   796  		// out of verify range.
   797  		{
   798  			name:                  "ModifyLastBlockSeparateFile",
   799  			verifyStart:           4*hostarch.PageSize + 123,
   800  			verifySize:            2 * hostarch.PageSize,
   801  			modifyByte:            6*hostarch.PageSize + 124,
   802  			dataAndTreeInSameFile: false,
   803  		},
   804  		{
   805  			name:                  "ModifyLastBlockSameFile",
   806  			verifyStart:           4*hostarch.PageSize + 123,
   807  			verifySize:            2 * hostarch.PageSize,
   808  			modifyByte:            6*hostarch.PageSize + 124,
   809  			dataAndTreeInSameFile: true,
   810  		},
   811  	}
   812  	for _, tc := range testCases {
   813  		t.Run(tc.name, func(t *testing.T) {
   814  			dataSize := int64(8 * hostarch.PageSize)
   815  			var buf bytes.Buffer
   816  			data, params := prepareVerify(t, dataSize, defaultHashAlgorithm, tc.dataAndTreeInSameFile, false /* isSymlink */, tc.verifyStart, tc.verifySize, &buf)
   817  			// Flip a bit in data and checks Verify results.
   818  			data[tc.modifyByte] ^= 1
   819  			if _, err := Verify(&params); errors.Is(err, nil) {
   820  				t.Errorf("Verification succeeded when expected to fail")
   821  			}
   822  		})
   823  	}
   824  }
   825  
   826  func TestVerifyRandom(t *testing.T) {
   827  	testCases := []struct {
   828  		name                  string
   829  		hashAlgorithm         int
   830  		dataAndTreeInSameFile bool
   831  	}{
   832  		{
   833  			name:                  "SHA256SeparateFile",
   834  			hashAlgorithm:         linux.FS_VERITY_HASH_ALG_SHA256,
   835  			dataAndTreeInSameFile: false,
   836  		},
   837  		{
   838  			name:                  "SHA512SeparateFile",
   839  			hashAlgorithm:         linux.FS_VERITY_HASH_ALG_SHA512,
   840  			dataAndTreeInSameFile: false,
   841  		},
   842  		{
   843  			name:                  "SHA256SameFile",
   844  			hashAlgorithm:         linux.FS_VERITY_HASH_ALG_SHA256,
   845  			dataAndTreeInSameFile: true,
   846  		},
   847  		{
   848  			name:                  "SHA512SameFile",
   849  			hashAlgorithm:         linux.FS_VERITY_HASH_ALG_SHA512,
   850  			dataAndTreeInSameFile: true,
   851  		},
   852  	}
   853  	for _, tc := range testCases {
   854  		t.Run(tc.name, func(t *testing.T) {
   855  			rand.Seed(time.Now().UnixNano())
   856  			// Use a random dataSize.  Minimum size 2 so that we can pick a random
   857  			// portion from it.
   858  			dataSize := rand.Int63n(200*hostarch.PageSize) + 2
   859  
   860  			// Pick a random portion of data.
   861  			start := rand.Int63n(dataSize - 1)
   862  			size := rand.Int63n(dataSize) + 1
   863  
   864  			var buf bytes.Buffer
   865  			data, params := prepareVerify(t, dataSize, tc.hashAlgorithm, tc.dataAndTreeInSameFile, false /* isSymlink */, start, size, &buf)
   866  
   867  			// Checks that the random portion of data from the original data is
   868  			// verified successfully.
   869  			n, err := Verify(&params)
   870  			if err != nil && err != io.EOF {
   871  				t.Errorf("Verification failed for correct data: %v", err)
   872  			}
   873  			if size > dataSize-start {
   874  				size = dataSize - start
   875  			}
   876  			if n != size {
   877  				t.Errorf("Got Verify output size %d, want %d", n, size)
   878  			}
   879  			if int64(buf.Len()) != size {
   880  				t.Errorf("Got Verify output buf size %d, want %d", buf.Len(), size)
   881  			}
   882  			if !bytes.Equal(data[start:start+size], buf.Bytes()) {
   883  				t.Errorf("Incorrect output buf from Verify")
   884  			}
   885  
   886  			// Verify that modified metadata should fail verification.
   887  			buf.Reset()
   888  			params.Name = defaultName + "abc"
   889  			if _, err := Verify(&params); errors.Is(err, nil) {
   890  				t.Error("Verify succeeded for modified metadata, expect failure")
   891  			}
   892  
   893  			// Flip a random bit in randPortion, and check that verification fails.
   894  			buf.Reset()
   895  			randBytePos := rand.Int63n(size)
   896  			data[start+randBytePos] ^= 1
   897  			params.File = bytes.NewReader(data)
   898  			params.Name = defaultName
   899  
   900  			if _, err := Verify(&params); errors.Is(err, nil) {
   901  				t.Error("Verification succeeded for modified data, expect failure")
   902  			}
   903  		})
   904  	}
   905  }