This file is indexed.

/usr/share/gsoap/plugin/mecevp.c is in gsoap 2.8.4-2.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

   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
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
/*
	mecevp.c

	gSOAP interface for streaming message encryption and decryption

gSOAP XML Web services tools
Copyright (C) 2000-2010, 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-2010, 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_DES_CBC	envelope decryption with triple DES CBC
- @ref SOAP_MEC_ENC_DES_CBC		symmetric encryption with triple DES CBC
- @ref SOAP_MEC_ENC_DES_CBC		symmetric decryption with triple DES CBC

Algorithm options:

- @ref SOAP_MEC_STORE		buffer all output in memory

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_smd_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_smd_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(); /* just in case */
  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)
    data->type = EVP_get_cipherbyname("AES128");
  else if (alg & SOAP_MEC_AES256)
    data->type = EVP_get_cipherbyname("AES256");
  else if (alg & SOAP_MEC_AES512)
    data->type = EVP_get_cipherbyname("AES512");
  else
    data->type = EVP_enc_null();
  data->rest = NULL;
  data->restlen = 0;
  if (alg & SOAP_MEC_ENC)
    EVP_EncryptInit_ex(data->ctx, data->type, NULL, NULL, NULL);
  switch (alg & ~SOAP_MEC_STORE)
  { 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 >= 0x01000000L)
      *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:
      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_DES_CBC:
    case SOAP_MEC_DEC_DES_CBC:
      data->pkey = pkey;
      data->key = key;
      data->keylen = *keylen;
      data->buflen = 2 * sizeof(soap->buf) + EVP_CIPHER_block_size(data->type);
      data->buf = (char*)SOAP_MALLOC(soap, data->buflen);
      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=%d\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(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)
{ 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=%d\n", data->alg));
  if (key)
    data->key = key;
  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
  { 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_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=%d\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=%d\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 triple DES 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_ENC|SOAP_MEC_STORE))
  { case SOAP_MEC_DES_CBC:
      return 21; /* triple DES 168 bits */
  }
  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=%d n=%lu (%p) --\n", data->alg, (unsigned long)*n, 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 > 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 > 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 >= 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=%d\n", data->alg));
        switch (data->alg & ~SOAP_MEC_STORE)
        { case SOAP_MEC_ENV_DEC_DES_CBC:
            ok = EVP_OpenInit(data->ctx, data->type, data->key, data->keylen, (unsigned char*)data->buf, (EVP_PKEY*)data->pkey);
	    break;
          case SOAP_MEC_DEC_DES_CBC:
            ok = EVP_DecryptInit_ex(data->ctx, data->type, NULL, data->key, (unsigned char*)data->buf);
	    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_STORE)
          { case SOAP_MEC_ENV_DEC_DES_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:
              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_STORE)
      { case SOAP_MEC_ENV_DEC_DES_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:
          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_STORE)
      { case SOAP_MEC_ENV_DEC_DES_CBC:
          ok = EVP_OpenFinal(data->ctx, (unsigned char*)(data->buf + data->bufidx), &len);
          break;
        case SOAP_MEC_DEC_DES_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)
  { unsigned long r;
    DBGLOG(TEST, SOAP_MESSAGE(fdebug, "-- MEC Error (%d)", ok));
    while ((r = ERR_get_error()))
    { ERR_error_string_n(r, soap->msgbuf, sizeof(soap->msgbuf));
      DBGLOG(TEST, SOAP_MESSAGE(fdebug, " %s: \"%s\";", msg, soap->msgbuf));
    }
    DBGLOG(TEST, SOAP_MESSAGE(fdebug, "\n"));
    /* cleanup and reset mecevp engine */
    soap_mec_cleanup(soap, data);
    return soap_set_receiver_error(soap, msg, soap->msgbuf, SOAP_SSL_ERROR);
  }
  return SOAP_OK;
}

/**
@fn void soap_mec_put_base64(struct soap *soap, struct soap_mec_data *data, const unsigned char *s, int n)
@brief Write base64 formatted data stored in s of length n to internal buffer
@param soap context
@param[in,out] data mecevp engine context
@param[in] s data to convert
@param[in] n length of data to convert
*/
static void
soap_mec_put_base64(struct soap *soap, struct soap_mec_data *data, const unsigned char *s, int n)
{ char *t;
  int i;
  unsigned long m;
  if (!s || !n)
    return;
  t = data->buf + data->bufidx;
  i = data->i;
  m = data->m;
  while (n--)
  { m = (m << 8) | *s++;
    if (i++ == 2)
    { for (i = 4; i > 0; m >>= 6)
        t[--i] = soap_base64o[m & 0x3F];
      t += 4;
      data->bufidx += 4;
    }
  }
  data->i = i;
  data->m = m;
}

/**
@fn void soap_mec_end_base64(struct soap *soap, struct soap_mec_data *data)
@brief End writing base64 formatted data to internal buffer
@param soap context
@param[in,out] data mecevp engine context
*/
static void
soap_mec_end_base64(struct soap *soap, struct soap_mec_data *data)
{ if (data->i)
  { char *t;
    int i;
    unsigned long m;
    t = data->buf + data->bufidx;
    i = data->i;
    m = data->m;
    for (; i < 3; i++)
      m <<= 8;
    for (i++; i > 0; m >>= 6)
      t[--i] = soap_base64o[m & 0x3F];
    for (i = 3; i > data->i; i--)
      t[i] = '=';
    data->bufidx += 4;
  }
  data->i = 0;
  data->m = 0;
}

