tor/src/trunnel/ed25519_cert.c
Nick Mathewson 818e6f939d prop220: Implement certificates and key storage/creation
For prop220, we have a new ed25519 certificate type. This patch
implements the code to create, parse, and validate those, along with
code for routers to maintain their own sets of certificates and
keys.  (Some parts of master identity key encryption are done, but
the implementation of that isn't finished)
2015-05-28 10:40:56 -04:00

887 lines
21 KiB
C

/* ed25519_cert.c -- generated by Trunnel v1.2.
* https://gitweb.torproject.org/trunnel.git
* You probably shouldn't edit this file.
*/
#include <stdlib.h>
#include "trunnel-impl.h"
#include "ed25519_cert.h"
#define TRUNNEL_SET_ERROR_CODE(obj) \
do { \
(obj)->trunnel_error_code_ = 1; \
} while (0)
#if defined(__COVERITY__) || defined(__clang_analyzer__)
/* If we're runnning a static analysis tool, we don't want it to complain
* that some of our remaining-bytes checks are dead-code. */
int edcert_deadcode_dummy__ = 0;
#define OR_DEADCODE_DUMMY || edcert_deadcode_dummy__
#else
#define OR_DEADCODE_DUMMY
#endif
#define CHECK_REMAINING(nbytes, label) \
do { \
if (remaining < (nbytes) OR_DEADCODE_DUMMY) { \
goto label; \
} \
} while (0)
ed25519_cert_extension_t *
ed25519_cert_extension_new(void)
{
ed25519_cert_extension_t *val = trunnel_calloc(1, sizeof(ed25519_cert_extension_t));
if (NULL == val)
return NULL;
return val;
}
/** Release all storage held inside 'obj', but do not free 'obj'.
*/
static void
ed25519_cert_extension_clear(ed25519_cert_extension_t *obj)
{
(void) obj;
TRUNNEL_DYNARRAY_WIPE(&obj->un_unparsed);
TRUNNEL_DYNARRAY_CLEAR(&obj->un_unparsed);
}
void
ed25519_cert_extension_free(ed25519_cert_extension_t *obj)
{
if (obj == NULL)
return;
ed25519_cert_extension_clear(obj);
trunnel_memwipe(obj, sizeof(ed25519_cert_extension_t));
trunnel_free_(obj);
}
uint16_t
ed25519_cert_extension_get_ext_length(ed25519_cert_extension_t *inp)
{
return inp->ext_length;
}
int
ed25519_cert_extension_set_ext_length(ed25519_cert_extension_t *inp, uint16_t val)
{
inp->ext_length = val;
return 0;
}
uint8_t
ed25519_cert_extension_get_ext_type(ed25519_cert_extension_t *inp)
{
return inp->ext_type;
}
int
ed25519_cert_extension_set_ext_type(ed25519_cert_extension_t *inp, uint8_t val)
{
inp->ext_type = val;
return 0;
}
uint8_t
ed25519_cert_extension_get_ext_flags(ed25519_cert_extension_t *inp)
{
return inp->ext_flags;
}
int
ed25519_cert_extension_set_ext_flags(ed25519_cert_extension_t *inp, uint8_t val)
{
inp->ext_flags = val;
return 0;
}
size_t
ed25519_cert_extension_getlen_un_signing_key(const ed25519_cert_extension_t *inp)
{
(void)inp; return 32;
}
uint8_t
ed25519_cert_extension_get_un_signing_key(const ed25519_cert_extension_t *inp, size_t idx)
{
trunnel_assert(idx < 32);
return inp->un_signing_key[idx];
}
int
ed25519_cert_extension_set_un_signing_key(ed25519_cert_extension_t *inp, size_t idx, uint8_t elt)
{
trunnel_assert(idx < 32);
inp->un_signing_key[idx] = elt;
return 0;
}
uint8_t *
ed25519_cert_extension_getarray_un_signing_key(ed25519_cert_extension_t *inp)
{
return inp->un_signing_key;
}
size_t
ed25519_cert_extension_getlen_un_unparsed(const ed25519_cert_extension_t *inp)
{
return TRUNNEL_DYNARRAY_LEN(&inp->un_unparsed);
}
uint8_t
ed25519_cert_extension_get_un_unparsed(ed25519_cert_extension_t *inp, size_t idx)
{
return TRUNNEL_DYNARRAY_GET(&inp->un_unparsed, idx);
}
int
ed25519_cert_extension_set_un_unparsed(ed25519_cert_extension_t *inp, size_t idx, uint8_t elt)
{
TRUNNEL_DYNARRAY_SET(&inp->un_unparsed, idx, elt);
return 0;
}
int
ed25519_cert_extension_add_un_unparsed(ed25519_cert_extension_t *inp, uint8_t elt)
{
TRUNNEL_DYNARRAY_ADD(uint8_t, &inp->un_unparsed, elt, {});
return 0;
trunnel_alloc_failed:
TRUNNEL_SET_ERROR_CODE(inp);
return -1;
}
uint8_t *
ed25519_cert_extension_getarray_un_unparsed(ed25519_cert_extension_t *inp)
{
return inp->un_unparsed.elts_;
}
int
ed25519_cert_extension_setlen_un_unparsed(ed25519_cert_extension_t *inp, size_t newlen)
{
uint8_t *newptr;
newptr = trunnel_dynarray_setlen(&inp->un_unparsed.allocated_,
&inp->un_unparsed.n_, inp->un_unparsed.elts_, newlen,
sizeof(inp->un_unparsed.elts_[0]), (trunnel_free_fn_t) NULL,
&inp->trunnel_error_code_);
if (newptr == NULL)
goto trunnel_alloc_failed;
inp->un_unparsed.elts_ = newptr;
return 0;
trunnel_alloc_failed:
TRUNNEL_SET_ERROR_CODE(inp);
return -1;
}
const char *
ed25519_cert_extension_check(const ed25519_cert_extension_t *obj)
{
if (obj == NULL)
return "Object was NULL";
if (obj->trunnel_error_code_)
return "A set function failed on this object";
switch (obj->ext_type) {
case CERTEXT_SIGNED_WITH_KEY:
break;
default:
break;
}
return NULL;
}
ssize_t
ed25519_cert_extension_encoded_len(const ed25519_cert_extension_t *obj)
{
ssize_t result = 0;
if (NULL != ed25519_cert_extension_check(obj))
return -1;
/* Length of u16 ext_length */
result += 2;
/* Length of u8 ext_type */
result += 1;
/* Length of u8 ext_flags */
result += 1;
switch (obj->ext_type) {
case CERTEXT_SIGNED_WITH_KEY:
/* Length of u8 un_signing_key[32] */
result += 32;
break;
default:
/* Length of u8 un_unparsed[] */
result += TRUNNEL_DYNARRAY_LEN(&obj->un_unparsed);
break;
}
return result;
}
int
ed25519_cert_extension_clear_errors(ed25519_cert_extension_t *obj)
{
int r = obj->trunnel_error_code_;
obj->trunnel_error_code_ = 0;
return r;
}
ssize_t
ed25519_cert_extension_encode(uint8_t *output, const size_t avail, const ed25519_cert_extension_t *obj)
{
ssize_t result = 0;
size_t written = 0;
uint8_t *ptr = output;
const char *msg;
#ifdef TRUNNEL_CHECK_ENCODED_LEN
const ssize_t encoded_len = ed25519_cert_extension_encoded_len(obj);
#endif
uint8_t *backptr_ext_length = NULL;
if (NULL != (msg = ed25519_cert_extension_check(obj)))
goto check_failed;
#ifdef TRUNNEL_CHECK_ENCODED_LEN
trunnel_assert(encoded_len >= 0);
#endif
/* Encode u16 ext_length */
backptr_ext_length = ptr;
trunnel_assert(written <= avail);
if (avail - written < 2)
goto truncated;
trunnel_set_uint16(ptr, trunnel_htons(obj->ext_length));
written += 2; ptr += 2;
/* Encode u8 ext_type */
trunnel_assert(written <= avail);
if (avail - written < 1)
goto truncated;
trunnel_set_uint8(ptr, (obj->ext_type));
written += 1; ptr += 1;
/* Encode u8 ext_flags */
trunnel_assert(written <= avail);
if (avail - written < 1)
goto truncated;
trunnel_set_uint8(ptr, (obj->ext_flags));
written += 1; ptr += 1;
{
size_t written_before_union = written;
/* Encode union un[ext_type] */
trunnel_assert(written <= avail);
switch (obj->ext_type) {
case CERTEXT_SIGNED_WITH_KEY:
/* Encode u8 un_signing_key[32] */
trunnel_assert(written <= avail);
if (avail - written < 32)
goto truncated;
memcpy(ptr, obj->un_signing_key, 32);
written += 32; ptr += 32;
break;
default:
/* Encode u8 un_unparsed[] */
{
size_t elt_len = TRUNNEL_DYNARRAY_LEN(&obj->un_unparsed);
trunnel_assert(written <= avail);
if (avail - written < elt_len)
goto truncated;
memcpy(ptr, obj->un_unparsed.elts_, elt_len);
written += elt_len; ptr += elt_len;
}
break;
}
/* Write the length field back to ext_length */
trunnel_assert(written >= written_before_union);
#if UINT16_MAX < SIZE_MAX
if (written - written_before_union > UINT16_MAX)
goto check_failed;
#endif
trunnel_set_uint16(backptr_ext_length, trunnel_htons(written - written_before_union));
}
trunnel_assert(ptr == output + written);
#ifdef TRUNNEL_CHECK_ENCODED_LEN
{
trunnel_assert(encoded_len >= 0);
trunnel_assert((size_t)encoded_len == written);
}
#endif
return written;
truncated:
result = -2;
goto fail;
check_failed:
(void)msg;
result = -1;
goto fail;
fail:
trunnel_assert(result < 0);
return result;
}
/** As ed25519_cert_extension_parse(), but do not allocate the output
* object.
*/
static ssize_t
ed25519_cert_extension_parse_into(ed25519_cert_extension_t *obj, const uint8_t *input, const size_t len_in)
{
const uint8_t *ptr = input;
size_t remaining = len_in;
ssize_t result = 0;
(void)result;
/* Parse u16 ext_length */
CHECK_REMAINING(2, truncated);
obj->ext_length = trunnel_ntohs(trunnel_get_uint16(ptr));
remaining -= 2; ptr += 2;
/* Parse u8 ext_type */
CHECK_REMAINING(1, truncated);
obj->ext_type = (trunnel_get_uint8(ptr));
remaining -= 1; ptr += 1;
/* Parse u8 ext_flags */
CHECK_REMAINING(1, truncated);
obj->ext_flags = (trunnel_get_uint8(ptr));
remaining -= 1; ptr += 1;
{
size_t remaining_after;
CHECK_REMAINING(obj->ext_length, truncated);
remaining_after = remaining - obj->ext_length;
remaining = obj->ext_length;
/* Parse union un[ext_type] */
switch (obj->ext_type) {
case CERTEXT_SIGNED_WITH_KEY:
/* Parse u8 un_signing_key[32] */
CHECK_REMAINING(32, fail);
memcpy(obj->un_signing_key, ptr, 32);
remaining -= 32; ptr += 32;
break;
default:
/* Parse u8 un_unparsed[] */
TRUNNEL_DYNARRAY_EXPAND(uint8_t, &obj->un_unparsed, remaining, {});
obj->un_unparsed.n_ = remaining;
memcpy(obj->un_unparsed.elts_, ptr, remaining);
ptr += remaining; remaining -= remaining;
break;
}
if (remaining != 0)
goto fail;
remaining = remaining_after;
}
trunnel_assert(ptr + remaining == input + len_in);
return len_in - remaining;
truncated:
return -2;
trunnel_alloc_failed:
return -1;
fail:
result = -1;
return result;
}
ssize_t
ed25519_cert_extension_parse(ed25519_cert_extension_t **output, const uint8_t *input, const size_t len_in)
{
ssize_t result;
*output = ed25519_cert_extension_new();
if (NULL == *output)
return -1;
result = ed25519_cert_extension_parse_into(*output, input, len_in);
if (result < 0) {
ed25519_cert_extension_free(*output);
*output = NULL;
}
return result;
}
ed25519_cert_t *
ed25519_cert_new(void)
{
ed25519_cert_t *val = trunnel_calloc(1, sizeof(ed25519_cert_t));
if (NULL == val)
return NULL;
val->version = 1;
return val;
}
/** Release all storage held inside 'obj', but do not free 'obj'.
*/
static void
ed25519_cert_clear(ed25519_cert_t *obj)
{
(void) obj;
{
unsigned idx;
for (idx = 0; idx < TRUNNEL_DYNARRAY_LEN(&obj->ext); ++idx) {
ed25519_cert_extension_free(TRUNNEL_DYNARRAY_GET(&obj->ext, idx));
}
}
TRUNNEL_DYNARRAY_WIPE(&obj->ext);
TRUNNEL_DYNARRAY_CLEAR(&obj->ext);
}
void
ed25519_cert_free(ed25519_cert_t *obj)
{
if (obj == NULL)
return;
ed25519_cert_clear(obj);
trunnel_memwipe(obj, sizeof(ed25519_cert_t));
trunnel_free_(obj);
}
uint8_t
ed25519_cert_get_version(ed25519_cert_t *inp)
{
return inp->version;
}
int
ed25519_cert_set_version(ed25519_cert_t *inp, uint8_t val)
{
if (! ((val == 1))) {
TRUNNEL_SET_ERROR_CODE(inp);
return -1;
}
inp->version = val;
return 0;
}
uint8_t
ed25519_cert_get_cert_type(ed25519_cert_t *inp)
{
return inp->cert_type;
}
int
ed25519_cert_set_cert_type(ed25519_cert_t *inp, uint8_t val)
{
inp->cert_type = val;
return 0;
}
uint32_t
ed25519_cert_get_exp_field(ed25519_cert_t *inp)
{
return inp->exp_field;
}
int
ed25519_cert_set_exp_field(ed25519_cert_t *inp, uint32_t val)
{
inp->exp_field = val;
return 0;
}
uint8_t
ed25519_cert_get_cert_key_type(ed25519_cert_t *inp)
{
return inp->cert_key_type;
}
int
ed25519_cert_set_cert_key_type(ed25519_cert_t *inp, uint8_t val)
{
inp->cert_key_type = val;
return 0;
}
size_t
ed25519_cert_getlen_certified_key(const ed25519_cert_t *inp)
{
(void)inp; return 32;
}
uint8_t
ed25519_cert_get_certified_key(const ed25519_cert_t *inp, size_t idx)
{
trunnel_assert(idx < 32);
return inp->certified_key[idx];
}
int
ed25519_cert_set_certified_key(ed25519_cert_t *inp, size_t idx, uint8_t elt)
{
trunnel_assert(idx < 32);
inp->certified_key[idx] = elt;
return 0;
}
uint8_t *
ed25519_cert_getarray_certified_key(ed25519_cert_t *inp)
{
return inp->certified_key;
}
uint8_t
ed25519_cert_get_n_extensions(ed25519_cert_t *inp)
{
return inp->n_extensions;
}
int
ed25519_cert_set_n_extensions(ed25519_cert_t *inp, uint8_t val)
{
inp->n_extensions = val;
return 0;
}
size_t
ed25519_cert_getlen_ext(const ed25519_cert_t *inp)
{
return TRUNNEL_DYNARRAY_LEN(&inp->ext);
}
struct ed25519_cert_extension_st *
ed25519_cert_get_ext(ed25519_cert_t *inp, size_t idx)
{
return TRUNNEL_DYNARRAY_GET(&inp->ext, idx);
}
int
ed25519_cert_set_ext(ed25519_cert_t *inp, size_t idx, struct ed25519_cert_extension_st * elt)
{
ed25519_cert_extension_t *oldval = TRUNNEL_DYNARRAY_GET(&inp->ext, idx);
if (oldval && oldval != elt)
ed25519_cert_extension_free(oldval);
return ed25519_cert_set0_ext(inp, idx, elt);
}
int
ed25519_cert_set0_ext(ed25519_cert_t *inp, size_t idx, struct ed25519_cert_extension_st * elt)
{
TRUNNEL_DYNARRAY_SET(&inp->ext, idx, elt);
return 0;
}
int
ed25519_cert_add_ext(ed25519_cert_t *inp, struct ed25519_cert_extension_st * elt)
{
#if SIZE_MAX >= UINT8_MAX
if (inp->ext.n_ == UINT8_MAX)
goto trunnel_alloc_failed;
#endif
TRUNNEL_DYNARRAY_ADD(struct ed25519_cert_extension_st *, &inp->ext, elt, {});
return 0;
trunnel_alloc_failed:
TRUNNEL_SET_ERROR_CODE(inp);
return -1;
}
struct ed25519_cert_extension_st * *
ed25519_cert_getarray_ext(ed25519_cert_t *inp)
{
return inp->ext.elts_;
}
int
ed25519_cert_setlen_ext(ed25519_cert_t *inp, size_t newlen)
{
struct ed25519_cert_extension_st * *newptr;
#if UINT8_MAX < SIZE_MAX
if (newlen > UINT8_MAX)
goto trunnel_alloc_failed;
#endif
newptr = trunnel_dynarray_setlen(&inp->ext.allocated_,
&inp->ext.n_, inp->ext.elts_, newlen,
sizeof(inp->ext.elts_[0]), (trunnel_free_fn_t) ed25519_cert_extension_free,
&inp->trunnel_error_code_);
if (newptr == NULL)
goto trunnel_alloc_failed;
inp->ext.elts_ = newptr;
return 0;
trunnel_alloc_failed:
TRUNNEL_SET_ERROR_CODE(inp);
return -1;
}
size_t
ed25519_cert_getlen_signature(const ed25519_cert_t *inp)
{
(void)inp; return 64;
}
uint8_t
ed25519_cert_get_signature(const ed25519_cert_t *inp, size_t idx)
{
trunnel_assert(idx < 64);
return inp->signature[idx];
}
int
ed25519_cert_set_signature(ed25519_cert_t *inp, size_t idx, uint8_t elt)
{
trunnel_assert(idx < 64);
inp->signature[idx] = elt;
return 0;
}
uint8_t *
ed25519_cert_getarray_signature(ed25519_cert_t *inp)
{
return inp->signature;
}
const char *
ed25519_cert_check(const ed25519_cert_t *obj)
{
if (obj == NULL)
return "Object was NULL";
if (obj->trunnel_error_code_)
return "A set function failed on this object";
if (! (obj->version == 1))
return "Integer out of bounds";
{
const char *msg;
unsigned idx;
for (idx = 0; idx < TRUNNEL_DYNARRAY_LEN(&obj->ext); ++idx) {
if (NULL != (msg = ed25519_cert_extension_check(TRUNNEL_DYNARRAY_GET(&obj->ext, idx))))
return msg;
}
}
if (TRUNNEL_DYNARRAY_LEN(&obj->ext) != obj->n_extensions)
return "Length mismatch for ext";
return NULL;
}
ssize_t
ed25519_cert_encoded_len(const ed25519_cert_t *obj)
{
ssize_t result = 0;
if (NULL != ed25519_cert_check(obj))
return -1;
/* Length of u8 version IN [1] */
result += 1;
/* Length of u8 cert_type */
result += 1;
/* Length of u32 exp_field */
result += 4;
/* Length of u8 cert_key_type */
result += 1;
/* Length of u8 certified_key[32] */
result += 32;
/* Length of u8 n_extensions */
result += 1;
/* Length of struct ed25519_cert_extension ext[n_extensions] */
{
unsigned idx;
for (idx = 0; idx < TRUNNEL_DYNARRAY_LEN(&obj->ext); ++idx) {
result += ed25519_cert_extension_encoded_len(TRUNNEL_DYNARRAY_GET(&obj->ext, idx));
}
}
/* Length of u8 signature[64] */
result += 64;
return result;
}
int
ed25519_cert_clear_errors(ed25519_cert_t *obj)
{
int r = obj->trunnel_error_code_;
obj->trunnel_error_code_ = 0;
return r;
}
ssize_t
ed25519_cert_encode(uint8_t *output, const size_t avail, const ed25519_cert_t *obj)
{
ssize_t result = 0;
size_t written = 0;
uint8_t *ptr = output;
const char *msg;
#ifdef TRUNNEL_CHECK_ENCODED_LEN
const ssize_t encoded_len = ed25519_cert_encoded_len(obj);
#endif
if (NULL != (msg = ed25519_cert_check(obj)))
goto check_failed;
#ifdef TRUNNEL_CHECK_ENCODED_LEN
trunnel_assert(encoded_len >= 0);
#endif
/* Encode u8 version IN [1] */
trunnel_assert(written <= avail);
if (avail - written < 1)
goto truncated;
trunnel_set_uint8(ptr, (obj->version));
written += 1; ptr += 1;
/* Encode u8 cert_type */
trunnel_assert(written <= avail);
if (avail - written < 1)
goto truncated;
trunnel_set_uint8(ptr, (obj->cert_type));
written += 1; ptr += 1;
/* Encode u32 exp_field */
trunnel_assert(written <= avail);
if (avail - written < 4)
goto truncated;
trunnel_set_uint32(ptr, trunnel_htonl(obj->exp_field));
written += 4; ptr += 4;
/* Encode u8 cert_key_type */
trunnel_assert(written <= avail);
if (avail - written < 1)
goto truncated;
trunnel_set_uint8(ptr, (obj->cert_key_type));
written += 1; ptr += 1;
/* Encode u8 certified_key[32] */
trunnel_assert(written <= avail);
if (avail - written < 32)
goto truncated;
memcpy(ptr, obj->certified_key, 32);
written += 32; ptr += 32;
/* Encode u8 n_extensions */
trunnel_assert(written <= avail);
if (avail - written < 1)
goto truncated;
trunnel_set_uint8(ptr, (obj->n_extensions));
written += 1; ptr += 1;
/* Encode struct ed25519_cert_extension ext[n_extensions] */
{
unsigned idx;
for (idx = 0; idx < TRUNNEL_DYNARRAY_LEN(&obj->ext); ++idx) {
trunnel_assert(written <= avail);
result = ed25519_cert_extension_encode(ptr, avail - written, TRUNNEL_DYNARRAY_GET(&obj->ext, idx));
if (result < 0)
goto fail; /* XXXXXXX !*/
written += result; ptr += result;
}
}
/* Encode u8 signature[64] */
trunnel_assert(written <= avail);
if (avail - written < 64)
goto truncated;
memcpy(ptr, obj->signature, 64);
written += 64; ptr += 64;
trunnel_assert(ptr == output + written);
#ifdef TRUNNEL_CHECK_ENCODED_LEN
{
trunnel_assert(encoded_len >= 0);
trunnel_assert((size_t)encoded_len == written);
}
#endif
return written;
truncated:
result = -2;
goto fail;
check_failed:
(void)msg;
result = -1;
goto fail;
fail:
trunnel_assert(result < 0);
return result;
}
/** As ed25519_cert_parse(), but do not allocate the output object.
*/
static ssize_t
ed25519_cert_parse_into(ed25519_cert_t *obj, const uint8_t *input, const size_t len_in)
{
const uint8_t *ptr = input;
size_t remaining = len_in;
ssize_t result = 0;
(void)result;
/* Parse u8 version IN [1] */
CHECK_REMAINING(1, truncated);
obj->version = (trunnel_get_uint8(ptr));
remaining -= 1; ptr += 1;
if (! (obj->version == 1))
goto fail;
/* Parse u8 cert_type */
CHECK_REMAINING(1, truncated);
obj->cert_type = (trunnel_get_uint8(ptr));
remaining -= 1; ptr += 1;
/* Parse u32 exp_field */
CHECK_REMAINING(4, truncated);
obj->exp_field = trunnel_ntohl(trunnel_get_uint32(ptr));
remaining -= 4; ptr += 4;
/* Parse u8 cert_key_type */
CHECK_REMAINING(1, truncated);
obj->cert_key_type = (trunnel_get_uint8(ptr));
remaining -= 1; ptr += 1;
/* Parse u8 certified_key[32] */
CHECK_REMAINING(32, truncated);
memcpy(obj->certified_key, ptr, 32);
remaining -= 32; ptr += 32;
/* Parse u8 n_extensions */
CHECK_REMAINING(1, truncated);
obj->n_extensions = (trunnel_get_uint8(ptr));
remaining -= 1; ptr += 1;
/* Parse struct ed25519_cert_extension ext[n_extensions] */
TRUNNEL_DYNARRAY_EXPAND(ed25519_cert_extension_t *, &obj->ext, obj->n_extensions, {});
{
ed25519_cert_extension_t * elt;
unsigned idx;
for (idx = 0; idx < obj->n_extensions; ++idx) {
result = ed25519_cert_extension_parse(&elt, ptr, remaining);
if (result < 0)
goto relay_fail;
trunnel_assert((size_t)result <= remaining);
remaining -= result; ptr += result;
TRUNNEL_DYNARRAY_ADD(ed25519_cert_extension_t *, &obj->ext, elt, {ed25519_cert_extension_free(elt);});
}
}
/* Parse u8 signature[64] */
CHECK_REMAINING(64, truncated);
memcpy(obj->signature, ptr, 64);
remaining -= 64; ptr += 64;
trunnel_assert(ptr + remaining == input + len_in);
return len_in - remaining;
truncated:
return -2;
relay_fail:
if (result >= 0) result = -1;
return result;
trunnel_alloc_failed:
return -1;
fail:
result = -1;
return result;
}
ssize_t
ed25519_cert_parse(ed25519_cert_t **output, const uint8_t *input, const size_t len_in)
{
ssize_t result;
*output = ed25519_cert_new();
if (NULL == *output)
return -1;
result = ed25519_cert_parse_into(*output, input, len_in);
if (result < 0) {
ed25519_cert_free(*output);
*output = NULL;
}
return result;
}