github.com/f-secure-foundry/tamago@v0.0.0-20220307101044-d73fcdd7f11b/soc/imx6/dcp/key.go (about)

     1  // NXP Data Co-Processor (DCP) driver
     2  // https://github.com/f-secure-foundry/tamago
     3  //
     4  // Copyright (c) F-Secure Corporation
     5  // https://foundry.f-secure.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  	"crypto/aes"
    14  	"encoding/binary"
    15  	"errors"
    16  
    17  	"github.com/f-secure-foundry/tamago/bits"
    18  	"github.com/f-secure-foundry/tamago/dma"
    19  	"github.com/f-secure-foundry/tamago/internal/reg"
    20  	"github.com/f-secure-foundry/tamago/soc/imx6"
    21  )
    22  
    23  // DeriveKeyMemory represents the DMA memory region used for exchanging DCP
    24  // derived keys when the derivation index points to an internal DCP key RAM
    25  // slot.
    26  //
    27  // The default value allocates a DMA region within the i.MX6 On-Chip RAM
    28  // (OCRAM/iRAM) to avoid passing through external RAM.
    29  //
    30  // The DeriveKey() function uses DeriveKeyMemory only when the default DMA
    31  // region is not already set within iRAM.
    32  //
    33  // Applications can override the region with an arbitrary one when the iRAM
    34  // needs to be avoided or is already used as non-default DMA region.
    35  var DeriveKeyMemory = &dma.Region{
    36  	Start: imx6.IRAMStart,
    37  	Size:  imx6.IRAMSize,
    38  }
    39  
    40  func init() {
    41  	DeriveKeyMemory.Init()
    42  }
    43  
    44  // DeriveKey derives a hardware unique key in a manner equivalent to PKCS#11
    45  // C_DeriveKey with CKM_AES_CBC_ENCRYPT_DATA.
    46  //
    47  // The diversifier is AES-CBC encrypted using the internal OTPMK key (when SNVS
    48  // is enabled).
    49  //
    50  // *WARNING*: when SNVS is not enabled a default non-unique test vector is used
    51  // and therefore key derivation is *unsafe*, see imx6.SNVS().
    52  //
    53  // A negative index argument results in the derived key being computed and
    54  // returned.
    55  //
    56  // An index argument equal or greater than 0 moves the derived key directly to
    57  // the corresponding internal DCP key RAM slot (see SetKey()). This is
    58  // accomplished through an iRAM reserved DMA buffer, to ensure that the key is
    59  // never exposed to external RAM or the Go runtime. In this case no key is
    60  // returned by the function.
    61  func DeriveKey(diversifier []byte, iv []byte, index int) (key []byte, err error) {
    62  	if len(iv) != aes.BlockSize {
    63  		return nil, errors.New("invalid IV size")
    64  	}
    65  
    66  	// prepare diversifier for in-place encryption
    67  	key = pad(diversifier, false)
    68  
    69  	region := dma.Default()
    70  
    71  	if index >= 0 {
    72  		// force use of iRAM if not already set as default DMA region
    73  		if !(region.Start > imx6.IRAMStart && region.Start < imx6.IRAMStart+imx6.IRAMSize) {
    74  			region = DeriveKeyMemory
    75  		}
    76  	}
    77  
    78  	pkt := &WorkPacket{}
    79  	pkt.SetCipherDefaults()
    80  
    81  	// Use device-specific hardware key for encryption.
    82  	pkt.Control0 |= 1 << DCP_CTRL0_CIPHER_ENCRYPT
    83  	pkt.Control0 |= 1 << DCP_CTRL0_OTP_KEY
    84  	pkt.Control1 |= KEY_SELECT_UNIQUE_KEY << DCP_CTRL1_KEY_SELECT
    85  
    86  	pkt.BufferSize = uint32(len(key))
    87  
    88  	pkt.SourceBufferAddress = region.Alloc(key, aes.BlockSize)
    89  	defer region.Free(pkt.SourceBufferAddress)
    90  
    91  	pkt.DestinationBufferAddress = pkt.SourceBufferAddress
    92  
    93  	pkt.PayloadPointer = region.Alloc(iv, 0)
    94  	defer region.Free(pkt.PayloadPointer)
    95  
    96  	ptr := region.Alloc(pkt.Bytes(), 0)
    97  	defer region.Free(ptr)
    98  
    99  	err = cmd(ptr, 1)
   100  
   101  	if err != nil {
   102  		return nil, err
   103  	}
   104  
   105  	if index >= 0 {
   106  		err = setKeyData(index, nil, pkt.SourceBufferAddress)
   107  	} else {
   108  		region.Read(pkt.SourceBufferAddress, 0, key)
   109  	}
   110  
   111  	return
   112  }
   113  
   114  func setKeyData(index int, key []byte, addr uint32) (err error) {
   115  	var keyLocation uint32
   116  	var subword uint32
   117  
   118  	if index < 0 || index > 3 {
   119  		return errors.New("key index must be between 0 and 3")
   120  	}
   121  
   122  	if key != nil && len(key) > aes.BlockSize {
   123  		return errors.New("invalid key size")
   124  	}
   125  
   126  	bits.SetN(&keyLocation, KEY_INDEX, 0b11, uint32(index))
   127  
   128  	mux.Lock()
   129  	defer mux.Unlock()
   130  
   131  	for subword < 4 {
   132  		off := subword * 4
   133  
   134  		bits.SetN(&keyLocation, KEY_SUBWORD, 0b11, subword)
   135  		reg.Write(DCP_KEY, keyLocation)
   136  
   137  		if key != nil {
   138  			k := key[off : off+4]
   139  			reg.Write(DCP_KEYDATA, binary.LittleEndian.Uint32(k))
   140  		} else {
   141  			reg.Move(DCP_KEYDATA, addr+off)
   142  		}
   143  
   144  		subword++
   145  	}
   146  
   147  	return
   148  }
   149  
   150  // SetKey configures an AES-128 key in one of the 4 available slots of the DCP
   151  // key RAM.
   152  func SetKey(index int, key []byte) (err error) {
   153  	return setKeyData(index, key, 0)
   154  }