X-Git-Url: http://git.eversberg.eu/gitweb.cgi?p=lcr.git;a=blobdiff_plain;f=crypt.cpp;fp=crypt.cpp;h=2babfb771e113377529e40eaa454a47c75d74a20;hp=0000000000000000000000000000000000000000;hb=2ed0fee489c37a6e2d4473f6185ebbe3e746ac11;hpb=1bf9bb306b9b92b50e56a2dcce131eb5e82e667a diff --git a/crypt.cpp b/crypt.cpp new file mode 100644 index 0000000..2babfb7 --- /dev/null +++ b/crypt.cpp @@ -0,0 +1,2046 @@ +/*****************************************************************************\ +** ** +** PBX4Linux ** +** ** +**---------------------------------------------------------------------------** +** Copyright: Andreas Eversberg ** +** ** +** cryption stuff ** +** ** +\*****************************************************************************/ + +/* how authentication is performed: + +Alice Bob +----------------------------------------------------------------- + unencrypted call +start * + | -----> sending random number -----> (gets lost) + | + | * start +comparing | <----- sending random number <----- | +master state | | + | -----> sending "slave" code -----> | slave state +calculating | | +rsa key pair | | + | | +done | -----> sending public key -----> | + | | crpyting + | | session key + | | + | <----- sending session key <----- | done +decrypting | | enable crypt +dession key | * stop + | +done | +enable crypt | +stop * + encrypted call + +When Bob and Alice activate their authentication process, they will make +up a random number. + +Lets assume Alice starts encryption first. She activates the authentication +process. Bob hat not activates the process yet. Alice sends a random number +to Bob, but he will ignore it, because he is not listening. + +Bob also activates the authentication process, and sends his random number. +Now Alice will receive that number and compares the values. If the values +are equal, the process will fail and Alice will send the message "LOOPED". +If Alice's random number is greater, she will identify herself as "master". +Bob will get a "SLAVE" message. Bob might also send a "MASTER" message, +if he got Alice's random number, due to parallel activation of the +authentication. + +After transmission or reception of a master message, more messages are +ignored. After reception of a "RANDOM" message, more messages are +ignored. A reception of a "RANDOM" message always causes to identify who +is slave. + +Now Alice starts calculating his public/private key pair, because she is +"master". When Bob receives the "SLAVE" message, he will change the timeout +value. If no random number or "SLAVE", "MASTER" or "LOOPED" is received within +a timeout value, the "ABORT" message is sent. If the "ABORT" message is +received at any state of the process, the process is aborted. + +After the key of Alices is calculated, she will send it to Bob. Bob will use +the key to start encryption of a random session key. Both will change their +timeout values. + +After Bob has finished is crypted session key, he will send it to Alice and +enable encryption. Bob has finished his process now. + +As soon as Alice received the encrypted key, she will encrypt it and also +enable encryption with the same key as Bob. Alis has finished her process now. + +Both will get displayed some of the first digits of the public key. Both can +talk about the digits now. A man in the middle cannot change the keys without +changing the public key. The voice of Alice and/or Bob is used to "sign" and +"check" that the public key is not modified. + +If Alice or Bob wants to stop encryption, one will send the "ABORT" message. +After transmission or reception. + + +The states of the process: + +CM_ST_NULL +---------- +no encryption + +CM_ST_IDENT +----------- +Waiting for the remote random number or "MASTER", "SLAVE", "LOOPED" message. + +CM_ST_KEYGEN +------------ +The master generates the key and waits for the key engine to finish. + +CM_ST_KEYWAIT +------------- +The slave waits for the master to send the key. + +CM_ST_CSKEY +----------- +The slave generates the session key and waits for the encryption engine to +finish. + +CM_ST_CSWAIT +------------ +The master waits for the slave to send the crypted session key. + +CM_ST_DSKEY +----------- +The master waits for the decryption engine to finish decryption of the session +key. + +CM_ST_ACTIVE +------------ +The encryption is established. + + +Timouts +------- +CM_TO_IDENT = waiting for the remote party to enable encryption +CM_TO_KEY = waiting for key generation +CM_TO_CSKEY = waiting for session key encryption +CM_TO_DSKEY = waiting for session key decryption + + +Structure of message: +--------------------- + +one octet message element +two octets element length (first octet = high-byte) +data as given in length + +last element is 0 +the message type is encoded as element + + +*/ + +#include +#include +#include +#include +#ifdef CRYPTO +#include +#endif +#include "main.h" + + +/* convert key string to binary key vector + * returns 0 if an error ocurred + */ +unsigned char *crypt_key(unsigned char *key, int *binary_len) +{ + static unsigned char binary_key[2048]; + int i = 0; + + binary_key[0] = '\0'; + + if (!key[0]) + return(NULL); + + /* check for 0xXXXX... type of key */ + if (!strncmp((char *)key, "0x", 2)) + { + key+=2; + while(*key) + { + if (i == (int)sizeof(binary_key)) + return(NULL); + + if (*key>='0' && *key<='9') + binary_key[i] = (*key-'0') << 8; + else if (*key>='a' && *key<='f') + binary_key[i] = (*key-'a'+10) << 8; + else if (*key>='A' && *key<='F') + binary_key[i] = (*key-'A'+10) << 8; + else + return(NULL); + key++; + + if (*key>='0' && *key<='9') + binary_key[i] += (*key - '0'); + else if (*key>='a' && *key<='f') + binary_key[i] += (*key - 'a' + 10); + else if (*key>='A' && *key<='F') + binary_key[i] += (*key - 'A' + 10); + else + return(NULL); + key++; + + i++; + } + *binary_len = i; + return(binary_key); + } + + /* ascii key too long */ + if (strlen((char *)key) >= sizeof((char *)binary_key)) + return(NULL); + + memcpy(binary_key, key, strlen((char *)key)); + *binary_len = strlen((char *)key); + return(binary_key); +} + +/* + * support routine to get cpu speed + */ +static unsigned long get_bogomips(void) +{ + FILE *fp; + char buffer[64], *p; + + fp = fopen("/proc/cpuinfo", "r"); + if (!fp) + { + PERROR("Cannot access /proc/cpuinfo. Will not use cpuinfo for identification of pear\n"); + return(0); + } + fduse++; + buffer[sizeof(buffer-1)] = '\0'; + while(fgets(buffer, sizeof(buffer)-1, fp)) + { + if (!!strncmp(buffer, "bogomips", 8)) + continue; + if (!strchr(buffer, ':')) + continue; + p = strchr(buffer, ':')+1; + while(*p == ' ') + p++; + if (strchr(p, '.')) + *strchr(p, '.') = '\0'; + fclose(fp); + fduse--; + return(atoi(p)); + } + fclose(fp); + fduse--; + PERROR("Cannot find 'bogomips' in /proc/cpuinfo. Will not use cpuinfo for identification of pear\n"); + return(0); +} + + +/* + * crc 32 stuff + */ +static unsigned long crc_reflect(unsigned long ref, char ch) +{ + unsigned long value = 0; + int i; + + i = 1; + while(i < ch+1) + { + if(ref & 1) + value |= 1 << (ch - i); + ref >>= 1; + i++; + } + return(value); +} + +static unsigned long crc32_table[256]; +static int crc_initialized = 0; + +void crc_init(void) +{ + unsigned long ulPolynomial = 0x04c11db7; + int i, j; + + i = 0; + while(i < 256) + { + crc32_table[i] = crc_reflect(i, 8) << 24; + j = 0; + while(j < 8) + { + crc32_table[i] = (crc32_table[i] << 1) ^ (crc32_table[i] & (1 << 31) ? ulPolynomial : 0); + j++; + } + crc32_table[i] = crc_reflect(crc32_table[i], 32); + i++; + } + crc_initialized = 1; +} + +unsigned long crc32(unsigned char *data, int len) +{ + unsigned long crc = 0xffffffff; + + if (!crc_initialized) + { + PERROR("crc not initialized, exitting..."); + exit(-1); + } + + while (len--) + crc = (crc >> 8) ^ crc32_table[(crc & 0xFF) ^ *data++]; + return(crc^0xffffffff); +} + + +CM_ST_NAMES + +/* give name of state */ +static char *statename(int state) +{ + if (state>=0 && state>"); +} + +/* + * authentication key generation, encryption, decryption + */ +struct auth_args { + class EndpointAppPBX *apppbx; + int job; +}; + +static void *keyengine_child(void *arg) +{ + struct auth_args *args = (struct auth_args *)arg; + class EndpointAppPBX *apppbx = args->apppbx; + int job = args->job; +#ifdef CRYPTO + RSA *rsa; + int exponent; + int i; +#endif + + struct sched_param schedp; + int ret; + + PDEBUG((DEBUG_EPOINT | DEBUG_CRYPT), "EPOINT(%d) child process started for using libcrypto\n", apppbx->ea_endpoint->ep_serial); + + /* lower priority to keep pbx running fluently */ + if (options.schedule > 0) + { + memset(&schedp, 0, sizeof(schedp)); + schedp.sched_priority = 0; + ret = sched_setscheduler(0, SCHED_OTHER, &schedp); + if (ret < 0) + { + PERROR("Scheduling to normal priority failed (errno = %d).\nExitting child process...\n", errno); + goto done; + } + } + + switch(job) + { + /* generate rsa key pair */ + case CK_GENRSA_REQ: +#ifndef CRYPTO + PERROR("Not compliled wiht crypto.\n"); + apppbx->e_crypt_keyengine_return = -1; +#else + srandom(*((unsigned int *)mISDN_rand) ^ random()); +// exponent = (((random()<<1)|1) & 0x7f) + 0x80; /* odd */ + exponent = 65537; +// if (exponent < 3) exponent = 3; /* >= 3 */ + rsa = RSA_generate_key(RSA_BITS, exponent, NULL, NULL); + if (!rsa) + { + PERROR("Failed to generate rsa key pair.\n"); + apppbx->e_crypt_keyengine_return = -1; + break; + } + ememuse++; + apppbx->e_crypt_rsa_n_len = BN_num_bytes(rsa->n); + if (apppbx->e_crypt_rsa_n_len > (int)sizeof(apppbx->e_crypt_rsa_n)) + { + kerror_buffer: + PERROR("e_crypt_rsa_* too small for bignum.\n"); + apppbx->e_crypt_keyengine_return = -1; + RSA_free(rsa); + ememuse--; + break; + } + BN_bn2bin(rsa->n, apppbx->e_crypt_rsa_n); + apppbx->e_crypt_rsa_n_len = BN_num_bytes(rsa->n); + if (apppbx->e_crypt_rsa_e_len > (int)sizeof(apppbx->e_crypt_rsa_e)) + goto kerror_buffer; + BN_bn2bin(rsa->e, apppbx->e_crypt_rsa_e); + apppbx->e_crypt_rsa_e_len = BN_num_bytes(rsa->e); + if (apppbx->e_crypt_rsa_d_len > (int)sizeof(apppbx->e_crypt_rsa_d)) + goto kerror_buffer; + BN_bn2bin(rsa->d, apppbx->e_crypt_rsa_d); + apppbx->e_crypt_rsa_p_len = BN_num_bytes(rsa->p); + if (apppbx->e_crypt_rsa_p_len > (int)sizeof(apppbx->e_crypt_rsa_p)) + goto kerror_buffer; + BN_bn2bin(rsa->p, apppbx->e_crypt_rsa_p); + apppbx->e_crypt_rsa_q_len = BN_num_bytes(rsa->q); + if (apppbx->e_crypt_rsa_q_len > (int)sizeof(apppbx->e_crypt_rsa_q)) + goto kerror_buffer; + BN_bn2bin(rsa->q, apppbx->e_crypt_rsa_q); + apppbx->e_crypt_rsa_dmp1_len = BN_num_bytes(rsa->dmp1); + if (apppbx->e_crypt_rsa_dmp1_len > (int)sizeof(apppbx->e_crypt_rsa_dmp1)) + goto kerror_buffer; + BN_bn2bin(rsa->dmp1, apppbx->e_crypt_rsa_dmp1); + apppbx->e_crypt_rsa_dmq1_len = BN_num_bytes(rsa->dmq1); + if (apppbx->e_crypt_rsa_dmq1_len > (int)sizeof(apppbx->e_crypt_rsa_dmq1)) + goto kerror_buffer; + BN_bn2bin(rsa->dmq1, apppbx->e_crypt_rsa_dmq1); + apppbx->e_crypt_rsa_iqmp_len = BN_num_bytes(rsa->iqmp); + if (apppbx->e_crypt_rsa_iqmp_len > (int)sizeof(apppbx->e_crypt_rsa_iqmp)) + goto kerror_buffer; + BN_bn2bin(rsa->iqmp, apppbx->e_crypt_rsa_iqmp); + PDEBUG(DEBUG_CRYPT, "gen: rsa n=%02x...\n", *apppbx->e_crypt_rsa_n); + PDEBUG(DEBUG_CRYPT, "gen: rsa e=%02x...\n", *apppbx->e_crypt_rsa_e); + PDEBUG(DEBUG_CRYPT, "gen: rsa d=%02x...\n", *apppbx->e_crypt_rsa_d); + PDEBUG(DEBUG_CRYPT, "gen: rsa p=%02x...\n", *apppbx->e_crypt_rsa_p); + PDEBUG(DEBUG_CRYPT, "gen: rsa q=%02x...\n", *apppbx->e_crypt_rsa_q); + PDEBUG(DEBUG_CRYPT, "gen: rsa dmp1=%02x...\n", *apppbx->e_crypt_rsa_dmp1); + PDEBUG(DEBUG_CRYPT, "gen: rsa dmq1=%02x...\n", *apppbx->e_crypt_rsa_dmq1); + PDEBUG(DEBUG_CRYPT, "gen: rsa iqmp=%02x...\n", *apppbx->e_crypt_rsa_iqmp); + apppbx->e_crypt_keyengine_return = 1; + RSA_free(rsa); + ememuse--; +#endif + break; + + /* encrypt session key */ + case CK_CPTRSA_REQ: +#ifndef CRYPTO + PERROR("No crypto lib.\n"); + apppbx->e_crypt_keyengine_return = -1; +#else + /* generating session key */ + srandom(*((unsigned int *)mISDN_rand) ^ random()); + i = 0; + while(i < 56) + { + apppbx->e_crypt_key[i] = random(); + apppbx->e_crypt_key[i] ^= mISDN_rand[random() & 0xff]; + i++; + } + apppbx->e_crypt_key_len = i; + /* encrypt via rsa */ + rsa = RSA_new(); + if (!rsa) + { + PERROR("Failed to allocate rsa structure.\n"); + apppbx->e_crypt_keyengine_return = 1; + break; + } + ememuse++; + rsa->n = BN_new(); + rsa->e = BN_new(); + if (!rsa->n || !rsa->e) + { + PERROR("Failed to generate rsa structure.\n"); + apppbx->e_crypt_keyengine_return = -1; + RSA_free(rsa); + ememuse--; + break; + } + if (!BN_bin2bn(apppbx->e_crypt_rsa_n, apppbx->e_crypt_rsa_n_len, rsa->n)) + { + eerror_bin2bn: + PERROR("Failed to convert binary to bignum.\n"); + apppbx->e_crypt_keyengine_return = -1; + RSA_free(rsa); + ememuse--; + break; + } + if ((apppbx->e_crypt_rsa_n_len*8) != BN_num_bits(rsa->n)) + { + PERROR("SOFTWARE API ERROR: length not equal stored data. (%d != %d)\n", apppbx->e_crypt_rsa_n_len*8, BN_num_bits(rsa->n)); + apppbx->e_crypt_keyengine_return = -1; + RSA_free(rsa); + ememuse--; + break; + } + if (!BN_bin2bn(apppbx->e_crypt_rsa_e, apppbx->e_crypt_rsa_e_len, rsa->e)) + goto eerror_bin2bn; + PDEBUG(DEBUG_CRYPT, "crypt: rsa n=%02x...\n", *apppbx->e_crypt_rsa_n); + PDEBUG(DEBUG_CRYPT, "crypt: rsa e=%02x...\n", *apppbx->e_crypt_rsa_e); + PDEBUG(DEBUG_CRYPT, "crypt: key =%02x%02x%02x%02x... (len=%d)\n", apppbx->e_crypt_key[0], apppbx->e_crypt_key[1], apppbx->e_crypt_key[2], apppbx->e_crypt_key[3], apppbx->e_crypt_key_len); + apppbx->e_crypt_ckey_len = RSA_public_encrypt( + apppbx->e_crypt_key_len, + apppbx->e_crypt_key, + apppbx->e_crypt_ckey, + rsa, + RSA_PKCS1_PADDING); + PDEBUG(DEBUG_CRYPT, "crypt: ckey =%02x%02x%02x%02x... (len=%d)\n", apppbx->e_crypt_ckey[0], apppbx->e_crypt_ckey[1], apppbx->e_crypt_ckey[2], apppbx->e_crypt_ckey[3], apppbx->e_crypt_ckey_len); + RSA_free(rsa); + ememuse--; + if (apppbx->e_crypt_ckey_len > 0) + apppbx->e_crypt_keyengine_return = 1; + else + apppbx->e_crypt_keyengine_return = -1; +#endif + break; + + /* decrypt session key */ + case CK_DECRSA_REQ: +#ifndef CRYPTO + PERROR("No crypto lib.\n"); + apppbx->e_crypt_keyengine_return = -1; +#else + rsa = RSA_new(); + if (!rsa) + { + PERROR("Failed to allocate rsa structure.\n"); + apppbx->e_crypt_keyengine_return = 1; + break; + } + ememuse++; + rsa->n = BN_new(); + rsa->e = BN_new(); + rsa->d = BN_new(); + rsa->p = BN_new(); + rsa->q = BN_new(); + rsa->dmp1 = BN_new(); + rsa->dmq1 = BN_new(); + rsa->iqmp = BN_new(); + if (!rsa->n || !rsa->e + || !rsa->d || !rsa->p + || !rsa->q || !rsa->dmp1 + || !rsa->dmq1 || !rsa->iqmp) + { + PERROR("Failed to generate rsa structure.\n"); + apppbx->e_crypt_keyengine_return = 1; + RSA_free(rsa); + ememuse--; + break; + } + if (!BN_bin2bn(apppbx->e_crypt_rsa_n, apppbx->e_crypt_rsa_n_len, rsa->n)) + { + derror_bin2bn: + PERROR("Failed to convert binary to bignum.\n"); + apppbx->e_crypt_keyengine_return = -1; + RSA_free(rsa); + ememuse--; + break; + } + if (!BN_bin2bn(apppbx->e_crypt_rsa_e, apppbx->e_crypt_rsa_e_len, rsa->e)) + goto derror_bin2bn; + if (!BN_bin2bn(apppbx->e_crypt_rsa_d, apppbx->e_crypt_rsa_d_len, rsa->d)) + goto derror_bin2bn; + if (!BN_bin2bn(apppbx->e_crypt_rsa_p, apppbx->e_crypt_rsa_p_len, rsa->p)) + goto derror_bin2bn; + if (!BN_bin2bn(apppbx->e_crypt_rsa_q, apppbx->e_crypt_rsa_q_len, rsa->q)) + goto derror_bin2bn; + if (!BN_bin2bn(apppbx->e_crypt_rsa_dmp1, apppbx->e_crypt_rsa_dmp1_len, rsa->dmp1)) + goto derror_bin2bn; + if (!BN_bin2bn(apppbx->e_crypt_rsa_dmq1, apppbx->e_crypt_rsa_dmq1_len, rsa->dmq1)) + goto derror_bin2bn; + if (!BN_bin2bn(apppbx->e_crypt_rsa_iqmp, apppbx->e_crypt_rsa_iqmp_len, rsa->iqmp)) + goto derror_bin2bn; + PDEBUG(DEBUG_CRYPT, "decrypt: ckey =%02x%02x%02x%02x... (len=%d)\n", apppbx->e_crypt_ckey[0], apppbx->e_crypt_ckey[1], apppbx->e_crypt_ckey[2], apppbx->e_crypt_ckey[3], apppbx->e_crypt_ckey_len); + apppbx->e_crypt_key_len = RSA_private_decrypt( + apppbx->e_crypt_ckey_len, + apppbx->e_crypt_ckey, + apppbx->e_crypt_key, + rsa, + RSA_PKCS1_PADDING); + PDEBUG(DEBUG_CRYPT, "decrypt: key =%02x%02x%02x%02x... (len=%d)\n", apppbx->e_crypt_key[0], apppbx->e_crypt_key[1], apppbx->e_crypt_key[2], apppbx->e_crypt_key[3], apppbx->e_crypt_key_len); + RSA_free(rsa); + ememuse--; + apppbx->e_crypt_keyengine_return = 1; +#endif + break; + + default: + PERROR("Unknown job %d\n", job); + apppbx->e_crypt_keyengine_return = -1; + } + + done: + PDEBUG((DEBUG_EPOINT | DEBUG_CRYPT), "child process done after using libcrypto with return value %d\n", apppbx->e_crypt_keyengine_return); + + /* exit process */ + apppbx->ea_endpoint->ep_use--; + memset(args, 0, sizeof(struct auth_args)); + free(args); + amemuse--; + return(NULL); +} + +void EndpointAppPBX::cryptman_keyengine(int job) +{ + struct auth_args *arg; + pthread_t tid; + + if (e_crypt_keyengine_busy) + { + e_crypt_keyengine_return = -1; + PERROR("engine currently busy.\n"); + return; + } + + arg = (struct auth_args *)calloc(1, sizeof(struct auth_args)); + if (!arg) + { + PERROR("failed to alloc memory.\n"); + e_crypt_keyengine_return = -1; + return; + } + + arg->apppbx = this; + arg->job = job; + e_crypt_keyengine_return = 0; + e_crypt_keyengine_busy = job; + + ea_endpoint->ep_use++; + if ((pthread_create(&tid, NULL, keyengine_child, arg)<0)) + { + ea_endpoint->ep_use--; + PERROR("failed to create keyengine-thread.\n"); + e_crypt_keyengine_return = -1; + return; + } + amemuse++; + + PDEBUG((DEBUG_EPOINT | DEBUG_CRYPT), "send_mail(%d): child process created for doing crypto stuff\n", ea_endpoint->ep_serial); + +} + + +/* handler for authentication (called by apppbx's handler) + */ +void EndpointAppPBX::cryptman_handler(void) +{ + if (e_crypt_keyengine_busy) + { + if (e_crypt_keyengine_return < 0) + { + e_crypt_keyengine_busy = 0; + cryptman_message(CK_ERROR_IND, NULL, 0); + } else + if (e_crypt_keyengine_return > 0) + { + switch(e_crypt_keyengine_busy) + { + case CK_GENRSA_REQ: + e_crypt_keyengine_busy = 0; + cryptman_message(CK_GENRSA_CONF, NULL, 0); + break; + case CK_CPTRSA_REQ: + e_crypt_keyengine_busy = 0; + cryptman_message(CK_CPTRSA_CONF, NULL, 0); + break; + case CK_DECRSA_REQ: + e_crypt_keyengine_busy = 0; + cryptman_message(CK_DECRSA_CONF, NULL, 0); + break; + } + } + } + + /* check for event, make next event */ + if (e_crypt_timeout_sec) if (e_crypt_timeout_secep_serial, l); + return; + } + p = CM_GETINF(CM_INFO_RANDOM, buf); + ran = (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; + l = CM_SIZEOFINF(CM_INFO_BOGOMIPS); + if (l != 4) + { + PDEBUG(DEBUG_CRYPT, "EPOINT(%d) missing (or corrupt) random bogomips, just comparing random (len = %d)\n", ea_endpoint->ep_serial, l); + goto compare_random; + } + p = CM_GETINF(CM_INFO_BOGOMIPS, buf); + bogomips = (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; + if (e_crypt_bogomips > bogomips) + { + PDEBUG(DEBUG_CRYPT, "EPOINT(%d) our cpu is faster, so we are master (%d > %d)\n", ea_endpoint->ep_serial, e_crypt_bogomips, bogomips); + cr_master(message, NULL, 0); + return; + } + if (e_crypt_bogomips < bogomips) + { + PDEBUG(DEBUG_CRYPT, "EPOINT(%d) our cpu is slower, so we are slave (%d < %d)\n", ea_endpoint->ep_serial, e_crypt_bogomips, bogomips); + cr_slave(message, NULL, 0); + return; + } + PDEBUG(DEBUG_CRYPT, "EPOINT(%d) our cpu is equal speed, so we check for random value (%d == %d)\n", ea_endpoint->ep_serial, e_crypt_bogomips, bogomips); + compare_random: + /* bogomips are equal, so we compare */ + if (e_crypt_random > ran) + { + PDEBUG(DEBUG_CRYPT, "EPOINT(%d) our random value is greater, so we are master (%d > %d)\n", ea_endpoint->ep_serial, e_crypt_random, ran); + cr_master(message, NULL, 0); + return; + } + if (e_crypt_random < ran) + { + PDEBUG(DEBUG_CRYPT, "EPOINT(%d) our random value is smaller, so we are slave (%d < %d)\n", ea_endpoint->ep_serial, e_crypt_random, ran); + cr_slave(message, NULL, 0); + return; + } + PDEBUG(DEBUG_CRYPT, "EPOINT(%d) random values are equal, so we are looped (%d == %d)\n", ea_endpoint->ep_serial, e_crypt_random, ran); + cr_looped(message, NULL, 0); +} + +/* key-exchange activation by the user */ +void EndpointAppPBX::cr_activate(int message, unsigned char *param, int len) +{ + unsigned char buf[128] = ""; + unsigned char msg; + unsigned char bogomips[4], ran[4]; + + /* activate listener */ + cryptman_msg2crengine(CR_LISTEN_REQ, NULL, 0); + /* send ident message */ + msg = CMSG_IDENT; + CM_ADDINF(CM_INFO_MESSAGE, 1, &msg); + /* random number element */ + srandom(now_tv.tv_sec ^ now_tv.tv_usec ^ random()); + e_crypt_random = random(); + ran[0] = e_crypt_random >> 24; + ran[1] = e_crypt_random >> 16; + ran[2] = e_crypt_random >> 8; + ran[3] = e_crypt_random; + CM_ADDINF(CM_INFO_RANDOM, 4, ran); + /* cpu speed element */ + e_crypt_bogomips = get_bogomips(); + if (e_crypt_bogomips > 0) + { + bogomips[0] = e_crypt_bogomips >> 24; + bogomips[1] = e_crypt_bogomips >> 16; + bogomips[2] = e_crypt_bogomips >> 8; + bogomips[3] = e_crypt_bogomips; + CM_ADDINF(CM_INFO_BOGOMIPS, 4, bogomips); + } + /* send ident message */ + cryptman_msg2peer(buf); + /* change state */ + cryptman_state(CM_ST_IDENT); + /* set timeout */ + cryptman_timeout(CM_TO_IDENT); +} + +/* deactivation by the user */ +void EndpointAppPBX::cr_deactivate(int message, unsigned char *param, int len) +{ + unsigned char buf[128] = ""; + unsigned char msg; + + /* deactivate listener (if not already) */ + cryptman_msg2crengine(CR_UNLISTEN_REQ, NULL, 0); + /* message */ + msg = CMSG_ABORT; + CM_ADDINF(CM_INFO_MESSAGE, 1, &msg); + cryptman_msg2peer(buf); + /* deactivate encryption */ + cryptman_msg2crengine(CC_DACT_REQ, NULL, 0); + /* change state */ + cryptman_state(CM_ST_NULL); + /* send message to user */ + cryptman_msg2user(CU_DACT_CONF, "Deactivated"); +} + +/* remote peer tells us to be master */ +void EndpointAppPBX::cr_master(int message, unsigned char *param, int len) +{ + unsigned char buf[128] = ""; + unsigned char msg; + + /* change to master state */ + cryptman_state(CM_ST_KEYGEN); + if (message == CP_IDENT) + { + /* send you-are-slave-message */ + msg = CMSG_SLAVE; + CM_ADDINF(CM_INFO_MESSAGE, 1, &msg); + cryptman_msg2peer(buf); + } + /* start generation of key */ + cryptman_keyengine(CK_GENRSA_REQ); + /* disable timeout */ + cryptman_timeout(0); + /* send message to user */ + cryptman_msg2user(CU_INFO_IND, "Master"); +} + +/* remote peer tells us to be slave */ +void EndpointAppPBX::cr_slave(int message, unsigned char *param, int len) +{ + unsigned char buf[128] = ""; + unsigned char msg; + + /* change to slave state */ + cryptman_state(CM_ST_KEYWAIT); + if (message == CP_IDENT) + { + /* send you-are-slave-message */ + msg = CMSG_MASTER; + /* message */ + CM_ADDINF(CM_INFO_MESSAGE, 1, &msg); + cryptman_msg2peer(buf); + } + /* set timeout */ + cryptman_timeout(CM_TO_PUBKEY); + /* send message to user */ + cryptman_msg2user(CU_INFO_IND, "Slave"); +} + +/* remote peer tells us about loop */ +void EndpointAppPBX::cr_looped(int message, unsigned char *param, int len) +{ + unsigned char buf[128] = ""; + unsigned char msg; + + /* change to idle state */ + cryptman_state(CM_ST_NULL); + /* deactivate listener */ + cryptman_msg2crengine(CR_UNLISTEN_REQ, NULL, 0); + if (message == CP_IDENT) + { + /* send looped */ + msg = CMSG_LOOPED; + /* message */ + CM_ADDINF(CM_INFO_MESSAGE, 1, &msg); + cryptman_msg2peer(buf); + } + /* disable timeout */ + cryptman_timeout(0); + /* send message to user */ + cryptman_msg2user(CU_ERROR_IND, "Loop Detected"); +} + +/* abort */ +void EndpointAppPBX::cr_abort(int message, unsigned char *param, int len) +{ + /* if already encrypting */ + if (e_crypt_state==CM_ST_WAIT_CRYPT + || e_crypt_state==CM_ST_SWAIT + || e_crypt_state==CM_ST_ACTIVE) + { + /* deactivate blowfish */ + cryptman_msg2crengine(CC_DACT_REQ, NULL, 0); + } + /* change to idle state */ + cryptman_state(CM_ST_NULL); + /* deactivate listener */ + cryptman_msg2crengine(CR_UNLISTEN_REQ, NULL, 0); + /* disable timeout */ + cryptman_timeout(0); + /* send message to user */ + if (message == CT_TIMEOUT) + cryptman_msg2user(CU_ERROR_IND, "Timeout"); + else if (message == CP_ABORT) + cryptman_msg2user(CU_ERROR_IND, "Remote Abort"); + else + cryptman_msg2user(CU_DACT_IND, NULL); +} + +/* abort but wait for engine to release*/ +void EndpointAppPBX::cr_abort_engine(int message, unsigned char *param, int len) +{ + /* change to release state */ + cryptman_state(CM_ST_RELEASE); + /* deactivate listener */ + cryptman_msg2crengine(CR_UNLISTEN_REQ, NULL, 0); + /* disable timeout */ + cryptman_timeout(0); + /* send message to user */ + if (message == CT_TIMEOUT) + cryptman_msg2user(CU_ERROR_IND, "Timeout"); + else if (message == CP_ABORT) + cryptman_msg2user(CU_ERROR_IND, "Remote Abort"); + else + cryptman_msg2user(CU_DACT_IND, NULL); +} + +/* abort and disable crypt engine */ +void EndpointAppPBX::cr_abort_wait(int message, unsigned char *param, int len) +{ + /* change to idle state */ + cryptman_state(CM_ST_NULL); + /* deactivate listener (if not already) */ + cryptman_msg2crengine(CR_UNLISTEN_REQ, NULL, 0); + /* deactivate blowfish */ + cryptman_msg2crengine(CC_DACT_REQ, NULL, 0); + /* disable timeout */ + cryptman_timeout(0); + /* send message to user */ + if (message == CT_TIMEOUT) + cryptman_msg2user(CU_ERROR_IND, "Timeout"); + else if (message == CP_ABORT) + cryptman_msg2user(CU_ERROR_IND, "Remote Abort"); + else + cryptman_msg2user(CU_DACT_IND, NULL); +} + +/* key engine tells us that the rsa is ready */ +void EndpointAppPBX::cr_genrsa(int message, unsigned char *param, int len) +{ + unsigned char buf[1024] = ""; + unsigned char msg; + + /* change to wait for crypted session key state */ + cryptman_state(CM_ST_CSWAIT); + /* message */ + msg = CMSG_PUBKEY; + CM_ADDINF(CM_INFO_MESSAGE, 1, &msg); + CM_ADDINF(CM_INFO_PUBKEY, e_crypt_rsa_n_len, &e_crypt_rsa_n); + CM_ADDINF(CM_INFO_PUBEXPONENT, e_crypt_rsa_e_len, &e_crypt_rsa_e); + cryptman_msg2peer(buf); + /* set timeout */ + cryptman_timeout(CM_TO_CSKEY); +} + +/* our engine has a key error */ +void EndpointAppPBX::cr_keyerror(int message, unsigned char *param, int len) +{ + unsigned char buf[128] = ""; + unsigned char msg; + + /* change to idle state */ + cryptman_state(CM_ST_NULL); + /* deactivate listener */ + cryptman_msg2crengine(CR_UNLISTEN_REQ, NULL, 0); + /* message */ + msg = CMSG_ABORT; + CM_ADDINF(CM_INFO_MESSAGE, 1, &msg); + cryptman_msg2peer(buf); + /* send message to user */ + cryptman_msg2user(CU_ERROR_IND, "Local Key Error"); +} + +/* remote sends us the rsa public key */ +void EndpointAppPBX::cr_pubkey(int message, unsigned char *param, int len) +{ + unsigned char buf[128] = ""; + unsigned char msg = CMSG_ABORT; + int l; + + l = CM_SIZEOFINF(CM_INFO_PUBKEY); + if (l<1 || l>(int)sizeof(e_crypt_rsa_n)) + { + size_error: + /* change to idle state */ + cryptman_state(CM_ST_NULL); + /* deactivate listener */ + cryptman_msg2crengine(CR_UNLISTEN_REQ, NULL, 0); + /* message */ + CM_ADDINF(CM_INFO_MESSAGE, 1, &msg); + cryptman_msg2peer(buf); + /* send message to user */ + cryptman_msg2user(CU_ERROR_IND, "Remote Key Error"); + return; + } + CM_GETINF(CM_INFO_PUBKEY, e_crypt_rsa_n); + e_crypt_rsa_n_len = l; + l = CM_SIZEOFINF(CM_INFO_PUBEXPONENT); + if (l<1 || l>(int)sizeof(e_crypt_rsa_e)) + goto size_error; + CM_GETINF(CM_INFO_PUBEXPONENT, e_crypt_rsa_e); + e_crypt_rsa_e_len = l; + /* change to generating encrypted sessnion key state */ + cryptman_state(CM_ST_CSKEY); + /* start generation of crypted session key */ + cryptman_keyengine(CK_CPTRSA_REQ); + /* disable timeout */ + cryptman_timeout(0); +} + +/* key engine tells us that the crypted session key is ready */ +void EndpointAppPBX::cr_cptrsa(int message, unsigned char *param, int len) +{ + unsigned char buf[1024] = ""; + unsigned char msg = CMSG_CSKEY; + + /* change to wait for crypt engine state */ + cryptman_state(CM_ST_WAIT_DELAY); + /* message */ + CM_ADDINF(CM_INFO_MESSAGE, 1, &msg); + CM_ADDINF(CM_INFO_CSKEY, e_crypt_ckey_len, &e_crypt_ckey); + cryptman_msg2peer(buf); + /* deactivate listener */ + cryptman_msg2crengine(CR_UNLISTEN_REQ, NULL, 0); + /* timeout 1 sec */ + cryptman_timeout(1); +} + +/* now we waited for the remote to receive and decrypt the session key */ +void EndpointAppPBX::cr_waitdelay(int message, unsigned char *param, int len) +{ + /* change to wait for crypt engine state */ + cryptman_state(CM_ST_WAIT_CRYPT); + /* disable timeout */ + cryptman_timeout(0); + /* send message to crypt engine */ + cryptman_msg2crengine(CC_ACTBF_REQ, e_crypt_key, e_crypt_key_len); +} + +/* remote sends us the crypted session key */ +void EndpointAppPBX::cr_cskey(int message, unsigned char *param, int len) +{ + unsigned char buf[128] = ""; + unsigned char msg = CMSG_ABORT; + int l; + + /* disable timeout */ + cryptman_timeout(0); + l = CM_SIZEOFINF(CM_INFO_CSKEY); + if (l<1 || l>(int)sizeof(e_crypt_ckey)) + { + /* change to idle state */ + cryptman_state(CM_ST_NULL); + /* deactivate listener */ + cryptman_msg2crengine(CR_UNLISTEN_REQ, NULL, 0); + /* message */ + CM_ADDINF(CM_INFO_MESSAGE, 1, &msg); + cryptman_msg2peer(buf); + /* send message to user */ + cryptman_msg2user(CU_ERROR_IND, "Remote Key Error"); + return; + } + CM_GETINF(CM_INFO_CSKEY, e_crypt_ckey); + e_crypt_ckey_len = l; + /* change to generating decrypted session key state */ + cryptman_state(CM_ST_SESSION); + /* start generation of decrypted session key */ + cryptman_keyengine(CK_DECRSA_REQ); +} + +/* key engine tells us that the decrypted session key is ready */ +void EndpointAppPBX::cr_decrsa(int message, unsigned char *param, int len) +{ + /* change to wait for crypt engine state */ + cryptman_state(CM_ST_WAIT_CRYPT); + /* deactivate listener */ + cryptman_msg2crengine(CR_UNLISTEN_REQ, NULL, 0); + /* send message to crypt engine */ + cryptman_msg2crengine(CC_ACTBF_REQ, e_crypt_key, e_crypt_key_len); +} + +/* blowfish now active */ +void EndpointAppPBX::cr_bfactive(int message, unsigned char *param, int len) +{ + char text[64]; + + /* change to active state */ + cryptman_state(CM_ST_ACTIVE); + /* send message to user */ + SPRINT(text, "PUB %02x%02x %02x%02x %02x%02x %02x%02x", e_crypt_key[0], e_crypt_key[1], e_crypt_key[2], e_crypt_key[3], e_crypt_key[4], e_crypt_key[5], e_crypt_key[6], e_crypt_key[7]); + cryptman_msg2user(CU_ACTK_CONF, text); +} + +/* our crypt engine sends an error */ +void EndpointAppPBX::cr_crypterror(int message, unsigned char *param, int len) +{ + unsigned char buf[128] = ""; + unsigned char msg = CMSG_ABORT; + + /* change to idle state */ + cryptman_state(CM_ST_NULL); + /* deactivate listener */ + cryptman_msg2crengine(CR_UNLISTEN_REQ, NULL, 0); + /* message */ + CM_ADDINF(CM_INFO_MESSAGE, 1, &msg); + cryptman_msg2peer(buf); + /* send message to user */ + cryptman_msg2user(CU_ERROR_IND, "Blowfish Error"); +} + +/* engine is done, now we are done with release */ +void EndpointAppPBX::cr_release(int message, unsigned char *param, int len) +{ + /* change to idle state */ + cryptman_state(CM_ST_NULL); +} + +/* activate using shared key */ +void EndpointAppPBX::cr_sactivate(int message, unsigned char *param, int len) +{ + /* change to 'wait for crypt engine' state */ + cryptman_state(CM_ST_SWAIT); + /* disable timeout */ + cryptman_timeout(0); + /* send key to crypt engine */ + cryptman_msg2crengine(CC_ACTBF_REQ, param, len); +} + +/* share key deactivation by the user */ +void EndpointAppPBX::cr_sdeactivate(int message, unsigned char *param, int len) +{ + /* deactivate encryption */ + cryptman_msg2crengine(CC_DACT_REQ, NULL, 0); + /* change state */ + cryptman_state(CM_ST_NULL); + /* send message to user */ + cryptman_msg2user(CU_DACT_CONF, NULL); +} + +/* shared key abort */ +void EndpointAppPBX::cr_sabort(int message, unsigned char *param, int len) +{ + /* change to idle state */ + cryptman_state(CM_ST_NULL); + /* send message to user */ + cryptman_msg2user(CU_DACT_IND, "Deactivated"); +} + +/* shared key: our crypt engine sends an error */ +void EndpointAppPBX::cr_scrypterror(int message, unsigned char *param, int len) +{ + /* change to idle state */ + cryptman_state(CM_ST_NULL); + /* send message to user */ + cryptman_msg2user(CU_ERROR_IND, "Blowfish Error"); +} + +/* blowfish now active */ +void EndpointAppPBX::cr_sbfactive(int message, unsigned char *param, int len) +{ + char text[64]; + + /* change to active state */ + cryptman_state(CM_ST_SACTIVE); + /* send message to user */ + SPRINT(text, "Call Secure"); + cryptman_msg2user(CU_ACTS_CONF, text); +} + +/* user requests info */ +void EndpointAppPBX::cr_info(int message, unsigned char *param, int len) +{ + /* send message to user */ + cryptman_msg2user(CU_INFO_CONF, e_crypt_info); +} + + +CM_MSG_NAMES + +void EndpointAppPBX::cryptman_message(int message, unsigned char *param, int len) +{ + char *msgtext = "<>"; + + if (message>=0 && messageep_serial, statename(e_crypt_state), msgtext, len); + + /* all states */ + if (message == CU_INFO_REQ) + { cr_info(message, param, len); return; } + + switch(e_crypt_state) + { + /* in idle state */ + case CM_ST_NULL: + if (message == CU_ACTK_REQ) /* request key-exchange encryption */ + { cr_activate(message, param, len); return; } + if (message == CU_ACTS_REQ) /* request shared encryption */ + { cr_sactivate(message, param, len); return; } + break; + + /* identifying state */ + case CM_ST_IDENT: + if (message == CP_IDENT) /* request encryption */ + { cr_ident(message, param, len); return; } + if (message == CP_SLAVE) /* we are slave */ + { cr_slave(message, param, len); return; } + if (message == CP_MASTER) /* we are master */ + { cr_master(message, param, len); return; } + if (message == CP_LOOPED) /* we are looped */ + { cr_looped(message, param, len); return; } + if (message == CI_DISCONNECT_IND /* request aborting */ + || message == CT_TIMEOUT /* timeout */ + || message == CP_ABORT) /* request aborting */ + { cr_abort(message, param, len); return; } + break; + + /* generating public key state */ + case CM_ST_KEYGEN: + if (message == CK_GENRSA_CONF) /* public key is done */ + { cr_genrsa(message, param, len); return; } + if (message == CK_ERROR_IND) /* key failed */ + { cr_keyerror(message, param, len); return; } + if (message == CI_DISCONNECT_IND /* request aborting */ + || message == CP_ABORT) /* request aborting */ + { cr_abort_engine(message, param, len); return; } + break; + + /* waiting for public key state */ + case CM_ST_KEYWAIT: + if (message == CP_PUBKEY) /* getting public key from remote */ + { cr_pubkey(message, param, len); return; } + if (message == CI_DISCONNECT_IND /* request aborting */ + || message == CT_TIMEOUT /* timeout */ + || message == CP_ABORT) /* request aborting */ + { cr_abort(message, param, len); return; } + break; + + /* generating crypted session key state */ + case CM_ST_CSKEY: + if (message == CK_CPTRSA_CONF) /* crypted session key is done */ + { cr_cptrsa(message, param, len); return; } + if (message == CK_ERROR_IND) /* key failed */ + { cr_keyerror(message, param, len); return; } + if (message == CI_DISCONNECT_IND /* request aborting */ + || message == CP_ABORT) /* request aborting */ + { cr_abort_engine(message, param, len); return; } + break; + + /* waiting for crypted session key state */ + case CM_ST_CSWAIT: + if (message == CP_CSKEY) /* getting crypted session key from remote */ + { cr_cskey(message, param, len); return; } + if (message == CI_DISCONNECT_IND /* request aborting */ + || message == CT_TIMEOUT /* timeout */ + || message == CP_ABORT) /* request aborting */ + { cr_abort(message, param, len); return; } + break; + + /* generating decrypted session key state */ + case CM_ST_SESSION: + if (message == CK_DECRSA_CONF) /* decrypted is done */ + { cr_decrsa(message, param, len); return; } + if (message == CK_ERROR_IND) /* key failed */ + { cr_keyerror(message, param, len); return; } + if (message == CI_DISCONNECT_IND /* request aborting */ + || message == CP_ABORT) /* request aborting */ + { cr_abort_engine(message, param, len); return; } + break; + + /* wait encryption on state */ + case CM_ST_WAIT_DELAY: + if (message == CT_TIMEOUT) /* timeout of delay */ + { cr_waitdelay(message, param, len); return; } + if (message == CC_ERROR_IND) /* encrpytion error */ + { cr_crypterror(message, param, len); return; } + if (message == CI_DISCONNECT_IND /* request aborting */ + || message == CP_ABORT) /* request aborting */ + { cr_abort_wait(message, param, len); return; } + break; + + /* wait encryption on state */ + case CM_ST_WAIT_CRYPT: + if (message == CC_ACTBF_CONF) /* encrpytion active */ + { cr_bfactive(message, param, len); return; } + if (message == CC_ERROR_IND) /* encrpytion error */ + { cr_crypterror(message, param, len); return; } + if (message == CI_DISCONNECT_IND /* request aborting */ + || message == CP_ABORT) /* request aborting */ + { cr_abort_wait(message, param, len); return; } + break; + + /* active state */ + case CM_ST_ACTIVE: + if (message == CU_DACT_REQ) /* deactivating encryption */ + { cr_deactivate(message, param, len); return; } + if (message == CI_DISCONNECT_IND /* request aborting */ + || message == CP_ABORT) /* request aborting */ + { cr_abort(message, param, len); return; } + break; + + + /* engine done after abort state */ + case CM_ST_RELEASE: + if (message == CK_GENRSA_CONF /* engine done */ + || message == CK_CPTRSA_CONF /* engine done */ + || message == CK_DECRSA_CONF /* engine done */ + || message == CK_ERROR_IND) /* engine error */ + { cr_release(message, param, len); return; } + break; + + /* shared active state */ + case CM_ST_SACTIVE: + if (message == CU_DACT_REQ) /* deactivating encryption */ + { cr_sdeactivate(message, param, len); return; } + if (message == CI_DISCONNECT_IND) /* request aborting */ + { cr_sabort(message, param, len); return; } + break; + + /* wait shared encryption on state */ + case CM_ST_SWAIT: + if (message == CC_ACTBF_CONF) /* encrpytion active */ + { cr_sbfactive(message, param, len); return; } + if (message == CC_ERROR_IND) /* encrpytion error */ + { cr_scrypterror(message, param, len); return; } + if (message == CI_DISCONNECT_IND) /* request aborting */ + { cr_sabort(message, param, len); return; } + break; + + } + + PDEBUG(DEBUG_CRYPT, "message not handled in state %d\n", e_crypt_state); +} + + +/* + * analyze the message element within the received message from peer and call 'cryptman_message' + */ +void EndpointAppPBX::cryptman_msg2man(unsigned char *param, int len) +{ + unsigned char *p; + unsigned char msg; + int i, l; + + /* check if frame is correct */ + PDEBUG(DEBUG_CRYPT, "EPOINT(%d) message from peer to crypt_manager.\n", ea_endpoint->ep_serial); + if (len == 0) + { + PDEBUG(DEBUG_CRYPT, "ignoring message with 0-length.\n"); + return; + } + i = 0; + p = param; + while(*p) + { + if (i == len) + { + PDEBUG(DEBUG_CRYPT, "end of message without 0-termination.\n"); + return; + } + if (i+3 > len) + { + PDEBUG(DEBUG_CRYPT, "message with element size, outside the frame length.\n"); + return; + } + l = (p[1]<<8) + p[2]; +// PDEBUG(DEBUG_CRYPT, " inf %d (len = %d)\n", *p, l); + if (i+3+l > len) + { + PDEBUG(DEBUG_CRYPT, "message with element data, outside the frame length.\n"); + return; + } + i += l + 3; + p += l + 3; + } + if (i+1 != len) + { + PDEBUG(DEBUG_CRYPT, "warning: received null-element before end of frame.\n"); + } + + l = CM_SIZEOFINF(CM_INFO_MESSAGE); + if (l != 1) + { + PDEBUG(DEBUG_CRYPT, "received message without (valid) message element (len = %d)\n", len); + return; + } + CM_GETINF(CM_INFO_MESSAGE, &msg); + switch (msg) + { + case CMSG_IDENT: + cryptman_message(CP_IDENT, param, len); + break; + case CMSG_SLAVE: + cryptman_message(CP_SLAVE, param, len); + break; + case CMSG_MASTER: + cryptman_message(CP_MASTER, param, len); + break; + case CMSG_PUBKEY: + cryptman_message(CP_PUBKEY, param, len); + break; + case CMSG_CSKEY: + cryptman_message(CP_CSKEY, param, len); + break; + case CMSG_ABORT: + cryptman_message(CP_ABORT, param, len); + break; + default: + PDEBUG(DEBUG_CRYPT, "received unknown message element %d\n", msg); + } +} + +/* add information element to buffer + */ +void EndpointAppPBX::cryptman_addinf(unsigned char *buf, int buf_size, int element, int len, void *data) +{ + int l; + + /* skip what we already have in the buffer */ + while (buf[0]) + { + l = (buf[1]<<8) + buf[2]; + if (l >= buf_size-3) + { + PERROR("EPOINT(%d) buffer overflow while adding information to peer message.\n", ea_endpoint->ep_serial); + return; + } + buf_size -= l + 3; + buf += l + 3; + } + /* check if we have not enough space to add element including id, len, data, and the null-termination */ + if (len+4 > buf_size) + { + PERROR("EPOINT(%d) cannot add element to message, because buffer would overflow.\n", ea_endpoint->ep_serial); + return; + } + buf[0] = element; + buf[1] = len >> 8; + buf[2] = len; + memcpy(buf+3, data, len); +} + + +/* get size of element in buffer + */ +int EndpointAppPBX::cryptman_sizeofinf(unsigned char *buf, int element) +{ + int l; + + /* skip what we already have in the buffer */ + while (buf[0]) + { + l = (buf[1]<<8) + buf[2]; + if (buf[0] == element) + return(l); + buf += l + 3; + } + return(-1); +} + + +/* get information element from buffer + */ +unsigned char *EndpointAppPBX::cryptman_getinf(unsigned char *buf, int element, unsigned char *to) +{ + int l; + + /* skip what we already have in the buffer */ + while (buf[0]) + { + l = (buf[1]<<8) + buf[2]; + if (buf[0] == element) + { + memcpy(to, buf+3, l); + return(to); + } + buf += l + 3; + } + return(NULL); +} + + +/* send message to peer + */ +void EndpointAppPBX::cryptman_msg2peer(unsigned char *buf) +{ + struct message *message; + unsigned char *p = buf; + int len = 0; + int l; + + /* get len */ + while(p[0]) + { + l = (p[1]<<8) + p[2]; + len += l + 3; + p += l + 3; + } + if (len+1 > (int)sizeof(message->param.crypt.data)) + { + PERROR("EPOINT(%d) message larger than allowed in param->crypt.data.\n", ea_endpoint->ep_serial); + return; + } + /* send message */ + message = message_create(ea_endpoint->ep_serial, ea_endpoint->ep_portlist->port_id, EPOINT_TO_PORT, MESSAGE_CRYPT); + message->param.crypt.type = CR_MESSAGE_REQ; + message->param.crypt.len = len+1; + memcpy(message->param.crypt.data, buf, len+1); + message_put(message); + + if (options.deb & DEBUG_CRYPT) + { + PDEBUG(DEBUG_CRYPT, "EPOINT(%d) sending message\n", ea_endpoint->ep_serial); + p = buf; + while(p[0]) + { + l = (p[1]<<8) + p[2]; + PDEBUG(DEBUG_CRYPT, " inf %d (len = %d)\n", p[0], l); + len += l + 3; + p += l + 3; + } + } +} + +/* send message to crypt engine + */ +void EndpointAppPBX::cryptman_msg2crengine(int msg, unsigned char *buf, int len) +{ + struct message *message; + + if (len > (int)sizeof(message->param.crypt.data)) + { + PERROR("EPOINT(%d) message larger than allowed in param->crypt.data.\n", ea_endpoint->ep_serial); + return; + } + /* send message */ + message = message_create(ea_endpoint->ep_serial, ea_endpoint->ep_portlist->port_id, EPOINT_TO_PORT, MESSAGE_CRYPT); + message->param.crypt.type = msg; + message->param.crypt.len = len; + if (len) + memcpy(message->param.crypt.data, buf, len); + message_put(message); + + if (options.deb & DEBUG_CRYPT) + { + char *msgtext = "<>"; + + if (msg>=0 && msgep_serial, msgtext, len); + } +} + +/* send message to user + */ +void EndpointAppPBX::cryptman_msg2user(int msg, char *text) +{ + struct message *message; + /* send message */ + message = message_create(ea_endpoint->ep_serial, ea_endpoint->ep_call_id, EPOINT_TO_CALL, MESSAGE_CRYPT); + message->param.crypt.type = msg; + if (!text) + text = ""; + SCPY(e_crypt_info, text); + if (text[0]) + { + UNCPY((char *)message->param.crypt.data, e_crypt_info, sizeof(message->param.crypt.data)-1); + message->param.crypt.len = strlen((char *)message->param.crypt.data)+1; + } + message_put(message); + + if (options.deb & DEBUG_CRYPT) + { + char *msgtext = "<>"; + + if (msg>=0 && msgep_serial, msgtext, text?text:""); + } +} + +/* change state + */ +void EndpointAppPBX::cryptman_state(int state) +{ + PDEBUG(DEBUG_CRYPT, "Changing state from %s to %s\n", statename(e_crypt_state), statename(state)); + e_crypt_state = state; +} + + +/* set timeout + */ +void EndpointAppPBX::cryptman_timeout(int secs) +{ + if (secs) + { + e_crypt_timeout_sec = now_tv.tv_sec+secs; + e_crypt_timeout_usec = now_tv.tv_usec; + PDEBUG(DEBUG_CRYPT, "Changing timeout to %d seconds\n", secs); + } else + { + e_crypt_timeout_sec = 0; + e_crypt_timeout_usec = 0; + PDEBUG(DEBUG_CRYPT, "turning timeout off\n", secs); + } +} + +/* encode a message to be sent via b-channel + */ +int cryptman_encode_bch(unsigned char *data, int len, unsigned char *buf, int buf_len) +{ + unsigned long crc; + int overhead = 18; + + len--; /* without null-termination */ + if (buf_len < len+overhead) + { + PERROR("frame too long for buffer"); + return(0); + } + PDEBUG(DEBUG_CRYPT, "encoding a block of %d bytes.\n", len); + + /* write identification sequence to the header */ + UNCPY((char *)buf, "CRYPTMAN" ,8); + buf += 8; + /* length + checksumme */ + buf[0] = len >> 8; + buf[1] = len & 0xff; + crc = crc32(buf, 2); + buf += 2; + buf[0] = crc >> 24; + buf[1] = crc >> 16; + buf[2] = crc >> 8; + buf[3] = crc; + buf += 4; + /* data + checksumme */ + memcpy(buf, data, len); + crc = crc32(buf, len); + buf += len; + buf[0] = crc >> 24; + buf[1] = crc >> 16; + buf[2] = crc >> 8; + buf[3] = crc; + buf += 4; + return(len + overhead); +} + +/* decode a message from b-channel + */ +void PmISDN::cryptman_listen_bch(unsigned char *p, int l) +{ + int i; + struct message *message; + + retry: + if (!l) + return; + + /* check for the keyword */ + if (p_m_crypt_listen_state == 0) + { + while((*p++)!='C' && l) + l--; + if (!l) + return; + l--; + p_m_crypt_listen_state++; + if (!l) + return; + } + if (p_m_crypt_listen_state < 8) + { + i = p_m_crypt_listen_state; + while(i < 8) + { + l--; + if (*p++ != "CRYPTMAN"[i]) + { + p_m_crypt_listen_state = 0; + goto retry; + } + p_m_crypt_listen_state++; + if (!l) + break; + i++; + } + if (!l) + return; + } + /* high byte of length */ + if (p_m_crypt_listen_state == 8) + { + p_m_crypt_listen_len = (*p++) << 8; + p_m_crypt_listen_state++; + if (!(--l)) + return; + } + /* low byte of length */ + if (p_m_crypt_listen_state == 9) + { + p_m_crypt_listen_len += *p++; + p_m_crypt_listen_state++; + if (!(--l)) + return; + } + /* crc */ + if (p_m_crypt_listen_state == 10) + { + p_m_crypt_listen_crc = (*p++) << 24; + p_m_crypt_listen_state++; + if (!(--l)) + return; + } + if (p_m_crypt_listen_state == 11) + { + p_m_crypt_listen_crc += (*p++) << 16; + p_m_crypt_listen_state++; + if (!(--l)) + return; + } + if (p_m_crypt_listen_state == 12) + { + p_m_crypt_listen_crc += (*p++) << 8; + p_m_crypt_listen_state++; + if (!(--l)) + return; + } + if (p_m_crypt_listen_state == 13) + { + unsigned char lencheck[2]; + p_m_crypt_listen_crc += *p++; + /* check for CRC */ + lencheck[0] = p_m_crypt_listen_len >> 8; + lencheck[1] = p_m_crypt_listen_len & 0xff; + if (crc32(lencheck, 2) != p_m_crypt_listen_crc) + { + PDEBUG(DEBUG_CRYPT, "PmISDN(%s) received a block of %d bytes, but checksumme of length is incorrect (must %08x is %08x\n", p_name, p_m_crypt_listen_len, crc32(lencheck, 2), p_m_crypt_listen_crc); + p_m_crypt_listen_state = 0; + goto retry; + } + if (p_m_crypt_listen_len > (int)sizeof(p_m_crypt_listen_msg)) + { + PDEBUG(DEBUG_CRYPT, "PmISDN(%s) received a block of %d bytes, but too big for buffer (%d bytes)\n", p_name, p_m_crypt_listen_len, sizeof(p_m_crypt_listen_msg)); + p_m_crypt_listen_state = 0; + goto retry; + } + if (!p_m_crypt_listen_len) + { + PDEBUG(DEBUG_CRYPT, "PmISDN(%s) received a block of 0 bytes\n", p_name); + p_m_crypt_listen_state = 0; + goto retry; + } + p_m_crypt_listen_state++; + if (!(--l)) + return; + } + /* read message */ + while (p_m_crypt_listen_state>=14 && p_m_crypt_listen_state<(p_m_crypt_listen_len+14)) + { + p_m_crypt_listen_msg[p_m_crypt_listen_state-14] = *p++; + p_m_crypt_listen_state++; + if (!(--l)) + return; + } + /* crc */ + if (p_m_crypt_listen_state == 14+p_m_crypt_listen_len) + { + p_m_crypt_listen_crc = (*p++) << 24; + p_m_crypt_listen_state++; + if (!(--l)) + return; + } + if (p_m_crypt_listen_state == 15+p_m_crypt_listen_len) + { + p_m_crypt_listen_crc += (*p++) << 16; + p_m_crypt_listen_state++; + if (!(--l)) + return; + } + if (p_m_crypt_listen_state == 16+p_m_crypt_listen_len) + { + p_m_crypt_listen_crc += (*p++) << 8; + p_m_crypt_listen_state++; + if (!(--l)) + return; + } + if (p_m_crypt_listen_state == 17+p_m_crypt_listen_len) + { + l--; + p_m_crypt_listen_crc += *p++; + /* check for CRC */ + if (crc32(p_m_crypt_listen_msg, p_m_crypt_listen_len) != p_m_crypt_listen_crc) + { + PDEBUG(DEBUG_CRYPT, "PmISDN(%s) received a block of %d bytes, but checksumme of data block is incorrect\n", p_name, p_m_crypt_listen_len); + p_m_crypt_listen_state = 0; + if (!l) + return; + goto retry; + } + /* now send message */ + p_m_crypt_listen_state = 0; + PDEBUG(DEBUG_CRYPT, "PmISDN(%s) received a block of %d bytes sending to crypt manager\n", p_name, p_m_crypt_listen_len); + if ((int)sizeof(message->param.crypt.data) < p_m_crypt_listen_len+1) /* null-terminated */ + { + PDEBUG(DEBUG_CRYPT, "PmISDN(%s) received a block of %d bytes that is too large for message buffer\n", p_name, p_m_crypt_listen_len); + if (!l) + return; + goto retry; + } + message = message_create(p_serial, ACTIVE_EPOINT(p_epointlist), PORT_TO_EPOINT, MESSAGE_CRYPT); + message->param.crypt.type = CR_MESSAGE_IND; + message->param.crypt.len = p_m_crypt_listen_len+1; /* null termination */ + memcpy(message->param.crypt.data, p_m_crypt_listen_msg, p_m_crypt_listen_len); + message_put(message); + p_m_crypt_listen_state = 0; + if (!l) + return; + goto retry; + } +} + + +/* encrypt call using share secret (keypad function) + */ +void EndpointAppPBX::encrypt_shared(void) +{ + struct message *message; + char *errstr = ""; + class Port *port; + int type, key_len; + unsigned char *key; + char *auth_pointer, *crypt_pointer, *key_pointer; + int ret; + + /* redisplay current crypt display */ + if (e_crypt != CRYPT_OFF) + { + PDEBUG(DEBUG_EPOINT, "EPOINT(%d) encryption in progress, so we request the current message.\n", ea_endpoint->ep_serial); + message = message_create(ea_endpoint->ep_serial, ea_endpoint->ep_call_id, EPOINT_TO_CALL, MESSAGE_CRYPT); + message->param.crypt.type = CU_INFO_REQ; + message_put(message); + return; + } + + if (check_external(&errstr, &port)) + { + reject: + message = message_create(ea_endpoint->ep_serial, ea_endpoint->ep_portlist->port_id, EPOINT_TO_PORT, MESSAGE_NOTIFY); + SCPY(message->param.notifyinfo.display, errstr); + message_put(message); + set_tone(ea_endpoint->ep_portlist, "crypt_off"); + e_tone[0] = '\0'; + return; + } + + /* check the key for the call */ + if (port->p_type==PORT_TYPE_DSS1_TE_OUT || port->p_type==PORT_TYPE_DSS1_NT_OUT || port->p_type==PORT_TYPE_SIP_OUT) + ret = parse_secrets((char *)e_terminal, (char *)port->p_dialinginfo.number, &auth_pointer, &crypt_pointer, &key_pointer); + else + { + if (!port->p_callerinfo.id[0]) + { + PDEBUG(DEBUG_EPOINT, "EPOINT(%d) incoming remote call has no caller ID.\n", ea_endpoint->ep_serial); + errstr = "No Remote ID"; + goto reject; + } + ret = parse_secrets((char *)e_terminal, (char *)numberrize_callerinfo(port->p_callerinfo.id, port->p_callerinfo.ntype), &auth_pointer, &crypt_pointer, &key_pointer); + } + if (!ret) + { + PDEBUG(DEBUG_EPOINT, "EPOINT(%d) Key was not found.\n", ea_endpoint->ep_serial); + errstr = "No Key"; + goto reject; + } + key = crypt_key((unsigned char *)key_pointer, &key_len); + if (!key) + { + PDEBUG(DEBUG_EPOINT, "EPOINT(%d) Key invalid.\n", ea_endpoint->ep_serial); + errstr = "Invalid Key"; + goto reject; + } + if (key_len > 128) + { + PDEBUG(DEBUG_EPOINT, "EPOINT(%d) Key too long.\n", ea_endpoint->ep_serial); + errstr = "Key Too Long"; + goto reject; + } + if (!!strcasecmp(auth_pointer, "manual")) + { + PDEBUG(DEBUG_EPOINT, "EPOINT(%d) Wrong authentication method.\n", ea_endpoint->ep_serial); + errstr = "Wrong Auth Type"; + goto reject; + } + if (!strcasecmp(crypt_pointer, "blowfish")) + { + type = CC_ACTBF_REQ; + if (key_len < 4) + { + PDEBUG(DEBUG_EPOINT, "EPOINT(%d) Key too short.\n", ea_endpoint->ep_serial); + errstr = "Key Too Short"; + goto reject; + } + if (key_len > 56) + { + PDEBUG(DEBUG_EPOINT, "EPOINT(%d) Key too long.\n", ea_endpoint->ep_serial); + errstr = "Key Too Long"; + goto reject; + } + } else + { + PDEBUG(DEBUG_EPOINT, "EPOINT(%d) Wrong crypt method.\n", ea_endpoint->ep_serial); + errstr = "Wrong Crypt Type"; + goto reject; + } + + PDEBUG(DEBUG_EPOINT, "EPOINT(%d) Key is ok, sending activation+key to cryptman.\n", ea_endpoint->ep_serial); + /* setting display message and state */ +// SPRINT(e_crypt_display, "Shared Key"); + e_crypt = CRYPT_SWAIT; + /* sending activation */ + message = message_create(ea_endpoint->ep_serial, ea_endpoint->ep_call_id, EPOINT_TO_CALL, MESSAGE_CRYPT); + message->param.crypt.type = CU_ACTS_REQ; + message->param.crypt.len = key_len; + memcpy(message->param.crypt.data, key, key_len); + message_put(message); +} + + +/* encrypt call using rsa authentication (keypad function) + */ +void EndpointAppPBX::encrypt_keyex(void) +{ + struct message *message; + char *errstr = ""; + class Port *port; + + /* redisplay current crypt display */ + if (e_crypt != CRYPT_OFF) + { + PDEBUG(DEBUG_EPOINT, "EPOINT(%d) encryption in progress, so we request the current message.\n", ea_endpoint->ep_serial); + message = message_create(ea_endpoint->ep_serial, ea_endpoint->ep_call_id, EPOINT_TO_CALL, MESSAGE_CRYPT); + message->param.crypt.type = CU_INFO_REQ; + message_put(message); + return; + } + + + if (check_external(&errstr, &port)) + { + message = message_create(ea_endpoint->ep_serial, ea_endpoint->ep_portlist->port_id, EPOINT_TO_PORT, MESSAGE_NOTIFY); + SCPY(message->param.notifyinfo.display, errstr); + message_put(message); + set_tone(ea_endpoint->ep_portlist, "crypt_off"); + e_tone[0] = '\0'; + return; + } + +#ifndef CRYPTO + message = message_create(ea_endpoint->ep_serial, ea_endpoint->ep_portlist->port_id, EPOINT_TO_PORT, MESSAGE_NOTIFY); + SCPY(message->param.notifyinfo.display, "Not Compiled"); + message_put(message); + set_tone(ea_endpoint->ep_portlist, "crypt_off"); + e_tone[0] = '\0'; +#else + PDEBUG(DEBUG_EPOINT, "EPOINT(%d) Sending key-exchange activation to cryptman.\n", ea_endpoint->ep_serial); + /* setting display message and state */ +// SPRINT(e_crypt_display, "Key-Exchange"); + message = message_create(ea_endpoint->ep_serial, ea_endpoint->ep_portlist->port_id, EPOINT_TO_PORT, MESSAGE_NOTIFY); + SCPY(message->param.notifyinfo.display, "Key-Exchange"); + message_put(message); + e_crypt = CRYPT_KWAIT; + /* sending activation */ + message = message_create(ea_endpoint->ep_serial, ea_endpoint->ep_call_id, EPOINT_TO_CALL, MESSAGE_CRYPT); + message->param.crypt.type = CU_ACTK_REQ; + message_put(message); +#endif /* CRYPTO */ +} + + +/* turn encryption off (keypad function) + */ +void EndpointAppPBX::encrypt_off(void) +{ + struct message *message; + + if (e_crypt!=CRYPT_ON && e_crypt!=CRYPT_OFF) + { + message = message_create(ea_endpoint->ep_serial, ea_endpoint->ep_portlist->port_id, EPOINT_TO_PORT, MESSAGE_NOTIFY); + SCPY(message->param.notifyinfo.display, "Please Wait"); + message_put(message); + return; + } + if (e_crypt == CRYPT_OFF) + { + message = message_create(ea_endpoint->ep_serial, ea_endpoint->ep_portlist->port_id, EPOINT_TO_PORT, MESSAGE_NOTIFY); + SCPY(message->param.notifyinfo.display, "No Encryption"); + message_put(message); + set_tone(ea_endpoint->ep_portlist, "crypt_off"); + e_tone[0] = '\0'; + return; + } + + PDEBUG(DEBUG_EPOINT, "EPOINT(%d) Sending deactivation to cryptman.\n", ea_endpoint->ep_serial); + /* setting display message and state */ +// SPRINT(e_crypt_display, "Deactivating"); + message = message_create(ea_endpoint->ep_serial, ea_endpoint->ep_portlist->port_id, EPOINT_TO_PORT, MESSAGE_NOTIFY); + SCPY(message->param.notifyinfo.display, "Deactivating"); + message_put(message); + e_crypt = CRYPT_RELEASE; + /* sending activation */ + message = message_create(ea_endpoint->ep_serial, ea_endpoint->ep_call_id, EPOINT_TO_CALL, MESSAGE_CRYPT); + message->param.crypt.type = CU_DACT_REQ; + message_put(message); +} + + +/* messages from manager to endpoint + */ +void EndpointAppPBX::encrypt_result(int msg, char *text) +{ + struct message *message; + + switch(msg) + { + case CU_ACTK_CONF: + case CU_ACTS_CONF: + PDEBUG(DEBUG_EPOINT, "EPOINT(%d) encryption now active.\n", ea_endpoint->ep_serial); + set_tone(ea_endpoint->ep_portlist, "crypt_on"); + e_tone[0] = '\0'; + e_crypt = CRYPT_ON; + display: + if (text) if (text[0]) + { + SCPY(e_crypt_info, text); + message = message_create(ea_endpoint->ep_serial, ea_endpoint->ep_portlist->port_id, EPOINT_TO_PORT, MESSAGE_NOTIFY); + SCPY(message->param.notifyinfo.display, e_crypt_info); + message_put(message); + } + break; + + case CU_ERROR_IND: + PDEBUG(DEBUG_EPOINT, "EPOINT(%d) encryption error. (%s)\n", ea_endpoint->ep_serial, text); + set_tone(ea_endpoint->ep_portlist, "crypt_off"); + e_tone[0] = '\0'; + e_crypt = CRYPT_OFF; + goto display; + break; + + case CU_DACT_CONF: + case CU_DACT_IND: + PDEBUG(DEBUG_EPOINT, "EPOINT(%d) encryption now off. (%s)\n", ea_endpoint->ep_serial, text); + set_tone(ea_endpoint->ep_portlist, "crypt_off"); + e_tone[0] = '\0'; + e_crypt = CRYPT_OFF; + goto display; + break; + + case CU_INFO_CONF: + case CU_INFO_IND: + PDEBUG(DEBUG_EPOINT, "EPOINT(%d) information. (%s)\n", ea_endpoint->ep_serial, text); + goto display; + break; + + default: + PERROR("EPOINT(%d) crypt manager sends us an invalid message. (type = %d)\n", ea_endpoint->ep_serial, msg); + } +} +