Initial commit.

This commit is contained in:
LoRd_MuldeR 2021-06-21 22:44:33 +02:00
commit 00bd137586
10 changed files with 1268 additions and 0 deletions

4
.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
/bin
/deps/**/*
/obj
/src/.magic.h

121
LICENSE.txt Normal file
View File

@ -0,0 +1,121 @@
Creative Commons Legal Code
CC0 1.0 Universal
CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
HEREUNDER.
Statement of Purpose
The laws of most jurisdictions throughout the world automatically confer
exclusive Copyright and Related Rights (defined below) upon the creator
and subsequent owner(s) (each and all, an "owner") of an original work of
authorship and/or a database (each, a "Work").
Certain owners wish to permanently relinquish those rights to a Work for
the purpose of contributing to a commons of creative, cultural and
scientific works ("Commons") that the public can reliably and without fear
of later claims of infringement build upon, modify, incorporate in other
works, reuse and redistribute as freely as possible in any form whatsoever
and for any purposes, including without limitation commercial purposes.
These owners may contribute to the Commons to promote the ideal of a free
culture and the further production of creative, cultural and scientific
works, or to gain reputation or greater distribution for their Work in
part through the use and efforts of others.
For these and/or other purposes and motivations, and without any
expectation of additional consideration or compensation, the person
associating CC0 with a Work (the "Affirmer"), to the extent that he or she
is an owner of Copyright and Related Rights in the Work, voluntarily
elects to apply CC0 to the Work and publicly distribute the Work under its
terms, with knowledge of his or her Copyright and Related Rights in the
Work and the meaning and intended legal effect of CC0 on those rights.
1. Copyright and Related Rights. A Work made available under CC0 may be
protected by copyright and related or neighboring rights ("Copyright and
Related Rights"). Copyright and Related Rights include, but are not
limited to, the following:
i. the right to reproduce, adapt, distribute, perform, display,
communicate, and translate a Work;
ii. moral rights retained by the original author(s) and/or performer(s);
iii. publicity and privacy rights pertaining to a person's image or
likeness depicted in a Work;
iv. rights protecting against unfair competition in regards to a Work,
subject to the limitations in paragraph 4(a), below;
v. rights protecting the extraction, dissemination, use and reuse of data
in a Work;
vi. database rights (such as those arising under Directive 96/9/EC of the
European Parliament and of the Council of 11 March 1996 on the legal
protection of databases, and under any national implementation
thereof, including any amended or successor version of such
directive); and
vii. other similar, equivalent or corresponding rights throughout the
world based on applicable law or treaty, and any national
implementations thereof.
2. Waiver. To the greatest extent permitted by, but not in contravention
of, applicable law, Affirmer hereby overtly, fully, permanently,
irrevocably and unconditionally waives, abandons, and surrenders all of
Affirmer's Copyright and Related Rights and associated claims and causes
of action, whether now known or unknown (including existing as well as
future claims and causes of action), in the Work (i) in all territories
worldwide, (ii) for the maximum duration provided by applicable law or
treaty (including future time extensions), (iii) in any current or future
medium and for any number of copies, and (iv) for any purpose whatsoever,
including without limitation commercial, advertising or promotional
purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
member of the public at large and to the detriment of Affirmer's heirs and
successors, fully intending that such Waiver shall not be subject to
revocation, rescission, cancellation, termination, or any other legal or
equitable action to disrupt the quiet enjoyment of the Work by the public
as contemplated by Affirmer's express Statement of Purpose.
3. Public License Fallback. Should any part of the Waiver for any reason
be judged legally invalid or ineffective under applicable law, then the
Waiver shall be preserved to the maximum extent permitted taking into
account Affirmer's express Statement of Purpose. In addition, to the
extent the Waiver is so judged Affirmer hereby grants to each affected
person a royalty-free, non transferable, non sublicensable, non exclusive,
irrevocable and unconditional license to exercise Affirmer's Copyright and
Related Rights in the Work (i) in all territories worldwide, (ii) for the
maximum duration provided by applicable law or treaty (including future
time extensions), (iii) in any current or future medium and for any number
of copies, and (iv) for any purpose whatsoever, including without
limitation commercial, advertising or promotional purposes (the
"License"). The License shall be deemed effective as of the date CC0 was
applied by Affirmer to the Work. Should any part of the License for any
reason be judged legally invalid or ineffective under applicable law, such
partial invalidity or ineffectiveness shall not invalidate the remainder
of the License, and in such case Affirmer hereby affirms that he or she
will not (i) exercise any of his or her remaining Copyright and Related
Rights in the Work or (ii) assert any associated claims and causes of
action with respect to the Work, in either case contrary to Affirmer's
express Statement of Purpose.
4. Limitations and Disclaimers.
a. No trademark or patent rights held by Affirmer are waived, abandoned,
surrendered, licensed or otherwise affected by this document.
b. Affirmer offers the Work as-is and makes no representations or
warranties of any kind concerning the Work, express, implied,
statutory or otherwise, including without limitation warranties of
title, merchantability, fitness for a particular purpose, non
infringement, or the absence of latent or other defects, accuracy, or
the present or absence of errors, whether or not discoverable, all to
the greatest extent permissible under applicable law.
c. Affirmer disclaims responsibility for clearing rights of other persons
that may apply to the Work or any use thereof, including without
limitation any person's Copyright and Related Rights in the Work.
Further, Affirmer disclaims responsibility for obtaining any necessary
consents, permissions or other rights required for any use of the
Work.
d. Affirmer understands and acknowledges that Creative Commons is not a
party to this document and has no duty or obligation with respect to
this CC0 or use of the Work.

66
Makefile Normal file
View File

@ -0,0 +1,66 @@
# ---------------------------------------------------------------------------
# FLAGS
# ---------------------------------------------------------------------------
MACHINE := $(shell gcc -dumpmachine)
ifeq ($(MACHINE),$(filter x86_64-%,$(MACHINE)))
MYCPU := x64
MARCH := x86-64
MTUNE := corei7
else
MYCPU := x86
MARCH := i486
MTUNE := intel
endif
CFLAGS = -municode -march=$(MARCH) -mtune=$(MTUNE) -Os -DNDEBUG -Wall -flto -Ideps/$(MYCPU)/include
LDFLAGS = -static -Ldeps/$(MYCPU)/lib -Wl,--strip-all
LIBS = -lcrypto
ifneq ($(XCFLAGS),)
CFLAGS += $(XCFLAGS)
endif
ifneq ($(XLDFLAGS),)
LDFLAGS += $(XLDFLAGS)
endif
ifneq ($(XLIBS),)
LIBS += $(XLIBS)
endif
# ---------------------------------------------------------------------------
# RULES
# ---------------------------------------------------------------------------
.PHONY: all keygen sign verify subdirs clean
all: keygen sign verify
keygen: subdirs src/.magic.h
windres -DAPP="Key Generator ($(MYCPU))" -DNAME=keygen -o obj/version_keygen.o res/version.rc
gcc $(CFLAGS) $(LDFLAGS) -o bin/codesign_keygen.exe src/codesign_keygen.c src/common.c obj/version_keygen.o $(LIBS)
sign: subdirs src/.magic.h
windres -DAPP="Signer ($(MYCPU))" -DNAME=sign -o obj/version_sign.o res/version.rc
gcc $(CFLAGS) $(LDFLAGS) -o bin/codesign_sign.exe src/codesign_sign.c src/common.c obj/version_sign.o $(LIBS)
verify: subdirs src/.magic.h
windres -DAPP="Verifier ($(MYCPU))" -DNAME=verify -o obj/version_verify.o res/version.rc
windres -DAPP="Verifier ($(MYCPU))" -DNAME=verifz -o obj/version_verifz.o res/version.rc
gcc $(CFLAGS) -UEMBED_PUBKEY $(LDFLAGS) -o bin/codesign_verify.exe src/codesign_verify.c src/common.c obj/version_verify.o $(LIBS)
gcc $(CFLAGS) -DEMBED_PUBKEY $(LDFLAGS) -o bin/codesign_verifz.exe src/codesign_verify.c src/common.c obj/version_verifz.o $(LIBS)
src/.magic.h:
str=$$(tr -dc '0-9A-F' < /dev/urandom | head -c 26); \
printf 'static const unsigned char MAGIC_NMBR[] = { ' > $@; \
for i in {0..12}; do \
[ $$i -gt 0 ] && printf ', '; \
printf '0x%s' $${str:((2*i)):2}; \
done >> $@; \
printf ' };\n' >> $@
subdirs:
@mkdir -p bin obj
clean:
rm -rf bin obj
rm -f src/.magic.h

51
deps/build-openssl.sh vendored Normal file
View File

@ -0,0 +1,51 @@
#!/bin/bash
set -e
trap 'read -p "Press any key..." x' EXIT
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Set up compiler
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
case "$(cc -dumpmachine)" in
i686-*)
readonly MY_CPU=x86
readonly MY_MARCH=i486
readonly MY_MTUNE=intel
;;
x86_64-*)
readonly MY_CPU=x64
readonly MY_MARCH=x86-64
readonly MY_MTUNE=corei7
;;
*)
echo "Unknown compiler detected!";
exit 1
;;
esac
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Initialize paths
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
readonly BASE_DIR="$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")"
readonly LIBS_DIR="${BASE_DIR}/${MY_CPU}"
readonly OSSL_DIR="${LIBS_DIR}/.build/openssl"
find "${BASE_DIR}" -maxdepth 1 -type d -name "*-${MY_CPU}" -exec rm -rf "{}" \;
rm -rf "${LIBS_DIR}" && mkdir -p "${LIBS_DIR}/bin" "${LIBS_DIR}/include" "${LIBS_DIR}/lib/pkgconfig" "${LIBS_DIR}/share" "${OSSL_DIR}"
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Fetch sources
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
wget -4 -O "${LIBS_DIR}/openssl.tar.gz" https://www.openssl.org/source/openssl-1.1.1k.tar.gz
tar -xvf "${LIBS_DIR}/openssl.tar.gz" --strip-components=1 -C "${OSSL_DIR}"
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Build
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[[ "${MY_CPU}" == "x64" ]] && readonly ossl_flag="no-sse2" || readonly ossl_flag="386"
[[ "${MY_CPU}" == "x64" ]] && readonly ossl_mngw="mingw64" || readonly ossl_mngw="mingw"
pushd "${OSSL_DIR}"
./Configure no-afalgeng no-aria no-asan no-asm no-async no-autoerrinit no-autoload-config no-bf no-blake2 no-buildtest-c++ no-camellia no-capieng no-cast no-chacha no-cmac no-cms no-comp no-crypto-mdebug no-crypto-mdebug-backtrace no-ct no-deprecated no-des no-devcryptoeng no-dgram no-dh no-dsa no-dso no-dtls no-dynamic-engine no-ec no-ec2m no-ecdh no-ecdsa no-ec_nistp_64_gcc_128 no-egd no-engine no-err no-external-tests no-filenames no-fuzz-libfuzzer no-fuzz-afl no-gost no-heartbeats no-hw no-idea no-makedepend no-md2 no-md4 no-mdc2 no-msan no-multiblock no-nextprotoneg no-pinshared no-ocb no-ocsp no-pic no-poly1305 no-posix-io no-psk no-rc2 no-rc4 no-rc5 no-rdrand no-rfc3779 no-rmd160 no-scrypt no-sctp no-seed no-shared no-siphash no-sm2 no-sm3 no-sm4 no-sock no-srp no-srtp no-ssl no-ssl-trace no-static-engine no-tests no-threads no-tls no-ts no-ubsan no-ui-console no-unit-test no-whirlpool no-weak-ssl-ciphers no-zlib no-zlib-dynamic ${ossl_flag} -static -march=${MY_MARCH} -mtune=${MY_MTUNE} -Os -flto -D_WIN32_WINNT=0x0501 -I"${LIBS_DIR}/include" -L"${LIBS_DIR}/lib" --prefix="${LIBS_DIR}" ${ossl_mngw}
make build_libs && make install_dev
popd
printf "\nCompleted.\n\n"

