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  }