mirror of https://github.com/yeka/zip.git
Add encrypt & decrypt using zip standard encryption
This commit is contained in:
parent
b64137d021
commit
00f9f9dae9
|
@ -216,3 +216,31 @@ func TestPasswordWriteSimple(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestZipCrypto(t *testing.T) {
|
||||||
|
contents := []byte("Hello World")
|
||||||
|
conLen := len(contents)
|
||||||
|
|
||||||
|
raw := new(bytes.Buffer)
|
||||||
|
zipw := NewWriter(raw)
|
||||||
|
w, err := zipw.Encrypt("hello.txt", "golang", ZipStandardEncryption)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Expected to create a new FileHeader")
|
||||||
|
}
|
||||||
|
n, err := io.Copy(w, bytes.NewReader(contents))
|
||||||
|
if err != nil || n != int64(conLen) {
|
||||||
|
t.Errorf("Expected to write the full contents to the writer.")
|
||||||
|
}
|
||||||
|
zipw.Close()
|
||||||
|
|
||||||
|
zipr, _ := NewReader(bytes.NewReader(raw.Bytes()), int64(raw.Len()))
|
||||||
|
zipr.File[0].SetPassword("golang")
|
||||||
|
r, _ := zipr.File[0].Open()
|
||||||
|
res := new(bytes.Buffer)
|
||||||
|
io.Copy(res, r)
|
||||||
|
r.Close()
|
||||||
|
|
||||||
|
if !bytes.Equal(contents, res.Bytes()) {
|
||||||
|
t.Errorf("Expected the unzipped contents to equal '%s', but was '%s' instead", contents, res.Bytes())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -11,7 +11,7 @@ import (
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/alexmullins/zip"
|
"github.com/yeka/zip"
|
||||||
)
|
)
|
||||||
|
|
||||||
func ExampleWriter() {
|
func ExampleWriter() {
|
||||||
|
@ -81,7 +81,7 @@ func ExampleWriter_Encrypt() {
|
||||||
// write a password zip
|
// write a password zip
|
||||||
raw := new(bytes.Buffer)
|
raw := new(bytes.Buffer)
|
||||||
zipw := zip.NewWriter(raw)
|
zipw := zip.NewWriter(raw)
|
||||||
w, err := zipw.Encrypt("hello.txt", "golang")
|
w, err := zipw.Encrypt("hello.txt", "golang", zip.AES256Encryption)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -145,7 +145,12 @@ func (f *File) Open() (rc io.ReadCloser, err error) {
|
||||||
rr := io.NewSectionReader(f.zipr, f.headerOffset+bodyOffset, size)
|
rr := io.NewSectionReader(f.zipr, f.headerOffset+bodyOffset, size)
|
||||||
// check for encryption
|
// check for encryption
|
||||||
if f.IsEncrypted() {
|
if f.IsEncrypted() {
|
||||||
if r, err = newDecryptionReader(rr, f); err != nil {
|
|
||||||
|
if f.ae == 0 {
|
||||||
|
if r, err = ZipCryptoDecryptor(rr, f.password()); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else if r, err = newDecryptionReader(rr, f); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
24
writer.go
24
writer.go
|
@ -229,14 +229,22 @@ func (w *Writer) CreateHeader(fh *FileHeader) (io.Writer, error) {
|
||||||
// check for password
|
// check for password
|
||||||
var sw io.Writer = fw.compCount
|
var sw io.Writer = fw.compCount
|
||||||
if fh.password != nil {
|
if fh.password != nil {
|
||||||
// we have a password and need to encrypt.
|
if fh.encryption == ZipStandardEncryption {
|
||||||
fh.writeWinZipExtra()
|
ew, err := ZipCryptoEncryptor(sw, fh.password, fw)
|
||||||
fh.Method = 99 // ok to change, we've gotten the comp and wrote extra
|
if err != nil {
|
||||||
ew, err := newEncryptionWriter(sw, fh.password, fw, fh.aesStrength)
|
return nil, err
|
||||||
if err != nil {
|
}
|
||||||
return nil, err
|
sw = ew
|
||||||
|
} else {
|
||||||
|
// we have a password and need to encrypt.
|
||||||
|
fh.writeWinZipExtra()
|
||||||
|
fh.Method = 99 // ok to change, we've gotten the comp and wrote extra
|
||||||
|
ew, err := newEncryptionWriter(sw, fh.password, fw, fh.aesStrength)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
sw = ew
|
||||||
}
|
}
|
||||||
sw = ew
|
|
||||||
}
|
}
|
||||||
var err error
|
var err error
|
||||||
fw.comp, err = comp(sw)
|
fw.comp, err = comp(sw)
|
||||||
|
@ -313,7 +321,7 @@ func (w *fileWriter) close() error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// if encrypted grab the hmac and write it out
|
// if encrypted grab the hmac and write it out
|
||||||
if w.header.IsEncrypted() {
|
if w.header.IsEncrypted() && w.header.encryption != ZipStandardEncryption {
|
||||||
authCode := w.hmac.Sum(nil)
|
authCode := w.hmac.Sum(nil)
|
||||||
authCode = authCode[:10]
|
authCode = authCode[:10]
|
||||||
_, err := w.compCount.Write(authCode)
|
_, err := w.compCount.Write(authCode)
|
||||||
|
|
|
@ -0,0 +1,110 @@
|
||||||
|
package zip
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"bytes"
|
||||||
|
"hash/crc32"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ZipCrypto struct {
|
||||||
|
password []byte
|
||||||
|
Keys [3]uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewZipCrypto(passphrase []byte) *ZipCrypto {
|
||||||
|
z := &ZipCrypto{}
|
||||||
|
z.password = passphrase
|
||||||
|
z.init()
|
||||||
|
return z
|
||||||
|
}
|
||||||
|
|
||||||
|
func (z *ZipCrypto) init() {
|
||||||
|
z.Keys[0] = 0x12345678
|
||||||
|
z.Keys[1] = 0x23456789
|
||||||
|
z.Keys[2] = 0x34567890
|
||||||
|
|
||||||
|
for i := 0; i < len(z.password); i++ {
|
||||||
|
z.updateKeys(z.password[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (z *ZipCrypto) updateKeys(byteValue byte) {
|
||||||
|
z.Keys[0] = crc32update(z.Keys[0], byteValue);
|
||||||
|
z.Keys[1] += z.Keys[0] & 0xff;
|
||||||
|
z.Keys[1] = z.Keys[1] * 134775813 + 1;
|
||||||
|
z.Keys[2] = crc32update(z.Keys[2], (byte) (z.Keys[1] >> 24));
|
||||||
|
}
|
||||||
|
|
||||||
|
func (z *ZipCrypto) magicByte() byte {
|
||||||
|
var t uint32 = z.Keys[2] | 2
|
||||||
|
return byte((t * (t ^ 1)) >> 8)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (z *ZipCrypto) Encrypt(data []byte) []byte {
|
||||||
|
length := len(data)
|
||||||
|
chiper := make([]byte, length)
|
||||||
|
for i := 0; i < length; i++ {
|
||||||
|
v := data[i]
|
||||||
|
chiper[i] = v ^ z.magicByte()
|
||||||
|
z.updateKeys(v)
|
||||||
|
}
|
||||||
|
return chiper
|
||||||
|
}
|
||||||
|
|
||||||
|
func (z *ZipCrypto) Decrypt(chiper []byte) []byte {
|
||||||
|
length := len(chiper)
|
||||||
|
plain := make([]byte, length)
|
||||||
|
for i, c := range chiper {
|
||||||
|
v := c ^ z.magicByte();
|
||||||
|
z.updateKeys(v)
|
||||||
|
plain[i] = v
|
||||||
|
}
|
||||||
|
return plain
|
||||||
|
}
|
||||||
|
|
||||||
|
func crc32update(pCrc32 uint32, bval byte) uint32 {
|
||||||
|
return crc32.IEEETable[(pCrc32 ^ uint32(bval)) & 0xff] ^ (pCrc32 >> 8)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ZipCryptoDecryptor(r *io.SectionReader, password []byte) (*io.SectionReader, error) {
|
||||||
|
z := NewZipCrypto(password)
|
||||||
|
b := make([]byte, r.Size())
|
||||||
|
|
||||||
|
r.Read(b)
|
||||||
|
|
||||||
|
m := z.Decrypt(b)
|
||||||
|
return io.NewSectionReader(bytes.NewReader(m), 12, int64(len(m))), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type zipCryptoWriter struct {
|
||||||
|
w io.Writer
|
||||||
|
z *ZipCrypto
|
||||||
|
first bool
|
||||||
|
fw *fileWriter
|
||||||
|
}
|
||||||
|
|
||||||
|
func (z *zipCryptoWriter) Write(p []byte) (n int, err error) {
|
||||||
|
err = nil
|
||||||
|
if z.first {
|
||||||
|
z.first = false
|
||||||
|
header := []byte{0xF8, 0x53, 0xCF, 0x05, 0x2D, 0xDD, 0xAD, 0xC8, 0x66, 0x3F, 0x8C, 0xAC}
|
||||||
|
header = z.z.Encrypt(header)
|
||||||
|
|
||||||
|
crc := z.fw.ModifiedTime
|
||||||
|
header[10] = byte(crc)
|
||||||
|
header[11] = byte(crc >> 8)
|
||||||
|
|
||||||
|
z.z.init()
|
||||||
|
z.w.Write(z.z.Encrypt(header))
|
||||||
|
n += 12
|
||||||
|
}
|
||||||
|
z.w.Write(z.z.Encrypt(p))
|
||||||
|
//z.fw.FileHeader.CompressedSize += uint32(n)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func ZipCryptoEncryptor(i io.Writer, pass passwordFn, fw *fileWriter) (io.Writer, error) {
|
||||||
|
z := NewZipCrypto(pass())
|
||||||
|
zc := &zipCryptoWriter{i, z, true, fw}
|
||||||
|
return zc, nil
|
||||||
|
}
|
Loading…
Reference in New Issue