/*
* Copyright (C) 2018 Yubico AB - See COPYING
*/

#include <openssl/bio.h>
#include <openssl/evp.h>
#include <limits.h>
#include <stdint.h>
#include <string.h>

#include "b64.h"

int b64_encode(const void *ptr, size_t len, char **out) {
 BIO *bio_b64 = NULL;
 BIO *bio_mem = NULL;
 char *b64_ptr = NULL;
 long b64_len;
 int n;
 int ok = 0;

 if (ptr == NULL || out == NULL || len > INT_MAX)
   return (0);

 *out = NULL;

 bio_b64 = BIO_new(BIO_f_base64());
 if (bio_b64 == NULL)
   goto fail;

 bio_mem = BIO_new(BIO_s_mem());
 if (bio_mem == NULL)
   goto fail;

 BIO_set_flags(bio_b64, BIO_FLAGS_BASE64_NO_NL);
 BIO_push(bio_b64, bio_mem);

 n = BIO_write(bio_b64, ptr, (int) len);
 if (n < 0 || (size_t) n != len)
   goto fail;

 if (BIO_flush(bio_b64) < 0)
   goto fail;

 b64_len = BIO_get_mem_data(bio_b64, &b64_ptr);
 if (b64_len < 0 || (size_t) b64_len == SIZE_MAX || b64_ptr == NULL)
   goto fail;

 *out = calloc(1, (size_t) b64_len + 1);
 if (*out == NULL)
   goto fail;

 memcpy(*out, b64_ptr, (size_t) b64_len);
 ok = 1;

fail:
 BIO_free(bio_b64);
 BIO_free(bio_mem);

 return (ok);
}

int b64_decode(const char *in, void **ptr, size_t *len) {
 BIO *bio_mem = NULL;
 BIO *bio_b64 = NULL;
 size_t alloc_len;
 int n;
 int ok = 0;

 if (in == NULL || ptr == NULL || len == NULL || strlen(in) > INT_MAX)
   return (0);

 *ptr = NULL;
 *len = 0;

 bio_b64 = BIO_new(BIO_f_base64());
 if (bio_b64 == NULL)
   goto fail;

 bio_mem = BIO_new_mem_buf((const void *) in, -1);
 if (bio_mem == NULL)
   goto fail;

 BIO_set_flags(bio_b64, BIO_FLAGS_BASE64_NO_NL);
 BIO_push(bio_b64, bio_mem);

 alloc_len = strlen(in);
 *ptr = calloc(1, alloc_len);
 if (*ptr == NULL)
   goto fail;

 n = BIO_read(bio_b64, *ptr, (int) alloc_len);
 if (n < 0 || BIO_eof(bio_b64) == 0)
   goto fail;

 *len = (size_t) n;
 ok = 1;

fail:
 BIO_free(bio_b64);
 BIO_free(bio_mem);

 if (!ok) {
   free(*ptr);
   *ptr = NULL;
   *len = 0;
 }

 return (ok);
}