From ce9c44583cfeec7decbe62d70b1895e870952638 Mon Sep 17 00:00:00 2001 From: antoine Date: Mon, 18 Jan 2016 00:12:13 +0100 Subject: [PATCH] =?UTF-8?q?premier=20commit=20programme=20fonctionne=20mai?= =?UTF-8?q?s=20code=20d=C3=A9geu?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- aes.cpp | 256 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ aes.hpp | 0 crypto.ex | Bin 0 -> 34704 bytes file.txt | 7 ++ makefile | 6 ++ 5 files changed, 269 insertions(+) create mode 100644 aes.cpp create mode 100644 aes.hpp create mode 100755 crypto.ex create mode 100644 file.txt create mode 100644 makefile diff --git a/aes.cpp b/aes.cpp new file mode 100644 index 0000000..289f636 --- /dev/null +++ b/aes.cpp @@ -0,0 +1,256 @@ +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define FAILURE -1 +#define SUCCESS 0 + +#define AES_KEYLEN 256 +#define AES_ROUNDS 6 + + +int aesDecrypt(EVP_CIPHER_CTX* aesDecryptCtx, unsigned char* aesKey, unsigned char* aesIV, unsigned char *encMsg, size_t encMsgLen, unsigned char **decMsg) { + size_t decLen = 0; + size_t blockLen = 0; + + *decMsg = (unsigned char*)malloc(encMsgLen); + if(*decMsg == NULL) return FAILURE; + + if(!EVP_DecryptInit_ex(aesDecryptCtx, EVP_aes_256_cbc(), NULL, aesKey, aesIV)) { + return FAILURE; + } + + if(!EVP_DecryptUpdate(aesDecryptCtx, (unsigned char*)*decMsg, (int*)&blockLen, encMsg, (int)encMsgLen)) { + return FAILURE; + } + decLen += blockLen; + + if(!EVP_DecryptFinal_ex(aesDecryptCtx, (unsigned char*)*decMsg + decLen, (int*)&blockLen)) { + return FAILURE; + } + decLen += blockLen; + + EVP_CIPHER_CTX_cleanup(aesDecryptCtx); + + return (int)decLen; +} + +int aesEncrypt(EVP_CIPHER_CTX* aesEncryptCtx, unsigned char* aesKey, unsigned char* aesIV, const unsigned char *msg, size_t msgLen, unsigned char **encMsg) { + size_t blockLen = 0; + size_t encMsgLen = 0; + + *encMsg = (unsigned char*)malloc(msgLen + AES_BLOCK_SIZE); + if(encMsg == NULL) return FAILURE; + + if(!EVP_EncryptInit_ex(aesEncryptCtx, EVP_aes_256_cbc(), NULL, aesKey, aesIV)) { + return FAILURE; + } + + if(!EVP_EncryptUpdate(aesEncryptCtx, *encMsg, (int*)&blockLen, (unsigned char*)msg, msgLen)) { + return FAILURE; + } + encMsgLen += blockLen; + + if(!EVP_EncryptFinal_ex(aesEncryptCtx, *encMsg + encMsgLen, (int*)&blockLen)) { + return FAILURE; + } + + EVP_CIPHER_CTX_cleanup(aesEncryptCtx); + + return encMsgLen + blockLen; +} + +void clear_all(EVP_CIPHER_CTX* aesEncryptCtx, EVP_CIPHER_CTX* aesDecryptCtx, unsigned char* aesKey, unsigned char* aesIV) { + EVP_CIPHER_CTX_cleanup(aesEncryptCtx); + EVP_CIPHER_CTX_cleanup(aesDecryptCtx); + + free(aesEncryptCtx); + free(aesDecryptCtx); + + free(aesIV); + free(aesKey); +} + +void init_all(EVP_CIPHER_CTX* aesEncryptCtx, EVP_CIPHER_CTX* aesDecryptCtx, unsigned char* aesKey, unsigned char* aesIV) { + EVP_CIPHER_CTX_init(aesEncryptCtx); + EVP_CIPHER_CTX_init(aesDecryptCtx); + + unsigned char *aesPass = (unsigned char*)malloc(AES_KEYLEN/8); + unsigned char *aesSalt = (unsigned char*)malloc(8); + + if(aesKey == NULL || aesIV == NULL || aesPass == NULL || aesSalt == NULL) { + exit(FAILURE); + } + + #define USE_PBKDF + #ifdef USE_PBKDF + std::cerr << "utilisation de USE_PBKDF" << std::endl; + // Get some random data to use as the AES pass and salt + if(RAND_bytes(aesPass, AES_KEYLEN/8) == 0) { + exit(FAILURE); + } + + if(RAND_bytes(aesSalt, 8) == 0) { + exit(FAILURE); + } + + if(EVP_BytesToKey(EVP_aes_256_cbc(), EVP_sha256(), aesSalt, aesPass, AES_KEYLEN/8, AES_ROUNDS, aesKey, aesIV) == 0) { + exit(FAILURE); + } + #else + if(RAND_bytes(aesKey, AES_KEYLEN/8) == 0) { + exit(FAILURE); + } + + if(RAND_bytes(aesIV, AES_KEYLEN/8) == 0) { + exit(FAILURE); + } + #endif + + free(aesPass); + free(aesSalt); +} + +void writeFile(char *filename, unsigned char *file, size_t fileLength) { + FILE *fd = fopen(filename, "wb"); + if(fd == NULL) { + fprintf(stderr, "Failed to open file: %s\n", strerror(errno)); + exit(1); + } + + size_t bytesWritten = fwrite(file, 1, fileLength, fd); + + if(bytesWritten != fileLength) { + fprintf(stderr, "Failed to write file\n"); + exit(1); + } + + fclose(fd); +} + +int readFile(char *filename, unsigned char **file) { + FILE *fd = fopen(filename, "rb"); + if(fd == NULL) { + fprintf(stderr, "Failed to open file: %s\n", strerror(errno)); + exit(1); + } + + // Determine size of the file + fseek(fd, 0, SEEK_END); + size_t fileLength = ftell(fd); + fseek(fd, 0, SEEK_SET); + + // Allocate space for the file + *file = (unsigned char*)malloc(fileLength); + if(*file == NULL) { + fprintf(stderr, "Failed to allocate memory\n"); + exit(1); + } + + // Read the file into the buffer + size_t bytesRead = fread(*file, 1, fileLength, fd); + + if(bytesRead != fileLength) { + fprintf(stderr, "Error reading file\n"); + exit(1); + } + + fclose(fd); + + return fileLength; +} + +int main(int argc, char* argv[]) { + if(argc != 2) { + fprintf(stderr, "No file argument supplied.\n"); + return 1; + } + + EVP_CIPHER_CTX* aesEncryptCtx = (EVP_CIPHER_CTX*)malloc(sizeof(EVP_CIPHER_CTX)); + EVP_CIPHER_CTX* aesDecryptCtx = (EVP_CIPHER_CTX*)malloc(sizeof(EVP_CIPHER_CTX)); + if(aesDecryptCtx == NULL || aesEncryptCtx == NULL) { + return FAILURE; + } + + unsigned char* aesKey = (unsigned char*)malloc(AES_KEYLEN/8); + unsigned char* aesIV = (unsigned char*)malloc(AES_KEYLEN/8); + + init_all(aesEncryptCtx, aesDecryptCtx, aesKey, aesIV); + + //*************************************************** + char* filename = argv[1]; + + // Read the file to encrypt + unsigned char *file; + size_t fileLength = readFile(filename, &file); + printf("%d bytes to be encrypted\n", (int)fileLength); + + // Encrypt the file + unsigned char *encryptedFile; + int encryptedFileLength; + if((encryptedFileLength = aesEncrypt(aesEncryptCtx, aesKey, aesIV, (const unsigned char*)file, fileLength, &encryptedFile)) == -1) { + fprintf(stderr, "Encryption failed\n"); + return 1; + } + printf("%d bytes encrypted\n", encryptedFileLength); + + // Append .enc to the filename + char *encryptedFilename = (char*)malloc(strlen(filename) + 5); + if(encryptedFilename == NULL) { + fprintf(stderr, "Failed to allocate memory\n"); + return 1; + } + sprintf(encryptedFilename, "%s.enc", filename); + + // Write the encrypted file to its own file + writeFile(encryptedFilename, encryptedFile, encryptedFileLength); + + std::cerr << "aesKey : "; + for (int i = 0; i < 32; i++) { + std::cerr << std::hex << aesKey[i]; + } + std::cerr << std::endl; + + printf("Encrypted message written to \"%s\"\n", encryptedFilename); + + free(file); + //*************************************************** + + fileLength = readFile(encryptedFilename, &file); + + // Decrypt the encrypted file + unsigned char *decryptedFile; + int decryptedFileLength; + if((decryptedFileLength = aesDecrypt(aesDecryptCtx, aesKey, aesIV, file, fileLength, &decryptedFile)) == -1) { + fprintf(stderr, "Decryption failed\n"); + return 1; + } + printf("%d bytes decrypted\n", (int)decryptedFileLength); + + // Append .dec to the filename + char *decryptedFilename = (char*)malloc(strlen(filename) + 5); + if(decryptedFilename == NULL) { + fprintf(stderr, "Failed to allocate memory\n"); + return 1; + } + sprintf(decryptedFilename, "%s.dec", filename); + + // Write the decrypted file to its own file + writeFile(decryptedFilename, decryptedFile, decryptedFileLength); + printf("Decrypted file written to \"%s\"\n", decryptedFilename); + + free(decryptedFile); + free(file); + + + //*************************************************** + clear_all(aesEncryptCtx, aesDecryptCtx, aesKey, aesIV); + return 0; +} \ No newline at end of file diff --git a/aes.hpp b/aes.hpp new file mode 100644 index 0000000..e69de29 diff --git a/crypto.ex b/crypto.ex new file mode 100755 index 0000000000000000000000000000000000000000..8c1725c00cff5c413e15f96f9b6e60e7fa0bf7db GIT binary patch literal 34704 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