219 lines
4.6 KiB
C
219 lines
4.6 KiB
C
|
|
/*
|
|
* Argyll Color Management System
|
|
*
|
|
* Very simple & concise base64 encoder/decoder
|
|
*
|
|
*/
|
|
|
|
/*
|
|
* Author: Graeme W. Gill
|
|
*
|
|
* Copyright 2014, Graeme W. Gill
|
|
* All rights reserved.
|
|
*
|
|
* This material is licenced under the GNU GENERAL PUBLIC LICENSE Version 2 or later :-
|
|
* see the License2.txt file for licencing details.
|
|
*/
|
|
|
|
|
|
#include <stdio.h>
|
|
# define DLL_LOCAL
|
|
#include "base64.h"
|
|
|
|
static int enc(int val) {
|
|
val &= 0x3f;
|
|
|
|
if (val <= 25)
|
|
return ('A' + val);
|
|
if (val <= 51)
|
|
return ('a' + val - 26);
|
|
if (val <= 61)
|
|
return ('0' + val - 52);
|
|
if (val == 62)
|
|
return '+';
|
|
return '/';
|
|
}
|
|
|
|
/* Encode slen bytes into nul terminated string. */
|
|
/* if dlen is !NULL, then set it to resulting string length excluding nul */
|
|
/* We assume that the destination buffer is long enough at EBASE64LEN */
|
|
void DLL_LOCAL ebase64(int *dlen, char *dst, unsigned char *src, int slen) {
|
|
unsigned char buf[3];
|
|
int i, j;
|
|
|
|
/* Do this 3 characters at a time */
|
|
for (j = i = 0; i < slen; i += 3) {
|
|
int ib;
|
|
|
|
/* Grab 3 bytes or zero pad, and count bits */
|
|
buf[0] = src[i]; ib = 8;
|
|
buf[1] = (i+1) < slen ? (ib += 8, src[i+1]) : 0;
|
|
buf[2] = (i+2) < slen ? (ib += 8, src[i+2]) : 0;
|
|
|
|
/* Output up to 4 encoded characters */
|
|
dst[j] = enc(buf[0] >> 2), j++;
|
|
if (ib > 6) {
|
|
dst[j] = enc(buf[0] << 4 | buf[1] >> 4), j++;
|
|
if (ib > 12) {
|
|
dst[j] = enc(buf[1] << 2 | buf[2] >> 6), j++;
|
|
if (ib > 18)
|
|
dst[j] = enc(buf[2]), j++;
|
|
}
|
|
}
|
|
}
|
|
if (dlen != NULL)
|
|
*dlen = j;
|
|
|
|
dst[j++] = '\000';
|
|
}
|
|
|
|
/* Return binary value corresponding to encoded character, */
|
|
/* -1 for invalid character, -2 if nul terminator */
|
|
/* (We're assuming ASCII encoding) */
|
|
static int dec(int val) {
|
|
val &= 0xff;
|
|
|
|
if (val == 0)
|
|
return -2;
|
|
if (val == '+')
|
|
return 62;
|
|
if (val == '/')
|
|
return 63;
|
|
if (val < '0')
|
|
return -1;
|
|
if (val <= '9')
|
|
return val - '0' + 52;
|
|
if (val < 'A')
|
|
return -1;
|
|
if (val <= 'Z')
|
|
return val - 'A';
|
|
if (val < 'a')
|
|
return -1;
|
|
if (val <= 'z')
|
|
return val - 'a' + 26;
|
|
return -1;
|
|
}
|
|
|
|
/* Decode nul terminated string into bytes, ignoring illegal characters. */
|
|
/* if dlen is !NULL, then set it to resulting byte length */
|
|
/* We assume that the destination buffer is long enough at DBASE64LEN */
|
|
void DLL_LOCAL dbase64(int *dlen, unsigned char *dst, char *src) {
|
|
int buf[4];
|
|
int j;
|
|
|
|
/* Do this 4 characters at a time */
|
|
for (j = 0;;) {
|
|
int v, ib;
|
|
|
|
/* Grab 4 characters skipping illegals, and count bits */
|
|
while ((buf[0] = dec(*src++)) == -1)
|
|
;
|
|
if (buf[0] == -2)
|
|
break;
|
|
ib = 6;
|
|
while ((buf[1] = dec(*src++)) == -1)
|
|
;
|
|
if (buf[1] != -2) {
|
|
ib += 6;
|
|
while ((buf[2] = dec(*src++)) == -1)
|
|
;
|
|
if (buf[2] != -2) {
|
|
ib += 6;
|
|
while ((buf[3] = dec(*src++)) == -1)
|
|
;
|
|
if (buf[3] != -2)
|
|
ib += 6;
|
|
else
|
|
buf[3] = 0;
|
|
} else
|
|
buf[2] = 0;
|
|
} else
|
|
buf[1] = 0;
|
|
|
|
/* Output up to 3 decoded bytes */
|
|
dst[j] = buf[0] << 2 | buf[1] >> 4, j++;
|
|
if (ib > 12) {
|
|
dst[j] = buf[1] << 4 | buf[2] >> 2, j++;
|
|
if (ib > 18) {
|
|
dst[j] = buf[2] << 6 | buf[3], j++;
|
|
continue; /* We didn't run out */
|
|
}
|
|
}
|
|
break; /* Must be done if didn't continue */
|
|
}
|
|
if (dlen != NULL)
|
|
*dlen = j;
|
|
}
|
|
|
|
|
|
#ifdef STANDALONE_TEST
|
|
/* test base64 code */
|
|
|
|
#include <numlib.h>
|
|
|
|
#define MAXLEN 30
|
|
|
|
int main() {
|
|
unsigned char src[MAXLEN], check[MAXLEN];
|
|
char dst[EBASE64LEN(MAXLEN) + 1];
|
|
int n, slen, dlen, chlen, i, j;
|
|
|
|
printf("Testing base64 encode/decode:\n");
|
|
|
|
for (n = 0; n < 10000000; n++) {
|
|
|
|
/* Create a random binary */
|
|
slen = i_rand(0, MAXLEN);
|
|
for (i = 0; i < slen; i++)
|
|
src[i] = i_rand(0, 255);
|
|
|
|
/* Encode it */
|
|
ebase64(&dlen, dst, src, slen);
|
|
|
|
if (dlen != EBASE64LEN(slen)) {
|
|
error("%d: Wrong encoded length, src %d, is %d should be %d\n",n,slen,dlen,EBASE64LEN(slen));
|
|
}
|
|
|
|
/* Decode it */
|
|
dbase64(&chlen, check, dst);
|
|
|
|
if (chlen != DBASE64LEN(dlen)) {
|
|
error("%d: Wrong decoded length, enc %d, is %d should be %d\n",n,dlen,chlen,DBASE64LEN(dlen));
|
|
}
|
|
|
|
/* Check characters match */
|
|
for (i = 0; i < slen; i++) {
|
|
if (src[i] != check[i]) {
|
|
for (j = 0; j < slen; j++) {
|
|
printf("%02X ",src[j]);
|
|
if ((j % 3) == 2)
|
|
printf(" ");
|
|
}
|
|
printf("\n");
|
|
|
|
for (j = 0; j < dlen; j++) {
|
|
printf("%c ",dst[j]);
|
|
if ((j % 4) == 3)
|
|
printf(" ");
|
|
}
|
|
printf("\n");
|
|
for (j = 0; j < slen; j++) {
|
|
printf("%02X ",check[j]);
|
|
if ((j % 3) == 2)
|
|
printf(" ");
|
|
}
|
|
printf("\n\n");
|
|
printf("src len %d, dst len %d\n",slen, dlen);
|
|
error("%d: Verify error at byte %d, is 0x%x should be 0x%x\n",n,i,check[i],src[i]);
|
|
}
|
|
}
|
|
|
|
if ((n % 10000) == 0)
|
|
printf("done %d tests\n",n);
|
|
}
|
|
printf("Testing %d conversions base64 complete\n",n);
|
|
}
|
|
|
|
#endif /* STANDALONE_TEST */
|