make scrypt ASN.1 parameter functions public
[openssl.git] / crypto / asn1 / a_int.c
index 81b021c91e984ba008cc8dabb506ed514564a1a9..217650a036c98b3452f3cb29f594bcd8b6c59080 100644 (file)
@@ -167,10 +167,21 @@ static size_t c2i_ibuf(unsigned char *b, int *pneg,
         }
         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);
@@ -196,56 +207,43 @@ int i2c_ASN1_INTEGER(ASN1_INTEGER *a, unsigned char **pp)
 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,
@@ -255,17 +253,25 @@ 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;
 }
@@ -326,18 +332,22 @@ static int asn1_string_get_int64(int64_t *pr, const ASN1_STRING *a, int itype)
 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,
@@ -361,12 +371,11 @@ 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);
 }
 
 /*
@@ -613,9 +622,9 @@ int c2i_uint64_int(uint64_t *ret, int *neg, const unsigned char **pp, long len)
 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);
 }