~ubuntu-branches/ubuntu/precise/gnupg2/precise-proposed

« back to all changes in this revision

Viewing changes to agent/protect.c

  • Committer: Bazaar Package Importer
  • Author(s): Andreas Mueller
  • Date: 2005-03-29 10:30:32 UTC
  • Revision ID: james.westby@ubuntu.com-20050329103032-sj42n2ain3ipx310
Tags: upstream-1.9.15
ImportĀ upstreamĀ versionĀ 1.9.15

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* protect.c - Un/Protect a secret key
 
2
 * Copyright (C) 1998, 1999, 2000, 2001, 2002,
 
3
 *               2003 Free Software Foundation, Inc.
 
4
 *
 
5
 * This file is part of GnuPG.
 
6
 *
 
7
 * GnuPG is free software; you can redistribute it and/or modify
 
8
 * it under the terms of the GNU General Public License as published by
 
9
 * the Free Software Foundation; either version 2 of the License, or
 
10
 * (at your option) any later version.
 
11
 *
 
12
 * GnuPG is distributed in the hope that it will be useful,
 
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
15
 * GNU General Public License for more details.
 
16
 *
 
17
 * You should have received a copy of the GNU General Public License
 
18
 * along with this program; if not, write to the Free Software
 
19
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
 
20
 */
 
21
 
 
22
#include <config.h>
 
23
#include <errno.h>
 
24
#include <stdio.h>
 
25
#include <stdlib.h>
 
26
#include <string.h>
 
27
#include <ctype.h>
 
28
#include <assert.h>
 
29
#include <unistd.h>
 
30
#include <sys/stat.h>
 
31
 
 
32
#include "agent.h"
 
33
 
 
34
#include "sexp-parse.h"
 
35
 
 
36
#define PROT_CIPHER        GCRY_CIPHER_AES
 
37
#define PROT_CIPHER_STRING "aes"
 
38
#define PROT_CIPHER_KEYLEN (128/8)
 
39
 
 
40
 
 
41
/* A table containing the information needed to create a protected
 
42
   private key */
 
43
static struct {
 
44
  const char *algo;
 
45
  const char *parmlist;
 
46
  int prot_from, prot_to;
 
47
} protect_info[] = {
 
48
  { "rsa",  "nedpqu", 2, 5 },
 
49
  { "dsa",  "pqgyx", 4, 4 },
 
50
  { "elg",  "pgyx", 3, 3 },
 
51
  { NULL }
 
52
};
 
53
 
 
54
 
 
55
static int
 
56
hash_passphrase (const char *passphrase, int hashalgo,
 
57
                 int s2kmode,
 
58
                 const unsigned char *s2ksalt, unsigned long s2kcount,
 
59
                 unsigned char *key, size_t keylen);
 
60
 
 
61
 
 
62
 
 
63
/* Calculate the MIC for a private key S-Exp. SHA1HASH should pint to
 
64
   a 20 byte buffer.  This function is suitable for any algorithms. */
 
65
static int 
 
66
calculate_mic (const unsigned char *plainkey, unsigned char *sha1hash)
 
67
{
 
68
  const unsigned char *hash_begin, *hash_end;
 
69
  const unsigned char *s;
 
70
  size_t n;
 
71
 
 
72
  s = plainkey;
 
73
  if (*s != '(')
 
74
    return gpg_error (GPG_ERR_INV_SEXP);
 
75
  s++;
 
76
  n = snext (&s);
 
77
  if (!n)
 
78
    return gpg_error (GPG_ERR_INV_SEXP); 
 
79
  if (!smatch (&s, n, "private-key"))
 
80
    return gpg_error (GPG_ERR_UNKNOWN_SEXP); 
 
81
  if (*s != '(')
 
82
    return gpg_error (GPG_ERR_UNKNOWN_SEXP);
 
83
  hash_begin = s;
 
84
  s++;
 
85
  n = snext (&s);
 
86
  if (!n)
 
87
    return gpg_error (GPG_ERR_INV_SEXP); 
 
88
  s += n; /* skip over the algorithm name */
 
89
 
 
90
  while (*s == '(')
 
91
    {
 
92
      s++;
 
93
      n = snext (&s);
 
94
      if (!n)
 
95
        return gpg_error (GPG_ERR_INV_SEXP); 
 
96
      s += n;
 
97
      n = snext (&s);
 
98
      if (!n)
 
99
        return gpg_error (GPG_ERR_INV_SEXP); 
 
100
      s += n;
 
101
      if ( *s != ')' )
 
102
        return gpg_error (GPG_ERR_INV_SEXP); 
 
103
      s++;
 
104
    }
 
105
  if (*s != ')')
 
106
    return gpg_error (GPG_ERR_INV_SEXP); 
 
107
  s++;
 
108
  hash_end = s;
 
109
 
 
110
  gcry_md_hash_buffer (GCRY_MD_SHA1, sha1hash,
 
111
                       hash_begin, hash_end - hash_begin);
 
112
 
 
113
  return 0;
 
114
}
 
