Coverage Report

Created: 2026-03-06 06:42

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/xmlsec/src/base64.c
Line
Count
Source
1
/*
2
 * XML Security Library (http://www.aleksey.com/xmlsec).
3
 *
4
 *
5
 * This is free software; see the Copyright file in the source
6
 * distribution for precise wording.
7
 *
8
 * Copyright (C) 2002-2024 Aleksey Sanin <aleksey@aleksey.com>. All Rights Reserved.
9
 */
10
/**
11
 * SECTION:base64
12
 * @Short_description: Base64 encoding/decoding functions and base64 transform implementation.
13
 * @Stability: Stable
14
 *
15
 */
16
17
#include "globals.h"
18
19
#include <stdlib.h>
20
#include <stdio.h>
21
#include <string.h>
22
23
#include <libxml/tree.h>
24
25
#include <xmlsec/xmlsec.h>
26
#include <xmlsec/keys.h>
27
#include <xmlsec/transforms.h>
28
#include <xmlsec/base64.h>
29
#include <xmlsec/errors.h>
30
31
#include "cast_helpers.h"
32
33
/*
34
 * the table to map numbers to base64
35
 */
36
static const xmlSecByte base64[] =
37
{
38
/*   0    1    2    3    4    5    6    7   */
39
    'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', /* 0 */
40
    'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', /* 1 */
41
    'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', /* 2 */
42
    'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', /* 3 */
43
    'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', /* 4 */
44
    'o', 'p', 'q', 'r', 's', 't', 'u', 'v', /* 5 */
45
    'w', 'x', 'y', 'z', '0', '1', '2', '3', /* 6 */
46
    '4', '5', '6', '7', '8', '9', '+', '/'  /* 7 */
47
};
48
49
50
/* few macros to simplify the code */
51
0
#define xmlSecBase64Encode1(a)          (((a) >> 2) & 0x3F)
52
0
#define xmlSecBase64Encode2(a, b)       ((((a) << 4) & 0x30) + (((b) >> 4) & 0x0F))
53
0
#define xmlSecBase64Encode3(b, c)       ((((b) << 2) & 0x3c) + (((c) >> 6) & 0x03))
54
0
#define xmlSecBase64Encode4(c)          ((c) & 0x3F)
55
56
0
#define xmlSecBase64Decode1(a, b)       ((xmlSecByte)(((a) << 2) | (((b) & 0x3F) >> 4)))
57
0
#define xmlSecBase64Decode2(b, c)       ((xmlSecByte)(((b) << 4) | (((c) & 0x3F) >> 2)))
58
0
#define xmlSecBase64Decode3(c, d)       ((xmlSecByte)(((c) << 6) | ((d) & 0x3F)))
59
60
0
#define xmlSecIsBase64Char(ch)          ((((ch) >= 'A') && ((ch) <= 'Z')) || \
61
0
                                         (((ch) >= 'a') && ((ch) <= 'z')) || \
62
0
                                         (((ch) >= '0') && ((ch) <= '9')) || \
63
0
                                         ((ch) == '+') || ((ch) == '/'))
64
0
#define xmlSecIsBase64Space(ch)         (((ch) == ' ') || ((ch) == '\t') || \
65
0
                                         ((ch) == '\x0d') || ((ch) == '\x0a'))
66
67
68
69
/***********************************************************************
70
 *
71
 * Base64 Context
72
 *
73
 ***********************************************************************/
74
typedef enum {
75
    xmlSecBase64StatusConsumeAndNext  = 0,
76
    xmlSecBase64StatusConsumeAndRepeat,
77
    xmlSecBase64StatusNext,
78
    xmlSecBase64StatusDone,
79
    xmlSecBase64StatusFailed
80
} xmlSecBase64Status;
81
82
struct _xmlSecBase64Ctx {
83
    int                 encode;
84
    xmlSecSize          columns;
85
    int                 inByte;
86
    xmlSecSize          inPos;
87
    xmlSecSize          linePos;
88
    int                 finished;
89
};
90
91
static xmlSecBase64Status       xmlSecBase64CtxEncodeByte       (xmlSecBase64CtxPtr ctx,
92
                                                                 xmlSecByte  inByte,
93
                                                                 xmlSecByte* outByte);
94
static xmlSecBase64Status       xmlSecBase64CtxEncodeByteFinal  (xmlSecBase64CtxPtr ctx,
95
                                                                 xmlSecByte* outByte);
96
static xmlSecBase64Status       xmlSecBase64CtxDecodeByte       (xmlSecBase64CtxPtr ctx,
97
                                                                 xmlSecByte inByte,
98
                                                                 xmlSecByte* outByte);
99
static int                      xmlSecBase64CtxEncode           (xmlSecBase64CtxPtr ctx,
100
                                                                 const xmlSecByte* inBuf,
101
                                                                 xmlSecSize inBufSize,
102
                                                                 xmlSecSize* inBufResSize,
103
                                                                 xmlSecByte* outBuf,
104
                                                                 xmlSecSize outBufSize,
105
                                                                 xmlSecSize* outBufResSize);
106
static int                      xmlSecBase64CtxEncodeFinal      (xmlSecBase64CtxPtr ctx,
107
                                                                 xmlSecByte* outBuf,
108
                                                                 xmlSecSize outBufSize,
109
                                                                 xmlSecSize* outBufResSize);
110
static int                      xmlSecBase64CtxDecode           (xmlSecBase64CtxPtr ctx,
111
                                                                 const xmlSecByte* inBuf,
112
                                                                 xmlSecSize inBufSize,
113
                                                                 xmlSecSize* inBufResSize,
114
                                                                 xmlSecByte* outBuf,
115
                                                                 xmlSecSize outBufSize,
116
                                                                 xmlSecSize* outBufResSize);
