From 41d2e26b69b2c9fe9227453a6f557a542700ac74 Mon Sep 17 00:00:00 2001 From: Francis PEROT Date: Tue, 22 Oct 2019 23:40:47 +0200 Subject: [PATCH] Let the modified date/time be settable when writing a zip file --- .gitignore | 5 +++++ crypto.go | 23 +++++++++++++++++++---- crypto_test.go | 11 ++++++++++- 3 files changed, 34 insertions(+), 5 deletions(-) diff --git a/.gitignore b/.gitignore index 72747d6..bfd966a 100644 --- a/.gitignore +++ b/.gitignore @@ -24,3 +24,8 @@ _testmain.go *.exe *.test *.prof + +# Go dependencies +vendor/ +Gopkg.toml +Gopkg.lock diff --git a/crypto.go b/crypto.go index df23856..3553248 100644 --- a/crypto.go +++ b/crypto.go @@ -15,12 +15,15 @@ import ( "errors" "hash" "io" + "time" "golang.org/x/crypto/pbkdf2" ) +// EncryptionMethod is a type which defines encryption methods type EncryptionMethod int +// Encryption methods and constants const ( StandardEncryption EncryptionMethod = 1 AES128Encryption EncryptionMethod = 2 @@ -388,7 +391,7 @@ func encryptStream(key []byte, w io.Writer) (io.Writer, error) { // data. The authcode will be written out in fileWriter.close(). func newEncryptionWriter(w io.Writer, password passwordFn, fw *fileWriter, aesstrength byte) (io.Writer, error) { keysize := aesKeyLen(aesstrength) - salt := make([]byte, keysize / 2) + salt := make([]byte, keysize/2) _, err := rand.Read(salt[:]) if err != nil { return nil, errors.New("zip: unable to generate random salt") @@ -467,17 +470,29 @@ func (h *FileHeader) SetPassword(password string) { // as a byte slice type passwordFn func() []byte -// Encrypt adds a file to the zip file using the provided name. +// EncryptTime adds a file to the zip file using the provided name. // It returns a Writer to which the file contents should be written. File // contents will be encrypted with AES-256 using the given password. The // file's contents must be written to the io.Writer before the next call -// to Create, CreateHeader, or Close. -func (w *Writer) Encrypt(name string, password string, enc EncryptionMethod) (io.Writer, error) { +// to Create, CreateHeader, or Close. It uses the provided modTime as +// the file's last modified date&time +func (w *Writer) EncryptTime(name string, password string, enc EncryptionMethod, modTime time.Time) (io.Writer, error) { fh := &FileHeader{ Name: name, Method: Deflate, } + fh.SetModTime(modTime) fh.SetPassword(password) fh.setEncryptionMethod(enc) return w.CreateHeader(fh) } + +// Encrypt adds a file to the zip file using the provided name. +// It returns a Writer to which the file contents should be written. File +// contents will be encrypted with AES-256 using the given password. The +// file's contents must be written to the io.Writer before the next call +// to Create, CreateHeader, or Close. It uses the current time as +// the file's last modified date&time +func (w *Writer) Encrypt(name string, password string, enc EncryptionMethod) (io.Writer, error) { + return w.EncryptTime(name, password, enc, time.Now()) +} diff --git a/crypto_test.go b/crypto_test.go index cc5e2de..30a58cc 100644 --- a/crypto_test.go +++ b/crypto_test.go @@ -5,6 +5,7 @@ import ( "io" "path/filepath" "testing" + "time" ) // Test simple password reading. @@ -56,7 +57,7 @@ func TestPasswordHelloWorldAes(t *testing.T) { var b bytes.Buffer for _, f := range r.File { if !f.IsEncrypted() { - t.Errorf("Expected %s to be encrypted.", f.FileInfo().Name) + t.Errorf("Expected %s to be encrypted.", f.FileInfo().Name()) } f.SetPassword("golang") rc, err := f.Open() @@ -235,6 +236,14 @@ func TestZipCrypto(t *testing.T) { zipr, _ := NewReader(bytes.NewReader(raw.Bytes()), int64(raw.Len())) zipr.File[0].SetPassword("golang") + + modifiedTime := zipr.File[0].FileHeader.ModTime() + duration := time.Since(modifiedTime) + // Take care of the 2 seconds granularity + if duration.Seconds() <= -2000 || 4 <= duration.Seconds() { + t.Errorf("Modified time is expected to be very recent") + } + r, _ := zipr.File[0].Open() res := new(bytes.Buffer) io.Copy(res, r)