115
 
 
116
 
 
117
 
 
118
/* Encrypt the parameter block starting at PROTBEGIN with length
 
119
   PROTLEN using the utf8 encoded key PASSPHRASE and return the entire
 
120
   encrypted block in RESULT or ereturn with an error code.  SHA1HASH
 
121
   is the 20 byte SHA-1 hash required for the integrity code.
 
122
 
 
123
   The parameter block is expected to be an incomplete S-Expression of
 
124
   the form (example in advanced format):
 
125
 
 
126
     (d #046129F..[some bytes not shown]..81#)
 
127
     (p #00e861b..[some bytes not shown]..f1#)
 
128
     (q #00f7a7c..[some bytes not shown]..61#)
 
129
     (u #304559a..[some bytes not shown]..9b#) 
 
130
 
 
131
   the returned block is the S-Expression:
 
132
 
 
133
    (protected mode (parms) encrypted_octet_string)
 
134
 
 
135
*/
 
136
static int
 
137
do_encryption (const char *protbegin, size_t protlen, 
 
138
               const char *passphrase,  const unsigned char *sha1hash,
 
139
               unsigned char **result, size_t *resultlen)
 
140
{
 
141
  gcry_cipher_hd_t hd;
 
142
  const char *modestr = "openpgp-s2k3-sha1-" PROT_CIPHER_STRING "-cbc";
 
143
  int blklen, enclen, outlen;
 
144
  char *iv = NULL;
 
145
  int rc;
 
146
  char *outbuf = NULL;
 
147
  char *p;
 
148
  int saltpos, ivpos, encpos;
 
149
 
 
150
  rc = gcry_cipher_open (&hd, PROT_CIPHER, GCRY_CIPHER_MODE_CBC,
 
151
                         GCRY_CIPHER_SECURE);
 
152
  if (rc)
 
153
    return rc;
 
154
 
 
155
 
 
156
  /* We need to work on a copy of the data because this makes it
 
157
     easier to add the trailer and the padding and more important we
 
158
     have to prefix the text with 2 parenthesis, so we have to
 
159
     allocate enough space for:
 
160
 
 
161
     ((<parameter_list>)(4:hash4:sha120:<hashvalue>)) + padding
 
162
 
 
163
     We always append a full block of random bytes as padding but
 
164
     encrypt only what is needed for a full blocksize */
 
165
  blklen = gcry_cipher_get_algo_blklen (PROT_CIPHER);
 
166
  outlen = 2 + protlen + 2 + 6 + 6 + 23 + 2 + blklen;
 
167
  enclen = outlen/blklen * blklen;
 
168
  outbuf = gcry_malloc_secure (outlen);
 
169
  if (!outbuf)
 
170
    rc = out_of_core ();
 
171
  if (!rc)
 
172
    {
 
173
      /* Allocate random bytes to be used as IV, padding and s2k salt. */
 
174
      iv = xtrymalloc (blklen*2+8);
 
175
      if (!iv)
 
176
        rc = gpg_error (GPG_ERR_ENOMEM);
 
177
      gcry_create_nonce (iv, blklen*2+8);
 
178
      rc = gcry_cipher_setiv (hd, iv, blklen);
 
179
    }
 
180
  if (!rc)
 
181
    {
 
182
      unsigned char *key;
 
183
      size_t keylen = PROT_CIPHER_KEYLEN;
 
184
      
 
185
      key = gcry_malloc_secure (keylen);
 
186
      if (!key)
 
187
        rc = out_of_core ();
 
188
      else
 
189
        {
 
190
          rc = hash_passphrase (passphrase, GCRY_MD_SHA1,
 
191
                                3, iv+2*blklen, 96, key, keylen);
 
192
          if (!rc)
 
193
            rc = gcry_cipher_setkey (hd, key, keylen);
 
194
          xfree (key);
 
195
        }
 
196
    }
 
197
  if (!rc)
 
198
    {
 
199
      p = outbuf;
 
200
      *p++ = '(';
 
201
      *p++ = '(';
 
202
      memcpy (p, protbegin, protlen);
 
203
      p += protlen;
 
204
      memcpy (p, ")(4:hash4:sha120:", 17);
 
205
      p += 17;
 
206
      memcpy (p, sha1hash, 20);
 
207
      p += 20;
 
208
      *p++ = ')';
 
209
      *p++ = ')';
 
210
      memcpy (p, iv+blklen, blklen); 
 
211
      p += blklen;
 
212
      assert ( p - outbuf == outlen);
 
213
      rc = gcry_cipher_encrypt (hd, outbuf, enclen, NULL, 0);
 
214
    }
 
215
  gcry_cipher_close (hd);
 
216
  if (rc)
 
217
    {
 
218
      xfree (iv);
 
219
      xfree (outbuf);
 
220
      return rc;
 
221
    }
 
222
 
 
223
  /* Now allocate the buffer we want to return.  This is
 
224
 
 
225
     (protected openpgp-s2k3-sha1-aes-cbc
 
226
       ((sha1 salt no_of_iterations) 16byte_iv)
 
227
       encrypted_octet_string)
 
228
       
 
229
     in canoncical format of course.  We use asprintf and %n modifier
 
230
     and spaces as palceholders.  */
 
231
  asprintf (&p,
 
232
            "(9:protected%d:%s((4:sha18:%n_8bytes_2:96)%d:%n%*s)%d:%n%*s)",
 
233
            (int)strlen (modestr), modestr,
 
234
            &saltpos, 
 
235
            blklen, &ivpos, blklen, "",
 
236
            enclen, &encpos, enclen, "");
 
237
  if (p)
 
238
    { /* asprintf does not use our malloc system */
 
239
      char *psave = p;
 
240
      p = xtrymalloc (strlen (psave)+1);
 
241
      if (p)
 
242
        strcpy (p, psave);
 
243
      free (psave);
 
244
    }
 
245
  if (!p)
 
246
    {
 
247
      gpg_error_t tmperr = out_of_core ();
 
248
      xfree (iv);
 
249
      xfree (outbuf);
 
250
      return tmperr;
 
251
    }
 
252
  *resultlen = strlen (p);
 
253
  *result = p;
 
254
  memcpy (p+saltpos, iv+2*blklen, 8);
 
255
  memcpy (p+ivpos, iv, blklen);
 
256
  memcpy (p+encpos, outbuf, enclen);
 
257
  xfree (iv);
 
258
  xfree (outbuf);
 
259
  return 0;
 
260
}
 