63
res/version.rc Normal file
View File

@ -0,0 +1,63 @@
/////////////////////////////////////////////////////////////////////////////
//
// Microsoft Visual C++ generated resource script.
//
#define APSTUDIO_READONLY_SYMBOLS
#include "WinResrc.h" //"afxres.h"
#undef APSTUDIO_READONLY_SYMBOLS
#define VERSION_MAJOR 1
#define VERSION_MINOR 0
#define VERSION_PATCH 0
#define VERSION_BUILD 0
#define _T(X) #X
#define T(X) _T(X)
/////////////////////////////////////////////////////////////////////////////
//
// Neutral resources
//
#ifdef _WIN32
LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
#pragma code_page(1252)
#endif //_WIN32
/////////////////////////////////////////////////////////////////////////////
//
// Version
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION VERSION_MAJOR,VERSION_MINOR,VERSION_PATCH,VERSION_BUILD
PRODUCTVERSION VERSION_MAJOR,VERSION_MINOR,VERSION_PATCH,VERSION_BUILD
FILEFLAGSMASK 0x17L
#ifdef _DEBUG
FILEFLAGS 0x3L
#else
FILEFLAGS 0x2L
#endif
FILEOS 0x40004L
FILETYPE 0x1L
FILESUBTYPE 0x0L
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "000004b0"
BEGIN
VALUE "ProductName", "CodeSign"
VALUE "FileDescription", "CodeSign - " T(APP)
VALUE "ProductVersion", T(VERSION_MAJOR) "." T(VERSION_MINOR) "." T(VERSION_PATCH)
VALUE "FileVersion", T(VERSION_MAJOR) "." T(VERSION_MINOR) "." T(VERSION_PATCH)
VALUE "InternalName", "codesign_" T(NAME)
VALUE "OriginalFilename", "codesign_" T(NAME) ".exe"
VALUE "LegalCopyright", "Created by LoRd_MuldeR <MuldeR2@GMX.de>"
VALUE "CompanyName", "Muldersoft"
VALUE "LegalTrademarks", "Muldersoft"
VALUE "Comments", "This work has been released under the CC0 1.0 Universal license!"
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x0, 1200
END
END

