123 lines
2.8 KiB
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;
|
||
|
}
|
||
|
|