mirror of https://github.com/yeka/zip.git
105 lines
2.2 KiB
Go
105 lines
2.2 KiB
Go
|
// Copyright 2009 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.
|
||
|
|
||
|
// 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.
|
||
|
|
||
|
// This is a reimplementation of Go's CTR mode to allow
|
||
|
// for little-endian, left-aligned uint32 counter. Go's
|
||
|
// NewCTR follows the NIST Standard SP 800-38A, pp 13-15
|
||
|
// which has a big-endian, right-aligned counter. WinZip
|
||
|
// AES requires the CTR mode to have a little-endian,
|
||
|
// left-aligned counter.
|
||
|
|
||
|
package zip
|
||
|
|
||
|
import "crypto/cipher"
|
||
|
|
||
|
type ctr struct {
|
||
|
b cipher.Block
|
||
|
ctr []byte
|
||
|
out []byte
|
||
|
outUsed int
|
||
|
}
|
||
|
|
||
|
const streamBufferSize = 512
|
||
|
|
||
|
// NewWinZipCTR returns a Stream which encrypts/decrypts using the given Block in
|
||
|
// counter mode. The counter is initially set to 1.
|
||
|
func NewWinZipCTR(block cipher.Block) cipher.Stream {
|
||
|
bufSize := streamBufferSize
|
||
|
if bufSize < block.BlockSize() {
|
||
|
bufSize = block.BlockSize()
|
||
|
}
|
||
|
// Set the IV (counter) to 1
|
||
|
iv := make([]byte, block.BlockSize())
|
||
|
iv[0] = 1
|
||
|
return &ctr{
|
||
|
b: block,
|
||
|
ctr: iv,
|
||
|
out: make([]byte, 0, bufSize),
|
||
|
outUsed: 0,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (x *ctr) refill() {
|
||
|
remain := len(x.out) - x.outUsed
|
||
|
if remain > x.outUsed {
|
||
|
return
|
||
|
}
|
||
|
copy(x.out, x.out[x.outUsed:])
|
||
|
x.out = x.out[:cap(x.out)]
|
||
|
bs := x.b.BlockSize()
|
||
|
for remain < len(x.out)-bs {
|
||
|
x.b.Encrypt(x.out[remain:], x.ctr)
|
||
|
remain += bs
|
||
|
|
||
|
// Increment counter
|
||
|
// for i := len(x.ctr) - 1; i >= 0; i-- {
|
||
|
// x.ctr[i]++
|
||
|
// if x.ctr[i] != 0 {
|
||
|
// break
|
||
|
// }
|
||
|
// }
|
||
|
|
||
|
// Change to allow for little-endian,
|
||
|
// left-aligned counter
|
||
|
for i := 0; i < len(x.ctr); i++ {
|
||
|
x.ctr[i]++
|
||
|
if x.ctr[i] != 0 {
|
||
|
break
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|
||
|
x.out = x.out[:remain]
|
||
|
x.outUsed = 0
|
||
|
}
|
||
|
|
||
|
func (x *ctr) XORKeyStream(dst, src []byte) {
|
||
|
for len(src) > 0 {
|
||
|
if x.outUsed >= len(x.out)-x.b.BlockSize() {
|
||
|
x.refill()
|
||
|
}
|
||
|
n := xorBytes(dst, src, x.out[x.outUsed:])
|
||
|
dst = dst[n:]
|
||
|
src = src[n:]
|
||
|
x.outUsed += n
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func xorBytes(dst, a, b []byte) int {
|
||
|
n := len(a)
|
||
|
if len(b) < n {
|
||
|
n = len(b)
|
||
|
}
|
||
|
for i := 0; i < n; i++ {
|
||
|
dst[i] = a[i] ^ b[i]
|
||
|
}
|
||
|
return n
|
||
|
}
|