tinygo.org/x/drivers@v0.27.1-0.20240509133757-7dbca2a54349/lora/lorawan/cmac.go (about) 1 package lorawan 2 3 import ( 4 "bytes" 5 "crypto/aes" 6 "crypto/cipher" 7 "hash" 8 "unsafe" 9 ) 10 11 type cmacHash struct { 12 ciph cipher.Block 13 k1 []byte 14 k2 []byte 15 data []byte 16 x []byte 17 } 18 19 const ( 20 Size = aes.BlockSize 21 blockSize = Size 22 ) 23 24 var ( 25 subkeyZero []byte 26 subkeyRb []byte 27 ) 28 29 func init() { 30 subkeyZero = bytes.Repeat([]byte{0x00}, blockSize) 31 subkeyRb = append(bytes.Repeat([]byte{0x00}, blockSize-1), 0x87) 32 } 33 34 // New returns an AES-CMAC hash using the supplied key. The key must be 16, 24, 35 // or 32 bytes long. 36 func NewCmac(key []byte) (hash.Hash, error) { 37 // Create a cipher. 38 ciph, err := aes.NewCipher(key) 39 if err != nil { 40 return nil, err 41 } 42 // Set up the hash object. 43 h := &cmacHash{ciph: ciph} 44 h.k1, h.k2 = generateSubkeys(ciph) 45 h.Reset() 46 return h, nil 47 } 48 49 func Xor(dst []byte, a []byte, b []byte) error { 50 if len(dst) != len(a) || len(a) != len(b) { 51 panic("crypto/Xor: bad length") 52 } 53 for i, _ := range a { 54 dst[i] = a[i] ^ b[i] 55 } 56 return nil 57 } 58 59 func (h *cmacHash) Reset() { 60 h.data = h.data[:0] 61 h.x = make([]byte, blockSize) 62 } 63 64 func (h *cmacHash) BlockSize() int { 65 return h.ciph.BlockSize() 66 } 67 68 func (h *cmacHash) Size() int { 69 return h.ciph.BlockSize() 70 } 71 72 func (h *cmacHash) Sum(b []byte) []byte { 73 dataLen := len(h.data) 74 75 // We should have at most one block left. 76 if dataLen > blockSize { 77 panic("cmacHash err1") 78 } 79 80 // Calculate M_last. 81 mLast := make([]byte, blockSize) 82 if dataLen == blockSize { 83 Xor(mLast, h.data, h.k1) 84 } else { 85 // TODO(jacobsa): Accept a destination buffer in common.PadBlock and 86 // simplify this code. 87 Xor(mLast, PadBlock(h.data), h.k2) 88 } 89 90 y := make([]byte, blockSize) 91 Xor(y, mLast, h.x) 92 93 result := make([]byte, blockSize) 94 h.ciph.Encrypt(result, y) 95 96 b = append(b, result...) 97 return b 98 } 99 100 func (h *cmacHash) Write(p []byte) (n int, err error) { 101 n = len(p) 102 103 // First step: consume enough data to expand h.data to a full block, if 104 // possible. 105 { 106 toConsume := blockSize - len(h.data) 107 if toConsume > len(p) { 108 toConsume = len(p) 109 } 110 111 h.data = append(h.data, p[:toConsume]...) 112 p = p[toConsume:] 113 } 114 115 // If there's no data left in p, it means h.data might not be a full block. 116 // Even if it is, we're not sure it's the final block, which we must treat 117 // specially. So we must stop here. 118 if len(p) == 0 { 119 return 120 } 121 122 // h.data is a full block and is not the last; process it. 123 h.writeBlocks(h.data) 124 h.data = h.data[:0] 125 126 // Consume any further full blocks in p that we're sure aren't the last. Note 127 // that we're sure that len(p) is greater than zero here. 128 blocksToProcess := (len(p) - 1) / blockSize 129 bytesToProcess := blocksToProcess * blockSize 130 131 h.writeBlocks(p[:bytesToProcess]) 132 p = p[bytesToProcess:] 133 134 // Store the rest for later. 135 h.data = append(h.data, p...) 136 137 return 138 } 139 140 func (h *cmacHash) writeBlocks(p []byte) { 141 y := make([]byte, blockSize) 142 143 for off := 0; off < len(p); off += blockSize { 144 block := p[off : off+blockSize] 145 146 xorBlock( 147 unsafe.Pointer(&y[0]), 148 unsafe.Pointer(&h.x[0]), 149 unsafe.Pointer(&block[0])) 150 151 h.ciph.Encrypt(h.x, y) 152 } 153 154 return 155 } 156 157 func xorBlock( 158 dstPtr unsafe.Pointer, 159 aPtr unsafe.Pointer, 160 bPtr unsafe.Pointer) { 161 // Check assumptions. (These are compile-time constants, so this should 162 // compile out.) 163 const wordSize = unsafe.Sizeof(uintptr(0)) 164 if blockSize != 4*wordSize { 165 panic("xorBlock err1") 166 } 167 168 // Convert. 169 a := (*[4]uintptr)(aPtr) 170 b := (*[4]uintptr)(bPtr) 171 dst := (*[4]uintptr)(dstPtr) 172 173 // Compute. 174 dst[0] = a[0] ^ b[0] 175 dst[1] = a[1] ^ b[1] 176 dst[2] = a[2] ^ b[2] 177 dst[3] = a[3] ^ b[3] 178 } 179 180 func PadBlock(block []byte) []byte { 181 blockLen := len(block) 182 if blockLen >= aes.BlockSize { 183 panic("PadBlock input must be less than 16 bytes.") 184 } 185 186 result := make([]byte, aes.BlockSize) 187 copy(result, block) 188 result[blockLen] = 0x80 189 190 return result 191 } 192 193 // Given the supplied cipher, whose block size must be 16 bytes, return two 194 // subkeys that can be used in MAC generation. See section 5.3 of NIST SP 195 // 800-38B. Note that the other NIST-approved block size of 8 bytes is not 196 // supported by this function. 197 func generateSubkeys(ciph cipher.Block) (k1 []byte, k2 []byte) { 198 if ciph.BlockSize() != blockSize { 199 panic("generateSubkeys requires a cipher with a block size of 16 bytes.") 200 } 201 202 // Step 1 203 l := make([]byte, blockSize) 204 ciph.Encrypt(l, subkeyZero) 205 206 // Step 2: Derive the first subkey. 207 if Msb(l) == 0 { 208 // TODO(jacobsa): Accept a destination buffer in ShiftLeft and then hoist 209 // the allocation in the else branch below. 210 k1 = ShiftLeft(l) 211 } else { 212 k1 = make([]byte, blockSize) 213 Xor(k1, ShiftLeft(l), subkeyRb) 214 } 215 216 // Step 3: Derive the second subkey. 217 if Msb(k1) == 0 { 218 k2 = ShiftLeft(k1) 219 } else { 220 k2 = make([]byte, blockSize) 221 Xor(k2, ShiftLeft(k1), subkeyRb) 222 } 223 224 return 225 } 226 227 func ShiftLeft(b []byte) []byte { 228 l := len(b) 229 if l == 0 { 230 panic("shiftLeft requires a non-empty buffer.") 231 } 232 233 output := make([]byte, l) 234 235 overflow := byte(0) 236 for i := int(l - 1); i >= 0; i-- { 237 output[i] = b[i] << 1 238 output[i] |= overflow 239 overflow = (b[i] & 0x80) >> 7 240 } 241 242 return output 243 } 244 245 // Msb returns the most significant bit of the supplied data (which must be 246 // non-empty). This is the MSB(L) function of RFC 4493. 247 func Msb(buf []byte) uint8 { 248 if len(buf) == 0 { 249 panic("msb requires non-empty buffer.") 250 } 251 252 return buf[0] >> 7 253 }