261
 
 
262
 
 
263
 
 
264
/* Protect the key encoded in canonical format in plainkey.  We assume
 
265
   a valid S-Exp here. */
 
266
int 
 
267
agent_protect (const unsigned char *plainkey, const char *passphrase,
 
268
               unsigned char **result, size_t *resultlen)
 
269
{
 
270
  int rc;
 
271
  const unsigned char *s;
 
272
  const unsigned char *hash_begin, *hash_end;
 
273
  const unsigned char *prot_begin, *prot_end, *real_end;
 
274
  size_t n;
 
275
  int c, infidx, i;
 
276
  unsigned char hashvalue[20];
 
277
  unsigned char *protected;
 
278
  size_t protectedlen;
 
279
  int depth = 0;
 
280
  unsigned char *p;
 
281
 
 
282
  s = plainkey;
 
283
  if (*s != '(')
 
284
    return gpg_error (GPG_ERR_INV_SEXP);
 
285
  depth++;
 
286
  s++;
 
287
  n = snext (&s);
 
288
  if (!n)
 
289
    return gpg_error (GPG_ERR_INV_SEXP); 
 
290
  if (!smatch (&s, n, "private-key"))
 
291
    return gpg_error (GPG_ERR_UNKNOWN_SEXP); 
 
292
  if (*s != '(')
 
293
    return gpg_error (GPG_ERR_UNKNOWN_SEXP);
 
294
  depth++;
 
295
  hash_begin = s;
 
296
  s++;
 
297
  n = snext (&s);
 
298
  if (!n)
 
299
    return gpg_error (GPG_ERR_INV_SEXP); 
 
300
 
 
301
  for (infidx=0; protect_info[infidx].algo
 
302
              && !smatch (&s, n, protect_info[infidx].algo); infidx++)
 
303
    ;
 
304
  if (!protect_info[infidx].algo)
 
305
    return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM); 
 
306
 
 
307
  prot_begin = prot_end = NULL;
 
308
  for (i=0; (c=protect_info[infidx].parmlist[i]); i++)
 
309
    {
 
310
      if (i == protect_info[infidx].prot_from)
 
311
        prot_begin = s;
 
312
      if (*s != '(')
 
313
        return gpg_error (GPG_ERR_INV_SEXP);
 
314
      depth++;
 
315
      s++;
 
316
      n = snext (&s);
 
317
      if (!n)
 
318
        return gpg_error (GPG_ERR_INV_SEXP); 
 
319
      if (n != 1 || c != *s)
 
320
        return gpg_error (GPG_ERR_INV_SEXP); 
 
321
      s += n;
 
322
      n = snext (&s);
 
323
      if (!n)
 
324
        return gpg_error (GPG_ERR_INV_SEXP); 
 
325
      s +=n; /* skip value */
 
326
      if (*s != ')')
 
327
        return gpg_error (GPG_ERR_INV_SEXP); 
 
328
      depth--;
 
329
      if (i == protect_info[infidx].prot_to)
 
330
        prot_end = s;
 
331
      s++;
 
332
    }
 
333
  if (*s != ')' || !prot_begin || !prot_end )
 
334
    return gpg_error (GPG_ERR_INV_SEXP); 
 
335
  depth--;
 
336
  hash_end = s;
 
337
  s++;
 
338
  /* skip to the end of the S-exp */
 
339
  assert (depth == 1);
 
340
  rc = sskip (&s, &depth);
 
341
  if (rc)
 
342
    return rc;
 
343
  assert (!depth);
 
344
  real_end = s-1;
 
345
 
 
346
  gcry_md_hash_buffer (GCRY_MD_SHA1, hashvalue,
 
347
                       hash_begin, hash_end - hash_begin + 1);
 
348
 
 
349
  rc = do_encryption (prot_begin, prot_end - prot_begin + 1,
 
350
                      passphrase,  hashvalue,
 
351
                      &protected, &protectedlen);
 
352
  if (rc)
 
353
    return rc;
 
354
 
 
355
  /* Now create the protected version of the key.  Note that the 10
 
356
     extra bytes are for for the inserted "protected-" string (the
 
357
     beginning of the plaintext reads: "((11:private-key(" ). */
 
358
  *resultlen = (10
 
359
                + (prot_begin-plainkey)
 
360
                + protectedlen
 
361
                + (real_end-prot_end));
 
362
  *result = p = xtrymalloc (*resultlen);
 
363
  if (!p)
 
