aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorSteffan Karger2018-07-22 12:06:45 +0200
committerGert Doering2018-07-22 14:22:31 +0200
commita5d35a01dcf73e6a93f59d687adb6e5be38c7750 (patch)
treeea6296521cd4690474572e4bddf38c9f123af201 /src
parent3e5561d34099f28a759865efff0523005116b6a1 (diff)
downloadopenvpn-a5d35a01dcf73e6a93f59d687adb6e5be38c7750.zip
openvpn-a5d35a01dcf73e6a93f59d687adb6e5be38c7750.tar.gz
Add crypto_pem_{encode,decode}()
Needed for tls-crypt-v2, but isolated enough to be reviewed as a separate patch. The encode API allocates memory, because it fits our typical gc-oriented code pattern and the caller does not have to do multiple calls or calculations to determine the required destination buffer size. The decode API does not allocate memory, because the required destination buffer is always smaller than the input buffer (so is easy to manage by the caller) and does not force the caller to use the heap. Signed-off-by: Steffan Karger <steffan.karger@fox-it.com> Acked-by: Antonio Quartulli <antonio@openvpn.net> Message-Id: <20180722100645.5813-1-steffan@karger.me> URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg17284.html Signed-off-by: Gert Doering <gert@greenie.muc.de>
Diffstat (limited to 'src')
-rw-r--r--src/openvpn/crypto_backend.h29
-rw-r--r--src/openvpn/crypto_mbedtls.c75
-rw-r--r--src/openvpn/crypto_openssl.c82
3 files changed, 186 insertions, 0 deletions
diff --git a/src/openvpn/crypto_backend.h b/src/openvpn/crypto_backend.h
index bb53b8c..f917c8d 100644
--- a/src/openvpn/crypto_backend.h
+++ b/src/openvpn/crypto_backend.h
@@ -36,6 +36,7 @@
#include "crypto_mbedtls.h"
#endif
#include "basic.h"
+#include "buffer.h"
/* TLS uses a tag of 128 bytes, let's do the same for OpenVPN */
#define OPENVPN_AEAD_TAG_LENGTH 16
@@ -105,6 +106,34 @@ void show_available_digests(void);
void show_available_engines(void);
+/**
+ * Encode binary data as PEM.
+ *
+ * @param name The name to use in the PEM header/footer.
+ * @param dst Destination buffer for PEM-encoded data. Must be a valid
+ * pointer to an uninitialized buffer structure. Iff this
+ * function returns true, the buffer will contain memory
+ * allocated through the supplied gc.
+ * @param src Source buffer.
+ * @param gc The garbage collector to use when allocating memory for dst.
+ *
+ * @return true iff PEM encode succeeded.
+ */
+bool crypto_pem_encode(const char *name, struct buffer *dst,
+ const struct buffer *src, struct gc_arena *gc);
+
+/**
+ * Decode a PEM buffer to binary data.
+ *
+ * @param name The name expected in the PEM header/footer.
+ * @param dst Destination buffer for decoded data.
+ * @param src Source buffer (PEM data).
+ *
+ * @return true iff PEM decode succeeded.
+ */
+bool crypto_pem_decode(const char *name, struct buffer *dst,
+ const struct buffer *src);
+
/*
*
* Random number functions, used in cases where we want
diff --git a/src/openvpn/crypto_mbedtls.c b/src/openvpn/crypto_mbedtls.c
index 8ff6704..82f4e57 100644
--- a/src/openvpn/crypto_mbedtls.c
+++ b/src/openvpn/crypto_mbedtls.c
@@ -44,11 +44,13 @@
#include "otime.h"
#include "misc.h"
+#include <mbedtls/base64.h>
#include <mbedtls/des.h>
#include <mbedtls/error.h>
#include <mbedtls/md5.h>
#include <mbedtls/cipher.h>
#include <mbedtls/havege.h>
+#include <mbedtls/pem.h>
#include <mbedtls/entropy.h>
@@ -229,6 +231,79 @@ show_available_engines(void)
"available\n");
}
+bool
+crypto_pem_encode(const char *name, struct buffer *dst,
+ const struct buffer *src, struct gc_arena *gc)
+{
+ /* 1000 chars is the PEM line length limit (+1 for tailing NUL) */
+ char header[1000+1] = { 0 };
+ char footer[1000+1] = { 0 };
+
+ if (!openvpn_snprintf(header, sizeof(header), "-----BEGIN %s-----\n", name))
+ {
+ return false;
+ }
+ if (!openvpn_snprintf(footer, sizeof(footer), "-----END %s-----\n", name))
+ {
+ return false;
+ }
+
+ size_t out_len = 0;
+ if (MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL !=
+ mbedtls_pem_write_buffer(header, footer, BPTR(src), BLEN(src),
+ NULL, 0, &out_len))
+ {
+ return false;
+ }
+
+ *dst = alloc_buf_gc(out_len, gc);
+ if (!mbed_ok(mbedtls_pem_write_buffer(header, footer, BPTR(src), BLEN(src),
+ BPTR(dst), BCAP(dst), &out_len))
+ || !buf_inc_len(dst, out_len))
+ {
+ CLEAR(*dst);
+ return false;
+ }
+
+ return true;
+}
+
+bool
+crypto_pem_decode(const char *name, struct buffer *dst,
+ const struct buffer *src)
+{
+ /* 1000 chars is the PEM line length limit (+1 for tailing NUL) */
+ char header[1000+1] = { 0 };
+ char footer[1000+1] = { 0 };
+
+ if (*(BLAST(src)) != '\0')
+ {
+ msg(M_WARN, "PEM decode error: source buffer not null-terminated");
+ return false;
+ }
+ if (!openvpn_snprintf(header, sizeof(header), "-----BEGIN %s-----", name))
+ {
+ return false;
+ }
+ if (!openvpn_snprintf(footer, sizeof(footer), "-----END %s-----", name))
+ {
+ return false;
+ }
+
+ size_t use_len = 0;
+ mbedtls_pem_context ctx = { 0 };
+ bool ret = mbed_ok(mbedtls_pem_read_buffer(&ctx, header, footer, BPTR(src),
+ NULL, 0, &use_len));
+ if (ret && !buf_write(dst, ctx.buf, ctx.buflen))
+ {
+ ret = false;
+ msg(M_WARN, "PEM decode error: destination buffer too small");
+ }
+
+ mbedtls_pem_free(&ctx);
+ return ret;
+}
+
/*
*
* Random number functions, used in cases where we want
diff --git a/src/openvpn/crypto_openssl.c b/src/openvpn/crypto_openssl.c
index 4fb2f6d..9ec2048 100644
--- a/src/openvpn/crypto_openssl.c
+++ b/src/openvpn/crypto_openssl.c
@@ -387,6 +387,88 @@ show_available_engines(void)
#endif
}
+
+bool
+crypto_pem_encode(const char *name, struct buffer *dst,
+ const struct buffer *src, struct gc_arena *gc)
+{
+ bool ret = false;
+ BIO *bio = BIO_new(BIO_s_mem());
+ if (!bio || !PEM_write_bio(bio, name, "", BPTR(src), BLEN(src)))
+ {
+ ret = false;
+ goto cleanup;
+ }
+
+ BUF_MEM *bptr;
+ BIO_get_mem_ptr(bio, &bptr);
+
+ *dst = alloc_buf_gc(bptr->length, gc);
+ ASSERT(buf_write(dst, bptr->data, bptr->length));
+
+ ret = true;
+cleanup:
+ if (!BIO_free(bio))
+ {
+ ret = false;;
+ }
+
+ return ret;
+}
+
+bool
+crypto_pem_decode(const char *name, struct buffer *dst,
+ const struct buffer *src)
+{
+ bool ret = false;
+
+ BIO *bio = BIO_new_mem_buf((char *)BPTR(src), BLEN(src));
+ if (!bio)
+ {
+ crypto_msg(M_FATAL, "Cannot open memory BIO for PEM decode");
+ }
+
+ char *name_read = NULL;
+ char *header_read = NULL;
+ uint8_t *data_read = NULL;
+ long data_read_len = 0;
+ if (!PEM_read_bio(bio, &name_read, &header_read, &data_read,
+ &data_read_len))
+ {
+ dmsg(D_CRYPT_ERRORS, "%s: PEM decode failed", __func__);
+ goto cleanup;
+ }
+
+ if (strcmp(name, name_read))
+ {
+ dmsg(D_CRYPT_ERRORS,
+ "%s: unexpected PEM name (got '%s', expected '%s')",
+ __func__, name_read, name);
+ goto cleanup;
+ }
+
+ uint8_t *dst_data = buf_write_alloc(dst, data_read_len);
+ if (!dst_data)
+ {
+ dmsg(D_CRYPT_ERRORS, "%s: dst too small (%i, needs %li)", __func__,
+ BCAP(dst), data_read_len);
+ goto cleanup;
+ }
+ memcpy(dst_data, data_read, data_read_len);
+
+ ret = true;
+cleanup:
+ OPENSSL_free(name_read);
+ OPENSSL_free(header_read);
+ OPENSSL_free(data_read);
+ if (!BIO_free(bio))
+ {
+ ret = false;;
+ }
+
+ return ret;
+}
+
/*
*
* Random number functions, used in cases where we want