| // Copyright 2026 The BoringSSL Authors | |
| // | |
| // 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 | |
| // | |
| // https://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. | |
| package main | |
| import ( | |
| "bytes" | |
| _ "embed" | |
| "fmt" | |
| "os" | |
| "path/filepath" | |
| "strings" | |
| "testing" | |
| "boringssl.googlesource.com/boringssl.git/util/fipstools/fipscommon" | |
| ) | |
| //go:embed testdata/test.o | |
| var testObj []byte | |
| //go:embed testdata/empty.o | |
| var emptyObj []byte | |
| func TestInjectHashTextAndROData(t *testing.T) { | |
| tmpDir := t.TempDir() | |
| inPath := filepath.Join(tmpDir, "input.o") | |
| outPath := filepath.Join(tmpDir, "output.o") | |
| if err := os.WriteFile(inPath, testObj, 0644); err != nil { | |
| t.Fatalf("failed to write input file: %s", err) | |
| } | |
| if err := do(outPath, inPath, "", "", false); err != nil { | |
| t.Fatalf("do() failed: %s", err) | |
| } | |
| outBytes, err := os.ReadFile(outPath) | |
| if err != nil { | |
| t.Fatalf("failed to read output file: %s", err) | |
| } | |
| if len(outBytes) != len(testObj) { | |
| t.Fatalf("output length %d does not match input length %d", len(outBytes), len(testObj)) | |
| } | |
| hashOffset := bytes.Index(testObj, fipscommon.UninitHashValue[:]) | |
| if hashOffset < 0 { | |
| t.Fatalf("could not find uninit hash value in testObj") | |
| } | |
| // Verify all surrounding bytes before the hash are strictly unchanged. | |
| if !bytes.Equal(outBytes[:hashOffset], testObj[:hashOffset]) { | |
| t.Errorf("bytes before the hash value were modified") | |
| } | |
| // Verify all surrounding bytes after the hash are strictly unchanged. | |
| if !bytes.Equal(outBytes[hashOffset+32:], testObj[hashOffset+32:]) { | |
| t.Errorf("bytes after the hash value were modified") | |
| } | |
| // Verify the placeholder was overwritten with the expected hash. | |
| const wantHash = "28369e972bef5e55f32607c72f3434d94b621e141f32f4d196c73183b15c9297" | |
| gotHash := fmt.Sprintf("%x", outBytes[hashOffset:hashOffset+32]) | |
| if gotHash != wantHash { | |
| t.Errorf("injected hash mismatch:\n got: %s\n want: %s", gotHash, wantHash) | |
| } | |
| } | |
| func TestIgnoreMissing(t *testing.T) { | |
| noHashObj := bytes.ReplaceAll(testObj, fipscommon.UninitHashValue[:], make([]byte, 32)) | |
| tests := []struct { | |
| name string | |
| inputBytes []byte | |
| wantErr string | |
| }{ | |
| { | |
| name: "MissingSymbols", | |
| inputBytes: emptyObj, | |
| wantErr: "could not find .text module boundaries", | |
| }, | |
| { | |
| name: "MissingUninitHash", | |
| inputBytes: noHashObj, | |
| wantErr: "did not find uninitialised hash value", | |
| }, | |
| { | |
| name: "NonELFInput", | |
| inputBytes: []byte("THIS_IS_A_TEXT_FILE_NOT_AN_ELF_OBJECT"), | |
| wantErr: "failed to parse object", | |
| }, | |
| } | |
| for _, tc := range tests { | |
| t.Run(tc.name, func(t *testing.T) { | |
| tmpDir := t.TempDir() | |
| inPath := filepath.Join(tmpDir, "input.o") | |
| outPath := filepath.Join(tmpDir, "output.o") | |
| if err := os.WriteFile(inPath, tc.inputBytes, 0644); err != nil { | |
| t.Fatalf("failed to write input file: %s", err) | |
| } | |
| // Without ignoreMissing: must fail with expected error | |
| err := do(outPath, inPath, "", "", false) | |
| if err == nil || !strings.Contains(err.Error(), tc.wantErr) { | |
| t.Errorf("expected error containing %q, got: %v", tc.wantErr, err) | |
| } | |
| // With ignoreMissing: must succeed and keep output byte-identical to input | |
| if err := do(outPath, inPath, "", "", true); err != nil { | |
| t.Fatalf("do(ignoreMissing=true) failed: %s", err) | |
| } | |
| outBytes, err := os.ReadFile(outPath) | |
| if err != nil { | |
| t.Fatalf("failed to read output file: %s", err) | |
| } | |
| if !bytes.Equal(outBytes, tc.inputBytes) { | |
| t.Errorf("output was modified when ignoring missing symbols/hashes") | |
| } | |
| }) | |
| } | |
| } |