117
static int                      xmlSecBase64CtxDecodeIsFinished (xmlSecBase64CtxPtr ctx);
118
119
120
static int g_xmlsec_base64_default_line_size = XMLSEC_BASE64_LINESIZE;
121
122
/**
123
 * xmlSecBase64GetDefaultLineSize:
124
 *
125
 * Gets the current default line size.
126
 *
127
 * Returns: the current default line size.
128
 */
129
int
130
xmlSecBase64GetDefaultLineSize(void)
131
0
{
132
0
    return g_xmlsec_base64_default_line_size;
133
0
}
134
135
/**
136
 * xmlSecBase64SetDefaultLineSize:
137
 * @columns: number of columns
138
 *
139
 * Sets the current default line size.
140
 */
141
void
142
xmlSecBase64SetDefaultLineSize(int columns)
143
0
{
144
0
    g_xmlsec_base64_default_line_size = columns;
145
0
}
146
147
/**
148
 * xmlSecBase64CtxCreate:
149
 * @encode:             the encode/decode flag (1 - encode, 0 - decode)
150
 * @columns:            the max line length.
151
 *
152
 * Allocates and initializes new base64 context.
153
 *
154
 * Returns: a pointer to newly created #xmlSecBase64Ctx structure
155
 * or NULL if an error occurs.
156
 */
157
xmlSecBase64CtxPtr
158
0
xmlSecBase64CtxCreate(int encode, int columns) {
159
0
    xmlSecBase64CtxPtr ctx;
160
0
    int ret;
161
162
    /*
163
     * Allocate a new xmlSecBase64CtxPtr and fill the fields.
164
     */
165
0
    ctx = (xmlSecBase64CtxPtr) xmlMalloc(sizeof(xmlSecBase64Ctx));
166
0
    if (ctx == NULL) {
167
0
        xmlSecMallocError(sizeof(xmlSecBase64Ctx), NULL);
168
0
        return(NULL);
169
0
    }
170
171
0
    ret = xmlSecBase64CtxInitialize(ctx, encode, columns);
172
0
    if(ret < 0) {
173
0
        xmlSecInternalError("xmlSecBase64CtxInitialize", NULL);
174
0
        xmlSecBase64CtxDestroy(ctx);
175
0
        return(NULL);
176
0
    }
177
0
    return(ctx);
178
0
}
179
180
/**
181
 * xmlSecBase64CtxDestroy:
182
 * @ctx:                the pointer to #xmlSecBase64Ctx structure.
183
 *
184
 * Destroys base64 context.
185
 */
186
void
187
0
xmlSecBase64CtxDestroy(xmlSecBase64CtxPtr ctx) {
188
0
    xmlSecAssert(ctx != NULL);
189
190
0
    xmlSecBase64CtxFinalize(ctx);
191
0
    xmlFree(ctx);
192
0
}
193
194
/**
195
 * xmlSecBase64CtxInitialize:
196
 * @ctx:                the pointer to #xmlSecBase64Ctx structure,
197
 * @encode:             the encode/decode flag (1 - encode, 0 - decode)
198
 * @columns:            the max line length.
199
 *
200
 * Initializes new base64 context.
201
 *
202
 * Returns: 0 on success and a negative value otherwise.
203
 */
204
int
205
0
xmlSecBase64CtxInitialize(xmlSecBase64CtxPtr ctx, int encode, int columns) {
206
0
    xmlSecAssert2(ctx != NULL, -1);
207
208
0
    memset(ctx, 0, sizeof(xmlSecBase64Ctx));
209
210
0
    ctx->encode  = encode;
211
0
    XMLSEC_SAFE_CAST_INT_TO_SIZE(columns, ctx->columns, return(-1), NULL);
212
0
    return(0);
213
0
}
214
215
/**
216
 * xmlSecBase64CtxFinalize:
217
 * @ctx:                the pointer to #xmlSecBase64Ctx structure,
218
 *
219
 * Frees all the resources allocated by @ctx.
220
 */
221
void
222
0
xmlSecBase64CtxFinalize(xmlSecBase64CtxPtr ctx) {
223
0
    xmlSecAssert(ctx != NULL);
224
225
0
    memset(ctx, 0, sizeof(xmlSecBase64Ctx));
226
0
}
227
228
/**
229
 * xmlSecBase64CtxUpdate_ex:
230
 * @ctx:                the pointer to #xmlSecBase64Ctx structure
231
 * @in:                 the input buffer
232
 * @inSize:             the input buffer size
233
 * @out:                the output buffer
234
 * @outSize:            the output buffer size
235
 * @outWritten:         the pointer to store the number of bytes written into the output
236
 *
237
 * Encodes or decodes the next piece of data from input buffer.
238
 *
239
 * Returns: 0 on success and a negative value otherwise.
240
 */
241
int
242
xmlSecBase64CtxUpdate_ex(xmlSecBase64CtxPtr ctx, const xmlSecByte *in, xmlSecSize inSize,
243
0
    xmlSecByte *out, xmlSecSize outSize, xmlSecSize* outWritten) {
244
0
    xmlSecSize inRead = 0;
245
0
    int ret;
246
247
0
    xmlSecAssert2(ctx != NULL, -1);
248
0
    xmlSecAssert2(in != NULL, -1);
249
0
    xmlSecAssert2(out != NULL, -1);
250
0
    xmlSecAssert2(outWritten != NULL, -1);
251
252
0
    if(ctx->encode != 0) {
253
0
        ret = xmlSecBase64CtxEncode(ctx, in, inSize, &inRead, out, outSize, outWritten);
254
0
        if((ret < 0) || (inRead != inSize)) {
255
0
            xmlSecInternalError("xmlSecBase64CtxEncode", NULL);
256
0
            return(-1);
257
0
        }
258
0
    } else {
259
0
        ret = xmlSecBase64CtxDecode(ctx, in, inSize, &inRead, out, outSize, outWritten);
260
0
        if((ret < 0) || (inRead != inSize)) {
261
0
            xmlSecInternalError("xmlSecBase64CtxDecode", NULL);
262
0
            return(-1);
263
0
        }
264
0
    }
265
266
0
    return(0);
267
0
}
268
269
/**
270
 * xmlSecBase64CtxFinal_ex:
271
 * @ctx:                the pointer to #xmlSecBase64Ctx structure
272
 * @out:                the output buffer
273
 * @outSize:            the output buffer size
274
 * @outWritten:         the pointer to store the number of bytes written into the output
275
 *
276
 * Encodes or decodes the last piece of data stored in the context
277
 * and finalizes the result.
278
 *
279
 * Returns: 0 on success and a negative value otherwise.
280
 */