/**
@fn 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)
@brief Convert base64-formatted data from s[0..n-1] into raw data in t[0..l-1]
where l is the max size and set equal or lower if data fits in t. If data does
not fit r points to remainder in s[0..n-1] of size k.
@param soap context
@param[in,out] data mecevp engine context
@param[in] t raw data (converted from base64)
@param[in,out] l max size of t[], afterwards actual size of data written to t[]
@param[in] s data in base64 format
@param[in] n size of base64 data
@param[out] r if data does not fit in t[], points to s[] remainder to convert
@param[out] k if data does not fit in t[], size of remainder in r[]
@return SOAP_OK or SOAP_SSL_ERROR
*/
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)
{ int i;
  unsigned long m;
  size_t j;
  int c;
  i = data->i;
  m = data->m;
  j = 0;
  for (;;)
  { do
    { if (!n--)
      { *l = j;
        data->i = i;
        data->m = m;
	*r = NULL;
	*k = 0;
	return SOAP_OK;
      }
      c = *s++;
      if (c == '=' || c == '<')
      { switch (i)
        { case 2:
            *t++ = (char)((m >> 4) & 0xFF);
            j++;
            break;
          case 3:
            *t++ = (char)((m >> 10) & 0xFF);
            *t++ = (char)((m >> 2) & 0xFF);
            j += 2;
        }
	if (c == '<')
        { s--;
	  n++;
	}
	else if (n && *s == '=')
        { s++;
	  n--;
	}
        *l = j;
	*k = n;
	if (n)
	  *r = s;
	else
	  *r = NULL;
        return SOAP_OK;
      }
      if (c >= '+' && c <= '+' + 79)
      { register int b = soap_base64i[c - '+'];
        if (b >= 64)
          return soap->error = SOAP_SSL_ERROR;
        m = (m << 6) + b;
        i++;
      }
      else if (c < 0 || c > 32)
        return soap->error = SOAP_SSL_ERROR;
    } while (i < 4);
    *t++ = (char)((m >> 16) & 0xFF);
    *t++ = (char)((m >> 8) & 0xFF);
    *t++ = (char)(m & 0xFF);
    j += 3;
    i = 0;
    m = 0;
  }
}

/******************************************************************************\
 *
 * Callbacks registered by plugin
 *
\******************************************************************************/

/**
@fn int soap_mec_filtersend(struct soap *soap, const char **s, size_t *n)
@brief Callback to modify outbound messages by encrypting through the engine.
@param soap context
@param[in,out] s plain text message, afterwards set to encrypted message
@param[in,out] n plain text message size, afterwards set to encrypted message size
@return SOAP_OK or SOAP_SSL_ERROR
*/
static int
soap_mec_filtersend(struct soap *soap, const char **s, size_t *n)
{ struct soap_mec_data *data = (struct soap_mec_data*)soap->data[1];
  if (!data)
     return SOAP_OK;
  /* encrypt to base64 */
  return soap_mec_upd(soap, data, s, n, 0);
}

/**
@fn int soap_mec_filterrecv(struct soap *soap, char *buf, size_t *len, size_t maxlen)
@brief Callback to modify inbound messages by decrypting through the engine.
@param soap context
@param[in,out] buf encrypted message, afterwards contains decrypted content
@param[in,out] len encrypted message size, afterwards set to decrypted content size
@param[in] maxlen max length of allocated buf size to contain decrypted content
@return SOAP_OK or SOAP_SSL_ERROR
*/
static int
soap_mec_filterrecv(struct soap *soap, char *buf, size_t *len, size_t maxlen)
{ struct soap_mec_data *data = (struct soap_mec_data*)soap->data[1];
  const char *s = buf;
  if (!data || data->alg == SOAP_MEC_NONE || (data->alg & SOAP_MEC_ENC))
     return SOAP_OK;
  DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Filter recv in=%lu\n", (unsigned long)*len));
  /* convert s[len] to new s with len (new s = data->buf) */
  if (len)
  { if (soap_mec_upd(soap, data, &s, len, 0))
      return soap->error;
  }
  else if (soap_mec_upd(soap, data, &s, len, 1))
    return soap->error;
  /* does the result fit in buf[maxlen]? */
  if (*len <= maxlen)
    memcpy(buf, s, *len); /* yes: copy data to buf[] */
  else
  { memcpy(buf, s, maxlen); /* no: copy first part to buf[maxlen] */
    memmove(data->buf, s + maxlen, *len - maxlen); /* shift rest to the left */
    data->bufidx = *len - maxlen; /* keep rest of the data in s (data->buf) */
    *len = maxlen;
  }
  DBGLOG(TEST, SOAP_MESSAGE(fdebug, "Filter recv out=%lu\n", (unsigned long)*len));
  return SOAP_OK;
}

#ifdef __cplusplus
}
#endif