github.com/usbarmory/tamago@v0.0.0-20240508072735-8612bbe1e454/soc/nxp/dcp/cipher.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  	"crypto/aes"
    14  	"errors"
    15  
    16  	"github.com/usbarmory/tamago/bits"
    17  	"github.com/usbarmory/tamago/dma"
    18  )
    19  
    20  // SetCipherDefaults initializes default values for a DCP work packet that
    21  // performs cipher operation.
    22  func (pkt *WorkPacket) SetCipherDefaults() {
    23  	pkt.Control0 |= 1 << DCP_CTRL0_INTERRUPT_ENABL
    24  	pkt.Control0 |= 1 << DCP_CTRL0_DECR_SEMAPHORE
    25  	pkt.Control0 |= 1 << DCP_CTRL0_ENABLE_CIPHER
    26  	pkt.Control0 |= 1 << DCP_CTRL0_CIPHER_INIT
    27  
    28  	pkt.Control1 |= CIPHER_SELECT_AES128 << DCP_CTRL1_CIPHER_SELECT
    29  	pkt.Control1 |= CIPHER_MODE_CBC << DCP_CTRL1_CIPHER_MODE
    30  }
    31  
    32  func (hw *DCP) cipher(buf []byte, index int, iv []byte, enc bool) (err error) {
    33  	if len(buf)%aes.BlockSize != 0 {
    34  		return errors.New("invalid input size")
    35  	}
    36  
    37  	if index < 0 || index > 3 {
    38  		return errors.New("key index must be between 0 and 3")
    39  	}
    40  
    41  	if len(iv) != aes.BlockSize {
    42  		return errors.New("invalid IV size")
    43  	}
    44  
    45  	sourceBufferAddress := dma.Alloc(buf, aes.BlockSize)
    46  	defer dma.Free(sourceBufferAddress)
    47  
    48  	payloadPointer := dma.Alloc(iv, 4)
    49  	defer dma.Free(payloadPointer)
    50  
    51  	pkt := &WorkPacket{}
    52  	pkt.SetCipherDefaults()
    53  
    54  	if enc {
    55  		pkt.Control0 |= 1 << DCP_CTRL0_CIPHER_ENCRYPT
    56  	}
    57  
    58  	// use key RAM slot
    59  	pkt.Control1 |= (uint32(index) & 0xff) << DCP_CTRL1_KEY_SELECT
    60  	pkt.SourceBufferAddress = uint32(sourceBufferAddress)
    61  	pkt.DestinationBufferAddress = pkt.SourceBufferAddress
    62  	pkt.BufferSize = uint32(len(buf))
    63  	pkt.PayloadPointer = uint32(payloadPointer)
    64  
    65  	ptr := dma.Alloc(pkt.Bytes(), 4)
    66  	defer dma.Free(ptr)
    67  
    68  	if err = hw.cmd(ptr, 1); err != nil {
    69  		return
    70  	}
    71  
    72  	dma.Read(sourceBufferAddress, 0, buf)
    73  
    74  	return
    75  }
    76  
    77  // Encrypt performs in-place buffer encryption using AES-128-CBC, the key can
    78  // be selected with the index argument from one previously set with SetKey().
    79  func (hw *DCP) Encrypt(buf []byte, index int, iv []byte) (err error) {
    80  	return hw.cipher(buf, index, iv, true)
    81  }
    82  
    83  // Decrypt performs in-place buffer decryption using AES-128-CBC, the key can
    84  // be selected with the index argument from one previously set with SetKey().
    85  func (hw *DCP) Decrypt(buf []byte, index int, iv []byte) (err error) {
    86  	return hw.cipher(buf, index, iv, false)
    87  }
    88  
    89  // CipherChain performs chained in-place buffer encryption/decryption using
    90  // AES-128-CBC, the key can be selected with the index argument from one
    91  // previously set with SetKey().
    92  //
    93  // The function expects a byte array with concatenated input data and a byte
    94  // array with concatenated initialization vectors, the count and size arguments
    95  // should reflect the number of slices, each to be ciphered and with the
    96  // corresponding initialization vector slice.
    97  func (hw *DCP) CipherChain(buf []byte, ivs []byte, count int, size int, index int, enc bool) (err error) {
    98  	if len(buf) != size*count || len(buf)%aes.BlockSize != 0 {
    99  		return errors.New("invalid input size")
   100  	}
   101  
   102  	if len(ivs) != aes.BlockSize*count {
   103  		return errors.New("invalid IV size")
   104  	}
   105  
   106  	if index < 0 || index > 3 {
   107  		return errors.New("key index must be between 0 and 3")
   108  	}
   109  
   110  	src := dma.Alloc(buf, aes.BlockSize)
   111  	defer dma.Free(src)
   112  
   113  	payloads := dma.Alloc(ivs, 4)
   114  	defer dma.Free(payloads)
   115  
   116  	pkts, pktBuf := dma.Reserve(WorkPacketLength*count, 4)
   117  	defer dma.Release(pkts)
   118  
   119  	pkt := &WorkPacket{}
   120  	pkt.SetCipherDefaults()
   121  	pkt.Control0 |= 1 << DCP_CTRL0_CHAIN
   122  	pkt.BufferSize = uint32(size)
   123  
   124  	bits.Clear(&pkt.Control0, DCP_CTRL0_INTERRUPT_ENABL)
   125  
   126  	if enc {
   127  		pkt.Control0 |= 1 << DCP_CTRL0_CIPHER_ENCRYPT
   128  	}
   129  
   130  	// use key RAM slot
   131  	pkt.Control1 |= (uint32(index) & 0xff) << DCP_CTRL1_KEY_SELECT
   132  
   133  	for i := 0; i < count; i++ {
   134  		pkt.SourceBufferAddress = uint32(src) + uint32(i*size)
   135  		pkt.DestinationBufferAddress = pkt.SourceBufferAddress
   136  		pkt.PayloadPointer = uint32(payloads) + uint32(i*aes.BlockSize)
   137  
   138  		if i < count-1 {
   139  			pkt.NextCmdAddr = uint32(pkts) + uint32((i+1)*WorkPacketLength)
   140  		} else {
   141  			bits.Clear(&pkt.Control0, DCP_CTRL0_CHAIN)
   142  			bits.Set(&pkt.Control0, DCP_CTRL0_INTERRUPT_ENABL)
   143  		}
   144  
   145  		copy(pktBuf[i*WorkPacketLength:], pkt.Bytes())
   146  	}
   147  
   148  	if err = hw.cmd(pkts, count); err != nil {
   149  		return
   150  	}
   151  
   152  	dma.Read(src, 0, buf)
   153  
   154  	return
   155  }