195
src/codesign_keygen.c Normal file
View File

@ -0,0 +1,195 @@
/******************************************************************************/
/* CodeSign, by LoRd_MuldeR <MuldeR2@GMX.de> */
/* This work has been released under the CC0 1.0 Universal license! */
/******************************************************************************/
#include "common.h"
#include <share.h>
#include <string.h>
#include <openssl/rsa.h>
#include <openssl/bn.h>
#include <openssl/pem.h>
#include <openssl/evp.h>
#define RSA_EXPONENT 0x10001
#define RSA_KEY_SIZE 8192
int wmain(int argc, wchar_t *argv[])
{
int exit_code = EXIT_FAILURE;
BIGNUM *exp = NULL;
RSA *rsa = NULL;
FILE *file_pubkey = NULL, *file_privkey = NULL;
const EVP_CIPHER *enc = NULL;
char *passwd = NULL;
/*-------------------------------------------------------*/
/* Check arguments */
/*-------------------------------------------------------*/
print_logo("Sign Tool");
if ((argc < 4) || (!argv[1][0]) || (!argv[2][0]) || (!argv[3][0]))
{
print_license();
fputs("Usage:\n", stderr);
fputs(" codesign_keygen.exe <passwd> <signkey.pub> <signkey.key>\n\n", stderr);
goto clean_up;
}
/*-------------------------------------------------------*/
/* Open output files */
/*-------------------------------------------------------*/
file_pubkey = _wfsopen(argv[2], L"wb", _SH_DENYRW);
if (!file_pubkey)
{
fputs("Error: Failed to open output file for public key!\n\n", stderr);
goto clean_up;
}
file_privkey = _wfsopen(argv[3], L"wb", _SH_DENYRW);
if (!file_privkey)
{
fputs("Error: Failed to open output file for private key!\n\n", stderr);
goto clean_up;
}
/*-------------------------------------------------------*/
/* Read password */
/*-------------------------------------------------------*/
if ((argv[1][0] == L'-') && (argv[1][1] == L'\0'))
{
passwd = read_line_from_file(stdin, 0);
if (!passwd)
{
fputs("Error: Failed to read password from STDIN stream!\n\n", stderr);
goto clean_up;
}
}
else
{
passwd = convert_wchar_to_UTF8(argv[1]);
if (!passwd)
{
fputs("Error: Failed to convert password to UTF-8 format!\n\n", stderr);
goto clean_up;
}
}
if (strlen(passwd) < PASSWD_MINLEN)
{
fprintf(stderr, "Error: Password is too short! (min. length: %d)\n\n", PASSWD_MINLEN);
goto clean_up;
}
/*-------------------------------------------------------*/
/* Generate RSA key-pair */
/*-------------------------------------------------------*/
exp = BN_new();
if (!exp)
{
fputs("Error: Failed to allocate exponent!\n\n", stderr);
goto clean_up;
}
if (BN_set_word(exp, RSA_EXPONENT) != 1)
{
fputs("Error: Failed to set up exponent!\n\n", stderr);
goto clean_up;
}
rsa = RSA_new();
if (!rsa)
{
fputs("Error: Failed to allocate RSA context!\n\n", stderr);
goto clean_up;
}
fputs("Generating RSA key-pair. Please be patient, this will take a while...\n", stderr);
fflush(stderr);
if (RSA_generate_key_ex(rsa, RSA_KEY_SIZE, exp, NULL) != 1)
{
fputs("Failed!\n\nError: Failed to generate RAS key!\n\n", stderr);
goto clean_up;
}
fputs("Completed.\n\n", stderr);
fflush(stderr);
/*-------------------------------------------------------*/
/* Write public and private keys to files */
/*-------------------------------------------------------*/
enc = EVP_aes_256_cbc();
if (!enc)
{
fputs("Error: Failed to initialize cipher for key export!\n\n", stderr);
goto clean_up;
}
if (PEM_write_RSAPublicKey(file_pubkey, rsa) != 1)
{
fputs("Error: Failed to write the public key file!\n\n", stderr);
goto clean_up;
}
if (!force_flush(file_pubkey))
{
fputs("I/O Error: Public key could not be written completely!\n\n", stderr);
goto clean_up;
}
if (PEM_write_RSAPrivateKey(file_privkey, rsa, enc, (unsigned char*)passwd, strlen(passwd), NULL, NULL) != 1)
{
fputs("Error: Failed to write the private key file!\n\n", stderr);
goto clean_up;
}
if (!force_flush(file_privkey))
{
fputs("I/O Error: Private key could not be written completely!\n\n", stderr);
goto clean_up;
}
fputs("RSA key-pair written successfully.\n\n", stderr);
exit_code = EXIT_SUCCESS;
/*-------------------------------------------------------*/
/* Final clean-up */
/*-------------------------------------------------------*/
clean_up:
if (passwd)
{
zero_memory(passwd, _msize(passwd));
free(passwd);
}
if (file_pubkey)
{
fclose(file_pubkey);
}
if (file_privkey)
{
fclose(file_privkey);
}
if (rsa)
{
RSA_free(rsa);
}
if (exp)
{
BN_free(exp);
}
return exit_code;
}