364
    {
 
365
      gpg_error_t tmperr = out_of_core ();
 
366
      xfree (protected);
 
367
      return tmperr;
 
368
    }
 
369
  memcpy (p, "(21:protected-", 14);
 
370
  p += 14;
 
371
  memcpy (p, plainkey+4, prot_begin - plainkey - 4);
 
372
  p += prot_begin - plainkey - 4;
 
373
  memcpy (p, protected, protectedlen);
 
374
  p += protectedlen;
 
375
  memcpy (p, prot_end+1, real_end - prot_end);
 
376
  p += real_end - prot_end;
 
377
  assert ( p - *result == *resultlen);
 
378
  xfree (protected);
 
379
  return 0;
 
380
}
 
381
 
 
382
 
 
383
/* Do the actual decryption and check the return list for consistency.  */
 
384
static int
 
385
do_decryption (const unsigned char *protected, size_t protectedlen, 
 
386
               const char *passphrase, 
 
387
               const unsigned char *s2ksalt, unsigned long s2kcount,
 
388
               const unsigned char *iv, size_t ivlen,
 
389
               unsigned char **result)
 
390
{
 
391
  int rc = 0;
 
392
  int blklen;
 
393
  gcry_cipher_hd_t hd;
 
394
  unsigned char *outbuf;
 
395
  size_t reallen;
 
396
 
 
397
  blklen = gcry_cipher_get_algo_blklen (PROT_CIPHER);
 
398
  if (protectedlen < 4 || (protectedlen%blklen))
 
399
    return gpg_error (GPG_ERR_CORRUPTED_PROTECTION);
 
400
 
 
401
  rc = gcry_cipher_open (&hd, PROT_CIPHER, GCRY_CIPHER_MODE_CBC,
 
402
                         GCRY_CIPHER_SECURE);
 
403
  if (rc)
 
404
    return rc;
 
405
 
 
406
  outbuf = gcry_malloc_secure (protectedlen);
 
407
  if (!outbuf)
 
408
    rc = out_of_core ();
 
409
  if (!rc)
 
410
    rc = gcry_cipher_setiv (hd, iv, ivlen);
 
411
  if (!rc)
 
412
    {
 
413
      unsigned char *key;
 
414
      size_t keylen = PROT_CIPHER_KEYLEN;
 
415
      
 
416
      key = gcry_malloc_secure (keylen);
 
417
      if (!key)
 
418
        rc = out_of_core ();
 
419
      else
 
420
        {
 
421
          rc = hash_passphrase (passphrase, GCRY_MD_SHA1,
 
422
                                3, s2ksalt, s2kcount, key, keylen);
 
423
          if (!rc)
 
424
            rc = gcry_cipher_setkey (hd, key, keylen);
 
425
          xfree (key);
 
426
        }
 
427
    }
 
428
  if (!rc)
 
429
    rc = gcry_cipher_decrypt (hd, outbuf, protectedlen,
 
430
                              protected, protectedlen);
 
431
  gcry_cipher_close (hd);
 
432
  if (rc)
 
433
    {
 
434
      xfree (outbuf);
 
435
      return rc;
 
436
    }
 
437
  /* Do a quick check first. */
 
438
  if (*outbuf != '(' && outbuf[1] != '(')
 
439
    {
 
440
      xfree (outbuf);
 
441
      return gpg_error (GPG_ERR_BAD_PASSPHRASE);
 
442
    }
 
443
  /* Check that we have a consistent S-Exp. */
 
444
  reallen = gcry_sexp_canon_len (outbuf, protectedlen, NULL, NULL);
 
445
  if (!reallen || (reallen + blklen < protectedlen) )
 
446
    {
 
447
      xfree (outbuf);
 
448
      return gpg_error (GPG_ERR_BAD_PASSPHRASE);
 
449
    }
 
450
  *result = outbuf;
 
451
  return 0;
 
452
}
 
453
 
 
454
 
 
455
/* Merge the parameter list contained in CLEARTEXT with the original
 
456
   protect lists PROTECTEDKEY by replacing the list at REPLACEPOS.
 
457
   Return the new list in RESULT and the MIC value in the 20 byte
 
458
   buffer SHA1HASH. */
 
459
static int
 
460
merge_lists (const unsigned char *protectedkey,
 
461
             size_t replacepos, 
 
462
             const unsigned char *cleartext,
 
463
             unsigned char *sha1hash,
 
464
             unsigned char **result, size_t *resultlen)
 
