encoding.c revision 1.10
1/* $OpenBSD: encoding.c,v 1.10 2021/11/24 15:24:16 claudio Exp $ */ 2/* 3 * Copyright (c) 2020 Claudio Jeker <claudio@openbsd.org> 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17#include <sys/stat.h> 18 19#include <err.h> 20#include <errno.h> 21#include <ctype.h> 22#include <fcntl.h> 23#include <limits.h> 24#include <stdlib.h> 25#include <string.h> 26#include <unistd.h> 27 28#include <openssl/evp.h> 29 30#include "extern.h" 31 32/* 33 * Load file from disk and return the buffer and size. 34 */ 35unsigned char * 36load_file(const char *name, size_t *len) 37{ 38 unsigned char *buf = NULL; 39 struct stat st; 40 ssize_t n; 41 size_t size; 42 int fd, saved_errno; 43 44 *len = 0; 45 46 if ((fd = open(name, O_RDONLY)) == -1) 47 return NULL; 48 if (fstat(fd, &st) != 0) 49 goto err; 50 if (st.st_size <= 0 || st.st_size > MAX_FILE_SIZE) { 51 errno = EFBIG; 52 goto err; 53 } 54 size = (size_t)st.st_size; 55 if ((buf = malloc(size)) == NULL) 56 goto err; 57 n = read(fd, buf, size); 58 if (n == -1) 59 goto err; 60 if ((size_t)n != size) { 61 errno = EIO; 62 goto err; 63 } 64 close(fd); 65 *len = size; 66 return buf; 67 68err: 69 saved_errno = errno; 70 close(fd); 71 free(buf); 72 errno = saved_errno; 73 return NULL; 74} 75 76/* 77 * Return the size of the data blob in outlen for an inlen sized base64 buffer. 78 * Returns 0 on success and -1 if inlen would overflow an int. 79 */ 80int 81base64_decode_len(size_t inlen, size_t *outlen) 82{ 83 *outlen = 0; 84 if (inlen >= INT_MAX - 3) 85 return -1; 86 *outlen = ((inlen + 3) / 4) * 3 + 1; 87 return 0; 88} 89 90/* 91 * Decode base64 encoded string into binary buffer returned in out. 92 * The out buffer size is stored in outlen. 93 * Returns 0 on success or -1 for any errors. 94 */ 95int 96base64_decode(const unsigned char *in, size_t inlen, 97 unsigned char **out, size_t *outlen) 98{ 99 static EVP_ENCODE_CTX *ctx; 100 unsigned char *to; 101 size_t tolen; 102 int evplen; 103 104 if (ctx == NULL && (ctx = EVP_ENCODE_CTX_new()) == NULL) 105 err(1, "EVP_ENCODE_CTX_new"); 106 107 *out = NULL; 108 *outlen = 0; 109 110 if (base64_decode_len(inlen, &tolen) == -1) 111 return -1; 112 if ((to = malloc(tolen)) == NULL) 113 return -1; 114 115 evplen = tolen; 116 EVP_DecodeInit(ctx); 117 if (EVP_DecodeUpdate(ctx, to, &evplen, in, inlen) == -1) 118 goto fail; 119 *outlen = evplen; 120 if (EVP_DecodeFinal(ctx, to + evplen, &evplen) == -1) 121 goto fail; 122 *outlen += evplen; 123 *out = to; 124 return 0; 125 126fail: 127 free(to); 128 return -1; 129} 130 131/* 132 * Return the size of the base64 blob in outlen for a inlen sized binary buffer. 133 * Returns 0 on success and -1 if inlen would overflow the calculation. 134 */ 135int 136base64_encode_len(size_t inlen, size_t *outlen) 137{ 138 *outlen = 0; 139 if (inlen >= INT_MAX / 2) 140 return -1; 141 *outlen = ((inlen + 2) / 3) * 4 + 1; 142 return 0; 143} 144 145/* 146 * Encode a binary buffer into a base64 encoded string returned in out. 147 * Returns 0 on success or -1 for any errors. 148 */ 149int 150base64_encode(const unsigned char *in, size_t inlen, char **out) 151{ 152 unsigned char *to; 153 size_t tolen; 154 155 *out = NULL; 156 157 if (base64_encode_len(inlen, &tolen) == -1) 158 return -1; 159 if ((to = malloc(tolen)) == NULL) 160 return -1; 161 162 EVP_EncodeBlock(to, in, inlen); 163 *out = to; 164 return 0; 165} 166 167/* 168 * Convert binary buffer of size dsz into an upper-case hex-string. 169 * Returns pointer to the newly allocated string. Function can't fail. 170 */ 171char * 172hex_encode(const unsigned char *in, size_t insz) 173{ 174 const char hex[] = "0123456789ABCDEF"; 175 size_t i; 176 char *out; 177 178 if ((out = calloc(2, insz + 1)) == NULL) 179 err(1, NULL); 180 181 for (i = 0; i < insz; i++) { 182 out[i * 2] = hex[in[i] >> 4]; 183 out[i * 2 + 1] = hex[in[i] & 0xf]; 184 } 185 out[i * 2] = '\0'; 186 187 return out; 188} 189 190/* 191 * Hex decode hexstring into the supplied buffer. 192 * Return 0 on success else -1, if buffer too small or bad encoding. 193 */ 194int 195hex_decode(const char *hexstr, char *buf, size_t len) 196{ 197 unsigned char ch, r; 198 size_t pos = 0; 199 int i; 200 201 while (*hexstr) { 202 r = 0; 203 for (i = 0; i < 2; i++) { 204 ch = hexstr[i]; 205 if (isdigit(ch)) 206 ch -= '0'; 207 else if (islower(ch)) 208 ch -= ('a' - 10); 209 else if (isupper(ch)) 210 ch -= ('A' - 10); 211 else 212 return -1; 213 if (ch > 0xf) 214 return -1; 215 r = r << 4 | ch; 216 } 217 if (pos < len) 218 buf[pos++] = r; 219 else 220 return -1; 221 222 hexstr += 2; 223 } 224 return 0; 225} 226 227