281
int
282
0
xmlSecBase64CtxFinal_ex(xmlSecBase64CtxPtr ctx, xmlSecByte *out, xmlSecSize outSize, xmlSecSize* outWritten) {
283
0
    xmlSecAssert2(ctx != NULL, -1);
284
0
    xmlSecAssert2(out != NULL, -1);
285
0
    xmlSecAssert2(outSize > 0, -1);
286
0
    xmlSecAssert2(outWritten != NULL, -1);
287
288
0
    if(ctx->encode != 0) {
289
0
        int ret;
290
291
0
        ret = xmlSecBase64CtxEncodeFinal(ctx, out, outSize, outWritten);
292
0
        if(ret < 0) {
293
0
            xmlSecInternalError2("xmlSecBase64CtxEncodeFinal", NULL,
294
0
                "outSize=" XMLSEC_SIZE_FMT, outSize);
295
0
            return(-1);
296
0
        }
297
0
    } else {
298
0
        if(!xmlSecBase64CtxDecodeIsFinished(ctx)) {
299
0
            xmlSecInternalError("xmlSecBase64CtxDecodeIsFinished", NULL);
300
0
            return(-1);
301
0
        }
302
0
        (*outWritten) = 0;
303
0
    }
304
305
    /* add \0 just in case (if we can) */
306
0
    if(((*outWritten) + 1) < outSize) {
307
0
        out[(*outWritten)] = '\0';
308
0
    }
309
0
    return(0);
310
0
}
311
312
static xmlSecBase64Status
313
0
xmlSecBase64CtxEncodeByte(xmlSecBase64CtxPtr ctx, xmlSecByte inByte, xmlSecByte* outByte) {
314
0
    xmlSecAssert2(ctx != NULL, xmlSecBase64StatusFailed);
315
0
    xmlSecAssert2(outByte != NULL, xmlSecBase64StatusFailed);
316
317
0
    if((ctx->columns > 0) && (ctx->linePos >= ctx->columns)) {
318
0
        (*outByte) = '\n';
319
0
        ctx->linePos = 0;
320
0
        return(xmlSecBase64StatusConsumeAndRepeat);
321
0
    } else if(ctx->inPos == 0) {
322
        /* we just started new block */
323
0
        (*outByte) = base64[xmlSecBase64Encode1(inByte)];
324
0
        ctx->inByte = inByte;
325
0
        ++ctx->linePos;
326
0
        ++ctx->inPos;
327
0
        return(xmlSecBase64StatusConsumeAndNext);
328
0
    } else if(ctx->inPos == 1) {
329
0
        (*outByte) = base64[xmlSecBase64Encode2(ctx->inByte, inByte)];
330
0
        ctx->inByte = inByte;
331
0
        ++ctx->linePos;
332
0
        ++ctx->inPos;
333
0
        return(xmlSecBase64StatusConsumeAndNext);
334
0
    } else if(ctx->inPos == 2) {
335
0
        (*outByte) = base64[xmlSecBase64Encode3(ctx->inByte, inByte)];
336
0
        ctx->inByte = inByte;
337
0
        ++ctx->linePos;
338
0
        ++ctx->inPos;
339
0
        return(xmlSecBase64StatusConsumeAndRepeat);
340
0
    } else if(ctx->inPos == 3) {
341
0
        (*outByte) = base64[xmlSecBase64Encode4(ctx->inByte)];
342
0
        ++ctx->linePos;
343
0
        ctx->inByte = 0;
344
0
        ctx->inPos  = 0;
345
0
        return(xmlSecBase64StatusConsumeAndNext);
346
0
    }
347
348
0
    xmlSecInvalidSizeDataError("ctx->inPos", ctx->inPos, "0,1,2,3", NULL);
349
0
    return(xmlSecBase64StatusFailed);
350
0
}
351
352
static xmlSecBase64Status
353
0
xmlSecBase64CtxEncodeByteFinal(xmlSecBase64CtxPtr ctx, xmlSecByte* outByte) {
354
0
    xmlSecAssert2(ctx != NULL, xmlSecBase64StatusFailed);
355
0
    xmlSecAssert2(outByte != NULL, xmlSecBase64StatusFailed);
356
357
0
    if(ctx->inPos == 0) {
358
0
        return(xmlSecBase64StatusDone);
359
0
    } else if((ctx->columns > 0) && (ctx->linePos >= ctx->columns)) {
360
0
        (*outByte) = '\n';
361
0
        ctx->linePos = 0;
362
0
        return(xmlSecBase64StatusConsumeAndRepeat);
363
0
    } else if(ctx->finished == 0) {
364
0
        ctx->finished = 1;
365
0
        return(xmlSecBase64CtxEncodeByte(ctx, 0, outByte));
366
0
    } else if(ctx->inPos < 3) {
367
0
        (*outByte) = '=';
368
0
        ++ctx->inPos;
369
0
        ++ctx->linePos;
370
0
        return(xmlSecBase64StatusConsumeAndRepeat);
371
0
    } else if(ctx->inPos == 3) {
372
0
        (*outByte) = '=';
373
0
        ++ctx->linePos;
374
0
        ctx->inPos = 0;
375
0
        return(xmlSecBase64StatusConsumeAndRepeat);
376
0
    }
377
378
0
    xmlSecInvalidSizeDataError("ctx->inPos", ctx->inPos, "0,1,2,3", NULL);
379
0
    return(xmlSecBase64StatusFailed);
380
0
}
381
382
static xmlSecBase64Status
383
0
xmlSecBase64CtxDecodeByte(xmlSecBase64CtxPtr ctx, xmlSecByte inByte, xmlSecByte* outByte) {
384
0
    xmlSecAssert2(ctx != NULL, xmlSecBase64StatusFailed);
385
0
    xmlSecAssert2(outByte != NULL, xmlSecBase64StatusFailed);
386
387
0
    if((ctx->finished != 0) && (ctx->inPos == 0)) {
388
0
        return(xmlSecBase64StatusDone);
389
0
    } if(inByte == '=') {
390
0
        ctx->finished = 1;
391
0
        if(ctx->inPos == 2) {
392
0
            ++ctx->inPos;
393
0
            return(xmlSecBase64StatusNext);
394
0
        } else if(ctx->inPos == 3) {
395
0
            ctx->inPos = 0;
396
0
            return(xmlSecBase64StatusNext);
397
0
        } else {
398
0
            xmlSecInvalidSizeDataError("ctx->inPos", ctx->inPos, "2,3", NULL);
399
0
            return(xmlSecBase64StatusFailed);
400
0
        }
401
0
    } else if(xmlSecIsBase64Space(inByte)) {
402
0
        return(xmlSecBase64StatusNext);
403
0
    } else if(!xmlSecIsBase64Char(inByte) || (ctx->finished != 0)) {
404
0
        xmlSecInvalidIntegerDataError("inByte", inByte, "base64 character", NULL);
405
0
        return(xmlSecBase64StatusFailed);
406
0
    }
407
408
    /* convert from character to position in base64 array */
409
0
    if((inByte >= 'A') && (inByte <= 'Z')) {
410
0
        inByte = (xmlSecByte)(inByte - 'A');
411
0
    } else if((inByte >= 'a') && (inByte <= 'z')) {
412
0
        inByte = (xmlSecByte)(26 + (inByte - 'a'));
413
0
    } else if((inByte >= '0') && (inByte <= '9')) {
414
0
        inByte = (xmlSecByte)(52 + (inByte - '0'));
415
0
    } else if(inByte == '+') {
416
0
        inByte = (xmlSecByte)62;
417
0
    } else if(inByte == '/') {
418
0
        inByte = (xmlSecByte)63;
419
0
    }
420
421
0
    if(ctx->inPos == 0) {
422
0
        ctx->inByte = inByte;
423
0
        ++ctx->inPos;
424
0
        return(xmlSecBase64StatusNext);
425
0
    } else if(ctx->inPos == 1) {
426
0
        (*outByte) = xmlSecBase64Decode1(ctx->inByte, inByte);
427
0
        ctx->inByte = inByte;
428
0
        ++ctx->inPos;
429
0
        return(xmlSecBase64StatusConsumeAndNext);
430
0
    } else if(ctx->inPos == 2) {
431
0
        (*outByte) = xmlSecBase64Decode2(ctx->inByte, inByte);
432
0
        ctx->inByte = inByte;
433
0
        ++ctx->inPos;
434
0
        return(xmlSecBase64StatusConsumeAndNext);
435
0
    } else if(ctx->inPos == 3) {
436
0
        (*outByte) = xmlSecBase64Decode3(ctx->inByte, inByte);
437
0
        ctx->inByte = 0;
438
0
        ctx->inPos = 0;
439
0
        return(xmlSecBase64StatusConsumeAndNext);
440
0
    }
441
442
0
    xmlSecInvalidSizeDataError("ctx->inPos", ctx->inPos, "0,1,2,3", NULL);
443
0
    return(xmlSecBase64StatusFailed);
444
0
}
445
446
447
static int
448
xmlSecBase64CtxEncode(xmlSecBase64CtxPtr ctx,
449
                     const xmlSecByte* inBuf, xmlSecSize inBufSize, xmlSecSize* inBufResSize,
450
0
                     xmlSecByte* outBuf, xmlSecSize outBufSize, xmlSecSize* outBufResSize) {
451
0
    xmlSecBase64Status status = xmlSecBase64StatusNext;
452
0
    xmlSecSize inPos, outPos;
453
454
0
    xmlSecAssert2(ctx != NULL, -1);
455
0
    xmlSecAssert2(inBuf != NULL, -1);
456
0
    xmlSecAssert2(inBufResSize != NULL, -1);
457
0
    xmlSecAssert2(outBuf != NULL, -1);
458
0
    xmlSecAssert2(outBufResSize != NULL, -1);
459
460
    /* encode */
461
0
    for(inPos = outPos = 0; (inPos < inBufSize) && (outPos < outBufSize); ) {
462
0
        status = xmlSecBase64CtxEncodeByte(ctx, inBuf[inPos], &(outBuf[outPos]));
463
0
        switch(status) {
464
0
            case xmlSecBase64StatusConsumeAndNext:
465
0
                ++inPos;
466
0
                ++outPos;
467
0
                break;
468
0
            case xmlSecBase64StatusConsumeAndRepeat:
469
0
                ++outPos;
470
0
                break;
471
0
            case xmlSecBase64StatusNext:
472
0
            case xmlSecBase64StatusDone:
473
0
            case xmlSecBase64StatusFailed:
474
0
                xmlSecInternalError2("xmlSecBase64CtxEncodeByte", NULL,
475
0
                    "status=" XMLSEC_ENUM_FMT, XMLSEC_ENUM_CAST(status));
476
0
                return(-1);
477
0
        }
478
0
    }
479
480
0
    (*inBufResSize)  = inPos;
481
0
    (*outBufResSize) = outPos;
482
483
0
    return(0);
484
0
}
485
486
static int
487
xmlSecBase64CtxEncodeFinal(xmlSecBase64CtxPtr ctx, xmlSecByte* outBuf, xmlSecSize outBufSize,
488
0
    xmlSecSize* outBufResSize) {
489
490
0
    xmlSecBase64Status status = xmlSecBase64StatusNext;
491
0
    xmlSecSize outPos;
492
493
0
    xmlSecAssert2(ctx != NULL, -1);
494
0
    xmlSecAssert2(outBuf != NULL, -1);
495
0
    xmlSecAssert2(outBufResSize != NULL, -1);
496
497
    /* encode final bytes */
498
0
    for(outPos = 0; (outPos < outBufSize) && (status != xmlSecBase64StatusDone); ) {
499
0
        status = xmlSecBase64CtxEncodeByteFinal(ctx, &(outBuf[outPos]));
500
0
        switch(status) {
501
0
            case xmlSecBase64StatusConsumeAndNext:
502
0
            case xmlSecBase64StatusConsumeAndRepeat:
503
0
                ++outPos;
504
0
                break;
505
0
            case xmlSecBase64StatusDone:
506
0
                break;
507
0
            case xmlSecBase64StatusNext:
508
0
            case xmlSecBase64StatusFailed:
509
0
                xmlSecInternalError2("xmlSecBase64CtxEncodeByteFinal", NULL,
510
0
                    "status=" XMLSEC_ENUM_FMT, XMLSEC_ENUM_CAST(status));
511
0
                return(-1);
512
0
        }
513
0
    }
514
515
0
    if(status != xmlSecBase64StatusDone) {
516
0
        xmlSecInvalidSizeOtherError("invalid base64 buffer size", NULL);
517
0
        return(-1);
518
0
    }
519
0
    if(outPos < outBufSize) {
520
0
        outBuf[outPos] = '\0'; /* just in case */
521
0
    }
522
523
0
    (*outBufResSize) = outPos;
524
0
    return(0);
525
0
}
526
527
528
static int
529
xmlSecBase64CtxDecode(xmlSecBase64CtxPtr ctx,
530
                     const xmlSecByte* inBuf, xmlSecSize inBufSize, xmlSecSize* inBufResSize,
531
0
                     xmlSecByte* outBuf, xmlSecSize outBufSize, xmlSecSize* outBufResSize) {
532
0
    xmlSecBase64Status status = xmlSecBase64StatusNext;
533
0
    xmlSecSize inPos, outPos;
534
535
0
    xmlSecAssert2(ctx != NULL, -1);
536
0
    xmlSecAssert2(inBuf != NULL, -1);
537
0
    xmlSecAssert2(inBufResSize != NULL, -1);
538
0
    xmlSecAssert2(outBuf != NULL, -1);
539
0
    xmlSecAssert2(outBufResSize != NULL, -1);
540
541
    /* decode */
542
0
    for(inPos = outPos = 0; (inPos < inBufSize) && (outPos < outBufSize) && (status != xmlSecBase64StatusDone); ) {
543
0
        status = xmlSecBase64CtxDecodeByte(ctx, inBuf[inPos], &(outBuf[outPos]));
544
0
        switch(status) {
545
0
            case xmlSecBase64StatusConsumeAndNext:
546
0
                ++inPos;
547
0
                ++outPos;
548
0
                break;
549
0
            case xmlSecBase64StatusConsumeAndRepeat:
550
0
                ++outPos;
551
0
                break;
552
0
            case xmlSecBase64StatusNext:
553
0
                ++inPos;
554
0
                break;
555
0
            case xmlSecBase64StatusDone:
556
0
                break;
557
0
            case xmlSecBase64StatusFailed:
558
0
                xmlSecInternalError2("xmlSecBase64CtxDecodeByte", NULL,
559
0
                    "status=" XMLSEC_ENUM_FMT, XMLSEC_ENUM_CAST(status));
560
0
                return(-1);
561
0
        }
562
0
    }
563
564
    /* skip spaces at the end */
565
0
    while((inPos < inBufSize) && xmlSecIsBase64Space(inBuf[inPos])) {
566
0
        ++inPos;
567
0
    }
568
569
0
    (*inBufResSize)  = inPos;
570
0
    (*outBufResSize) = outPos;
571
572
0
    return(0);
573
0
}
574
575
static int
576
0
xmlSecBase64CtxDecodeIsFinished(xmlSecBase64CtxPtr ctx) {
577
0
    xmlSecAssert2(ctx != NULL, -1);
578
579
0
    return((ctx->inPos == 0) ? 1 : 0);
580
0
}
581
582
static xmlSecSize
583
0
xmlSecBase64GetEncodeSize(xmlSecSize columnsSize, xmlSecSize inSize) {
584
0
    xmlSecSize size;
585
586
0
    size = (4 * inSize) / 3 + 4;
587
0
    if(columnsSize > 0) {
588
0
        size += (size / columnsSize) + 4;
589
0
    }
590
0
    return(size + 1);
591
0
}
592
593
594
static xmlSecSize
595
0
xmlSecBase64GetDecodeSize(xmlSecSize inSize) {
596
0
    return(3 * inSize / 4 + 8);
597
0
}
598
599
/**
600
 * xmlSecBase64Encode:
601
 * @in:                 the input buffer.
602
 * @inSize:             the input buffer size.
603
 * @columns:            the output max line length (if 0 then no line breaks
604
 *                      would be inserted)
605
 *
606
 * Encodes the data from input buffer and allocates the string for the result.
607
 * The caller is responsible for freeing returned buffer using
608
 * xmlFree() function.
609
 *
610
 * Returns: newly allocated string with base64 encoded data
611
 * or NULL if an error occurs.
612
 */
