/
secretchat_support_lib.c
373 lines (334 loc) · 9.93 KB
/
secretchat_support_lib.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
#include "secretchat_support_lib.h"
#include "comm.h"
#include "dhread.h"
#include <openssl/evp.h>
#include <openssl/pem.h>
#include <openssl/rand.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#define PRIV_KEY "privkey.pem"
/* This function signs the buffer passed as argument, returns the length of the signature
* else -1 on error
* It leaves the sign in **sign_buf (which is allocated)
*/
int sign_hello(unsigned char* hello_buf,unsigned int hello_len,unsigned char** sign_buf){
EVP_MD_CTX* ctx = NULL;
unsigned int sign_len;
EVP_PKEY* evp = EVP_PKEY_new();
FILE* fp;
*sign_buf = NULL;
ctx = (EVP_MD_CTX*)calloc(1,sizeof(EVP_MD_CTX));
EVP_MD_CTX_init(ctx);
OpenSSL_add_all_algorithms();
if((fp=fopen(PRIV_KEY,"r"))==NULL){
goto fail;
}
if((evp=PEM_read_PrivateKey(fp,NULL,NULL,NULL))==NULL){
goto fail;
}
*sign_buf = (unsigned char*)calloc(1,EVP_PKEY_size(evp));
if(EVP_SignInit(ctx,EVP_sha512())==0){
goto fail;
}
if(EVP_SignUpdate(ctx,hello_buf,hello_len)==0){
goto fail;
}
if(EVP_SignFinal(ctx,*sign_buf,&sign_len,evp)==0){
goto fail;
}
EVP_MD_CTX_cleanup(ctx);
free(ctx);
EVP_PKEY_free(evp);
return sign_len;
fail:
EVP_MD_CTX_cleanup(ctx);
free(ctx);
if (*sign_buf != NULL) {
free(*sign_buf);
}
return -1;
}
/*
* This function prepares an hello message (mail1' 'mail2' 'nonce pubkey) and leaves it in **hello_buf
* (which is allocated) and then It signs hello_buf using the sign function.
* It returns 1 on success or -1 in case of error
*/
int prepare_and_sign_hello(char* mail1,unsigned int length1,char *mail2,unsigned int length2,int nonce,unsigned char* pubkey, unsigned int publen,unsigned char** hello_buf, unsigned int* hello_len,unsigned char** sign_buf,unsigned int* sign_len){
/*create the hello*/
uint32_t tmp;
*hello_len = length1+length2+2+sizeof(tmp)+publen;
*hello_buf = (unsigned char*)calloc(1,*hello_len);
unsigned int pos;
memcpy(*hello_buf,mail1,length1);
*(*hello_buf+length1) = (unsigned char)FIELD_SEPARATOR;
pos = length1 + 1;
memcpy(*hello_buf + pos, mail2, length2);
pos += length2;
*(*hello_buf + pos) = (unsigned char)FIELD_SEPARATOR;
pos++;
tmp = htonl(nonce);
memcpy(*hello_buf + pos, &tmp, sizeof(tmp));
pos += sizeof(tmp);
memcpy(*hello_buf + pos, pubkey, publen);
pos+=publen;
/*sign the hello*/
if((*sign_len=sign_hello(*hello_buf,*hello_len,sign_buf))<=0){
return -1;
}
return 1;
}
/* This function takes an open file pointer fp and it closes it before
* returning.
* It takes the fp of a certificate and write it in a buffer.
* It returns the pointer to the buffer or NULL in case of error.
* This function allocates cert
*/
unsigned char* prepare_cert(FILE* fp,unsigned int* cert_len){
struct stat file_info;
int fd;
unsigned char* cert;
if(fp == NULL){
return NULL;
}
//to come back to the start
rewind(fp);
fd = fileno(fp);
if(fstat(fd,&file_info) < 0){
return NULL;
}
cert = (unsigned char*)calloc(1,file_info.st_size);
if(fread(cert,1,file_info.st_size,fp)<file_info.st_size){
return NULL;
}
*cert_len = file_info.st_size;
fclose(fp);
return cert;
}
/*
* Creates the hello + certificate message and sends it.
* It returns the number of bytes sent or -1 if an error occured. This function
* set the value of errno in case of error.
*/
int send_hello(int sk, unsigned char* hello, unsigned int hello_len, \
unsigned char* sign, unsigned int sign_len, \
unsigned char* cert, unsigned int cert_len)
{
uint32_t tmp;
int num_bytes, ret;
unsigned char* msg;
if (sk < 0 || hello == NULL || sign == NULL || cert == NULL) {
errno = EINVAL;
return -1;
}
/* this message contains the hello string length, the hello string, the
* hello string signature length, the hello string signature, the
* certificate length and the certificate
*/
msg = (unsigned char*)calloc(1, hello_len + sign_len + cert_len + \
3*sizeof(tmp));
/* appending hello */
tmp = htonl(hello_len);
memcpy(msg, &tmp, sizeof(tmp));
num_bytes = sizeof(tmp);
memcpy(msg + num_bytes, hello, hello_len);
num_bytes += hello_len;
/*appending signature*/
tmp = htonl(sign_len);
memcpy(msg + num_bytes, &tmp, sizeof(tmp));
num_bytes += sizeof(tmp);
memcpy(msg + num_bytes, sign, sign_len);
num_bytes += sign_len;
/*appending certificate*/
tmp = htonl(cert_len);
memcpy(msg + num_bytes, &tmp, sizeof(tmp));
num_bytes += sizeof(tmp);
memcpy(msg + num_bytes, cert, cert_len);
num_bytes += cert_len;
/* sending */
if ((ret = send_msg(sk, msg, num_bytes,(char)HELO_MSG1)) <= 0) {
free(msg);
return -1;
}
free(msg);
return ret;
}
/*
* This function decrypts a string after it has been received on a socket.
* It performs previous checking on the format and may discard the
* message and return an error if the format mismatch. We can avoid a
* decryption if the format mismatches.
*
* @return It returns the plaintext length or -1 if a generic error occured,
* -2 if the format is not the one expected and 0 in case of disconnection.
* It leaves the decrypted message in **plain ( which is allocated).
*/
int decrypt_msg(int sk, char format, unsigned char** plain, unsigned char* shared_secret)
{
EVP_CIPHER_CTX* ctx;
unsigned char iv[EVP_MAX_IV_LENGTH];
unsigned int iv_len = EVP_MAX_IV_LENGTH;
unsigned char* msg = NULL;//msg has to set free in this function
unsigned int msg_len;
char recv_format;
int outlen, outtot = 0, ret;
*plain = NULL;
ctx = (EVP_CIPHER_CTX*)calloc(1, sizeof(EVP_CIPHER_CTX));
EVP_CIPHER_CTX_init(ctx);
if ((msg_len = recv_msg(sk, &msg, &recv_format)) <= 0) {
ret = msg_len;
goto fail;
}
if (recv_format != format) {
ret = -2;
goto fail;
}
*plain = (unsigned char*)calloc(1, msg_len - iv_len);
memcpy(iv, msg, iv_len);
if (EVP_DecryptInit(ctx, EVP_aes_256_cbc(), shared_secret, iv) == 0) {
ret = -1;
goto fail;
}
if (EVP_DecryptUpdate(ctx, *plain, &outlen, msg + iv_len, msg_len - iv_len) == 0) {
ret = -1;
goto fail;
}
outtot = outlen;
if (EVP_DecryptFinal(ctx, *plain + outtot, &outlen) == 0) {
ret = -1;
goto fail;
}
outtot += outlen;
EVP_CIPHER_CTX_cleanup(ctx);
free(ctx);
free(msg);
return outtot;
fail: EVP_CIPHER_CTX_cleanup(ctx);
free(ctx);
if (*plain != NULL) {
free(*plain);
}
if (msg != NULL) {
free(msg);
}
return ret;
}
/*
* This function encrypts a string before calling send_msg.
* It also append the given IV for the ecnryption mode.
* It returns the length of the cipher text (iv is not considered), -1 on error.
* Errno is set appropriately
*/
int encrypt_msg(int sk, char format, unsigned char* plain, unsigned int plain_len, unsigned char* shared_secret)
{
EVP_CIPHER_CTX* ctx;
unsigned char* iv;
unsigned int iv_len = EVP_MAX_IV_LENGTH;
unsigned char* outbuf = NULL;
int outlen, outtot = 0;
ctx = (EVP_CIPHER_CTX*)calloc(1, sizeof(EVP_CIPHER_CTX));
EVP_CIPHER_CTX_init(ctx);
iv = (unsigned char*)calloc(1, iv_len);
RAND_bytes(iv, iv_len);
if (EVP_EncryptInit(ctx, EVP_aes_256_cbc(), shared_secret, iv) == 0) {
goto fail;
}
outbuf = (unsigned char*)calloc(1, plain_len + EVP_CIPHER_block_size(EVP_aes_256_cbc()) + iv_len);
if (EVP_EncryptUpdate(ctx, outbuf + iv_len, &outlen, plain, plain_len) == 0) {
goto fail;
}
outtot += outlen;
if (EVP_EncryptFinal(ctx, outbuf + iv_len + outtot, &outlen) == 0) {
goto fail;
}
outtot += outlen;
//We concatenate iv and cipher text together
memcpy(outbuf, iv, iv_len);
if (send_msg(sk, outbuf, outtot + iv_len, format) < outtot + iv_len) {
goto fail;
}
EVP_CIPHER_CTX_cleanup(ctx);
free(ctx);
free(iv);
free(outbuf);
return outtot;
fail: EVP_CIPHER_CTX_cleanup(ctx);
free(ctx);
free(iv);
if (outbuf != NULL) {
free(outbuf);
}
return -1;
}
/*
* This function generates my Diffie Hellmann parameter
*/
DH* dh_genkey(){
DH *dh = get_dh1024();
if(DH_generate_key(dh)!=1){
DH_free(dh);
return NULL;
}
return dh;
}
/*
* This function receives an hello + certificate message
* If something goes wrong, it returns -1 in case of generic error, -2
* for format mismatching or 0 in case of disconnection.
* In case of success it returns the length of the received hello string.
* The given pointers (hello, sign, cert) are allocated using calloc and
* must be freed. They are intitially set to NULL becasue if something
* goes wrong the calling function has no need to set them free
*/
int recv_hello(int sk, unsigned char** hello, unsigned int* hello_len, \
unsigned char** sign, unsigned int *sign_len, \
unsigned char** cert, unsigned int* cert_len)
{
uint32_t tmp;
int msg_len, pos, ret;
unsigned char* msg = NULL;//msg has to set free later in this function
char format;
*hello = NULL;
*sign = NULL;
*cert = NULL;
if (sk < 0) {
errno = EINVAL;
return -1;
}
if ((msg_len = recv_msg(sk, &msg,&format)) <= 0) {
ret = msg_len;
goto fail;
}
if(format != HELO_MSG1){
errno = EINVAL;
ret = -2;
goto fail;
}
/* reading hello */
memcpy(&tmp, msg, sizeof(tmp));
*hello_len = (unsigned int)ntohl(tmp);
pos = sizeof(tmp);
*hello = (unsigned char*)calloc(1, *hello_len);
memcpy(*hello, msg + pos, *hello_len);
pos += *hello_len;
/* reading hello signature */
memcpy(&tmp, msg + pos, sizeof(tmp));
*sign_len = (unsigned int)ntohl(tmp);
pos += sizeof(tmp);
*sign = (unsigned char*)calloc(1, *sign_len);
memcpy(*sign, msg + pos, *sign_len);
pos += *sign_len;
/* reading certificate */
memcpy(&tmp, msg + pos, sizeof(tmp));
pos += sizeof(tmp);
*cert_len = (int)ntohl(tmp);
*cert = (unsigned char*)calloc(1, *cert_len);
memcpy(*cert, msg + pos, *cert_len);
pos += *cert_len;
free(msg);
return pos;
fail: if (msg != NULL) {
free(msg);
}
return ret;
}