Files
mars-matrixssl/core/include/psbuf.h
Janne Johansson eec42aa814 MatrixSSL 4.3.0
2020-07-31 13:31:27 +03:00

659 lines
18 KiB
C

/**
* @file psbuf.h
* @version $Format:%h%d$
*
* API for handling buffers containing binary data.
*/
/*
* Copyright (c) 2017 INSIDE Secure Corporation
* All Rights Reserved
*
* The latest version of this code is available at http://www.matrixssl.org
*
* This software is open source; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This General Public License does NOT permit incorporating this software
* into proprietary programs. If you are unable to comply with the GPL, a
* commercial license for this software may be purchased from INSIDE at
* http://www.insidesecure.com/
*
* This program is distributed in WITHOUT ANY WARRANTY; without even the
* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
* http://www.gnu.org/copyleft/gpl.html
*/
/******************************************************************************/
#ifndef _h_PS_BUF
# define _h_PS_BUF
# include "coreApi.h"
# include "osdep_string.h"
/* API for psBuf initialization and basic usage.*/
void *psBufInit(psPool_t *pool, psBuf_t *buf, size_t capacity);
void psBufUninit(psPool_t *pool, psBuf_t *buf);
int32_t psBufFromStaticData(psBuf_t *buf, const void *data, size_t len);
int32_t psBufEmptyFromPointerSize(psBuf_t *buf, void *data, size_t len);
void *psBufDetach(psPool_t *pool, psBuf_t *buf, size_t *len_p);
void *psBufAppendSize(psBuf_t *buf, size_t sz);
size_t psBufGetMaxAppendSize(const psBuf_t *buf);
void psBufReservePrepend(psBuf_t *buf, size_t sz);
void *psBufPrependSize(psBuf_t *buf, size_t sz);
size_t psBufGetMaxPrependSize(const psBuf_t *buf);
void *psBufGetData(psBuf_t *buf);
size_t psBufGetDataSize(const psBuf_t *buf);
void psBufNormalize(psBuf_t *buf);
char *psBufAsHex(psPool_t *pool, const psBuf_t *buf);
int32_t psBufCopyDataN(psBuf_t *buf, size_t reqLen,
unsigned char *target, size_t *targetlen);
int32_t psBufFromData(psPool_t *pool, psBuf_t *buf,
const void *data, size_t len);
static inline int psBufEq(const psBuf_t *buf1, const psBuf_t *buf2)
{
return (buf1->end - buf1->start) == (buf2->end - buf2->start) &&
buf1->start != NULL &&
buf2->start != NULL &&
Memcmp(buf1->start, buf2->start, (buf1->end - buf1->start))
== 0;
}
#include <stdio.h>
static inline void *psBufAppendChar(psBuf_t *buf, char ch)
{
void *loc = psBufAppendSize(buf, 1);
if (loc)
{
*(char *) loc = ch;
}
return loc;
}
static inline void *psBufPrependChar(psBuf_t *buf, char ch)
{
void *loc = psBufPrependSize(buf, 1);
if (loc)
{
*(char *) loc = ch;
}
return loc;
}
/* API for Dynamic Buffers initialization and basic usage. */
void *psDynBufInit(psPool_t *pool, psDynBuf_t *db, size_t capacity);
void psDynBufUninit(psDynBuf_t *db);
void *psDynBufDetach(psDynBuf_t *db, size_t *len_p);
void *psDynBufDetachPsSize(psDynBuf_t *db, psSize_t *len_p);
void *psDynBufAppendSize(psDynBuf_t *db, size_t sz);
void psDynBufReservePrepend(psDynBuf_t *db, size_t sz);
void *psDynBufPrependSize(psDynBuf_t *db, size_t sz);
static inline void *psDynBufAppendChar(psDynBuf_t *db, char ch)
{
void *loc = psDynBufAppendSize(db, 1);
if (loc)
{
*(char *) loc = ch;
}
return loc;
}
static __inline void *psDynBufAppendByte(psDynBuf_t *db, unsigned char ch)
{
void *loc = psDynBufAppendSize(db, 1);
if (loc)
{
*(unsigned char *) loc = ch;
}
return loc;
}
void *psDynBufAppendAsBigEndianUint16(psDynBuf_t *db, uint16_t val);
void *psDynBufAppendAsBigEndianUint32(psDynBuf_t *db, uint32_t val);
void *psDynBufAppendUtf8(psDynBuf_t *db, int chr);
void *psDynBufAppendUtf16(psDynBuf_t *db, int chr);
void *psDynBufAppendUtf32(psDynBuf_t *db, int chr);
static inline void *psDynBufPrependChar(psDynBuf_t *db, char ch)
{
void *loc = psDynBufPrependSize(db, 1);
if (loc)
{
*(char *) loc = ch;
}
return loc;
}
static inline void *psDynBufAppendStr(psDynBuf_t *db, const char *s)
{
size_t len = s ? Strlen(s) : 0;
void *loc = psDynBufAppendSize(db, len);
if (loc)
{
Memcpy(loc, s, len);
}
return loc;
}
static inline void *psDynBufPrependStr(psDynBuf_t *db, const char *s)
{
size_t len = s ? Strlen(s) : 0;
void *loc = psDynBufPrependSize(db, len);
if (loc)
{
Memcpy(loc, s, len);
}
return loc;
}
static inline void *psDynBufAppendOctets(psDynBuf_t *db, const void *data,
size_t len)
{
void *loc = psDynBufAppendSize(db, len);
if (loc)
{
Memcpy(loc, data, len);
}
return loc;
}
static inline void *psDynBufAppendOctetNTimes(psDynBuf_t *db,
uint8_t octet,
size_t n)
{
size_t i;
void *loc;
for (i = 0; i < n; i++)
{
loc = psDynBufAppendOctets(db, &octet, 1);
}
return loc;
}
static inline void *psDynBufAppendBuf(psDynBuf_t *db, const psBuf_t *b)
{
return psDynBufAppendOctets(db, b->start, b->end - b->start);
}
static inline void *psDynBufAppendParseBuf(psDynBuf_t *db,
const psParseBuf_t *pb)
{
if (!pb || pb->err)
{
db->err++;
return NULL;
}
return psDynBufAppendBuf(db, &(pb->buf));
}
static inline void *psDynBufIncorporateDynBuf(psDynBuf_t *db, psDynBuf_t *db2)
{
size_t len;
void *data = psDynBufDetach(db2, &len);
if (data)
{
return psDynBufAppendOctets(db, data, len);
}
else
{
db->err++;
return NULL;
}
}
/* Dynamic buffer subbuffers. */
void *psDynBufSubInit(psDynBuf_t *db, psDynBuf_t *sub, size_t capacity);
void *psDynBufSubInitAt(psDynBuf_t *db, psDynBuf_t *sub, size_t at,
size_t length);
void *psDynBufSubFinish(psDynBuf_t *sub);
/* Note: This variable argument function is currently implemented as a macro. */
# define psDynBufAppendStrf(ps_dyn_buf_p, ...) \
do { \
char tmp; \
size_t len = 1 + Snprintf(&tmp, 0, __VA_ARGS__); \
char *target = psDynBufAppendSize((ps_dyn_buf_p), len); \
if (target) { \
Snprintf(target, len, __VA_ARGS__); \
(ps_dyn_buf_p)->buf.end -= 1; \
} \
} while (0)
/* Subset of ASN.1 via psDynBuf. */
char *psDynBufAppendAsn1TagGen(psDynBuf_t *db, unsigned char tag,
const unsigned char *bytes, size_t len);
static inline
char *psDynBufAppendAsn1IntegerSmall(psDynBuf_t *db, signed char byte)
{
unsigned char bytes[1];
bytes[0] = (unsigned char) byte;
return psDynBufAppendAsn1TagGen(db, 0x02, bytes, 1);
}
static inline
char *psDynBufAppendAsn1OctetString(psDynBuf_t *db,
const unsigned char *bytes, size_t len)
{
return psDynBufAppendAsn1TagGen(db, 0x04, bytes, len);
}
static inline
char *psDynBufAppendAsn1Oid(psDynBuf_t *db,
const unsigned char *oidbytes, size_t len)
{
/* Note: oidbytes shall not include OID identifier (6) or length. */
return psDynBufAppendAsn1TagGen(db, 0x06, oidbytes, len);
}
char *psDynBufBeginConstructedTag(psDynBuf_t *db, psDynBuf_t *sub);
char *psDynBufEndConstructedTag(psDynBuf_t *sub, unsigned char tag);
static inline char *psDynBufBeginSequence(psDynBuf_t *db, psDynBuf_t *sub)
{
return psDynBufBeginConstructedTag(db, sub);
}
static inline char *psDynBufEndSequence(psDynBuf_t *sub)
{
return psDynBufEndConstructedTag(sub, 0x30);
}
static inline int32_t psDynBufDetachBuf(psDynBuf_t *db, psBuf_t *target)
{
size_t sz;
void *buf;
buf = psDynBufDetach(db, &sz);
target->start = target->buf = (unsigned char *) buf;
if (!buf)
{
/* Exception path: memory allocation failure. */
target->size = 0;
target->end = (unsigned char *) buf;
return PS_MEM_FAIL;
}
target->size = sz;
target->end = ((unsigned char *) buf) + sz;
return PS_SUCCESS;
}
/* Append a TLS representation language vector.
The minLen and maxLen arguments are the minimum and maximum
vector lengths. They must be must be taken from the vector
specification. For example, "opaque key_exchange<1..2^16-1>"
would be encoded with minLen = 1, maxLen = (1 << 16) - 1.
@param[out] db DynBuf where to append the vector.
@param[in] minLen Minimum vector length, as defined by spec.
@param[in] maxLen Maximum vector length, as defined by spec.
Maximum vector length in the TLS specification is currently
2^24 - 1. This function checks that maxLen does not exceed this.
@param[in] data The data to encode into the vector.
@param[in] len Number of data octets to encode.
*/
int32_t psDynBufAppendTlsVector(psDynBuf_t *db,
size_t minLen,
size_t maxLen,
const unsigned char *data,
size_t len);
/* Start parsing static data using psParseBuf_t. */
int32_t psParseBufFromStaticData(psParseBuf_t *pb,
const void *data,
size_t len);
/* Check if there is sufficient data to parse left. */
static inline int psParseCanRead(const psParseBuf_t *pb, size_t nbytes)
{
size_t bytes_readable;
if (pb->err)
{
return 0;
}
bytes_readable = pb->buf.end - pb->buf.start;
return bytes_readable >= nbytes;
}
static inline size_t psParseGetRemainingLen(const psParseBuf_t *pb)
{
size_t bytes_readable;
if (pb->err)
{
return 0;
}
bytes_readable = pb->buf.end - pb->buf.start;
return bytes_readable;
}
static __inline int psParseTlsRecordHeader(psParseBuf_t *pb,
unsigned char *type,
unsigned char *majVer,
unsigned char *minVer,
unsigned short *len)
{
int can_read;
can_read = psParseCanRead(pb, 5);
if (!can_read)
{
return 0;
}
*type = (unsigned char) *(pb->buf.start); pb->buf.start++;
*majVer = (unsigned char) *(pb->buf.start); pb->buf.start++;
*minVer = (unsigned char) *(pb->buf.start); pb->buf.start++;
*len = (unsigned char) *(pb->buf.start); pb->buf.start++;
*len <<= 8;
*len += (unsigned char) *(pb->buf.start); pb->buf.start++;
return 5;
}
static __inline int psParseTlsHandshakeHeader(psParseBuf_t *pb,
unsigned char *type,
unsigned int *len)
{
int can_read;
can_read = psParseCanRead(pb, 4);
if (!can_read)
{
return 0;
}
*type = (unsigned char) *(pb->buf.start); pb->buf.start++;
*len = (unsigned char) *(pb->buf.start); pb->buf.start++;
*len <<= 8;
*len += (unsigned char) *(pb->buf.start); pb->buf.start++;
*len <<= 8;
*len += (unsigned char) *(pb->buf.start); pb->buf.start++;
return 4;
}
static __inline int psParseOctet(psParseBuf_t *pb,
unsigned char *octet)
{
if (!psParseCanRead(pb, 1))
{
return 0;
}
*octet = (unsigned char)*pb->buf.start; pb->buf.start++;
return 1;
}
int psParseTlsVariableLengthVec(const unsigned char *start,
const unsigned char *end,
psSizeL_t minLen,
psSizeL_t maxLen,
psSizeL_t *vecDataLen);
/** Maps to psParseTlsVariableLengthVec. */
static __inline psResSize_t psParseBufParseTlsVector(psParseBuf_t *pb,
psSizeL_t minLen,
psSizeL_t maxLen,
psSizeL_t *vecDataLen)
{
int rc;
if (pb->err)
{
return PS_FAILURE;
}
rc = psParseTlsVariableLengthVec(pb->buf.start,
pb->buf.end,
minLen,
maxLen,
vecDataLen);
if (rc < 0)
{
return rc;
}
/* rc == number of length octets. */
pb->buf.start += rc;
return rc;
}
static __inline int psParseBufTryParseOctets(psParseBuf_t *pb,
size_t num_octets,
unsigned char *parsed_octets,
psBool_t store_value)
{
size_t i;
if (!psParseCanRead(pb, num_octets))
{
return 0;
}
for (i = 0; i < num_octets; i++)
{
if (store_value)
{
parsed_octets[i] = (unsigned char)*pb->buf.start;
}
pb->buf.start++;
}
return num_octets;
}
static __inline int psParseBufTryParseBigEndianUint16(psParseBuf_t *pb,
uint16_t *value)
{
uint16_t val = 0;
if (!psParseCanRead(pb, 2))
{
return 0;
}
val = (*pb->buf.start << 8); pb->buf.start++;
val |= *pb->buf.start; pb->buf.start++;
*value = val;
return 2;
}
static __inline int psParseBufTryParseBigEndianUint32(psParseBuf_t *pb,
uint32_t *value)
{
uint32_t val = 0;
if (!psParseCanRead(pb, 4))
{
return 0;
}
val = (*pb->buf.start << 24); pb->buf.start++;
val |= (*pb->buf.start << 16); pb->buf.start++;
val |= (*pb->buf.start << 8); pb->buf.start++;
val |= *pb->buf.start; pb->buf.start++;
*value = val;
return 4;
}
static __inline int psParseTryForward(psParseBuf_t *pb,
size_t num_bytes)
{
if (!psParseCanRead(pb, num_bytes))
{
return 0;
}
pb->buf.start += num_bytes;
return num_bytes;
}
static __inline void psParseForward(psParseBuf_t *pb,
size_t num_bytes)
{
pb->buf.start += num_bytes;
}
static __inline void psParseRewind(psParseBuf_t *pb,
size_t num_bytes)
{
pb->buf.start -= num_bytes;
}
/* Check if there is sufficient data to parse left. */
PSPUBLIC int psParseBufCanReadUtf8(const psParseBuf_t *pb);
/* Check if there is sufficient data to parse left. */
PSPUBLIC unsigned int psParseBufReadUtf8(psParseBuf_t *pb);
/* Get length of following ASN.1 tag
(specify tag as unsigned char or 0 for ANY).
This is for parsing content of parsebuf as ASN.1 DER data.
If hdrLen_p is non-null, the function also returns the length of tag header.
*/
size_t psParseBufGetTagLen(const psParseBuf_t *pb, unsigned char tag,
size_t *hdrLen_p);
/* Test if there is specific (tag > 0) or any (tag == 0) ASN.1 der
encoding at current parsing location.
return 0 no, 1 yes */
int psParseBufCanGetTag(const psParseBuf_t *pb, unsigned char tag);
/* Try read ASN.1 DER specific (tag > 0) or any (tag == 0) at the
current parsing location. Reading will return sub parsebuf,
which allows reading constructed tags.
Returns length of tag read (including header) or 0 if no tag was read.
Parsing location will advance once finished with content.
*/
size_t psParseBufTryReadTagSub(const psParseBuf_t *pb,
psParseBuf_t *content, unsigned char tag);
/* Read specified (or any) ASN.1 tag at the current parsing location.
it is considered error if read fails.
Parsing location will advance once finished with content. */
size_t psParseBufReadTagSub(psParseBuf_t *pb,
psParseBuf_t *content, unsigned char tag);
/* Copy all data from parse buffer. Supports length negotiations.
Fails if parsing errors have been seen. */
int32_t psParseBufCopyAll(const psParseBuf_t *pb, unsigned char *target,
size_t *targetlen);
/* Return true only if buffers have encountered no parsing errors and
the contents are equal. */
int psParseBufEq(const psParseBuf_t *pb1, const psParseBuf_t *pb2);
/* return PS_SUCCESS if pb has not encountered parsing errors. */
int32_t psParseBufCheckState(const psParseBuf_t *pb);
/* Alias (implemented as a macro) for psParseBufTryReadTagSub used on sequence.
*/
# define psParseBufTryReadSequenceSub(pb, content) \
psParseBufTryReadTagSub(pb, content, 0x30)
/* Alias (implemented as a macro) for psParseBufReadTagSub used on sequence. */
# define psParseBufReadSequenceSub(pb, content) \
psParseBufReadTagSub(pb, content, 0x30)
/* Skip tag with specified tag id (or 0 for any) if possible.
If tag was skipped, return length in bytes that was skipped. */
size_t psParseBufTrySkipTag(psParseBuf_t *pb, unsigned char tag);
/* Skip tag with specified tag id (or 0 for any) if possible.
Tag not existing is considered error and error is set in pb. */
size_t psParseBufSkipTag(psParseBuf_t *pb, unsigned char tag);
/* Signal errors from sub to master buffer.
If invoked on allocated main parse buffer, the memory will be freed.
If invoked on subbuffer, the position on main buffer is advanced.
The return value will only be PS_SUCCESS is no errors have been observed. */
int32_t psParseBufFinish(psParseBuf_t *buf);
/* Cancel processing of subbuffer.
Errors are not propagated to the master buffer, and master buffer is not
advanced.
If invoked on allocated main parse buffer, any memory allocated will
still be freed. */
void psParseBufCancel(psParseBuf_t *buf);
/* Skip specified bytes (such as tag with specific contents or non-ASN.1 data)*/
size_t psParseBufTrySkipBytes(psParseBuf_t *pb,
const unsigned char *bytes,
size_t numbytes);
/* Skip specified bytes. Not finding the bytes is an error. */
size_t psParseBufSkipBytes(psParseBuf_t *pb, const unsigned char *bytes,
size_t numbytes);
/* Read given tag and fill-in reference with the content.
Parse buffer is moved to point to the next parsing location. */
size_t psParseBufReadTagRef(psParseBuf_t *pb,
psBuf_t *ref, unsigned char tag);
/* Copy data from parse buffer until stopbyte is seen.
The target shall be large enough or NULL to inquire size.
The stopbyte will be included in bytes copied.
This function does not update buffer parsing position. */
int32_t psParseBufCopyUntilByte(psParseBuf_t *pb, unsigned char stopbyte,
unsigned char *target, size_t *targetlen);
/* Copy specified amount of data from parse buffer without moving buffer
parsing position. */
int32_t psParseBufCopyN(const psParseBuf_t *pb, size_t reqLen,
unsigned char *target, size_t *targetlen);
static inline
int32_t psParseBufCopyNPsSize(const psParseBuf_t *pb,
psSize_t reqLen,
unsigned char *target,
psSize_t *targetlen)
{
size_t tLen;
int32_t rc;
tLen = *targetlen;
rc = psParseBufCopyN(pb, reqLen, target, &tLen);
if (tLen > (size_t)PS_SIZE_MAX)
{
return PS_LIMIT_FAIL;
}
*targetlen = tLen;
return rc;
}
#endif /* _h_PS_BUF */
/* end of file psbuf.h */