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