forked from mirror/zip
Selectable Encryption Method
This commit is contained in:
parent
f15f84f02c
commit
feedcee201
31
crypto.go
31
crypto.go
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue