Permalink
Find file
Fetching contributors…
Cannot retrieve contributors at this time
206 lines (166 sloc) 5.74 KB
---
layout: post
title: '(minor) WTF Openssl'
permalink: '/wtf/openssl_wtf.html'
tags: ['crypto', 'openssl', 'wtf']
---
<div class="lead">
<p>
A minor WTF in openssl, where mixing encryption &amp; decryption functions
does not result in an error. This can lead to subtle security bugs if the
code is poorly written.
</p>
<p>This <a href="https://github.com/openssl/openssl/pull/172" class="external">pull request</a> to address this issue has been open for over a year; it seems nobody cares?</p>
<p>
A sane library should fail at the EVP_DecryptUpdate on line 123. Running
this code (openssl 1.0.1) however results in:
</p>
<pre>
Encrypting:
assertion ok: EVP_EncryptInit_ex
assertion ok: EVP_EncryptUpdate
assertion ok: EVP_EncryptFinal_ex
assertion ok: check ct1 len
cipher text: eb87ad64ea2696370c99e6e7
Decrypting:
assertion ok: EVP_DecryptInit_ex
assertion ok: EVP_DecryptUpdate
assertion ok: EVP_DecryptFinal_ex
assertion ok: check pt2 len
assertion ok: pt2 == pt1
plain text: hello world
Decrypting a subset, with EVP_EncryptInit_ex
assertion ok: EVP_EncryptInit_ex
assertion ok: EVP_DecryptUpdate
assertion ok: EVP_DecryptFinal_ex
plain text: hello worl
Decrypting a subset, with EVP_DecryptInit_ex
assertion ok: EVP_DecryptInit_ex
assertion ok: EVP_DecryptUpdate
ASSERTION FAILURE: EVP_DecryptFinal_ex
plain text: hello worl
</pre>
</div>
<p>openssl_wtf.c:</p>
<pre class="prettyprint linenums lang-c">
/**
* openssl's EVP library does not prevent you from mixing
* encryption &amp; decryption functions on a given ctx. This
* can lead to subtle bugs if the code is poorly written.
* Data will get decrypted but not authenticated!
*
* Compile and run with:
* gcc -o openssl_wtf openssl_wtf.c
* -L/usr/lib/x86_64-linux-gnu/ -lssl -lcrypto
* ./openssl_wtf
*/
#include &lt;stdio.h&gt;
#include &lt;strings.h&gt;
#include &lt;openssl/bio.h&gt;
#include &lt;openssl/evp.h&gt;
#include &lt;openssl/rand.h&gt;
void assert(int exp, char *msg) {
if (!exp) {
printf(" ASSERTION FAILURE: %s\n", msg);
} else {
printf(" assertion ok: %s\n", msg);
}
}
int main(int argc, char **argv) {
unsigned char *key = (unsigned char *)"blah";
unsigned char iv[12] = { 0x00 };
unsigned char tag[16] = { 0x00 };
unsigned char pt1[] =
{'h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd', 0};
unsigned char ct1[sizeof(pt1)];
unsigned char ct2[sizeof(pt1) - 2];
unsigned char pt2[sizeof(pt1)];
EVP_CIPHER_CTX ctx;
int ct_offset, pt2_offset, t, r;
EVP_CIPHER_CTX_init(&amp;ctx);
/* Initialize IV, don't use pseudo_bytes in real code */
RAND_pseudo_bytes(iv, sizeof(iv));
/* Encrypt the plain text */
printf("Encrypting:\n");
bzero(ct1, sizeof(ct1));
r = EVP_EncryptInit_ex(&amp;ctx, EVP_aes_256_gcm(), NULL, key, iv);
assert(r == 1, "EVP_EncryptInit_ex");
ct_offset = 0;
r = EVP_EncryptUpdate(&amp;ctx, ct1 + ct_offset, &amp;t, pt1, sizeof(pt1));
ct_offset += t;
assert(r == 1, "EVP_EncryptUpdate");
r = EVP_EncryptFinal_ex(&amp;ctx, ct1 + ct_offset, &amp;t);
ct_offset += t;
assert(r == 1, "EVP_EncryptFinal_ex");
assert(ct_offset == sizeof(ct1), "check ct1 len");
/* Save tag */
EVP_CIPHER_CTX_ctrl(&amp;ctx, EVP_CTRL_GCM_GET_TAG, sizeof(tag), tag);
/* Clean up */
EVP_CIPHER_CTX_cleanup(&amp;ctx);
printf("cipher text: ");
for (t=0; t&lt;sizeof(ct1); t++) {
printf("%02x", ct1[t]);
}
printf("\n\n");
/* Decrypt the cipher text */
printf("Decrypting:\n");
bzero(pt2, sizeof(pt1));
EVP_CIPHER_CTX_init(&amp;ctx);
r = EVP_DecryptInit_ex(&amp;ctx, EVP_aes_256_gcm(), NULL, key, iv);
assert(r == 1, "EVP_DecryptInit_ex");
EVP_CIPHER_CTX_ctrl(&amp;ctx, EVP_CTRL_GCM_SET_TAG, sizeof(tag), tag);
pt2_offset = 0;
r = EVP_DecryptUpdate(&amp;ctx, pt2 + pt2_offset, &amp;t, ct1, sizeof(ct1));
pt2_offset += t;
assert(r == 1, "EVP_DecryptUpdate");
r = EVP_DecryptFinal_ex(&amp;ctx, pt2 + pt2_offset, &amp;t);
pt2_offset += t;
assert(r == 1, "EVP_DecryptFinal_ex");
assert(pt2_offset == sizeof(pt1), "check pt2 len");
r = 1;
for (t=0; t&lt;sizeof(pt1); t++) {
r = r &amp;&amp; (pt1[t] == pt2[t]);
}
assert(r == 1, "pt2 == pt1");
printf("plain text: %s\n", pt2);
EVP_CIPHER_CTX_cleanup(&amp;ctx);
printf("\n");
/* Truncate the cipher text */
for (t=0; t&lt;sizeof(ct2); t++) {
ct2[t] = ct1[t];
}
/* Decrypt a subset */
printf("Decrypting a subset, with EVP_EncryptInit_ex\n");
bzero(pt2, sizeof(pt2));
EVP_CIPHER_CTX_init(&amp;ctx);
r = EVP_EncryptInit_ex(&amp;ctx, EVP_aes_256_gcm(), NULL, key, iv);
assert(r == 1, "EVP_EncryptInit_ex");
EVP_CIPHER_CTX_ctrl(&amp;ctx, EVP_CTRL_GCM_SET_TAG, sizeof(tag), tag);
pt2_offset = 0;
r = EVP_DecryptUpdate(&amp;ctx, pt2 + pt2_offset, &amp;t, ct2, sizeof(ct2));
pt2_offset += t;
assert(r == 1, "EVP_DecryptUpdate");
r = EVP_DecryptFinal_ex(&amp;ctx, pt2 + pt2_offset, &amp;t);
pt2_offset += t;
assert(r == 1, "EVP_DecryptFinal_ex");
printf("plain text: %s\n", pt2);
EVP_CIPHER_CTX_cleanup(&amp;ctx);
printf("\n");
/* Decrypt a subset, correctly this time */
printf("Decrypting a subset, with EVP_DecryptInit_ex\n");
bzero(pt2, sizeof(pt2));
EVP_CIPHER_CTX_init(&amp;ctx);
r = EVP_DecryptInit_ex(&amp;ctx, EVP_aes_256_gcm(), NULL, key, iv);
assert(r == 1, "EVP_DecryptInit_ex");
EVP_CIPHER_CTX_ctrl(&amp;ctx, EVP_CTRL_GCM_SET_TAG, sizeof(tag), tag);
pt2_offset = 0;
r = EVP_DecryptUpdate(&amp;ctx, pt2 + pt2_offset, &amp;t, ct2, sizeof(ct2));
pt2_offset += t;
assert(r == 1, "EVP_DecryptUpdate");
r = EVP_DecryptFinal_ex(&amp;ctx, pt2 + pt2_offset, &amp;t);
pt2_offset += t;
assert(r == 1, "EVP_DecryptFinal_ex");
printf("plain text: %s\n", pt2);
EVP_CIPHER_CTX_cleanup(&amp;ctx);
}
</pre>