Back to home page

Enduro/X

 
 

    


0001 /**
0002  * @brief Cryptography related routines
0003  *   We have a poor logging options where, because logger might not be initialized
0004  *   when we will require to log some messages. Thus critical things will be logged
0005  *   to the userlog();
0006  *   The first byte of data will indicate the length of padding used.
0007  *   We also need to introduce standard library error handling there. This is needed
0008  *   for fact that we might want to give some explanation why decryption failed at
0009  *   at the application boot.
0010  *
0011  * @file crypto.c
0012  */
0013 /* -----------------------------------------------------------------------------
0014  * Enduro/X Middleware Platform for Distributed Transaction Processing
0015  * Copyright (C) 2009-2016, ATR Baltic, Ltd. All Rights Reserved.
0016  * Copyright (C) 2017-2023, Mavimax, Ltd. All Rights Reserved.
0017  * This software is released under one of the following licenses:
0018  * AGPL (with Java and Go exceptions) or Mavimax's license for commercial use.
0019  * See LICENSE file for full text.
0020  * -----------------------------------------------------------------------------
0021  * AGPL license:
0022  *
0023  * This program is free software; you can redistribute it and/or modify it under
0024  * the terms of the GNU Affero General Public License, version 3 as published
0025  * by the Free Software Foundation;
0026  *
0027  * This program is distributed in the hope that it will be useful, but WITHOUT ANY
0028  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
0029  * PARTICULAR PURPOSE. See the GNU Affero General Public License, version 3
0030  * for more details.
0031  *
0032  * You should have received a copy of the GNU Affero General Public License along 
0033  * with this program; if not, write to the Free Software Foundation, Inc.,
0034  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
0035  *
0036  * -----------------------------------------------------------------------------
0037  * A commercial use license is available from Mavimax, Ltd
0038  * contact@mavimax.com
0039  * -----------------------------------------------------------------------------
0040  */
0041 #include <ndrx_config.h>
0042 #include <string.h>
0043 #include <stdio.h>
0044 #include <stdlib.h>
0045 #include <memory.h>
0046 #include <time.h>
0047 #include <sys/time.h>
0048 #include <unistd.h>
0049 #include <stdarg.h>
0050 #include <arpa/inet.h>
0051 #include <errno.h>
0052 
0053 #include <ndrstandard.h>
0054 #include <ndebug.h>
0055 #include <nstdutil.h>
0056 #include <sys_unix.h>
0057 #include <exsha1.h>
0058 #include <excrypto.h>
0059 #include <userlog.h>
0060 #include <expluginbase.h>
0061 #include <exaes.h>
0062 #include <exbase64.h>
0063 
0064 #include "atmi_int.h"
0065 
0066 /*---------------------------Externs------------------------------------*/
0067 /*---------------------------Macros-------------------------------------*/
0068 #define IV_INIT {   0xab, 0xcc, 0x1b, 0xc2, \
0069                     0x3d, 0xe4, 0x44, 0x11, \
0070                     0x30, 0x54, 0x34, 0x09, \
0071                     0xef, 0xaf, 0xfc, 0xf5 \
0072                 }
0073       
0074 /* #define CRYPTODEBUG */
0075 
0076 /* #define CRYPTODEBUG_DUMP */
0077 
0078 #define CRYPTO_LEN_PFX_BYTES    4
0079 
0080 #define API_ENTRY {_Nunset_error();}
0081 
0082 /*---------------------------Enums--------------------------------------*/
0083 /*---------------------------Typedefs-----------------------------------*/
0084 /*---------------------------Globals------------------------------------*/
0085 /*---------------------------Statics------------------------------------*/
0086 /*---------------------------Prototypes---------------------------------*/
0087 
0088 
0089 /**
0090  * Get encryption key.
0091  * This is used in expluginbase.c for default crypto function.
0092  * @param key_out
0093  * @param klen
0094  * @return EXSUCCEED/EXFAIL
0095  */
0096 expublic int ndrx_crypto_getkey_std(char *key_out, int key_out_bufsz)
0097 {
0098     int ret = EXSUCCEED;
0099     long len;
0100     API_ENTRY;
0101 
0102     /* TODO Add some caching over the crypto key! */
0103 
0104     if (EXSUCCEED!=ndrx_sys_get_hostname(key_out, key_out_bufsz))
0105     {
0106         _Nset_error_fmt(NEUNIX, "Failed to get hostname!");
0107         EXFAIL_OUT(ret);
0108     }
0109     
0110     len = strlen(key_out);
0111     
0112     
0113     if (len+1 /*incl EOS*/ < key_out_bufsz)
0114     {
0115         snprintf(key_out+len, key_out_bufsz - len, "%s", 
0116                 ndrx_sys_get_cur_username());
0117     }
0118     
0119 #ifdef CRYPTODEBUG
0120     NDRX_LOG_EARLY(log_debug, "Password built: [%s]", key_out);
0121 #endif
0122     
0123 out:
0124     return ret;
0125 }
0126 
0127 /**
0128  * Get final key
0129  * @param sha1key, buffer size NDRX_ENCKEY_BUFSZ
0130  * @return EXSUCCEED/EXFAIL
0131  */
0132 exprivate int ndrx_get_final_key(char *sha1key)
0133 {
0134     int ret = EXSUCCEED;
0135     char password[PATH_MAX+1];
0136     
0137     /* first we take password & hash it */
0138     
0139     if (ndrx_G_plugins.p_ndrx_crypto_getkey(password, sizeof(password)))
0140     {
0141         userlog("Failed to get encryption key by plugin "
0142                 "function, provider: [%s]", 
0143                 ndrx_G_plugins.ndrx_crypto_getkey_provider);
0144         
0145         _Nset_error_fmt(NEPLUGIN, "Failed to get encryption key by plugin "
0146                 "function, provider: [%s]", 
0147                 ndrx_G_plugins.ndrx_crypto_getkey_provider);
0148         
0149         EXFAIL_OUT(ret);
0150     }
0151     
0152 #ifdef CRYPTODEBUG
0153     NDRX_LOG_EARLY(log_debug, "Clear password: [%s]", password);
0154 #endif
0155     
0156     /* make sha1 */
0157     EXSHA1( sha1key, password, strlen(password) );
0158     
0159 #ifdef CRYPTODEBUG_DUMP
0160     NDRX_DUMP(log_debug, "SHA1 key", sha1key, NDRX_ENCKEY_BUFSZ);
0161 #endif
0162     
0163 out:
0164     return ret;
0165 }
0166 
0167 /**
0168  * Decrypt data block (internal version, no API entry)
0169  * @param input input data block
0170  * @param ibufsz input data block buffer size
0171  * @param output encrypted data block
0172  * @param obufsz encrypted data block buffer size
0173  * @return EXSUCCEED/EXFAIL
0174  */
0175 exprivate int ndrx_crypto_enc_int(char *input, long ilen, char *output, long *olen)
0176 {
0177     int ret = EXSUCCEED;
0178     char sha1key[NDRX_ENCKEY_BUFSZ];
0179     long size_estim;
0180     uint32_t *len_ind = (uint32_t *)output;
0181     uint8_t  iv[]  = IV_INIT;
0182     
0183     /* encrypt data block */
0184     
0185     if (EXSUCCEED!=ndrx_get_final_key(sha1key))
0186     {
0187         EXFAIL_OUT(ret);
0188     }
0189     
0190     /* estimate the encrypted data len round to */
0191     size_estim = 
0192             ((ilen + NDRX_ENC_BLOCK_SIZE - 1) / NDRX_ENC_BLOCK_SIZE) 
0193             * NDRX_ENC_BLOCK_SIZE  
0194             + CRYPTO_LEN_PFX_BYTES;
0195     
0196 #ifdef CRYPTODEBUG
0197     NDRX_LOG_EARLY(log_debug, "%s: Data size: %ld, estimated: %ld, output buffer: %ld",
0198             __func__, ilen, size_estim, *olen);
0199 #endif
0200 #ifdef CRYPTODEBUG_DUMP
0201     NDRX_DUMP(log_debug, "About to encrypt: ", input, ilen);
0202 #endif
0203     
0204     if (size_estim > *olen)
0205     {
0206         userlog("Encryption output buffer too short, estimated: %ld, but on input: %ld",
0207                 size_estim, *olen);
0208         
0209         _Nset_error_fmt(NENOSPACE, "Encryption output buffer too short, "
0210                 "estimated: %ld, but on input: %ld",
0211                 size_estim, *olen);
0212     
0213         *olen = size_estim;
0214         EXFAIL_OUT(ret);
0215     }
0216     *olen = size_estim;
0217     
0218     /* so data len will not be encrypted */
0219     *len_ind = htonl((uint32_t)ilen);
0220     
0221     EXAES_CBC_encrypt_buffer((uint8_t*)(output+CRYPTO_LEN_PFX_BYTES), 
0222             (uint8_t*)input, ilen, (const uint8_t*)sha1key, (const uint8_t*) iv);
0223     
0224 #ifdef CRYPTODEBUG_DUMP
0225     
0226     /* DUMP the data block */
0227     NDRX_DUMP(log_debug, "Encrypted data block", output, *olen);
0228     
0229 #endif
0230     
0231 out:
0232     return ret;
0233 }
0234 
0235 /**
0236  * Decrypt data block (API entry function)
0237  * @param input input data block
0238  * @param ibufsz input data block buffer size
0239  * @param output encrypted data block
0240  * @param obufsz encrypted data block buffer size
0241  * @return EXSUCCEED/EXFAIL
0242  */
0243 expublic int ndrx_crypto_enc(char *input, long ilen, char *output, long *olen)
0244 {
0245     API_ENTRY;
0246     return ndrx_crypto_enc_int(input, ilen, output, olen);
0247 }
0248 
0249 /**
0250  * Decrypt data block (internal, no API entry)
0251  * @param input input buffer
0252  * @param ilen input len
0253  * @param output output buffer
0254  * @param olen on input indicates the buffer length on output, output data len
0255  * @return EXSUCCEED/EXFAIL
0256  */
0257 expublic int ndrx_crypto_dec_int(char *input, long ilen, char *output, long *olen)
0258 {
0259     int ret = EXSUCCEED;
0260     char sha1key[NDRX_ENCKEY_BUFSZ];
0261     uint32_t *len_ind = (uint32_t *)input;
0262     uint8_t  iv[]  = IV_INIT;
0263     long data_size = ntohl(*len_ind);
0264     
0265     /* encrypt data block */
0266     
0267     if (EXSUCCEED!=ndrx_get_final_key(sha1key))
0268     {
0269         EXFAIL_OUT(ret);
0270     }
0271     
0272 #ifdef CRYPTODEBUG_DUMP
0273     NDRX_DUMP(log_debug, "About to decrypt (incl 4 byte len): ", input, ilen);
0274 #endif
0275     
0276 #ifdef CRYPTODEBUG
0277     NDRX_LOG_EARLY(log_debug, "Data size: %ld, output buffer: %ld",
0278             data_size, *olen);
0279 #endif
0280     
0281     if (data_size > *olen)
0282     {
0283         userlog("Decryption output buffer too short, data: %ld, output buffer: %ld",
0284                 data_size, *olen);
0285         
0286         _Nset_error_fmt(NENOSPACE, "Decryption output buffer too short, "
0287                 "data: %ld, output buffer: %ld",
0288                 data_size, *olen);
0289       
0290         *olen = data_size;
0291         EXFAIL_OUT(ret);
0292     }
0293     *olen = data_size;
0294     
0295     EXAES_CBC_decrypt_buffer((uint8_t*)(output), 
0296             (uint8_t*)(input+CRYPTO_LEN_PFX_BYTES), ilen-CRYPTO_LEN_PFX_BYTES, 
0297             (const uint8_t*)sha1key, (const uint8_t*) iv);
0298     
0299     /* DUMP the data block */
0300     
0301 #ifdef CRYPTODEBUG_DUMP
0302     NDRX_DUMP(log_debug, "Decrypted data block", output, *olen);
0303 #endif
0304     
0305 out:
0306     return ret;
0307 }
0308 
0309 /**
0310  * Decrypt data block, API entry
0311  * @param input input buffer
0312  * @param ilen input len
0313  * @param output output buffer
0314  * @param olen on input indicates the buffer length on output, output data len
0315  * @return EXSUCCEED/EXFAIL
0316  */
0317 expublic int ndrx_crypto_dec(char *input, long ilen, char *output, long *olen)
0318 {
0319     API_ENTRY;
0320     return ndrx_crypto_dec_int(input, ilen, output, olen);
0321 }
0322 
0323 /**
0324  * Encrypt string
0325  * @param input input string, zero terminated
0326  * @param output base64 string
0327  * @param olen output buffer length
0328  * @return EXSUCCEED/EXFAIL
0329  */
0330 expublic int ndrx_crypto_enc_string(char *input, char *output, long *olen)
0331 {
0332     int ret = EXSUCCEED;
0333     char *buf = NULL;
0334     long bufsz;
0335     long estim_size;
0336     long inlen = strlen(input);
0337     size_t b64len;
0338     API_ENTRY;
0339     
0340     NDRX_SYSBUF_MALLOC_OUT(buf, bufsz, ret);
0341 
0342     /* encrypt data block */
0343     if (EXSUCCEED!=ndrx_crypto_enc_int(input, inlen, buf, &bufsz))
0344     {
0345         EXFAIL_OUT(ret);
0346     }
0347     
0348     /* translate data block the the base64 (with size estim) */
0349     estim_size = NDRX_BASE64_SIZE(bufsz) + 1;
0350     
0351     if (NDRX_BASE64_SIZE(bufsz) +1 /* for EOS */ > *olen)
0352     {
0353         userlog("Output buffer too short. Required for "
0354                 "base64 %ld bytes, but got %ld", estim_size, *olen);
0355 #ifdef CRYPTODEBUG
0356         NDRX_LOG_EARLY(log_error, "Output buffer too short. Required for "
0357                 "base64 %ld bytes, but got %ld", estim_size, *olen);
0358 #endif
0359         
0360         _Nset_error_fmt(NENOSPACE, "Output buffer too short. Required for "
0361                 "base64 %ld bytes, but got %ld",
0362               estim_size, *olen);
0363         
0364         *olen = NDRX_BASE64_SIZE(bufsz) +1;
0365         EXFAIL_OUT(ret);
0366     }
0367     
0368     /* encode to base64... */
0369     b64len = (size_t)*olen;
0370     ndrx_base64_encode((unsigned char *)buf, bufsz, &b64len, output);
0371     
0372     /* this includes EOS */
0373     *olen=b64len;
0374     /* output[b64len] = EXEOS; */
0375     
0376 #ifdef CRYPTODEBUG
0377     
0378     NDRX_LOG_EARLY(log_debug, "%s: input: [%s]", __func__, input);
0379     NDRX_LOG_EARLY(log_debug, "%s: output: [%s]", __func__, output);
0380     
0381 #endif
0382     
0383 out:
0384     if (NULL!=buf)
0385     {
0386         NDRX_SYSBUF_FREE(buf);
0387     }
0388     return ret;
0389 }
0390 
0391 /**
0392  * Decrypt the base64 string
0393  * @param input base64 containing encrypted data
0394  * @param output clear text output storage
0395  * @param olen output buffer size
0396  */
0397 expublic int ndrx_crypto_dec_string(char *input, char *output, long *olen)
0398 {
0399     int ret = EXSUCCEED;
0400     long len = strlen(input);
0401     char *buf = NULL;
0402     size_t bufsz = len;
0403     uint32_t *len_ind;
0404     long data_size;
0405     
0406     API_ENTRY;
0407     
0408     if (NULL==(buf = NDRX_MALLOC(bufsz)))
0409     {
0410         int err = errno;
0411         
0412         NDRX_LOG_EARLY(log_error, "%s: Failed to allocate %ld bytes: %s",
0413                 __func__, len, strerror(err));
0414         userlog("%s: Failed to allocate %ld bytes: %s",
0415                 __func__, len, strerror(err));
0416         
0417         _Nset_error_fmt(NEUNIX, "%s: Failed to allocate %ld bytes: %s",
0418                 __func__, len, strerror(err));
0419         EXFAIL_OUT(ret);
0420     }
0421     
0422     len_ind = (uint32_t *)buf;
0423     
0424 #ifdef CRYPTODEBUG
0425     NDRX_LOG_EARLY(log_debug, "%s, output buf %p, olen=%ld input len: %ld", 
0426             __func__, output, *olen, len);
0427     NDRX_LOG_EARLY(log_debug, "%s: About to decrypt (b64): [%s]", __func__, input);
0428 #endif
0429 
0430     /* convert base64 to bin */
0431     if (NULL==ndrx_base64_decode(input, len, &bufsz, buf))
0432     {
0433         _Nset_error_fmt(NEFORMAT, "%s, ndrx_base64_decode failed (input len: %ld) input: [%s]", 
0434                 __func__, len, input);
0435         NDRX_LOG_EARLY(log_error, "%s, ndrx_base64_decode failed (input len: %ld) input: [%s]", 
0436                 __func__, len, input);
0437         userlog("%s, ndrx_base64_decode failed (input len: %ld)", 
0438                 __func__, len);
0439         EXFAIL_OUT(ret);
0440     }
0441 
0442 #ifdef CRYPTODEBUG_DUMP
0443     NDRX_DUMP(log_debug, "Binary data to decrypt: ", buf, bufsz);
0444 #endif
0445     
0446     /* Check the output buffer len */
0447     data_size = ntohl(*len_ind);
0448     
0449     if (data_size +1 > *olen)
0450     {
0451         userlog("String decryption output buffer too short, data (incl. EOS): %ld, "
0452                 "output buffer: %ld", data_size+1, *olen);
0453         
0454         _Nset_error_fmt(NENOSPACE, "String decryption output buffer too short, "
0455                 "data (incl. EOS): %ld, output buffer: %ld",
0456                 data_size+1, *olen);
0457         
0458         *olen=data_size+1;
0459         EXFAIL_OUT(ret);
0460     }
0461     
0462     /* decrypt the data to the output buffer */
0463     
0464     if (EXSUCCEED!=ndrx_crypto_dec_int(buf, bufsz, output, olen))
0465     {
0466 #ifdef CRYPTODEBUG
0467         NDRX_LOG_EARLY(log_error, "%s: Failed to decrypt [%s]!", __func__, input);
0468 #endif
0469         userlog("%s: Failed to decrypt [%s]!", __func__, input);
0470     }
0471     
0472     output[*olen] = EXEOS;
0473     
0474     /* check that data does not contain binary zero
0475      * because we operate with 0x00 strings, thus if zero is found, data
0476      * is not decrypted.
0477      */
0478     if ((len=strlen(output)) != *olen)
0479     {
0480         userlog("Found EOS at %ld. Output data len %ld", len, *olen);
0481         
0482         _Nset_error_fmt(NEINVALKEY, "Found EOS at %ld. Output data len %ld", 
0483                 len, *olen);
0484         
0485         EXFAIL_OUT(ret);
0486     }
0487     
0488     /* add EOS... */
0489     (*olen)++;
0490     
0491 out:
0492 
0493     if (NULL!=buf)
0494     {
0495         NDRX_FREE(buf);
0496     }
0497 
0498     return ret;
0499 }
0500 /* vim: set ts=4 sw=4 et smartindent: */