diff options
Diffstat (limited to 'src/per_support.c')
-rw-r--r-- | src/per_support.c | 307 |
1 files changed, 280 insertions, 27 deletions
diff --git a/src/per_support.c b/src/per_support.c index 173e696..d536bc0 100644 --- a/src/per_support.c +++ b/src/per_support.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2006, 2007 Lev Walkin <vlm@lionet.info>. + * Copyright (c) 2005-2014 Lev Walkin <vlm@lionet.info>. * All rights reserved. * Redistribution and modifications are permitted subject to BSD license. */ @@ -13,11 +13,11 @@ per_data_string(asn_per_data_t *pd) { static int n; n = (n+1) % 2; snprintf(buf[n], sizeof(buf), - "{m=%d span %+d[%d..%d] (%d)}", - pd->moved, - (((int)pd->buffer) & 0xf), - pd->nboff, pd->nbits, - pd->nbits - pd->nboff); + "{m=%ld span %+ld[%d..%d] (%d)}", + (long)pd->moved, + (((long)pd->buffer) & 0xf), + (int)pd->nboff, (int)pd->nbits, + (int)(pd->nbits - pd->nboff)); return buf[n]; } @@ -31,6 +31,16 @@ per_get_undo(asn_per_data_t *pd, int nbits) { } } +int32_t +aper_get_align(asn_per_data_t *pd) { + + if(pd->nboff & 0x7) { + ASN_DEBUG("Aligning %d bits", 8 - (pd->nboff & 0x7)); + return per_get_few_bits(pd, 8 - (pd->nboff & 0x7)); + } + return 0; +} + /* * Extract a small number of bits (<= 31) from the specified PER data pointer. */ @@ -49,7 +59,8 @@ per_get_few_bits(asn_per_data_t *pd, int nbits) { int32_t tailv, vhead; if(!pd->refill || nbits > 31) return -1; /* Accumulate unused bytes before refill */ - ASN_DEBUG("Obtain the rest %d bits (want %d)", nleft, nbits); + ASN_DEBUG("Obtain the rest %d bits (want %d)", + (int)nleft, (int)nbits); tailv = per_get_few_bits(pd, nleft); if(tailv < 0) return -1; /* Refill (replace pd contents with new data) */ @@ -103,13 +114,13 @@ per_get_few_bits(asn_per_data_t *pd, int nbits) { accum &= (((uint32_t)1 << nbits) - 1); - ASN_DEBUG(" [PER got %2d<=%2d bits => span %d %+d[%d..%d]:%02x (%d) => 0x%x]", - nbits, nleft, - pd->moved, - (((int)pd->buffer) & 0xf), - pd->nboff, pd->nbits, + ASN_DEBUG(" [PER got %2d<=%2d bits => span %d %+ld[%d..%d]:%02x (%d) => 0x%02x]", + (int)nbits, (int)nleft, + (int)pd->moved, + (((long)pd->buffer) & 0xf), + (int)pd->nboff, (int)pd->nbits, pd->buffer[0], - pd->nbits - pd->nboff, + (int)(pd->nbits - pd->nboff), (int)accum); return accum; @@ -122,6 +133,8 @@ int per_get_many_bits(asn_per_data_t *pd, uint8_t *dst, int alright, int nbits) { int32_t value; + ASN_DEBUG("align: %s, nbits %d", alright ? "YES":"NO", nbits); + if(alright && (nbits & 7)) { /* Perform right alignment of a first few bits */ value = per_get_few_bits(pd, nbits & 0x07); @@ -186,6 +199,36 @@ uper_get_length(asn_per_data_t *pd, int ebits, int *repeat) { return (16384 * value); } +ssize_t +aper_get_length(asn_per_data_t *pd, int range, int ebits, int *repeat) { + ssize_t value; + + *repeat = 0; + + if (range <= 65536 && range >= 0) + return aper_get_nsnnwn(pd, range); + + if (aper_get_align(pd) < 0) + return -1; + + if(ebits >= 0) return per_get_few_bits(pd, ebits); + + value = per_get_few_bits(pd, 8); + if(value < 0) return -1; + if((value & 128) == 0) /* #10.9.3.6 */ + return (value & 0x7F); + if((value & 64) == 0) { /* #10.9.3.7 */ + value = ((value & 63) << 8) | per_get_few_bits(pd, 8); + if(value < 0) return -1; + return value; + } + value &= 63; /* this is "m" from X.691, #10.9.3.8 */ + if(value < 1 || value > 4) + return -1; + *repeat = 1; + return (16384 * value); +} + /* * Get the normally small length "n". * This procedure used to decode length of extensions bit-maps @@ -200,7 +243,7 @@ uper_get_nslength(asn_per_data_t *pd) { if(per_get_few_bits(pd, 1) == 0) { length = per_get_few_bits(pd, 6) + 1; if(length <= 0) return -1; - ASN_DEBUG("l=%d", length); + ASN_DEBUG("l=%d", (int)length); return length; } else { int repeat; @@ -210,6 +253,25 @@ uper_get_nslength(asn_per_data_t *pd) { } } +ssize_t +aper_get_nslength(asn_per_data_t *pd) { + ssize_t length; + + ASN_DEBUG("Getting normally small length"); + + if(per_get_few_bits(pd, 1) == 0) { + length = per_get_few_bits(pd, 6) + 1; + if(length <= 0) return -1; + ASN_DEBUG("l=%d", length); + return length; + } else { + int repeat; + length = aper_get_length(pd, -1, -1, &repeat); + if(length >= 0 && !repeat) return length; + return -1; /* Error, or do not support >16K extensions */ + } +} + /* * Get the normally small non-negative whole number. * X.691, #10.6 @@ -236,14 +298,49 @@ uper_get_nsnnwn(asn_per_data_t *pd) { return value; } +ssize_t +aper_get_nsnnwn(asn_per_data_t *pd, int range) { + ssize_t value; + int bytes = 0; + + ASN_DEBUG("getting nsnnwn with range %d", range); + + if(range <= 255) { + if (range < 0) return -1; + /* 1 -> 8 bits */ + int i; + for (i = 1; i <= 8; i++) { + int upper = 1 << i; + if (upper >= range) + break; + } + value = per_get_few_bits(pd, i); + return value; + } else if (range == 256){ + /* 1 byte */ + bytes = 1; + return -1; + } else if (range <= 65536) { + /* 2 bytes */ + bytes = 2; + } else { + return -1; + } + if (aper_get_align(pd) < 0) + return -1; + value = per_get_few_bits(pd, 8 * bytes); + return value; +} + /* - * Put the normally small non-negative whole number. - * X.691, #10.6 + * X.691-11/2008, #11.6 + * Encoding of a normally small non-negative whole number */ int uper_put_nsnnwn(asn_per_outp_t *po, int n) { int bytes; + ASN_DEBUG("uper put nsnnwn n %d", n); if(n <= 63) { if(n < 0) return -1; return per_put_few_bits(po, n, 7); @@ -263,6 +360,58 @@ uper_put_nsnnwn(asn_per_outp_t *po, int n) { } +/* X.691-2008/11, #11.5.6 -> #11.3 */ +int uper_get_constrained_whole_number(asn_per_data_t *pd, unsigned long *out_value, int nbits) { + unsigned long lhalf; /* Lower half of the number*/ + long half; + + if(nbits <= 31) { + half = per_get_few_bits(pd, nbits); + if(half < 0) return -1; + *out_value = half; + return 0; + } + + if((size_t)nbits > 8 * sizeof(*out_value)) + return -1; /* RANGE */ + + half = per_get_few_bits(pd, 31); + if(half < 0) return -1; + + if(uper_get_constrained_whole_number(pd, &lhalf, nbits - 31)) + return -1; + + *out_value = ((unsigned long)half << (nbits - 31)) | lhalf; + return 0; +} + + +/* X.691-2008/11, #11.5.6 -> #11.3 */ +int uper_put_constrained_whole_number_s(asn_per_outp_t *po, long v, int nbits) { + /* + * Assume signed number can be safely coerced into + * unsigned of the same range. + * The following testing code will likely be optimized out + * by compiler if it is true. + */ + unsigned long uvalue1 = ULONG_MAX; + long svalue = uvalue1; + unsigned long uvalue2 = svalue; + assert(uvalue1 == uvalue2); + return uper_put_constrained_whole_number_u(po, v, nbits); +} + +int uper_put_constrained_whole_number_u(asn_per_outp_t *po, unsigned long v, int nbits) { + if(nbits <= 31) { + return per_put_few_bits(po, v, nbits); + } else { + /* Put higher portion first, followed by lower 31-bit */ + if(uper_put_constrained_whole_number_u(po, v >> 31, nbits - 31)) + return -1; + return per_put_few_bits(po, v, 31); + } +} + /* * Put a small number of bits (<= 31). */ @@ -275,7 +424,7 @@ per_put_few_bits(asn_per_outp_t *po, uint32_t bits, int obits) { if(obits <= 0 || obits >= 32) return obits ? -1 : 0; ASN_DEBUG("[PER put %d bits %x to %p+%d bits]", - obits, (int)bits, po->buffer, po->nboff); + obits, (int)bits, po->buffer, (int)po->nboff); /* * Normalize position indicator. @@ -291,8 +440,8 @@ per_put_few_bits(asn_per_outp_t *po, uint32_t bits, int obits) { */ if(po->nboff + obits > po->nbits) { int complete_bytes = (po->buffer - po->tmpspace); - ASN_DEBUG("[PER output %d complete + %d]", - complete_bytes, po->flushed_bytes); + ASN_DEBUG("[PER output %ld complete + %ld]", + (long)complete_bytes, (long)po->flushed_bytes); if(po->outper(po->tmpspace, complete_bytes, po->op_key) < 0) return -1; if(po->nboff) @@ -307,47 +456,106 @@ per_put_few_bits(asn_per_outp_t *po, uint32_t bits, int obits) { */ buf = po->buffer; omsk = ~((1 << (8 - po->nboff)) - 1); - off = (po->nboff += obits); + off = (po->nboff + obits); /* Clear data of debris before meaningful bits */ bits &= (((uint32_t)1 << obits) - 1); ASN_DEBUG("[PER out %d %u/%x (t=%d,o=%d) %x&%x=%x]", obits, (int)bits, (int)bits, - po->nboff - obits, off, buf[0], omsk&0xff, buf[0] & omsk); + (int)po->nboff, (int)off, + buf[0], (int)(omsk&0xff), + (int)(buf[0] & omsk)); if(off <= 8) /* Completely within 1 byte */ + po->nboff = off, bits <<= (8 - off), buf[0] = (buf[0] & omsk) | bits; else if(off <= 16) + po->nboff = off, bits <<= (16 - off), buf[0] = (buf[0] & omsk) | (bits >> 8), buf[1] = bits; else if(off <= 24) + po->nboff = off, bits <<= (24 - off), buf[0] = (buf[0] & omsk) | (bits >> 16), buf[1] = bits >> 8, buf[2] = bits; else if(off <= 31) + po->nboff = off, bits <<= (32 - off), buf[0] = (buf[0] & omsk) | (bits >> 24), buf[1] = bits >> 16, buf[2] = bits >> 8, buf[3] = bits; else { - ASN_DEBUG("->[PER out split %d]", obits); - po->nboff -= obits; /* undo incrementation from a few lines above */ - per_put_few_bits(po, bits >> (obits - 24), 24); /* shift according to the rest of the bits */ + per_put_few_bits(po, bits >> (obits - 24), 24); per_put_few_bits(po, bits, obits - 24); - ASN_DEBUG("<-[PER out split %d]", obits); } - ASN_DEBUG("[PER out %u/%x => %02x buf+%d]", - (int)bits, (int)bits, buf[0], po->buffer - po->tmpspace); + ASN_DEBUG("[PER out %u/%x => %02x buf+%ld]", + (int)bits, (int)bits, buf[0], + (long)(po->buffer - po->tmpspace)); return 0; } +int +aper_put_nsnnwn(asn_per_outp_t *po, int range, int number) { + int bytes; + + ASN_DEBUG("aper put nsnnwn %d with range %d", number, range); + /* 10.5.7.1 X.691 */ + if(range < 0) { + int i; + for (i = 1; ; i++) { + int bits = 1 << (8 * i); + if (number <= bits) + break; + } + bytes = i; + assert(i <= 4); + } + if(range <= 255) { + int i; + for (i = 1; i <= 8; i++) { + int bits = 1 << i; + if (range <= bits) + break; + } + return per_put_few_bits(po, number, i); + } else if(range == 256) { + bytes = 1; + } else if(range <= 65536) { + bytes = 2; + } else { /* Ranges > 64K */ + int i; + for (i = 1; ; i++) { + int bits = 1 << (8 * i); + if (range <= bits) + break; + } + assert(i <= 4); + bytes = i; + } + if(aper_put_align(po) < 0) /* Aligning on octet */ + return -1; +// if(per_put_few_bits(po, bytes, 8)) +// return -1; + + return per_put_few_bits(po, number, 8 * bytes); +} + +int aper_put_align(asn_per_outp_t *po) { + + if(po->nboff & 0x7) { + ASN_DEBUG("Aligning %d bits", 8 - (po->nboff & 0x7)); + if(per_put_few_bits(po, 0x00, (8 - (po->nboff & 0x7)))) + return -1; + } + return 0; +} /* * Output a large number of bits. @@ -387,6 +595,8 @@ per_put_many_bits(asn_per_outp_t *po, const uint8_t *src, int nbits) { ssize_t uper_put_length(asn_per_outp_t *po, size_t length) { + ASN_DEBUG("UPER put length %d", length); + if(length <= 127) /* #10.9.3.6 */ return per_put_few_bits(po, length, 8) ? -1 : (ssize_t)length; @@ -401,6 +611,33 @@ uper_put_length(asn_per_outp_t *po, size_t length) { ? -1 : (ssize_t)(length << 14); } +ssize_t +aper_put_length(asn_per_outp_t *po, int range, size_t length) { + + ASN_DEBUG("APER put length %d with range %d", length, range); + + /* 10.9 X.691 Note 2 */ + if (range <= 65536 && range >= 0) + return aper_put_nsnnwn(po, range, length); + + if (aper_put_align(po) < 0) + return -1; + + if(length <= 127) /* #10.9.3.6 */{ + return per_put_few_bits(po, length, 8) + ? -1 : (ssize_t)length; + } + else if(length < 16384) /* #10.9.3.7 */ + return per_put_few_bits(po, length|0x8000, 16) + ? -1 : (ssize_t)length; + + length >>= 14; + if(length > 4) length = 4; + + return per_put_few_bits(po, 0xC0 | length, 8) + ? -1 : (ssize_t)(length << 14); +} + /* * Put the normally small length "n" into the stream. @@ -424,3 +661,19 @@ uper_put_nslength(asn_per_outp_t *po, size_t length) { return 0; } +int +aper_put_nslength(asn_per_outp_t *po, size_t length) { + + if(length <= 64) { + /* #10.9.3.4 */ + if(length == 0) return -1; + return per_put_few_bits(po, length-1, 7) ? -1 : 0; + } else { + if(aper_put_length(po, -1, length) != (ssize_t)length) { + /* This might happen in case of >16K extensions */ + return -1; + } + } + + return 0; +} |