OpenVMS Source Code Demos

ssl_aes_api_demo01

//==============================================================================
// title   : SSL_AES_API_demo01.c
// author  : Neil Rieck	( https://neilrieck.net - mailto: [email protected] )
//	     Waterloo, Ontario, Canada.
// created : 2017-12-12
// source  : derived from SSL$EXAMPLES:SSL$AES.C (folder on OpenVMS-8.4)
//           copyrighted (c) 2004 Hewlett-Packard Development Company, L.P.
// platform: OpenVMS-8.4 (Alpha, Itanium)
// other   : 1) routines defined here are called from my VMS-BASIC demo program
//         : 2) routines defined here call OpenSSL routines found in these
//		run-time libraries.
//		1) sys$library:ssl$libcrypto_shr32.exe
//		2) sys$library:ssl$libssl_shr32.exe /share
// ver who when     what
// --- --- -------- ------------------------------------------------------------
// 1   NSR 20171212 1. original effort (encryption side only)
//		    2. removed optional support for DES (now is AES only)
//		    3. replaced macro "check_vms" with real code
//     NSR 20171215 4. coded the decryption side (hey, this is a spare time project)
//==============================================================================
#define __NEW_STARLET	1							// enable strict starlet (>= OpenVMS70)
#include "SSL$EXAMPLES:SSL_EXAMPLES.H"						//
#include "SSL$INCLUDE:EVP.H"							//
#include <string.h>								//
#include <descrip.h>								// for VMS string descriptors for C
#include <str$routines.h>							// for VMS string routines for C
#include "base64.h"								// downloaded from github (only for debug)
//
//	VMSIFY1
//      a macro for use in the VMS world (VMS strings employ this structure)
//	notes:	1. this macro can be used to create VMS strings in c space
//		2. the $DESCRIPTOR macro does something similar employing sizeof-1
//		3. this macro combines two operations
//		4. use str$copy_dx() to copy string data up to the calling program
//
#define VMSIFY1(a,b) {						\
    a.dsc$b_dtype = DSC$K_DTYPE_T;				\
    a.dsc$b_class = DSC$K_CLASS_S;				\
    a.dsc$w_length = strlen(b);					\
    a.dsc$a_pointer = (char *) malloc(strlen(b));		\
    strncpy(a.dsc$a_pointer,b,a.dsc$w_length);			\
}

