/*- * 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" #ifdef HAVE_STRING_H #include #endif #include "archive.h" #include "archive_cryptor_private.h" #ifdef ARCHIVE_CRYPTOR_USE_Apple_CommonCrypto static int pbkdf2_sha1(const char *pw, size_t pw_len, const uint8_t *salt, size_t salt_len, unsigned rounds, uint8_t *derived_key, size_t derived_key_len) { CCKeyDerivationPBKDF(kCCPBKDF2, (const char *)pw, pw_len, salt, salt_len, kCCPRFHmacAlgSHA1, rounds, derived_key, derived_key_len); return 0; } #elif defined(_WIN32) && !defined(__CYGWIN__) && defined(HAVE_BCRYPT_H) #ifdef _MSC_VER #pragma comment(lib, "Bcrypt.lib") #endif static int pbkdf2_sha1(const char *pw, size_t pw_len, const uint8_t *salt, size_t salt_len, unsigned rounds, uint8_t *derived_key, size_t derived_key_len) { NTSTATUS status; BCRYPT_ALG_HANDLE hAlg; status = BCryptOpenAlgorithmProvider(&hAlg, BCRYPT_SHA1_ALGORITHM, MS_PRIMITIVE_PROVIDER, BCRYPT_ALG_HANDLE_HMAC_FLAG); if (!BCRYPT_SUCCESS(status)) return -1; status = BCryptDeriveKeyPBKDF2(hAlg, (PUCHAR)(uintptr_t)pw, (ULONG)pw_len, (PUCHAR)(uintptr_t)salt, (ULONG)salt_len, rounds, (PUCHAR)derived_key, (ULONG)derived_key_len, 0); BCryptCloseAlgorithmProvider(hAlg, 0); return (BCRYPT_SUCCESS(status)) ? 0: -1; } #elif defined(HAVE_LIBNETTLE) && defined(HAVE_NETTLE_PBKDF2_H) static int pbkdf2_sha1(const char *pw, size_t pw_len, const uint8_t *salt, size_t salt_len, unsigned rounds, uint8_t *derived_key, size_t derived_key_len) { pbkdf2_hmac_sha1((unsigned)pw_len, (const uint8_t *)pw, rounds, salt_len, salt, derived_key_len, derived_key); return 0; } #elif defined(HAVE_LIBCRYPTO) && defined(HAVE_PKCS5_PBKDF2_HMAC_SHA1) static int pbkdf2_sha1(const char *pw, size_t pw_len, const uint8_t *salt, size_t salt_len, unsigned rounds, uint8_t *derived_key, size_t derived_key_len) { PKCS5_PBKDF2_HMAC_SHA1(pw, pw_len, salt, salt_len, rounds, derived_key_len, derived_key); return 0; } #else /* Stub */ static int pbkdf2_sha1(const char *pw, size_t pw_len, const uint8_t *salt, size_t salt_len, unsigned rounds, uint8_t *derived_key, size_t derived_key_len) { (void)pw; /* UNUSED */ (void)pw_len; /* UNUSED */ (void)salt; /* UNUSED */ (void)salt_len; /* UNUSED */ (void)rounds; /* UNUSED */ (void)derived_key; /* UNUSED */ (void)derived_key_len; /* UNUSED */ return -1; /* UNSUPPORTED */ } #endif #ifdef ARCHIVE_CRYPTOR_USE_Apple_CommonCrypto # if MAC_OS_X_VERSION_MAX_ALLOWED < 1090 # define kCCAlgorithmAES kCCAlgorithmAES128 # endif static int aes_ctr_init(archive_crypto_ctx *ctx, const uint8_t *key, size_t key_len) { CCCryptorStatus r; ctx->key_len = key_len; memcpy(ctx->key, key, key_len); memset(ctx->nonce, 0, sizeof(ctx->nonce)); ctx->encr_pos = AES_BLOCK_SIZE; r = CCCryptorCreateWithMode(kCCEncrypt, kCCModeECB, kCCAlgorithmAES, ccNoPadding, NULL, key, key_len, NULL, 0, 0, 0, &ctx->ctx); return (r == kCCSuccess)? 0: -1; } static int aes_ctr_encrypt_counter(archive_crypto_ctx *ctx) { CCCryptorRef ref = ctx->ctx; CCCryptorStatus r; r = CCCryptorReset(ref, NULL); if (r != kCCSuccess) return -1; r = CCCryptorUpdate(ref, ctx->nonce, AES_BLOCK_SIZE, ctx->encr_buf, AES_BLOCK_SIZE, NULL); return (r == kCCSuccess)? 0: -1; } static int aes_ctr_release(archive_crypto_ctx *ctx) { memset(ctx->key, 0, ctx->key_len); memset(ctx->nonce, 0, sizeof(ctx->nonce)); return 0; } #elif defined(_WIN32) && !defined(__CYGWIN__) && defined(HAVE_BCRYPT_H) static int aes_ctr_init(archive_crypto_ctx *ctx, const uint8_t *key, size_t key_len) { BCRYPT_ALG_HANDLE hAlg; BCRYPT_KEY_HANDLE hKey; DWORD keyObj_len, aes_key_len; PBYTE keyObj; ULONG result; NTSTATUS status; BCRYPT_KEY_LENGTHS_STRUCT key_lengths; ctx->hAlg = NULL; ctx->hKey = NULL; ctx->keyObj = NULL; switch (key_len) { case 16: aes_key_len = 128; break; case 24: aes_key_len = 192; break; case 32: aes_key_len = 256; break; default: return -1; } status = BCryptOpenAlgorithmProvider(&hAlg, BCRYPT_AES_ALGORITHM, MS_PRIMITIVE_PROVIDER, 0); if (!BCRYPT_SUCCESS(status)) return -1; status = BCryptGetProperty(hAlg, BCRYPT_KEY_LENGTHS, (PUCHAR)&key_lengths, sizeof(key_lengths), &result, 0); if (!BCRYPT_SUCCESS(status)) { BCryptCloseAlgorithmProvider(hAlg, 0); return -1; } if (key_lengths.dwMinLength > aes_key_len || key_lengths.dwMaxLength < aes_key_len) { BCryptCloseAlgorithmProvider(hAlg, 0); return -1; } status = BCryptGetProperty(hAlg, BCRYPT_OBJECT_LENGTH, (PUCHAR)&keyObj_len, sizeof(keyObj_len), &result, 0); if (!BCRYPT_SUCCESS(status)) { BCryptCloseAlgorithmProvider(hAlg, 0); return -1; } keyObj = (PBYTE)HeapAlloc(GetProcessHeap(), 0, keyObj_len); if (keyObj == NULL) { BCryptCloseAlgorithmProvider(hAlg, 0); return -1; } status = BCryptSetProperty(hAlg, BCRYPT_CHAINING_MODE, (PUCHAR)BCRYPT_CHAIN_MODE_ECB, sizeof(BCRYPT_CHAIN_MODE_ECB), 0); if (!BCRYPT_SUCCESS(status)) { BCryptCloseAlgorithmProvider(hAlg, 0); HeapFree(GetProcessHeap(), 0, keyObj); return -1; } status = BCryptGenerateSymmetricKey(hAlg, &hKey, keyObj, keyObj_len, (PUCHAR)(uintptr_t)key, (ULONG)key_len, 0); if (!BCRYPT_SUCCESS(status)) { BCryptCloseAlgorithmProvider(hAlg, 0); HeapFree(GetProcessHeap(), 0, keyObj); return -1; } ctx->hAlg = hAlg; ctx->hKey = hKey; ctx->keyObj = keyObj; ctx->keyObj_len = keyObj_len; ctx->encr_pos = AES_BLOCK_SIZE; return 0; } static int aes_ctr_encrypt_counter(archive_crypto_ctx *ctx) { NTSTATUS status; ULONG result; status = BCryptEncrypt(ctx->hKey, (PUCHAR)ctx->nonce, AES_BLOCK_SIZE, NULL, NULL, 0, (PUCHAR)ctx->encr_buf, AES_BLOCK_SIZE, &result, 0); return BCRYPT_SUCCESS(status) ? 0 : -1; } static int aes_ctr_release(archive_crypto_ctx *ctx) { if (ctx->hAlg != NULL) { BCryptCloseAlgorithmProvider(ctx->hAlg, 0); ctx->hAlg = NULL; BCryptDestroyKey(ctx->hKey); ctx->hKey = NULL; HeapFree(GetProcessHeap(), 0, ctx->keyObj); ctx->keyObj = NULL; } memset(ctx, 0, sizeof(*ctx)); return 0; } #elif defined(HAVE_LIBNETTLE) static int aes_ctr_init(archive_crypto_ctx *ctx, const uint8_t *key, size_t key_len) { ctx->key_len = key_len; memcpy(ctx->key, key, key_len); memset(ctx->nonce, 0, sizeof(ctx->nonce)); ctx->encr_pos = AES_BLOCK_SIZE; memset(&ctx->ctx, 0, sizeof(ctx->ctx)); return 0; } static int aes_ctr_encrypt_counter(archive_crypto_ctx *ctx) { aes_set_encrypt_key(&ctx->ctx, ctx->key_len, ctx->key); aes_encrypt(&ctx->ctx, AES_BLOCK_SIZE, ctx->encr_buf, ctx->nonce); return 0; } static int aes_ctr_release(archive_crypto_ctx *ctx) { memset(ctx, 0, sizeof(*ctx)); return 0; } #elif defined(HAVE_LIBCRYPTO) static int aes_ctr_init(archive_crypto_ctx *ctx, const uint8_t *key, size_t key_len) { switch (key_len) { case 16: ctx->type = EVP_aes_128_ecb(); break; case 24: ctx->type = EVP_aes_192_ecb(); break; case 32: ctx->type = EVP_aes_256_ecb(); break; default: ctx->type = NULL; return -1; } ctx->key_len = key_len; memcpy(ctx->key, key, key_len); memset(ctx->nonce, 0, sizeof(ctx->nonce)); ctx->encr_pos = AES_BLOCK_SIZE; EVP_CIPHER_CTX_init(&ctx->ctx); return 0; } static int aes_ctr_encrypt_counter(archive_crypto_ctx *ctx) { int outl = 0; int r; r = EVP_EncryptInit_ex(&ctx->ctx, ctx->type, NULL, ctx->key, NULL); if (r == 0) return -1; r = EVP_EncryptUpdate(&ctx->ctx, ctx->encr_buf, &outl, ctx->nonce, AES_BLOCK_SIZE); if (r == 0 || outl != AES_BLOCK_SIZE) return -1; return 0; } static int aes_ctr_release(archive_crypto_ctx *ctx) { EVP_CIPHER_CTX_cleanup(&ctx->ctx); memset(ctx->key, 0, ctx->key_len); memset(ctx->nonce, 0, sizeof(ctx->nonce)); return 0; } #else #define ARCHIVE_CRYPTOR_STUB /* Stub */ static int aes_ctr_init(archive_crypto_ctx *ctx, const uint8_t *key, size_t key_len) { (void)ctx; /* UNUSED */ (void)key; /* UNUSED */ (void)key_len; /* UNUSED */ return -1; } static int aes_ctr_encrypt_counter(archive_crypto_ctx *ctx) { (void)ctx; /* UNUSED */ return -1; } static int aes_ctr_release(archive_crypto_ctx *ctx) { (void)ctx; /* UNUSED */ return 0; } #endif #ifdef ARCHIVE_CRYPTOR_STUB static int aes_ctr_update(archive_crypto_ctx *ctx, const uint8_t * const in, size_t in_len, uint8_t * const out, size_t *out_len) { (void)ctx; /* UNUSED */ (void)in; /* UNUSED */ (void)in_len; /* UNUSED */ (void)out; /* UNUSED */ (void)out_len; /* UNUSED */ aes_ctr_encrypt_counter(ctx); /* UNUSED */ /* Fix unused function warning */ return -1; } #else static void aes_ctr_increase_counter(archive_crypto_ctx *ctx) { uint8_t *const nonce = ctx->nonce; int j; for (j = 0; j < 8; j++) { if (++nonce[j]) break; } } static int aes_ctr_update(archive_crypto_ctx *ctx, const uint8_t * const in, size_t in_len, uint8_t * const out, size_t *out_len) { uint8_t *const ebuf = ctx->encr_buf; unsigned pos = ctx->encr_pos; unsigned max = (unsigned)((in_len < *out_len)? in_len: *out_len); unsigned i; for (i = 0; i < max; ) { if (pos == AES_BLOCK_SIZE) { aes_ctr_increase_counter(ctx); if (aes_ctr_encrypt_counter(ctx) != 0) return -1; while (max -i >= AES_BLOCK_SIZE) { for (pos = 0; pos < AES_BLOCK_SIZE; pos++) out[i+pos] = in[i+pos] ^ ebuf[pos]; i += AES_BLOCK_SIZE; aes_ctr_increase_counter(ctx); if (aes_ctr_encrypt_counter(ctx) != 0) return -1; } pos = 0; if (i >= max) break; } out[i] = in[i] ^ ebuf[pos++]; i++; } ctx->encr_pos = pos; *out_len = i; return 0; } #endif /* ARCHIVE_CRYPTOR_STUB */ const struct archive_cryptor __archive_cryptor = { &pbkdf2_sha1, &aes_ctr_init, &aes_ctr_update, &aes_ctr_release, &aes_ctr_init, &aes_ctr_update, &aes_ctr_release, };