diff --git a/crypto.go b/crypto.go index 137b674..d99c96d 100644 --- a/crypto.go +++ b/crypto.go @@ -443,6 +443,12 @@ func (h *FileHeader) SetPassword(password string) { } } +func (h *FileHeader) SetDecryptionPassword(password string) { + h.password = func() []byte { + return []byte(password) + } +} + // PasswordFn is a function that returns the password // as a byte slice type passwordFn func() []byte @@ -457,6 +463,6 @@ func (w *Writer) Encrypt(name string, password string) (io.Writer, error) { Name: name, Method: Deflate, } - fh.SetPassword(password) + fh.SetDecryptionPassword(password) return w.CreateHeader(fh) } diff --git a/reader.go b/reader.go index a9e3f6b..54c54b3 100644 --- a/reader.go +++ b/reader.go @@ -143,14 +143,21 @@ func (f *File) Open() (rc io.ReadCloser, err error) { size := int64(f.CompressedSize64) var r io.Reader 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 { + fmt.Printf("CRC32: %X ---- %v\n", f.CRC32, f.CRC32) + if r, err = ZipCryptoDecryptor(rr, f.password()); err != nil { + return + } + } else if r, err = newDecryptionReader(rr, f); err != nil { return } } else { r = rr } + dcomp := decompressor(f.Method) if dcomp == nil { err = ErrAlgorithm @@ -254,14 +261,21 @@ func readDirectoryHeader(f *File, r io.Reader) error { f.CreatorVersion = b.uint16() f.ReaderVersion = b.uint16() f.Flags = b.uint16() + fmt.Printf("FLAG: %X\n", f.Flags) f.Method = b.uint16() + fmt.Printf("METHOD: %X\n", f.Method) f.ModifiedTime = b.uint16() f.ModifiedDate = b.uint16() f.CRC32 = b.uint32() + fmt.Printf("Directory CRC: %X\n", f.CRC32) f.CompressedSize = b.uint32() f.UncompressedSize = b.uint32() f.CompressedSize64 = uint64(f.CompressedSize) f.UncompressedSize64 = uint64(f.UncompressedSize) + fmt.Printf("ZIP Size 1: %v\n", f.CompressedSize) + fmt.Printf("ZIP Size 2: %v\n", f.CompressedSize64) + fmt.Printf("File Size 1: %v\n", f.UncompressedSize) + fmt.Printf("File Size 2: %v\n", f.UncompressedSize64) filenameLen := int(b.uint16()) extraLen := int(b.uint16()) commentLen := int(b.uint16()) diff --git a/temp/int.go b/temp/int.go new file mode 100644 index 0000000..29b1736 --- /dev/null +++ b/temp/int.go @@ -0,0 +1,32 @@ +package main + +import "fmt" + +type Int int + +func (i *Int) Incr(n int) Int { + *i = (*i) + Int(n) + return *i +} + + +func main() { + i := Int(10) + i.Incr(12) + fmt.Println(i) + + b := &Bytes{b: []byte{1, 2, 3, 4, 5}, i: 0} + c := b.Get(0, 3) + c[1] = 11 + fmt.Println(b) + fmt.Println(c) + + j := &Bytes{b: []byte{1, 2, 3, 4, 5}, i: 0} + k := j.IGet(3) + l := j.IGet(2) + k[0] = 10 + l[0] = 11 + fmt.Println(j) + fmt.Println(k) + fmt.Println(l) +} diff --git a/temp/play.go b/temp/play.go new file mode 100644 index 0000000..c5ce803 --- /dev/null +++ b/temp/play.go @@ -0,0 +1,544 @@ +package main + +import ( + zip2 "github.com/yeka/zip" + "bytes" + "os" + "io" + "fmt" + "archive/zip" + "log" + "hash/crc32" + "encoding/binary" + "io/ioutil" +) + +func main() { + //fmt.Printf("%X\n", crc32.Update(0, crc32.IEEETable, []byte{0})) + //return + + password := "zipcrypto" + new(ZipReader).FromBytes(createzip(password), password) + new(ZipReader).Open("java2.zip", password) // FA176C7F + + os.Remove("myzip/test1.zip") + ioutil.WriteFile("myzip/test1.zip", createzip(password), os.ModePerm) + return + //z.Open("java2.zip", "zipcrypto") // FA176C7F + //z.Open("java.zip", "123") + + //z.Open("a.zip") + //z.Open("test.zip") + fmt.Println("=======================================") + return + //unknown1() + //show("java.zip", "zipcrypto") + zipunzip() + return + fmt.Printf("%X\n", 878082192) + show("banyak.zip", "123") + //show("pass.zip", "zipcrypto") + //show("java.zip", "zipcrypto") + //show("a.zip", "aa") // Incorrect password + //show("test.zip", "") + + fmt.Printf("%b\n", zip2.Crc32update(0, 0)) + h := crc32.NewIEEE() + h.Write([]byte{0}) + fmt.Printf("%b\n", h.Sum32()) + fmt.Printf("%b\n", crc32.Update(0, crc32.IEEETable, []byte{0})) + +} + +func play1() { + de := []byte("hello") + //e := zip.NewZipCrypto([]byte("Passwordnya panjang banget kk")) + //en := e.EncryptMessage([]byte("Hello Worlds Muahaha")) + //d := zip.NewZipCrypto([]byte("Passwordnya panjang banget kk")) + //de = d.DecryptMessage(en) + fmt.Println(string(de)) + + os.Exit(0) + buf := new(bytes.Buffer) + //a := zip.NewWriter(buf) + //w, _ := a.Create("a.txt") + //w.Write([]byte(`Hello World`)) + //a.Close() + + f, _ := os.Create("temp/test.zip") + io.Copy(f, buf) + f.Close() + + _ = zip.Deflate +} + +func show(filename string, password string) { + r, err := zip2.OpenReader(filename) + if err != nil { + log.Fatal(err) + } + defer r.Close() + + for _, f := range r.File { + fmt.Printf("------ %s ------\n", f.Name) + + f.SetDecryptionPassword(password) + rc, err := f.Open() + if err != nil { + fmt.Println("Opening error") + log.Print(err) + continue + } + buf := new(bytes.Buffer) + if _, err = io.Copy(buf, rc); err != nil { + fmt.Println("buffering error") + log.Print(err) + continue + } + + fmt.Printf("Size: %d\nContent:\n", buf.Len()) + fmt.Println(string(buf.Bytes())) + //fmt.Println(buf.Bytes()) + rc.Close() + fmt.Println() + fmt.Println() + } +} + +func createzip(password string) []byte { + fmt.Printf("\n\n========================== Writing ========================== \n\n") + mydata, _ := ioutil.ReadFile("a.java") + b := new(bytes.Buffer) + z := zip2.NewWriter(b) + //w, _ := z.Create("a.java") + w, _ := z.Encrypt("a.java", password) + fmt.Println(w.Write(mydata)) + z.Close() + return b.Bytes() +} + +func zipunzip() { + fmt.Printf("\n\n========================== Writing ========================== \n\n") + mydata := []byte(`Hello World`) + password := "golang" + b := new(bytes.Buffer) + z := zip2.NewWriter(b) + //w, _ := z.Create("a.txt") + w, _ := z.Encrypt("a.txt", password) + fmt.Println(w.Write(mydata)) + z.Close() + + fmt.Printf("\n\n========================== Reading ========================== \n\n") + rz, _ := zip2.NewReader(bytes.NewReader(b.Bytes()), int64(b.Len())) + rz.File[0].SetDecryptionPassword(password) + r, _ := rz.File[0].Open() + myresult := new(bytes.Buffer) + io.Copy(myresult, r) + r.Close() + + fmt.Println(mydata) + fmt.Println(myresult.Bytes()) + fmt.Println(0 == bytes.Compare(mydata, myresult.Bytes())) +} + +func unknown1() { + hello := []byte("Hello world\n") + b := new(bytes.Buffer) + + l := &zipwriterboongan{b} + c := &zipwriterboongan{l} + //m := &minues{l} + //c := &minues2{m} + // + ////i := io.MultiWriter(b, m, l, c) + //c.Write(hello) + c.Write(hello) + fmt.Println(string(b.Bytes())) +} + +type zipwriterboongan struct { + w io.Writer +} + +func (m *zipwriterboongan) Write(p []byte) (i int, err error) { + temp := make([]byte, len(p)) + for i, b := range p { + temp[i] = b ^ 0xFF + } + m.w.Write(temp) + return +} + +type ftpwriterboongan struct { + w io.Writer +} + +func (m *ftpwriterboongan) Write(p []byte) (i int, err error) { + temp := make([]byte, len(p)) + for i, b := range p { + temp[i] = b ^ 0xFF + } + m.w.Write(temp) + return +} + +type ZipReader struct { + b *Bytes + password []byte + files []*ZipFile + central_dir []*ZipCentralDirectory + dir_end *ZipDirectoryEnd +} + +type ZipFile struct { + zip *ZipReader + header *ZipFileHeader + data *ZipFileData + descriptor *ZipFileDescriptor +} + +func (zf *ZipFile) init(offset, datasize uint64) { + h := &ZipFileHeader{file: zf} + h.init(offset) + + f := &ZipFileData{file: zf} + f.content = zf.zip.b.IGet(datasize) + + e := &ZipFileDescriptor{file: zf} + if u16x(h.general_purpose_bit_flag) & 8 > 0 { + e.init(zf.zip.b.Pos()) + } + + zf.header = h + zf.data = f + zf.descriptor = e +} + +type ZipFileHeader struct { + file *ZipFile + local_file_header_signature []byte + version_needed_to_extract []byte + general_purpose_bit_flag []byte + compression_method []byte + last_mod_file_time []byte + last_mod_file_date []byte + crc_32 []byte + compressed_size []byte + uncompressed_size []byte + filename_length []byte + extra_field_length []byte + filename []byte + extra_field []byte +} + +func (fh *ZipFileHeader) init(offset uint64) { + b := fh.file.zip.b + b.Offset(offset) + fh.local_file_header_signature = b.IGet(4) + fh.version_needed_to_extract = b.IGet(2) + fh.general_purpose_bit_flag = b.IGet(2) + fh.compression_method = b.IGet(2) + fh.last_mod_file_time = b.IGet(2) + fh.last_mod_file_date = b.IGet(2) + fh.crc_32 = b.IGet(4) + fh.compressed_size = b.IGet(4) + fh.uncompressed_size = b.IGet(4) + fh.filename_length = b.IGet(2) + fh.extra_field_length = b.IGet(2) + fh.filename = b.IGet(u16x(fh.filename_length)) + fh.extra_field = b.IGet(u16x(fh.extra_field_length)) +} + +type ZipFileData struct { + file *ZipFile + content []byte +} + +type ZipFileDescriptor struct { + file *ZipFile + signature []byte + crc_32 []byte + compressed_size []byte + uncompressed_size []byte +} + +func (fd *ZipFileDescriptor) init(offset uint64) { + b := fd.file.zip.b + b.Offset(offset) + sig := b.IGet(4) + if u32x(sig) == 0x08074b50 { + fd.signature = sig + fd.crc_32 = b.IGet(4) + } else { + fd.crc_32 = sig + } + fd.compressed_size = b.IGet(4) + fd.uncompressed_size = b.IGet(4) +} + +type ZipCentralDirectory struct { + zip *ZipReader + central_file_header_signature []byte + version_made_by []byte + version_needed_to_extrac []byte + general_purpose_bit_flag []byte + compression_method []byte + last_mod_file_time []byte + last_mod_file_date []byte + crc_32 []byte + compressed_size []byte + uncompressed_size []byte + filename_length []byte + extra_field_length []byte + file_comment_length []byte + disk_num_start []byte + internal_file_attr []byte + external_file_attr []byte + local_header_relative_offset []byte + filename []byte + extra_field []byte + file_comment []byte +} + +func (cd *ZipCentralDirectory) init(offset uint64) (size uint64) { + cd.zip.b.Offset(offset) + cd.central_file_header_signature = cd.zip.b.IGet(4) + cd.version_made_by = cd.zip.b.IGet(2) + cd.version_needed_to_extrac = cd.zip.b.IGet(2) + cd.general_purpose_bit_flag = cd.zip.b.IGet(2) + cd.compression_method = cd.zip.b.IGet(2) + cd.last_mod_file_time = cd.zip.b.IGet(2) + cd.last_mod_file_date = cd.zip.b.IGet(2) + cd.crc_32 = cd.zip.b.IGet(4) + cd.compressed_size = cd.zip.b.IGet(4) + cd.uncompressed_size = cd.zip.b.IGet(4) + cd.filename_length = cd.zip.b.IGet(2) + cd.extra_field_length = cd.zip.b.IGet(2) + cd.file_comment_length = cd.zip.b.IGet(2) + cd.disk_num_start = cd.zip.b.IGet(2) + cd.internal_file_attr = cd.zip.b.IGet(2) + cd.external_file_attr = cd.zip.b.IGet(4) + cd.local_header_relative_offset = cd.zip.b.IGet(4) + cd.filename = cd.zip.b.IGet(u16x(cd.filename_length)) + cd.extra_field = cd.zip.b.IGet(u16x(cd.extra_field_length)) + cd.file_comment = cd.zip.b.IGet(u16x(cd.file_comment_length)) + size = 46 + u16x(cd.filename_length) + u16x(cd.extra_field_length) + u16x(cd.file_comment_length) + return +} + +type ZipDirectoryEnd struct { + zip *ZipReader + signature []byte + disknum []byte + disknum_start_of_central_directory []byte + entries_in_this_disk []byte + num_of_entries []byte + size_of_central_dir []byte + offset_of_central_dir []byte + zipfile_comment_length []byte + zipfile_comment []byte +} + +func (zde *ZipDirectoryEnd) init(offset uint64) { + zde.zip.b.Offset(offset) + zde.signature = zde.zip.b.IGet(4) + zde.disknum = zde.zip.b.IGet(2) + zde.disknum_start_of_central_directory = zde.zip.b.IGet(2) + zde.entries_in_this_disk = zde.zip.b.IGet(2) + zde.num_of_entries = zde.zip.b.IGet(2) + zde.size_of_central_dir = zde.zip.b.IGet(4) + zde.offset_of_central_dir = zde.zip.b.IGet(4) + zde.zipfile_comment_length = zde.zip.b.IGet(2) + zde.zipfile_comment = zde.zip.b.IGet(u16x(zde.zipfile_comment_length)) +} + +func (z *ZipReader) FromBytes(buf []byte, password string) { + z.b = &Bytes{buf, 0} + z.password = []byte(password) + z.Init() +} + +func (z *ZipReader) Open(filename string, password string) { + buf, _ := ioutil.ReadFile(filename) + z.FromBytes(buf, password) +} + +func (z *ZipReader) Init() { + // First find Directory End + var i uint64 + for i = uint64(len(z.b.b)) - 22; i >= 0; i-- { + if z.b.BGet(i, 4).I64() == 0x06054b50 { + z.dir_end = &ZipDirectoryEnd{zip: z} + z.dir_end.init(i) + break + } + } + + // Read all Central Directory Header + central_dir_offset := u16x(z.dir_end.offset_of_central_dir) + for i = 0; i < u16x(z.dir_end.num_of_entries); i++ { + cdfh := &ZipCentralDirectory{zip: z} + central_dir_offset += cdfh.init(central_dir_offset) + z.central_dir = append(z.central_dir, cdfh) + } + + for i = 0; i < u16x(z.dir_end.num_of_entries); i++ { + zf := &ZipFile{zip: z} + zf.init(u32x(z.central_dir[i].local_header_relative_offset), u32x(z.central_dir[i].compressed_size)) + z.files = append(z.files, zf) + } + + fmt.Printf("%0 X\n", z.files[0].header) + fmt.Printf("%0 X\n", z.files[0].descriptor) + // PlayGround + //fmt.Printf("%v\n", z.files[0].data.content) + //zc := zip2.NewZipCrypto(z.password) + //buf := zc.DecryptMessage(z.files[0].data.content) + ////fmt.Printf("%v\n", buf) + //rc := flate.NewReader(bytes.NewBuffer(buf[12:])) + //defer rc.Close() + //res, err := ioutil.ReadAll(rc) + //if err != nil { + // fmt.Println(err.Error()) + //} + //fmt.Printf("%v\n", string(res)) + + //fmt.Printf("---------------- %v ----------------\n", string(h.filename)) + //fmt.Printf("Signature: %02X\n", h.local_file_header_signature) + //fmt.Printf("Version: %v\n", h.version_needed_to_extract) + //fmt.Printf("General Purpose Bit Flag: %08b\n", h.general_purpose_bit_flag) + //fmt.Printf("Compression Method: %v\n", h.compression_method) + //fmt.Printf("CRC32: %0 X, Compressed: %v, Uncompressed: %v\n", h.crc_32, u16x(h.compressed_size), u16x(h.uncompressed_size)) + //fmt.Printf("FileName Length: %v\n", u16x(h.filename_length)) + //fmt.Printf("ExtraField Length: %v\n", u16x(h.extra_field_length)) + //fmt.Printf("ExtraField: %0 X\n", h.extra_field) + //if u16x(h.general_purpose_bit_flag) & 8 > 0 { + // crc32 := z.b.IGet(4) + // if u32x(crc32) == 0x08074b50 { + // // Signature may or may not be there + // crc32 = z.b.IGet(4) + // } + // csize := z.b.IGet(4) + // usize := z.b.IGet(4) + // fmt.Printf("DataDescriptor:\nCRC32: %0 X, Compressed: %v, Uncompressed: %v, Next Signature: %X\n", crc32, u16x(csize), u16x(usize), z.b.IGet(4)) + //} + // + //crcbuf := make([]byte, 4) + //crcbuf[3] = h.crc_32[3] + //crcbuf[2] = (h.crc_32[3] >> 8) + //crcbuf[1] = (h.crc_32[3] >> 16) + //crcbuf[0] = (h.crc_32[3] >> 24) + // + ////fmt.Printf("CRC BUF: %x %v\n", crcbuf, crcbuf) + // + ////fmt.Printf("% X\n", file_data) + //if u16x(h.general_purpose_bit_flag) & 1 > 0 { + // fmt.Println("Encrypted") + // zc := zip2.NewZipCrypto(z.password) + // + // //result := file_data[0] + // //for i := 0; i < 12; i++ { + // // if i == 11 { + // // //fmt.Printf("HASH MAGIC: %X %v\n", result ^ zc.MagicByte(), result ^ zc.MagicByte()) + // // } + // // zc.UpdateKeys(result ^ zc.MagicByte()) + // // if i < 12 { + // // result = file_data[i + 1] + // // } + // //} + // file_header := zc.DecryptMessage(file_data[0:12]) + // //fmt.Printf("Magic Byte: %X\n", zc.MagicByte()) + // file_data = zc.DecryptMessage(file_data[12:]) + // fmt.Printf("%0 X : %0 X\n", file_header, file_data) + // //fmt.Printf("%v %v\n", string(file_data[0:12]), string(file_data[12:])) + // //file_data = file_data[12:] + //} + //if u16x(h.compression_method) == 8 { + // //fmt.Printf("Compressed: %v\n", len(file_data)) + // rc := flate.NewReader(bytes.NewBuffer(file_data)) + // defer rc.Close() + // res, err := ioutil.ReadAll(rc) + // file_data = res + // if err != nil { + // fmt.Println(err.Error()) + // } + //} + //crc := crc32.NewIEEE() + //crc.Write(file_data) + //fmt.Printf("GO CRC: %X\n", crc.Sum32()) + //fmt.Printf("\n%v\n", string(file_data)) + //} +} + +func (z *ZipReader) DirectoryStructure() { + +} + +func u16(b []byte) uint16 { + return binary.LittleEndian.Uint16(b) +} + +func u32(b []byte) uint32 { + return binary.LittleEndian.Uint32(b) +} + +func u16x(b []byte) uint64 { + return uint64(u16(b)) +} + +func u32x(b []byte) uint64 { + return uint64(u32(b)) +} + +func u64x(b []byte) uint64 { + return uint64(u32(b)) +} + + +type Bytes struct { + b []byte + i uint64 +} + +// Get slice of byte +func (b *Bytes) Get(offset, size uint64) []byte { + return b.b[offset : offset + size] +} + +func (b *Bytes) BGet(offset, size uint64) *Bytes { + return &Bytes{b.Get(offset, size), 0} +} + +// Incremental Get +func (b *Bytes) IGet(size uint64) []byte { + i := b.i + b.i = i + size + return b.b[i : b.i] +} + +// Incremental Get +func (b *Bytes) IBGet(size uint64) *Bytes { + return &Bytes{b.IGet(size), 0} +} + +func (b *Bytes) I64() uint64 { + switch len(b.b) { + case 1: + return uint64(b.b[0]) + case 2: + return u16x(b.b) + case 4: + return u32x(b.b) + case 8: + return u64x(b.b) + } + return 0 +} +// Set offset +func (b *Bytes) Offset(i uint64) { + b.i = i +} + +func (b *Bytes) Pos() uint64 { + return b.i +} \ No newline at end of file diff --git a/writer.go b/writer.go index 27d125e..28d4b03 100644 --- a/writer.go +++ b/writer.go @@ -11,17 +11,18 @@ import ( "hash" "hash/crc32" "io" + "fmt" ) // TODO(adg): support zip file comments -// TODO(adg): support specifying deflate level // Writer implements a zip file writer. type Writer struct { - cw *countWriter - dir []*header - last *fileWriter - closed bool + cw *countWriter + dir []*header + last *fileWriter + closed bool + compressors map[uint16]Compressor } type header struct { @@ -52,7 +53,7 @@ func (w *Writer) Flush() error { } // Close finishes writing the zip file by writing the central directory. -// It does not (and can not) close the underlying writer. +// It does not (and cannot) close the underlying writer. func (w *Writer) Close() error { if w.last != nil && !w.last.closed { if err := w.last.close(); err != nil { @@ -78,7 +79,8 @@ func (w *Writer) Close() error { b.uint16(h.ModifiedTime) b.uint16(h.ModifiedDate) b.uint32(h.CRC32) - if h.isZip64() || h.offset > uint32max { + + if h.isZip64() || h.offset >= uint32max { // the file needs a zip64 header. store maxint in both // 32 bit size fields (and offset later) to signal that the // zip64 extra header should be used. @@ -98,6 +100,7 @@ func (w *Writer) Close() error { b.uint32(h.CompressedSize) b.uint32(h.UncompressedSize) } + b.uint16(uint16(len(h.Name))) b.uint16(uint16(len(h.Extra))) b.uint16(uint16(len(h.Comment))) @@ -211,8 +214,7 @@ func (w *Writer) CreateHeader(fh *FileHeader) (io.Writer, error) { } fh.Flags |= 0x8 // we will write a data descriptor - // TODO(alex): Look at spec and see if these need to be changed - // when using encryption. + fh.CreatorVersion = fh.CreatorVersion&0xff00 | zipVersion20 // preserve compatibility byte fh.ReaderVersion = zipVersion20 @@ -221,18 +223,17 @@ func (w *Writer) CreateHeader(fh *FileHeader) (io.Writer, error) { compCount: &countWriter{w: w.cw}, crc32: crc32.NewIEEE(), } - // Get the compressor before possibly changing Method to 99 due to password - comp := compressor(fh.Method) + comp := w.compressor(fh.Method) if comp == nil { return nil, ErrAlgorithm } // 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) + if !fh.IsEncrypted() { + fh.setEncryptionBit() + } + ew, err := ZipCryptoEncryptor(sw, fh.password, fw) if err != nil { return nil, err } @@ -272,6 +273,7 @@ func writeHeader(w io.Writer, h *FileHeader) error { b.uint32(0) // since we are writing a data descriptor crc32, b.uint32(0) // compressed size, b.uint32(0) // and uncompressed size should be zero + fmt.Printf("File HEader Wring %0 X, %v, %v\n", h.CRC32, h.CompressedSize, h.UncompressedSize) b.uint16(uint16(len(h.Name))) b.uint16(uint16(len(h.Extra))) if _, err := w.Write(buf[:]); err != nil { @@ -284,6 +286,24 @@ func writeHeader(w io.Writer, h *FileHeader) error { return err } +// RegisterCompressor registers or overrides a custom compressor for a specific +// method ID. If a compressor for a given method is not found, Writer will +// default to looking up the compressor at the package level. +func (w *Writer) RegisterCompressor(method uint16, comp Compressor) { + if w.compressors == nil { + w.compressors = make(map[uint16]Compressor) + } + w.compressors[method] = comp +} + +func (w *Writer) compressor(method uint16) Compressor { + comp := w.compressors[method] + if comp == nil { + comp = compressor(method) + } + return comp +} + type fileWriter struct { *header zipw io.Writer @@ -312,21 +332,10 @@ func (w *fileWriter) close() error { if err := w.comp.Close(); err != nil { return err } - // if encrypted grab the hmac and write it out - if w.header.IsEncrypted() { - authCode := w.hmac.Sum(nil) - authCode = authCode[:10] - _, err := w.compCount.Write(authCode) - if err != nil { - return errors.New("zip: error writing authcode") - } - } + // update FileHeader fh := w.header.FileHeader - // ae-2 we don't write out CRC - if !fh.IsEncrypted() { - fh.CRC32 = w.crc32.Sum32() - } + fh.CRC32 = w.crc32.Sum32() fh.CompressedSize64 = uint64(w.compCount.count) fh.UncompressedSize64 = uint64(w.rawCount.count) diff --git a/zipcrypto.go b/zipcrypto.go new file mode 100644 index 0000000..3857365 --- /dev/null +++ b/zipcrypto.go @@ -0,0 +1,121 @@ +package zip + +import ( + "io" + "bytes" + "fmt" + "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) EncryptMessage(plaintext []byte) []byte { + length := len(plaintext) + CipherText := make([]byte, length) + for i := 0; i < length; i++ { + C := plaintext[i] + CipherText[i] = plaintext[i] ^ z.MagicByte() + z.UpdateKeys(C) + } + return CipherText +} + +func (z *ZipCrypto) DecryptMessage(chipertext []byte) []byte { + length := len(chipertext) + PlainText := make([]byte, length) + for i, c := range chipertext { + v := c ^ z.MagicByte(); + z.UpdateKeys(v) + PlainText[i] = v + } + return PlainText +} + +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) { + //return r, nil + z := NewZipCrypto(password) + b := make([]byte, r.Size()) + + r.Read(b) + + m := z.DecryptMessage(b) + fmt.Printf("Header: %X %X %X\n%v\n", m[0:4], m[4:8], m[8:12], m[0:12]) + fmt.Printf("Header: %X\n", m[11]) + fmt.Printf("Content: %X\n", m[12:]) + fmt.Printf("Content: %v\n", string(m[12:])) + //if m[11] > 0xFF { + // return nil, errors.New("incorrect password") + //} + //fmt.Printf("Size: %v\n", len(m)) + 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) { + if z.first { + z.first = false + header := []byte{0xF8, 0x53, 0xCF, 0x05, 0x2D, 0xDD, 0xAD, 0xC8, 0x66, 0x3F, 0x8C, 0xAC} + header = z.z.EncryptMessage(header) + + crc := z.fw.ModifiedTime + header[10] = byte(crc) + header[11] = byte(crc >> 8) + + z.z.Init() + z.w.Write(z.z.EncryptMessage(header)) + n += 12 + //z.z.Init() + } + z.w.Write(z.z.EncryptMessage(p)) + z.fw.FileHeader.CompressedSize += uint32(n) + return +} + +func ZipCryptoEncryptor(i io.Writer, pass passwordFn, fw *fileWriter) (io.Writer, error) { + fmt.Printf("Initializationg...\n") + z := NewZipCrypto(pass()) + zc := &zipCryptoWriter{i, z, true, fw} + return zc, nil +} \ No newline at end of file