613
xmlChar*
614
0
xmlSecBase64Encode(const xmlSecByte *in, xmlSecSize inSize, int columns) {
615
0
    xmlSecBase64Ctx ctx;
616
0
    int ctx_initialized = 0;
617
0
    xmlSecByte* ptr = NULL;
618
0
    xmlChar* res = NULL;
619
0
    xmlSecSize outSize, outUpdatedSize, outFinalSize;
620
0
    int ret;
621
622
0
    xmlSecAssert2(in != NULL, NULL);
623
624
0
    ret = xmlSecBase64CtxInitialize(&ctx, 1, columns);
625
0
    if(ret < 0) {
626
0
        xmlSecInternalError("xmlSecBase64CtxInitialize", NULL);
627
0
        goto done;
628
0
    }
629
0
    ctx_initialized = 1;
630
631
    /* create result buffer */
632
0
    outSize = xmlSecBase64GetEncodeSize(ctx.columns, inSize);
633
0
    if(outSize == 0) {
634
0
        xmlSecInternalError("xmlSecBase64GetEncodeSize", NULL);
635
0
        goto done;
636
0
    }
637
0
    ptr = (xmlSecByte*)xmlMalloc(outSize);
638
0
    if(ptr == NULL) {
639
0
        xmlSecMallocError(outSize, NULL);
640
0
        goto done;
641
0
    }
642
643
0
    ret = xmlSecBase64CtxUpdate_ex(&ctx, in, inSize, ptr, outSize, &outUpdatedSize);
644
0
    if (ret < 0) {
645
0
        xmlSecInternalError3("xmlSecBase64CtxUpdate_ex", NULL,
646
0
            "inSize=" XMLSEC_SIZE_FMT "; outSize=" XMLSEC_SIZE_FMT, inSize, outSize);
647
0
        goto done;
648
0
    }
649
650
0
    ret = xmlSecBase64CtxFinal_ex(&ctx, ptr + outUpdatedSize, outSize - outUpdatedSize,
651
0
        &outFinalSize);
652
0
    if (ret < 0) {
653
0
        xmlSecInternalError("xmlSecBase64CtxFinal_ex", NULL);
654
0
        goto done;
655
0
    }
656
657
    /* success */
658
0
    ptr[outUpdatedSize + outFinalSize] = '\0';
659
0
    res = BAD_CAST(ptr);
660
0
    ptr = NULL;
661
662
0
done:
663
0
    if(ptr != NULL) {
664
0
        xmlFree(ptr);
665
0
    }
666
0
    if(ctx_initialized != 0) {
667
0
        xmlSecBase64CtxFinalize(&ctx);
668
0
    }
669
0
    return(res);
670
0
}
671
672
/**
673
 * xmlSecBase64Decode_ex:
674
 * @str:                the input buffer with base64 encoded string
675
 * @out:                the output buffer
676
 * @outSize:            the output buffer size
677
 * @outWritten:         the pointer to store the number of bytes written into the output.
678
 *
679
 * Decodes input base64 encoded string and puts result into
680
 * the output buffer.
681
 *
682
 * Returns: 0 on success and a negative value otherwise.
683
 */
