/**
* XML Security Library (http://www.aleksey.com/xmlsec).
*
* Base64 encode/decode transform and utility functions.
*
* This is free software; see Copyright file in the source
* distribution for preciese wording.
*
* Copyright (C) 2002-2003 Aleksey Sanin <aleksey@aleksey.com>
* Portion Copyright © 2009 Nokia Corporation and/or its subsidiary(-ies). All rights reserved.
*/
#include "xmlsec_globals.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <libxml2_tree.h>
#include <libxml2_globals.h>
#include "xmlsec_xmlsec.h"
#include "xmlsec_keys.h"
#include "xmlsec_transforms.h"
#include "xmlsec_base64.h"
#include "xmlsec_errors.h"
/*
* the table to map numbers to base64
*/
static const xmlSecByte base64[] =
{
/* 0 1 2 3 4 5 6 7 */
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', /* 0 */
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', /* 1 */
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', /* 2 */
'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', /* 3 */
'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', /* 4 */
'o', 'p', 'q', 'r', 's', 't', 'u', 'v', /* 5 */
'w', 'x', 'y', 'z', '0', '1', '2', '3', /* 6 */
'4', '5', '6', '7', '8', '9', '+', '/' /* 7 */
};
/* few macros to simplify the code */
#define xmlSecBase64Encode1(a) (((a) >> 2) & 0x3F)
#define xmlSecBase64Encode2(a, b) ((((a) << 4) & 0x30) + (((b) >> 4) & 0x0F))
#define xmlSecBase64Encode3(b, c) ((((b) << 2) & 0x3c) + (((c) >> 6) & 0x03))
#define xmlSecBase64Encode4(c) ((c) & 0x3F)
#define xmlSecBase64Decode1(a, b) (((a) << 2) | (((b) & 0x3F) >> 4))
#define xmlSecBase64Decode2(b, c) (((b) << 4) | (((c) & 0x3F) >> 2))
#define xmlSecBase64Decode3(c, d) (((c) << 6) | ((d) & 0x3F))
#define xmlSecIsBase64Char(ch) ((((ch) >= 'A') && ((ch) <= 'Z')) || \
(((ch) >= 'a') && ((ch) <= 'z')) || \
(((ch) >= '0') && ((ch) <= '9')) || \
((ch) == '+') || ((ch) == '/'))
#define xmlSecIsBase64Space(ch) (((ch) == ' ') || ((ch) == '\t') || \
((ch) == '\x0d') || ((ch) == '\x0a'))
/***********************************************************************
*
* Base64 Context
*
***********************************************************************/
typedef enum {
xmlSecBase64StatusConsumeAndNext = 0,
xmlSecBase64StatusConsumeAndRepeat,
xmlSecBase64StatusNext,
xmlSecBase64StatusDone,
xmlSecBase64StatusFailed
} xmlSecBase64Status;
struct _xmlSecBase64Ctx {
int encode;
int inByte;
int inPos;
xmlSecSize linePos;
xmlSecSize columns;
int finished;
};
static xmlSecBase64Status xmlSecBase64CtxEncodeByte (xmlSecBase64CtxPtr ctx,
xmlSecByte inByte,
xmlSecByte* outByte);
static xmlSecBase64Status xmlSecBase64CtxEncodeByteFinal (xmlSecBase64CtxPtr ctx,
xmlSecByte* outByte);
static xmlSecBase64Status xmlSecBase64CtxDecodeByte (xmlSecBase64CtxPtr ctx,
xmlSecByte inByte,
xmlSecByte* outByte);
static int xmlSecBase64CtxEncode (xmlSecBase64CtxPtr ctx,
const xmlSecByte* inBuf,
xmlSecSize inBufSize,
xmlSecSize* inBufResSize,
xmlSecByte* outBuf,
xmlSecSize outBufSize,
xmlSecSize* outBufResSize);
static int xmlSecBase64CtxEncodeFinal (xmlSecBase64CtxPtr ctx,
xmlSecByte* outBuf,
xmlSecSize outBufSize,
xmlSecSize* outBufResSize);
static int xmlSecBase64CtxDecode (xmlSecBase64CtxPtr ctx,
const xmlSecByte* inBuf,
xmlSecSize inBufSize,
xmlSecSize* inBufResSize,
xmlSecByte* outBuf,
xmlSecSize outBufSize,
xmlSecSize* outBufResSize);
static int xmlSecBase64CtxDecodeIsFinished (xmlSecBase64CtxPtr ctx);
/**
* xmlSecBase64CtxCreate:
* @encode: the encode/decode flag (1 - encode, 0 - decode)
* @columns: the max line length.
*
* Allocates and initializes new base64 context.
*
* Returns a pointer to newly created #xmlSecBase64Ctx structure
* or NULL if an error occurs.
*/
EXPORT_C
xmlSecBase64CtxPtr
xmlSecBase64CtxCreate(int encode, int columns) {
xmlSecBase64CtxPtr ctx;
int ret;
/*
* Allocate a new xmlSecBase64CtxPtr and fill the fields.
*/
ctx = (xmlSecBase64CtxPtr) xmlMalloc(sizeof(xmlSecBase64Ctx));
if (ctx == NULL) {
xmlSecError(XMLSEC_ERRORS_HERE,
NULL,
NULL,
XMLSEC_ERRORS_R_MALLOC_FAILED,
"sizeof(xmlSecBase64Ctx)=%d",
sizeof(xmlSecBase64Ctx));
return(NULL);
}
ret = xmlSecBase64CtxInitialize(ctx, encode, columns);
if(ret < 0) {
xmlSecError(XMLSEC_ERRORS_HERE,
NULL,
"xmlSecBase64CtxInitialize",
XMLSEC_ERRORS_R_XMLSEC_FAILED,
XMLSEC_ERRORS_NO_MESSAGE);
xmlSecBase64CtxDestroy(ctx);
return(NULL);
}
return(ctx);
}
/**
* xmlSecBase64CtxDestroy:
* @ctx: the pointer to #xmlSecBase64Ctx structure.
*
* Destroys base64 context.
*/
EXPORT_C
void
xmlSecBase64CtxDestroy(xmlSecBase64CtxPtr ctx) {
xmlSecAssert(ctx != NULL);
xmlSecBase64CtxFinalize(ctx);
xmlFree(ctx);
}
/**
* xmlSecBase64CtxInitialize:
* @ctx: the pointer to #xmlSecBase64Ctx structure,
* @encode: the encode/decode flag (1 - encode, 0 - decode)
* @columns: the max line length.
*
* Initializes new base64 context.
*
* Returns 0 on success and a negative value otherwise.
*/
EXPORT_C
int
xmlSecBase64CtxInitialize(xmlSecBase64CtxPtr ctx, int encode, int columns) {
xmlSecAssert2(ctx != NULL, -1);
memset(ctx, 0, sizeof(xmlSecBase64Ctx));
ctx->encode = encode;
ctx->columns = columns;
return(0);
}
/**
* xmlSecBase64CtxFinalize:
* @ctx: the pointer to #xmlSecBase64Ctx structure,
*
* Frees all the resources allocated by @ctx.
*/
EXPORT_C
void
xmlSecBase64CtxFinalize(xmlSecBase64CtxPtr ctx) {
xmlSecAssert(ctx != NULL);
memset(ctx, 0, sizeof(xmlSecBase64Ctx));
}
/**
* xmlSecBase64CtxUpdate:
* @ctx: the pointer to #xmlSecBase64Ctx structure
* @in: the input buffer
* @inSize: the input buffer size
* @out: the output buffer
* @outSize: the output buffer size
*
* Encodes or decodes the next piece of data from input buffer.
*
* Returns the number of bytes written to output buffer or
* -1 if an error occurs.
*/
EXPORT_C
int
xmlSecBase64CtxUpdate(xmlSecBase64CtxPtr ctx,
const xmlSecByte *in, xmlSecSize inSize,
xmlSecByte *out, xmlSecSize outSize) {
xmlSecSize inResSize = 0, outResSize = 0;
int ret;
xmlSecAssert2(ctx != NULL, -1);
xmlSecAssert2(in != NULL, -1);
xmlSecAssert2(out != NULL, -1);
if(ctx->encode != 0) {
ret = xmlSecBase64CtxEncode(ctx, in, inSize, &inResSize,
out, outSize, &outResSize);
if((ret < 0) || (inResSize != inSize)) {
xmlSecError(XMLSEC_ERRORS_HERE,
NULL,
"xmlSecBase64CtxEncode",
XMLSEC_ERRORS_R_XMLSEC_FAILED,
XMLSEC_ERRORS_NO_MESSAGE);
return(-1);
}
} else {
ret = xmlSecBase64CtxDecode(ctx, in, inSize, &inResSize,
out, outSize, &outResSize);
if((ret < 0) || (inResSize != inSize)) {
xmlSecError(XMLSEC_ERRORS_HERE,
NULL,
"xmlSecBase64CtxDecode",
XMLSEC_ERRORS_R_XMLSEC_FAILED,
XMLSEC_ERRORS_NO_MESSAGE);
return(-1);
}
}
return(outResSize);
}
/**
* xmlSecBase64CtxFinal:
* @ctx: the pointer to #xmlSecBase64Ctx structure
* @out: the output buffer
* @outSize: the output buffer size
*
* Encodes or decodes the last piece of data stored in the context
* and finalizes the result.
*
* Returns the number of bytes written to output buffer or
* -1 if an error occurs.
*/
EXPORT_C
int
xmlSecBase64CtxFinal(xmlSecBase64CtxPtr ctx,
xmlSecByte *out, xmlSecSize outSize) {
xmlSecSize outResSize = 0;
int ret;
xmlSecAssert2(ctx != NULL, -1);
xmlSecAssert2(out != NULL, -1);
xmlSecAssert2(outSize > 0, -1);
if(ctx->encode != 0) {
ret = xmlSecBase64CtxEncodeFinal(ctx, out, outSize, &outResSize);
if(ret < 0) {
xmlSecError(XMLSEC_ERRORS_HERE,
NULL,
"xmlSecBase64CtxEncodeFinal",
XMLSEC_ERRORS_R_XMLSEC_FAILED,
"outSize=%d", outSize);
return(-1);
}
} else {
if(!xmlSecBase64CtxDecodeIsFinished(ctx)) {
xmlSecError(XMLSEC_ERRORS_HERE,
NULL,
"xmlSecBase64CtxIsFinished",
XMLSEC_ERRORS_R_XMLSEC_FAILED,
XMLSEC_ERRORS_NO_MESSAGE);
return(-1);
}
}
/* add \0 */
if((outResSize + 1) < outSize) {
out[outResSize] = '\0';
}
return(outResSize);
}
static xmlSecBase64Status
xmlSecBase64CtxEncodeByte(xmlSecBase64CtxPtr ctx, xmlSecByte inByte, xmlSecByte* outByte) {
xmlSecAssert2(ctx != NULL, xmlSecBase64StatusFailed);
xmlSecAssert2(outByte != NULL, xmlSecBase64StatusFailed);
if((ctx->columns > 0) && (ctx->linePos >= ctx->columns)) {
(*outByte) = '\n';
ctx->linePos = 0;
return(xmlSecBase64StatusConsumeAndRepeat);
} else if(ctx->inPos == 0) {
/* we just started new block */
(*outByte) = base64[xmlSecBase64Encode1(inByte)];
ctx->inByte = inByte;
++ctx->linePos;
++ctx->inPos;
return(xmlSecBase64StatusConsumeAndNext);
} else if(ctx->inPos == 1) {
(*outByte) = base64[xmlSecBase64Encode2(ctx->inByte, inByte)];
ctx->inByte = inByte;
++ctx->linePos;
++ctx->inPos;
return(xmlSecBase64StatusConsumeAndNext);
} else if(ctx->inPos == 2) {
(*outByte) = base64[xmlSecBase64Encode3(ctx->inByte, inByte)];
ctx->inByte = inByte;
++ctx->linePos;
++ctx->inPos;
return(xmlSecBase64StatusConsumeAndRepeat);
} else if(ctx->inPos == 3) {
(*outByte) = base64[xmlSecBase64Encode4(ctx->inByte)];
++ctx->linePos;
ctx->inByte = 0;
ctx->inPos = 0;
return(xmlSecBase64StatusConsumeAndNext);
}
xmlSecError(XMLSEC_ERRORS_HERE,
NULL,
NULL,
XMLSEC_ERRORS_R_INVALID_DATA,
"ctx->inPos=%d", ctx->inPos);
return(xmlSecBase64StatusFailed);
}
static xmlSecBase64Status
xmlSecBase64CtxEncodeByteFinal(xmlSecBase64CtxPtr ctx, xmlSecByte* outByte) {
xmlSecAssert2(ctx != NULL, xmlSecBase64StatusFailed);
xmlSecAssert2(outByte != NULL, xmlSecBase64StatusFailed);
if(ctx->inPos == 0) {
return(xmlSecBase64StatusDone);
} else if((ctx->columns > 0) && (ctx->linePos >= ctx->columns)) {
(*outByte) = '\n';
ctx->linePos = 0;
return(xmlSecBase64StatusConsumeAndRepeat);
} else if(ctx->finished == 0) {
ctx->finished = 1;
return(xmlSecBase64CtxEncodeByte(ctx, 0, outByte));
} else if(ctx->inPos < 3) {
(*outByte) = '=';
++ctx->inPos;
++ctx->linePos;
return(xmlSecBase64StatusConsumeAndRepeat);
} else if(ctx->inPos == 3) {
(*outByte) = '=';
++ctx->linePos;
ctx->inPos = 0;
return(xmlSecBase64StatusConsumeAndRepeat);
}
xmlSecError(XMLSEC_ERRORS_HERE,
NULL,
NULL,
XMLSEC_ERRORS_R_INVALID_DATA,
"ctx->inPos=%d", ctx->inPos);
return(xmlSecBase64StatusFailed);
}
static xmlSecBase64Status
xmlSecBase64CtxDecodeByte(xmlSecBase64CtxPtr ctx, xmlSecByte inByte, xmlSecByte* outByte) {
xmlSecAssert2(ctx != NULL, xmlSecBase64StatusFailed);
xmlSecAssert2(outByte != NULL, xmlSecBase64StatusFailed);
if((ctx->finished != 0) && (ctx->inPos == 0)) {
return(xmlSecBase64StatusDone);
} if(inByte == '=') {
ctx->finished = 1;
if(ctx->inPos < 2) {
xmlSecError(XMLSEC_ERRORS_HERE,
NULL,
NULL,
XMLSEC_ERRORS_R_INVALID_DATA,
"ctx->inPos=%d", ctx->inPos);
return(xmlSecBase64StatusFailed);
} else if(ctx->inPos == 2) {
++ctx->inPos;
return(xmlSecBase64StatusNext);
} else if(ctx->inPos == 3) {
ctx->inPos = 0;
return(xmlSecBase64StatusNext);
} else {
xmlSecError(XMLSEC_ERRORS_HERE,
NULL,
NULL,
XMLSEC_ERRORS_R_INVALID_DATA,
"ctx->inPos=%d", ctx->inPos);
return(xmlSecBase64StatusFailed);
}
} else if(xmlSecIsBase64Space(inByte)) {
return(xmlSecBase64StatusNext);
} else if(!xmlSecIsBase64Char(inByte) || (ctx->finished != 0)) {
xmlSecError(XMLSEC_ERRORS_HERE,
NULL,
NULL,
XMLSEC_ERRORS_R_INVALID_DATA,
"inByte=0x%02x", inByte);
return(xmlSecBase64StatusFailed);
}
/* convert from character to position in base64 array */
if((inByte >= 'A') && (inByte <= 'Z')) {
inByte = (inByte - 'A');
} else if((inByte >= 'a') && (inByte <= 'z')) {
inByte = 26 + (inByte - 'a');
} else if((inByte >= '0') && (inByte <= '9')) {
inByte = 52 + (inByte - '0');
} else if(inByte == '+') {
inByte = 62;
} else if(inByte == '/') {
inByte = 63;
}
if(ctx->inPos == 0) {
ctx->inByte = inByte;
++ctx->inPos;
return(xmlSecBase64StatusNext);
} else if(ctx->inPos == 1) {
(*outByte) = xmlSecBase64Decode1(ctx->inByte, inByte);
ctx->inByte = inByte;
++ctx->inPos;
return(xmlSecBase64StatusConsumeAndNext);
} else if(ctx->inPos == 2) {
(*outByte) = xmlSecBase64Decode2(ctx->inByte, inByte);
ctx->inByte = inByte;
++ctx->inPos;
return(xmlSecBase64StatusConsumeAndNext);
} else if(ctx->inPos == 3) {
(*outByte) = xmlSecBase64Decode3(ctx->inByte, inByte);
ctx->inByte = 0;
ctx->inPos = 0;
return(xmlSecBase64StatusConsumeAndNext);
}
xmlSecError(XMLSEC_ERRORS_HERE,
NULL,
NULL,
XMLSEC_ERRORS_R_INVALID_DATA,
"ctx->inPos=%d", ctx->inPos);
return(xmlSecBase64StatusFailed);
}
static int
xmlSecBase64CtxEncode(xmlSecBase64CtxPtr ctx,
const xmlSecByte* inBuf, xmlSecSize inBufSize, xmlSecSize* inBufResSize,
xmlSecByte* outBuf, xmlSecSize outBufSize, xmlSecSize* outBufResSize) {
xmlSecBase64Status status = xmlSecBase64StatusNext;
xmlSecSize inPos, outPos;
xmlSecAssert2(ctx != NULL, -1);
xmlSecAssert2(inBuf != NULL, -1);
xmlSecAssert2(inBufResSize != NULL, -1);
xmlSecAssert2(outBuf != NULL, -1);
xmlSecAssert2(outBufResSize != NULL, -1);
/* encode */
for(inPos = outPos = 0; (inPos < inBufSize) && (outPos < outBufSize); ) {
status = xmlSecBase64CtxEncodeByte(ctx, inBuf[inPos], &(outBuf[outPos]));
switch(status) {
case xmlSecBase64StatusConsumeAndNext:
++inPos;
++outPos;
break;
case xmlSecBase64StatusConsumeAndRepeat:
++outPos;
break;
case xmlSecBase64StatusNext:
case xmlSecBase64StatusDone:
case xmlSecBase64StatusFailed:
xmlSecError(XMLSEC_ERRORS_HERE,
NULL,
"xmlSecBase64CtxEncodeByte",
XMLSEC_ERRORS_R_XMLSEC_FAILED,
"status=%d", status);
return(-1);
}
}
(*inBufResSize) = inPos;
(*outBufResSize) = outPos;
return(0);
}
static int
xmlSecBase64CtxEncodeFinal(xmlSecBase64CtxPtr ctx,
xmlSecByte* outBuf, xmlSecSize outBufSize, xmlSecSize* outBufResSize) {
xmlSecBase64Status status = xmlSecBase64StatusNext;
xmlSecSize outPos;
xmlSecAssert2(ctx != NULL, -1);
xmlSecAssert2(outBuf != NULL, -1);
xmlSecAssert2(outBufResSize != NULL, -1);
/* encode final bytes */
for(outPos = 0; (outPos < outBufSize) && (status != xmlSecBase64StatusDone); ) {
status = xmlSecBase64CtxEncodeByteFinal(ctx, &(outBuf[outPos]));
switch(status) {
case xmlSecBase64StatusConsumeAndNext:
case xmlSecBase64StatusConsumeAndRepeat:
++outPos;
break;
case xmlSecBase64StatusDone:
break;
case xmlSecBase64StatusNext:
case xmlSecBase64StatusFailed:
xmlSecError(XMLSEC_ERRORS_HERE,
NULL,
"xmlSecBase64CtxEncodeByteFinal",
XMLSEC_ERRORS_R_XMLSEC_FAILED,
"status=%d", status);
return(-1);
}
}
if(status != xmlSecBase64StatusDone) {
xmlSecError(XMLSEC_ERRORS_HERE,
NULL,
NULL,
XMLSEC_ERRORS_R_INVALID_SIZE,
"outBufSize=%d", outBufSize);
return(-1);
}
if(outPos < outBufSize) {
outBuf[outPos] = '\0'; /* just in case */
}
(*outBufResSize) = outPos;
return(0);
}
static int
xmlSecBase64CtxDecode(xmlSecBase64CtxPtr ctx,
const xmlSecByte* inBuf, xmlSecSize inBufSize, xmlSecSize* inBufResSize,
xmlSecByte* outBuf, xmlSecSize outBufSize, xmlSecSize* outBufResSize) {
xmlSecBase64Status status = xmlSecBase64StatusNext;
xmlSecSize inPos, outPos;
xmlSecAssert2(ctx != NULL, -1);
xmlSecAssert2(inBuf != NULL, -1);
xmlSecAssert2(inBufResSize != NULL, -1);
xmlSecAssert2(outBuf != NULL, -1);
xmlSecAssert2(outBufResSize != NULL, -1);
/* decode */
for(inPos = outPos = 0; (inPos < inBufSize) && (outPos < outBufSize) && (status != xmlSecBase64StatusDone); ) {
status = xmlSecBase64CtxDecodeByte(ctx, inBuf[inPos], &(outBuf[outPos]));
switch(status) {
case xmlSecBase64StatusConsumeAndNext:
++inPos;
++outPos;
break;
case xmlSecBase64StatusConsumeAndRepeat:
++outPos;
break;
case xmlSecBase64StatusNext:
++inPos;
break;
case xmlSecBase64StatusDone:
break;
case xmlSecBase64StatusFailed:
xmlSecError(XMLSEC_ERRORS_HERE,
NULL,
"xmlSecBase64CtxDecodeByte",
XMLSEC_ERRORS_R_XMLSEC_FAILED,
"status=%d", status);
return(-1);
}
}
/* skip spaces at the end */
while((inPos < inBufSize) && xmlSecIsBase64Space(inBuf[inPos])) {
++inPos;
}
(*inBufResSize) = inPos;
(*outBufResSize) = outPos;
return(0);
}
static int
xmlSecBase64CtxDecodeIsFinished(xmlSecBase64CtxPtr ctx) {
xmlSecAssert2(ctx != NULL, -1);
return((ctx->inPos == 0) ? 1 : 0);
}
/**
* xmlSecBase64Encode:
* @buf: the input buffer.
* @len: the input buffer size.
* @columns: the output max line length (if 0 then no line breaks
* would be inserted)
*
* Encodes the data from input buffer and allocates the string for the result.
* The caller is responsible for freeing returned buffer using
* xmlFree() function.
*
* Returns newly allocated string with base64 encoded data
* or NULL if an error occurs.
*/
EXPORT_C
xmlChar*
xmlSecBase64Encode(const xmlSecByte *buf, xmlSecSize len, int columns) {
xmlSecBase64Ctx ctx;
xmlChar *ptr;
xmlSecSize size;
int size_update, size_final;
int ret;
xmlSecAssert2(buf != NULL, NULL);
ret = xmlSecBase64CtxInitialize(&ctx, 1, columns);
if(ret < 0) {
xmlSecError(XMLSEC_ERRORS_HERE,
NULL,
"xmlSecBase64CtxInitialize",
XMLSEC_ERRORS_R_XMLSEC_FAILED,
XMLSEC_ERRORS_NO_MESSAGE);
return(NULL);
}
/* create result buffer */
size = (4 * len) / 3 + 4;
if(columns > 0) {
size += (size / columns) + 4;
}
ptr = (xmlChar*) xmlMalloc(size);
if(ptr == NULL) {
xmlSecError(XMLSEC_ERRORS_HERE,
NULL,
NULL,
XMLSEC_ERRORS_R_MALLOC_FAILED,
"size=%d", size);
xmlSecBase64CtxFinalize(&ctx);
return(NULL);
}
ret = xmlSecBase64CtxUpdate(&ctx, buf, len, (xmlSecByte*)ptr, size);
if(ret < 0) {
xmlSecError(XMLSEC_ERRORS_HERE,
NULL,
"xmlSecBase64CtxUpdate",
XMLSEC_ERRORS_R_XMLSEC_FAILED,
"len=%d", len);
xmlFree(ptr);
xmlSecBase64CtxFinalize(&ctx);
return(NULL);
}
size_update = ret;
ret = xmlSecBase64CtxFinal(&ctx, ((xmlSecByte*)ptr) + size_update, size - size_update);
if(ret < 0) {
xmlSecError(XMLSEC_ERRORS_HERE,
NULL,
"xmlSecBase64CtxFinal",
XMLSEC_ERRORS_R_XMLSEC_FAILED,
XMLSEC_ERRORS_NO_MESSAGE);
xmlFree(ptr);
xmlSecBase64CtxFinalize(&ctx);
return(NULL);
}
size_final = ret;
ptr[size_update + size_final] = '\0';
xmlSecBase64CtxFinalize(&ctx);
return(ptr);
}
/**
* xmlSecBase64Decode:
* @str: the input buffer with base64 encoded string
* @buf: the output buffer
* @len: the output buffer size
*
* Decodes input base64 encoded string and puts result into
* the output buffer.
*
* Returns the number of bytes written to the output buffer or
* a negative value if an error occurs
*/
EXPORT_C
int
xmlSecBase64Decode(const xmlChar* str, xmlSecByte *buf, xmlSecSize len) {
xmlSecBase64Ctx ctx;
int size_update;
int size_final;
int ret;
xmlSecAssert2(str != NULL, -1);
xmlSecAssert2(buf != NULL, -1);
ret = xmlSecBase64CtxInitialize(&ctx, 0, 0);
if(ret < 0) {
xmlSecError(XMLSEC_ERRORS_HERE,
NULL,
"xmlSecBase64CtxInitialize",
XMLSEC_ERRORS_R_XMLSEC_FAILED,
XMLSEC_ERRORS_NO_MESSAGE);
return(-1);
}
ret = xmlSecBase64CtxUpdate(&ctx, (const xmlSecByte*)str, xmlStrlen(str), buf, len);
if(ret < 0) {
xmlSecError(XMLSEC_ERRORS_HERE,
NULL,
"xmlSecBase64CtxUpdate",
XMLSEC_ERRORS_R_XMLSEC_FAILED,
XMLSEC_ERRORS_NO_MESSAGE);
xmlSecBase64CtxFinalize(&ctx);
return(-1);
}
size_update = ret;
ret = xmlSecBase64CtxFinal(&ctx, buf + size_update, len - size_update);
if(ret < 0) {
xmlSecError(XMLSEC_ERRORS_HERE,
NULL,
"xmlSecBase64CtxFinal",
XMLSEC_ERRORS_R_XMLSEC_FAILED,
XMLSEC_ERRORS_NO_MESSAGE);
xmlSecBase64CtxFinalize(&ctx);
return(-1);
}
size_final = ret;
xmlSecBase64CtxFinalize(&ctx);
return(size_update + size_final);
}
/**************************************************************
*
* Base64 Transform
*
* xmlSecBase64Ctx is located after xmlSecTransform
*
**************************************************************/
#define xmlSecBase64Size \
(sizeof(xmlSecTransform) + sizeof(xmlSecBase64Ctx))
#define xmlSecBase64GetCtx(transform) \
((xmlSecTransformCheckSize((transform), xmlSecBase64Size)) ? \
(xmlSecBase64CtxPtr)(((xmlSecByte*)(transform)) + sizeof(xmlSecTransform)) : \
(xmlSecBase64CtxPtr)NULL)
static int xmlSecBase64Initialize (xmlSecTransformPtr transform);
static void xmlSecBase64Finalize (xmlSecTransformPtr transform);
static int xmlSecBase64Execute (xmlSecTransformPtr transform,
int last,
xmlSecTransformCtxPtr transformCtx);
static xmlSecTransformKlass xmlSecBase64Klass = {
/* klass/object sizes */
sizeof(xmlSecTransformKlass), /* xmlSecSize klassSize */
xmlSecBase64Size, /* xmlSecSize objSize */
xmlSecNameBase64, /* const xmlChar* name; */
xmlSecHrefBase64, /* const xmlChar* href; */
xmlSecTransformUsageDSigTransform, /* xmlSecAlgorithmUsage usage; */
xmlSecBase64Initialize, /* xmlSecTransformInitializeMethod initialize; */
xmlSecBase64Finalize, /* xmlSecTransformFinalizeMethod finalize; */
NULL, /* xmlSecTransformNodeReadMethod readNode; */
NULL, /* xmlSecTransformNodeWriteMethod writeNode; */
NULL, /* xmlSecTransformSetKeyReqMethod setKeyReq; */
NULL, /* xmlSecTransformSetKeyMethod setKey; */
NULL, /* xmlSecTransformValidateMethod validate; */
xmlSecTransformDefaultGetDataType, /* xmlSecTransformGetDataTypeMethod getDataType; */
xmlSecTransformDefaultPushBin, /* xmlSecTransformPushBinMethod pushBin; */
xmlSecTransformDefaultPopBin, /* xmlSecTransformPopBinMethod popBin; */
NULL, /* xmlSecTransformPushXmlMethod pushXml; */
NULL, /* xmlSecTransformPopXmlMethod popXml; */
xmlSecBase64Execute, /* xmlSecTransformExecuteMethod execute; */
NULL, /* void* reserved0; */
NULL, /* void* reserved1; */
};
/**
* xmlSecTransformBase64GetKlass:
*
* The Base64 transform klass (http://www.w3.org/TR/xmldsig-core/#sec-Base-64).
* The normative specification for base64 decoding transforms is RFC 2045
* (http://www.ietf.org/rfc/rfc2045.txt). The base64 Transform element has
* no content. The input is decoded by the algorithms. This transform is
* useful if an application needs to sign the raw data associated with
* the encoded content of an element.
*
* Returns base64 transform id.
*/
EXPORT_C
xmlSecTransformId
xmlSecTransformBase64GetKlass(void) {
return(&xmlSecBase64Klass);
}
/**
* xmlSecTransformBase64SetLineSize:
* @transform: the pointer to BASE64 encode transform.
* @lineSize: the new max line size.
*
* Sets the max line size to @lineSize.
*/
EXPORT_C
void
xmlSecTransformBase64SetLineSize(xmlSecTransformPtr transform, xmlSecSize lineSize) {
xmlSecBase64CtxPtr ctx;
xmlSecAssert(xmlSecTransformCheckId(transform, xmlSecTransformBase64Id));
ctx = xmlSecBase64GetCtx(transform);
xmlSecAssert(ctx != NULL);
ctx->columns = lineSize;
}
static int
xmlSecBase64Initialize(xmlSecTransformPtr transform) {
xmlSecBase64CtxPtr ctx;
int ret;
xmlSecAssert2(xmlSecTransformCheckId(transform, xmlSecTransformBase64Id), -1);
ctx = xmlSecBase64GetCtx(transform);
xmlSecAssert2(ctx != NULL, -1);
transform->operation = xmlSecTransformOperationDecode;
ret = xmlSecBase64CtxInitialize(ctx, 0, XMLSEC_BASE64_LINESIZE);
if(ret < 0) {
xmlSecError(XMLSEC_ERRORS_HERE,
xmlSecErrorsSafeString(xmlSecTransformGetName(transform)),
"xmlSecBase64CtxInitialize",
XMLSEC_ERRORS_R_XMLSEC_FAILED,
XMLSEC_ERRORS_NO_MESSAGE);
return(-1);
}
return(0);
}
static void
xmlSecBase64Finalize(xmlSecTransformPtr transform) {
xmlSecBase64CtxPtr ctx;
xmlSecAssert(xmlSecTransformCheckId(transform, xmlSecTransformBase64Id));
ctx = xmlSecBase64GetCtx(transform);
xmlSecAssert(ctx != NULL);
xmlSecBase64CtxFinalize(ctx);
}
static int
xmlSecBase64Execute(xmlSecTransformPtr transform, int last, xmlSecTransformCtxPtr transformCtx) {
xmlSecBase64CtxPtr ctx;
xmlSecBufferPtr in, out;
xmlSecSize inSize, outSize, outLen;
int ret;
xmlSecAssert2(xmlSecTransformCheckId(transform, xmlSecTransformBase64Id), -1);
xmlSecAssert2((transform->operation == xmlSecTransformOperationEncode) || (transform->operation == xmlSecTransformOperationDecode), -1);
xmlSecAssert2(transformCtx != NULL, -1);
ctx = xmlSecBase64GetCtx(transform);
xmlSecAssert2(ctx != NULL, -1);
in = &(transform->inBuf);
out = &(transform->outBuf);
if(transform->status == xmlSecTransformStatusNone) {
ctx->encode = (transform->operation == xmlSecTransformOperationEncode) ? 1 : 0;
transform->status = xmlSecTransformStatusWorking;
}
switch(transform->status) {
case xmlSecTransformStatusWorking:
inSize = xmlSecBufferGetSize(in);
outSize = xmlSecBufferGetSize(out);
if(inSize > 0) {
if(ctx->encode != 0) {
outLen = 4 * inSize / 3 + 8;
if(ctx->columns > 0) {
outLen += inSize / ctx->columns + 4;
}
} else {
outLen = 3 * inSize / 4 + 8;
}
ret = xmlSecBufferSetMaxSize(out, outSize + outLen);
if(ret < 0) {
xmlSecError(XMLSEC_ERRORS_HERE,
xmlSecErrorsSafeString(xmlSecTransformGetName(transform)),
"xmlSecBufferSetMaxSize",
XMLSEC_ERRORS_R_XMLSEC_FAILED,
"size=%d", outSize + outLen);
return(-1);
}
/* encode/decode the next chunk */
ret = xmlSecBase64CtxUpdate(ctx, xmlSecBufferGetData(in), inSize,
xmlSecBufferGetData(out) + outSize,
outLen);
if(ret < 0) {
xmlSecError(XMLSEC_ERRORS_HERE,
xmlSecErrorsSafeString(xmlSecTransformGetName(transform)),
"xmlSecBase64CtxUpdate",
XMLSEC_ERRORS_R_XMLSEC_FAILED,
XMLSEC_ERRORS_NO_MESSAGE);
return(-1);
}
outLen = ret;
/* set correct size */
ret = xmlSecBufferSetSize(out, outSize + outLen);
if(ret < 0) {
xmlSecError(XMLSEC_ERRORS_HERE,
xmlSecErrorsSafeString(xmlSecTransformGetName(transform)),
"xmlSecBufferSetSize",
XMLSEC_ERRORS_R_XMLSEC_FAILED,
"size=%d", outSize + outLen);
return(-1);
}
/* remove chunk from input */
ret = xmlSecBufferRemoveHead(in, inSize);
if(ret < 0) {
xmlSecError(XMLSEC_ERRORS_HERE,
xmlSecErrorsSafeString(xmlSecTransformGetName(transform)),
"xmlSecBufferRemoveHead",
XMLSEC_ERRORS_R_XMLSEC_FAILED,
"size=%d", inSize);
return(-1);
}
}
if(last) {
outSize = xmlSecBufferGetSize(out);
ret = xmlSecBufferSetMaxSize(out, outSize + 16);
if(ret < 0) {
xmlSecError(XMLSEC_ERRORS_HERE,
xmlSecErrorsSafeString(xmlSecTransformGetName(transform)),
"xmlSecBufferSetMaxSize",
XMLSEC_ERRORS_R_XMLSEC_FAILED,
"size=%d", outSize + 16);
return(-1);
}
/* add from ctx buffer */
ret = xmlSecBase64CtxFinal(ctx, xmlSecBufferGetData(out) + outSize, 16);
if(ret < 0) {
xmlSecError(XMLSEC_ERRORS_HERE,
xmlSecErrorsSafeString(xmlSecTransformGetName(transform)),
"xmlSecBase64CtxFinal",
XMLSEC_ERRORS_R_XMLSEC_FAILED,
XMLSEC_ERRORS_NO_MESSAGE);
return(-1);
}
outLen = ret;
/* set correct size */
ret = xmlSecBufferSetSize(out, outSize + outLen);
if(ret < 0) {
xmlSecError(XMLSEC_ERRORS_HERE,
xmlSecErrorsSafeString(xmlSecTransformGetName(transform)),
"xmlSecBufferSetSize",
XMLSEC_ERRORS_R_XMLSEC_FAILED,
"size=%d", outSize + outLen);
return(-1);
}
transform->status = xmlSecTransformStatusFinished;
}
break;
case xmlSecTransformStatusFinished:
/* the only way we can get here is if there is no input */
xmlSecAssert2(xmlSecBufferGetSize(in) == 0, -1);
break;
default:
xmlSecError(XMLSEC_ERRORS_HERE,
xmlSecErrorsSafeString(xmlSecTransformGetName(transform)),
NULL,
XMLSEC_ERRORS_R_INVALID_STATUS,
"status=%d", transform->status);
return(-1);
}
return(0);
}