Add support for traditional PKWARE encryption.
This commit is contained in:
1 parent
03b47ac01d
commit
de0a204ecc
9 files changed
+569
-15
No files matched your search
@@ -538,6 +538,7 @@ LA_CHECK_INCLUDE_FILE("memory.h" HAVE_MEMORY_H)
|
||||
LA_CHECK_INCLUDE_FILE("paths.h" HAVE_PATHS_H)
|
||||
LA_CHECK_INCLUDE_FILE("poll.h" HAVE_POLL_H)
|
||||
LA_CHECK_INCLUDE_FILE("process.h" HAVE_PROCESS_H)
|
||||
LA_CHECK_INCLUDE_FILE("pthread.h" HAVE_PTHREAD_H)
|
||||
LA_CHECK_INCLUDE_FILE("pwd.h" HAVE_PWD_H)
|
||||
LA_CHECK_INCLUDE_FILE("regex.h" HAVE_REGEX_H)
|
||||
LA_CHECK_INCLUDE_FILE("signal.h" HAVE_SIGNAL_H)
|
||||
@@ -1112,6 +1113,7 @@ IF ("CMAKE_C_COMPILER_ID" MATCHES "^GNU$")
|
||||
SET(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -fno-builtin")
|
||||
ENDIF ("CMAKE_C_COMPILER_ID" MATCHES "^GNU$")
|
||||
CHECK_SYMBOL_EXISTS(_CrtSetReportMode "crtdbg.h" HAVE__CrtSetReportMode)
|
||||
CHECK_FUNCTION_EXISTS_GLIBC(arc4random_buf HAVE_CHFLAGS)
|
||||
CHECK_FUNCTION_EXISTS_GLIBC(chflags HAVE_CHFLAGS)
|
||||
CHECK_FUNCTION_EXISTS_GLIBC(chown HAVE_CHOWN)
|
||||
CHECK_FUNCTION_EXISTS_GLIBC(chroot HAVE_CHROOT)
|
||||
|
||||
@@ -126,6 +126,8 @@ libarchive_la_SOURCES= \
|
||||
libarchive/archive_ppmd7.c \
|
||||
libarchive/archive_ppmd7_private.h \
|
||||
libarchive/archive_private.h \
|
||||
libarchive/archive_random.c \
|
||||
libarchive/archive_random_private.h \
|
||||
libarchive/archive_rb.c \
|
||||
libarchive/archive_rb.h \
|
||||
libarchive/archive_read.c \
|
||||
|
||||
@@ -328,6 +328,9 @@ typedef uint64_t uintmax_t;
|
||||
/* True for systems with POSIX ACL support */
|
||||
#cmakedefine HAVE_ACL_USER 1
|
||||
|
||||
/* Define to 1 if you have the `arc4random_buf' function. */
|
||||
#cmakedefine HAVE_ARC4RANDOM_BUF 1
|
||||
|
||||
/* Define to 1 if you have the <attr/xattr.h> header file. */
|
||||
#cmakedefine HAVE_ATTR_XATTR_H 1
|
||||
|
||||
@@ -757,6 +760,9 @@ typedef uint64_t uintmax_t;
|
||||
/* Define to 1 if you have the <process.h> header file. */
|
||||
#cmakedefine HAVE_PROCESS_H 1
|
||||
|
||||
/* Define to 1 if you have the <pthread.h> header file. */
|
||||
#cmakedefine HAVE_PTHREAD_H 1
|
||||
|
||||
/* Define to 1 if you have the <pwd.h> header file. */
|
||||
#cmakedefine HAVE_PWD_H 1
|
||||
|
||||
|
||||
+2
-2
@@ -267,7 +267,7 @@ AS_VAR_IF([ac_cv_have_decl_EXT2_IOC_GETFLAGS], [yes],
|
||||
|
||||
AC_CHECK_HEADERS([inttypes.h io.h langinfo.h limits.h])
|
||||
AC_CHECK_HEADERS([linux/fiemap.h linux/fs.h linux/magic.h linux/types.h])
|
||||
AC_CHECK_HEADERS([locale.h paths.h poll.h pwd.h signal.h spawn.h])
|
||||
AC_CHECK_HEADERS([locale.h paths.h poll.h pthread.h pwd.h signal.h spawn.h])
|
||||
AC_CHECK_HEADERS([stdarg.h stdint.h stdlib.h string.h])
|
||||
AC_CHECK_HEADERS([sys/acl.h sys/cdefs.h sys/extattr.h])
|
||||
AC_CHECK_HEADERS([sys/ioctl.h sys/mkdev.h sys/mount.h])
|
||||
@@ -569,7 +569,7 @@ AC_FUNC_VPRINTF
|
||||
# To avoid necessity for including windows.h or special forward declaration
|
||||
# workarounds, we use 'void *' for 'struct SECURITY_ATTRIBUTES *'
|
||||
AC_CHECK_STDCALL_FUNC([CreateHardLinkA],[const char *, const char *, void *])
|
||||
AC_CHECK_FUNCS([chflags chown chroot ctime_r dirfd])
|
||||
AC_CHECK_FUNCS([arc4random_buf chflags chown chroot ctime_r dirfd])
|
||||
AC_CHECK_FUNCS([fchdir fchflags fchmod fchown fcntl fdopendir fork])
|
||||
AC_CHECK_FUNCS([fstat fstatat fstatfs fstatvfs ftruncate])
|
||||
AC_CHECK_FUNCS([futimens futimes futimesat])
|
||||
|
||||
@@ -33,6 +33,7 @@ SET(libarchive_SOURCES
|
||||
archive_entry_xattr.c
|
||||
archive_getdate.c
|
||||
archive_hmac.c
|
||||
archive_hmac_private.c
|
||||
archive_match.c
|
||||
archive_options.c
|
||||
archive_options_private.h
|
||||
|
||||
@@ -0,0 +1,266 @@
|
||||
/*-
|
||||
* Copyright (c) 2014 Michihiro NAKAJIMA
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "archive_platform.h"
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#ifdef HAVE_STDLIB_H
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
|
||||
#if !defined(HAVE_ARC4RANDOM_BUF) && (!defined(_WIN32) || defined(__CYGWIN__))
|
||||
|
||||
#ifdef HAVE_FCNTL
|
||||
#include <fcntl.h>
|
||||
#endif
|
||||
#ifdef HAVE_LIMITS_H
|
||||
#include <limits.h>
|
||||
#endif
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#ifdef HAVE_SYS_TYPES_H
|
||||
#include <sys/types.h>
|
||||
#endif
|
||||
#ifdef HAVE_SYS_TIME_H
|
||||
#include <sys/time.h>
|
||||
#endif
|
||||
#ifdef HAVE_PTHREAD_H
|
||||
#include <pthread.h>
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_WINCRYPT_H) && !defined(__CYGWIN__)
|
||||
#include <wincrypt.h>
|
||||
#endif
|
||||
|
||||
static void arc4random_buf(void *, size_t);
|
||||
|
||||
#endif /* HAVE_ARC4RANDOM_BUF */
|
||||
|
||||
#include "archive.h"
|
||||
#include "archive_random_private.h"
|
||||
|
||||
/*
|
||||
* Random number generator function.
|
||||
* This simply calls arc4random_buf function if the platform provides it.
|
||||
*/
|
||||
|
||||
int
|
||||
archive_random(void *buf, size_t nbytes)
|
||||
{
|
||||
#if defined(_WIN32) && !defined(__CYGWIN__)
|
||||
HCRYPTPROV hProv;
|
||||
BOOL success;
|
||||
|
||||
success = CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL,
|
||||
CRYPT_VERIFYCONTEXT);
|
||||
if (!success && GetLastError() == NTE_BAD_KEYSET) {
|
||||
success = CryptAcquireContext(&hProv, NULL, NULL,
|
||||
PROV_RSA_FULL, CRYPT_NEWKEYSET);
|
||||
}
|
||||
if (success) {
|
||||
success = CryptGenRandom(hProv, (DWORD)nbytes, (BYTE*)buf))
|
||||
CryptReleaseContext(hProv, 0);
|
||||
if (success)
|
||||
return ARCHIVE_OK;
|
||||
}
|
||||
/* TODO: Does this case really happen? */
|
||||
return ARCHIVE_FAILED;
|
||||
#else
|
||||
arc4random_buf(buf, nbytes);
|
||||
return ARCHIVE_OK;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if !defined(HAVE_ARC4RANDOM_BUF) && (!defined(_WIN32) || defined(__CYGWIN__))
|
||||
|
||||
/* $OpenBSD: arc4random.c,v 1.24 2013/06/11 16:59:50 deraadt Exp $ */
|
||||
/*
|
||||
* Copyright (c) 1996, David Mazieres <dm@uun.org>
|
||||
* Copyright (c) 2008, Damien Miller <djm@openbsd.org>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Arc4 random number generator for OpenBSD.
|
||||
*
|
||||
* This code is derived from section 17.1 of Applied Cryptography,
|
||||
* second edition, which describes a stream cipher allegedly
|
||||
* compatible with RSA Labs "RC4" cipher (the actual description of
|
||||
* which is a trade secret). The same algorithm is used as a stream
|
||||
* cipher called "arcfour" in Tatu Ylonen's ssh package.
|
||||
*
|
||||
* RC4 is a registered trademark of RSA Laboratories.
|
||||
*/
|
||||
|
||||
#ifdef __GNUC__
|
||||
#define inline __inline
|
||||
#else /* !__GNUC__ */
|
||||
#define inline
|
||||
#endif /* !__GNUC__ */
|
||||
|
||||
struct arc4_stream {
|
||||
u_int8_t i;
|
||||
u_int8_t j;
|
||||
u_int8_t s[256];
|
||||
};
|
||||
|
||||
static pthread_mutex_t arc4random_mtx = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
#define RANDOMDEV "/dev/urandom"
|
||||
#define KEYSIZE 128
|
||||
#ifdef HAVE_PTHREAD_H
|
||||
#define _ARC4_LOCK() pthread_mutex_lock(&arc4random_mtx);
|
||||
#define _ARC4_UNLOCK() pthread_mutex_unlock(&arc4random_mtx);
|
||||
#else
|
||||
#define _ARC4_LOCK()
|
||||
#define _ARC4_UNLOCK()
|
||||
#endif
|
||||
|
||||
static int rs_initialized;
|
||||
static struct arc4_stream rs;
|
||||
static pid_t arc4_stir_pid;
|
||||
static int arc4_count;
|
||||
|
||||
static inline u_int8_t arc4_getbyte(void);
|
||||
static void arc4_stir(void);
|
||||
|
||||
static inline void
|
||||
arc4_init(void)
|
||||
{
|
||||
int n;
|
||||
|
||||
for (n = 0; n < 256; n++)
|
||||
rs.s[n] = n;
|
||||
rs.i = 0;
|
||||
rs.j = 0;
|
||||
}
|
||||
|
||||
static inline void
|
||||
arc4_addrandom(u_char *dat, int datlen)
|
||||
{
|
||||
int n;
|
||||
u_int8_t si;
|
||||
|
||||
rs.i--;
|
||||
for (n = 0; n < 256; n++) {
|
||||
rs.i = (rs.i + 1);
|
||||
si = rs.s[rs.i];
|
||||
rs.j = (rs.j + si + dat[n % datlen]);
|
||||
rs.s[rs.i] = rs.s[rs.j];
|
||||
rs.s[rs.j] = si;
|
||||
}
|
||||
rs.j = rs.i;
|
||||
}
|
||||
|
||||
static void
|
||||
arc4_stir(void)
|
||||
{
|
||||
int done, fd, i;
|
||||
struct {
|
||||
struct timeval tv;
|
||||
pid_t pid;
|
||||
u_char rnd[KEYSIZE];
|
||||
} rdat;
|
||||
|
||||
if (!rs_initialized) {
|
||||
arc4_init();
|
||||
rs_initialized = 1;
|
||||
}
|
||||
done = 0;
|
||||
fd = open(RANDOMDEV, O_RDONLY | O_CLOEXEC, 0);
|
||||
if (fd >= 0) {
|
||||
if (read(fd, &rdat, KEYSIZE) == KEYSIZE)
|
||||
done = 1;
|
||||
(void)close(fd);
|
||||
}
|
||||
if (!done) {
|
||||
(void)gettimeofday(&rdat.tv, NULL);
|
||||
rdat.pid = getpid();
|
||||
/* We'll just take whatever was on the stack too... */
|
||||
}
|
||||
|
||||
arc4_addrandom((u_char *)&rdat, KEYSIZE);
|
||||
|
||||
/*
|
||||
* Discard early keystream, as per recommendations in:
|
||||
* "(Not So) Random Shuffles of RC4" by Ilya Mironov.
|
||||
*/
|
||||
for (i = 0; i < 1024; i++)
|
||||
(void)arc4_getbyte();
|
||||
arc4_count = 1600000;
|
||||
}
|
||||
|
||||
static void
|
||||
arc4_stir_if_needed(void)
|
||||
{
|
||||
pid_t pid = getpid();
|
||||
|
||||
if (arc4_count <= 0 || !rs_initialized || arc4_stir_pid != pid) {
|
||||
arc4_stir_pid = pid;
|
||||
arc4_stir();
|
||||
}
|
||||
}
|
||||
|
||||
static inline u_int8_t
|
||||
arc4_getbyte(void)
|
||||
{
|
||||
u_int8_t si, sj;
|
||||
|
||||
rs.i = (rs.i + 1);
|
||||
si = rs.s[rs.i];
|
||||
rs.j = (rs.j + si);
|
||||
sj = rs.s[rs.j];
|
||||
rs.s[rs.i] = sj;
|
||||
rs.s[rs.j] = si;
|
||||
return (rs.s[(si + sj) & 0xff]);
|
||||
}
|
||||
|
||||
static void
|
||||
arc4random_buf(void *_buf, size_t n)
|
||||
{
|
||||
u_char *buf = (u_char *)_buf;
|
||||
_ARC4_LOCK();
|
||||
arc4_stir_if_needed();
|
||||
while (n--) {
|
||||
if (--arc4_count <= 0)
|
||||
arc4_stir();
|
||||
buf[n] = arc4_getbyte();
|
||||
}
|
||||
_ARC4_UNLOCK();
|
||||
}
|
||||
|
||||
#endif /* !HAVE_ARC4RANDOM_BUF */
|
||||
@@ -0,0 +1,36 @@
|
||||
/*-
|
||||
* Copyright (c) 2014 Michihiro NAKAJIMA
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef __LIBARCHIVE_BUILD
|
||||
#error This header is only to be used internally to libarchive.
|
||||
#endif
|
||||
|
||||
#ifndef ARCHIVE_RANDOM_PRIVATE_H_INCLUDED
|
||||
#define ARCHIVE_RANDOM_PRIVATE_H_INCLUDED
|
||||
|
||||
/* Random number generator. */
|
||||
int archive_random(void *buf, size_t nbytes);
|
||||
|
||||
#endif /* ARCHIVE_RANDOM_PRIVATE_H_INCLUDED */
|
||||
@@ -1,7 +1,7 @@
|
||||
/*-
|
||||
* Copyright (c) 2008 Anselm Strauss
|
||||
* Copyright (c) 2009 Joerg Sonnenberger
|
||||
* Copyright (c) 2011-2012 Michihiro NAKAJIMA
|
||||
* Copyright (c) 2011-2012,2014 Michihiro NAKAJIMA
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@@ -53,16 +53,17 @@ __FBSDID("$FreeBSD: head/lib/libarchive/archive_write_set_format_zip.c 201168 20
|
||||
#include "archive_entry.h"
|
||||
#include "archive_entry_locale.h"
|
||||
#include "archive_private.h"
|
||||
#include "archive_random_private.h"
|
||||
#include "archive_write_private.h"
|
||||
|
||||
#ifndef HAVE_ZLIB_H
|
||||
#include "archive_crc32.h"
|
||||
#endif
|
||||
|
||||
#define ZIP_ENTRY_FLAG_ENCRYPTED (1<<0)
|
||||
#define ZIP_ENTRY_FLAG_LENGTH_AT_END (1<<3)
|
||||
#define ZIP_ENTRY_FLAG_UTF8_NAME (1 << 11)
|
||||
|
||||
|
||||
enum compression {
|
||||
COMPRESSION_UNSPECIFIED = -1,
|
||||
COMPRESSION_STORE = 0,
|
||||
@@ -75,6 +76,16 @@ enum compression {
|
||||
#define COMPRESSION_DEFAULT COMPRESSION_STORE
|
||||
#endif
|
||||
|
||||
enum encryption {
|
||||
ENCRYPTION_NONE = 0,
|
||||
ENCRYPTION_TRADITIONAL, /* Traditional PKWARE encryption. */
|
||||
ENCRYPTION_WINZIP_AES128, /* WinZIP AES-128 encryption. */
|
||||
ENCRYPTION_WINZIP_AES256, /* WinZIP AES-256 encryption. */
|
||||
};
|
||||
|
||||
#define TRAD_HEADER_SIZE 12
|
||||
|
||||
|
||||
struct cd_segment {
|
||||
struct cd_segment *next;
|
||||
size_t buff_size;
|
||||
@@ -82,6 +93,10 @@ struct cd_segment {
|
||||
unsigned char *p;
|
||||
};
|
||||
|
||||
struct trad_enc_ctx {
|
||||
uint32_t keys[3];
|
||||
};
|
||||
|
||||
struct zip {
|
||||
|
||||
int64_t entry_offset;
|
||||
@@ -93,9 +108,13 @@ struct zip {
|
||||
struct archive_entry *entry;
|
||||
uint32_t entry_crc32;
|
||||
enum compression entry_compression;
|
||||
enum encryption entry_encryption;
|
||||
int entry_flags;
|
||||
int entry_uses_zip64;
|
||||
int experiments;
|
||||
struct trad_enc_ctx tctx;
|
||||
char tctx_valid;
|
||||
unsigned char trad_chkdat;
|
||||
|
||||
unsigned char *file_header;
|
||||
size_t file_header_extra_offset;
|
||||
@@ -112,6 +131,8 @@ struct zip {
|
||||
struct archive_string_conv *sconv_default;
|
||||
enum compression requested_compression;
|
||||
int init_default_conversion;
|
||||
enum encryption encryption_type;
|
||||
struct archive_string password;
|
||||
|
||||
#define ZIP_FLAG_AVOID_ZIP64 1
|
||||
#define ZIP_FLAG_FORCE_ZIP64 2
|
||||
@@ -143,6 +164,9 @@ static size_t path_length(struct archive_entry *);
|
||||
static int write_path(struct archive_entry *, struct archive_write *);
|
||||
static void copy_path(struct archive_entry *, unsigned char *);
|
||||
static struct archive_string_conv *get_sconv(struct archive_write *, struct zip *);
|
||||
static int trad_enc_init(struct trad_enc_ctx *, const char *, size_t);
|
||||
static unsigned trad_enc_encrypt_update(struct trad_enc_ctx *, const uint8_t *,
|
||||
size_t, uint8_t *, size_t);
|
||||
|
||||
static unsigned char *
|
||||
cd_alloc(struct zip *zip, size_t length)
|
||||
@@ -223,6 +247,19 @@ archive_write_zip_options(struct archive_write *a, const char *key,
|
||||
ret = ARCHIVE_OK;
|
||||
}
|
||||
return (ret);
|
||||
} else if (strcmp(key, "encryption") == 0) {
|
||||
if (val == NULL) {
|
||||
zip->encryption_type = ENCRYPTION_NONE;
|
||||
ret = ARCHIVE_OK;
|
||||
} else if (val[0] == '1' || strcmp(val, "traditional") == 0) {
|
||||
zip->encryption_type = ENCRYPTION_TRADITIONAL;
|
||||
ret = ARCHIVE_OK;
|
||||
} else {
|
||||
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
|
||||
"%s: unknown encryption '%s'",
|
||||
a->format_name, val);
|
||||
}
|
||||
return (ret);
|
||||
} else if (strcmp(key, "experimental") == 0) {
|
||||
if (val == NULL || val[0] == 0) {
|
||||
zip->flags &= ~ ZIP_FLAG_EXPERIMENT_xl;
|
||||
@@ -258,6 +295,16 @@ archive_write_zip_options(struct archive_write *a, const char *key,
|
||||
ret = ARCHIVE_FATAL;
|
||||
}
|
||||
return (ret);
|
||||
} else if (strcmp(key, "password") == 0) {
|
||||
if (val == NULL || val[0] == 0) {
|
||||
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
|
||||
"%s: password option needs its value",
|
||||
a->format_name);
|
||||
} else {
|
||||
archive_strcpy(&zip->password, val);
|
||||
ret = ARCHIVE_OK;
|
||||
}
|
||||
return (ret);
|
||||
} else if (strcmp(key, "zip64") == 0) {
|
||||
/*
|
||||
* Bias decisions about Zip64: force them to be
|
||||
@@ -355,7 +402,7 @@ archive_write_set_format_zip(struct archive *_a)
|
||||
zip->requested_compression = COMPRESSION_UNSPECIFIED;
|
||||
zip->crc32func = real_crc32;
|
||||
|
||||
#ifdef HAVE_ZLIB_H
|
||||
/* A buffer used for both compression and encryption. */
|
||||
zip->len_buf = 65536;
|
||||
zip->buf = malloc(zip->len_buf);
|
||||
if (zip->buf == NULL) {
|
||||
@@ -364,7 +411,6 @@ archive_write_set_format_zip(struct archive *_a)
|
||||
"Can't allocate compression buffer");
|
||||
return (ARCHIVE_FATAL);
|
||||
}
|
||||
#endif
|
||||
|
||||
a->format_data = zip;
|
||||
a->format_name = "zip";
|
||||
@@ -448,10 +494,12 @@ archive_write_zip_header(struct archive_write *a, struct archive_entry *entry)
|
||||
zip->entry_flags = 0;
|
||||
zip->entry_uses_zip64 = 0;
|
||||
zip->entry_crc32 = zip->crc32func(0, NULL, 0);
|
||||
zip->entry_encryption = 0;
|
||||
if (zip->entry != NULL) {
|
||||
archive_entry_free(zip->entry);
|
||||
zip->entry = NULL;
|
||||
}
|
||||
zip->tctx_valid = 0;
|
||||
|
||||
#if defined(_WIN32) && !defined(__CYGWIN__)
|
||||
/* Make sure the path separators in pahtname, hardlink and symlink
|
||||
@@ -546,6 +594,8 @@ archive_write_zip_header(struct archive_write *a, struct archive_entry *entry)
|
||||
if (zip->entry_compression == COMPRESSION_UNSPECIFIED) {
|
||||
zip->entry_compression = COMPRESSION_DEFAULT;
|
||||
}
|
||||
if (archive_entry_size(zip->entry) > 0)
|
||||
zip->entry_encryption = zip->encryption_type;
|
||||
if (zip->entry_compression == COMPRESSION_STORE) {
|
||||
zip->entry_compressed_size = size;
|
||||
zip->entry_uncompressed_size = size;
|
||||
@@ -554,6 +604,12 @@ archive_write_zip_header(struct archive_write *a, struct archive_entry *entry)
|
||||
zip->entry_uncompressed_size = size;
|
||||
version_needed = 20;
|
||||
}
|
||||
if (zip->entry_encryption == ENCRYPTION_TRADITIONAL) {
|
||||
if (zip->entry_compression == COMPRESSION_STORE)
|
||||
zip->entry_compressed_size += TRAD_HEADER_SIZE;
|
||||
version_needed = 20;
|
||||
zip->entry_flags |= ZIP_ENTRY_FLAG_ENCRYPTED;
|
||||
}
|
||||
if ((zip->flags & ZIP_FLAG_FORCE_ZIP64) /* User asked. */
|
||||
|| (zip->entry_uncompressed_size > 0xffffffffLL)) {
|
||||
/* Large entry. */
|
||||
@@ -569,6 +625,7 @@ archive_write_zip_header(struct archive_write *a, struct archive_entry *entry)
|
||||
* length-at-end more reliable. */
|
||||
zip->entry_compression = COMPRESSION_DEFAULT;
|
||||
zip->entry_flags |= ZIP_ENTRY_FLAG_LENGTH_AT_END;
|
||||
zip->entry_encryption = zip->encryption_type;
|
||||
if ((zip->flags & ZIP_FLAG_AVOID_ZIP64) == 0) {
|
||||
zip->entry_uses_zip64 = 1;
|
||||
version_needed = 45;
|
||||
@@ -577,6 +634,11 @@ archive_write_zip_header(struct archive_write *a, struct archive_entry *entry)
|
||||
} else {
|
||||
version_needed = 20;
|
||||
}
|
||||
if (zip->entry_encryption == ENCRYPTION_TRADITIONAL) {
|
||||
zip->entry_flags |= ZIP_ENTRY_FLAG_ENCRYPTED;
|
||||
if (version_needed < 20)
|
||||
version_needed = 20;
|
||||
}
|
||||
}
|
||||
|
||||
/* Format the local header. */
|
||||
@@ -602,6 +664,13 @@ archive_write_zip_header(struct archive_write *a, struct archive_entry *entry)
|
||||
}
|
||||
archive_le16enc(local_header + 26, filename_length);
|
||||
|
||||
if (zip->entry_encryption == ENCRYPTION_TRADITIONAL) {
|
||||
if (zip->entry_flags & ZIP_ENTRY_FLAG_LENGTH_AT_END)
|
||||
zip->trad_chkdat = local_header[11];
|
||||
else
|
||||
zip->trad_chkdat = local_header[17];
|
||||
}
|
||||
|
||||
/* Format as much of central directory file header as we can: */
|
||||
zip->file_header = cd_alloc(zip, 46);
|
||||
/* If (zip->file_header == NULL) XXXX */
|
||||
@@ -779,13 +848,65 @@ archive_write_zip_data(struct archive_write *a, const void *buff, size_t s)
|
||||
|
||||
if (s == 0) return 0;
|
||||
|
||||
switch (zip->entry_compression) {
|
||||
case COMPRESSION_STORE:
|
||||
ret = __archive_write_output(a, buff, s);
|
||||
if (zip->entry_encryption == ENCRYPTION_TRADITIONAL
|
||||
&& zip->tctx_valid == 0) {
|
||||
/* Initialize traditoinal PKWARE encryption context. */
|
||||
uint8_t key[TRAD_HEADER_SIZE];
|
||||
uint8_t key_encrypted[TRAD_HEADER_SIZE];
|
||||
|
||||
if (zip->password.s == NULL
|
||||
|| archive_strlen(&zip->password) == 0) {
|
||||
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
|
||||
"Encryption needs password");
|
||||
return ARCHIVE_FAILED;
|
||||
}
|
||||
if (archive_random(key, sizeof(key)-1) != ARCHIVE_OK) {
|
||||
archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC,
|
||||
"Can't generate random number for encryption");
|
||||
return ARCHIVE_FATAL;
|
||||
}
|
||||
trad_enc_init(&zip->tctx, zip->password.s,
|
||||
archive_strlen(&zip->password));
|
||||
/* Set the last key code which will be used as a check code
|
||||
* for ferifying password in decryption. */
|
||||
key[TRAD_HEADER_SIZE-1] = zip->trad_chkdat;
|
||||
trad_enc_encrypt_update(&zip->tctx, key, TRAD_HEADER_SIZE,
|
||||
key_encrypted, TRAD_HEADER_SIZE);
|
||||
/* Write encrypted keys in the top of the file content. */
|
||||
ret = __archive_write_output(a, key_encrypted,
|
||||
TRAD_HEADER_SIZE);
|
||||
if (ret != ARCHIVE_OK)
|
||||
return (ret);
|
||||
zip->written_bytes += s;
|
||||
zip->entry_compressed_written += s;
|
||||
zip->written_bytes += TRAD_HEADER_SIZE;
|
||||
zip->entry_compressed_written += TRAD_HEADER_SIZE;
|
||||
zip->tctx_valid = 1;
|
||||
}
|
||||
|
||||
switch (zip->entry_compression) {
|
||||
case COMPRESSION_STORE:
|
||||
if (zip->tctx_valid) {
|
||||
const uint8_t *rb = (const uint8_t *)buff;
|
||||
const uint8_t * const re = rb + s;
|
||||
|
||||
while (rb < re) {
|
||||
size_t l;
|
||||
|
||||
l = trad_enc_encrypt_update(&zip->tctx,
|
||||
rb, re - rb, zip->buf, zip->len_buf);
|
||||
ret = __archive_write_output(a, zip->buf, l);
|
||||
if (ret != ARCHIVE_OK)
|
||||
return (ret);
|
||||
zip->entry_compressed_written += l;
|
||||
zip->written_bytes += l;
|
||||
rb += l;
|
||||
}
|
||||
} else {
|
||||
ret = __archive_write_output(a, buff, s);
|
||||
if (ret != ARCHIVE_OK)
|
||||
return (ret);
|
||||
zip->written_bytes += s;
|
||||
zip->entry_compressed_written += s;
|
||||
}
|
||||
break;
|
||||
#if HAVE_ZLIB_H
|
||||
case COMPRESSION_DEFLATE:
|
||||
@@ -796,6 +917,11 @@ archive_write_zip_data(struct archive_write *a, const void *buff, size_t s)
|
||||
if (ret == Z_STREAM_ERROR)
|
||||
return (ARCHIVE_FATAL);
|
||||
if (zip->stream.avail_out == 0) {
|
||||
if (zip->tctx_valid) {
|
||||
trad_enc_encrypt_update(&zip->tctx,
|
||||
zip->buf, zip->len_buf,
|
||||
zip->buf, zip->len_buf);
|
||||
}
|
||||
ret = __archive_write_output(a, zip->buf,
|
||||
zip->len_buf);
|
||||
if (ret != ARCHIVE_OK)
|
||||
@@ -835,6 +961,10 @@ archive_write_zip_finish_entry(struct archive_write *a)
|
||||
if (ret == Z_STREAM_ERROR)
|
||||
return (ARCHIVE_FATAL);
|
||||
remainder = zip->len_buf - zip->stream.avail_out;
|
||||
if (zip->tctx_valid) {
|
||||
trad_enc_encrypt_update(&zip->tctx,
|
||||
zip->buf, remainder, zip->buf, remainder);
|
||||
}
|
||||
ret = __archive_write_output(a, zip->buf, remainder);
|
||||
if (ret != ARCHIVE_OK)
|
||||
return (ret);
|
||||
@@ -1006,10 +1136,12 @@ archive_write_zip_free(struct archive_write *a)
|
||||
free(segment->buff);
|
||||
free(segment);
|
||||
}
|
||||
#ifdef HAVE_ZLIB_H
|
||||
free(zip->buf);
|
||||
#endif
|
||||
archive_entry_free(zip->entry);
|
||||
if (zip->password.s != NULL) {
|
||||
memset(zip->password.s, 0, archive_strlen(&zip->password));
|
||||
archive_string_free(&zip->password);
|
||||
}
|
||||
/* TODO: Free opt_sconv, sconv_default */
|
||||
|
||||
free(zip);
|
||||
@@ -1129,3 +1261,56 @@ get_sconv(struct archive_write *a, struct zip *zip)
|
||||
}
|
||||
return (zip->sconv_default);
|
||||
}
|
||||
|
||||
/*
|
||||
Traditional PKWARE Decryption functions.
|
||||
*/
|
||||
|
||||
static void
|
||||
trad_enc_update_keys(struct trad_enc_ctx *ctx, uint8_t c)
|
||||
{
|
||||
uint8_t t;
|
||||
#define CRC32(c, b) (crc32(c ^ 0xffffffffUL, &b, 1) ^ 0xffffffffUL)
|
||||
|
||||
ctx->keys[0] = CRC32(ctx->keys[0], c);
|
||||
ctx->keys[1] = (ctx->keys[1] + (ctx->keys[0] & 0xff)) * 134775813L + 1;
|
||||
t = (ctx->keys[1] >> 24) & 0xff;
|
||||
ctx->keys[2] = CRC32(ctx->keys[2], t);
|
||||
#undef CRC32
|
||||
}
|
||||
|
||||
static uint8_t
|
||||
trad_enc_decypt_byte(struct trad_enc_ctx *ctx)
|
||||
{
|
||||
unsigned temp = ctx->keys[2] | 2;
|
||||
return (uint8_t)((temp * (temp ^ 1)) >> 8) & 0xff;
|
||||
}
|
||||
|
||||
static unsigned
|
||||
trad_enc_encrypt_update(struct trad_enc_ctx *ctx, const uint8_t *in,
|
||||
size_t in_len, uint8_t *out, size_t out_len)
|
||||
{
|
||||
unsigned i, max;
|
||||
|
||||
max = (unsigned)((in_len < out_len)? in_len: out_len);
|
||||
|
||||
for (i = 0; i < max; i++) {
|
||||
uint8_t t = in[i];
|
||||
out[i] = t ^ trad_enc_decypt_byte(ctx);
|
||||
trad_enc_update_keys(ctx, t);
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
static int
|
||||
trad_enc_init(struct trad_enc_ctx *ctx, const char *pw, size_t pw_len)
|
||||
{
|
||||
|
||||
ctx->keys[0] = 305419896L;
|
||||
ctx->keys[1] = 591751049L;
|
||||
ctx->keys[2] = 878082192L;
|
||||
|
||||
for (;pw_len; --pw_len)
|
||||
trad_enc_update_keys(ctx, *pw++);
|
||||
return 0;
|
||||
}
|
||||
@@ -585,8 +585,6 @@ DEFINE_TEST(test_write_format_zip64)
|
||||
archive_write_set_options(a, "zip:zip64"));
|
||||
assertEqualIntA(a, ARCHIVE_OK,
|
||||
archive_write_set_options(a, "zip:experimental"));
|
||||
assertEqualIntA(a, ARCHIVE_OK,
|
||||
archive_write_set_options(a, "zip:experimental"));
|
||||
assertEqualIntA(a, ARCHIVE_OK,
|
||||
archive_write_open_memory(a, buff, buffsize, &used));
|
||||
write_contents(a);
|
||||
@@ -619,3 +617,61 @@ DEFINE_TEST(test_write_format_zip64)
|
||||
|
||||
free(buff);
|
||||
}
|
||||
|
||||
DEFINE_TEST(test_write_format_zip_traditional_pkware_encryption)
|
||||
{
|
||||
struct archive *a;
|
||||
size_t used;
|
||||
size_t buffsize = 1000000;
|
||||
char *buff;
|
||||
|
||||
buff = malloc(buffsize);
|
||||
|
||||
/* Create a new archive in memory. */
|
||||
assert((a = archive_write_new()) != NULL);
|
||||
assertEqualIntA(a, ARCHIVE_OK, archive_write_set_format_zip(a));
|
||||
assertEqualIntA(a, ARCHIVE_OK, archive_write_add_filter_none(a));
|
||||
assertEqualIntA(a, ARCHIVE_OK,
|
||||
archive_write_set_options(a, "zip:encryption=traditional"));
|
||||
assertEqualIntA(a, ARCHIVE_OK,
|
||||
archive_write_set_options(a, "zip:password=password1234"));
|
||||
assertEqualIntA(a, ARCHIVE_OK,
|
||||
archive_write_set_options(a, "zip:experimental"));
|
||||
assertEqualIntA(a, ARCHIVE_OK,
|
||||
archive_write_open_memory(a, buff, buffsize, &used));
|
||||
write_contents(a);
|
||||
dumpfile("constructed.zip", buff, used);
|
||||
|
||||
/*
|
||||
* Now, read the data back.
|
||||
*/
|
||||
/* With the standard memory reader. */
|
||||
assert((a = archive_read_new()) != NULL);
|
||||
assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a));
|
||||
assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a));
|
||||
assertEqualIntA(a, ARCHIVE_OK,
|
||||
archive_read_set_options(a, "zip:password=password1234"));
|
||||
assertEqualIntA(a, ARCHIVE_OK, archive_read_open_memory(a, buff, used));
|
||||
verify_contents(a, 1);
|
||||
|
||||
/* With the test memory reader -- streaming mode. */
|
||||
assert((a = archive_read_new()) != NULL);
|
||||
assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a));
|
||||
assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a));
|
||||
assertEqualIntA(a, ARCHIVE_OK,
|
||||
archive_read_set_options(a, "zip:password=password1234"));
|
||||
assertEqualIntA(a, ARCHIVE_OK, read_open_memory(a, buff, used, 7));
|
||||
/* Streaming reader doesn't see mode information from Central Directory. */
|
||||
verify_contents(a, 0);
|
||||
|
||||
/* With the test memory reader -- seeking mode. */
|
||||
assert((a = archive_read_new()) != NULL);
|
||||
assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a));
|
||||
assertEqualIntA(a, ARCHIVE_OK, archive_read_support_filter_all(a));
|
||||
assertEqualIntA(a, ARCHIVE_OK,
|
||||
archive_read_set_options(a, "zip:password=password1234"));
|
||||
assertEqualIntA(a, ARCHIVE_OK, read_open_memory_seek(a, buff, used, 7));
|
||||
verify_contents(a, 1);
|
||||
|
||||
free(buff);
|
||||
}
|
||||
Reference in new issue
Block a user