}
return 1;
}
- if (p[0] == 0 || p[0] == 0xFF)
+
+ pad = 0;
+ if (p[0] == 0) {
pad = 1;
- else
- pad = 0;
+ } else if (p[0] == 0xFF) {
+ size_t i;
+
+ /*
+ * Special case [of "one less minimal negative" for given length]:
+ * if any other bytes non zero it was padded, otherwise not.
+ */
+ for (pad = 0, i = 1; i < plen; i++)
+ pad |= p[i];
+ pad = pad != 0 ? 1 : 0;
+ }
/* reject illegal padding: first two octets MSB can't match */
if (pad && (neg == (p[1] & 0x80))) {
ASN1err(ASN1_F_C2I_IBUF, ASN1_R_ILLEGAL_PADDING);
static int asn1_get_uint64(uint64_t *pr, const unsigned char *b, size_t blen)
{
size_t i;
+ uint64_t r;
+
if (blen > sizeof(*pr)) {
ASN1err(ASN1_F_ASN1_GET_UINT64, ASN1_R_TOO_LARGE);
return 0;
}
- *pr = 0;
if (b == NULL)
return 0;
- for (i = 0; i < blen; i++) {
- *pr <<= 8;
- *pr |= b[i];
+ for (r = 0, i = 0; i < blen; i++) {
+ r <<= 8;
+ r |= b[i];
}
+ *pr = r;
return 1;
}
-static size_t asn1_put_uint64(unsigned char *b, uint64_t r)
+/*
+ * Write uint64_t to big endian buffer and return offset to first
+ * written octet. In other words it returns offset in range from 0
+ * to 7, with 0 denoting 8 written octets and 7 - one.
+ */
+static size_t asn1_put_uint64(unsigned char b[sizeof(uint64_t)], uint64_t r)
{
- if (r >= 0x100) {
- unsigned char *p;
- uint64_t rtmp = r;
- size_t i = 0;
-
- /* Work out how many bytes we need */
- while (rtmp) {
- rtmp >>= 8;
- i++;
- }
-
- /* Copy from end to beginning */
- p = b + i - 1;
+ size_t off = sizeof(uint64_t);
- do {
- *p-- = r & 0xFF;
- r >>= 8;
- } while (p >= b);
-
- return i;
- }
-
- b[0] = (unsigned char)r;
- return 1;
+ do {
+ b[--off] = (unsigned char)r;
+ } while (r >>= 8);
+ return off;
}
/*
- * Absolute value of INT64_MIN: we can't just use -INT64_MIN as it produces
+ * Absolute value of INT64_MIN: we can't just use -INT64_MIN as gcc produces
* overflow warnings.
*/
-
-#define ABS_INT64_MIN \
- ((uint64_t)INT64_MAX + (uint64_t)(-(INT64_MIN + INT64_MAX)))
+#define ABS_INT64_MIN ((uint64_t)INT64_MAX + (-(INT64_MIN + INT64_MAX)))
/* signed version of asn1_get_uint64 */
static int asn1_get_int64(int64_t *pr, const unsigned char *b, size_t blen,
if (asn1_get_uint64(&r, b, blen) == 0)
return 0;
if (neg) {
- if (r > ABS_INT64_MIN) {
+ if (r <= INT64_MAX) {
+ /* Most significant bit is guaranteed to be clear, negation
+ * is guaranteed to be meaningful in platform-neutral sense. */
+ *pr = -(int64_t)r;
+ } else if (r == ABS_INT64_MIN) {
+ /* This never happens if INT64_MAX == ABS_INT64_MIN, e.g.
+ * on ones'-complement system. */
+ *pr = (int64_t)(0 - r);
+ } else {
ASN1err(ASN1_F_ASN1_GET_INT64, ASN1_R_TOO_SMALL);
return 0;
}
- *pr = 0 - (uint64_t)r;
} else {
- if (r > INT64_MAX) {
+ if (r <= INT64_MAX) {
+ *pr = (int64_t)r;
+ } else {
ASN1err(ASN1_F_ASN1_GET_INT64, ASN1_R_TOO_LARGE);
return 0;
}
- *pr = (int64_t)r;
}
return 1;
}
static int asn1_string_set_int64(ASN1_STRING *a, int64_t r, int itype)
{
unsigned char tbuf[sizeof(r)];
- size_t l;
+ size_t off;
+
a->type = itype;
if (r < 0) {
- l = asn1_put_uint64(tbuf, -r);
+ /* Most obvious '-r' triggers undefined behaviour for most
+ * common INT64_MIN. Even though below '0 - (uint64_t)r' can
+ * appear two's-complement centric, it does produce correct/
+ * expected result even on one's-complement. This is because
+ * cast to unsigned has to change bit pattern... */
+ off = asn1_put_uint64(tbuf, 0 - (uint64_t)r);
a->type |= V_ASN1_NEG;
} else {
- l = asn1_put_uint64(tbuf, r);
+ off = asn1_put_uint64(tbuf, r);
a->type &= ~V_ASN1_NEG;
}
- if (l == 0)
- return 0;
- return ASN1_STRING_set(a, tbuf, l);
+ return ASN1_STRING_set(a, tbuf + off, sizeof(tbuf) - off);
}
static int asn1_string_get_uint64(uint64_t *pr, const ASN1_STRING *a,
static int asn1_string_set_uint64(ASN1_STRING *a, uint64_t r, int itype)
{
unsigned char tbuf[sizeof(r)];
- size_t l;
+ size_t off;
+
a->type = itype;
- l = asn1_put_uint64(tbuf, r);
- if (l == 0)
- return 0;
- return ASN1_STRING_set(a, tbuf, l);
+ off = asn1_put_uint64(tbuf, r);
+ return ASN1_STRING_set(a, tbuf + off, sizeof(tbuf) - off);
}
/*
int i2c_uint64_int(unsigned char *p, uint64_t r, int neg)
{
unsigned char buf[sizeof(uint64_t)];
- size_t buflen;
+ size_t off;
- buflen = asn1_put_uint64(buf, r);
- return i2c_ibuf(buf, buflen, neg, &p);
+ off = asn1_put_uint64(buf, r);
+ return i2c_ibuf(buf + off, sizeof(buf) - off, neg, &p);
}