//==============================================================================
//	AES Encryption
//==============================================================================
long NSR_AES_ENCRYPT(	struct dsc$descriptor_d *vms_dsc_plaintext	, // VMS string descriptors
			struct dsc$descriptor_d *vms_dsc_iv_hex		,
			struct dsc$descriptor_d *vms_dsc_key		,
			struct dsc$descriptor_d *vms_dsc_enc_hex	,
			struct dsc$descriptor_d *vms_dsc_enc_b64	,
			long			debug			) {
    //--------------------------------------------------------------------------
    int			status;
    unsigned char	iv[16]		= {0};
    unsigned char	key[32]		= {0};
    unsigned char	txtbuf[132]	= {0};
    unsigned char	encbuf[512]	= {0};	// minimum size = sizeof(txtbuf) + 256
    unsigned char *	plaintext_ptr	= NULL;
    int			plaintext_len	= 0;
    unsigned char * 	ciphertext_ptr	= NULL;
    int			ciphertext_len	= 0;
    int			outlen		= 0;
    char *		ptr7		= 0;					//
    char *		ptr9		= 0;					// debug use only
    int			b64_len		= 0;					// debug use only
    EVP_CIPHER_CTX	ctx;							// create
    EVP_CIPHER_CTX	*ctx_ptr	= &ctx;					// setup
    char hexstring[] =	"0123456789abcdef";					// only used by my dehex
    long		i,j,k,x,y;						// only used by my parse
    long		rc;							//
    //
    // The following six functions are obsolete but are retained for compatibility
    // with existing code:
    //
    //	EVP_EncryptInit(), EVP_EncryptFinal(),
    //	EVP_DecryptInit(), EVP_DecryptFinal(),
    //	EVP_CipherInit() and EVP_CipherFinal().
    //
    // New code should use the following new calls as they can reuse an
    // existing context without allocating and freeing it up on each call:
    //
    // 	EVP_EncryptInit_ex(), EVP_EncryptFinal_ex(),
    //	EVP_DecryptInit_ex(), EVP_DecryptFinal_ex(),
    //	EVP_CipherInit_ex() and EVP_CipherFinal_ex()
    //
    EVP_CIPHER_CTX_init( ctx_ptr );						// initialize
    //--------------------------------------------------------------------------
    //	real work begins
    //	(1) copy inbound key
    //	(2) copy inbound iv hex
    //	(3) copy inbound plaintext
    //--------------------------------------------------------------------------
    //
    //	(1) copy inbound key
    //
    if (debug>0)
	printf("step 1 (key)\n");
    strncat((char*) key, vms_dsc_key->dsc$a_pointer, vms_dsc_key->dsc$w_length);
    //
    //	(2) copy inbound iv_hex
    //
    if (debug>0)
	printf("step 2 (iv)\n");
    if (vms_dsc_iv_hex->dsc$w_length==0){					// if blank
	if (debug>0)
	    printf("step 2a (blank)\n");					//
	memset(&iv,0,sizeof(iv));						// superfluous?
	goto iv_hex_end;							// c-coders gasp...
    }
    if (debug>0)
	printf("step 2b (non-blank)\n");					//
    if (vms_dsc_iv_hex->dsc$w_length>(sizeof(iv)*2)) {				// cuz two hex characters become one binary byte
	printf("-e- iv is too long\n");
	return(2);
    }
    if ((vms_dsc_iv_hex->dsc$w_length % 2)!=0){
	printf("-e-iv is not an even length\n");
	return(2);
    }
    //
    //	convert hex pairs to binary byte
    //
    for (int i=0, j=0; i<vms_dsc_iv_hex->dsc$w_length; i=i+2, j++){
	int tens, ones;
	char* ptr;
	ptr = strchr(&hexstring[0],vms_dsc_iv_hex->dsc$a_pointer[i]);
	if (ptr==NULL){
	    printf("-e-encounted non-hex charcter in IV\n");
	    return(1);
	}
	tens = (ptr - hexstring) * 16;
	ptr = strchr(&hexstring[0],vms_dsc_iv_hex->dsc$a_pointer[i+1]);
	if (ptr==NULL){
	    printf("-e-encounted non-hex charcter in IV\n");
	    return(1);
	}
	ones = (ptr - hexstring);
	iv[j] = (char) tens + ones;
    }
    iv_hex_end:;
    //
    //	(3) copy inbound plaintext
    //
    if (debug>0)								//
	printf("step 3 (plaintext)\n");						//
    if (vms_dsc_plaintext->dsc$w_length>sizeof(txtbuf)){			//
	printf("-e-plain text is too long\n");					//
	return(2);								//
    }
    strncat((char*) txtbuf, vms_dsc_plaintext->dsc$a_pointer, sizeof(txtbuf));	//
    plaintext_ptr = txtbuf;							//
    plaintext_len = vms_dsc_plaintext->dsc$w_length;				//
    //
    //	finish setup
    //
    ciphertext_ptr = encbuf;							//
    ciphertext_len = sizeof (encbuf);						// superfluous?
    //
    //	init
    //
    if (debug>0)								//
	printf("step 4\n");							//
    status = EVP_EncryptInit_ex(&ctx			,
				EVP_aes_256_cbc()	,
				NULL			,
			      	key			,
				iv			);
    if (status!=1){								//
	printf("-e-EVP_EncryptInit_ex status: %ld",status);			//
	return(2);								//
    }										//
    //
    // Uncomment the following call (after initialization) to disable padding of data
    // blocks. But then YOU must ensure that plaintext is an exact multiple of an AES
    // 16 byte, 128 bit data block (8 bytes, 64 bits = DES), or else Encrypt + Decrypt
    // will fail.
    //
    //  EVP_CIPHER_CTX_set_padding( ctx_ptr , 0);
    //
    //	magic happens here
    //
    if (debug>0)								//
	printf("step 5\n");							//
    status = EVP_EncryptUpdate(	&ctx ,						//
				ciphertext_ptr , &outlen ,			//
				plaintext_ptr , plaintext_len );		//
    if (status!=1){								//
	printf("-e-EVP_EncryptUpdate_ex status: %ld",status);			//
	return(2);								//
    }										//
    //
    //	finish up
    //
    if (debug>0){								//
	printf("step 6\n");							//
	printf ("EncryptUpdate() writes %d bytes \n", outlen );			//
    }										//
    ciphertext_ptr += outlen;							// Adjust output buffer pointer
    ciphertext_len = outlen; 							// Initial encryption
    status = EVP_EncryptFinal_ex( &ctx , ciphertext_ptr , &outlen );		//
    if (status!=1){								//
	printf("-e-EVP_EncryptFinal_ex status: %ld\n",status);			//
	return(2);								//
    }										//
    if (debug>0)								//
	printf ("EncryptFinal_ex() writes %d bytes \n", outlen );		//
    ciphertext_len += outlen ;							// Total encryption with padding byte
    //
    //	let's see the result in hex
    //
    ptr7 = malloc((ciphertext_len*2)+1);					// need twice as much space
    ptr7[0] = '\0';								//
    for (int i=0, j=0; i < ciphertext_len; i++, j+=2) {				//
	sprintf(&ptr7[j],"%02x", encbuf[i]);					//
    }										//
    ptr7[(ciphertext_len*2)] = '\0';						// superfluous?
    if (debug>0)								//
	printf("-i-cipher in hex   : %s\n", ptr7);				//
    struct dsc$descriptor_s c_dsc_enc_hex;					// create vms string descriptor in c
    VMSIFY1(c_dsc_enc_hex, ptr7)						//
    rc = str$copy_dx(vms_dsc_enc_hex, &c_dsc_enc_hex);				// copy back to VMS string space
    if ((rc & 7)!=1) printf("-e-str$copy_dx-rc: %ld\n",rc);			//
    //
    //	convert to base64 for developer debug
    //
    ptr9 = (char*) base64(encbuf,ciphertext_len,&b64_len);			//
    if (debug>0)								//
	printf("-i-cipher in base64: %s\n", ptr9);				//
    struct dsc$descriptor_s c_dsc_enc_b64;					//
    VMSIFY1(c_dsc_enc_b64, ptr9)						//
    rc = str$copy_dx(vms_dsc_enc_b64, &c_dsc_enc_b64);				// copy back to VMS string space
    if ((rc & 7)!=1) printf("-e-str$copy_dx-rc: %ld\n",rc);			//
    //
    EVP_CIPHER_CTX_cleanup( ctx_ptr );						//
    return( status );								//
}
//==============================================================================
//	AES Decryption
//==============================================================================
long NSR_AES_DECRYPT(	struct dsc$descriptor_d *vms_dsc_enc_hex	,
			struct dsc$descriptor_d *vms_dsc_iv_hex		,
			struct dsc$descriptor_d *vms_dsc_key		,
			struct dsc$descriptor_d *vms_dsc_plaintext	,
			long			debug			) {
    //--------------------------------------------------------------------------
    int			status;
    unsigned char	iv[16]		= {0};
    unsigned char	key[32]		= {0};
    unsigned char	txtbuf[1024]	= {0};
             char	hexbuf[1024]	= {0};
    unsigned char	encbuf[1024]	= {0};
    unsigned char *	plaintext_ptr	= NULL;
    int			plaintext_len	= 0;
    unsigned char * 	ciphertext_ptr	= NULL;
    int			ciphertext_len	= 0;
    int			outlen		= 0;
    char *		ptr7		= 0;
    EVP_CIPHER_CTX	ctx;							// create
    EVP_CIPHER_CTX	*ctx_ptr	= &ctx;					// setup
    char hexstring[] =	"0123456789abcdef";					// only used by my dehex
    long		i,j,k,x,y;						// only used by my parse
    long		rc;							//
    //--------------------------------------------------------------------------
    //	real work begins
    //	(1) copy inbound key
    //  (2) copy inbound iv hex
    //	(3) process inbound encrypted hex
    //--------------------------------------------------------------------------
    //
    //	(1) copy inbound key
    //
    if (debug>0)
	printf("step 1 (key)\n");
    strncat((char*) key, vms_dsc_key->dsc$a_pointer, vms_dsc_key->dsc$w_length);
    //
    //	(2) copy inbound iv_hex
    //
    if (debug>0)
	printf("step 2 (iv)\n");
    if (vms_dsc_iv_hex->dsc$w_length==0){					// if blank
	if (debug>0)
	    printf("step 2a (blank)\n");					//
	memset(&iv,0,sizeof(iv));						// superfluous?
	goto iv_hex_end;							// c-coders gasp...
    }
    if (debug>0)
	printf("step 2b (non-blank)\n");					//
    if (vms_dsc_iv_hex->dsc$w_length>(sizeof(iv)*2)) {				// cuz two hex characters become one binary byte
	printf("-e- iv is too long\n");
	return(2);
    }
    if ((vms_dsc_iv_hex->dsc$w_length % 2)!=0){
	printf("-e-iv is not an even length\n");
	return(2);
    }
    //
    //	convert hex pairs to binary byte
    //
    for (int i=0, j=0; i<vms_dsc_iv_hex->dsc$w_length; i=i+2, j++){
	int tens, ones;
	char* ptr;
	ptr = strchr(&hexstring[0],vms_dsc_iv_hex->dsc$a_pointer[i]);
	if (ptr==NULL){
	    printf("-e-encounted non-hex charcter in IV\n");
	    return(1);
	}
	tens = (ptr - hexstring) * 16;
	ptr = strchr(&hexstring[0],vms_dsc_iv_hex->dsc$a_pointer[i+1]);
	if (ptr==NULL){
	    printf("-e-encounted non-hex charcter in IV\n");
	    return(1);
	}
	ones = (ptr - hexstring);
	iv[j] = (char) tens + ones;
    }
    iv_hex_end:;
    //
    //	(3) copy inbound encrypted hex
    //
    if (debug>0)								//
	printf("step 3 (encoded hex)\n");					//
    if (vms_dsc_enc_hex->dsc$w_length>sizeof(hexbuf)){				//
	printf("-e-plain encoded hex is too long\n");				//
	return(2);								//
    }
    strncat((char*) hexbuf, vms_dsc_enc_hex->dsc$a_pointer, vms_dsc_enc_hex->dsc$w_length);
    //
    //	now convert encrypted hex to binary
    //
    for (int i=0, j=0; i<strlen(hexbuf); i=i+2, j++){
	char* ptr;
	int tens, ones;
	ptr = strchr(&hexstring[0],hexbuf[i]);
	if (ptr==NULL){
	    printf("-e-encounted non-hex charcter in enc_hex\n");
	    return(1);
	}
	tens = (ptr - hexstring) * 16;
	ptr = strchr(&hexstring[0],hexbuf[i+1]);
	if (ptr==NULL){
	    printf("-e-encounted non-hex charcter in enc_hex\n");
	    return(1);
	}
	ones = (ptr - hexstring);
	encbuf[j] = (char) tens + ones;
    }
    ciphertext_len = strlen(hexbuf) / 2;
    //
    //	finish setup
    //
    plaintext_ptr = txtbuf ;				// decrypted text goes here
    plaintext_len = sizeof(txtbuf);			//
    ciphertext_ptr = encbuf ;				//
    //
    if (debug>0)					//
	printf("step 4\n");				//
    status = EVP_DecryptInit_ex(&ctx			,
				EVP_aes_256_cbc()	,
				NULL			,
				key			,
				iv			);
    //
    if (status!=1){
	printf("-e-EVP_DecryptInit_ex status: %ld\n",status);
	return(2);
    }
    if (debug>0)								//
	printf("step 5\n");							//
    status = EVP_DecryptUpdate(	&ctx ,
				plaintext_ptr , &outlen ,
				ciphertext_ptr , ciphertext_len );
    if (status!=1){
	printf("-e-EVP_DecryptUpdate status: %ld\n",status);
	return(2);
    }
    if (debug>0){								//
	printf("step 6\n");							//
	printf ("DecryptUpdate() writes %d bytes \n", outlen);
    }
    plaintext_len = outlen; 							//
    plaintext_ptr += outlen;							// adjust decrypted O/P buffer

    status = EVP_DecryptFinal_ex(	&ctx ,
					plaintext_ptr ,
					&outlen );

    if (status!=1){
	printf("-e-EVP_DecryptFinal_ex status: %ld\n",status);
	return(2);
    }
    if (debug>0){
	printf ("DecryptFinal_ex() writes %d bytes \n", outlen );
    }

    plaintext_len += outlen ;							// Total decryption with padding
    plaintext_ptr += outlen ;							// adjust decrypted O/P buffer

    *plaintext_ptr='\0';							// terminate string at data's end
    if (debug>0){
	printf("Decrypted value is: \n%s \n", txtbuf );
    }
    struct dsc$descriptor_s c_dsc_decrypted;					// create vms string descriptor in c
    VMSIFY1(c_dsc_decrypted,(char*) &txtbuf)					//
    rc = str$copy_dx(vms_dsc_plaintext, &c_dsc_decrypted);			// copy back to VMS string space
    if ((rc & 7)!=1) printf("-e-str$copy_dx-rc: %ld\n",rc);			//
    //
    EVP_CIPHER_CTX_cleanup( ctx_ptr );
    return( status );
}

home Back to Home
Neil Rieck
Waterloo, Ontario, Canada.