1226031Sstas/*
2226031Sstas * Copyright (c) 1997 - 2008 Kungliga Tekniska H��gskolan
3226031Sstas * (Royal Institute of Technology, Stockholm, Sweden).
4226031Sstas * All rights reserved.
5226031Sstas *
6226031Sstas * Redistribution and use in source and binary forms, with or without
7226031Sstas * modification, are permitted provided that the following conditions
8226031Sstas * are met:
9226031Sstas *
10226031Sstas * 1. Redistributions of source code must retain the above copyright
11226031Sstas *    notice, this list of conditions and the following disclaimer.
12226031Sstas *
13226031Sstas * 2. Redistributions in binary form must reproduce the above copyright
14226031Sstas *    notice, this list of conditions and the following disclaimer in the
15226031Sstas *    documentation and/or other materials provided with the distribution.
16226031Sstas *
17226031Sstas * 3. Neither the name of the Institute nor the names of its contributors
18226031Sstas *    may be used to endorse or promote products derived from this software
19226031Sstas *    without specific prior written permission.
20226031Sstas *
21226031Sstas * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22226031Sstas * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23226031Sstas * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24226031Sstas * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25226031Sstas * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26226031Sstas * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27226031Sstas * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28226031Sstas * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29226031Sstas * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30226031Sstas * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31226031Sstas * SUCH DAMAGE.
32226031Sstas */
33226031Sstas
34226031Sstas#include "krb5_locl.h"
35226031Sstas
36226031Sstasvoid
37226031Sstas_krb5_evp_schedule(krb5_context context,
38226031Sstas		   struct _krb5_key_type *kt,
39226031Sstas		   struct _krb5_key_data *kd)
40226031Sstas{
41226031Sstas    struct _krb5_evp_schedule *key = kd->schedule->data;
42226031Sstas    const EVP_CIPHER *c = (*kt->evp)();
43226031Sstas
44226031Sstas    EVP_CIPHER_CTX_init(&key->ectx);
45226031Sstas    EVP_CIPHER_CTX_init(&key->dctx);
46226031Sstas
47226031Sstas    EVP_CipherInit_ex(&key->ectx, c, NULL, kd->key->keyvalue.data, NULL, 1);
48226031Sstas    EVP_CipherInit_ex(&key->dctx, c, NULL, kd->key->keyvalue.data, NULL, 0);
49226031Sstas}
50226031Sstas
51226031Sstasvoid
52226031Sstas_krb5_evp_cleanup(krb5_context context, struct _krb5_key_data *kd)
53226031Sstas{
54226031Sstas    struct _krb5_evp_schedule *key = kd->schedule->data;
55226031Sstas    EVP_CIPHER_CTX_cleanup(&key->ectx);
56226031Sstas    EVP_CIPHER_CTX_cleanup(&key->dctx);
57226031Sstas}
58226031Sstas
59226031Sstaskrb5_error_code
60226031Sstas_krb5_evp_encrypt(krb5_context context,
61226031Sstas		struct _krb5_key_data *key,
62226031Sstas		void *data,
63226031Sstas		size_t len,
64226031Sstas		krb5_boolean encryptp,
65226031Sstas		int usage,
66226031Sstas		void *ivec)
67226031Sstas{
68226031Sstas    struct _krb5_evp_schedule *ctx = key->schedule->data;
69226031Sstas    EVP_CIPHER_CTX *c;
70226031Sstas    c = encryptp ? &ctx->ectx : &ctx->dctx;
71226031Sstas    if (ivec == NULL) {
72226031Sstas	/* alloca ? */
73226031Sstas	size_t len2 = EVP_CIPHER_CTX_iv_length(c);
74226031Sstas	void *loiv = malloc(len2);
75226031Sstas	if (loiv == NULL) {
76226031Sstas	    krb5_clear_error_message(context);
77226031Sstas	    return ENOMEM;
78226031Sstas	}
79226031Sstas	memset(loiv, 0, len2);
80226031Sstas	EVP_CipherInit_ex(c, NULL, NULL, NULL, loiv, -1);
81226031Sstas	free(loiv);
82226031Sstas    } else
83226031Sstas	EVP_CipherInit_ex(c, NULL, NULL, NULL, ivec, -1);
84226031Sstas    EVP_Cipher(c, data, data, len);
85226031Sstas    return 0;
86226031Sstas}
87226031Sstas
88226031Sstasstatic const unsigned char zero_ivec[EVP_MAX_BLOCK_LENGTH] = { 0 };
89226031Sstas
90226031Sstaskrb5_error_code
91226031Sstas_krb5_evp_encrypt_cts(krb5_context context,
92226031Sstas		      struct _krb5_key_data *key,
93226031Sstas		      void *data,
94226031Sstas		      size_t len,
95226031Sstas		      krb5_boolean encryptp,
96226031Sstas		      int usage,
97226031Sstas		      void *ivec)
98226031Sstas{
99226031Sstas    size_t i, blocksize;
100226031Sstas    struct _krb5_evp_schedule *ctx = key->schedule->data;
101226031Sstas    unsigned char tmp[EVP_MAX_BLOCK_LENGTH], ivec2[EVP_MAX_BLOCK_LENGTH];
102226031Sstas    EVP_CIPHER_CTX *c;
103226031Sstas    unsigned char *p;
104226031Sstas
105226031Sstas    c = encryptp ? &ctx->ectx : &ctx->dctx;
106226031Sstas
107226031Sstas    blocksize = EVP_CIPHER_CTX_block_size(c);
108226031Sstas
109226031Sstas    if (len < blocksize) {
110226031Sstas	krb5_set_error_message(context, EINVAL,
111226031Sstas			       "message block too short");
112226031Sstas	return EINVAL;
113226031Sstas    } else if (len == blocksize) {
114226031Sstas	EVP_CipherInit_ex(c, NULL, NULL, NULL, zero_ivec, -1);
115226031Sstas	EVP_Cipher(c, data, data, len);
116226031Sstas	return 0;
117226031Sstas    }
118226031Sstas
119226031Sstas    if (ivec)
120226031Sstas	EVP_CipherInit_ex(c, NULL, NULL, NULL, ivec, -1);
121226031Sstas    else
122226031Sstas	EVP_CipherInit_ex(c, NULL, NULL, NULL, zero_ivec, -1);
123226031Sstas
124226031Sstas    if (encryptp) {
125226031Sstas
126226031Sstas	p = data;
127226031Sstas	i = ((len - 1) / blocksize) * blocksize;
128226031Sstas	EVP_Cipher(c, p, p, i);
129226031Sstas	p += i - blocksize;
130226031Sstas	len -= i;
131226031Sstas	memcpy(ivec2, p, blocksize);
132226031Sstas
133226031Sstas	for (i = 0; i < len; i++)
134226031Sstas	    tmp[i] = p[i + blocksize] ^ ivec2[i];
135226031Sstas	for (; i < blocksize; i++)
136226031Sstas	    tmp[i] = 0 ^ ivec2[i];
137226031Sstas
138226031Sstas	EVP_CipherInit_ex(c, NULL, NULL, NULL, zero_ivec, -1);
139226031Sstas	EVP_Cipher(c, p, tmp, blocksize);
140226031Sstas
141226031Sstas	memcpy(p + blocksize, ivec2, len);
142226031Sstas	if (ivec)
143226031Sstas	    memcpy(ivec, p, blocksize);
144226031Sstas    } else {
145226031Sstas	unsigned char tmp2[EVP_MAX_BLOCK_LENGTH], tmp3[EVP_MAX_BLOCK_LENGTH];
146226031Sstas
147226031Sstas	p = data;
148226031Sstas	if (len > blocksize * 2) {
149226031Sstas	    /* remove last two blocks and round up, decrypt this with cbc, then do cts dance */
150226031Sstas	    i = ((((len - blocksize * 2) + blocksize - 1) / blocksize) * blocksize);
151226031Sstas	    memcpy(ivec2, p + i - blocksize, blocksize);
152226031Sstas	    EVP_Cipher(c, p, p, i);
153226031Sstas	    p += i;
154226031Sstas	    len -= i + blocksize;
155226031Sstas	} else {
156226031Sstas	    if (ivec)
157226031Sstas		memcpy(ivec2, ivec, blocksize);
158226031Sstas	    else
159226031Sstas		memcpy(ivec2, zero_ivec, blocksize);
160226031Sstas	    len -= blocksize;
161226031Sstas	}
162226031Sstas
163226031Sstas	memcpy(tmp, p, blocksize);
164226031Sstas	EVP_CipherInit_ex(c, NULL, NULL, NULL, zero_ivec, -1);
165226031Sstas	EVP_Cipher(c, tmp2, p, blocksize);
166226031Sstas
167226031Sstas	memcpy(tmp3, p + blocksize, len);
168226031Sstas	memcpy(tmp3 + len, tmp2 + len, blocksize - len); /* xor 0 */
169226031Sstas
170226031Sstas	for (i = 0; i < len; i++)
171226031Sstas	    p[i + blocksize] = tmp2[i] ^ tmp3[i];
172226031Sstas
173226031Sstas	EVP_CipherInit_ex(c, NULL, NULL, NULL, zero_ivec, -1);
174226031Sstas	EVP_Cipher(c, p, tmp3, blocksize);
175226031Sstas
176226031Sstas	for (i = 0; i < blocksize; i++)
177226031Sstas	    p[i] ^= ivec2[i];
178226031Sstas	if (ivec)
179226031Sstas	    memcpy(ivec, tmp, blocksize);
180226031Sstas    }
181226031Sstas    return 0;
182226031Sstas}
183