Search code examples
copensslcryptographysha

sha1 example in C using openssl library


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 *’

Solution

  • 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