asn1/x_long.c: remove conditions in inner loops and dependency on BN.
[openssl.git] / crypto / asn1 / x_long.c
1 /*
2  * Copyright 2000-2016 The OpenSSL Project Authors. All Rights Reserved.
3  *
4  * Licensed under the OpenSSL license (the "License").  You may not use
5  * this file except in compliance with the License.  You can obtain a copy
6  * in the file LICENSE in the source distribution or at
7  * https://www.openssl.org/source/license.html
8  */
9
10 #include <stdio.h>
11 #include "internal/cryptlib.h"
12 #include <openssl/asn1t.h>
13
14 /*
15  * Custom primitive type for long handling. This converts between an
16  * ASN1_INTEGER and a long directly.
17  */
18
19 static int long_new(ASN1_VALUE **pval, const ASN1_ITEM *it);
20 static void long_free(ASN1_VALUE **pval, const ASN1_ITEM *it);
21
22 static int long_i2c(ASN1_VALUE **pval, unsigned char *cont, int *putype,
23                     const ASN1_ITEM *it);
24 static int long_c2i(ASN1_VALUE **pval, const unsigned char *cont, int len,
25                     int utype, char *free_cont, const ASN1_ITEM *it);
26 static int long_print(BIO *out, ASN1_VALUE **pval, const ASN1_ITEM *it,
27                       int indent, const ASN1_PCTX *pctx);
28
29 static ASN1_PRIMITIVE_FUNCS long_pf = {
30     NULL, 0,
31     long_new,
32     long_free,
33     long_free,                  /* Clear should set to initial value */
34     long_c2i,
35     long_i2c,
36     long_print
37 };
38
39 ASN1_ITEM_start(LONG)
40         ASN1_ITYPE_PRIMITIVE, V_ASN1_INTEGER, NULL, 0, &long_pf, ASN1_LONG_UNDEF, "LONG"
41 ASN1_ITEM_end(LONG)
42
43 ASN1_ITEM_start(ZLONG)
44         ASN1_ITYPE_PRIMITIVE, V_ASN1_INTEGER, NULL, 0, &long_pf, 0, "ZLONG"
45 ASN1_ITEM_end(ZLONG)
46
47 static int long_new(ASN1_VALUE **pval, const ASN1_ITEM *it)
48 {
49     *(long *)pval = it->size;
50     return 1;
51 }
52
53 static void long_free(ASN1_VALUE **pval, const ASN1_ITEM *it)
54 {
55     *(long *)pval = it->size;
56 }
57
58 /*
59  * Originally BN_num_bits_word was called to perform this operation, but
60  * trouble is that there is no guarantee that sizeof(long) equals to
61  * sizeof(BN_ULONG). BN_ULONG is a configurable type that can be as wide
62  * as long, but also double or half...
63  */
64 static int num_bits_ulong(unsigned long value)
65 {
66     size_t i;
67     unsigned long ret = 0;
68
69     /*
70      * It is argued that *on average* constant counter loop performs
71      * not worse [if not better] than one with conditional break or
72      * mask-n-table-lookup-style, because of branch misprediction
73      * penalties.
74      */
75     for (i = 0; i < sizeof(value) * 8; i++) {
76         ret += (value != 0);
77         value >>= 1;
78     }
79
80     return (int)ret;
81 }
82
83 static int long_i2c(ASN1_VALUE **pval, unsigned char *cont, int *putype,
84                     const ASN1_ITEM *it)
85 {
86     long ltmp;
87     unsigned long utmp, sign;
88     int clen, pad, i;
89     /* this exists to bypass broken gcc optimization */
90     char *cp = (char *)pval;
91
92     /* use memcpy, because we may not be long aligned */
93     memcpy(&ltmp, cp, sizeof(long));
94
95     if (ltmp == it->size)
96         return -1;
97     /*
98      * Convert the long to positive: we subtract one if negative so we can
99      * cleanly handle the padding if only the MSB of the leading octet is
100      * set.
101      */
102     if (ltmp < 0) {
103         sign = 0xff;
104         utmp = 0 - (unsigned long)ltmp - 1;
105     } else {
106         sign = 0;
107         utmp = ltmp;
108     }
109     clen = num_bits_ulong(utmp);
110     /* If MSB of leading octet set we need to pad */
111     if (!(clen & 0x7))
112         pad = 1;
113     else
114         pad = 0;
115
116     /* Convert number of bits to number of octets */
117     clen = (clen + 7) >> 3;
118
119     if (cont != NULL) {
120         if (pad)
121             *cont++ = (unsigned char)sign;
122         for (i = clen - 1; i >= 0; i--) {
123             cont[i] = (unsigned char)(utmp ^ sign);
124             utmp >>= 8;
125         }
126     }
127     return clen + pad;
128 }
129
130 static int long_c2i(ASN1_VALUE **pval, const unsigned char *cont, int len,
131                     int utype, char *free_cont, const ASN1_ITEM *it)
132 {
133     int i;
134     long ltmp;
135     unsigned long utmp = 0, sign = 0x100;
136     char *cp = (char *)pval;
137
138     if (len > 1) {
139         /*
140          * Check possible pad byte.  Worst case, we're skipping past actual
141          * content, but since that's only with 0x00 and 0xff and we set neg
142          * accordingly, the result will be correct in the end anyway.
143          */
144         switch (cont[0]) {
145         case 0xff:
146             cont++;
147             len--;
148             sign = 0xff;
149             break;
150         case 0:
151             cont++;
152             len--;
153             sign = 0;
154             break;
155         }
156     }
157     if (len > (int)sizeof(long)) {
158         ASN1err(ASN1_F_LONG_C2I, ASN1_R_INTEGER_TOO_LARGE_FOR_LONG);
159         return 0;
160     }
161
162     if (sign == 0x100) {
163         /* Is it negative? */
164         if (len && (cont[0] & 0x80))
165             sign = 0xff;
166         else
167             sign = 0;
168     } else if (((sign ^ cont[0]) & 0x80) == 0) { /* same sign bit? */
169         ASN1err(ASN1_F_LONG_C2I, ASN1_R_ILLEGAL_PADDING);
170         return 0;
171     }
172     utmp = 0;
173     for (i = 0; i < len; i++) {
174         utmp <<= 8;
175         utmp |= cont[i] ^ sign;
176     }
177     ltmp = (long)utmp;
178     if (ltmp < 0) {
179         ASN1err(ASN1_F_LONG_C2I, ASN1_R_INTEGER_TOO_LARGE_FOR_LONG);
180         return 0;
181     }
182     if (sign)
183         ltmp = -ltmp - 1;
184     if (ltmp == it->size) {
185         ASN1err(ASN1_F_LONG_C2I, ASN1_R_INTEGER_TOO_LARGE_FOR_LONG);
186         return 0;
187     }
188     memcpy(cp, &ltmp, sizeof(long));
189     return 1;
190 }
191
192 static int long_print(BIO *out, ASN1_VALUE **pval, const ASN1_ITEM *it,
193                       int indent, const ASN1_PCTX *pctx)
194 {
195     return BIO_printf(out, "%ld\n", *(long *)pval);
196 }