| /* |
| * 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; |
| } |
| |