Merge tag 'release_3_0_12' into branch moonshot-fr-3.0.12-upgrade.
[freeradius.git] / src / modules / rlm_eap / types / rlm_eap_fast / eap_fast_crypto.c
1 /*
2  * fast-crypto.c  Cryptographic functions for EAP-FAST.
3  *
4  * Version:     $Id$
5  *
6  *   This program is free software; you can redistribute it and/or modify
7  *   it under the terms of the GNU General Public License as published by
8  *   the Free Software Foundation; either version 2 of the License, or
9  *   (at your option) any later version.
10  *
11  *   This program is distributed in the hope that it will be useful,
12  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *   GNU General Public License for more details.
15  *
16  *   You should have received a copy of the GNU General Public License
17  *   along with this program; if not, write to the Free Software
18  *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  *
20  * Copyright 2016 Alan DeKok <aland@freeradius.org>
21  * Copyright 2016 The FreeRADIUS server project
22  */
23
24 RCSID("$Id$")
25 USES_APPLE_DEPRECATED_API       /* OpenSSL API has been deprecated by Apple */
26
27 #include <stdio.h>
28 #include <freeradius-devel/libradius.h>
29
30 #include <openssl/evp.h>
31 #include <openssl/aes.h>
32 #include <openssl/err.h>
33
34 #include "eap_fast_crypto.h"
35
36 // http://stackoverflow.com/a/29838852
37 static void NEVER_RETURNS handleErrors(void)
38 {
39         unsigned long errCode;
40
41         fprintf(stderr, "An error occurred\n");
42         while((errCode = ERR_get_error()))
43         {
44                 char *err = ERR_error_string(errCode, NULL);
45                 fprintf(stderr, "%s\n", err);
46         }
47         abort();
48 }
49
50 // https://wiki.openssl.org/index.php/EVP_Authenticated_Encryption_and_Decryption#Authenticated_Encryption_using_GCM_mode
51 int eap_fast_encrypt(uint8_t const *plaintext, size_t plaintext_len,
52                      uint8_t const *aad, size_t aad_len,
53                      uint8_t const *key, uint8_t *iv, unsigned char *ciphertext,
54                      uint8_t *tag)
55 {
56         EVP_CIPHER_CTX *ctx;
57
58         int len;
59
60         int ciphertext_len;
61
62
63         /* Create and initialise the context */
64         if (!(ctx = EVP_CIPHER_CTX_new())) handleErrors();
65
66         /* Initialise the encryption operation. */
67         if (1 != EVP_EncryptInit_ex(ctx, EVP_aes_256_gcm(), NULL, NULL, NULL))
68                 handleErrors();
69
70         /* Set IV length if default 12 bytes (96 bits) is not appropriate */
71         if (1 != EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, 16, NULL))
72                 handleErrors();
73
74         /* Initialise key and IV */
75         if (1 != EVP_EncryptInit_ex(ctx, NULL, NULL, key, iv)) handleErrors();
76
77         /* Provide any AAD data. This can be called zero or more times as
78          * required
79          */
80         if (1 != EVP_EncryptUpdate(ctx, NULL, &len, aad, aad_len))
81                 handleErrors();
82
83         /* Provide the message to be encrypted, and obtain the encrypted output.
84          * EVP_EncryptUpdate can be called multiple times if necessary
85          */
86         if (1 != EVP_EncryptUpdate(ctx, ciphertext, &len, plaintext, plaintext_len))
87                 handleErrors();
88         ciphertext_len = len;
89
90         /* Finalise the encryption. Normally ciphertext bytes may be written at
91          * this stage, but this does not occur in GCM mode
92          */
93         if (1 != EVP_EncryptFinal_ex(ctx, ciphertext + len, &len)) handleErrors();
94         ciphertext_len += len;
95
96         /* Get the tag */
97         if (1 != EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, 16, tag))
98                 handleErrors();
99
100         /* Clean up */
101         EVP_CIPHER_CTX_free(ctx);
102
103         return ciphertext_len;
104 }
105
106 int eap_fast_decrypt(uint8_t const *ciphertext, size_t ciphertext_len,
107                      uint8_t const *aad, size_t aad_len,
108                      uint8_t const *tag, uint8_t const *key, uint8_t const *iv, uint8_t *plaintext)
109 {
110         EVP_CIPHER_CTX *ctx;
111         int len;
112         int plaintext_len;
113         int ret;
114
115         /* Create and initialise the context */
116         if (!(ctx = EVP_CIPHER_CTX_new())) handleErrors();
117
118         /* Initialise the decryption operation. */
119         if (!EVP_DecryptInit_ex(ctx, EVP_aes_256_gcm(), NULL, NULL, NULL))
120                 handleErrors();
121
122         /* Set IV length. Not necessary if this is 12 bytes (96 bits) */
123         if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, 16, NULL))
124                 handleErrors();
125
126         /* Initialise key and IV */
127         if (!EVP_DecryptInit_ex(ctx, NULL, NULL, key, iv)) handleErrors();
128
129         /* Provide any AAD data. This can be called zero or more times as
130          * required
131          */
132         if (!EVP_DecryptUpdate(ctx, NULL, &len, aad, aad_len))
133                 handleErrors();
134
135         /* Provide the message to be decrypted, and obtain the plaintext output.
136          * EVP_DecryptUpdate can be called multiple times if necessary
137          */
138         if (!EVP_DecryptUpdate(ctx, plaintext, &len, ciphertext, ciphertext_len))
139                 handleErrors();
140         plaintext_len = len;
141
142         {
143                 unsigned char *tmp;
144
145                 memcpy(&tmp, &tag, sizeof(tmp));
146
147                 /* Set expected tag value. Works in OpenSSL 1.0.1d and later */
148                 if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, 16, tmp)) handleErrors();
149         }
150
151         /* Finalise the decryption. A positive return value indicates success,
152          * anything else is a failure - the plaintext is not trustworthy.
153          */
154         ret = EVP_DecryptFinal_ex(ctx, plaintext + len, &len);
155
156         /* Clean up */
157         EVP_CIPHER_CTX_free(ctx);
158
159         if (ret > 0)
160         {
161                 /* Success */
162                 plaintext_len += len;
163                 return plaintext_len;
164         }
165         else
166         {
167                 /* Verify failed */
168                 return -1;
169         }
170 }