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

View as plain text