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 | } |