Difference between revisions of "EVP Signing and Verifying"

From OpenSSLWiki
Jump to: navigation, search
m (Add CMAC note to introduction to make it obvious.)
 
(31 intermediate revisions by 5 users not shown)
Line 1: Line 1:
There are two APIs available for performing sign and verify operations. The older EVP_Sign* functions, and the newer and more flexible EVP_DigestSign* functions. They are very similar, but for new applications the EVP_DigestSign* versions should be preferred.
+
{{DocInclude
 +
|Name=Signing and Verifying
 +
|Url=http://wiki.openssl.org/index.php/Manual:Evp(3)
 +
|Include=evp.h}}
  
==Signing a Message==
+
There are two APIs available to perform sign and verify operations. The first are the older <tt>EVP_Sign*</tt> and <tt>EVP_Verify*</tt> functions; and the second are the newer and more flexible <tt>EVP_DigestSign*</tt> and <tt>EVP_DigestVerify*</tt> functions. Though the APIs are similar, new applications should use the <tt>EVP_DigestSign*</tt> and <tt>EVP_DigestVerify*</tt> functions.
  
Signing a message is a three stage process:
+
The examples below use the new <tt>EVP_DigestSign*</tt> and <tt>EVP_DigestVerify*</tt> functions to demonstarte signing and verification. The first example uses an HMAC, and the second example uses RSA key pairs. Additionally, the code for the examples are available for download.
* Initialise the operation.  
 
* Add message data (this step can be repeated as many times as necessary to add more message data)
 