260
src/codesign_sign.c Normal file
View File

@ -0,0 +1,260 @@
/******************************************************************************/
/* CodeSign, by LoRd_MuldeR <MuldeR2@GMX.de> */
/* This work has been released under the CC0 1.0 Universal license! */
/******************************************************************************/
#include "common.h"
#include ".magic.h"
#include <share.h>
#include <string.h>
#include <openssl/rsa.h>
#include <openssl/pem.h>
#include <openssl/evp.h>
#define BUFFSIZE 4096
int wmain(int argc, wchar_t *argv[])
{
int exit_code = EXIT_FAILURE;
RSA *rsa = NULL;
SHA512_CTX sha512 = { };
FILE *file_privkey = NULL, *file_data = NULL, *file_signature = NULL;
char *passwd = NULL, *base64 = NULL;
unsigned char buffer[BUFFSIZE], digest[SHA512_DIGEST_LENGTH], timestamp[sizeof(UI64_T)], *output = NULL;
unsigned int signature_length = 0U, output_length = 0U, base64_length = 0U;
/*-------------------------------------------------------*/
/* Check arguments */
/*-------------------------------------------------------*/
print_logo("Signer");
if ((argc < 5) || (!argv[1][0]) || (!argv[2][0]) || (!argv[3][0]) || (!argv[4][0]))
{
print_license();
fputs("Usage:\n", stderr);
fputs(" codesign_sign.exe <passwd> <signkey.key> <filename.dat> <signature.sig>\n\n", stderr);
goto clean_up;
}
/*-------------------------------------------------------*/
/* Open input/output files */
/*-------------------------------------------------------*/
file_privkey = _wfsopen(argv[2], L"rb", _SH_DENYWR);
if (!file_privkey)
{
fputs("Error: Failed to open private key file!\n\n", stderr);
goto clean_up;
}
file_data = _wfsopen(argv[3], L"rb", _SH_DENYWR);
if (!file_data)
{
fputs("Error: Failed to open input file to be signed!\n\n", stderr);
goto clean_up;
}
file_signature = _wfsopen(argv[4], L"wb", _SH_DENYRW);
if (!file_signature)
{
fputs("Error: Failed to open output file for signature!\n\n", stderr);
goto clean_up;
}
/*-------------------------------------------------------*/
/* Read password */
/*-------------------------------------------------------*/
if ((argv[1][0] == L'-') && (argv[1][1] == L'\0'))
{
passwd = read_line_from_file(stdin, 0);
if (!passwd)
{
fputs("Error: Failed to read password from STDIN stream!\n\n", stderr);
goto clean_up;
}
}
else
{
passwd = convert_wchar_to_UTF8(argv[1]);
if (!passwd)
{
fputs("Error: Failed to convert password to UTF-8 format!\n\n", stderr);
goto clean_up;
}
}
if (strlen(passwd) < PASSWD_MINLEN)
{
fprintf(stderr, "Error: Password is too short! (min. length: %d)\n\n", PASSWD_MINLEN);
goto clean_up;
}
/*-------------------------------------------------------*/
/* Read the private key */
/*-------------------------------------------------------*/
if (!PEM_read_RSAPrivateKey(file_privkey, &rsa, NULL, passwd))
{
fputs("Error: Failed to read the private key! Wrong password?\n\n", stderr);
goto clean_up;
}
store_uint64(timestamp, get_current_time());
/*-------------------------------------------------------*/
/* Compute file digest */
/*-------------------------------------------------------*/
if (SHA512_Init(&sha512) != 1)
{
fputs("Error: Failed to initialize SHA-512 digest!\n\n", stderr);
goto clean_up;
}
fputs("Generating the RSA signature, please wait...\n", stderr);
fflush(stderr);
if (SHA512_Update(&sha512, timestamp, sizeof(timestamp)) != 1)
{
fputs("Failed!\n\nError: Failed to update SHA-512 digest!\n\n", stderr);
goto clean_up;
}
while (!(feof(file_data) || ferror(file_data)))
{
const size_t count = fread(buffer, sizeof(unsigned char), BUFFSIZE, file_data);
if (count > 0U)
{
if (SHA512_Update(&sha512, buffer, count) != 1)
{
fputs("Failed!\n\nError: Failed to update SHA-512 digest!\n\n", stderr);
goto clean_up;
}
}
}
if (ferror(file_data))
{
fputs("Failed!\n\nI/O Error: Failed to read input file!\n\n", stderr);
goto clean_up;
}
if (SHA512_Update(&sha512, MAGIC_NMBR, sizeof(MAGIC_NMBR)) != 1)
{
fputs("Failed!\n\nError: Failed to update SHA-512 digest!\n\n", stderr);
goto clean_up;
}
if (SHA512_Final(digest, &sha512) != 1)
{
fputs("Failed!\n\nError: Failed to finalize SHA-512 digest!\n\n", stderr);
goto clean_up;
}
/*-------------------------------------------------------*/
/* Compute the RSA signature */
/*-------------------------------------------------------*/
output = (unsigned char*) malloc(RSA_size(rsa) + sizeof(timestamp));
if (!output)
{
fputs("Failed!\n\nError: Failed to allocate output buffer!\n\n", stderr);
goto clean_up;
}
memcpy(output, timestamp, sizeof(timestamp));
if (RSA_sign(NID_sha512, digest, SHA512_DIGEST_LENGTH, output + sizeof(timestamp), &signature_length, rsa) != 1)
{
fputs("Failed!\n\nError: Failed to compute signature!\n\n", stderr);
goto clean_up;
}
fputs("Completed.\n\n", stderr);
fflush(stderr);
/*-------------------------------------------------------*/
/* Write signature the output file */
/*-------------------------------------------------------*/
base64 = (char*) malloc(1U + (base64_length = 4U * (((output_length = signature_length + sizeof(timestamp)) + 2U) / 3U)));
if (!base64)
{
fputs("Error: Failed to allocate hex-string buffer!\n\n", stderr);
goto clean_up;
}
if (EVP_EncodeBlock((unsigned char*)base64, output, output_length) != base64_length)
{
fputs("Error: Failed to encode signature to Base64!\n\n", stderr);
goto clean_up;
}
if (fwrite(base64, sizeof(char), base64_length, file_signature) != base64_length)
{
fputs("Error: Failed to write signature to output file!\n\n", stderr);
goto clean_up;
}
if (!force_flush(file_signature))
{
fputs("I/O Error: Signature could not be written completely!\n\n", stderr);
goto clean_up;
}
fputs("RSA signature written successfully.\n\n", stderr);
exit_code = EXIT_SUCCESS;
/*-------------------------------------------------------*/
/* Final clean-up */
/*-------------------------------------------------------*/
clean_up:
if (passwd)
{
zero_memory(passwd, strlen(passwd));
free(passwd);
}
if (base64)
{
zero_memory(base64, strlen(base64));
free(base64);
}
if (output)
{
zero_memory(output, _msize(output));
free(output);
}
if (file_privkey)
{
fclose(file_privkey);
}
if (file_data)
{
fclose(file_data);
}
if (file_signature)
{
fclose(file_signature);
}
if (rsa)
{
RSA_free(rsa);
}
zero_memory(timestamp, sizeof(timestamp));
zero_memory(&sha512, sizeof(SHA512_CTX));
return exit_code;
}

