/* base64_dec.c */ /* * This file is part of the AVR-Crypto-Lib. * Copyright (C) 2006-2015 Daniel Otte (bg@nerilex.org) * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ /** * base64 decoder (RFC3548) * Author: Daniel Otte * License: GPLv3 * * */ #include #include "base64_dec.h" #include "cli.h" /* #define USE_GCC_EXTENSION */ #if 1 #ifdef USE_GCC_EXTENSION static int ascii2bit6(char a) { switch(a) { case 'A'...'Z': return a-'A'; case 'a'...'z': return a-'a'+26; case '0'...'9': return a-'0'+52; case '+': case '-': return 62; case '/': case '_': return 63; default: return -1; } } #else static uint8_t ascii2bit6(char a) { int r; switch (a >> 4) { case 0x5: case 0x4: r = a - 'A'; if (r < 0 || r > 25) { return -1; } else { return r; } case 0x7: case 0x6: r = a - 'a'; if (r < 0 || r > 25) { return -1; } else { return r + 26; } break; case 0x3: if (a > '9') return -1; return a - '0' + 52; default: break; } switch (a) { case '+': case '-': return 62; case '/': case '_': return 63; default: return 0xff; } } #endif #else static uint8_t ascii2bit6(uint8_t a) { if(a>='A' && a<='Z') { return a-'A'; } else { if(a>='a' && a<= 'z') { return a-'a'+26; } else { if(a>='0' && a<='9') { return a-'0'+52; } else { if(a=='+' || a=='-') { return 62; } else { if(a=='/' || a=='_') { return 63; } else { return 0xff; } } } } } } #endif int base64_binlength(char *str, uint8_t strict) { int l = 0; uint8_t term = 0; for (;;) { if (*str == '\0') break; if (*str == '\n' || *str == '\r') { str++; continue; } if (*str == '=') { term++; str++; if (term == 2) { break; } continue; } if (term) return -1; if (ascii2bit6(*str) == -1) { if (strict) return -1; } else { l++; } str++; } switch (term) { case 0: if (l % 4 != 0) return -1; return l / 4 * 3; case 1: if (l % 4 != 3) return -1; return (l + 1) / 4 * 3 - 1; case 2: if (l % 4 != 2) return -1; return (l + 2) / 4 * 3 - 2; default: return -1; } } /* |543210543210543210543210| |765432107654321076543210| . . . . |54321054|32105432|10543210| |76543210|76543210|76543210| */ int base64dec(void *dest, const char *b64str, uint8_t strict) { uint8_t buffer[4]; uint8_t idx = 0; uint8_t term = 0; for (;;) { // cli_putstr_P(PSTR("\r\n DBG: got 0x")); // cli_hexdump(b64str, 1); buffer[idx] = ascii2bit6(*b64str); // cli_putstr_P(PSTR(" --> 0x")); // cli_hexdump(buffer+idx, 1); if (buffer[idx] == 0xFF) { if (*b64str == '=') { term++; b64str++; if (term == 2) goto finalize; /* definitly the end */ } else { if (*b64str == '\0') { goto finalize; /* definitly the end */ } else { if (*b64str == '\r' || *b64str == '\n' || !(strict)) { b64str++; /* charcters that we simply ignore */ } else { return -1; } } } } else { if (term) return -1; /* this happens if we get a '=' in the stream */ idx++; b64str++; } if (idx == 4) { ((uint8_t*) dest)[0] = buffer[0] << 2 | buffer[1] >> 4; ((uint8_t*) dest)[1] = buffer[1] << 4 | buffer[2] >> 2; ((uint8_t*) dest)[2] = buffer[2] << 6 | buffer[3]; dest = (uint8_t*) dest + 3; idx = 0; } } finalize: /* the final touch */ if (idx == 0) return 0; if (term == 1) { ((uint8_t*) dest)[0] = buffer[0] << 2 | buffer[1] >> 4; ((uint8_t*) dest)[1] = buffer[1] << 4 | buffer[2] >> 2; return 0; } if (term == 2) { ((uint8_t*) dest)[0] = buffer[0] << 2 | buffer[1] >> 4; return 0; } return -1; }