/* ** ** Krishna Puttaswamy: created on 29th May 2006 ** description: The collection of function to perform symmetric/asymmetric encryption/decryption for cashmere */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "mycrypto.h" #include "chimera.h" #include "cashmere.h" typedef unsigned long long u_llong; /* ** Return some random data from /dev/urandom. using urandom instead of random because random generates random data at a limited rate ** which might lead to blocking of this function */ void get_data_from_random_file (unsigned char *data, int length) { int i, j, fd; if ((fd = open ("/dev/urandom", O_RDONLY)) == -1) perror ("Not able to read the /dev/urandom file"); if ((read (fd, data, length)) == -1) perror ("Error reading data from the random file"); close (fd); } // print the contents in hex void print_in_hex (unsigned char *data, int length) { int i = 0; for (i = 0; i < length; i++) printf ("%2x", data[i]); printf ("\n"); } void init_ciphers() { static int invoke = 0; if (invoke == 0) { OpenSSL_add_all_ciphers (); invoke = 1; } } /* ** Encrypt the data of given len, using the given key and IV and return the data; store the length of the output in outlen ** #### The returned data should be freed by the caller */ unsigned char *symmetric_encrypt (unsigned char *data, int inputlen, unsigned char *key, int keylen, unsigned char *iv, int ivlen, int *outlen) { EVP_CIPHER_CTX ctx; const EVP_CIPHER *cipher_type; EVP_CIPHER_CTX_init (&ctx); init_ciphers(); cipher_type = EVP_get_cipherbyname (SYMMETRIC_CYPHER_TYPE); if (cipher_type == NULL) { fprintf (stderr, "Unknown cipher %s\n", SYMMETRIC_CYPHER_TYPE); return NULL; } if (EVP_EncryptInit_ex (&ctx, cipher_type, NULL, key, iv) == 0) { fprintf (stderr, "Not able to initialize encrypt_init \n"); return NULL; } int maxbuffersize = inputlen + EVP_CIPHER_CTX_block_size (&ctx); unsigned char *encrypt = (unsigned char *) malloc (maxbuffersize); int templen = 0; if (EVP_EncryptUpdate (&ctx, encrypt, outlen, data, inputlen) != 1) { fprintf (stderr, "encrypt: error in encrypt update\n"); return NULL; } if (EVP_EncryptFinal_ex (&ctx, encrypt + *outlen, &templen) != 1) { fprintf (stderr, "encrypt: error in encrypt final\n"); return NULL; } *outlen += templen; // fprintf (stderr, "The encrypted length is %d \n", *outlen); EVP_CIPHER_CTX_cleanup (&ctx); return encrypt; } /* ** Decrypt the data of given len, using the given key and IV and return the data; store the length of the output in outlen ** #### The returned data should be freed by the caller */ unsigned char *symmetric_decrypt (ChimeraState *state, unsigned char *data, int inputlen, unsigned char *key, int keylen, unsigned char *iv, int ivlen, int *outlen) { EVP_CIPHER_CTX ctx; const EVP_CIPHER *cipher_type; EVP_CIPHER_CTX_init (&ctx); init_ciphers(); cipher_type = EVP_get_cipherbyname (SYMMETRIC_CYPHER_TYPE); if (cipher_type == NULL) { fprintf (stderr, "Unknown cipher %s\n", SYMMETRIC_CYPHER_TYPE); log_message(state->log, LOG_CASHMERE, "Unknown cipher %s\n", SYMMETRIC_CYPHER_TYPE); return NULL; } EVP_DecryptInit_ex (&ctx, cipher_type, NULL, key, iv); int maxbuffersize = inputlen + EVP_CIPHER_CTX_block_size (&ctx); unsigned char *encrypt = (unsigned char *) malloc (maxbuffersize); int templen = 0; if (EVP_DecryptUpdate (&ctx, encrypt, outlen, data, inputlen) != 1) { fprintf (stderr, "encrypt: error in decrypt update\n"); log_message(state->log, LOG_CASHMERE, "encrypt: error in decrypt update\n"); return NULL; } if (EVP_DecryptFinal_ex (&ctx, encrypt + *outlen, &templen) != 1) { // no need of fprintf here log_message(state->log, LOG_CASHMERE, "encrypt: error in decrypt final\n"); return NULL; } *outlen += templen; //fprintf (stderr, "The decrypted length is %d \n", *outlen); EVP_CIPHER_CTX_cleanup (&ctx); return encrypt; } /* ** generate a symmetric key and iv of specified lenght and return them. ** memory need to be allocated before */ void generate_symmetric_key (unsigned char *key, int keylen, unsigned char *iv, int ivlen) { RAND_bytes (key, keylen); RAND_pseudo_bytes (iv, ivlen); } /**********************the functions below are for asymmetric encryption *********/ // read the private key from a pem formate file EVP_PKEY *ReadPrivateKey (const char *keyfile) { FILE *fp = fopen (keyfile, "r"); EVP_PKEY *pkey; if (!fp) return NULL; pkey = PEM_read_PrivateKey (fp, NULL, 0, NULL); fclose (fp); if (pkey == NULL) ERR_print_errors_fp (stderr); return pkey; } // read the public key from a pem format file EVP_PKEY *ReadPublicKey (const char *keyfile) { FILE *fp = fopen (keyfile, "r"); EVP_PKEY *pkey; if (!fp) return NULL; pkey = PEM_read_PUBKEY (fp, NULL, 0, NULL); fclose (fp); if (pkey == NULL) ERR_print_errors_fp (stderr); return pkey; } /* ** Encrypt the input data of given length using RAS public key in the given file. Encrypted data is returned. ** ######## the returned encrypted data pointer need to be freed by the caller */ unsigned char *rsa_encrypt (unsigned char *data, int inputlen, char *pubKeyFile, int *outlen) { // u_llong start = get_cyclecounter(); EVP_PKEY *pubKey; ERR_load_crypto_strings (); pubKey = ReadPublicKey (pubKeyFile); if (!pubKey) { EVP_PKEY_free (pubKey); fprintf (stderr, "error loading public key from file \n"); return NULL; } int keylen = EVP_PKEY_size (pubKey); int blocksize = keylen - 16; // for keys of 256 bytes long ... this is the largest block size int i = 0; unsigned char *encryptdata = (unsigned char *) malloc (((inputlen % blocksize) + 1) * keylen); *outlen = 0; // This is the loop that splits the input string into multiple blocks of size blocksize and then // encrypt each block using asymmetric encryption // This had to be done because there is no direct way to encrypt the entire block of data in one go :( for (i = 0; (i * blocksize) < inputlen; i++) { int toencrypt = blocksize; if ((i + 1) * blocksize > inputlen) toencrypt = inputlen - i * blocksize; unsigned char *temp = (unsigned char*) malloc (keylen); int templen = 0; templen = RSA_public_encrypt (toencrypt, (data + i * blocksize), temp, pubKey->pkey.rsa, RSA_PKCS1_PADDING); if (templen != keylen) { fprintf (stderr, "Error: ciphertext should match length of key\n"); return NULL; } memcpy (encryptdata + *outlen, temp, templen); *outlen += templen; } // u_llong end = get_cyclecounter(); // double millisecs = (end-start)*1000.00/get_cycles_per_second(); // fprintf(stderr, "rsa_encrypt: %f\n", millisecs); EVP_PKEY_free (pubKey); return encryptdata; } /* ** Decrypt the input data of given length using RAS private key in the given file. Decrypted data is returned. ** ######## the returned decrypted data pointer need to be freed by the caller */ unsigned char *rsa_decrypt (unsigned char *data, int inputlen, char *privKeyFile, int *outlen) { // u_llong start = get_cyclecounter(); EVP_PKEY *privKey; ERR_load_crypto_strings (); privKey = ReadPrivateKey (privKeyFile); if (!privKey) { EVP_PKEY_free (privKey); fprintf (stderr, "error loading rsa private key from file \n"); return NULL; } int keylen = EVP_PKEY_size (privKey); unsigned char *decryptdata = (unsigned char *) malloc (inputlen); *outlen = 0; int i = 0; unsigned char *temp = (unsigned char*) calloc (keylen, 1); // This loop will decode one block at a time and construct the original data for (i = 0; (i * keylen) < inputlen; i++) { int templen = 0; memset(temp, 0, keylen); templen = RSA_private_decrypt (keylen, data + i * keylen, temp, privKey->pkey.rsa, RSA_PKCS1_PADDING); if (templen == -1) // then this is not the guy who should decrypt { free(temp); EVP_PKEY_free (privKey); free (decryptdata); return NULL; } memcpy (decryptdata + *outlen, temp, templen); *outlen += templen; } // u_llong end = get_cyclecounter(); // double millisecs = (end-start)*1000.00/get_cycles_per_second(); // fprintf(stderr, "rsa_decrypt: %f\n", millisecs); free(temp); EVP_PKEY_free (privKey); return decryptdata; } /* int main() { unsigned char data[100], key[SYMMETRIC_KEY_LENGTH], iv[IV_LENGTH]; get_data_from_random_file(data, RAND_SEED_LENGTH); RAND_seed(data, RAND_SEED_LENGTH); if(RAND_status() == 0) printf("Didn't have enuf randomness \n"); generate_symmetric_key(key, SYMMETRIC_KEY_LENGTH, iv, IV_LENGTH); printf("The symmetric key is \n"); print_in_hex(key, SYMMETRIC_KEY_LENGTH); printf("The IV is \n"); print_in_hex(iv, IV_LENGTH); printf("Enter text to encrypt \n"); scanf("%s", data); int encryptedlen = 0, decryptedlen = 0; unsigned char *encryptedText = symmetric_encrypt(data, strlen((const char*)data) + 1, key, SYMMETRIC_KEY_LENGTH, iv, IV_LENGTH, &encryptedlen); unsigned char *decryptedText = symmetric_decrypt(encryptedText, encryptedlen, key, SYMMETRIC_KEY_LENGTH, iv, IV_LENGTH, &decryptedlen); printf("Original data is of len %d\n%s\n", strlen((char*) data), data); printf("Decrypted data is of len %d\n%s\n", strlen((char*) decryptedText), decryptedText); free(decryptedText); free(encryptedText); } */ /* this is to test the asymmetric encryption ... int main() { unsigned char data[1000], key[SYMMETRIC_KEY_LENGTH], iv[IV_LENGTH]; printf("Enter text to encrypt \n"); scanf("%s", data); //get_data_from_random_file(data, 1000); //printf("The random data is \n"); //print_in_hex(data, 1000); printf("\n"); int encryptedlen = 0, decryptedlen = 0; //unsigned char *encryptedText = rsa_encrypt(data, 1000, "pubkey1.pem", &encryptedlen); unsigned char *encryptedText = rsa_encrypt(data, strlen(data) + 1, "pubkey1.pem", &encryptedlen); unsigned char *decryptedText = rsa_decrypt(encryptedText, encryptedlen, "privkey1.pem", &decryptedlen); printf("The length of the encrypted data is %d \n", encryptedlen); printf("Original data is of len %d\n%s\n", strlen((char*) data), data); printf("Decrypted data is of len %d\n%s\n", strlen((char*) decryptedText), decryptedText); //printf("after decryption of length %d\n", decryptedlen); //print_in_hex(decryptedText, decryptedlen); //printf("\n"); free(decryptedText); free(encryptedText); } */