commit 9566ed6fd9e4aab2ce6b84afc2f7112550cf0483 Author: Nick Mathewson nickm@torproject.org Date: Thu Jul 19 15:47:48 2018 -0400
Add rudimentary support for PEM-encoding, since NSS doesn't do that. --- src/lib/encoding/.may_include | 1 + src/lib/encoding/include.am | 2 + src/lib/encoding/pem.c | 106 ++++++++++++++++++++++++++++++++++++ src/lib/encoding/pem.h | 26 +++++++++ src/test/include.am | 1 + src/test/test.c | 1 + src/test/test.h | 1 + src/test/test_pem.c | 122 ++++++++++++++++++++++++++++++++++++++++++ 8 files changed, 260 insertions(+)
diff --git a/src/lib/encoding/.may_include b/src/lib/encoding/.may_include index 92231b513..7c2ef3692 100644 --- a/src/lib/encoding/.may_include +++ b/src/lib/encoding/.may_include @@ -1,5 +1,6 @@ orconfig.h lib/cc/*.h +lib/ctime/*.h lib/encoding/*.h lib/intmath/*.h lib/log/*.h diff --git a/src/lib/encoding/include.am b/src/lib/encoding/include.am index 868e531b6..2d2aa3988 100644 --- a/src/lib/encoding/include.am +++ b/src/lib/encoding/include.am @@ -9,6 +9,7 @@ src_lib_libtor_encoding_a_SOURCES = \ src/lib/encoding/confline.c \ src/lib/encoding/cstring.c \ src/lib/encoding/keyval.c \ + src/lib/encoding/pem.c \ src/lib/encoding/time_fmt.c
src_lib_libtor_encoding_testing_a_SOURCES = \ @@ -21,4 +22,5 @@ noinst_HEADERS += \ src/lib/encoding/confline.h \ src/lib/encoding/cstring.h \ src/lib/encoding/keyval.h \ + src/lib/encoding/pem.h \ src/lib/encoding/time_fmt.h diff --git a/src/lib/encoding/pem.c b/src/lib/encoding/pem.c new file mode 100644 index 000000000..0d4a814f6 --- /dev/null +++ b/src/lib/encoding/pem.c @@ -0,0 +1,106 @@ +/* Copyright (c) 2001, Matej Pfajfar. + * Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2018, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file pem.c + * + * \brief Implement a trivial version of PEM encoding, for use with NSS. + * + * We deliberately do not support any encryption here. + **/ + +#include "orconfig.h" + +#include "lib/encoding/pem.h" + +#include "lib/ctime/di_ops.h" +#include "lib/encoding/binascii.h" +#include "lib/log/util_bug.h" +#include "lib/malloc/malloc.h" +#include "lib/string/printf.h" +#include "lib/string/util_string.h" + +#include <string.h> + +/** + * Return the length of a <b>src_len</b>-byte object when tagged with + * <b>objtype</b> and PEM-encoded. Includes terminating NUL. + */ +size_t +pem_encoded_size(size_t src_len, const char *objtype) +{ + return + strlen("-----BEGIN -----\n") + + strlen("-----END -----\n") + + strlen(objtype) * 2 + + base64_encode_size(src_len, BASE64_ENCODE_MULTILINE) + + 1; +} + +/** + * PEM-encode the <b>srclen</b>-byte object at <b>src</b> into the + * <b>destlen<\b>-byte buffer at <b>dest</b>, tagging it with <b>objtype</b>. + * Return 0 on success and -1 on failure. + */ +int +pem_encode(char *dest, size_t destlen, const uint8_t *src, size_t srclen, + const char *objtype) +{ + if (tor_snprintf(dest, destlen, "-----BEGIN %s-----\n", objtype) < 0) + return -1; + + size_t offset = strlen(dest); + + int n = base64_encode(dest + offset, destlen - offset, + (const char *)src, srclen, BASE64_ENCODE_MULTILINE); + if (n < 0) + return -1; + offset += n; + if (BUG(offset > destlen)) + return -1; + + if (tor_snprintf(dest + offset, destlen - offset, + "-----END %s-----\n", objtype) < 0) + return -1; + + tor_assert(strlen(dest) + 1 <= pem_encoded_size(srclen, objtype)); + return 0; +} + +/** + * Given a PEM-encoded block of size <b>srclen</b> in <b>src</b>, if it has + * object type <b>objtype</b>, decode it into the <b>destlen</b>-byte buffer + * at <b>dest</b>. Return the number of characters decoded on success, or -1 + * on failure. + */ +int +pem_decode(uint8_t *dest, size_t destlen, const char *src, size_t srclen, + const char *objtype) +{ + const char *eos = src + srclen; + + src = eat_whitespace_eos(src, eos); + + char *tag = NULL; + tor_asprintf(&tag, "-----BEGIN %s-----\n", objtype); + if ((size_t)(eos-src) < strlen(tag) || fast_memneq(src, tag, strlen(tag))) { + tor_free(tag); + return -1; + } + src += strlen(tag); + tor_free(tag); + + // NOTE lack of trailing \n. We do not enforce its presence. + tor_asprintf(&tag, "\n-----END %s-----", objtype); + const char *end_of_base64 = tor_memstr(src, eos-src, tag); + tor_free(tag); + if (end_of_base64 == NULL) + return -1; + + /* Should we actually allow extra stuff at the end? */ + + return base64_decode((char*)dest, destlen, src, end_of_base64-src); +} diff --git a/src/lib/encoding/pem.h b/src/lib/encoding/pem.h new file mode 100644 index 000000000..ba2122884 --- /dev/null +++ b/src/lib/encoding/pem.h @@ -0,0 +1,26 @@ +/* Copyright (c) 2001, Matej Pfajfar. + * Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2018, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file pem.h + * + * \brief Header for pem.c + **/ + +#ifndef TOR_PEM_H +#define TOR_PEM_H + +#include "orconfig.h" +#include <stddef.h> +#include "lib/cc/torint.h" + +size_t pem_encoded_size(size_t src_len, const char *objtype); +int pem_encode(char *dest, size_t destlen, const uint8_t *src, size_t srclen, + const char *objtype); +int pem_decode(uint8_t *dest, size_t destlen, const char *src, size_t srclen, + const char *objtype); + +#endif diff --git a/src/test/include.am b/src/test/include.am index 46990597f..68372adc7 100644 --- a/src/test/include.am +++ b/src/test/include.am @@ -151,6 +151,7 @@ src_test_test_SOURCES += \ src/test/test_oom.c \ src/test/test_oos.c \ src/test/test_options.c \ + src/test/test_pem.c \ src/test/test_periodic_event.c \ src/test/test_policy.c \ src/test/test_procmon.c \ diff --git a/src/test/test.c b/src/test/test.c index 745aa987a..94eae5a4f 100644 --- a/src/test/test.c +++ b/src/test/test.c @@ -867,6 +867,7 @@ struct testgroup_t testgroups[] = { { "crypto/", crypto_tests }, { "crypto/ope/", crypto_ope_tests }, { "crypto/openssl/", crypto_openssl_tests }, + { "crypto/pem/", pem_tests }, { "dir/", dir_tests }, { "dir_handle_get/", dir_handle_get_tests }, { "dir/md/", microdesc_tests }, diff --git a/src/test/test.h b/src/test/test.h index bfe50cbb8..b3a73271e 100644 --- a/src/test/test.h +++ b/src/test/test.h @@ -234,6 +234,7 @@ extern struct testcase_t nodelist_tests[]; extern struct testcase_t oom_tests[]; extern struct testcase_t oos_tests[]; extern struct testcase_t options_tests[]; +extern struct testcase_t pem_tests[]; extern struct testcase_t periodic_event_tests[]; extern struct testcase_t policy_tests[]; extern struct testcase_t procmon_tests[]; diff --git a/src/test/test_pem.c b/src/test/test_pem.c new file mode 100644 index 000000000..2bae286e2 --- /dev/null +++ b/src/test/test_pem.c @@ -0,0 +1,122 @@ +/* Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2018, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#include "orconfig.h" + +#include "lib/encoding/pem.h" +#include "lib/cc/compat_compiler.h" +#include "lib/malloc/malloc.h" + +#include "test/test.h" + +#include <string.h> +#include <stdio.h> +#include <stdlib.h> + +static const char example_pre[] = + "Lest you get the wrong impression, we wombats " + "are not in the habit of tunneling madly about, without any supplies " + "or even a map."; /* -- Ursula Vernon, _Digger_ */ +static const char expected[] = + "-----BEGIN WOMBAT QUOTE-----\n" + "TGVzdCB5b3UgZ2V0IHRoZSB3cm9uZyBpbXByZXNzaW9uLCB3ZSB3b21iYXRzIGFy\n" + "ZSBub3QgaW4gdGhlIGhhYml0IG9mIHR1bm5lbGluZyBtYWRseSBhYm91dCwgd2l0\n" + "aG91dCBhbnkgc3VwcGxpZXMgb3IgZXZlbiBhIG1hcC4=\n" + "-----END WOMBAT QUOTE-----\n"; + +static void +test_crypto_pem_encode(void *arg) +{ + (void)arg; + + char buf[4096]; + + int n = (int) pem_encoded_size(strlen(example_pre), "WOMBAT QUOTE"); + + int n2 = pem_encode(buf, sizeof(buf), + (const unsigned char *)example_pre, strlen(example_pre), + "WOMBAT QUOTE"); + tt_int_op(strlen(buf)+1, OP_EQ, n); + tt_int_op(n2, OP_EQ, 0); + tt_str_op(buf, OP_EQ, expected); + + /* Now make sure it succeeds if the buffer is exactly the length we want. */ + memset(buf, 0, sizeof(buf)); + n2 = pem_encode(buf, n, (const unsigned char *)example_pre, + strlen(example_pre), "WOMBAT QUOTE"); + tt_int_op(n2, OP_EQ, 0); + tt_str_op(buf, OP_EQ, expected); + + /* Make sure it fails if the buffer is too short. */ + memset(buf, 0, sizeof(buf)); + n2 = pem_encode(buf, n - 1, (const unsigned char *)example_pre, + strlen(example_pre), "WOMBAT QUOTE"); + tt_int_op(n2, OP_EQ, -1); + + done: + ; +} + +static void +test_crypto_pem_decode(void *arg) +{ + (void)arg; + + unsigned char buf[4096]; + + /* Try a straightforward decoding. */ + int n = pem_decode(buf, sizeof(buf), + expected, strlen(expected), + "WOMBAT QUOTE"); + tt_int_op(n, OP_EQ, strlen(example_pre)); + tt_mem_op(buf, OP_EQ, example_pre, n); + + /* Succeed if the buffer is exactly the right size. */ + memset(buf, 0xff, sizeof(buf)); + n = pem_decode(buf, strlen(example_pre), + expected, strlen(expected), + "WOMBAT QUOTE"); + tt_int_op(n, OP_EQ, strlen(example_pre)); + tt_mem_op(buf, OP_EQ, example_pre, n); + tt_int_op(buf[n], OP_EQ, 0xff); + + /* Verify that it fails if the buffer is too small. */ + memset(buf, 0xff, sizeof(buf)); + n = pem_decode(buf, strlen(example_pre) - 1, + expected, strlen(expected), + "WOMBAT QUOTE"); + tt_int_op(n, OP_EQ, -1); + + /* Verify that it fails with an incorrect tag. */ + memset(buf, 0xff, sizeof(buf)); + n = pem_decode(buf, sizeof(buf), + expected, strlen(expected), + "QUOKKA VOTE"); + tt_int_op(n, OP_EQ, -1); + + /* Try truncated buffers of different sizes. */ + size_t i; + for (i = 0; i <= strlen(expected); ++i) { + char *truncated = tor_memdup(expected, i); + n = pem_decode(buf, sizeof(buf), + truncated, i, + "WOMBAT QUOTE"); + tor_free(truncated); + if (i < strlen(expected) - 1) { + tt_int_op(n, OP_EQ, -1); + } else { + tt_int_op(n, OP_EQ, strlen(example_pre)); + } + } + + done: + ; +} + +struct testcase_t pem_tests[] = { + { "encode", test_crypto_pem_encode, 0, NULL, NULL }, + { "decode", test_crypto_pem_decode, 0, NULL, NULL }, + END_OF_TESTCASES +};
tor-commits@lists.torproject.org