sha1 example in C using openssl library

13.5k views Asked by At

I have a simplest possible example of sha256 written in C, using the openSSL library.

// compile with: gcc -o sha256 sha256.c -lcrypto

#include <openssl/sha.h>
#include <stdio.h>
int main(int argc, char **argv)
{
    unsigned char buffer[BUFSIZ];
    FILE *f;
    SHA256_CTX ctx;
    size_t len;
    if (argc < 2) {
        fprintf(stderr, "usage: %s <file>\n", argv[0]);
        return 1;
    }

    f = fopen(argv[1], "r");
    if (!f) {
        fprintf(stderr, "couldn't open %s\n", argv[1]);
        return 1;
    }

    SHA256_Init(&ctx);

    do {
        len = fread(buffer, 1, BUFSIZ, f);
        SHA256_Update(&ctx, buffer, len);
    } while (len == BUFSIZ);

    SHA256_Final(buffer, &ctx);

    fclose(f);

    for (len = 0; len < SHA256_DIGEST_LENGTH; ++len)
        printf("%02x", buffer[len]);
    putchar('\n');
    return 0;
}

I need the same for sha1, but I could not find similar simple example that actually works. The naive approach of replacing occurrences of SHA256 in the above code with SHA1 does not work (obviously).

How can I modify my program for SHA1 ?

UPDATE

as suggested by @dbush, I have used his EVP code, and integrated it into my program. My program now looks like this:

#include <stdio.h>
#include <openssl/sha.h>
#include <openssl/evp.h>
#include <openssl/err.h>

int main(int argc, char **argv)
{

FILE *f;
size_t len;
unsigned char buffer[BUFSIZ];

if (argc < 2) {
    fprintf(stderr, "usage: %s <file>\n", argv[0]);
    return 1;
}

f = fopen(argv[1], "r");

if (!f) {
    fprintf(stderr, "couldn't open %s\n", argv[1]);
    return 1;
}


EVP_MD_CTX hashctx;
//EVP_MD *hashptr = EVP_get_digestbyname("SHA256");
EVP_MD *hashptr = EVP_get_digestbyname("SHA1");

EVP_MD_CTX_init(&hashctx);
EVP_DigestInit_ex(&hashctx, hashptr, NULL));

do {
    len = fread(buffer, 1, BUFSIZ, f);
    EVP_DigestUpdate(&hashctx, buffer, len);
} while (len == BUFSIZ);

EVP_DigestFinal_ex(&hashctx, buffer, &len);
EVP_MD_CTX_cleanup(&hashctx);

fclose(f);

int i;
for (i = 0; i < len; ++i)
    printf("%02x", buffer[i]);

    return 0;
}

When I compile it using gcc -o evp evp.c -lcrypto, I get couple of errors, such as:

evp.c: In function ‘main’:
evp.c:29:19: warning: initialization discards ‘const’ qualifier from pointer target type [enabled by default]
evp.c:32:43: error: expected ‘;’ before ‘)’ token
evp.c:32:43: error: expected statement before ‘)’ token
evp.c:39:1: warning: passing argument 3 of ‘EVP_DigestFinal_ex’ from incompatible pointer type [enabled by default]
In file included from evp.c:4:0:
/usr/include/openssl/evp.h:574:5: note: expected ‘unsigned int *’ but argument is of type ‘size_t *’
2

There are 2 answers

6
dbush On BEST ANSWER

Rather than using the SHA1 or SHA256 specific functions, use the EVP_Digest* family of functions which work with any hash.

...

// makes all algorithms available to the EVP* routines
OpenSSL_add_all_algorithms();
// load the error strings for ERR_error_string
ERR_load_crypto_strings();

EVP_MD_CTX hashctx;
//const EVP_MD *hashptr = EVP_get_digestbyname("SHA256");
const EVP_MD *hashptr = EVP_get_digestbyname("SHA1");

EVP_MD_CTX_init(&hashctx);
EVP_DigestInit_ex(&hashctx, hashptr, NULL);

do {
    len = fread(buffer, 1, BUFSIZ, f);
    EVP_DigestUpdate(&hashctx, buffer, len);
} while (len == BUFSIZ);

unsigned int outlen;
EVP_DigestFinal_ex(&hashctx, buffer, &outlen);
EVP_MD_CTX_cleanup(&hashctx);

fclose(f);

int i;
for (i = 0; i < outlen; ++i)
    printf("%02x", buffer[i]);

I've omitted the error checking for brevity. To check for errors, do the following:

if (function_to_check() == 0) {
    char errstr[1000];
    ERR_error_string(ERR_get_error(), errstr);
    printf("error: %s\n", errstr;
}

EDIT:

There were some error in the above code that have been corrected:

  • hashptr was declared EVP_MD *, is now const EVP_MD *.
  • The call to EVP_DigestInit_ex had an extra parenthesis at the end
  • The third parameter to EVP_DigestFinal_ex is specifically given an unsigned int * instead a size_t *, which may not necessarily be the same.
  • Added calls to two OpenSSL initialization functions at the top
0
Arushi Singhal On

You can simply do like this:-

#include <openssl/sha.h>
#include <stdio.h>
int main(int argc, char **argv)
 {
    unsigned char buffer[BUFSIZ];
    FILE *f;
    SHA_CTX ctx;
    size_t len;
    if (argc < 2) {
            fprintf(stderr, "usage: %s <file>\n", argv[0]);
            return 1;
    }

    f = fopen(argv[1], "r");
    if (!f) {
            fprintf(stderr, "couldn't open %s\n", argv[1]);
            return 1;
    }

    SHA1_Init(&ctx);

    do {
            len = fread(buffer, 1, BUFSIZ, f);
            SHA1_Update(&ctx, buffer, len);
    } while (len == BUFSIZ);

    SHA1_Final(buffer, &ctx);

    fclose(f);

    for (len = 0; len < SHA_DIGEST_LENGTH; ++len)
            printf("%02x", buffer[len]);
    putchar('\n');
    return 0;
 }