Selectable Encryption Method

This commit is contained in:
yeka 2016-10-16 20:47:17 +07:00
parent fcc3c52422
commit b64137d021
4 changed files with 66 additions and 42 deletions

View File

@ -19,7 +19,14 @@ import (
"golang.org/x/crypto/pbkdf2" "golang.org/x/crypto/pbkdf2"
) )
type EncryptionMethod int
const ( const (
ZipStandardEncryption EncryptionMethod = 1
AES128Encryption EncryptionMethod = 2
AES192Encryption EncryptionMethod = 3
AES256Encryption EncryptionMethod = 4
// AES key lengths // AES key lengths
aes128 = 16 aes128 = 16
aes192 = 24 aes192 = 24
@ -379,13 +386,14 @@ func encryptStream(key []byte, w io.Writer) (io.Writer, error) {
// newEncryptionWriter returns an io.Writer that when written to, 1. writes // newEncryptionWriter returns an io.Writer that when written to, 1. writes
// out the salt, 2. writes out pwv, and 3. writes out authenticated, encrypted // out the salt, 2. writes out pwv, and 3. writes out authenticated, encrypted
// data. The authcode will be written out in fileWriter.close(). // data. The authcode will be written out in fileWriter.close().
func newEncryptionWriter(w io.Writer, password passwordFn, fw *fileWriter) (io.Writer, error) { func newEncryptionWriter(w io.Writer, password passwordFn, fw *fileWriter, aesstrength byte) (io.Writer, error) {
var salt [16]byte keysize := aesKeyLen(aesstrength)
salt := make([]byte, keysize / 2)
_, 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(password(), salt[:], aes256) ekey, akey, pwv := generateKeys(password(), salt[:], keysize)
fw.hmac = hmac.New(sha1.New, akey) fw.hmac = hmac.New(sha1.New, akey)
aw := &authWriter{ aw := &authWriter{
hmac: fw.hmac, hmac: fw.hmac,
@ -424,11 +432,23 @@ func (h *FileHeader) writeWinZipExtra() {
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(h.aesStrength) // aes256
eb.uint16(h.Method) // original compression method eb.uint16(h.Method) // original compression method
h.Extra = append(h.Extra, buf[:]...) h.Extra = append(h.Extra, buf[:]...)
} }
func (h *FileHeader) setEncryptionMethod(enc EncryptionMethod) {
h.encryption = enc
switch enc {
case AES128Encryption:
h.aesStrength = 1
case AES192Encryption:
h.aesStrength = 2
case AES256Encryption:
h.aesStrength = 3
}
}
func (h *FileHeader) setEncryptionBit() { func (h *FileHeader) setEncryptionBit() {
h.Flags |= 0x1 h.Flags |= 0x1
} }
@ -452,11 +472,12 @@ type passwordFn func() []byte
// contents will be encrypted with AES-256 using the given password. The // 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 // file's contents must be written to the io.Writer before the next call
// to Create, CreateHeader, or Close. // to Create, CreateHeader, or Close.
func (w *Writer) Encrypt(name string, password string) (io.Writer, error) { func (w *Writer) Encrypt(name string, password string, enc EncryptionMethod) (io.Writer, error) {
fh := &FileHeader{ fh := &FileHeader{
Name: name, Name: name,
Method: Deflate, Method: Deflate,
} }
fh.SetPassword(password) fh.SetPassword(password)
fh.setEncryptionMethod(enc)
return w.CreateHeader(fh) return w.CreateHeader(fh)
} }

View File

@ -175,42 +175,44 @@ func TestPasswordWriteSimple(t *testing.T) {
contents := []byte("Hello World") contents := []byte("Hello World")
conLen := len(contents) conLen := len(contents)
raw := new(bytes.Buffer) for _, enc := range []EncryptionMethod{AES128Encryption, AES192Encryption, AES256Encryption} {
zipw := NewWriter(raw) raw := new(bytes.Buffer)
w, err := zipw.Encrypt("hello.txt", "golang") zipw := NewWriter(raw)
if err != nil { w, err := zipw.Encrypt("hello.txt", "golang", enc)
t.Errorf("Expected to create a new FileHeader") 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) { n, err := io.Copy(w, bytes.NewReader(contents))
t.Errorf("Expected to write the full contents to the writer.") if err != nil || n != int64(conLen) {
} t.Errorf("Expected to write the full contents to the writer.")
zipw.Close() }
zipw.Close()
// Read the zip // Read the zip
buf := new(bytes.Buffer) buf := new(bytes.Buffer)
zipr, err := NewReader(bytes.NewReader(raw.Bytes()), int64(raw.Len())) zipr, err := NewReader(bytes.NewReader(raw.Bytes()), int64(raw.Len()))
if err != nil { if err != nil {
t.Errorf("Expected to open a new zip reader: %v", err) t.Errorf("Expected to open a new zip reader: %v", err)
} }
nn := len(zipr.File) nn := len(zipr.File)
if nn != 1 { if nn != 1 {
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.SetPassword("golang") 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)
} }
n, err = io.Copy(buf, rr) n, err = io.Copy(buf, rr)
if err != nil { if err != nil {
t.Errorf("Expected to write to temporary buffer: %v", err) t.Errorf("Expected to write to temporary buffer: %v", err)
} }
if n != int64(conLen) { if n != int64(conLen) {
t.Errorf("Expected to copy %d bytes to temp buffer, but copied %d bytes instead", conLen, n) t.Errorf("Expected to copy %d bytes to temp buffer, but copied %d bytes instead", conLen, n)
} }
if !bytes.Equal(contents, buf.Bytes()) { if !bytes.Equal(contents, buf.Bytes()) {
t.Errorf("Expected the unzipped contents to equal '%s', but was '%s' instead", contents, buf.Bytes()) t.Errorf("Expected the unzipped contents to equal '%s', but was '%s' instead", contents, buf.Bytes())
}
} }
} }

View File

@ -101,6 +101,7 @@ type FileHeader struct {
// https://www.imperialviolet.org/2015/05/16/aeads.html // https://www.imperialviolet.org/2015/05/16/aeads.html
DeferAuth bool DeferAuth bool
encryption EncryptionMethod
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

View File

@ -232,7 +232,7 @@ func (w *Writer) CreateHeader(fh *FileHeader) (io.Writer, error) {
// we have a password and need to encrypt. // we have a password and need to encrypt.
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.password, fw) ew, err := newEncryptionWriter(sw, fh.password, fw, fh.aesStrength)
if err != nil { if err != nil {
return nil, err return nil, err
} }