github.com/usbarmory/tamago@v0.0.0-20240508072735-8612bbe1e454/soc/nxp/dcp/key.go (about)

     1  // NXP Data Co-Processor (DCP) driver
     2  // https://github.com/usbarmory/tamago
     3  //
     4  // Copyright (c) WithSecure Corporation
     5  // https://foundry.withsecure.com
     6  //
     7  // Use of this source code is governed by the license
     8  // that can be found in the LICENSE file.
     9  
    10  package dcp
    11  
    12  import (
    13  	"bytes"
    14  	"crypto/aes"
    15  	"encoding/binary"
    16  	"errors"
    17  
    18  	"github.com/usbarmory/tamago/bits"
    19  	"github.com/usbarmory/tamago/internal/reg"
    20  )
    21  
    22  // DeriveKey derives a hardware unique key in a manner equivalent to PKCS#11
    23  // C_DeriveKey with CKM_AES_CBC_ENCRYPT_DATA.
    24  //
    25  // The diversifier is AES-128-CBC encrypted using the internal OTPMK (when SNVS
    26  // is enabled).
    27  //
    28  // *WARNING*: when SNVS is not enabled a default non-unique test vector is used
    29  // and therefore key derivation is *unsafe*, see snvs.Available().
    30  //
    31  // A negative index argument results in the derived key being computed and
    32  // returned.
    33  //
    34  // An index argument equal or greater than 0 moves the derived key, through
    35  // DeriveKeyMemory, to the corresponding internal DCP key RAM slot (see
    36  // SetKey()). In this case no key is returned by the function.
    37  func (hw *DCP) DeriveKey(diversifier []byte, iv []byte, index int) (key []byte, err error) {
    38  	if len(iv) != aes.BlockSize {
    39  		return nil, errors.New("invalid IV size")
    40  	}
    41  
    42  	// prepare diversifier for in-place encryption
    43  	key = pad(diversifier, false)
    44  
    45  	region := hw.DeriveKeyMemory
    46  
    47  	if index >= 0 {
    48  		if region == nil {
    49  			return nil, errors.New("invalid DeriveKeyMemory")
    50  		}
    51  	}
    52  
    53  	sourceBufferAddress := region.Alloc(key, aes.BlockSize)
    54  	defer region.Free(sourceBufferAddress)
    55  
    56  	payloadPointer := region.Alloc(iv, 0)
    57  	defer region.Free(payloadPointer)
    58  
    59  	pkt := &WorkPacket{}
    60  	pkt.SetCipherDefaults()
    61  
    62  	// Use device-specific hardware key for encryption.
    63  	pkt.Control0 |= 1 << DCP_CTRL0_CIPHER_ENCRYPT
    64  	pkt.Control0 |= 1 << DCP_CTRL0_OTP_KEY
    65  	pkt.Control1 |= KEY_SELECT_UNIQUE_KEY << DCP_CTRL1_KEY_SELECT
    66  	pkt.SourceBufferAddress = uint32(sourceBufferAddress)
    67  	pkt.DestinationBufferAddress = pkt.SourceBufferAddress
    68  	pkt.BufferSize = uint32(len(key))
    69  	pkt.PayloadPointer = uint32(payloadPointer)
    70  
    71  	ptr := region.Alloc(pkt.Bytes(), 0)
    72  	defer region.Free(ptr)
    73  
    74  	if err = hw.cmd(ptr, 1); err != nil {
    75  		return nil, err
    76  	}
    77  
    78  	if index >= 0 {
    79  		return nil, hw.setKeyData(index, nil, pkt.DestinationBufferAddress)
    80  	} else {
    81  		region.Read(sourceBufferAddress, 0, key)
    82  	}
    83  
    84  	return
    85  }
    86  
    87  func (hw *DCP) setKeyData(index int, key []byte, addr uint32) (err error) {
    88  	var keyLocation uint32
    89  	var subword uint32
    90  
    91  	if index < 0 || index > 3 {
    92  		return errors.New("key index must be between 0 and 3")
    93  	}
    94  
    95  	if key != nil && len(key) > aes.BlockSize {
    96  		return errors.New("invalid key size")
    97  	}
    98  
    99  	bits.SetN(&keyLocation, KEY_INDEX, 0b11, uint32(index))
   100  
   101  	hw.Lock()
   102  	defer hw.Unlock()
   103  
   104  	for subword < 4 {
   105  		off := subword * 4
   106  
   107  		bits.SetN(&keyLocation, KEY_SUBWORD, 0b11, subword)
   108  		reg.Write(hw.key, keyLocation)
   109  
   110  		if key != nil {
   111  			k := key[off : off+4]
   112  			reg.Write(hw.keydata, binary.LittleEndian.Uint32(k))
   113  		} else {
   114  			reg.Move(hw.keydata, addr+off)
   115  		}
   116  
   117  		subword++
   118  	}
   119  
   120  	return
   121  }
   122  
   123  // SetKey configures an AES-128 key in one of the 4 available slots of the DCP
   124  // key RAM.
   125  func (hw *DCP) SetKey(index int, key []byte) (err error) {
   126  	return hw.setKeyData(index, key, 0)
   127  }
   128  
   129  func pad(buf []byte, extraBlock bool) []byte {
   130  	padLen := 0
   131  	r := len(buf) % aes.BlockSize
   132  
   133  	if r != 0 {
   134  		padLen = aes.BlockSize - r
   135  	} else if extraBlock {
   136  		padLen = aes.BlockSize
   137  	}
   138  
   139  	padding := []byte{(byte)(padLen)}
   140  	padding = bytes.Repeat(padding, padLen)
   141  
   142  	return append(buf, padding...)
   143  }