Fixed issue where DRBG_CTR fails if NO_DF is used - when entropy is called
[openssl.git] / crypto / asn1 / x_long.c
index d4b75e6b9aa82a86f68f3a6513105785332217cd..bf9371ef55aaf123821f32a7fc9d9c6016246dfb 100644 (file)
@@ -1,66 +1,21 @@
-/* x_long.c */
 /*
- * Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL project
- * 2000.
- */
-/* ====================================================================
- * Copyright (c) 2000 The OpenSSL Project.  All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- *
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in
- *    the documentation and/or other materials provided with the
- *    distribution.
- *
- * 3. All advertising materials mentioning features or use of this
- *    software must display the following acknowledgment:
- *    "This product includes software developed by the OpenSSL Project
- *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
- *
- * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
- *    endorse or promote products derived from this software without
- *    prior written permission. For written permission, please contact
- *    licensing@OpenSSL.org.
- *
- * 5. Products derived from this software may not be called "OpenSSL"
- *    nor may "OpenSSL" appear in their names without prior written
- *    permission of the OpenSSL Project.
- *
- * 6. Redistributions of any form whatsoever must retain the following
- *    acknowledgment:
- *    "This product includes software developed by the OpenSSL Project
- *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
- *
- * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
- * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
- * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
- * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
- * OF THE POSSIBILITY OF SUCH DAMAGE.
- * ====================================================================
- *
- * This product includes cryptographic software written by Eric Young
- * (eay@cryptsoft.com).  This product includes software written by Tim
- * Hudson (tjh@cryptsoft.com).
+ * Copyright 2000-2017 The OpenSSL Project Authors. All Rights Reserved.
  *
+ * Licensed under the OpenSSL license (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
  */
 
 #include <stdio.h>
 #include "internal/cryptlib.h"
 #include <openssl/asn1t.h>
