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