1 /* crypto.c: crytography-related functions
2    Copyright (c) 2002-2005 Philip Kendall
3 
4    $Id: crypto.c 3698 2008-06-30 15:12:02Z pak21 $
5 
6    This program 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 2 of the License, or
9    (at your option) any later version.
10 
11    This program 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 along
17    with this program; if not, write to the Free Software Foundation, Inc.,
18    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 
20    Author contact information:
21 
22    E-mail: philip-fuse@shadowmagic.org.uk
23 
24 */
25 
26 #include <config.h>
27 
28 #ifdef HAVE_GCRYPT_H
29 
30 #include <gcrypt.h>
31 
32 #include "internals.h"
33 
34 static const char *private_key_format =
35   "(key-data (private-key (dsa (p %m) (q %m) (g %m) (y %m) (x %m))))";
36 static const char *public_key_format =
37   "(key-data (public-key (dsa (p %m) (q %m) (g %m) (y %m))))";
38 static const char *hash_format = "(data (flags raw) (value %m))";
39 static const char *signature_format = "(sig-val (dsa (r %m) (s %m)))";
40 
41 #define HASH_ALGORITHM GCRY_MD_SHA1
42 #define MPI_COUNT 5
43 
44 static libspectrum_error
45 get_signature( gcry_mpi_t *r, gcry_mpi_t *s, libspectrum_byte *data,
46 	       size_t data_length, libspectrum_rzx_dsa_key *key );
47 static libspectrum_error
48 get_hash( gcry_sexp_t *hash, const libspectrum_byte *data,
49 	  size_t data_length );
50 static libspectrum_error
51 create_key( gcry_sexp_t *s_key, libspectrum_rzx_dsa_key *key, int secret_key);
52 static void free_mpis( gcry_mpi_t *mpis, size_t n );
53 static libspectrum_error get_mpi( gcry_mpi_t *mpi, gcry_sexp_t signature,
54 				  const char *token );
55 static libspectrum_error
56 serialise_mpis( libspectrum_byte **signature, size_t *signature_length,
57 		gcry_mpi_t r, gcry_mpi_t s );
58 
59 libspectrum_error
libspectrum_sign_data(libspectrum_byte ** signature,size_t * signature_length,libspectrum_byte * data,size_t data_length,libspectrum_rzx_dsa_key * key)60 libspectrum_sign_data( libspectrum_byte **signature, size_t *signature_length,
61 		       libspectrum_byte *data, size_t data_length,
62 		       libspectrum_rzx_dsa_key *key )
63 {
64   int error;
65   gcry_mpi_t r, s;
66 
67   error = get_signature( &r, &s, data, data_length, key );
68   if( error ) return error;
69 
70   error = serialise_mpis( signature, signature_length, r, s );
71   if( error ) { gcry_mpi_release( r ); gcry_mpi_release( s ); return error; }
72 
73   gcry_mpi_release( r ); gcry_mpi_release( s );
74 
75   return LIBSPECTRUM_ERROR_NONE;
76 }
77 
78 static libspectrum_error
get_signature(gcry_mpi_t * r,gcry_mpi_t * s,libspectrum_byte * data,size_t data_length,libspectrum_rzx_dsa_key * key)79 get_signature( gcry_mpi_t *r, gcry_mpi_t *s, libspectrum_byte *data,
80 	       size_t data_length, libspectrum_rzx_dsa_key *key )
81 {
82   libspectrum_error error;
83   gcry_error_t gcrypt_error;
84   gcry_sexp_t hash, s_key, s_signature;
85 
86   error = get_hash( &hash, data, data_length ); if( error ) return error;
87 
88   error = create_key( &s_key, key, 1 );
89   if( error ) { gcry_sexp_release( hash ); return error; }
90 
91   gcrypt_error = gcry_pk_sign( &s_signature, hash, s_key );
92   if( gcrypt_error ) {
93     libspectrum_print_error( LIBSPECTRUM_ERROR_LOGIC,
94 			     "get_signature: error signing data: %s",
95 			     gcry_strerror( gcrypt_error ) );
96     gcry_sexp_release( s_key ); gcry_sexp_release( hash );
97     return LIBSPECTRUM_ERROR_LOGIC;
98   }
99 
100   gcry_sexp_release( s_key ); gcry_sexp_release( hash );
101 
102   error = get_mpi( r, s_signature, "r" );
103   if( error ) { gcry_sexp_release( s_signature ); return error; }
104   error = get_mpi( s, s_signature, "s" );
105   if( error ) {
106     gcry_sexp_release( s_signature ); gcry_mpi_release( *r );
107     return error;
108   }
109 
110   gcry_sexp_release( s_signature );
111 
112   return LIBSPECTRUM_ERROR_NONE;
113 }
114 
115 static libspectrum_error
get_hash(gcry_sexp_t * hash,const libspectrum_byte * data,size_t data_length)116 get_hash( gcry_sexp_t *hash, const libspectrum_byte *data, size_t data_length )
117 {
118   gcry_error_t error;
119   unsigned char *digest; size_t digest_length;
120   gcry_mpi_t hash_mpi;
121 
122   digest_length = gcry_md_get_algo_dlen( HASH_ALGORITHM );
123   digest = libspectrum_malloc( digest_length );
124 
125   gcry_md_hash_buffer( HASH_ALGORITHM, digest, data, data_length );
126 
127   error = gcry_mpi_scan( &hash_mpi, GCRYMPI_FMT_USG, digest, digest_length,
128 			 NULL );
129   if( error ) {
130     libspectrum_print_error( LIBSPECTRUM_ERROR_LOGIC,
131 			     "get_hash: error creating hash MPI: %s",
132 			     gcry_strerror( error )
133     );
134     libspectrum_free( digest );
135     return LIBSPECTRUM_ERROR_LOGIC;
136   }
137 
138   libspectrum_free( digest );
139 
140   error = gcry_sexp_build( hash, NULL, hash_format, hash_mpi );
141   if( error ) {
142     libspectrum_print_error( LIBSPECTRUM_ERROR_LOGIC,
143 			     "get_hash: error creating hash sexp: %s",
144 			     gcry_strerror( error )
145     );
146     gcry_mpi_release( hash_mpi );
147     return LIBSPECTRUM_ERROR_LOGIC;
148   }
149 
150   gcry_mpi_release( hash_mpi );
151 
152   return LIBSPECTRUM_ERROR_NONE;
153 }
154 
155 static libspectrum_error
create_key(gcry_sexp_t * s_key,libspectrum_rzx_dsa_key * key,int secret_key)156 create_key( gcry_sexp_t *s_key, libspectrum_rzx_dsa_key *key,
157 	    int secret_key )
158 {
159   gcry_error_t error;
160   size_t i;
161   gcry_mpi_t mpis[MPI_COUNT];
162   const char *format;
163 
164   for( i=0; i<MPI_COUNT; i++ ) mpis[i] = NULL;
165 
166     error = gcry_mpi_scan( &mpis[0], GCRYMPI_FMT_HEX, (unsigned char*)key->p,
167 			   0, NULL );
168   if( !error )
169     error = gcry_mpi_scan( &mpis[1], GCRYMPI_FMT_HEX, (unsigned char*)key->q,
170 			   0, NULL );
171   if( !error )
172     error = gcry_mpi_scan( &mpis[2], GCRYMPI_FMT_HEX, (unsigned char*)key->g,
173 			   0, NULL );
174   if( !error )
175     error = gcry_mpi_scan( &mpis[3], GCRYMPI_FMT_HEX, (unsigned char*)key->y,
176 			   0, NULL );
177   if( !error && secret_key )
178     error = gcry_mpi_scan( &mpis[4], GCRYMPI_FMT_HEX, (unsigned char*)key->x,
179 			   0, NULL );
180 
181   if( error ) {
182     libspectrum_print_error( LIBSPECTRUM_ERROR_LOGIC,
183 			     "create_key: error creating MPIs: %s",
184 			     gcry_strerror( error ) );
185     free_mpis( mpis, MPI_COUNT );
186     return LIBSPECTRUM_ERROR_LOGIC;
187   }
188 
189   format = secret_key ? private_key_format : public_key_format;
190 
191   error = gcry_sexp_build( s_key, NULL, format,
192 			   mpis[0], mpis[1], mpis[2], mpis[3], mpis[4] );
193   if( error ) {
194     libspectrum_print_error( LIBSPECTRUM_ERROR_LOGIC,
195 			     "create_key: error creating key: %s",
196 			     gcry_strerror( error ) );
197     free_mpis( mpis, MPI_COUNT );
198     return LIBSPECTRUM_ERROR_LOGIC;
199   }
200 
201   free_mpis( mpis, MPI_COUNT );
202 
203   /* FIXME: Test public keys as well once gcry_pk_testkey acquires this
204      functionality */
205   if( secret_key ) {
206     error = gcry_pk_testkey( *s_key );
207     if( error ) {
208       libspectrum_print_error( LIBSPECTRUM_ERROR_LOGIC,
209 			       "create_key: key is not sane: %s",
210 			       gcry_strerror( error ) );
211       return LIBSPECTRUM_ERROR_LOGIC;
212     }
213   }
214 
215   return LIBSPECTRUM_ERROR_NONE;
216 }
217 
218 static void
free_mpis(gcry_mpi_t * mpis,size_t n)219 free_mpis( gcry_mpi_t *mpis, size_t n )
220 {
221   size_t i;
222   for( i=0; i<n; i++ ) if( mpis[i] ) gcry_mpi_release( mpis[i] );
223 }
224 
225 static libspectrum_error
get_mpi(gcry_mpi_t * mpi,gcry_sexp_t sexp,const char * token)226 get_mpi( gcry_mpi_t *mpi, gcry_sexp_t sexp, const char *token )
227 {
228   gcry_sexp_t pair;
229 
230   pair = gcry_sexp_find_token( sexp, token, strlen( token ) );
231   if( !pair ) {
232     libspectrum_print_error( LIBSPECTRUM_ERROR_LOGIC,
233 			     "get_mpis: couldn't find '%s'", token );
234     return LIBSPECTRUM_ERROR_LOGIC;
235   }
236 
237   *mpi = gcry_sexp_nth_mpi( pair, 1, GCRYMPI_FMT_STD );
238   if( !(*mpi) ) {
239     libspectrum_print_error( LIBSPECTRUM_ERROR_LOGIC,
240 			     "get_mpis: couldn't create MPI '%s'", token );
241     return LIBSPECTRUM_ERROR_LOGIC;
242   }
243 
244   return LIBSPECTRUM_ERROR_NONE;
245 }
246 
247 static libspectrum_error
serialise_mpis(libspectrum_byte ** signature,size_t * signature_length,gcry_mpi_t r,gcry_mpi_t s)248 serialise_mpis( libspectrum_byte **signature, size_t *signature_length,
249 		gcry_mpi_t r, gcry_mpi_t s )
250 {
251   gcry_error_t error;
252   size_t length, length_s;
253   unsigned char *ptr;
254 
255   error = gcry_mpi_print( GCRYMPI_FMT_PGP, NULL, 0, &length, r );
256   if( error ) {
257     libspectrum_print_error( LIBSPECTRUM_ERROR_LOGIC,
258 			     "serialise_mpis: length of r: %s",
259 			     gcry_strerror( error ) );
260     return LIBSPECTRUM_ERROR_LOGIC;
261   }
262 
263   error = gcry_mpi_print( GCRYMPI_FMT_PGP, NULL, 0, &length_s, s );
264   if( error ) {
265     libspectrum_print_error( LIBSPECTRUM_ERROR_LOGIC,
266 			     "serialise_mpis: length of s: %s",
267 			     gcry_strerror( error ) );
268     return LIBSPECTRUM_ERROR_LOGIC;
269   }
270 
271   length += length_s; *signature_length = length;
272 
273   *signature = libspectrum_malloc( length );
274 
275   error = gcry_mpi_print( GCRYMPI_FMT_PGP, *signature, length, &length, r );
276   if( error ) {
277     libspectrum_print_error( LIBSPECTRUM_ERROR_LOGIC,
278 			     "serialise_mpis: printing r: %s",
279 			     gcry_strerror( error ) );
280     libspectrum_free( *signature );
281     return LIBSPECTRUM_ERROR_LOGIC;
282   }
283 
284   ptr = *signature + length; length = *signature_length - length;
285   error = gcry_mpi_print( GCRYMPI_FMT_PGP, ptr, length, NULL, s );
286   if( error ) {
287     libspectrum_print_error( LIBSPECTRUM_ERROR_LOGIC,
288 			     "serialise_mpis: printing s: %s",
289 			     gcry_strerror( error ) );
290     libspectrum_free( *signature );
291     return LIBSPECTRUM_ERROR_LOGIC;
292   }
293 
294   return LIBSPECTRUM_ERROR_NONE;
295 }
296 
297 #include <stdio.h>
298 
299 libspectrum_error
libspectrum_verify_signature(libspectrum_signature * signature,libspectrum_rzx_dsa_key * key)300 libspectrum_verify_signature( libspectrum_signature *signature,
301 			      libspectrum_rzx_dsa_key *key )
302 {
303   libspectrum_error error;
304   gcry_error_t gcrypt_error;
305   gcry_sexp_t hash, key_sexp, signature_sexp;
306 
307   error = get_hash( &hash, signature->start, signature->length );
308   if( error ) return error;
309 
310   error = create_key( &key_sexp, key, 0 );
311   if( error ) { gcry_sexp_release( hash ); return error; }
312 
313   error = gcry_sexp_build( &signature_sexp, NULL, signature_format,
314 			   signature->r, signature->s );
315 
316   if( error ) {
317     libspectrum_print_error(
318       LIBSPECTRUM_ERROR_LOGIC,
319       "create_signature: error building signature sexp: %s",
320       gcry_strerror( error )
321     );
322     gcry_sexp_release( key_sexp ); gcry_sexp_release( hash );
323     return LIBSPECTRUM_ERROR_LOGIC;
324   }
325 
326   gcrypt_error = gcry_pk_verify( signature_sexp, hash, key_sexp );
327 
328   gcry_sexp_release( signature_sexp );
329   gcry_sexp_release( key_sexp ); gcry_sexp_release( hash );
330 
331   if( gcrypt_error ) {
332     if( gcry_err_code( gcrypt_error ) == GPG_ERR_BAD_SIGNATURE ) {
333       return LIBSPECTRUM_ERROR_SIGNATURE;
334     } else {
335       libspectrum_print_error(
336         LIBSPECTRUM_ERROR_LOGIC,
337 	"libgcrypt error verifying signature: %s",
338 	gcry_strerror( gcrypt_error )
339       );
340       return LIBSPECTRUM_ERROR_LOGIC;
341     }
342   }
343 
344   return LIBSPECTRUM_ERROR_NONE;
345 }
346 
347 libspectrum_error
libspectrum_signature_free(libspectrum_signature * signature)348 libspectrum_signature_free( libspectrum_signature *signature )
349 {
350   gcry_mpi_release( signature->r ); gcry_mpi_release( signature->s );
351 
352   return LIBSPECTRUM_ERROR_NONE;
353 }
354 
355 #endif				/* #ifdef HAVE_GCRYPT_H */
356