From 5d015b38eb9f828acf522fa89e4944f3b343678c Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Thu, 23 Apr 2020 09:51:15 +0200 Subject: [PATCH] common: Add functions to help create DER objects. * common/tlv.c (put_tlv_to_membuf): New. (get_tlv_length): New. * common/tlv.h: Include membuf.h. Signed-off-by: Werner Koch --- common/tlv.c | 101 +++++++++++++++++++++++++++++++++++++++++++++++++++ common/tlv.h | 12 +++++- 2 files changed, 111 insertions(+), 2 deletions(-) diff --git a/common/tlv.c b/common/tlv.c index 0058b67ca..86e954a19 100644 --- a/common/tlv.c +++ b/common/tlv.c @@ -157,6 +157,107 @@ find_tlv_unchecked (const unsigned char *buffer, size_t length, } +/* Write TAG of CLASS to MEMBUF. CONSTRUCTED is a flag telling + * whether the value is constructed. LENGTH gives the length of the + * value, if it is 0 undefinite length is assumed. LENGTH is ignored + * for the NULL tag. TAG must be less that 0x1f. */ +void +put_tlv_to_membuf (membuf_t *membuf, int class, int tag, + int constructed, size_t length) +{ + unsigned char buf[20]; + int buflen = 0; + int i; + + if (tag < 0x1f) + { + *buf = (class << 6) | tag; + if (constructed) + *buf |= 0x20; + buflen++; + } + else + BUG (); + + if (!tag && !class) + buf[buflen++] = 0; /* end tag */ + else if (tag == TAG_NULL && !class) + buf[buflen++] = 0; /* NULL tag */ + else if (!length) + buf[buflen++] = 0x80; /* indefinite length */ + else if (length < 128) + buf[buflen++] = length; + else + { + /* If we know the sizeof a size_t we could support larger + * objects - however this is pretty ridiculous */ + i = (length <= 0xff ? 1: + length <= 0xffff ? 2: + length <= 0xffffff ? 3: 4); + + buf[buflen++] = (0x80 | i); + if (i > 3) + buf[buflen++] = length >> 24; + if (i > 2) + buf[buflen++] = length >> 16; + if (i > 1) + buf[buflen++] = length >> 8; + buf[buflen++] = length; + } + + put_membuf (membuf, buf, buflen); +} + + +/* Return the length of the to be constructed TLV. CONSTRUCTED is a + * flag telling whether the value is constructed. LENGTH gives the + * length of the value, if it is 0 undefinite length is assumed. + * LENGTH is ignored for the NULL tag. TAG must be less that 0x1f. */ +size_t +get_tlv_length (int class, int tag, int constructed, size_t length) +{ + size_t buflen = 0; + int i; + + (void)constructed; /* Not used, but passed for uniformity of such calls. */ + + if (tag < 0x1f) + { + buflen++; + } + else + { + buflen++; /* assume one and let the actual write function bail out */ + } + + if (!tag && !class) + buflen++; /* end tag */ + else if (tag == TAG_NULL && !class) + buflen++; /* NULL tag */ + else if (!length) + buflen++; /* indefinite length */ + else if (length < 128) + buflen++; + else + { + i = (length <= 0xff ? 1: + length <= 0xffff ? 2: + length <= 0xffffff ? 3: 4); + + buflen++; + if (i > 3) + buflen++; + if (i > 2) + buflen++; + if (i > 1) + buflen++; + buflen++; + } + + return buflen + length; +} + + /* ASN.1 BER parser: Parse BUFFER of length SIZE and return the tag and the length part from the TLV triplet. Update BUFFER and SIZE on success. */ diff --git a/common/tlv.h b/common/tlv.h index ba4ea2e42..0c915e63f 100644 --- a/common/tlv.h +++ b/common/tlv.h @@ -30,6 +30,7 @@ #ifndef SCD_TLV_H #define SCD_TLV_H 1 +#include "membuf.h" enum tlv_tag_class { CLASS_UNIVERSAL = 0, @@ -86,13 +87,20 @@ const unsigned char *find_tlv_unchecked (const unsigned char *buffer, size_t length, int tag, size_t *nbytes); +/* Wite a TLV header to MEMBUF. */ +void put_tlv_to_membuf (membuf_t *membuf, int class, int tag, + int constructed, size_t length); + +/* Count the length of a to be constructed TLV. */ +size_t get_tlv_length (int class, int tag, int constructed, size_t length); + /* ASN.1 BER parser: Parse BUFFER of length SIZE and return the tag and the length part from the TLV triplet. Update BUFFER and SIZE on success. */ gpg_error_t parse_ber_header (unsigned char const **buffer, size_t *size, - int *r_class, int *r_tag, - int *r_constructed, + int *r_class, int *r_tag, + int *r_constructed, int *r_ndef, size_t *r_length, size_t *r_nhdr);