-#include <openssl/bn.h>
+
+#if !(OPENSSL_API_COMPAT < 0x10200000L)
+NON_EMPTY_TRANSLATION_UNIT
+#else
+
+#define COPY_SIZE(a, b) (sizeof(a) < sizeof(b) ? sizeof(a) : sizeof(b))
 
 /*
  * Custom primitive type for long handling. This converts between an
@@ -97,27 +52,48 @@ ASN1_ITEM_end(ZLONG)
 
 static int long_new(ASN1_VALUE **pval, const ASN1_ITEM *it)
 {
-    *(long *)pval = it->size;
+    memcpy(pval, &it->size, COPY_SIZE(*pval, it->size));
     return 1;
 }
 
 static void long_free(ASN1_VALUE **pval, const ASN1_ITEM *it)
 {
-    *(long *)pval = it->size;
+    memcpy(pval, &it->size, COPY_SIZE(*pval, it->size));
+}
+
+/*
+ * Originally BN_num_bits_word was called to perform this operation, but
+ * trouble is that there is no guarantee that sizeof(long) equals to
+ * sizeof(BN_ULONG). BN_ULONG is a configurable type that can be as wide
+ * as long, but also double or half...
+ */
+static int num_bits_ulong(unsigned long value)
+{
+    size_t i;
+    unsigned long ret = 0;
+
+    /*
+     * It is argued that *on average* constant counter loop performs
+     * not worse [if not better] than one with conditional break or
+     * mask-n-table-lookup-style, because of branch misprediction
+     * penalties.
+     */
+    for (i = 0; i < sizeof(value) * 8; i++) {
+        ret += (value != 0);
+        value >>= 1;
+    }
+
+    return (int)ret;
 }
 
 static int long_i2c(ASN1_VALUE **pval, unsigned char *cont, int *putype,
                     const ASN1_ITEM *it)
 {
     long ltmp;
-    unsigned long utmp;
+    unsigned long utmp, sign;
     int clen, pad, i;
-    /* this exists to bypass broken gcc optimization */
-    char *cp = (char *)pval;
-
-    /* use memcpy, because we may not be long aligned */
-    memcpy(&ltmp, cp, sizeof(long));
 
+    memcpy(&ltmp, pval, COPY_SIZE(*pval, ltmp));
     if (ltmp == it->size)
         return -1;
     /*
@@ -125,11 +101,14 @@ static int long_i2c(ASN1_VALUE **pval, unsigned char *cont, int *putype,
      * cleanly handle the padding if only the MSB of the leading octet is
      * set.
      */
-    if (ltmp < 0)
-        utmp = -ltmp - 1;
-    else
+    if (ltmp < 0) {
+        sign = 0xff;
+        utmp = 0 - (unsigned long)ltmp - 1;
+    } else {
+        sign = 0;
         utmp = ltmp;
-    clen = BN_num_bits_word(utmp);
+    }
+    clen = num_bits_ulong(utmp);
     /* If MSB of leading octet set we need to pad */
     if (!(clen & 0x7))
         pad = 1;
@@ -139,13 +118,11 @@ static int long_i2c(ASN1_VALUE **pval, unsigned char *cont, int *putype,
     /* Convert number of bits to number of octets */
     clen = (clen + 7) >> 3;
 
-    if (cont) {
+    if (cont != NULL) {
         if (pad)
-            *cont++ = (ltmp < 0) ? 0xff : 0;
+            *cont++ = (unsigned char)sign;
         for (i = clen - 1; i >= 0; i--) {
-            cont[i] = (unsigned char)(utmp & 0xff);
-            if (ltmp < 0)
-                cont[i] ^= 0xff;
+            cont[i] = (unsigned char)(utmp ^ sign);
             utmp >>= 8;
         }
     }
@@ -155,42 +132,70 @@ static int long_i2c(ASN1_VALUE **pval, unsigned char *cont, int *putype,
 static int long_c2i(ASN1_VALUE **pval, const unsigned char *cont, int len,
                     int utype, char *free_cont, const ASN1_ITEM *it)
 {
-    int neg, i;
+    int i;
     long ltmp;
-    unsigned long utmp = 0;
-    char *cp = (char *)pval;
+    unsigned long utmp = 0, sign = 0x100;
+
+    if (len > 1) {
+        /*
+         * Check possible pad byte.  Worst case, we're skipping past actual
+         * content, but since that's only with 0x00 and 0xff and we set neg
+         * accordingly, the result will be correct in the end anyway.
+         */
+        switch (cont[0]) {
+        case 0xff:
+            cont++;
+            len--;
+            sign = 0xff;
+            break;
+        case 0:
+            cont++;
+            len--;
+            sign = 0;
+            break;
+        }
+    }
     if (len > (int)sizeof(long)) {
         ASN1err(ASN1_F_LONG_C2I, ASN1_R_INTEGER_TOO_LARGE_FOR_LONG);
         return 0;
     }
-    /* Is it negative? */
-    if (len && (cont[0] & 0x80))
-        neg = 1;
-    else
-        neg = 0;
+
+    if (sign == 0x100) {
+        /* Is it negative? */
+        if (len && (cont[0] & 0x80))
+            sign = 0xff;
+        else
+            sign = 0;
+    } else if (((sign ^ cont[0]) & 0x80) == 0) { /* same sign bit? */
+        ASN1err(ASN1_F_LONG_C2I, ASN1_R_ILLEGAL_PADDING);
+        return 0;
+    }
     utmp = 0;
     for (i = 0; i < len; i++) {
         utmp <<= 8;
-        if (neg)
-            utmp |= cont[i] ^ 0xff;
-        else
-            utmp |= cont[i];
+        utmp |= cont[i] ^ sign;
     }
     ltmp = (long)utmp;
-    if (neg) {
-        ltmp++;
-        ltmp = -ltmp;
+    if (ltmp < 0) {
+        ASN1err(ASN1_F_LONG_C2I, ASN1_R_INTEGER_TOO_LARGE_FOR_LONG);
+        return 0;
     }
+    if (sign)
+        ltmp = -ltmp - 1;
     if (ltmp == it->size) {
         ASN1err(ASN1_F_LONG_C2I, ASN1_R_INTEGER_TOO_LARGE_FOR_LONG);
         return 0;
     }
-    memcpy(cp, &ltmp, sizeof(long));
+    memcpy(pval, &ltmp, COPY_SIZE(*pval, ltmp));
     return 1;
 }
 
 static int long_print(BIO *out, ASN1_VALUE **pval, const ASN1_ITEM *it,
                       int indent, const ASN1_PCTX *pctx)
 {
-    return BIO_printf(out, "%ld\n", *(long *)pval);
+    long l;
+
+    memcpy(&l, pval, COPY_SIZE(*pval, l));
+    return BIO_printf(out, "%ld\n", l);
 }
+#endif