Make log_clock easier to read.
[gnupg.git] / common / openpgp-oid.c
1 /* openpgp-oids.c - OID helper for OpenPGP
2  * Copyright (C) 2011 Free Software Foundation, Inc.
3  *
4  * This file is part of GnuPG.
5  *
6  * This file is free software; you can redistribute it and/or modify
7  * it under the terms of either
8  *
9  *   - the GNU Lesser General Public License as published by the Free
10  *     Software Foundation; either version 3 of the License, or (at
11  *     your option) any later version.
12  *
13  * or
14  *
15  *   - the GNU General Public License as published by the Free
16  *     Software Foundation; either version 2 of the License, or (at
17  *     your option) any later version.
18  *
19  * or both in parallel, as here.
20  *
21  * This file is distributed in the hope that it will be useful,
22  * but WITHOUT ANY WARRANTY; without even the implied warranty of
23  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
24  * GNU General Public License for more details.
25  *
26  * You should have received a copy of the GNU General Public License
27  * along with this program; if not, see <http://www.gnu.org/licenses/>.
28  */
29
30 #include <config.h>
31 #include <stdlib.h>
32 #include <errno.h>
33 #include <ctype.h>
34 #include <assert.h>
35
36 #include "util.h"
37
38
39 /* Helper for openpgp_oid_from_str.  */
40 static size_t
41 make_flagged_int (unsigned long value, char *buf, size_t buflen)
42 {
43   int more = 0;
44   int shift;
45
46   /* fixme: figure out the number of bits in an ulong and start with
47      that value as shift (after making it a multiple of 7) a more
48      straigtforward implementation is to do it in reverse order using
49      a temporary buffer - saves a lot of compares */
50   for (more=0, shift=28; shift > 0; shift -= 7)
51     {
52       if (more || value >= (1<<shift))
53         {
54           buf[buflen++] = 0x80 | (value >> shift);
55           value -= (value >> shift) << shift;
56           more = 1;
57         }
58     }
59   buf[buflen++] = value;
60   return buflen;
61 }
62
63
64 /* Convert the OID given in dotted decimal form in STRING to an DER
65  * encoding and store it as an opaque value at R_MPI.  The format of
66  * the DER encoded is not a regular ASN.1 object but the modified
67  * format as used by OpenPGP for the ECC curve description.  On error
68  * the function returns and error code an NULL is stored at R_BUG.
69  * Note that scanning STRING stops at the first white space
70  * character.  */
71 gpg_error_t
72 openpgp_oid_from_str (const char *string, gcry_mpi_t *r_mpi)
73 {
74   unsigned char *buf;
75   size_t buflen;
76   unsigned long val1, val;
77   const char *endp;
78   int arcno;
79
80   *r_mpi = NULL;
81
82   if (!string || !*string)
83     return gpg_error (GPG_ERR_INV_VALUE);
84
85   /* We can safely assume that the encoded OID is shorter than the string. */
86   buf = xtrymalloc (1 + strlen (string) + 2);
87   if (!buf)
88     return gpg_error_from_syserror ();
89   /* Save the first byte for the length.  */
90   buflen = 1;
91
92   val1 = 0; /* Avoid compiler warning.  */
93   arcno = 0;
94   do {
95     arcno++;
96     val = strtoul (string, (char**)&endp, 10);
97     if (!digitp (string) || !(*endp == '.' || !*endp))
98       {
99         xfree (buf);
100         return gpg_error (GPG_ERR_INV_OID_STRING);
101       }
102     if (*endp == '.')
103       string = endp+1;
104
105     if (arcno == 1)
106       {
107         if (val > 2)
108           break; /* Not allowed, error catched below.  */
109         val1 = val;
110       }
111     else if (arcno == 2)
112       { /* Need to combine the first two arcs in one octet.  */
113         if (val1 < 2)
114           {
115             if (val > 39)
116               {
117                 xfree (buf);
118                 return gpg_error (GPG_ERR_INV_OID_STRING);
119               }
120             buf[buflen++] = val1*40 + val;
121           }
122         else
123           {
124             val += 80;
125             buflen = make_flagged_int (val, buf, buflen);
126           }
127       }
128     else
129       {
130         buflen = make_flagged_int (val, buf, buflen);
131       }
132   } while (*endp == '.');
133
134   if (arcno == 1 || buflen < 2 || buflen > 254 )
135     { /* It is not possible to encode only the first arc.  */
136       xfree (buf);
137       return gpg_error (GPG_ERR_INV_OID_STRING);
138     }
139
140   *buf = buflen - 1;
141   *r_mpi = gcry_mpi_set_opaque (NULL, buf, buflen * 8);
142   if (!*r_mpi)
143     {
144       xfree (buf);
145       return gpg_error_from_syserror ();
146     }
147   return 0;
148 }
149
150
151 /* Return a malloced string represenation of the OID in the opaque MPI
152    A.  In case of an error NULL is returned and ERRNO is set.  */
153 char *
154 openpgp_oid_to_str (gcry_mpi_t a)
155 {
156   const unsigned char *buf;
157   size_t length;
158   unsigned int lengthi;
159   char *string, *p;
160   int n = 0;
161   unsigned long val, valmask;
162
163   valmask = (unsigned long)0xfe << (8 * (sizeof (valmask) - 1));
164
165   if (!a || !gcry_mpi_get_flag (a, GCRYMPI_FLAG_OPAQUE))
166     {
167       gpg_err_set_errno (EINVAL);
168       return NULL;
169     }
170
171   buf = gcry_mpi_get_opaque (a, &lengthi);
172   length = (lengthi+7)/8;
173
174   /* The first bytes gives the length; check consistency.  */
175   if (!length || buf[0] != length -1)
176     {
177       gpg_err_set_errno (EINVAL);
178       return NULL;
179     }
180   /* Skip length byte.  */
181   length--;
182   buf++;
183
184   /* To calculate the length of the string we can safely assume an
185      upper limit of 3 decimal characters per byte.  Two extra bytes
186      account for the special first octect */
187   string = p = xtrymalloc (length*(1+3)+2+1);
188   if (!string)
189     return NULL;
190   if (!buf || !length)
191     {
192       *p = 0;
193       return string;
194     }
195
196   if (buf[0] < 40)
197     p += sprintf (p, "0.%d", buf[n]);
198   else if (buf[0] < 80)
199     p += sprintf (p, "1.%d", buf[n]-40);
200   else {
201     val = buf[n] & 0x7f;
202     while ( (buf[n]&0x80) && ++n < length )
203       {
204         if ( (val & valmask) )
205           goto badoid;  /* Overflow.  */
206         val <<= 7;
207         val |= buf[n] & 0x7f;
208       }
209     val -= 80;
210     sprintf (p, "2.%lu", val);
211     p += strlen (p);
212   }
213   for (n++; n < length; n++)
214     {
215       val = buf[n] & 0x7f;
216       while ( (buf[n]&0x80) && ++n < length )
217         {
218           if ( (val & valmask) )
219             goto badoid;  /* Overflow.  */
220           val <<= 7;
221           val |= buf[n] & 0x7f;
222         }
223       sprintf (p, ".%lu", val);
224       p += strlen (p);
225     }
226
227   *p = 0;
228   return string;
229
230  badoid:
231   /* Return a special OID (gnu.gnupg.badoid) to indicate the error
232      case.  The OID is broken and thus we return one which can't do
233      any harm.  Formally this does not need to be a bad OID but an OID
234      with an arc that can't be represented in a 32 bit word is more
235      than likely corrupt.  */
236   xfree (string);
237   return xtrystrdup ("1.3.6.1.4.1.11591.2.12242973");
238 }