ctr.mx raw

   1  // Copyright 2023 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 aes
   6  
   7  import (
   8  	"crypto/internal/fips140"
   9  	"crypto/internal/fips140/alias"
  10  	"crypto/internal/fips140/subtle"
  11  	"crypto/internal/fips140deps/byteorder"
  12  	"math/bits"
  13  )
  14  
  15  type CTR struct {
  16  	b          Block
  17  	ivlo, ivhi uint64 // start counter as 64-bit limbs
  18  	offset     uint64 // for XORKeyStream only
  19  }
  20  
  21  func NewCTR(b *Block, iv []byte) *CTR {
  22  	// Allocate the CTR here, in an easily inlineable function, so
  23  	// the allocation can be done in the caller's stack frame
  24  	// instead of the heap.  See issue 70499.
  25  	c := newCTR(b, iv)
  26  	return &c
  27  }
  28  func newCTR(b *Block, iv []byte) CTR {
  29  	if len(iv) != BlockSize {
  30  		panic("bad IV length")
  31  	}
  32  
  33  	return CTR{
  34  		b:      *b,
  35  		ivlo:   byteorder.BEUint64(iv[8:16]),
  36  		ivhi:   byteorder.BEUint64(iv[0:8]),
  37  		offset: 0,
  38  	}
  39  }
  40  
  41  func (c *CTR) XORKeyStream(dst, src []byte) {
  42  	c.XORKeyStreamAt(dst, src, c.offset)
  43  
  44  	var carry uint64
  45  	c.offset, carry = bits.Add64(c.offset, uint64(len(src)), 0)
  46  	if carry != 0 {
  47  		panic("crypto/aes: counter overflow")
  48  	}
  49  }
  50  
  51  // RoundToBlock is used by CTR_DRBG, which discards the rightmost unused bits at
  52  // each request. It rounds the offset up to the next block boundary.
  53  func RoundToBlock(c *CTR) {
  54  	if remainder := c.offset % BlockSize; remainder != 0 {
  55  		var carry uint64
  56  		c.offset, carry = bits.Add64(c.offset, BlockSize-remainder, 0)
  57  		if carry != 0 {
  58  			panic("crypto/aes: counter overflow")
  59  		}
  60  	}
  61  }
  62  
  63  // XORKeyStreamAt behaves like XORKeyStream but keeps no state, and instead
  64  // seeks into the keystream by the given bytes offset from the start (ignoring
  65  // any XORKetStream calls). This allows for random access into the keystream, up
  66  // to 16 EiB from the start.
  67  func (c *CTR) XORKeyStreamAt(dst, src []byte, offset uint64) {
  68  	if len(dst) < len(src) {
  69  		panic("crypto/aes: len(dst) < len(src)")
  70  	}
  71  	dst = dst[:len(src)]
  72  	if alias.InexactOverlap(dst, src) {
  73  		panic("crypto/aes: invalid buffer overlap")
  74  	}
  75  	fips140.RecordApproved()
  76  
  77  	ivlo, ivhi := add128(c.ivlo, c.ivhi, offset/BlockSize)
  78  
  79  	if blockOffset := offset % BlockSize; blockOffset != 0 {
  80  		// We have a partial block at the beginning.
  81  		var in, out [BlockSize]byte
  82  		copy(in[blockOffset:], src)
  83  		ctrBlocks1(&c.b, &out, &in, ivlo, ivhi)
  84  		n := copy(dst, out[blockOffset:])
  85  		src = src[n:]
  86  		dst = dst[n:]
  87  		ivlo, ivhi = add128(ivlo, ivhi, 1)
  88  	}
  89  
  90  	for len(src) >= 8*BlockSize {
  91  		ctrBlocks8(&c.b, (*[8 * BlockSize]byte)(dst), (*[8 * BlockSize]byte)(src), ivlo, ivhi)
  92  		src = src[8*BlockSize:]
  93  		dst = dst[8*BlockSize:]
  94  		ivlo, ivhi = add128(ivlo, ivhi, 8)
  95  	}
  96  
  97  	// The tail can have at most 7 = 4 + 2 + 1 blocks.
  98  	if len(src) >= 4*BlockSize {
  99  		ctrBlocks4(&c.b, (*[4 * BlockSize]byte)(dst), (*[4 * BlockSize]byte)(src), ivlo, ivhi)
 100  		src = src[4*BlockSize:]
 101  		dst = dst[4*BlockSize:]
 102  		ivlo, ivhi = add128(ivlo, ivhi, 4)
 103  	}
 104  	if len(src) >= 2*BlockSize {
 105  		ctrBlocks2(&c.b, (*[2 * BlockSize]byte)(dst), (*[2 * BlockSize]byte)(src), ivlo, ivhi)
 106  		src = src[2*BlockSize:]
 107  		dst = dst[2*BlockSize:]
 108  		ivlo, ivhi = add128(ivlo, ivhi, 2)
 109  	}
 110  	if len(src) >= 1*BlockSize {
 111  		ctrBlocks1(&c.b, (*[1 * BlockSize]byte)(dst), (*[1 * BlockSize]byte)(src), ivlo, ivhi)
 112  		src = src[1*BlockSize:]
 113  		dst = dst[1*BlockSize:]
 114  		ivlo, ivhi = add128(ivlo, ivhi, 1)
 115  	}
 116  
 117  	if len(src) != 0 {
 118  		// We have a partial block at the end.
 119  		var in, out [BlockSize]byte
 120  		copy(in[:], src)
 121  		ctrBlocks1(&c.b, &out, &in, ivlo, ivhi)
 122  		copy(dst, out[:])
 123  	}
 124  }
 125  
 126  // Each ctrBlocksN function XORs src with N blocks of counter keystream, and
 127  // stores it in dst. src is loaded in full before storing dst, so they can
 128  // overlap even inexactly. The starting counter value is passed in as a pair of
 129  // little-endian 64-bit integers.
 130  
 131  func ctrBlocks(b *Block, dst, src []byte, ivlo, ivhi uint64) {
 132  	buf := []byte{:len(src):8*BlockSize}
 133  	for i := 0; i < len(buf); i += BlockSize {
 134  		byteorder.BEPutUint64(buf[i:], ivhi)
 135  		byteorder.BEPutUint64(buf[i+8:], ivlo)
 136  		ivlo, ivhi = add128(ivlo, ivhi, 1)
 137  		encryptBlock(b, buf[i:], buf[i:])
 138  	}
 139  	// XOR into buf first, in case src and dst overlap (see above).
 140  	subtle.XORBytes(buf, src, buf)
 141  	copy(dst, buf)
 142  }
 143  
 144  func add128(lo, hi uint64, x uint64) (uint64, uint64) {
 145  	lo, c := bits.Add64(lo, x, 0)
 146  	hi, _ = bits.Add64(hi, 0, c)
 147  	return lo, hi
 148  }
 149