github.com/mvdan/u-root-coreutils@v0.0.0-20230122170626-c2eef2898555/pkg/boot/fit/fit_test.go (about) 1 // Copyright 2020 the u-root Authors. All rights reserved 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package fit 6 7 import ( 8 "bytes" 9 "errors" 10 "fmt" 11 "io" 12 "os" 13 "path/filepath" 14 "reflect" 15 "strings" 16 "testing" 17 18 "github.com/mvdan/u-root-coreutils/pkg/boot" 19 "github.com/mvdan/u-root-coreutils/pkg/vfile" 20 21 "github.com/ProtonMail/go-crypto/openpgp" 22 "github.com/ProtonMail/go-crypto/openpgp/packet" 23 ) 24 25 const ( 26 // Number of configs in the fitimage.itb 27 fbcCnt = 2 28 // Size in bytes the of content for each image in the fitimage.itb 29 // This arbitrary value is defined in testdata/README.md for data creation. 30 imageSize = 100 31 // Fill patterns for the image content 32 kernel0Fill = "k0" 33 kernel1Fill = "k1" 34 initram0Fill = "i0" 35 ) 36 37 func TestLoadConfig(t *testing.T) { 38 i, err := New("testdata/fitimage.itb") 39 if err != nil { 40 t.Fatal(err) 41 } 42 43 kn, rn, err := i.LoadConfig() 44 if err != nil { 45 t.Fatal(err) 46 } 47 48 t.Logf("kernel name: %s", kn) 49 t.Logf("ramdisk name: %s", rn) 50 if kn != "kernel@0" { 51 t.Errorf("Expected kernel %s, got %s", "kernel@0", kn) 52 } 53 if rn != "ramdisk@0" { 54 t.Errorf("Expected ramdisk %s, got %s", "ramdisk@0", rn) 55 } 56 } 57 58 func TestLoadConfigMiss(t *testing.T) { 59 i, err := New("testdata/fitimage.itb") 60 if err != nil { 61 t.Fatal(err) 62 } 63 64 i.ConfigOverride = "MagicNonExistentConfig" 65 kn, rn, err := i.LoadConfig() 66 67 if kn != "" { 68 t.Errorf("Kernel %s returned on expected config miss", kn) 69 } 70 71 if rn != "" { 72 t.Errorf("Initramfs %s returned on expected config miss", rn) 73 } 74 75 if err == nil { 76 t.Fatal("Expected error message for miss on FIT config, got nil") 77 } 78 } 79 80 func TestLoad(t *testing.T) { 81 keyFiles := []string{"key0", "key1"} 82 83 var keys []*openpgp.Entity 84 for _, k := range keyFiles { 85 b, err := os.ReadFile(filepath.Join("testdata", k)) 86 if err != nil { 87 t.Fatal(err) 88 } 89 key, err := openpgp.ReadEntity(packet.NewReader(bytes.NewBuffer(b))) 90 if err != nil { 91 t.Fatal(err) 92 } 93 keys = append(keys, key) 94 } 95 96 for _, tt := range []struct { 97 desc string 98 kernel string 99 initram string 100 keyring openpgp.KeyRing 101 want error 102 wantKernelFill string 103 wantInitFill string 104 }{ 105 { 106 desc: "Successful kernel0/init0 read with key0", 107 keyring: openpgp.EntityList{keys[0]}, 108 kernel: "kernel@0", 109 initram: "ramdisk@0", 110 want: nil, 111 wantKernelFill: kernel0Fill, 112 wantInitFill: initram0Fill, 113 }, 114 { 115 desc: "Successful unsigned kernel1/init", 116 keyring: nil, 117 kernel: "kernel@1", 118 initram: "ramdisk@0", 119 want: nil, 120 wantKernelFill: kernel1Fill, 121 wantInitFill: initram0Fill, 122 }, 123 { 124 desc: "Successful unsigned kernel1", 125 keyring: nil, 126 kernel: "kernel@1", 127 initram: "", 128 want: nil, 129 wantKernelFill: kernel1Fill, 130 wantInitFill: "", 131 }, 132 { 133 desc: "bad kernel0 good init0 read with key1", 134 keyring: openpgp.EntityList{keys[1]}, 135 kernel: "kernel@0", 136 initram: "ramdisk@0", 137 want: vfile.ErrUnsigned{}, 138 wantKernelFill: "", 139 wantInitFill: "", 140 }, 141 { 142 desc: "missing kernel", 143 keyring: nil, 144 kernel: "", 145 initram: "ramdisk@0", 146 want: fmt.Errorf(""), 147 wantKernelFill: "", 148 wantInitFill: "", 149 }, 150 } { 151 t.Run(tt.desc, func(t *testing.T) { 152 i, err := New("testdata/fitimage.itb") 153 if err != nil { 154 t.Fatal(err) 155 } 156 157 i.Kernel, i.InitRAMFS, i.KeyRing = tt.kernel, tt.initram, tt.keyring 158 159 defer func(old func(i *boot.LinuxImage, verbose bool) error) { loadImage = old }(loadImage) 160 161 loadImage = func(i *boot.LinuxImage, verbose bool) error { 162 if i == nil { 163 t.Errorf("Load() of kernel:%s, init:%s, keys: %v - passed nil to loadImage", tt.kernel, tt.initram, tt.keyring) 164 return nil 165 } 166 167 if i.Kernel == nil && tt.wantKernelFill != "" { 168 t.Errorf("loadImage gave nil kernel: want pattern '%s'", tt.wantKernelFill) 169 } 170 if i.Kernel != nil { 171 compareImage(t, "kernel", tt.wantKernelFill, i.Kernel) 172 } 173 174 if i.Initrd == nil && tt.wantInitFill != "" { 175 t.Errorf("loadImage gave nil initram: want pattern '%s'", tt.wantInitFill) 176 } 177 if i.Initrd != nil { 178 compareImage(t, "initram", tt.wantInitFill, i.Initrd) 179 } 180 return nil 181 } 182 183 gotErr := i.Load(true) 184 185 // Shallow check to verify the correct error 186 if (tt.want == nil && gotErr != nil) || reflect.TypeOf(gotErr) != reflect.TypeOf(tt.want) { 187 t.Errorf("Load() of kernel:%s, init:%s, keys: %v - got %T, want %T", tt.kernel, tt.initram, tt.keyring, gotErr, tt.want) 188 } 189 }) 190 } 191 } 192 193 func compareImage(t *testing.T, name string, wantFill string, image io.ReaderAt) { 194 // Make sure we can only read imageSize by expecting an EOF instead of 195 // the imageSize+1 byte. 196 gotImage := make([]byte, imageSize+1) 197 gotSize, err := image.ReadAt(gotImage, 0) 198 if !errors.Is(err, io.EOF) { 199 t.Errorf("failed reading %s passed to loadImage: ReadAt(%d)/expected %d: got %v, want %v", name, imageSize+1, imageSize, err, io.EOF) 200 } 201 if gotSize != imageSize { 202 t.Errorf("failed reading %s passed to loadImage: bytes got %v bytes, want %v of pattern %s.\nReturned image:\t%v", name, gotSize, imageSize, wantFill, gotImage) 203 } else { 204 fill := []byte(wantFill) 205 for i := 0; i < gotSize; i++ { 206 if gotImage[i] != fill[i%len(fill)] { 207 t.Errorf("loadImage gave %s: %v, want pattern: \"%v...\". Mismatch at index %d: want %d, got %d", name, gotImage, wantFill, i, gotImage[i], fill[i%len(fill)]) 208 } 209 } 210 } 211 } 212 213 func TestReadSignedImage(t *testing.T) { 214 keyFiles := []string{"key0", "key1"} 215 216 var keys []*openpgp.Entity 217 for _, k := range keyFiles { 218 b, err := os.ReadFile(filepath.Join("testdata", k)) 219 if err != nil { 220 t.Fatal(err) 221 } 222 key, err := openpgp.ReadEntity(packet.NewReader(bytes.NewBuffer(b))) 223 if err != nil { 224 t.Fatal(err) 225 } 226 keys = append(keys, key) 227 } 228 229 for _, tt := range []struct { 230 desc string 231 keyring openpgp.KeyRing 232 image string 233 want error 234 isSignatureValid bool 235 wantContentFill string 236 }{ 237 { 238 desc: "Successful kernel read with key0", 239 keyring: openpgp.EntityList{keys[0]}, 240 image: "kernel@0", 241 want: nil, 242 wantContentFill: kernel0Fill, 243 isSignatureValid: true, 244 }, 245 { 246 desc: "Successful initram read with key0", 247 keyring: openpgp.EntityList{keys[0]}, 248 image: "ramdisk@0", 249 want: nil, 250 wantContentFill: initram0Fill, 251 isSignatureValid: true, 252 }, 253 { 254 desc: "Successful initram read with key1", 255 keyring: openpgp.EntityList{keys[1]}, 256 image: "ramdisk@0", 257 want: nil, 258 wantContentFill: initram0Fill, 259 isSignatureValid: true, 260 }, 261 { 262 desc: "Read unsigned kernel1", 263 keyring: openpgp.EntityList{keys[0], keys[1]}, 264 image: "kernel@1", 265 want: vfile.ErrUnsigned{}, 266 wantContentFill: kernel1Fill, 267 isSignatureValid: false, 268 }, 269 { 270 desc: "Read signed kernel0 with wrong key", 271 keyring: openpgp.EntityList{keys[1]}, 272 image: "kernel@0", 273 want: vfile.ErrUnsigned{}, 274 wantContentFill: kernel0Fill, 275 isSignatureValid: false, 276 }, 277 } { 278 t.Run(tt.desc, func(t *testing.T) { 279 i, err := New("testdata/fitimage.itb") 280 if err != nil { 281 t.Fatal(err) 282 } 283 284 b, gotErr := i.ReadSignedImage(tt.image, tt.keyring) 285 286 // Shallow check to verify we're claiming it's signed or unsigned 287 if (tt.want == nil && gotErr != nil) || reflect.TypeOf(gotErr) != reflect.TypeOf(tt.want) { 288 t.Errorf("ReadSignedImage(%s, %v) = %T, want %T", tt.image, tt.keyring, gotErr, tt.want) 289 } 290 291 if isSignatureValid := (gotErr == nil); isSignatureValid != tt.isSignatureValid { 292 t.Errorf("isSignatureValid(%v) = %v, want %v", gotErr, isSignatureValid, tt.isSignatureValid) 293 } 294 295 if b != nil { 296 compareImage(t, tt.image, tt.wantContentFill, b) 297 } 298 }) 299 } 300 } 301 302 func TestParseConfig(t *testing.T) { 303 f, err := os.Open("testdata/fitimage.itb") 304 if err != nil { 305 t.Fatal(err) 306 } 307 308 imgs, err := ParseConfig(f) 309 if err != nil { 310 t.Fatal(err) 311 } 312 313 if len(imgs) != fbcCnt { 314 t.Fatalf("Expected 2 images from ParseConfig, got %x", len(imgs)) 315 } 316 317 cs := [fbcCnt]string{"conf@1", "conf_bz@1"} 318 for c, i := range imgs { 319 t.Logf("config name: %s", i.ConfigOverride) 320 t.Logf("kernel name: %s", i.Kernel) 321 t.Logf("ramdisk name: %s", i.InitRAMFS) 322 if i.ConfigOverride != cs[c] { 323 t.Errorf("Expected config %s, got %s", cs[c], i.ConfigOverride) 324 } 325 if i.Kernel != "kernel@0" { 326 t.Errorf("Expected kernel %s, got %s", "kernel@0", i.Kernel) 327 } 328 if i.InitRAMFS != "ramdisk@0" { 329 t.Errorf("Expected ramdisk %s, got %s", "ramdisk@0", i.InitRAMFS) 330 } 331 } 332 } 333 334 func TestParseConfigMiss(t *testing.T) { 335 f, err := os.Open("testdata/fitimage.its") 336 if err != nil { 337 t.Fatal(err) 338 } 339 340 imgs, err := ParseConfig(f) 341 342 if imgs != nil { 343 t.Errorf("Expected nil on bad image parse, got %#v", imgs) 344 } 345 346 if err == nil { 347 t.Fatal("Expected error on failed ParseConfig, got nil") 348 } 349 } 350 351 func TestLabel(t *testing.T) { 352 n, kn, rn := "conf_bz@1", "kernel@0", "ramdisk@0" 353 img := &Image{name: n, Kernel: kn, InitRAMFS: rn} 354 l := img.Label() 355 if !strings.Contains(l, n) { 356 t.Fatalf("Expected Image label to contain name %s, got %s", n, l) 357 } 358 } 359 360 func TestRank(t *testing.T) { 361 testRank := 2 362 img := &Image{BootRank: testRank} 363 l := img.Rank() 364 if l != testRank { 365 t.Fatalf("Expected Image rank %d, got %d", testRank, l) 366 } 367 }