465
{
 
466
  size_t n, newlistlen;
 
467
  unsigned char *newlist, *p;
 
468
  const unsigned char *s;
 
469
  const unsigned char *startpos, *endpos;
 
470
  int i, rc;
 
471
  
 
472
  if (replacepos < 26)
 
473
    return gpg_error (GPG_ERR_BUG);
 
474
 
 
475
  /* Estimate the required size of the resulting list.  We have a large
 
476
     safety margin of >20 bytes (MIC hash from CLEARTEXT and the
 
477
     removed "protected-" */
 
478
  newlistlen = gcry_sexp_canon_len (protectedkey, 0, NULL, NULL);
 
479
  if (!newlistlen)
 
480
    return gpg_error (GPG_ERR_BUG);
 
481
  n = gcry_sexp_canon_len (cleartext, 0, NULL, NULL);
 
482
  if (!n)
 
483
    return gpg_error (GPG_ERR_BUG);
 
484
  newlistlen += n;
 
485
  newlist = gcry_malloc_secure (newlistlen);
 
486
  if (!newlist)
 
487
    return out_of_core ();
 
488
 
 
489
  /* Copy the initial segment */
 
490
  strcpy (newlist, "(11:private-key");
 
491
  p = newlist + 15;
 
492
  memcpy (p, protectedkey+15+10, replacepos-15-10);
 
493
  p += replacepos-15-10;
 
494
 
 
495
  /* copy the cleartext */
 
496
  s = cleartext;
 
497
  if (*s != '(' && s[1] != '(')
 
498
    return gpg_error (GPG_ERR_BUG);  /*we already checked this */
 
499
  s += 2;
 
500
  startpos = s;
 
501
  while ( *s == '(' )
 
502
    {
 
503
      s++;
 
504
      n = snext (&s);
 
505
      if (!n)
 
506
        goto invalid_sexp;
 
507
      s += n;
 
508
      n = snext (&s);
 
509
      if (!n)
 
510
        goto invalid_sexp;
 
511
      s += n;
 
512
      if ( *s != ')' )
 
513
        goto invalid_sexp;
 
514
      s++;
 
515
    }
 
516
  if ( *s != ')' )
 
517
    goto invalid_sexp;
 
518
  endpos = s;
 
519
  s++;
 
520
  /* short intermezzo: Get the MIC */
 
521
  if (*s != '(')
 
522
    goto invalid_sexp;
 
523
  s++;
 
524
  n = snext (&s);
 
525
  if (!smatch (&s, n, "hash"))
 
526
    goto invalid_sexp;
 
527
  n = snext (&s);
 
528
  if (!smatch (&s, n, "sha1"))
 
529
    goto invalid_sexp; 
 
530
  n = snext (&s);
 
531
  if (n != 20)
 
532
    goto invalid_sexp;
 
533
  memcpy (sha1hash, s, 20);
 
534
  s += n;
 
535
  if (*s != ')')
 
536
    goto invalid_sexp;
 
537
  /* end intermezzo */
 
538
 
 
539
  /* append the parameter list */
 
540
  memcpy (p, startpos, endpos - startpos);
 
541
  p += endpos - startpos;
 
542
  
 
543
  /* skip overt the protected list element in the original list */
 
544
  s = protectedkey + replacepos;
 
545
  assert (*s == '(');
 
546
  s++;
 
547
  i = 1;
 
548
  rc = sskip (&s, &i);
 
549
  if (rc)
 
550
    goto failure;
 
551
  startpos = s;
 
552
  i = 2; /* we are inside this level */
 
553
  rc = sskip (&s, &i);
 
554
  if (rc)
 
555
    goto failure;
 
556
  assert (s[-1] == ')');
 
557
  endpos = s; /* one behind the end of the list */
 
558
 
 
559
  /* append the rest */
 
560
  memcpy (p, startpos, endpos - startpos);
 
561
  p += endpos - startpos;
 
562
 
 
563
  /* ready */
 
564
  *result = newlist;
 
565
  *resultlen = newlistlen;
 
566
  return 0;
 
567
 
 
568
 failure:
 
569
  wipememory (newlist, newlistlen);
 
570
  xfree (newlist);
 
571
  return rc;
 
572
 
 
573
 invalid_sexp:
 
574
  wipememory (newlist, newlistlen);
 
575
  xfree (newlist);
 
576
  return gpg_error (GPG_ERR_INV_SEXP);
 
577
}
 
578
 
 
579
 
 
580
 
 
581
/* Unprotect the key encoded in canonical format.  We assume a valid
 
582
   S-Exp here. */
 
583
int 
 
584
agent_unprotect (const unsigned char *protectedkey, const char *passphrase,
 
585
                 unsigned char **result, size_t *resultlen)
 
586
{
 
587
  int rc;
 
588
  const unsigned char *s;
 
589
  size_t n;
 
590
  int infidx, i;
 
591
  unsigned char sha1hash[20], sha1hash2[20];
 
592
  const unsigned char *s2ksalt;
 
593
  unsigned long s2kcount;
 
594
  const unsigned char *iv;
 
595
  const unsigned char *prot_begin;
 
596
  unsigned char *cleartext;
 
597
  unsigned char *final;
 
598
  size_t finallen;
 
599
 
 
600
  s = protectedkey;
 
601
  if (*s != '(')
 
602
    return gpg_error (GPG_ERR_INV_SEXP);
 
603
  s++;
 
604
  n = snext (&s);
 
605
  if (!n)
 
606
    return gpg_error (GPG_ERR_INV_SEXP); 
 
607
  if (!smatch (&s, n, "protected-private-key"))
 
608
    return gpg_error (GPG_ERR_UNKNOWN_SEXP); 
 
609
  if (*s != '(')
 
610
    return gpg_error (GPG_ERR_UNKNOWN_SEXP);
 
611
  s++;
 
612
  n = snext (&s);
 
613
  if (!n)
 
614
    return gpg_error (GPG_ERR_INV_SEXP); 
 
615
 
 
616
  for (infidx=0; protect_info[infidx].algo
 
617
              && !smatch (&s, n, protect_info[infidx].algo); infidx++)
 
618
    ;
 
619
  if (!protect_info[infidx].algo)
 
620
    return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM); 
 
