Home | History | Annotate | Download | only in cipher
      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 
     13 package cipher
     14 
     15 type ctr struct {
     16 	b       Block
     17 	ctr     []byte
     18 	out     []byte
     19 	outUsed int
     20 }
     21 
     22 const streamBufferSize = 512
     23 
     24 // ctrAble is an interface implemented by ciphers that have a specific optimized
     25 // implementation of CTR, like crypto/aes. NewCTR will check for this interface
     26 // and return the specific Stream if found.
     27 type ctrAble interface {
     28 	NewCTR(iv []byte) Stream
     29 }
     30 
     31 // NewCTR returns a Stream which encrypts/decrypts using the given Block in
     32 // counter mode. The length of iv must be the same as the Block's block size.
     33 func NewCTR(block Block, iv []byte) Stream {
     34 	if ctr, ok := block.(ctrAble); ok {
     35 		return ctr.NewCTR(iv)
     36 	}
     37 	if len(iv) != block.BlockSize() {
     38 		panic("cipher.NewCTR: IV length must equal block size")
     39 	}
     40 	bufSize := streamBufferSize
     41 	if bufSize < block.BlockSize() {
     42 		bufSize = block.BlockSize()
     43 	}
     44 	return &ctr{
     45 		b:       block,
     46 		ctr:     dup(iv),
     47 		out:     make([]byte, 0, bufSize),
     48 		outUsed: 0,
     49 	}
     50 }
     51 
     52 func (x *ctr) refill() {
     53 	remain := len(x.out) - x.outUsed
     54 	copy(x.out, x.out[x.outUsed:])
     55 	x.out = x.out[:cap(x.out)]
     56 	bs := x.b.BlockSize()
     57 	for remain <= len(x.out)-bs {
     58 		x.b.Encrypt(x.out[remain:], x.ctr)
     59 		remain += bs
     60 
     61 		// Increment counter
     62 		for i := len(x.ctr) - 1; i >= 0; i-- {
     63 			x.ctr[i]++
     64 			if x.ctr[i] != 0 {
     65 				break
     66 			}
     67 		}
     68 	}
     69 	x.out = x.out[:remain]
     70 	x.outUsed = 0
     71 }
     72 
     73 func (x *ctr) XORKeyStream(dst, src []byte) {
     74 	for len(src) > 0 {
     75 		if x.outUsed >= len(x.out)-x.b.BlockSize() {
     76 			x.refill()
     77 		}
     78 		n := xorBytes(dst, src, x.out[x.outUsed:])
     79 		dst = dst[n:]
     80 		src = src[n:]
     81 		x.outUsed += n
     82 	}
     83 }
     84