cmac.mx raw

   1  // Copyright 2024 The Go Authors. All rights reserved.
   2  // Use of this source code is governed by a BSD-style
   3  // license that can be found in the LICENSE file.
   4  
   5  package gcm
   6  
   7  import (
   8  	"crypto/internal/fips140"
   9  	"crypto/internal/fips140/aes"
  10  	"crypto/internal/fips140/subtle"
  11  )
  12  
  13  // CMAC implements the CMAC mode from NIST SP 800-38B.
  14  //
  15  // It is optimized for use in Counter KDF (SP 800-108r1) and XAES-256-GCM
  16  // (https://c2sp.org/XAES-256-GCM), rather than for exposing it to applications
  17  // as a stand-alone MAC.
  18  type CMAC struct {
  19  	b  aes.Block
  20  	k1 [aes.BlockSize]byte
  21  	k2 [aes.BlockSize]byte
  22  }
  23  
  24  func NewCMAC(b *aes.Block) *CMAC {
  25  	c := &CMAC{b: *b}
  26  	c.deriveSubkeys()
  27  	return c
  28  }
  29  
  30  func (c *CMAC) deriveSubkeys() {
  31  	aes.EncryptBlockInternal(&c.b, c.k1[:], c.k1[:])
  32  	msb := shiftLeft(&c.k1)
  33  	c.k1[len(c.k1)-1] ^= msb * 0b10000111
  34  
  35  	c.k2 = c.k1
  36  	msb = shiftLeft(&c.k2)
  37  	c.k2[len(c.k2)-1] ^= msb * 0b10000111
  38  }
  39  
  40  func (c *CMAC) MAC(m []byte) [aes.BlockSize]byte {
  41  	fips140.RecordApproved()
  42  	_ = c.b // Hoist the nil check out of the loop.
  43  	var x [aes.BlockSize]byte
  44  	if len(m) == 0 {
  45  		// Special-cased as a single empty partial final block.
  46  		x = c.k2
  47  		x[len(m)] ^= 0b10000000
  48  		aes.EncryptBlockInternal(&c.b, x[:], x[:])
  49  		return x
  50  	}
  51  	for len(m) >= aes.BlockSize {
  52  		subtle.XORBytes(x[:], m[:aes.BlockSize], x[:])
  53  		if len(m) == aes.BlockSize {
  54  			// Final complete block.
  55  			subtle.XORBytes(x[:], c.k1[:], x[:])
  56  		}
  57  		aes.EncryptBlockInternal(&c.b, x[:], x[:])
  58  		m = m[aes.BlockSize:]
  59  	}
  60  	if len(m) > 0 {
  61  		// Final incomplete block.
  62  		subtle.XORBytes(x[:], m, x[:])
  63  		subtle.XORBytes(x[:], c.k2[:], x[:])
  64  		x[len(m)] ^= 0b10000000
  65  		aes.EncryptBlockInternal(&c.b, x[:], x[:])
  66  	}
  67  	return x
  68  }
  69  
  70  // shiftLeft sets x to x << 1, and returns MSB₁(x).
  71  func shiftLeft(x *[aes.BlockSize]byte) byte {
  72  	var msb byte
  73  	for i := len(x) - 1; i >= 0; i-- {
  74  		msb, x[i] = x[i]>>7, x[i]<<1|msb
  75  	}
  76  	return msb
  77  }
  78