diff --git a/crypto_test.go b/crypto_test.go index 0c75234..7f86994 100644 --- a/crypto_test.go +++ b/crypto_test.go @@ -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()) + } +} diff --git a/example_test.go b/example_test.go index d625b7e..dce27ee 100644 --- a/example_test.go +++ b/example_test.go @@ -11,7 +11,7 @@ import ( "log" "os" - "github.com/alexmullins/zip" + "github.com/yeka/zip" ) func ExampleWriter() { @@ -81,7 +81,7 @@ func ExampleWriter_Encrypt() { // write a password zip raw := new(bytes.Buffer) zipw := zip.NewWriter(raw) - w, err := zipw.Encrypt("hello.txt", "golang") + w, err := zipw.Encrypt("hello.txt", "golang", zip.AES256Encryption) if err != nil { log.Fatal(err) } diff --git a/reader.go b/reader.go index a9e3f6b..48a7c17 100644 --- a/reader.go +++ b/reader.go @@ -145,7 +145,12 @@ func (f *File) Open() (rc io.ReadCloser, err error) { rr := io.NewSectionReader(f.zipr, f.headerOffset+bodyOffset, size) // check for encryption 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 } } else { diff --git a/writer.go b/writer.go index cc08a3e..37fa662 100644 --- a/writer.go +++ b/writer.go @@ -229,14 +229,22 @@ func (w *Writer) CreateHeader(fh *FileHeader) (io.Writer, error) { // check for password var sw io.Writer = fw.compCount if fh.password != nil { - // 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 + if fh.encryption == ZipStandardEncryption { + ew, err := ZipCryptoEncryptor(sw, fh.password, fw) + 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 fw.comp, err = comp(sw) @@ -313,7 +321,7 @@ func (w *fileWriter) close() error { return err } // 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 = authCode[:10] _, err := w.compCount.Write(authCode) diff --git a/zipcrypto.go b/zipcrypto.go new file mode 100644 index 0000000..e1e7745 --- /dev/null +++ b/zipcrypto.go @@ -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 +} \ No newline at end of file