320
src/codesign_verify.c Normal file
View File

@ -0,0 +1,320 @@
/******************************************************************************/
/* CodeSign, by LoRd_MuldeR <MuldeR2@GMX.de> */
/* This work has been released under the CC0 1.0 Universal license! */
/******************************************************************************/
#include "common.h"
#include ".magic.h"
#include <share.h>
#include <string.h>
#include <openssl/rsa.h>
#include <openssl/pem.h>
#include <openssl/evp.h>
#include <openssl/err.h>
#ifdef EMBED_PUBKEY
#define ARGV_INPUTFILE argv[1]
#define ARGV_SIGNATURE argv[2]
#else
#define ARGV_INPUTFILE argv[2]
#define ARGV_SIGNATURE argv[3]
#endif
#define BUFFSIZE 4096
int wmain(int argc, wchar_t *argv[])
{
int exit_code = EXIT_FAILURE;
BIO *bio_pubkey = NULL;
RSA *rsa = NULL;
SHA512_CTX sha512 = { };
char *base64 = NULL;
FILE *file_pubkey = NULL, *file_data = NULL, *file_signature = NULL;
UI64_T timestamp = 0U, current_time = 0U;
unsigned char buffer[BUFFSIZE], digest[SHA512_DIGEST_LENGTH], *input = NULL;
unsigned int input_length = 0U, base64_length = 0U;
#ifdef EMBED_PUBKEY
const unsigned char *public_key = NULL, *checksum_pubkey = NULL;
unsigned int pubkey_length = 0U, checksum_length = 0U;
#endif
/*-------------------------------------------------------*/
/* Check arguments */
/*-------------------------------------------------------*/
print_logo("Verifier");
#ifdef EMBED_PUBKEY
if ((argc < 3) || (!argv[1][0]) || (!argv[2][0]))
{
print_license();
fputs("Usage:\n", stderr);
fputs(" codesign_verify.exe <filename.dat> <signature.sig>\n\n", stderr);
goto clean_up;
}
#else
if ((argc < 4) || (!argv[1][0]) || (!argv[2][0]) || (!argv[3][0]))
{
print_license();
fputs("Usage:\n", stderr);
fputs(" codesign_verify.exe <signkey.pub> <filename.dat> <signature.sig>\n\n", stderr);
goto clean_up;
}
#endif
/*-------------------------------------------------------*/
/* Load the public key */
/*-------------------------------------------------------*/
#ifdef EMBED_PUBKEY
public_key = load_resource_data(L"RSA_PUBLIC_KEY", &pubkey_length);
if (!public_key)
{
fputs("Error: Failed to load public key data from resources!\n\n", stderr);
goto clean_up;
}
checksum_pubkey = load_resource_data(L"CHECKSUM_SHA512", &checksum_length);
if ((!checksum_pubkey) || (checksum_length < SHA512_DIGEST_LENGTH))
{
fputs("Error: Failed to load public key checksum from resources!\n\n", stderr);
goto clean_up;
}
if (!SHA512(public_key, pubkey_length, digest))
{
fputs("Error: Failed to compute checksum of the public key!\n\n", stderr);
goto clean_up;
}
if (memcmp(digest, checksum_pubkey, SHA512_DIGEST_LENGTH) != 0)
{
fputs("Error: Public key checksum does not match the expected value!\n\n", stderr);
goto clean_up;
}
bio_pubkey = BIO_new_mem_buf(public_key, pubkey_length);
if (!bio_pubkey)
{
fputs("Error: Failed to allocate memory BIO for public key!\n\n", stderr);
goto clean_up;
}
if (!PEM_read_bio_RSAPublicKey(bio_pubkey, &rsa, NULL, NULL))
{
fputs("Error: Failed to read the public key from memory!\n\n", stderr);
goto clean_up;
}
#else
file_pubkey = _wfsopen(argv[1], L"rb", _SH_DENYWR);
if (!file_pubkey)
{
fputs("Error: Failed to open public key input file!\n\n", stderr);
goto clean_up;
}
if (!PEM_read_RSAPublicKey(file_pubkey, &rsa, NULL, NULL))
{
fputs("Error: Failed to read the public key from file!\n\n", stderr);
goto clean_up;
}
#endif
/*-------------------------------------------------------*/
/* Open input files */
/*-------------------------------------------------------*/
file_data = _wfsopen(ARGV_INPUTFILE, L"rb", _SH_DENYWR);
if (!file_data)
{
fputs("Error: Failed to open the input file!\n\n", stderr);
goto clean_up;
}
file_signature = _wfsopen(ARGV_SIGNATURE, L"rb", _SH_DENYWR);
if (!file_signature)
{
fputs("Error: Failed to open the signature file!\n\n", stderr);
goto clean_up;
}
base64 = read_line_from_file(file_signature, 1);
if (!base64)
{
fputs("Error: Failed to read the signature from file!\n\n", stderr);
goto clean_up;
}
if ((base64_length = strlen(base64)) < 4U)
{
fputs("Error: The signature appears to be incomplete!\n\n", stderr);
goto clean_up;
}
/*-------------------------------------------------------*/
/* Decode signature input data */
/*-------------------------------------------------------*/
input = (unsigned char*) malloc(1U + (3U * ((base64_length + 3U) / 4U)));
if (!input)
{
fputs("Error: Failed to allocate the input buffer!\n\n", stderr);
goto clean_up;
}
input_length = EVP_DecodeBlock(input, (unsigned char*)base64, base64_length);
if ((int)input_length <= 0)
{
fputs("Error: Failed to decode signature data from Base64 format!\n\n", stderr);
goto clean_up;
}
if (input_length <= 2U + sizeof(UI64_T))
{
fputs("Error: Signature binary data appears to be truncated!\n\n", stderr);
goto clean_up;
}
if (base64[base64_length - 1U] == '=')
{
input_length -= (base64[base64_length - 2U] == '=') ? 2U : 1U; /*remove padding!*/
}
/*-------------------------------------------------------*/
/* Check the time-stamp */
/*-------------------------------------------------------*/
timestamp = load_uint64(input);
if (timestamp > (current_time = get_current_time()))
{
fputs("Error: Signature time-stamp appears to be from the future!\n\n", stderr);
goto clean_up;
}
if (current_time - timestamp > 94670856000000ULL)
{
fputs("Error: Signature time-stamp is more than 3 years old!\n\n", stderr);
goto clean_up;
}
/*-------------------------------------------------------*/
/* Compute file digest */
/*-------------------------------------------------------*/
if (SHA512_Init(&sha512) != 1)
{
fputs("Error: Failed to initialize SHA-512 digest!\n\n", stderr);
goto clean_up;
}
fputs("Verifying the RSA signature, please wait...\n", stderr);
fflush(stderr);
if (SHA512_Update(&sha512, input, sizeof(UI64_T)) != 1)
{
fputs("Failed!\n\nError: Failed to update SHA-512 digest!\n\n", stderr);
goto clean_up;
}
while (!(feof(file_data) || ferror(file_data)))
{
const size_t count = fread(buffer, sizeof(unsigned char), BUFFSIZE, file_data);
if (count > 0U)
{
if (SHA512_Update(&sha512, buffer, count) != 1)
{
fputs("Failed!\n\nError: Failed to update SHA-512 digest!\n\n", stderr);
goto clean_up;
}
}
}
if (ferror(file_data))
{
fputs("Failed!\n\nI/O Error: Failed to read input file!\n\n", stderr);
goto clean_up;
}
if (SHA512_Update(&sha512, MAGIC_NMBR, sizeof(MAGIC_NMBR)) != 1)
{
fputs("Failed!\n\nError: Failed to update SHA-512 digest!\n\n", stderr);
goto clean_up;
}
if (SHA512_Final(digest, &sha512) != 1)
{
fputs("Failed!\n\nError: Failed to finalize SHA-512 digest!\n\n", stderr);
goto clean_up;
}
/*-------------------------------------------------------*/
/* Validate the RSA signature */
/*-------------------------------------------------------*/
if (RSA_verify(NID_sha512, digest, SHA512_DIGEST_LENGTH, input + sizeof(UI64_T), input_length - sizeof(UI64_T), rsa) != 1)
{
fputs("Failed!\n\nInvalid signature or corrupted file :-(\n\n", stderr);
goto clean_up;
}
fputs("Completed.\n\nThe signature is valid :-)\n\n", stderr);
exit_code = EXIT_SUCCESS;
/*-------------------------------------------------------*/
/* Final clean-up */
/*-------------------------------------------------------*/
clean_up:
if (input)
{
zero_memory(input, _msize(input));
free(input);
}
if (base64)
{
zero_memory(base64, strlen(base64));
free(base64);
}
if (file_pubkey)
{
fclose(file_pubkey);
}
if (file_data)
{
fclose(file_data);
}
if (file_signature)
{
fclose(file_signature);
}
if (bio_pubkey)
{
BIO_free(bio_pubkey);
}
if (rsa)
{
RSA_free(rsa);
}
zero_memory(&sha512, sizeof(SHA512_CTX));
return exit_code;
}

