1// Copyright 2009 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// Counter (CTR) mode.
6
7// CTR converts a block cipher into a stream cipher by
8// repeatedly encrypting an incrementing counter and
9// xoring the resulting stream of data with the input.
10
11// See NIST SP 800-38A, pp 13-15
12
13package cipher
14
15import (
16	"bytes"
17	"crypto/internal/alias"
18	"crypto/subtle"
19)
20
21type ctr struct {
22	b       Block
23	ctr     []byte
24	out     []byte
25	outUsed int
26}
27
28const streamBufferSize = 512
29
30// ctrAble is an interface implemented by ciphers that have a specific optimized
31// implementation of CTR, like crypto/aes. NewCTR will check for this interface
32// and return the specific Stream if found.
33type ctrAble interface {
34	NewCTR(iv []byte) Stream
35}
36
37// NewCTR returns a [Stream] which encrypts/decrypts using the given [Block] in
38// counter mode. The length of iv must be the same as the [Block]'s block size.
39func NewCTR(block Block, iv []byte) Stream {
40	if ctr, ok := block.(ctrAble); ok {
41		return ctr.NewCTR(iv)
42	}
43	if len(iv) != block.BlockSize() {
44		panic("cipher.NewCTR: IV length must equal block size")
45	}
46	bufSize := streamBufferSize
47	if bufSize < block.BlockSize() {
48		bufSize = block.BlockSize()
49	}
50	return &ctr{
51		b:       block,
52		ctr:     bytes.Clone(iv),
53		out:     make([]byte, 0, bufSize),
54		outUsed: 0,
55	}
56}
57
58func (x *ctr) refill() {
59	remain := len(x.out) - x.outUsed
60	copy(x.out, x.out[x.outUsed:])
61	x.out = x.out[:cap(x.out)]
62	bs := x.b.BlockSize()
63	for remain <= len(x.out)-bs {
64		x.b.Encrypt(x.out[remain:], x.ctr)
65		remain += bs
66
67		// Increment counter
68		for i := len(x.ctr) - 1; i >= 0; i-- {
69			x.ctr[i]++
70			if x.ctr[i] != 0 {
71				break
72			}
73		}
74	}
75	x.out = x.out[:remain]
76	x.outUsed = 0
77}
78
79func (x *ctr) XORKeyStream(dst, src []byte) {
80	if len(dst) < len(src) {
81		panic("crypto/cipher: output smaller than input")
82	}
83	if alias.InexactOverlap(dst[:len(src)], src) {
84		panic("crypto/cipher: invalid buffer overlap")
85	}
86	for len(src) > 0 {
87		if x.outUsed >= len(x.out)-x.b.BlockSize() {
88			x.refill()
89		}
90		n := subtle.XORBytes(dst, src, x.out[x.outUsed:])
91		dst = dst[n:]
92		src = src[n:]
93		x.outUsed += n
94	}
95}
96