* genkey.c (store_key): Protect the key.
[gnupg.git] / agent / protect.c
1 /* protect.c - Un/Protect a secret key
2  * Copyright (C) 1998, 1999, 2000, 2001, 2002 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 <errno.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <ctype.h>
27 #include <assert.h>
28 #include <unistd.h>
29 #include <sys/stat.h>
30
31 #include "agent.h"
32
33 #define PROT_CIPHER        GCRY_CIPHER_AES
34 #define PROT_CIPHER_STRING "aes"
35 #define PROT_CIPHER_KEYLEN (128/8)
36
37
38 /* A table containing the information needed to create a protected
39    private key */
40 static struct {
41   const char *algo;
42   const char *parmlist;
43   int prot_from, prot_to;
44 } protect_info[] = {
45   { "rsa",  "nedpqu", 2, 5 },
46   { NULL }
47 };
48
49
50 static int
51 hash_passphrase (const char *passphrase, int hashalgo,
52                  int s2kmode,
53                  const unsigned char *s2ksalt, unsigned long s2kcount,
54                  unsigned char *key, size_t keylen);
55
56
57
58 /* Return the length of the next S-Exp part and update the pointer to
59    the first data byte.  0 is return on error */
60 static size_t
61 snext (unsigned char const **buf)
62 {
63   const unsigned char *s;
64   int n;
65
66   s = *buf;
67   for (n=0; *s && *s != ':' && digitp (s); s++)
68     n = n*10 + atoi_1 (s);
69   if (!n || *s != ':')
70     return 0; /* we don't allow empty lengths */
71   *buf = s+1;
72   return n;
73 }
74
75 /* Skip over the S-Expression BUF points to and update BUF to point to
76    the chacter right behind.  DEPTH gives the initial number of open
77    lists and may be passed as a positive number to skip over the
78    remainder of an S-Expression if the current position is somewhere
79    in an S-Expression.  The function may return an error code if it
80    encounters an impossible conditions */
81 static int
82 sskip (unsigned char const **buf, int *depth)
83 {
84   const unsigned char *s = *buf;
85   size_t n;
86   int d = *depth;
87   
88   while (d > 0)
89     {
90       if (*s == '(')
91         {
92           d++;
93           s++;
94         }
95       else if (*s == ')')
96         {
97           d--;
98           s++;
99         }
100       else
101         {
102           if (!d)
103             return GNUPG_Invalid_Sexp;
104           n = snext (&s);
105           if (!n)
106             return GNUPG_Invalid_Sexp; 
107           s += n;
108         }
109     }
110   *buf = s;
111   *depth = d;
112   return 0;
113 }
114
115
116 /* Check whether the the string at the address BUF points to matches
117    the token.  Return true on match and update BUF to point behind the
118    token. */
119 static int
120 smatch (unsigned char const **buf, size_t buflen, const char *token)
121 {
122   size_t toklen = strlen (token);
123
124   if (buflen != toklen || memcmp (*buf, token, toklen))
125     return 0;
126   *buf += toklen;
127   return 1;
128 }
129
130
131 \f
132 /* Calculate the MIC for a private key S-Exp. SHA1HASH should pint to
133    a 20 byte buffer.  This function is suitable for any algorithms. */
134 static int 
135 calculate_mic (const unsigned char *plainkey, unsigned char *sha1hash)
136 {
137   const unsigned char *hash_begin, *hash_end;
138   const unsigned char *s;
139   size_t n;
140
141   s = plainkey;
142   if (*s != '(')
143     return GNUPG_Invalid_Sexp;
144   s++;
145   n = snext (&s);
146   if (!n)
147     return GNUPG_Invalid_Sexp; 
148   if (!smatch (&s, n, "private-key"))
149     return GNUPG_Unknown_Sexp; 
150   if (*s != '(')
151     return GNUPG_Unknown_Sexp;
152   hash_begin = s;
153   s++;
154   n = snext (&s);
155   if (!n)
156     return GNUPG_Invalid_Sexp; 
157   s += n; /* skip over the algorithm name */
158
159   while (*s == '(')
160     {
161       s++;
162       n = snext (&s);
163       if (!n)
164         return GNUPG_Invalid_Sexp; 
165       s += n;
166       n = snext (&s);
167       if (!n)
168         return GNUPG_Invalid_Sexp; 
169       s += n;
170       if ( *s != ')' )
171         return GNUPG_Invalid_Sexp; 
172       s++;
173     }
174   if (*s != ')')
175     return GNUPG_Invalid_Sexp; 
176   s++;
177   hash_end = s;
178
179   gcry_md_hash_buffer (GCRY_MD_SHA1, sha1hash,
180                        hash_begin, hash_end - hash_begin);
181
182   return 0;
183 }
184
185
186 \f
187 /* Encrypt the parameter block starting at PROTBEGIN with length
188    PROTLEN using the utf8 encoded key PASSPHRASE and return the entire
189    encrypted block in RESULT or ereturn with an error code.  SHA1HASH
190    is the 20 byte SHA-1 hash required for the integrity code.
191
192    The parameter block is expected to be an incomplete S-Expression of
193    the form (example in advanced format):
194
195      (d #046129F..[some bytes not shown]..81#)
196      (p #00e861b..[some bytes not shown]..f1#)
197      (q #00f7a7c..[some bytes not shown]..61#)
198      (u #304559a..[some bytes not shown]..9b#) 
199
200    the returned block is the S-Expression:
201
202     (protected mode (parms) encrypted_octet_string)
203
204 */
205 static int
206 do_encryption (const char *protbegin, size_t protlen, 
207                const char *passphrase,  const unsigned char *sha1hash,
208                unsigned char **result, size_t *resultlen)
209 {
210   GCRY_CIPHER_HD hd;
211   const char *modestr = "openpgp-s2k3-sha1-" PROT_CIPHER_STRING "-cbc";
212   int blklen, enclen, outlen;
213   char *iv = NULL;
214   int rc = 0;
215   char *outbuf = NULL;
216   char *p;
217   int saltpos, ivpos, encpos;
218
219   hd = gcry_cipher_open (PROT_CIPHER, GCRY_CIPHER_MODE_CBC,
220                          GCRY_CIPHER_SECURE);
221   if (!hd)
222     return map_gcry_err (gcry_errno());
223
224
225   /* We need to work on a copy of the data because this makes it
226      easier to add the trailer and the padding and more important we
227      have to prefix the text with 2 parenthesis, so we have to
228      allocate enough space for:
229
230      ((<parameter_list>)(4:hash4:sha120:<hashvalue>)) + padding
231
232      We always append a full block of random bytes as padding but
233      encrypt only what is needed for a full blocksize */
234   blklen = gcry_cipher_get_algo_blklen (PROT_CIPHER);
235   outlen = 2 + protlen + 2 + 6 + 6 + 23 + 2 + blklen;
236   enclen = outlen/blklen * blklen;
237   outbuf = gcry_malloc_secure (outlen);
238   if (!outbuf)
239     rc = GNUPG_Out_Of_Core;
240   if (!rc)
241     {
242       /* allocate random bytes to be used as IV, padding and s2k salt*/
243       iv = gcry_random_bytes (blklen*2+8, GCRY_WEAK_RANDOM);
244       if (!iv)
245         rc = GNUPG_Out_Of_Core;
246       else
247         rc = gcry_cipher_setiv (hd, iv, blklen);
248     }
249   if (!rc)
250     {
251       unsigned char *key;
252       size_t keylen = PROT_CIPHER_KEYLEN;
253       
254       key = gcry_malloc_secure (keylen);
255       if (!key)
256         rc = GNUPG_Out_Of_Core;
257       else
258         {
259           rc = hash_passphrase (passphrase, GCRY_MD_SHA1,
260                                 3, iv+2*blklen, 96, key, keylen);
261           if (!rc)
262             rc = gcry_cipher_setkey (hd, key, keylen);
263           xfree (key);
264         }
265     }
266   if (!rc)
267     {
268       p = outbuf;
269       *p++ = '(';
270       *p++ = '(';
271       memcpy (p, protbegin, protlen);
272       p += protlen;
273       memcpy (p, ")(4:hash4:sha120:", 17);
274       p += 17;
275       memcpy (p, sha1hash, 20);
276       p += 20;
277       *p++ = ')';
278       *p++ = ')';
279       memcpy (p, iv+blklen, blklen); 
280       p += blklen;
281       assert ( p - outbuf == outlen);
282       rc = gcry_cipher_encrypt (hd, outbuf, enclen, NULL, 0);
283     }
284   gcry_cipher_close (hd);
285   if (rc)
286     {
287       xfree (iv);
288       xfree (outbuf);
289       return rc;
290     }
291
292   /* Now allocate the buffer we want to return.  This is
293
294      (protected openpgp-s2k3-sha1-aes-cbc
295        ((sha1 salt no_of_iterations) 16byte_iv)
296        encrypted_octet_string)
297        
298      in canoncical format of course.  We use asprintf and %n modifier
299      and spaces as palceholders.  */
300   asprintf (&p,
301             "(9:protected%d:%s((4:sha18:%n_8bytes_2:96)%d:%n%*s)%d:%n%*s)",
302             (int)strlen (modestr), modestr,
303             &saltpos, 
304             blklen, &ivpos, blklen, "",
305             enclen, &encpos, enclen, "");
306   if (p)
307     { /* asprintf does not use out malloc system */
308       char *psave = p;
309       p = xtrymalloc (strlen (psave)+1);
310       if (p)
311         strcpy (p, psave);
312       free (psave);
313     }
314   if (!p)
315     {
316       xfree (iv);
317       xfree (outbuf);
318       return GNUPG_Out_Of_Core;
319     }
320   *resultlen = strlen (p);
321   *result = p;
322   memcpy (p+saltpos, iv+2*blklen, 8);
323   memcpy (p+ivpos, iv, blklen);
324   memcpy (p+encpos, outbuf, enclen);
325   xfree (iv);
326   xfree (outbuf);
327   return 0;
328 }
329
330
331
332 /* Protect the key encoded in canonical format in plainkey.  We assume
333    a valid S-Exp here. */
334 int 
335 agent_protect (const unsigned char *plainkey, const char *passphrase,
336                unsigned char **result, size_t *resultlen)
337 {
338   int rc;
339   const unsigned char *s;
340   const unsigned char *hash_begin, *hash_end;
341   const unsigned char *prot_begin, *prot_end, *real_end;
342   size_t n;
343   int c, infidx, i;
344   unsigned char hashvalue[20];
345   unsigned char *protected;
346   size_t protectedlen;
347   int depth = 0;
348   unsigned char *p;
349
350   s = plainkey;
351   if (*s != '(')
352     return GNUPG_Invalid_Sexp;
353   depth++;
354   s++;
355   n = snext (&s);
356   if (!n)
357     return GNUPG_Invalid_Sexp; 
358   if (!smatch (&s, n, "private-key"))
359     return GNUPG_Unknown_Sexp; 
360   if (*s != '(')
361     return GNUPG_Unknown_Sexp;
362   depth++;
363   hash_begin = s;
364   s++;
365   n = snext (&s);
366   if (!n)
367     return GNUPG_Invalid_Sexp; 
368
369   for (infidx=0; protect_info[infidx].algo
370               && !smatch (&s, n, protect_info[infidx].algo); infidx++)
371     ;
372   if (!protect_info[infidx].algo)
373     return GNUPG_Unsupported_Algorithm; 
374
375   prot_begin = prot_end = NULL;
376   for (i=0; (c=protect_info[infidx].parmlist[i]); i++)
377     {
378       if (i == protect_info[infidx].prot_from)
379         prot_begin = s;
380       if (*s != '(')
381         return GNUPG_Invalid_Sexp;
382       depth++;
383       s++;
384       n = snext (&s);
385       if (!n)
386         return GNUPG_Invalid_Sexp; 
387       if (n != 1 || c != *s)
388         return GNUPG_Invalid_Sexp; 
389       s += n;
390       n = snext (&s);
391       if (!n)
392         return GNUPG_Invalid_Sexp; 
393       s +=n; /* skip value */
394       if (*s != ')')
395         return GNUPG_Invalid_Sexp; 
396       depth--;
397       if (i == protect_info[infidx].prot_to)
398         prot_end = s;
399       s++;
400     }
401   if (*s != ')' || !prot_begin || !prot_end )
402     return GNUPG_Invalid_Sexp; 
403   depth--;
404   hash_end = s;
405   s++;
406   /* skip to the end of the S-exp */
407   assert (depth == 1);
408   rc = sskip (&s, &depth);
409   if (rc)
410     return rc;
411   assert (!depth);
412   real_end = s-1;
413
414   gcry_md_hash_buffer (GCRY_MD_SHA1, hashvalue,
415                        hash_begin, hash_end - hash_begin + 1);
416
417   rc = do_encryption (prot_begin, prot_end - prot_begin + 1,
418                       passphrase,  hashvalue,
419                       &protected, &protectedlen);
420   if (rc)
421     return rc;
422
423   /* Now create the protected version of the key.  Note that the 10
424      extra bytes are for for the inserted "protected-" string (the
425      beginning of the plaintext reads: "((11:private-key(" ). */
426   *resultlen = (10
427                 + (prot_begin-plainkey)
428                 + protectedlen
429                 + (real_end-prot_end));
430   *result = p = xtrymalloc (*resultlen);
431   if (!p)
432     {
433       xfree (protected);
434       return GNUPG_Out_Of_Core;
435     }
436   memcpy (p, "(21:protected-", 14);
437   p += 14;
438   memcpy (p, plainkey+4, prot_begin - plainkey - 4);
439   p += prot_begin - plainkey - 4;
440   memcpy (p, protected, protectedlen);
441   p += protectedlen;
442   memcpy (p, prot_end+1, real_end - prot_end);
443   p += real_end - prot_end;
444   assert ( p - *result == *resultlen);
445   xfree (protected);
446   return 0;
447 }
448
449 \f
450 /* Do the actual decryption and check the return list for consistency.  */
451 static int
452 do_decryption (const unsigned char *protected, size_t protectedlen, 
453                const char *passphrase, 
454                const unsigned char *s2ksalt, unsigned long s2kcount,
455                const unsigned char *iv, size_t ivlen,
456                unsigned char **result)
457 {
458   int rc = 0;
459   int blklen;
460   GCRY_CIPHER_HD hd;
461   unsigned char *outbuf;
462   size_t reallen;
463
464   blklen = gcry_cipher_get_algo_blklen (PROT_CIPHER);
465   if (protectedlen < 4 || (protectedlen%blklen))
466     return GNUPG_Corrupted_Protection;
467
468   hd = gcry_cipher_open (PROT_CIPHER, GCRY_CIPHER_MODE_CBC,
469                          GCRY_CIPHER_SECURE);
470   if (!hd)
471     return map_gcry_err (gcry_errno());
472
473   outbuf = gcry_malloc_secure (protectedlen);
474   if (!outbuf)
475     rc = GNUPG_Out_Of_Core;
476   if (!rc)
477     rc = gcry_cipher_setiv (hd, iv, ivlen);
478   if (!rc)
479     {
480       unsigned char *key;
481       size_t keylen = PROT_CIPHER_KEYLEN;
482       
483       key = gcry_malloc_secure (keylen);
484       if (!key)
485         rc = GNUPG_Out_Of_Core;
486       else
487         {
488           rc = hash_passphrase (passphrase, GCRY_MD_SHA1,
489                                 3, s2ksalt, s2kcount, key, keylen);
490           if (!rc)
491             rc = gcry_cipher_setkey (hd, key, keylen);
492           xfree (key);
493         }
494     }
495   if (!rc)
496     rc = gcry_cipher_decrypt (hd, outbuf, protectedlen,
497                               protected, protectedlen);
498   gcry_cipher_close (hd);
499   if (rc)
500     {
501       xfree (outbuf);
502       return rc;
503     }
504   /* do a quick check first */
505   if (*outbuf != '(' && outbuf[1] != '(')
506     {
507       xfree (outbuf);
508       return GNUPG_Bad_Passphrase;
509     }
510   /* check that we have a consistent S-Exp */
511   reallen = gcry_sexp_canon_len (outbuf, protectedlen, NULL, NULL);
512   if (!reallen || (reallen + blklen < protectedlen) )
513     {
514       xfree (outbuf);
515       return GNUPG_Bad_Passphrase;
516     }
517   *result = outbuf;
518   return 0;
519 }
520
521
522 /* Merge the parameter list contained in CLEARTEXT with the original
523    protect lists PROTECTEDKEY by replacing the list at REPLACEPOS.
524    Return the new list in RESULT and the MIC value in the 20 byte
525    buffer SHA1HASH. */
526 static int
527 merge_lists (const unsigned char *protectedkey,
528              size_t replacepos, 
529              const unsigned char *cleartext,
530              unsigned char *sha1hash, unsigned char **result)
531 {
532   size_t n, newlistlen;
533   unsigned char *newlist, *p;
534   const unsigned char *s;
535   const unsigned char *startpos, *endpos;
536   int i, rc;
537   
538   if (replacepos < 26)
539     return GNUPG_Bug;
540
541   /* Estimate the required size of the resulting list.  We have a large
542      safety margin of >20 bytes (MIC hash from CLEARTEXT and the
543      removed "protected-" */
544   newlistlen = gcry_sexp_canon_len (protectedkey, 0, NULL, NULL);
545   if (!newlistlen)
546     return GNUPG_Bug;
547   n = gcry_sexp_canon_len (cleartext, 0, NULL, NULL);
548   if (!n)
549     return GNUPG_Bug;
550   newlistlen += n;
551   newlist = gcry_malloc_secure (newlistlen);
552   if (!newlist)
553     return GNUPG_Out_Of_Core;
554
555   /* Copy the initial segment */
556   strcpy (newlist, "(11:private-key");
557   p = newlist + 15;
558   memcpy (p, protectedkey+15+10, replacepos-15-10);
559   p += replacepos-15-10;
560
561   /* copy the cleartext */
562   s = cleartext;
563   if (*s != '(' && s[1] != '(')
564     return GNUPG_Bug;  /*we already checked this */
565   s += 2;
566   startpos = s;
567   while ( *s == '(' )
568     {
569       s++;
570       n = snext (&s);
571       if (!n)
572         goto invalid_sexp;
573       s += n;
574       n = snext (&s);
575       if (!n)
576         goto invalid_sexp;
577       s += n;
578       if ( *s != ')' )
579         goto invalid_sexp;
580       s++;
581     }
582   if ( *s != ')' )
583     goto invalid_sexp;
584   endpos = s;
585   s++;
586   /* short intermezzo: Get the MIC */
587   if (*s != '(')
588     goto invalid_sexp;
589   s++;
590   n = snext (&s);
591   if (!smatch (&s, n, "hash"))
592     goto invalid_sexp;
593   n = snext (&s);
594   if (!smatch (&s, n, "sha1"))
595     goto invalid_sexp; 
596   n = snext (&s);
597   if (n != 20)
598     goto invalid_sexp;
599   memcpy (sha1hash, s, 20);
600   s += n;
601   if (*s != ')')
602     goto invalid_sexp;
603   /* end intermezzo */
604
605   /* append the parameter list */
606   memcpy (p, startpos, endpos - startpos);
607   p += endpos - startpos;
608   
609   /* skip overt the protected list element in the original list */
610   s = protectedkey + replacepos;
611   assert (*s == '(');
612   s++;
613   i = 1;
614   rc = sskip (&s, &i);
615   if (rc)
616     goto failure;
617   startpos = s;
618   i = 2; /* we are inside this level */
619   rc = sskip (&s, &i);
620   if (rc)
621     goto failure;
622   assert (s[-1] == ')');
623   endpos = s; /* one behind the end of the list */
624
625   /* append the rest */
626   memcpy (p, startpos, endpos - startpos);
627   p += endpos - startpos;
628
629   /* ready */
630   *result = newlist;
631   return 0;
632
633  failure:
634   xfree (newlist);
635   return rc;
636
637  invalid_sexp:
638   xfree (newlist);
639   return GNUPG_Invalid_Sexp;
640 }
641
642
643
644 /* Unprotect the key encoded in canonical format.  We assume a valid
645    S-Exp here. */
646 int 
647 agent_unprotect (const unsigned char *protectedkey, const char *passphrase,
648                  unsigned char **result, size_t *resultlen)
649 {
650   int rc;
651   const unsigned char *s;
652   size_t n;
653   int infidx, i;
654   unsigned char sha1hash[20], sha1hash2[20];
655   const unsigned char *s2ksalt;
656   unsigned long s2kcount;
657   const unsigned char *iv;
658   const unsigned char *prot_begin;
659   unsigned char *cleartext;
660   unsigned char *final;
661
662   s = protectedkey;
663   if (*s != '(')
664     return GNUPG_Invalid_Sexp;
665   s++;
666   n = snext (&s);
667   if (!n)
668     return GNUPG_Invalid_Sexp; 
669   if (!smatch (&s, n, "protected-private-key"))
670     return GNUPG_Unknown_Sexp; 
671   if (*s != '(')
672     return GNUPG_Unknown_Sexp;
673   s++;
674   n = snext (&s);
675   if (!n)
676     return GNUPG_Invalid_Sexp; 
677
678   for (infidx=0; protect_info[infidx].algo
679               && !smatch (&s, n, protect_info[infidx].algo); infidx++)
680     ;
681   if (!protect_info[infidx].algo)
682     return GNUPG_Unsupported_Algorithm; 
683
684   /* now find the list with the protected information.  Here is an
685      example for such a list:
686      (protected openpgp-s2k3-sha1-aes-cbc 
687         ((sha1 <salt> <count>) <Initialization_Vector>)
688         <encrypted_data>)
689    */
690   for (;;)
691     {
692       if (*s != '(')
693         return GNUPG_Invalid_Sexp;
694       prot_begin = s;
695       s++;
696       n = snext (&s);
697       if (!n)
698         return GNUPG_Invalid_Sexp; 
699       if (smatch (&s, n, "protected"))
700         break;
701       s += n;
702       i = 1;
703       rc = sskip (&s, &i);
704       if (rc)
705         return rc;
706     }
707   /* found */
708   n = snext (&s);
709   if (!n)
710     return GNUPG_Invalid_Sexp; 
711   if (!smatch (&s, n, "openpgp-s2k3-sha1-" PROT_CIPHER_STRING "-cbc"))
712     return GNUPG_Unsupported_Protection;
713   if (*s != '(' || s[1] != '(')
714     return GNUPG_Invalid_Sexp;
715   s += 2;
716   n = snext (&s);
717   if (!n)
718     return GNUPG_Invalid_Sexp; 
719   if (!smatch (&s, n, "sha1"))
720     return GNUPG_Unsupported_Protection;
721   n = snext (&s);
722   if (n != 8)
723     return GNUPG_Corrupted_Protection;
724   s2ksalt = s;
725   s += n;
726   n = snext (&s);
727   if (!n)
728     return GNUPG_Corrupted_Protection;
729   /* We expect a list close as next, so we can simply use strtoul()
730      here.  We might want to check that we only have digits - but this
731      is nothing we should worry about */
732   if (s[n] != ')' )
733     return GNUPG_Invalid_Sexp;
734   s2kcount = strtoul (s, NULL, 10);
735   if (!s2kcount)
736     return GNUPG_Corrupted_Protection;
737   s += n;
738   s++; /* skip list end */
739
740   n = snext (&s);
741   if (n != 16) /* Wrong blocksize for IV (we support ony aes-128) */
742     return GNUPG_Corrupted_Protection;
743   iv = s;
744   s += n;
745   if (*s != ')' )
746     return GNUPG_Invalid_Sexp;
747   s++;
748   n = snext (&s);
749   if (!n)
750     return GNUPG_Invalid_Sexp; 
751   
752   rc = do_decryption (s, n,
753                       passphrase, s2ksalt, s2kcount,
754                       iv, 16,
755                       &cleartext);
756   if (rc)
757     return rc;
758
759   rc = merge_lists (protectedkey, prot_begin-protectedkey, cleartext,
760                     sha1hash, &final);
761   xfree (cleartext);
762   if (rc)
763     return rc;
764
765   rc = calculate_mic (final, sha1hash2);
766   if (!rc && memcmp (sha1hash, sha1hash2, 20))
767     rc = GNUPG_Corrupted_Protection;
768   if (rc)
769     {
770       xfree (final);
771       return rc;
772     }
773
774   *result = final;
775   *resultlen = gcry_sexp_canon_len (final, 0, NULL, NULL);
776   return 0;
777 }
778
779
780
781 \f
782 /* Transform a passphrase into a suitable key of length KEYLEN and
783    store this key in the caller provided buffer KEY.  The caller must
784    provide an HASHALGO, a valid S2KMODE (see rfc-2440) and depending on
785    that mode an S2KSALT of 8 random bytes and an S2KCOUNT (a suitable
786    value is 96).
787   
788    Returns an error code on failure.  */
789 static int
790 hash_passphrase (const char *passphrase, int hashalgo,
791                  int s2kmode,
792                  const unsigned char *s2ksalt,
793                  unsigned long s2kcount,
794                  unsigned char *key, size_t keylen)
795 {
796   GCRY_MD_HD md;
797   int pass, i;
798   int used = 0;
799   int pwlen = strlen (passphrase);
800
801   if ( (s2kmode != 0 && s2kmode != 1 && s2kmode != 3)
802       || !hashalgo || !keylen || !key || !passphrase)
803     return GNUPG_Invalid_Value;
804   if ((s2kmode == 1 ||s2kmode == 3) && !s2ksalt)
805     return GNUPG_Invalid_Value;
806   
807   md = gcry_md_open (hashalgo, GCRY_MD_FLAG_SECURE);
808   if (!md)
809     return map_gcry_err (gcry_errno());
810
811   for (pass=0; used < keylen; pass++)
812     {
813       if (pass)
814         {
815           gcry_md_reset (md);
816           for (i=0; i < pass; i++) /* preset the hash context */
817             gcry_md_putc (md, 0);
818         }
819
820       if (s2kmode == 1 || s2kmode == 3)
821         {
822           int len2 = pwlen + 8;
823           unsigned long count = len2;
824
825           if (s2kmode == 3)
826             {
827               count = (16ul + (s2kcount & 15)) << ((s2kcount >> 4) + 6);
828               if (count < len2)
829                 count = len2;
830             }
831
832           while (count > len2)
833             {
834               gcry_md_write (md, s2ksalt, 8);
835               gcry_md_write (md, passphrase, pwlen);
836               count -= len2;
837             }
838           if (count < 8)
839             gcry_md_write (md, s2ksalt, count);
840           else 
841             {
842               gcry_md_write (md, s2ksalt, 8);
843               count -= 8;
844               gcry_md_write (md, passphrase, count);
845             }
846         }
847       else
848         gcry_md_write (md, passphrase, pwlen);
849       
850       gcry_md_final (md);
851       i = gcry_md_get_algo_dlen (hashalgo);
852       if (i > keylen - used)
853         i = keylen - used;
854       memcpy  (key+used, gcry_md_read (md, hashalgo), i);
855       used += i;
856     }
857   gcry_md_close(md);
858   return 0;
859 }
860
861