mirror of
https://gitlab.torproject.org/tpo/core/tor.git
synced 2024-12-06 19:41:15 +01:00
Add rudimentary support for PEM-encoding, since NSS doesn't do that.
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
orconfig.h
|
||||
lib/cc/*.h
|
||||
lib/ctime/*.h
|
||||
lib/encoding/*.h
|
||||
lib/intmath/*.h
|
||||
lib/log/*.h
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
@@ -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 \
|
||||
|
||||
@@ -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 },
|
||||
|
||||
@@ -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[];
|
||||
|
||||
@@ -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
|
||||
};
|
||||
Reference in New Issue
Block a user