-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- add parameters for verification, verbosity and such - changed output to output "hash *file" - add SHA256SUMS verification via -c FILE - add visual studio solution - changed argument parsing - add exit codes for every success and failure - rewrote documentation - add version -
- Loading branch information
Showing
18 changed files
with
1,364 additions
and
424 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,4 @@ | ||
*.obj | ||
*.exe | ||
*.exe | ||
x64/ | ||
.vs/ |
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,35 +1,86 @@ | ||
# sha256sum for Windows | ||
|
||
Since `certutil` in CMD is not ideal for use in a Dockerfile and I prefer not to rely on random authors on the web, I've | ||
created my own `sha256sum.exe` tool. | ||
The built-in certutil utility in CMD is not ideal for use in a Dockerfile, primarily because it prints out multiple lines and lacks a -c option for file verification. To avoid relying on third-party solutions from unknown authors, I've created my own sha256sum.exe utility. | ||
|
||
## Requirements | ||
|
||
The *Makefile* is designed for use with MSVC. Specifically, I've developed it using VS 2017 version 14.16.27023. | ||
The _Makefile_ is designed for use with MSVC. Specifically, I've developed it using VS 2022 version v17.7.4. | ||
|
||
## Building sha256sum.exe | ||
|
||
1. Open the *x64 Native Tools Command Prompt for VS 2017*. | ||
1. Open the _x64 Native Tools Command Prompt for VS 2022_. | ||
2. Navigate to the folder containing the Makefile. | ||
3. Run the `nmake` command. | ||
3. Run the `msbuild` command. | ||
|
||
## How to Run | ||
Alternatively, you can open the Solution file in the Visual Studio IDE. | ||
|
||
Either download the pre-compiled `sha256sum.exe` or build it yourself. To run the tool, execute it with a file | ||
parameter. For example: | ||
## Usage | ||
|
||
```bash | ||
sha256sum [OPTION]... [FILE]... | ||
``` | ||
|
||
| Option | Description | | ||
| ------------------ | --------------------------------------------------------------------------------------- | | ||
| -c, --check <FILE> | read checksums from the FILE and check them, input must be UTF-8 encoded | | ||
| -b, --binary | read in binary mode, this is default | | ||
| -t, --text | read in text mode, fails because WinAPI's ReadFile/CreateFile only reads in binary mode | | ||
| -q, --quiet | don't print OK, just FAILED if checks fail | | ||
| -s, --status | don't print anything, just return status code | | ||
| -w, --warn | shows SHA256SUMS errors | | ||
| -v, --version | shows program's version | | ||
|
||
### Examples | ||
|
||
Here's how to use the utility: | ||
|
||
```bash | ||
sha256sum.exe myText.txt | ||
sha256sum.exe *.txt | ||
sha256sum.exe hello.txt world.txt | ||
sha256sum.exe *.txt > SHA256SUMS | ||
sha256sum.exe -c SHA256SUMS | ||
``` | ||
|
||
## Design Decisions | ||
### Exit Codes | ||
|
||
| Code | Name | Description | | ||
| ---- | --------------------------------------------- | -------------------------------------------------------------------------- | | ||
| 1 | MAIN_FAILED_TO_FIND_FILES | WinAPI's FindFirstFile was not able to find files wth given FILE arguments | | ||
| 2 | PARSE_ARGS_MISSING_PARAMETER | too few arguments, at least 1 is required | | ||
| 3 | PARSE_ARGS_MISSING_SHASUMS_FILE | -c argument found but missing following sum file, `-c <FILE>` | | ||
| 4 | PARSE_ARGS_ALLOCATE_ERROR | memory allocation failed for file list, memory low? | | ||
| 5 | CALC_HASH_FAILED_TO_OPEN_FILE | failed to open FILE, check permissions, if file exists | | ||
| 6 | CALC_HASH_FAILED_TO_OPEN_ALG_HANDLE | failed to open algorithm handle[1] | | ||
| 7 | CALC_HASH_FAILED_TO_ALLOCATE_HASH_BUFFER_SIZE | failed to calculate hash buffer size[1] | | ||
| 8 | CALC_HASH_FAILED_TO_ALLOCATE_HASH_OBJECT | failed to allocate hash object[1] | | ||
| 9 | CALC_HASH_FAILED_TO_CALC_HASH_LENGTH | failed to calculate hash length[1] | | ||
| 10 | CALC_HASH_FAILED_TO_ALLOCATE_HASH_BUFFER | failed to allocate hash buffer[1] | | ||
| 11 | CALC_HASH_FAILED_TO_CREATE_HASH | failed to create hash[1] | | ||
| 12 | CALC_HASH_FAILED_TO_READ | failed to read from given FILE | | ||
| 13 | CALC_HASH_FAILED_TO_HASH | failed to hash[1] | | ||
| 14 | CALC_HASH_FAILED_TO_FINISH_HASH | failed to finish hash[1] | | ||
| 15 | CALC_HASH_FAILED_TO_ALLOCATE_FILE_HASH | failed to allocate memory for file hash, check your memory | | ||
| 16 | PARSE_LINE_INVALID_HASH_TOKEN | invalid hash token may happen if the line does not contain spaces | | ||
| 17 | PARSE_LINE_INVALID_HASH_LENGTH | fails when the token is not 64 characters long | | ||
| 18 | PARSE_LINE_INAVLID_FILE | fails if the file does not have a second string after the space(s) | | ||
| 19 | CHECK_SUMS_FAILED_TO_OPEN_SUM_FILE | failed to open -c FILE | | ||
| 20 | CHECK_SUMS_LINE_TOO_LONG | line in sum file is longer exceeds internal buffer (1024) | | ||
| 21 | CHECK_SUMS_FAILED_TO_ALLOCATE_WIDE_BUFFER1 | line buffer allocation for UTF-16 failed | | ||
| 22 | CHECK_SUMS_FAILED_TO_ALLOCATE_FILE_HASH1 | file hash object allocation failed | | ||
| 23 | CHECK_SUMS_FAILED_TO_ALLOCATE_WIDE_BUFFER2 | line buffer allocation for UTF-16 failed[2] | | ||
| 24 | CHECK_SUMS_FAILED_TO_ALLOCATE_FILE_HASH2 | file hash object allocation failed[2] | | ||
| 25 | CHECK_SUMS_FAILED_TO_READ | failed to read from -c FILE | | ||
| 26 | CHECK_SUM_CHECKSUM_FAILED | checksum verification failed | | ||
|
||
[1] This should never occur. sha256sum.exe uses Microsoft's Cryptography API: Next Generation (CNG) with fixed values. If this happens the system is probably missing the CNG. | ||
|
||
I made several decisions regarding the APIs used in the code. My initial attempt involved using OpenSSL's EVP functions, | ||
but I encountered issues with static linking. Using a shared library resulted in an executable that was only about | ||
120 KB in size, but it had a dependency on `libcrypto-3-x64.dll`, which is around 5-6 MB. | ||
[2] There is a duplication for parsing the file when the end of file is not reached yet and for the remaining characters that are still in the read buffer. | ||
|
||
## Design Decisions | ||
|
||
I also tried writing `sha256sum.exe` in Go, which resulted in a file size of around 1.2 MB. While this size is | ||
acceptable, I wanted to minimize the file size for use in a nanoserver environment. | ||
I made several decisions regarding the APIs used in the code: | ||
|
||
The current implementation uses the *Cryptography API: Next Generation* (CNG) and the Windows API for file reading. It | ||
doesn't depend on any third-party libraries not already included in Windows, resulting in a very compact executable. | ||
- **OpenSSL's EVP Functions**: My initial attempt involved using these, but I encountered issues with static linking. | ||
- **Go Implementation**: Resulted in a larger file size than desired. | ||
- **CNG and Windows API**: The current implementation uses these native APIs to minimize dependencies and file size. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
e3247fa228bcdd1eaf815cb664761bd3ccd72b247105a1118a169d1e6e75e754 *.gitignore | ||
88b177b001f949aca6e29cf9d481cf241c5ecd46ff39bfa709ee085d99facf77 *args.c | ||
147a3761456127bcb8ada2c34728a301d01acd316946aad7d605c3a9ce37e6f2 *LICENSE | ||
64c524482575b98db15fbfdaae6723fbaba0660d50986e77f237503c31377a0b *main.c | ||
78e6e19baff8e8b2a2624d4219d08a8a781c2deb595da04cc7b14a4e2bc29c21 *README.md | ||
08cb7d72f4915b23ae173d410e8d89e94d07f57fb550aa325da8ffb5c166558c *sha256.c | ||
96c0c25c4958bdba7e26cd76fe09a05f8ee974730e1a3c0a011be33971374268 *sha256sum.h | ||
f9b427d603ff904a4aeb6ebed684ba6ea59734a79da0d90a9ab8363b24d59140 *tests.c |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,40 +1,127 @@ | ||
#include "args.h" | ||
#include "sha256sum.h" | ||
|
||
errno_t parse_args(int argc, wchar_t *argv[], wchar_t **out_shasums_file) | ||
void usage(__in LPWSTR prog, __in_opt LPWSTR message) | ||
{ | ||
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) | ||
#ifndef _UNITTESTS | ||
if (NULL != message) | ||
{ | ||
// 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; | ||
} | ||
wprintf(L"%ls\n", message); | ||
} | ||
} | ||
|
||
return OK; | ||
wprintf(L"Usage: %ls [-c sha256sums_file] [file...]\n", prog); | ||
#endif | ||
} | ||
|
||
void usage(wchar_t *prog, wchar_t *message) | ||
ErrorCode parse_args(__out Args* args, __in int argc, __in LPWSTR argv[]) | ||
{ | ||
if (NULL != message) | ||
{ | ||
fwprintf(stderr, L"%ls\n", message); | ||
} | ||
ErrorCode status = SUCCESS; | ||
FileList* current = NULL; | ||
|
||
// default values | ||
args->files = NULL; | ||
args->sum_file = NULL; | ||
args->quiet = FALSE; | ||
args->status = FALSE; | ||
args->warn = FALSE; | ||
args->show_version = FALSE; | ||
args->text_mode = FALSE; | ||
|
||
// check if there are any argments given | ||
if (argc < 2) | ||
{ | ||
usage(argv[0], L"too few arguments"); | ||
status = PARSE_ARGS_MISSING_PARAMETER; | ||
goto Cleanup; | ||
} | ||
|
||
for (int i = 1; i < argc; ++i) | ||
{ | ||
// -v, --version | ||
if (wcscmp(argv[i], L"-v") == 0 || wcscmp(argv[i], L"--version") == 0) | ||
{ | ||
args->show_version = TRUE; | ||
goto Cleanup; | ||
} | ||
|
||
// -t, --text | ||
if (wcscmp(argv[i], L"-t") == 0 || wcscmp(argv[i], L"--text") == 0) | ||
{ | ||
args->text_mode = TRUE; | ||
goto Cleanup; | ||
} | ||
|
||
// -b, --binary | ||
if (wcscmp(argv[i], L"-b") == 0 || wcscmp(argv[i], L"--binary") == 0) | ||
{ | ||
// do nothing, since this only works with binary | ||
continue; | ||
} | ||
|
||
// -q, --quiet | ||
if (wcscmp(argv[i], L"-q") == 0 || wcscmp(argv[i], L"--quiet") == 0) | ||
{ | ||
args->quiet = TRUE; | ||
continue; | ||
} | ||
|
||
// -s, --status | ||
if (wcscmp(argv[i], L"-s") == 0 || wcscmp(argv[i], L"--status") == 0) | ||
{ | ||
args->status = TRUE; | ||
continue; | ||
} | ||
|
||
// -w, --warn | ||
if (wcscmp(argv[i], L"-w") == 0 || wcscmp(argv[i], L"--warn") == 0) | ||
{ | ||
args->warn = TRUE; | ||
continue; | ||
} | ||
|
||
// -c, --check <file> | ||
// checks for -c or --check and checks the following argument | ||
// fails when there is no other argument after -c | ||
if (wcscmp(argv[i], L"-c") == 0 || wcscmp(argv[i], L"--check") == 0) | ||
{ | ||
// check if there is another argument after -c | ||
if (i + 1 < argc) | ||
{ | ||
args->sum_file = argv[i + 1]; | ||
++i; // skip next argument since we used it here | ||
continue; | ||
} | ||
else | ||
{ | ||
usage(argv[0], L"missing SHA256SUMS file"); | ||
status = PARSE_ARGS_MISSING_SHASUMS_FILE; | ||
goto Cleanup; | ||
} | ||
} | ||
// if there are no argument handling left, we assume the rest are files | ||
else | ||
{ | ||
FileList* newFile = malloc(sizeof(FileList)); | ||
if (newFile == NULL) | ||
{ | ||
wprintf(L"allocation for new file list item failed\n"); | ||
status = PARSE_ARGS_ALLOCATE_ERROR; | ||
goto Cleanup; | ||
} | ||
newFile->file = argv[i]; | ||
newFile->next = NULL; | ||
|
||
wprintf(L"Usage: %ls [-c sha256sums_file] [file...]\n", prog); | ||
} | ||
if (args->files == NULL) | ||
{ | ||
args->files = newFile; | ||
current = args->files; | ||
} | ||
else | ||
{ | ||
current->next = newFile; | ||
current = newFile; | ||
} | ||
} | ||
} | ||
|
||
Cleanup: | ||
return status; | ||
} |
Oops, something went wrong.