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  }