// The source code refers to the go standard library, which will be combined with AES in the future.
// Use of this source code is governed by an MIT license
// that can be found in the LICENSE file.
//
// Counter (CTR) mode.
//
// CTR converts a block cipher into a stream cipher by
// repeatedly encrypting an incrementing counter and
// xoring the resulting stream of data with the input.
//
// See NIST SP 800-38A, pp 13-15
module cipher

import crypto.internal.subtle

struct Ctr {
mut:
	b        Block
	next     []u8
	out      []u8
	out_used int
}

// free the resources taken by the Ctr `c`
@[unsafe]
pub fn (mut x Ctr) free() {
	$if prealloc {
		return
	}
	unsafe {
		// x.b.free() TODO add?
		x.out.free()
		x.next.free()
	}
}

// new_ctr returns a Ctr which encrypts/decrypts using the given Block in
// counter mode. The length of iv must be the same as the Block's block size.
pub fn new_ctr(b Block, iv []u8) Ctr {
	block_size := b.block_size
	if iv.len != block_size {
		panic('cipher.new_cfb: IV length must be equal block size')
	}
	return Ctr{
		b:        b
		out:      []u8{len: b.block_size}
		next:     iv.clone()
		out_used: block_size
	}
}

// xor_key_stream xors each byte in the given slice with a byte from the key stream.
pub fn (mut x Ctr) xor_key_stream(mut dst []u8, src []u8) {
	unsafe {
		mut local_dst := *dst
		mut local_src := src
		if local_dst.len < local_src.len {
			panic('crypto.cipher.xor_key_stream: output smaller than input')
		}

		if subtle.inexact_overlap(local_dst[..local_src.len], local_src) {
			panic('crypto.cipher.xor_key_stream: invalid buffer overlap')
		}

		for local_src.len > 0 {
			if x.out_used == x.out.len {
				x.b.encrypt(mut x.out, x.next)
				x.out_used = 0
			}

			n := xor_bytes(mut local_dst, local_src, x.out[x.out_used..])

			// increment counter
			for i := x.next.len - 1; i >= 0; i-- {
				x.next[i]++
				if x.next[i] != 0 {
					break
				}
			}

			local_dst = local_dst[n..]
			local_src = local_src[n..]
			x.out_used += n
		}
	}
}
