// Copyright 2010 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// CFB (Cipher Feedback) Mode.

package cipher

type cfb struct {
	b       Block
	out     []byte
	outUsed int
	decrypt bool
}

// NewCFBEncrypter returns a Stream which encrypts with cipher feedback mode,
// using the given Block. The iv must be the same length as the Block's block
// size.
func NewCFBEncrypter(block Block, iv []byte) Stream {
	if len(iv) != block.BlockSize() {
		panic("cipher.NewCBFEncrypter: IV length must equal block size")
	}
	return newCFB(block, iv, false)
}

// NewCFBDecrypter returns a Stream which decrypts with cipher feedback mode,
// using the given Block. The iv must be the same length as the Block's block
// size.
func NewCFBDecrypter(block Block, iv []byte) Stream {
	if len(iv) != block.BlockSize() {
		panic("cipher.NewCBFEncrypter: IV length must equal block size")
	}
	return newCFB(block, iv, true)
}

func newCFB(block Block, iv []byte, decrypt bool) Stream {
	blockSize := block.BlockSize()
	if len(iv) != blockSize {
		return nil
	}

	x := &cfb{
		b:       block,
		out:     make([]byte, blockSize),
		outUsed: 0,
		decrypt: decrypt,
	}
	block.Encrypt(x.out, iv)

	return x
}

func (x *cfb) XORKeyStream(dst, src []byte) {
	for i := 0; i < len(src); i++ {
		if x.outUsed == len(x.out) {
			x.b.Encrypt(x.out, x.out)
			x.outUsed = 0
		}

		if x.decrypt {
			t := src[i]
			dst[i] = src[i] ^ x.out[x.outUsed]
			x.out[x.outUsed] = t
		} else {
			x.out[x.outUsed] ^= src[i]
			dst[i] = x.out[x.outUsed]
		}
		x.outUsed++
	}
}