621
 
 
622
  /* Now find the list with the protected information.  Here is an
 
623
     example for such a list:
 
624
     (protected openpgp-s2k3-sha1-aes-cbc 
 
625
        ((sha1 <salt> <count>) <Initialization_Vector>)
 
626
        <encrypted_data>)
 
627
   */
 
628
  for (;;)
 
629
    {
 
630
      if (*s != '(')
 
631
        return gpg_error (GPG_ERR_INV_SEXP);
 
632
      prot_begin = s;
 
633
      s++;
 
634
      n = snext (&s);
 
635
      if (!n)
 
636
        return gpg_error (GPG_ERR_INV_SEXP); 
 
637
      if (smatch (&s, n, "protected"))
 
638
        break;
 
639
      s += n;
 
640
      i = 1;
 
641
      rc = sskip (&s, &i);
 
642
      if (rc)
 
643
        return rc;
 
644
    }
 
645
  /* found */
 
646
  n = snext (&s);
 
647
  if (!n)
 
648
    return gpg_error (GPG_ERR_INV_SEXP); 
 
649
  if (!smatch (&s, n, "openpgp-s2k3-sha1-" PROT_CIPHER_STRING "-cbc"))
 
650
    return gpg_error (GPG_ERR_UNSUPPORTED_PROTECTION);
 
651
  if (*s != '(' || s[1] != '(')
 
652
    return gpg_error (GPG_ERR_INV_SEXP);
 
653
  s += 2;
 
654
  n = snext (&s);
 
655
  if (!n)
 
656
    return gpg_error (GPG_ERR_INV_SEXP); 
 
657
  if (!smatch (&s, n, "sha1"))
 
658
    return gpg_error (GPG_ERR_UNSUPPORTED_PROTECTION);
 
659
  n = snext (&s);
 
660
  if (n != 8)
 
661
    return gpg_error (GPG_ERR_CORRUPTED_PROTECTION);
 
662
  s2ksalt = s;
 
663
  s += n;
 
664
  n = snext (&s);
 
665
  if (!n)
 
666
    return gpg_error (GPG_ERR_CORRUPTED_PROTECTION);
 
667
  /* We expect a list close as next, so we can simply use strtoul()
 
668
     here.  We might want to check that we only have digits - but this
 
669
     is nothing we should worry about */
 
670
  if (s[n] != ')' )
 
671
    return gpg_error (GPG_ERR_INV_SEXP);
 
672
  s2kcount = strtoul (s, NULL, 10);
 
673
  if (!s2kcount)
 
674
    return gpg_error (GPG_ERR_CORRUPTED_PROTECTION);
 
675
  s += n;
 
676
  s++; /* skip list end */
 
677
 
 
678
  n = snext (&s);
 
679
  if (n != 16) /* Wrong blocksize for IV (we support only aes-128). */
 
680
    return gpg_error (GPG_ERR_CORRUPTED_PROTECTION);
 
681
  iv = s;
 
682
  s += n;
 
683
  if (*s != ')' )
 
684
    return gpg_error (GPG_ERR_INV_SEXP);
 
685
  s++;
 
686
  n = snext (&s);
 
687
  if (!n)
 
688
    return gpg_error (GPG_ERR_INV_SEXP); 
 
689
  
 
690
  rc = do_decryption (s, n,
 
691
                      passphrase, s2ksalt, s2kcount,
 
692
                      iv, 16,
 
693
                      &cleartext);
 
694
  if (rc)
 
695
    return rc;
 
696
 
 
697
  rc = merge_lists (protectedkey, prot_begin-protectedkey, cleartext,
 
698
                    sha1hash, &final, &finallen);
 
699
  /* Albeit cleartext has been allocated in secure memory and thus
 
700
     xfree will wipe it out, we do an extra wipe just in case
 
701
     somethings goes badly wrong. */
 
702
  wipememory (cleartext, n);
 
703
  xfree (cleartext);
 
704
  if (rc)
 
705
    return rc;
 
706
 
 
707
  rc = calculate_mic (final, sha1hash2);
 
708
  if (!rc && memcmp (sha1hash, sha1hash2, 20))
 
709
    rc = gpg_error (GPG_ERR_CORRUPTED_PROTECTION);
 
710
  if (rc)
 
711
    {
 
712
      wipememory (final, finallen);
 
713
      xfree (final);
 
714
      return rc;
 
715
    }
 
716
 
 
717
  *result = final;
 
718
  *resultlen = gcry_sexp_canon_len (final, 0, NULL, NULL);
 
719
  return 0;
 
720
}
 
721
 
 
722
/* Check the type of the private key, this is one of the constants:
 
723
   PRIVATE_KEY_UNKNOWN if we can't figure out the type (this is the
 
724
   value 0), PRIVATE_KEY_CLEAR for an unprotected private key.
 
725
   PRIVATE_KEY_PROTECTED for an protected private key or
 
726
   PRIVATE_KEY_SHADOWED for a sub key where the secret parts are stored
 
727
   elsewhere. */
 
728
int
 
729
agent_private_key_type (const unsigned char *privatekey)
 
