-
Notifications
You must be signed in to change notification settings - Fork 10
/
mecevp.c
1214 lines (1148 loc) · 43.9 KB
/
mecevp.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
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*
mecevp.c
gSOAP interface for streaming message encryption and decryption
gSOAP XML Web services tools
Copyright (C) 2000-2012, Robert van Engelen, Genivia Inc., All Rights Reserved.
This part of the software is released under one of the following licenses:
GPL, the gSOAP public license, or Genivia's license for commercial use.
--------------------------------------------------------------------------------
gSOAP public license.
The contents of this file are subject to the gSOAP Public License Version 1.3
(the "License"); you may not use this file except in compliance with the
License. You may obtain a copy of the License at
http://www.cs.fsu.edu/~engelen/soaplicense.html
Software distributed under the License is distributed on an "AS IS" basis,
WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
for the specific language governing rights and limitations under the License.
The Initial Developer of the Original Code is Robert A. van Engelen.
Copyright (C) 2000-2012, Robert van Engelen, Genivia, Inc., All Rights Reserved.
--------------------------------------------------------------------------------
GPL license.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
Foundation; either version 2 of the License, or (at your option) any later
version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
this program; if not, write to the Free Software Foundation, Inc., 59 Temple
Place, Suite 330, Boston, MA 02111-1307 USA
Author contact information:
engelen@genivia.com / engelen@acm.org
This program is released under the GPL with the additional exemption that
compiling, linking, and/or using OpenSSL is allowed.
--------------------------------------------------------------------------------
A commercial use license is available from Genivia, Inc., contact@genivia.com
--------------------------------------------------------------------------------
*/
/**
@page mecevp The mecevp streaming message encryption and decryption engine
The gSOAP mecevp engine encrypts and decrypts messages using the EVP interface
of OpenSSL. It supports envelope encryption/decryption with public and private
RSA keys and symmetric encryption with shared secret keys. Streaming and
buffered message encryption modes are supported.
An encryption and decryption algorithm and mode is selected with one of the
following:
- @ref SOAP_MEC_ENV_ENC_DES_CBC envelope encryption with triple DES CBC
- @ref SOAP_MEC_ENV_ENC_AES256_CBC envelope encryption with AES256 CBC
- @ref SOAP_MEC_ENC_DES_CBC symmetric encryption with triple DES CBC
- @ref SOAP_MEC_ENC_AES256_CBC symmetric encryption with AES256 CBC
- @ref SOAP_MEC_ENV_DEC_DES_CBC envelope decryption with triple DES CBC
- @ref SOAP_MEC_ENV_DEC_AES256_CBC envelope decryption with AES256 CBC
- @ref SOAP_MEC_DEC_DES_CBC symmetric decryption with triple DES CBC
- @ref SOAP_MEC_DEC_AES256_CBC symmetric decryption with AES256 CBC
where, in the above, AES256 can be replaced with any choice of AES128, AES192,
AES512.
Algorithm options:
- @ref SOAP_MEC_STORE buffer all output in memory
- @ref SOAP_MEC_OAEP use OAEP padding
The mecevp engine wraps the EVP API with four new functions:
- @ref soap_mec_init to initialize the engine
- @ref soap_mec_update to encrypt/decrypt a message part
- @ref soap_mec_final to finalize encryption/decryption
- @ref soap_mec_cleanup to deallocate the engine and buffers
All cipher data is written and read in base64 format.
A higher-level interface for message encryption/decryption in parts (such as
individual XML elements) is defined by two new functions:
- @ref soap_mec_begin to begin a streaming sequence of encryptions/decryptions
- @ref soap_mec_start to start encryption/decryption of a message part
- @ref soap_mec_stop to stop encryption/decryption of a message part
- @ref soap_mec_end to end the sequence and deallocate the engine buffers
Compile all source codes with -DWITH_OPENSSL and link with ssl and crypto
libraries.
Here is an example to encrypt a message while streaming it to the output. The
example uses the public key of the recipient/reader of the message. The
recipient/reader uses its private key to decrypt. Envelope encryption is used
with SOAP_MEC_ENV_ENC_DES_CBC, which means an ephemeral secret key is generated
and encrypted with the public key. This encrypted secret key should be
communicated to the recipient/reader with the message to decrypt:
@code
#include "mecevp.h"
soap_mec_data mec;
ns__Object object;
int alg = SOAP_MEC_ENV_ENC_DES_CBC;
FILE *fd = fopen("key.pem", "r");
EVP_PKEY *pubk;
unsigned char *key;
int keylen;
if (...) // key file contains public key?
pubk = PEM_read_PUBKEY(fd, NULL, NULL, NULL);
else // key file contains certificate
{ X509 *cert = PEM_read_X509(fd, NULL, NULL, NULL);
pubk = X509_get_pubkey(cert);
X509_free(cert);
}
fclose(fd);
key = soap_malloc(soap, soap_mec_size(alg, pubk));
if (soap_begin_send(soap)
|| soap_mec_begin(soap, &mec, alg, pubk, key, &keylen)
|| soap_mec_start(soap, NULL)
|| soap_out_ns__Object(soap, "ns:Object", 0, &object, NULL)
|| soap_mec_stop(soap)
|| soap_mec_end(soap, &mec)
|| soap_end_send(soap))
{ soap_mec_cleanup(soap, &mec); // clean up when error
soap_print_fault(soap, stderr);
}
EVP_PKEY_free(pubk);
@endcode
The decryption by the recipient/reader requires the ephemeral encrypted secret
key generated by soap_mec_begin by the sender (as set above) to decrypt the
message using envelope decryption with SOAP_MEC_ENV_DEC_DES_CBC.
@code
#include "mecevp.h"
soap_mec_data mec;
ns__Object object;
int alg = SOAP_MEC_ENV_DEC_DES_CBC;
FILE *fd = fopen("key.pem", "r");
EVP_PKEY *privk = PEM_read_PrivateKey(fd, NULL, NULL, "password");
unsigned char *key;
int keylen;
fclose(fd);
key = ... // value set as above by sender
keylen = ... // value set as above by sender
if (soap_begin_recv(soap)
|| soap_mec_begin(soap, &mec, alg, privk, key, &keylen)
|| soap_mec_start(soap)
|| soap_in_ns__Object(soap, "ns:Object", &object, NULL) == NULL
|| soap_mec_stop(soap)
|| soap_mec_end(soap, &mec)
|| soap_end_recv(soap))
{ soap_mec_cleanup(soap, &mec); // clean up when error
soap_print_fault(soap, stderr);
}
EVP_PKEY_free(privk);
@endcode
Note that the encrypted secret key can be send in the clear or stored openly,
since only the recipient/reader will be able to decode it for use in message
decryption.
Symmetric encryption and decryption can be used if both parties can safely
share a secret symmetric key that no other party has access to. We use
SOAP_MEC_ENC_DES_CBC for encryption and SOAP_MEC_DEC_DES_CBC for decryption
using a 160-bit triple DES key.
Here is an example to encrypt a message using a shared secret key while
streaming it to the output.
@code
#include "mecevp.h"
soap_mec_data mec;
ns__Object object;
int alg = SOAP_MEC_ENC_DES_CBC;
unsigned char key[20] = { ... }; // shared secret triple DES key
int keylen = 20;
if (soap_begin_send(soap)
|| soap_mec_begin(soap, &mec, alg, NULL, key, &keylen)
|| soap_mec_start(soap, NULL)
|| soap_out_ns__Object(soap, "ns:Object", 0, &object, NULL)
|| soap_mec_stop(soap)
|| soap_mec_end(soap, &mec)
|| soap_end_send(soap))
{ soap_mec_cleanup(soap, &mec); // clean up when error
soap_print_fault(soap, stderr);
}
@endcode
The decryption by the recipient/reader requires the same shared secret key to
decrypt the message using envelope decryption with SOAP_MEC_DEC_DES_CBC. This
key is secret and unencrypted, so it should never be shared with any other
party besides the sender/writer and recipient/reader.
@code
#include "mecevp.h"
soap_mec_data mec;
ns__Object object;
int alg = SOAP_MEC_DEC_DES_CBC;
unsigned char key[20] = { ... }; // shared secret triple DES key
int keylen = 20;
if (soap_begin_recv(soap)
|| soap_mec_begin(soap, &mec, alg, NULL, key, &keylen)
|| soap_mec_start(soap)
|| soap_in_ns__Object(soap, "ns:Object", &object, NULL) == NULL
|| soap_mec_stop(soap)
|| soap_mec_end(soap, &mec)
|| soap_end_recv(soap))
{ soap_mec_cleanup(soap, &mec); // clean up when error
soap_print_fault(soap, stderr);
}
@endcode
@note
The mecevp engine uses callbacks of the gSOAP engine that were introduced in
version 2.8.1. Earlier gSOAP version releases are not compatible with the
mecevp plugin and engine.
*/
#include "mecevp.h"
#ifdef __cplusplus
extern "C" {
#endif
/******************************************************************************\
*
* Static local functions used
*
\******************************************************************************/
static int soap_mec_upd(struct soap *soap, struct soap_mec_data *data, const char **s, size_t *n, int final);
static int soap_mec_upd_enc(struct soap *soap, struct soap_mec_data *data, const char **s, size_t *n, int final);
static int soap_mec_upd_dec(struct soap *soap, struct soap_mec_data *data, const char **s, size_t *n, int final);
static int soap_mec_check(struct soap *soap, struct soap_mec_data *data, int err, const char *msg);
static void soap_mec_put_base64(struct soap *soap, struct soap_mec_data *data, const unsigned char *s, int n);
static void soap_mec_end_base64(struct soap *soap, struct soap_mec_data *data);
static int soap_mec_get_base64(struct soap *soap, struct soap_mec_data *data, char *t, size_t *l, const char *s, size_t n, const char **r, size_t *k);
static int soap_mec_filtersend(struct soap *soap, const char **s, size_t *n);
static int soap_mec_filterrecv(struct soap *soap, char *buf, size_t *len, size_t maxlen);
/******************************************************************************\
*
* soap_mec API functions
*
\******************************************************************************/
/**
@fn int soap_mec_init(struct soap *soap, struct soap_mec_data *data, int alg, SOAP_MEC_KEY_TYPE *pkey, unsigned char *key, int *keylen)
@brief Initialize mecevp engine state and create context for
encryption/decryption algorithm using a private/public key or symmetric secret
key.
@param soap context
@param[in,out] data mecevp engine context
@param[in] alg encryption/decryption algorithm
@param[in] pkey public/private key or NULL
@param[in,out] key secret key or encrypted ephemeral secret key set with envelope encryption, or NULL
@param[in,out] keylen secret key length
@return SOAP_OK or SOAP_SSL_ERROR
*/
int
soap_mec_init(struct soap *soap, struct soap_mec_data *data, int alg, SOAP_MEC_KEY_TYPE *pkey, unsigned char *key, int *keylen)
{ int ok = 1;
DBGLOG(TEST, SOAP_MESSAGE(fdebug, "soap_mec_init()\n"));
soap_ssl_init();
data->ctx = (EVP_CIPHER_CTX*)SOAP_MALLOC(soap, sizeof(EVP_CIPHER_CTX));
if (!data->ctx)
return soap->error = SOAP_EOM;
EVP_CIPHER_CTX_init(data->ctx);
data->alg = alg;
data->state = SOAP_MEC_STATE_NONE;
if (alg & SOAP_MEC_DES_CBC)
data->type = EVP_des_ede3_cbc(); /* triple DES CBC */
else if (alg & SOAP_MEC_AES128_CBC)
data->type = EVP_get_cipherbyname("AES128");
else if (alg & SOAP_MEC_AES192_CBC)
data->type = EVP_get_cipherbyname("AES192");
else if (alg & SOAP_MEC_AES256_CBC)
data->type = EVP_get_cipherbyname("AES256");
else if (alg & SOAP_MEC_AES512_CBC)
data->type = EVP_get_cipherbyname("AES512");
else
data->type = EVP_enc_null();
data->buf = NULL;
data->rest = NULL;
data->restlen = 0;
if (alg & SOAP_MEC_ENC)
{ if (!data->type)
return soap_mec_check(soap, data, 0, "soap_mec_init() failed: cannot load cipher");
EVP_EncryptInit_ex(data->ctx, data->type, NULL, NULL, NULL);
}
if (alg & SOAP_MEC_OAEP)
EVP_CIPHER_CTX_set_padding(data->ctx, RSA_PKCS1_OAEP_PADDING);
else
EVP_CIPHER_CTX_set_padding(data->ctx, RSA_PKCS1_PADDING);
switch (alg & SOAP_MEC_MASK)
{ case SOAP_MEC_ENV_ENC_AES128_CBC:
case SOAP_MEC_ENV_ENC_AES192_CBC:
case SOAP_MEC_ENV_ENC_AES256_CBC:
case SOAP_MEC_ENV_ENC_AES512_CBC:
case SOAP_MEC_ENV_ENC_DES_CBC:
ok = EVP_CIPHER_CTX_rand_key(data->ctx, data->ekey);
/* generate ephemeral secret key */
#if (OPENSSL_VERSION_NUMBER >= 0x10000000L)
*keylen = EVP_PKEY_encrypt_old(key, data->ekey, EVP_CIPHER_CTX_key_length(data->ctx), pkey);
#else
*keylen = EVP_PKEY_encrypt(key, data->ekey, EVP_CIPHER_CTX_key_length(data->ctx), pkey);
#endif
key = data->ekey;
/* fall through to next arm */
case SOAP_MEC_ENC_DES_CBC:
case SOAP_MEC_ENC_AES128_CBC:
case SOAP_MEC_ENC_AES192_CBC:
case SOAP_MEC_ENC_AES256_CBC:
case SOAP_MEC_ENC_AES512_CBC:
data->bufidx = 0;
data->buflen = 1024; /* > iv in base64 must fit */
data->buf = (char*)SOAP_MALLOC(soap, data->buflen);
data->key = key;
break;
case SOAP_MEC_ENV_DEC_AES128_CBC:
case SOAP_MEC_ENV_DEC_AES192_CBC:
case SOAP_MEC_ENV_DEC_AES256_CBC:
case SOAP_MEC_ENV_DEC_AES512_CBC:
case SOAP_MEC_ENV_DEC_DES_CBC:
case SOAP_MEC_DEC_DES_CBC:
case SOAP_MEC_DEC_AES128_CBC:
case SOAP_MEC_DEC_AES192_CBC:
case SOAP_MEC_DEC_AES256_CBC:
case SOAP_MEC_DEC_AES512_CBC:
data->pkey = pkey;
data->key = key;
data->keylen = *keylen;
break;
default:
return soap_set_receiver_error(soap, "Unsupported encryption algorithm", NULL, SOAP_SSL_ERROR);
}
return soap_mec_check(soap, data, ok, "soap_mec_init() failed");
}
/**
@fn int soap_mec_update(struct soap *soap, struct soap_mec_data *data, const char **s, size_t *n)
@brief Update mecevp engine state: encrypts plain text (or raw data) or
decrypts cipher data in base64 format.
@param soap context
@param[in,out] data mecevp engine context
@param[in,out] s input data to convert, afterwards points to converted data (original content is unchanged)
@param[in,out] n size of input, afterwards size of output
@return SOAP_OK or SOAP_SSL_ERROR
*/
int
soap_mec_update(struct soap *soap, struct soap_mec_data *data, const char **s, size_t *n)
{ return soap_mec_upd(soap, data, s, n, 0);
}
/**
@fn int soap_mec_final(struct soap *soap, struct soap_mec_data *data, const char **s, size_t *n)
@brief Ends mecevp engine state: encrypt/decrypt remainder from buffers.
@param soap context
@param[in,out] data mecevp engine context
@param[out] s afterwards points to converted remaining data in streaming mode, or entire converted data in buffer mode (SOAP_MEC_STORE option)
@param[out] n afterwards size of remaining data
@return SOAP_OK or SOAP_SSL_ERROR
*/
int
soap_mec_final(struct soap *soap, struct soap_mec_data *data, const char **s, size_t *n)
{ DBGLOG(TEST, SOAP_MESSAGE(fdebug, "soap_mec_final()\n"));
*n = 0;
if (!data->ctx)
return SOAP_OK;
if (soap_mec_upd(soap, data, s, n, 1))
return soap->error;
return SOAP_OK;
}
/**
@fn void soap_mec_cleanup(struct soap *soap, struct soap_mec_data *data)
@brief Clean up mecevp engine and deallocate cipher context and buffers.
@param soap context
@param[in,out] data mecevp engine context
@return SOAP_OK or SOAP_SSL_ERROR
*/
void
soap_mec_cleanup(struct soap *soap, struct soap_mec_data *data)
{ DBGLOG(TEST, SOAP_MESSAGE(fdebug, "soap_mec_cleanup()\n"));
if (data->ctx)
{ EVP_CIPHER_CTX_cleanup(data->ctx);
SOAP_FREE(soap, data->ctx);
data->ctx = NULL;
}
if (data->buf)
{ SOAP_FREE(soap, data->buf);
data->buf = NULL;
}
if (data->rest)
{ SOAP_FREE(soap, data->rest);
data->rest = NULL;
}
}
/**
@fn int soap_mec_begin(struct soap *soap, struct soap_mec_data *data, int alg, SOAP_MEC_KEY_TYPE *pkey, unsigned char *key, int *keylen)
@brief Initialize the mecevp engine data and begin encryption or decryption
message sequence using a private/public key or symmetric secret key.
@param soap context
@param[in,out] data mecevp engine context
@param[in] alg encryption/decryption algorithm
@param[in] pkey public/private key or NULL
@param[in,out] key secret key or encrypted ephemeral secret key set with envelope encryption, or NULL
@param[in,out] keylen secret key length
@return SOAP_OK or error code
*/
int
soap_mec_begin(struct soap *soap, struct soap_mec_data *data, int alg, SOAP_MEC_KEY_TYPE *pkey, unsigned char *key, int *keylen)
{ DBGLOG(TEST, SOAP_MESSAGE(fdebug, "MEC Begin alg=%x\n", alg));
/* save and set the engine's 'data' field to pass data to the callbacks */
soap->data[1] = (void*)data;
data->ctx = NULL;
data->type = EVP_enc_null();
data->pkey = NULL;
data->key = NULL;
data->buf = NULL;
/* save the mode flag */
data->mode = soap->mode;
if (alg & SOAP_MEC_ENC)
{ /* clear the IO flags and DOM flag */
soap->mode &= ~(SOAP_IO | SOAP_IO_LENGTH | SOAP_ENC_ZLIB | SOAP_XML_DOM);
/* clear the XML attribute store */
soap_clr_attr(soap);
/* load the local XML namespaces store */
soap_set_local_namespaces(soap);
if (soap->mode & SOAP_XML_CANONICAL)
soap->ns = 0; /* for in c14n, we must have all xmlns bindings available */
}
else
{ /* save and override the callbacks */
data->ffilterrecv = soap->ffilterrecv;
soap->ffilterrecv = soap_mec_filterrecv;
}
/* init the soap_mec engine */
return soap_mec_init(soap, data, alg, pkey, key, keylen);
}
/**
@fn int soap_mec_start_alg(struct soap *soap, int alg, const unsigned char *key)
@brief Start encryption or decryption of current message. If key is non-NULL,
use the symmetric triple DES key. Use soap_mec_start only after soap_mec_begin.
The soap_mec_start should be followed by a soap_mec_stop call.
@param soap context
@param[in] alg algorithm
@param[in] key secret triple DES key or NULL
@return SOAP_OK or error code
*/
int
soap_mec_start_alg(struct soap *soap, int alg, const unsigned char *key)
{ struct soap_mec_data *data;
int ok = 1;
data = (struct soap_mec_data*)soap->data[1];
if (!data)
return soap->error = SOAP_USER_ERROR;
DBGLOG(TEST, SOAP_MESSAGE(fdebug, "MEC Start alg=%x\n", data->alg));
if (key)
data->key = key;
if (alg != SOAP_MEC_NONE)
data->alg = alg;
if (data->alg & SOAP_MEC_ENC)
{ unsigned char iv[EVP_MAX_IV_LENGTH];
int ivlen;
/* save and override the callbacks */
data->ffiltersend = soap->ffiltersend;
soap->ffiltersend = soap_mec_filtersend;
data->bufidx = 0;
data->i = 0;
data->m = 0;
ivlen = EVP_CIPHER_iv_length(data->type);
if (ivlen)
{ RAND_pseudo_bytes(iv, ivlen);
soap_mec_put_base64(soap, data, (unsigned char*)iv, ivlen);
}
DBGLOG(TEST, SOAP_MESSAGE(fdebug, "IV = "));
DBGHEX(TEST, iv, ivlen);
DBGLOG(TEST, SOAP_MESSAGE(fdebug, "\n--\n"));
ok = EVP_EncryptInit_ex(data->ctx, NULL, NULL, data->key, iv);
}
else
{ size_t len;
/* algorithm */
if (data->alg & SOAP_MEC_DES_CBC)
data->type = EVP_des_ede3_cbc(); /* triple DES CBC */
else if (data->alg & SOAP_MEC_AES128_CBC)
data->type = EVP_get_cipherbyname("AES128");
else if (data->alg & SOAP_MEC_AES192_CBC)
data->type = EVP_get_cipherbyname("AES192");
else if (data->alg & SOAP_MEC_AES256_CBC)
data->type = EVP_get_cipherbyname("AES256");
else if (data->alg & SOAP_MEC_AES512_CBC)
data->type = EVP_get_cipherbyname("AES512");
else
data->type = EVP_enc_null();
len = 2 * sizeof(soap->buf) + EVP_CIPHER_block_size(data->type);
if (!data->buf || data->buflen < len)
{ if (data->buf)
SOAP_FREE(soap, data->buf);
data->buflen = len;
data->buf = (char*)SOAP_MALLOC(soap, data->buflen);
}
data->bufidx = soap->buflen - soap->bufidx;
/* copy buf[bufidx..buflen-1] to data buf */
memcpy(data->buf, soap->buf + soap->bufidx, data->bufidx);
DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Alloc buf=%lu, copy %lu message bytes\n", (unsigned long)data->buflen, (unsigned long)data->bufidx));
/* trigger ffilterrecv() */
soap->bufidx = soap->buflen;
/* INIT state */
data->i = 0;
data->m = 0;
data->state = SOAP_MEC_STATE_INIT;
}
return soap_mec_check(soap, data, ok, "soap_mec_start() failed");
}
/**
@fn int soap_mec_start(struct soap *soap, const unsigned char *key)
@brief Start encryption or decryption of current message. If key is non-NULL,
use the symmetric triple DES key. Use soap_mec_start only after soap_mec_begin.
The soap_mec_start should be followed by a soap_mec_stop call.
@param soap context
@param[in] key secret triple DES key or NULL
@return SOAP_OK or error code
*/
int
soap_mec_start(struct soap *soap, const unsigned char *key)
{ return soap_mec_start_alg(soap, SOAP_MEC_NONE, key);
}
/**
@fn int soap_mec_stop(struct soap *soap)
@brief Stops encryption or decryption of current message. Use after
soap_mec_start.
@param soap context
@return SOAP_OK or error code
*/
int
soap_mec_stop(struct soap *soap)
{ struct soap_mec_data *data;
int err = SOAP_OK;
const char *s = NULL;
size_t n = 0;
data = (struct soap_mec_data*)soap->data[1];
if (!data)
return soap->error = SOAP_USER_ERROR;
DBGLOG(TEST, SOAP_MESSAGE(fdebug, "MEC Stop alg=%x\n", data->alg));
err = soap_mec_final(soap, data, &s, &n);
if (data->alg & SOAP_MEC_ENC)
{ /* reset callbacks */
if (soap->ffiltersend == soap_mec_filtersend)
soap->ffiltersend = data->ffiltersend;
/* send remaining cipher data */
if (!err && n)
if (soap_send_raw(soap, s, n))
return soap->error;
}
return err;
}
/**
@fn int soap_mec_end(struct soap *soap, struct soap_mec_data *data)
@brief Ends encryption or decryption of a sequence of message parts that began
with soap_mec_begin.
@param soap context
@param[in,out] data mecevp engine context
@return SOAP_OK or error code
*/
int
soap_mec_end(struct soap *soap, struct soap_mec_data *data)
{ DBGLOG(TEST, SOAP_MESSAGE(fdebug, "MEC End alg=%x\n", data->alg));
/* reset callbacks */
if (soap->ffiltersend == soap_mec_filtersend)
soap->ffiltersend = data->ffiltersend;
if (soap->ffilterrecv == soap_mec_filterrecv)
soap->ffilterrecv = data->ffilterrecv;
/* restore the mode flag */
soap->mode = data->mode;
/* cleanup and reset mecevp engine */
soap_mec_cleanup(soap, data);
soap->data[1] = NULL;
return SOAP_OK;
}
/**
@fn size_t soap_mec_size(int alg, SOAP_MEC_KEY_TYPE *pkey)
@brief Returns the number of octets needed to store the public/private key or
the symmetric key, depending on the algorithm.
@param[in] alg is the algorithm to be used
@param[in] pkey is a pointer to an EVP_PKEY object or NULL for symmetric keys
@return size_t number of octets that is needed to hold the key.
*/
size_t
soap_mec_size(int alg, SOAP_MEC_KEY_TYPE *pkey)
{ if (alg & SOAP_MEC_ENV)
return EVP_PKEY_size(pkey);
switch (alg & SOAP_MEC_MASK & ~SOAP_MEC_ENC)
{ case SOAP_MEC_DES_CBC:
return 20; /* triple DES 160 bits */
case SOAP_MEC_AES128_CBC:
return 16;
case SOAP_MEC_AES192_CBC:
return 24;
case SOAP_MEC_AES256_CBC:
return 32;
case SOAP_MEC_AES512_CBC:
return 64;
}
return 0;
}
/******************************************************************************\
*
* Static local functions
*
\******************************************************************************/
/**
@fn int soap_mec_upd(struct soap *soap, struct soap_mec_data *data, const char **s, size_t *n, int final)
@brief Update encryption/decryption state depending on the current algorithm
@param soap context
@param[in,out] data mecevp engine context
@param[in,out] s input data to convert, afterwards points to converted data (original content is unchanged)
@param[in,out] n size of input, afterwards size of output
@param[in] final flag to indicate no more input, output is flushed to s
@return SOAP_OK or SOAP_SSL_ERROR
*/
static int
soap_mec_upd(struct soap *soap, struct soap_mec_data *data, const char **s, size_t *n, int final)
{ if (!data || !data->ctx)
return soap->error = SOAP_USER_ERROR;
DBGLOG(TEST, SOAP_MESSAGE(fdebug, "-- MEC Update alg=%x n=%lu final=%d (%p) --\n", data->alg, (unsigned long)*n, final, data->ctx));
DBGMSG(TEST, *s, *n);
DBGLOG(TEST, SOAP_MESSAGE(fdebug, "\n--\n"));
if (data->alg & SOAP_MEC_ENC)
{ if (soap_mec_upd_enc(soap, data, s, n, final))
return soap->error;
}
else
{ if (soap_mec_upd_dec(soap, data, s, n, final))
return soap->error;
}
DBGLOG(TEST, SOAP_MESSAGE(fdebug, "\n--\n"));
DBGMSG(TEST, *s, *n);
DBGLOG(TEST, SOAP_MESSAGE(fdebug, "\n--\n"));
return SOAP_OK;
}
/**
@fn int soap_mec_upd_enc(struct soap *soap, struct soap_mec_data *data, const char **s, size_t *n, int final)
@brief Update encryption state with input plain text (or raw) data and output
in base64 format.
@param soap context
@param[in,out] data mecevp engine context
@param[in,out] s input plain text, afterwards points to output cipher data
@param[in,out] n size of input text, afterwards size of cipher data
@param[in] final flag to indicate no more input, output is flushed to s
@return SOAP_OK or SOAP_SSL_ERROR
*/
static int
soap_mec_upd_enc(struct soap *soap, struct soap_mec_data *data, const char **s, size_t *n, int final)
{ size_t k;
int m;
int ok = 0;
/* cipher size */
k = *n + EVP_CIPHER_block_size(data->type);
/* scale by base64 size + in-use part + 8 margin */
m = data->bufidx + 8 + (k + 2) / 3 * 4 + 1;
/* fits in buf after bufidx? */
if (m > (int)data->buflen)
{ char *t = data->buf;
data->buflen = m; /* + slack? */
data->buf = (char*)SOAP_MALLOC(soap, data->buflen);
if (t)
{ memcpy(data->buf, t, data->bufidx); /* copy in-use part */
SOAP_FREE(soap, t);
}
}
if (!final)
{ /* envelope encryption or with shared key? */
if (data->alg & SOAP_MEC_ENV)
ok = EVP_SealUpdate(data->ctx, (unsigned char*)data->buf + data->buflen - k, &m, (unsigned char*)*s, (int)*n);
else
ok = EVP_EncryptUpdate(data->ctx, (unsigned char*)data->buf + data->buflen - k, &m, (unsigned char*)*s, (int)*n);
DBGHEX(TEST, (unsigned char*)(data->buf + data->buflen - k), m);
/* convert to base64 */
soap_mec_put_base64(soap, data, (unsigned char*)(data->buf + data->buflen - k), m);
*s = data->buf;
*n = data->bufidx;
if (!(data->alg & SOAP_MEC_STORE))
data->bufidx = 0;
}
else
{ /* envelope encryption or with shared key? */
if (data->alg & SOAP_MEC_ENV)
ok = EVP_SealFinal(data->ctx, (unsigned char*)data->buf + data->buflen - k, &m);
else
ok = EVP_EncryptFinal(data->ctx, (unsigned char*)data->buf + data->buflen - k, &m);
DBGHEX(TEST, (unsigned char*)(data->buf + data->buflen - k), m);
/* convert to base64 */
soap_mec_put_base64(soap, data, (unsigned char*)(data->buf + data->buflen - k), m);
soap_mec_end_base64(soap, data);
*s = data->buf;
*n = data->bufidx;
if (!(data->alg & SOAP_MEC_STORE))
data->bufidx = 0;
}
if (m > (int)k)
{ DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Assertion m<=k failed k=%lu m=%lu\n", (unsigned long)k, (unsigned long)m));
return soap->error = SOAP_USER_ERROR;
}
return soap_mec_check(soap, data, ok, "soap_mec_upd_enc() failed");
}
/**
@fn int soap_mec_upd_dec(struct soap *soap, struct soap_mec_data *data, const char **s, size_t *n, int final)
@brief Update decryption state with input cipher data in base64 format and output in plain text (or raw) format
@param soap context
@param[in,out] data mecevp engine context
@param[in,out] s input cipher data, afterwards points to output plain text
@param[in,out] n size of input cipher data, afterwards size of plain text
@param[in] final flag to indicate no more input, output is flushed to s
@return SOAP_OK or SOAP_SSL_ERROR
*/
static int
soap_mec_upd_dec(struct soap *soap, struct soap_mec_data *data, const char **s, size_t *n, int final)
{ const char *r = NULL;
size_t k = 0, l = 0, m = 0;
int len = 0;
int ok = 1;
enum SOAP_MEC_STATE state = data->state;
if (final && state == SOAP_MEC_STATE_DECRYPT)
data->state = SOAP_MEC_STATE_FINAL;
/* if flushing the buf, no base64-decode or decryption to do */
if (state == SOAP_MEC_STATE_FLUSH)
{ /* old + new fit in buf? */
if (data->bufidx + *n > data->buflen)
{ char *t = data->buf;
do
data->buflen += sizeof(soap->buf);
while (data->buflen < data->bufidx + *n);
DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Enlarging buffer n=%lu\n", (unsigned long)data->buflen));
data->buf = (char*)SOAP_MALLOC(soap, data->buflen);
if (t)
{ memcpy(data->buf, t, data->bufidx); /* copy old */
SOAP_FREE(soap, t);
}
}
/* concat old + new */
memcpy(data->buf + data->bufidx, *s, *n);
*s = data->buf;
*n += data->bufidx;
DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Flush state n=%lu\n", (unsigned long)*n));
/* release old + new for next round (assuming consumer fetches all) */
if (!(data->alg & SOAP_MEC_STORE))
data->bufidx = 0;
return SOAP_OK;
}
if (state == SOAP_MEC_STATE_INIT)
{ /* at init, base64 is in data->buf[bufidx] copied from buf[] */
data->i = 0;
data->m = 0;
k = (data->bufidx + *n + 3) / 4 * 3; /* decoded size from old + new */
}
else
k = (*n + 3) / 4 * 3; /* base64-decoded size */
m = k + EVP_CIPHER_block_size(data->type); /* decrypted data size */
/* decrypted + base64-decoded will fit in current buf? */
if (data->bufidx + m > data->buflen - k)
{ /* no, need to enlarge */
char *t = data->buf;
do
data->buflen += sizeof(soap->buf);
while (data->buflen < data->bufidx + m + k);
DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Enlarging buffer n=%lu\n", (unsigned long)data->buflen));
data->buf = (char*)SOAP_MALLOC(soap, data->buflen);
if (t)
{ memcpy(data->buf, t, data->bufidx); /* copy old part */
SOAP_FREE(soap, t);
}
}
/* base64 decode */
if (state == SOAP_MEC_STATE_INIT)
{ DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Init state stage 1 n=%lu\n", (unsigned long)data->bufidx));
/* base64 is in data buf[0..bufidx-1] and *s */
if (soap_mec_get_base64(soap, data, data->buf + data->buflen - k, &m, data->buf, data->bufidx, &r, &l))
return soap->error;
/* position 'r' is at a spot that gets overwritten, copy to rest */
if (r && l)
{ DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Init state stage 2 rest=%lu bytes\n", (unsigned long)l));
}
else
{ size_t j;
DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Init state stage 3 n=%lu\n", (unsigned long)*n));
/* base64-decode *s */
if (soap_mec_get_base64(soap, data, data->buf + data->buflen - k + m, &j, *s, *n, &r, &l))
return soap->error;
m += j;
}
data->bufidx = 0;
}
else if (state != SOAP_MEC_STATE_FINAL && state != SOAP_MEC_STATE_FLUSH)
{ /* base64-decode *s */
if (soap_mec_get_base64(soap, data, data->buf + data->buflen - k, &m, *s, *n, &r, &l))
return soap->error;
}
if (r && l)
{ DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Rest=%lu bytes\n", (unsigned long)l));
if (data->restlen < l)
{ if (data->rest)
SOAP_FREE(soap, data->rest);
data->rest = (char*)SOAP_MALLOC(soap, l);
}
data->restlen = l;
memcpy(data->rest, r, l);
}
/* debug */
DBGHEX(TEST, (unsigned char*)(data->buf + data->buflen - k), m);
DBGLOG(TEST, SOAP_MESSAGE(fdebug, "\n--\n"));
/* decryption of data buf[buflen-k] */
switch (data->state)
{ case SOAP_MEC_STATE_INIT:
/* move to next state */
state = SOAP_MEC_STATE_IV;
case SOAP_MEC_STATE_IV:
/* get the IV data from buf[buflen-k] */
memmove(data->buf + data->bufidx, data->buf + data->buflen - k, m);
/* add to IV */
data->bufidx += m;
/* got all IV data? */
if (data->bufidx >= (size_t)EVP_CIPHER_iv_length(data->type))
{ DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Get IV = "));
DBGHEX(TEST, (unsigned char*)data->buf, EVP_CIPHER_iv_length(data->type));
DBGLOG(TEST, SOAP_MESSAGE(fdebug, "\nInitializing alg=%x\n", data->alg));
switch (data->alg & SOAP_MEC_MASK)
{ case SOAP_MEC_ENV_DEC_DES_CBC:
case SOAP_MEC_ENV_DEC_AES128_CBC:
case SOAP_MEC_ENV_DEC_AES192_CBC:
case SOAP_MEC_ENV_DEC_AES256_CBC:
case SOAP_MEC_ENV_DEC_AES512_CBC:
ok = EVP_OpenInit(data->ctx, data->type, data->key, data->keylen, (unsigned char*)data->buf, (EVP_PKEY*)data->pkey);
DBGLOG(TEST, SOAP_MESSAGE(fdebug, "EVP_OpenInit ok=%d\n", ok));
break;
case SOAP_MEC_DEC_DES_CBC:
case SOAP_MEC_DEC_AES128_CBC:
case SOAP_MEC_DEC_AES192_CBC:
case SOAP_MEC_DEC_AES256_CBC:
case SOAP_MEC_DEC_AES512_CBC:
ok = EVP_DecryptInit_ex(data->ctx, data->type, NULL, data->key, (unsigned char*)data->buf);
DBGLOG(TEST, SOAP_MESSAGE(fdebug, "EVP_DecryptInit_ex ok=%d\n", ok));
break;
}
if (ok)
{ /* shift rest of data to cipher section */
k = data->bufidx - EVP_CIPHER_iv_length(data->type);
memmove(data->buf + data->buflen - k, data->buf + EVP_CIPHER_iv_length(data->type), k);
DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Decrypt %lu bytes\n", (unsigned long)k));
/* decrypt to buf */
len = 0;
switch (data->alg & SOAP_MEC_MASK)
{ case SOAP_MEC_ENV_DEC_DES_CBC:
case SOAP_MEC_ENV_DEC_AES128_CBC:
case SOAP_MEC_ENV_DEC_AES192_CBC:
case SOAP_MEC_ENV_DEC_AES256_CBC:
case SOAP_MEC_ENV_DEC_AES512_CBC:
ok = EVP_OpenUpdate(data->ctx, (unsigned char*)data->buf, &len, (unsigned char*)(data->buf + data->buflen - k), k);
break;
case SOAP_MEC_DEC_DES_CBC:
case SOAP_MEC_DEC_AES128_CBC:
case SOAP_MEC_DEC_AES192_CBC:
case SOAP_MEC_DEC_AES256_CBC:
case SOAP_MEC_DEC_AES512_CBC:
ok = EVP_DecryptUpdate(data->ctx, (unsigned char*)data->buf, &len, (unsigned char*)(data->buf + data->buflen - k), k);
break;
}
*s = data->buf;
*n = (size_t)len;
}
if (!(data->alg & SOAP_MEC_STORE))
data->bufidx = 0; /* next decoded goes to start of buf */
else
data->bufidx = *n;
/* next state */
state = SOAP_MEC_STATE_DECRYPT;
}
else
{ /* nothing to return yet, need more data */
*n = 0;
}
DBGLOG(TEST, SOAP_MESSAGE(fdebug, "IV/decrypt state n=%lu\n", (unsigned long)*n));
break;
case SOAP_MEC_STATE_DECRYPT:
DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Decrypt %lu bytes\n", (unsigned long)m));
len = 0;
switch (data->alg & SOAP_MEC_MASK)
{ case SOAP_MEC_ENV_DEC_DES_CBC:
case SOAP_MEC_ENV_DEC_AES128_CBC:
case SOAP_MEC_ENV_DEC_AES192_CBC:
case SOAP_MEC_ENV_DEC_AES256_CBC:
case SOAP_MEC_ENV_DEC_AES512_CBC:
ok = EVP_OpenUpdate(data->ctx, (unsigned char*)(data->buf + data->bufidx), &len, (unsigned char*)(data->buf + data->buflen - k), m);
break;
case SOAP_MEC_DEC_DES_CBC:
case SOAP_MEC_DEC_AES128_CBC:
case SOAP_MEC_DEC_AES192_CBC:
case SOAP_MEC_DEC_AES256_CBC:
case SOAP_MEC_DEC_AES512_CBC:
ok = EVP_DecryptUpdate(data->ctx, (unsigned char*)(data->buf + data->bufidx), &len, (unsigned char*)data->buf + data->buflen - k, m);
break;
}
*s = data->buf;
*n = data->bufidx + (size_t)len;
DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Decrypt state n=%lu\n", (unsigned long)*n));
if (!(data->alg & SOAP_MEC_STORE))
data->bufidx = 0; /* next decoded goes to start of buf */
break;
case SOAP_MEC_STATE_FINAL:
{ /* we know there is enough space to flush *s and *n through the buf */
const char *t = *s;
k = *n;
len = 0;
switch (data->alg & SOAP_MEC_MASK)
{ case SOAP_MEC_ENV_DEC_DES_CBC:
case SOAP_MEC_ENV_DEC_AES128_CBC:
case SOAP_MEC_ENV_DEC_AES192_CBC:
case SOAP_MEC_ENV_DEC_AES256_CBC:
case SOAP_MEC_ENV_DEC_AES512_CBC:
ok = EVP_OpenFinal(data->ctx, (unsigned char*)(data->buf + data->bufidx), &len);
break;
case SOAP_MEC_DEC_DES_CBC:
case SOAP_MEC_DEC_AES128_CBC:
case SOAP_MEC_DEC_AES192_CBC:
case SOAP_MEC_DEC_AES256_CBC:
case SOAP_MEC_DEC_AES512_CBC:
ok = EVP_DecryptFinal(data->ctx, (unsigned char*)(data->buf + data->bufidx), &len);
break;
}
DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Append %d bytes from decrypted\n", len));
*s = data->buf;
*n = data->bufidx + (size_t)len;
if (data->restlen)
{ DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Append %lu bytes from rest\n", data->restlen));
memcpy(data->buf + *n, data->rest, data->restlen);
*n += data->restlen;
}
if (k)
{ DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Append %lu bytes from input\n", k));
memmove(data->buf + *n, t, k);
*n += k;
}
if (!(data->alg & SOAP_MEC_STORE))
data->bufidx = 0; /* next decoded goes to start of buf */
DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Final len=%lu\n", (unsigned long)*n));
state = SOAP_MEC_STATE_FLUSH; /* flush data buf, if needed */
break;
}
default:
DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Error in decryption state\n"));
return soap->error = SOAP_SSL_ERROR;
}
if (l)
{ if (state == SOAP_MEC_STATE_DECRYPT)
{ state = SOAP_MEC_STATE_FINAL;
DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Append rest of stream %lu (%lu <= %lu)\n", data->restlen, *n, data->buflen));
}
else
{ DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Error in decryption state\n"));
return soap->error = SOAP_SSL_ERROR;
}
}
data->state = state;
return soap_mec_check(soap, data, ok, "soap_mec_upd_dec() failed");
}
/**
@fn int soap_mec_check(struct soap *soap, struct soap_mec_data *data, int ok, const char *msg)
@brief Check result of init/update/final mecevp engine operations.
@param soap context
@param[in,out] data mecevp engine context
@param[in] ok EVP error value
@param[in] msg error message
@return SOAP_OK or SOAP_SSL_ERROR
*/
static int
soap_mec_check(struct soap *soap, struct soap_mec_data *data, int ok, const char *msg)
{ if (ok <= 0)