Add support for traditional PKWARE encryption.

This commit is contained in:
Michihiro NAKAJIMA committed 2014-09-08 19:41:34 +09:00
1 parent 03b47ac01d
commit de0a204ecc
9 files changed
+569 -15

No files matched your search

+2
View File
@@ -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)
+2
View File
@@ -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 \
+6
View File
@@ -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
View File
@@ -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])
+1
View File
@@ -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
+266
View File
@@ -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 */
+36
View File
@@ -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 */
+196 -11
View File
@@ -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;
}
+58 -2
View File
@@ -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);
}