730
{
 
731
  const unsigned char *s;
 
732
  size_t n;
 
733
 
 
734
  s = privatekey;
 
735
  if (*s != '(')
 
736
    return PRIVATE_KEY_UNKNOWN;
 
737
  s++;
 
738
  n = snext (&s);
 
739
  if (!n)
 
740
    return PRIVATE_KEY_UNKNOWN;
 
741
  if (smatch (&s, n, "protected-private-key"))
 
742
    return PRIVATE_KEY_PROTECTED;
 
743
  if (smatch (&s, n, "shadowed-private-key"))
 
744
    return PRIVATE_KEY_SHADOWED;
 
745
  if (smatch (&s, n, "private-key"))
 
746
    return PRIVATE_KEY_CLEAR;
 
747
  return PRIVATE_KEY_UNKNOWN;
 
748
}
 
749
 
 
750
 
 
751
 
 
752
/* Transform a passphrase into a suitable key of length KEYLEN and
 
753
   store this key in the caller provided buffer KEY.  The caller must
 
754
   provide an HASHALGO, a valid S2KMODE (see rfc-2440) and depending on
 
755
   that mode an S2KSALT of 8 random bytes and an S2KCOUNT (a suitable
 
756
   value is 96).
 
757
  
 
758
   Returns an error code on failure.  */
 
759
static int
 
760
hash_passphrase (const char *passphrase, int hashalgo,
 
761
                 int s2kmode,
 
762
                 const unsigned char *s2ksalt,
 
763
                 unsigned long s2kcount,
 
764
                 unsigned char *key, size_t keylen)
 
765
{
 
766
  int rc;
 
767
  gcry_md_hd_t md;
 
768
  int pass, i;
 
769
  int used = 0;
 
770
  int pwlen = strlen (passphrase);
 
771
 
 
772
  if ( (s2kmode != 0 && s2kmode != 1 && s2kmode != 3)
 
773
      || !hashalgo || !keylen || !key || !passphrase)
 
774
    return gpg_error (GPG_ERR_INV_VALUE);
 
775
  if ((s2kmode == 1 ||s2kmode == 3) && !s2ksalt)
 
776
    return gpg_error (GPG_ERR_INV_VALUE);
 
777
  
 
778
  rc = gcry_md_open (&md, hashalgo, GCRY_MD_FLAG_SECURE);
 
779
  if (rc)
 
780
    return rc;
 
781
 
 
782
  for (pass=0; used < keylen; pass++)
 
783
    {
 
784
      if (pass)
 
785
        {
 
786
          gcry_md_reset (md);
 
787
          for (i=0; i < pass; i++) /* preset the hash context */
 
788
            gcry_md_putc (md, 0);
 
789
        }
 
790
 
 
791
      if (s2kmode == 1 || s2kmode == 3)
 
792
        {
 
793
          int len2 = pwlen + 8;
 
794
          unsigned long count = len2;
 
795
 
 
796
          if (s2kmode == 3)
 
797
            {
 
798
              count = (16ul + (s2kcount & 15)) << ((s2kcount >> 4) + 6);
 
799
              if (count < len2)
 
800
                count = len2;
 
801
            }
 
802
 
 
803
          while (count > len2)
 
804
            {
 
805
              gcry_md_write (md, s2ksalt, 8);
 
806
              gcry_md_write (md, passphrase, pwlen);
 
807
              count -= len2;
 
808
            }
 
809
          if (count < 8)
 
810
            gcry_md_write (md, s2ksalt, count);
 
811
          else 
 
812
            {
 
813
              gcry_md_write (md, s2ksalt, 8);
 
814
              count -= 8;
 
815
              gcry_md_write (md, passphrase, count);
 
816
            }
 
817
        }
 
818
      else
 
819
        gcry_md_write (md, passphrase, pwlen);
 
820
      
 
821
      gcry_md_final (md);
 
822
      i = gcry_md_get_algo_dlen (hashalgo);
 
823
      if (i > keylen - used)
 
824
        i = keylen - used;
 
825
      memcpy  (key+used, gcry_md_read (md, hashalgo), i);
 
826
      used += i;
 
827
    }
 
828
  gcry_md_close(md);
 
829
  return 0;
 
830
}
 
831
 
 
832
 
 
833
 
 
834
/* Create a shadow key from a public key.  We use the shadow protocol
 
835
  "ti-v1" and insert the S-expressionn SHADOW_INFO.  The resulting
 
836
  S-expression is returned in an allocated buffer RESULT will point
 
837
  to. The input parameters are expected to be valid canonilized
 
838
  S-expressions */
 
839
int 
 
840
agent_shadow_key (const unsigned char *pubkey,
 
841
                  const unsigned char *shadow_info,
 
842
                  unsigned char **result)
 
