OpenVMS Source Code Demos
ssl_aes_api_demo01
//==============================================================================
// title : SSL_AES_API_demo01.c
// author : Neil Rieck ( https://neilrieck.net - mailto: n.rieck@bell.net )
// 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 );
}
Back to
Home
Neil Rieck
Waterloo, Ontario, Canada.