github.com/transparency-dev/armored-witness-os@v0.1.3-0.20240514084412-27eef7325168/internal/hab/hab.go (about) 1 // Copyright 2022 The Armored Witness OS authors. All Rights Reserved. 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 hab 16 17 import ( 18 "bytes" 19 "crypto/sha256" 20 "errors" 21 "fmt" 22 "log" 23 24 "github.com/usbarmory/tamago/soc/nxp/imx6ul" 25 26 "github.com/usbarmory/crucible/otp" 27 "github.com/usbarmory/crucible/util" 28 ) 29 30 // Activate enables secure boot by following the procedure described at: 31 // 32 // https://github.com/usbarmory/usbarmory/wiki/Secure-boot-(Mk-II)#activating-hab 33 // 34 // IMPORTANT: enabling secure boot functionality on NXP i.MX SoCs, unlike 35 // similar features on modern PCs, is an irreversible action that permanently 36 // fuses verification keys hashes on the device. This means that any errors in 37 // the process or loss of the signing PKI will result in a bricked device 38 // incapable of executing unsigned code. This is a security feature, not a bug. 39 func Activate(srk []byte) (err error) { 40 switch { 41 case imx6ul.SNVS.Available(): 42 return errors.New("HAB already enabled") 43 case len(srk) != sha256.Size: 44 return errors.New("invalid SRK") 45 case bytes.Equal(srk, make([]byte, sha256.Size)): 46 return errors.New("invalid SRK") 47 default: 48 // Enable High Assurance Boot (i.e. secure boot) 49 return hab(srk) 50 } 51 52 return 53 } 54 55 func fuse(name string, bank int, word int, off int, size int, val []byte) error { 56 log.Printf("fusing %s bank:%d word:%d off:%d size:%d val:%x", name, bank, word, off, size, val) 57 58 if res, err := otp.ReadOCOTP(bank, word, off, size); err != nil { 59 return fmt.Errorf("read error for %s, res:%x err:%v\n", name, res, err) 60 } else { 61 log.Printf(" pre-read val: %x", res) 62 } 63 64 if err := otp.BlowOCOTP(bank, word, off, size, val); err != nil { 65 return err 66 } 67 68 if res, err := otp.ReadOCOTP(bank, word, off, size); err != nil || !bytes.Equal(val, res) { 69 return fmt.Errorf("readback error for %s, val:%x res:%x err:%v\n", name, val, res, err) 70 } 71 72 return nil 73 } 74 75 func hab(srk []byte) (err error) { 76 if len(srk) != sha256.Size { 77 return errors.New("fatal error, invalid SRK hash") 78 } 79 80 // fuse HAB public keys hash 81 if err = fuse("SRK_HASH", 3, 0, 0, 256, util.SwitchEndianness(srk)); err != nil { 82 return 83 } 84 85 // lock HAB public keys hash 86 if err = fuse("SRK_LOCK", 0, 0, 14, 1, []byte{1}); err != nil { 87 return 88 } 89 90 // set device in Closed Configuration (IMX6ULRM Table 8-2, p245) 91 if err = fuse("SEC_CONFIG", 0, 6, 0, 2, []byte{0b11}); err != nil { 92 return 93 } 94 95 // disable NXP reserved mode (IMX6ULRM 8.2.6, p244) 96 if err = fuse("DIR_BT_DIS", 0, 6, 3, 1, []byte{1}); err != nil { 97 return 98 } 99 100 // Disable debugging features (IMX6ULRM Table 5-9, p216) 101 102 // disable Secure JTAG controller 103 if err = fuse("SJC_DISABLE", 0, 6, 20, 1, []byte{1}); err != nil { 104 return 105 } 106 107 // disable JTAG debug mode 108 if err = fuse("JTAG_SMODE", 0, 6, 22, 2, []byte{0b11}); err != nil { 109 return 110 } 111 112 // disable HAB ability to enable JTAG 113 if err = fuse("JTAG_HEO", 0, 6, 27, 1, []byte{1}); err != nil { 114 return 115 } 116 117 // disable tracing 118 if err = fuse("KTE", 0, 6, 26, 1, []byte{1}); err != nil { 119 return 120 } 121 122 // Further reduce the attack surface 123 124 // disable Serial Download Protocol (SDP) READ_REGISTER command (IMX6ULRM 8.9.3, p310) 125 if err = fuse("SDP_READ_DISABLE", 0, 6, 18, 1, []byte{1}); err != nil { 126 return 127 } 128 129 // disable SDP over UART (IMX6ULRM 8.9, p305) 130 if err = fuse("UART_SERIAL_DOWNLOAD_DISABLE", 0, 7, 4, 1, []byte{1}); err != nil { 131 return 132 } 133 134 return 135 }