684
int
685
0
xmlSecBase64Decode_ex(const xmlChar* str, xmlSecByte* out, xmlSecSize outSize, xmlSecSize* outWritten) {
686
0
    xmlSecBase64Ctx ctx;
687
0
    int ctx_initialized = 0;
688
0
    xmlSecSize outUpdateSize, outFinalSize;
689
0
    int ret;
690
0
    int res = -1;
691
692
0
    xmlSecAssert2(str != NULL, -1);
693
0
    xmlSecAssert2(out != NULL, -1);
694
0
    xmlSecAssert2(outWritten != NULL, -1);
695
696
0
    ret = xmlSecBase64CtxInitialize(&ctx, 0, 0);
697
0
    if(ret < 0) {
698
0
        xmlSecInternalError("xmlSecBase64CtxInitialize", NULL);
699
0
        goto done;
700
0
    }
701
0
    ctx_initialized = 1;
702
703
0
    ret = xmlSecBase64CtxUpdate_ex(&ctx, (const xmlSecByte*)str, xmlSecStrlen(str),
704
0
        out, outSize, &outUpdateSize);
705
0
    if (ret < 0) {
706
0
        xmlSecInternalError("xmlSecBase64CtxUpdate_ex", NULL);
707
0
        goto done;
708
0
    }
709
710
0
    ret = xmlSecBase64CtxFinal_ex(&ctx, out + outUpdateSize, outSize - outUpdateSize,
711
0
        &outFinalSize);
712
0
    if (ret < 0) {
713
0
        xmlSecInternalError("xmlSecBase64CtxFinal_ex", NULL);
714
0
        goto done;
715
0
    }
716
717
    /* success */
718
0
    (*outWritten) = (outUpdateSize + outFinalSize);
719
0
    res = 0;
720
721
0
done:
722
0
    if(ctx_initialized != 0) {
723
0
        xmlSecBase64CtxFinalize(&ctx);
724
0
    }
725
0
    return(res);
726
0
}
727
728
/**
729
 * xmlSecBase64DecodeInPlace:
730
 * @str:                the input/output buffer
731
 * @outWritten:         the pointer to store the number of bytes written into the output.
732
 *
733
 * Decodes input base64 encoded string from @str "in-place" (i.e. puts results into @str buffer).
734
 *
735
 * Returns: 0 on success and a negative value otherwise.
736
 */
