See ChangeLog: Mon Mar 13 19:22:46 CET 2000 Werner Koch
[gnupg.git] / cipher / md.c
1 /* md.c  -  message digest dispatcher
2  *      Copyright (C) 1998,1999 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 2 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, write to the Free Software
18  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
19  */
20
21 #include <config.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <errno.h>
26 #include <assert.h>
27
28 #include "g10lib.h"
29 #include "cipher.h"
30 #include "dynload.h"
31 #include "rmd.h"
32
33
34 struct md_digest_list_s;
35
36 /* this structure is put right after the GCRY_MD_HD buffer, so that
37  * only one memory block is needed. */
38 struct gcry_md_context {
39     int  magic;
40     int  secure;
41     FILE  *debug;
42     int finalized;
43     struct md_digest_list_s *list;
44 };
45 #define CTX_MAGIC_NORMAL 0x11071961
46 #define CTX_MAGIC_SECURE 0x16917011
47
48 static const char * digest_algo_to_string( int algo );
49 static int check_digest_algo( int algo );
50 static GCRY_MD_HD md_open( int algo, int secure );
51 static int  md_enable( GCRY_MD_HD hd, int algo );
52 static GCRY_MD_HD md_copy( GCRY_MD_HD a );
53 static void md_close(GCRY_MD_HD a);
54 static void md_write( GCRY_MD_HD a, byte *inbuf, size_t inlen);
55 static void md_final(GCRY_MD_HD a);
56 static byte *md_read( GCRY_MD_HD a, int algo );
57 static int md_get_algo( GCRY_MD_HD a );
58 static int md_digest_length( int algo );
59 static const byte *md_asn_oid( int algo, size_t *asnlen, size_t *mdlen );
60 static void md_start_debug( GCRY_MD_HD a, const char *suffix );
61 static void md_stop_debug( GCRY_MD_HD a );
62
63 /****************
64  * This structure is used for the list of available algorithms
65  * and for the list of algorithms in GCRY_MD_HD.
66  */
67 struct md_digest_list_s {
68     struct md_digest_list_s *next;
69     const char *name;
70     int algo;
71     byte *asnoid;
72     int asnlen;
73     int mdlen;
74     void (*init)( void *c );
75     void (*write)( void *c, byte *buf, size_t nbytes );
76     void (*final)( void *c );
77     byte *(*read)( void *c );
78     size_t contextsize; /* allocate this amount of context */
79     PROPERLY_ALIGNED_TYPE context;
80 };
81
82 static struct md_digest_list_s *digest_list;
83
84
85 static struct md_digest_list_s *
86 new_list_item( int algo,
87                const char *(*get_info)( int, size_t*,byte**, int*, int*,
88                                        void (**)(void*),
89                                        void (**)(void*,byte*,size_t),
90                                        void (**)(void*),byte *(**)(void*)) )
91 {
92     struct md_digest_list_s *r;
93
94     r = g10_xcalloc( 1, sizeof *r );
95     r->algo = algo,
96     r->name = (*get_info)( algo, &r->contextsize,
97                            &r->asnoid, &r->asnlen, &r->mdlen,
98                            &r->init, &r->write, &r->final, &r->read );
99     if( !r->name ) {
100         g10_free(r);
101         r = NULL;
102     }
103     return r;
104 }
105
106
107
108 /****************
109  * Try to load the modules with the requeste algorithm
110  * and return true if new modules are available
111  * If req_alog is -1 try to load all digest algorithms.
112  */
113 static int
114 load_digest_module( int req_algo )
115 {
116     static int initialized = 0;
117     static u32 checked_algos[256/32];
118     static int checked_all = 0;
119     struct md_digest_list_s *r;
120     void *context = NULL;
121     int algo;
122     int any = 0;
123     const char *(*get_info)( int, size_t*,byte**, int*, int*,
124                             void (**)(void*),
125                             void (**)(void*,byte*,size_t),
126                             void (**)(void*),byte *(**)(void*));
127
128     if( !initialized ) {
129         cipher_modules_constructor();
130         initialized = 1;
131     }
132     algo = req_algo;
133     if( algo > 255 || !algo )
134         return 0; /* algorithm number too high (does not fit into out bitmap)*/
135     if( checked_all )
136         return 0; /* already called with -1 */
137     if( algo < 0 )
138         checked_all = 1;
139     else if( (checked_algos[algo/32] & (1 << (algo%32))) )
140         return 0; /* already checked and not found */
141     else
142         checked_algos[algo/32] |= (1 << (algo%32));
143
144     while( enum_gnupgext_digests( &context, &algo, &get_info ) ) {
145         if( req_algo != -1 && algo != req_algo )
146             continue;
147         for(r=digest_list; r; r = r->next )
148             if( r->algo == algo )
149                 break;
150         if( r ) {
151             log_info("skipping digest %d: already loaded\n", algo );
152             continue;
153         }
154         r = new_list_item( algo, get_info );
155         if( ! r ) {
156             log_info("skipping digest %d: no name\n", algo );
157             continue;
158         }
159         /* put it into the list */
160         if( g10_log_verbosity( 2 ) )
161             log_info("loaded digest %d\n", algo);
162         r->next = digest_list;
163         digest_list = r;
164         any = 1;
165         if( req_algo != -1 )
166             break;
167     }
168     enum_gnupgext_digests( &context, NULL, NULL );
169     return any;
170 }
171
172
173
174 /****************
175  * Map a string to the digest algo
176  */
177 int
178 gcry_md_map_name( const char *string )
179 {
180     struct md_digest_list_s *r;
181
182     do {
183         for(r = digest_list; r; r = r->next )
184             if( !stricmp( r->name, string ) )
185                 return r->algo;
186     } while( !r && load_digest_module(-1) );
187     return 0;
188 }
189
190
191 /****************
192  * Map a digest algo to a string
193  */
194 static const char *
195 digest_algo_to_string( int algo )
196 {
197     struct md_digest_list_s *r;
198
199     do {
200         for(r = digest_list; r; r = r->next )
201             if( r->algo == algo )
202                 return r->name;
203     } while( !r && load_digest_module( algo ) );
204     return NULL;
205 }
206
207 /****************
208  * This function simply returns the name of the algorithm or some constant
209  * string when there is no algo.  It will never return NULL.
210  * Use  the macro gcry_md_test_algo() to check whether the algorithm
211  * is valid.
212  */
213 const char *
214 gcry_md_algo_name( int algo )
215 {
216     const char *s = digest_algo_to_string( algo );
217     return s? s: "?";
218 }
219
220
221 static int
222 check_digest_algo( int algo )
223 {
224     struct md_digest_list_s *r;
225
226     do {
227         for(r = digest_list; r; r = r->next )
228             if( r->algo == algo )
229                 return 0;
230     } while( !r && load_digest_module(algo) );
231     return GCRYERR_INV_MD_ALGO;
232 }
233
234
235
236 /****************
237  * Open a message digest handle for use with algorithm ALGO.
238  * More algorithms may be added by md_enable(). The initial algorithm
239  * may be 0.
240  */
241 static GCRY_MD_HD
242 md_open( int algo, int secure )
243 {
244     GCRY_MD_HD hd;
245     struct gcry_md_context *ctx;
246     int bufsize = secure? 512 : 1024;
247     size_t n;
248
249     /* Allocate a memory area to hold the caller visible buffer with it's
250      * control information and the data required by this module. Set the
251      * context pointer at the beginning to this area.
252      * We have to use this strange scheme because we want to hide the
253      * internal data but have a variable sized buffer.
254      *
255      *  +---+------+---........------+-------------+
256      *  !ctx! bctl !  buffer         ! private     !
257      *  +---+------+---........------+-------------+
258      *    !                           ^
259      *    !---------------------------!
260      *
261      * We have to make sture that private is well aligned.
262      */
263     n = sizeof( struct gcry_md_handle ) + bufsize;
264     n = ((n + sizeof(PROPERLY_ALIGNED_TYPE)-1)
265          / sizeof(PROPERLY_ALIGNED_TYPE) ) * sizeof(PROPERLY_ALIGNED_TYPE);
266
267     /* allocate and set the Context pointer to the private data */
268     hd = secure ? g10_malloc_secure( n + sizeof( struct gcry_md_context ) )
269                 : g10_malloc(        n + sizeof( struct gcry_md_context ) );
270     if( !hd ) {
271         set_lasterr( GCRYERR_NO_MEM );
272         return NULL;
273     }
274
275     hd->ctx = ctx = (struct gcry_md_context*)( (char*)hd + n );
276     /* setup the globally visible data (bctl in the diagram)*/
277     hd->bufsize = n - sizeof( struct gcry_md_handle ) + 1;
278     hd->bufpos = 0;
279     /* initialize the private data */
280     memset( hd->ctx, 0, sizeof *hd->ctx );
281     ctx->magic = secure ? CTX_MAGIC_SECURE : CTX_MAGIC_NORMAL;
282     ctx->secure = secure;
283     fast_random_poll(); /* FIXME: should we really do that? */
284     if( algo && md_enable( hd, algo ) ) {
285         md_close( hd );
286         return NULL;
287     }
288     return hd;
289 }
290
291
292 GCRY_MD_HD
293 gcry_md_open( int algo, unsigned int flags )
294 {
295     GCRY_MD_HD hd;
296     /* fixme: check that algo is available and that only valid
297      * flag values are used */
298     hd = md_open( algo, (flags & GCRY_MD_FLAG_SECURE) );
299     return hd;
300 }
301
302
303
304 static int
305 md_enable( GCRY_MD_HD hd, int algo )
306 {
307     struct gcry_md_context *h = hd->ctx;
308     struct md_digest_list_s *r, *ac;
309
310     for( ac=h->list; ac; ac = ac->next )
311         if( ac->algo == algo )
312             return 0; /* already enabled */
313     /* find the algorithm */
314     do {
315         for(r = digest_list; r; r = r->next )
316             if( r->algo == algo )
317                 break;
318     } while( !r && load_digest_module( algo ) );
319     if( !r ) {
320         log_debug("md_enable: algorithm %d not available\n", algo );
321         return set_lasterr( GCRYERR_INV_MD_ALGO );
322     }
323     /* and allocate a new list entry */
324     ac = h->secure? g10_malloc_secure( sizeof *ac + r->contextsize
325                                                - sizeof(r->context) )
326                   : g10_malloc( sizeof *ac + r->contextsize
327                                                - sizeof(r->context) );
328     if( !ac )
329         return set_lasterr( GCRYERR_NO_MEM );
330
331     *ac = *r;
332     ac->next = h->list;
333     h->list = ac;
334     /* and init this instance */
335     (*ac->init)( &ac->context.c );
336     return 0;
337 }
338
339
340 int
341 gcry_md_enable( GCRY_MD_HD hd, int algo )
342 {
343     return md_enable( hd, algo );
344 }
345
346 static GCRY_MD_HD
347 md_copy( GCRY_MD_HD ahd )
348 {
349     struct gcry_md_context *a = ahd->ctx;
350     struct gcry_md_context *b;
351     GCRY_MD_HD bhd;
352     struct md_digest_list_s *ar, *br;
353     size_t n;
354
355     if( ahd->bufpos )
356         md_write( ahd, NULL, 0 );
357
358     n = (char*)ahd->ctx - (char*)ahd;
359     bhd = a->secure ? g10_malloc_secure( n + sizeof( struct gcry_md_context ) )
360                     : g10_malloc(        n + sizeof( struct gcry_md_context ) );
361     if( !bhd ) {
362         set_lasterr( GCRYERR_NO_MEM );
363         return NULL;
364     }
365
366     bhd->ctx = b = (struct gcry_md_context*)( (char*)bhd + n );
367     /* no need to copy the buffer due to the write above */
368     assert( ahd->bufsize == (n - sizeof( struct gcry_md_handle ) + 1) );
369     bhd->bufsize = ahd->bufsize;
370     bhd->bufpos = 0;  assert( !ahd->bufpos );
371     memcpy( b, a, sizeof *a );
372     b->list = NULL;
373     b->debug = NULL;
374     /* and now copy the complete list of algorithms */
375     /* I know that the copied list is reversed, but that doesn't matter */
376     for( ar=a->list; ar; ar = ar->next ) {
377         br = a->secure ? g10_xmalloc_secure( sizeof *br + ar->contextsize
378                                                - sizeof(ar->context) )
379                        : g10_xmalloc( sizeof *br + ar->contextsize
380                                                - sizeof(ar->context) );
381         memcpy( br, ar, sizeof(*br) + ar->contextsize
382                                     - sizeof(ar->context) );
383         br->next = b->list;
384         b->list = br;
385     }
386
387     if( a->debug )
388         md_start_debug( bhd, "unknown" );
389     return bhd;
390 }
391
392 GCRY_MD_HD
393 gcry_md_copy( GCRY_MD_HD hd )
394 {
395     return md_copy( hd );
396 }
397
398 /****************
399  * Reset all contexts and discard any buffered stuff.  This may be used
400  * instead of a md_close(); md_open().
401  */
402 void
403 gcry_md_reset( GCRY_MD_HD a )
404 {
405     struct md_digest_list_s *r;
406
407     a->bufpos = a->ctx->finalized = 0;
408     for( r=a->ctx->list; r; r = r->next ) {
409         memset( r->context.c, 0, r->contextsize );
410         (*r->init)( &r->context.c );
411     }
412 }
413
414
415 static void
416 md_close(GCRY_MD_HD a)
417 {
418     struct md_digest_list_s *r, *r2;
419
420     if( !a )
421         return;
422     if( a->ctx->debug )
423         md_stop_debug(a);
424     for(r=a->ctx->list; r; r = r2 ) {
425         r2 = r->next;
426         g10_free(r);
427     }
428     g10_free(a);
429 }
430
431
432 void
433 gcry_md_close( GCRY_MD_HD hd )
434 {
435     md_close( hd );
436 }
437
438
439 static void
440 md_write( GCRY_MD_HD a, byte *inbuf, size_t inlen)
441 {
442     struct md_digest_list_s *r;
443
444     if( a->ctx->debug ) {
445         if( a->bufpos && fwrite(a->buf, a->bufpos, 1, a->ctx->debug ) != 1 )
446             BUG();
447         if( inlen && fwrite(inbuf, inlen, 1, a->ctx->debug ) != 1 )
448             BUG();
449     }
450     for(r=a->ctx->list; r; r = r->next ) {
451         if( a->bufpos )
452             (*r->write)( &r->context.c, a->buf, a->bufpos );
453         (*r->write)( &r->context.c, inbuf, inlen );
454     }
455     a->bufpos = 0;
456 }
457
458
459 void
460 gcry_md_write( GCRY_MD_HD hd, const byte *inbuf, size_t inlen)
461 {
462     md_write( hd, (byte*)inbuf, inlen );
463 }
464
465
466
467 static void
468 md_final(GCRY_MD_HD a)
469 {
470     struct md_digest_list_s *r;
471
472     if( a->ctx->finalized )
473         return;
474
475     if( a->bufpos )
476         md_write( a, NULL, 0 );
477
478     for(r=a->ctx->list; r; r = r->next ) {
479         (*r->final)( &r->context.c );
480     }
481     a->ctx->finalized = 1;
482 }
483
484
485 int
486 gcry_md_ctl( GCRY_MD_HD hd, int cmd, byte *buffer, size_t buflen)
487 {
488     if( cmd == GCRYCTL_FINALIZE )
489         md_final( hd );
490     else
491         return GCRYERR_INV_OP;
492     return 0;
493 }
494
495
496 /****************
497  * if ALGO is null get the digest for the used algo (which should be only one)
498  */
499 static byte *
500 md_read( GCRY_MD_HD a, int algo )
501 {
502     struct md_digest_list_s *r;
503
504     if( !algo ) {  /* return the first algorithm */
505         if( (r=a->ctx->list) ) {
506             if( r->next )
507                 log_debug("more than algorithm in md_read(0)\n");
508             return (*r->read)( &r->context.c );
509         }
510     }
511     else {
512         for(r=a->ctx->list; r; r = r->next )
513             if( r->algo == algo )
514                 return (*r->read)( &r->context.c );
515     }
516     BUG();
517     return NULL;
518 }
519
520 /****************
521  * Read out the complete digest, this function implictly finalizes
522  * the hash.
523  */
524 byte *
525 gcry_md_read( GCRY_MD_HD hd, int algo )
526 {
527     gcry_md_ctl( hd, GCRYCTL_FINALIZE, NULL, 0 );
528     return md_read( hd, algo);
529 }
530
531
532 /****************
533  * This function combines md_final and md_read but keeps the context
534  * intact.  This function can be used to calculate intermediate
535  * digests.  The digest is copied into buffer and the digestlength is
536  * returned.  If buffer is NULL only the needed size for buffer is returned.
537  * buflen gives the max size of buffer. If the buffer is too shourt to
538  * hold the complete digest, the buffer is filled with as many bytes are
539  * possible and this value is returned.
540  */
541 #if 0
542 static int
543 md_digest( GCRY_MD_HD a, int algo, byte *buffer, int buflen )
544 {
545     struct md_digest_list_s *r = NULL;
546     char *context;
547     char *digest;
548
549     if( a->bufpos )
550         md_write( a, NULL, 0 );
551
552     if( !algo ) {  /* return digest for the first algorithm */
553         if( (r=a->ctx->list) && r->next )
554             log_debug("more than algorithm in md_digest(0)\n");
555     }
556     else {
557         for(r=a->ctx->list; r; r = r->next )
558             if( r->algo == algo )
559                 break;
560     }
561     if( !r )
562         BUG();
563
564     if( !buffer )
565         return r->mdlen;
566
567     /* I don't want to change the interface, so I simply work on a copy
568      * of the context (extra overhead - should be fixed)*/
569     context = a->ctx->secure ? g10_xmalloc_secure( r->contextsize )
570                              : g10_xmalloc( r->contextsize );
571     memcpy( context, r->context.c, r->contextsize );
572     (*r->final)( context );
573     digest = (*r->read)( context );
574
575     if( buflen > r->mdlen )
576         buflen = r->mdlen;
577     memcpy( buffer, digest, buflen );
578
579     g10_free(context);
580     return buflen;
581 }
582 #endif
583
584 /****************
585  * Read out an intermediate digest.
586  */
587 int
588 gcry_md_get( GCRY_MD_HD hd, int algo, byte *buffer, int buflen )
589 {
590     /*md_digest ... */
591     return GCRYERR_INTERNAL;
592 }
593
594
595 /****************
596  * Shortcut function to hash a buffer with a given algo. The only supported
597  * algorithm is RIPE-MD. The supplied digest buffer must be large enough
598  * to store the resulting hash.  No error is returned, the function will
599  * abort on an invalid algo.  DISABLED_ALGOS are ignored here.
600  */
601 void
602 gcry_md_hash_buffer( int algo, char *digest, const char *buffer, size_t length)
603 {
604     if( algo == GCRY_MD_RMD160 )
605         rmd160_hash_buffer( digest, buffer, length );
606     else { /* for the others we do not have a fast function, so
607             * we use the normal functions to do it */
608         GCRY_MD_HD h = md_open( algo, 0 );
609         if( !h )
610             BUG(); /* algo not available */
611         md_write( h, (byte*)buffer, length );
612         md_final( h );
613         memcpy( digest, md_read( h, algo ), md_digest_length( algo ) );
614     }
615 }
616
617 static int
618 md_get_algo( GCRY_MD_HD a )
619 {
620     struct md_digest_list_s *r;
621
622     if( (r=a->ctx->list) ) {
623         if( r->next )
624             log_error("WARNING: more than algorithm in md_get_algo()\n");
625         return r->algo;
626     }
627     return 0;
628 }
629
630
631 int
632 gcry_md_get_algo( GCRY_MD_HD hd )
633 {
634     return md_get_algo( hd ); /* fixme: we need error handling */
635 }
636
637
638 /****************
639  * Return the length of the digest
640  */
641 static int
642 md_digest_length( int algo )
643 {
644     struct md_digest_list_s *r;
645
646     do {
647         for(r = digest_list; r; r = r->next ) {
648             if( r->algo == algo )
649                 return r->mdlen;
650         }
651     } while( !r && load_digest_module( algo ) );
652     return 0;
653 }
654
655 /****************
656  * Return the length of the digest in bytes.
657  * This function will return 0 in case of errors.
658  */
659 unsigned int
660 gcry_md_get_algo_dlen( int algo )
661 {
662     /* we do some very quick checks here */
663     switch( algo )
664     {
665       case GCRY_MD_MD5: return 16;
666       case GCRY_MD_SHA1:
667       case GCRY_MD_RMD160: return 20;
668       default: {
669             int len = md_digest_length( algo );
670             if( !len )
671                 set_lasterr( GCRYERR_INV_MD_ALGO );
672             return 0;
673         }
674     }
675 }
676
677
678 /* Hmmm: add a mode to enumerate the OIDs
679  *      to make g10/sig-check.c more portable */
680 static const byte *
681 md_asn_oid( int algo, size_t *asnlen, size_t *mdlen )
682 {
683     struct md_digest_list_s *r;
684
685     do {
686         for(r = digest_list; r; r = r->next ) {
687             if( r->algo == algo ) {
688                 if( asnlen )
689                     *asnlen = r->asnlen;
690                 if( mdlen )
691                     *mdlen = r->mdlen;
692                 return r->asnoid;
693             }
694         }
695     } while( !r && load_digest_module( algo ) );
696     log_bug("no asn for md algo %d\n", algo);
697     return NULL;
698 }
699
700
701
702 /****************
703  * Return information about the given cipher algorithm
704  * WHAT select the kind of information returned:
705  *  GCRYCTL_TEST_ALGO:
706  *      Returns 0 when the specified algorithm is available for use.
707  *      buffer and nbytes must be zero.
708  *  GCRYCTL_GET_ASNOID:
709  *      Return the ASNOID of the algorithm in buffer. if buffer is NULL, only
710  *      the required length is returned.
711  *
712  * On error the value -1 is returned and the error reason may be
713  * retrieved by gcry_errno().
714  * Note:  Because this function is in most caes used to return an
715  * integer value, we can make it easier for the caller to just look at
716  * the return value.  The caller will in all cases consult the value
717  * and thereby detecting whether a error occured or not (i.e. while checking
718  * the block size)
719  */
720 int
721 gcry_md_algo_info( int algo, int what, void *buffer, size_t *nbytes)
722 {
723     switch( what ) {
724       case GCRYCTL_TEST_ALGO:
725         if( buffer || nbytes ) {
726             set_lasterr( GCRYERR_INV_ARG );
727             return -1;
728         }
729         if( check_digest_algo( algo ) ) {
730             set_lasterr( GCRYERR_INV_MD_ALGO );
731             return -1;
732         }
733         break;
734
735       case GCRYCTL_GET_ASNOID: {
736             size_t asnlen;
737             const char *asn = md_asn_oid( algo, &asnlen, NULL );
738             if( buffer && *nbytes >= asnlen ) {
739                 memcpy( buffer, asn, asnlen );
740                 *nbytes = asnlen;
741                 return 0;
742             }
743             if( !buffer && nbytes ) {
744                 *nbytes = asnlen;
745                 return 0;
746             }
747             set_lasterr( buffer ? GCRYERR_TOO_SHORT : GCRYERR_INV_ARG );
748             return -1;
749         }
750         break;
751
752       default:
753         set_lasterr( GCRYERR_INV_OP );
754         return -1;
755     }
756     return 0;
757 }
758
759
760
761
762 void
763 md_start_debug( GCRY_MD_HD md, const char *suffix )
764 {
765     static int idx=0;
766     char buf[25];
767
768     if( md->ctx->debug ) {
769         log_debug("Oops: md debug already started\n");
770         return;
771     }
772     idx++;
773     sprintf(buf, "dbgmd-%05d.%.10s", idx, suffix );
774     md->ctx->debug = fopen(buf, "w");
775     if( !md->ctx->debug )
776         log_debug("md debug: can't open %s\n", buf );
777 }
778
779 void
780 md_stop_debug( GCRY_MD_HD md )
781 {
782     if( md->ctx->debug ) {
783         if( md->bufpos )
784             md_write( md, NULL, 0 );
785         fclose(md->ctx->debug);
786         md->ctx->debug = NULL;
787     }
788   #ifdef HAVE_U64_TYPEDEF
789     {  /* a kludge to pull in the __muldi3 for Solaris */
790        volatile u32 a = (u32)(ulong)md;
791        volatile u64 b = 42;
792        volatile u64 c;
793        c = a * b;
794     }
795   #endif
796 }
797
798
799
800 /****************
801  * Return information about the digest handle.
802  *  GCRYCTL_IS_SECURE:
803  *      Returns 1 when the handle works on secured memory
804  *      otherwise 0 is returned.  There is no error return.
805  */
806 int
807 gcry_md_info( GCRY_MD_HD h, int cmd, void *buffer, size_t *nbytes)
808 {
809     switch( cmd ) {
810       case GCRYCTL_IS_SECURE:
811         return h->ctx->secure;
812
813       default:
814         set_lasterr( GCRYERR_INV_OP );
815         return -1;
816     }
817     return 0;
818 }
819