kopia lustrzana https://github.com/Hamlib/Hamlib
544 wiersze
14 KiB
C
544 wiersze
14 KiB
C
/*
|
|
* AESStringCrypt.c
|
|
*
|
|
* AES String Crypt 1.1
|
|
* Copyright (C) 2007, 2008, 2009, 2012, 2015
|
|
*
|
|
* Author: Paul E. Jones <paulej@packetizer.com>
|
|
*
|
|
* This library will encrypt octet strings of the specified length up
|
|
* to ULLONG_MAX - 70 octet in length. If there is an error, the return
|
|
* value from the encryption or decryption function will be
|
|
* AESSTRINGCRYPT_ERROR. Any other value, including zero, is a valid
|
|
* length value. Note that an encrypted string can be up to 69 octets
|
|
* longer than the original plaintext string, thus the restriction on the
|
|
* input string size.
|
|
*
|
|
* The output of the string encryption function is a string that is
|
|
* compliant with the AES Crypt version 0 file format. For reference,
|
|
* see: https://www.aescrypt.com/aes_file_format.html.
|
|
*
|
|
* This software is licensed as "freeware." Permission to distribute
|
|
* this software in source and binary forms is hereby granted without a
|
|
* fee. THIS SOFTWARE IS PROVIDED 'AS IS' AND WITHOUT ANY EXPRESSED OR
|
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
|
* THE AUTHOR SHALL NOT BE HELD LIABLE FOR ANY DAMAGES RESULTING FROM
|
|
* THE USE OF THIS SOFTWARE, EITHER DIRECTLY OR INDIRECTLY, INCLUDING,
|
|
* BUT NOT LIMITED TO, LOSS OF DATA OR DATA BEING RENDERED INACCURATE.
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
#ifdef _WIN32
|
|
#include <windows.h>
|
|
#include <wincrypt.h>
|
|
#else
|
|
#include <unistd.h>
|
|
#include <stdlib.h>
|
|
#include <time.h>
|
|
#endif
|
|
|
|
#include "AESStringCrypt.h"
|
|
|
|
/*
|
|
* AESStringCrypt
|
|
*
|
|
* Description
|
|
* This function is called to encrypt the string "plaintext".
|
|
* The encrypted string is placed in "ciphertext". Note that
|
|
* the encrypted string is up to 68 bytes larger than the
|
|
* plaintext string. This is to accomodate the header defined
|
|
* by "AES Crypt File Format 0" and to store the last cipher
|
|
* block (which is padded to 16 octets).
|
|
*
|
|
* Parameters
|
|
* password [in]
|
|
* The password used to encrypt the string in UCS-16
|
|
* format.
|
|
* password_length [in]
|
|
* The length of the password in octets
|
|
* plaintext [in]
|
|
* The plaintext string to be encrypted
|
|
* plaintext_length [in]
|
|
* The length of the plaintext string
|
|
* ciphertext [out]
|
|
* The encrypted string
|
|
*
|
|
* Returns
|
|
* Returns the length of the ciphertext string or AESSTRINGCRYPT_ERROR
|
|
* if there was an error when trying to encrypt the string.
|
|
*/
|
|
unsigned long long AESStringCrypt(unsigned char *password,
|
|
unsigned long password_length,
|
|
unsigned char *plaintext,
|
|
unsigned long long plaintext_length,
|
|
unsigned char *ciphertext)
|
|
{
|
|
aes_context aes_ctx;
|
|
sha256_context sha_ctx;
|
|
sha256_t digest;
|
|
unsigned char IV[16];
|
|
int i, n;
|
|
unsigned char buffer[32];
|
|
unsigned char ipad[64], opad[64];
|
|
#ifdef _WIN32
|
|
HCRYPTPROV hProv;
|
|
DWORD result_code;
|
|
#else
|
|
time_t current_time;
|
|
pid_t process_id;
|
|
FILE *randfp = NULL;
|
|
#endif
|
|
unsigned char *p;
|
|
|
|
/*
|
|
* Write an AES signature at the head of the file, along
|
|
* with the AES file format version number.
|
|
*/
|
|
ciphertext[0] = 'A';
|
|
ciphertext[1] = 'E';
|
|
ciphertext[2] = 'S';
|
|
ciphertext[3] = 0x00; /* Version 0 */
|
|
ciphertext[4] = (plaintext_length & 0x0F);
|
|
|
|
/*
|
|
* We will use p as the pointer into the cipher
|
|
*/
|
|
p = ciphertext + 5;
|
|
|
|
#ifdef _WIN32
|
|
|
|
/*
|
|
* Prepare for random number generation
|
|
*/
|
|
if (!CryptAcquireContext(&hProv,
|
|
NULL,
|
|
NULL,
|
|
PROV_RSA_FULL,
|
|
CRYPT_VERIFYCONTEXT))
|
|
{
|
|
result_code = GetLastError();
|
|
|
|
if (GetLastError() == NTE_BAD_KEYSET)
|
|
{
|
|
if (!CryptAcquireContext(&hProv,
|
|
NULL,
|
|
NULL,
|
|
PROV_RSA_FULL,
|
|
CRYPT_NEWKEYSET | CRYPT_VERIFYCONTEXT))
|
|
{
|
|
result_code = GetLastError();
|
|
}
|
|
else
|
|
{
|
|
result_code = ERROR_SUCCESS;
|
|
}
|
|
}
|
|
|
|
if (result_code != ERROR_SUCCESS)
|
|
{
|
|
return AESSTRINGCRYPT_ERROR;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Create the 16-bit IV used for encrypting the plaintext.
|
|
* We do not fully trust the system's randomization functions,
|
|
* so we improve on that by also hashing the random octets
|
|
* and using only a portion of the hash. This IV
|
|
* generation could be replaced with any good random
|
|
* source of data.
|
|
*/
|
|
memset(IV, 0, 16);
|
|
memset(buffer, 0, 32);
|
|
|
|
sha256_starts(&sha_ctx);
|
|
|
|
for (i = 0; i < 256; i++)
|
|
{
|
|
if (!CryptGenRandom(hProv,
|
|
32,
|
|
(BYTE *) buffer))
|
|
{
|
|
CryptReleaseContext(hProv, 0);
|
|
return AESSTRINGCRYPT_ERROR;
|
|
}
|
|
|
|
sha256_update(&sha_ctx, buffer, 32);
|
|
}
|
|
|
|
sha256_finish(&sha_ctx, digest);
|
|
|
|
/*
|
|
* We're finished collecting random data
|
|
*/
|
|
CryptReleaseContext(hProv, 0);
|
|
|
|
/*
|
|
* Get the IV from the digest buffer
|
|
*/
|
|
memcpy(IV, digest, 16);
|
|
|
|
#else
|
|
|
|
/*
|
|
* Open the source for random data. Note that while the entropy
|
|
* might be lower with /dev/urandom than /dev/random, it will not
|
|
* fail to produce something. Also, we're going to hash the result
|
|
* anyway.
|
|
*/
|
|
if ((randfp = fopen("/dev/urandom", "r")) == NULL)
|
|
{
|
|
return AESSTRINGCRYPT_ERROR;
|
|
}
|
|
|
|
/*
|
|
* We will use an initialization vector comprised of the current time
|
|
* process ID, and random data, all hashed together with SHA-256.
|
|
*/
|
|
current_time = time(NULL);
|
|
|
|
for (i = 0; i < 8; i++)
|
|
{
|
|
buffer[i] = (unsigned char)
|
|
(current_time >> (i * 8));
|
|
}
|
|
|
|
process_id = getpid();
|
|
|
|
for (i = 0; i < 8; i++)
|
|
{
|
|
buffer[i + 8] = (unsigned char)
|
|
(process_id >> (i * 8));
|
|
}
|
|
|
|
sha256_starts(&sha_ctx);
|
|
sha256_update(&sha_ctx, buffer, 16);
|
|
|
|
for (i = 0; i < 256; i++)
|
|
{
|
|
if (fread(buffer, 1, 32, randfp) != 32)
|
|
{
|
|
return AESSTRINGCRYPT_ERROR;
|
|
}
|
|
|
|
sha256_update(&sha_ctx,
|
|
buffer,
|
|
32);
|
|
}
|
|
|
|
sha256_finish(&sha_ctx, digest);
|
|
|
|
/*
|
|
* We're finished collecting random data
|
|
*/
|
|
fclose(randfp);
|
|
|
|
/*
|
|
* Get the IV from the digest buffer
|
|
*/
|
|
memcpy(IV, digest, 16);
|
|
#endif
|
|
|
|
/*
|
|
* Copy the IV to the ciphertext string
|
|
*/
|
|
memcpy(p, IV, 16);
|
|
p += 16;
|
|
|
|
/*
|
|
* Hash the IV and password 8192 times
|
|
*/
|
|
memset(digest, 0, 32);
|
|
memcpy(digest, IV, 16);
|
|
|
|
for (i = 0; i < 8192; i++)
|
|
{
|
|
sha256_starts(&sha_ctx);
|
|
sha256_update(&sha_ctx, digest, 32);
|
|
sha256_update(&sha_ctx,
|
|
password,
|
|
password_length);
|
|
sha256_finish(&sha_ctx,
|
|
digest);
|
|
}
|
|
|
|
/*
|
|
* Set the AES encryption key
|
|
*/
|
|
aes_set_key(&aes_ctx, digest, 256);
|
|
|
|
/*
|
|
* Set the ipad and opad arrays with values as
|
|
* per RFC 2104 (HMAC). HMAC is defined as
|
|
* H(K XOR opad, H(K XOR ipad, text))
|
|
*/
|
|
memset(ipad, 0x36, 64);
|
|
memset(opad, 0x5C, 64);
|
|
|
|
for (i = 0; i < 32; i++)
|
|
{
|
|
ipad[i] ^= digest[i];
|
|
opad[i] ^= digest[i];
|
|
}
|
|
|
|
sha256_starts(&sha_ctx);
|
|
sha256_update(&sha_ctx, ipad, 64);
|
|
|
|
while (plaintext_length > 0)
|
|
{
|
|
/*
|
|
* Grab the next block of plaintext
|
|
*/
|
|
if (plaintext_length >= 16)
|
|
{
|
|
n = 16;
|
|
}
|
|
else
|
|
{
|
|
n = (int) plaintext_length;
|
|
}
|
|
|
|
plaintext_length -= n;
|
|
|
|
memcpy(buffer, plaintext, n);
|
|
plaintext += n;
|
|
|
|
/*
|
|
* XOR plain text block with previous encrypted
|
|
* output (i.e., use CBC)
|
|
*/
|
|
for (i = 0; i < 16; i++)
|
|
{
|
|
buffer[i] ^= IV[i];
|
|
}
|
|
|
|
/*
|
|
* Encrypt the contents of the buffer
|
|
*/
|
|
aes_encrypt(&aes_ctx, buffer, buffer);
|
|
|
|
/*
|
|
* Concatenate the "text" as we compute the HMAC
|
|
*/
|
|
sha256_update(&sha_ctx, buffer, 16);
|
|
|
|
/*
|
|
* Write the encrypted block
|
|
*/
|
|
memcpy(p, buffer, 16);
|
|
p += 16;
|
|
|
|
/*
|
|
* Update the IV (CBC mode)
|
|
*/
|
|
memcpy(IV, buffer, 16);
|
|
}
|
|
|
|
/*
|
|
* Write the HMAC
|
|
*/
|
|
sha256_finish(&sha_ctx, digest);
|
|
sha256_starts(&sha_ctx);
|
|
sha256_update(&sha_ctx, opad, 64);
|
|
sha256_update(&sha_ctx, digest, 32);
|
|
sha256_finish(&sha_ctx, digest);
|
|
memcpy(p, digest, 32);
|
|
p += 32;
|
|
|
|
return (p - ciphertext);
|
|
}
|
|
|
|
/*
|
|
* AESStringDecrypt
|
|
*
|
|
* Description
|
|
* This function is called to decrypt the string "ciphertext".
|
|
* The decrypted string is placed in "plaintext".
|
|
*
|
|
* Parameters
|
|
* password [in]
|
|
* The password used to encrypt the string in UCS-16
|
|
* format.
|
|
* password_length [in]
|
|
* The length of the password in octets
|
|
* ciphertext [in]
|
|
* The ciphertext string to be decrypted
|
|
* ciphertext_length [in]
|
|
* The length of the ciphertext string
|
|
* plaintext [out]
|
|
* The decrypted string
|
|
*
|
|
* Returns
|
|
* Returns the length of the plaintext string or AESSTRINGCRYPT_ERROR
|
|
* if there was an error when trying to encrypt the string.
|
|
*/
|
|
unsigned long long AESStringDecrypt(unsigned char *password,
|
|
unsigned long password_length,
|
|
unsigned char *ciphertext,
|
|
unsigned long long ciphertext_length,
|
|
unsigned char *plaintext)
|
|
{
|
|
aes_context aes_ctx;
|
|
sha256_context sha_ctx;
|
|
sha256_t digest;
|
|
unsigned char IV[16];
|
|
int i, n;
|
|
unsigned char buffer[64], buffer2[32];
|
|
unsigned char ipad[64], opad[64];
|
|
unsigned char *p;
|
|
int final_block_size;
|
|
|
|
/*
|
|
* Encrypted strings will be at least 53 octets in length
|
|
* and the rest must be a multiple of 16 octets
|
|
*/
|
|
if (ciphertext_length < 53)
|
|
{
|
|
return AESSTRINGCRYPT_ERROR;
|
|
}
|
|
|
|
if (!(ciphertext[0] == 'A' && ciphertext[1] == 'E' &&
|
|
ciphertext[2] == 'S'))
|
|
{
|
|
return AESSTRINGCRYPT_ERROR;
|
|
}
|
|
|
|
/*
|
|
* Validate the version number and take any version-specific actions
|
|
*/
|
|
if (ciphertext[3] > 0)
|
|
{
|
|
return AESSTRINGCRYPT_ERROR;
|
|
}
|
|
|
|
/*
|
|
* Take note of the final block size
|
|
*/
|
|
final_block_size = ciphertext[4];
|
|
|
|
/*
|
|
* Move pointers and count beyond header
|
|
*/
|
|
ciphertext += 5;
|
|
ciphertext_length -= 5;
|
|
|
|
/*
|
|
* We will use p to write into the plaintext buffer
|
|
*/
|
|
p = plaintext;
|
|
|
|
/*
|
|
* Read the initialization vector
|
|
*/
|
|
memcpy(IV, ciphertext, 16);
|
|
ciphertext += 16;
|
|
ciphertext_length -= 16;
|
|
|
|
/*
|
|
* Hash the IV and password 8192 times
|
|
*/
|
|
memset(digest, 0, 32);
|
|
memcpy(digest, IV, 16);
|
|
|
|
for (i = 0; i < 8192; i++)
|
|
{
|
|
sha256_starts(&sha_ctx);
|
|
sha256_update(&sha_ctx, digest, 32);
|
|
sha256_update(&sha_ctx,
|
|
password,
|
|
password_length);
|
|
sha256_finish(&sha_ctx,
|
|
digest);
|
|
}
|
|
|
|
/*
|
|
* Set the AES encryption key
|
|
*/
|
|
aes_set_key(&aes_ctx, digest, 256);
|
|
|
|
/*
|
|
* Set the ipad and opad arrays with values as
|
|
* per RFC 2104 (HMAC). HMAC is defined as
|
|
* H(K XOR opad, H(K XOR ipad, text))
|
|
*/
|
|
memset(ipad, 0x36, 64);
|
|
memset(opad, 0x5C, 64);
|
|
|
|
for (i = 0; i < 32; i++)
|
|
{
|
|
ipad[i] ^= digest[i];
|
|
opad[i] ^= digest[i];
|
|
}
|
|
|
|
sha256_starts(&sha_ctx);
|
|
sha256_update(&sha_ctx, ipad, 64);
|
|
|
|
while (ciphertext_length > 32)
|
|
{
|
|
memcpy(buffer, ciphertext, 16);
|
|
memcpy(buffer2, ciphertext, 16);
|
|
ciphertext += 16;
|
|
ciphertext_length -= 16;
|
|
|
|
sha256_update(&sha_ctx, buffer, 16);
|
|
aes_decrypt(&aes_ctx, buffer, buffer);
|
|
|
|
/*
|
|
* XOR plain text block with previous encrypted output (i.e., use CBC)
|
|
*/
|
|
for (i = 0; i < 16; i++)
|
|
{
|
|
buffer[i] ^= IV[i];
|
|
}
|
|
|
|
/*
|
|
* Update the IV (CBC mode)
|
|
*/
|
|
memcpy(IV, buffer2, 16);
|
|
|
|
/*
|
|
* If this is the final block, then we may
|
|
* write less than 16 octets
|
|
*/
|
|
if ((ciphertext_length > 32) || (!final_block_size))
|
|
{
|
|
n = 16;
|
|
}
|
|
else
|
|
{
|
|
n = final_block_size;
|
|
}
|
|
|
|
/*
|
|
* Write the decrypted block
|
|
*/
|
|
memcpy(p, buffer, n);
|
|
p += n;
|
|
}
|
|
|
|
/*
|
|
* Verify that the HMAC is correct
|
|
*/
|
|
if (ciphertext_length != 32)
|
|
{
|
|
return AESSTRINGCRYPT_ERROR;
|
|
}
|
|
|
|
sha256_finish(&sha_ctx, digest);
|
|
sha256_starts(&sha_ctx);
|
|
sha256_update(&sha_ctx, opad, 64);
|
|
sha256_update(&sha_ctx, digest, 32);
|
|
sha256_finish(&sha_ctx, digest);
|
|
|
|
if (memcmp(digest, ciphertext, 32))
|
|
{
|
|
return AESSTRINGCRYPT_ERROR;
|
|
}
|
|
|
|
return (p - plaintext);
|
|
}
|
|
|