737
int
738
0
xmlSecBase64DecodeInPlace(xmlChar* str, xmlSecSize* outWritten) {
739
0
    xmlSecAssert2(str != NULL, -1);
740
0
    return(xmlSecBase64Decode_ex(str, (xmlSecByte*)str, xmlSecStrlen(str) + 1,outWritten));
741
0
}
742
743
/**************************************************************
744
 *
745
 * Base64 Transform
746
 *
747
 * xmlSecTransform + xmlSecBase64Ctx
748
 *
749
 **************************************************************/
750
0
XMLSEC_TRANSFORM_DECLARE(Base64, xmlSecBase64Ctx)
751
0
#define xmlSecBase64Size XMLSEC_TRANSFORM_SIZE(Base64)
752
0
753
0
static int              xmlSecBase64Initialize          (xmlSecTransformPtr transform);
754
0
static void             xmlSecBase64Finalize            (xmlSecTransformPtr transform);
755
0
static int              xmlSecBase64Execute             (xmlSecTransformPtr transform,
756
0
                                                         int last,
757
0
                                                         xmlSecTransformCtxPtr transformCtx);
758
0
759
0
static xmlSecTransformKlass xmlSecBase64Klass = {
760
0
    /* klass/object sizes */
761
0
    sizeof(xmlSecTransformKlass),               /* xmlSecSize klassSize */
762
0
    xmlSecBase64Size,                           /* xmlSecSize objSize */
763
0
764
0
    xmlSecNameBase64,                           /* const xmlChar* name; */
765
0
    xmlSecHrefBase64,                           /* const xmlChar* href; */
766
0
    xmlSecTransformUsageDSigTransform,          /* xmlSecAlgorithmUsage usage; */
767
0
768
0
    xmlSecBase64Initialize,                     /* xmlSecTransformInitializeMethod initialize; */
769
0
    xmlSecBase64Finalize,                       /* xmlSecTransformFinalizeMethod finalize; */
770
0
    NULL,                                       /* xmlSecTransformNodeReadMethod readNode; */
771
0
    NULL,                                       /* xmlSecTransformNodeWriteMethod writeNode; */
772
0
    NULL,                                       /* xmlSecTransformSetKeyReqMethod setKeyReq; */
773
0
    NULL,                                       /* xmlSecTransformSetKeyMethod setKey; */
774
0
    NULL,                                       /* xmlSecTransformValidateMethod validate; */
775
0
    xmlSecTransformDefaultGetDataType,          /* xmlSecTransformGetDataTypeMethod getDataType; */
776
0
    xmlSecTransformDefaultPushBin,              /* xmlSecTransformPushBinMethod pushBin; */
777
0
    xmlSecTransformDefaultPopBin,               /* xmlSecTransformPopBinMethod popBin; */
778
0
    NULL,                                       /* xmlSecTransformPushXmlMethod pushXml; */
779
0
    NULL,                                       /* xmlSecTransformPopXmlMethod popXml; */
780
0
    xmlSecBase64Execute,                        /* xmlSecTransformExecuteMethod execute; */
781
0
782
0
    NULL,                                       /* void* reserved0; */
783
0
    NULL,                                       /* void* reserved1; */
784
0
};
785
0
786
0
/**
787
0
 * xmlSecTransformBase64GetKlass:
788
0
 *
789
0
 * The Base64 transform klass (http://www.w3.org/TR/xmldsig-core/#sec-Base-64).
790
0
 * The normative specification for base64 decoding transforms is RFC 2045
791
0
 * (http://www.ietf.org/rfc/rfc2045.txt). The base64 Transform element has
792
0
 * no content. The input is decoded by the algorithms. This transform is
793
0
 * useful if an application needs to sign the raw data associated with
794
0
 * the encoded content of an element.
795
0
 *
796
0
 * Returns: base64 transform id.
797
0
 */