* Create the signature
 
  
In order to initialise the operation, you need to have first set up a EVP_PKEY object containing a public key for an algorithm that supports signing (this includes MAC codes). Refer to [[EVP#Working with EVP_PKEYs|Working with EVP_PKEYs]] for further information. You also need to provide a message digest algorithm (refer to [[EVP#Working with Algorithms and Modes|Working with Algorithms and Modes]]).
+
'''''Note''''': CMAC is only supported since the version 1.1.0 of OpenSSL.  
  
See the following for an example of signing a message:
+
'''''Note''''': DSA handling changed for SSL/TLS cipher suites in OpenSSL 1.1.0. For details, see [http://groups.google.com/forum/#!topic/mailing.openssl.users/1_TFpK6XzQ4 DSA with OpenSSL-1.1] on the mailing list.
  
EVP_MD_CTX *mdctx = NULL;
+
==Overview==
int ret = 0;
+
 
 +
In general, signing a message is a three stage process:
 +
 
 +
* Initialize the context with a message digest/hash function and <tt>EVP_PKEY</tt> key
 +
* Add the message data (this step can be repeated as many times as necessary)
 +
* Finalize the context to create the signature
 +
 
 +
In order to initialize, you first need to select a message digest algorithm (refer to [[EVP#Working with Algorithms and Modes|Working with Algorithms and Modes]]). Second, you need to provide a <tt>EVP_PKEY</tt> containing a key for an algorithm that supports signing (refer to [[EVP#Working with EVP_PKEYs|Working with EVP_PKEYs]]). Both the digest and the key are provided to <tt>EVP_DigestSignInit</tt>.
 +
 
 +
To add the message data, you call <tt>EVP_DigestSignUpdate</tt> one or more times.
 +
 
 +
To finalize the operation and retrieve the signature, you call <tt>EVP_DigestSignFinal</tt>.
 +
 
 +
In general, verification follows the same steps. The key difference is the finalization:
 +
 
 +
* Initialize the context with a message digest/hash function and <tt>EVP_PKEY</tt> key
 +
* Add the message data (this step can be repeated as many times as necessary)
 +
* Finalize the context '''''with''''' the previous signature to verify the message
 +
 
 +
When finalizing during verification, you add the signature in the call. <tt>EVP_DigestVerifyFinal</tt> will then perform the validate the signature on the message.
 +
 
 +
==HMAC==
 +
 
 +
The first example shows how to create an HMAC value of a message with <tt>EVP_DigestSignInit</tt>, <tt>EVP_DigestSignUpdate</tt> and <tt>EVP_DigestSignFinal</tt>. The second part shows how to verify an HMAC value over the message using the same <tt>EVP_DigestSign</tt> functions. You do not use the <tt>EVP_DigestVerify</tt> functions to verify.
 +
 
 +
'''''Note well''''': you do not use <tt>EVP_DigestVerify</tt> to verify an HMAC. <tt>EVP_DigestVerifyInit</tt> will fail with an error 0x608f096: <tt>error:0608F096:digital envelope routines:EVP_PKEY_verify_init:operation not supported for this keytype</tt>.
 +
 
 +
===Calculating HMAC===
 +
 
 +
The code below calculates HMAC for a string.
 +
 
 +
<pre>int hmac_it(const byte* msg, size_t mlen, byte** val, size_t* vlen, EVP_PKEY* pkey)
 +
{
 +
    /* Returned to caller */
 +
    int result = -1;
 +
   
 +
    if(!msg || !mlen || !val || !pkey) {
 +
        assert(0);
 +
        return -1;
 +
    }
 +
   
 +
    if(*val)
 +
        OPENSSL_free(*val);
 +
   
 +
    *val = NULL;
 +
    *vlen = 0;
 +
   
 +
    EVP_MD_CTX* ctx = NULL;
 +
   
 +
    do
 +
    {
 +
        ctx = EVP_MD_CTX_create();
 +
        assert(ctx != NULL);
 +
        if(ctx == NULL) {
 +
            printf("EVP_MD_CTX_create failed, error 0x%lx\n", ERR_get_error());
 +
            break; /* failed */
 +
        }
 +
       
 +
        const EVP_MD* md = EVP_get_digestbyname("SHA256");
 +
        assert(md != NULL);
 +
        if(md == NULL) {
 +
            printf("EVP_get_digestbyname failed, error 0x%lx\n", ERR_get_error());
 +
            break; /* failed */
 +
        }
 +
       
 +
        int rc = EVP_DigestInit_ex(ctx, md, NULL);
 +
        assert(rc == 1);
 +
        if(rc != 1) {
 +
            printf("EVP_DigestInit_ex failed, error 0x%lx\n", ERR_get_error());
 +
            break; /* failed */
 +
        }
 +
       
 +
        rc = EVP_DigestSignInit(ctx, NULL, md, NULL, pkey);
 +
        assert(rc == 1);
 +
        if(rc != 1) {
 +
            printf("EVP_DigestSignInit failed, error 0x%lx\n", ERR_get_error());
 +
            break; /* failed */
 +
        }
 +
       
 +
        rc = EVP_DigestSignUpdate(ctx, msg, mlen);
 +
        assert(rc == 1);
 +
        if(rc != 1) {
 +
            printf("EVP_DigestSignUpdate failed, error 0x%lx\n", ERR_get_error());
 +
            break; /* failed */
 +
        }
 +
       
 +
        size_t req = 0;
 +
        rc = EVP_DigestSignFinal(ctx, NULL, &req);
 +
        assert(rc == 1);
 +
        if(rc != 1) {
 +
            printf("EVP_DigestSignFinal failed (1), error 0x%lx\n", ERR_get_error());
 +
            break; /* failed */
 +
        }
 +
       
 +
        assert(req > 0);
 +
        if(!(req > 0)) {
 +
            printf("EVP_DigestSignFinal failed (2), error 0x%lx\n", ERR_get_error());
 +
            break; /* failed */
 +
        }
 +
       
 +
        *val = OPENSSL_malloc(req);
 +
        assert(*val != NULL);
 +
        if(*val == NULL) {
 +
            printf("OPENSSL_malloc failed, error 0x%lx\n", ERR_get_error());
 +
            break; /* failed */
 +
        }
 +
       
 +
        *vlen = req;
 +
        rc = EVP_DigestSignFinal(ctx, *val, vlen);
 +
        assert(rc == 1);
 +
        if(rc != 1) {
 +
            printf("EVP_DigestSignFinal failed (3), return code %d, error 0x%lx\n", rc, ERR_get_error());
 +
            break; /* failed */
 +
        }
 +
       
 +
        assert(req == *vlen);
 +
        if(req != *vlen) {
 +
            printf("EVP_DigestSignFinal failed, mismatched signature sizes %ld, %ld", req, *vlen);
 +
            break; /* failed */
 +
        }
 +
       
 +
        result = 0;
 +
       
 +
    } while(0);
 +
   
 +
    if(ctx) {
 +
        EVP_MD_CTX_destroy(ctx);
 +
        ctx = NULL;
 +
    }
 +
   
 +
    /* Convert to 0/1 result */
 +
    return !!result;
 +
}</pre>
 +
 
 +
===Verifying HMAC===
 +
 
 +
The code below performs verification of a string using an HMAC.
 +
 
 +
<pre>int verify_it(const byte* msg, size_t mlen, const byte* val, size_t vlen, EVP_PKEY* pkey)
 +
{
 +
    /* Returned to caller */
 +
    int result = -1;
 +
   
 +
    if(!msg || !mlen || !val || !vlen || !pkey) {
 +
        assert(0);
 +
        return -1;
 +
    }
 +
 
 +
    EVP_MD_CTX* ctx = NULL;
 +
   
 +
    do
 +
    {
 +
        ctx = EVP_MD_CTX_create();
 +
        assert(ctx != NULL);
 +
        if(ctx == NULL) {
 +
            printf("EVP_MD_CTX_create failed, error 0x%lx\n", ERR_get_error());
 +
            break; /* failed */
 +
        }
 +
       
 +
        const EVP_MD* md = EVP_get_digestbyname("SHA256");
 +
        assert(md != NULL);
 +
        if(md == NULL) {
 +
            printf("EVP_get_digestbyname failed, error 0x%lx\n", ERR_get_error());
 +
            break; /* failed */
 +
        }
 +
       
 +
        int rc = EVP_DigestInit_ex(ctx, md, NULL);
 +
        assert(rc == 1);
 +
        if(rc != 1) {
 +
            printf("EVP_DigestInit_ex failed, error 0x%lx\n", ERR_get_error());
 +
            break; /* failed */
 +
        }
 +
       
 +
        rc = EVP_DigestSignInit(ctx, NULL, md, NULL, pkey);
 +
        assert(rc == 1);
 +
        if(rc != 1) {
 +
            printf("EVP_DigestSignInit failed, error 0x%lx\n", ERR_get_error());
 +
            break; /* failed */
 +
        }
 +
       
 +
        rc = EVP_DigestSignUpdate(ctx, msg, mlen);
 +
        assert(rc == 1);
 +
        if(rc != 1) {
 +
            printf("EVP_DigestSignUpdate failed, error 0x%lx\n", ERR_get_error());
 +
            break; /* failed */
 +
        }
 +
       
 +
        byte buff[EVP_MAX_MD_SIZE];
 +
        size_t size = sizeof(buff);
 +
       
 +
        rc = EVP_DigestSignFinal(ctx, buff, &size);
 +
        assert(rc == 1);
 +
        if(rc != 1) {
 +
            printf("EVP_DigestVerifyFinal failed, error 0x%lx\n", ERR_get_error());
 +
            break; /* failed */
 +
        }
 +
       
 +
        assert(size > 0);
 +
        if(!(size > 0)) {
 +
            printf("EVP_DigestSignFinal failed (2)\n");
 +
            break; /* failed */
 +
        }
 +
       
 +
        const size_t m = (vlen < size ? vlen : size);
 +
        result = !!CRYPTO_memcmp(val, buff, m);
 +
       
 +
        OPENSSL_cleanse(buff, sizeof(buff));
 +
       
 +
    } while(0);
 +
   
 +
    if(ctx) {
 +
        EVP_MD_CTX_destroy(ctx);
 +
        ctx = NULL;
 +
    }
 +
   
 +
    /* Convert to 0/1 result */
 +
    return !!result;
 +
}</pre>
 +
 
 +
==Asymmetric Key==
 +
 
 +
The second example shows how to create a signature over a message using private keys with <tt>EVP_DigestSignInit</tt>, <tt>EVP_DigestSignUpdate</tt> and <tt>EVP_DigestSignFinal</tt>. The second example shows how to verify a signature over the message using public keys with <tt>EVP_DigestVerifyInit</tt>, <tt>EVP_DigestVerifyUpdate</tt> and <tt>EVP_DigestVerifyFinal</tt>. Unlike HMACs, you do use the <tt>EVP_DigestVerify</tt> functions to verify.
 +
 
 +
===Signing===
 +
 
 +
 
 +
 
 +
<pre>EVP_MD_CTX *mdctx = NULL;
 +
int ret = 0;
 
   
 
   
*sig = NULL;
+
*sig = NULL;
 
   
 
   
/* Create the Message Digest Context */
+
/* Create the Message Digest Context */
if(!(mdctx = EVP_MD_CTX_create())) goto err;
+
if(!(mdctx = EVP_MD_CTX_create())) goto err;
 
   
 
   
/* Initialise the DigestSign operation */
+
/* Initialise the DigestSign operation - SHA-256 has been selected as the message digest function in this example */
  if(!EVP_DigestSignInit(mdctx, NULL, EVP_sha256(), NULL, key)) goto err;
+
  if(1 != EVP_DigestSignInit(mdctx, NULL, EVP_sha256(), NULL, key)) goto err;
 
   
 
   
 
  /* Call update with the message */
 
  /* Call update with the message */
  if(!EVP_DigestSignUpdate(mdctx, msg, strlen(msg))) goto err;
+
  if(1 != EVP_DigestSignUpdate(mdctx, msg, strlen(msg))) goto err;
 
   
 
   
 
  /* Finalise the DigestSign operation */
 
  /* Finalise the DigestSign operation */
  if(!EVP_DigestSignFinal(mdctx, *sig, slen)) goto err;
+
/* First call EVP_DigestSignFinal with a NULL sig parameter to obtain the length of the
  if(!(*sig = malloc(sizeof(unsigned char) * (*slen)))) goto err;
+
  * signature. Length is returned in slen */
  if(!EVP_DigestSignFinal(mdctx, *sig, slen)) goto err;
+
  if(1 != EVP_DigestSignFinal(mdctx, NULL, slen)) goto err;
 +
/* Allocate memory for the signature based on size in slen */
 +
  if(!(*sig = OPENSSL_malloc(sizeof(unsigned char) * (*slen)))) goto err;
 +
/* Obtain the signature */
 +
  if(1 != EVP_DigestSignFinal(mdctx, *sig, slen)) goto err;
 
   
 
   
 
  /* Success */
 
  /* Success */
Line 41: Line 273:
 
   
 
   
 
  /* Clean up */
 
  /* Clean up */
  if(*sig && !ret) free(*sig);
+
  if(*sig && !ret) OPENSSL_free(*sig);
  if(mdctx) EVP_MD_CTX_destroy(mdctx);
+
  if(mdctx) EVP_MD_CTX_destroy(mdctx);</pre>
 +
 
 +
Note: There is no difference in the API between signing using an asymmetric algorithm, and calculating a MAC value. In the case of CMAC no message digest function is required (NULL can be passed). Signing using the EVP_Sign* functions is very similar to the above example, except there is no support for MAC values. Note that CMAC is only supported since the version 1.1.0 of OpenSSL.
 +
 
 +
One gotcha to be aware of is that the first call to EVP_DigestSignFinal simply returns the maximum size of the buffer required. This will not always be the same as the size of the generated signature (specifically in the case of DSA and ECDSA). This means that you should also take account of the value of the length returned on the second call (in the slen variable in this example) when making use of the signature.
 +
 
 +
Refer to [[Manual:EVP_DigestSignInit(3)]] for further details on the EVP_DigestSign* functions, and [[Manual:EVP_SignInit(3)]] for the EVP_Sign* functions.
 +
 
 +
===Verifying===
 +
 
 +
Verifying a message is very similar to signing except the EVP_DigestVerify* functions (or EVP_Verify* functions) are used instead. Clearly only a public key is required for a verify operation:
 +
 
 +
<pre>/* Initialize `key` with a public key */
 +
if(1 != EVP_DigestVerifyInit(mdctx, NULL, EVP_sha256(), NULL, key)) goto err;
 +
 
 +
/* Initialize `key` with a public key */
 +
if(1 != EVP_DigestVerifyUpdate(mdctx, msg, strlen(msg))) goto err;
 +
 
 +
if(1 == EVP_DigestVerifyFinal(mdctx, sig, slen))
 +
{
 +
    /* Success */
 +
}
 +
else
 +
{
 +
    /* Failure */
 +
}
 +
</pre>
 +
 
 +
Note that MAC operations do not support the verify operation. Verifying a MAC value is done by calling the sign operations and confirming that the generated code is identical to the one provided. It is important that when comparing a supplied MAC with an expected MAC that the comparison takes a constant time whether the comparison returns a match or not. Failure to do this can expose your code to timing attacks, which could (for example) enable an attacker to forge MAC codes. To do this use the CRYPTO_memcmp function as shown in the code example below. '''Never''' use memcmp for this test:
 +
 
 +
<pre>
 +
if(!(mdctx = EVP_MD_CTX_create())) goto err;
 +
 
 +
/* Create a buffer to store the MAC for the received message */
 +
if(!(valtmp = OPENSSL_malloc(sizeof(unsigned char) * EVP_PKEY_size(key)))) goto err;
 +
valtmplen = EVP_PKEY_size(key);
 +
 
 +
/* Calculate the MAC for the received message */
 +
if(1 != EVP_DigestSignInit(mdctx, NULL, EVP_sha256(), NULL, key)) goto err;
 +
if(1 != EVP_DigestSignUpdate(mdctx, msg, strlen(msg))) goto err;
 +
if(1 != EVP_DigestSignFinal(mdctx, valtmp, &valtmplen)) goto err;
 +
 
 +
/* Check the lengths of the calculated and supplied MACs are the same */
 +
if(valtmplen != vlen) goto err;
 +
 
 +
/* Calculated MAC is in valtmp. Supplied MAC is in val. Compare the two */
 +
if(CRYPTO_memcmp(val, valtmp, vlen))
 +
/* Verify failure */ goto failure;
 +
else
 +
/* Verify success */;
 +
</pre>
 +
 
 +
Refer to [[Manual:EVP_DigestVerifyInit(3)]] and [[Manual:EVP_VerifyInit(3)]] for further information on the verify functions.
 +
 
  
Note: There is no difference in the API between signing using an asymmetric algorithm, and generating a MAC code. Signing using the EVP_Sign* functions is very similar to the above example, except there is no support for MAC codes.
+
==Downloads==
  
Refer to http://www.openssl.org/docs/crypto/EVP_DigestSignInit.html for further details on the EVP_DigestSign* functions, and http://www.openssl.org/docs/crypto/EVP_SignInit.html for the EVP_Sign* functions.
+
[[Media:t-hmac.c.tar.gz|t-hmac.c.tar.gz]] - sample program to calculate HMAC and verify a string using an HMAC with the <tt>EVP_DigestSign*</tt> and <tt>EVP_DigestVerify*</tt> functions.
  
==Verifying a Message==
+
[[Media:t-rsa.c.tar.gz|t-rsa.c.tar.gz]] - sample program to sign and verify a string using RSA with the <tt>EVP_DigestSign*</tt> and <tt>EVP_DigestVerify*</tt> functions.
  
Verifying a message is very similar to signing except the EVP_DigestVerify* functions (or EVP_Verify* functions) are used instead:
+
==See also==
 +
* [[EVP]]
 +
* [[Libcrypto API]]
 +
* [[EVP Symmetric Encryption and Decryption]]
 +
* [[EVP Authenticated Encryption and Decryption]]
 +
* [[EVP Asymmetric Encryption and Decryption of an Envelope]]
 +
* [[EVP Message Digests]]
 +
* [[EVP Key Agreement]]
 +
* [[EVP Key and Parameter Generation]]
  
if(!EVP_DigestVerifyInit(mdctx, NULL, EVP_sha256(), NULL, key)) goto err;
+
[[Category:Crypto API]]
if(!EVP_DigestVerifyUpdate(mdctx, msg, strlen(msg))) goto err;
+
[[Category:C level]]
if(!EVP_DigestVerifyFinal(mdctx, sig, slen)) goto err;
+
[[Category:Examples]]

Latest revision as of 13:47, 15 January 2018

Signing and Verifying
Documentation
#include <openssl/evp.h>

There are two APIs available to perform sign and verify operations. The first are the older EVP_Sign* and EVP_Verify* functions; and the second are the newer and more flexible EVP_DigestSign* and EVP_DigestVerify* functions. Though the APIs are similar, new applications should use the EVP_DigestSign* and EVP_DigestVerify* functions.

The examples below use the new EVP_DigestSign* and EVP_DigestVerify* functions to demonstarte signing and verification. The first example uses an HMAC, and the second example uses RSA key pairs. Additionally, the code for the examples are available for download.

Note: CMAC is only supported since the version 1.1.0 of OpenSSL.

Note: DSA handling changed for SSL/TLS cipher suites in OpenSSL 1.1.0. For details, see DSA with OpenSSL-1.1 on the mailing list.

Overview[edit]

In general, signing a message is a three stage process:

  • Initialize the context with a message digest/hash function and EVP_PKEY key
  • Add the message data (this step can be repeated as many times as necessary)
  • Finalize the context to create the signature

In order to initialize, you first need to select a message digest algorithm (refer to Working with Algorithms and Modes). Second, you need to provide a EVP_PKEY containing a key for an algorithm that supports signing (refer to Working with EVP_PKEYs). Both the digest and the key are provided to EVP_DigestSignInit.

To add the message data, you call EVP_DigestSignUpdate one or more times.

To finalize the operation and retrieve the signature, you call EVP_DigestSignFinal.

In general, verification follows the same steps. The key difference is the finalization:

  • Initialize the context with a message digest/hash function and EVP_PKEY key
  • Add the message data (this step can be repeated as many times as necessary)
  • Finalize the context with the previous signature to verify the message

When finalizing during verification, you add the signature in the call. EVP_DigestVerifyFinal will then perform the validate the signature on the message.

HMAC[edit]

The first example shows how to create an HMAC value of a message with EVP_DigestSignInit, EVP_DigestSignUpdate and EVP_DigestSignFinal. The second part shows how to verify an HMAC value over the message using the same EVP_DigestSign functions. You do not use the EVP_DigestVerify functions to verify.

Note well: you do not use EVP_DigestVerify to verify an HMAC. EVP_DigestVerifyInit will fail with an error 0x608f096: error:0608F096:digital envelope routines:EVP_PKEY_verify_init:operation not supported for this keytype.

Calculating HMAC[edit]

The code below calculates HMAC for a string.

int hmac_it(const byte* msg, size_t mlen, byte** val, size_t* vlen, EVP_PKEY* pkey)
{
    /* Returned to caller */
    int result = -1;
    
    if(!msg || !mlen || !val || !pkey) {
        assert(0);
        return -1;
    }
    
    if(*val)
        OPENSSL_free(*val);
    
    *val = NULL;
    *vlen = 0;
    
    EVP_MD_CTX* ctx = NULL;
    
    do
    {
        ctx = EVP_MD_CTX_create();
        assert(ctx != NULL);
        if(ctx == NULL) {
            printf("EVP_MD_CTX_create failed, error 0x%lx\n", ERR_get_error());
            break; /* failed */
        }
        
        const EVP_MD* md = EVP_get_digestbyname("SHA256");
        assert(md != NULL);
        if(md == NULL) {
            printf("EVP_get_digestbyname failed, error 0x%lx\n", ERR_get_error());
            break; /* failed */
        }
        
        int rc = EVP_DigestInit_ex(ctx, md, NULL);
        assert(rc == 1);
        if(rc != 1) {
            printf("EVP_DigestInit_ex failed, error 0x%lx\n", ERR_get_error());
            break; /* failed */
        }
        
        rc = EVP_DigestSignInit(ctx, NULL, md, NULL, pkey);
        assert(rc == 1);
        if(rc != 1) {
            printf("EVP_DigestSignInit failed, error 0x%lx\n", ERR_get_error());
            break; /* failed */
        }
        
        rc = EVP_DigestSignUpdate(ctx, msg, mlen);
        assert(rc == 1);
        if(rc != 1) {
            printf("EVP_DigestSignUpdate failed, error 0x%lx\n", ERR_get_error());
            break; /* failed */
        }
        
        size_t req = 0;
        rc = EVP_DigestSignFinal(ctx, NULL, &req);
        assert(rc == 1);
        if(rc != 1) {
            printf("EVP_DigestSignFinal failed (1), error 0x%lx\n", ERR_get_error());
            break; /* failed */
        }
        
        assert(req > 0);
        if(!(req > 0)) {
            printf("EVP_DigestSignFinal failed (2), error 0x%lx\n", ERR_get_error());
            break; /* failed */
        }
        
        *val = OPENSSL_malloc(req);
        assert(*val != NULL);
        if(*val == NULL) {
            printf("OPENSSL_malloc failed, error 0x%lx\n", ERR_get_error());
            break; /* failed */
        }
        
        *vlen = req;
        rc = EVP_DigestSignFinal(ctx, *val, vlen);
        assert(rc == 1);
        if(rc != 1) {
            printf("EVP_DigestSignFinal failed (3), return code %d, error 0x%lx\n", rc, ERR_get_error());
            break; /* failed */
        }
        
        assert(req == *vlen);
        if(req != *vlen) {
            printf("EVP_DigestSignFinal failed, mismatched signature sizes %ld, %ld", req, *vlen);
            break; /* failed */
        }
        
        result = 0;
        
    } while(0);
    
    if(ctx) {
        EVP_MD_CTX_destroy(ctx);
        ctx = NULL;
    }
    
    /* Convert to 0/1 result */
    return !!result;
}

Verifying HMAC[edit]

The code below performs verification of a string using an HMAC.

int verify_it(const byte* msg, size_t mlen, const byte* val, size_t vlen, EVP_PKEY* pkey)
{
    /* Returned to caller */
    int result = -1;
    
    if(!msg || !mlen || !val || !vlen || !pkey) {
        assert(0);
        return -1;
    }

    EVP_MD_CTX* ctx = NULL;
    
    do
    {
        ctx = EVP_MD_CTX_create();
        assert(ctx != NULL);
        if(ctx == NULL) {
            printf("EVP_MD_CTX_create failed, error 0x%lx\n", ERR_get_error());
            break; /* failed */
        }
        
        const EVP_MD* md = EVP_get_digestbyname("SHA256");
        assert(md != NULL);
        if(md == NULL) {
            printf("EVP_get_digestbyname failed, error 0x%lx\n", ERR_get_error());
            break; /* failed */
        }
        
        int rc = EVP_DigestInit_ex(ctx, md, NULL);
        assert(rc == 1);
        if(rc != 1) {
            printf("EVP_DigestInit_ex failed, error 0x%lx\n", ERR_get_error());
            break; /* failed */
        }
        
        rc = EVP_DigestSignInit(ctx, NULL, md, NULL, pkey);
        assert(rc == 1);
        if(rc != 1) {
            printf("EVP_DigestSignInit failed, error 0x%lx\n", ERR_get_error());
            break; /* failed */
        }
        
        rc = EVP_DigestSignUpdate(ctx, msg, mlen);
        assert(rc == 1);
        if(rc != 1) {
            printf("EVP_DigestSignUpdate failed, error 0x%lx\n", ERR_get_error());
            break; /* failed */
        }
        
        byte buff[EVP_MAX_MD_SIZE];
        size_t size = sizeof(buff);
        
        rc = EVP_DigestSignFinal(ctx, buff, &size);
        assert(rc == 1);
        if(rc != 1) {
            printf("EVP_DigestVerifyFinal failed, error 0x%lx\n", ERR_get_error());
            break; /* failed */
        }
        
        assert(size > 0);
        if(!(size > 0)) {
            printf("EVP_DigestSignFinal failed (2)\n");
            break; /* failed */
        }
        
        const size_t m = (vlen < size ? vlen : size);
        result = !!CRYPTO_memcmp(val, buff, m);
        
        OPENSSL_cleanse(buff, sizeof(buff));
        
    } while(0);
    
    if(ctx) {
        EVP_MD_CTX_destroy(ctx);
        ctx = NULL;
    }
    
    /* Convert to 0/1 result */
    return !!result;
}

Asymmetric Key[edit]

The second example shows how to create a signature over a message using private keys with EVP_DigestSignInit, EVP_DigestSignUpdate and EVP_DigestSignFinal. The second example shows how to verify a signature over the message using public keys with EVP_DigestVerifyInit, EVP_DigestVerifyUpdate and EVP_DigestVerifyFinal. Unlike HMACs, you do use the EVP_DigestVerify functions to verify.

Signing[edit]

EVP_MD_CTX *mdctx = NULL;
int ret = 0;
 
*sig = NULL;
 
/* Create the Message Digest Context */
if(!(mdctx = EVP_MD_CTX_create())) goto err;
 
/* Initialise the DigestSign operation - SHA-256 has been selected as the message digest function in this example */
 if(1 != EVP_DigestSignInit(mdctx, NULL, EVP_sha256(), NULL, key)) goto err;
 
 /* Call update with the message */
 if(1 != EVP_DigestSignUpdate(mdctx, msg, strlen(msg))) goto err;
 
 /* Finalise the DigestSign operation */
 /* First call EVP_DigestSignFinal with a NULL sig parameter to obtain the length of the
  * signature. Length is returned in slen */
 if(1 != EVP_DigestSignFinal(mdctx, NULL, slen)) goto err;
 /* Allocate memory for the signature based on size in slen */
 if(!(*sig = OPENSSL_malloc(sizeof(unsigned char) * (*slen)))) goto err;
 /* Obtain the signature */
 if(1 != EVP_DigestSignFinal(mdctx, *sig, slen)) goto err;
 
 /* Success */
 ret = 1;
 
 err:
 if(ret != 1)
 {
   /* Do some error handling */
 }
 
 /* Clean up */
 if(*sig && !ret) OPENSSL_free(*sig);
 if(mdctx) EVP_MD_CTX_destroy(mdctx);

Note: There is no difference in the API between signing using an asymmetric algorithm, and calculating a MAC value. In the case of CMAC no message digest function is required (NULL can be passed). Signing using the EVP_Sign* functions is very similar to the above example, except there is no support for MAC values. Note that CMAC is only supported since the version 1.1.0 of OpenSSL.

One gotcha to be aware of is that the first call to EVP_DigestSignFinal simply returns the maximum size of the buffer required. This will not always be the same as the size of the generated signature (specifically in the case of DSA and ECDSA). This means that you should also take account of the value of the length returned on the second call (in the slen variable in this example) when making use of the signature.

Refer to Manual:EVP_DigestSignInit(3) for further details on the EVP_DigestSign* functions, and Manual:EVP_SignInit(3) for the EVP_Sign* functions.

Verifying[edit]

Verifying a message is very similar to signing except the EVP_DigestVerify* functions (or EVP_Verify* functions) are used instead. Clearly only a public key is required for a verify operation:

/* Initialize `key` with a public key */
if(1 != EVP_DigestVerifyInit(mdctx, NULL, EVP_sha256(), NULL, key)) goto err;

/* Initialize `key` with a public key */
if(1 != EVP_DigestVerifyUpdate(mdctx, msg, strlen(msg))) goto err;

if(1 == EVP_DigestVerifyFinal(mdctx, sig, slen))
{
    /* Success */
}
else
{
    /* Failure */
}

Note that MAC operations do not support the verify operation. Verifying a MAC value is done by calling the sign operations and confirming that the generated code is identical to the one provided. It is important that when comparing a supplied MAC with an expected MAC that the comparison takes a constant time whether the comparison returns a match or not. Failure to do this can expose your code to timing attacks, which could (for example) enable an attacker to forge MAC codes. To do this use the CRYPTO_memcmp function as shown in the code example below. Never use memcmp for this test:

	if(!(mdctx = EVP_MD_CTX_create())) goto err;

	/* Create a buffer to store the MAC for the received message */
	if(!(valtmp = OPENSSL_malloc(sizeof(unsigned char) * EVP_PKEY_size(key)))) goto err;
	valtmplen = EVP_PKEY_size(key);

	/* Calculate the MAC for the received message */
	if(1 != EVP_DigestSignInit(mdctx, NULL, EVP_sha256(), NULL, key)) goto err;
	if(1 != EVP_DigestSignUpdate(mdctx, msg, strlen(msg))) goto err;
	if(1 != EVP_DigestSignFinal(mdctx, valtmp, &valtmplen)) goto err;

	/* Check the lengths of the calculated and supplied MACs are the same */ 
	if(valtmplen != vlen) goto err;

	/* Calculated MAC is in valtmp. Supplied MAC is in val. Compare the two */
	if(CRYPTO_memcmp(val, valtmp, vlen))	
		/* Verify failure */ goto failure;
	else
		/* Verify success */;

Refer to Manual:EVP_DigestVerifyInit(3) and Manual:EVP_VerifyInit(3) for further information on the verify functions.


Downloads[edit]

t-hmac.c.tar.gz - sample program to calculate HMAC and verify a string using an HMAC with the EVP_DigestSign* and EVP_DigestVerify* functions.

t-rsa.c.tar.gz - sample program to sign and verify a string using RSA with the EVP_DigestSign* and EVP_DigestVerify* functions.

See also[edit]