Source file src/crypto/internal/fips140/aes/ctr.go

     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 := make([]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  

View as plain text