843
{
 
844
  const unsigned char *s;
 
845
  const unsigned char *point;
 
846
  size_t n;
 
847
  int depth = 0;
 
848
  unsigned char *p;
 
849
  size_t pubkey_len = gcry_sexp_canon_len (pubkey, 0, NULL,NULL);
 
850
  size_t shadow_info_len = gcry_sexp_canon_len (shadow_info, 0, NULL,NULL);
 
851
 
 
852
  if (!pubkey_len || !shadow_info_len)
 
853
    return gpg_error (GPG_ERR_INV_VALUE);
 
854
  s = pubkey;
 
855
  if (*s != '(')
 
856
    return gpg_error (GPG_ERR_INV_SEXP);
 
857
  depth++;
 
858
  s++;
 
859
  n = snext (&s);
 
860
  if (!n)
 
861
    return gpg_error (GPG_ERR_INV_SEXP); 
 
862
  if (!smatch (&s, n, "public-key"))
 
863
    return gpg_error (GPG_ERR_UNKNOWN_SEXP); 
 
864
  if (*s != '(')
 
865
    return gpg_error (GPG_ERR_UNKNOWN_SEXP);
 
866
  depth++;
 
867
  s++;
 
868
  n = snext (&s); 
 
869
  if (!n)
 
870
    return gpg_error (GPG_ERR_INV_SEXP); 
 
871
  s += n; /* skip over the algorithm name */
 
872
 
 
873
  while (*s != ')')
 
874
    {
 
875
      if (*s != '(')
 
876
        return gpg_error (GPG_ERR_INV_SEXP);
 
877
      depth++;
 
878
      s++;
 
879
      n = snext (&s);
 
880
      if (!n) 
 
881
        return gpg_error (GPG_ERR_INV_SEXP); 
 
882
      s += n;
 
883
      n = snext (&s);
 
884
      if (!n)
 
885
        return gpg_error (GPG_ERR_INV_SEXP); 
 
886
      s +=n; /* skip value */
 
887
      if (*s != ')')
 
888
        return gpg_error (GPG_ERR_INV_SEXP); 
 
889
      depth--;
 
890
      s++;
 
891
    }
 
892
  point = s; /* insert right before the point */
 
893
  depth--;
 
894
  s++;
 
895
  assert (depth == 1);
 
896
 
 
897
  /* calculate required length by taking in account: the "shadowed-"
 
898
     prefix, the "shadowed", "t1-v1" as well as some parenthesis */
 
899
  n = 12 + pubkey_len + 1 + 3+8 + 2+5 + shadow_info_len + 1;
 
900
  *result = p = xtrymalloc (n);
 
901
  if (!p)
 
902
      return out_of_core ();
 
903
  p = stpcpy (p, "(20:shadowed-private-key");
 
904
  /* (10:public-key ...)*/
 
905
  memcpy (p, pubkey+14, point - (pubkey+14));
 
906
  p += point - (pubkey+14);
 
907
  p = stpcpy (p, "(8:shadowed5:t1-v1");
 
908
  memcpy (p, shadow_info, shadow_info_len);
 
909
  p += shadow_info_len;
 
910
  *p++ = ')';
 
911
  memcpy (p, point, pubkey_len - (point - pubkey));
 
912
  p += pubkey_len - (point - pubkey);
 
913
 
 
914
  return 0;
 
915
}
 
916
 
 
917
/* Parse a canonical encoded shadowed key and return a pointer to the
 
918
   inner list with the shadow_info */
 
919
int 
 
920
agent_get_shadow_info (const unsigned char *shadowkey,
 
921
                       unsigned char const **shadow_info)
 
922
{
 
923
  const unsigned char *s;
 
924
  size_t n;
 
925
  int depth = 0;
 
926
 
 
927
  s = shadowkey;
 
928
  if (*s != '(')
 
929
    return gpg_error (GPG_ERR_INV_SEXP);
 
930
  depth++;
 
931
  s++;
 
932
  n = snext (&s);
 
933
  if (!n)
 
934
    return gpg_error (GPG_ERR_INV_SEXP); 
 
935
  if (!smatch (&s, n, "shadowed-private-key"))
 
936
    return gpg_error (GPG_ERR_UNKNOWN_SEXP); 
 
937
  if (*s != '(')
 
938
    return gpg_error (GPG_ERR_UNKNOWN_SEXP);
 
939
  depth++;
 
940
  s++;
 
941
  n = snext (&s); 
 
942
  if (!n)
 
943
    return gpg_error (GPG_ERR_INV_SEXP); 
 
944
  s += n; /* skip over the algorithm name */
 
945
 
 
946
  for (;;)
 
947
    {
 
948
      if (*s == ')')
 
949
        return gpg_error (GPG_ERR_UNKNOWN_SEXP);
 
950
      if (*s != '(')
 
951
        return gpg_error (GPG_ERR_INV_SEXP);
 
952
      depth++;
 
953
      s++;
 
954
      n = snext (&s);
 
955
      if (!n) 
 
956
        return gpg_error (GPG_ERR_INV_SEXP); 
 
957
      if (smatch (&s, n, "shadowed"))
 
958
        break;
 
959
      s += n;
 
960
      n = snext (&s);
 
961
      if (!n)
 
962
        return gpg_error (GPG_ERR_INV_SEXP); 
 
963
      s +=n; /* skip value */
 
964
      if (*s != ')')
 
965
        return gpg_error (GPG_ERR_INV_SEXP); 
 
966
      depth--;
 
967
      s++;
 
968
    }
 
969
  /* Found the shadowed list, S points to the protocol */
 
970
  n = snext (&s);
 
971
  if (!n) 
 
972
    return gpg_error (GPG_ERR_INV_SEXP); 
 
973
  if (smatch (&s, n, "t1-v1"))
 
974
    {
 
975
      if (*s != '(')
 
976
        return gpg_error (GPG_ERR_INV_SEXP);
 
977
      *shadow_info = s;
 
978
    }
 
979
  else
 
980
    return gpg_error (GPG_ERR_UNSUPPORTED_PROTOCOL);
 
981
  return 0;
 
982
}
 
983