1 /*
2  * Copyright (c) 2000, 2001, 2002, 2005 X-Way Rights BV
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17  *
18  */
19 
20 /*!\file dhies.c
21  * \brief DHIES encryption scheme.
22  * \author Bob Deblier <bob.deblier@telenet.be>
23  * \ingroup DL_m DL_dh_m
24  */
25 
26 #define BEECRYPT_DLL_EXPORT
27 
28 #if HAVE_CONFIG_H
29 # include "config.h"
30 #endif
31 
32 #include "beecrypt/dhies.h"
33 #include "beecrypt/dlsvdp-dh.h"
34 #include "beecrypt/blockmode.h"
35 #include "beecrypt/blockpad.h"
36 
37 /*
38  * Good combinations will be:
39  *
40  * For 128-bit encryption:
41  *  DHIES(SHA-256,AES,HMAC-SHA-256)
42  *  DHIES(SHA-256,Blowfish,HMAC-SHA-256)
43  *
44  * For 192-bit encryption:
45  *  DHIES(SHA-384,AES,HMAC-SHA-384)
46  *  DHIES(SHA-384,Blowfish,HMAC-SHA-384)
47  *
48  * For 256-bit encryption:
49  *  DHIES(SHA-512,AES,HMAC-SHA-512)
50  *  DHIES(SHA-512,Blowfish,HMAC-SHA-512)
51  *
52  */
53 
dhies_pUsable(const dhies_pParameters * params)54 int dhies_pUsable(const dhies_pParameters* params)
55 {
56 	size_t keybits = (params->hash->digestsize << 3); /* digestsize in bytes times 8 bits */
57 	size_t cipherkeybits = params->cipherkeybits;
58 	size_t mackeybits = params->mackeybits;
59 
60 	/* test if keybits is a multiple of 32 */
61 	if ((keybits & 31) != 0)
62 		return 0;
63 
64 	/* test if cipherkeybits + mackeybits < keybits */
65 	if ((cipherkeybits + mackeybits) > keybits)
66 		return 0;
67 
68 	if (mackeybits == 0)
69 	{
70 		if (cipherkeybits == 0)
71 			cipherkeybits = mackeybits = (keybits >> 1);
72 		else
73 			mackeybits = keybits - cipherkeybits;
74 	}
75 
76 	/* test if keybits length is appropriate for cipher */
77 	if ((cipherkeybits < params->cipher->keybitsmin) ||
78 			(cipherkeybits > params->cipher->keybitsmax))
79 		return 0;
80 
81 	if (((cipherkeybits - params->cipher->keybitsmin) % params->cipher->keybitsinc) != 0)
82 		return 0;
83 
84 	/* test if keybits length is appropriate for mac */
85 	if ((mackeybits < params->mac->keybitsmin) ||
86 			(params->mackeybits > params->mac->keybitsmax))
87 		return 0;
88 
89 	if (((mackeybits - params->mac->keybitsmin) % params->mac->keybitsinc) != 0)
90 		return 0;
91 
92 	return 1;
93 }
94 
dhies_pContextInit(dhies_pContext * ctxt,const dhies_pParameters * params)95 int dhies_pContextInit(dhies_pContext* ctxt, const dhies_pParameters* params)
96 {
97 	if (ctxt == (dhies_pContext*) 0)
98 		return -1;
99 
100 	if (params == (dhies_pParameters*) 0)
101 		return -1;
102 
103 	if (params->param == (dldp_p*) 0)
104 		return -1;
105 
106 	if (params->hash == (hashFunction*) 0)
107 		return -1;
108 
109 	if (params->cipher == (blockCipher*) 0)
110 		return -1;
111 
112 	if (params->mac == (keyedHashFunction*) 0)
113 		return -1;
114 
115 	if (!dhies_pUsable(params))
116 		return -1;
117 
118 	dldp_pInit(&ctxt->param);
119 	dldp_pCopy(&ctxt->param, params->param);
120 
121 	mpnzero(&ctxt->pub);
122 	mpnzero(&ctxt->pri);
123 
124 	if (hashFunctionContextInit(&ctxt->hash, params->hash))
125 		return -1;
126 
127 	if (blockCipherContextInit(&ctxt->cipher, params->cipher))
128 		return -1;
129 
130 	if (keyedHashFunctionContextInit(&ctxt->mac, params->mac))
131 		return -1;
132 
133 	ctxt->cipherkeybits = params->cipherkeybits;
134 	ctxt->mackeybits = params->mackeybits;
135 
136 	return 0;
137 }
138 
dhies_pContextInitDecrypt(dhies_pContext * ctxt,const dhies_pParameters * params,const mpnumber * pri)139 int dhies_pContextInitDecrypt(dhies_pContext* ctxt, const dhies_pParameters* params, const mpnumber* pri)
140 {
141 	if (dhies_pContextInit(ctxt, params))
142 		return -1;
143 
144 	mpncopy(&ctxt->pri, pri);
145 
146 	return 0;
147 }
148 
dhies_pContextInitEncrypt(dhies_pContext * ctxt,const dhies_pParameters * params,const mpnumber * pub)149 int dhies_pContextInitEncrypt(dhies_pContext* ctxt, const dhies_pParameters* params, const mpnumber* pub)
150 {
151 	if (dhies_pContextInit(ctxt, params))
152 		return -1;
153 
154 	mpncopy(&ctxt->pub, pub);
155 
156 	return 0;
157 }
158 
dhies_pContextFree(dhies_pContext * ctxt)159 int dhies_pContextFree(dhies_pContext* ctxt)
160 {
161 	dldp_pFree(&ctxt->param);
162 
163 	mpnfree(&ctxt->pub);
164 	mpnfree(&ctxt->pri);
165 
166 	if (hashFunctionContextFree(&ctxt->hash))
167 		return -1;
168 
169 	if (blockCipherContextFree(&ctxt->cipher))
170 		return -1;
171 
172 	if (keyedHashFunctionContextFree(&ctxt->mac))
173 		return -1;
174 
175 	return 0;
176 }
177 
dhies_pContextSetup(dhies_pContext * ctxt,const mpnumber * private,const mpnumber * public,const mpnumber * message,cipherOperation op)178 static int dhies_pContextSetup(dhies_pContext* ctxt, const mpnumber* private, const mpnumber* public, const mpnumber* message, cipherOperation op)
179 {
180 	register int rc;
181 
182 	mpnumber secret;
183 
184 	byte* digest = (byte*) malloc(ctxt->hash.algo->digestsize);
185 
186 	if (digest == (byte*) 0)
187 		return -1;
188 
189 	/* compute the shared secret, Diffie-Hellman style */
190 	mpnzero(&secret);
191 	if (dlsvdp_pDHSecret(&ctxt->param, private, public, &secret))
192 	{
193 		mpnfree(&secret);
194 		free(digest);
195 		return -1;
196 	}
197 
198 	/* compute the hash of the message (ephemeral public) key and the shared secret */
199 
200 	hashFunctionContextReset   (&ctxt->hash);
201 	hashFunctionContextUpdateMP(&ctxt->hash, message);
202 	hashFunctionContextUpdateMP(&ctxt->hash, &secret);
203 	hashFunctionContextDigest  (&ctxt->hash, digest);
204 
205 	/* we don't need the secret anymore */
206 	mpnwipe(&secret);
207 	mpnfree(&secret);
208 
209 	/*
210 	 * NOTE: blockciphers and keyed hash functions take keys with sizes
211 	 * specified in bits and key data passed in bytes.
212 	 *
213 	 * Both blockcipher and keyed hash function have a min and max key size.
214 	 *
215 	 * This function will split the digest of the shared secret in two halves,
216 	 * and pad with zero bits or truncate if necessary to meet algorithm key
217 	 * size requirements.
218 	 */
219 
220 	if (ctxt->hash.algo->digestsize > 0)
221 	{
222 		byte* mackey = digest;
223 		byte* cipherkey = digest + ((ctxt->mackeybits + 7) >> 3);
224 
225 		if ((rc = keyedHashFunctionContextSetup(&ctxt->mac, mackey, ctxt->mackeybits)))
226 			goto setup_end;
227 
228 		if ((rc = blockCipherContextSetup(&ctxt->cipher, cipherkey, ctxt->cipherkeybits, op)))
229 			goto setup_end;
230 
231 		rc = 0;
232 	}
233 	else
234 		rc = -1;
235 
236 setup_end:
237 	/* wipe digest for good measure */
238 	memset(digest, 0, ctxt->hash.algo->digestsize);
239 	free(digest);
240 
241 	return rc;
242 }
243 
dhies_pContextEncrypt(dhies_pContext * ctxt,mpnumber * ephemeralPublicKey,mpnumber * mac,const memchunk * cleartext,randomGeneratorContext * rng)244 memchunk* dhies_pContextEncrypt(dhies_pContext* ctxt, mpnumber* ephemeralPublicKey, mpnumber* mac, const memchunk* cleartext, randomGeneratorContext* rng)
245 {
246 	memchunk* ciphertext = (memchunk*) 0;
247 	memchunk* paddedtext;
248 
249 	mpnumber ephemeralPrivateKey;
250 
251 	/* make the ephemeral keypair */
252 	mpnzero(&ephemeralPrivateKey);
253 	dldp_pPair(&ctxt->param, rng, &ephemeralPrivateKey, ephemeralPublicKey);
254 
255 	/* Setup the key and initialize the mac and the blockcipher */
256 	if (dhies_pContextSetup(ctxt, &ephemeralPrivateKey, &ctxt->pub, ephemeralPublicKey, ENCRYPT))
257 		goto encrypt_end;
258 
259 	/* add pkcs-5 padding */
260 	paddedtext = pkcs5PadCopy(ctxt->cipher.algo->blocksize, cleartext);
261 
262 	/* encrypt the memchunk in CBC mode */
263 	if (blockEncryptCBC(ctxt->cipher.algo, ctxt->cipher.param, (uint32_t*) paddedtext->data, (const uint32_t*) paddedtext->data, paddedtext->size / ctxt->cipher.algo->blocksize))
264 	{
265 		free(paddedtext->data);
266 		free(paddedtext);
267 		goto encrypt_end;
268 	}
269 
270 	/* Compute the mac */
271 	if (keyedHashFunctionContextUpdateMC(&ctxt->mac, paddedtext))
272 	{
273 		free(paddedtext->data);
274 		free(paddedtext);
275 		goto encrypt_end;
276 	}
277 
278 	if (keyedHashFunctionContextDigestMP(&ctxt->mac, mac))
279 	{
280 		free(paddedtext->data);
281 		free(paddedtext);
282 		goto encrypt_end;
283 	}
284 
285 	ciphertext = paddedtext;
286 
287 encrypt_end:
288 	mpnwipe(&ephemeralPrivateKey);
289 	mpnfree(&ephemeralPrivateKey);
290 
291 	return ciphertext;
292 }
293 
dhies_pContextDecrypt(dhies_pContext * ctxt,const mpnumber * ephemeralPublicKey,const mpnumber * mac,const memchunk * ciphertext)294 memchunk* dhies_pContextDecrypt(dhies_pContext* ctxt, const mpnumber* ephemeralPublicKey, const mpnumber* mac, const memchunk* ciphertext)
295 {
296 	memchunk* cleartext = (memchunk*) 0;
297 	memchunk* paddedtext;
298 
299 	/* Setup the key and initialize the mac and the blockcipher */
300 	if (dhies_pContextSetup(ctxt, &ctxt->pri, ephemeralPublicKey, ephemeralPublicKey, DECRYPT))
301 		goto decrypt_end;
302 
303 	/* Verify the mac */
304 	if (keyedHashFunctionContextUpdateMC(&ctxt->mac, ciphertext))
305 		goto decrypt_end;
306 
307 	if (keyedHashFunctionContextDigestMatch(&ctxt->mac, mac) == 0)
308 		goto decrypt_end;
309 
310 	/* decrypt the memchunk with CBC mode */
311 	paddedtext = (memchunk*) calloc(1, sizeof(memchunk));
312 
313 	if (paddedtext == (memchunk*) 0)
314 		goto decrypt_end;
315 
316 	paddedtext->size = ciphertext->size;
317 	paddedtext->data = (byte*) malloc(ciphertext->size);
318 
319 	if (paddedtext->data == (byte*) 0)
320 	{
321 		free(paddedtext);
322 		goto decrypt_end;
323 	}
324 
325 	if (blockDecryptCBC(ctxt->cipher.algo, ctxt->cipher.param, (uint32_t*) paddedtext->data, (const uint32_t*) ciphertext->data, paddedtext->size / ctxt->cipher.algo->blocksize))
326 	{
327 		free(paddedtext->data);
328 		free(paddedtext);
329 		goto decrypt_end;
330 	}
331 
332 	/* remove pkcs-5 padding */
333 	cleartext = pkcs5Unpad(ctxt->cipher.algo->blocksize, paddedtext);
334 
335 	if (cleartext == (memchunk*) 0)
336 	{
337 		free(paddedtext->data);
338 		free(paddedtext);
339 	}
340 
341 decrypt_end:
342 
343 	return cleartext;
344 }
345