1 /*        $NetBSD: crypto-evp.c,v 1.5 2023/06/19 21:41:44 christos Exp $        */
2 
3 /*
4  * Copyright (c) 1997 - 2008 Kungliga Tekniska Högskolan
5  * (Royal Institute of Technology, Stockholm, Sweden).
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  *
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * 3. Neither the name of the Institute nor the names of its contributors
20  *    may be used to endorse or promote products derived from this software
21  *    without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  */
35 
36 #include "krb5_locl.h"
37 
38 void
_krb5_evp_schedule(krb5_context context,struct _krb5_key_type * kt,struct _krb5_key_data * kd)39 _krb5_evp_schedule(krb5_context context,
40                        struct _krb5_key_type *kt,
41                        struct _krb5_key_data *kd)
42 {
43     struct _krb5_evp_schedule *key = kd->schedule->data;
44     const EVP_CIPHER *c = (*kt->evp)();
45 
46 #if OPENSSL_VERSION_NUMBER < 0x10100000UL
47     key->ectx = malloc(sizeof(*key->ectx));
48     key->dctx = malloc(sizeof(*key->dctx));
49     EVP_CIPHER_CTX_init(key->ectx);
50     EVP_CIPHER_CTX_init(key->dctx);
51 #else
52     key->ectx = EVP_CIPHER_CTX_new();
53     key->dctx = EVP_CIPHER_CTX_new();
54 #endif
55 
56     if (!EVP_CipherInit_ex(key->ectx, c, NULL, kd->key->keyvalue.data, NULL, 1))
57           krb5_abortx(context, "can't initialize cipher");
58     if (!EVP_CipherInit_ex(key->dctx, c, NULL, kd->key->keyvalue.data, NULL, 0))
59           krb5_abortx(context, "can't initialize cipher");
60 }
61 
62 void
_krb5_evp_cleanup(krb5_context context,struct _krb5_key_data * kd)63 _krb5_evp_cleanup(krb5_context context, struct _krb5_key_data *kd)
64 {
65     struct _krb5_evp_schedule *key = kd->schedule->data;
66 #if OPENSSL_VERSION_NUMBER < 0x10100000UL
67     EVP_CIPHER_CTX_cleanup(key->ectx);
68     EVP_CIPHER_CTX_cleanup(key->dctx);
69     free(key->ectx);
70     free(key->dctx);
71 #else
72     EVP_CIPHER_CTX_free(key->ectx);
73     EVP_CIPHER_CTX_free(key->dctx);
74 #endif
75 }
76 
77 krb5_error_code
_krb5_evp_encrypt(krb5_context context,struct _krb5_key_data * key,void * data,size_t len,krb5_boolean encryptp,int usage,void * ivec)78 _krb5_evp_encrypt(krb5_context context,
79                     struct _krb5_key_data *key,
80                     void *data,
81                     size_t len,
82                     krb5_boolean encryptp,
83                     int usage,
84                     void *ivec)
85 {
86     struct _krb5_evp_schedule *ctx = key->schedule->data;
87     EVP_CIPHER_CTX *c;
88     c = encryptp ? ctx->ectx : ctx->dctx;
89     if (ivec == NULL) {
90           /* alloca ? */
91           size_t len2 = EVP_CIPHER_CTX_iv_length(c);
92           void *loiv = malloc(len2);
93           if (loiv == NULL)
94               return krb5_enomem(context);
95           memset(loiv, 0, len2);
96           if (!EVP_CipherInit_ex(c, NULL, NULL, NULL, loiv, -1))
97               krb5_abortx(context, "can't initialize cipher");
98           free(loiv);
99     } else if (!EVP_CipherInit_ex(c, NULL, NULL, NULL, ivec, -1))
100           krb5_abortx(context, "can't initialize cipher");
101 
102     EVP_Cipher(c, data, data, len);
103     return 0;
104 }
105 
106 static const unsigned char zero_ivec[EVP_MAX_BLOCK_LENGTH] = { 0 };
107 
108 krb5_error_code
_krb5_evp_encrypt_cts(krb5_context context,struct _krb5_key_data * key,void * data,size_t len,krb5_boolean encryptp,int usage,void * ivec)109 _krb5_evp_encrypt_cts(krb5_context context,
110                           struct _krb5_key_data *key,
111                           void *data,
112                           size_t len,
113                           krb5_boolean encryptp,
114                           int usage,
115                           void *ivec)
116 {
117     size_t i, blocksize;
118     int ret;
119     struct _krb5_evp_schedule *ctx = key->schedule->data;
120     unsigned char tmp[EVP_MAX_BLOCK_LENGTH], ivec2[EVP_MAX_BLOCK_LENGTH];
121     EVP_CIPHER_CTX *c;
122     unsigned char *p;
123 
124     c = encryptp ? ctx->ectx : ctx->dctx;
125 
126     blocksize = EVP_CIPHER_CTX_block_size(c);
127 
128     if (len < blocksize) {
129           krb5_set_error_message(context, EINVAL,
130                                      "message block too short");
131           return EINVAL;
132     } else if (len == blocksize) {
133           if (!EVP_CipherInit_ex(c, NULL, NULL, NULL, zero_ivec, -1))
134               krb5_abortx(context, "can't initialize cipher");
135           EVP_Cipher(c, data, data, len);
136           return 0;
137     }
138 
139     if (ivec)
140           ret = EVP_CipherInit_ex(c, NULL, NULL, NULL, ivec, -1);
141     else
142           ret = EVP_CipherInit_ex(c, NULL, NULL, NULL, zero_ivec, -1);
143     if (!ret)
144           krb5_abortx(context, "can't initialize cipher");
145 
146     if (encryptp) {
147 
148           p = data;
149           i = ((len - 1) / blocksize) * blocksize;
150           EVP_Cipher(c, p, p, i);
151           p += i - blocksize;
152           len -= i;
153           memcpy(ivec2, p, blocksize);
154 
155           for (i = 0; i < len; i++)
156               tmp[i] = p[i + blocksize] ^ ivec2[i];
157           for (; i < blocksize; i++)
158               tmp[i] = 0 ^ ivec2[i];
159 
160           if (!EVP_CipherInit_ex(c, NULL, NULL, NULL, zero_ivec, -1))
161               krb5_abortx(context, "can't initialize cipher");
162           EVP_Cipher(c, p, tmp, blocksize);
163 
164           memcpy(p + blocksize, ivec2, len);
165           if (ivec)
166               memcpy(ivec, p, blocksize);
167     } else {
168           unsigned char tmp2[EVP_MAX_BLOCK_LENGTH], tmp3[EVP_MAX_BLOCK_LENGTH];
169 
170           p = data;
171           if (len > blocksize * 2) {
172               /* remove last two blocks and round up, decrypt this with cbc, then do cts dance */
173               i = ((((len - blocksize * 2) + blocksize - 1) / blocksize) * blocksize);
174               memcpy(ivec2, p + i - blocksize, blocksize);
175               EVP_Cipher(c, p, p, i);
176               p += i;
177               len -= i + blocksize;
178           } else {
179               if (ivec)
180                     memcpy(ivec2, ivec, blocksize);
181               else
182                     memcpy(ivec2, zero_ivec, blocksize);
183               len -= blocksize;
184           }
185 
186           memcpy(tmp, p, blocksize);
187           if (!EVP_CipherInit_ex(c, NULL, NULL, NULL, zero_ivec, -1))
188               krb5_abortx(context, "can't initialize cipher");
189           EVP_Cipher(c, tmp2, p, blocksize);
190 
191           memcpy(tmp3, p + blocksize, len);
192           memcpy(tmp3 + len, tmp2 + len, blocksize - len); /* xor 0 */
193 
194           for (i = 0; i < len; i++)
195               p[i + blocksize] = tmp2[i] ^ tmp3[i];
196 
197           if (!EVP_CipherInit_ex(c, NULL, NULL, NULL, zero_ivec, -1))
198               krb5_abortx(context, "can't initialize cipher");
199           EVP_Cipher(c, p, tmp3, blocksize);
200 
201           for (i = 0; i < blocksize; i++)
202               p[i] ^= ivec2[i];
203           if (ivec)
204               memcpy(ivec, tmp, blocksize);
205     }
206     return 0;
207 }
208