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 }