162
src/common.c Normal file
View File

@ -0,0 +1,162 @@
/******************************************************************************/
/* CodeSign, by LoRd_MuldeR <MuldeR2@GMX.de> */
/* This work has been released under the CC0 1.0 Universal license! */
/******************************************************************************/
#include "common.h"
#include <sys/time.h>
#include <io.h>
#include <fcntl.h>
#define WIN32_LEAN_AND_MEAN 1
#include <Windows.h>
#include <openssl/crypto.h>
void print_logo(const char *const app_name)
{
SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX);
_setmode(_fileno(stdin), _O_BINARY);
fprintf(stderr, "CodeSign - %s [" __DATE__ "], by LoRd_MuldeR <mulder2@gmx.de>\n", app_name);
fprintf(stderr, "using %.15s\n\n", OpenSSL_version(OPENSSL_VERSION));
fflush(stderr);
}
void print_license(void)
{
fputs("This work has been released under the CC0 1.0 Universal license!\n", stderr);
fputs("Please see http://www.muldersoft.com for additional information.\n\n", stderr);
}
char *convert_wchar_to_UTF8(const wchar_t *str_w)
{
char *str_utf8 = NULL;
if (str_w)
{
int bytes = WideCharToMultiByte(CP_UTF8, 0, str_w, -1, NULL, 0, NULL, NULL);
if (bytes > 0)
{
str_utf8 = malloc(bytes);
if(str_utf8)
{
if (WideCharToMultiByte(CP_UTF8, 0, str_w, -1, str_utf8, bytes, NULL, NULL) == 0)
{
free(str_utf8);
return NULL;
}
}
}
}
return str_utf8;
}
char *read_line_from_file(FILE *const file, const int trim)
{
char buffer[4096], *line;
size_t len;
while(!(feof(file) || ferror(file)))
{
if((line = fgets(buffer, sizeof(buffer), file)))
{
if (trim)
{
while (*line && isspace(*line))
{
++line;
}
}
len = strlen(line);
while (len > 0)
{
if ((line[len - 1U] != '\r') && (line[len - 1U] != '\n'))
{
break;
}
line[--len] = '\0';
}
if (trim)
{
while (len > 0)
{
if (!isspace(line[len - 1U]))
{
break;
}
line[--len] = '\0';
}
}
if (len > 0)
{
return strdup(line);
}
}
}
return NULL;
}
UI64_T get_current_time(void)
{
struct timeval t;
if(gettimeofday(&t, NULL) == 0)
{
return (((UI64_T)t.tv_sec) * 1000000ULL) + ((UI64_T)t.tv_usec);
}
return 0ULL;
}
void store_uint64(unsigned char *const buffer, const UI64_T value)
{
buffer[0U] = (BYTE)(value >> 56);
buffer[1U] = (BYTE)(value >> 48);
buffer[2U] = (BYTE)(value >> 40);
buffer[3U] = (BYTE)(value >> 32);
buffer[4U] = (BYTE)(value >> 24);
buffer[5U] = (BYTE)(value >> 16);
buffer[6U] = (BYTE)(value >> 8);
buffer[7U] = (BYTE)(value >> 0);
}
UI64_T load_uint64(const unsigned char *const buffer)
{
UI64_T value = 0U;
value |= ((UI64_T)buffer[0U]) << 56;
value |= ((UI64_T)buffer[1U]) << 48;
value |= ((UI64_T)buffer[2U]) << 40;
value |= ((UI64_T)buffer[3U]) << 32;
value |= ((UI64_T)buffer[4U]) << 24;
value |= ((UI64_T)buffer[5U]) << 16;
value |= ((UI64_T)buffer[6U]) << 8;
value |= ((UI64_T)buffer[7U]) << 0;
return value;
}
const unsigned char* load_resource_data(const wchar_t *const name, unsigned int *const length)
{
HRSRC resource = FindResourceW(NULL, name, RT_RCDATA);
if (resource)
{
*length = SizeofResource(NULL, resource);
if (*length > 0U)
{
HGLOBAL data = LoadResource(NULL, resource);
if (data)
{
return (const unsigned char*) LockResource(data);
}
}
}
*length = 0U;
return NULL;
}
int force_flush(FILE *const file)
{
const int result = fflush(file);
FlushFileBuffers((HANDLE)_get_osfhandle(_fileno(file)));
return (result == 0);
}
void zero_memory(void *const ptr, const size_t size)
{
SecureZeroMemory(ptr, size);
}

26
src/common.h Normal file
View File

@ -0,0 +1,26 @@
/******************************************************************************/
/* CodeSign, by LoRd_MuldeR <MuldeR2@GMX.de> */
/* This work has been released under the CC0 1.0 Universal license! */
/******************************************************************************/
#ifndef _COMMON_H
#define _COMMON_H
#include <stdlib.h>
#include <stdio.h>
#define PASSWD_MINLEN 8
typedef unsigned long long UI64_T;
void print_logo(const char *const app_name);
void print_license(void);
char *convert_wchar_to_UTF8(const wchar_t *str_w);
char *read_line_from_file(FILE *const file, const int trim);
UI64_T get_current_time(void);
void store_uint64(unsigned char *const buffer, const UI64_T value);
UI64_T load_uint64(const unsigned char *const buffer);
const unsigned char* load_resource_data(const wchar_t *const name, unsigned int *const length);
int force_flush(FILE *const file);
void zero_memory(void *const ptr, const size_t size);
#endif /*_COMMON_H*/