mirror of https://github.com/yeka/zip.git
Added little-endian, left-aligned CTR mode
This commit is contained in:
parent
5e7b6fad69
commit
99096bc20c
|
@ -11,10 +11,7 @@ hello.txt -> compress -> encrypt -> .zip -> decrypt -> decompress -> hello.txt
|
||||||
|
|
||||||
Roadmap
|
Roadmap
|
||||||
==============================================================================
|
==============================================================================
|
||||||
Reading - Working on it. Seems like the AES-CTR counter incrementer for WinZip
|
Reading - Works. See ctr.go for implementation.
|
||||||
encryption follows a little-endian, left-aligned counter whereas Go's AES-CTR
|
|
||||||
implementation is big-endian, right-aligned counter. Will have to read block by
|
|
||||||
block and increment the counter by hand.
|
|
||||||
Writing - Not started.
|
Writing - Not started.
|
||||||
Testing - Needs more.
|
Testing - Needs more.
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,104 @@
|
||||||
|
// 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
|
||||||
|
}
|
11
reader.go
11
reader.go
|
@ -231,19 +231,16 @@ func newDecryptionReader(r io.Reader, f *File) (io.Reader, error) {
|
||||||
if !checkAuthentication(data, authcode, authKey) {
|
if !checkAuthentication(data, authcode, authKey) {
|
||||||
return nil, ErrDecryption
|
return nil, ErrDecryption
|
||||||
}
|
}
|
||||||
// set the IV
|
|
||||||
// see: https://forum.golangbridge.org/t/iv-counter-help-for-aes-ctr/1369
|
return decryptStream(data, decKey), nil
|
||||||
var iv [aes.BlockSize]byte
|
|
||||||
iv[0] = 1
|
|
||||||
return decryptStream(data, decKey, iv[:]), nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func decryptStream(ciphertext, key, iv []byte) io.Reader {
|
func decryptStream(ciphertext, key []byte) io.Reader {
|
||||||
block, err := aes.NewCipher(key)
|
block, err := aes.NewCipher(key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
stream := cipher.NewCTR(block, iv)
|
stream := NewWinZipCTR(block)
|
||||||
// Not decrypting stream correctly if the number of bytes being read is >16
|
// Not decrypting stream correctly if the number of bytes being read is >16
|
||||||
reader := cipher.StreamReader{S: stream, R: bytes.NewReader(ciphertext)}
|
reader := cipher.StreamReader{S: stream, R: bytes.NewReader(ciphertext)}
|
||||||
return reader
|
return reader
|
||||||
|
|
Loading…
Reference in New Issue