/** \ingroup DH_m * \file dhaes.c * * DHAES, code. * * This code implements the encryption scheme from the paper: * * "DHAES: An Encryption Scheme Based on the Diffie-Hellman Problem" * Michel Abdalla, Mihir Bellare, Phillip Rogaway * September 1998 * * Good combinations will be: * * - For 64-bit encryption: * - DHAES(MD5, Blowfish, HMAC-MD5) <- best candidate * - DHAES(MD5, Blowfish, HMAC-SHA-1) * - DHAES(MD5, Blowfish, HMAC-SHA-256) * * - For 96-bit encryption with 64-bit mac: * - DHAES(SHA-1, Blowfish, HMAC-MD5, 96) * - DHAES(SHA-1, Blowfish, HMAC-SHA-1, 96) <- best candidate * - DHAES(SHA-1, Blowfish, HMAC-SHA-256, 96) <- best candidate * * - For 128-bit encryption: * - DHAES(SHA-256, Blowfish, HMAC-MD5) * - DHAES(SHA-256, Blowfish, HMAC-SHA-1) * - DHAES(SHA-256, Blowfish, HMAC-SHA-256) */ /* * Copyright (c) 2000, 2001 Virtual Unlimited, B.V. * * Author: Bob Deblier * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #define BEECRYPT_DLL_EXPORT #include "dhaes.h" #include "dlsvdp-dh.h" #include "blockmode.h" #include "blockpad.h" #if HAVE_STDLIB_H # include #endif #if HAVE_MALLOC_H # include #endif int dhaes_pUsable(const dhaes_pParameters* params) { int keybits = (params->hash->digestsize << 3); /* digestsize in bytes times 8 bits */ int cipherkeybits = params->cipherkeybits; int mackeybits = params->mackeybits; /* test if keybits is a multiple of 32 */ if ((keybits & 31) != 0) return 0; /* test if cipherkeybits + mackeybits < keybits */ if ((cipherkeybits + mackeybits) > keybits) return 0; if (mackeybits == 0) { if (cipherkeybits == 0) cipherkeybits = mackeybits = (((uint32)keybits) >> 1); else mackeybits = keybits - cipherkeybits; } /* test if keybits length is appropriate for cipher */ if ((cipherkeybits < params->cipher->keybitsmin) || (cipherkeybits > params->cipher->keybitsmax)) return 0; if (((cipherkeybits - params->cipher->keybitsmin) % params->cipher->keybitsinc) != 0) return 0; /* test if keybits length is appropriate for mac */ if ((mackeybits < params->mac->keybitsmin) || (params->mackeybits > params->mac->keybitsmax)) return 0; if (((mackeybits - params->mac->keybitsmin) % params->mac->keybitsinc) != 0) return 0; return 1; } int dhaes_pContextInit(dhaes_pContext* ctxt, const dhaes_pParameters* params) { if (ctxt == (dhaes_pContext*) 0) return -1; if (params == (dhaes_pParameters*) 0) return -1; if (params->param == (dldp_p*) 0) return -1; if (params->hash == (hashFunction*) 0) return -1; if (params->cipher == (blockCipher*) 0) return -1; if (params->mac == (keyedHashFunction*) 0) return -1; if (!dhaes_pUsable(params)) return -1; (void) dldp_pInit(&ctxt->param); (void) dldp_pCopy(&ctxt->param, params->param); mp32nzero(&ctxt->pub); mp32nzero(&ctxt->pri); /*@-modobserver@*/ if (hashFunctionContextInit(&ctxt->hash, params->hash)) return -1; if (blockCipherContextInit(&ctxt->cipher, params->cipher)) return -1; if (keyedHashFunctionContextInit(&ctxt->mac, params->mac)) return -1; /*@=modobserver@*/ ctxt->cipherkeybits = params->cipherkeybits; ctxt->mackeybits = params->mackeybits; return 0; } int dhaes_pContextInitDecrypt(dhaes_pContext* ctxt, const dhaes_pParameters* params, const mp32number* pri) { if (dhaes_pContextInit(ctxt, params)) return -1; mp32ncopy(&ctxt->pri, pri); return 0; } int dhaes_pContextInitEncrypt(dhaes_pContext* ctxt, const dhaes_pParameters* params, const mp32number* pub) { if (dhaes_pContextInit(ctxt, params)) return -1; mp32ncopy(&ctxt->pub, pub); return 0; } int dhaes_pContextFree(dhaes_pContext* ctxt) { (void) dldp_pFree(&ctxt->param); mp32nfree(&ctxt->pub); mp32nfree(&ctxt->pri); /*@-mustfree -modobserver @*/ /* ctxt is OK */ if (hashFunctionContextFree(&ctxt->hash)) return -1; if (blockCipherContextFree(&ctxt->cipher)) return -1; if (keyedHashFunctionContextFree(&ctxt->mac)) return -1; return 0; /*@=mustfree =modobserver @*/ } /** */ static int dhaes_pContextSetup(dhaes_pContext* ctxt, const mp32number* privkey, const mp32number* pubkey, const mp32number* message, cipherOperation op) /*@modifies ctxt @*/ { register int rc; mp32number secret; mp32number digest; /* compute the shared secret, Diffie-Hellman style */ mp32nzero(&secret); if (dlsvdp_pDHSecret(&ctxt->param, privkey, pubkey, &secret)) /*@-mustfree@*/ /* FIX: secret.data leak? */ return -1; /*@=mustfree@*/ /* compute the hash of the message (ephemeral public) key and the shared secret */ mp32nzero(&digest); (void) hashFunctionContextReset (&ctxt->hash); (void) hashFunctionContextUpdateMP32(&ctxt->hash, message); (void) hashFunctionContextUpdateMP32(&ctxt->hash, &secret); (void) hashFunctionContextDigest (&ctxt->hash, &digest); /* we don't need the secret anymore */ mp32nwipe(&secret); mp32nfree(&secret); /** * NOTE: blockciphers and keyed hash functions take keys with sizes * specified in bits and key data passed in 32-bit words. * * Both blockcipher and keyed hash function have a min and max key size. * * This function will split the digest of the shared secret in two halves, * and pad with zero bits or truncate if necessary to meet algorithm key * size requirements. */ /*@-usedef@*/ /* LCL: digest already set */ if (digest.size > 0) { uint32* mackey = digest.data; uint32* cipherkey = digest.data + (((uint32)(ctxt->mackeybits + 31)) >> 5); if ((rc = keyedHashFunctionContextSetup(&ctxt->mac, mackey, ctxt->mackeybits))) goto setup_end; if ((rc = blockCipherContextSetup(&ctxt->cipher, cipherkey, ctxt->cipherkeybits, op))) goto setup_end; rc = 0; } else rc = -1; /*@=usedef@*/ setup_end: mp32nwipe(&digest); mp32nfree(&digest); /*@-mustfree@*/ /* {secret,digest}.data are OK */ return rc; /*@=mustfree@*/ } memchunk* dhaes_pContextEncrypt(dhaes_pContext* ctxt, mp32number* ephemeralPublicKey, mp32number* mac, const memchunk* cleartext, randomGeneratorContext* rng) { memchunk* ciphertext = (memchunk*) 0; memchunk* paddedtext; mp32number ephemeralPrivateKey; /* make the ephemeral keypair */ mp32nzero(&ephemeralPrivateKey); (void) dldp_pPair(&ctxt->param, rng, &ephemeralPrivateKey, ephemeralPublicKey); /* Setup the key and initialize the mac and the blockcipher */ if (dhaes_pContextSetup(ctxt, &ephemeralPrivateKey, &ctxt->pub, ephemeralPublicKey, ENCRYPT)) goto encrypt_end; /* add pkcs-5 padding */ paddedtext = pkcs5PadCopy(ctxt->cipher.algo->blocksize, cleartext); if (paddedtext == (memchunk*) 0) goto encrypt_end; /* encrypt the memchunk in CBC mode */ if (blockEncrypt(ctxt->cipher.algo, ctxt->cipher.param, CBC, paddedtext->size / ctxt->cipher.algo->blocksize, (uint32*) paddedtext->data, (const uint32*) paddedtext->data)) { free(paddedtext->data); free(paddedtext); goto encrypt_end; } /* Compute the mac */ if (keyedHashFunctionContextUpdateMC(&ctxt->mac, paddedtext)) { free(paddedtext->data); free(paddedtext); goto encrypt_end; } if (keyedHashFunctionContextDigest(&ctxt->mac, mac)) { free(paddedtext->data); free(paddedtext); goto encrypt_end; } ciphertext = paddedtext; encrypt_end: mp32nwipe(&ephemeralPrivateKey); mp32nfree(&ephemeralPrivateKey); /*@-mustfree@*/ /* ephemeralPrivateKey.data is OK */ return ciphertext; /*@=mustfree@*/ } memchunk* dhaes_pContextDecrypt(dhaes_pContext* ctxt, const mp32number* ephemeralPublicKey, const mp32number* mac, const memchunk* ciphertext) { memchunk* cleartext = (memchunk*) 0; memchunk* paddedtext; /* Setup the key and initialize the mac and the blockcipher */ if (dhaes_pContextSetup(ctxt, &ctxt->pri, ephemeralPublicKey, ephemeralPublicKey, DECRYPT)) goto decrypt_end; /* Verify the mac */ if (keyedHashFunctionContextUpdateMC(&ctxt->mac, ciphertext)) goto decrypt_end; if (keyedHashFunctionContextDigestMatch(&ctxt->mac, mac) == 0) goto decrypt_end; /* decrypt the memchunk with CBC mode */ /*@-sizeoftype@*/ paddedtext = (memchunk*) calloc(1, sizeof(memchunk)); /*@=sizeoftype@*/ if (paddedtext == (memchunk*) 0) goto decrypt_end; paddedtext->size = ciphertext->size; /*@-mustfree@*/ /* paddedtext->data is OK */ paddedtext->data = (byte*) malloc(ciphertext->size); /*@=mustfree@*/ if (paddedtext->data == (byte*) 0) { free(paddedtext); goto decrypt_end; } if (blockDecrypt(ctxt->cipher.algo, ctxt->cipher.param, CBC, paddedtext->size / ctxt->cipher.algo->blocksize, (uint32*) paddedtext->data, (const uint32*) ciphertext->data)) { free(paddedtext->data); free(paddedtext); goto decrypt_end; } /* remove pkcs-5 padding */ cleartext = pkcs5Unpad(ctxt->cipher.algo->blocksize, paddedtext); if (cleartext == (memchunk*) 0) { free(paddedtext->data); free(paddedtext); } decrypt_end: /*@-usereleased@*/ /* LCL: cleartext released??? */ return cleartext; /*@=usereleased@*/ }