798
0
xmlSecTransformId
799
0
xmlSecTransformBase64GetKlass(void) {
800
0
    return(&xmlSecBase64Klass);
801
0
}
802
803
/**
804
 * xmlSecTransformBase64SetLineSize:
805
 * @transform:          the pointer to BASE64 encode transform.
806
 * @lineSize:           the new max line size.
807
 *
808
 * Sets the max line size to @lineSize.
809
 */
810
void
811
0
xmlSecTransformBase64SetLineSize(xmlSecTransformPtr transform, xmlSecSize lineSize) {
812
0
    xmlSecBase64CtxPtr ctx;
813
814
0
    xmlSecAssert(xmlSecTransformCheckId(transform, xmlSecTransformBase64Id));
815
816
0
    ctx = xmlSecBase64GetCtx(transform);
817
0
    xmlSecAssert(ctx != NULL);
818
819
0
    ctx->columns = lineSize;
820
0
}
821
822
static int
823
0
xmlSecBase64Initialize(xmlSecTransformPtr transform) {
824
0
    xmlSecBase64CtxPtr ctx;
825
0
    int ret;
826
0
    int columns;
827
828
0
    xmlSecAssert2(xmlSecTransformCheckId(transform, xmlSecTransformBase64Id), -1);
829
830
0
    ctx = xmlSecBase64GetCtx(transform);
831
0
    xmlSecAssert2(ctx != NULL, -1);
832
833
0
    columns = xmlSecBase64GetDefaultLineSize();
834
0
    transform->operation = xmlSecTransformOperationDecode;
835
0
    ret = xmlSecBase64CtxInitialize(ctx, 0, columns);
836
0
    if(ret < 0) {
837
0
        xmlSecInternalError("xmlSecBase64CtxInitialize", xmlSecTransformGetName(transform));
838
0
        return(-1);
839
0
    }
840
841
0
    return(0);
842
0
}
843
844
static void
845
0
xmlSecBase64Finalize(xmlSecTransformPtr transform) {
846
0
    xmlSecBase64CtxPtr ctx;
847
848
0
    xmlSecAssert(xmlSecTransformCheckId(transform, xmlSecTransformBase64Id));
849
850
0
    ctx = xmlSecBase64GetCtx(transform);
851
0
    xmlSecAssert(ctx != NULL);
852
853
0
    xmlSecBase64CtxFinalize(ctx);
854
0
}
855
856
static int
857
0
xmlSecBase64Execute(xmlSecTransformPtr transform, int last, xmlSecTransformCtxPtr transformCtx) {
858
0
    xmlSecBase64CtxPtr ctx;
859
0
    xmlSecBufferPtr in, out;
860
0
    xmlSecSize inSize, outSize, outMaxLen, outLen;
861
0
    int ret;
862
863
0
    xmlSecAssert2(xmlSecTransformCheckId(transform, xmlSecTransformBase64Id), -1);
864
0
    xmlSecAssert2((transform->operation == xmlSecTransformOperationEncode) || (transform->operation == xmlSecTransformOperationDecode), -1);
865
0
    xmlSecAssert2(transformCtx != NULL, -1);
866
867
0
    ctx = xmlSecBase64GetCtx(transform);
868
0
    xmlSecAssert2(ctx != NULL, -1);
869
870
0
    in = &(transform->inBuf);
871
0
    out = &(transform->outBuf);
872
873
0
    if(transform->status == xmlSecTransformStatusNone) {
874
0
        ctx->encode = (transform->operation == xmlSecTransformOperationEncode) ? 1 : 0;
875
0
        transform->status = xmlSecTransformStatusWorking;
876
0
    }
877
878
0
    switch(transform->status) {
879
0
        case xmlSecTransformStatusWorking:
880
0
            inSize = xmlSecBufferGetSize(in);
881
0
            outSize = xmlSecBufferGetSize(out);
882
0
            if(inSize > 0) {
883
0
                if(ctx->encode != 0) {
884
0
                    outMaxLen = xmlSecBase64GetEncodeSize(ctx->columns, inSize);
885
0
                } else {
886
0
                    outMaxLen = xmlSecBase64GetDecodeSize(inSize);
887
0
                }
888
0
                ret = xmlSecBufferSetMaxSize(out, outSize + outMaxLen);
889
0
                if(ret < 0) {
890
0
                    xmlSecInternalError2("xmlSecBufferSetMaxSize", xmlSecTransformGetName(transform),
891
0
                        "size=" XMLSEC_SIZE_FMT, (outSize + outMaxLen));
892
0
                    return(-1);
893
0
                }
894
895
                /* encode/decode the next chunk */
896
0
                ret = xmlSecBase64CtxUpdate_ex(ctx, xmlSecBufferGetData(in), inSize,
897
0
                    xmlSecBufferGetData(out) + outSize, outMaxLen, &outLen);
898
0
                if(ret < 0) {
899
0
                    xmlSecInternalError("xmlSecBase64CtxUpdate_ex", xmlSecTransformGetName(transform));
900
0
                    return(-1);
901
0
                }
902
903
                /* set correct size */
904
0
                ret = xmlSecBufferSetSize(out, outSize + outLen);
905
0
                if(ret < 0) {
906
0
                    xmlSecInternalError2("xmlSecBufferSetSize", xmlSecTransformGetName(transform),
907
0
                        "size=" XMLSEC_SIZE_FMT, (outSize + outLen));
908
0
                    return(-1);
909
0
                }
910
911
                /* remove chunk from input */
912
0
                ret = xmlSecBufferRemoveHead(in, inSize);
913
0
                if(ret < 0) {
914
0
                    xmlSecInternalError2("xmlSecBufferRemoveHead", xmlSecTransformGetName(transform),
915
0
                        "size=" XMLSEC_SIZE_FMT, inSize);
916
0
                    return(-1);
917
0
                }
918
0
            }
919
920
0
            if(last) {
921
0
                outSize = xmlSecBufferGetSize(out);
922
0
                outMaxLen = 16; /* last block */
923
924
0
                ret = xmlSecBufferSetMaxSize(out, outSize + outMaxLen);
925
0
                if(ret < 0) {
926
0
                    xmlSecInternalError2("xmlSecBufferSetMaxSize", xmlSecTransformGetName(transform),
927
0
                        "size=" XMLSEC_SIZE_FMT, (outSize + outMaxLen));
928
0
                    return(-1);
929
0
                }
930
931
                /* add from ctx buffer */
932
0
                ret = xmlSecBase64CtxFinal_ex(ctx, xmlSecBufferGetData(out) + outSize,
933
0
                    outMaxLen, &outLen);
934
0
                if (ret < 0) {
935
0
                    xmlSecInternalError("xmlSecBase64CtxFinal_ex", xmlSecTransformGetName(transform));
936
0
                    return(-1);
937
0
                }
938
939
                /* set correct size */
940
0
                ret = xmlSecBufferSetSize(out, outSize + outLen);
941
0
                if(ret < 0) {
942
0
                    xmlSecInternalError2("xmlSecBufferSetSize", xmlSecTransformGetName(transform),
943
0
                        "size=" XMLSEC_SIZE_FMT, (outSize + outLen));
944
0
                    return(-1);
945
0
                }
946
0
                transform->status = xmlSecTransformStatusFinished;
947
0
            }
948
0
            break;
949
0
        case xmlSecTransformStatusFinished:
950
            /* the only way we can get here is if there is no input */
951
0
            xmlSecAssert2(xmlSecBufferGetSize(in) == 0, -1);
952
0
            break;
953
0
        default:
954
0
            xmlSecInvalidTransfromStatusError(transform);
955
0
            return(-1);
956
0
    }
957
0
    return(0);
958
0
}