From 4274ca93c7622495bb368d579c9a7c96c3069053 Mon Sep 17 00:00:00 2001 From: Christian Wansart Date: Mon, 25 Sep 2023 20:22:42 +0200 Subject: [PATCH] refactor to implement arguments --- .gitignore | 4 +- .vscode/settings.json | 7 ++ Makefile | 2 +- Makefile.tests | 32 +++++++ args.c | 40 +++++++++ args.h | 9 ++ main.c | 189 +++------------------------------------- sha256.c | 197 ++++++++++++++++++++++++++++++++++++++++++ sha256.h | 4 + tests.c | 74 ++++++++++++++++ 10 files changed, 380 insertions(+), 178 deletions(-) create mode 100644 .vscode/settings.json create mode 100644 Makefile.tests create mode 100644 args.c create mode 100644 args.h create mode 100644 sha256.c create mode 100644 sha256.h create mode 100644 tests.c diff --git a/.gitignore b/.gitignore index 11fc1f7..73fb0bd 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,2 @@ -main.obj -sha256sum.exe \ No newline at end of file +*.obj +*.exe \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..46055b9 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,7 @@ +{ + "files.associations": { + "sha256.h": "c", + "wchar.h": "c", + "args.h": "c" + } +} \ No newline at end of file diff --git a/Makefile b/Makefile index 3e2aab5..5457421 100644 --- a/Makefile +++ b/Makefile @@ -8,7 +8,7 @@ CFLAGS = /W4 /MD /O2 OUTFILE = sha256sum.exe # Source files -SOURCES = main.c +SOURCES = main.c args.c sha256.c # Object files (one per source file) OBJECTS = $(SOURCES:.c=.obj) diff --git a/Makefile.tests b/Makefile.tests new file mode 100644 index 0000000..8c64fb5 --- /dev/null +++ b/Makefile.tests @@ -0,0 +1,32 @@ +# Compiler +CC = cl + +# Flags for the compiler +CFLAGS = /W4 /MD /O2 + +# Name of the final executable +OUTFILE = tests.exe + +# Source files +SOURCES = tests.c args.c + +# Object files (one per source file) +OBJECTS = $(SOURCES:.c=.obj) + +# Default target +all: $(OUTFILE) + +# Linking rule +$(OUTFILE): $(OBJECTS) + $(CC) $(CFLAGS) /Fe$@ $(OBJECTS) /link + +# Compilation rule +.c.obj: + $(CC) $(CFLAGS) /c $< /Fo$@ + +# Clean rule +clean: + del *.obj $(OUTFILE) + +# Phony targets +.PHONY: all clean diff --git a/args.c b/args.c new file mode 100644 index 0000000..2be1c65 --- /dev/null +++ b/args.c @@ -0,0 +1,40 @@ +#include "args.h" + +errno_t parse_args(int argc, wchar_t *argv[], wchar_t **out_shasums_file) +{ + if (argc < 2) + { + usage(argv[0], L"missing parameter"); + return ARGS_MISSING_PARAMETER; + } + + for (int i = 0; i < argc; ++i) + { + if (wcscmp(argv[i], L"-c") == 0) + { + // check if there is another argument after -c + if (i + 1 < argc) + { + *out_shasums_file = argv[i + 1]; + ++i; // skip next argument since we used it here + } + else + { + usage(argv[0], L""); + return ARGS_MISSING_SHASUMS_FILE; + } + } + } + + return OK; +} + +void usage(wchar_t *prog, wchar_t *message) +{ + if (NULL != message) + { + fwprintf(stderr, L"%ls\n", message); + } + + wprintf(L"Usage: %ls [-c sha256sums_file] [file...]\n", prog); +} \ No newline at end of file diff --git a/args.h b/args.h new file mode 100644 index 0000000..7c9ffee --- /dev/null +++ b/args.h @@ -0,0 +1,9 @@ +#pragma once +#include + +#define OK 0 +#define ARGS_MISSING_PARAMETER 1 +#define ARGS_MISSING_SHASUMS_FILE 2 + +errno_t parse_args(int argc, wchar_t *argv[], wchar_t **out_shasums_file); +void usage(wchar_t *prog, wchar_t *message); diff --git a/main.c b/main.c index e14dca6..b237cc2 100644 --- a/main.c +++ b/main.c @@ -1,189 +1,28 @@ -#include -#include -#include +#include "args.h" +#include "sha256.h" -#pragma comment(lib, "bcrypt.lib") - -#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0) -#define STATUS_UNSUCCESSFUL ((NTSTATUS)0xC0000001L) -#define SET_FAILURE_AND_CLEANUP do { \ - failure = TRUE; \ - goto Cleanup; \ -} while(0) - -int __cdecl wmain(int argc, wchar_t *argv[]) +int wmain(int argc, wchar_t *argv[]) { - BOOL failure = FALSE; - HANDLE hFile; - DWORD dwBytesRead; - BYTE buffer[1024] = {0}; + wchar_t *shasums_file = NULL; - BCRYPT_ALG_HANDLE hAlg = NULL; - BCRYPT_HASH_HANDLE hHash = NULL; - NTSTATUS status = STATUS_UNSUCCESSFUL; - DWORD cbData = 0, - cbHash = 0, - cbHashObject = 0; - PBYTE pbHashObject = NULL; - PBYTE pbHash = NULL; - - // read args - if (argc < 2) + errno_t parse_result = parse_args(argc, argv, &shasums_file); + if (parse_result != OK) { - wprintf(L"missing file parameter\n"); - wprintf(L"usage: %ls \n", argv[0]); return 1; } - // open file - hFile = CreateFileW(argv[1], // File name - GENERIC_READ, // Open for reading - 0, // No sharing - NULL, // Default security - OPEN_EXISTING, // Existing file only - FILE_ATTRIBUTE_NORMAL, // Normal file - NULL); - if (hFile == INVALID_HANDLE_VALUE) - { - wprintf(L"open file failed: %d\n", GetLastError()); - SET_FAILURE_AND_CLEANUP; - } - - // open an algorithm handle - if (!NT_SUCCESS(status = BCryptOpenAlgorithmProvider( - &hAlg, - BCRYPT_SHA256_ALGORITHM, - NULL, - 0))) - { - wprintf(L"open an algorithm handle failed: %x\n", status); - SET_FAILURE_AND_CLEANUP; - } - - // calculate the size of the buffer to hold the hash object - if (!NT_SUCCESS(status = BCryptGetProperty( - hAlg, - BCRYPT_OBJECT_LENGTH, - (PBYTE)&cbHashObject, - sizeof(DWORD), - &cbData, - 0))) - { - wprintf(L"hash buffer size allocation failed, err: %x\n", status); - SET_FAILURE_AND_CLEANUP; - } - - // allocate the hash object on the heap - pbHashObject = (PBYTE)HeapAlloc(GetProcessHeap(), 0, cbHashObject); - if (NULL == pbHashObject) - { - wprintf(L"memory allocation for hash object failed\n"); - SET_FAILURE_AND_CLEANUP; - } - - // calculate the length of the hash - if (!NT_SUCCESS(status = BCryptGetProperty( - hAlg, - BCRYPT_HASH_LENGTH, - (PBYTE)&cbHash, - sizeof(DWORD), - &cbData, - 0))) + // just print checksum + if (shasums_file == NULL) { - wprintf(L"hash length calculation failed: %x\n", status); - SET_FAILURE_AND_CLEANUP; - } - - // allocate the hash buffer on the heap - pbHash = (PBYTE)HeapAlloc(GetProcessHeap(), 0, cbHash); - if (NULL == pbHash) - { - wprintf(L"memory allocation for hash buffer failed\n"); - SET_FAILURE_AND_CLEANUP; - } - - // create a hash - if (!NT_SUCCESS(status = BCryptCreateHash( - hAlg, - &hHash, - pbHashObject, - cbHashObject, - NULL, - 0, - 0))) - { - wprintf(L"hash creation failed: %x\n", status); - SET_FAILURE_AND_CLEANUP; - } - - while (TRUE) - { - if (!ReadFile(hFile, buffer, sizeof(buffer), &dwBytesRead, NULL)) + wchar_t *file_hash = calc_hash(argv[1]); + if (file_hash != NULL) { - wprintf(L"read file failed: %d\n", GetLastError()); - SET_FAILURE_AND_CLEANUP; - } - - if (dwBytesRead == 0) - { - break; - } - - // hash some data - if (!NT_SUCCESS(status = BCryptHashData( - hHash, - (PBYTE)buffer, - dwBytesRead, - 0))) - { - wprintf(L"data hashing failed: %x\n", status); - SET_FAILURE_AND_CLEANUP; + wprintf(L"%ls %ls\n", file_hash, argv[1]); } } - - // close the hash - if (!NT_SUCCESS(status = BCryptFinishHash( - hHash, - pbHash, - cbHash, - 0))) + // check SHASUMS + else { - wprintf(L"hash finalization failed: %x\n", status); - SET_FAILURE_AND_CLEANUP; + wprintf(L"to be implemented"); } - - // Output the hash - for (DWORD i = 0; i < cbHash; i++) - { - printf("%02x", pbHash[i]); - } - -Cleanup: - - if (hFile) - { - CloseHandle(hFile); - } - - if (hAlg) - { - BCryptCloseAlgorithmProvider(hAlg, 0); - } - - if (hHash) - { - BCryptDestroyHash(hHash); - } - - if (pbHashObject) - { - HeapFree(GetProcessHeap(), 0, pbHashObject); - } - - if (pbHash) - { - HeapFree(GetProcessHeap(), 0, pbHash); - } - - return failure; } diff --git a/sha256.c b/sha256.c new file mode 100644 index 0000000..471690f --- /dev/null +++ b/sha256.c @@ -0,0 +1,197 @@ +#include +#include +#include + +#pragma comment(lib, "bcrypt.lib") + +#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0) +#define STATUS_UNSUCCESSFUL ((NTSTATUS)0xC0000001L) +#define SET_FAILURE_AND_CLEANUP \ + do \ + { \ + goto Cleanup; \ + } while (0) + +wchar_t *calc_hash(wchar_t *file) +{ + wchar_t *file_hash = NULL; + HANDLE hFile; + DWORD dwBytesRead; + BYTE buffer[1024] = {0}; + + BCRYPT_ALG_HANDLE hAlg = NULL; + BCRYPT_HASH_HANDLE hHash = NULL; + NTSTATUS status = STATUS_UNSUCCESSFUL; + DWORD cbData = 0, + cbHash = 0, + cbHashObject = 0; + PBYTE pbHashObject = NULL; + PBYTE pbHash = NULL; + + // open file + hFile = CreateFileW(file, // File name + GENERIC_READ, // Open for reading + 0, // No sharing + NULL, // Default security + OPEN_EXISTING, // Existing file only + FILE_ATTRIBUTE_NORMAL, // Normal file + NULL); + if (hFile == INVALID_HANDLE_VALUE) + { + wprintf(L"open file failed: %d\n", GetLastError()); + SET_FAILURE_AND_CLEANUP; + } + + // open an algorithm handle + if (!NT_SUCCESS(status = BCryptOpenAlgorithmProvider( + &hAlg, + BCRYPT_SHA256_ALGORITHM, + NULL, + 0))) + { + wprintf(L"open an algorithm handle failed: %x\n", status); + SET_FAILURE_AND_CLEANUP; + } + + // calculate the size of the buffer to hold the hash object + if (!NT_SUCCESS(status = BCryptGetProperty( + hAlg, + BCRYPT_OBJECT_LENGTH, + (PBYTE)&cbHashObject, + sizeof(DWORD), + &cbData, + 0))) + { + wprintf(L"hash buffer size allocation failed, err: %x\n", status); + SET_FAILURE_AND_CLEANUP; + } + + // allocate the hash object on the heap + pbHashObject = (PBYTE)HeapAlloc(GetProcessHeap(), 0, cbHashObject); + if (NULL == pbHashObject) + { + wprintf(L"memory allocation for hash object failed\n"); + SET_FAILURE_AND_CLEANUP; + } + + // calculate the length of the hash + if (!NT_SUCCESS(status = BCryptGetProperty( + hAlg, + BCRYPT_HASH_LENGTH, + (PBYTE)&cbHash, + sizeof(DWORD), + &cbData, + 0))) + { + wprintf(L"hash length calculation failed: %x\n", status); + SET_FAILURE_AND_CLEANUP; + } + + // allocate the hash buffer on the heap + pbHash = (PBYTE)HeapAlloc(GetProcessHeap(), 0, cbHash); + if (NULL == pbHash) + { + wprintf(L"memory allocation for hash buffer failed\n"); + SET_FAILURE_AND_CLEANUP; + } + + // create a hash + if (!NT_SUCCESS(status = BCryptCreateHash( + hAlg, + &hHash, + pbHashObject, + cbHashObject, + NULL, + 0, + 0))) + { + wprintf(L"hash creation failed: %x\n", status); + SET_FAILURE_AND_CLEANUP; + } + + while (TRUE) + { + if (!ReadFile(hFile, buffer, sizeof(buffer), &dwBytesRead, NULL)) + { + wprintf(L"read file failed: %d\n", GetLastError()); + SET_FAILURE_AND_CLEANUP; + } + + if (dwBytesRead == 0) + { + break; + } + + // hash some data + if (!NT_SUCCESS(status = BCryptHashData( + hHash, + (PBYTE)buffer, + dwBytesRead, + 0))) + { + wprintf(L"data hashing failed: %x\n", status); + SET_FAILURE_AND_CLEANUP; + } + } + + // close the hash + if (!NT_SUCCESS(status = BCryptFinishHash( + hHash, + pbHash, + cbHash, + 0))) + { + wprintf(L"hash finalization failed: %x\n", status); + SET_FAILURE_AND_CLEANUP; + } + + // Output the hash + file_hash = (wchar_t *)malloc((cbHash * 2 + 1) * sizeof(wchar_t)); + if (file_hash == NULL) + { + wprintf(L"memory allocation for file hash failed\n"); + free(file_hash); + SET_FAILURE_AND_CLEANUP; + } + for (DWORD i = 0; i < cbHash; i++) + { + _swprintf(&file_hash[i * 2], L"%02x", pbHash[i]); + // _swprintf_s(&file_hash[i * 2], 3, L"%02x", pbHash[i]); + // _swprintf(&file_hash[i * 2], 3, L"%02x", pbHash[i]); + } + file_hash[cbHash * 2] = L'\0'; + +Cleanup: + + if (hFile) + { + CloseHandle(hFile); + } + + if (hAlg) + { + BCryptCloseAlgorithmProvider(hAlg, 0); + } + + if (hHash) + { + BCryptDestroyHash(hHash); + } + + if (pbHashObject) + { + HeapFree(GetProcessHeap(), 0, pbHashObject); + } + + if (pbHash) + { + HeapFree(GetProcessHeap(), 0, pbHash); + } + + return file_hash; +} + +errno_t check_sums(wchar_t *checksum_file) +{ + return 0; +} diff --git a/sha256.h b/sha256.h new file mode 100644 index 0000000..d2399f7 --- /dev/null +++ b/sha256.h @@ -0,0 +1,4 @@ +#pragma once + +wchar_t * calc_hash(wchar_t *); +errno_t check_sums(wchar_t *); diff --git a/tests.c b/tests.c new file mode 100644 index 0000000..60e3c23 --- /dev/null +++ b/tests.c @@ -0,0 +1,74 @@ +#include "args.h" + +#define TEST_RESULT(test_num, exp, act) \ + do \ + { \ + if ((act) == (exp)) \ + { \ + wprintf(L"TEST %ld: RESULT OK\n", (test_num)); \ + } \ + else \ + { \ + fwprintf(stderr, L"TEST %ld: RESULT expected=%ld, actual=%ld\n", (test_num), (exp), (act)); \ + } \ + } while (0) + +#define TEST_SHA256_FILE(test_num, exp, act) \ + do \ + { \ + wchar_t *exp_str = (exp == NULL) ? L"NULL" : exp; \ + wchar_t *act_str = (act == NULL) ? L"NULL" : act; \ + if (wcscmp(exp_str, act_str) == 0) \ + { \ + wprintf(L"TEST %ld: SHA FILE OK\n", (test_num)); \ + } \ + else \ + { \ + fwprintf(stderr, L"TEST %ld SHA FILE expected=%ls, actual=%ls\n", (test_num), (exp_str), (act_str)); \ + } \ + } while (0) + +void wmain() +{ + int test_num = 1; + + { // TEST 1 + int argc = 1; + wchar_t *argv[] = {L"prog"}; + wchar_t *shasums_file = NULL; + errno_t act = parse_args(argc, argv, &shasums_file); + errno_t exp = ARGS_MISSING_PARAMETER; + TEST_RESULT(test_num, exp, act); + TEST_SHA256_FILE(test_num++, NULL, shasums_file); + } + + { // TEST 2 + int argc = 2; + wchar_t *argv[] = {L"prog", L"-c"}; + wchar_t *shasums_file = NULL; + errno_t act = parse_args(argc, argv, &shasums_file); + errno_t exp = ARGS_MISSING_SHASUMS_FILE; + TEST_RESULT(test_num, exp, act); + TEST_SHA256_FILE(test_num++, NULL, shasums_file); + } + + { // TEST 3 + int argc = 3; + wchar_t *argv[] = {L"prog", L"-c", L"SHA256SUMS"}; + wchar_t *shasums_file = NULL; + errno_t act = parse_args(argc, argv, &shasums_file); + errno_t exp = OK; + TEST_RESULT(test_num, exp, act); + TEST_SHA256_FILE(test_num++, L"SHA256SUMS", shasums_file); + } + + { // TEST 4 + int argc = 3; + wchar_t *argv[] = {L"prog", L"file1", L"file2"}; + wchar_t *shasums_file = NULL; + errno_t act = parse_args(argc, argv, &shasums_file); + errno_t exp = OK; + TEST_RESULT(test_num, exp, act); + TEST_SHA256_FILE(test_num++, NULL, shasums_file); + } +}