mirror of https://github.com/yeka/zip.git
Clean up the API and writer code.
Added an Example in documentation for code usage. Cleaned up documentation and README.txt.
This commit is contained in:
parent
dff173efe5
commit
e1460042c2
12
README.txt
12
README.txt
|
@ -6,17 +6,19 @@ This package DOES NOT intend to implement the encryption methods
|
||||||
mentioned in the original PKWARE spec (sections 6.0 and 7.0):
|
mentioned in the original PKWARE spec (sections 6.0 and 7.0):
|
||||||
https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT
|
https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT
|
||||||
|
|
||||||
The process
|
Status - Alpha. More tests and code clean up next.
|
||||||
============
|
|
||||||
1. hello.txt -> compressed -> encrypted -> .zip
|
|
||||||
2. .zip -> decrypted -> decompressed -> hello.txt
|
|
||||||
|
|
||||||
Roadmap
|
Roadmap
|
||||||
========
|
========
|
||||||
Reading - Done.
|
Reading - Done.
|
||||||
Writing - Starting.
|
Writing - Done.
|
||||||
Testing - Needs more.
|
Testing - Needs more.
|
||||||
|
|
||||||
|
The process
|
||||||
|
============
|
||||||
|
1. hello.txt -> compressed -> encrypted -> .zip
|
||||||
|
2. .zip -> decrypted -> decompressed -> hello.txt
|
||||||
|
|
||||||
WinZip AES specifies
|
WinZip AES specifies
|
||||||
=====================
|
=====================
|
||||||
1. Encryption-Decryption w/ AES-CTR (128, 192, or 256 bits)
|
1. Encryption-Decryption w/ AES-CTR (128, 192, or 256 bits)
|
||||||
|
|
180
crypto.go
180
crypto.go
|
@ -26,8 +26,22 @@ const (
|
||||||
aes256 = 32
|
aes256 = 32
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func aesKeyLen(strength byte) int {
|
||||||
|
switch strength {
|
||||||
|
case 1:
|
||||||
|
return aes128
|
||||||
|
case 2:
|
||||||
|
return aes192
|
||||||
|
case 3:
|
||||||
|
return aes256
|
||||||
|
default:
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Encryption/Decryption Errors
|
// Encryption/Decryption Errors
|
||||||
var (
|
var (
|
||||||
|
ErrEncryption = errors.New("zip: encryption error")
|
||||||
ErrDecryption = errors.New("zip: decryption error")
|
ErrDecryption = errors.New("zip: decryption error")
|
||||||
ErrPassword = errors.New("zip: invalid password")
|
ErrPassword = errors.New("zip: invalid password")
|
||||||
ErrAuthentication = errors.New("zip: authentication failed")
|
ErrAuthentication = errors.New("zip: authentication failed")
|
||||||
|
@ -38,12 +52,11 @@ var (
|
||||||
// repeatedly encrypting an incrementing counter and
|
// repeatedly encrypting an incrementing counter and
|
||||||
// xoring the resulting stream of data with the input.
|
// xoring the resulting stream of data with the input.
|
||||||
|
|
||||||
// This is a reimplementation of Go's CTR mode to allow
|
// This is a re-implementation of Go's CTR mode to allow
|
||||||
// for little-endian, left-aligned uint32 counter. Go's
|
// for a little-endian, left-aligned uint32 counter, which
|
||||||
// cipher.NewCTR follows the NIST Standard SP 800-38A, pp 13-15
|
// is required for WinZip AES encryption. Go's cipher.NewCTR
|
||||||
// which has a big-endian, right-aligned counter. WinZip
|
// follows the NIST Standard SP 800-38A, pp 13-15
|
||||||
// AES requires the CTR mode to have a little-endian,
|
// which has a big-endian, right-aligned counter.
|
||||||
// left-aligned counter.
|
|
||||||
|
|
||||||
type ctr struct {
|
type ctr struct {
|
||||||
b cipher.Block
|
b cipher.Block
|
||||||
|
@ -55,7 +68,7 @@ type ctr struct {
|
||||||
const streamBufferSize = 512
|
const streamBufferSize = 512
|
||||||
|
|
||||||
// NewWinZipCTR returns a Stream which encrypts/decrypts using the given Block in
|
// NewWinZipCTR returns a Stream which encrypts/decrypts using the given Block in
|
||||||
// counter mode. The counter is initially set to 1.
|
// counter mode. The counter is initially set to 1 per WinZip AES.
|
||||||
func newWinZipCTR(block cipher.Block) cipher.Stream {
|
func newWinZipCTR(block cipher.Block) cipher.Stream {
|
||||||
bufSize := streamBufferSize
|
bufSize := streamBufferSize
|
||||||
if bufSize < block.BlockSize() {
|
if bufSize < block.BlockSize() {
|
||||||
|
@ -128,6 +141,29 @@ func xorBytes(dst, a, b []byte) int {
|
||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// newAuthReader returns either a buffered or streaming authentication reader.
|
||||||
|
// Buffered authentication is recommended. Streaming authentication is only
|
||||||
|
// recommended if: 1. you buffer the data yourself and wait for authentication
|
||||||
|
// before streaming to another source such as the network, or 2. you just don't
|
||||||
|
// care about authenticating unknown ciphertext before use :).
|
||||||
|
func newAuthReader(akey []byte, data, adata io.Reader, streaming bool) io.Reader {
|
||||||
|
ar := authReader{
|
||||||
|
data: data,
|
||||||
|
adata: adata,
|
||||||
|
mac: hmac.New(sha1.New, akey),
|
||||||
|
err: nil,
|
||||||
|
auth: false,
|
||||||
|
}
|
||||||
|
if streaming {
|
||||||
|
return &ar
|
||||||
|
}
|
||||||
|
return &bufferedAuthReader{
|
||||||
|
ar,
|
||||||
|
new(bytes.Buffer),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Streaming authentication
|
||||||
type authReader struct {
|
type authReader struct {
|
||||||
data io.Reader // data to be authenticated
|
data io.Reader // data to be authenticated
|
||||||
adata io.Reader // the authentication code to read
|
adata io.Reader // the authentication code to read
|
||||||
|
@ -136,7 +172,6 @@ type authReader struct {
|
||||||
auth bool
|
auth bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// Streaming authentication
|
|
||||||
func (a *authReader) Read(p []byte) (int, error) {
|
func (a *authReader) Read(p []byte) (int, error) {
|
||||||
if a.err != nil {
|
if a.err != nil {
|
||||||
return 0, a.err
|
return 0, a.err
|
||||||
|
@ -173,34 +208,12 @@ func (a *authReader) Read(p []byte) (int, error) {
|
||||||
return n, a.err
|
return n, a.err
|
||||||
}
|
}
|
||||||
|
|
||||||
// newAuthReader returns either a buffered or streaming authentication reader.
|
// buffered authentication
|
||||||
// Buffered authentication is recommended. Streaming authentication is only
|
|
||||||
// recommended if: 1. you buffer the data yourself and wait for authentication
|
|
||||||
// before streaming to another source such as the network, or 2. you just don't
|
|
||||||
// care about authenticating unknown ciphertext before use :).
|
|
||||||
func newAuthReader(akey []byte, data, adata io.Reader, streaming bool) io.Reader {
|
|
||||||
ar := authReader{
|
|
||||||
data: data,
|
|
||||||
adata: adata,
|
|
||||||
mac: hmac.New(sha1.New, akey),
|
|
||||||
err: nil,
|
|
||||||
auth: false,
|
|
||||||
}
|
|
||||||
if streaming {
|
|
||||||
return &ar
|
|
||||||
}
|
|
||||||
return &bufferedAuthReader{
|
|
||||||
ar,
|
|
||||||
new(bytes.Buffer),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type bufferedAuthReader struct {
|
type bufferedAuthReader struct {
|
||||||
authReader
|
authReader
|
||||||
buf *bytes.Buffer // buffer to store data to authenticate
|
buf *bytes.Buffer // buffer to store data to authenticate
|
||||||
}
|
}
|
||||||
|
|
||||||
// buffered authentication
|
|
||||||
func (a *bufferedAuthReader) Read(b []byte) (int, error) {
|
func (a *bufferedAuthReader) Read(b []byte) (int, error) {
|
||||||
// check for sticky error
|
// check for sticky error
|
||||||
if a.err != nil {
|
if a.err != nil {
|
||||||
|
@ -275,10 +288,10 @@ func newDecryptionReader(r *io.SectionReader, f *File) (io.Reader, error) {
|
||||||
salt := saltpwvv[:saltLen]
|
salt := saltpwvv[:saltLen]
|
||||||
pwvv := saltpwvv[saltLen : saltLen+2]
|
pwvv := saltpwvv[saltLen : saltLen+2]
|
||||||
// generate keys
|
// generate keys
|
||||||
if f.Password == nil {
|
if f.password == nil {
|
||||||
return nil, ErrPassword
|
return nil, ErrPassword
|
||||||
}
|
}
|
||||||
decKey, authKey, pwv := generateKeys(f.Password(), salt, keyLen)
|
decKey, authKey, pwv := generateKeys(f.password(), salt, keyLen)
|
||||||
// check password verifier (pwv)
|
// check password verifier (pwv)
|
||||||
// Change to use crypto/subtle for constant time comparison
|
// Change to use crypto/subtle for constant time comparison
|
||||||
if !checkPasswordVerification(pwv, pwvv) {
|
if !checkPasswordVerification(pwv, pwvv) {
|
||||||
|
@ -294,7 +307,7 @@ func newDecryptionReader(r *io.SectionReader, f *File) (io.Reader, error) {
|
||||||
authOff := dataOff + dataLen
|
authOff := dataOff + dataLen
|
||||||
authcode := io.NewSectionReader(r, authOff, 10)
|
authcode := io.NewSectionReader(r, authOff, 10)
|
||||||
// setup auth reader, (buffered)/streaming
|
// setup auth reader, (buffered)/streaming
|
||||||
ar := newAuthReader(authKey, data, authcode, false)
|
ar := newAuthReader(authKey, data, authcode, f.DeferAuth)
|
||||||
// return decryption reader
|
// return decryption reader
|
||||||
dr := decryptStream(decKey, ar)
|
dr := decryptStream(decKey, ar)
|
||||||
if dr == nil {
|
if dr == nil {
|
||||||
|
@ -313,19 +326,7 @@ func decryptStream(key []byte, ciphertext io.Reader) io.Reader {
|
||||||
return reader
|
return reader
|
||||||
}
|
}
|
||||||
|
|
||||||
func aesKeyLen(strength byte) int {
|
// writes encrypted data to hmac as it passes through
|
||||||
switch strength {
|
|
||||||
case 1:
|
|
||||||
return aes128
|
|
||||||
case 2:
|
|
||||||
return aes192
|
|
||||||
case 3:
|
|
||||||
return aes256
|
|
||||||
default:
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type authWriter struct {
|
type authWriter struct {
|
||||||
hmac hash.Hash // from fw.hmac
|
hmac hash.Hash // from fw.hmac
|
||||||
w io.Writer // this will be the compCount writer
|
w io.Writer // this will be the compCount writer
|
||||||
|
@ -344,8 +345,8 @@ type encryptionWriter struct {
|
||||||
pwv []byte // password verification code to be written
|
pwv []byte // password verification code to be written
|
||||||
salt []byte // salt to be written
|
salt []byte // salt to be written
|
||||||
w io.Writer // where to write the salt + pwv
|
w io.Writer // where to write the salt + pwv
|
||||||
es io.Writer // where to write encrypted file data
|
es io.Writer // where to write plaintext
|
||||||
first bool // first write?
|
first bool // first write
|
||||||
err error // last error
|
err error // last error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -368,16 +369,26 @@ func (ew *encryptionWriter) Write(p []byte) (int, error) {
|
||||||
return ew.es.Write(p)
|
return ew.es.Write(p)
|
||||||
}
|
}
|
||||||
|
|
||||||
// newEncryptionWriter returns a io.Writer that when written to, 1. writes
|
func encryptStream(key []byte, w io.Writer) (io.Writer, error) {
|
||||||
// out the salt, 2. writes out pwv, 3. writes out encrypted the data, and finally
|
block, err := aes.NewCipher(key)
|
||||||
// 4. will write to hmac.
|
if err != nil {
|
||||||
|
return nil, errors.New("zip: couldn't create AES cipher")
|
||||||
|
}
|
||||||
|
stream := newWinZipCTR(block)
|
||||||
|
writer := &cipher.StreamWriter{S: stream, W: w}
|
||||||
|
return writer, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// newEncryptionWriter returns an io.Writer that when written to, 1. writes
|
||||||
|
// out the salt, 2. writes out pwv, and 3. writes out authenticated, encrypted
|
||||||
|
// data. The authcode will be written out in fileWriter.close().
|
||||||
func newEncryptionWriter(w io.Writer, fh *FileHeader, fw *fileWriter) (io.Writer, error) {
|
func newEncryptionWriter(w io.Writer, fh *FileHeader, fw *fileWriter) (io.Writer, error) {
|
||||||
var salt [16]byte
|
var salt [16]byte
|
||||||
_, err := rand.Read(salt[:])
|
_, err := rand.Read(salt[:])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.New("zip: unable to generate random salt")
|
return nil, errors.New("zip: unable to generate random salt")
|
||||||
}
|
}
|
||||||
ekey, akey, pwv := generateKeys(fh.Password(), salt[:], aes256)
|
ekey, akey, pwv := generateKeys(fh.password(), salt[:], aes256)
|
||||||
fw.hmac = hmac.New(sha1.New, akey)
|
fw.hmac = hmac.New(sha1.New, akey)
|
||||||
aw := &authWriter{
|
aw := &authWriter{
|
||||||
hmac: fw.hmac,
|
hmac: fw.hmac,
|
||||||
|
@ -397,29 +408,56 @@ func newEncryptionWriter(w io.Writer, fh *FileHeader, fw *fileWriter) (io.Writer
|
||||||
return ew, nil
|
return ew, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func encryptStream(key []byte, w io.Writer) (io.Writer, error) {
|
// IsEncrypted indicates whether this file's data is encrypted.
|
||||||
block, err := aes.NewCipher(key)
|
func (h *FileHeader) IsEncrypted() bool {
|
||||||
if err != nil {
|
return h.Flags&0x1 == 1
|
||||||
return nil, errors.New("zip: couldn't create AES cipher")
|
|
||||||
}
|
|
||||||
stream := newWinZipCTR(block)
|
|
||||||
writer := &cipher.StreamWriter{S: stream, W: w}
|
|
||||||
return writer, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fh *FileHeader) writeWinZipExtra() {
|
// WinZip AE-2 specifies that no CRC value is written and
|
||||||
|
// should be skipped when reading.
|
||||||
|
func (h *FileHeader) isAE2() bool {
|
||||||
|
return h.ae == 2
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *FileHeader) writeWinZipExtra() {
|
||||||
// total size is 11 bytes
|
// total size is 11 bytes
|
||||||
var buf [11]byte
|
var buf [11]byte
|
||||||
eb := writeBuf(buf[:])
|
eb := writeBuf(buf[:])
|
||||||
eb.uint16(winzipAesExtraId)
|
eb.uint16(winzipAesExtraId) // 0x9901
|
||||||
eb.uint16(7) // following data size is 7
|
eb.uint16(7) // following data size is 7
|
||||||
eb.uint16(2) // ae 2
|
eb.uint16(2) // ae 2
|
||||||
eb.uint16(0x4541) // "AE"
|
eb.uint16(0x4541) // "AE"
|
||||||
eb.uint8(3) // aes256
|
eb.uint8(3) // aes256
|
||||||
eb.uint16(fh.Method) // original compression method
|
eb.uint16(h.Method) // original compression method
|
||||||
fh.Extra = append(fh.Extra, buf[:]...)
|
h.Extra = append(h.Extra, buf[:]...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fh *FileHeader) setEncryptionBit() {
|
func (h *FileHeader) setEncryptionBit() {
|
||||||
fh.Flags |= 0x1
|
h.Flags |= 0x1
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetPassword sets the password used for encryption/decryption.
|
||||||
|
func (h *FileHeader) SetPassword(password string) {
|
||||||
|
if !h.IsEncrypted() {
|
||||||
|
h.setEncryptionBit()
|
||||||
|
}
|
||||||
|
h.password = func() []byte {
|
||||||
|
return []byte(password)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PasswordFn is a function that returns the password
|
||||||
|
// as a byte slice
|
||||||
|
type passwordFn func() []byte
|
||||||
|
|
||||||
|
// Encrypt is similar to Create except that it will encrypt the file contents
|
||||||
|
// using AES-256 with the given password. Must follow all the same constraints
|
||||||
|
// as Create.
|
||||||
|
func (w *Writer) Encrypt(name string, password string) (io.Writer, error) {
|
||||||
|
fh := &FileHeader{
|
||||||
|
Name: name,
|
||||||
|
Method: Deflate,
|
||||||
|
}
|
||||||
|
fh.SetPassword(password)
|
||||||
|
return w.CreateHeader(fh)
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,10 +7,6 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func pwFn() []byte {
|
|
||||||
return []byte("golang")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test simple password reading.
|
// Test simple password reading.
|
||||||
func TestPasswordReadSimple(t *testing.T) {
|
func TestPasswordReadSimple(t *testing.T) {
|
||||||
file := "hello-aes.zip"
|
file := "hello-aes.zip"
|
||||||
|
@ -30,7 +26,7 @@ func TestPasswordReadSimple(t *testing.T) {
|
||||||
if f.Method != 0 {
|
if f.Method != 0 {
|
||||||
t.Errorf("Expected %s to have its Method set to 0.", file)
|
t.Errorf("Expected %s to have its Method set to 0.", file)
|
||||||
}
|
}
|
||||||
f.Password = pwFn
|
f.SetPassword("golang")
|
||||||
rc, err := f.Open()
|
rc, err := f.Open()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Expected to open the readcloser: %v.", err)
|
t.Errorf("Expected to open the readcloser: %v.", err)
|
||||||
|
@ -45,6 +41,7 @@ func TestPasswordReadSimple(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test for multi-file password protected zip.
|
// Test for multi-file password protected zip.
|
||||||
|
// Each file can be protected with a different password.
|
||||||
func TestPasswordHelloWorldAes(t *testing.T) {
|
func TestPasswordHelloWorldAes(t *testing.T) {
|
||||||
file := "world-aes.zip"
|
file := "world-aes.zip"
|
||||||
expecting := "helloworld"
|
expecting := "helloworld"
|
||||||
|
@ -61,7 +58,7 @@ func TestPasswordHelloWorldAes(t *testing.T) {
|
||||||
if !f.IsEncrypted() {
|
if !f.IsEncrypted() {
|
||||||
t.Errorf("Expected %s to be encrypted.", f.FileInfo().Name)
|
t.Errorf("Expected %s to be encrypted.", f.FileInfo().Name)
|
||||||
}
|
}
|
||||||
f.Password = pwFn
|
f.SetPassword("golang")
|
||||||
rc, err := f.Open()
|
rc, err := f.Open()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Expected to open readcloser: %v", err)
|
t.Errorf("Expected to open readcloser: %v", err)
|
||||||
|
@ -91,7 +88,7 @@ func TestPasswordMacbethAct1(t *testing.T) {
|
||||||
if !f.IsEncrypted() {
|
if !f.IsEncrypted() {
|
||||||
t.Errorf("Expected %s to be encrypted.", f.Name)
|
t.Errorf("Expected %s to be encrypted.", f.Name)
|
||||||
}
|
}
|
||||||
f.Password = pwFn
|
f.SetPassword("golang")
|
||||||
rc, err := f.Open()
|
rc, err := f.Open()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Expected to open readcloser: %v", err)
|
t.Errorf("Expected to open readcloser: %v", err)
|
||||||
|
@ -131,7 +128,7 @@ func TestPasswordAE1BadCRC(t *testing.T) {
|
||||||
if !f.IsEncrypted() {
|
if !f.IsEncrypted() {
|
||||||
t.Errorf("Expected zip to be encrypted")
|
t.Errorf("Expected zip to be encrypted")
|
||||||
}
|
}
|
||||||
f.Password = pwFn
|
f.SetPassword("golang")
|
||||||
rc, err := f.Open()
|
rc, err := f.Open()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Expected the readcloser to open.")
|
t.Errorf("Expected the readcloser to open.")
|
||||||
|
@ -162,7 +159,7 @@ func TestPasswordTamperedData(t *testing.T) {
|
||||||
if !f.IsEncrypted() {
|
if !f.IsEncrypted() {
|
||||||
t.Errorf("Expected zip to be encrypted")
|
t.Errorf("Expected zip to be encrypted")
|
||||||
}
|
}
|
||||||
f.Password = pwFn
|
f.SetPassword("golang")
|
||||||
rc, err := f.Open()
|
rc, err := f.Open()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Expected the readcloser to open.")
|
t.Errorf("Expected the readcloser to open.")
|
||||||
|
@ -178,14 +175,9 @@ func TestPasswordWriteSimple(t *testing.T) {
|
||||||
contents := []byte("Hello World")
|
contents := []byte("Hello World")
|
||||||
conLen := len(contents)
|
conLen := len(contents)
|
||||||
|
|
||||||
// Write a zip
|
|
||||||
fh := &FileHeader{
|
|
||||||
Name: "hello.txt",
|
|
||||||
Password: pwFn,
|
|
||||||
}
|
|
||||||
raw := new(bytes.Buffer)
|
raw := new(bytes.Buffer)
|
||||||
zipw := NewWriter(raw)
|
zipw := NewWriter(raw)
|
||||||
w, err := zipw.CreateHeader(fh)
|
w, err := zipw.Encrypt("hello.txt", "golang")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Expected to create a new FileHeader")
|
t.Errorf("Expected to create a new FileHeader")
|
||||||
}
|
}
|
||||||
|
@ -206,7 +198,7 @@ func TestPasswordWriteSimple(t *testing.T) {
|
||||||
t.Errorf("Expected to have one file in the zip archive, but has %d files", nn)
|
t.Errorf("Expected to have one file in the zip archive, but has %d files", nn)
|
||||||
}
|
}
|
||||||
z := zipr.File[0]
|
z := zipr.File[0]
|
||||||
z.Password = pwFn
|
z.SetPassword("golang")
|
||||||
rr, err := z.Open()
|
rr, err := z.Open()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Expected to open the readcloser: %v", err)
|
t.Errorf("Expected to open the readcloser: %v", err)
|
||||||
|
|
|
@ -58,9 +58,6 @@ func ExampleReader() {
|
||||||
// Iterate through the files in the archive,
|
// Iterate through the files in the archive,
|
||||||
// printing some of their contents.
|
// printing some of their contents.
|
||||||
for _, f := range r.File {
|
for _, f := range r.File {
|
||||||
// if f.IsEncrypted() {
|
|
||||||
// f.SetPassword([]byte("password"))
|
|
||||||
// }
|
|
||||||
fmt.Printf("Contents of %s:\n", f.Name)
|
fmt.Printf("Contents of %s:\n", f.Name)
|
||||||
rc, err := f.Open()
|
rc, err := f.Open()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -77,3 +74,40 @@ func ExampleReader() {
|
||||||
// Contents of README:
|
// Contents of README:
|
||||||
// This is the source code repository for the Go programming language.
|
// This is the source code repository for the Go programming language.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ExampleWriter_Encrypt() {
|
||||||
|
contents := []byte("Hello World")
|
||||||
|
|
||||||
|
// write a password zip
|
||||||
|
raw := new(bytes.Buffer)
|
||||||
|
zipw := zip.NewWriter(raw)
|
||||||
|
w, err := zipw.Encrypt("hello.txt", "golang")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
_, err = io.Copy(w, bytes.NewReader(contents))
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
zipw.Close()
|
||||||
|
|
||||||
|
// read the password zip
|
||||||
|
zipr, err := zip.NewReader(bytes.NewReader(raw.Bytes()), int64(raw.Len()))
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
for _, z := range zipr.File {
|
||||||
|
z.SetPassword("golang")
|
||||||
|
rr, err := z.Open()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
_, err = io.Copy(os.Stdout, rr)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
rr.Close()
|
||||||
|
}
|
||||||
|
// Output:
|
||||||
|
// Hello World
|
||||||
|
}
|
||||||
|
|
24
struct.go
24
struct.go
|
@ -17,7 +17,7 @@ for normal archives both fields will be the same. For files requiring
|
||||||
the ZIP64 format the 32 bit fields will be 0xffffffff and the 64 bit
|
the ZIP64 format the 32 bit fields will be 0xffffffff and the 64 bit
|
||||||
fields must be used instead.
|
fields must be used instead.
|
||||||
|
|
||||||
Can read/write AES encrypted files that use Winzip's AES encryption method.
|
Can read/write password protected files that use Winzip's AES encryption method.
|
||||||
See: http://www.winzip.com/aes_info.htm
|
See: http://www.winzip.com/aes_info.htm
|
||||||
*/
|
*/
|
||||||
package zip
|
package zip
|
||||||
|
@ -93,31 +93,19 @@ type FileHeader struct {
|
||||||
ExternalAttrs uint32 // Meaning depends on CreatorVersion
|
ExternalAttrs uint32 // Meaning depends on CreatorVersion
|
||||||
Comment string
|
Comment string
|
||||||
|
|
||||||
// DeferAuth determines whether hmac checks happen before
|
// DeferAuth being set to true will delay hmac auth/integrity
|
||||||
// any ciphertext is decrypted. It is recommended to leave this
|
// checks when decrypting a file meaning the reader will be
|
||||||
// set to false. For more detail:
|
// getting unauthenticated plaintext. It is recommended to leave
|
||||||
|
// this set to false. For more detail:
|
||||||
// https://www.imperialviolet.org/2014/06/27/streamingencryption.html
|
// https://www.imperialviolet.org/2014/06/27/streamingencryption.html
|
||||||
// https://www.imperialviolet.org/2015/05/16/aeads.html
|
// https://www.imperialviolet.org/2015/05/16/aeads.html
|
||||||
DeferAuth bool
|
DeferAuth bool
|
||||||
|
|
||||||
Password PasswordFn // Returns the password to use when reading/writing
|
password passwordFn // Returns the password to use when reading/writing
|
||||||
ae uint16
|
ae uint16
|
||||||
aesStrength byte
|
aesStrength byte
|
||||||
}
|
}
|
||||||
|
|
||||||
// PasswordFn is a function that returns the password
|
|
||||||
// as a byte slice
|
|
||||||
type PasswordFn func() []byte
|
|
||||||
|
|
||||||
// IsEncrypted indicates whether this file's data is encrypted.
|
|
||||||
func (f *FileHeader) IsEncrypted() bool {
|
|
||||||
return f.Flags&0x1 == 1
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *FileHeader) isAE2() bool {
|
|
||||||
return f.ae == 2
|
|
||||||
}
|
|
||||||
|
|
||||||
// FileInfo returns an os.FileInfo for the FileHeader.
|
// FileInfo returns an os.FileInfo for the FileHeader.
|
||||||
func (h *FileHeader) FileInfo() os.FileInfo {
|
func (h *FileHeader) FileInfo() os.FileInfo {
|
||||||
return headerFileInfo{h}
|
return headerFileInfo{h}
|
||||||
|
|
|
@ -228,10 +228,8 @@ 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.
|
// we have a password and need to encrypt.
|
||||||
// 1. Set encryption bit in fh.Flags
|
|
||||||
fh.setEncryptionBit()
|
|
||||||
fh.writeWinZipExtra()
|
fh.writeWinZipExtra()
|
||||||
fh.Method = 99 // ok to change, we've gotten the comp and wrote extra
|
fh.Method = 99 // ok to change, we've gotten the comp and wrote extra
|
||||||
ew, err := newEncryptionWriter(sw, fh, fw)
|
ew, err := newEncryptionWriter(sw, fh, fw)
|
||||||
|
|
Loading…
Reference in New Issue