1// Copyright (c) 2017, Google Inc. 2// 3// Permission to use, copy, modify, and/or distribute this software for any 4// purpose with or without fee is hereby granted, provided that the above 5// copyright notice and this permission notice appear in all copies. 6// 7// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 10// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION 12// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 13// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 14 15//go:build ignore 16 17// break-hash parses an ELF binary containing the FIPS module and corrupts the 18// first byte of the module. This should cause the integrity check to fail. 19package main 20 21import ( 22 "bytes" 23 "crypto/hmac" 24 "crypto/sha512" 25 "debug/elf" 26 "encoding/hex" 27 "errors" 28 "fmt" 29 "os" 30) 31 32func do(outPath, inPath string) error { 33 objectBytes, err := os.ReadFile(inPath) 34 if err != nil { 35 return err 36 } 37 38 object, err := elf.NewFile(bytes.NewReader(objectBytes)) 39 if err != nil { 40 return errors.New("failed to parse object: " + err.Error()) 41 } 42 43 // Find the .text section. 44 var textSection *elf.Section 45 var textSectionIndex elf.SectionIndex 46 for i, section := range object.Sections { 47 if section.Name == ".text" { 48 textSectionIndex = elf.SectionIndex(i) 49 textSection = section 50 break 51 } 52 } 53 54 if textSection == nil { 55 return errors.New("failed to find .text section in object") 56 } 57 58 symbols, err := object.Symbols() 59 if err != nil { 60 fmt.Fprintf(os.Stderr, "%s\nTrying dynamic symbols\n", err) 61 symbols, err = object.DynamicSymbols() 62 } 63 if err != nil { 64 return errors.New("failed to parse symbols: " + err.Error()) 65 } 66 67 // Find the start and end markers of the module. 68 var startSeen, endSeen bool 69 var start, end uint64 70 71 for _, symbol := range symbols { 72 if symbol.Section != textSectionIndex { 73 continue 74 } 75 76 switch symbol.Name { 77 case "BORINGSSL_bcm_text_start": 78 if startSeen { 79 return errors.New("duplicate start symbol found") 80 } 81 startSeen = true 82 start = symbol.Value 83 case "BORINGSSL_bcm_text_end": 84 if endSeen { 85 return errors.New("duplicate end symbol found") 86 } 87 endSeen = true 88 end = symbol.Value 89 default: 90 continue 91 } 92 } 93 94 if !startSeen || !endSeen { 95 return errors.New("could not find module in object") 96 } 97 98 moduleText := make([]byte, end-start) 99 if n, err := textSection.ReadAt(moduleText, int64(start-textSection.Addr)); err != nil { 100 return fmt.Errorf("failed to read from module start (at %d of %d) in .text: %s", start, textSection.Size, err) 101 } else if n != len(moduleText) { 102 return fmt.Errorf("short read from .text: wanted %d, got %d", len(moduleText), n) 103 } 104 105 // In order to match up the module start with the raw ELF contents, 106 // search for the first 256 bytes and assume that will be unique. 107 offset := bytes.Index(objectBytes, moduleText[:256]) 108 if offset < 0 { 109 return errors.New("did not find module prefix in object file") 110 } 111 112 if bytes.Index(objectBytes[offset+1:], moduleText[:256]) >= 0 { 113 return errors.New("found two occurrences of prefix in object file") 114 } 115 116 // Corrupt the module in the ELF. 117 objectBytes[offset] ^= 1 118 119 // Calculate the before and after hash of the module. 120 var zeroKey [64]byte 121 mac := hmac.New(sha512.New, zeroKey[:]) 122 mac.Write(moduleText) 123 hashWas := mac.Sum(nil) 124 125 moduleText[0] ^= 1 126 mac.Reset() 127 mac.Write(moduleText) 128 newHash := mac.Sum(nil) 129 130 fmt.Printf("Found start of module at offset 0x%x (VMA 0x%x):\n", start-textSection.Addr, start) 131 fmt.Println(hex.Dump(moduleText[:128])) 132 fmt.Printf("\nHash of module was: %x\n", hashWas) 133 fmt.Printf("Hash of corrupted module is: %x\n", newHash) 134 135 return os.WriteFile(outPath, objectBytes, 0755) 136} 137 138func main() { 139 if len(os.Args) != 3 { 140 usage() 141 os.Exit(1) 142 } 143 144 if err := do(os.Args[2], os.Args[1]); err != nil { 145 fmt.Fprintf(os.Stderr, "%s\n", err) 146 os.Exit(1) 147 } 148} 149 150func usage() { 151 fmt.Fprintf(os.Stderr, "Usage: %s <input binary> <output path>\n", os.Args[0]) 152} 153