1/* $NetBSD: base64.c,v 1.8 2022/09/23 12:15:33 christos Exp $ */ 2 3/* 4 * Copyright (C) Internet Systems Consortium, Inc. ("ISC") 5 * 6 * SPDX-License-Identifier: MPL-2.0 7 * 8 * This Source Code Form is subject to the terms of the Mozilla Public 9 * License, v. 2.0. If a copy of the MPL was not distributed with this 10 * file, you can obtain one at https://mozilla.org/MPL/2.0/. 11 * 12 * See the COPYRIGHT file distributed with this work for additional 13 * information regarding copyright ownership. 14 */ 15 16/*! \file */ 17 18#include <stdbool.h> 19 20#include <isc/base64.h> 21#include <isc/buffer.h> 22#include <isc/lex.h> 23#include <isc/string.h> 24#include <isc/util.h> 25 26#define RETERR(x) \ 27 do { \ 28 isc_result_t _r = (x); \ 29 if (_r != ISC_R_SUCCESS) \ 30 return ((_r)); \ 31 } while (0) 32 33/*@{*/ 34/*! 35 * These static functions are also present in lib/dns/rdata.c. I'm not 36 * sure where they should go. -- bwelling 37 */ 38static isc_result_t 39str_totext(const char *source, isc_buffer_t *target); 40 41static isc_result_t 42mem_tobuffer(isc_buffer_t *target, void *base, unsigned int length); 43 44static const char base64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvw" 45 "xyz0123456789+/="; 46/*@}*/ 47 48isc_result_t 49isc_base64_totext(isc_region_t *source, int wordlength, const char *wordbreak, 50 isc_buffer_t *target) { 51 char buf[5]; 52 unsigned int loops = 0; 53 54 if (wordlength < 4) { 55 wordlength = 4; 56 } 57 58 memset(buf, 0, sizeof(buf)); 59 while (source->length > 2) { 60 buf[0] = base64[(source->base[0] >> 2) & 0x3f]; 61 buf[1] = base64[((source->base[0] << 4) & 0x30) | 62 ((source->base[1] >> 4) & 0x0f)]; 63 buf[2] = base64[((source->base[1] << 2) & 0x3c) | 64 ((source->base[2] >> 6) & 0x03)]; 65 buf[3] = base64[source->base[2] & 0x3f]; 66 RETERR(str_totext(buf, target)); 67 isc_region_consume(source, 3); 68 69 loops++; 70 if (source->length != 0 && (int)((loops + 1) * 4) >= wordlength) 71 { 72 loops = 0; 73 RETERR(str_totext(wordbreak, target)); 74 } 75 } 76 if (source->length == 2) { 77 buf[0] = base64[(source->base[0] >> 2) & 0x3f]; 78 buf[1] = base64[((source->base[0] << 4) & 0x30) | 79 ((source->base[1] >> 4) & 0x0f)]; 80 buf[2] = base64[((source->base[1] << 2) & 0x3c)]; 81 buf[3] = '='; 82 RETERR(str_totext(buf, target)); 83 isc_region_consume(source, 2); 84 } else if (source->length == 1) { 85 buf[0] = base64[(source->base[0] >> 2) & 0x3f]; 86 buf[1] = base64[((source->base[0] << 4) & 0x30)]; 87 buf[2] = buf[3] = '='; 88 RETERR(str_totext(buf, target)); 89 isc_region_consume(source, 1); 90 } 91 return (ISC_R_SUCCESS); 92} 93 94/*% 95 * State of a base64 decoding process in progress. 96 */ 97typedef struct { 98 int length; /*%< Desired length of binary data or -1 */ 99 isc_buffer_t *target; /*%< Buffer for resulting binary data */ 100 int digits; /*%< Number of buffered base64 digits */ 101 bool seen_end; /*%< True if "=" end marker seen */ 102 int val[4]; 103} base64_decode_ctx_t; 104 105static void 106base64_decode_init(base64_decode_ctx_t *ctx, int length, isc_buffer_t *target) { 107 ctx->digits = 0; 108 ctx->seen_end = false; 109 ctx->length = length; 110 ctx->target = target; 111} 112 113static isc_result_t 114base64_decode_char(base64_decode_ctx_t *ctx, int c) { 115 const char *s; 116 117 if (ctx->seen_end) { 118 return (ISC_R_BADBASE64); 119 } 120 if ((s = strchr(base64, c)) == NULL) { 121 return (ISC_R_BADBASE64); 122 } 123 ctx->val[ctx->digits++] = (int)(s - base64); 124 if (ctx->digits == 4) { 125 int n; 126 unsigned char buf[3]; 127 if (ctx->val[0] == 64 || ctx->val[1] == 64) { 128 return (ISC_R_BADBASE64); 129 } 130 if (ctx->val[2] == 64 && ctx->val[3] != 64) { 131 return (ISC_R_BADBASE64); 132 } 133 /* 134 * Check that bits that should be zero are. 135 */ 136 if (ctx->val[2] == 64 && (ctx->val[1] & 0xf) != 0) { 137 return (ISC_R_BADBASE64); 138 } 139 /* 140 * We don't need to test for ctx->val[2] != 64 as 141 * the bottom two bits of 64 are zero. 142 */ 143 if (ctx->val[3] == 64 && (ctx->val[2] & 0x3) != 0) { 144 return (ISC_R_BADBASE64); 145 } 146 n = (ctx->val[2] == 64) ? 1 : (ctx->val[3] == 64) ? 2 : 3; 147 if (n != 3) { 148 ctx->seen_end = true; 149 if (ctx->val[2] == 64) { 150 ctx->val[2] = 0; 151 } 152 if (ctx->val[3] == 64) { 153 ctx->val[3] = 0; 154 } 155 } 156 buf[0] = (ctx->val[0] << 2) | (ctx->val[1] >> 4); 157 buf[1] = (ctx->val[1] << 4) | (ctx->val[2] >> 2); 158 buf[2] = (ctx->val[2] << 6) | (ctx->val[3]); 159 RETERR(mem_tobuffer(ctx->target, buf, n)); 160 if (ctx->length >= 0) { 161 if (n > ctx->length) { 162 return (ISC_R_BADBASE64); 163 } else { 164 ctx->length -= n; 165 } 166 } 167 ctx->digits = 0; 168 } 169 return (ISC_R_SUCCESS); 170} 171 172static isc_result_t 173base64_decode_finish(base64_decode_ctx_t *ctx) { 174 if (ctx->length > 0) { 175 return (ISC_R_UNEXPECTEDEND); 176 } 177 if (ctx->digits != 0) { 178 return (ISC_R_BADBASE64); 179 } 180 return (ISC_R_SUCCESS); 181} 182 183isc_result_t 184isc_base64_tobuffer(isc_lex_t *lexer, isc_buffer_t *target, int length) { 185 unsigned int before, after; 186 base64_decode_ctx_t ctx; 187 isc_textregion_t *tr; 188 isc_token_t token; 189 bool eol; 190 191 REQUIRE(length >= -2); 192 193 base64_decode_init(&ctx, length, target); 194 195 before = isc_buffer_usedlength(target); 196 while (!ctx.seen_end && (ctx.length != 0)) { 197 unsigned int i; 198 199 if (length > 0) { 200 eol = false; 201 } else { 202 eol = true; 203 } 204 RETERR(isc_lex_getmastertoken(lexer, &token, 205 isc_tokentype_string, eol)); 206 if (token.type != isc_tokentype_string) { 207 break; 208 } 209 tr = &token.value.as_textregion; 210 for (i = 0; i < tr->length; i++) { 211 RETERR(base64_decode_char(&ctx, tr->base[i])); 212 } 213 } 214 after = isc_buffer_usedlength(target); 215 if (ctx.length < 0 && !ctx.seen_end) { 216 isc_lex_ungettoken(lexer, &token); 217 } 218 RETERR(base64_decode_finish(&ctx)); 219 if (length == -2 && before == after) { 220 return (ISC_R_UNEXPECTEDEND); 221 } 222 return (ISC_R_SUCCESS); 223} 224 225isc_result_t 226isc_base64_decodestring(const char *cstr, isc_buffer_t *target) { 227 base64_decode_ctx_t ctx; 228 229 base64_decode_init(&ctx, -1, target); 230 for (;;) { 231 int c = *cstr++; 232 if (c == '\0') { 233 break; 234 } 235 if (c == ' ' || c == '\t' || c == '\n' || c == '\r') { 236 continue; 237 } 238 RETERR(base64_decode_char(&ctx, c)); 239 } 240 RETERR(base64_decode_finish(&ctx)); 241 return (ISC_R_SUCCESS); 242} 243 244static isc_result_t 245str_totext(const char *source, isc_buffer_t *target) { 246 unsigned int l; 247 isc_region_t region; 248 249 isc_buffer_availableregion(target, ®ion); 250 l = strlen(source); 251 252 if (l > region.length) { 253 return (ISC_R_NOSPACE); 254 } 255 256 memmove(region.base, source, l); 257 isc_buffer_add(target, l); 258 return (ISC_R_SUCCESS); 259} 260 261static isc_result_t 262mem_tobuffer(isc_buffer_t *target, void *base, unsigned int length) { 263 isc_region_t tr; 264 265 isc_buffer_availableregion(target, &tr); 266 if (length > tr.length) { 267 return (ISC_R_NOSPACE); 268 } 269 memmove(tr.base, base, length); 270 isc_buffer_add(target, length); 271 return (ISC_R_SUCCESS); 272} 273