cnb.cool/svn/zip is a production-oriented fork of Go's archive/zip with password protection, WinZip AES support, ZipCrypto compatibility, LZMA, Zstandard, fs.FS, raw copy APIs, and hardened path/encryption validation.
StandardEncryption for legacy tools such as Info-ZIP zip / unzip.SetPassword without an explicit method defaults to AES-256.DefaultPBKDF2Iterations = 100000 for new AES files.MinPBKDF2Iterations = 1000 enforced on write/read metadata.LegacyPBKDF2Iterations = 1000 for strict WinZip AES / 7-Zip interoperability.| Method | ID | Notes |
|---|---|---|
Store | 0 | No compression |
Deflate | 8 | Standard ZIP default |
LZMA | 14 | Better ratio, slower |
Zstandard | 93 | Fast compression with good ratio |
LZMA and Zstandard are supported by this library; external ZIP tools may require matching support.
Reader.Files() and Reader.EncryptedFiles().fs.FS support via Reader.Open.File.OpenRaw, Writer.CreateRaw, and Writer.Copy.Writer.AddFS.FileHeader.Modified.go get cnb.cool/svn/zip
Requirements:
github.com/ulikunitz/xz for LZMAgithub.com/klauspost/compress for Zstandardgolang.org/x/crypto/pbkdf2 for AES key derivationpackage main
import (
"log"
"os"
"cnb.cool/svn/zip"
)
func main() {
f, err := os.Create("secret.zip")
if err != nil {
log.Fatal(err)
}
defer f.Close()
zw := zip.NewWriter(f)
defer zw.Close()
w, err := zw.Encrypt("secret.txt", "strong-password", zip.AES256Encryption)
if err != nil {
log.Fatal(err)
}
if _, err := w.Write([]byte("confidential data")); err != nil {
log.Fatal(err)
}
}
fh := &zip.FileHeader{
Name: "confidential.txt",
Method: zip.Deflate,
PBKDF2Iterations: 100000,
}
fh.SetPassword("strong-password")
fh.SetEncryptionMethod(zip.AES256Encryption)
w, err := zw.CreateHeader(fh)
if err != nil {
log.Fatal(err)
}
w.Write([]byte("secret"))
When PBKDF2Iterations is not set for new AES files, cnb.cool/svn/zip uses DefaultPBKDF2Iterations and writes private metadata automatically. When reading third-party AES archives without that metadata, the reader falls back to LegacyPBKDF2Iterations.
fh := &zip.FileHeader{Name: "legacy.txt", Method: zip.Deflate}
fh.SetPassword("password")
fh.SetEncryptionMethod(zip.StandardEncryption)
w, err := zw.CreateHeader(fh)
if err != nil {
log.Fatal(err)
}
w.Write([]byte("compatible with Info-ZIP unzip"))
Use StandardEncryption only when compatibility with old tools is required.
r, err := zip.OpenReader("secret.zip")
if err != nil {
log.Fatal(err)
}
defer r.Close()
for _, f := range r.File {
if f.IsEncrypted() {
f.SetPassword("strong-password")
// For third-party AES files with non-standard iterations and no cnb.cool/svn/zip
// metadata, set f.PBKDF2Iterations explicitly if known.
}
rc, err := f.Open()
if err != nil {
log.Fatal(err)
}
data, err := io.ReadAll(rc)
rc.Close()
if err != nil {
log.Fatal(err)
}
_ = data
}
r, _ := zip.OpenReader("archive.zip")
defer r.Close()
for f := range r.Files() {
fmt.Println(f.Name)
}
for f := range r.EncryptedFiles() {
f.SetPassword("password")
}
r, _ := zip.OpenReader("archive.zip")
defer r.Close()
var fsys fs.FS = &r.Reader
fs.WalkDir(fsys, ".", func(path string, d fs.DirEntry, err error) error {
fmt.Println(path)
return err
})
src, _ := zip.OpenReader("source.zip")
defer src.Close()
dstFile, _ := os.Create("copy.zip")
defer dstFile.Close()
zw := zip.NewWriter(dstFile)
defer zw.Close()
for _, f := range src.File {
if err := zw.Copy(f); err != nil {
log.Fatal(err)
}
}
zw := zip.NewWriter(file)
defer zw.Close()
if err := zw.AddFS(os.DirFS("./myproject")); err != nil {
log.Fatal(err)
}
const CustomMethod = 99
type nopWriteCloser struct{ io.Writer }
func (nopWriteCloser) Close() error { return nil }
zw := zip.NewWriter(file)
zw.RegisterCompressor(CustomMethod, func(w io.Writer) (io.WriteCloser, error) {
return nopWriteCloser{w}, nil
})
r, _ := zip.OpenReader("archive.zip")
r.RegisterDecompressor(CustomMethod, func(r io.Reader) io.ReadCloser {
return io.NopCloser(r)
})
The package now fails early for unsafe or inconsistent inputs:
ErrInsecurePath.ErrPassword.ErrEncryption.MinPBKDF2Iterations return ErrWeakPBKDF2.ErrInvalidPBKDF2.ErrAuthentication.ErrAlgorithm.Entry names must be relative slash-separated ZIP names. Empty names, absolute paths, .., backslashes, and Windows drive paths are rejected.
| Tool | ZipCrypto / StandardEncryption | AES with LegacyPBKDF2Iterations | AES with cnb.cool/svn/zip high iterations |
|---|---|---|---|
| cnb.cool/svn/zip | ✅ | ✅ | ✅ |
Info-ZIP zip / unzip | ✅ | ❌ | ❌ |
| 7-Zip | ✅ | ✅ | ❌ |
| WinZip | ✅ | ✅ | ❌ |
| Windows Explorer | ✅ | ❌ | ❌ |
| macOS Archive Utility | ✅ | ❌ | ❌ |
The current code was verified with the host zip / unzip tools in both directions:
unzip.StandardEncryption archives can be decrypted by unzip -P.SetPassword.StandardEncryption / ZipCrypto is cryptographically weak. Use it only for legacy interoperability.DefaultPBKDF2Iterations or higher for cnb.cool/svn/zip-only workflows.LegacyPBKDF2Iterations only when external WinZip AES tools must decrypt the archive.DeferAuth at its default false unless the caller deliberately accepts unauthenticated plaintext streaming.BSD 3-Clause License. See LICENSE.