asn1c/skeletons/oer_support.c

123 lines
2.8 KiB
C

/*
* Copyright (c) 2017 Lev Walkin <vlm@lionet.info>.
* All rights reserved.
* Redistribution and modifications are permitted subject to BSD license.
*/
#include <asn_system.h>
#include <asn_internal.h>
#include <oer_support.h>
/*
* Fetch the length determinant (X.696 08/2015, #8.6) into *len_r.
* RETURN VALUES:
* 0: More data expected than bufptr contains.
* -1: Fatal error deciphering length.
* >0: Number of bytes used from bufptr.
*/
ssize_t
oer_fetch_length(const void *bufptr, size_t size, size_t *len_r) {
uint8_t first_byte;
size_t len_len; /* Length of the length determinant */
const uint8_t *b;
const uint8_t *bend;
size_t len;
if(size == 0) {
*len_r = 0;
return 0;
}
first_byte = *(const uint8_t *)bufptr;
if((first_byte & 0x80) == 0) { /* Short form */
*len_r = first_byte; /* 0..127 */
return 1;
}
len_len = (first_byte & 0x7f);
if((1 + len_len) > size) {
*len_r = 0;
return 0;
}
b = (const uint8_t *)bufptr + 1;
bend = b + len_len;
for(; b < bend && *b == 0; b++) {
/* Skip the leading 0-bytes */
}
if((bend - b) > (ssize_t)sizeof(size_t)) {
/* Length is not representable by the native size_t type */
*len_r = 0;
return -1;
}
for(len = 0; b < bend; b++) {
len = (len << 8) + *b;
}
if(len > RSIZE_MAX) { /* A bit of C11 validation */
*len_r = 0;
return -1;
}
*len_r = len;
assert(len_len + 1 == (size_t)(bend - (const uint8_t *)bufptr));
return len_len + 1;
}
/*
* Serialize OER length. Returns the number of bytes serialized
* or -1 if a given callback returned with negative result.
*/
ssize_t
oer_serialize_length(size_t length, asn_app_consume_bytes_f *cb,
void *app_key) {
uint8_t scratch[1 + sizeof(length)];
uint8_t *sp = scratch;
int littleEndian = 1; /* Run-time detection */
const uint8_t *pstart;
const uint8_t *pend;
const uint8_t *p;
int add;
if(length <= 127) {
uint8_t b = length;
if(cb(&b, 1, app_key) < 0) {
return -1;
}
return 1;
}
if(*(char *)&littleEndian) {
pstart = (const uint8_t *)&length + sizeof(length) - 1;
pend = (const uint8_t *)&length;
add = -1;
} else {
pstart = (const uint8_t *)&length;
pend = pstart + sizeof(length);
add = 1;
}
for(p = pstart; p != pend; p += add) {
/* Skip leading zeros. */
if(*p) break;
}
for(sp = scratch + 1; ; p += add) {
*sp++ = *p;
if(p == pend) break;
}
assert((sp - scratch) - 1 <= 0x7f);
scratch[0] = 0x80 + ((sp - scratch) - 1);
if(cb(scratch, sp - scratch, app_key) < 0) {
return -1;
}
return sp - scratch;
}